From e826472962233b97698b1ad74b261791fd49e73d Mon Sep 17 00:00:00 2001 From: karosc Date: Thu, 16 Mar 2023 22:23:06 -0400 Subject: [PATCH] Merge in USEPA-SWMM5.2.0 --- .gitignore | 3 + CMakeLists.txt | 3 +- extern/boost.cmake | 4 - src/outfile/CMakeLists.txt | 5 +- src/outfile/errormanager.h | 2 + src/outfile/messages.h | 2 + src/run/main.c | 42 +- src/solver/CMakeLists.txt | 14 +- src/solver/Roadmap.txt | 30 +- src/solver/bindings/swmm5.def | 9 + src/solver/climate.c | 206 ++- src/solver/consts.h | 20 +- src/solver/controls.c | 572 ++++++-- src/solver/culvert.c | 16 +- src/solver/datetime.c | 31 +- src/solver/datetime.h | 10 +- src/solver/dwflow.c | 57 +- src/solver/dynwave.c | 99 +- src/solver/enums.h | 67 +- src/solver/error.c | 246 +--- src/solver/error.h | 337 +++-- src/solver/error.txt | 135 ++ src/solver/exfil.c | 99 +- src/solver/exfil.h | 10 +- src/solver/findroot.h | 6 +- src/solver/flowrout.c | 43 +- src/solver/forcmain.c | 4 +- src/solver/funcs.h | 106 +- src/solver/gage.c | 151 +- src/solver/globals.h | 40 +- src/solver/gwater.c | 20 +- src/solver/hash.c | 8 +- src/solver/hash.h | 10 +- src/solver/headers.h | 16 +- src/solver/hotstart.c | 51 +- src/solver/iface.c | 80 +- src/solver/include/swmm5.h | 137 +- src/solver/include/toolkit.h | 4 + src/solver/include/toolkit_error.h | 18 + src/solver/include/toolkit_errors.txt | 14 + src/solver/include/toolkit_structs.h | 2 + src/solver/infil.c | 51 +- src/solver/infil.h | 18 +- src/solver/inflow.c | 67 +- src/solver/inlet.c | 1945 +++++++++++++++++++++++++ src/solver/inlet.h | 30 + src/solver/input.c | 76 +- src/solver/inputrpt.c | 43 +- src/solver/keywords.c | 50 +- src/solver/keywords.h | 11 +- src/solver/kinwave.c | 14 +- src/solver/landuse.c | 12 +- src/solver/lid.c | 310 ++-- src/solver/lid.h | 60 +- src/solver/lidproc.c | 122 +- src/solver/link.c | 154 +- src/solver/macros.h | 4 +- src/solver/massbal.c | 39 +- src/solver/mathexpr.c | 100 +- src/solver/mathexpr.h | 2 +- src/solver/mempool.c | 1 - src/solver/node.c | 371 +++-- src/solver/objects.h | 161 +- src/solver/odesolve.c | 12 +- src/solver/output.c | 206 +-- src/solver/project.c | 234 +-- src/solver/qualrout.c | 65 +- src/solver/rain.c | 30 +- src/solver/rdii.c | 41 +- src/solver/report.c | 312 ++-- src/solver/roadway.c | 8 +- src/solver/routing.c | 348 +++-- src/solver/runoff.c | 27 +- src/solver/shape.c | 4 +- src/solver/snow.c | 15 +- src/solver/stats.c | 300 ++-- src/solver/statsrpt.c | 73 +- src/solver/street.c | 162 ++ src/solver/street.h | 21 + src/solver/subcatch.c | 111 +- src/solver/surfqual.c | 16 +- src/solver/swmm5.c | 1203 +++++++++++++-- src/solver/table.c | 206 +-- src/solver/text.h | 67 +- src/solver/toolkit.c | 672 ++++----- src/solver/toposort.c | 12 +- src/solver/transect.c | 200 ++- src/solver/treatmnt.c | 65 +- src/solver/xsect.c | 156 +- src/solver/xsect.dat | 4 +- tests/CMakeLists.txt | 4 +- tests/solver/test_canonical.cpp | 4 +- tests/solver/test_lid.cpp | 192 +-- tests/solver/test_stats.cpp | 20 +- tests/solver/test_toolkit.cpp | 106 +- 95 files changed, 7756 insertions(+), 3510 deletions(-) create mode 100644 src/solver/error.txt create mode 100644 src/solver/include/toolkit_error.h create mode 100644 src/solver/include/toolkit_errors.txt create mode 100644 src/solver/inlet.c create mode 100644 src/solver/inlet.h create mode 100644 src/solver/street.c create mode 100644 src/solver/street.h diff --git a/.gitignore b/.gitignore index 54fd8cba7..6fed650b3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ + +.DS_Store + # Eclipse Stuff .metadata/ .settings/ diff --git a/CMakeLists.txt b/CMakeLists.txt index a2409aa80..61ddf0599 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ endif() project(swmm-solver - VERSION 5.1.15 + VERSION 5.2.0 LANGUAGES C CXX ) @@ -48,6 +48,7 @@ set(CONFIG_DIST "cmake") # Define build options option(BUILD_TESTS "Build component tests (requires Boost)" OFF) option(BUILD_DOCS "Build toolkit docs (requires Doxygen)" OFF) +option(BUILD_DEF "Builds library with def file interface" OFF) # Added option to statically link libraries to address GitHub Ubuntu 20.04 symbol errors (issue #340) option(BUILD_SHARED_LIBS "Build using shared libraries" ON) diff --git a/extern/boost.cmake b/extern/boost.cmake index 4f9eb1792..74e329006 100644 --- a/extern/boost.cmake +++ b/extern/boost.cmake @@ -7,10 +7,6 @@ # Author: Michael E. Tryby # US EPA - ORD/CESER # -# Usage: -# Create environment variable with the following pattern -- "BOOST_ROOT_X_XX_X" -# where Xs are the Boost version -- pointing to install location. -# if(WIN32) diff --git a/src/outfile/CMakeLists.txt b/src/outfile/CMakeLists.txt index 6732b9027..c9c4acc8e 100644 --- a/src/outfile/CMakeLists.txt +++ b/src/outfile/CMakeLists.txt @@ -78,8 +78,9 @@ install( swmm-output-config.cmake ) - -install(FILES ${SWMM_OUT_PUBLIC_HEADERS} +install( + FILES + ${SWMM_OUT_PUBLIC_HEADERS} DESTINATION "${INCLUDE_DIST}" ) diff --git a/src/outfile/errormanager.h b/src/outfile/errormanager.h index cfa97ba70..b61334200 100644 --- a/src/outfile/errormanager.h +++ b/src/outfile/errormanager.h @@ -10,6 +10,7 @@ #ifndef ERRORMANAGER_H_ #define ERRORMANAGER_H_ + #define ERR_MAXMSG 256 typedef struct error_s { @@ -24,4 +25,5 @@ int set_error(error_handle_t* error_handle, int errorcode); char* check_error(error_handle_t* error_handle); void clear_error(error_handle_t* error_handle); + #endif /* ERRORMANAGER_H_ */ diff --git a/src/outfile/messages.h b/src/outfile/messages.h index 9fa691df3..54af706ff 100644 --- a/src/outfile/messages.h +++ b/src/outfile/messages.h @@ -10,6 +10,7 @@ #ifndef SRC_MESSAGES_H_ #define SRC_MESSAGES_H_ + #define MAXMSG 56 /*------------------- Error Messages --------------------*/ @@ -28,4 +29,5 @@ #define ERR440 "ERROR 440: an unspecified error has occurred" + #endif /* SRC_MESSAGES_H_ */ diff --git a/src/run/main.c b/src/run/main.c index ac1d2a018..d727ad0d4 100644 --- a/src/run/main.c +++ b/src/run/main.c @@ -1,23 +1,22 @@ -/* - * main.c - Main stub for the command line version of EPA SWMM 5.1 - * - * Created on: October 9, 2020 - * Updated on: - * - * Author: See CONTRIBUTORS - */ +//----------------------------------------------------------------------------- +// main.c +// +// Project: EPA SWMM5 +// Version: 5.2 +// Date: 03/24/2021 +// Author: L. Rossman + +// Main stub for the command line version of EPA SWMM 5.2 +// to be run with swmm5.dll. -#include #include +#include #include -#include -#include +#include "swmm5.h" -// Private project includes +// OWA Addition ################################################################# +#include #include "timer.h" - -// Public project includes -#include "swmm5.h" #include "toolkit.h" @@ -74,20 +73,25 @@ void progress_bar(double *ratio) pct, format_time(tmp, t_r)); write_console(msg); } - +// ############################################################################## int main(int argc, char *argv[]) // // Input: argc = number of command line arguments // argv = array of command line arguments // Output: returns error status -// Purpose: runs the command line version of EPA SWMM 5.1. +// Purpose: runs the command line version of EPA SWMM 5.2. // -// Command line is: swmm5 f1 f2 f3 +// Command line is: runswmm f1 f2 f3 // where f1 = name of input file, f2 = name of report file, and // f3 = name of binary output file if saved (or blank if not saved). // { + + // OWA Edit ##################################################################### + // OWA runs adds a progress bar to the SWMM executable, so this main funciton is + // slightly diffent than EPA's + if (argc == 4) { char *inputFile = argv[1]; char *reportFile = argv[2]; @@ -140,4 +144,6 @@ int main(int argc, char *argv[]) } return 0; + + // ############################################################################## } diff --git a/src/solver/CMakeLists.txt b/src/solver/CMakeLists.txt index 0741ffe3d..7e0781bd9 100644 --- a/src/solver/CMakeLists.txt +++ b/src/solver/CMakeLists.txt @@ -2,7 +2,7 @@ # CMakeLists.txt - CMake configuration file for swmm-solver/library # # Created: Jul 11, 2019 -# Updated: May 19, 2021 +# Updated: Dec 1, 2021 # # Author: Michael E. Tryby # US EPA ORD/CESER @@ -31,7 +31,6 @@ file(GLOB RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.c *.h ) - if(BUILD_DEF) # Builds library with def file interface for backward compatibility set_source_files_properties(${PROJECT_SOURCE_DIR}/bindings/swmm5.def @@ -40,7 +39,7 @@ if(BUILD_DEF) add_library(swmm5 ${SWMM_SOURCES} - ${PROJECT_SOURCE_DIR/bindings/swmm5.def} + ${PROJECT_SOURCE_DIR}/bindings/swmm5.def $ ) @@ -126,9 +125,14 @@ install( "${CONFIG_DIST}" FILE swmm5-config.cmake - ) +) -install(FILES ${SWMM_PUBLIC_HEADERS} DESTINATION "${INCLUDE_DIST}") +install( + FILES + ${SWMM_PUBLIC_HEADERS} + DESTINATION + "${INCLUDE_DIST}" +) # copy swmm5 to build tree for testing diff --git a/src/solver/Roadmap.txt b/src/solver/Roadmap.txt index 11952fad7..66ff3f9a8 100644 --- a/src/solver/Roadmap.txt +++ b/src/solver/Roadmap.txt @@ -1,11 +1,11 @@ A Roadmap to the SWMM 5 Engine Source Code ========================================== -The SWMM 5 computational engine consists of 49 C-code files plus several -header files. The engine can be compiled either as a Dynamic Link Library -(DLL) under Windows or as a stand-alone console application under both -Windows and Linux, depending on which of the #define DLL and #define CLE -declarations at the top of swmm5.c is commented out. +The SWMM 5 computational engine consists of 55 C-code files plus 22 +header files. The engine should be compiled into a Dynamic Link Library +(DLL) under Windows or to a shared object library under Linux or MacOS. +A main.c file is also provided to build an executable that uses the engine +library to run a complete SWMM simulation from the command line. The following header files contain definitions that are used throughout the code and should be consulted if the meaning of a variable, a data structure, @@ -31,8 +31,8 @@ funcs.h contains prototypes of functions that can be called from any The following modules form the main core of the SWMM 5 engine: -swmm5.c contains functions that provide supervisory control over the - program. +swmm5.c contains a small API with functions that provide supervisory + control over the program. project.c contains functions that create and destroy all project data, establish default values, and look up objects by ID name. @@ -48,8 +48,7 @@ routing.c routes runoff and external inflows through the project's massbal.c performs mass balance calculations for runoff and routing. stats.c collects summary statistics on flow rates, water depths, - solution iterations, and variable time steps for a - simulation. + solution iterations, and variable time steps for a simulation. statsrpt.c writes summary simulation results to a status report. @@ -58,7 +57,7 @@ output.c writes/reads runoff and routing results to/from a binary report.c prepares a status report of simulation results and, for the command line version of SWMM 5, reports complete results for - selected subcatchments, nodes, and links. + selected subcatchments, nodes, and links. inputrpt.c writes a summary of a project's input data to the status report. @@ -103,11 +102,14 @@ flowrout.c implements top-level control of flow routing through a project's inflow.c provides direct time series inflows and recurring dry weather inflows to the drainage system's nodes at each step of the - simulation. + simulation. rdii.c computes rainfall dependent infiltration/inflow at selected nodes of the drainage network. +inlet.c computes flow captured by street inlet drains that is diverted to + sewer nodes using methods from the FHWA HEC-22 manual. + kinwave.c performs kinematic wave flow routing calculations at each time step of the simulation. @@ -125,7 +127,7 @@ qualrout.c performs routing of water quality constituents through the treatmnt.c computes pollutant removal at specific nodes of the drainage system where user-defined treatment functions have been - assigned. + assigned. node.c contains functions used to compute the properties and behavior of the drainage system's nodes which include junctions, flow @@ -164,7 +166,7 @@ keywords.c defines lists of keywords that appear as part of a SWMM 5 input file. mathexpr.c functions that parse and evaluate user-supplied mathematical - expressions for pollutant removal at treatment nodes. + expressions. mempool.c functions that provide a memory pool used to store object ID names. @@ -175,6 +177,8 @@ odesolve.c implementation of a fifth-order Runge-Kutta ordinary shape.c functions that compute the geometric cross-section properties of closed conduits with user-defined shapes. +street.c reads the geometric properties of a street cross section. + table.c functions used for accessing lookup tables that contain SWMM 5's curve data and time series data. diff --git a/src/solver/bindings/swmm5.def b/src/solver/bindings/swmm5.def index 98391ee06..8bcaf6eb4 100644 --- a/src/solver/bindings/swmm5.def +++ b/src/solver/bindings/swmm5.def @@ -2,13 +2,22 @@ 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 diff --git a/src/solver/climate.c b/src/solver/climate.c index a44c5faae..336f0aaf0 100644 --- a/src/solver/climate.c +++ b/src/solver/climate.c @@ -2,36 +2,32 @@ // climate.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/10 (Build 5.1.001) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) -// 05/10/18 (Build 5.1.013) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Climate related functions. // +// Update History +// ============== // Build 5.1.007: // - NCDC GHCN climate file format added. // - Monthly adjustments for temperature, evaporation & rainfall added. -// // Build 5.1.008: // - Monthly adjustments for hyd. conductivity added. // - Time series evaporation rates can now vary within a day. // - Evaporation rates are now properly updated when only flow routing // is being simulated. -// // Build 5.1.010: // - Hargreaves evaporation now computed using 7-day average temperatures. -// // Build 5.1.011: // - Monthly adjustment for hyd. conductivity <= 0 is ignored. -// // Build 5.1.013: // - Reads names of monthly adjustment patterns for various parameters // of a subcatchment from the [ADJUSTMENTS] section of input file. +// Build 5.2.0: +// - Reads temperature units for use with GHCND climate files. +// - Support added for relative file names. ///----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -55,8 +51,10 @@ static const int MAXDAYSPERMONTH = 32; // These variables are used when processing climate files. enum ClimateVarType {TMIN, TMAX, EVAP, WIND}; enum WindSpeedType {WDMV, AWND}; +enum TempUnitsType {DEG_C10, DEG_C, DEG_F}; static char* ClimateVarWords[] = {"TMIN", "TMAX", "EVAP", "WDMV", "AWND", NULL}; +static char* TempUnitsWords[] = {"C10", "C", "F", NULL}; //----------------------------------------------------------------------------- // Data Structures @@ -108,6 +106,7 @@ static char FileLine[MAXLINE+1]; // line from climate data file static int FileFieldPos[4]; // start of data fields for file record static int FileDateFieldPos; // start of date field for file record static int FileWindType; // wind speed type +static int FileTempUnits; // GHCND file temperature units (C10, C or F) //----------------------------------------------------------------------------- // External functions (defined in funcs.h) @@ -147,6 +146,7 @@ static void setTD3200FileValues(int param); static int isGhcndFormat(char* line); static void readGhcndFileLine(int *year, int *month); static void parseGhcndFileLine(void); +static double convertGhcndValue(int var, double v); //============================================================================= @@ -159,7 +159,7 @@ int climate_readParams(char* tok[], int ntoks) // // Format of data can be // TIMESERIES name -// FILE name +// FILE name (start) (units) // WINDSPEED MONTHLY v1 v2 ... v12 // WINDSPEED FILE // SNOWMELT v1 v2 ... v6 @@ -169,6 +169,7 @@ int climate_readParams(char* tok[], int ntoks) int i, j, k; double x[6], y; DateTime aDate; + char fname[MAXFNAME + 1]; // --- identify keyword k = findmatch(tok[0], TempKeyWords); @@ -194,7 +195,8 @@ int climate_readParams(char* tok[], int ntoks) // --- save name and usage mode of external climate file Fclimate.mode = USE_FILE; - sstrncpy(Fclimate.name, tok[1], MAXFNAME); + sstrncpy(fname, tok[1], MAXFNAME); + sstrncpy(Fclimate.name, addAbsolutePath(fname), MAXFNAME); // --- save starting date to read from file if one is provided Temp.fileStartDate = NO_DATE; @@ -207,6 +209,18 @@ int climate_readParams(char* tok[], int ntoks) Temp.fileStartDate = aDate; } } + + // --- file temperature units + FileTempUnits = DEG_F; + if (UnitSystem == SI) + FileTempUnits = DEG_C; + if (ntoks > 3) + { + i = findmatch(tok[3], TempUnitsWords); + if (i < 0) + return error_setInpError(ERR_KEYWORD, tok[3]); + FileTempUnits = i; + } break; case 2: // Wind speeds @@ -373,11 +387,11 @@ int climate_readAdjustments(char* tok[], int ntoks) // EVAPORATION v1 ... v12 // RAINFALL v1 ... v12 // CONDUCTIVITY v1 ... v12 -// N-PERV subcatchID patternID //(5.1.013 -// DSTORE subcatchID patternID // -// INFIL subcatchID patternID // +// N-PERV subcatchID patternID +// DSTORE subcatchID patternID +// INFIL subcatchID patternID { - int i, j; //(5.1.013) + int i, j; if (ntoks == 1) return 0; @@ -426,7 +440,6 @@ int climate_readAdjustments(char* tok[], int ntoks) return 0; } -//// Following code segments added to release 5.1.013. //// //(5.1.013) if ( match(tok[0], "N-PERV") ) { if ( ntoks < 3 ) return error_setInpError(ERR_ITEMS, ""); @@ -459,7 +472,6 @@ int climate_readAdjustments(char* tok[], int ntoks) Subcatch[i].infilPattern = j; return 0; } -//// return error_setInpError(ERR_KEYWORD, tok[0]); } @@ -550,14 +562,14 @@ void climate_openFile() // --- position file to begin reading climate file at either user-specified // month/year or at start of simulation period. rewind(Fclimate.file); - strcpy(FileLine, ""); + sstrncpy(FileLine, "", 0); if ( Temp.fileStartDate == NO_DATE ) datetime_decodeDate(StartDate, &FileYear, &FileMonth, &FileDay); else datetime_decodeDate(Temp.fileStartDate, &FileYear, &FileMonth, &FileDay); while ( !feof(Fclimate.file) ) { - strcpy(FileLine, ""); + sstrncpy(FileLine, "", 0); readFileLine(&y, &m); if ( y == FileYear && m == FileMonth ) break; } @@ -982,7 +994,7 @@ double getTempEvap(int day, double tave, double trng) double lamda = 2.50 - 0.002361 * ta; //latent heat of vaporization double dr = 1.0 + 0.033*cos(a*day); //relative earth-sun distance double phi = Temp.anglat*2.0*PI/360.0; //latitude angle (rad) - double del = 0.4093*sin(a*(284+day)); //solar declination angle (rad) + double del = 0.4093*sin(a*(284.+(double)day)); //solar declination angle (rad) double omega = acos(-tan(phi)*tan(del)); //sunset hour angle (rad) double ra = 37.6*dr* //extraterrestrial radiation (omega*sin(phi)*sin(del) + @@ -1097,11 +1109,9 @@ void readTD3200FileLine(int* y, int* m) char recdType[4] = ""; char year[5] = ""; char month[3] = ""; - int len; // --- check for minimum number of characters - len = strlen(FileLine); - if ( len < 30 ) + if ( strlen(FileLine) < 30 ) { report_writeErrorMsg(ERR_CLIMATE_FILE_READ, Fclimate.name); return; @@ -1134,11 +1144,9 @@ void readDLY0204FileLine(int* y, int* m) { char year[5] = ""; char month[3] = ""; - int len; // --- check for minimum number of characters - len = strlen(FileLine); - if ( len < 16 ) + if ( strlen(FileLine) < 16 ) { report_writeErrorMsg(ERR_CLIMATE_FILE_READ, Fclimate.name); return; @@ -1184,7 +1192,7 @@ void readFileValues() case DLY0204: parseDLY0204FileLine(); break; case GHCND: parseGhcndFileLine(); break; } - strcpy(FileLine, ""); + sstrncpy(FileLine, "", 0); } } @@ -1201,10 +1209,10 @@ void parseUserFileLine() int n; int y, m, d; char staID[80]; - char s0[80]; - char s1[80]; - char s2[80]; - char s3[80]; + char s0[80] = ""; + char s1[80] = ""; + char s2[80] = ""; + char s3[80] = ""; double x; // --- read day, Tmax, Tmin, Evap, & Wind from file line @@ -1275,15 +1283,13 @@ void setTD3200FileValues(int i) double x; int nValues; int j, k, d; - int lineLength; // --- parse number of days with data from cols. 27-29 of file line sstrncpy(valCount, &FileLine[27], 3); nValues = atoi(valCount); - lineLength = strlen(FileLine); // --- check for enough characters on line - if ( lineLength >= 12*nValues + 30 ) + if ( (int)strlen(FileLine) >= 12*nValues + 30 ) { // --- for each day's value for (j=0; j= 0 ) return TRUE; @@ -1463,59 +1469,98 @@ void parseGhcndFileLine() // wind speed. // { - int y, m, d, n, v; - double x; + int y, m, d, n, i; + double v; // --- parse day of month from date field n = sscanf(&FileLine[FileDateFieldPos], "%4d%2d%2d", &y, &m, &d); if ( n < 3 ) return; if ( d < 1 || d > 31 ) return; - // --- parse temperatures (in tenths of deg. C) to deg F - if ( FileFieldPos[TMAX] >= 0 ) - { - if ( sscanf(&FileLine[FileFieldPos[TMAX]], "%8d", &v) > 0 ) - { - if ( abs(v) < 9999 ) - FileData[TMAX][d] = (double)v*0.1*9.0/5.0 + 32.0; - } - } - if ( FileFieldPos[TMIN] >= 0 ) - { - if ( sscanf(&FileLine[FileFieldPos[TMIN]], "%8d", &v) > 0 ) - { - if ( abs(v) < 9999 ) - FileData[TMIN][d] = (double)v*0.1*9.0/5.0 + 32.0; - } - } - - // -- parse evaporation (in tenths of mm) to user units - if ( FileFieldPos[EVAP] >= 0 ) + // --- parse climate variables + for (i = TMIN; i <= WIND; i++) { - if ( sscanf(&FileLine[FileFieldPos[EVAP]], "%8d", &v) > 0 ) + if ( FileFieldPos[i] >= 0 ) { - if ( abs(v) < 9999 ) + if ( sscanf(&FileLine[FileFieldPos[i]], "%8lf", &v) > 0 ) { - x = (double)v * 0.1; - if ( UnitSystem == US ) x /= MMperINCH; - FileData[EVAP][d] = x; + if ( fabs(v) < 9999. ) + FileData[i][d] = convertGhcndValue(i, v); } } } +} + +//============================================================================= - // --- parse wind speed (in km/day for WDMV or tenths of m/s for AWND) - // to miles/hr - if ( FileFieldPos[WIND] >= 0 ) +double convertGhcndValue(int var, double v) +// +// Input: var = climate variable code +// v = climate variable value +// Output: climate variable value in SWMM's internal units +// Purpose: converts a climate variable value read from a NCDC GHCN Daily file +// to SWMM's internal units. +// +{ + switch (var) { - if ( sscanf(&FileLine[FileFieldPos[WIND]], "%8d", &v) > 0 ) - { - if ( abs(v) < 9999 ) + case TMIN: + case TMAX: + switch (FileTempUnits) { - if ( FileWindType == WDMV ) x = (double)v * 0.62137 / 24.; - else x = (double)v * 0.1 / 1000. * 0.62137 * 3600.; - FileData[WIND][d] = x; + case DEG_C10: // tenths deg. C ==> deg. F + return v / 10. * 9.0 / 5.0 + 32.0; + + case DEG_C: // deg. C ==> deg. F + return v * 9.0 / 5.0 + 32.0; + + default: // deg. F + return v; } - } + case EVAP: + switch (FileTempUnits) + { + case DEG_C10: // tenths mm ==> inches or mm + v /= 10.; + if (UnitSystem == US) v /= MMperINCH; + return v; + + case DEG_C: // mm ==> inches or mm + if (UnitSystem == US) v /= MMperINCH; + return v; + + default: // inches ==> inches or mm + if (UnitSystem == SI) v *= MMperINCH; + return v; + } + case WIND: + switch (FileTempUnits) + { + case DEG_C10: + // km/day ==> miles/hr + if (FileWindType == WDMV) + return v * 0.62137 / 24.; + // tenths m/s ==> miles/hr + else + return v / 10. / 1000. * 0.62137 * 3600.; + case DEG_C: + // km/day ==> miles/hr + if (FileWindType == WDMV) + return v * 0.62137 / 24.; + // m/s ==> miles/hr + else + return v / 1000. * 0.62137 * 3600.; + + default: + // miles ==> miles/hr + if (FileWindType == WDMV) + return v / 24.; + // miles/hr + else + return v; + } + default: + return v; } } @@ -1532,14 +1577,15 @@ void updateTempMoveAve(double tmin, double tmax) { double ta, // new day's average temperature (deg F) tr; // new day's temperature range (deg F) - int count = Tma.count; + int kount = Tma.count; + double count = kount; // --- find ta and tr from new day's min and max temperature ta = (tmin + tmax) / 2.0; tr = fabs(tmax - tmin); // --- if the array used to store previous days' temperatures is full - if ( count == Tma.maxCount ) + if ( kount == Tma.maxCount ) { // --- update the moving averages with the new day's value Tma.tAve = (Tma.tAve * count + ta - Tma.ta[Tma.front]) / count; diff --git a/src/solver/consts.h b/src/solver/consts.h index b1697c368..2e678f637 100644 --- a/src/solver/consts.h +++ b/src/solver/consts.h @@ -2,12 +2,8 @@ // consts.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 08/01/16 (Build 5.1.011) -// 05/10/18 (Build 5.1.013) -// 03/01/20 (Build 5.1.014) -// 04/01/20 (Build 5.1.015) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Various Constants @@ -16,10 +12,12 @@ #ifndef CONSTS_H #define CONSTS_H - //------------------ // General Constants //------------------ + +// OWA Version string stored in version.h +// #define VERSION 52000 #define MAGICNUMBER 516114522 #define EOFMARK 0x1A // Use 0x04 for UNIX systems #define MAXTITLE 3 // Max. # title lines @@ -39,7 +37,9 @@ #define PI 3.141592654 // Value of pi #define GRAVITY 32.2 // accel. of gravity in US units #define SI_GRAVITY 9.81 // accel of gravity in SI units +/* DEPRECATED #define MAXFILESIZE 2147483647L // largest file size in bytes +*/ //----------------------------- // Units factor in Manning Eqn. @@ -70,7 +70,7 @@ //----------------------------------------------------- // Minimum flow depth and area for dynamic wave routing //----------------------------------------------------- -#define FUDGE 0.0001 // ft or ft2 +#define FUDGE 0.0001 // ft or ft2 //--------------------------- // Various conversion factors @@ -96,8 +96,8 @@ //--------------------------- // Token separator characters -//--------------------------- -#define SEPSTR " \t\n\r" +//--------------------------- +#define SEPSTR " \t\n\r" #endif //CONSTS_H diff --git a/src/solver/controls.c b/src/solver/controls.c index 33e1b2155..d2af28131 100644 --- a/src/solver/controls.c +++ b/src/solver/controls.c @@ -2,12 +2,8 @@ // controls.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/21/14 (Build 5.1.001) -// 03/19/15 (Build 5.1.008) -// 04/30/15 (Build 5.1.009) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Rule-based controls functions. @@ -36,20 +32,22 @@ // E.g.: Pump abc status = OFF // Weir xyz setting = 0.5 // +// Update History +// ============== // Build 5.1.008: // - Support added for r.h.s. variables in rule premises. // - Node volume added as a premise variable. -// // Build 5.1.009: // - Fixed problem with parsing a RHS premise variable. -// // Build 5.1.010: // - Support added for link TIMEOPEN & TIMECLOSED premises. -// // Build 5.1.011: // - Support added for DAYOFYEAR attribute. // - Modulated controls no longer included in reported control actions. -// +// Build 5.2.0: +// - Additional attributes added to condition clauses. +// - Support added for named variables in condition clauses. +// - Support added for math expressions in condition clauses. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -62,26 +60,31 @@ // Constants //----------------------------------------------------------------------------- enum RuleState {r_RULE, r_IF, r_AND, r_OR, r_THEN, r_ELSE, r_PRIORITY, - r_ERROR}; -enum RuleObject {r_NODE, r_LINK, r_CONDUIT, r_PUMP, r_ORIFICE, r_WEIR, - r_OUTLET, r_SIMULATION}; -enum RuleAttrib {r_DEPTH, r_HEAD, r_VOLUME, r_INFLOW, r_FLOW, r_STATUS, - r_SETTING, r_TIMEOPEN, r_TIMECLOSED, r_TIME, r_DATE, - r_CLOCKTIME, r_DAYOFYEAR, r_DAY, r_MONTH}; + r_VARIABLE, r_EXPRESSION, r_ERROR}; +enum RuleObject {r_GAGE, r_NODE, r_LINK, r_CONDUIT, r_PUMP, r_ORIFICE, + r_WEIR, r_OUTLET, r_SIMULATION}; +enum RuleAttrib {r_DEPTH, r_MAXDEPTH, r_HEAD, r_VOLUME, r_INFLOW, + r_FLOW, r_FULLFLOW, r_FULLDEPTH, r_STATUS, r_SETTING, + r_LENGTH, r_SLOPE, r_VELOCITY, r_TIMEOPEN, r_TIMECLOSED, + r_TIME, r_DATE, r_CLOCKTIME, r_DAYOFYEAR, r_DAY, r_MONTH}; enum RuleRelation {EQ, NE, LT, LE, GT, GE}; enum RuleSetting {r_CURVE, r_TIMESERIES, r_PID, r_NUMERIC}; +#define MAXVARNAME 32 + static char* ObjectWords[] = - {"NODE", "LINK", "CONDUIT", "PUMP", "ORIFICE", "WEIR", "OUTLET", - "SIMULATION", NULL}; + {"GAGE", "NODE", "LINK", "CONDUIT", "PUMP", "ORIFICE", "WEIR", "OUTLET", + "SIMULATION", NULL}; static char* AttribWords[] = - {"DEPTH", "HEAD", "VOLUME", "INFLOW", "FLOW", "STATUS", "SETTING", - "TIMEOPEN", "TIMECLOSED","TIME", "DATE", "CLOCKTIME", "DAYOFYEAR", - "DAY", "MONTH", NULL}; + {"DEPTH", "MAXDEPTH", "HEAD", "VOLUME", "INFLOW", + "FLOW", "FULLFLOW", "FULLDEPTH", "STATUS", "SETTING", + "LENGTH", "SLOPE", "VELOCITY", "TIMEOPEN", "TIMECLOSED", + "TIME", "DATE", "CLOCKTIME", "DAYOFYEAR", "DAY", "MONTH", NULL}; static char* RelOpWords[] = {"=", "<>", "<", "<=", ">", ">=", NULL}; static char* StatusWords[] = {"OFF", "ON", NULL}; static char* ConduitWords[] = {"CLOSED", "OPEN", NULL}; static char* SettingTypeWords[] = {"CURVE", "TIMESERIES", "PID", NULL}; +static char* IntensityWord = "INTENSITY"; //----------------------------------------------------------------------------- // Data Structures @@ -89,15 +92,30 @@ static char* SettingTypeWords[] = {"CURVE", "TIMESERIES", "PID", NULL}; // Rule Premise Variable struct TVariable { - int node; // index of a node (-1 if N/A) - int link; // index of a link (-1 if N/A) - int attribute; // type of attribute for node/link + int object; // type of object + int index; // index in object's array + int attribute; // object's attribute +}; + +// Named Variable +struct TNamedVariable +{ + struct TVariable variable; // a rule premise variable + char name[MAXVARNAME+1]; // name used in math expression +}; + +// Rule Premise Function +struct TExpression +{ + MathExpr* expression; // tokenized math expression + char name[MAXVARNAME+1]; // expression name }; // Rule Premise Clause struct TPremise { int type; // clause type (IF/AND/OR) + int exprIndex; // expression index (-1 if N/A) struct TVariable lhsVar; // left hand side variable struct TVariable rhsVar; // right hand side variable int relation; // relational operator (>, <, =, etc) @@ -149,11 +167,22 @@ double SetPoint; // value of controller setpoint DateTime CurrentDate; // current date in whole days DateTime CurrentTime; // current time of day (decimal) +int VariableCount; +int ExpressionCount; +int CurrentVariable; +int CurrentExpression; +struct TNamedVariable* NamedVariable; // array of named variables +struct TExpression* Expression; // array of math expressions + //----------------------------------------------------------------------------- // External functions (declared in funcs.h) //----------------------------------------------------------------------------- // controls_create // controls_delete +// controls_init +// controls_addToCount +// controls_addVariable +// controls_addExpression // controls_addRuleClause // controls_evaluate @@ -161,7 +190,7 @@ DateTime CurrentTime; // current time of day (decimal) // Local functions //----------------------------------------------------------------------------- int addPremise(int r, int type, char* Tok[], int nToks); -int getPremiseVariable(char* tok[], int* k, struct TVariable* v); +int getPremiseVariable(char* tok[], int nToks, int* k, struct TVariable* v); int getPremiseValue(char* token, int attrib, double* value); int addAction(int r, char* Tok[], int nToks); @@ -183,6 +212,43 @@ int setActionSetting(char* tok[], int nToks, int* curve, int* tseries, void updateActionValue(struct TAction* a, DateTime currentTime, double dt); double getPIDSetting(struct TAction* a, double dt); +int getVariableIndex(char* varName); +double getNamedVariableValue(int varIndex); +int getExpressionIndex(char* exprName); +int getGageAttrib(char* token); +double getRainValue(struct TVariable v); + +//============================================================================= + +void controls_init() +// +// Input: none +// Output: none +// Purpose: initializes the control rule system. +// +{ + Rules = NULL; + NamedVariable = NULL; + Expression = NULL; + RuleCount = 0; + VariableCount = 0; + ExpressionCount = 0; +} + +//============================================================================= + +void controls_addToCount(char* s) +// +// Input: s = either VARIABLE or EXPRESSION +// Output: none +// Purpose: updates the number of named variables or math expressions used +// by control rules. +// +{ + if (match(s, w_VARIABLE)) VariableCount++; + else if (match(s, w_EXPRESSION)) ExpressionCount++; +} + //============================================================================= int controls_create(int n) @@ -192,21 +258,38 @@ int controls_create(int n) // Purpose: creates an array of control rules. // { - int r; - ActionList = NULL; - InputState = r_PRIORITY; - RuleCount = n; - if ( n == 0 ) return 0; - Rules = (struct TRule *) calloc(RuleCount, sizeof(struct TRule)); - if (Rules == NULL) return ERR_MEMORY; - for ( r=0; r 0) + { + Rules = (struct TRule *) calloc(RuleCount, sizeof(struct TRule)); + if (Rules == NULL) return ERR_MEMORY; + for ( r=0; r 0) + { + NamedVariable = (struct TNamedVariable *) calloc(VariableCount, + sizeof(struct TNamedVariable)); + if (NamedVariable == NULL) return ERR_MEMORY; + } + if (ExpressionCount > 0) { - Rules[r].ID = NULL; - Rules[r].firstPremise = NULL; - Rules[r].lastPremise = NULL; - Rules[r].thenActions = NULL; - Rules[r].elseActions = NULL; - Rules[r].priority = 0.0; + Expression = (struct TExpression *) calloc(ExpressionCount, + sizeof(struct TExpression)); + if (Expression == NULL) return ERR_MEMORY; } return 0; } @@ -220,6 +303,16 @@ void controls_delete(void) // Purpose: deletes all control rules. // { + int i; + + for (i = 0; i < ExpressionCount; i++) + { + mathexpr_delete(Expression[i].expression); + Expression[i].expression = NULL; + } + FREE(Expression); + FREE(NamedVariable); + if ( RuleCount == 0 ) return; deleteActionList(); deleteRules(); @@ -227,6 +320,119 @@ void controls_delete(void) //============================================================================= +int controls_addVariable(char* tok[], int nToks) +// +// Input: tok = an array of string tokens +// n = the size of tok[] +// Output: returns error code +// Purpose: adds a named variable to the control rule system from a +// tokenized line of input with formats: +// VARIABLE name = Object id attribute +// VARIABLE name = SIMULATION attribute +// +{ + struct TVariable v1; + int k, err; + + CurrentVariable++; + if (nToks < 5) return ERR_ITEMS; + if (findExactMatch(tok[1], AttribWords) >= 0) + return error_setInpError(ERR_KEYWORD, tok[1]); + if (!match(tok[2], "=")) return error_setInpError(ERR_KEYWORD, tok[2]); + if (!match(tok[3], "SIMULATION") && nToks < 6) return ERR_ITEMS; + k = 3; + err = getPremiseVariable(tok, nToks, &k, &v1); + if (err > 0) return err; + k = CurrentVariable; + NamedVariable[k].variable = v1; + sstrncpy(NamedVariable[k].name, tok[1], MAXVARNAME); + return 0; +} + +//============================================================================= + +int controls_addExpression(char* tok[], int nToks) +// +// Input: tok = an array of string tokens +// n = number of tokens +// Output: returns error code +// Purpose: adds a math expression to the control rule system from a +// a tokenized line of input with format: +// EXPRESSION name = +// +{ + int i, k; + char s[MAXLINE + 1]; + MathExpr* expr; + + CurrentExpression++; + if (nToks < 4) return ERR_ITEMS; + k = CurrentExpression; + Expression[k].expression = NULL; + sstrncpy(Expression[k].name, tok[1], MAXVARNAME); + sstrncpy(s, tok[3], MAXLINE); + for (i = 4; i < nToks; i++) + { + sstrcat(s, " ", MAXLINE); + sstrcat(s, tok[i], MAXLINE); + } + + expr = mathexpr_create(s, getVariableIndex); + if (expr == NULL) + return error_setInpError(ERR_MATH_EXPR, ""); + + Expression[k].expression = expr; + return 0; +} + +//============================================================================= + +int getVariableIndex(char* varName) +// +// Input: varName = string containing a variable name +// Output: returns the index of the named variable or -1 if not found +// Purpose: finds the array index of a named variable. +// +{ + int i; + for (i = 0; i < VariableCount; i++) + { + if (match(varName, NamedVariable[i].name)) return i; + } + return -1; +} + +//============================================================================= + +double getNamedVariableValue(int varIndex) +// +// Input: varIndex = index of a named variable +// Output: returns the current value of the variable +// Purpose: finds the value of a named variable. +// +{ + return getVariableValue(NamedVariable[varIndex].variable); +} + +//============================================================================= + +int getExpressionIndex(char* exprName) +// +// Input: exprName = string containing an expression name +// Output: returns the index of the expression or -1 if not found +// Purpose: finds the array index of a math expression +// +{ + int i; + for (i = 0; i < ExpressionCount; i++) + { + if (match(exprName, Expression[i].name)) return i; + } + return -1; +} + +//============================================================================= + int controls_addRuleClause(int r, int keyword, char* tok[], int nToks) // // Input: r = rule index @@ -359,53 +565,84 @@ int addPremise(int r, int type, char* tok[], int nToks) struct TPremise* p; struct TVariable v1; struct TVariable v2; + int obj, exprIndex, varIndex = -1; - // --- check for minimum number of tokens - if ( nToks < 5 ) return ERR_ITEMS; - - // --- get LHS variable + // --- initialize LHS variable v1 + if (nToks < 4) return ERR_ITEMS; + v1.attribute = -1; + v1.object = -1; + v1.index = -1; n = 1; - err = getPremiseVariable(tok, &n, &v1); - if ( err > 0 ) return err; + + // --- check if 2nd token is a math expression + exprIndex = getExpressionIndex(tok[1]); + + // --- if not then check if it's a named variable + if (exprIndex < 0) + { + varIndex = getVariableIndex(tok[n]); + if (varIndex >= 0) + { + v1 = NamedVariable[varIndex].variable; + } + + // otherwise parse object|index|attribute tokens + else + { + err = getPremiseVariable(tok, nToks, &n, &v1); + if ( err > 0 ) return err; + } + } // --- get relational operator n++; + if ( n >= nToks ) return error_setInpError(ERR_ITEMS, ""); relation = findExactMatch(tok[n], RelOpWords); if ( relation < 0 ) return error_setInpError(ERR_KEYWORD, tok[n]); - n++; - // --- initialize RHS variable + // --- initialize RHS variable v2 v2.attribute = -1; - v2.link = -1; - v2.node = -1; + v2.object = -1; + v2.index = -1; + n++; + if (n >= nToks) return error_setInpError(ERR_ITEMS, ""); - // --- check that more tokens remain - if ( n >= nToks ) return error_setInpError(ERR_ITEMS, ""); - - // --- see if a RHS variable is supplied - if ( findmatch(tok[n], ObjectWords) >= 0 && n + 3 >= nToks ) + // --- check for named RHS variable + varIndex = getVariableIndex(tok[n]); + if (varIndex >= 0) { - err = getPremiseVariable(tok, &n, &v2); - if ( err > 0 ) return ERR_RULE; - if ( v1.attribute != v2.attribute) - report_writeWarningMsg(WARN11, Rules[r].ID); + v2 = NamedVariable[varIndex].variable; } - // --- otherwise get value to which LHS variable is compared to + // --- check for object|index|attribute variable else { - err = getPremiseValue(tok[n], v1.attribute, &value); - n++; + obj = findmatch(tok[n], ObjectWords); + if (obj >= 0) + { + err = getPremiseVariable(tok, nToks, &n, &v2); + if ( err > 0 ) return ERR_RULE; + if (exprIndex < 0 && v1.attribute != v2.attribute) + report_writeWarningMsg(WARN11, Rules[r].ID); + } + + // --- check for a single RHS value + else + { + err = getPremiseValue(tok[n], v1.attribute, &value); + if ( err > 0 ) return err; + } } - if ( err > 0 ) return err; // --- make sure another clause is not on same line + n++; if ( n < nToks && findmatch(tok[n], RuleKeyWords) >= 0 ) return ERR_RULE; // --- create the premise object p = (struct TPremise *) malloc(sizeof(struct TPremise)); if ( !p ) return ERR_MEMORY; p->type = type; + p->exprIndex = exprIndex; p->lhsVar = v1; p->rhsVar = v2; p->relation = relation; @@ -425,19 +662,19 @@ int addPremise(int r, int type, char* tok[], int nToks) //============================================================================= -int getPremiseVariable(char* tok[], int* k, struct TVariable* v) +int getPremiseVariable(char* tok[], int nToks, int* k, struct TVariable* v) // -// Input: tok = array of string tokens containing premise statement +// Input: tok = array of string tokens +// nToks = number of tokens // k = index of current token // Output: returns an error code; updates k to new current token and // places identity of specified variable in v -// Purpose: parses a variable (e.g., Node 123 Depth) specified in a -// premise clause of a control rule. +// Purpose: parses a variable (e.g., Node 123 Depth) used in a control rule. // { int n = *k; - int node = -1; - int link = -1; + int object = -1; + int index = -1; int obj, attrib; // --- get object type @@ -446,11 +683,19 @@ int getPremiseVariable(char* tok[], int* k, struct TVariable* v) // --- get object index from its name n++; + if (n >= nToks) return error_setInpError(ERR_ITEMS, ""); switch (obj) { + case r_GAGE: + index = project_findObject(GAGE, tok[n]); + if (index < 0) return error_setInpError(ERR_NAME, tok[n]); + object = r_GAGE; + break; + case r_NODE: - node = project_findObject(NODE, tok[n]); - if ( node < 0 ) return error_setInpError(ERR_NAME, tok[n]); + index = project_findObject(NODE, tok[n]); + if ( index < 0 ) return error_setInpError(ERR_NAME, tok[n]); + object = r_NODE; break; case r_LINK: @@ -459,21 +704,32 @@ int getPremiseVariable(char* tok[], int* k, struct TVariable* v) case r_ORIFICE: case r_WEIR: case r_OUTLET: - link = project_findObject(LINK, tok[n]); - if ( link < 0 ) return error_setInpError(ERR_NAME, tok[n]); + index = project_findObject(LINK, tok[n]); + if ( index < 0 ) return error_setInpError(ERR_NAME, tok[n]); + object = r_LINK; break; default: n--; } n++; + if (n >= nToks) return error_setInpError(ERR_ITEMS, ""); // --- get attribute index from its name - attrib = findmatch(tok[n], AttribWords); + if (object == r_GAGE) + attrib = getGageAttrib(tok[n]); + else + attrib = findmatch(tok[n], AttribWords); if ( attrib < 0 ) return error_setInpError(ERR_KEYWORD, tok[n]); // --- check that attribute belongs to object type - if ( obj == r_NODE ) switch (attrib) + if (obj == r_GAGE) + { + + } + + else if ( obj == r_NODE ) switch (attrib) { case r_DEPTH: + case r_MAXDEPTH: case r_HEAD: case r_VOLUME: case r_INFLOW: break; @@ -481,30 +737,36 @@ int getPremiseVariable(char* tok[], int* k, struct TVariable* v) } // --- check for link TIMEOPEN & TIMECLOSED attributes - else if ( link >= 0 && - ( (attrib == r_TIMEOPEN || - attrib == r_TIMECLOSED) + else if ( object == r_LINK && index >= 0 && + ( (attrib == r_TIMEOPEN || attrib == r_TIMECLOSED) )) { - + // nothing to do here } else if ( obj == r_LINK || obj == r_CONDUIT ) switch (attrib) { case r_STATUS: case r_DEPTH: - case r_FLOW: break; + case r_FULLFLOW: + case r_FULLDEPTH: + case r_FLOW: + case r_LENGTH: + case r_SLOPE: + case r_VELOCITY: break; default: return error_setInpError(ERR_KEYWORD, tok[n]); } else if ( obj == r_PUMP ) switch (attrib) { case r_FLOW: + case r_SETTING: case r_STATUS: break; default: return error_setInpError(ERR_KEYWORD, tok[n]); } else if ( obj == r_ORIFICE || obj == r_WEIR || obj == r_OUTLET ) switch (attrib) { + case r_FLOW: case r_SETTING: break; default: return error_setInpError(ERR_KEYWORD, tok[n]); } @@ -520,8 +782,8 @@ int getPremiseVariable(char* tok[], int* k, struct TVariable* v) } // --- populate variable structure - v->node = node; - v->link = link; + v->object = object; + v->index = index; v->attribute = attrib; *k = n; return 0; @@ -529,6 +791,33 @@ int getPremiseVariable(char* tok[], int* k, struct TVariable* v) //============================================================================= +int getGageAttrib(char* token) +// +// Input: token = a string token +// Output: returns an attribute code or -1 if an error occurred +// Purpose: determines the atrribute code for a rain gage variable. +// Note: a valid token is INTENSITY for current rainfall intensity +// (attribute code = 0) or nHR_PRECIP for total rain depth +// over past n hours (attribute code = n). +// +{ + int attrib; + + // --- check if token is currrent rainfall intensity + if (match(token, IntensityWord)) + return 0; + + // --- token is past rain depth - read number of past hours + attrib = atoi(token); + + // --- check that number of hours is in allowable range + if (attrib < 1 || attrib > MAXPASTRAIN) + return -1; + return attrib; +} + +//============================================================================= + int getPremiseValue(char* token, int attrib, double* value) // // Input: token = a string token @@ -544,7 +833,7 @@ int getPremiseValue(char* token, int attrib, double* value) { case r_STATUS: *value = findmatch(token, StatusWords); - if ( *value < 0.0 ) *value = findmatch(token, ConduitWords); + if ( *value < 0.0 ) *value = findmatch(token, ConduitWords); if ( *value < 0.0 ) return error_setInpError(ERR_KEYWORD, token); break; @@ -576,8 +865,8 @@ int getPremiseValue(char* token, int attrib, double* value) break; case r_DAYOFYEAR: - strncpy(strDate, token, 6); - strcat(strDate, "/1947"); + sstrncpy(strDate, token, 6); + sstrcat(strDate, "/1947", 25); if ( datetime_strToDate(strDate, value) ) { *value = datetime_dayOfYear(*value); @@ -626,9 +915,9 @@ int addAction(int r, char* tok[], int nToks) switch (obj) { case r_CONDUIT: - if ( Link[link].type != CONDUIT ) - return error_setInpError(ERR_NAME, tok[2]); - break; + if ( Link[link].type != CONDUIT ) + return error_setInpError(ERR_NAME, tok[2]); + break; case r_PUMP: if ( Link[link].type != PUMP ) return error_setInpError(ERR_NAME, tok[2]); @@ -830,11 +1119,11 @@ double getPIDSetting(struct TAction* a, double dt) // a->e2 = error from two time steps ago { double e0, setting; - double p, i, d, update; - double tolerance = 0.0001; + double p, i, d, update; + double tolerance = 0.0001; - // --- convert time step from days to minutes - dt *= 1440.0; + // --- convert time step from days to minutes + dt *= 1440.0; // --- determine relative error in achieving controller set point e0 = SetPoint - ControlValue; @@ -844,24 +1133,24 @@ double getPIDSetting(struct TAction* a, double dt) else e0 = e0/ControlValue; } - // --- reset previous errors to 0 if controller gets stuck - if (fabs(e0 - a->e1) < tolerance) - { - a->e2 = 0.0; - a->e1 = 0.0; - } + // --- reset previous errors to 0 if controller gets stuck + if (fabs(e0 - a->e1) < tolerance) + { + a->e2 = 0.0; + a->e1 = 0.0; + } // --- use the recursive form of the PID controller equation to // determine the new setting for the controlled link - p = (e0 - a->e1); - if ( a->ki == 0.0 ) i = 0.0; - else i = e0 * dt / a->ki; - d = a->kd * (e0 - 2.0*a->e1 + a->e2) / dt; - update = a->kp * (p + i + d); - if ( fabs(update) < tolerance ) update = 0.0; - setting = Link[a->link].targetSetting + update; - - // --- update previous errors + p = (e0 - a->e1); + if ( a->ki == 0.0 ) i = 0.0; + else i = e0 * dt / a->ki; + d = a->kd * (e0 - 2.0*a->e1 + a->e2) / dt; + update = a->kp * (p + i + d); + if ( fabs(update) < tolerance ) update = 0.0; + setting = Link[a->link].targetSetting + update; + + // --- update previous errors a->e2 = a->e1; a->e1 = e0; @@ -900,13 +1189,13 @@ void updateActionList(struct TAction* a) } // --- action not listed so add it to ActionList - if ( !listItem ) + listItem = (struct TActionList *) malloc(sizeof(struct TActionList)); + if (listItem) { - listItem = (struct TActionList *) malloc(sizeof(struct TActionList)); listItem->next = ActionList; ActionList = listItem; + listItem->action = a; } - listItem->action = a; } //============================================================================= @@ -959,10 +1248,21 @@ int evaluatePremise(struct TPremise* p, double tStep) double lhsValue, rhsValue; int result = FALSE; - lhsValue = getVariableValue(p->lhsVar); + // --- check if left hand side (lhs) of premise is an expression + if (p->exprIndex >= 0) + lhsValue = mathexpr_eval(Expression[p->exprIndex].expression, + getNamedVariableValue); + + // --- otherwise get value of the lhs variable + else + lhsValue = getVariableValue(p->lhsVar); + + // --- if right hand side (rhs) of premise is a variable then get its value if ( p->value == MISSING ) rhsValue = getVariableValue(p->rhsVar); else rhsValue = p->value; if ( lhsValue == MISSING || rhsValue == MISSING ) return FALSE; + + // --- compare the lhs of the premise to the rhs switch (p->lhsVar.attribute) { case r_TIME: @@ -982,8 +1282,13 @@ int evaluatePremise(struct TPremise* p, double tStep) double getVariableValue(struct TVariable v) { - int i = v.node; - int j = v.link; + int i = -1; // a node index + int j = -1; // a link index + + if (v.object == r_GAGE) + return getRainValue(v); + if (v.object == r_NODE) i = v.index; + if (v.object == r_LINK) j = v.index; switch ( v.attribute ) { @@ -1011,7 +1316,9 @@ double getVariableValue(struct TVariable v) else return Link[j].setting; case r_SETTING: - if ( j < 0 || (Link[j].type != ORIFICE && Link[j].type != WEIR) ) + if ( j < 0 || (Link[j].type != PUMP && + Link[j].type != ORIFICE && + Link[j].type != WEIR) ) return MISSING; else return Link[j].setting; @@ -1019,12 +1326,34 @@ double getVariableValue(struct TVariable v) if ( j < 0 ) return MISSING; else return Link[j].direction*Link[j].newFlow*UCF(FLOW); + case r_FULLFLOW: + case r_FULLDEPTH: + case r_VELOCITY: + case r_LENGTH: + case r_SLOPE: + if ( j < 0 ) return MISSING; + else if (Link[j].type != CONDUIT) return MISSING; + switch (v.attribute) + { + case r_FULLFLOW: return Link[j].qFull * UCF(FLOW); + case r_FULLDEPTH: return Link[j].xsect.yFull * UCF(LENGTH); + case r_VELOCITY: + return link_getVelocity(j, Link[j].newFlow, Link[j].newDepth) + * UCF(LENGTH); + case r_LENGTH: return Conduit[Link[j].subIndex].length * UCF(LENGTH); + case r_SLOPE: return Conduit[Link[j].subIndex].slope; + default: return MISSING; + } case r_DEPTH: if ( j >= 0 ) return Link[j].newDepth*UCF(LENGTH); else if ( i >= 0 ) return Node[i].newDepth*UCF(LENGTH); else return MISSING; + case r_MAXDEPTH: + if (i >= 0) return Node[i].fullDepth*UCF(LENGTH); + else return MISSING; + case r_HEAD: if ( i < 0 ) return MISSING; return (Node[i].newDepth + Node[i].invertElev) * UCF(LENGTH); @@ -1053,6 +1382,23 @@ double getVariableValue(struct TVariable v) //============================================================================= +double getRainValue(struct TVariable v) +// +// Input: v = a rule premise variable for a rain gage +// Output: returns current or past rainfall amount +// Purpose: retrieves either the current rainfall intensity or the past +// rainfall total for a rain gage. +// +{ + if (v.index < 0) return MISSING; + else if (Gage[v.index].isUsed == FALSE) return 0.0; + else if (v.attribute == 0) + return Gage[v.index].rainfall; + else return gage_getPastRain(v.index, v.attribute); +} + +//============================================================================= + int compareTimes(double lhsValue, int relation, double rhsValue, double halfStep) // // Input: lhsValue = date/time value on left hand side of relation diff --git a/src/solver/culvert.c b/src/solver/culvert.c index 37c7c8d56..5f62877b7 100644 --- a/src/solver/culvert.c +++ b/src/solver/culvert.c @@ -2,9 +2,8 @@ // culvert.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 05/10/18 (Build 5.1.013) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Culvert equations for SWMM5 @@ -12,6 +11,8 @@ // Computes flow reduction in a culvert-type conduit due to // inlet control using equations from the FHWA HEC-5 circular. // +// Update History +// ============== // Build 5.1.013: // - C parameter corrected for Arch, Corrugated Metal, Mitered culvert. //----------------------------------------------------------------------------- @@ -108,7 +109,7 @@ static const double Params[58][5] = { // Arch, Corrugated Metal {1.0, 0.0083, 2.00, 0.0379, 0.69}, //90 deg headwall - {1.0, 0.0300, 1.00, 0.0473, 0.75}, //Mitered to slope //(5.1.013) + {1.0, 0.0300, 1.00, 0.0473, 0.75}, //Mitered to slope {1.0, 0.0340, 1.50, 0.0496, 0.57}, //Thin wall projecting // Circular Culvert @@ -163,10 +164,10 @@ static double getTransitionFlow(int code, double h, double h1, double h2, TCulvert* culvert); static double getForm1Flow(double h, TCulvert* culvert); static double form1Eqn(double yc, void* p); - +/* static void report_CulvertControl(int j, double q0, double q, int condition, double yRatio); //for debugging only - +*/ //============================================================================= @@ -391,7 +392,7 @@ double form1Eqn(double yc, void* p) } //============================================================================= - +/* void report_CulvertControl(int j, double q0, double q, int condition, double yRatio) // // Used for debugging only @@ -407,3 +408,4 @@ void report_CulvertControl(int j, double q0, double q, int condition, double yRa "\n %11s: %8s Culvert %s flow reduced from %.3f to %.3f cfs for %s flow (%.2f).", theDate, theTime, Link[j].ID, q0, q, conditionTxt[condition], yRatio); } +*/ diff --git a/src/solver/datetime.c b/src/solver/datetime.c index e139ff207..d96ef7f3b 100644 --- a/src/solver/datetime.c +++ b/src/solver/datetime.c @@ -2,17 +2,17 @@ // datetime.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 08/01/16 (Build 5.1.011) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // DateTime functions. // -// Build 5.1.011 +// Update History +// ============== +// Build 5.1.011: // - decodeTime() no longer rounds up. // - New getTimeStamp function added. -// //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -125,7 +125,8 @@ DateTime datetime_encodeDate(int year, int month, int day) { for (j = 0; j < month-1; j++) day += DaysPerMonth[i][j]; i = year - 1; - return i*365 + i/4 - i/100 + i/400 + day - DateDelta; + i = i*365 + i/4 - i/100 + i/400 + day - DateDelta; + return i; } else return -DateDelta; } @@ -249,27 +250,23 @@ void datetime_dateToStr(DateTime date, char* s) { int y, m, d; - char dateStr[DATE_STR_SIZE]; datetime_decodeDate(date, &y, &m, &d); switch (DateFormat) { case Y_M_D: - sprintf(dateStr, "%4d-%3s-%02d", y, MonthTxt[m-1], d); + snprintf(s, DATE_STR_SIZE, "%4d-%3s-%02d", y, MonthTxt[m-1], d); break; case M_D_Y: //sprintf(dateStr, "%3s-%02d-%4d", MonthTxt[m-1], d, y); - sprintf(dateStr, "%02d/%02d/%04d", m, d, y); + snprintf(s, DATE_STR_SIZE, "%02d/%02d/%04d", m, d, y); break; default: - sprintf(dateStr, "%02d-%3s-%4d", d, MonthTxt[m-1], y); + snprintf(s, DATE_STR_SIZE, "%02d-%3s-%4d", d, MonthTxt[m-1], y); } - strcpy(s, dateStr); } -//============================================================================= - void datetime_timeToStr(DateTime time, char* s) // Input: time = decimal fraction of a day @@ -278,10 +275,8 @@ void datetime_timeToStr(DateTime time, char* s) { int hr, min, sec; - char timeStr[TIME_STR_SIZE]; datetime_decodeTime(time, &hr, &min, &sec); - sprintf(timeStr, "%02d:%02d:%02d", hr, min, sec); - strcpy(s, timeStr); + snprintf(s, TIME_STR_SIZE, "%02d:%02d:%02d", hr, min, sec); } //============================================================================= @@ -524,10 +519,10 @@ void datetime_getTimeStamp(int fmt, DateTime aDate, int stampSize, char* timeSta char timeStr[TIME_STR_SIZE]; int oldDateFormat = DateFormat; - if ( stampSize < DATE_STR_SIZE + TIME_STR_SIZE + 2 ) return; + if ( stampSize < TIME_STAMP_SIZE ) return; datetime_setDateFormat(fmt); datetime_dateToStr(aDate, dateStr); DateFormat = oldDateFormat; datetime_timeToStr(aDate, timeStr); - sprintf(timeStamp, "%s %s", dateStr, timeStr); + snprintf(timeStamp, stampSize, "%s %s", dateStr, timeStr); } diff --git a/src/solver/datetime.h b/src/solver/datetime.h index 8c84b3b79..98b87b48e 100644 --- a/src/solver/datetime.h +++ b/src/solver/datetime.h @@ -2,9 +2,8 @@ // datetime.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 08/01/16 (Build 5.1.011) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // The DateTime type is used to store date and time values. It is @@ -14,7 +13,9 @@ // passed since 12/31/1899. The fractional part of a DateTime value is the // fraction of a 24 hour day that has elapsed. // -// Build 5.1.011 +// Update History +// ============== +// Build 5.1.011: // - New getTimeStamp function added. //----------------------------------------------------------------------------- @@ -30,6 +31,7 @@ typedef double DateTime; #define NO_DATE -693594 // 1/1/0001 #define DATE_STR_SIZE 12 #define TIME_STR_SIZE 9 +#define TIME_STAMP_SIZE 21 // Functions for encoding a date or time value to a DateTime value DateTime datetime_encodeDate(int year, int month, int day); diff --git a/src/solver/dwflow.c b/src/solver/dwflow.c index de096ad38..14338b2de 100644 --- a/src/solver/dwflow.c +++ b/src/solver/dwflow.c @@ -2,29 +2,24 @@ // dwflow.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 03/19/15 (Build 5.1.008) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// 03/01/20 (Build 5.1.014) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // M. Tryby (EPA) // R. Dickinson (CDM) // // Solves the momentum equation for flow in a conduit under dynamic wave // flow routing. // +// Update History +// ============== // Build 5.1.008: // - Bug in finding if conduit was upstrm/dnstrm full was fixed. -// // Build 5.1.012: // - Modified uniform loss rate term of conduit momentum equation. -// // Build 5.1.013: // - Preissmann slot surcharge option implemented. // - Changed sign of uniform loss rate term (dq6) in flow updating equation. -// // Build 5.1.014: // - Conduit evap. and seepage loss initialized to 0 in dwflow_findConduitFlow. // - Most current flow (qLast) used instead of previous time period flow @@ -32,8 +27,8 @@ //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE -#include "headers.h" #include +#include "headers.h" static const double MAXVELOCITY = 50.; // max. allowable velocity (ft/sec) @@ -46,8 +41,8 @@ static double findLocalLosses(int link, double a1, double a2, double aMid, double q); static double getWidth(TXsect* xsect, double y); -static double getSlotWidth(TXsect* xsect, double y); //(5.1.013) -static double getArea(TXsect* xsect, double y, double wSlot); //(5.1.013) +static double getSlotWidth(TXsect* xsect, double y); +static double getArea(TXsect* xsect, double y, double wSlot); static double getHydRad(TXsect* xsect, double y); static double checkNormalFlow(int j, double q, double y1, double y2, @@ -82,7 +77,7 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) double rho; // upstream weighting factor double sigma; // inertial damping factor double length; // effective conduit length (ft) - double wSlot; // Preissmann slot width (ft) //(5.1.013) + double wSlot; // Preissmann slot width (ft) double dq1, dq2, dq3, dq4, dq5, // terms in momentum eqn. dq6; // term for evap and infil losses double denom; // denominator of flow update formula @@ -102,8 +97,8 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) barrels = Conduit[k].barrels; qOld = Link[j].oldFlow / barrels; qLast = Conduit[k].q1; - Conduit[k].evapLossRate = 0.0; //(5.1.014) - Conduit[k].seepLossRate = 0.0; //(5.1.014) + Conduit[k].evapLossRate = 0.0; + Conduit[k].seepLossRate = 0.0; // --- get most current heads at upstream and downstream ends of conduit n1 = Link[j].node1; @@ -123,7 +118,7 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) y2 = MAX(y2, FUDGE); // --- flow depths can't exceed full depth of conduit if slot not used - if ( SurchargeMethod != SLOT ) //(5.1.013) + if ( SurchargeMethod != SLOT ) { y1 = MIN(y1, xsect->yFull); y2 = MIN(y2, xsect->yFull); @@ -141,16 +136,16 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) findSurfArea(j, qLast, length, &h1, &h2, &y1, &y2); // --- compute area at each end of conduit & hyd. radius at upstream end - wSlot = getSlotWidth(xsect, y1); //(5.1.013) - a1 = getArea(xsect, y1, wSlot); //(5.1.013) + wSlot = getSlotWidth(xsect, y1); + a1 = getArea(xsect, y1, wSlot); r1 = getHydRad(xsect, y1); - wSlot = getSlotWidth(xsect, y2); //(5.1.013) - a2 = getArea(xsect, y2, wSlot); //(5.1.013) + wSlot = getSlotWidth(xsect, y2); + a2 = getArea(xsect, y2, wSlot); // --- compute area & hyd. radius at midpoint yMid = 0.5 * (y1 + y2); - wSlot = getSlotWidth(xsect, yMid); //(5.1.013) - aMid = getArea(xsect, yMid, wSlot); //(5.1.013) + wSlot = getSlotWidth(xsect, yMid); + aMid = getArea(xsect, yMid, wSlot); rMid = getHydRad(xsect, yMid); // --- alternate approach not currently used, but might produce better @@ -234,11 +229,11 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) } // --- 6. term for evap and seepage losses per unit length - dq6 = link_getLossRate(j, qLast) * 2.5 * dt * v / link_getLength(j); //(5.1.014) + dq6 = link_getLossRate(j, qLast) * 2.5 * dt * v / link_getLength(j); // --- combine terms to find new conduit flow denom = 1.0 + dq1 + dq5; - q = (qOld - dq2 + dq3 + dq4 + dq6) / denom; //(5.1.013) + q = (qOld - dq2 + dq3 + dq4 + dq6) / denom; // --- compute derivative of flow w.r.t. head Link[j].dqdh = 1.0 / denom * GRAVITY * dt * aWtd / length * barrels; @@ -288,7 +283,7 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) Conduit[k].q2 = q; Link[j].newDepth = MIN(yMid, xsect->yFull); aMid = (a1 + a2) / 2.0; -// aMid = MIN(aMid, xsect->aFull); //Slot can have aMid > aFull //(5.1.013) +// aMid = MIN(aMid, xsect->aFull); //Slot can have aMid > aFull Conduit[k].fullState = link_getFullState(a1, a2, xsect->aFull); Link[j].newVolume = aMid * link_getLength(j) * barrels; Link[j].newFlow = q * barrels; @@ -454,7 +449,6 @@ void findSurfArea(int j, double q, double length, double* h1, double* h2, normalDepth = (flowDepth1 + flowDepth2) / 2.0; criticalDepth = normalDepth; -//// Following code segment modified for release 5.1.013. //// //(5.1.013) // --- find conduit's flow classification fullDepth = xsect->yFull; if (flowDepth1 >= fullDepth && flowDepth2 >= fullDepth) @@ -463,7 +457,6 @@ void findSurfArea(int j, double q, double length, double* h1, double* h2, } else Link[j].flowClass = getFlowClass(j, q, *h1, *h2, *y1, *y2, &criticalDepth, &normalDepth, &fasnh); -/////////////////////////////////////////////////////////////// // --- add conduit's surface area to its end nodes depending on flow class switch ( Link[j].flowClass ) @@ -576,8 +569,6 @@ double findLocalLosses(int j, double a1, double a2, double aMid, double q) //============================================================================= -//// New function added to release 5.1.013. //// //(5.1.013) - double getSlotWidth(TXsect* xsect, double y) { double yNorm = y / xsect->yFull; @@ -595,8 +586,6 @@ double getSlotWidth(TXsect* xsect, double y) //============================================================================= -//// This function was re-written for release 5.1.013. //// //(5.1.013) - double getWidth(TXsect* xsect, double y) // // Input: xsect = ptr. to conduit cross section @@ -614,8 +603,6 @@ double getWidth(TXsect* xsect, double y) //============================================================================= -//// This function was re-written for release 5.1.013. //// //(5.1.013) - double getArea(TXsect* xsect, double y, double wSlot) // // Input: xsect = ptr. to conduit cross section @@ -630,8 +617,6 @@ double getArea(TXsect* xsect, double y, double wSlot) //============================================================================= -//// This function was re-written for release 5.1.013. //// //(5.1.013) - double getHydRad(TXsect* xsect, double y) // // Input: xsect = ptr. to conduit cross section diff --git a/src/solver/dynwave.c b/src/solver/dynwave.c index 8c901057e..ac302e849 100644 --- a/src/solver/dynwave.c +++ b/src/solver/dynwave.c @@ -2,16 +2,9 @@ // dynwave.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (5.1.001) -// 03/28/14 (5.1.002) -// 09/15/14 (5.1.007) -// 03/19/15 (5.1.008) -// 08/01/16 (5.1.011) -// 05/10/18 (5.1.013) -// 03/01/20 (5.1.014) -// 07/10/20 (5.1.015) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // M. Tryby (EPA) // R. Dickinson (CDM) // @@ -22,47 +15,41 @@ // to solve the explicit form of the continuity and momentum equations // for conduits. // +// Update History +// ============== // Build 5.1.002: // - Only non-ponded nodal surface area is saved for use in // surcharge algorithm. -// // Build 5.1.007: // - Node losses added to node outflow variable instead of treated // as a separate item when computing change in node flow volume. -// // Build 5.1.008: // - Module-specific constants moved here from project.c. // - Support added for user-specified minimum variable time step. // - Node crown elevations found here instead of in flowrout.c module. // - OpenMP use to parallelize findLinkFlows() & findNodeDepths(). // - Bug in finding complete list of capacity limited links fixed. -// // Build 5.1.011: // - Added test for failed memory allocation. // - Fixed illegal array index bug for Ideal Pumps. -// // Build 5.1.013: // - Include omp.h protected against lack of compiler support for OpenMP. // - SurchargeMethod option used to decide how node surcharging is handled. // - Storage nodes allowed to pressurize if their surcharge depth > 0. // - Minimum flow needed to compute a Courant time step modified. -// // Build 5.1.014: // - updateNodeFlows() modified to subtract conduit evap. and seepage losses // from downstream node inflow instead of upstream node outflow. -// // Build 5.1.015: // - Roll back the 5.1.014 change for conduit losses in updateNodeFlows(). -// +// Build 5.2.0: +// - Support added for reporting most frequent non-converging links. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE -#include "headers.h" #include #include -#if defined(_OPENMP) //(5.1.013) -#include -#endif +#include "headers.h" //----------------------------------------------------------------------------- // Constants @@ -71,9 +58,9 @@ static const double MINTIMESTEP = 0.001; // min. time step (sec) static const double OMEGA = 0.5; // under-relaxation parameter static const double DEFAULT_SURFAREA = 12.566; // Min. nodal surface area (~4 ft diam.) static const double DEFAULT_HEADTOL = 0.005; // Default head tolerance (ft) -static const double EXTRAN_CROWN_CUTOFF = 0.96; // crown cutoff for EXTRAN //(5.1.013) -static const double SLOT_CROWN_CUTOFF = 0.985257; // crown cutoff for SLOT //(5.1.013) -static const int DEFAULT_MAXTRIALS = 8; // Max. trials per time step +static const double EXTRAN_CROWN_CUTOFF = 0.96; // crown cutoff for EXTRAN +static const double SLOT_CROWN_CUTOFF = 0.985257; // crown cutoff for SLOT +static const int DEFAULT_MAXTRIALS = 8; // Max. trials per time step //----------------------------------------------------------------------------- @@ -111,6 +98,7 @@ static void findNonConduitFlow(int link, double dt); static void findNonConduitSurfArea(int link); static double getModPumpFlow(int link, double q, double dt); static void updateNodeFlows(int link); +static void updateConvergenceStats(); static int findNodeDepths(double dt); static void setNodeDepth(int node, double dt); @@ -163,9 +151,9 @@ void dynwave_init() Link[i].dqdh = 0.0; } - // --- set crown cutoff for finding top width of closed conduits //(5.1.013) - if ( SurchargeMethod == SLOT ) CrownCutoff = SLOT_CROWN_CUTOFF; //(5.1.013) - else CrownCutoff = EXTRAN_CROWN_CUTOFF; //(5.1.013) + // --- set crown cutoff for finding top width of closed conduits + if ( SurchargeMethod == SLOT ) CrownCutoff = SLOT_CROWN_CUTOFF; + else CrownCutoff = EXTRAN_CROWN_CUTOFF; } //============================================================================= @@ -262,7 +250,7 @@ int dynwave_execute(double tStep) findBypassedLinks(); } } - if ( !converged ) NonConvergeCount++; + if ( !converged ) updateConvergenceStats(); // --- identify any capacity-limited conduits findLimitedLinks(); @@ -271,6 +259,16 @@ int dynwave_execute(double tStep) //============================================================================= +void updateConvergenceStats() +{ + int i; + NonConvergeCount++; + for (i = 0; i < Nobjects[NODE]; i++) + stats_updateConvergenceStats(i, Xnode[i].converged); +} + +//============================================================================= + void initRoutingStep() { int i; @@ -313,12 +311,6 @@ void initNodeStates() Xnode[i].newSurfArea = node_getSurfArea(i, Node[i].newDepth); } -/* //// Removed for release 5.1.013. /// //(5.1.013) - if ( Xnode[i].newSurfArea < MinSurfArea ) - { - Xnode[i].newSurfArea = MinSurfArea; - } -*/ // --- initialize nodal inflow & outflow Node[i].inflow = 0.0; Node[i].outflow = Node[i].losses; @@ -550,19 +542,19 @@ void updateNodeFlows(int i) k = Link[i].subIndex; uniformLossRate = Conduit[k].evapLossRate + Conduit[k].seepLossRate; barrels = Conduit[k].barrels; - uniformLossRate *= barrels; //(5.1.014) + uniformLossRate *= barrels; } // --- update total inflow & outflow at upstream/downstream nodes if ( q >= 0.0 ) { - Node[n1].outflow += q + uniformLossRate; //(5.1.015) - Node[n2].inflow += q; //(5.1.015) + Node[n1].outflow += q + uniformLossRate; + Node[n2].inflow += q; } else { - Node[n1].inflow -= q; //(5.1.015) - Node[n2].outflow -= q - uniformLossRate; //(5.1.015) + Node[n1].inflow -= q; + Node[n2].outflow -= q - uniformLossRate; } // --- add surf. area contributions to upstream/downstream nodes @@ -585,9 +577,14 @@ void updateNodeFlows(int i) //============================================================================= int findNodeDepths(double dt) +// +// Input: dt = time step (sec) +// Output: returns TRUE if depth change at all non-Outfall nodes is +// within the convergence tolerance and FALSE otherwise +// Purpose: finds new depth at all nodes and checks if convergence achieved. +// { int i; - int converged; // convergence flag double yOld; // previous node depth (ft) // --- compute outfall depths based on flow in connecting link @@ -595,7 +592,6 @@ int findNodeDepths(double dt) // --- compute new depth for all non-outfall nodes and determine if // depth change from previous iteration is below tolerance - converged = TRUE; #pragma omp parallel num_threads(NumThreads) { #pragma omp for private(yOld) @@ -607,12 +603,18 @@ int findNodeDepths(double dt) Xnode[i].converged = TRUE; if ( fabs(yOld - Node[i].newDepth) > HeadTol ) { - converged = FALSE; Xnode[i].converged = FALSE; } } } - return converged; + + // --- return FALSE if any non-Outfall node failed to converge + for (i = 0; i < Nobjects[NODE]; i++) + { + if ( Node[i].type == OUTFALL ) continue; + if (Xnode[i].converged == FALSE) return FALSE; + } + return TRUE; } //============================================================================= @@ -627,7 +629,7 @@ void setNodeDepth(int i, double dt) { int canPond; // TRUE if node can pond overflows int isPonded; // TRUE if node is currently ponded - int isSurcharged = FALSE; // TRUE if node is surcharged //(5.1.013) + int isSurcharged = FALSE; // TRUE if node is surcharged double dQ; // inflow minus outflow at node (cfs) double dV; // change in node volume (ft3) double dy; // change in node depth (ft) @@ -651,13 +653,13 @@ void setNodeDepth(int i, double dt) yLast = Node[i].newDepth; Node[i].overflow = 0.0; surfArea = Xnode[i].newSurfArea; - surfArea = MAX(surfArea, MinSurfArea); //(5.1.013) + surfArea = MAX(surfArea, MinSurfArea); // --- determine average net flow volume into node over the time step dQ = Node[i].inflow - Node[i].outflow; dV = 0.5 * (Node[i].oldNetInflow + dQ) * dt; -//// Following code segment added to release 5.1.013. //// //(5.1.013) + // --- determine if node is EXTRAN surcharged if (SurchargeMethod == EXTRAN) { @@ -674,10 +676,9 @@ void setNodeDepth(int i, double dt) // --- surcharge occurs when node depth exceeds top of its highest link else isSurcharged = (yCrown > 0.0 && yLast > yCrown); } -///////////////////////////////////////////////////////////// // --- if node not surcharged, base depth change on surface area - if (!isSurcharged) //(5.1.013) + if (!isSurcharged) { dy = dV / surfArea; yNew = yOld + dy; @@ -841,7 +842,7 @@ double getLinkStep(double tMin, int *minLink) // --- skip conduits with negligible flow, area or Fr k = Link[i].subIndex; q = fabs(Link[i].newFlow) / Conduit[k].barrels; - if ( q <= FUDGE //(5.1.013) + if ( q <= FUDGE || Conduit[k].a1 <= FUDGE || Link[i].froude <= 0.01 ) continue; diff --git a/src/solver/enums.h b/src/solver/enums.h index 1ee7b0bea..d2db866a7 100644 --- a/src/solver/enums.h +++ b/src/solver/enums.h @@ -2,41 +2,35 @@ // enums.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 04/14/14 (Build 5.1.004) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) -// 05/10/18 (Build 5.1.013) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // -// Enumerated variables +// Enumerated constants // +// Update History +// ============== // Build 5.1.004: // - IGNORE_RDII for the ignore RDII option added. -// // Build 5.1.007: // - s_GWF for [GWF] input file section added. // - s_ADJUST for [ADJUSTMENTS] input file section added. -// // Build 5.1.008: // - Enumerations for fullness state of a conduit added. // - NUM_THREADS added for number of parallel threads option. // - Runoff flow categories added to represent mass balance components. -// // Build 5.1.010: // - New ROADWAY_WEIR type of weir added. // - Potential evapotranspiration (PET) added as a system output variable. -// // Build 5.1.011: // - s_EVENT added to InputSectionType enumeration. -// // Build 5.1.013: // - SURCHARGE_METHOD and RULE_STEP options added. -// - WEIR_CURVE added as a curve type. -// +// - WEIR_CURVE added as a curve type. +// Build 5.2.0: +// - Support added for Streets and Inlets. +// - Support added for variable speed pumps. +// - Support added for analytical storage shapes. //----------------------------------------------------------------------------- #ifndef ENUMS_H @@ -63,12 +57,14 @@ SNOWMELT, // snowmelt parameter set SHAPE, // custom conduit shape LID, // LID treatment units + STREET, // street cross section + INLET, // street inlet design MAX_OBJ_TYPES}; //------------------------------------- // Names of Node sub-types //------------------------------------- - #define MAX_NODE_TYPES 4 + #define MAX_NODE_TYPES 5 enum NodeType { JUNCTION, OUTFALL, @@ -112,7 +108,7 @@ enum GageDataType { RAIN_TSERIES, // rainfall from user-supplied time series RAIN_FILE, // rainfall from external file - RAIN_API}; // rainfall from API(Modify rainfall mid simulation) + RAIN_API}; // rainfall from OWA Toolkit API (Modify rainfall mid simulation) //------------------------------------- // Cross section shape types @@ -142,7 +138,8 @@ SEMICIRCULAR, // 21 closed IRREGULAR, // 22 CUSTOM, // 23 closed - FORCE_MAIN}; // 24 closed + FORCE_MAIN, // 24 closed + STREET_XSECT}; // 25 //------------------------------------- // Measurement units types @@ -221,7 +218,7 @@ LINK_QUAL}; // concentration of each pollutant //------------------------------------- -// System-wide flow quantities +// System-wide quantities //------------------------------------- #define MAX_SYS_RESULTS 15 enum SysFlowType { @@ -307,7 +304,7 @@ enum WindType { DRYONLY}; // evap. allowed only in dry periods enum NormalizerType { - PER_AREA, // buildup is per unit or area + PER_AREA, // buildup is per unit of area PER_CURB}; // buildup is per unit of curb length enum BuildupType { @@ -367,7 +364,6 @@ enum CompatibilityType { PARTIAL_DAMPING, // partial damping FULL_DAMPING}; // full damping -//// Added to release 5.1.013. //// //(5.1.013) enum SurchargeMethodType { EXTRAN, // original EXTRAN method SLOT}; // Preissmann slot method @@ -391,13 +387,17 @@ enum CompatibilityType { enum OutfallType { FREE_OUTFALL, // critical depth outfall condition NORMAL_OUTFALL, // normal flow depth outfall condition - STAGED_OUTFALL, // fixed depth outfall condition (API Support) + FIXED_OUTFALL, // fixed depth outfall condition TIDAL_OUTFALL, // variable tidal stage outfall condition TIMESERIES_OUTFALL}; // variable time series outfall depth enum StorageType { TABULAR, // area v. depth from table - FUNCTIONAL}; // area v. depth from power function + FUNCTIONAL, // area v. depth from power function + CYLINDRICAL, // area v. depth from elliptical cylinder + CONICAL, // area v. depth from elliptical cone + PARABOLOID, // area v. depth from elliptical paraboloid + PYRAMIDAL}; // area v. depth from rectangular pyramid enum ReactorType { CSTR, // completely mixed reactor @@ -418,6 +418,7 @@ enum CompatibilityType { TYPE2_PUMP, // flow varies stepwise with inlet depth TYPE3_PUMP, // flow varies with head delivered TYPE4_PUMP, // flow varies with inlet depth + TYPE5_PUMP, // variable speed version of TYPE3 pump IDEAL_PUMP}; // outflow equals inflow enum OrificeType { @@ -438,11 +439,18 @@ enum CompatibilityType { RATING_CURVE, // flow rate v. head for outlet link CONTROL_CURVE, // control setting v. controller variable SHAPE_CURVE, // width v. depth for custom x-section - WEIR_CURVE, // discharge coeff. v. head for weir //(5.1.013) + WEIR_CURVE, // discharge coeff. v. head for weir PUMP1_CURVE, // flow v. wet well volume for pump PUMP2_CURVE, // flow v. depth for pump (discrete) PUMP3_CURVE, // flow v. head for pump (continuous) - PUMP4_CURVE}; // flow v. depth for pump (continuous) + PUMP4_CURVE, // flow v. depth for pump (continuous) + PUMP5_CURVE}; // variable speed version of TYPE3 pump + + enum NodeInletType { + NO_INLET, + BYPASS, + CAPTURE + }; enum InputSectionType { s_TITLE, s_OPTION, s_FILE, s_RAINGAGE, @@ -458,14 +466,15 @@ enum CompatibilityType { s_COORDINATE, s_VERTICES, s_POLYGON, s_LABEL, s_SYMBOL, s_BACKDROP, s_TAG, s_PROFILE, s_MAP, s_LID_CONTROL, s_LID_USAGE, s_GWF, - s_ADJUST, s_EVENT}; + s_ADJUST, s_EVENT, s_STREET, s_INLET_USAGE, + s_INLET}; enum InputOptionType { FLOW_UNITS, INFIL_MODEL, ROUTE_MODEL, START_DATE, START_TIME, END_DATE, END_TIME, REPORT_START_DATE, REPORT_START_TIME, SWEEP_START, SWEEP_END, START_DRY_DAYS, - WET_STEP, DRY_STEP, ROUTE_STEP, RULE_STEP, //(5.1.013) + WET_STEP, DRY_STEP, ROUTE_STEP, RULE_STEP, REPORT_STEP, ALLOW_PONDING, INERT_DAMPING, SLOPE_WEIGHTING, VARIABLE_STEP, NORMAL_FLOW_LTD, LENGTHENING_STEP, MIN_SURFAREA, COMPATIBILITY, @@ -474,7 +483,7 @@ enum CompatibilityType { IGNORE_SNOWMELT, IGNORE_GWATER, IGNORE_ROUTING, IGNORE_QUALITY, MAX_TRIALS, HEAD_TOL, SYS_FLOW_TOL, LAT_FLOW_TOL, IGNORE_RDII, - MIN_ROUTE_STEP, NUM_THREADS, SURCHARGE_METHOD}; //(5.1.013) + MIN_ROUTE_STEP, NUM_THREADS, SURCHARGE_METHOD}; enum NoYesType { NO, diff --git a/src/solver/error.c b/src/solver/error.c index f0d40f62b..de1ca09aa 100644 --- a/src/solver/error.c +++ b/src/solver/error.c @@ -2,256 +2,46 @@ // error.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 03/19/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 04/14/20 (Build 5.1.015) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Error messages // +// Update History +// ============== // Build 5.1.008: // - Text of Error 217 for control rules modified. -// // Build 5.1.010: // - Text of Error 318 for rainfall data files modified. -// // Build 5.1.015: // - Added new Error 140 for storage nodes. +// Build 5.2.0: +// - Re-designed error message system. +// - Added new Error 235 for invalid infiltration parameters. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE #include #include "error.h" -#define ERR101 "\n ERROR 101: memory allocation error." -#define ERR103 "\n ERROR 103: cannot solve KW equations for Link %s." -#define ERR105 "\n ERROR 105: cannot open ODE solver." -#define ERR107 "\n ERROR 107: cannot compute a valid time step." - -#define ERR108 "\n ERROR 108: ambiguous outlet ID name for Subcatchment %s." -#define ERR109 "\n ERROR 109: invalid parameter values for Aquifer %s." -#define ERR110 \ -"\n ERROR 110: ground elevation is below water table for Subcatchment %s." - -#define ERR111 "\n ERROR 111: invalid length for Conduit %s." -#define ERR112 "\n ERROR 112: elevation drop exceeds length for Conduit %s." -#define ERR113 "\n ERROR 113: invalid roughness for Conduit %s." -#define ERR114 "\n ERROR 114: invalid number of barrels for Conduit %s." -#define ERR115 "\n ERROR 115: adverse slope for Conduit %s." -#define ERR117 "\n ERROR 117: no cross section defined for Link %s." -#define ERR119 "\n ERROR 119: invalid cross section for Link %s." -#define ERR121 \ -"\n ERROR 121: missing or invalid pump curve assigned to Pump %s." -#define ERR122 \ -"\n ERROR 122: startup depth not higher than shutoff depth for Pump %s." - -#define ERR131 \ -"\n ERROR 131: the following links form cyclic loops in the drainage system:" -#define ERR133 "\n ERROR 133: Node %s has more than one outlet link." -#define ERR134 "\n ERROR 134: Node %s has illegal DUMMY link connections." - -#define ERR135 "\n ERROR 135: Divider %s does not have two outlet links." -#define ERR136 "\n ERROR 136: Divider %s has invalid diversion link." -#define ERR137 "\n ERROR 137: Weir Divider %s has invalid parameters." -#define ERR138 \ -"\n ERROR 138: Node %s has initial depth greater than maximum depth." -#define ERR139 "\n ERROR 139: Regulator %s is the outlet of a non-storage node." -#define ERR140 \ -"\n ERROR 140: Storage node %s has negative volume at full depth." //(5.1.015) -#define ERR141 \ -"\n ERROR 141: Outfall %s has more than 1 inlet link or an outlet link." -#define ERR143 "\n ERROR 143: Regulator %s has invalid cross-section shape." -#define ERR145 "\n ERROR 145: Drainage system has no acceptable outlet nodes." - -#define ERR151 "\n ERROR 151: a Unit Hydrograph in set %s has invalid time base." -#define ERR153 \ -"\n ERROR 153: a Unit Hydrograph in set %s has invalid response ratios." -#define ERR155 "\n ERROR 155: invalid sewer area for RDII at node %s." - -#define ERR156 "\n ERROR 156: ambiguous station ID for Rain Gage %s." -#define ERR157 "\n ERROR 157: inconsistent rainfall format for Rain Gage %s." -#define ERR158 \ -"\n ERROR 158: time series for Rain Gage %s is also used by another object." -#define ERR159 \ -"\n ERROR 159: recording interval greater than time series interval for Rain Gage %s." - -#define ERR161 \ -"\n ERROR 161: cyclic dependency in treatment functions at node %s." - -#define ERR171 "\n ERROR 171: Curve %s has invalid or out of sequence data." -#define ERR173 "\n ERROR 173: Time Series %s has its data out of sequence." - -#define ERR181 "\n ERROR 181: invalid Snow Melt Climatology parameters." -#define ERR182 "\n ERROR 182: invalid parameters for Snow Pack %s." - -#define ERR183 "\n ERROR 183: no type specified for LID %s." -#define ERR184 "\n ERROR 184: missing layer for LID %s." -#define ERR185 "\n ERROR 185: invalid parameter value for LID %s." -#define ERR186 "\n ERROR 186: invalid parameter value for LID placed in Subcatchment %s." -#define ERR187 "\n ERROR 187: LID area exceeds total area for Subcatchment %s." -#define ERR188 \ -"\n ERROR 188: LID capture area exceeds total impervious area for Subcatchment %s." - -#define ERR191 "\n ERROR 191: simulation start date comes after ending date." -#define ERR193 "\n ERROR 193: report start date comes after ending date." -#define ERR195 \ -"\n ERROR 195: reporting time step or duration is less than routing time step." - -#define ERR200 "\n ERROR 200: one or more errors in input file." -#define ERR201 "\n ERROR 201: too many characters in input line " -#define ERR203 "\n ERROR 203: too few items " -#define ERR205 "\n ERROR 205: invalid keyword %s " -#define ERR207 "\n ERROR 207: duplicate ID name %s " -#define ERR209 "\n ERROR 209: undefined object %s " -#define ERR211 "\n ERROR 211: invalid number %s " -#define ERR213 "\n ERROR 213: invalid date/time %s " -#define ERR217 "\n ERROR 217: control rule clause invalid or out of sequence " //(5.1.008) -#define ERR219 "\n ERROR 219: data provided for unidentified transect " -#define ERR221 "\n ERROR 221: transect station out of sequence " -#define ERR223 "\n ERROR 223: Transect %s has too few stations." -#define ERR225 "\n ERROR 225: Transect %s has too many stations." -#define ERR227 "\n ERROR 227: Transect %s has no Manning's N." -#define ERR229 "\n ERROR 229: Transect %s has invalid overbank locations." -#define ERR231 "\n ERROR 231: Transect %s has no depth." -#define ERR233 "\n ERROR 233: invalid treatment function expression " - -#define ERR301 "\n ERROR 301: files share same names." -#define ERR303 "\n ERROR 303: cannot open input file." -#define ERR305 "\n ERROR 305: cannot open report file." -#define ERR307 "\n ERROR 307: cannot open binary results file." -#define ERR309 "\n ERROR 309: error writing to binary results file." -#define ERR311 "\n ERROR 311: error reading from binary results file." - -#define ERR313 "\n ERROR 313: cannot open scratch rainfall interface file." -#define ERR315 "\n ERROR 315: cannot open rainfall interface file %s." -#define ERR317 "\n ERROR 317: cannot open rainfall data file %s." -#define ERR318 \ -"\n ERROR 318: the following line is out of sequence in rainfall data file %s." //(5.1.010) -#define ERR319 "\n ERROR 319: unknown format for rainfall data file %s." -#define ERR320 "\n ERROR 320: invalid format for rainfall interface file." -#define ERR321 "\n ERROR 321: no data in rainfall interface file for gage %s." - -#define ERR323 "\n ERROR 323: cannot open runoff interface file %s." -#define ERR325 \ -"\n ERROR 325: incompatible data found in runoff interface file." -#define ERR327 \ -"\n ERROR 327: attempting to read beyond end of runoff interface file." -#define ERR329 "\n ERROR 329: error in reading from runoff interface file." - -#define ERR330 "\n ERROR 330: hotstart interface files have same names." -#define ERR331 "\n ERROR 331: cannot open hotstart interface file %s." -#define ERR333 \ -"\n ERROR 333: incompatible data found in hotstart interface file." -#define ERR335 "\n ERROR 335: error in reading from hotstart interface file." - -#define ERR336 \ -"\n ERROR 336: no climate file specified for evaporation and/or wind speed." -#define ERR337 "\n ERROR 337: cannot open climate file %s." -#define ERR338 "\n ERROR 338: error in reading from climate file %s." -#define ERR339 \ -"\n ERROR 339: attempt to read beyond end of climate file %s." - -#define ERR341 "\n ERROR 341: cannot open scratch RDII interface file." -#define ERR343 "\n ERROR 343: cannot open RDII interface file %s." -#define ERR345 "\n ERROR 345: invalid format for RDII interface file." - -#define ERR351 "\n ERROR 351: cannot open routing interface file %s." -#define ERR353 "\n ERROR 353: invalid format for routing interface file %s." -#define ERR355 "\n ERROR 355: mis-matched names in routing interface file %s." -#define ERR357 "\n ERROR 357: inflows and outflows interface files have same name." - -#define ERR361 "\n ERROR 361: could not open external file used for Time Series %s." -#define ERR363 "\n ERROR 363: invalid data in external file used for Time Series %s." - -#define ERR401 "\n ERROR 401: general system error." -#define ERR402 \ -"\n ERROR 402: cannot open new project while current project still open." -#define ERR403 "\n ERROR 403: project not open or last run not ended." -#define ERR405 \ -"\n ERROR 405: amount of output produced will exceed maximum file size;" \ -"\n either reduce Ending Date or increase Reporting Time Step." - -// API Error Keys -#define ERR501 "\n API Key Error: Object Type Outside Bonds" -#define ERR502 "\n API Key Error: Network Not Initialized (Input file open?)" -#define ERR503 "\n API Key Error: Simulation Not Running" -#define ERR504 "\n API Key Error: Incorrect object type for parameter chosen" -#define ERR505 "\n API Key Error: Object index out of Bounds." -#define ERR506 "\n API Key Error: Invalid Pollutant Index" -#define ERR507 "\n API Key Error: Invalid Inflow Type" -#define ERR508 "\n API Key Error: Invalid Timeseries Index" -#define ERR509 "\n API Key Error: Invalid Pattern Index" -#define ERR510 "\n API Key Error: Invalid Lid Unit Index" -#define ERR511 "\n API Key Error: Undefined Subcatchment Lid" -#define ERR512 "\n API Key Error: No memory allocated for return value" - -//////////////////////////////////////////////////////////////////////////// -// NOTE: Need to update ErrorMsgs[], ErrorCodes[], and ErrorType -// (in error.h) whenever a new error message is added. -///////////////////////////////////////////////////////////////////////////// - -char* ErrorMsgs[] = - { "", ERR101, ERR103, ERR105, ERR107, ERR108, ERR109, ERR110, ERR111, - ERR112, ERR113, ERR114, ERR115, ERR117, ERR119, ERR121, ERR122, ERR131, - ERR133, ERR134, ERR135, ERR136, ERR137, ERR138, ERR139, ERR141, ERR143, - ERR145, ERR151, ERR153, ERR155, ERR156, ERR157, ERR158, ERR159, ERR161, - ERR171, ERR173, ERR181, ERR182, ERR183, ERR184, ERR185, ERR186, ERR187, - ERR188, ERR191, ERR193, ERR195, ERR200, ERR201, ERR203, ERR205, ERR207, - ERR209, ERR211, ERR213, ERR217, ERR219, ERR221, ERR223, ERR225, ERR227, - ERR229, ERR231, ERR233, ERR301, ERR303, ERR305, ERR307, ERR309, ERR311, - ERR313, ERR315, ERR317, ERR318, ERR319, ERR320, ERR321, ERR323, ERR325, - ERR327, ERR329, ERR330, ERR331, ERR333, ERR335, ERR336, ERR337, ERR338, - ERR339, ERR341, ERR343, ERR345, ERR351, ERR353, ERR355, ERR357, ERR361, - ERR363, ERR401, ERR402, ERR403, ERR405, ERR501, ERR502, ERR503, ERR504, - ERR505, ERR506, ERR507, ERR508, ERR509, ERR510, ERR511, ERR512, ERR140}; - -int ErrorCodes[] = - { 0, 101, 103, 105, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 117, 119, 121, 122, 131, - 133, 134, 135, 136, 137, 138, 139, 141, 143, - 145, 151, 153, 155, 156, 157, 158, 159, 161, - 171, 173, 181, 182, 183, 184, 185, 186, 187, - 188, 191, 193, 195, 200, 201, 203, 205, 207, - 209, 211, 213, 217, 219, 221, 223, 225, 227, - 229, 231, 233, 301, 303, 305, 307, 309, 311, - 313, 315, 317, 318, 319, 320, 321, 323, 325, - 327, 329, 330, 331, 333, 335, 336, 337, 338, - 339, 341, 343, 345, 351, 353, 355, 357, 361, - 363, 401, 402, 403, 405, 501, 502, 503, 504, - 505, 506, 507, 508, 509, 510, 511, 512, 140}; - char ErrString[256]; -char* error_getMsg(int i) +char* error_getMsg(int errCode, char* msg) { - if ( i >= 0 && i < MAXERRMSG ) return ErrorMsgs[i]; - else return ErrorMsgs[0]; -}; + switch (errCode) + { -int error_getCode(int i) -{ - if ( i >= 0 && i < MAXERRMSG ) return ErrorCodes[i]; - else return 0; -} +#define ERR(code,string) case code: strcpy(msg, string); break; +#include "error.txt" +#include "toolkit_errors.txt" // OWA Edit - include toolkit error messages +#undef ERR -int error_getErrorIndex(int ErrorCode) -{ - int ErrorIndex = 0; - int i = 0; - - while ( i < MAXERRMSG ) - { - if ( ErrorCodes[i] == ErrorCode ) - { - ErrorIndex = i; - break; - } - else i += 1; + default: + strcpy(msg, ""); } - return ErrorIndex; -} + return (msg); +}; int error_setInpError(int errcode, char* s) { diff --git a/src/solver/error.h b/src/solver/error.h index 3697cc129..c68fe2f25 100644 --- a/src/solver/error.h +++ b/src/solver/error.h @@ -2,9 +2,8 @@ // error.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 04/14/20 (Build 5.1.015) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Error codes @@ -14,178 +13,172 @@ #ifndef ERROR_H #define ERROR_H +#include "toolkit_error.h" // include OWA toolkit errors enum ErrorType { - //... Runtime Errors - ERR_NONE, // 0 - ERR_MEMORY, //101 1 - ERR_KINWAVE, //103 2 - ERR_ODE_SOLVER, //105 3 - ERR_TIMESTEP, //107 4 - - //... Subcatchment/Aquifer Errors - ERR_SUBCATCH_OUTLET, //108 5 - ERR_AQUIFER_PARAMS, //109 6 - ERR_GROUND_ELEV, //110 7 - - //... Conduit/Pump Errors - ERR_LENGTH, //111 8 - ERR_ELEV_DROP, //112 9 - ERR_ROUGHNESS, //113 10 - ERR_BARRELS, //114 11 - ERR_SLOPE, //115 12 - ERR_NO_XSECT, //117 13 - ERR_XSECT, //119 14 - ERR_NO_CURVE, //121 15 - ERR_PUMP_LIMITS, //122 16 - - //... Topology Errors - ERR_LOOP, //131 17 - ERR_MULTI_OUTLET, //133 18 - ERR_DUMMY_LINK, //134 19 - - //... Node Errors - ERR_DIVIDER, //135 20 - ERR_DIVIDER_LINK, //136 21 - ERR_WEIR_DIVIDER, //137 22 - ERR_NODE_DEPTH, //138 23 - ERR_REGULATOR, //139 24 - ERR_OUTFALL, //141 25 - ERR_REGULATOR_SHAPE, //143 26 - ERR_NO_OUTLETS, //145 27 - - //... RDII Errors - ERR_UNITHYD_TIMES, //151 28 - ERR_UNITHYD_RATIOS, //153 29 - ERR_RDII_AREA, //155 30 - - //... Rain Gage Errors - ERR_RAIN_FILE_CONFLICT, //156 31 - ERR_RAIN_GAGE_FORMAT, //157 32 - ERR_RAIN_GAGE_TSERIES, //158 33 - ERR_RAIN_GAGE_INTERVAL, //159 34 - - //... Treatment Function Error - ERR_CYCLIC_TREATMENT, //161 35 - - //... Curve/Time Series Errors - ERR_CURVE_SEQUENCE, //171 36 - ERR_TIMESERIES_SEQUENCE, //173 37 - - //... Snowmelt Errors - ERR_SNOWMELT_PARAMS, //181 38 - ERR_SNOWPACK_PARAMS, //182 39 - - //... LID Errors - ERR_LID_TYPE, //183 40 - ERR_LID_LAYER, //184 41 - ERR_LID_PARAMS, //185 42 - ERR_SUBCATCH_LID, //186 43 - ERR_LID_AREAS, //187 44 - ERR_LID_CAPTURE_AREA, //188 45 - - //... Simulation Date/Time Errors - ERR_START_DATE, //191 46 - ERR_REPORT_DATE, //193 47 - ERR_REPORT_STEP, //195 48 - - //... Input Parser Errors - ERR_INPUT, //200 49 - ERR_LINE_LENGTH, //201 50 - ERR_ITEMS, //203 51 - ERR_KEYWORD, //205 52 - ERR_DUP_NAME, //207 53 - ERR_NAME, //209 54 - ERR_NUMBER, //211 55 - ERR_DATETIME, //213 56 - ERR_RULE, //217 57 - ERR_TRANSECT_UNKNOWN, //219 58 - ERR_TRANSECT_SEQUENCE, //221 59 - ERR_TRANSECT_TOO_FEW, //223 60 - ERR_TRANSECT_TOO_MANY, //225 61 - ERR_TRANSECT_MANNING, //227 62 - ERR_TRANSECT_OVERBANK, //229 63 - ERR_TRANSECT_NO_DEPTH, //231 64 - ERR_TREATMENT_EXPR, //233 65 - - //... File Name/Opening Errors - ERR_FILE_NAME, //301 66 - ERR_INP_FILE, //303 67 - ERR_RPT_FILE, //305 68 - ERR_OUT_FILE, //307 69 - ERR_OUT_WRITE, //309 70 - ERR_OUT_READ, //311 71 - - //... Rain File Errors - ERR_RAIN_FILE_SCRATCH, //313 72 - ERR_RAIN_FILE_OPEN, //315 73 - ERR_RAIN_FILE_DATA, //317 74 - ERR_RAIN_FILE_SEQUENCE, //318 75 - ERR_RAIN_FILE_FORMAT, //319 76 - ERR_RAIN_IFACE_FORMAT, //320 77 - ERR_RAIN_FILE_GAGE, //321 78 - - //... Runoff File Errors - ERR_RUNOFF_FILE_OPEN , //323 79 - ERR_RUNOFF_FILE_FORMAT, //325 80 - ERR_RUNOFF_FILE_END, //327 81 - ERR_RUNOFF_FILE_READ, //329 82 - - //... Hotstart File Errors - ERR_HOTSTART_FILE_NAMES, //330 83 - ERR_HOTSTART_FILE_OPEN, //331 84 - ERR_HOTSTART_FILE_FORMAT, //333 85 - ERR_HOTSTART_FILE_READ, //335 86 - - //... Climate File Errors - ERR_NO_CLIMATE_FILE, //336 87 - ERR_CLIMATE_FILE_OPEN, //337 88 - ERR_CLIMATE_FILE_READ, //338 89 - ERR_CLIMATE_END_OF_FILE, //339 90 - - //... RDII File Errors - ERR_RDII_FILE_SCRATCH, //341 91 - ERR_RDII_FILE_OPEN, //343 92 - ERR_RDII_FILE_FORMAT, //345 93 - - //... Routing File Errors - ERR_ROUTING_FILE_OPEN, //351 94 - ERR_ROUTING_FILE_FORMAT, //353 95 - ERR_ROUTING_FILE_NOMATCH, //355 96 - ERR_ROUTING_FILE_NAMES, //357 97 - - //... Time Series File Errors - ERR_TABLE_FILE_OPEN, //361 98 - ERR_TABLE_FILE_READ, //363 99 - - //... Runtime Errors - ERR_SYSTEM, //401 100 - ERR_NOT_CLOSED, //402 101 - ERR_NOT_OPEN, //403 102 - ERR_FILE_SIZE, //405 103 - - //... API Errors - ERR_API_OUTBOUNDS, //501 104 - ERR_API_INPUTNOTOPEN, //502 105 - ERR_API_SIM_NRUNNING, //503 106 - ERR_API_WRONG_TYPE, //504 107 - ERR_API_OBJECT_INDEX, //505 108 - ERR_API_POLLUT_INDEX, //506 109 - ERR_API_INFLOWTYPE, //507 110 - ERR_API_TSERIES_INDEX, //508 111 - ERR_API_PATTERN_INDEX, //509 112 - ERR_API_LIDUNIT_INDEX, //510 113 - ERR_API_UNDEFINED_LID, //511 114 - ERR_API_MEMORY, //512 115 - //... Additional Errors - ERR_STORAGE_VOLUME, //140 116 //(5.1.015) - MAXERRMSG}; - -char* error_getMsg(int i); -int error_getCode(int i); -int error_getErrorIndex(int ErrorCode); +// ... Runtime Errors + ERR_NONE = 0, + ERR_MEMORY = 101, + ERR_KINWAVE = 103, + ERR_ODE_SOLVER = 105, + ERR_TIMESTEP = 107, + +// ... Subcatchment/Aquifer Errors + ERR_SUBCATCH_OUTLET = 108, + ERR_AQUIFER_PARAMS = 109, + ERR_GROUND_ELEV = 110, + +// ... Conduit/Pump Errors + ERR_LENGTH = 111, + ERR_ELEV_DROP = 112, + ERR_ROUGHNESS = 113, + ERR_BARRELS = 114, + ERR_SLOPE = 115, + ERR_NO_XSECT = 117, + ERR_XSECT = 119, + ERR_NO_CURVE = 121, + ERR_PUMP_LIMITS = 122, + +// ... Topology Errors + ERR_LOOP = 131, + ERR_MULTI_OUTLET = 133, + ERR_DUMMY_LINK = 134, + +// ... Node Errors + ERR_DIVIDER = 135, + ERR_DIVIDER_LINK = 136, + ERR_WEIR_DIVIDER = 137, + ERR_NODE_DEPTH = 138, + ERR_REGULATOR = 139, + ERR_STORAGE_VOLUME = 140, + ERR_OUTFALL = 141, + ERR_REGULATOR_SHAPE = 143, + ERR_NO_OUTLETS = 145, + +// ... RDII Errors + ERR_UNITHYD_TIMES = 151, + ERR_UNITHYD_RATIOS = 153, + ERR_RDII_AREA = 155, + +// ... Rain Gage Errors + ERR_RAIN_FILE_CONFLICT = 156, + ERR_RAIN_GAGE_FORMAT = 157, + ERR_RAIN_GAGE_TSERIES = 158, + ERR_RAIN_GAGE_INTERVAL = 159, + +// ... Treatment Function Error + ERR_CYCLIC_TREATMENT = 161, + +// ... Curve/Time Series Errors + ERR_CURVE_SEQUENCE = 171, + ERR_TIMESERIES_SEQUENCE = 173, + +// ... Snowmelt Errors + ERR_SNOWMELT_PARAMS = 181, + ERR_SNOWPACK_PARAMS = 182, + +// ... LID Errors + ERR_LID_TYPE = 183, + ERR_LID_LAYER = 184, + ERR_LID_PARAMS = 185, + ERR_LID_AREAS = 187, + ERR_LID_CAPTURE_AREA = 188, + +// ... Simulation Date/Time Errors + ERR_START_DATE = 191, + ERR_REPORT_DATE = 193, + ERR_REPORT_STEP = 195, + +// ... Input Parser Errors + ERR_INPUT = 200, + ERR_LINE_LENGTH = 201, + ERR_ITEMS = 203, + ERR_KEYWORD = 205, + ERR_DUP_NAME = 207, + ERR_NAME = 209, + ERR_NUMBER = 211, + ERR_DATETIME = 213, + ERR_RULE = 217, + ERR_TRANSECT_UNKNOWN = 219, + ERR_TRANSECT_SEQUENCE = 221, + ERR_TRANSECT_TOO_FEW = 223, + ERR_TRANSECT_TOO_MANY = 225, + ERR_TRANSECT_MANNING = 227, + ERR_TRANSECT_OVERBANK = 229, + ERR_TRANSECT_NO_DEPTH = 231, + ERR_MATH_EXPR = 233, + ERR_INFIL_PARAMS = 235, + +// ... File Name/Opening Errors + ERR_FILE_NAME = 301, + ERR_INP_FILE = 303, + ERR_RPT_FILE = 305, + ERR_OUT_FILE = 307, + ERR_OUT_SIZE = 308, + ERR_OUT_WRITE = 309, + ERR_OUT_READ = 311, + +// ... Rain File Errors + ERR_RAIN_FILE_SCRATCH = 313, + ERR_RAIN_FILE_OPEN = 315, + ERR_RAIN_FILE_DATA = 317, + ERR_RAIN_FILE_SEQUENCE = 318, + ERR_RAIN_FILE_FORMAT = 319, + ERR_RAIN_IFACE_FORMAT = 320, + ERR_RAIN_FILE_GAGE = 321, + +// ... Runoff File Errors + ERR_RUNOFF_FILE_OPEN = 323, + ERR_RUNOFF_FILE_FORMAT = 325, + ERR_RUNOFF_FILE_END = 327, + ERR_RUNOFF_FILE_READ = 329, + +// ... Hotstart File Errors + ERR_HOTSTART_FILE_OPEN = 331, + ERR_HOTSTART_FILE_FORMAT = 333, + ERR_HOTSTART_FILE_READ = 335, + +// ... Climate File Errors + ERR_NO_CLIMATE_FILE = 336, + ERR_CLIMATE_FILE_OPEN = 337, + ERR_CLIMATE_FILE_READ = 338, + ERR_CLIMATE_END_OF_FILE = 339, + +// ... RDII File Errors + ERR_RDII_FILE_SCRATCH = 341, + ERR_RDII_FILE_OPEN = 343, + ERR_RDII_FILE_FORMAT = 345, + +// ... Routing File Errors + ERR_ROUTING_FILE_OPEN = 351, + ERR_ROUTING_FILE_FORMAT = 353, + ERR_ROUTING_FILE_NOMATCH = 355, + ERR_ROUTING_FILE_NAMES = 357, + +// ... Time Series File Errors + ERR_TABLE_FILE_OPEN = 361, + ERR_TABLE_FILE_READ = 363, + +// ... Runtime Errors + ERR_SYSTEM = 500, + +// ... API Errors + ERR_API_NOT_OPEN = 501, + ERR_API_NOT_STARTED = 502, + ERR_API_NOT_ENDED = 503, + ERR_API_OBJECT_TYPE = 504, + ERR_API_OBJECT_INDEX = 505, + ERR_API_OBJECT_NAME = 506, + ERR_API_PROPERTY_TYPE = 507, + ERR_API_PROPERTY_VALUE = 508, + ERR_API_TIME_PERIOD = 509, + +// ... Additional Errors + MAXERRMSG = 1000 +}; + +char* error_getMsg(int i, char* msg); int error_setInpError(int errcode, char* s); - #endif //ERROR_H diff --git a/src/solver/error.txt b/src/solver/error.txt new file mode 100644 index 000000000..30d45a783 --- /dev/null +++ b/src/solver/error.txt @@ -0,0 +1,135 @@ +// SWMM 5.2 Error Messages + +ERR(101,"\n ERROR 101: memory allocation error.") +ERR(103,"\n ERROR 103: cannot solve KW equations for Link %s.") +ERR(105,"\n ERROR 105: cannot open ODE solver.") +ERR(107,"\n ERROR 107: cannot compute a valid time step.") + +ERR(108,"\n ERROR 108: ambiguous outlet ID name for Subcatchment %s.") +ERR(109,"\n ERROR 109: invalid parameter values for Aquifer %s.") +ERR(110,"\n ERROR 110: ground elevation is below water table for Subcatchment %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(114,"\n ERROR 114: invalid number of barrels for Conduit %s.") +ERR(115,"\n ERROR 115: adverse slope for Conduit %s.") +ERR(117,"\n ERROR 117: no cross section defined for Link %s.") +ERR(119,"\n ERROR 119: invalid cross section for Link %s.") +ERR(121,"\n ERROR 121: missing or invalid pump curve assigned to Pump %s.") +ERR(122,"\n ERROR 122: startup depth not higher than shutoff depth for Pump %s.") + +ERR(131,"\n ERROR 131: the following links form cyclic loops in the drainage system:") +ERR(133,"\n ERROR 133: Node %s has more than one outlet link.") +ERR(134,"\n ERROR 134: Node %s has illegal DUMMY link connections.") + +ERR(135,"\n ERROR 135: Divider %s does not have two outlet links.") +ERR(136,"\n ERROR 136: Divider %s has invalid diversion link.") +ERR(137,"\n ERROR 137: Weir Divider %s has invalid parameters.") +ERR(138,"\n ERROR 138: Node %s has initial depth greater than maximum depth.") +ERR(139,"\n ERROR 139: Regulator %s is the outlet of a non-storage node.") +ERR(140,"\n ERROR 140: Storage node %s has negative volume at full depth.") +ERR(141,"\n ERROR 141: Outfall %s has more than 1 inlet link or an outlet link.") +ERR(143,"\n ERROR 143: Regulator %s has invalid cross-section shape.") +ERR(145,"\n ERROR 145: Drainage system has no acceptable outlet nodes.") + +ERR(151,"\n ERROR 151: a Unit Hydrograph in set %s has invalid time base.") +ERR(153,"\n ERROR 153: a Unit Hydrograph in set %s has invalid response ratios.") +ERR(155,"\n ERROR 155: invalid sewer area for RDII at node %s.") + +ERR(156,"\n ERROR 156: ambiguous station ID for Rain Gage %s.") +ERR(157,"\n ERROR 157: inconsistent rainfall format for Rain Gage %s.") +ERR(158,"\n ERROR 158: time series for Rain Gage %s is also used by another object.") +ERR(159,"\n ERROR 159: recording interval greater than time series interval for Rain Gage %s.") + +ERR(161,"\n ERROR 161: cyclic dependency in treatment functions at node %s.") + +ERR(171,"\n ERROR 171: Curve %s has invalid or out of sequence data.") +ERR(173,"\n ERROR 173: Time Series %s has its data out of sequence.") + +ERR(181,"\n ERROR 181: invalid Snow Melt Climatology parameters.") +ERR(182,"\n ERROR 182: invalid parameters for Snow Pack %s.") + +ERR(183,"\n ERROR 183: no type specified for LID %s.") +ERR(184,"\n ERROR 184: missing layer for LID %s.") +ERR(185,"\n ERROR 185: invalid parameter value for LID %s.") +ERR(187,"\n ERROR 187: LID area exceeds total area for Subcatchment %s.") +ERR(188,"\n ERROR 188: LID capture area exceeds total impervious area for Subcatchment %s.") + +ERR(191,"\n ERROR 191: simulation start date comes after ending date.") +ERR(193,"\n ERROR 193: report start date comes after ending date.") +ERR(195,"\n ERROR 195: reporting time step or duration is less than routing time step.") + +ERR(200,"\n ERROR 200: one or more errors in input file.") +ERR(201,"\n ERROR 201: too many characters in input line ") +ERR(203,"\n ERROR 203: too few items ") +ERR(205,"\n ERROR 205: invalid keyword %s ") +ERR(207,"\n ERROR 207: duplicate ID name %s ") +ERR(209,"\n ERROR 209: undefined object %s ") +ERR(211,"\n ERROR 211: invalid number %s ") +ERR(213,"\n ERROR 213: invalid date/time %s ") +ERR(217,"\n ERROR 217: control rule clause invalid or out of sequence ") +ERR(219,"\n ERROR 219: data provided for unidentified transect ") +ERR(221,"\n ERROR 221: transect station 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.") +ERR(227,"\n ERROR 227: Transect %s has no Manning's N.") +ERR(229,"\n ERROR 229: Transect %s has invalid overbank locations.") +ERR(231,"\n ERROR 231: Transect %s has no depth.") +ERR(233,"\n ERROR 233: invalid math expression ") +ERR(235,"\n ERROR 235: invalid infiltration parameters ") + +ERR(301,"\n ERROR 301: files share same names.") +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(308,"\n ERROR 308: amount of output produced will exceed maximum file size.") + +ERR(309,"\n ERROR 309: error writing to binary results file.") +ERR(311,"\n ERROR 311: error reading from binary results file.") + +ERR(313,"\n ERROR 313: cannot open scratch rainfall interface file.") +ERR(315,"\n ERROR 315: cannot open rainfall interface file %s.") +ERR(317,"\n ERROR 317: cannot open rainfall data file %s.") +ERR(318,"\n ERROR 318: the following line is out of sequence in rainfall data file %s.") +ERR(319,"\n ERROR 319: unknown format for rainfall data file %s.") +ERR(320,"\n ERROR 320: invalid format for rainfall interface file.") +ERR(321,"\n ERROR 321: no data in rainfall interface file for gage %s.") + +ERR(323,"\n ERROR 323: cannot open runoff interface file %s.") +ERR(325,"\n ERROR 325: incompatible data found in runoff interface file.") +ERR(327,"\n ERROR 327: attempting to read beyond end of runoff interface file.") +ERR(329,"\n ERROR 329: error in reading from runoff interface file.") + +ERR(331,"\n ERROR 331: cannot open hot start interface file %s.") +ERR(333,"\n ERROR 333: incompatible data found in hot start interface file.") +ERR(335,"\n ERROR 335: error in reading from hot start interface file.") + +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.") +ERR(339,"\n ERROR 339: attempt to read beyond end of climate file %s.") + +ERR(341,"\n ERROR 341: cannot open scratch RDII interface file.") +ERR(343,"\n ERROR 343: cannot open RDII interface file %s.") +ERR(345,"\n ERROR 345: invalid format for RDII interface file.") + +ERR(351,"\n ERROR 351: cannot open routing interface file %s.") +ERR(353,"\n ERROR 353: invalid format for routing interface file %s.") +ERR(355,"\n ERROR 355: mis-matched names in routing interface file %s.") +ERR(357,"\n ERROR 357: inflows and outflows interface files have same name.") + +ERR(361,"\n ERROR 361: could not open external file used for Time Series %s.") +ERR(363,"\n ERROR 363: invalid data in external file used for Time Series %s.") + +// API Error Keys +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(504,"\n API Error 504: invalid object type.") +ERR(505,"\n API Error 505: invalid object index.") +ERR(506,"\n API Error 506: invalid object name.") +ERR(507,"\n API Error 507: invalid property type.") +ERR(508,"\n API Error 508: invalid property value.") +ERR(509,"\n API Error 509: invalid time period.") diff --git a/src/solver/exfil.c b/src/solver/exfil.c index 58ef6f5b2..f1cb2adf6 100644 --- a/src/solver/exfil.c +++ b/src/solver/exfil.c @@ -2,24 +2,22 @@ // exfil.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Storage unit exfiltration functions. // +// Update History +// ============== // Build 5.1.008: // - Monthly conductivity adjustment applied to exfiltration rate. -// // Build 5.1.010: // - New modified Green-Ampt infiltration option used. -// // Build 5.1.011: // - Fixed units conversion error for storage units with surface area curves. -// +// Build 5.2.0: +// - Support added for analytical storage shapes. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -92,48 +90,65 @@ void exfil_initState(int k) grnampt_initState(exfil->btmExfil); grnampt_initState(exfil->bankExfil); - // --- shape given by a Storage Curve - i = Storage[k].aCurve; - if ( i >= 0 ) + switch (Storage[k].shape) { - // --- get bottom area - aCurve = &Curve[i]; - Storage[k].exfil->btmArea = table_lookupEx(aCurve, 0.0); - - // --- find min/max bank depths and max. bank area - table_getFirstEntry(aCurve, &d, &a); + // --- shape given by a Storage Curve + case TABULAR: + i = Storage[k].aCurve; + exfil->btmArea = 0.0; exfil->bankMinDepth = 0.0; exfil->bankMaxDepth = 0.0; exfil->bankMaxArea = 0.0; - alast = a; - while ( table_getNextEntry(aCurve, &d, &a) ) + if ( i >= 0 ) { - if ( a < alast ) break; - else if ( a > alast ) + // --- get bottom area + aCurve = &Curve[i]; + Storage[k].exfil->btmArea = table_lookupEx(aCurve, 0.0); + + // --- find min/max bank depths and max. bank area + table_getFirstEntry(aCurve, &d, &a); + alast = a; + while ( table_getNextEntry(aCurve, &d, &a) ) { - exfil->bankMaxArea = a; - exfil->bankMaxDepth = d; + if ( a < alast ) break; + else if ( a > alast ) + { + exfil->bankMaxArea = a; + exfil->bankMaxDepth = d; + } + else if ( exfil->bankMaxArea == 0.0 ) + exfil->bankMinDepth = d; + else break; + alast = a; } - else if ( exfil->bankMaxArea == 0.0 ) exfil->bankMinDepth = d; - else break; - alast = a; - } - - // --- convert from user units to internal units - exfil->btmArea /= UCF(LENGTH) * UCF(LENGTH); - exfil->bankMaxArea /= UCF(LENGTH) * UCF(LENGTH); - exfil->bankMinDepth /= UCF(LENGTH); - exfil->bankMaxDepth /= UCF(LENGTH); - } - // --- functional storage shape curve - else - { - exfil->btmArea = Storage[k].aConst; - if ( Storage[k].aExpon == 0.0 ) exfil->btmArea +=Storage[k].aCoeff; - exfil->bankMinDepth = 0.0; - exfil->bankMaxDepth = BIG; - exfil->bankMaxArea = BIG; + // --- convert from user units to internal units + exfil->btmArea /= UCF(LENGTH) * UCF(LENGTH); + exfil->bankMaxArea /= UCF(LENGTH) * UCF(LENGTH); + exfil->bankMinDepth /= UCF(LENGTH); + exfil->bankMaxDepth /= UCF(LENGTH); + } + break; + + // --- functional storage shape curve + case FUNCTIONAL: + exfil->btmArea = Storage[k].a0; + if ( Storage[k].a2 == 0.0 ) + exfil->btmArea +=Storage[k].a1; + exfil->bankMinDepth = 0.0; + exfil->bankMaxDepth = BIG; + exfil->bankMaxArea = BIG; + break; + + // --- cylindrical, conical & prismatic shapes + case CYLINDRICAL: + case CONICAL: + case PYRAMIDAL: + exfil->btmArea = Storage[k].a0; + exfil->bankMinDepth = 0.0; + exfil->bankMaxDepth = BIG; + exfil->bankMaxArea = BIG; + break; } } } diff --git a/src/solver/exfil.h b/src/solver/exfil.h index 8315c08c4..8da4da302 100644 --- a/src/solver/exfil.h +++ b/src/solver/exfil.h @@ -2,9 +2,9 @@ // exfil.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 09/15/14 (Build 5.1.007) -// Author: L. Rossman (US EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // // Public interface for exfiltration functions. //----------------------------------------------------------------------------- @@ -12,7 +12,6 @@ #ifndef EXFIL_H #define EXFIL_H - //---------------------------- // EXFILTRATION OBJECT //---------------------------- @@ -33,5 +32,4 @@ int exfil_readStorageParams(int k, char* tok[], int ntoks, int n); void exfil_initState(int k); double exfil_getLoss(TExfil* exfil, double tStep, double depth, double area); - -#endif //EXFIL_H +#endif diff --git a/src/solver/findroot.h b/src/solver/findroot.h index c0ae10dff..3b54c6456 100644 --- a/src/solver/findroot.h +++ b/src/solver/findroot.h @@ -9,12 +9,10 @@ #ifndef FINDROOT_H #define FINDROOT_H - int findroot_Newton(double x1, double x2, double* rts, double xacc, void (*func) (double x, double* f, double* df, void* p), - void* p); + void* p); double findroot_Ridder(double x1, double x2, double xacc, - double (*func)(double, void* p), void* p); - + double (*func)(double, void* p), void* p); #endif //FINDROOT_H diff --git a/src/solver/flowrout.c b/src/solver/flowrout.c index 675e39300..d38b68488 100644 --- a/src/solver/flowrout.c +++ b/src/solver/flowrout.c @@ -2,39 +2,34 @@ // flowrout.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/19/14 (Build 5.1.001) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 03/14/17 (Build 5.1.012) -// 03/01/20 (Build 5.1.014) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // M. Tryby (EPA) // // Flow routing functions. // -// +// Update History +// ============== // Build 5.1.007: // - updateStorageState() modified in response to node outflow being // initialized with current evap & seepage losses in routing_execute(). -// // Build 5.1.008: // - Determination of node crown elevations moved to dynwave.c. // - Support added for new way of recording conduit's fullness state. -// // Build 5.1.012: // - Overflow computed in updateStorageState() must be non-negative. // - Terminal storage nodes now updated corectly. -// // Build 5.1.014: // - Arguments to function link_getLossRate changed. -// +// Build 5.2.0: +// - Correction made to updating state of terminal storage nodes. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE -#include "headers.h" #include #include +#include "headers.h" //----------------------------------------------------------------------------- // Constants @@ -135,7 +130,7 @@ double flowrout_getRoutingStep(int routingModel, double fixedStep) int flowrout_execute(int links[], int routingModel, double tStep) // -// Input: links = array of link indexes in topo-sorted order +// Input: links = array of link indexes in topo-sorted order (per routing model) // routingModel = type of routing method used // tStep = routing time step (sec) // Output: returns number of computational steps taken @@ -179,10 +174,11 @@ int flowrout_execute(int links[], int routingModel, double tStep) // --- retrieve inflow at upstream end of link qin = getLinkInflow(j, tStep); - // route flow through link + // --- route flow through link if ( routingModel == SF ) steps += steadyflow_execute(j, &qin, &qout, tStep); - else steps += kinwave_execute(j, &qin, &qout, tStep); + else + steps += kinwave_execute(j, &qin, &qout, tStep); Link[j].newFlow = qout; // adjust outflow at upstream node and inflow at downstream node @@ -641,15 +637,14 @@ void setNewNodeState(int j, double dt) // --- update terminal storage nodes if ( Node[j].type == STORAGE ) { - if ( Node[j].updated == FALSE ) - updateStorageState(j, Nobjects[LINK], NULL, dt); - return; + if ( Node[j].updated == FALSE ) + updateStorageState(j, Nobjects[LINK], NULL, dt); + return; } - // --- update stored volume using mid-point integration + // --- update stored volume newNetInflow = Node[j].inflow - Node[j].outflow - Node[j].losses; - Node[j].newVolume = Node[j].oldVolume + - 0.5 * (Node[j].oldNetInflow + newNetInflow) * dt; + Node[j].newVolume = Node[j].oldVolume + newNetInflow * dt; if ( Node[j].newVolume < FUDGE ) Node[j].newVolume = 0.0; // --- determine any overflow lost from system @@ -727,7 +722,7 @@ void updateNodeDepth(int i, double y) if ( Node[i].type == STORAGE ) return; // --- if non-outfall node is flooded, then use full depth - if ( Node[i].type != OUTFALL && + if ( Node[i].type != OUTFALL && Node[i].degree > 0 && Node[i].overflow > 0.0 ) y = Node[i].fullDepth; // --- if current new depth below y @@ -770,7 +765,7 @@ int steadyflow_execute(int j, double* qin, double* qout, double tStep) else { // --- adjust flow for evap and infil losses - q -= link_getLossRate(j, q); //(5.1.014) + q -= link_getLossRate(j, q); // --- flow can't exceed full flow if ( q > Link[j].qFull ) diff --git a/src/solver/forcmain.c b/src/solver/forcmain.c index b2c70fd75..a6314bbce 100644 --- a/src/solver/forcmain.c +++ b/src/solver/forcmain.c @@ -2,8 +2,8 @@ // forcemain.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Special Non-Manning Force Main functions diff --git a/src/solver/funcs.h b/src/solver/funcs.h index 09a2db4fc..aad84fc04 100644 --- a/src/solver/funcs.h +++ b/src/solver/funcs.h @@ -2,42 +2,42 @@ // funcs.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.000) -// 09/15/14 (Build 5.1.007) -// 04/02/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 05/10/18 (Build 5.1.013) -// 03/01/20 (Build 5.1.014) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // M. Tryby (EPA) // // Global interfacing functions. // +// Update History +// ============== // Build 5.1.007: // - climate_readAdjustments() added. -// // Build 5.1.008: // - Function list was re-ordered and blank lines added for readability. // - Pollutant buildup/washoff functions for the new surfqual.c module added. // - Several other functions added, re-named or have modified arguments. -// // Build 5.1.010: // - New roadway_getInflow() function added. -// // Build 5.1.013: // - Additional arguments added to function stats_updateSubcatchStats. -// // Build 5.1.014: // - Arguments to link_getLossRate function changed. -// +// Build 5.2.0: +// - Support added for Streets and Inlets. +// - Support added for reporting most frequent non-converging links. +// - Support added for named variables & math expressions in control rules. +// - Support added for tracking a gage's prior n-hour rainfall total. +// - Refactored external inflow code. //----------------------------------------------------------------------------- #ifndef FUNCS_H #define FUNCS_H - -void project_open(char *f1, char *f2, char *f3); +//----------------------------------------------------------------------------- +// Project Methods +//----------------------------------------------------------------------------- +void project_open(const char *f1, const char *f2, const char *f3); void project_close(void); void project_readInput(void); @@ -46,8 +46,7 @@ void project_validate(void); int project_init(void); int project_addObject(int type, char* id, int n); - -int project_findObject(int type, char* id); +int project_findObject(int type, const char* id); char* project_findID(int type, char* id); double** project_createMatrix(int nrows, int ncols); @@ -64,7 +63,7 @@ int input_readData(void); //----------------------------------------------------------------------------- int report_readOptions(char* tok[], int ntoks); -void report_writeLine(char* line); +void report_writeLine(const char* line); void report_writeSysTime(void); void report_writeLogo(void); void report_writeTitle(void); @@ -87,7 +86,9 @@ void report_writeQualError(TRoutingTotals* totals); void report_writeMaxStats(TMaxStats massBalErrs[], TMaxStats CourantCrit[], int nMaxStats); void report_writeMaxFlowTurns(TMaxStats flowTurns[], int nMaxStats); -void report_writeSysStats(TSysStats* sysStats); +void report_writeNonconvergedStats(TMaxStats maxNonconverged[], + int nMaxStats); +void report_writeTimeStepStats(TTimeStepStats* timeStepStats); void report_writeErrorMsg(int code, char* msg); void report_writeErrorCode(void); @@ -156,13 +157,12 @@ void routing_close(int routingModel); int output_open(void); void output_end(void); void output_close(void); -void output_checkFileSize(void); void output_saveResults(double reportTime); void output_updateAvgResults(void); -void output_readDateTime(int period, DateTime *aDate); -void output_readSubcatchResults(int period, int area); -void output_readNodeResults(int period, int node); -void output_readLinkResults(int period, int link); +void output_readDateTime(long period, DateTime *aDate); +void output_readSubcatchResults(long period, int index); +void output_readNodeResults(int long, int index); +void output_readLinkResults(int long, int index); //----------------------------------------------------------------------------- // Groundwater Methods @@ -257,6 +257,7 @@ void massbal_updateGwaterTotals(double vInfil, double vUpperEvap, double vLowerEvap, double vLowerPerc, double vGwater); void massbal_updateRoutingTotals(double tStep); + void massbal_initTimeStepTotals(void); void massbal_addInflowFlow(int type, double q); void massbal_addInflowQual(int type, int pollut, double w); @@ -279,16 +280,19 @@ void stats_close(void); void stats_report(void); void stats_updateCriticalTimeCount(int node, int link); -void stats_updateFlowStats(double tStep, DateTime aDate, int stepCount, - int steadyState); -void stats_updateSubcatchStats(int subcatch, double rainVol, +void stats_updateFlowStats(double tStep, DateTime aDate); +void stats_updateTimeStepStats(double tStep, int trialsCount, int steadyState); + +void stats_updateSubcatchStats(int subcatch, double rainVol, double runonVol, double evapVol, double infilVol, - double impervVol, double pervVol, double runoffVol, double runoff); //(5.1.013) + double impervVol, double pervVol, double runoffVol, double runoff); void stats_updateGwaterStats(int j, double infil, double evap, double latFlow, double deepFlow, double theta, double waterTable, double tStep); void stats_updateMaxRunoff(void); void stats_updateMaxNodeDepth(int node, double depth); +void stats_updateConvergenceStats(int node, int converged); + //----------------------------------------------------------------------------- // Raingage Methods @@ -300,6 +304,8 @@ void gage_setState(int gage, DateTime aDate); double gage_getPrecip(int gage, double *rainfall, double *snowfall); void gage_setReportRainfall(int gage, DateTime aDate); DateTime gage_getNextRainDate(int gage, DateTime aDate); +void gage_updatePastRain(int j, int tStep); +double gage_getPastRain(int gage, int hrs); //----------------------------------------------------------------------------- // Subcatchment Methods @@ -316,7 +322,7 @@ void subcatch_setOldState(int subcatch); double subcatch_getFracPerv(int subcatch); double subcatch_getStorage(int subcatch); double subcatch_getDepth(int subcatch); -double subcatch_getBuildup(int subcatch, int pollut); +double subcatch_getBuildup(int subcatch, int pollut); // (OWA addition) void subcatch_getRunon(int subcatch); void subcatch_addRunonFlow(int subcatch, double flow); @@ -341,7 +347,7 @@ int node_readParams(int node, int type, int subIndex, char* tok[], int ntoks void node_validate(int node); void node_initState(int node); -void node_initInflow(int node, double tStep); +void node_initFlows(int node, double tStep); void node_setOldHydState(int node); void node_setOldQualState(int node); void node_setOutletDepth(int node, double yNorm, double yCrit, double z); @@ -363,18 +369,16 @@ void node_getResults(int node, double wt, float x[]); int inflow_readExtInflow(char* tok[], int ntoks); int inflow_readDwfInflow(char* tok[], int ntoks); int inflow_readDwfPattern(char* tok[], int ntoks); -int inflow_setExtInflow(int j, int param, int type, - int tSeries, int basePat, double cf, - double baseline, double sf); +int inflow_setExtInflow(int j, int param, int type, int tSeries, + int basePat, double cf, double baseline, double sf); +// OWA Addition - additional function for validating inflows set by toolkit API int inflow_validate(int param, int type, int tSeries, int basePat, double *cf); - void inflow_initDwfInflow(TDwfInflow* inflow); void inflow_initDwfPattern(int pattern); double inflow_getExtInflow(TExtInflow* inflow, DateTime aDate); double inflow_getDwfInflow(TDwfInflow* inflow, int m, int d, int h); -double inflow_getPatternFactor(int p, int month, int day, int hour); void inflow_deleteExtInflows(int node); void inflow_deleteDwfInflows(int node); @@ -421,7 +425,7 @@ double link_getYnorm(int link, double q); double link_getVelocity(int link, double q, double y); double link_getFroude(int link, double v, double y); double link_getPower(int link); -double link_getLossRate(int link, double q); //(5.1.014) +double link_getLossRate(int link, double q); char link_getFullState(double a1, double a2, double aFull); void link_getResults(int link, double wt, float x[]); @@ -433,6 +437,7 @@ int xsect_isOpen(int type); int xsect_setParams(TXsect *xsect, int type, double p[], double ucf); void xsect_setIrregXsectParams(TXsect *xsect); void xsect_setCustomXsectParams(TXsect *xsect); +void xsect_setStreetXsectParams(TXsect *xsect); double xsect_getAmax(TXsect* xsect); double xsect_getSofA(TXsect* xsect, double area); @@ -466,6 +471,15 @@ int transect_create(int n); void transect_delete(void); int transect_readParams(int* count, char* tok[], int ntoks); void transect_validate(int j); +void transect_createStreetTransect(TStreet* street); + +//----------------------------------------------------------------------------- +// Street Cross-Section Methods +//----------------------------------------------------------------------------- +int street_create(int nStreets); +void street_delete(); +int street_readParams(char* tok[], int ntoks); +double street_getExtentFilled(int link); //----------------------------------------------------------------------------- // Custom Shape Cross-Section Methods @@ -477,6 +491,10 @@ int shape_validate(TShape *shape, TTable *curve); //----------------------------------------------------------------------------- int controls_create(int n); void controls_delete(void); +void controls_init(void); +void controls_addToCount(char* s); +int controls_addVariable(char* tok[], int ntoks); +int controls_addExpression(char* tok[], int ntoks); int controls_addRuleClause(int rule, int keyword, char* Tok[], int nTokens); int controls_evaluate(DateTime currentTime, DateTime elapsedTime, double tStep); @@ -502,8 +520,8 @@ double table_inverseLookup(TTable* table, double y); double table_getSlope(TTable *table, double x); double table_getMaxY(TTable *table, double x); -double table_getArea(TTable* table, double x); -double table_getInverseArea(TTable* table, double a); +double table_getStorageVolume(TTable* table, double x); +double table_getStorageDepth(TTable* table, double v); void table_tseriesInit(TTable *table); double table_tseriesLookup(TTable* table, double t, char extend); @@ -518,13 +536,15 @@ int getDouble(char *s, double *y); // get double from string char* getTempFileName(char *s); // get temporary file name int findmatch(char *s, char *keyword[]); // search for matching keyword int match(char *str, char *substr); // true if substr matches part of str -int strcomp(char *s1, char *s2); // case insensitive string compare -char* sstrncpy(char *dest, const char *src, - size_t maxlen); // safe string copy -void writecon(char *s); // writes string to console +int strcomp(const char *s1, const char *s2); // case insensitive string compare +size_t sstrncpy(char *dest, const char *src, + size_t n); // safe string copy +size_t sstrcat(char* dest, const char* src, + size_t destsize); // safe string concatenation +void writecon(const char *s); // writes string to console DateTime getDateTime(double elapsedMsec); // convert elapsed time to date void getElapsedTime(DateTime aDate, // convert elapsed date int* days, int* hrs, int* mins); - +char* addAbsolutePath(char *fname); // add full path to a file name #endif //FUNCS_H diff --git a/src/solver/gage.c b/src/solver/gage.c index b8677da1f..84e0af2da 100644 --- a/src/solver/gage.c +++ b/src/solver/gage.c @@ -2,20 +2,22 @@ // gage.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/10 (Build 5.1.001) -// 09/15/14 (Build 5.1.007) -// 05/10/18 (Build 5.1.013) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Rain gage functions. // +// Update History +// ============== // Build 5.1.007: // - Support for monthly rainfall adjustments added. -// // Build 5.1.013: // - Validation no longer performed on unused gages. -// +// Build 5.2.0: +// - Support added for tracking a gage's prior n-hour rainfall total. +// - Support added for relative file names. +// - Support added for setting rainfall through API call. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -37,6 +39,9 @@ const double OneSecond = 1.1574074e-5; // gage_setState (called by runoff_execute & getRainfall in rdii.c) // gage_getPrecip (called by subcatch_getRunoff) // gage_getNextRainDate (called by runoff_getTimeStep) +// gage_updatePastRain (called by runoff_execute) +// gage_getPastRain (called by getRainValue in controls.c) +// gage_setReportRainfall (called by output_saveSubcatchResults) //----------------------------------------------------------------------------- // Local functions @@ -46,7 +51,7 @@ static int readGageFileFormat(char* tok[], int ntoks, double x[]); static int getFirstRainfall(int gage); static int getNextRainfall(int gage); static double convertRainfall(int gage, double rain); - +static void initPastRain(int gage); //============================================================================= @@ -82,16 +87,16 @@ int gage_readParams(int j, char* tok[], int ntoks) x[4] = NO_DATE; // Default is no start/end date x[5] = NO_DATE; x[6] = 0.0; // US units - strcpy(fname, ""); - strcpy(staID, ""); + fname[0] = '\0'; + staID[0] = '\0'; if ( ntoks < 5 ) return error_setInpError(ERR_ITEMS, ""); k = findmatch(tok[4], GageDataWords); - if ( k == RAIN_TSERIES ) + if ( k == RAIN_TSERIES ) { err = readGageSeriesFormat(tok, ntoks, x); } - else if ( k == RAIN_FILE ) + else if ( k == RAIN_FILE ) { if ( ntoks < 8 ) return error_setInpError(ERR_ITEMS, ""); sstrncpy(fname, tok[5], MAXFNAME); @@ -112,7 +117,7 @@ int gage_readParams(int j, char* tok[], int ntoks) else Gage[j].dataSource = RAIN_FILE; if ( Gage[j].dataSource == RAIN_FILE ) { - sstrncpy(Gage[j].fname, fname, MAXFNAME); + sstrncpy(Gage[j].fname, addAbsolutePath(fname), MAXFNAME); sstrncpy(Gage[j].staID, staID, MAXMSG); Gage[j].startFileDate = x[4]; Gage[j].endFileDate = x[5]; @@ -154,7 +159,7 @@ int readGageSeriesFormat(char* tok[], int ntoks, double x[]) ts = project_findObject(TSERIES, tok[5]); if ( ts < 0 ) return error_setInpError(ERR_NAME, tok[5]); x[0] = (double)ts; - strcpy(tok[2], ""); + sstrncpy(tok[2], "", 0); return 0; } @@ -269,15 +274,16 @@ void gage_initState(int j) // Purpose: initializes state of rain gage. // { - // --- initialize actual and reported rainfall //(5.1.013) + // --- initialize actual and reported rainfall Gage[j].rainfall = 0.0; + Gage[j].apiRainfall = MISSING; Gage[j].reportRainfall = 0.0; - // --- rainfall api sets external rainfall rate + // --- OWA EDIT - toolkit api sets external rainfall rate Gage[j].externalRain = 0.0; if ( IgnoreRainfall ) return; // --- for gage with file data: - if ( Gage[j].dataSource == RAIN_FILE) + if ( Gage[j].dataSource == RAIN_FILE ) { // --- set current file position to start of period of record Gage[j].currentFilePos = Gage[j].startFilePos; @@ -311,6 +317,7 @@ void gage_initState(int j) else if ( !getNextRainfall(j) ) Gage[j].nextDate = NO_DATE; } else Gage[j].startDate = NO_DATE; + initPastRain(j); } //============================================================================= @@ -335,20 +342,29 @@ void gage_setState(int j, DateTime t) // --- use rainfall from co-gage (gage with lower index that uses // same rainfall time series or file) if it exists + // OWA EDIT - toolkit API overrides co-gage values if ( Gage[j].coGage >= 0 && Gage[j].dataSource != RAIN_API) { Gage[j].rainfall = Gage[Gage[j].coGage].rainfall; return; } + // --- use rainfall supplied by API function call + // (where constant ZERO (1.e-10) is used for 0 rainfall) + if (Gage[j].apiRainfall != MISSING) + { + Gage[j].rainfall = Gage[j].apiRainfall; + return; + } + + // OWA EDIT - toolkit API sets rainfall if (Gage[j].dataSource == RAIN_API) { getNextRainfall(j); Gage[j].rainfall = Gage[j].nextRainfall; return; } - else - { + // --- otherwise march through rainfall record until date t is bracketed t += OneSecond; for (;;) @@ -374,7 +390,7 @@ void gage_setState(int j, DateTime t) } // --- no rainfall if t >= interval end date & no next interval exists - if ( Gage[j].nextDate == NO_DATE) + if ( Gage[j].nextDate == NO_DATE ) { Gage[j].rainfall = 0.0; return; @@ -392,14 +408,92 @@ void gage_setState(int j, DateTime t) Gage[j].endDate = datetime_addSeconds(Gage[j].startDate, Gage[j].rainInterval); Gage[j].rainfall = Gage[j].nextRainfall; - if ( !getNextRainfall(j) ) Gage[j].nextDate = NO_DATE; + } +} + +//============================================================================= + +void initPastRain(int j) +{ + // --- initialize past hourly rain accumulation + int i; + for (i = 0; i <= MAXPASTRAIN; i++) + Gage[j].pastRain[i] = 0.0; + Gage[j].pastInterval = 0; +} + +//============================================================================= + +void gage_updatePastRain(int j, int tStep) +// +// Input: j = rain gage index +// tStep = current runoff time step (sec) +// Output: none +// Purpose: updates past MAXPASTRAIN hourly rain totals. +// +// Note: pastRain[0] is past rain volume prior to 1 hour, +// pastRain[n] is past rain volume after n hours, +// pastInterval is time since last hour was reached. +{ + int i, t; + double r; + + // --- current rainfall intensity (in/sec or mm/sec) + r = Gage[j].rainfall / 3600.; + + // --- process each hourly interval of current time step + while (tStep > 0) + { + // --- time for most recent rainfall interval to reach 1 hr + t = 3600 - Gage[j].pastInterval; + + // --- remaining time step is greater than this time + if (tStep > t) + { + // --- add current rain to most recent interval + Gage[j].pastRain[0] += t * r; + + // --- shift all prior hourly rain amounts by 1 hour + for (i = MAXPASTRAIN; i > 0; i-- ) + Gage[j].pastRain[i] = Gage[j].pastRain[i-1]; + + // --- begin a new most recent interval + Gage[j].pastInterval = 0; + Gage[j].pastRain[0] = 0.0; + tStep -= t; + } + // --- time to reach 1 hr in most recent interval is greater + // than remaining time step so update most recent interval + else + { + Gage[j].pastRain[0] += tStep * r; + Gage[j].pastInterval += tStep; + tStep = 0; } } } //============================================================================= +double gage_getPastRain(int j, int n) +// +// Input: j = rain gage index +// n = number of hours prior to current date +// Output: cumulative rain volume (inches or mm) in last n hours +// Purpose: retrieves rainfall total over some previous number of hours. +// +{ + int i; + double result = 0.0; + if (n < 1 || n > MAXPASTRAIN) return 0.0; + for (i = 1; i <= n; i++) + result += Gage[j].pastRain[i]; + return result; +} + +//============================================================================= + DateTime gage_getNextRainDate(int j, DateTime aDate) // // Input: j = rain gage index @@ -455,6 +549,13 @@ void gage_setReportRainfall(int j, DateTime reportDate) return; } + // --- rainfall set by API call + if (Gage[j].apiRainfall != MISSING) + { + Gage[j].reportRainfall = Gage[j].apiRainfall; + return; + } + // --- otherwise increase reporting time by 1 second to avoid // roundoff problems reportDate += OneSecond; @@ -492,7 +593,7 @@ int getFirstRainfall(int j) // --- initialize internal cumulative rainfall value Gage[j].rainAccum = 0; - // --- use rainfall API if provided + // --- OWA EDIT - use toolkit API rainfall if provided if (Gage[j].dataSource == RAIN_API) { Gage[j].rainfall = Gage[j].externalRain; @@ -555,6 +656,8 @@ int getNextRainfall(int j) double rNext; // next rain intensity (in/hr or mm/hr) Gage[j].nextRainfall = 0.0; + + // OWA EDIT - if using toolkit API to set rainfall if (Gage[j].dataSource == RAIN_API) { rNext = Gage[j].externalRain; @@ -576,7 +679,7 @@ int getNextRainfall(int j) else return 0; } - else if (Gage[j].dataSource == RAIN_TSERIES) + else { k = Gage[j].tSeries; if ( k >= 0 ) @@ -587,8 +690,6 @@ int getNextRainfall(int j) } else return 0; } - - } while (rNext == 0.0); } Gage[j].nextRainfall = rNext; @@ -618,7 +719,7 @@ double convertRainfall(int j, double r) case CUMULATIVE_RAINFALL: if ( r < Gage[j].rainAccum ) - r1 = r / Gage[j].rainInterval * 3600.0; + r1 = r / Gage[j].rainInterval * 3600.0; else r1 = (r - Gage[j].rainAccum) / Gage[j].rainInterval * 3600.0; Gage[j].rainAccum = r; break; diff --git a/src/solver/globals.h b/src/solver/globals.h index 93c11f2d7..193903697 100644 --- a/src/solver/globals.h +++ b/src/solver/globals.h @@ -2,43 +2,34 @@ // globals.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/19/14 (Build 5.1.000) -// 04/14/14 (Build 5.1.004) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 08/01/16 (Build 5.1.011) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// 04/01/20 (Build 5.1.015) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Global Variables // +// Update History +// ============== // Build 5.1.004: // - Ignore RDII option added. -// // Build 5.1.007: // - Monthly climate variable adjustments added. -// // Build 5.1.008: // - Number of parallel threads for dynamic wave routing added. // - Minimum dynamic wave routing variable time step added. -// // Build 5.1.011: // - Changed WarningCode to Warnings (# warnings issued) // - Added error message text as a variable. // - Added elapsed simulation time (in decimal days) variable. // - Added variables associated with detailed routing events. -// // Build 5.1.012: // - InSteadyState variable made local to routing_execute in routing.c. -// // Build 5.1.013: // - CrownCutoff and RuleStep added as analysis option variables. -// // Build 5.1.015: // - Fixes bug in summary statistics when Report Start date > Start Date. +// Build 5.2.0: +// - Support for relative file names added. //----------------------------------------------------------------------------- #ifndef GLOBALS_H @@ -60,15 +51,16 @@ EXTERN TFile EXTERN long Nperiods, // Number of reporting periods - TotalStepCount, // Total routing steps used //(5.1.015) - ReportStepCount, // Reporting routing steps used //(5.1.015) + TotalStepCount, // Total routing steps used + ReportStepCount, // Reporting routing steps used NonConvergeCount; // Number of non-converging steps EXTERN char Msg[MAXMSG+1], // Text of output message ErrorMsg[MAXMSG+1], // Text of error message Title[MAXTITLE][MAXMSG+1],// Project title - TempDir[MAXFNAME+1]; // Temporary file directory + TempDir[MAXFNAME+1], // Temporary file directory + InpDir[MAXFNAME+1]; // Input file directory EXTERN TRptFlags RptFlags; // Reporting options @@ -83,7 +75,7 @@ EXTERN int RouteModel, // Flow routing method ForceMainEqn, // Flow equation for force mains LinkOffsets, // Link offset convention - SurchargeMethod, // EXTRAN or SLOT method //(5.1.013) + SurchargeMethod, // EXTRAN or SLOT method AllowPonding, // Allow water to pond at nodes InertDamping, // Degree of inertial damping NormalFlowLtd, // Normal flow limited @@ -101,14 +93,13 @@ EXTERN int WetStep, // Runoff wet time step (sec) DryStep, // Runoff dry time step (sec) ReportStep, // Reporting time step (sec) - RuleStep, // Rule evaluation time step (sec) //(5.1.013) + RuleStep, // Rule evaluation time step (sec) SweepStart, // Day of year when sweeping starts SweepEnd, // Day of year when sweeping ends MaxTrials, // Max. trials for DW routing NumThreads, // Number of parallel threads used - NumEvents, // Number of detailed events - ExtPollutFlag; // Enable external pollutant injection - //InSteadyState; // System flows remain constant + ExtPollutFlag, // OWA EDIT - toolkit API for set external pollutant injection + NumEvents; // Number of detailed events EXTERN double RouteStep, // Routing time step (sec) @@ -125,7 +116,7 @@ EXTERN double HeadTol, // DW routing head tolerance (ft) SysFlowTol, // Tolerance for steady system flow LatFlowTol, // Tolerance for steady nodal inflow - CrownCutoff; // Fractional pipe crown cutoff //(5.1.013) + CrownCutoff; // Fractional pipe crown cutoff EXTERN DateTime StartDate, // Starting date @@ -174,6 +165,7 @@ EXTERN TPattern* Pattern; // Array of time patterns EXTERN TTable* Curve; // Array of curve tables EXTERN TTable* Tseries; // Array of time series tables EXTERN TTransect* Transect; // Array of transect data +EXTERN TStreet* Street; // Array of defined Street cross-sections EXTERN TShape* Shape; // Array of custom conduit shapes EXTERN TEvent* Event; // Array of routing events diff --git a/src/solver/gwater.c b/src/solver/gwater.c index 8de1ba85e..48db9de17 100644 --- a/src/solver/gwater.c +++ b/src/solver/gwater.c @@ -2,28 +2,24 @@ // gwater.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/19/14 (Build 5.1.000) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Groundwater functions. // +// Update History +// ============== // Build 5.1.007: // - User-supplied function for deep GW seepage flow added. // - New variable names for use in user-supplied GW flow equations added. -// // Build 5.1.008: // - More variable names for user-supplied GW flow equations added. // - Subcatchment area made into a shared variable. // - Evaporation loss initialized to 0. // - Support for collecting GW statistics added. -// // Build 5.1.010: // - Unsaturated hydraulic conductivity added to GW flow equation variables. -// //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -291,11 +287,11 @@ int gwater_readFlowExpression(char* tok[], int ntoks) else return error_setInpError(ERR_KEYWORD, tok[1]); // --- concatenate remaining tokens into a single string - strcpy(exprStr, tok[2]); + sstrncpy(exprStr, tok[2], MAXLINE); for ( i = 3; i < ntoks; i++) { - strcat(exprStr, " "); - strcat(exprStr, tok[i]); + sstrcat(exprStr, " ", MAXLINE+1); + sstrcat(exprStr, tok[i], MAXLINE+1); } // --- delete any previous flow eqn. @@ -306,7 +302,7 @@ int gwater_readFlowExpression(char* tok[], int ntoks) // (getVariableIndex is the function that converts a GW // variable's name into an index number) expr = mathexpr_create(exprStr, getVariableIndex); - if ( expr == NULL ) return error_setInpError(ERR_TREATMENT_EXPR, ""); + if ( expr == NULL ) return error_setInpError(ERR_MATH_EXPR, ""); // --- save expression tree with the subcatchment if ( k == 1 ) Subcatch[j].gwLatFlowExpr = expr; diff --git a/src/solver/hash.c b/src/solver/hash.c index 4915733cb..4a73e6ccd 100644 --- a/src/solver/hash.c +++ b/src/solver/hash.c @@ -21,7 +21,7 @@ #define UCHAR(x) (((x) >= 'a' && (x) <= 'z') ? ((x)&~32) : (x)) /* Case-insensitive comparison of strings s1 and s2 */ -int samestr(char *s1, char *s2) +int samestr(const char *s1, const char *s2) { int i; for (i=0; UCHAR(s1[i]) == UCHAR(s2[i]); i++) @@ -30,7 +30,7 @@ int samestr(char *s1, char *s2) } /* End of samestr */ /* Use Fletcher's checksum to compute 2-byte hash of string */ -unsigned int hash(char *str) +unsigned int hash(const char *str) { unsigned int sum1= 0, check1; unsigned long sum2= 0L; @@ -70,7 +70,7 @@ int HTinsert(HTtable *ht, char *key, int data) return(1); } -int HTfind(HTtable *ht, char *key) +int HTfind(HTtable *ht, const char *key) { unsigned int i = hash(key); struct HTentry *entry; @@ -84,7 +84,7 @@ int HTfind(HTtable *ht, char *key) return(NOTFOUND); } -char *HTfindKey(HTtable *ht, char *key) +char *HTfindKey(HTtable *ht, const char *key) { unsigned int i = hash(key); struct HTentry *entry; diff --git a/src/solver/hash.h b/src/solver/hash.h index 90ac1ca22..ba42be00e 100644 --- a/src/solver/hash.h +++ b/src/solver/hash.h @@ -20,11 +20,11 @@ struct HTentry typedef struct HTentry *HTtable; -HTtable *HTcreate(void); -int HTinsert(HTtable *, char *, int); -int HTfind(HTtable *, char *); -char *HTfindKey(HTtable *, char *); -void HTfree(HTtable *); +HTtable* HTcreate(void); +int HTinsert(HTtable *, char *, int); +int HTfind(HTtable *, const char *); +char* HTfindKey(HTtable *, const char *); +void HTfree(HTtable *); #endif //HASH_H diff --git a/src/solver/headers.h b/src/solver/headers.h index 016f5445c..87139b060 100644 --- a/src/solver/headers.h +++ b/src/solver/headers.h @@ -2,25 +2,19 @@ // headers.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Header files included in most SWMM5 modules. // // DO NOT CHANGE THE ORDER OF THE #INCLUDE STATEMENTS //----------------------------------------------------------------------------- - -#include -#include "consts.h" #include "macros.h" -#include "enums.h" -#include "error.h" -#include "datetime.h" #include "objects.h" +#define EXTERN extern +#include "globals.h" #include "funcs.h" +#include "error.h" #include "text.h" #include "keywords.h" -#define EXTERN extern -#include "globals.h" -#include "lid.h" \ No newline at end of file diff --git a/src/solver/hotstart.c b/src/solver/hotstart.c index 33c005e45..69f7abcc9 100644 --- a/src/solver/hotstart.c +++ b/src/solver/hotstart.c @@ -2,17 +2,12 @@ // hotstart.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 03/28/14 (Build 5.1.002) -// 04/23/14 (Build 5.1.005) -// 03/19/15 (Build 5.1.008) -// 08/01/16 (Build 5.1.011) -// 04/01/20 (Build 5.1.015) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // // Hot Start file functions. - +// // A SWMM hot start file contains the state of a SWMM project after // a simulation has been run, allowing it to be used to initialize // a subsequent simulation that picks up where the previous run ended. @@ -29,15 +24,15 @@ // insure that these components are of the same sub-type and maintain // the same order as when the hot start file was created. // +// Update History +// ============== // Build 5.1.008: // - Storage node hydraulic residence time (HRT) was added to the file. // - Link control settings are now applied when reading a hot start file. // - Runoff read from file assigned to newRunoff property instead of oldRunoff. // - Array indexing bug when reading snowpack state from file fixed. -// // Build 5.1.011: // - Link control setting bug when reading a hot start file fixed. -// // Build 5.1.015: // - Support added for multiple infiltration methods within a project. //----------------------------------------------------------------------------- @@ -365,13 +360,10 @@ void saveRunoff(void) // Purpose: saves current state of all subcatchments to hotstart file. // { - int i, j, k, sizeX; - double* x; + int i, j, k; + double x[6]; FILE* f = Fhotstart2.file; - sizeX = MAX(6, Nobjects[POLLUT]+1); - x = (double *) calloc(sizeX, sizeof(double)); - for (i = 0; i < Nobjects[SUBCATCH]; i++) { // Ponded depths for each sub-area & total runoff (4 elements) @@ -380,8 +372,8 @@ void saveRunoff(void) fwrite(x, sizeof(double), 4, f); // Infiltration state (max. of 6 elements) - for (j=0; j 0 ) { // Runoff quality - for (j=0; j 0 ) @@ -479,22 +485,21 @@ int getIfaceFilePolluts() } // --- read pollutant names & units - if ( NumIfacePolluts > 0 ) + if ( NumIfacePolluts > 0 && Nobjects[POLLUT] > 0 ) { // --- check each pollutant name on file with project's pollutants for (i=0; i 0 ) - { - j = project_findObject(POLLUT, s1); - if ( j < 0 ) continue; - if ( !strcomp(s2, QualUnitsWords[Pollut[j].units]) ) - return ERR_ROUTING_FILE_NOMATCH; - IfacePolluts[j] = i; - } + if ( sscanf(line, "%s %s", s1, s2) < 2 ) + return ERR_ROUTING_FILE_FORMAT; + j = project_findObject(POLLUT, s1); + if ( j < 0 ) continue; + if ( !strcomp(s2, QualUnitsWords[Pollut[j].units]) ) + return ERR_ROUTING_FILE_NOMATCH; + IfacePolluts[j] = i; } } return 0; @@ -515,8 +520,8 @@ int getIfaceFileNodes() // --- read number of interface nodes fgets(line, MAXLINE, Finflows.file); - sscanf(line, "%d", &NumIfaceNodes); - if ( NumIfaceNodes <= 0 ) return ERR_ROUTING_FILE_FORMAT; + if ( !sscanf(line, "%d", &NumIfaceNodes) || NumIfaceNodes <= 0 ) + return ERR_ROUTING_FILE_FORMAT; // --- allocate memory for interface nodes index array IfaceNodes = (int *) calloc(NumIfaceNodes, sizeof(int)); @@ -525,9 +530,12 @@ int getIfaceFileNodes() // --- read names of interface nodes from file & save their indexes for ( i=0; if0 * InfilFactor; //(5.1.013) - double fmin = infil->fmin * InfilFactor; //(5.1.013) + double f0 = infil->f0 * InfilFactor; + double fmin = infil->fmin * InfilFactor; double Fmax = infil->Fmax; double tp = infil->tp; double df = f0 - fmin; @@ -515,8 +508,8 @@ double modHorton_getInfil(THorton *infil, double tstep, double irate, // --- assign local variables double f = 0.0; double fp, fa; - double f0 = infil->f0 * InfilFactor; //(5.1.013) - double fmin = infil->fmin * InfilFactor; //(5.1.013) + double f0 = infil->f0 * InfilFactor; + double fmin = infil->fmin * InfilFactor; double df = f0 - fmin; double kd = infil->decay; double kr = infil->regen * Evap.recoveryFactor; @@ -588,7 +581,7 @@ int grnampt_setParams(TGrnAmpt *infil, double p[]) { double ksat; // sat. hyd. conductivity in in/hr - if ( p[0] < 0.0 || p[1] <= 0.0 || p[2] < 0.0 ) return FALSE; + if ( p[0] < 0.0 || p[1] <= 0.0 || p[2] < 0.0 || p[2] > 1.0) return FALSE; infil->S = p[0] / UCF(RAINDEPTH); // Capillary suction head (ft) infil->Ks = p[1] / UCF(RAINFALL); // Sat. hyd. conductivity (ft/sec) infil->IMDmax = p[2]; // Max. init. moisture deficit @@ -652,7 +645,7 @@ double grnampt_getInfil(TGrnAmpt *infil, double tstep, double irate, // { // --- find saturated upper soil zone water volume - Fumax = infil->IMDmax * infil->Lu * sqrt(InfilFactor); //(5.1.013) + Fumax = infil->IMDmax * infil->Lu * sqrt(InfilFactor); // --- reduce time until next event infil->T -= tstep; @@ -680,8 +673,8 @@ double grnampt_getUnsatInfil(TGrnAmpt *infil, double tstep, double irate, // { double ia, c1, F2, dF, Fs, kr, ts; - double ks = infil->Ks * InfilFactor; //(5.1.013) - double lu = infil->Lu * sqrt(InfilFactor); //(5.1.013) + double ks = infil->Ks * InfilFactor; + double lu = infil->Lu * sqrt(InfilFactor); // --- get available infiltration rate (rainfall + ponded water) ia = irate + depth / tstep; @@ -786,8 +779,8 @@ double grnampt_getSatInfil(TGrnAmpt *infil, double tstep, double irate, // { double ia, c1, dF, F2; - double ks = infil->Ks * InfilFactor; //(5.1.013) - double lu = infil->Lu * sqrt(InfilFactor); //(5.1.013) + double ks = infil->Ks * InfilFactor; + double lu = infil->Lu * sqrt(InfilFactor); // --- get available infiltration rate (rainfall + ponded water) ia = irate + depth / tstep; diff --git a/src/solver/infil.h b/src/solver/infil.h index c4923e88d..f5ddf31aa 100644 --- a/src/solver/infil.h +++ b/src/solver/infil.h @@ -2,22 +2,18 @@ // infil.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 09/15/14 (Build 5.1.007) -// 08/05/15 (Build 5.1.010) -// 05/10/18 (Build 5.1.013) -// 04/01/20 (Build 5.1.015) -// Author: L. Rossman (US EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // // Public interface for infiltration functions. // +// Update History +// ============== // Build 5.1.010: // - New Modified Green Ampt infiltration option added. -// // Build 5.1.013: // - New function infil_setInfilFactor() added. -// // Build 5.1.015: // - Support added for multiple infiltration methods within a project. //----------------------------------------------------------------------------- @@ -25,7 +21,6 @@ #ifndef INFIL_H #define INFIL_H - //--------------------- // Enumerated Constants //--------------------- @@ -114,5 +109,4 @@ void grnampt_initState(TGrnAmpt *infil); double grnampt_getInfil(TGrnAmpt *infil, double tstep, double irate, double depth, int modelType); - -#endif //INFIL_H +#endif diff --git a/src/solver/inflow.c b/src/solver/inflow.c index 42976c13e..cdb36590b 100644 --- a/src/solver/inflow.c +++ b/src/solver/inflow.c @@ -2,12 +2,17 @@ // inflow.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Manages any Direct External or Dry Weather Flow inflows // that have been assigned to nodes of the drainage system. +// +// Update History +// ============== +// Build 5.2.0: +// - Removed references to unused extIfaceInflow member of ExtInflow struct. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -24,10 +29,14 @@ // inflow_deleteExtInflows (called by deleteObjects in project.c) // inflow_deleteDwfInflows (called by deleteObjects in project.c) // inflow_getExtInflow (called by addExternalInflows in routing.c) +// inflow_setExtInflow (called by setNodeInflow in swmm5.c) // inflow_getDwfInflow (called by addDryWeatherInflows in routing.c) -// inflow_getPatternFactor -//============================================================================= +//----------------------------------------------------------------------------- +// Local Functions +//----------------------------------------------------------------------------- +double getPatternFactor(int p, int month, int day, int hour); + int inflow_readExtInflow(char* tok[], int ntoks) // @@ -75,6 +84,7 @@ int inflow_readExtInflow(char* tok[], int ntoks) if (param == -1) { type = FLOW_INFLOW; + cf = 1.0/UCF(FLOW); } // --- do the same for a pollutant inflow @@ -116,10 +126,16 @@ int inflow_readExtInflow(char* tok[], int ntoks) if ( basePat < 0 ) return error_setInpError(ERR_NAME, tok[7]); } + // --- include LperFT3 term in conversion factor for MASS_INFLOW + if ( type == MASS_INFLOW ) cf /= LperFT3; + return(inflow_setExtInflow(j, param, type, tseries, basePat, cf, baseline, sf)); } +// OWA Addition ############################################################### +// Inflow validation function for OWA toolkit API use + int inflow_validate(int param, int type, int tseries, int basePat, double *cf) // // Purpose: Validates Inflow @@ -134,7 +150,7 @@ int inflow_validate(int param, int type, int tseries, int basePat, double *cf) // Validate param if (param >= Nobjects[POLLUT]) { - errcode = ERR_API_POLLUT_INDEX; + errcode = ERR_TKAPI_POLLUT_INDEX; } // Validate Type else if (type != FLOW_INFLOW @@ -146,12 +162,12 @@ int inflow_validate(int param, int type, int tseries, int basePat, double *cf) // Validate Timeseries Index else if (tseries >= Nobjects[TSERIES]) { - errcode = ERR_API_TSERIES_INDEX; + errcode = ERR_TKAPI_TSERIES_INDEX; } // Validate Timepattern Index else if (basePat >= Nobjects[TIMEPATTERN]) { - errcode = ERR_API_PATTERN_INDEX; + errcode = ERR_TKAPI_PATTERN_INDEX; } else { @@ -169,7 +185,8 @@ int inflow_validate(int param, int type, int tseries, int basePat, double *cf) return(errcode); } - +// ############################################################################ +//============================================================================= int inflow_setExtInflow(int j, int param, int type, int tseries, int basePat, double cf, double baseline, double sf) @@ -185,14 +202,18 @@ int inflow_setExtInflow(int j, int param, int type, int tseries, int basePat, // Return: returns Error Code { + // OWA EDIT ################################################################ + // OWA SWMM addition for inflow_setExtInflow to return error if + // inflow does not validate. EPA SWMM always returns error code 0 int errcode = 0; TExtInflow* inflow; // external inflow object - // Validate Inflow + // Validate Inflow for OWA toolkit API use errcode = inflow_validate(param, type, tseries, basePat, &cf); if (errcode == 0) { + // ########################################################################## // --- check if an external inflow object for this constituent already exists inflow = Node[j].extInflow; @@ -214,7 +235,7 @@ int inflow_setExtInflow(int j, int param, int type, int tseries, int basePat, Node[j].extInflow = inflow; } - // Assigning Values to the inflow object + // --- assign property values to the inflow object inflow->param = param; inflow->type = type; inflow->tSeries = tseries; @@ -222,7 +243,7 @@ int inflow_setExtInflow(int j, int param, int type, int tseries, int basePat, inflow->sFactor = sf; inflow->baseline = baseline; inflow->basePat = basePat; - inflow->extIfaceInflow = 0.0; + inflow->extIfaceInflow = 0.0; // OWA addition - inflow struct prop for holding external inflow set by OWA toolkit API } return(errcode); } @@ -265,18 +286,22 @@ double inflow_getExtInflow(TExtInflow* inflow, DateTime aDate) double sf = inflow->sFactor; // scaling factor double blv = inflow->baseline; // baseline value double tsv = 0.0; // time series value - double extIfaceInflow = inflow->extIfaceInflow;// external interfacing inflow + double extIfaceInflow = inflow->extIfaceInflow; // OWA Addition - for toolkit API external interfacing inflow if ( p >= 0 ) { month = datetime_monthOfYear(aDate) - 1; day = datetime_dayOfWeek(aDate) - 1; hour = datetime_hourOfDay(aDate); - blv *= inflow_getPatternFactor(p, month, day, hour); + blv *= getPatternFactor(p, month, day, hour); } if ( k >= 0 ) tsv = table_tseriesLookup(&Tseries[k], aDate, FALSE) * sf; + // OWA Edit ############################################################# + // EPA removed extIfaceInflow usage in SWMM 5.2.0. + // OWA keeps it to use with toolkit API in addition to new apiExtInflow + // used by EPA API return cf * (tsv + blv) + cf * extIfaceInflow; -} +} // ###################################################################### //============================================================================= @@ -418,19 +443,19 @@ double inflow_getDwfInflow(TDwfInflow* inflow, int month, int day, int hour) double f = 1.0; // pattern factor p1 = inflow->patterns[MONTHLY_PATTERN]; - if ( p1 >= 0 ) f *= inflow_getPatternFactor(p1, month, day, hour); + if ( p1 >= 0 ) f *= getPatternFactor(p1, month, day, hour); p1 = inflow->patterns[DAILY_PATTERN]; - if ( p1 >= 0 ) f *= inflow_getPatternFactor(p1, month, day, hour); + if ( p1 >= 0 ) f *= getPatternFactor(p1, month, day, hour); p1 = inflow->patterns[HOURLY_PATTERN]; p2 = inflow->patterns[WEEKEND_PATTERN]; if ( p2 >= 0 ) { if ( day == 0 || day == 6 ) - f *= inflow_getPatternFactor(p2, month, day, hour); + f *= getPatternFactor(p2, month, day, hour); else if ( p1 >= 0 ) - f *= inflow_getPatternFactor(p1, month, day, hour); + f *= getPatternFactor(p1, month, day, hour); } - else if ( p1 >= 0 ) f *= inflow_getPatternFactor(p1, month, day, hour); + else if ( p1 >= 0 ) f *= getPatternFactor(p1, month, day, hour); return f * inflow->avgValue; } @@ -499,7 +524,7 @@ int inflow_readDwfPattern(char* tok[], int ntoks) //============================================================================= -double inflow_getPatternFactor(int p, int month, int day, int hour) +double getPatternFactor(int p, int month, int day, int hour) // // Input: p = time pattern index // month = current month of year of simulation @@ -528,5 +553,3 @@ double inflow_getPatternFactor(int p, int month, int day, int hour) } return 1.0; } - -//============================================================================= \ No newline at end of file diff --git a/src/solver/inlet.c b/src/solver/inlet.c new file mode 100644 index 000000000..2a09642d3 --- /dev/null +++ b/src/solver/inlet.c @@ -0,0 +1,1945 @@ +//----------------------------------------------------------------------------- +// inlet.c +// +// Project: EPA SWMM5 +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman +// +// Street/Channel Inlet Functions +// +// Computes capture efficiency of inlets placed in Street conduits +// or Rectangular/Trapezoidal channels using FHWA HEC-22 methods (see +// Brown, S.A. et al., Urban Drainage Design Manual, Federal Highway +// Administration Hydraulic Engineering Circular No. 22, 3rd Edition, +// FHWA-NHI-10-009, August 2013). +// +//----------------------------------------------------------------------------- +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include +#include "headers.h" + +// Grate inlet +typedef struct +{ + int type; // type of grate used + double length; // length (parallel to flow) (ft) + double width; // width (perpendicular to flow) (ft) + double fracOpenArea; // fraction of grate area that is open + double splashVeloc; // splash-over velocity (ft/s) +} TGrateInlet; + +// Slotted drain inlet +typedef struct +{ + double length; // length (parallel to flow) (ft) + double width; // width (perpendicular to flow) (ft) +} TSlottedInlet; + +// Curb opening inlet +typedef struct +{ + double length; // length of curb opening (ft) + double height; // height of curb opening (ft) + int throatAngle; // type of throat angle +} TCurbInlet; + +// Custom inlet +typedef struct +{ + int onGradeCurve; // flow diversion curve index + int onSagCurve; // flow rating curve index +} TCustomInlet; + +// Inlet design object +typedef struct +{ + char * ID; // name assigned to inlet design + int type; // type of inlet used (grate, curb, etc) + TGrateInlet grateInlet; // length = 0 if not used + TSlottedInlet slottedInlet; // length = 0 if not used + TCurbInlet curbInlet; // length = 0 if not used + int customCurve; // curve index = -1 if not used +} TInletDesign; + + +// Inlet performance statistics +typedef struct +{ + int flowPeriods; // # periods with approach flow + int capturePeriods; // # periods with captured flow + int backflowPeriods; // # periods with backflow + double peakFlow; // peak flow seen by inlet (cfs) + double peakFlowCapture; // capture efficiency at peak flow + double avgFlowCapture; // average capture efficiency + double bypassFreq; // frequency of bypass flow +} TInletStats; + +// Inlet list object +struct TInlet +{ + int linkIndex; // index of conduit link with the inlet + int designIndex; // index of inlet's design + int nodeIndex; // index of node receiving captured flow + int numInlets; // # inlets on each side of street or in channel + int placement; // whether inlet is on-grade or on-sag + double clogFactor; // fractional degree of inlet clogging + double flowLimit; // inlet flow restriction (cfs) + double localDepress; // local gutter depression (ft) + double localWidth; // local depression width (ft) + + double flowFactor; // flow = flowFactor * (flow spread)^2.67 + double flowCapture; // captured flow rate (cfs) + double backflow; // backflow from capture node (cfs) + double backflowRatio; // inlet backflow / capture node overflow + TInletStats stats; // inlet performance statistics + TInlet * nextInlet; // next inlet in list +}; + +// Shared inlet variables +TInletDesign * InletDesigns; // array of available inlet designs +int InletDesignCount; // number of inlet designs +int UsesInlets; // TRUE if project uses inlets + +//----------------------------------------------------------------------------- +// Enumerations +//----------------------------------------------------------------------------- + +enum InletType { + GRATE_INLET, CURB_INLET, COMBO_INLET, SLOTTED_INLET, + DROP_GRATE_INLET, DROP_CURB_INLET, CUSTOM_INLET +}; + +enum GrateType { + P50, P50x100, P30, CURVED_VANE, TILT_BAR_45, + TILT_BAR_30, RETICULINE, GENERIC +}; + +enum InletPlacementType { AUTOMATIC, ON_GRADE, ON_SAG }; + +enum ThroatAngleType { HORIZONTAL_THROAT, INCLINED_THROAT, VERTICAL_THROAT }; + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- +static char* InletTypeWords[] = + {"GRATE", "CURB", "", "SLOTTED", "DROP_GRATE", "DROP_CURB", "CUSTOM", NULL}; + +static char* GrateTypeWords[] = + {"P_BAR-50", "P_BAR-50x100", "P_BAR-30", "CURVED_VANE", "TILT_BAR-45", "TILT_BAR-30", + "RETICULINE", "GENERIC", NULL}; + +static char* ThroatAngleWords[] = + {"HORIZONTAL", "INCLINED", "VERTICAL", NULL}; + +static char *PlacementTypeWords[] = + {"AUTOMATIC", "ON_GRADE", "ON_SAG"}; + +// Coefficients for cubic polynomials fitted to Splash Over Velocity v. +// Grate Length curves in Chart 5B of HEC-22 manual taken from Denver +// UDFCD manual. +static const double SplashCoeffs[][4] = { + {2.22, 4.03, 0.65, 0.06}, //P_BAR-50 + {0.74, 2.44, 0.27, 0.02}, //P_BAR-50x100 + {1.76, 3.12, 0.45, 0.03}, //P_BAR-30 + {0.30, 4.85, 1.31, 0.15}, //Curved_Vane + {0.99, 2.64, 0.36, 0.03}, //Tilt_Bar-45 + {0.51, 2.34, 0.2, 0.01}, //Tilt_Bar-30 + {0.28, 2.28, 0.18, 0.01}}; //Reticuline + +// Grate opening ratios (Chart 9B of HEC-22 manual) +static const double GrateOpeningRatios[] = { + 0.90, //P_BAR-50 + 0.80, //P_BAR-50x100 + 0.60, //P_BAR-30 + 0.35, //Curved_Vane + 0.17, //Tilt_Bar-45 (assumed) + 0.34, //Tilt_Bar-30 + 0.80, //Reticuline + 1.00}; //Generic + +//----------------------------------------------------------------------------- +// Imported Variables +//----------------------------------------------------------------------------- +extern TLinkStats* LinkStats; // defined in STATS.C +extern TNodeStats* NodeStats; // defined in STATS.C + +//----------------------------------------------------------------------------- +// Local Shared Variables +//----------------------------------------------------------------------------- +// Variables as named in the HEC-22 manual. +static double Sx; // street cross slope +static double SL; // conduit longitudinal slope +static double Sw; // gutter + cross slope +static double a; // street gutter depression (ft) +static double W; // street gutter width (ft) +static double T; // top width of flow spread (ft) +static double n; // Manning's roughness coeff. + +// Additional variables +static int Nsides; // 1- or 2-sided street +static double Tcrown; // distance from street curb to crown (ft) +static double Beta; // = 1.486 * sqrt(SL) / n +static double Qfactor; // factor f in Izzard's eqn. Q = f*T^2.67 +static TXsect* xsect; // cross-section data of inlet's conduit +static double* InletFlow; // captured inlet flow received by each node +static TInlet* FirstInlet; // head of list of deployed inlets + +//----------------------------------------------------------------------------- +// External functions (declared in inlet.h) +//----------------------------------------------------------------------------- +// inlet_create called by createObjects in project.c +// inlet_delete called by deleteObjects in project.c +// inlet_readDesignParams called by parseLine in input.c +// inlet_readUsageParams called by parseLine in input.c +// inlet_validate called by project_validate +// inlet_findCapturedFlows called by routing_execute +// inlet_adjustQualInflows called by routing_execute +// inlet_adjustQualOutflows called by routing execute +// inlet_writeStatsReport called by statsrpt_writeReport +// inlet_capturedFlow called by findLinkMassFlow in qualrout.c + +//----------------------------------------------------------------------------- +// Local functions +//----------------------------------------------------------------------------- +static int readGrateInletParams(int inletIndex, char* tok[], int ntoks); +static int readCurbInletParams(int inletIndex, char* tok[], int ntoks); +static int readSlottedInletParams(int inletIndex, char* tok[], int ntoks); +static int readCustomInletParams(int inletIndex, char* tok[], int ntoks); + +static void initInletStats(TInlet* inlet); +static void updateInletStats(TInlet* inlet, double q); +static void writeStreetStatsHeader(); +static void writeStreetStats(int link); + +static void getBackflowRatios(); +static double getInletArea(TInlet* inlet); + +static int getInletPlacement(TInlet* inlet, int node); +static void getConduitGeometry(TInlet* inlet); +static double getFlowSpread(double flow); +static double getEo(double slopeRatio, double spread, double gutterWidth); + +static double getCustomCapturedFlow(TInlet* inlet, double flow, double depth); +static double getOnGradeCapturedFlow(TInlet* inlet, double flow, double depth); +static double getOnGradeInletCapture(int inletIndex, double flow, double depth); +static double getGrateInletCapture(int inletIndex, double flow); +static double getCurbInletCapture(double flow, double length); + +static double getGutterFlowRatio(double gutterWidth); +static double getGutterAreaRatio(double grateWidth, double area); +static double getSplashOverVelocity(int grateType, double grateLength); + +static double getOnSagCapturedFlow(TInlet* inlet, double flow, double depth); +static double getOnSagInletCapture(int inletIndex, double depth); +static void findOnSagGrateFlows(int inletIndex, double depth, + double *weirFlow, double *orificeFlow); +static void findOnSagCurbFlows(int inletIndex, double depth, + double openingLength, double *weirFlow, + double *orificeFlow); +static double getCurbOrificeFlow(double flowDepth, double openingHeight, + double openingLength, int throatAngle); +static double getOnSagSlottedFlow(int inletIndex, double depth); + +//============================================================================= + +int inlet_create(int numInlets) +// +// Input: numInlets = number of inlet designs to create +// Output: none +// Purpose: creats a collection of inlet designs. +// +{ + int i; + + InletDesigns = NULL; + InletFlow = NULL; + InletDesignCount = 0; + UsesInlets = FALSE; + FirstInlet = NULL; + InletDesigns = (TInletDesign *)calloc(numInlets, sizeof(TInletDesign)); + if (InletDesigns == NULL) return ERR_MEMORY; + InletDesignCount = numInlets; + + InletFlow = (double *)calloc(Nobjects[NODE], sizeof(double)); + if (InletFlow == NULL) return ERR_MEMORY; + + for (i = 0; i < InletDesignCount; i++) + { + InletDesigns[i].customCurve = -1; + InletDesigns[i].curbInlet.length = 0.0; + InletDesigns[i].grateInlet.length = 0.0; + InletDesigns[i].slottedInlet.length = 0.0; + InletDesigns[i].type = CUSTOM_INLET; + } + return 0; +} + +//============================================================================= + +void inlet_delete() +// +// Input: none +// Output: none +// Purpose: frees all memory allocated for inlet analysis. +// +{ + TInlet* inlet = FirstInlet; + TInlet* nextInlet; + while (inlet) + { + nextInlet = inlet->nextInlet; + free(inlet); + inlet = nextInlet; + } + FirstInlet = NULL; + FREE(InletFlow); + FREE(InletDesigns); +} + +//============================================================================= + +int inlet_readDesignParams(char* tok[], int ntoks) +// +// Input: tok[] = array of string tokens +// ntoks = number of tokens +// Output: returns an error code +// Purpose: extracts a set of inlet design parameters from a tokenized line +// of the [INLETS] section of a SWMM input file. +// +// Format of input line is: +// ID GRATE Length Width GrateType (OpenArea) (SplashVeloc) +// ID CURB Length Height (ThroatType) +// ID SLOTTED Length Width +// ID DROP_GRATE Length Width GrateType (OpenArea) (SplashVeloc) +// ID DROP_CURB Length Height +// ID CUSTOM CurveID +// +{ + int i; + + // --- check for minimum number of tokens + if ( ntoks < 3 ) return error_setInpError(ERR_ITEMS, ""); + + // --- check that design ID already registered in project + i = project_findObject(INLET, tok[0]); + if ( i < 0 ) return error_setInpError(ERR_NAME, tok[0]); + InletDesigns[i].ID = project_findID(INLET, tok[0]); + + // --- retrieve type of inlet design + InletDesigns[i].type = findmatch(tok[1], InletTypeWords); + + // --- read inlet's design parameters + switch (InletDesigns[i].type) + { + case GRATE_INLET: + case DROP_GRATE_INLET: + return readGrateInletParams(i, tok, ntoks); + case CURB_INLET: + case DROP_CURB_INLET: + return readCurbInletParams(i, tok, ntoks); + case SLOTTED_INLET: + return readSlottedInletParams(i, tok, ntoks); + case CUSTOM_INLET: + return readCustomInletParams(i, tok, ntoks); + default: return error_setInpError(ERR_KEYWORD, tok[1]); + } + return 0; +} +//============================================================================= + +int inlet_readUsageParams(char* tok[], int ntoks) +// +// Input: tok[] = array of string tokens +// ntoks = number of tokens +// Output: returns an error code +// Purpose: extracts inlet usage parameters from a tokenized line +// of the [INLET_USAGE] section of a SWMM input file. +// +// Format of input line is: +// linkID inletID nodeID (#Inlets %Clog Qmax aLocal wLocal placement) +// where +// linkID = ID name of link containing the inlet +// inletID = ID name of inlet design being used +// nodeID = ID name of node receiving captured flow +// #Inlets = number of identical inlets used (default = 1) +// %Clog = percent that inlet is clogged +// Qmax = maximum flow that inlet can capture (default = 0 (no limit)) +// aLocal = local gutter depression (ft or m) (default = 0) +// wLocal = width of local gutter depression (ft or m) (default = 0) +// placement = ON_GRADE, ON_SAG, or AUTO (the default) +// +{ + int linkIndex, designIndex, nodeIndex, numInlets = 1; + int placement = AUTOMATIC; + double flowLimit = 0.0, pctClogged = 0.0; + double aLocal = 0.0, wLocal = 0.0; + TInlet* inlet; + + // --- check that inlet's link exists + if (ntoks < 3) return error_setInpError(ERR_ITEMS, ""); + linkIndex = project_findObject(LINK, tok[0]); + if (linkIndex < 0) return error_setInpError(ERR_NAME, tok[0]); + + // --- check that inlet design type exists + designIndex = project_findObject(INLET, tok[1]); + if (designIndex < 0) return error_setInpError(ERR_NAME, tok[1]); + + // --- check that receiving node exists + nodeIndex = project_findObject(NODE, tok[2]); + if (nodeIndex < 0) return error_setInpError(ERR_NAME, tok[2]); + + // --- get number of inlets + if (ntoks > 3) + if (!getInt(tok[3], &numInlets) || numInlets < 1) + return error_setInpError(ERR_NUMBER, tok[3]); + + // --- get flow limit & percent clogged + if (ntoks > 4) + { + if (!getDouble(tok[4], &pctClogged) || pctClogged < 0.0 + || pctClogged > 99.) + return error_setInpError(ERR_NUMBER, tok[4]); + } + if (ntoks > 5) + if (!getDouble(tok[5], &flowLimit) || flowLimit < 0.0) + return error_setInpError(ERR_NUMBER, tok[5]); + + // --- get local depression parameters + if (ntoks > 6) + if (!getDouble(tok[6], &aLocal) || aLocal < 0.0) + return error_setInpError(ERR_NUMBER, tok[6]); + if (ntoks > 7) + if (!getDouble(tok[7], &wLocal) || wLocal < 0.0) + return error_setInpError(ERR_NUMBER, tok[7]); + + // --- get inlet placement + if (ntoks > 8) + { + placement = findmatch(tok[8], PlacementTypeWords); + if (placement < 0) return error_setInpError(ERR_KEYWORD, tok[8]); + } + + // --- create an inlet usage object for the link + inlet = Link[linkIndex].inlet; + if (inlet == NULL) + { + inlet = (TInlet *)malloc(sizeof(TInlet)); + if (!inlet) return error_setInpError(ERR_MEMORY, ""); + Link[linkIndex].inlet = inlet; + inlet->nextInlet = FirstInlet; + FirstInlet = inlet; + } + + // --- save inlet usage parameters + inlet->linkIndex = linkIndex; + inlet->designIndex = designIndex; + inlet->nodeIndex = nodeIndex; + inlet->numInlets = numInlets; + inlet->placement = placement; + inlet->clogFactor = 1.0 - (pctClogged / 100.); + inlet->flowLimit = flowLimit / UCF(FLOW); + inlet->localDepress = aLocal / UCF(LENGTH); + inlet->localWidth = wLocal / UCF(LENGTH); + inlet->flowFactor = 0.0; + inlet->backflowRatio = 0.0; + initInletStats(inlet); + UsesInlets = TRUE; + return 0; +} + +//============================================================================= + +void inlet_validate() +// +// Input: none +// Output: none +// Purpose: checks that inlets have been assigned to conduits with proper +// cross section shapes and counts the number of inlets that each +// node receives either bypased or captured flow from. +// +{ + int i, j, inletType, inletValid; + TInlet* inlet; + TInlet* prevInlet; + + // --- traverse the list of inlets placed in conduits + if (!UsesInlets) return; + prevInlet = FirstInlet; + inlet = FirstInlet; + while (inlet) + { + // --- check that inlet's conduit can accept the inlet's type + inletValid = FALSE; + i = inlet->linkIndex; + xsect = &Link[i].xsect; + inletType = InletDesigns[inlet->designIndex].type; + if (inletType == CUSTOM_INLET) + { + j = InletDesigns[inlet->designIndex].customCurve; + if (j >= 0) + { + if (Curve[j].curveType == DIVERSION_CURVE || + Curve[j].curveType == RATING_CURVE) + inletValid = TRUE; + } + } + else if ((xsect->type == TRAPEZOIDAL || xsect->type == RECT_OPEN) && + (inletType == DROP_GRATE_INLET || + inletType == DROP_CURB_INLET)) + inletValid = TRUE; + else if (xsect->type == STREET_XSECT && + inletType != DROP_GRATE_INLET && + inletType != DROP_CURB_INLET) + inletValid = TRUE; + + // --- if inlet placement is valid then + if (inletValid) + { + // --- record that receptor node has inlets + Node[Link[i].node2].inlet = BYPASS; + Node[inlet->nodeIndex].inlet = CAPTURE; + + // --- initialize inlet's backflow + inlet->backflow = 0.0; + + // --- compute street inlet's flow factor for Izzard's eqn. + // (used in Q = flowFactor * Spread^2.67 equation) + getConduitGeometry(inlet); + inlet->flowFactor = (0.56/n) * pow(SL,0.5) * pow(Sx,1.67); + + // --- save reference to current inlet & continue to next inlet + prevInlet = inlet; + inlet = inlet->nextInlet; + } + + // --- if inlet placement is not valid then issue a warning message + // and remove the inlet from the conduit + else + { + report_writeWarningMsg(WARN12, Link[i].ID); + if (inlet == FirstInlet) + { + FirstInlet = inlet->nextInlet; + prevInlet = FirstInlet; + free(inlet); + inlet = FirstInlet; + } + else + { + prevInlet->nextInlet = inlet->nextInlet; + free(inlet); + inlet = prevInlet->nextInlet; + } + Link[i].inlet = NULL; + } + } + + // --- determine how capture node's overflow is split between its inlets + getBackflowRatios(); +} + +//============================================================================= + +void inlet_findCapturedFlows(double tStep) +// +// Input: tStep = current flow routing time step (sec) +// Output: none +// Purpose: computes flow captured by each inlet and adjusts the +// lateral flows of the inlet's bypass and capture nodes accordingly. +// +// This function is called after regular lateral flows to all nodes have been +// set but before a flow routing step has been taken. +{ + int i, j, m, placement; + double q; + TInlet *inlet; + + // --- For non-DW routing find conduit flow into each node + // (used to limit max. amount of on-sag capture) + if (!UsesInlets) return; + memset(InletFlow, 0, Nobjects[NODE]*sizeof(double)); + if (RouteModel != DW) + { + for (j = 0; j < Nobjects[NODE]; j++) + Node[j].inflow = MAX(0., Node[j].newLatFlow); + for (i = 0; i < Nobjects[LINK]; i++) + Node[Link[i].node2].inflow += MAX(0.0, Link[i].newFlow); + } + + // --- loop through each inlet + for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet) + { + // --- identify indexes of inlet's bypass (j) and capture (m) nodes + i = inlet->linkIndex; + j = Link[i].node2; + m = inlet->nodeIndex; + + // --- get inlet's placement (ON_GRADE or ON_SAG) + placement = getInletPlacement(inlet, j); + + // --- find flow captured by a Custom inlet + if (InletDesigns[inlet->designIndex].type == CUSTOM_INLET) + { + q = fabs(Link[i].newFlow); + inlet->flowCapture = getCustomCapturedFlow(inlet, q, Node[j].newDepth); + } + + // --- find flow captured by on-grade inlet + else if (placement == ON_GRADE) + { + q = fabs(Link[i].newFlow); + inlet->flowCapture = getOnGradeCapturedFlow(inlet, q, Node[j].newDepth); + } + + // --- find flow captured by on-sag inlet + else + { + q = Node[j].inflow; + inlet->flowCapture = getOnSagCapturedFlow(inlet, q, Node[j].newDepth); + } + if (fabs(inlet->flowCapture) < FUDGE) inlet->flowCapture = 0.0; + + // --- add to total flow captured by inlet's node + InletFlow[j] += inlet->flowCapture; + + // --- capture node's overflow becomes inlet's backflow + inlet->backflow = Node[m].overflow * inlet->backflowRatio; + if (fabs(inlet->backflow) < FUDGE) inlet->backflow = 0.0; + } + + // --- make second pass through each inlet + for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet) + { + // --- identify indexes of inlet's bypass (j) and capture (m) nodes + i = inlet->linkIndex; + j = Link[i].node2; + m = inlet->nodeIndex; + + // --- for on-sag placement under non-DW routing, captured flow + // is limited to inlet's share of bypass node's inflow plus + // any stored volume + if (RouteModel != DW && getInletPlacement(inlet, j) == ON_SAG) + { + q = Node[j].newVolume / tStep; + q += MAX(Node[j].inflow, 0.0); + if (InletFlow[j] > q) + inlet->flowCapture *= q / InletFlow[j]; + } + + // --- adjust lateral flows at bypass and capture nodes + // (subtract captured flow from bypass node, add it to capture + // node, and add any backflow to bypass node) + Node[j].newLatFlow -= (inlet->flowCapture - inlet->backflow); + Node[m].newLatFlow += inlet->flowCapture; + + // --- update inlet's performance if reporting has begun + if (getDateTime(NewRoutingTime) > ReportStart) + updateInletStats(inlet, fabs(Link[i].newFlow)); + } +} + +//============================================================================= + +void inlet_adjustQualInflows() +// +// Input: none +// Output: none +// Purpose: adjusts accumulated flow rates and pollutant mass inflows at each +// inlet's bypass and capture nodes after a flow routing step has +// been taken prior to a quality routing step. +// +{ + int i, j, m, p; + double qNet; + TInlet* inlet; + + if (!UsesInlets) return; + if (IgnoreQuality || Nobjects[POLLUT] == 0) return; + for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet) + { + // --- identify indexes of inlet's bypass (j) and capture (m) nodes + i = inlet->linkIndex; + j = Link[i].node2; + m = inlet->nodeIndex; + + // --- there's a net flow from the bypass to the capture node + qNet = inlet->flowCapture - inlet->backflow; + if (qNet > 0.0) + { + // --- add net capture flow to capture node's accumulated flow + // inflow for quality routing + Node[m].qualInflow += qNet; + + // --- and do the same for pollutant mass flows + // (Node[m].newQual is the mass inflow accumulator for node m) + for (p = 0; p < Nobjects[POLLUT]; p++) + Node[m].newQual[p] += qNet * Node[j].oldQual[p]; + } + + // --- there's a net backflow from the capture to the bypass node + else + { + // --- add the backflow flow rate and pollutant mass flow to the + // bypass node's accumulated flow and pollutant mass inflow + qNet = -qNet; + Node[j].qualInflow += qNet; + for (p = 0; p < Nobjects[POLLUT]; p++) + Node[j].newQual[p] += qNet * Node[m].oldQual[p]; + } + } +} + +//============================================================================= + +void inlet_adjustQualOutflows() +// +// Input: none +// Output: none +// Purpose: adjusts mass balance totals after a complete routing step has been +// taken so as not to treat inlet transfer flows as system outflows. +// +{ + int j, p; + double q, w; + TInlet* inlet; + + // --- these variables, declared in massbal.c, accumulate system-wide flow and + // pollutant mass fluxes over a time step to use in mass balances + extern TRoutingTotals StepFlowTotals; + extern TRoutingTotals* StepQualTotals; + + // --- examine each node + for (j = 0; j < Nobjects[NODE]; j++) + { + // --- node receives captured flow from an inlet + if (Node[j].inlet == CAPTURE) + { + // --- node also has an overflow (e.g., it's a surcharged sewer node) + q = Node[j].overflow; + if (q > 0.0) + { + // --- remove overflow from system flooding total since it does + // not leave the system (it is sent to inlet's bypass node) + StepFlowTotals.flooding -= q; + + // --- also remove pollutant overflow mass from system totals + if (!IgnoreQuality) + for (p = 0; p < Nobjects[POLLUT]; p++) + { + w = q * Node[j].newQual[p]; + StepQualTotals[p].flooding -= w; + } + } + } + } + + // --- for WQ analysis, examine each inlet's bypass node + if (!IgnoreQuality && Nobjects[POLLUT] > 0) + { + for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet) + { + j = Link[inlet->linkIndex].node2; + + // --- inlet has net positive flow capture leading to + // node having a net negative lateral inflow + q = inlet->flowCapture - inlet->backflow; + if (q > 0.0 && Node[j].newLatFlow < 0.0) + + // --- remove the pollutant mass in the captured flow from + // the system totals since it does not leave the system + // (it is sent to the inlet's capture node) + for (p = 0; p < Nobjects[POLLUT]; p++) + { + w = q * Node[j].newQual[p]; + StepQualTotals[p].outflow -= w; + } + } + } +} + +//============================================================================= + +void inlet_writeStatsReport() +// +// Input: none +// Output: none +// Purpose: writes table of street & inlet flow statistics to SWMM's report file. +// +{ + int j, header = FALSE; + + if (Nobjects[STREET] == 0) return; + for (j = 0; j < Nobjects[LINK]; j++) + { + if (Link[j].xsect.type == STREET_XSECT) + { + if (!header) + { + writeStreetStatsHeader(); + header = TRUE; + } + writeStreetStats(j); + } + } + report_writeLine(""); +} + +//============================================================================= + +double inlet_capturedFlow(int i) +// +// Input: i = a link index +// Output: returns captured flow rate (cfs) +// Purpose: gets the current flow captured by an inlet. +// +{ + if (Link[i].inlet) return Link[i].inlet->flowCapture; + return 0.0; +} + +//============================================================================= + +int readGrateInletParams(int i, char* tok[], int ntoks) +{ +// +// Input: i = inlet index +// tok[] = array of string tokens +// ntoks = number of tokens +// Output: returns an error code +// Purpose: extracts a grate's inlet parameters from a set of string tokens. +// + int grateType; + double width, length, areaRatio = 0.0, vSplash = 0.0; + + // --- check for enough tokens + if (ntoks < 5) return error_setInpError(ERR_ITEMS, ""); + + // --- retrieve length & width + if (!getDouble(tok[2], &length) || length <= 0.0) + return error_setInpError(ERR_NUMBER, tok[2]); + if (!getDouble(tok[3], &width) || width <= 0.0) + return error_setInpError(ERR_NUMBER, tok[3]); + + // --- retrieve grate type + grateType = findmatch(tok[4], GrateTypeWords); + if (grateType < 0) return error_setInpError(ERR_KEYWORD, tok[4]); + + // --- only read open area & splash velocity for GENERIC type grate + if (grateType == GENERIC) + { + if (ntoks < 6) return error_setInpError(ERR_ITEMS, ""); + if (!getDouble(tok[5], &areaRatio) || areaRatio <= 0.0 + || areaRatio > 1.0) return error_setInpError(ERR_NUMBER, tok[5]); + if (ntoks > 6) + { + if (!getDouble(tok[6], &vSplash) || vSplash < 0.0) + return error_setInpError(ERR_NUMBER, tok[6]); + } + } + + // --- save grate inlet parameters + InletDesigns[i].grateInlet.length = length / UCF(LENGTH); + InletDesigns[i].grateInlet.width = width / UCF(LENGTH); + InletDesigns[i].grateInlet.type = grateType; + InletDesigns[i].grateInlet.fracOpenArea = areaRatio; + InletDesigns[i].grateInlet.splashVeloc = vSplash / UCF(LENGTH); + + // --- check if grate is part of a combo inlet + if (InletDesigns[i].type == GRATE_INLET && + InletDesigns[i].curbInlet.length > 0.0) + InletDesigns[i].type = COMBO_INLET; + return 0; +} + +//============================================================================= + +int readCurbInletParams(int i, char* tok[], int ntoks) +// +// Input: i = inlet index +// tok[] = array of string tokens +// ntoks = number of tokens +// Output: returns an error code +// Purpose: extracts curb opening inlet parameters from a set of string tokens. +// +{ + int throatAngle; + double height, length; + + // --- check for enough tokens + if (ntoks < 4) return error_setInpError(ERR_ITEMS, ""); + + // --- retrieve length & width of opening + if (!getDouble(tok[2], &length) || length <= 0.0) + return error_setInpError(ERR_NUMBER, tok[2]); + if (!getDouble(tok[3], &height) || height <= 0.0) + return error_setInpError(ERR_NUMBER, tok[3]); + + // --- retrieve type of throat angle for curb inlet + throatAngle = VERTICAL_THROAT; + if (InletDesigns[i].type == CURB_INLET && ntoks > 4) + { + throatAngle = findmatch(tok[4], ThroatAngleWords); + if (throatAngle < 0) return error_setInpError(ERR_KEYWORD, tok[4]); + } + + // ---- save curb opening inlet parameters + InletDesigns[i].curbInlet.length = length / UCF(LENGTH); + InletDesigns[i].curbInlet.height = height / UCF(LENGTH); + InletDesigns[i].curbInlet.throatAngle = throatAngle; + + // --- check if curb inlet is part of a combo inlet + if (InletDesigns[i].type == CURB_INLET && + InletDesigns[i].grateInlet.length > 0.0) + InletDesigns[i].type = COMBO_INLET; + return 0; +} + +//============================================================================= + +int readSlottedInletParams(int i, char* tok[], int ntoks) +// +// Input: i = inlet index +// tok[] = array of string tokens +// ntoks = number of tokens +// Output: returns an error code +// Purpose: extracts slotted drain inlet parameters from a set of string tokens. +// +{ + double width, length; + + // --- check for enough tokens + if (ntoks < 4) return error_setInpError(ERR_ITEMS, ""); + + // --- retrieve length and width + if (!getDouble(tok[2], &length) || length <= 0.0) + return error_setInpError(ERR_NUMBER, tok[2]); + if (!getDouble(tok[3], &width) || width <= 0.0) + return error_setInpError(ERR_NUMBER, tok[3]); + + // --- save slotted inlet parameters + InletDesigns[i].slottedInlet.length = length / UCF(LENGTH); + InletDesigns[i].slottedInlet.width = width / UCF(LENGTH); + return 0; +} + +//============================================================================= + +int readCustomInletParams(int i, char* tok[], int ntoks) +// +// Input: i = inlet index +// tok[] = array of string tokens +// ntoks = number of tokens +// Output: returns an error code +// Purpose: extracts custom inlet parameters from a set of string tokens. +// +{ + int c; // capture curve index + + if (ntoks < 3) return error_setInpError(ERR_ITEMS, ""); + else + { + c = project_findObject(CURVE, tok[2]); + if (c < 0) return error_setInpError(ERR_NAME, tok[2]); + } + InletDesigns[i].customCurve = c; + return 0; +} + +//============================================================================= + +void initInletStats(TInlet* inlet) +// +// Input: inlet = an inlet object placed in a conduit link +// Output: none +// Purpose: initializes the performance statistics of an inlet. +// +{ + if (inlet) + { + inlet->flowCapture = 0.0; + inlet->backflow = 0.0; + inlet->stats.flowPeriods = 0; + inlet->stats.capturePeriods = 0; + inlet->stats.backflowPeriods = 0; + inlet->stats.peakFlow = 0.0; + inlet->stats.peakFlowCapture = 0; + inlet->stats.avgFlowCapture = 0; + inlet->stats.bypassFreq = 0; + } +} + +//============================================================================= + +void updateInletStats(TInlet* inlet, double q) +// +// Input: inlet = an inlet object placed in a conduit link +// q = inlet's approach flow (cfs) +// Output: none +// Purpose: updates the performance statistics of an inlet. +// +{ + double qCapture = inlet->flowCapture, + qBackflow = inlet->backflow, + qNet = qCapture - qBackflow, + qBypass = q - qNet, + fCapture = 0.0; + + // --- check for no flow condition + if (q < MIN_RUNOFF_FLOW && qBackflow <= 0.0) return; + inlet->stats.flowPeriods++; + + // --- there is positive net flow from inlet to capture node + if (qNet > 0.0) + { + inlet->stats.capturePeriods++; + fCapture = qNet / q; + fCapture = MIN(fCapture, 1.0); + inlet->stats.avgFlowCapture += fCapture; + if (qBypass > MIN_RUNOFF_FLOW) inlet->stats.bypassFreq++; + } + + // --- otherwise inlet receives backflow from capture node + else inlet->stats.backflowPeriods++; + + // --- update peak flow stats + if (q > inlet->stats.peakFlow) + { + inlet->stats.peakFlow = q; + inlet->stats.peakFlowCapture = fCapture * 100.0; + } +} + +//============================================================================= + +void writeStreetStatsHeader() +// +// Input: none +// Output: none +// Purpose: writes column headers for Street Flow Summary table to SWMM's report file. +// +{ + report_writeLine(""); + report_writeLine("*******************"); + report_writeLine("Street Flow Summary"); + report_writeLine("*******************"); + report_writeLine(""); + fprintf(Frpt.file, +"\n ----------------------------------------------------------------------------------------------------------------------" +"\n Peak Maximum Maximum Peak Flow Average Bypass BackFlow" +"\n Flow Spread Depth Inlet Inlet Capture Capture Frequency Frequency"); + if (UnitSystem == US) fprintf(Frpt.file, +"\n Street Conduit %3s ft ft Design Location %% %% %% %%", + FlowUnitWords[FlowUnits]); + else fprintf(Frpt.file, +"\n Street Conduit %3s m m Design Location %% %% %% %%", + FlowUnitWords[FlowUnits]); + fprintf(Frpt.file, +"\n ----------------------------------------------------------------------------------------------------------------------"); +} + +//============================================================================= + +void writeStreetStats(int link) +// +// Input: link = index of a conduit link containing an inlet +// Output: none +// Purpose: writes flow statistics for a Street conduit and its inlet to +// SWMM's report file. +// +{ + int k, t, placement; + double maxSpread, maxDepth, maxFlow; + double fp, cp, afc = 0.0, bpf = 0.0; + TInlet* inlet; + + // --- retrieve street parameters + k = Link[link].subIndex; + t = Link[link].xsect.transect; + inlet = Link[link].inlet; + + // --- get recorded max flow and depth + maxFlow = LinkStats[link].maxFlow; + maxDepth = LinkStats[link].maxDepth; + + // --- SWMM's spread (flow width) at max depth + maxSpread = xsect_getWofY(&Link[link].xsect, maxDepth) / Street[t].sides; + maxSpread = MIN(maxSpread, Street[t].width); +/* + // HEC-22's spread based on max flow (doesn't account for backwater) + Sx = Street[t].slope; + a = Street[t].gutterDepression; + W = Street[t].gutterWidth; + n = Street[t].roughness; + Qfactor = (0.56 / n) * sqrt(Conduit[k].slope) * pow(Sx, 1.67); + maxSpread = getFlowSpread(maxFlow / Street[t].sides); + maxSpread = MIN(maxSpread, Street[t].width); +*/ + // --- write street stats + fprintf(Frpt.file, "\n %-16s", Link[link].ID); + fprintf(Frpt.file, " %9.3f", maxFlow * UCF(FLOW)); + fprintf(Frpt.file, " %9.3f", maxSpread * UCF(LENGTH)); + fprintf(Frpt.file, " %9.3f", maxDepth * UCF(LENGTH)); + + // --- write inlet stats + if (inlet) + { + fprintf(Frpt.file, " %-16s", InletDesigns[inlet->designIndex].ID); + placement = getInletPlacement(inlet, Link[inlet->linkIndex].node2); + if (placement == ON_GRADE) + fprintf(Frpt.file, " ON-GRADE"); + else + fprintf(Frpt.file, " ON-SAG "); + fp = inlet->stats.flowPeriods / 100.0; + if (fp > 0.0) + { + cp = inlet->stats.capturePeriods / 100.0; + fprintf(Frpt.file, " %9.2f", inlet->stats.peakFlowCapture); + if (cp > 0.0) + { + afc = inlet->stats.avgFlowCapture / cp; + bpf = inlet->stats.bypassFreq / cp; + } + fprintf(Frpt.file, " %9.2f", afc); + fprintf(Frpt.file, " %9.2f", bpf); + fprintf(Frpt.file, " %9.2f", inlet->stats.backflowPeriods / fp); + } + } +} + +//============================================================================= + +int getInletPlacement(TInlet* inlet, int j) +// +// Input: inlet = an inlet object placed in a conduit link +// j = index of inlet's bypass node +// Output: returns type of inlet placement +// Purpose: determines actual placement for an inlet with AUTOMATIC placement. +// +{ + if (inlet->placement == AUTOMATIC) + { + if (Node[j].degree > 0) return ON_GRADE; + else return ON_SAG; + } + else return inlet->placement; +} + +//============================================================================= + +void getConduitGeometry(TInlet* inlet) +// +// Input: inlet = an inlet object placed in a conduit link +// Output: none +// Purpose: assigns properties of an inlet's conduit to +// module-level shared variables used by other functions. +// +{ + int linkIndex = inlet->linkIndex; + int t, k = Link[linkIndex].subIndex; + + SL = Conduit[k].slope; // longitudinal slope + Beta = Conduit[k].beta; // 1.486 * sqrt(SL) / n + xsect = &Link[linkIndex].xsect; + + // --- if conduit has a Street cross section + if (xsect->type == STREET_XSECT) + { + t = xsect->transect; + Sx = Street[t].slope; // street cross slope + a = Street[t].gutterDepression; // gutter depression + W = Street[t].gutterWidth; // gutter width + n = Street[t].roughness; // street roughness + Nsides = Street[t].sides; // 1 or 2 sided street + Tcrown = Street[t].width; // distance from curb to crown + Qfactor = inlet->flowFactor; // factor used in Izzard's eqn. + + // --- add inlet's local depression to street's continuous depression + if (inlet && inlet->localDepress * inlet->localWidth > 0) + { + a += inlet->localDepress; // inlet depression + W = inlet->localWidth; // inlet depressed width + } + + // --- slope of depressed gutter section + if (W * a > 0.0) Sw = Sx + a / W; + else Sw = Sx; + } + + // --- conduit has rectangular or trapezoidal cross section + else + { + a = 0.0; + W = 0.0; + n = Conduit[k].roughness; + Nsides = 1; + Sx = 0.01; + Sw = Sx; + } +} + +//============================================================================= + +double getFlowSpread(double Q) +// +// Input: Q = conduit flow rate (cfs) +// Output: returns width of flow spread (ft) +// Purpose: computes width of flow spread across a Street cross section using +// HEC-22 equations derived from Izzard's form of the Manning eqn. +// +{ + int iter; + double f, f1, Sr, Ts1, Ts2, Tw, Qs, Eo; + + f = Qfactor; // = (0.56/n) * SL^0.5 * Sx^1.67 + + // --- no depressed curb + if (a == 0.0) + { + Ts1 = pow(Q / f, 0.375); //HEC-22 Eq(4-2) + } + else + { + // --- check if spread is within curb width + f1 = f * pow((a / W) / Sx, 1.67); + Tw = pow(Q / f1, 0.375); //HEC-22 Eq(4-2) + if (Tw <= W) Ts1 = Tw; + else + { + // --- spread extends beyond curb width + Sr = (Sx + a / W) / Sx; + iter = 1; + Ts1 = pow(Q / f, 0.375) - W; + if (Ts1 <= 0) Ts1 = Tw - W; + while (iter < 11) + { + Eo = getEo(Sr, Ts1, W); + Qs = (1.0 - Eo) * Q; //HEC-22 Eq(4-6) + Ts2 = pow(Qs / f, 0.375); //HEC-22 Eq(4-2) + if (fabs(Ts2 - Ts1) < 0.01) break; + Ts1 = Ts2; + iter++; + } + Ts1 = Ts2 + W; + } + } + return MIN(Ts1, Tcrown); +} + +//============================================================================= + +double getEo(double Sr, double Ts, double w) +// +// Input: Sr = ratio of gutter slope to street cross slope +// Ts = amount of flow spread outside of gutter width (ft) +// w = gutter width (ft) +// Output: returns ratio of gutter flow to total flow in street cross section +// Purpose: solves HEC-22 Eq. (4-4) for Eo with Ts/w substituted for +// (T/w) - 1 where Ts = T - w. +// +{ + double x; + x = Sr / (Ts / w); + x = pow((1.0 + x), 2.67) - 1.0; + x = 1.0 + Sr / x; + return 1.0 / x; +} + +//============================================================================= + +double getOnGradeCapturedFlow(TInlet* inlet, double q, double d) +// +// Input: inlet = an inlet object placed in a conduit link +// q = flow in link prior to any inlet capture (cfs) +// d = flow depth seen by inlet (ft) +// Output: returns flow captured by the inlet (cfs) +// Purpose: computes flow captured by an inlet placed on-grade. +// +// An inlet object placed in a conduit can have multiple inlets of +// the same type distributed along the conduit's length that all +// send their captured flow to the same sewer node. This function +// finds the total captured flow as each individual inlet is analyzed +// sequentially, where its approach flow has been reduced by the +// amount of flow captured by prior inlets. +{ + int i, + linkIndex; // index of link containing inlets + double qApproach, // single inlet's approach flow (cfs) + qc, // single inlet's captured flow (cfs) + qCaptured, // total flow captured by link's inlets (cfs) + qBypassed, // total flow bypassed by link's inlets (cfs) + qMax; // max. flow that a single inlet can capture (cfs) + + if (inlet->numInlets == 0) return 0.0; + linkIndex = inlet->linkIndex; + + // --- check that link has flow + qApproach = q; + if (qApproach < MIN_RUNOFF_FLOW) return 0.0; + + // --- store conduit geometry in shared variables + getConduitGeometry(inlet); + + // --- adjust flow for 2-sided street + qApproach /= Nsides; + qBypassed = qApproach; + qCaptured = 0.0; + + // --- set limit on max. flow captured per inlet + qMax = BIG; + if (inlet->flowLimit > 0.0) qMax = inlet->flowLimit; + + // --- evaluate each inlet + for (i = 1; i <= inlet->numInlets; i++) + { + qc = getOnGradeInletCapture(inlet->designIndex, qBypassed, d) * + inlet->clogFactor; + qc = MIN(qc, qMax); + qc = MIN(qc, qBypassed); + qCaptured += qc; + qBypassed -= qc; + if (qBypassed < MIN_RUNOFF_FLOW) break; + } + return qCaptured *= Nsides; +} + +//============================================================================= + +double getOnGradeInletCapture(int i, double Q, double d) +// +// Input: i = an InletDesigns index +// Q = flow rate seen by inlet (cfs) +// d = flow depth seen by inlet (ft) +// Output: returns captured flow rate (cfs) +// Purpose: finds the flow captured by a single on-grade inlet. +// +{ + double Q1 = Q, Qc = 0.0, Lsweep = 0.0, Lcurb = 0.0, Lgrate = 0.0; + + // --- drop curb inlet (in non-Street conduit) only operates in on sag mode + if (InletDesigns[i].type == DROP_CURB_INLET) + { + Qc = getOnSagInletCapture(i, d); + return MIN(Qc, Q); + } + + // --- drop grate inlet (in non-Street conduit) + if (InletDesigns[i].type == DROP_GRATE_INLET) + { + Qc = getGrateInletCapture(i, Q); + return MIN(Qc, Q); + } + + // --- Remaining inlet types apply to Street conduits + + // --- find flow spread + T = getFlowSpread(Q); + + // --- slotted inlet (behaves as a curb opening inlet per HEC-22) + if (InletDesigns[i].type == SLOTTED_INLET) + { + Qc = getCurbInletCapture(Q, InletDesigns[i].slottedInlet.length); + return MIN(Qc, Q); + } + + Lcurb = InletDesigns[i].curbInlet.length; + Lgrate = InletDesigns[i].grateInlet.length; + + // --- curb opening inlet + if (Lcurb > 0.0) + { + Lsweep = Lcurb - Lgrate; + if (Lsweep > 0.0) + { + Qc = getCurbInletCapture(Q1, Lsweep); + Q1 -= Qc; + } + } + + // --- grate inlet + if (Lgrate > 0.0 && Q1 > 0.0) + { + if (Q1 != Q) T = getFlowSpread(Q1); + Qc += getGrateInletCapture(i, Q1); + } + return Qc; +} + +//============================================================================= + +double getGrateInletCapture(int i, double Q) +// +// Input: i = inlet type index +// Q = flow rate seen by inlet (cfs) +// Output: returns captured flow rate (cfs) +// Purpose: finds the flow captured by an on-grade grate inlet. +// +{ + int grateType; + double Lg, // grate length (ft) + Wg, // grate width (ft) + A, // total cross section flow area (ft2) + Y, // flow depth (ft) + Eo, // ratio of gutter to total flow + V, // flow velocity (ft/s) + Vo, // splash-over velocity (ft/s) + Qo = Q, // flow over street area (cfs) + Rf = 1.0, // ratio of intercepted to total frontal flow + Rs = 0.0; // ratio of intercepted to total side flow + +// xsect, a, W, & Sx were from getConduitGeometry(). T was from getFlowSpread(). + + Lg = InletDesigns[i].grateInlet.length; + Wg = InletDesigns[i].grateInlet.width; + + // --- flow ratio for drop inlet + if (xsect->type == TRAPEZOIDAL || xsect->type == RECT_OPEN) + { + A = xsect_getAofS(xsect, Q / Beta); + Y = xsect_getYofA(xsect, A); + T = xsect_getWofY(xsect, Y); + Eo = Beta * pow(Y*Wg, 1.67) / pow(Wg + 2*Y, 0.67) / Q; + if (Wg > 0.99*xsect->yBot && xsect->type == TRAPEZOIDAL && xsect->sBot > 0.0) + { + Wg = xsect->yBot; + Sx = 1.0 / xsect->sBot; + } + } + + // --- flow ratio & area for conventional street gutter + else if (a == 0.0) + { + A = T * T * Sx / 2.0; + Eo = getGutterFlowRatio(Wg); // flow ratio based on grate width + if (T >= Tcrown) Qo = Qfactor * pow(Tcrown, 2.67); + } + + // --- flow ratio & area for composite street gutter + else + { + // --- spread confined to gutter + if (T <= W) A = T * T * Sw / 2.0; + + // --- spread beyond gutter width + else A = (T * T * Sx + a * W) / 2.0; + + // flow ratio based on gutter width corrected for grate width + Eo = getGutterFlowRatio(W); + if (Eo < 1.0) + { + if (T >= Tcrown) + Qo = Qfactor * pow(Tcrown, 2.67) / (1.0 - Eo); + Eo = Eo * getGutterAreaRatio(Wg, A); //HEC-22 Eq(4-20a) + } + } + + // --- flow and splash-over velocities + V = Qo / A; + grateType = InletDesigns[i].grateInlet.type; + if (grateType < 0 || grateType == GENERIC) + Vo = InletDesigns[i].grateInlet.splashVeloc; + else + Vo = getSplashOverVelocity(grateType, Lg); + + // --- frontal flow capture efficiency + if (V > Vo) Rf = 1.0 - 0.09 * (V - Vo); //HEC-22 Eq(4-18) + + // --- side flow capture efficiency + if (Eo < 1.0) + { + Rs = 1.0 / (1.0 + (0.15 * pow(V, 1.8) / + Sx / pow(Lg, 2.3))); //HEC-22 Eq(4-19) + } + + // --- return total flow captured + return Q * (Rf * Eo + Rs * (1.0 - Eo)); //HEC-22 Eq(4-21) +} + +//============================================================================= + +double getCurbInletCapture(double Q, double L) +// +// Input: Q = flow rate seen by inlet (cfs) +// L = length of inlet opening (ft) +// Output: returns captured flow rate (cfs) +// Purpose: finds the flow captured by an on-sag inlet. +// +{ + double Se = Sx, // equivalent gutter slope + Lt, // length for full capture + Sr, // ratio of gutter slope to cross slope + Eo = 0.0, // ratio of gutter to total flow + E = 1.0; // capture efficiency + +// a, W, Sx, Sw, SL, & n were from getConduitGeometry(). T was from getFlowSpread(). + + // --- for depressed gutter section + if (a > 0.0) + { + Sr = Sw / Sx; + Eo = getEo(Sr, T-W, W); + Se = Sx + Sw * Eo; //HEC-22 Eq(4-24) + } + + // --- opening length for full capture + Lt = 0.6 * pow(Q, 0.42) * pow(SL, 0.3) * + pow(1.0/(n*Se), 0.6); //HEC-22 Eq(4-22a) + + // --- capture efficiency for actual opening length + if (L < Lt) + { + E = 1.0 - (L/Lt); + E = 1 - pow(E, 1.8); //HEC-22 Eq(4-23) + } + E = MIN(E, 1.0); + E = MAX(E, 0.0); + return E * Q; +} + +//============================================================================= + +double getGutterFlowRatio(double w) +// +// Input: w = gutter width (ft) +// Output: returns a flow ratio +// Purpose: computes the ratio of flow over a width of gutter to the total +// flow in a street cross section. +// +{ + if (T <= w) return 1.0; + else if (a > 0.0) + return getEo(Sw / Sx, T - w, w); + else + return 1.0 - pow((1.0 - w / T), 2.67); //HEC-22 Eq(4-16) +} + +//============================================================================= + +double getGutterAreaRatio(double Wg, double A) +// +// Input: Wg = width of grate inlet (ft) +// A = total flow area (ft2) +// Output: returns an area ratio +// Purpose: computes the ratio of the flow area above a grate to the flow +// area above depressed gutter in a street cross section. +// +{ + double As, // flow area beyond gutter width (ft2) + Ag; // flow area over grate width (ft2) + + if (Wg >= W) return 1.0; + if (T <= Wg) return 1.0; + if (T <= W) return Wg / T; + As = 0.5 * SQR((T - W)) * Sx; + Ag = Wg * ( (T * Sx) + a - (Wg * Sw / 2.) ); + return Ag / (A - As); +} + +//============================================================================= + +double getSplashOverVelocity(int grateType, double L) +// +// Input: grateType = grate inlet type code +// L = length of grate inlet (ft) +// Output: returns a splash over velocity +// Purpose: computes the splash over velocity for a standard type of grate +// inlet as a function of its length. +// +{ + return SplashCoeffs[grateType][0] + + SplashCoeffs[grateType][1] * L - + SplashCoeffs[grateType][2] * L * L + + SplashCoeffs[grateType][3] * L * L * L; +} + +//============================================================================= + +double getOnSagCapturedFlow(TInlet* inlet, double q, double d) +// +// Input: inlet = an inlet object placed in a conduit link +// q = flow in link prior to any inlet capture (cfs) +// d = flow depth seen by inlet (ft) +// Output: returns flow captured by the inlet (cfs) +// Purpose: computes flow captured by an inlet placed on-sag. +// +{ + int linkIndex, designIndex, totalInlets; + double qCaptured = 0.0, qMax = HUGE; + + if (inlet->numInlets == 0) return 0.0; + totalInlets = Nsides * inlet->numInlets; + linkIndex = inlet->linkIndex; + designIndex = inlet->designIndex; + + // --- store conduit geometry in shared variables + getConduitGeometry(inlet); + + // --- set flow limit per inlet + if (inlet->flowLimit > 0.0) + qMax = inlet->flowLimit; + + // --- find nominal flow captured by inlet + qCaptured = getOnSagInletCapture(designIndex, fabs(d)); + + // --- find actual flow captured by the inlet + qCaptured *= inlet->clogFactor; + qCaptured = MIN(qCaptured, qMax); + qCaptured *= (double)totalInlets; + return qCaptured; +} + +//============================================================================= + +double getOnSagInletCapture(int i, double d) +// +// Input: i = inlet type index +// d = water level seen by inlet (ft) +// Output: returns captured flow rate (cfs) +// Purpose: finds the flow captured by an on-sag inlet. +// +{ + double Lsweep = 0.0, Lcurb = 0.0, Lgrate = 0.0; + double Qsw = 0.0, //Sweeper curb opening weir flow + Qso = 0.0, //Sweeper curb opening orifice flow + Qgw = 0.0, //Grate weir flow + Qgo = 0.0, //Grate orifice flow + Qcw = 0.0, //Curb opening weir flow + Qco = 0.0; //Curb opening orifice flow + + if (InletDesigns[i].slottedInlet.length > 0.0) + return getOnSagSlottedFlow(i, d); + + Lgrate = InletDesigns[i].grateInlet.length; + if (Lgrate > 0.0) findOnSagGrateFlows(i, d, &Qgw, &Qgo); + + Lcurb = InletDesigns[i].curbInlet.length; + if (Lcurb > 0.0) + { + Lsweep = Lcurb - Lgrate; + if (Lsweep > 0.0) findOnSagCurbFlows(i, d, Lsweep, &Qsw, &Qso); + if (Qgo > 0.0) findOnSagCurbFlows(i, d, Lgrate, &Qcw, &Qco); + } + return Qgw + Qgo + Qsw + Qso + Qco; +} + +//============================================================================= + +void findOnSagGrateFlows(int i, double d, double *Qw, double *Qo) +// +// Input: i = inlet type index +// d = water level seen by inlet (ft) +// Output: Qw = flow captured in weir mode (cfs) +// Qo = flow captured in orifice mode (cfs) +// Purpose: finds the flow captured by an on-sag grate inlet. +// +{ + int grateType = InletDesigns[i].grateInlet.type; + double Lg = InletDesigns[i].grateInlet.length; + double Wg = InletDesigns[i].grateInlet.width; + double P, // grate perimeter (ft) + Ao, // grate opening area (ft2) + di; // average flow depth across grate (ft) + + // --- for drop grate inlets + if (InletDesigns[i].type == DROP_GRATE_INLET) + { + di = d; + P = 2.0 * (Lg + Wg); + } + + // --- for gutter grate inlets: + else + { + // --- check for spread within grate width + if (d <= Wg * Sw) + Wg = d / Sw; + + // --- avergage depth over grate + di = d - (Wg / 2.0) * Sw; + + // --- effective grate perimeter + P = Lg + 2.0 * Wg; + } + + if (grateType == GENERIC) + Ao = Lg * Wg * InletDesigns[i].grateInlet.fracOpenArea; + else + Ao = Lg * Wg * GrateOpeningRatios[grateType]; + + // --- weir flow applies (based on depth where result of + // weir eqn. equals result of orifice eqn.) + + if (d <= 1.79 * Ao / P) + { + *Qw = 3.0 * P * pow(di, 1.5); //HEC-22 Eq(4-26) + } + + // --- orifice flow applies + else + { + *Qo = 0.67 * Ao * sqrt(2.0 * GRAVITY * di); //HEC-22 Eq(4-27) + } +} + +//============================================================================= + +void findOnSagCurbFlows(int i, double d, double L, double *Qw, double *Qo) +// +// Input: i = inlet type index +// d = water level seen by inlet (ft) +// L = length of curb opening (ft) +// Output: Qw = flow captured in weir mode (cfs) +// Qo = flow captured in orifice mode (cfs) +// Purpose: finds the flow captured by an on-sag curb opening inlet. +// +{ + int throatAngle = InletDesigns[i].curbInlet.throatAngle; + double h = InletDesigns[i].curbInlet.height; + double Qweir, Qorif, P; + double dweir, dorif, r; + + // --- check for orifice flow + if (L <= 0.0) return; + if (InletDesigns[i].type == DROP_CURB_INLET) L = L * 4.0; + dorif = 1.4 * h; + if (d > dorif) + { + *Qo = getCurbOrificeFlow(d, h, L, throatAngle); + return; + } + + // --- for uniform cross slope or very long opening + if (a == 0.0 || L > 12.0) + { + // --- check for weir flow + dweir = h; + if (d < dweir) + { + *Qw = 3.0 * L * pow(d, 1.5); //HEC-22 Eq(4-30) + return; + } + else Qweir = 3.0 * L * pow(dweir, 1.5); + } + + // --- for depressed gutter + else + { + // --- check for weir flow + P = L + 1.8 * W; + dweir = h + a; + if (d < dweir) + { + *Qw = 2.3 * P * pow(d, 1.5); //HEC-22 Eq(4-28) + return; + } + else Qweir = 2.3 * P * pow(dweir, 1.5); + } + + // --- interpolate between Qweir at depth dweir and Qorif at depth dorif + Qorif = getCurbOrificeFlow(dorif, h, L, throatAngle); + r = (d - dweir) / (dorif - dweir); + *Qw = (1.0 -r) * Qweir; + *Qo = r * Qorif; +} + +//============================================================================= + +double getCurbOrificeFlow(double di, double h, double L, int throatAngle) +// +// Input: di = water level at lip of inlet opening (ft) +// h = height of curb opening (ft) +// L = length of curb opening (ft) +// throatAngle = type of throat angle in curb opening +// Output: return flow captured by inlet (cfs) +// Purpose: finds the flow captured by an on-sag curb opening inlet under +// orifice flow conditions. +// +{ + double d = di; + if (throatAngle == HORIZONTAL_THROAT) + d = di - h / 2.0; + else if (throatAngle == INCLINED_THROAT) + d = di + (h / 2.0) * 0.7071; + return 0.67 * h * L * sqrt(2.0 * GRAVITY * d); //HEC-22 Eq(4-31a) +} + +//============================================================================= + +double getOnSagSlottedFlow(int i, double d) +// +// Input: i = inlet type index +// d = water level seen by inlet (ft) +// Output: returns captured flow rate (cfs) +// Purpose: finds the flow captured by an on-sag slotted inlet. +// +// Note: weir flow = orifice flow at d = 2.587 * inlet width +{ + double L = InletDesigns[i].slottedInlet.length; + double w = InletDesigns[i].slottedInlet.width; + + if (d <= 2.587 * w) + return 2.48 * L * pow(d, 1.5); //HEC-22 Eq(4-32) + else + return 0.8 * L * w * sqrt(64.32 * d); //HEC-22 Eq(4-33) +} + +//============================================================================= + +void getBackflowRatios() +// +// Input: none +// Output: overflow ratio for each inlet +// Purpose: finds the fraction of the overflow produced by an inlet's capture +// node that becomes backflow into the inlet. +// +// Note: when a capture node receives flow from two or more inlets +// its backflow is divided among the inlets based on: +// i) the fraction of total open area for standard inlets +// ii) the fraction of total number of inlets for custom inlets +{ + TInlet* inlet; + double area; + double f; + int n; + + // --- info for each node receiving flow from an inlet + typedef struct + { + int numInletLinks; // total # inlet links + int numStdInletLinks; // total # standard inlet links + int numCustomInlets; // # custom inlets + double totalInletArea; // open area of standard inlets + } TInletNode; + TInletNode* inletNodes = (TInletNode *) calloc(Nobjects[NODE], sizeof(TInletNode)); + if (inletNodes == NULL) return; + + // --- Finds each inlet's contribution to its capture node + for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet) + { + n = inlet->nodeIndex; + inletNodes[n].numInletLinks++; + area = getInletArea(inlet); + if (area > 0.0) + { + inletNodes[n].numStdInletLinks++; + inletNodes[n].totalInletArea += area; + } + else + inletNodes[n].numCustomInlets += inlet->numInlets; + } + + // --- find fraction of capture node's overflow that becomes inlet backflow + for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet) + { + // --- f is ratio of links with standard inlets to all inlet links + // connected to receptor node n + n = inlet->nodeIndex; + f = (double) inletNodes[n].numStdInletLinks / + (double) inletNodes[n].numInletLinks; + + // --- backflow ratio depends if inlet is standard or custom (area = 0) + area = getInletArea(inlet); + if (area == 0.0) + inlet->backflowRatio = (double)inlet->numInlets / + (double)inletNodes[n].numCustomInlets * (1. - f); + else + inlet->backflowRatio = area / inletNodes[n].totalInletArea * f; + } + free(inletNodes); +} + +//============================================================================= + +double getInletArea(TInlet* inlet) +// +// Input: inlet = an inlet object placed in a conduit link +// Output: returns the unclogged open area of the inlet (ft2) +// Purpose: finds the total open flow area inlets placed in a conduit. +// +{ + double area = 0.0; + double curbLength; + int i = inlet->designIndex; + int grateType = InletDesigns[i].grateInlet.type; + + if (InletDesigns[i].grateInlet.length > 0.0) + { + area = InletDesigns[i].grateInlet.length * InletDesigns[i].grateInlet.width; + if (grateType == GENERIC) + area *= InletDesigns[i].grateInlet.fracOpenArea; + else + area *= GrateOpeningRatios[grateType]; + } + + curbLength = InletDesigns[i].curbInlet.length - InletDesigns[i].grateInlet.length; + if (curbLength > 0.0) + area += curbLength * InletDesigns[i].curbInlet.height; + + if (InletDesigns[i].slottedInlet.length > 0.0) + area = InletDesigns[i].slottedInlet.length * InletDesigns[i].slottedInlet.width; + return area * inlet->numInlets * inlet->clogFactor; +} + +//============================================================================= + +double getCustomCapturedFlow(TInlet* inlet, double q, double d) +{ + int i = inlet->designIndex; // inlet's position in InletDesigns array + int j; // counter for replicate inlets + int sides = 1; // number of sides for inlet's street (1 or 2) + int c; // an index into the Curve array + double qApproach, // inlet's approach flow (cfs) + qBypassed, // inlet's bypassed flow (cfs) + qCaptured, // inlet's captured flow (cfs) + qIncrement, // increment to captured flow (cfs) + qMax = HUGE; // user-supplied flow capture limit (cfs) + + if (inlet->numInlets == 0) return 0.0; + + // --- set limit on max. flow captured per inlet + qMax = BIG; + if (inlet->flowLimit > 0.0) qMax = inlet->flowLimit; + + // --- get number of sides to a street xsection + xsect = &Link[inlet->linkIndex].xsect; + if (xsect->type == STREET_XSECT) + sides = Street[xsect->transect].sides; + + // --- adjust flow for 2-sided street + qApproach = q / sides; + qBypassed = qApproach; + qCaptured = 0.0; + + // --- get index of inlet's capture curve + c = InletDesigns[i].customCurve; + if (c >= 0) + { + // --- curve is captured flow v. approach flow + if (Curve[c].curveType == DIVERSION_CURVE) + { + // --- add up incrmental capture of each replicate inlet + for (j = 1; j <= inlet->numInlets; j++) + { + qIncrement = inlet->clogFactor * + table_lookupEx(&Curve[c], qBypassed * UCF(FLOW)) / UCF(FLOW); + qIncrement = MIN(qIncrement, qMax); + qIncrement = MIN(qIncrement, qBypassed); + qCaptured += qIncrement; + qBypassed -= qIncrement; + if (qBypassed < MIN_RUNOFF_FLOW) break; + } + } + + // --- curve is captured flow v. downstream node depth + else if (Curve[c].curveType == RATING_CURVE) + { + qCaptured = inlet->numInlets * inlet->clogFactor * + table_lookupEx(&Curve[c], d * UCF(LENGTH)) / UCF(FLOW); + } + qCaptured *= sides; + } + return qCaptured; +} diff --git a/src/solver/inlet.h b/src/solver/inlet.h new file mode 100644 index 000000000..ca7830725 --- /dev/null +++ b/src/solver/inlet.h @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// inlet.h +// +// Project: EPA SWMM5 +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman +// +// Street/Channel Inlet Functions +// +//----------------------------------------------------------------------------- +#ifndef INLET_H +#define INLET_H + +typedef struct TInlet TInlet; + +int inlet_create(int nInlets); +void inlet_delete(); +int inlet_readDesignParams(char* tok[], int ntoks); +int inlet_readUsageParams(char* tok[], int ntoks); +void inlet_validate(); + +void inlet_findCapturedFlows(double tStep); +void inlet_adjustQualInflows(); +void inlet_adjustQualOutflows(); + +void inlet_writeStatsReport(); +double inlet_capturedFlow(int link); + +#endif diff --git a/src/solver/input.c b/src/solver/input.c index 390b6f088..3613297b9 100644 --- a/src/solver/input.c +++ b/src/solver/input.c @@ -2,29 +2,28 @@ // input.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 09/15/14 (Build 5.1.007) -// 08/01/16 (Build 5.1.011) -// 04/01/20 (Build 5.1.015) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Input data processing functions. // +// Update History +// ============== // Build 5.1.007: // - Support added for climate adjustment input data. -// // Build 5.1.011: // - Support added for reading hydraulic event dates. -// // Build 5.1.015: // - Support added for multiple infiltration methods within a project. +// Build 5.2.0: +// - Support added for Streets and Inlets. +// - Support added for named variables & math expressions in control rules. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE #include #include -#include #include #include "headers.h" #include "lid.h" @@ -87,14 +86,15 @@ int input_countObjects() for (i = 0; i < MAX_OBJ_TYPES; i++) Nobjects[i] = 0; for (i = 0; i < MAX_NODE_TYPES; i++) Nnodes[i] = 0; for (i = 0; i < MAX_LINK_TYPES; i++) Nlinks[i] = 0; + controls_init(); // --- make pass through data file counting number of each object while ( fgets(line, MAXLINE, Finp.file) != NULL ) { // --- skip blank lines & those beginning with a comment lineCount++; - strcpy(wLine, line); // make working copy of line - tok = strtok(wLine, SEPSTR); // get first text token on line + sstrncpy(wLine, line, MAXLINE); // make working copy of line + tok = strtok(wLine, SEPSTR); // get first text token on line if ( tok == NULL ) continue; if ( *tok == ';' ) continue; @@ -176,7 +176,7 @@ int input_readData() { // --- make copy of line and scan for tokens lineCount++; - strcpy(wLine, line); + sstrncpy(wLine, line, MAXLINE); Ntokens = getTokens(wLine); // --- skip blank lines and comments @@ -184,12 +184,12 @@ int input_readData() if ( *Tok[0] == ';' ) continue; // --- check if max. line length exceeded - lineLength = strlen(line); + lineLength = (int)strlen(line); if ( lineLength >= MAXLINE ) { // --- don't count comment if present comment = strchr(line, ';'); - if ( comment ) lineLength = comment - line; // Pointer math here + if ( comment ) lineLength = (int)(comment - line); // Pointer math here if ( lineLength >= MAXLINE ) { inperr = ERR_LINE_LENGTH; @@ -407,6 +407,7 @@ int addObject(int objType, char* id) case s_CONTROL: if ( match(id, w_RULE) ) Nobjects[CONTROL]++; + else controls_addToCount(id); break; case s_TRANSECT: @@ -436,6 +437,22 @@ int addObject(int objType, char* id) break; case s_EVENT: NumEvents++; break; + + case s_STREET: + if ( !project_addObject(STREET, id, Nobjects[STREET]) ) + errcode = error_setInpError(ERR_DUP_NAME, id); + Nobjects[STREET]++; + break; + + case s_INLET: + // --- an INLET object can span several lines + if (project_findObject(INLET, id) < 0) + { + if ( !project_addObject(INLET, id, Nobjects[INLET]) ) + errcode = error_setInpError(ERR_DUP_NAME, id); + Nobjects[INLET]++; + } + break; } return errcode; } @@ -481,7 +498,7 @@ int parseLine(int sect, char *line) return subcatch_readSubareaParams(Tok, Ntokens); case s_INFIL: - return infil_readParams(InfilModel, Tok, Ntokens); //(5.1.015) + return infil_readParams(InfilModel, Tok, Ntokens); case s_AQUIFER: j = Mobjects[AQUIFER]; @@ -600,6 +617,15 @@ int parseLine(int sect, char *line) case s_EVENT: return readEvent(Tok, Ntokens); + case s_STREET: + return street_readParams(Tok, Ntokens); + + case s_INLET: + return inlet_readDesignParams(Tok, Ntokens); + + case s_INLET_USAGE: + return inlet_readUsageParams(Tok, Ntokens); + default: return 0; } } @@ -620,6 +646,11 @@ int readControl(char* tok[], int ntoks) // --- check for minimum number of tokens if ( ntoks < 2 ) return error_setInpError(ERR_ITEMS, ""); + if (match(tok[0], w_VARIABLE)) + return controls_addVariable(tok, ntoks); + if (match(tok[0], w_EXPRESSION)) + return controls_addExpression(tok, ntoks); + // --- get index of control rule keyword keyword = findmatch(tok[0], RuleKeyWords); if ( keyword < 0 ) return error_setInpError(ERR_KEYWORD, tok[0]); @@ -672,7 +703,7 @@ int readTitle(char* line) if ( strlen(Title[i]) == 0 ) { // --- strip line feed character from input line - n = strlen(line); + n = (int)strlen(line); if (line[n-1] == 10) line[n-1] = ' '; // --- copy input line into Title entry @@ -771,19 +802,19 @@ int match(char *str, char *substr) // (not case sensitive). // { - int i,j; + int i,j,k; // --- fail if substring is empty if (!substr[0]) return(0); // --- skip leading blanks of str - for (i = 0; str[i]; i++) + for (k = 0; str[k]; k++) { - if (str[i] != ' ') break; + if (str[k] != ' ') break; } // --- check if substr matches remainder of str - for (i = i,j = 0; substr[j]; i++,j++) + for (i = k,j = 0; substr[j]; i++,j++) { if (!str[i] || UCHAR(str[i]) != UCHAR(substr[j])) return(0); } @@ -858,7 +889,8 @@ int getTokens(char *s) // in CONSTS.H. Text between quotes is treated as a single token. // { - int len, m, n; + int n; + size_t len, m; char *c; // --- begin with no tokens @@ -868,7 +900,7 @@ int getTokens(char *s) // --- truncate s at start of comment c = strchr(s,';'); if (c) *c = '\0'; - len = strlen(s); + len = (int)strlen(s); // --- scan s for tokens until nothing left while (len > 0 && n < MAXTOKS) @@ -890,7 +922,7 @@ int getTokens(char *s) } len -= m+1; // update length of s } - return(n); + return n; } //============================================================================= diff --git a/src/solver/inputrpt.c b/src/solver/inputrpt.c index 31798c90b..46a6f952f 100644 --- a/src/solver/inputrpt.c +++ b/src/solver/inputrpt.c @@ -2,12 +2,16 @@ // inputrpt.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Report writing functions for input data summary. // +// Update History +// ============== +// Build 5.2.0: +// - Support added for reporting Street geometry tables. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -237,6 +241,9 @@ void inputrpt_writeInput() else if ( Link[i].xsect.type == IRREGULAR ) fprintf(Frpt.file, "%-16s ", Transect[Link[i].xsect.transect].ID); + else if ( Link[i].xsect.type == STREET_XSECT ) + fprintf(Frpt.file, "%-16s ", + Street[Link[i].xsect.transect].ID); else fprintf(Frpt.file, "%-16s ", XsectTypeWords[Link[i].xsect.type]); fprintf(Frpt.file, "%8.2f %8.2f %8.2f %8.2f %3d %8.2f", @@ -281,7 +288,6 @@ void inputrpt_writeInput() } } } - WRITE(""); if (Nobjects[TRANSECT] > 0) { @@ -313,5 +319,36 @@ void inputrpt_writeInput() } } } + + if (Nobjects[STREET] > 0) + { + WRITE(""); + WRITE(""); + WRITE("**************"); + WRITE("Street Summary"); + WRITE("**************"); + for (i = 0; i < Nobjects[STREET]; i++) + { + fprintf(Frpt.file, "\n\n Street %s", Street[i].ID); + fprintf(Frpt.file, "\n Area: "); + for (m = 1; m < Street[i].transect.nTbl; m++) + { + if (m % 5 == 1) fprintf(Frpt.file, "\n "); + fprintf(Frpt.file, "%10.4f ", Street[i].transect.areaTbl[m]); + } + fprintf(Frpt.file, "\n Hrad: "); + for (m = 1; m < Street[i].transect.nTbl; m++) + { + if (m % 5 == 1) fprintf(Frpt.file, "\n "); + fprintf(Frpt.file, "%10.4f ", Street[i].transect.hradTbl[m]); + } + fprintf(Frpt.file, "\n Width: "); + for (m = 1; m < Street[i].transect.nTbl; m++) + { + if (m % 5 == 1) fprintf(Frpt.file, "\n "); + fprintf(Frpt.file, "%10.4f ", Street[i].transect.widthTbl[m]); + } + } + } WRITE(""); } diff --git a/src/solver/keywords.c b/src/solver/keywords.c index 7ae5b2d5a..17973d182 100644 --- a/src/solver/keywords.c +++ b/src/solver/keywords.c @@ -2,14 +2,8 @@ // keywords.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.000) -// 04/14/14 (Build 5.1.004) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) -// 05/10/18 (Build 5.1.013) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Exportable keyword dictionary @@ -19,24 +13,27 @@ // must be terminated by NULL. The actual text of each keyword // is defined in text.h. // +// Update History +// ============== // Build 5.1.007: // - Keywords for Ignore RDII option and groundwater flow equation // and climate adjustment input sections added. -// // Build 5.1.008: // - Keyword arrays placed in alphabetical order for better readability. // - Keywords added for Minimum Routing Step and Number of Threads options. -// // Build 5.1.010: // - New Modified Green Ampt keyword added to InfilModelWords. // - New Roadway weir keyword added to WeirTypeWords. -// // Build 5.1.011: // - New section keyword for [EVENTS] added. -// // Build 5.1.013: // - New option keywords w_SURCHARGE_METHOD, w_RULE_STEP, w_AVERAGES // and w_WEIR added. +// Build 5.2.0: +// - Support added for Streets and Inlets. +// - Support added for variable speed pumps. +// - Support added for analytical storage shapes. +// - Support added for RptFlags.disabled option. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -45,8 +42,9 @@ char* BuildupTypeWords[] = { w_NONE, w_POW, w_EXP, w_SAT, w_EXT, NULL}; char* CurveTypeWords[] = { w_STORAGE, w_DIVERSION, w_TIDAL, w_RATING, - w_CONTROLS, w_SHAPE, w_WEIR, //(5.1.013) - w_PUMP1, w_PUMP2, w_PUMP3, w_PUMP4, NULL}; + w_CONTROLS, w_SHAPE, w_WEIR, + w_PUMP1, w_PUMP2, w_PUMP3, w_PUMP4, + w_PUMP5, NULL}; char* DividerTypeWords[] = { w_CUTOFF, w_TABULAR, w_WEIR, w_OVERFLOW, NULL}; char* EvapTypeWords[] = { w_CONSTANT, w_MONTHLY, w_TIMESERIES, w_TEMPERATURE, w_FILE, w_RECOVERY, @@ -79,7 +77,7 @@ char* OptionWords[] = { w_FLOW_UNITS, w_INFIL_MODEL, w_REPORT_START_TIME, w_SWEEP_START, w_SWEEP_END, w_START_DRY_DAYS, w_WET_STEP, w_DRY_STEP, - w_ROUTE_STEP, w_RULE_STEP, //(5.1.013) + w_ROUTE_STEP, w_RULE_STEP, w_REPORT_STEP, w_ALLOW_PONDING, w_INERT_DAMPING, w_SLOPE_WEIGHTING, w_VARIABLE_STEP, @@ -93,7 +91,7 @@ char* OptionWords[] = { w_FLOW_UNITS, w_INFIL_MODEL, w_MAX_TRIALS, w_HEAD_TOL, w_SYS_FLOW_TOL, w_LAT_FLOW_TOL, w_IGNORE_RDII, w_MIN_ROUTE_STEP, - w_NUM_THREADS, w_SURCHARGE_METHOD, //(5.1.013) + w_NUM_THREADS, w_SURCHARGE_METHOD, NULL }; char* OrificeTypeWords[] = { w_SIDE, w_BOTTOM, NULL}; char* OutfallTypeWords[] = { w_FREE, w_NORMAL, w_FIXED, w_TIDAL, @@ -101,14 +99,16 @@ char* OutfallTypeWords[] = { w_FREE, w_NORMAL, w_FIXED, w_TIDAL, char* PatternTypeWords[] = { w_MONTHLY, w_DAILY, w_HOURLY, w_WEEKEND, NULL}; char* PondingUnitsWords[] = { w_PONDED_FEET, w_PONDED_METERS }; char* ProcessVarWords[] = { w_HRT, w_DT, w_FLOW, w_DEPTH, w_AREA, NULL}; -char* PumpTypeWords[] = { w_TYPE1, w_TYPE2, w_TYPE3, w_TYPE4, w_IDEAL }; +char* PumpTypeWords[] = { w_TYPE1, w_TYPE2, w_TYPE3, w_TYPE4, w_TYPE5, w_IDEAL }; char* QualUnitsWords[] = { w_MGperL, w_UGperL, w_COUNTperL, NULL}; char* RainTypeWords[] = { w_INTENSITY, w_VOLUME, w_CUMULATIVE, NULL}; char* RainUnitsWords[] = { w_INCHES, w_MMETER, NULL}; -char* RelationWords[] = { w_TABULAR, w_FUNCTIONAL, NULL}; -char* ReportWords[] = { w_INPUT, w_CONTINUITY, w_FLOWSTATS, - w_CONTROLS, w_SUBCATCH, w_NODE, w_LINK, - w_NODESTATS, w_AVERAGES, NULL}; //(5.1.013) +char* RelationWords[] = { w_TABULAR, w_FUNCTIONAL, + w_CYLINDRICAL, w_CONICAL, w_PARABOLIC, + w_PYRAMIDAL, NULL}; +char* ReportWords[] = { w_DISABLED, w_INPUT, w_SUBCATCH, w_NODE, w_LINK, + w_CONTINUITY, w_FLOWSTATS,w_CONTROLS, + w_AVERAGES, w_NODESTATS, NULL}; char* RouteModelWords[] = { w_NONE, w_STEADY, w_KINWAVE, w_XKINWAVE, w_DYNWAVE, NULL}; char* RuleKeyWords[] = { w_RULE, w_IF, w_AND, w_OR, w_THEN, w_ELSE, @@ -140,9 +140,10 @@ char* SectWords[] = { ws_TITLE, ws_OPTION, ws_MAP, ws_LID_CONTROL, ws_LID_USAGE, ws_GWF, ws_ADJUST, ws_EVENT, - NULL}; + ws_STREET, ws_INLET_USAGE, + ws_INLET, NULL}; char* SnowmeltWords[] = { w_PLOWABLE, w_IMPERV, w_PERV, w_REMOVAL, NULL}; -char* SurchargeWords[] = { w_EXTRAN, w_SLOT, NULL}; //(5.1.013) +char* SurchargeWords[] = { w_EXTRAN, w_SLOT, NULL}; char* TempKeyWords[] = { w_TIMESERIES, w_FILE, w_WINDSPEED, w_SNOWMELT, w_ADC, NULL}; char* TransectKeyWords[] = { w_NC, w_X1, w_GR, NULL}; @@ -165,4 +166,5 @@ char* XsectTypeWords[] = { w_DUMMY, w_CIRCULAR, w_CATENARY, w_SEMIELLIPTICAL, w_BASKETHANDLE, w_SEMICIRCULAR, w_IRREGULAR, w_CUSTOM, - w_FORCE_MAIN, NULL}; + w_FORCE_MAIN, w_STREET, + NULL}; diff --git a/src/solver/keywords.h b/src/solver/keywords.h index af89e0d58..e330c59bc 100644 --- a/src/solver/keywords.h +++ b/src/solver/keywords.h @@ -2,17 +2,16 @@ // keywords.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/19/14 (Build 5.1.000) -// 03/19/15 (Build 5.1.008) -// 05/10/18 (Build 5.1.013) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Exportable keyword dictionary // +// Update History +// ============== // Build 5.1.008: // - Keyword arrays listed in alphabetical order. -// // Build 5.1.013: // - New keyword array defined for surcharge method. //----------------------------------------------------------------------------- @@ -59,7 +58,7 @@ extern char* RouteModelWords[]; extern char* RuleKeyWords[]; extern char* SectWords[]; extern char* SnowmeltWords[]; -extern char* SurchargeWords[]; //(5.1.013) +extern char* SurchargeWords[]; extern char* TempKeyWords[]; extern char* TransectKeyWords[]; extern char* TreatTypeWords[]; diff --git a/src/solver/kinwave.c b/src/solver/kinwave.c index c0fdf6fce..b137e4ce9 100644 --- a/src/solver/kinwave.c +++ b/src/solver/kinwave.c @@ -2,21 +2,19 @@ // kinwave.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 03/19/15 (Build 5.1.008) -// 03/01/20 (Build 5.1.014) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // M. Tryby (EPA) // // Kinematic wave flow routing functions. // +// Update History +// ============== // Build 5.1.008: // - Conduit inflow passed to function that computes conduit losses. -// // Build 5.1.014: // - Arguments to function link_getLossRate changed. -// //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -102,7 +100,7 @@ int kinwave_execute(int j, double* qinflow, double* qoutflow, double tStep) qin = (*qinflow) / Conduit[k].barrels / Qfull; // --- compute evaporation and infiltration loss rate - q3 = link_getLossRate(j, qin*Qfull) / Qfull; //(5.1.014) + q3 = link_getLossRate(j, qin*Qfull) / Qfull; // --- normalize previous areas a1 = Conduit[k].a1 / Afull; diff --git a/src/solver/landuse.c b/src/solver/landuse.c index 6c237dd55..f0342a96a 100644 --- a/src/solver/landuse.c +++ b/src/solver/landuse.c @@ -2,19 +2,19 @@ // landuse.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 03/19/15 (Build 5.1.008) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Pollutant buildup and washoff functions. // +// Update History +// ============== // Build 5.1.008: // - landuse_getWashoffMass() re-named to landuse_getWashoffQual() and // modified to return concentration instead of mass load. // - landuse_getRunoffLoad() re-named to landuse_getWashoffLoad() and // modified to work with landuse_getWashoffQual(). -// //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -41,8 +41,6 @@ //----------------------------------------------------------------------------- static double landuse_getBuildupDays(int landuse, int pollut, double buildup); static double landuse_getBuildupMass(int landuse, int pollut, double days); -static double landuse_getRunoffLoad(int landuse, int pollut, double area, - TLandFactor landFactor[], double runoff, double tStep); static double landuse_getWashoffQual(int landuse, int pollut, double buildup, double runoff, double area); static double landuse_getExternalBuildup(int i, int p, double buildup, @@ -201,7 +199,7 @@ int landuse_readBuildupParams(char* tok[], int ntoks) // { int i, j, k, n, p; - double c[3], tmax; + double c[3] = {0, 0, 0}, tmax; if ( ntoks < 3 ) return 0; j = project_findObject(LANDUSE, tok[0]); diff --git a/src/solver/lid.c b/src/solver/lid.c index 45cb020f1..d9a0c7e78 100644 --- a/src/solver/lid.c +++ b/src/solver/lid.c @@ -2,19 +2,9 @@ // lid.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 05/19/14 (Build 5.1.006) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 04/30/15 (Build 5.1.009) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// 03/01/20 (Build 5.1.014) -// 04/01/20 (Build 5.1.015) -// Author: L. Rossman (US EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // // This module handles all data processing involving LID (Low Impact // Development) practices used to treat runoff for individual subcatchments @@ -43,44 +33,41 @@ // levels for a specific LID unit to be written to a text file named by the // user for viewing outside of the SWMM program. // +// Update History +// ============== // Build 5.1.008: // - More input error reporting added. // - Rooftop Disconnection added to the types of LIDs. // - LID drain flows are now tracked separately. // - LID drain flows can now be routed to separate outlets. // - Check added to insure LID flows not returned to nonexistent pervious area. -// // Build 5.1.009: // - Fixed bug where LID's could return outflow to non-LID area when LIDs // make up entire subcatchment. -// // Build 5.1.010: // - Support for new Modified Green Ampt infiltration model added. // - Imported variable HasWetLids now properly initialized. // - Initial state of reporting (lidUnit->rptFile->wasDry) changed to // prevent duplicate printing of first line of detailed report file. -// // Build 5.1.011: // - The top of the storage layer is no longer used as a limit for an // underdrain offset thus allowing upturned drains to be modeled. // - Column headings for the detailed LID report file were modified. -// // Build 5.1.012: // - Redefined initialization of wasDry for LID reporting. -// // Build 5.1.013: // - Support added for LID units treating pervious area runoff. // - Support added for open/closed head levels and multiplier v. head // control curve for underdrain flow. // - Support added for unclogging permeable pavement at fixed intervals. // - Support added for pollutant removal in underdrain flow. -// // Build 5.1.014: // - Fixed bug in creating LidProcs when there are no subcatchments. // - Fixed bug in adding underdrain pollutant loads to mass balances. -// // Build 5.1.015: // - Support added for mutiple infiltration methods within a project. +// Build 5.2.0: +// - Covered property added to RAIN_BARREL parameters //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -94,7 +81,7 @@ #define ERR_SWALE_SURF " - check swale surface parameters" #define ERR_GREEN_AMPT " - check subcatchment Green-Ampt parameters" #define ERR_DRAIN_OFFSET " - drain offset exceeds storage height" -#define ERR_DRAIN_HEADS " - invalid drain open/closed heads" //(5.1.013) +#define ERR_DRAIN_HEADS " - invalid drain open/closed heads" #define ERR_SWALE_WIDTH " - invalid swale width" //----------------------------------------------------------------------------- @@ -107,14 +94,14 @@ enum LidLayerTypes { PAVE, // pavement layer DRAINMAT, // drainage mat layer DRAIN, // underdrain system - REMOVALS}; // pollutant removals //(5.1.013) + REMOVALS}; // pollutant removals //// Note: DRAINMAT must be placed before DRAIN so the two keywords can /// be distinguished from one another when parsing a line of input. char* LidLayerWords[] = {"SURFACE", "SOIL", "STORAGE", "PAVEMENT", "DRAINMAT", "DRAIN", - "REMOVALS", NULL}; //(5.1.013) + "REMOVALS", NULL}; char* LidTypeWords[] = {"BC", //bio-retention cell @@ -130,7 +117,7 @@ char* LidTypeWords[] = //----------------------------------------------------------------------------- // Data Structures //----------------------------------------------------------------------------- -// LID List - list of LID units contained in an LID group +// OWA EDIT - LidList and LidGroup struct defs moved to lid.h to be shared by toolkit.c //----------------------------------------------------------------------------- // Shared Variables @@ -189,6 +176,7 @@ extern char HasWetLids; // TRUE if any LIDs are wet // lid_writeSummary called by inputrpt_writeInput // lid_writeWaterBalance called by statsrpt_writeReport +// OWA function additions ################################################# // lid_getLidUnitCount called by LID API toolkit in toolkitAPI.c // lid_getLidUnit called by LID API toolkit in toolkitAPI.c // lid_getLidProc called by LID API toolkit in toolkitAPI.c @@ -198,6 +186,7 @@ extern char HasWetLids; // TRUE if any LIDs are wet // lid_updateLidUnit called by LID API toolkit in toolkitAPI.c // lid_updateAllLidUnit called by LID API toolkit in toolkitAPI.c // lid_updateLidGroup called by LID API toolkit in toolkitAPI.c +// ######################################################################## //----------------------------------------------------------------------------- // Local Functions //----------------------------------------------------------------------------- @@ -208,7 +197,7 @@ static int readSoilData(int j, char* tok[], int ntoks); static int readStorageData(int j, char* tok[], int ntoks); static int readDrainData(int j, char* tok[], int ntoks); static int readDrainMatData(int j, char* toks[], int ntoks); -static int readRemovalsData(int j, char* toks[], int ntoks); //(5.1.013) +static int readRemovalsData(int j, char* toks[], int ntoks); static int addLidUnit(int j, int k, int n, double x[], char* fname, int drainSubcatch, int drainNode); @@ -220,10 +209,12 @@ static void validateLidGroup(int j); static int isLidPervious(int k); static double getImpervAreaRunoff(int j); -static double getPervAreaRunoff(int j); //(5.1.013) +static double getPervAreaRunoff(int j); static double getSurfaceDepth(int subcatch); +static double getRainInflow(int j, TLidUnit* lidUnit); static void findNativeInfil(int j, double tStep); + static void evalLidUnit(int j, TLidUnit* lidUnit, double lidArea, double lidInflow, double tStep, double *qRunoff, double *qDrain, double *qReturn); @@ -284,14 +275,14 @@ void lid_create(int lidCount, int subcatchCount) LidProcs[j].drain.offset = 0.0; LidProcs[j].drainMat.thickness = 0.0; LidProcs[j].drainMat.roughness = 0.0; - LidProcs[j].drainRmvl = NULL; //(5.1.013) - LidProcs[j].drainRmvl = (double *) // - calloc(Nobjects[POLLUT], sizeof(double)); // - if (LidProcs[j].drainRmvl == NULL) // - { // - ErrorCode = ERR_MEMORY; // - return; // - } // + LidProcs[j].drainRmvl = NULL; + LidProcs[j].drainRmvl = (double *) + calloc(Nobjects[POLLUT], sizeof(double)); + if (LidProcs[j].drainRmvl == NULL) + { + ErrorCode = ERR_MEMORY; + return; + } } } @@ -307,7 +298,7 @@ void lid_delete() int j; for (j = 0; j < GroupCount; j++) freeLidGroup(j); FREE(LidGroups); - for (j = 0; j < LidCount; j++) FREE(LidProcs[j].drainRmvl); //(5.1.013) + for (j = 0; j < LidCount; j++) FREE(LidProcs[j].drainRmvl); FREE(LidProcs); GroupCount = 0; LidCount = 0; @@ -365,7 +356,7 @@ int lid_readProcParams(char* toks[], int ntoks) // LID_ID STORAGE // LID_ID DRAIN // LID_ID DRAINMAT -// LID_ID REMOVALS //(5.1.013) +// LID_ID REMOVALS // { int j, m; @@ -401,7 +392,7 @@ int lid_readProcParams(char* toks[], int ntoks) case PAVE: return readPavementData(j, toks, ntoks); case DRAIN: return readDrainData(j, toks, ntoks); case DRAINMAT: return readDrainMatData(j, toks, ntoks); - case REMOVALS: return readRemovalsData(j, toks, ntoks); //(5.1.013) + case REMOVALS: return readRemovalsData(j, toks, ntoks); } return error_setInpError(ERR_KEYWORD, toks[1]); } @@ -417,7 +408,7 @@ int lid_readGroupParams(char* toks[], int ntoks) // // Format of input data line is: // Subcatch_ID LID_ID Number Area Width InitSat FromImp ToPerv -// (RptFile DrainTo FromPerv) //(5.1.013) +// (RptFile DrainTo FromPerv) // where: // Subcatch_ID = name of subcatchment // LID_ID = name of LID process @@ -429,11 +420,11 @@ int lid_readGroupParams(char* toks[], int ntoks) // ToPerv (x[4]) = 1 if outflow goes to pervious sub-area; 0 if not // RptFile = name of detailed results file (optional) // DrainTo = name of subcatch/node for drain flow (optional) -// FromPerv (x[5]) = % of pervious runoff sent to LID //(5.1.013) +// FromPerv (x[5]) = % of pervious runoff sent to LID // { int i, j, k, n; - double x[6]; //(5.1.013) + double x[6]; char* fname = NULL; int drainSubcatch = -1, drainNode = -1; @@ -478,13 +469,13 @@ int lid_readGroupParams(char* toks[], int ntoks) } } - //... read percent of pervious area treated by LID unit //(5.1.013) - x[5] = 0.0; // - if (ntoks >= 11) // - { // - if (!getDouble(toks[10], &x[5]) || x[5] < 0.0 || x[5] > 100.0) // - return error_setInpError(ERR_NUMBER, toks[10]); // - } // + //... read percent of pervious area treated by LID unit + x[5] = 0.0; + if (ntoks >= 11) + { + if (!getDouble(toks[10], &x[5]) || x[5] < 0.0 || x[5] > 100.0) + return error_setInpError(ERR_NUMBER, toks[10]); + } //... create a new LID unit and add it to the subcatchment's LID group return addLidUnit(j, k, n, x, fname, drainSubcatch, drainNode); @@ -545,7 +536,7 @@ int addLidUnit(int j, int k, int n, double x[], char* fname, lidUnit->initSat = x[2] / 100.0; lidUnit->fromImperv = x[3] / 100.0; lidUnit->toPerv = (x[4] > 0.0); - lidUnit->fromPerv = x[5] / 100.0; //(5.1.013) + lidUnit->fromPerv = x[5] / 100.0; lidUnit->drainSubcatch = drainSubcatch; lidUnit->drainNode = drainNode; @@ -584,7 +575,7 @@ int readSurfaceData(int j, char* toks[], int ntoks) // Output: returns error code // // Format of data is: -// LID_ID SURFACE StorageHt VegVolFrac Roughness SurfSlope SideSlope DamHt +// LID_ID SURFACE StorageHt VegVolFrac Roughness SurfSlope SideSlope // { int i; @@ -620,11 +611,11 @@ int readPavementData(int j, char* toks[], int ntoks) // // Format of data is: // LID_ID PAVEMENT Thickness VoidRatio FracImperv Permeability ClogFactor -// (RegenDays RegenDegree) //(5.1.013) +// (RegenDays RegenDegree) // { int i; - double x[7]; //(5.1.013) + double x[7]; if ( ntoks < 7 ) return error_setInpError(ERR_ITEMS, ""); for (i = 2; i < 7; i++) @@ -633,19 +624,19 @@ int readPavementData(int j, char* toks[], int ntoks) return error_setInpError(ERR_NUMBER, toks[i]); } - // ... read optional clogging regeneration properties //(5.1.013) - x[5] = 0.0; // - if (ntoks > 7) // - { // - if (!getDouble(toks[7], &x[5]) || x[5] < 0.0) // - return error_setInpError(ERR_NUMBER, toks[7]); // - } // - x[6] = 0.0; // - if (ntoks > 8) // - { // - if (!getDouble(toks[8], &x[6]) || x[6] < 0.0 || x[6] > 1.0) // - return error_setInpError(ERR_NUMBER, toks[8]); // - } // + // ... read optional clogging regeneration properties + x[5] = 0.0; + if (ntoks > 7) + { + if (!getDouble(toks[7], &x[5]) || x[5] < 0.0) + return error_setInpError(ERR_NUMBER, toks[7]); + } + x[6] = 0.0; + if (ntoks > 8) + { + if (!getDouble(toks[8], &x[6]) || x[6] < 0.0 || x[6] > 1.0) + return error_setInpError(ERR_NUMBER, toks[8]); + } //... convert void ratio to void fraction x[1] = x[1]/(x[1] + 1.0); @@ -655,6 +646,9 @@ int readPavementData(int j, char* toks[], int ntoks) LidProcs[j].pavement.impervFrac = x[2]; LidProcs[j].pavement.kSat = x[3] / UCF(RAINFALL); + // OWA EDIT ########################################################################## + // moved from validateLidProcs in 14d2e62e9a6baad89f6cd2dc0c55907e1d2289b2 + // to allow multiple calls to validateLidProcs when setting Lid params in toolkit if (LidProcs[j].pavement.thickness > 0.0) { LidProcs[j].pavement.clogFactor = x[4] * @@ -666,8 +660,9 @@ int readPavementData(int j, char* toks[], int ntoks) { LidProcs[j].pavement.clogFactor = 0.0; } - LidProcs[j].pavement.regenDays = x[5]; //(5.1.013) - LidProcs[j].pavement.regenDegree = x[6]; // + // ################################################################################## + LidProcs[j].pavement.regenDays = x[5]; + LidProcs[j].pavement.regenDegree = x[6]; return 0; } @@ -717,10 +712,11 @@ int readStorageData(int j, char* toks[], int ntoks) // Output: returns error code // // Format of data is: -// LID_ID STORAGE Thickness VoidRatio Ksat ClogFactor +// LID_ID STORAGE Thickness VoidRatio Ksat ClogFactor (YES/NO) // { int i; + int covered = FALSE; double x[6]; //... read numerical parameters @@ -731,6 +727,13 @@ int readStorageData(int j, char* toks[], int ntoks) return error_setInpError(ERR_NUMBER, toks[i]); } + //... check if rain barrel is covered + if (ntoks > 6) + { + if (match(toks[6], w_YES)) + covered = TRUE; + } + //... convert void ratio to void fraction x[1] = x[1]/(x[1] + 1.0); @@ -739,6 +742,10 @@ int readStorageData(int j, char* toks[], int ntoks) LidProcs[j].storage.voidFrac = x[1]; LidProcs[j].storage.kSat = x[2] / UCF(RAINFALL); + // OWA EDIT ############################################################################ + // moved from validateLidProcs in 14d2e62e9a6baad89f6cd2dc0c55907e1d2289b2 + // to allow multiple calls to validateLidProcs when setting Lid params in toolkit + if (LidProcs[j].storage.thickness > 0.0) { LidProcs[j].storage.clogFactor = x[3] * @@ -749,7 +756,8 @@ int readStorageData(int j, char* toks[], int ntoks) { LidProcs[j].storage.clogFactor = 0.0; } - + // #################################################################################### + LidProcs[j].storage.covered = covered; return 0; } @@ -765,36 +773,36 @@ int readDrainData(int j, char* toks[], int ntoks) // Output: returns error code // // Format of data is: -// LID_ID DRAIN coeff expon offset delay hOpen hClose curve //(5.1.013) +// LID_ID DRAIN coeff expon offset delay hOpen hClose curve // { int i; - double x[6]; //(5.1.013) + double x[6]; //... read numerical parameters if ( ntoks < 6 ) return error_setInpError(ERR_ITEMS, ""); - for (i = 0; i < 6; i++) x[i] = 0.0; //(5.1.013) - for (i = 2; i < 8; i++) // + for (i = 0; i < 6; i++) x[i] = 0.0; + for (i = 2; i < 8; i++) { - if ( ntoks > i && ! getDouble(toks[i], &x[i-2]) || x[i-2] < 0.0 ) //(5.1.013) + if ( (ntoks > i) && (! getDouble(toks[i], &x[i-2]) || x[i-2]) < 0.0 ) return error_setInpError(ERR_NUMBER, toks[i]); } - i = -1; //(5.1.013) - if ( ntoks >= 9 ) // - { // - i = project_findObject(CURVE, toks[8]); // - if (i < 0) return error_setInpError(ERR_NAME, toks[8]); // - } // + i = -1; + if ( ntoks >= 9 ) + { + i = project_findObject(CURVE, toks[8]); + if (i < 0) return error_setInpError(ERR_NAME, toks[8]); + } //... save parameters to LID drain layer structure LidProcs[j].drain.coeff = x[0]; LidProcs[j].drain.expon = x[1]; LidProcs[j].drain.offset = x[2] / UCF(RAINDEPTH); LidProcs[j].drain.delay = x[3] * 3600.0; - LidProcs[j].drain.hOpen = x[4] / UCF(RAINDEPTH); //(5.1.013) - LidProcs[j].drain.hClose = x[5] / UCF(RAINDEPTH); // - LidProcs[j].drain.qCurve = i; // + LidProcs[j].drain.hOpen = x[4] / UCF(RAINDEPTH); + LidProcs[j].drain.hClose = x[5] / UCF(RAINDEPTH); + LidProcs[j].drain.qCurve = i; return 0; } @@ -834,8 +842,6 @@ int readDrainMatData(int j, char* toks[], int ntoks) //============================================================================= -//// This function was added to release 5.1.013. //// //(5.1.013) - int readRemovalsData(int j, char* toks[], int ntoks) // // Purpose: reads pollutant removal data for a LID process from line of input @@ -915,10 +921,10 @@ void lid_writeSummary() k = lidUnit->lidIndex; pctArea = lidUnit->area * lidUnit->number / Subcatch[j].area * 100.0; fprintf(Frpt.file, "\n %-16s %-16s", Subcatch[j].ID, LidProcs[k].ID); - fprintf(Frpt.file, "%6d %10.2f %10.2f %10.2f %10.2f %10.2f", //(5.1.013) + fprintf(Frpt.file, "%6d %10.2f %10.2f %10.2f %10.2f %10.2f", lidUnit->number, lidUnit->area * SQR(UCF(LENGTH)), lidUnit->fullWidth * UCF(LENGTH), pctArea, - lidUnit->fromImperv*100.0, lidUnit->fromPerv*100.0); //(5.1.013) + lidUnit->fromImperv*100.0, lidUnit->fromPerv*100.0); lidList = lidList->nextLidUnit; } } @@ -990,8 +996,8 @@ void validateLidProc(int j) || LidProcs[j].pavement.impervFrac > 1.0 ) { - strcpy(Msg, LidProcs[j].ID); - strcat(Msg, ERR_PAVE_LAYER); + sstrncpy(Msg, LidProcs[j].ID, MAXMSG); + sstrcat(Msg, ERR_PAVE_LAYER, MAXMSG); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } } @@ -1005,8 +1011,8 @@ void validateLidProc(int j) || LidProcs[j].soil.kSat <= 0.0 || LidProcs[j].soil.kSlope < 0.0 ) { - strcpy(Msg, LidProcs[j].ID); - strcat(Msg, ERR_SOIL_LAYER); + sstrncpy(Msg, LidProcs[j].ID, MAXMSG); + sstrcat(Msg, ERR_SOIL_LAYER, MAXMSG); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } } @@ -1017,8 +1023,8 @@ void validateLidProc(int j) if ( LidProcs[j].storage.voidFrac <= 0.0 || LidProcs[j].storage.voidFrac > 1.0 ) { - strcpy(Msg, LidProcs[j].ID); - strcat(Msg, ERR_STOR_LAYER); + sstrncpy(Msg, LidProcs[j].ID, MAXMSG); + sstrcat(Msg, ERR_STOR_LAYER, MAXMSG); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } } @@ -1030,14 +1036,14 @@ void validateLidProc(int j) LidProcs[j].drain.offset = 0.0; } - //... check for invalid drain open/closed heads //(5.1.013) - if (LidProcs[j].drain.hOpen > 0.0 && // - LidProcs[j].drain.hOpen <= LidProcs[j].drain.hClose) // - { // - strcpy(Msg, LidProcs[j].ID); // - strcat(Msg, ERR_DRAIN_HEADS); // - report_writeErrorMsg(ERR_LID_PARAMS, Msg); // - } // + //... check for invalid drain open/closed heads + if (LidProcs[j].drain.hOpen > 0.0 && + LidProcs[j].drain.hOpen <= LidProcs[j].drain.hClose) + { + sstrncpy(Msg, LidProcs[j].ID, MAXMSG); + sstrcat(Msg, ERR_DRAIN_HEADS, MAXMSG); + report_writeErrorMsg(ERR_LID_PARAMS, Msg); + } //... compute the surface layer's overland flow constant (alpha) if ( LidProcs[j].lidType == VEG_SWALE ) @@ -1047,8 +1053,8 @@ void validateLidProc(int j) LidProcs[j].surface.thickness == 0.0 ) { - strcpy(Msg, LidProcs[j].ID); - strcat(Msg, ERR_SWALE_SURF); + sstrncpy(Msg, LidProcs[j].ID, MAXMSG); + sstrcat(Msg, ERR_SWALE_SURF, MAXMSG); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } else LidProcs[j].surface.alpha = @@ -1072,6 +1078,10 @@ void validateLidProc(int j) } else LidProcs[j].drainMat.alpha = 0.0; + // OWA EDIT ########################################################################## + // moved clogging factor conversion logic to readStorageData and readPavementData + // in 14d2e62e9a6baad89f6cd2dc0c55907e1d2289b2 to allow multiple calls to validateLidProc + // in toolkit.c //... for certain LID types, immediate overflow of excess surface water // occurs if either the surface roughness or slope is zero LidProcs[j].surface.canOverflow = TRUE; @@ -1118,7 +1128,7 @@ void validateLidGroup(int j) double totalArea = Subcatch[j].area; double totalLidArea = 0.0; double fromImperv = 0.0; - double fromPerv = 0.0; //(5.1.013) + double fromPerv = 0.0; TLidUnit* lidUnit; TLidList* lidList; TLidGroup lidGroup; @@ -1134,7 +1144,7 @@ void validateLidGroup(int j) //... update contributing fractions totalLidArea += (lidUnit->area * lidUnit->number); fromImperv += lidUnit->fromImperv; - fromPerv += lidUnit->fromPerv; //(5.1.013) + fromPerv += lidUnit->fromPerv; //... assign biocell soil layer infiltration parameters lidUnit->soilInfil.Ks = 0.0; @@ -1146,8 +1156,8 @@ void validateLidGroup(int j) (1.0 - lidUnit->initSat); if ( grnampt_setParams(&(lidUnit->soilInfil), p) == FALSE ) { - strcpy(Msg, LidProcs[k].ID); - strcat(Msg, ERR_SOIL_LAYER); + sstrncpy(Msg, LidProcs[k].ID, MAXMSG); + sstrcat(Msg, ERR_SOIL_LAYER, MAXMSG); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } } @@ -1155,21 +1165,21 @@ void validateLidGroup(int j) //... assign vegetative swale infiltration parameters if ( LidProcs[k].lidType == VEG_SWALE ) { - if ( Subcatch[j].infilModel == GREEN_AMPT || //(5.1.015) - Subcatch[j].infilModel == MOD_GREEN_AMPT ) //(5.1.015) + if ( Subcatch[j].infilModel == GREEN_AMPT || + Subcatch[j].infilModel == MOD_GREEN_AMPT ) { - grnampt_getParams(j, p); //(5.1.015) + grnampt_getParams(j, p); if ( grnampt_setParams(&(lidUnit->soilInfil), p) == FALSE ) { - strcpy(Msg, LidProcs[k].ID); - strcat(Msg, ERR_GREEN_AMPT); + sstrncpy(Msg, LidProcs[k].ID, MAXMSG); + sstrcat(Msg, ERR_GREEN_AMPT, MAXMSG); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } } if ( lidUnit->fullWidth <= 0.0 ) { - strcpy(Msg, LidProcs[k].ID); - strcat(Msg, ERR_SWALE_WIDTH); + sstrncpy(Msg, LidProcs[k].ID, MAXMSG); + sstrcat(Msg, ERR_SWALE_WIDTH, MAXMSG); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } } @@ -1192,7 +1202,7 @@ void validateLidGroup(int j) { report_writeErrorMsg(ERR_LID_AREAS, Subcatch[j].ID); } - if ( fromImperv > 1.001 || fromPerv > 1.001 ) //(5.1.013) + if ( fromImperv > 1.001 || fromPerv > 1.001 ) { report_writeErrorMsg(ERR_LID_CAPTURE_AREA, Subcatch[j].ID); } @@ -1243,8 +1253,8 @@ void lid_initState() lidUnit->soilMoisture = 0.0; lidUnit->paveDepth = 0.0; lidUnit->dryTime = initDryTime; - lidUnit->volTreated = 0.0; //(5.1.013) - lidUnit->nextRegenDay = LidProcs[k].pavement.regenDays; // + lidUnit->volTreated = 0.0; + lidUnit->nextRegenDay = LidProcs[k].pavement.regenDays; initVol = 0.0; if ( LidProcs[k].soil.thickness > 0.0 ) { @@ -1269,7 +1279,10 @@ void lid_initState() //... initialize water balance totals lidproc_initWaterBalance(lidUnit, initVol); - //... initialize water rate + // OWA EDIT in 86bccdffbee0d07716b71044e2890deac1529fd3 + // initialize water rate add more output to lid api toolkit + // to show detailed report values / lid layer rates + // lidproc_initWaterRate not defined in EPA SWMM lidproc_initWaterRate(lidUnit); lidUnit->volTreated = 0.0; @@ -1443,8 +1456,6 @@ double lid_getDrainFlow(int j, int timePeriod) //============================================================================= -//// This function was modified for relelase 5.1.013. //// //(5.1.013) - void lid_addDrainLoads(int j, double c[], double tStep) // // Purpose: adds pollutant loads routed from drains to system @@ -1508,11 +1519,11 @@ void lid_addDrainRunon(int j) // Output: none. // { - int i; // index of an LID unit's LID process //(5.1.013) + int i; // index of an LID unit's LID process int k; // index of subcatchment receiving LID drain flow int p; // pollutant index double q; // drain flow rate (cfs) - double w; // mass of polllutant from drain flow //(5.1.013) + double w; // mass of polllutant from drain flow TLidUnit* lidUnit; TLidList* lidList; TLidGroup lidGroup; @@ -1527,7 +1538,7 @@ void lid_addDrainRunon(int j) { //... see if LID's drain discharges to another subcatchment lidUnit = lidList->lidUnit; - i = lidUnit->lidIndex; //(5.1.013) + i = lidUnit->lidIndex; k = lidUnit->drainSubcatch; if ( k >= 0 && k != j ) { @@ -1540,9 +1551,9 @@ void lid_addDrainRunon(int j) // point which is converted later on to a concentration) for (p = 0; p < Nobjects[POLLUT]; p++) { - w = q * Subcatch[j].oldQual[p] * LperFT3; //(5.1.013) - w = w * (1.0 - LidProcs[i].drainRmvl[p]); // - Subcatch[k].newQual[p] += w; // + w = q * Subcatch[j].oldQual[p] * LperFT3; + w = w * (1.0 - LidProcs[i].drainRmvl[p]); + Subcatch[k].newQual[p] += w; } } lidList = lidList->nextLidUnit; @@ -1563,7 +1574,7 @@ void lid_addDrainInflow(int j, double f) // and pollutant mass (Node[].newQual[]) inflow seen by nodes that // receive drain flow from the LID units in subcatchment j. { - int i, // LID process index //(5.1.013) + int i, // LID process index k, // node index p; // pollutant index double q, // drain flow (cfs) @@ -1582,7 +1593,7 @@ void lid_addDrainInflow(int j, double f) { //... see if LID's drain discharges to conveyance system node lidUnit = lidList->lidUnit; - i = lidUnit->lidIndex; //(5.1.013) + i = lidUnit->lidIndex; k = lidUnit->drainNode; if ( k >= 0 ) { @@ -1600,7 +1611,7 @@ void lid_addDrainInflow(int j, double f) //... add interpolated load to node's wet weather loading w = (1.0 - f) * w1 + f * w2; - w = w * (1.0 - LidProcs[i].drainRmvl[p]); //(5.1.013) + w = w * (1.0 - LidProcs[i].drainRmvl[p]); Node[k].newQual[p] += w; massbal_addInflowQual(WET_WEATHER_INFLOW, p, w); } @@ -1626,8 +1637,8 @@ void lid_getRunoff(int j, double tStep) TLidUnit* lidUnit; // a member of the list of LID units double lidArea; // area of an LID unit double qImperv = 0.0; // runoff from impervious areas (cfs) - double qPerv = 0.0; // runoff from pervious areas (cfs) //(5.1.013) - double lidInflow = 0.0; // inflow to an LID unit (ft/s) + double qPerv = 0.0; // runoff from pervious areas (cfs) + double lidInflow = 0.0; // inflow to an LID unit (ft/s) double qRunoff = 0.0; // surface runoff from all LID units (cfs) double qDrain = 0.0; // drain flow from all LID units (cfs) double qReturn = 0.0; // LID outflow returned to pervious area (cfs) @@ -1650,7 +1661,7 @@ void lid_getRunoff(int j, double tStep) if ( Subcatch[j].area > Subcatch[j].lidArea ) { qImperv = getImpervAreaRunoff(j); - qPerv = getPervAreaRunoff(j); //(5.1.013) + qPerv = getPervAreaRunoff(j); } //... evaluate performance of each LID unit placed in the subcatchment @@ -1664,14 +1675,14 @@ void lid_getRunoff(int j, double tStep) if ( lidArea > 0.0 ) { //... find runoff from non-LID area treated by LID area (ft/sec) - lidInflow = (qImperv * lidUnit->fromImperv + //(5.1.013) - qPerv * lidUnit->fromPerv) / lidArea; // + lidInflow = (qImperv * lidUnit->fromImperv + + qPerv * lidUnit->fromPerv) / lidArea; //... update total runoff volume treated VlidIn += lidInflow * lidArea * tStep; //... add rainfall onto LID inflow (ft/s) - lidInflow = lidInflow + Subcatch[j].rainfall; + lidInflow = lidInflow + getRainInflow(j, lidUnit); // ... add upstream runon only if LID occupies full subcatchment if ( Subcatch[j].area == Subcatch[j].lidArea ) @@ -1724,7 +1735,7 @@ void findNativeInfil(int j, double tStep) NativeInfil = infil_getInfil(j, tStep, Subcatch[j].rainfall, Subcatch[j].runon, - getSurfaceDepth(j)); //(5.1.015) + getSurfaceDepth(j)); } //... see if there is any groundwater-imposed limit on infil. @@ -1737,6 +1748,23 @@ void findNativeInfil(int j, double tStep) //============================================================================= +double getRainInflow(int j, TLidUnit* lidUnit) +// +// Purpose: gets rainfall inflow to an LID unit. +// Input: j = subcatchment index +// lidUnit = ptr. to an LID unit +// Output: returns rainfall rate over the LID unit (ft/sec) +// +{ + TLidProc* lidProc = &LidProcs[lidUnit->lidIndex]; + + if (lidProc->lidType == RAIN_BARREL && + lidProc->storage.covered == TRUE) return 0.0; + return Subcatch[j].rainfall; +} + +//============================================================================= + double getImpervAreaRunoff(int j) // // Purpose: computes runoff from impervious area of a subcatchment that @@ -1767,8 +1795,6 @@ double getImpervAreaRunoff(int j) //============================================================================= -//// This function was added for release 5.1.013. //// //(5.1.013) - double getPervAreaRunoff(int j) // // Purpose: computes runoff from pervious area of a subcatchment that @@ -2016,9 +2042,11 @@ void initLidRptFile(char* title, char* lidID, char* subcatchID, TLidUnit* lidUni //... initialize LID dryness state lidUnit->rptFile->wasDry = 1; - strcpy(lidUnit->rptFile->results, ""); + sstrncpy(lidUnit->rptFile->results, "", 0); } +// OWA EDIT ######################################################## +// additional lid getter and setter functions for toolkit api int lid_getLidUnitCount(int index) // Input: index = Index of desired subcatchment @@ -2064,7 +2092,7 @@ TLidUnit* lid_getLidUnit(int index, int lidIndex, int* errcode) lidGroup = LidGroups[index]; if (!lidGroup) { - *errcode = ERR_API_UNDEFINED_LID; + *errcode = ERR_TKAPI_UNDEFINED_LID; } else { @@ -2078,7 +2106,7 @@ TLidUnit* lid_getLidUnit(int index, int lidIndex, int* errcode) if (lidIndex > (unitCount - 1)) { - *errcode = ERR_API_LIDUNIT_INDEX; + *errcode = ERR_TKAPI_LIDUNIT_INDEX; return(NULL); } else @@ -2097,7 +2125,7 @@ TLidUnit* lid_getLidUnit(int index, int lidIndex, int* errcode) // Verify that the lid unit found matches the one specified by the user if (!((currLidIndex - 1) == lidIndex)) { - *errcode = ERR_API_LIDUNIT_INDEX; + *errcode = ERR_TKAPI_LIDUNIT_INDEX; lidUnit = NULL; } } diff --git a/src/solver/lid.h b/src/solver/lid.h index d36ecda72..c3ff11592 100644 --- a/src/solver/lid.h +++ b/src/solver/lid.h @@ -2,28 +2,23 @@ // lid.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 03/19/15 (Build 5.1.008) -// 08/01/16 (Build 5.1.011) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// Author: L. Rossman (US EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // // Public interface for LID functions. // +// Update History +// ============== // Build 5.1.008: // - Support added for Roof Disconnection LID. // - Support added for separate routing of LID drain flows. // - Detailed LID reporting modified. -// // Build 5.1.011: // - Water depth replaces moisture content for LID's pavement layer. // - Arguments for lidproc_saveResults() modified. -// // Build 5.1.012: // - Redefined meaning of wasDry in TLidRptFile structure. -// // Build 5.1.013: // - New member fromPerv added to TLidUnit structure to allow LID // units to also treat pervious area runoff. @@ -35,13 +30,13 @@ // pollutant removal values. // - New members added to TPavementLayer and TLidUnit to support // unclogging permeable pavement at fixed intervals. -// +// Build 5.2.0: +// - Covered property added to RAIN_BARREL parameters //----------------------------------------------------------------------------- #ifndef LID_H #define LID_H - #include #include #include @@ -89,8 +84,8 @@ typedef struct double impervFrac; // impervious area fraction double kSat; // permeability (ft/sec) double clogFactor; // clogging factor - double regenDays; // clogging regeneration interval (days) //(5.1.013) - double regenDegree; // degree of clogging regeneration // + double regenDays; // clogging regeneration interval (days) + double regenDegree; // degree of clogging regeneration } TPavementLayer; // LID Soil Layer @@ -112,6 +107,7 @@ typedef struct double voidFrac; // void volume / total volume double kSat; // saturated hydraulic conductivity (ft/sec) double clogFactor; // clogging factor + int covered; // TRUE if rain barrel is covered } TStorageLayer; // Underdrain System (part of Storage Layer) @@ -121,9 +117,9 @@ typedef struct double expon; // underdrain head exponent (for in or mm) double offset; // offset height of underdrain (ft) double delay; // rain barrel drain delay time (sec) - double hOpen; // head when drain opens (ft) //(5.1.013) - double hClose; // head when drain closes (ft) // - int qCurve; // curve controlling flow rate (optional) // + double hOpen; // head when drain opens (ft) + double hClose; // head when drain closes (ft) + int qCurve; // curve controlling flow rate (optional) } TDrainLayer; // Drainage Mat Layer (for green roofs) @@ -146,7 +142,7 @@ typedef struct TStorageLayer storage; // storage layer parameters TDrainLayer drain; // underdrain system parameters TDrainMatLayer drainMat; // drainage mat layer - double* drainRmvl; // underdrain pollutant removals //(5.1.013) + double* drainRmvl; // underdrain pollutant removals } TLidProc; // Water Balance Statistics @@ -161,7 +157,10 @@ typedef struct double finalVol; // final stored volume (ft) } TWaterBalance; -// +// OWA EDIT ################################################################################## +// OWA SWMM exposes additional data variables used to compute the water balance of LID Units. +// Those variables may be found in lidproc_getOutflow in lidproc.c and are stored in the waterRate +// prop of lidUnits typedef struct { double evap; // evaporation rate (ft/s) @@ -179,6 +178,7 @@ typedef struct double storageEvap; // evap. rate from storage layer (ft/s) double storageDrain; // underdrain flow rate layer (ft/s) } TWaterRate; +// ########################################################################################### // LID Report File typedef struct @@ -198,7 +198,7 @@ typedef struct double botWidth; // bottom width of single unit (ft) double initSat; // initial saturation of soil & storage layers double fromImperv; // fraction of impervious area runoff treated - double fromPerv; // fraction of pervious area runoff treated //(5.1.013) + double fromPerv; // fraction of pervious area runoff treated int toPerv; // 1 if outflow sent to pervious area; 0 if not int drainSubcatch; // subcatchment receiving drain flow int drainNode; // node receiving drain flow @@ -216,12 +216,15 @@ typedef struct double dryTime; // time since last rainfall (sec) double oldDrainFlow; // previous drain flow (cfs) double newDrainFlow; // current drain flow (cfs) - double volTreated; // total volume treated (ft) //(5.1.013) - double nextRegenDay; // next day when unit regenerated // + double volTreated; // total volume treated (ft) + double nextRegenDay; // next day when unit regenerated TWaterBalance waterBalance; // water balance quantites - TWaterRate waterRate; // water rate within lid layers + TWaterRate waterRate; // OWA Addition - water rate within lid layers } TLidUnit; +// OWA EDIT ################################################################################## +// LidList and LidGroup struct defs moved to lid.h from lid.c to be shared by toolkit.c + // LID List - list of LID units contained in an LID group struct LidList { @@ -241,7 +244,7 @@ struct LidGroup TLidList* lidList; // list of LID units in the group }; typedef struct LidGroup* TLidGroup; - +// ########################################################################################### //----------------------------------------------------------------------------- // LID Methods //----------------------------------------------------------------------------- @@ -266,6 +269,8 @@ void lid_getRunoff(int subcatch, double tStep); void lid_writeSummary(void); void lid_writeWaterBalance(void); +// OWA EDIT ######################################################### +// additional setter and getter functions for toolkit api int lid_getLidUnitCount(int index); TLidUnit* lid_getLidUnit(int index, int lidIndex, int* errcode); TLidProc* lid_getLidProc(int index); @@ -275,10 +280,12 @@ void lid_validateLidGroup(int index); void lid_updateLidUnit(TLidUnit* lidUnit, int subIndex); void lid_updateAllLidUnit(int lidIndex); void lid_updateLidGroup(int index); +void lidproc_initWaterRate(TLidUnit *lidUnit); +// ################################################################## //----------------------------------------------------------------------------- void lidproc_initWaterBalance(TLidUnit *lidUnit, double initVol); -void lidproc_initWaterRate(TLidUnit *lidUnit); + double lidproc_getOutflow(TLidUnit* lidUnit, TLidProc* lidProc, double inflow, double evap, double infil, double maxInfil, double tStep, double* lidEvap, double* lidInfil, double* lidDrain); @@ -286,5 +293,4 @@ double lidproc_getOutflow(TLidUnit* lidUnit, TLidProc* lidProc, void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDepth); - -#endif //LID_H +#endif diff --git a/src/solver/lidproc.c b/src/solver/lidproc.c index 249442509..a74fd67f7 100644 --- a/src/solver/lidproc.c +++ b/src/solver/lidproc.c @@ -2,28 +2,20 @@ // lidproc.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/12 (Build 5.1.001) -// 05/19/14 (Build 5.1.006) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 04/30/15 (Build 5.1.009) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// 03/01/20 (Build 5.1.014) -// Author: L. Rossman (US EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // // This module computes the hydrologic performance of an LID (Low Impact // Development) unit at a given point in time. // +// Update History +// ============== // Build 5.1.007: // - Euler integration now applied to all LID types except Vegetative // Swale which continues to use successive approximation. // - LID layer flux routines were re-written to more accurately model // flooded conditions. -// // Build 5.1.008: // - MAX_STATE_VARS replaced with MAX_LAYERS. // - Optional soil layer added to Porous Pavement LID. @@ -33,13 +25,10 @@ // - Detailed reporting procedure fixed. // - Possibile negative head on Bioretention Cell drain avoided. // - Bug in computing flow through Green Roof drainage mat fixed. -// // Build 5.1.009: // - Fixed typo in net flux rate for vegetative swale LID. -// // Build 5.1.010: // - New modified version of Green-Ampt used for surface layer infiltration. -// // Build 5.1.011: // - Re-named STOR_INFIL to STOR_EXFIL and StorageInfil to StorageExfil to // better reflect their meaning. @@ -48,22 +37,22 @@ // - Flux rate routines for LIDs with underdrains modified to produce more // physically meaningful results. // - Reporting of detailed results re-written. -// // Build 5.1.012: // - Modified upper limit for soil layer percolation. // - Modified upper limit on surface infiltration into rain gardens. // - Modified upper limit on drain flow for LIDs with storage layers. // - Used re-defined wasDry variable for LID reports to fix duplicate lines. -// // Build 5.1.013: // - Support added for open/closed head levels and multiplier v. head curve // to control underdrain flow. // - Support added for regenerating pavement permeability at fixed intervals. -// // Build 5.1.014: // - Fixed failure to initialize all LID layer moisture volumes to 0 before // computing LID unit performance in lidproc_getOutflow. -// +// Build 5.2.0: +// - Fixed failure to account for effect of Impervious Surface Fraction on +// pavement permeability for Permeable Pavement LID +// - Fixed units conversion for pavement depth in detailed report file. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -145,9 +134,10 @@ static double Xold[MAX_LAYERS]; // previous moisture level in LID layers // External Functions (declared in lid.h) //----------------------------------------------------------------------------- // lidproc_initWaterBalance (called by lid_initState) -// lidproc_initWaterRate (called by lid_initState) +// lidproc_initWaterRate (called by lid_initState, OWA addition) // lidproc_getOutflow (called by evalLidUnit in lid.c) // lidproc_saveResults (called by evalLidUnit in lid.c) + //----------------------------------------------------------------------------- // Local Functions //----------------------------------------------------------------------------- @@ -174,18 +164,20 @@ static void updateWaterBalance(TLidUnit *lidUnit, double inflow, double evap, double infil, double surfFlow, double drainFlow, double storage); +// OWA EDIT ################################################################################## +// function to store additional data variables used to compute the water balance of LID Units. static void updateWaterRate(TLidUnit *lidUnit, double evap, double maxNativeInfil, double surfaceInflow, double surfInfil, double surfaceEvap, double surfaceOutflow, double paveEvap, double pavePerc, double soilEvap, double soilPerc, double storageInflow, double storageExfil, double storageEvap, double storageDrain); +// ########################################################################################### static int modpuls_solve(int n, double* x, double* xOld, double* xPrev, double* xMin, double* xMax, double* xTol, double* qOld, double* q, double dt, double omega, void (*derivs)(double*, double*)); - //============================================================================= void lidproc_initWaterBalance(TLidUnit *lidUnit, double initVol) @@ -205,6 +197,9 @@ void lidproc_initWaterBalance(TLidUnit *lidUnit, double initVol) lidUnit->waterBalance.finalVol = initVol; } +// OWA EDIT ################################################################################## +// initilize struct that stores additional data variables used to compute the water balance +// of LID Units. These variables are used in EPA SWMM, but are not stored for each LID Unit void lidproc_initWaterRate(TLidUnit* lidUnit) // // Purpose: initializes the water balance components of a LID unit. @@ -228,6 +223,8 @@ void lidproc_initWaterRate(TLidUnit* lidUnit) lidUnit->waterRate.storageEvap = 0.0; lidUnit->waterRate.storageDrain = 0.0; } +// ########################################################################################### + //============================================================================= double lidproc_getOutflow(TLidUnit* lidUnit, TLidProc* lidProc, double inflow, @@ -408,7 +405,7 @@ void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDe double totalVolume; // total volume stored in LID (ft) double rptVars[MAX_RPT_VARS]; // array of reporting variables int isDry = FALSE; // true if current state of LID is dry - char timeStamp[24]; // date/time stamp + char timeStamp[TIME_STAMP_SIZE + 1]; // date/time stamp double elapsedHrs; // elapsed hours //... find total evap. rate and stored volume @@ -419,18 +416,20 @@ void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDe updateWaterBalance(theLidUnit, SurfaceInflow, totalEvap, StorageExfil, SurfaceOutflow, StorageDrain, totalVolume); - //... update water rate + // OWA EDIT ############################################################### + //... update water rate structs updateWaterRate(theLidUnit, EvapRate, MaxNativeInfil, SurfaceInflow, SurfaceInfil, SurfaceEvap, SurfaceOutflow, PaveEvap, PavePerc, SoilEvap, SoilPerc, StorageInflow, StorageExfil, StorageEvap, StorageDrain); + // ######################################################################## //... check if dry-weather conditions hold if ( SurfaceInflow < MINFLOW && SurfaceOutflow < MINFLOW && StorageDrain < MINFLOW && StorageExfil < MINFLOW && - totalEvap < MINFLOW + totalEvap < MINFLOW ) isDry = TRUE; //... update status of HasWetLids @@ -453,7 +452,7 @@ void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDe //... convert storage results to original units (in or mm) ucf = ucfRainDepth; rptVars[SURF_DEPTH] = theLidUnit->surfaceDepth*ucf; - rptVars[PAVE_DEPTH] = theLidUnit->paveDepth; + rptVars[PAVE_DEPTH] = theLidUnit->paveDepth*ucf; rptVars[SOIL_MOIST] = theLidUnit->soilMoisture; rptVars[STOR_DEPTH] = theLidUnit->storageDepth*ucf; @@ -463,14 +462,15 @@ void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDe if ( !isDry && theLidUnit->rptFile->wasDry > 1) { fprintf(theLidUnit->rptFile->file, "%s", - theLidUnit->rptFile->results); + theLidUnit->rptFile->results); } //... write the current results to a string which is saved between // reporting periods elapsedHrs = NewRunoffTime / 1000.0 / 3600.0; - datetime_getTimeStamp(M_D_Y, getDateTime(NewRunoffTime), 24, timeStamp); - sprintf(theLidUnit->rptFile->results, + datetime_getTimeStamp( + M_D_Y, getDateTime(NewRunoffTime), TIME_STAMP_SIZE, timeStamp); + snprintf(theLidUnit->rptFile->results, sizeof(theLidUnit->rptFile->results), "\n%20s\t %8.3f\t %8.3f\t %8.4f\t %8.3f\t %8.3f\t %8.3f\t %8.3f\t" "%8.3f\t %8.3f\t %8.3f\t %8.3f\t %8.3f\t %8.3f", timeStamp, elapsedHrs, rptVars[0], rptVars[1], rptVars[2], @@ -485,7 +485,7 @@ void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDe if ( theLidUnit->rptFile->wasDry == 0 ) { fprintf(theLidUnit->rptFile->file, "%s", - theLidUnit->rptFile->results); + theLidUnit->rptFile->results); } //... increment the number of successive dry periods @@ -496,8 +496,8 @@ void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDe else { //... write the current results to the report file - fprintf(theLidUnit->rptFile->file, "%s", - theLidUnit->rptFile->results); + fprintf(theLidUnit->rptFile->file, "%s", + theLidUnit->rptFile->results); //... re-set the number of successive dry periods to 0 theLidUnit->rptFile->wasDry = 0; @@ -698,8 +698,7 @@ void biocellFluxRates(double x[], double f[]) maxRate = (soilPorosity - soilTheta) * soilThickness / Tstep + SoilPerc + SoilEvap; SurfaceInfil = MIN(SurfaceInfil, maxRate); - - } + } //... storage & soil layers are full else if ( soilTheta >= soilPorosity && storageDepth >= storageThickness ) @@ -914,10 +913,10 @@ void pavementFluxRates(double x[], double f[]) SurfaceInfil = SurfaceInflow + (SurfaceVolume / Tstep); //... find perc rate out of pavement layer - PavePerc = getPavementPermRate(); + PavePerc = getPavementPermRate() * pervFrac; - //... surface infiltration can't exceed pavement permeability //(5.1.013) - SurfaceInfil = MIN(SurfaceInfil, PavePerc); // + //... surface infiltration can't exceed pavement permeability + SurfaceInfil = MIN(SurfaceInfil, PavePerc); //... limit pavement perc by available water maxRate = PaveVolume/Tstep + SurfaceInfil - PaveEvap; @@ -1217,7 +1216,7 @@ void barrelFluxRates(double x[], double f[]) // { double storageDepth = x[STOR]; - double head; + double head; double maxValue; //... assign values to layer volumes @@ -1233,16 +1232,16 @@ void barrelFluxRates(double x[], double f[]) //... compute outflow if time since last rain exceeds drain delay // (dryTime is updated in lid.evalLidUnit at each time step) if ( theLidProc->drain.delay == 0.0 || - theLidUnit->dryTime >= theLidProc->drain.delay ) - { - head = storageDepth - theLidProc->drain.offset; - if ( head > 0.0 ) - { - StorageDrain = getStorageDrainRate(storageDepth, 0.0, 0.0, 0.0); - maxValue = (head/Tstep); - StorageDrain = MIN(StorageDrain, maxValue); - } - } + theLidUnit->dryTime >= theLidProc->drain.delay ) + { + head = storageDepth - theLidProc->drain.offset; + if ( head > 0.0 ) + { + StorageDrain = getStorageDrainRate(storageDepth, 0.0, 0.0, 0.0); + maxValue = (head/Tstep); + StorageDrain = MIN(StorageDrain, maxValue); + } + } //... limit inflow to available storage StorageInflow = SurfaceInflow; @@ -1392,7 +1391,7 @@ double getStorageDrainRate(double storageDepth, double soilTheta, // layers above it (soil, pavement, and surface in that order) // minus the drain outlet offset. { - int curve = theLidProc->drain.qCurve; //(5.1.013) + int curve = theLidProc->drain.qCurve; double head = storageDepth; double outflow = 0.0; double paveThickness = theLidProc->pavement.thickness; @@ -1432,13 +1431,13 @@ double getStorageDrainRate(double storageDepth, double soilTheta, } } - // --- no outflow if: //(5.1.013) - // a) no prior outflow and head below open threshold // - // b) prior outflow and head below closed threshold // - if ( theLidUnit->oldDrainFlow == 0.0 && // - head <= theLidProc->drain.hOpen ) return 0.0; // - if ( theLidUnit->oldDrainFlow > 0.0 && // - head <= theLidProc->drain.hClose ) return 0.0; // + // --- no outflow if: + // a) no prior outflow and head below open threshold + // b) prior outflow and head below closed threshold + if ( theLidUnit->oldDrainFlow == 0.0 && + head <= theLidProc->drain.hOpen ) return 0.0; + if ( theLidUnit->oldDrainFlow > 0.0 && + head <= theLidProc->drain.hClose ) return 0.0; // --- make head relative to drain offset head -= theLidProc->drain.offset; @@ -1455,7 +1454,7 @@ double getStorageDrainRate(double storageDepth, double soilTheta, pow(head, theLidProc->drain.expon); // --- apply user-supplied control curve to outflow - if (curve >= 0) outflow *= table_lookup(&Curve[curve], head); //(5.1.013) + if (curve >= 0) outflow *= table_lookup(&Curve[curve], head); // --- convert outflow to ft/s outflow /= UCF(RAINFALL); @@ -1559,11 +1558,10 @@ void updateWaterBalance(TLidUnit *lidUnit, double inflow, double evap, // surfFlow = surface runoff from the unit (ft/s) // drainFlow = underdrain flow from the unit // storage = volume of water stored in the unit (ft) -// tstep = current time step (s) // Output: none // { - lidUnit->volTreated += inflow * Tstep; //(5.1.013) + lidUnit->volTreated += inflow * Tstep; lidUnit->waterBalance.inflow += inflow * Tstep; lidUnit->waterBalance.evap += evap * Tstep; lidUnit->waterBalance.infil += infil * Tstep; @@ -1571,7 +1569,7 @@ void updateWaterBalance(TLidUnit *lidUnit, double inflow, double evap, lidUnit->waterBalance.drainFlow += drainFlow * Tstep; lidUnit->waterBalance.finalVol = storage; } - +// OWA EDIT ################################################################################## void updateWaterRate(TLidUnit *lidUnit, double evap, double maxNativeInfil, double surfaceInflow, double surfaceInfil, double surfaceEvap, double surfaceOutflow, double paveEvap, double pavePerc, double soilEvap, double soilPerc, @@ -1612,6 +1610,8 @@ void updateWaterRate(TLidUnit *lidUnit, double evap, double maxNativeInfil, lidUnit->waterRate.storageEvap = storageEvap; lidUnit->waterRate.storageDrain = storageDrain; } +// ########################################################################################### + //============================================================================= int modpuls_solve(int n, double* x, double* xOld, double* xPrev, @@ -1677,4 +1677,4 @@ int modpuls_solve(int n, double* x, double* xOld, double* xPrev, //... no convergence so return 0 return 0; -} \ No newline at end of file +} diff --git a/src/solver/link.c b/src/solver/link.c index 3abc2f5f8..6de3688d7 100644 --- a/src/solver/link.c +++ b/src/solver/link.c @@ -2,54 +2,46 @@ // link.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// 03/01/20 (Build 5.1.014) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // M. Tryby (EPA) // // Conveyance system link functions // +// Update History +// ============== // Build 5.1.007: // - Optional surcharging of weirs introduced. -// // Build 5.1.008: // - Bug in finding flow through surcharged weir fixed. // - Bug in finding if conduit is upstrm/dnstrm full fixed. // - Monthly conductivity adjustment applied to conduit seepage. // - Conduit seepage limited by conduit's flow rate. -// // Build 5.1.010: // - Support added for new ROADWAY_WEIR object. // - Time of last setting change initialized for links. -// // Build 5.1.011: // - Crest elevation of regulator links raised to downstream invert. // - Fixed converting roadWidth weir parameter to internal units. // - Weir shape parameter deprecated. // - Extra geometric parameters ignored for non-conduit open rectangular // cross sections. -// // Build 5.1.012: // - Conduit seepage rate now based on flow width, not wetted perimeter. // - Formula for side flow weir corrected. // - Crest length contraction adjustments corrected. -// // Build 5.1.013: // - Maximum depth adjustments made for storage units that can surcharge. // - Support added for head-dependent weir coefficient curves. // - Adjustment of regulator link crest offset to match downstream node invert // now only done for Dynamic Wave flow routing. -// // Build 5.1.014: // - Conduit evap. and seepage losses initialized to 0 in conduit_initState() // and not allowed to exceed current flow rate in conduit_getLossRate(). +// Build 5.2.0: +// - Support added for Streets and Inlets. +// - Support added for variable speed pumps. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -57,6 +49,7 @@ #include #include #include "headers.h" +#include "inlet.h" //----------------------------------------------------------------------------- // Constants @@ -102,7 +95,7 @@ static double conduit_getLength(int j); static double conduit_getLengthFactor(int j, int k, double roughness); static double conduit_getSlope(int j); static double conduit_getInflow(int j); -static double conduit_getLossRate(int j, double q); //(5.1.014) +static double conduit_getLossRate(int j, double q); static int pump_readParams(int j, int k, char* tok[], int ntoks); static void pump_validate(int j, int k); @@ -166,13 +159,19 @@ int link_readXsectParams(char* tok[], int ntoks) // Output: returns an error code // Purpose: reads a link's cross section parameters from a tokenized // line of input data. +// Formats: +// Link Shape Geom1 Geom2 Geom3 Geom4 (Barrels Culvert) +// Link IRREGULAR TransectID +// Link STREET StreetID // { int i, j, k; double x[4]; + // --- check for minimum number of tokens + if (ntoks < 3) return error_setInpError(ERR_ITEMS, ""); + // --- get index of link - if ( ntoks < 6 ) return error_setInpError(ERR_ITEMS, ""); j = project_findObject(LINK, tok[0]); if ( j < 0 ) return error_setInpError(ERR_NAME, tok[0]); @@ -193,9 +192,24 @@ int link_readXsectParams(char* tok[], int ntoks) if ( i < 0 ) return error_setInpError(ERR_NAME, tok[2]); Link[j].xsect.type = k; Link[j].xsect.transect = i; + return 0; } + + // --- for street cross section, find index of Street object + else if (k == STREET_XSECT) + { + i = project_findObject(STREET, tok[2]); + if (i < 0) return error_setInpError(ERR_NAME, tok[2]); + Link[j].xsect.type = k; + Link[j].xsect.transect = i; + return 0; + } + else { + // --- check that geometric parameters are present + if (ntoks < 6) return error_setInpError(ERR_ITEMS, ""); + // --- parse max. depth & shape curve for a custom shape if ( k == CUSTOM ) { @@ -241,7 +255,6 @@ int link_readXsectParams(char* tok[], int ntoks) if ( i < 0 ) return error_setInpError(ERR_NUMBER, tok[7]); else Link[j].xsect.culvertCode = i; } - } return 0; } @@ -360,7 +373,7 @@ void link_setParams(int j, int type, int n1, int n2, int k, double x[]) Weir[k].canSurcharge = (int)x[6]; Weir[k].roadWidth = x[7] / UCF(LENGTH); Weir[k].roadSurface = (int)x[8]; - Weir[k].cdCurve = (int)x[9]; //(5.1.013) + Weir[k].cdCurve = (int)x[9]; break; case OUTLET: @@ -407,13 +420,13 @@ void link_validate(int j) if ( Node[Link[j].node1].invertElev + Link[j].offset1 < Node[Link[j].node2].invertElev ) { - if (RouteModel == DW) //(5.1.013) + if (RouteModel == DW) { Link[j].offset1 = Node[Link[j].node2].invertElev - Node[Link[j].node1].invertElev; - report_writeWarningMsg(WARN10b, Link[j].ID); //(5.1.013) + report_writeWarningMsg(WARN10b, Link[j].ID); } - else report_writeWarningMsg(WARN10a, Link[j].ID); //(5.1.013) + else report_writeWarningMsg(WARN10a, Link[j].ID); } } @@ -427,7 +440,7 @@ void link_validate(int j) // --- extend upstream node's full depth to link's crown elevation n = Link[j].node1; - if ( Node[n].type != STORAGE || Node[n].surDepth > 0.0 ) //(5.1.013) + if ( Node[n].type != STORAGE || Node[n].surDepth > 0.0 ) { Node[n].fullDepth = MAX(Node[n].fullDepth, Link[j].offset1 + Link[j].xsect.yFull); @@ -435,7 +448,7 @@ void link_validate(int j) // --- do same for downstream node only for conduit links n = Link[j].node2; - if ( (Node[n].type != STORAGE || Node[n].surDepth > 0.0) && //(5.1.013) + if ( (Node[n].type != STORAGE || Node[n].surDepth > 0.0) && Link[j].type == CONDUIT ) { Node[n].fullDepth = MAX(Node[n].fullDepth, @@ -514,7 +527,7 @@ void link_initState(int j) { Link[j].oldQual[p] = 0.0; Link[j].newQual[p] = 0.0; - Link[j].totalLoad[p] = 0.0; + Link[j].totalLoad[p] = 0.0; } } @@ -870,7 +883,7 @@ double link_getPower(int j) //============================================================================= -double link_getLossRate(int j, double q) //(5.1.014) +double link_getLossRate(int j, double q) // // Input: j = link index // q = flow rate (ft3/sec) @@ -880,7 +893,7 @@ double link_getLossRate(int j, double q) / // evaporation and seepage. // { - if ( Link[j].type == CONDUIT ) return conduit_getLossRate(j, q); //(5.1.014) + if ( Link[j].type == CONDUIT ) return conduit_getLossRate(j, q); else return 0.0; } @@ -999,6 +1012,13 @@ void conduit_validate(int j, int k) Conduit[k].roughness = Transect[Link[j].xsect.transect].roughness; } + // --- if street xsection, then set its parameters + if (Link[j].xsect.type == STREET_XSECT) + { + xsect_setStreetXsectParams(&Link[j].xsect); + Conduit[k].roughness = Street[Link[j].xsect.transect].roughness; + } + // --- if force main xsection, adjust units on D-W roughness height if ( Link[j].xsect.type == FORCE_MAIN ) { @@ -1031,7 +1051,7 @@ void conduit_validate(int j, int k) report_writeWarningMsg(WARN03, Link[j].ID); Link[j].offset1 = 0.0; } - if ( Link[j].offset2 < 0.0 ) + if ( Link[j].offset2 < 0.0 ) { report_writeWarningMsg(WARN03, Link[j].ID); Link[j].offset2 = 0.0; @@ -1282,8 +1302,8 @@ void conduit_initState(int j, int k) { Link[j].newDepth = link_getYnorm(j, Link[j].q0 / Conduit[k].barrels); Link[j].oldDepth = Link[j].newDepth; - Conduit[k].evapLossRate = 0.0; //(5.1.014) - Conduit[k].seepLossRate = 0.0; //(5.1.014) + Conduit[k].evapLossRate = 0.0; + Conduit[k].seepLossRate = 0.0; } //============================================================================= @@ -1302,8 +1322,6 @@ double conduit_getInflow(int j) //============================================================================= -//// This function was modified for relese 5.1.014. //// //(5.1.014) - double conduit_getLossRate(int j, double q) // // Input: j = link index @@ -1457,7 +1475,7 @@ void pump_validate(int j, int k) else { if ( Curve[m].curveType < PUMP1_CURVE || - Curve[m].curveType > PUMP4_CURVE ) + Curve[m].curveType > PUMP5_CURVE ) report_writeErrorMsg(ERR_NO_CURVE, Link[j].ID); // --- store pump curve type with pump's parameters @@ -1521,6 +1539,7 @@ double pump_getInflow(int j) int n1, n2; double vol, depth, head; double qIn, qIn1, dh = 0.001; + double s = 1.0; // speed setting k = Link[j].subIndex; m = Pump[k].pumpCurve; @@ -1558,23 +1577,22 @@ double pump_getInflow(int j) break; case PUMP3_CURVE: - head = ( (Node[n2].newDepth + Node[n2].invertElev) - - (Node[n1].newDepth + Node[n1].invertElev) ); - - head = MAX(head, 0.0); - - qIn = table_lookup(&Curve[m], head*UCF(LENGTH)) / UCF(FLOW); - - // --- compute dQ/dh (slope of pump curve) and - // reverse sign since flow decreases with increasing head - Link[j].dqdh = -table_getSlope(&Curve[m], head*UCF(LENGTH)) * - UCF(LENGTH) / UCF(FLOW); - - // --- check if off of pump curve - head *= UCF(LENGTH); - if ( head < Pump[k].xMin || head > Pump[k].xMax ) - Link[j].flowClass = YES; - break; + case PUMP5_CURVE: + if (Curve[m].curveType == PUMP5_CURVE) s = Link[j].setting; + head = ((Node[n2].newDepth + Node[n2].invertElev) - + (Node[n1].newDepth + Node[n1].invertElev)) / s / s; + head = MAX(head, 0.0) * UCF(LENGTH); + qIn = table_lookup(&Curve[m], head) / UCF(FLOW); + + // --- compute dQ/dh (slope of pump curve) and + // reverse sign since flow decreases with increasing head + Link[j].dqdh = -table_getSlope(&Curve[m], head) * + UCF(LENGTH) / UCF(FLOW) / s; + + // --- check if off of pump curve + if (head < Pump[k].xMin || head > Pump[k].xMax) + Link[j].flowClass = YES; + break; case PUMP4_CURVE: depth = Node[n1].newDepth; @@ -1984,7 +2002,7 @@ int weir_readParams(int j, int k, char* tok[], int ntoks) { int m; int n1, n2; - double x[10]; //(5.1.013) + double x[10]; char* id; // --- check for valid ID and end node IDs @@ -2011,7 +2029,7 @@ int weir_readParams(int j, int k, char* tok[], int ntoks) x[6] = 1.0; x[7] = 0.0; x[8] = 0.0; - x[9] = -1.0; //(5.1.013) + x[9] = -1.0; if ( ntoks >= 7 && *tok[6] != '*' ) { m = findmatch(tok[6], NoYesWords); @@ -2050,12 +2068,12 @@ int weir_readParams(int j, int k, char* tok[], int ntoks) } } - if (ntoks >= 13 && *tok[12] != '*') //(5.1.013) - { // - m = project_findObject(CURVE, tok[12]); // coeff. curve // - if (m < 0) return error_setInpError(ERR_NAME, tok[12]); // - x[9] = m; // - } // + if (ntoks >= 13 && *tok[12] != '*') + { + m = project_findObject(CURVE, tok[12]); // coeff. curve + if (m < 0) return error_setInpError(ERR_NAME, tok[12]); + x[9] = m; + } // --- add parameters to weir object Link[j].ID = id; @@ -2303,8 +2321,8 @@ void weir_getFlow(int j, int k, double head, double dir, int hasFlapGate, double area; double veloc; int wType; - int cdCurve = Weir[k].cdCurve; //(5.1.013) - double cDisch1 = Weir[k].cDisch1; // + int cdCurve = Weir[k].cdCurve; + double cDisch1 = Weir[k].cDisch1; // --- q1 = flow through central portion of weir, // q2 = flow through end sections of trapezoidal weir @@ -2317,8 +2335,8 @@ void weir_getFlow(int j, int k, double head, double dir, int hasFlapGate, length = Link[j].xsect.wMax * UCF(LENGTH); h = head * UCF(LENGTH); - // --- lookup tabulated discharge coeff. //(5.1.013) - if ( cdCurve >= 0 ) cDisch1 = table_lookup(&Curve[cdCurve], h); // + // --- lookup tabulated discharge coeff. + if ( cdCurve >= 0 ) cDisch1 = table_lookup(&Curve[cdCurve], h); // --- use appropriate formula for weir flow wType = Weir[k].type; @@ -2331,7 +2349,7 @@ void weir_getFlow(int j, int k, double head, double dir, int hasFlapGate, // --- reduce length when end contractions present length -= 0.1 * Weir[k].endCon * h; length = MAX(length, 0.0); - *q1 = cDisch1 * length * pow(h, 1.5); //(5.1.013) + *q1 = cDisch1 * length * pow(h, 1.5); break; case SIDEFLOW_WEIR: @@ -2342,23 +2360,23 @@ void weir_getFlow(int j, int k, double head, double dir, int hasFlapGate, // --- weir behaves as a transverse weir under reverse flow if ( dir < 0.0 ) - *q1 = cDisch1 * length * pow(h, 1.5); //(5.1.013) + *q1 = cDisch1 * length * pow(h, 1.5); else // Corrected formula (see Metcalf & Eddy, Inc., // Wastewater Engineering, McGraw-Hill, 1972 p. 164). - *q1 = cDisch1 * pow(length, 0.83) * pow(h, 1.67); //(5.1.013) + *q1 = cDisch1 * pow(length, 0.83) * pow(h, 1.67); break; case VNOTCH_WEIR: - *q1 = cDisch1 * Weir[k].slope * pow(h, 2.5); //(5.1.013) + *q1 = cDisch1 * Weir[k].slope * pow(h, 2.5); break; case TRAPEZOIDAL_WEIR: y = (1.0 - Link[j].setting) * Link[j].xsect.yFull; length = xsect_getWofY(&Link[j].xsect, y) * UCF(LENGTH); - *q1 = cDisch1 * length * pow(h, 1.5); //(5.1.013) + *q1 = cDisch1 * length * pow(h, 1.5); *q2 = Weir[k].cDisch2 * Weir[k].slope * pow(h, 2.5); } diff --git a/src/solver/macros.h b/src/solver/macros.h index 6f10ae464..c15749e4b 100644 --- a/src/solver/macros.h +++ b/src/solver/macros.h @@ -2,8 +2,8 @@ // macros.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/07 (Build 5.1.001) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman //----------------------------------------------------------------------------- diff --git a/src/solver/massbal.c b/src/solver/massbal.c index 8807d4396..893cf36d6 100644 --- a/src/solver/massbal.c +++ b/src/solver/massbal.c @@ -2,39 +2,30 @@ // massbal.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/19/14 (Build 5.1.001) -// 09/15/14 (Build 5.1.007) -// 04/02/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // M. Tryby (EPA) // // Mass balance functions // +// Update History +// ============== // Build 5.1.007: // - Mass balances modified to to correctly handle negative external inflows. // - Volume from minimum surface area at nodes included in mass balances. -// // Build 5.1.008: // - massbal_updateRunoffTotals() modified. // - LID drain flows and returned outfall flows added to components of // runoff mass balance. // - Seepage pollutant loss added into mass balances. -// // Build 5.1.010: // - Remaining pollutant mass in "dry" elements now added to final storage. -// // Build 5.1.011: // - Final stored pollutant mass in links ignored for Steady Flow routing. -// // Build 5.1.012: // - Terminal storage nodes no longer treated as non-storage terminal // nodes are when updating total outflow volume. -// // Build 5.1.013: // - Volume from MinSurfArea no longer included in initial & final storage. //----------------------------------------------------------------------------- @@ -44,7 +35,6 @@ #include #include #include "headers.h" -#include "swmm5.h" //----------------------------------------------------------------------------- // Constants @@ -626,19 +616,19 @@ void massbal_updateRoutingTotals(double tStep) QualTotals[j].finalStorage += StepQualTotals[j].finalStorage; } - for ( j = 0; j < Nobjects[NODE]; j++) + for (j = 0; j < Nobjects[NODE]; j++) { NodeInflow[j] += Node[j].inflow * tStep; - if ( Node[j].type == OUTFALL || - (Node[j].degree == 0 && Node[j].type != STORAGE) ) + if (Node[j].type == OUTFALL || + (Node[j].degree == 0 && Node[j].type != STORAGE)) { NodeOutflow[j] += Node[j].inflow * tStep; } else { - NodeOutflow[j] += Node[j].outflow * tStep; - if ( Node[j].newVolume <= Node[j].fullVolume ) - NodeOutflow[j] += Node[j].overflow * tStep; + NodeOutflow[j] += Node[j].outflow * tStep; + if (Node[j].newVolume <= Node[j].fullVolume) + NodeOutflow[j] += Node[j].overflow * tStep; } } } @@ -1055,6 +1045,9 @@ double massbal_getStoredMass(int p) return storedMass; } +// OWA EDIT ########################################################################## +// function to return the model routing, runoff, and inflow mass balence during a simulation. +// EPA SWMM computes these values after the simulation is over. int massbal_getRoutingTotal(TRoutingTotals **routingTotal) // // Input: element = element to return @@ -1137,4 +1130,6 @@ int massbal_getNodeTotalInflow(int index, double *value) *value = NodeInflow[index] * UCF(VOLUME); return 0; -} \ No newline at end of file +} + +// ################################################################################### diff --git a/src/solver/mathexpr.c b/src/solver/mathexpr.c index d5e944b85..2ce4d423d 100644 --- a/src/solver/mathexpr.c +++ b/src/solver/mathexpr.c @@ -1,13 +1,14 @@ /****************************************************************************** ** MODULE: MATHEXPR.C -** PROJECT: EPA SWMM 5.1 +** PROJECT: EPA SWMM5 ** DESCRIPTION: Evaluates symbolic mathematical expression consisting ** of numbers, variable names, math functions & arithmetic ** operators. ** AUTHORS: L. Rossman, US EPA - NRMRL ** F. Shang, University of Cincinnati -** VERSION: 5.1.008 -** LAST UPDATE: 04/01/15 +** VERSION: 5.2.0 +** LAST UPDATE: 11/01/21 +** BUG FIXES: Problems related to '^' operator (L.Rossman, 11/01/21) ******************************************************************************/ /* ** Operand codes: @@ -148,7 +149,7 @@ int isLetter(char c) void getToken() { char c[] = " "; - strcpy(Token, ""); + Token[0] = '\0'; while ( Pos <= Len && ( isLetter(S[Pos]) || isDigit(S[Pos]) ) ) { @@ -191,7 +192,7 @@ double getNumber() int errflag = 0; /* --- get whole number portion of number */ - strcpy(sNumber, ""); + sNumber[0] = '\0'; while (Pos < Len && isDigit(S[Pos])) { c[0] = S[Pos]; @@ -327,11 +328,8 @@ ExprTree * newNode() ExprTree * getSingleOp(int *lex) { - int bracket; int opcode; ExprTree *left; - ExprTree *right; - ExprTree *node; /* --- open parenthesis, so continue to grow the tree */ if ( *lex == 1 ) @@ -376,41 +374,6 @@ ExprTree * getSingleOp(int *lex) } } *lex = getLex(); - - /* --- exponentiation */ - while ( *lex == 31 ) - { - *lex = getLex(); - bracket = 0; - if ( *lex == 1 ) - { - bracket = 1; - *lex = getLex(); - } - if ( *lex != 7 ) - { - Err = 1; - return NULL; - } - right = newNode(); - right->opcode = *lex; - right->fvalue = Fvalue; - node = newNode(); - node->left = left; - node->right = right; - node->opcode = 31; - left = node; - if (bracket) - { - *lex = getLex(); - if ( *lex != 2 ) - { - Err = 1; - return NULL; - } - } - *lex = getLex(); - } return left; } @@ -435,7 +398,7 @@ ExprTree * getOp(int *lex) else if ( *lex == 3) *lex = getLex(); } left = getSingleOp(lex); - while ( *lex == 5 || *lex == 6 ) + while ( *lex == 5 || *lex == 6 || *lex == 31) { opcode = *lex; *lex = getLex(); @@ -506,11 +469,14 @@ void traverseTree(ExprTree *tree, MathExpr **expr) traverseTree(tree->left, expr); traverseTree(tree->right, expr); node = (MathExpr *) malloc(sizeof(MathExpr)); - node->fvalue = tree->fvalue; - node->opcode = tree->opcode; - node->ivar = tree->ivar; - node->next = NULL; - node->prev = (*expr); + if (node) + { + node->fvalue = tree->fvalue; + node->opcode = tree->opcode; + node->ivar = tree->ivar; + node->next = NULL; + node->prev = (*expr); + } if (*expr) (*expr)->next = node; (*expr) = node; } @@ -545,27 +511,30 @@ double mathexpr_eval(MathExpr *expr, double (*getVariableValue) (int)) int stackindex = 0; ExprStack[0] = 0.0; - while(node != NULL) + while(node != NULL && stackindex >= 0) { switch (node->opcode) { - case 3: - r1 = ExprStack[stackindex]; + case 3: + r1 = ExprStack[stackindex]; stackindex--; + if (stackindex < 0) break; r2 = ExprStack[stackindex]; ExprStack[stackindex] = r2 + r1; break; case 4: - r1 = ExprStack[stackindex]; + r1 = ExprStack[stackindex]; stackindex--; + if (stackindex < 0) break; r2 = ExprStack[stackindex]; ExprStack[stackindex] = r2 - r1; break; case 5: - r1 = ExprStack[stackindex]; + r1 = ExprStack[stackindex]; stackindex--; + if (stackindex < 0) break; r2 = ExprStack[stackindex]; ExprStack[stackindex] = r2 * r1; break; @@ -573,12 +542,14 @@ double mathexpr_eval(MathExpr *expr, double (*getVariableValue) (int)) case 6: r1 = ExprStack[stackindex]; stackindex--; - r2 = ExprStack[stackindex]; + if (stackindex < 0) break; + r2 = ExprStack[stackindex]; ExprStack[stackindex] = r2 / r1; break; case 7: stackindex++; + if (stackindex >= MAX_STACK_SIZE) break; ExprStack[stackindex] = node->fvalue; break; @@ -589,7 +560,8 @@ double mathexpr_eval(MathExpr *expr, double (*getVariableValue) (int)) } else r1 = 0.0; stackindex++; - ExprStack[stackindex] = r1; + if (stackindex >= MAX_STACK_SIZE) break; + ExprStack[stackindex] = r1; break; case 9: @@ -719,16 +691,20 @@ double mathexpr_eval(MathExpr *expr, double (*getVariableValue) (int)) case 31: r1 = ExprStack[stackindex]; - r2 = ExprStack[stackindex-1]; + stackindex--; + if (stackindex < 0) break; + r2 = ExprStack[stackindex]; if (r2 <= 0.0) r2 = 0.0; - else r2 = exp(r1*log(r2)); - ExprStack[stackindex-1] = r2; - stackindex--; + else r2 = pow(r2, r1); + ExprStack[stackindex] = r2; break; } node = node->next; } - r1 = ExprStack[stackindex]; + if (stackindex >= 0) + r1 = ExprStack[stackindex]; + else + r1 = 0.0; // Set result to 0 if it is NaN due to an illegal math op if ( r1 != r1 ) r1 = 0.0; @@ -759,7 +735,7 @@ MathExpr * mathexpr_create(char *formula, int (*getVar) (char *)) PrevLex = 0; CurLex = 0; S = formula; - Len = strlen(S); + Len = (int)strlen(S); Pos = 0; Bc = 0; tree = getTree(); diff --git a/src/solver/mathexpr.h b/src/solver/mathexpr.h index ab56b7fba..b36d3db1d 100644 --- a/src/solver/mathexpr.h +++ b/src/solver/mathexpr.h @@ -1,6 +1,6 @@ /****************************************************************************** ** MODULE: MATHEXPR.H -** PROJECT: SWMM 5.1 +** PROJECT: SWMM5 ** DESCRIPTION: header file for the math expression parser in mathexpr.c. ** AUTHORS: L. Rossman, US EPA - NRMRL ** F. Shang, University of Cincinnati diff --git a/src/solver/mempool.c b/src/solver/mempool.c index 495447801..d59fb1595 100644 --- a/src/solver/mempool.c +++ b/src/solver/mempool.c @@ -16,7 +16,6 @@ //----------------------------------------------------------------------------- -#include #include #include "mempool.h" diff --git a/src/solver/node.c b/src/solver/node.c index f2445097d..7aaef966b 100644 --- a/src/solver/node.c +++ b/src/solver/node.c @@ -2,40 +2,34 @@ // node.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 09/15/14 (Build 5.1.007) -// 04/02/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 05/10/18 (Build 5.1.013) -// 03/01/20 (Build 5.1.014) -// 04/14/20 (Build 5.1.015) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Conveyance system node functions. // +// Update History +// ============== // Build 5.1.007: // - Ponded area property for storage nodes deprecated. // - Support for Green-Ampt seepage from bottom and sides of storage node added. // - Storage node evap. & seepage losses now computed at start of each routing // time step. -// // Build 5.1.008: -// - Support added for sending outfall discharge to a subctchment. -// +// - Support added for sending outfall discharge to a subcatchment. // Build 5.1.010: // - Storage losses now based on node's new volume instead of old volume. -// // Build 5.1.013: // - A surcharge depth can now be applied to storage nodes. // - A negative inflow is now assigned to an Outfall node with backflow. -// // Build 5.1.014: // - Fixed bug in storage_losses() that affected storage exfiltration. -// // Build 5.1.015: // - Fatal error issued if a storage node's area curve produces a negative // volume when extrapolated to the node's full depth. +// Build 5.2.0: +// - Support added Streets and Inlets. +// - Support added for analytical storage shapes. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -62,7 +56,7 @@ typedef struct // node_initState (called from project_init) // node_setOldHydState (called from routing_execute) // node_setOldQualState (called from routing_execute) -// node_initInflow (called from routing_execute) +// node_initFlows (called from routing_execute) // node_setOutletDepth (called from routing_execute) // node_getLosses (called from routing_execute) // node_getSystemOutflow (called from removeOutflows in routing.c) @@ -142,6 +136,7 @@ void node_setParams(int j, int type, int k, double x[]) Node[j].surDepth = 0.0; Node[j].pondedArea = 0.0; Node[j].degree = 0; + Node[j].inlet = NO_INLET; switch (type) { case JUNCTION: @@ -153,7 +148,7 @@ void node_setParams(int j, int type, int k, double x[]) case OUTFALL: Outfall[k].type = (int)x[1]; - Outfall[k].outfallStage= x[2] / UCF(LENGTH); + Outfall[k].fixedStage = x[2] / UCF(LENGTH); Outfall[k].tideCurve = (int)x[3]; Outfall[k].stageSeries = (int)x[4]; Outfall[k].hasFlapGate = (char)x[5]; @@ -169,15 +164,13 @@ void node_setParams(int j, int type, int k, double x[]) case STORAGE: Node[j].fullDepth = x[1] / UCF(LENGTH); Node[j].initDepth = x[2] / UCF(LENGTH); - Storage[k].aCoeff = x[3]; - Storage[k].aExpon = x[4]; - Storage[k].aConst = x[5]; - Storage[k].aCurve = (int)x[6]; - - // Surcharge depth replaces ponded area //(5.1.013) - Node[j].surDepth = x[7] / UCF(LENGTH); // - - Storage[k].fEvap = x[8]; + Storage[k].shape = (int)x[3]; + Storage[k].a1 = x[4]; + Storage[k].a2 = x[5]; + Storage[k].a0 = x[6]; + Storage[k].aCurve = (int)x[7]; + Node[j].surDepth = x[8] / UCF(LENGTH); + Storage[k].fEvap = x[9]; break; case DIVIDER: @@ -216,10 +209,10 @@ void node_validate(int j) if ( Node[j].initDepth > Node[j].fullDepth + Node[j].surDepth ) report_writeErrorMsg(ERR_NODE_DEPTH, Node[j].ID); - // --- check for negative volume for storage node at full depth //(5.1.015) - if (Node[j].type == STORAGE) // - if (node_getVolume(j, Node[j].fullDepth) < 0.0) // - report_writeErrorMsg(ERR_STORAGE_VOLUME, Node[j].ID); // + // --- check for negative volume for storage node at full depth + if (Node[j].type == STORAGE) + if (node_getVolume(j, Node[j].fullDepth) < 0.0) + report_writeErrorMsg(ERR_STORAGE_VOLUME, Node[j].ID); if ( Node[j].type == DIVIDER ) divider_validate(j); @@ -262,6 +255,7 @@ void node_initState(int j) // --- initialize any inflow Node[j].oldLatFlow = 0.0; Node[j].newLatFlow = 0.0; + Node[j].apiExtInflow = 0.0; Node[j].losses = 0.0; // --- initialize storage nodes @@ -298,6 +292,8 @@ void node_setOldHydState(int j) { Node[j].oldDepth = Node[j].newDepth; Node[j].oldVolume = Node[j].newVolume; + Node[j].oldFlowInflow = Node[j].inflow; + Node[j].oldNetInflow = Node[j].inflow - Node[j].outflow; } //============================================================================= @@ -319,17 +315,15 @@ void node_setOldQualState(int j) //============================================================================= -void node_initInflow(int j, double tStep) +void node_initFlows(int j, double tStep) // // Input: j = node index // tStep = time step (sec) // Output: none -// Purpose: initializes a node's inflow at start of next time step. +// Purpose: initializes a node's inflow/outflow/overflow at start of time step. // { // --- initialize inflow & outflow - Node[j].oldFlowInflow = Node[j].inflow; - Node[j].oldNetInflow = Node[j].inflow - Node[j].outflow; Node[j].inflow = Node[j].newLatFlow; Node[j].outflow = Node[j].losses; @@ -646,7 +640,6 @@ int junc_readParams(int j, int k, char* tok[], int ntoks) return 0; } - //============================================================================= // S T O R A G E M E T H O D S //============================================================================= @@ -661,12 +654,15 @@ int storage_readParams(int j, int k, char* tok[], int ntoks) // Purpose: reads a storage unit's properties from a tokenized line of input. // // Format of input line is: -// nodeID elev maxDepth initDepth FUNCTIONAL a1 a2 a0 surDepth fEvap (infil) //(5.1.013) -// nodeID elev maxDepth initDepth TABULAR curveID surDepth fEvap (infil) // -// +// nodeID elev maxDepth initDepth curveType a1 a2 a0 surDepth fEvap (infil) +// nodeID elev maxDepth initDepth TABULAR curveID surDepth fEvap (infil) +// x[0] x[1] x[2] x[3] x[4..7] x[8] x[9] { int i, m, n; - double x[9]; + double x[10], y[3]; + double A, B; //base semi-axis length & width for conical shape + double L, W; //base length & width for pyramidal shape + double Z; //run over rise for conical & pyramidal sides char* id; // --- get ID name @@ -684,40 +680,106 @@ int storage_readParams(int j, int k, char* tok[], int ntoks) // --- get surf. area relation type m = findmatch(tok[4], RelationWords); if ( m < 0 ) return error_setInpError(ERR_KEYWORD, tok[4]); - x[3] = 0.0; // a1 - x[4] = 0.0; // a2 - x[5] = 0.0; // a0 - x[6] = -1.0; // curveID - x[7] = 0.0; // aPond - x[8] = 0.0; // fEvap + x[3] = m; + x[4] = 0.0; // a1 + x[5] = 0.0; // a2 + x[6] = 0.0; // a0 + x[7] = -1.0; // curveID + x[8] = 0.0; // surDepth + x[9] = 0.0; // fEvap + + // --- get surf. area curve name + if (m == TABULAR) + { + i = project_findObject(CURVE, tok[5]); + if (i < 0) return error_setInpError(ERR_NAME, tok[5]); + x[7] = i; + n = 6; + } // --- get surf. area function coeffs. - if ( m == FUNCTIONAL ) + else { + if (ntoks < 8) return error_setInpError(ERR_ITEMS, ""); for (i=5; i<=7; i++) { - if ( i < ntoks ) - { - if ( ! getDouble(tok[i], &x[i-2]) ) - return error_setInpError(ERR_NUMBER, tok[i]); - } + if ( ! getDouble(tok[i], &y[i-5]) ) + return error_setInpError(ERR_NUMBER, tok[i]); } n = 8; } - // --- get surf. area curve name - else + // --- check for valid data + switch (m) { - m = project_findObject(CURVE, tok[5]); - if ( m < 0 ) return error_setInpError(ERR_NAME, tok[5]); - x[6] = m; - n = 6; + case FUNCTIONAL: + // area at 0 depth can't be negative + if (y[2] < 0.0) return error_setInpError(ERR_NUMBER, tok[7]); + break; + + case CYLINDRICAL: + case CONICAL: + case PARABOLOID: + case PYRAMIDAL: + // length or width can't be <= 0, slope can't be < 0 + if (y[0] <= 0.0 ) return error_setInpError(ERR_NUMBER, tok[5]); + if (y[1] <= 0.0) return error_setInpError(ERR_NUMBER, tok[6]); + if (y[2] < 0.0) return error_setInpError(ERR_NUMBER, tok[7]); + break; + } + // --- height of paraboloid shape can't be 0 + if (m == PARABOLOID && y[2] == 0.0) + return error_setInpError(ERR_NUMBER, tok[7]); + + // --- convert supplied parameters to coeffs. in surface area equation + switch (m) + { + case FUNCTIONAL: + x[4] = y[0]; + x[5] = y[1]; + x[6] = y[2]; + break; + + case CYLINDRICAL: + A = y[0] / 2.; // base semi-axis length + B = y[1] / 2.; // base semi axis width + x[4] = 0.0; // a1 term + x[5] = 0.0; // a2 term + x[6] = PI * A * B; // a0 term + break; + + case CONICAL: + A = y[0] / 2.; // base semi-axis length + B = y[1] / 2.; // base semi axis width + Z = y[2]; // side slope + x[4] = 2.0 * PI * B * Z; // linear coeff. + x[5] = PI * B / A * Z * Z; // quadratic coeff. + x[6] = PI * A * B; // constant + break; + + case PARABOLOID: + A = y[0] / 2.; // top semi-axis length + B = y[1] / 2.; // top semi-axis width + Z = y[2]; // top height + x[4] = PI * A * B / Z; // a1 term + x[5] = 0.0; // a2 term + x[6] = 0.0; // a0 term + break; + + case PYRAMIDAL: + L = y[0]; + W = y[1]; + Z = y[2]; + x[4] = 2.0 * (L + W) * Z; // linear coeff. + x[5] = 4.0 * Z * Z; // quadratic coeff. + x[6] = L * W; // constant + break; } - // --- ponded area replaced by surcharge depth //(5.1.013) + // --- get surcharge depth if present if ( ntoks > n) { - if ( ! getDouble(tok[n], &x[7]) ) + if ( ! getDouble(tok[n], &x[8]) ) return error_setInpError(ERR_NUMBER, tok[n]); n++; } @@ -725,7 +787,7 @@ int storage_readParams(int j, int k, char* tok[], int ntoks) // --- get evaporation fraction if present if ( ntoks > n ) { - if ( ! getDouble(tok[n], &x[8]) ) + if ( ! getDouble(tok[n], &x[9]) ) return error_setInpError(ERR_NUMBER, tok[n]); n++; } @@ -751,8 +813,12 @@ double storage_getDepth(int j, double v) { int k = Node[j].subIndex; int i = Storage[k].aCurve; + int shape = Storage[k].shape; + double a0 = Storage[k].a0; + double a1 = Storage[k].a1; + double a2 = Storage[k].a2; double d, e; - TStorageVol storageVol; + TStorageVol storageVol; // --- return max depth if a max. volume has been computed // and volume is > max. volume @@ -760,35 +826,72 @@ double storage_getDepth(int j, double v) && v >= Node[j].fullVolume ) return Node[j].fullDepth; if ( v == 0.0 ) return 0.0; - // --- use tabular area v. depth curve - if ( i >= 0 ) - return table_getInverseArea(&Curve[i], v*UCF(VOLUME)) / UCF(LENGTH); + // --- convert volume to user's units + v *= UCF(VOLUME); + storageVol.k = k; + storageVol.v = v; - // --- use functional area v. depth relation - else + switch (shape) { - v *= UCF(VOLUME); - if ( Storage[k].aExpon == 0.0 ) + case TABULAR: + i = Storage[k].aCurve; + if (i >= 0) + d = table_getStorageDepth(&Curve[i], v); + else d = 0.0; + break; + + case CYLINDRICAL: + // area = a0; v = a0*d; + d = v / a0; + break; + + case PARABOLOID: + // area = a1*d; v = (a1/2)*d^2 + d = sqrt(2.0 * v / a1); + break; + + case FUNCTIONAL: + // area = a0 + a1; v = (a0 + a1) * d + if (a2 == 0.0) { - d = v / (Storage[k].aConst + Storage[k].aCoeff); + d = v / (a0 + a1); } - else if ( Storage[k].aConst == 0.0 ) + // area = a1*d^a2; v = a1/(a2+1)*d^(a2+1) + else if (a0 == 0.0) { - e = 1.0 / (Storage[k].aExpon + 1.0); - d = pow(v / (Storage[k].aCoeff * e), e); + e = 1.0 / (a2 + 1.0); + d = pow(v / (a1 * e), e); + } + // area = a0 + a1*d; v = a0*d + (a1/2)*d^2 + else if (a2 == 1.0 && a1 > 0.0) + { + d = (sqrt(a0*a0 + 2.*a1*v) - a0) / a1; } else + // area = a0 + a1*d^a2 { - storageVol.k = k; - storageVol.v = v; - d = v / (Storage[k].aConst + Storage[k].aCoeff); + d = v / (a0 + a1); findroot_Newton(0.0, Node[j].fullDepth*UCF(LENGTH), &d, - 0.001, storage_getVolDiff, &storageVol); + 0.001, storage_getVolDiff, &storageVol); } - d /= UCF(LENGTH); - if ( d > Node[j].fullDepth ) d = Node[j].fullDepth; - return d; + break; + + case CONICAL: + case PYRAMIDAL: + // area = a0 + a1*d + a2*d^2; v = a0*d + (a1/2)*d^2 + (a2/3)*d^3 + d = v / a0; + findroot_Newton(0.0, Node[j].fullDepth*UCF(LENGTH), &d, + 0.001, storage_getVolDiff, &storageVol); + break; + + default: + d = 0.0; } + + d /= UCF(LENGTH); + if ( d > Node[j].fullDepth ) + d = Node[j].fullDepth; + return d; } //============================================================================= @@ -796,28 +899,23 @@ double storage_getDepth(int j, double v) void storage_getVolDiff(double y, double* f, double* df, void* p) // // Input: y = depth of water (ft) +// p = pointer to a TStorageVol object // Output: f = volume of water (ft3) -// df = dVolume/dDepth (ft2) -// Purpose: computes volume and its derivative with respect to depth -// at storage node Kstar using the node's area versus depth function. +// df = dVolume/dDepth ( = surface area)(ft2) +// Purpose: computes volume difference and its derivative at a storage node +// using the node's area versus depth function. // { int k; - double e, v; TStorageVol* storageVol; - // ... cast void pointer p to a TStorageVol object + // --- cast void pointer p to a TStorageVol object storageVol = (TStorageVol *)p; k = storageVol->k; - // ... find storage volume at depth y - e = Storage[k].aExpon + 1.0; - v = Storage[k].aConst * y + Storage[k].aCoeff / e * pow(y, e); - - // ... compute difference between this volume and target volume - // as well as its derivative w.r.t. y - *f = v - storageVol->v; - *df = Storage[k].aConst + Storage[k].aCoeff * pow(y, e-1.0); + // --- compute volume & surface area at depth y + *f = storage_getVolume(k, y) - storageVol->v; + *df = storage_getSurfArea(k, y); } //============================================================================= @@ -831,27 +929,41 @@ double storage_getVolume(int j, double d) // { int k = Node[j].subIndex; - int i = Storage[k].aCurve; - double v; + int i; + double n, v; // --- return full volume if depth >= max. depth if ( d == 0.0 ) return 0.0; if ( d >= Node[j].fullDepth && Node[j].fullVolume > 0.0 ) return Node[j].fullVolume; - // --- use table integration if area v. depth table exists - if ( i >= 0 ) - return table_getArea(&Curve[i], d*UCF(LENGTH)) / UCF(VOLUME); - - // --- otherwise use functional area v. depth relation - else + switch (Storage[k].shape) { - d *= UCF(LENGTH); - v = Storage[k].aConst * d; - v += Storage[k].aCoeff / (Storage[k].aExpon+1.0) * - pow(d, Storage[k].aExpon+1.0); - return v / UCF(VOLUME); - + // --- for tabular shape function, use end area method + case TABULAR: + i = Storage[k].aCurve; + if (i >= 0) + return table_getStorageVolume(&Curve[i], d*UCF(LENGTH)) / + UCF(VOLUME); + else return 0.0; + + // --- for FUNCTIONAL relation, integrate a0 + a1*d^a2 + case FUNCTIONAL: + d *= UCF(LENGTH); + n = Storage[k].a2 + 1.0; + v = (Storage[k].a0 * d) + Storage[k].a1 / n * pow(d, n); + return v / UCF(VOLUME); + + // --- for other shapes evaluate cubic eqn. a0*d + (a1/2)*d^2 + (a2/3)*d^3 + case CYLINDRICAL: + case CONICAL: + case PARABOLOID: + case PYRAMIDAL: + d *= UCF(LENGTH); + v = d * (Storage[k].a0 + d * (Storage[k].a1 / 2.0 + d * Storage[k].a2 / 3.0)); + return v / UCF(VOLUME); + + default: return 0.0; } } @@ -865,18 +977,35 @@ double storage_getSurfArea(int j, double d) // Purpose: computes a storage node's surface area from its water depth. // { - double area; + double area = 0.0; int k = Node[j].subIndex; - int i = Storage[k].aCurve; - if ( i >= 0 ) - area = table_lookupEx(&Curve[i], d*UCF(LENGTH)); - else + int i; + + switch (Storage[k].shape) { - if ( Storage[k].aCoeff <= 0.0 ) area = Storage[k].aConst; - else if ( Storage[k].aExpon == 0.0 ) - area = Storage[k].aConst + Storage[k].aCoeff; - else area = Storage[k].aConst + Storage[k].aCoeff * - pow(d*UCF(LENGTH), Storage[k].aExpon); + // --- for tabular shape function, use table look-up + case TABULAR: + i = Storage[k].aCurve; + if (i >= 0) + area = table_lookupEx(&Curve[i], d*UCF(LENGTH)); + break; + + // --- for FUNCTIONAL relation, evaluate a0 + a1*d^a2 + case FUNCTIONAL: + area = Storage[k].a0 + Storage[k].a1 * + pow(d*UCF(LENGTH), Storage[k].a2); + break; + + // --- for other shapes, evaluate quadratic a0 + a1*d + a2*d^2 + case CYLINDRICAL: + case CONICAL: + case PARABOLOID: + case PYRAMIDAL: + d *= UCF(LENGTH); + area = Storage[k].a0 + d * (Storage[k].a1 + d * Storage[k].a2); + break; + + default: return 0.0; } return area / UCF(LENGTH) / UCF(LENGTH); } @@ -1204,7 +1333,7 @@ int outfall_readParams(int j, int k, char* tok[], int ntoks) // Purpose: reads an outfall's properties from a tokenized line of input. // // Format of input line is: -// nodeID elev FIXED outfallStage (flapGate) (routeTo) +// nodeID elev FIXED fixedStage (flapGate) (routeTo) // nodeID elev TIDAL curveID (flapGate) (routeTo) // nodeID elev TIMESERIES tseriesID (flapGate) (routTo) // nodeID elev FREE (flapGate) (routeTo) @@ -1228,16 +1357,16 @@ int outfall_readParams(int j, int k, char* tok[], int ntoks) x[3] = -1.; // tidal curve x[4] = -1.; // tide series x[5] = 0.; // flap gate - x[6] = -1.; // route to subcatch//(5.1.008) + x[6] = -1.; // route to subcatch n = 4; - if ( i >= STAGED_OUTFALL ) + if ( i >= FIXED_OUTFALL ) { if ( ntoks < 4 ) return error_setInpError(ERR_ITEMS, ""); n = 5; switch ( i ) { - case STAGED_OUTFALL: // fixed stage + case FIXED_OUTFALL: // fixed stage if ( ! getDouble(tok[3], &x[2]) ) return error_setInpError(ERR_NUMBER, tok[3]); break; @@ -1303,8 +1432,8 @@ void outfall_setOutletDepth(int j, double yNorm, double yCrit, double z) else Node[j].newDepth = yNorm; return; - case STAGED_OUTFALL: - stage = Outfall[i].outfallStage; + case FIXED_OUTFALL: + stage = Outfall[i].fixedStage; break; case TIDAL_OUTFALL: diff --git a/src/solver/objects.h b/src/solver/objects.h index 265d0409f..2e5146444 100644 --- a/src/solver/objects.h +++ b/src/solver/objects.h @@ -2,16 +2,9 @@ // objects.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/19/14 (Build 5.1.000) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) -// 05/10/18 (Build 5.1.013) -// 04/01/20 (Build 5.1.015) -// -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // M. Tryby (EPA) // R. Dickinson (CDM) // @@ -26,51 +19,58 @@ // In many structure definitions, a blank line separates the set of // input properties from the set of computed output properties. // +// Update History +// ============== // Build 5.1.007: // - Data structure for monthly adjustments of temperature, evaporation, // and rainfall added. // - User-supplied equation for deep GW flow added to subcatchment object. // - Exfiltration added to storage node object. // - Surcharge option added to weir object. -// // Build 5.1.008: // - Route to subcatchment option added to Outfall data structure. // - Hydraulic conductivity added to monthly adjustments data structure. // - Total LID drain flow and outfall runon added to Runoff Totals. // - Groundwater statistics object added. // - Maximum depth for reporting times added to node statistics object. -// // Build 5.1.010: // - Additional fields added to Weir object to support ROADWAY_WEIR type. // - New field added to Link object to record when its setting was changed. // - q1Old and q2Old of Link object restored. -// // Build 5.1.011: // - Description of oldFlow & newFlow for TGroundwater object modified. // - Weir shape parameter deprecated. // - Added definition of a hydraulic event time period (TEvent). -// // Build 5.1.013: // - New member 'averages' added to the TRptFlags structure. // - Adjustment patterns added to TSubcatch structure. // - Members impervRunoff and pervRunoff added to TSubcatchStats structure. // - Member cdCurve (weir coeff. curve) added to TWeir structure. -// // Build 5.1.015: // - Support added for multiple infiltration methods within a project. // - Support added for grouped freqency table of routing time steps. +// Build 5.2.0: +// - Support added for Street and Inlet objects. +// - Support added for analytical storage shapes. +// - Support added for reporting most frequent non-converging links. +// - Support added for tracking a gage's prior n-hour rainfall total. +// - Removed extIfaceInflow member from ExtInflow struct. +// - Refactored TRptFlags struct. //----------------------------------------------------------------------------- #ifndef OBJECTS_H #define OBJECTS_H - +#include +#include "consts.h" +#include "enums.h" +#include "datetime.h" #include "mathexpr.h" +#include "inlet.h" #include "infil.h" #include "exfil.h" -#include "toolkit_structs.h" // Declaration of public structs - +#include "toolkit_structs.h" // OWA Addition - Declaration of public structs //----------------- // FILE INFORMATION @@ -115,10 +115,11 @@ typedef struct //----------------- // RAIN GAGE OBJECT //----------------- +#define MAXPASTRAIN 48 typedef struct { char* ID; // raingage name - double externalRain; // rainfall-rate injected RAIN API + double externalRain; // rainfall-rate injected RAIN API (OWA addition) int dataSource; // data from time series or file int tSeries; // rainfall data time series index char fname[MAXFNAME+1]; // name of rainfall data file @@ -140,7 +141,10 @@ typedef struct DateTime nextDate; // next date with recorded rainfall double rainfall; // current rainfall (in/hr or mm/hr) double nextRainfall; // next rainfall (in/hr or mm/hr) + double apiRainfall; // rainfall from API function (in/hr or mm/hr) double reportRainfall; // rainfall value used for reported results + double pastRain[MAXPASTRAIN+1]; // previous hourly rain volume (in or mm) + int pastInterval; // seconds since pastRain last updated int coGage; // index of gage with same rain timeseries int isUsed; // TRUE if gage used by any subcatchment int isCurrent; // TRUE if gage's rainfall is current @@ -375,11 +379,11 @@ typedef struct typedef struct { char* ID; // subcatchment name - char rptFlag; // reporting flag + int rptFlag; // reporting flag int gage; // raingage index int outNode; // outlet node index int outSubcatch; // outlet subcatchment index - int infilModel; // infiltration method index //(5.1.015) + int infilModel; // infiltration method index int infil; // infiltration object index TSubarea subArea[3]; // sub-area data double width; // overland flow width (ft) @@ -393,9 +397,9 @@ typedef struct MathExpr* gwLatFlowExpr; // user-supplied lateral outflow expression MathExpr* gwDeepFlowExpr; // user-supplied deep percolation expression TSnowpack* snowpack; // associated snow pack data - int nPervPattern; // pervious N pattern index //(5.1.013) - int dStorePattern; // depression storage pattern index // - int infilPattern; // infiltration rate pattern index // + int nPervPattern; // pervious N pattern index + int dStorePattern; // depression storage pattern index + int infilPattern; // infiltration rate pattern index //----------------------------- double lidArea; // area devoted to LIDs (ft2) double rainfall; // current rainfall (ft/sec) @@ -409,9 +413,9 @@ typedef struct double* oldQual; // previous runoff quality (mass/L) double* newQual; // current runoff quality (mass/L) double* pondedQual; // ponded surface water quality (mass) - double* concPonded; // ponded surface water quality concentration (mass/L) + double* concPonded; // ponded surface water quality concentration (mass/L) (OWA addition) double* totalLoad; // total washoff load (lbs or kg) - double* surfaceBuildup; // current surface buildup (mass) + double* surfaceBuildup; // current surface buildup (mass) (OWA addition) } TSubcatch; //----------------------- @@ -437,7 +441,7 @@ struct ExtInflow double cFactor; // units conversion factor for mass inflow double baseline; // constant baseline value double sFactor; // time series scaling factor - double extIfaceInflow;// external interfacing inflow + double extIfaceInflow;// external interfacing inflow (OWA addition) struct ExtInflow* next; // pointer to next inflow data object }; typedef struct ExtInflow TExtInflow; @@ -495,8 +499,8 @@ typedef struct char* ID; // node ID int type; // node type code int subIndex; // index of node's sub-category - int* extPollutFlag; // external pollutant flag - char rptFlag; // reporting flag + int* extPollutFlag; // external pollutant flag (OWA addition) + int rptFlag; // reporting flag double invertElev; // invert elevation (ft) double initDepth; // initial storage level (ft) double fullDepth; // dist. from invert to surface (ft) @@ -508,12 +512,13 @@ typedef struct TTreatment* treatment; // array of treatment data //----------------------------- int degree; // number of outflow links + int inlet; // is an inlet BYPASS or CAPTURE node char updated; // true if state has been updated double crownElev; // top of highest flowing closed conduit (ft) double inflow; // total inflow (cfs) double outflow; // total outflow (cfs) double losses; // evap + exfiltration loss (ft3) - double hrt; // hydraulic retention time + double hrt; // hydraulic retention time (OWA addition) double oldVolume; // previous volume (ft3) double newVolume; // current volume (ft3) double fullVolume; // max. storage available (ft3) @@ -524,11 +529,14 @@ typedef struct double newLatFlow; // current lateral inflow (cfs) double* oldQual; // previous quality state double* newQual; // current quality state - double* extQual; // external quality state - double* inQual; // inflow quality state - double* reactorQual; // concentration in the mixed reactor + double* extQual; // external quality state (OWA addition) + double* inQual; // inflow quality state (OWA addition) + double* reactorQual; // concentration in the mixed reactor (OWA addition) double oldFlowInflow; // previous flow inflow double oldNetInflow; // previous net inflow + double qualInflow; // inflow seen for quality routing (cfs) + double apiExtInflow; // inflow from swmm_setValue function (cfs) + } TNode; //--------------- @@ -538,7 +546,7 @@ typedef struct { int type; // outfall type code char hasFlapGate; // true if contains flap gate - double outfallStage; // outfall stage (ft) + double fixedStage; // fixed outfall stage (ft) int tideCurve; // index of tidal stage curve int stageSeries; // index of outfall stage time series int routeTo; // subcatchment index routed onto @@ -552,10 +560,11 @@ typedef struct typedef struct { double fEvap; // fraction of evaporation realized - double aConst; // surface area at zero height (ft2) - double aCoeff; // coeff. of area v. height curve - double aExpon; // exponent of area v. height curve + double a0; // surface area at zero height (ft2) + double a1; // coeff. of area v. height curve + double a2; // coeff. of area v. height curve int aCurve; // index of tabulated area v. height curve + int shape; // type of shape from StorageType enum TExfil* exfil; // ptr. to exfiltration object //----------------------------- double hrt; // hydraulic residence time (sec) @@ -623,6 +632,25 @@ typedef struct int nTbl; // size of geometry tables } TTransect; +//------------------------------- +// STREET CROSS SECTION STRUCTURE +//------------------------------- +typedef struct +{ + char* ID; // name of street section + int sides; // 1 or 2 sided street + double slope; // cross slope (Sx) + double width; // distance from curb to crown (ft) (Tmax) + double curbHeight; // curb height incl. depression (ft) (Hc) + double gutterDepression; // gutter depression (ft) (a) + double gutterWidth; // gutter width (ft) (W) + double roughness; // street's Manning n + double backSlope; + double backWidth; + double backRoughness; + TTransect transect; // street's transect +} TStreet; + //------------------------------------- // CUSTOM CROSS SECTION SHAPE STRUCTURE //------------------------------------- @@ -649,8 +677,8 @@ typedef struct char* ID; // link ID int type; // link type code int subIndex; // index of link's sub-category - int* extPollutFlag; // external pollutant flag - char rptFlag; // reporting flag + int* extPollutFlag; // external pollutant flag (OWA addition) + int rptFlag; // reporting flag int node1; // start node index int node2; // end node index double offset1; // ht. above start node invert (ft) @@ -663,6 +691,7 @@ typedef struct double cLossAvg; // avg. loss coeff. double seepRate; // seepage rate (ft/sec) int hasFlapGate; // true if flap gate present + TInlet *inlet; // pointer to a street inlet object //----------------------------- double oldFlow; // previous flow rate (cfs) double newFlow; // current flow rate (cfs) @@ -680,8 +709,8 @@ typedef struct double* oldQual; // previous quality state double* newQual; // current quality state double* totalLoad; // total quality mass loading - double* extQual; // external quality state - double* reactorQual; // concentration in the mixed reactor + double* extQual; // external quality state (OWA addition) + double* reactorQual; // concentration in the mixed reactor (OWA addition) int flowClass; // flow classification double dqdh; // change in flow w.r.t. head (ft2/sec) signed char direction; // flow direction flag @@ -759,7 +788,7 @@ typedef struct int canSurcharge; // true if weir can surcharge double roadWidth; // width for ROADWAY weir int roadSurface; // road surface material - int cdCurve; // discharge coeff. curve index //(5.1.013) + int cdCurve; // discharge coeff. curve index //----------------------------- double cSurcharge; // orifice coeff. for surcharge double length; // equivalent length (ft) @@ -838,19 +867,21 @@ typedef struct //-------------------------- typedef struct { - char report; // TRUE if results report generated + char disabled; // TRUE if reporting is disabled char input; // TRUE if input summary included char subcatchments; // TRUE if subcatchment results reported char nodes; // TRUE if node results reported char links; // TRUE if link results reported char continuity; // TRUE if continuity errors reported char flowStats; // TRUE if routing link flow stats. reported - char nodeStats; // TRUE if routing node depth stats. reported char controls; // TRUE if control actions reported - char averages; // TRUE if average results reported //(5.1.013) + char averages; // TRUE if report step averaged results used int linesPerPage; // number of lines printed per page } TRptFlags; +// OWA EDIT ############################################################################# +// TRunoffTotals alias of SM_RunoffTotals which is defined in toolkit_structs.h +// Moved to toolkit_structs.h to expose struct to external API //------------------------------- // CUMULATIVE RUNOFF TOTALS //------------------------------- @@ -871,6 +902,7 @@ typedef struct // } TRunoffTotals; typedef SM_RunoffTotals TRunoffTotals; +// ###################################################################################### //-------------------------- // CUMULATIVE LOADING TOTALS @@ -903,6 +935,9 @@ typedef struct double pctError; // continuity error (%) } TGwaterTotals; +// OWA EDIT ############################################################################# +// TRoutingTotals alias of SM_RoutingTotals which is defined in toolkit_structs.h +// Moved to toolkit_structs.h to expose struct to external API //---------------------------- // CUMULATIVE ROUTING TOTALS //---------------------------- @@ -924,21 +959,23 @@ typedef struct // } TRoutingTotals; typedef SM_RoutingTotals TRoutingTotals; +// ###################################################################################### -//----------------------- -// SYSTEM-WIDE STATISTICS -//----------------------- -#define TIMELEVELS 6 //(5.1.015) +//--------------------- +// TIME STEP STATISTICS +//--------------------- +#define TIMELEVELS 6 typedef struct { - double minTimeStep; - double maxTimeStep; - double avgTimeStep; - double avgStepCount; - double steadyStateCount; - double timeStepIntervals[TIMELEVELS]; //(5.1.015) - int timeStepCounts[TIMELEVELS]; //(5.1.015) -} TSysStats; + double minTimeStep; // min. routing time step taken (sec) + double maxTimeStep; // max. routing time step taken (sec) + double routingTime; // sum of routing time steps taken (sec) + int timeStepCount; // number of routing time steps + double trialsCount; // total routing trials used + double steadyStateTime; // total time in steady state (sec) + double timeStepIntervals[TIMELEVELS]; // time step intervals (sec) + int timeStepCounts[TIMELEVELS]; // count of steps in interval +} TTimeStepStats; //-------------------- // RAINFALL STATISTICS @@ -952,6 +989,8 @@ typedef struct long periodsMalfunc; } TRainStats; +// OWA EDIT ############################################################################# +// These status typedefs have moved to toolkit_structs.h to expose struct to external API //------------------------ // SUBCATCHMENT STATISTICS //------------------------ @@ -963,8 +1002,8 @@ typedef struct // double infil; // double runoff; // double maxFlow; -// double impervRunoff; //(5.1.013) -// double pervRunoff; // +// double impervRunoff; +// double pervRunoff; // } TSubcatchStats; typedef SM_SubcatchStats TSubcatchStats; @@ -987,6 +1026,7 @@ typedef struct // double maxInflow; // double maxOverflow; // double maxPondedVol; +// int nonConvergedCount; // DateTime maxInflowDate; // DateTime maxOverflowDate; // } TNodeStats; @@ -1050,6 +1090,7 @@ typedef SM_PumpStats TPumpStats; // DateTime maxFlowDate; // double maxVeloc; // double maxDepth; +// double maxStreetFilled; // double timeNormalFlow; // double timeInletControl; // double timeSurcharged; @@ -1064,6 +1105,7 @@ typedef SM_PumpStats TPumpStats; // } TLinkStats; typedef SM_LinkStats TLinkStats; +// ###################################################################################### //------------------------- // MAXIMUM VALUE STATISTICS @@ -1086,5 +1128,4 @@ typedef struct int Precision; // number of decimal places when reported } TRptField; - #endif //OBJECTS_H diff --git a/src/solver/odesolve.c b/src/solver/odesolve.c index 409f1ffac..e67e8405c 100644 --- a/src/solver/odesolve.c +++ b/src/solver/odesolve.c @@ -47,13 +47,14 @@ void rkck(double x, int n, double h, void (*derivs)(double, double*, double*)); //----------------------------------------------------------------------------- int odesolve_open(int n) { + int n5 = n*5; nmax = 0; y = (double *) calloc(n, sizeof(double)); yscal = (double *) calloc(n, sizeof(double)); dydx = (double *) calloc(n, sizeof(double)); yerr = (double *) calloc(n, sizeof(double)); ytemp = (double *) calloc(n, sizeof(double)); - ak = (double *) calloc(5*n, sizeof(double)); + ak = (double *) calloc(n5, sizeof(double)); if ( !y || !yscal || !dydx || !yerr || !ytemp || !ak ) return 0; nmax = n; return 1; @@ -195,11 +196,14 @@ void rkck(double x, int n, double h, void (*derivs)(double, double*, double*)) double dc1=c1-2825.0/27648.0, dc3=c3-18575.0/48384.0, dc4=c4-13525.0/55296.0, dc6=c6-0.25; int i; + int n2 = n*2; + int n3 = n*3; + int n4 = n*4; double *ak2 = (ak); double *ak3 = ((ak)+(n)); - double *ak4 = ((ak)+(2*n)); - double *ak5 = ((ak)+(3*n)); - double *ak6 = ((ak)+(4*n)); + double *ak4 = ((ak)+(n2)); + double *ak5 = ((ak)+(n3)); + double *ak6 = ((ak)+(n4)); for (i=0; i #include #include - #include "headers.h" -#include "version.h" +#include "version.h" // OWA manages model version differently from EPA SWMM // Definition of 4-byte integer, 4-byte real and 8-byte real types #define INT4 int @@ -46,18 +52,18 @@ enum InputDataType {INPUT_TYPE_CODE, INPUT_AREA, INPUT_INVERT, INPUT_MAX_DEPTH, INPUT_OFFSET, INPUT_LENGTH}; -typedef struct //(5.1.013) -{ // - REAL4* xAvg; // -} TAvgResults; // +typedef struct +{ + REAL4* xAvg; +} TAvgResults; //----------------------------------------------------------------------------- // Shared variables //----------------------------------------------------------------------------- -static INT4 IDStartPos; // starting file position of ID names -static INT4 InputStartPos; // starting file position of input data -static INT4 OutputStartPos; // starting file position of output data -static INT4 BytesPerPeriod; // bytes saved per simulation time period +static F_OFF IDStartPos; // starting file position of ID names +static F_OFF InputStartPos; // starting file position of input data +static F_OFF OutputStartPos; // starting file position of output data +static F_OFF BytesPerPeriod; // bytes saved per simulation time period static INT4 NumSubcatchVars; // number of subcatchment output variables static INT4 NumNodeVars; // number of node output variables static INT4 NumLinkVars; // number of link output variables @@ -65,11 +71,12 @@ static INT4 NumSubcatch; // number of subcatchments reported on static INT4 NumNodes; // number of nodes reported on static INT4 NumLinks; // number of links reported on static INT4 NumPolluts; // number of pollutants reported on + static REAL4 SysResults[MAX_SYS_RESULTS]; // values of system output vars. -static TAvgResults* AvgLinkResults; //(5.1.013) -static TAvgResults* AvgNodeResults; // -static int Nsteps; // +static TAvgResults* AvgLinkResults; +static TAvgResults* AvgNodeResults; +static int Nsteps; //----------------------------------------------------------------------------- // Exportable variables (shared with report.c) @@ -88,11 +95,10 @@ static void output_saveSubcatchResults(double reportTime, FILE* file); static void output_saveNodeResults(double reportTime, FILE* file); static void output_saveLinkResults(double reportTime, FILE* file); -static int output_openAvgResults(void); //(5.1.013) -static void output_closeAvgResults(void); // -static void output_initAvgResults(void); // -static void output_saveAvgResults(FILE* file); // - +static int output_openAvgResults(void); +static void output_closeAvgResults(void); +static void output_initAvgResults(void); +static void output_saveAvgResults(FILE* file); //----------------------------------------------------------------------------- // External functions (declared in funcs.h) @@ -100,7 +106,7 @@ static void output_saveAvgResults(FILE* file); / // output_open (called by swmm_start in swmm5.c) // output_end (called by swmm_end in swmm5.c) // output_close (called by swmm_close in swmm5.c) -// output_updateAvgResults (called by swmm_step in swmm5.c) //(5.1.013) +// output_updateAvgResults (called by swmm_step in swmm5.c) // output_saveResults (called by swmm_step in swmm5.c) // output_checkFileSize (called by swmm_report) // output_readDateTime (called by routines in report.c) @@ -123,6 +129,7 @@ int output_open() INT4 k; REAL4 x; REAL8 z; + F_OFF numResults; // --- open binary output file output_openOutFile(); @@ -140,7 +147,7 @@ int output_open() // Total Inflow, Overflow and Quality NumNodeVars = MAX_NODE_RESULTS - 1 + NumPolluts; - // --- link results consist of Depth, Flow, Velocity, Volume, //(5.1.013) + // --- link results consist of Depth, Flow, Velocity, Volume, // Capacity and Quality NumLinkVars = MAX_LINK_RESULTS - 1 + NumPolluts; @@ -152,11 +159,11 @@ int output_open() for (j=0; j= (double)MAXFILESIZE ) { - report_writeErrorMsg(ERR_FILE_SIZE, ""); + report_writeErrorMsg(ERR_OUT_SIZE, ""); } } } - +*/ //============================================================================= @@ -454,7 +467,7 @@ void output_saveResults(double reportTime) // { int i; - extern TRoutingTotals StepFlowTotals; // defined in massbal.c //(5.1.013) + extern TRoutingTotals StepFlowTotals; // defined in massbal.c DateTime reportDate = getDateTime(reportTime); REAL8 date; @@ -470,11 +483,11 @@ void output_saveResults(double reportTime) if (Nobjects[SUBCATCH] > 0) output_saveSubcatchResults(reportTime, Fout.file); - // --- save average routing results over reporting period if called for //(5.1.013) - if ( RptFlags.averages ) output_saveAvgResults(Fout.file); // + // --- save average routing results over reporting period if called for + if ( RptFlags.averages ) output_saveAvgResults(Fout.file); - // --- otherwise save interpolated point routing results //(5.1.013) - else // + // --- otherwise save interpolated point routing results + else { if (Nobjects[NODE] > 0) output_saveNodeResults(reportTime, Fout.file); @@ -517,7 +530,7 @@ void output_end() fwrite(&OutputStartPos, sizeof(INT4), 1, Fout.file); k = Nperiods; fwrite(&k, sizeof(INT4), 1, Fout.file); - k = (INT4)error_getCode(ErrorCode); + k = (INT4)ErrorCode; fwrite(&k, sizeof(INT4), 1, Fout.file); k = MAGICNUMBER; if (fwrite(&k, sizeof(INT4), 1, Fout.file) < 1) @@ -538,7 +551,7 @@ void output_close() FREE(SubcatchResults); FREE(NodeResults); FREE(LinkResults); - output_closeAvgResults(); //(5.1.013) + output_closeAvgResults(); } //============================================================================= @@ -551,7 +564,7 @@ void output_saveID(char* id, FILE* file) // Purpose: writes an object's name to the binary output file. // { - INT4 n = strlen(id); + INT4 n = (INT4)strlen(id); fwrite(&n, sizeof(INT4), 1, file); fwrite(id, sizeof(char), n, file); } @@ -625,8 +638,6 @@ void output_saveSubcatchResults(double reportTime, FILE* file) //============================================================================= -//// This function was re-written for release 5.1.013. //// //(5.1.013) - void output_saveNodeResults(double reportTime, FILE* file) // // Input: reportTime = elapsed simulation time (millisec) @@ -676,7 +687,7 @@ void output_saveLinkResults(double reportTime, FILE* file) for (j=0; j #include -#include #include -#if defined(_OPENMP) //(5.1.013) - #include // -#else // - int omp_get_num_threads(void) { return 1;} // -#endif // +// Protect against lack of compiler support for OpenMP +#if defined(_OPENMP) + #include +#else + int omp_get_max_threads(void) { return 1;} +#endif #include "headers.h" #include "lid.h" @@ -103,7 +94,7 @@ static char MemPoolAllocated; // TRUE if memory pool allocated //----------------------------------------------------------------------------- static void initPointers(void); static void setDefaults(void); -static void openFiles(char *f1, char *f2, char *f3); +static void openFiles(const char *f1, const char *f2, const char *f3); static void createObjects(void); static void deleteObjects(void); static void createHashTables(void); @@ -112,7 +103,7 @@ static void deleteHashTables(void); //============================================================================= -void project_open(char *f1, char *f2, char *f3) +void project_open(const char *f1, const char *f2, const char *f3) // // Input: f1 = pointer to name of input file // f2 = pointer to name of report file @@ -164,9 +155,15 @@ void project_readInput() else { // --- compute total duration of simulation in seconds + // OWA EDIT ################################################################ + // EPA SWMM has some precision loss when calculating the TotalDuration. + // As a result the last report result may not be added in output file. + // This change in OWA SWMM preserves more precision in the TotalDuration + double durationDate = EndDate - StartDate; double durationTime = EndTime - StartTime; TotalDuration = floor(durationDate * SECperDAY + durationTime * SECperDAY); + // ######################################################################### // --- reporting step must be <= total duration if ( (double)ReportStep > TotalDuration ) @@ -218,7 +215,7 @@ void project_validate() if ( Nobjects[AQUIFER] == 0 ) IgnoreGwater = TRUE; for ( i=0; i 0) + { + Subcatch[j].rptFlag = k; + k++; + } + } + k = 1; + for (j=0; j 0) + { + Node[j].rptFlag = k; + k++; + } + } + k = 1; + for (j=0; j 0) + { + Link[j].rptFlag = k; + k++; + } + } return ErrorCode; } @@ -319,7 +342,7 @@ int project_addObject(int type, char *id, int n) // { int result; - int len; + long len; char *newID; // --- do nothing if object already placed in hash table @@ -327,9 +350,9 @@ int project_addObject(int type, char *id, int n) // --- use memory from the hash tables' common memory pool to store // a copy of the object's ID string - len = strlen(id) + 1; - newID = (char *) Alloc(len*sizeof(char)); - strcpy(newID, id); + len = (long)strlen(id); + newID = (char *) Alloc((len+1)*sizeof(char)); + sstrncpy(newID, id, len); // --- insert object's ID into the hash table for that type of object result = HTinsert(Htable[type], newID, n); @@ -339,7 +362,7 @@ int project_addObject(int type, char *id, int n) //============================================================================= -int project_findObject(int type, char *id) +int project_findObject(int type, const char *id) // // Input: type = object type // id = object ID @@ -350,9 +373,6 @@ int project_findObject(int type, char *id) return HTfind(Htable[type], id); } - - - //============================================================================= char *project_findID(int type, char *id) @@ -376,22 +396,30 @@ double ** project_createMatrix(int nrows, int ncols) // Purpose: allocates memory for a matrix of doubles. // { - int i,j; + int i; double **a; + size_t size = (size_t)nrows * (size_t)ncols; + // --- allocate pointers to rows + if (nrows < 1 || ncols < 1) + return NULL; a = (double **) malloc(nrows * sizeof(double *)); - if ( !a ) return NULL; - + if ( !a ) + return NULL; + // --- allocate rows and set pointers to them - a[0] = (double *) malloc (nrows * ncols * sizeof(double)); - if ( !a[0] ) return NULL; - for ( i = 1; i < nrows; i++ ) a[i] = a[i-1] + ncols; - - for ( i = 0; i < nrows; i++) + a[0] = (double *) malloc (size * sizeof(double)); + if ( !a[0] ) { - for ( j = 0; j < ncols; j++) a[i][j] = 0.0; + free(a); + return NULL; } + for ( i = 1; i < nrows; i++ ) + a[i] = a[i-1] + ncols; + + // --- fill matrix with zeroes + memset(a[0], 0, size); // --- return pointer to array of pointers to rows return a; @@ -515,8 +543,8 @@ int project_readOption(char* s1, char* s2) // function can be applied) case SWEEP_START: case SWEEP_END: - strcpy(strDate, s2); - strcat(strDate, "/1947"); + sstrncpy(strDate, s2, 24); + sstrcat(strDate, "/1947", 25); if ( !datetime_strToDate(strDate, &aDate) ) { return error_setInpError(ERR_DATETIME, s2); @@ -540,7 +568,7 @@ int project_readOption(char* s1, char* s2) case WET_STEP: case DRY_STEP: case REPORT_STEP: - case RULE_STEP: //(5.1.013) + case RULE_STEP: if ( !datetime_strToTime(s2, &aTime) ) { return error_setInpError(ERR_DATETIME, s2); @@ -549,19 +577,19 @@ int project_readOption(char* s1, char* s2) h += 24*(int)aTime; s = s + 60*m + 3600*h; - // --- RuleStep allowed to be 0 while other time steps must be > 0 //(5.1.013) - if (k == RULE_STEP) // - { // - if (s < 0) return error_setInpError(ERR_NUMBER, s2); // - } // - else if ( s <= 0 ) return error_setInpError(ERR_NUMBER, s2); // + // --- RuleStep allowed to be 0 while other time steps must be > 0 + if (k == RULE_STEP) + { + if (s < 0) return error_setInpError(ERR_NUMBER, s2); + } + else if ( s <= 0 ) return error_setInpError(ERR_NUMBER, s2); switch ( k ) { case WET_STEP: WetStep = s; break; case DRY_STEP: DryStep = s; break; case REPORT_STEP: ReportStep = s; break; - case RULE_STEP: RuleStep = s; break; //(5.1.013) + case RULE_STEP: RuleStep = s; break; } break; @@ -678,10 +706,10 @@ int project_readOption(char* s1, char* s2) // --- minimum surface area (ft2 or sq. meters) associated with nodes // under dynamic wave flow routing case MIN_SURFAREA: - if (!getDouble(s2, &MinSurfArea)) //(5.1.013) - return error_setInpError(ERR_NUMBER, s2); //(5.1.013) - if (MinSurfArea < 0.0) //(5.1.013) - return error_setInpError(ERR_NUMBER, s2); //(5.1.013) + if (!getDouble(s2, &MinSurfArea)) + return error_setInpError(ERR_NUMBER, s2); + if (MinSurfArea < 0.0) + return error_setInpError(ERR_NUMBER, s2); break; // --- minimum conduit slope (%) @@ -726,7 +754,7 @@ int project_readOption(char* s1, char* s2) LatFlowTol /= 100.0; break; - // --- method used for surcharging in dynamic wave flow routing //(5.1.013) + // --- method used for surcharging in dynamic wave flow routing case SURCHARGE_METHOD: m = findmatch(s2, SurchargeWords); if (m < 0) return error_setInpError(ERR_KEYWORD, s2); @@ -769,10 +797,10 @@ void initPointers() Tseries = NULL; Transect = NULL; Shape = NULL; - Aquifer = NULL; - UnitHyd = NULL; - Snowmelt = NULL; - Event = NULL; + Aquifer = NULL; + UnitHyd = NULL; + Snowmelt = NULL; + Event = NULL; MemPoolAllocated = FALSE; } @@ -788,8 +816,8 @@ void setDefaults() int i, j; // Project title & temp. file path - for (i = 0; i < MAXTITLE; i++) strcpy(Title[i], ""); - strcpy(TempDir, ""); + for (i = 0; i < MAXTITLE; i++) sstrncpy(Title[i], "", 0); + sstrncpy(TempDir, "", 0); // Interface files Frain.mode = SCRATCH_FILE; // Use scratch rainfall file @@ -815,9 +843,9 @@ void setDefaults() UnitSystem = US; // US unit system FlowUnits = CFS; // CFS flow units InfilModel = HORTON; // Horton infiltration method - RouteModel = KW; // Kin. wave flow routing method - SurchargeMethod = EXTRAN; // Use EXTRAN method for surcharging //(5.1.013) - CrownCutoff = 0.96; //(5.1.013) + RouteModel = DW; // Dynamic wave flow routing method + SurchargeMethod = EXTRAN; // Use EXTRAN method for surcharging + CrownCutoff = 0.96; // Fractional pipe crown cutoff AllowPonding = FALSE; // No ponding at nodes InertDamping = SOME; // Partial inertial damping NormalFlowLtd = BOTH; // Default normal flow limitation @@ -837,7 +865,7 @@ void setDefaults() WetStep = 300; // Runoff wet time step (secs) DryStep = 3600; // Runoff dry time step (secs) RuleStep = 0; // Rules evaluated at each routing step - RouteStep = 300.0; // Routing time step (secs) + RouteStep = 20; // Routing time step (secs) MinRouteStep = 0.5; // Minimum variable time step (sec) ReportStep = 900; // Reporting time step (secs) StartDryDays = 0.0; // Antecedent dry days @@ -864,6 +892,7 @@ void setDefaults() SweepEnd = 365; // Reporting options + RptFlags.disabled = FALSE; RptFlags.input = FALSE; RptFlags.continuity = TRUE; RptFlags.flowStats = TRUE; @@ -871,7 +900,6 @@ void setDefaults() RptFlags.subcatchments = FALSE; RptFlags.nodes = FALSE; RptFlags.links = FALSE; - RptFlags.nodeStats = FALSE; RptFlags.averages = FALSE; // Temperature data @@ -924,7 +952,7 @@ void setDefaults() //============================================================================= -void openFiles(char *f1, char *f2, char *f3) +void openFiles(const char *f1, const char *f2, const char *f3) // // Input: f1 = name of input file // f2 = name of report file @@ -1006,7 +1034,7 @@ void createObjects() Shape = (TShape *) calloc(Nobjects[SHAPE], sizeof(TShape)); // --- create array of detailed routing event periods - Event = (TEvent *) calloc(NumEvents+1, sizeof(TEvent)); + Event = (TEvent *) calloc((size_t)NumEvents+1, sizeof(TEvent)); Event[NumEvents].start = BIG; Event[NumEvents].end = BIG + 1.0; @@ -1021,8 +1049,14 @@ void createObjects() ErrorCode = transect_create(Nobjects[TRANSECT]); if ( ErrorCode ) return; + // --- create street cross sections & inlet designs + ErrorCode = street_create(Nobjects[STREET]); + if ( ErrorCode ) return; + ErrorCode = inlet_create(Nobjects[INLET]); + if ( ErrorCode ) return; + // --- allocate memory for infiltration data - infil_create(Nobjects[SUBCATCH]); //(5.1.015) + infil_create(Nobjects[SUBCATCH]); // --- allocate memory for water quality state variables for (j = 0; j < Nobjects[SUBCATCH]; j++) @@ -1032,18 +1066,18 @@ void createObjects() Subcatch[j].oldQual = (double *) calloc(Nobjects[POLLUT], sizeof(double)); Subcatch[j].newQual = (double *) calloc(Nobjects[POLLUT], sizeof(double)); Subcatch[j].pondedQual = (double *) calloc(Nobjects[POLLUT], sizeof(double)); - Subcatch[j].concPonded = (double *) calloc(Nobjects[POLLUT], sizeof(double)); + Subcatch[j].concPonded = (double *) calloc(Nobjects[POLLUT], sizeof(double)); // (OWA addition) Subcatch[j].totalLoad = (double *) calloc(Nobjects[POLLUT], sizeof(double)); - Subcatch[j].surfaceBuildup = (double *) calloc(Nobjects[POLLUT], sizeof(double)); + Subcatch[j].surfaceBuildup = (double *) calloc(Nobjects[POLLUT], sizeof(double)); // (OWA addition) } for (j = 0; j < Nobjects[NODE]; j++) { Node[j].oldQual = (double *) calloc(Nobjects[POLLUT], sizeof(double)); Node[j].newQual = (double *) calloc(Nobjects[POLLUT], sizeof(double)); - Node[j].extQual = (double *) calloc(Nobjects[POLLUT], sizeof(double)); - Node[j].inQual = (double *) calloc(Nobjects[POLLUT], sizeof(double)); - Node[j].reactorQual = (double *) calloc(Nobjects[POLLUT], sizeof(double)); - Node[j].extPollutFlag = (int *) calloc(Nobjects[POLLUT], sizeof(int)); + Node[j].extQual = (double *) calloc(Nobjects[POLLUT], sizeof(double)); // (OWA addition) + Node[j].inQual = (double *) calloc(Nobjects[POLLUT], sizeof(double)); // (OWA addition) + Node[j].reactorQual = (double *) calloc(Nobjects[POLLUT], sizeof(double)); // (OWA addition) + Node[j].extPollutFlag = (int *) calloc(Nobjects[POLLUT], sizeof(int)); // (OWA addition) Node[j].extInflow = NULL; Node[j].dwfInflow = NULL; Node[j].rdiiInflow = NULL; @@ -1051,12 +1085,13 @@ void createObjects() } for (j = 0; j < Nobjects[LINK]; j++) { + Link[j].inlet = NULL; Link[j].oldQual = (double *) calloc(Nobjects[POLLUT], sizeof(double)); Link[j].newQual = (double *) calloc(Nobjects[POLLUT], sizeof(double)); - Link[j].extQual = (double *) calloc(Nobjects[POLLUT], sizeof(double)); + Link[j].extQual = (double *) calloc(Nobjects[POLLUT], sizeof(double)); // (OWA addition) Link[j].totalLoad = (double *) calloc(Nobjects[POLLUT], sizeof(double)); - Link[j].reactorQual = (double *) calloc(Nobjects[POLLUT], sizeof(double)); - Link[j].extPollutFlag = (int *) calloc(Nobjects[POLLUT], sizeof(int)); + Link[j].reactorQual = (double *) calloc(Nobjects[POLLUT], sizeof(double)); // (OWA addition) + Link[j].extPollutFlag = (int *) calloc(Nobjects[POLLUT], sizeof(int)); // (OWA addition) } // --- allocate memory for land use buildup/washoff functions @@ -1095,7 +1130,7 @@ void createObjects() for (j = 0; j < Nobjects[GAGE]; j++) { Gage[j].tSeries = -1; - strcpy(Gage[j].fname, ""); + sstrncpy(Gage[j].fname, "", 0); } // --- initialize subcatchment properties @@ -1200,6 +1235,7 @@ void deleteObjects() FREE(Link[j].oldQual); FREE(Link[j].newQual); FREE(Link[j].totalLoad); + // Any inlet assigned to Link[j].inlet is freed in inlet_delete(). } // --- free memory used for rainfall infiltration @@ -1238,6 +1274,10 @@ void deleteObjects() // --- delete cross section transects transect_delete(); + // --- delete street and inlet design objects + street_delete(); + inlet_delete(); + // --- delete control rules controls_delete(); diff --git a/src/solver/qualrout.c b/src/solver/qualrout.c index d8a933beb..23b81ce7d 100644 --- a/src/solver/qualrout.c +++ b/src/solver/qualrout.c @@ -2,29 +2,27 @@ // qualrout.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 04/02/15 (Build 5.1.008) -// 04/30/15 (Build 5.1.009) -// 08/05/15 (Build 5.1.010) -// 04/01/20 (Build 5.1.015) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Water quality routing functions. // +// Update History +// ============== // Build 5.1.008: // - Pollutant mass lost to seepage flow added to mass balance totals. // - Pollutant concen. increased when evaporation occurs. -// // Build 5.1.009: // - Criterion for dry link/storage node changed to avoid concen. blowup. -// // Build 5.1.010: // - Entire module re-written to be more compact and easier to follow. // - Neglible depth limit replaced with a negligible volume limit. -// // Build 5.1.015: // - Fixed mass balance issue for empty storage nodes that flood. +// Build 5.2.0: +// - Support added for flow capture by inlet structures. +// - Definition of a dry node/link modified. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -37,6 +35,7 @@ // Constants //----------------------------------------------------------------------------- static const double ZeroVolume = 0.0353147; // 1 liter in ft3 +static const double ZeroDepth = 0.003281; // 1 mm in ft //----------------------------------------------------------------------------- // External functions (declared in funcs.h) @@ -70,7 +69,7 @@ void qualrout_init() for (i = 0; i < Nobjects[NODE]; i++) { - isWet = ( Node[i].newDepth > FUDGE ); + isWet = ( Node[i].newDepth > ZeroDepth ); for (p = 0; p < Nobjects[POLLUT]; p++) { c = 0.0; @@ -82,7 +81,7 @@ void qualrout_init() for (i = 0; i < Nobjects[LINK]; i++) { - isWet = ( Link[i].newDepth > FUDGE ); + isWet = ( Link[i].newDepth > ZeroDepth ); for (p = 0; p < Nobjects[POLLUT]; p++) { c = 0.0; @@ -104,7 +103,6 @@ void qualrout_execute(double tStep) // { int i, j; - int p; double qIn, vAvg; // --- find mass flow each link contributes to its downstream node @@ -113,35 +111,33 @@ void qualrout_execute(double tStep) // --- find new water quality concentration at each node for (j = 0; j < Nobjects[NODE]; j++) { - // --- get node inflow and average volume - qIn = Node[j].inflow; + qIn = Node[j].qualInflow; + vAvg = (Node[j].oldVolume + Node[j].newVolume) / 2.0; // --- save inflow concentrations if treatment applied - if ( Node[j].treatment || ExtPollutFlag == 1) + if ( Node[j].treatment || ExtPollutFlag == 1) // (OWA EDIT: call treatmnt_setInflow when using toolkit API ) { if ( qIn < ZERO ) qIn = 0.0; treatmnt_setInflow(qIn, Node[j].newQual); } // --- find new quality at the node - if ( Node[j].type == STORAGE || Node[j].oldVolume > FUDGE ) + if ( Node[j].type == STORAGE || Node[j].oldVolume > ZeroVolume ) { findStorageQual(j, tStep); } else findNodeQual(j); // --- apply treatment to new quality values - if ( Node[j].treatment || ExtPollutFlag == 1) treatmnt_treat(j, qIn, vAvg, tStep); + if ( Node[j].treatment || ExtPollutFlag == 1) treatmnt_treat(j, qIn, vAvg, tStep); // (OWA EDIT: call treatmnt_treat when using toolkit API ) } // --- find new water quality in each link for ( i = 0; i < Nobjects[LINK]; i++ ) findLinkQual(i, tStep); - // --- default to swmm treatment - if (ExtPollutFlag == 1){ExtPollutFlag = 0;} - + if (ExtPollutFlag == 1){ExtPollutFlag = 0;} // (OWA EDIT: reset PollutFlag if using toolkit API default to swmm treatment) } //============================================================================= @@ -200,7 +196,11 @@ void findLinkMassFlow(int i, double tStep) // --- identify index of downstream node j = Link[i].node2; if ( qLink < 0.0 ) j = Link[i].node1; + + // --- flow rate into downstream node (adjusted for inlet capture) qLink = fabs(qLink); + if (RouteModel != DW) + qLink -= inlet_capturedFlow(i); // --- examine each pollutant for (p = 0; p < Nobjects[POLLUT]; p++) @@ -212,6 +212,7 @@ void findLinkMassFlow(int i, double tStep) // --- update total load transported by link Link[i].totalLoad[p] += w * tStep; } + Node[j].qualInflow += qLink; } //============================================================================= @@ -227,8 +228,8 @@ void findNodeQual(int j) double qNode; // --- if there is flow into node then concen. = mass inflow/node flow - qNode = Node[j].inflow; - if ( qNode > ZERO ) + qNode = Node[j].qualInflow; + if ( qNode > ZERO && Node[j].newDepth > ZeroDepth) { for (p = 0; p < Nobjects[POLLUT]; p++) { @@ -264,7 +265,7 @@ void findLinkQual(int i, double tStep) vLosses, // evap. + seepage volume loss (ft3) fEvap, // evaporation concentration factor barrels, // number of barrels in conduit - lossExtQual; // loss value for external quality + lossExtQual; // loss value for external quality (OWA addition) // --- identify index of upstream node j = Link[i].node1; @@ -334,12 +335,16 @@ void findLinkQual(int i, double tStep) c2 = getMixedQual(c2, v1, wIn, qIn, tStep); // --- set concen. to zero if remaining volume is negligible - if ( v2 < ZeroVolume ) + if ( v2 < ZeroVolume || Link[i].newDepth <= ZeroDepth) { massbal_addToFinalStorage(p, c2 * v2); c2 = 0.0; } + // OWA EDIT ########################################################## + // assign qual if running normally else if using toolkit API compute + // mass balance and assign qual + // --- set reactor qual for external pollutant handling Link[i].reactorQual[p] = c2; @@ -358,6 +363,7 @@ void findLinkQual(int i, double tStep) Link[i].newQual[p] = Link[i].extQual[p]; Link[i].extPollutFlag[p] = 0; } + // ################################################################### } } @@ -424,7 +430,7 @@ void findStorageQual(int j, double tStep) fEvap = 1.0; // evaporation concentration factor // --- get inflow rate & initial volume - qIn = Node[j].inflow; + qIn = Node[j].qualInflow; v1 = Node[j].oldVolume; // -- for storage nodes @@ -469,8 +475,9 @@ void findStorageQual(int j, double tStep) wIn = Node[j].newQual[p]; c2 = getMixedQual(c1, v1, wIn, qIn, tStep); -// --- set concen. to zero if remaining volume & inflow is negligible //(5.1.015) - if (Node[j].newVolume <= ZeroVolume && qIn <= FLOW_TOL) //(5.1.015) + // --- set concen. to zero if remaining volume & inflow is negligible + if ((Node[j].newVolume <= ZeroVolume || + Node[j].newDepth <= ZeroDepth) && qIn <= ZERO) { massbal_addToFinalStorage(p, c2 * Node[j].newVolume); c2 = 0.0; @@ -478,7 +485,7 @@ void findStorageQual(int j, double tStep) // --- assign new concen. to node Node[j].newQual[p] = c2; - Node[j].reactorQual[p] = c2; + Node[j].reactorQual[p] = c2; // (OWA addition) } } @@ -525,4 +532,4 @@ double getReactedQual(int p, double c, double v1, double tStep) massbal_addReactedMass(p, lossRate); return c2; } - + \ No newline at end of file diff --git a/src/solver/rain.c b/src/solver/rain.c index 873031e74..dc7b3b2ad 100644 --- a/src/solver/rain.c +++ b/src/solver/rain.c @@ -2,12 +2,8 @@ // rain.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 08/05/15 (Build 5.1.010) -// 08/22/16 (Build 5.1.011) -// 05/10/18 (Build 5.1.013) -// 03/01/20 (Build 5.1.014) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Places rainfall data from external files into a SWMM rainfall @@ -41,15 +37,14 @@ // Date/time for start of period (8-byte double) // Rain depth (inches) (4-byte float) // +// Update History +// ============== // Release 5.1.010: // - Modified error message for records out of sequence in std. format file. -// // Release 5.1.011: // - Can now read decimal rainfall values in newer NWS online format. -// // Release 5.1.013: // - Variable x properly initialized with float value in readNwsOnlineValue(). -// // Release 5.1.014: // - Fixed indexing bug in rainFileConflict() function. //----------------------------------------------------------------------------- @@ -232,6 +227,7 @@ void createRainFile(int count) // --- write default fill-in header records to file for each gage // (will be replaced later with actual records) if ( count > 0 ) report_writeRainStats(-1, &RainStats); + strcpy(staID, " "); for ( i = 0; i < count; i++ ) { fwrite(staID, sizeof(char), MAXMSG+1, Frain.file); @@ -397,7 +393,7 @@ int findGageInFile(int i, int kount) int k; int interval; int filePos1, filePos2; - char staID[MAXMSG+1]; + char staID[MAXMSG+1] = ""; for ( k = 1; k <= kount; k++ ) { @@ -438,7 +434,7 @@ int findFileFormat(FILE *f, int i, int *hdrLines) long sn2; char recdType[4]; char elemType[5]; - char coopID[6]; + char coopID[6] = ""; char line[MAXLINE]; int year, month, day, hour, minute; int elem; @@ -606,7 +602,7 @@ int findNWSOnlineFormat(FILE *f, char *line) { Interval = 3600; TimeOffset = Interval; - ValueOffset = str - line; + ValueOffset = (int)(str - line); fileFormat = NWS_ONLINE_60; } @@ -615,7 +611,7 @@ int findNWSOnlineFormat(FILE *f, char *line) { Interval = 900; TimeOffset = Interval; - ValueOffset = str - line; + ValueOffset = (int)(str - line); fileFormat = NWS_ONLINE_15; } else return UNKNOWN_FORMAT; @@ -633,7 +629,7 @@ int findNWSOnlineFormat(FILE *f, char *line) if ( str == NULL ) return UNKNOWN_FORMAT; // --- use pointer arithmetic to convert pointer to character position - n = str - line; + n = (int)(str - line); DataOffset = n - 11; return fileFormat; } @@ -737,7 +733,7 @@ int readNWSLine(char *line, int fileFormat, DateTime day1, DateTime day2) int hour, minute; long v; float x; - int lineLength = strlen(line)-1; + int lineLength = (int)strlen(line)-1; int nameLength = 0; // --- get year, month, & day from line @@ -874,7 +870,7 @@ int readNwsOnlineValue(char* s, long* v, char* flag) // { int n; - float x = 99.99f; //(5.1.013) + float x = 99.99f; // --- check for newer format of decimal inches if ( strchr(s, '.') ) @@ -1147,7 +1143,7 @@ void saveRainfall(DateTime date1, int hour, int minute, float x, char isMissing) // { DateTime date2; - double seconds; + int seconds; if ( isMissing ) RainStats.periodsMissing++; else RainStats.periodsRain++; diff --git a/src/solver/rdii.c b/src/solver/rdii.c index d94962489..ed00db89d 100644 --- a/src/solver/rdii.c +++ b/src/solver/rdii.c @@ -2,13 +2,9 @@ // rdii.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 04/04/14 (Build 5.1.003) -// 04/14/14 (Build 5.1.004) -// 09/15/14 (Build 5.1.007) -// 03/01/20 (Build 5.1.014) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // R. Dickinson (CDM) // // RDII processing functions. @@ -16,10 +12,11 @@ // Note: RDII means rainfall dependent infiltration/inflow, // UH means unit hydrograph. // +// Update History +// ============== // Build 5.1.007: // - Ignore RDII option implemented. // - Rainfall climate adjustment implemented. -// // Build 5.1.014: // - Fixes bug related to isUsed property of a unit hydrograph's rain gage. //----------------------------------------------------------------------------- @@ -602,15 +599,15 @@ int readRdiiTextFileHeader() // Purpose: reads header information from a text RDII file. // { - int i; + int i, j; char line[MAXLINE+1]; // line from RDII data file - char s1[MAXLINE+1]; // general string variable + char s1[MAXLINE+1] = ""; // general string variable char s2[MAXLINE+1]; // --- check for correct file type fgets(line, MAXLINE, Frdii.file); - sscanf(line, "%s", s1); - if ( strcmp(s1, "SWMM5") != 0 ) return ERR_RDII_FILE_FORMAT; + if ( !sscanf(line, "%s", s1) || strcmp(s1, "SWMM5") != 0 ) + return ERR_RDII_FILE_FORMAT; // --- skip title line fgets(line, MAXLINE, Frdii.file); @@ -618,21 +615,23 @@ int readRdiiTextFileHeader() // --- read RDII UH time step interval (sec) RdiiStep = 0; fgets(line, MAXLINE, Frdii.file); - sscanf(line, "%d", &RdiiStep); - if ( RdiiStep <= 0 ) return ERR_RDII_FILE_FORMAT; + if ( !sscanf(line, "%d", &RdiiStep) || RdiiStep <= 0 ) + return ERR_RDII_FILE_FORMAT; // --- skip over line with number of constituents (= 1 for RDII) fgets(line, MAXLINE, Frdii.file); // --- read flow units fgets(line, MAXLINE, Frdii.file); - sscanf(line, "%s %s", s1, s2); + if ( sscanf(line, "%s %s", s1, s2) < 2 ) + return ERR_RDII_FILE_FORMAT; RdiiFlowUnits = findmatch(s2, FlowUnitWords); if ( RdiiFlowUnits < 0 ) return ERR_RDII_FILE_FORMAT; // --- read number of RDII nodes fgets(line, MAXLINE, Frdii.file); - if ( sscanf(line, "%d", &NumRdiiNodes) < 1 ) return ERR_RDII_FILE_FORMAT; + if ( sscanf(line, "%d", &NumRdiiNodes) < 1 || NumRdiiNodes <= 0 ) + return ERR_RDII_FILE_FORMAT; // --- allocate memory for RdiiNodeIndex & RdiiNodeFlow arrays RdiiNodeIndex = (int *) calloc(NumRdiiNodes, sizeof(int)); @@ -645,8 +644,12 @@ int readRdiiTextFileHeader() { if ( feof(Frdii.file) ) return ERR_RDII_FILE_FORMAT; fgets(line, MAXLINE, Frdii.file); - sscanf(line, "%s", s1); - RdiiNodeIndex[i] = project_findObject(NODE, s1); + if ( !sscanf(line, "%s", s1) ) + return ERR_RDII_FILE_FORMAT; + j = project_findObject(NODE, s1); + if ( j < 0 ) + return ERR_RDII_FILE_FORMAT; + RdiiNodeIndex[i] = j; } // --- skip column heading line @@ -906,7 +909,7 @@ void openRdiiProcessor() n = 0; for (j=0; j Start Date. // - Support added for grouped freqency table of routing time steps. +// Build 5.2.0: +// - Support added for reporting most frequent non-converging links. +// - Support added for RptFlags.disabled flag. +// - Refactored report_readOptions(). //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -55,10 +46,9 @@ #include #include #include - #include "headers.h" -#include "version.h" +#include "version.h" // OWA manages model version differently from EPA SWMM #define WRITE(x) (report_writeLine((x))) #define LINE_10 "----------" @@ -83,6 +73,10 @@ extern REAL4* NodeResults; // " extern REAL4* LinkResults; // " extern char ErrString[81]; // defined in ERROR.C + +extern TNodeStats* NodeStats; + + //----------------------------------------------------------------------------- // Local functions //----------------------------------------------------------------------------- @@ -94,7 +88,7 @@ static void report_Nodes(void); static void report_NodeHeader(char *id); static void report_Links(void); static void report_LinkHeader(char *id); -static void report_RouteStepFreq(TSysStats* sysStats); //(5.1.015) +static void report_RouteStepFreq(TTimeStepStats* timeStepStats); //============================================================================= @@ -111,63 +105,45 @@ int report_readOptions(char* tok[], int ntoks) if ( ntoks < 2 ) return error_setInpError(ERR_ITEMS, ""); k = (char)findmatch(tok[0], ReportWords); if ( k < 0 ) return error_setInpError(ERR_KEYWORD, tok[0]); - switch ( k ) - { - case 0: // Input - m = findmatch(tok[1], NoYesWords); - if ( m == YES ) RptFlags.input = TRUE; - else if ( m == NO ) RptFlags.input = FALSE; - else return error_setInpError(ERR_KEYWORD, tok[1]); - return 0; - - case 1: // Continuity - m = findmatch(tok[1], NoYesWords); - if ( m == YES ) RptFlags.continuity = TRUE; - else if ( m == NO ) RptFlags.continuity = FALSE; - else return error_setInpError(ERR_KEYWORD, tok[1]); - return 0; - case 2: // Flow Statistics - m = findmatch(tok[1], NoYesWords); - if ( m == YES ) RptFlags.flowStats = TRUE; - else if ( m == NO ) RptFlags.flowStats = FALSE; - else return error_setInpError(ERR_KEYWORD, tok[1]); - return 0; - - case 3: // Controls + // --- keyword not SUBCATCHMENT, NODE, or LINK + if (k < 2 || k > 4) + { m = findmatch(tok[1], NoYesWords); - if ( m == YES ) RptFlags.controls = TRUE; - else if ( m == NO ) RptFlags.controls = FALSE; - else return error_setInpError(ERR_KEYWORD, tok[1]); - return 0; - - case 4: m = SUBCATCH; break; // Subcatchments - case 5: m = NODE; break; // Nodes - case 6: m = LINK; break; // Links + if (m < NO || m > YES) + return error_setInpError(ERR_KEYWORD, tok[1]); + switch (k) + { + case 0: RptFlags.disabled = m; return 0; // DISABLED + case 1: RptFlags.input = m; return 0; // INPUT + case 5: RptFlags.continuity = m; return 0; // CONTINUITY + case 6: RptFlags.flowStats = m; return 0; // FLOWSTATS + case 7: RptFlags.controls = m; return 0; // CONTROLS + case 8: RptFlags.averages = m; return 0; // AVERAGES + case 9: return 0; // NODESTATS deprecated + default: return error_setInpError(ERR_KEYWORD, tok[1]); + } + } - case 7: // Node Statistics - m = findmatch(tok[1], NoYesWords); - if ( m == YES ) RptFlags.nodeStats = TRUE; - else if ( m == NO ) RptFlags.nodeStats = FALSE; - else return error_setInpError(ERR_KEYWORD, tok[1]); - return 0; - - case 8: // Averages //(5.1.013) - m = findmatch(tok[1], NoYesWords); // - if (m == YES) RptFlags.averages = TRUE; // - else if (m == NO) RptFlags.averages = FALSE; // - else return error_setInpError(ERR_KEYWORD, tok[1]); // - return 0; // - - default: return error_setInpError(ERR_KEYWORD, tok[1]); + // --- SUBCATCHMENT, NODE, & LINK keywords + else + { + switch(k) + { + case 2: m = SUBCATCH; break; // Subcatchments + case 3: m = NODE; break; // Nodes + case 4: m = LINK; break; // Links + } } - + + // --- determine object's reporting flag if (strcomp(tok[1], w_NONE)) k = NONE; else if (strcomp(tok[1], w_ALL)) k = ALL; else { + // --- set reporting flag for individual objects k = SOME; for (t = 1; t < ntoks; t++) { @@ -181,6 +157,8 @@ int report_readOptions(char* tok[], int ntoks) } } } + + // --- assign object type's reporting flag switch ( m ) { case SUBCATCH: RptFlags.subcatchments = k; break; @@ -192,7 +170,7 @@ int report_readOptions(char* tok[], int ntoks) //============================================================================= -void report_writeLine(char *line) +void report_writeLine(const char *line) // // Input: line = line of text // Output: none @@ -247,6 +225,7 @@ void report_writeLogo() // Output: none // Purpose: writes report header lines to report file. // +// OWA manages model version differently from EPA SWMM. { sprintf(Msg, \ "\n OWA STORM WATER MANAGEMENT MODEL - VERSION v%s (OWA %.10s)", @@ -275,7 +254,6 @@ void report_writeTitle() WRITE(Title[i]); lineCount++; } - if ( lineCount > 0 ) WRITE(""); } //============================================================================= @@ -289,12 +267,6 @@ void report_writeOptions() { char str[80]; WRITE(""); - WRITE("*********************************************************"); - WRITE("NOTE: The summary statistics displayed in this report are"); - WRITE("based on results found at every computational time step, "); - WRITE("not just on results from each reporting time step."); - WRITE("*********************************************************"); - WRITE(""); WRITE("****************"); WRITE("Analysis Options"); WRITE("****************"); @@ -341,9 +313,9 @@ void report_writeOptions() fprintf(Frpt.file, "\n Flow Routing Method ...... %s", RouteModelWords[RouteModel]); - if (RouteModel == DW) //(5.1.013) - fprintf(Frpt.file, "\n Surcharge Method ......... %s", //(5.1.013) - SurchargeWords[SurchargeMethod]); //(5.1.013) + if (RouteModel == DW) + fprintf(Frpt.file, "\n Surcharge Method ......... %s", + SurchargeWords[SurchargeMethod]); datetime_dateToStr(StartDate, str); fprintf(Frpt.file, "\n Starting Date ............ %s", str); @@ -366,18 +338,18 @@ void report_writeOptions() if ( Nobjects[LINK] > 0 ) { fprintf(Frpt.file, "\n Routing Time Step ........ %.2f sec", RouteStep); - if ( RouteModel == DW ) - { - fprintf(Frpt.file, "\n Variable Time Step ....... "); - if ( CourantFactor > 0.0 ) fprintf(Frpt.file, "YES"); - else fprintf(Frpt.file, "NO"); - fprintf(Frpt.file, "\n Maximum Trials ........... %d", MaxTrials); - fprintf(Frpt.file, "\n Number of Threads ........ %d", NumThreads); - fprintf(Frpt.file, "\n Head Tolerance ........... %.6f ", - HeadTol*UCF(LENGTH)); - if ( UnitSystem == US ) fprintf(Frpt.file, "ft"); - else fprintf(Frpt.file, "m"); - } + if ( RouteModel == DW ) + { + fprintf(Frpt.file, "\n Variable Time Step ....... "); + if ( CourantFactor > 0.0 ) fprintf(Frpt.file, "YES"); + else fprintf(Frpt.file, "NO"); + fprintf(Frpt.file, "\n Maximum Trials ........... %d", MaxTrials); + fprintf(Frpt.file, "\n Number of Threads ........ %d", NumThreads); + fprintf(Frpt.file, "\n Head Tolerance ........... %.6f ", + HeadTol*UCF(LENGTH)); + if ( UnitSystem == US ) fprintf(Frpt.file, "ft"); + else fprintf(Frpt.file, "m"); + } } WRITE(""); } @@ -397,6 +369,8 @@ void report_writeRainStats(int i, TRainStats* r) { char date1[] = "***********"; char date2[] = "***********"; + if (RptFlags.disabled) + return; if ( i < 0 ) { WRITE(""); @@ -408,16 +382,17 @@ void report_writeRainStats(int i, TRainStats* r) fprintf(Frpt.file, "\n ID Date Date Frequency w/Precip Missing Malfunc."); fprintf(Frpt.file, -"\n -------------------------------------------------------------------------------\n"); +"\n -------------------------------------------------------------------------------"); } else { if ( r->startDate != NO_DATE ) datetime_dateToStr(r->startDate, date1); if ( r->endDate != NO_DATE ) datetime_dateToStr(r->endDate, date2); - fprintf(Frpt.file, " %-10s %-11s %-11s %5d min %6ld %6ld %6ld\n", + fprintf(Frpt.file, "%-10s %10s %-10s %5d min %6ld %6ld %6ld\n", Gage[i].staID, date1, date2, Gage[i].rainInterval/60, r->periodsRain, r->periodsMissing, r->periodsMalfunc); } + WRITE(""); } @@ -436,6 +411,9 @@ void report_writeRdiiStats(double rainVol, double rdiiVol) double ratio; double ucf1, ucf2; + if (RptFlags.disabled) + return; + ucf1 = UCF(LENGTH) * UCF(LANDAREA); if ( UnitSystem == US) ucf2 = MGDperCFS / SECperDAY; else ucf2 = MLDperCFS / SECperDAY; @@ -489,8 +467,11 @@ void report_writeControlAction(DateTime aDate, char* linkID, double value, // Purpose: reports action taken by a control rule. // { - char theDate[12]; - char theTime[9]; + char theDate[DATE_STR_SIZE]; + char theTime[TIME_STR_SIZE]; + + if (RptFlags.disabled) + return; datetime_dateToStr(aDate, theDate); datetime_timeToStr(aDate, theTime); fprintf(Frpt.file, @@ -511,7 +492,6 @@ void report_writeRunoffError(TRunoffTotals* totals, double totalArea) // Purpose: writes runoff continuity error to report file. // { - if ( Frunoff.mode == USE_FILE ) { WRITE(""); @@ -650,7 +630,7 @@ void report_LoadingErrors(int p1, int p2, TLoadingTotals* totals) { i = UnitSystem; if ( Pollut[p].units == COUNT ) i = 2; - strcpy(units, LoadUnitsWords[i]); + sstrncpy(units, LoadUnitsWords[i], 14); fprintf(Frpt.file, "%14s", units); } fprintf(Frpt.file, "\n **************************"); @@ -862,7 +842,7 @@ void report_QualErrors(int p1, int p2, TRoutingTotals* QualTotals) { i = UnitSystem; if ( Pollut[p].units == COUNT ) i = 2; - strcpy(units, LoadUnitsWords[i]); + sstrncpy(units, LoadUnitsWords[i], 14); fprintf(Frpt.file, "%14s", units); } fprintf(Frpt.file, "\n **************************"); @@ -1035,54 +1015,83 @@ void report_writeMaxFlowTurns(TMaxStats flowTurns[], int nMaxStats) //============================================================================= -void report_writeSysStats(TSysStats* sysStats) +void report_writeNonconvergedStats(TMaxStats maxNonconverged[], int nMaxStats) +{ + int i, j; + if (Nobjects[NODE] == 0 || RouteModel != DW) return; + WRITE(""); + WRITE("*********************************"); + WRITE("Most Frequent Nonconverging Nodes"); + WRITE("*********************************"); + if (nMaxStats <= 0 || maxNonconverged[0].index <= 0 || + maxNonconverged[0].value < 0.00005) + fprintf(Frpt.file, "\n Convergence obtained at all time steps."); + else + { + for (i = 0; i < nMaxStats; i++) + { + j = maxNonconverged[i].index; + if (j < 0 || maxNonconverged[i].value <= 0.0) continue; + fprintf(Frpt.file, "\n Node %s (%.2f%%)", + Node[j].ID, 100.0 * maxNonconverged[i].value); + } + } + WRITE(""); +} + +//============================================================================= + +void report_writeTimeStepStats(TTimeStepStats* timeStepStats) // -// Input: sysStats = simulation statistics for overall system +// Input: timeStepStats = routing time step statistics // Output: none -// Purpose: writes simulation statistics for overall system to report file. +// Purpose: writes routing time step statistics to report file. // { - double x; - double eventStepCount; // Routing steps taken during reporting period //(5.1.015) + double timeStepCount = timeStepStats->timeStepCount; + double totalRoutingTime; // time taken for all flow routing steps + double fSteadyState = 0.0; // fraction of reporting time in steady state + + if ( Nobjects[LINK] == 0 || timeStepStats->timeStepCount == 0.0 ) + return; + + totalRoutingTime = timeStepStats->steadyStateTime + timeStepStats->routingTime; + if (totalRoutingTime > 0.0) + fSteadyState = 100.0 * timeStepStats->steadyStateTime / totalRoutingTime; - eventStepCount = ReportStepCount - sysStats->steadyStateCount; //(5.1.015) - if ( Nobjects[LINK] == 0 || TotalStepCount == 0 - || eventStepCount == 0.0 ) return; WRITE(""); WRITE("*************************"); WRITE("Routing Time Step Summary"); WRITE("*************************"); fprintf(Frpt.file, "\n Minimum Time Step : %7.2f sec", - sysStats->minTimeStep); + timeStepStats->minTimeStep); fprintf(Frpt.file, "\n Average Time Step : %7.2f sec", - sysStats->avgTimeStep / eventStepCount); + timeStepStats->routingTime / timeStepCount); fprintf(Frpt.file, "\n Maximum Time Step : %7.2f sec", - sysStats->maxTimeStep); - x = (1.0 - sysStats->avgTimeStep * 1000.0 / NewRoutingTime) * 100.0; + timeStepStats->maxTimeStep); fprintf(Frpt.file, - "\n Percent in Steady State : %7.2f", MIN(x, 100.0)); + "\n %% of Time in Steady State : %7.2f", MIN(fSteadyState, 100.0)); fprintf(Frpt.file, "\n Average Iterations per Step : %7.2f", - sysStats->avgStepCount / eventStepCount); + timeStepStats->trialsCount / timeStepCount); fprintf(Frpt.file, - "\n Percent Not Converging : %7.2f", - 100.0 * (double)NonConvergeCount / eventStepCount); + "\n %% of Steps Not Converging : %7.2f", + 100.0 * (double)NonConvergeCount / timeStepCount); - // --- write grouped frequency table of variable routing time steps //(5.1.015) - if (RouteModel == DW && CourantFactor > 0.0) // - report_RouteStepFreq(sysStats); // + // --- write grouped frequency table of variable routing time steps + if (RouteModel == DW && CourantFactor > 0.0) + report_RouteStepFreq(timeStepStats); WRITE(""); } //============================================================================= -//// New function added to release 5.1.015. //// //(5.1.015) -void report_RouteStepFreq(TSysStats* sysStats) +void report_RouteStepFreq(TTimeStepStats* timeStepStats) // -// Input: sysStats = simulation statistics for overall system +// Input: timeStepStats = routing time step statistics // Output: none // Purpose: writes grouped frequency table of routing time steps to report file. // @@ -1091,14 +1100,16 @@ void report_RouteStepFreq(TSysStats* sysStats) int i; for (i = 1; i < TIMELEVELS; i++) - totalSteps += sysStats->timeStepCounts[i]; + totalSteps += timeStepStats->timeStepCounts[i]; + if (totalSteps == 0) return; fprintf(Frpt.file, "\n Time Step Frequencies :"); for (i = 1; i < TIMELEVELS; i++) fprintf(Frpt.file, "\n %6.3f - %6.3f sec : %7.2f %%", - sysStats->timeStepIntervals[i-1], sysStats->timeStepIntervals[i], - 100.0 * (double)(sysStats->timeStepCounts[i]) / totalSteps); + timeStepStats->timeStepIntervals[i-1], + timeStepStats->timeStepIntervals[i], + 100.0 * (double)(timeStepStats->timeStepCounts[i]) / totalSteps); } @@ -1138,21 +1149,21 @@ void report_Subcatchments() int j, p, k; int period; DateTime days; - char theDate[12]; - char theTime[9]; + char theDate[DATE_STR_SIZE]; + char theTime[TIME_STR_SIZE]; int hasSnowmelt = (Nobjects[SNOWMELT] > 0 && !IgnoreSnowmelt); int hasGwater = (Nobjects[AQUIFER] > 0 && !IgnoreGwater); int hasQuality = (Nobjects[POLLUT] > 0 && !IgnoreQuality); if ( Nobjects[SUBCATCH] == 0 ) return; WRITE(""); - WRITE("********************"); - WRITE("Subcatchment Results"); - WRITE("********************"); - k = 0; + WRITE("********************************"); + WRITE("Subcatchment Time Series Results"); + WRITE("********************************"); for (j = 0; j < Nobjects[SUBCATCH]; j++) { - if ( Subcatch[j].rptFlag == TRUE ) + k = Subcatch[j].rptFlag - 1; + if ( k >= 0 ) { report_SubcatchHeader(Subcatch[j].ID); for ( period = 1; period <= Nperiods; period++ ) @@ -1179,7 +1190,6 @@ void report_Subcatchments() SubcatchResults[SUBCATCH_WASHOFF+p]); } WRITE(""); - k++; } } } @@ -1265,18 +1275,18 @@ void report_Nodes() int j, p, k; int period; DateTime days; - char theDate[20]; - char theTime[20]; + char theDate[DATE_STR_SIZE]; + char theTime[TIME_STR_SIZE]; if ( Nobjects[NODE] == 0 ) return; WRITE(""); - WRITE("************"); - WRITE("Node Results"); - WRITE("************"); - k = 0; + WRITE("************************"); + WRITE("Node Time Series Results"); + WRITE("************************"); for (j = 0; j < Nobjects[NODE]; j++) { - if ( Node[j].rptFlag == TRUE ) + k = Node[j].rptFlag - 1; + if ( k >= 0 ) { report_NodeHeader(Node[j].ID); for ( period = 1; period <= Nperiods; period++ ) @@ -1293,7 +1303,6 @@ void report_Nodes() fprintf(Frpt.file, " %9.3f", NodeResults[NODE_QUAL + p]); } WRITE(""); - k++; } } } @@ -1318,8 +1327,8 @@ void report_NodeHeader(char *id) "\n Inflow Flooding Depth Head"); if ( !IgnoreQuality ) for (i = 0; i < Nobjects[POLLUT]; i++) fprintf(Frpt.file, "%10s", Pollut[i].ID); - if ( UnitSystem == US) strcpy(lengthUnits, "feet"); - else strcpy(lengthUnits, "meters"); + if ( UnitSystem == US) sstrncpy(lengthUnits, "feet", 8); + else sstrncpy(lengthUnits, "meters", 8); fprintf(Frpt.file, "\n Date Time %9s %9s %9s %9s", FlowUnitWords[FlowUnits], FlowUnitWords[FlowUnits], @@ -1344,18 +1353,18 @@ void report_Links() int j, p, k; int period; DateTime days; - char theDate[12]; - char theTime[9]; + char theDate[DATE_STR_SIZE]; + char theTime[TIME_STR_SIZE]; if ( Nobjects[LINK] == 0 ) return; WRITE(""); - WRITE("************"); - WRITE("Link Results"); - WRITE("************"); - k = 0; + WRITE("************************"); + WRITE("Link Time Series Results"); + WRITE("************************"); for (j = 0; j < Nobjects[LINK]; j++) { - if ( Link[j].rptFlag == TRUE ) + k = Link[j].rptFlag - 1; + if ( k >= 0 ) { report_LinkHeader(Link[j].ID); for ( period = 1; period <= Nperiods; period++ ) @@ -1372,7 +1381,6 @@ void report_Links() fprintf(Frpt.file, " %9.3f", LinkResults[LINK_QUAL + p]); } WRITE(""); - k++; } } } @@ -1429,14 +1437,14 @@ void report_writeErrorMsg(int code, char* s) if ( Frpt.file ) { WRITE(""); - fprintf(Frpt.file, error_getMsg(code), s); + fprintf(Frpt.file, error_getMsg(code, Msg), s); } ErrorCode = code; // --- save message to ErrorMsg if it's not for a line of input data if ( ErrorCode <= ERR_INPUT || ErrorCode >= ERR_FILE_NAME ) { - sprintf(ErrorMsg, error_getMsg(ErrorCode), s); + snprintf(ErrorMsg, MAXMSG, error_getMsg(ErrorCode, Msg), s); } } @@ -1454,7 +1462,7 @@ void report_writeErrorCode() if ( (ErrorCode >= ERR_MEMORY && ErrorCode <= ERR_TIMESTEP) || (ErrorCode >= ERR_FILE_NAME && ErrorCode <= ERR_OUT_FILE) || (ErrorCode == ERR_SYSTEM) ) - fprintf(Frpt.file, "%s", error_getMsg(ErrorCode)); //(5.1.013) + fprintf(Frpt.file, "%s", error_getMsg(ErrorCode, Msg)); } } diff --git a/src/solver/roadway.c b/src/solver/roadway.c index 0237ea96d..ed8667af0 100644 --- a/src/solver/roadway.c +++ b/src/solver/roadway.c @@ -2,20 +2,22 @@ // roadway.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 08/05/15 (Build 5.1.010) -// 03/14/17 (Build 5.1.012) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Roadway Weir module for SWMM5 // // Computes flow overtopping a roadway (with a ROADWAY_WEIR object) using // the FWHA HDS-5 methodology. +// // Typically used in conjuction with a culvert crossing where the culvert // conduit is placed at zero offset at the upstream node and the Roadway // weir has the same upstream node but with an offset equal to the height // of the roadway. // +// Update History +// ============== // Build 5.1.012: // - Entries in discharge coeff. table for gravel roadways corrected. //----------------------------------------------------------------------------- diff --git a/src/solver/routing.c b/src/solver/routing.c index 9882746c4..7641f0c45 100644 --- a/src/solver/routing.c +++ b/src/solver/routing.c @@ -2,48 +2,41 @@ // routing.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/19/14 (Build 5.1.000) -// 09/15/14 (Build 5.1.007) -// 04/02/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // M. Tryby (EPA) // // Conveyance system routing functions. // +// Update History +// ============== // Build 5.1.007: // - Nodal evap/seepage losses computed using conditions at start of time step. // - DWF pollutant concentrations ignored if DWF is negative. // - Separate mass balance accounting made for storage evap. & seepage. // - Nodal mass balance accounting for negative lateral inflows corrected. -// // Build 5.1.008: // - Initialization of flow and quality routing systems moved here from swmm5.c. // - Lateral inflows now evaluated at start (not end) of time step. // - Flows from LID drains included in lateral inflows. // - Conduit evap/seepage losses multiplied by number of barrels before // being added into mass balances. -// // Build 5.1.010: // - Time when a link's setting is changed is recorded. -// // Build 5.1.011: // - Support added for limiting flow routing to specific events. -// // Build 5.1.012: // - routing_execute() was re-written so that Routing Events and // Skip Steady Flow options work together correctly. -// // Build 5.1.013: // - Support added for evaluating controls rules at RuleStep time interval. // - Back flow through Outfall nodes now treated as External Inflows for // mass balance purposes. // - Global infiltration factor for storage seepage set in routing_execute. -// +// Build 5.2.0: +// - Support added for street flow capture and sewer backflow thru inlets. +// - Shell sort replaces insertion sort for sorting Event array. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -59,7 +52,7 @@ static int* SortedLinks; static int NextEvent; static int BetweenEvents; -static double NewRuleTime; //(5.1.013) +static double NewRuleTime; //----------------------------------------------------------------------------- // External functions (declared in funcs.h) @@ -72,6 +65,14 @@ static double NewRuleTime; / //----------------------------------------------------------------------------- // Function declarations //----------------------------------------------------------------------------- +static int evaluateControlRules(DateTime currentDate, double routingStep); +static void sortEvents(void); +static int isBetweenEvents(DateTime currentDate); +static int isInSteadyState(int actionCount, double stepFlowError); +static int inflowHasChanged(void); + +static void initSystemInflows(); +static void addSystemInflows(DateTime currentDate, double routingStep); static void addExternalInflows(DateTime currentDate); static void addDryWeatherInflows(DateTime currentDate); static void addWetWeatherInflows(double routingTime); @@ -79,11 +80,13 @@ static void addGroundwaterInflows(double routingTime); static void addRdiiInflows(DateTime currentDate); static void addIfaceInflows(DateTime currentDate); static void addLidDrainInflows(double routingTime); + +static int routeFlow(int routingModel, double routingStep); +static void removeSystemOutflows(double routingStep); static void removeStorageLosses(double tStep); static void removeConduitLosses(void); static void removeOutflows(double tStep); -static int inflowHasChanged(void); -static void sortEvents(void); + //============================================================================= @@ -122,7 +125,7 @@ int routing_open() if ( NumEvents > 0 ) sortEvents(); NextEvent = 0; BetweenEvents = (NumEvents > 0); - NewRuleTime = 0.0; //(5.1.013) + NewRuleTime = 0.0; return ErrorCode; } @@ -146,8 +149,6 @@ void routing_close(int routingModel) //============================================================================= -//// This function was modified for release 5.1.013. //// //(5.1.013) - double routing_getRoutingStep(int routingModel, double fixedStep) // // Input: routingModel = routing method code @@ -207,31 +208,79 @@ void routing_execute(int routingModel, double routingStep) // Purpose: executes the routing process at the current time period. // { - int j; - int stepCount = 1; - int actionCount = 0; - int inSteadyState = FALSE; - DateTime currentDate; - double stepFlowError; - - // --- update continuity with current state - // applied over 1/2 of time step + int trialsCount = 1; // trials required to solve flow routing + int actionCount = 0; // number of control actions taken + int inSteadyState = TRUE; // system is in steady state + DateTime currentDate; // date at start of routing step + double stepFlowError; // 1 - (system outflow) / (system inflow) + if ( ErrorCode ) return; + + // --- update mass balance totals over previous half time step massbal_updateRoutingTotals(routingStep/2.); + // --- take any applicable control rule actions + currentDate = getDateTime(NewRoutingTime); + actionCount = evaluateControlRules(currentDate, routingStep); + + // --- initialize mass balance and system inflow variables + stepFlowError = massbal_getStepFlowError(); + massbal_initTimeStepTotals(); + initSystemInflows(); + + // --- check that current date falls within a user-speficied event period + BetweenEvents = isBetweenEvents(currentDate); + if (BetweenEvents == FALSE) + { + // --- apply current inflows to conveyance system + addSystemInflows(currentDate, routingStep); + inlet_findCapturedFlows(routingStep); + + // --- route flows if system is not in steady state + inSteadyState = isInSteadyState(actionCount, stepFlowError); + if (inSteadyState == FALSE) + trialsCount = routeFlow(routingModel, routingStep); + + // --- route water quality constituents + if (Nobjects[POLLUT] > 0 && !IgnoreQuality) + { + inlet_adjustQualInflows(); + qualrout_execute(routingStep); + } + + // --- update mass balance totals for flows leaving the system + removeSystemOutflows(routingStep); + inlet_adjustQualOutflows(); + + // --- update time step & flow routing statistics + if (Nobjects[LINK] > 0) + { + stats_updateFlowStats(routingStep, getDateTime(NewRoutingTime)); + stats_updateTimeStepStats(routingStep, trialsCount, inSteadyState); + } + } + + // --- update mass balance totals over the current half time step + massbal_updateRoutingTotals(routingStep / 2.); +} + +//============================================================================= + + int evaluateControlRules(DateTime currentDate, double routingStep) +{ + int j; + int actionCount = 0; + // --- find new link target settings that are not related to // --- control rules (e.g., pump on/off depth limits) for (j=0; j 0 ) @@ -267,9 +320,9 @@ void routing_execute(int routingModel, double routingStep) for (j=0; j 0 ) + // --- currrent event period has ended so result is true + if ( currentDate > Event[NextEvent].end ) { - if ( currentDate > Event[NextEvent].end ) - { - BetweenEvents = TRUE; - NextEvent++; - } - else if ( currentDate >= Event[NextEvent].start && BetweenEvents == TRUE ) - { - BetweenEvents = FALSE; - } + NextEvent++; + return TRUE; } - // --- if not between routing events - if ( BetweenEvents == FALSE ) + // --- we've entered the next event period so result is false + else if ( currentDate >= Event[NextEvent].start ) { - // --- find evap. & seepage losses from storage nodes - for (j = 0; j < Nobjects[NODE]; j++) - { - Node[j].losses = node_getLosses(j, routingStep); - } + return FALSE; + } + return TRUE; +} - // --- add lateral inflows and evap/seepage losses at nodes - addExternalInflows(currentDate); - addDryWeatherInflows(currentDate); - addWetWeatherInflows(OldRoutingTime); - addGroundwaterInflows(OldRoutingTime); - addLidDrainInflows(OldRoutingTime); - addRdiiInflows(currentDate); - addIfaceInflows(currentDate); - - // --- check if can skip steady state periods based on flows - if ( SkipSteadyState ) - { - if ( OldRoutingTime == 0.0 - || actionCount > 0 - || fabs(stepFlowError) > SysFlowTol - || inflowHasChanged() ) inSteadyState = FALSE; - else inSteadyState = TRUE; - } +//============================================================================= - // --- find new hydraulic state if system has changed - if ( inSteadyState == FALSE ) - { - // --- replace old hydraulic state values with current ones - for (j = 0; j < Nobjects[LINK]; j++) link_setOldHydState(j); - for (j = 0; j < Nobjects[NODE]; j++) - { - node_setOldHydState(j); - node_initInflow(j, routingStep); - } +void addSystemInflows(DateTime currentDate, double routingStep) +{ + int j; - // --- route flow through the drainage network - if ( Nobjects[LINK] > 0 ) - { - stepCount = flowrout_execute(SortedLinks, routingModel, routingStep); - } - } + // --- find evap. & seepage losses from storage nodes + for (j = 0; j < Nobjects[NODE]; j++) + Node[j].losses = node_getLosses(j, routingStep); + + // --- add lateral inflows at nodes + addExternalInflows(currentDate); + addDryWeatherInflows(currentDate); + addWetWeatherInflows(OldRoutingTime); + addGroundwaterInflows(OldRoutingTime); + addLidDrainInflows(OldRoutingTime); + addRdiiInflows(currentDate); + addIfaceInflows(currentDate); + + // --- initialize node inflow for quality routing + for (j = 0; j < Nobjects[NODE]; j++) + Node[j].qualInflow = MAX(0.0, Node[j].newLatFlow); +} - // --- route quality through the drainage network - if ( Nobjects[POLLUT] > 0 && !IgnoreQuality ) - { - qualrout_execute(routingStep); - } +//============================================================================= - // --- remove evaporation, infiltration & outflows from system - removeStorageLosses(routingStep); - removeConduitLosses(); - removeOutflows(routingStep); +int isInSteadyState(int actionCount, double stepFlowError) +{ + // --- check if can skip steady state periods based on flows + if ( SkipSteadyState ) + { + if ( OldRoutingTime == 0.0 + || actionCount > 0 + || fabs(stepFlowError) > SysFlowTol + || inflowHasChanged() ) return FALSE; + else return TRUE; } - else inSteadyState = TRUE; - - // --- update continuity with new totals - // applied over 1/2 of routing step - massbal_updateRoutingTotals(routingStep/2.); + return FALSE; +} + +//============================================================================= + +int routeFlow(int routingModel, double routingStep) +{ + int j; + int stepCount = 1; + + // --- replace old hydraulic state values with current ones + for (j = 0; j < Nobjects[LINK]; j++) link_setOldHydState(j); + for (j = 0; j < Nobjects[NODE]; j++) node_setOldHydState(j); + + // --- initialize node inflows to lateral flows, outflows to evap + + // seepage losses, & overflows to excess stored volume + for (j = 0; j < Nobjects[NODE]; j++) + node_initFlows(j, routingStep); - // --- update summary statistics - if ( RptFlags.flowStats && Nobjects[LINK] > 0 ) + // --- route flow through the drainage network + if ( Nobjects[LINK] > 0 ) { - stats_updateFlowStats(routingStep, getDateTime(NewRoutingTime), - stepCount, inSteadyState); + stepCount = flowrout_execute(SortedLinks, routingModel, routingStep); } + + // --- save overflows at inlet capture nodes as inlet backflow + return stepCount; +} + +//============================================================================= + +void removeSystemOutflows(double routingStep) +{ + // --- remove evaporation, infiltration & outflows from system + removeStorageLosses(routingStep); + removeConduitLosses(); + removeOutflows(routingStep); } //============================================================================= @@ -379,16 +446,14 @@ void addExternalInflows(DateTime currentDate) // --- for each node with a defined external inflow for (j = 0; j < Nobjects[NODE]; j++) { - inflow = Node[j].extInflow; - if ( !inflow ) continue; - // --- get flow inflow - q = 0.0; + q = Node[j].apiExtInflow; + inflow = Node[j].extInflow; while ( inflow ) { if ( inflow->type == FLOW_INFLOW ) { - q = inflow_getExtInflow(inflow, currentDate); + q += inflow_getExtInflow(inflow, currentDate); break; } else inflow = inflow->next; @@ -397,7 +462,13 @@ void addExternalInflows(DateTime currentDate) // --- add flow inflow to node's lateral inflow Node[j].newLatFlow += q; - massbal_addInflowFlow(EXTERNAL_INFLOW, q); + if (q >= 0.0) + massbal_addInflowFlow(EXTERNAL_INFLOW, q); + else + { + massbal_addOutflowFlow(-q, FALSE); + continue; + } // --- add on any inflow (i.e., reverse flow) through an outfall if ( Node[j].type == OUTFALL && Node[j].oldNetInflow < 0.0 ) @@ -700,8 +771,6 @@ void addIfaceInflows(DateTime currentDate) } } - - //============================================================================= int inflowHasChanged() @@ -780,11 +849,11 @@ void removeConduitLosses() int i, k; double barrels, evapLoss = 0.0, - seepLoss = 0.0; + seepLoss = 0.0; for ( i = 0; i < Nobjects[LINK]; i++ ) { - if (Link[i].type == CONDUIT) + if (Link[i].type == CONDUIT) { // --- retrieve number of barrels k = Link[i].subIndex; @@ -830,7 +899,7 @@ void removeOutflows(double tStep) // --- update mass balance with flow and mass leaving the system // through outfalls and flooded interior nodes q = node_getSystemOutflow(i, &isFlooded); - if ( q > 0.0 ) //(5.1.013) + if ( q > 0.0 ) { massbal_addOutflowFlow(q, isFlooded); for ( p = 0; p < Nobjects[POLLUT]; p++ ) @@ -839,7 +908,7 @@ void removeOutflows(double tStep) massbal_addOutflowQual(p, w, isFlooded); } } - else massbal_addInflowFlow(EXTERNAL_INFLOW, -q); //(5.1.013) + else massbal_addInflowFlow(EXTERNAL_INFLOW, -q); // --- update mass balance with mass leaving system through negative // lateral inflows (lateral flow was previously accounted for) @@ -852,7 +921,6 @@ void removeOutflows(double tStep) massbal_addOutflowQual(p, w, FALSE); } } - } } @@ -865,20 +933,22 @@ void sortEvents() // Purpose: sorts the entries of the Event array in chronological order. // { - int i, j; + int i, j, gap; TEvent temp; - // Apply simple exchange sort to event list - for (i = 0; i < NumEvents-1; i++) + // Apply shell sort to event list + for (gap = NumEvents/2; gap >= 1; gap /= 2) { - for (j = i+1; j < NumEvents; j++) + for (i = gap; i < NumEvents; i += gap) { - if ( Event[i].start > Event[j].start ) + temp = Event[i]; + j = i - gap; + while (j >= 0 && Event[j].start > temp.start) { - temp = Event[j]; - Event[j] = Event[i]; - Event[i] = temp; + Event[j+gap] = Event[j]; + j -= gap; } + if (j != i-gap) Event[j+gap] = temp; } } diff --git a/src/solver/runoff.c b/src/solver/runoff.c index a7a6f5d83..b8f9fb72b 100644 --- a/src/solver/runoff.c +++ b/src/solver/runoff.c @@ -2,37 +2,32 @@ // runoff.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 08/01/16 (Build 5.1.011) -// 03/14/17 (Build 5.1.012) -// 03/01/20 (Build 5.1.014) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // M. Tryby // // Runoff analysis functions. // +// Update History +// ============== // Build 5.1.007: // - Climate file now opened in climate.c module. -// // Build 5.1.008: // - Memory for runoff pollutant load now allocated and freed in this module. // - Runoff time step chosen so that simulation does not exceed total duration. // - State of LIDs considered when choosing wet or dry time step. // - More checks added to skip over subcatchments with zero area. // - Support added for sending outfall node discharge onto a subcatchment. -// // Build 5.1.011: // - Runoff wet time step kept aligned with reporting times. // - Prior runoff time step used to convert returned outfall volume to flow. -// // Build 5.1.012: // - Runoff wet time step no longer kept aligned with reporting times. -// // Build 5.1.014: // - Fixed street sweeping bug. +// Build 5.2.0: +// - Support added for saving rainfall amounts in previous 48 hours. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -185,7 +180,7 @@ void runoff_execute() if ( Nobjects[SUBCATCH] == 0 ) { OldRunoffTime = NewRunoffTime; - NewRunoffTime += (double)(1000 * DryStep); + NewRunoffTime += (double)(DryStep) * 1000.; NewRunoffTime = MIN(NewRunoffTime, TotalDuration); return; } @@ -235,6 +230,10 @@ void runoff_execute() NewRunoffTime = TotalDuration; } + // --- update past n-hour rain totals + for (j = 0; j < Nobjects[GAGE]; j++) + gage_updatePastRain(j, (int)runoffStep); + // --- update old state of each subcatchment, for (j = 0; j < Nobjects[SUBCATCH]; j++) subcatch_setOldState(j); @@ -433,7 +432,7 @@ void runoff_readFromFile(void) // --- read runoff time step kount = 0; - kount += fread(&tStep, sizeof(float), 1, Frunoff.file); + kount += (int)fread(&tStep, sizeof(float), 1, Frunoff.file); // --- compute number of results saved for each subcatchment nResults = MAX_SUBCATCH_RESULTS + Nobjects[POLLUT] - 1; @@ -442,7 +441,7 @@ void runoff_readFromFile(void) for (j = 0; j < Nobjects[SUBCATCH]; j++) { // --- read vector of saved results - kount += fread(SubcatchResults, sizeof(float), nResults, Frunoff.file); + kount += (int)fread(SubcatchResults, sizeof(float), nResults, Frunoff.file); // --- extract hydrologic results, converting units where necessary // (results were saved to file in user's units) diff --git a/src/solver/shape.c b/src/solver/shape.c index ebf38ad85..975f9d88d 100644 --- a/src/solver/shape.c +++ b/src/solver/shape.c @@ -2,8 +2,8 @@ // shape.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Geometry functions for custom cross-section shapes. diff --git a/src/solver/snow.c b/src/solver/snow.c index e188f8402..2e1c74370 100644 --- a/src/solver/snow.c +++ b/src/solver/snow.c @@ -2,19 +2,21 @@ // snow.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 03/19/15 (Build 5.1.008) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Models snow melt processes. // +// Update History +// ============== // Build 5.1.008: // - Adjustment of snowmelt and subcatchment's net precipitation for area // covered by snow was corrected. // - Area covered by snow now included in calculation of rate that liquid // water leaves a snowpack. -// +// Build 5.2.0: +// - Subcatchment snow pack area should not include LID area. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -399,7 +401,8 @@ void snow_plowSnow(int j, double tStep) exc = snowpack->wsnow[SNOW_PLOWABLE]; // --- plow out of system - f = snowpack->fArea[SNOW_PLOWABLE] * Subcatch[j].area; + f = snowpack->fArea[SNOW_PLOWABLE] * + (Subcatch[j].area - Subcatch[j].lidArea); Snow.removed += Snowmelt[k].sfrac[0] * exc * f; sfracTotal = Snowmelt[k].sfrac[0]; @@ -544,7 +547,7 @@ double snow_getSnowCover(int j) snowCover += (snowpack->wsnow[i] + snowpack->fw[i]) * snowpack->fArea[i]; } - return snowCover * Subcatch[j].area; + return snowCover * (Subcatch[j].area - Subcatch[j].lidArea); } //============================================================================= diff --git a/src/solver/stats.c b/src/solver/stats.c index b23d015c1..2e4d2f7fc 100644 --- a/src/solver/stats.c +++ b/src/solver/stats.c @@ -2,46 +2,41 @@ // stats.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 08/01/16 (Build 5.1.011) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// 04/01/20 (Build 5.1.015) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // R. Dickinson (CDM) // // Simulation statistics functions. // +// Update History +// ============== // Build 5.1.007: // - Exfiltration losses added to storage node statistics. -// // Build 5.1.008: // - Support for updating groundwater statistics added. // - Support for updating maximum reported nodal depths added. // - OpenMP parallelization applied to updating node and link flow statistics. // - Updating of time that conduit is upstrm/dnstrm full was modified. -// // Build 5.1.011: // - Surcharging is now evaluated only under dynamic wave flow routing and // storage nodes cannot be classified as surcharged. -// // Build 5.1.012: // - Time step statistics now evaluated only in non-steady state periods. // - Check for full conduit flow now accounts for number of barrels. -// // Build 5.1.013: // - Include omp.h protected against lack of compiler support for OpenMP. // - Statistics on impervious and pervious runoff totals added. // - Storage nodes with a non-zero surcharge depth (e.g. enclosed tanks) // can now be classified as being surcharged. -// // Build 5.1.015: // - Fixes bug in summary statistics when Report Start date > Start Date. // - Fixes failure to initialize all subcatchment groundwater statistics. // - Support added for grouped freqency table of routing time steps. +// Build 5.2.0: +// - Support added for reporting most frequent non-converging nodes. +// - Support added for RptFlags.disabled option. +// - Fixed display of routing statistics report for RptFlags.flowStats = FALSE. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -49,32 +44,30 @@ #include #include #include "headers.h" -#include "swmm5.h" -#if defined(_OPENMP) //(5.1.013) -#include -#endif //----------------------------------------------------------------------------- // Shared variables //----------------------------------------------------------------------------- #define MAX_STATS 5 -static TSysStats SysStats; +static TTimeStepStats TimeStepStats; static TMaxStats MaxMassBalErrs[MAX_STATS]; static TMaxStats MaxCourantCrit[MAX_STATS]; static TMaxStats MaxFlowTurns[MAX_STATS]; +static TMaxStats MaxNonConverged[MAX_STATS]; static double SysOutfallFlow; //----------------------------------------------------------------------------- // Exportable variables (shared with statsrpt.c) //----------------------------------------------------------------------------- -TSubcatchStats* SubcatchStats; -TNodeStats* NodeStats; -TLinkStats* LinkStats; -TStorageStats* StorageStats; -TOutfallStats* OutfallStats; -TPumpStats* PumpStats; -double MaxOutfallFlow; -double MaxRunoffFlow; +TSubcatchStats* SubcatchStats; +TNodeStats* NodeStats; +TLinkStats* LinkStats; +TStorageStats* StorageStats; +TOutfallStats* OutfallStats; +TPumpStats* PumpStats; +double MaxOutfallFlow; +double MaxRunoffFlow; +double RoutingTimeSpan; //----------------------------------------------------------------------------- // Imported variables @@ -91,8 +84,10 @@ extern double* NodeOutflow; // defined in massbal.c // stats_updateSubcatchStats (called from subcatch_getRunoff) // stats_updateGwaterStats (called from gwater_getGroundwater) // stats_updateFlowStats (called from routing_execute) +// stats_updateTimeStepStats (called from routing_execute) // stats_updateCriticalTimeCount (called from getVariableStep in dynwave.c) // stats_updateMaxNodeDepth (called from output_saveNodeResults) +// stats_updateConvergenceStats (called from updateConvergenceStats in dynwave.c) //----------------------------------------------------------------------------- // Local functions @@ -112,9 +107,9 @@ int stats_open() // { int j, k; - double timeStepDelta; //(5.1.015) - double logMaxTimeStep; //(5.1.015) - double logMinTimeStep; //(5.1.015) + double timeStepDelta; + double logMaxTimeStep; + double logMinTimeStep; // --- set all pointers to NULL NodeStats = NULL; @@ -142,8 +137,8 @@ int stats_open() SubcatchStats[j].infil = 0.0; SubcatchStats[j].runoff = 0.0; SubcatchStats[j].maxFlow = 0.0; - SubcatchStats[j].impervRunoff = 0.0; //(5.1.013) - SubcatchStats[j].pervRunoff = 0.0; // + SubcatchStats[j].impervRunoff = 0.0; + SubcatchStats[j].pervRunoff = 0.0; } for (j=0; jstats.deepFlow = 0.0; Subcatch[j].groundwater->stats.evap = 0.0; Subcatch[j].groundwater->stats.maxFlow = 0.0; - Subcatch[j].groundwater->stats.finalUpperMoist = 0.0; //(5.1.015) - Subcatch[j].groundwater->stats.finalWaterTable = 0.0; // + Subcatch[j].groundwater->stats.finalUpperMoist = 0.0; + Subcatch[j].groundwater->stats.finalWaterTable = 0.0; } } @@ -189,6 +184,7 @@ int stats_open() NodeStats[j].maxInflow = 0.0; NodeStats[j].maxOverflow = 0.0; NodeStats[j].maxPondedVol = 0.0; + NodeStats[j].nonConvergedCount = 0; NodeStats[j].maxInflowDate = StartDateTime; NodeStats[j].maxOverflowDate = StartDateTime; } @@ -199,6 +195,7 @@ int stats_open() LinkStats[j].maxFlow = 0.0; LinkStats[j].maxVeloc = 0.0; LinkStats[j].maxDepth = 0.0; + LinkStats[j].maxStreetFilled = 0.0; LinkStats[j].timeSurcharged = 0.0; LinkStats[j].timeFullUpstream = 0.0; LinkStats[j].timeFullDnstream = 0.0; @@ -267,10 +264,10 @@ int stats_open() } // --- allocate memory & initialize pumping statistics - if ( Nlinks[PUMP] > 0 ) - { + if ( Nlinks[PUMP] > 0 ) + { PumpStats = (TPumpStats *) calloc(Nlinks[PUMP], sizeof(TPumpStats)); - if ( !PumpStats ) + if ( !PumpStats ) { report_writeErrorMsg(ERR_MEMORY, ""); return ErrorCode; @@ -280,37 +277,39 @@ int stats_open() PumpStats[j].utilized = 0.0; PumpStats[j].minFlow = 0.0; PumpStats[j].avgFlow = 0.0; - PumpStats[j].maxFlow = 0.0; + PumpStats[j].maxFlow = 0.0; PumpStats[j].volume = 0.0; PumpStats[j].energy = 0.0; PumpStats[j].startUps = 0; - PumpStats[j].offCurveLow = 0.0; + PumpStats[j].offCurveLow = 0.0; PumpStats[j].offCurveHigh = 0.0; - } - } + } + } // --- initialize system stats MaxRunoffFlow = 0.0; MaxOutfallFlow = 0.0; - SysStats.maxTimeStep = 0.0; - SysStats.minTimeStep = RouteStep; - SysStats.avgTimeStep = 0.0; - SysStats.avgStepCount = 0.0; - SysStats.steadyStateCount = 0.0; - - // --- divide range between min and max routing time steps into //(5.1.015) - // equal intervals using a logarithmic scale // - logMaxTimeStep = log10(RouteStep); // - logMinTimeStep = log10(MinRouteStep); // - timeStepDelta = (logMaxTimeStep - logMinTimeStep) / (TIMELEVELS-1); // - SysStats.timeStepIntervals[0] = RouteStep; // - for (j = 1; j < TIMELEVELS; j++) // - { // - SysStats.timeStepIntervals[j] = // - pow(10., logMaxTimeStep - j * timeStepDelta); // - SysStats.timeStepCounts[j] = 0; // - } // - SysStats.timeStepIntervals[TIMELEVELS - 1] = MinRouteStep; // + TimeStepStats.maxTimeStep = 0.0; + TimeStepStats.minTimeStep = RouteStep; + TimeStepStats.routingTime = 0.0; + TimeStepStats.trialsCount = 0.0; + TimeStepStats.steadyStateTime = 0.0; + TimeStepStats.timeStepCount = 0; + + // --- divide range between min and max routing time steps into + // equal intervals using a logarithmic scale + logMaxTimeStep = log10(RouteStep); + logMinTimeStep = log10(MinRouteStep); + timeStepDelta = (logMaxTimeStep - logMinTimeStep) / (double)(TIMELEVELS-1); + TimeStepStats.timeStepIntervals[0] = RouteStep; + for (j = 1; j < TIMELEVELS; j++) + { + TimeStepStats.timeStepIntervals[j] = + pow(10., logMaxTimeStep - j * timeStepDelta); + TimeStepStats.timeStepCounts[j] = 0; + } + TimeStepStats.timeStepIntervals[TIMELEVELS - 1] = MinRouteStep; + RoutingTimeSpan = 0.0; return 0; } @@ -319,7 +318,7 @@ int stats_open() void stats_close() // // Input: none -// Output: +// Output: // Purpose: closes the simulation statistics system. // { @@ -328,7 +327,7 @@ void stats_close() FREE(SubcatchStats); FREE(NodeStats); FREE(LinkStats); - FREE(StorageStats); + FREE(StorageStats); if ( OutfallStats ) { for ( j=0; j 0 && RouteModel != NO_ROUTING ) { stats_findMaxStats(); - report_writeMaxStats(MaxMassBalErrs, MaxCourantCrit, MAX_STATS); - report_writeMaxFlowTurns(MaxFlowTurns, MAX_STATS); - report_writeSysStats(&SysStats); + if (!RptFlags.disabled && RptFlags.flowStats) + { + report_writeMaxStats(MaxMassBalErrs, MaxCourantCrit, MAX_STATS); + report_writeMaxFlowTurns(MaxFlowTurns, MAX_STATS); + report_writeNonconvergedStats(MaxNonConverged, MAX_STATS); + report_writeTimeStepStats(&TimeStepStats); + } } // --- report summary statistics - statsrpt_writeReport(); + if (!RptFlags.disabled) + statsrpt_writeReport(); } //============================================================================= @@ -386,8 +390,8 @@ void stats_updateSubcatchStats(int j, double rainVol, double runonVol, SubcatchStats[j].infil += infilVol; SubcatchStats[j].runoff += runoffVol; SubcatchStats[j].maxFlow = MAX(SubcatchStats[j].maxFlow, runoff); - SubcatchStats[j].impervRunoff += impervVol; //(5.1.013) - SubcatchStats[j].pervRunoff += pervVol; // + SubcatchStats[j].impervRunoff += impervVol; + SubcatchStats[j].pervRunoff += pervVol; } //============================================================================= @@ -421,10 +425,10 @@ void stats_updateMaxRunoff() { int j; double sysRunoff = 0.0; - + for (j=0; j 0 ) + if (OldRoutingTime > 0) { - SysStats.minTimeStep = MIN(SysStats.minTimeStep, tStep); - - // --- locate interval that logged time step falls in //(5.1.015) - // and update its count // - for (j = 1; j < TIMELEVELS; j++) // - if (tStep >= SysStats.timeStepIntervals[j]) // - { // - SysStats.timeStepCounts[j]++; // - break; // - } // - } - SysStats.avgTimeStep += tStep; - SysStats.maxTimeStep = MAX(SysStats.maxTimeStep, tStep); - - // --- update iteration step count stats - SysStats.avgStepCount += stepCount; - } + TimeStepStats.minTimeStep = MIN(TimeStepStats.minTimeStep, tStep); - // --- update max. system outfall flow - MaxOutfallFlow = MAX(MaxOutfallFlow, SysOutfallFlow); + // --- locate interval that logged time step falls in + // and update its count + for (j = 1; j < TIMELEVELS; j++) + if (tStep >= TimeStepStats.timeStepIntervals[j]) + { + TimeStepStats.timeStepCounts[j]++; + break; + } + } + TimeStepStats.maxTimeStep = MAX(TimeStepStats.maxTimeStep, tStep); + TimeStepStats.routingTime += tStep; + TimeStepStats.timeStepCount++; + TimeStepStats.trialsCount += trialsCount; + } } //============================================================================= - + void stats_updateCriticalTimeCount(int node, int link) // // Input: node = node index @@ -518,6 +533,13 @@ void stats_updateCriticalTimeCount(int node, int link) //============================================================================= +void stats_updateConvergenceStats(int node, int converged) +{ + if (converged == FALSE) NodeStats[node].nonConvergedCount++; +} + +//============================================================================= + void stats_updateNodeStats(int j, double tStep, DateTime aDate) // // Input: j = node index @@ -530,7 +552,6 @@ void stats_updateNodeStats(int j, double tStep, DateTime aDate) int k, p; double newVolume = Node[j].newVolume; double newDepth = Node[j].newDepth; - double yCrown = Node[j].crownElev - Node[j].invertElev; int canPond = (AllowPonding && Node[j].pondedArea > 0.0); // --- update depth statistics @@ -540,7 +561,7 @@ void stats_updateNodeStats(int j, double tStep, DateTime aDate) NodeStats[j].maxDepth = newDepth; NodeStats[j].maxDepthDate = aDate; } - + // --- update flooding, ponding, and surcharge statistics if ( Node[j].type != OUTFALL ) { @@ -553,11 +574,11 @@ void stats_updateNodeStats(int j, double tStep, DateTime aDate) (newVolume - Node[j].fullVolume)); } - // --- for dynamic wave routing, classify a node as //(5.1.013) + // --- for dynamic wave routing, classify a node as // surcharged if its water level exceeds its crown elev. - if (RouteModel == DW) //(5.1.013) + if (RouteModel == DW) { - if ((Node[j].type != STORAGE || Node[j].surDepth > 0.0) && //(5.1.013) + if ((Node[j].type != STORAGE || Node[j].surDepth > 0.0) && newDepth + Node[j].invertElev + FUDGE >= Node[j].crownElev) { NodeStats[j].timeSurcharged += tStep; @@ -570,10 +591,10 @@ void stats_updateNodeStats(int j, double tStep, DateTime aDate) { k = Node[j].subIndex; StorageStats[k].avgVol += newVolume; - StorageStats[k].evapLosses += - Storage[Node[j].subIndex].evapLoss; + StorageStats[k].evapLosses += + Storage[Node[j].subIndex].evapLoss; StorageStats[k].exfilLosses += - Storage[Node[j].subIndex].exfilLoss; + Storage[Node[j].subIndex].exfilLoss; newVolume = MIN(newVolume, Node[j].fullVolume); if ( newVolume > StorageStats[k].maxVol ) @@ -585,7 +606,7 @@ void stats_updateNodeStats(int j, double tStep, DateTime aDate) } // --- update outfall statistics - if ( Node[j].type == OUTFALL ) + if ( Node[j].type == OUTFALL ) { k = Node[j].subIndex; if ( Node[j].inflow >= MIN_RUNOFF_FLOW ) @@ -596,15 +617,15 @@ void stats_updateNodeStats(int j, double tStep, DateTime aDate) } for (p=0; p fabs(NodeStats[j].maxLatFlow) ) NodeStats[j].maxLatFlow = Node[j].newLatFlow; if ( Node[j].inflow > NodeStats[j].maxInflow ) @@ -685,22 +706,21 @@ void stats_updateLinkStats(int j, double tStep, DateTime aDate) } else if ( Link[j].type == CONDUIT ) { - - // --- update time under normal flow & inlet control + // --- update time under normal flow & inlet control if ( Link[j].normalFlow ) LinkStats[j].timeNormalFlow += tStep; if ( Link[j].inletControl ) LinkStats[j].timeInletControl += tStep; - + // --- update flow classification distribution k = Link[j].flowClass; if ( k >= 0 && k < MAX_FLOW_CLASSES ) { - ++LinkStats[j].timeInFlowClass[k]; + LinkStats[j].timeInFlowClass[k] += tStep; } // --- update time conduit is full k = Link[j].subIndex; if ( q >= Link[j].qFull * (double)Conduit[k].barrels ) - LinkStats[j].timeFullFlow += tStep; + LinkStats[j].timeFullFlow += tStep; if ( Conduit[k].capacityLimited ) LinkStats[j].timeCapacityLimited += tStep; @@ -717,6 +737,11 @@ void stats_updateLinkStats(int j, double tStep, DateTime aDate) case DN_FULL: LinkStats[j].timeFullDnstream += tStep; } + + // --- update max. degree filled for streets + if (Link[j].xsect.type == STREET_XSECT) + LinkStats[j].maxStreetFilled = + MAX(LinkStats[j].maxStreetFilled, street_getExtentFilled(j)); } // --- update flow turn count @@ -737,8 +762,8 @@ void stats_findMaxStats() // { int j; - double x; - double stepCount = ReportStepCount - SysStats.steadyStateCount; //(5.1.015) + double x, z; + double stepCount; // --- initialize max. stats arrays for (j=0; j 2 ) //(5.1.015) + // --- find links with most flow turns during reporting period + if ( ReportStepCount > 2 ) { + stepCount = ReportStepCount; + z = 100.0 / (2./3. * (stepCount - 2.)); for (j=0; j Start Date. +// Build 5.2.0: +// - Adds a new Street & Inlet Summary table. +// - Fixes value used for total reporting time. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -51,8 +46,10 @@ extern TOutfallStats* OutfallStats; extern TPumpStats* PumpStats; extern double MaxOutfallFlow; extern double MaxRunoffFlow; +extern double RoutingTimeSpan; + extern double* NodeInflow; // defined in MASSBAL.C -extern double* NodeOutflow; // defined in massbal.c +extern double* NodeOutflow; //----------------------------------------------------------------------------- // Local functions @@ -87,8 +84,8 @@ void statsrpt_writeReport() // { // --- set number of decimal places for reporting flow values - if ( FlowUnits == MGD || FlowUnits == CMS ) strcpy(FlowFmt, "%9.3f"); - else strcpy(FlowFmt, "%9.2f"); + if ( FlowUnits == MGD || FlowUnits == CMS ) sstrncpy(FlowFmt, "%9.3f", 5); + else sstrncpy(FlowFmt, "%9.2f", 5); // --- volume conversion factor from ft3 to Mgal or Mliters if (UnitSystem == US) Vcf = 7.48 / 1.0e6; @@ -117,6 +114,7 @@ void statsrpt_writeReport() writeNodeFlooding(); writeStorageVolumes(); writeOutfallLoads(); + inlet_writeStatsReport(); writeLinkFlows(); writeFlowClass(); writeLinkSurcharge(); @@ -140,8 +138,6 @@ void writeSubcatchRunoff() WRITE(""); fprintf(Frpt.file, -//////// Segment below modified for release 5.1.013. ///////// - "\n ------------------------------------------------------------------------------------------------------------------------------" "\n Total Total Total Total Imperv Perv Total Total Peak Runoff" "\n Precip Runon Evap Infil Runoff Runoff Runoff Runoff Runoff Coeff"); @@ -154,8 +150,6 @@ void writeSubcatchRunoff() fprintf(Frpt.file, "\n ------------------------------------------------------------------------------------------------------------------------------"); -///////////////////////////////////////////////////////////////// - for ( j = 0; j < Nobjects[SUBCATCH]; j++ ) { a = Subcatch[j].area; @@ -169,10 +163,10 @@ void writeSubcatchRunoff() fprintf(Frpt.file, " %10.2f", x/a); x = SubcatchStats[j].infil * UCF(RAINDEPTH); fprintf(Frpt.file, " %10.2f", x/a); - x = SubcatchStats[j].impervRunoff * UCF(RAINDEPTH); //(5.1.013) - fprintf(Frpt.file, " %10.2f", x/a); // - x = SubcatchStats[j].pervRunoff * UCF(RAINDEPTH); // - fprintf(Frpt.file, " %10.2f", x/a); // + x = SubcatchStats[j].impervRunoff * UCF(RAINDEPTH); + fprintf(Frpt.file, " %10.2f", x/a); + x = SubcatchStats[j].pervRunoff * UCF(RAINDEPTH); + fprintf(Frpt.file, " %10.2f", x/a); x = SubcatchStats[j].runoff * UCF(RAINDEPTH); fprintf(Frpt.file, " %10.2f", x/a); x = SubcatchStats[j].runoff * Vcf; @@ -271,7 +265,7 @@ void writeSubcatchLoads() { i = UnitSystem; if ( Pollut[p].units == COUNT ) i = 2; - strcpy(units, LoadUnitsWords[i]); + sstrncpy(units, LoadUnitsWords[i], 14); fprintf(Frpt.file, "%14s", units); totals[p] = 0.0; } @@ -341,7 +335,7 @@ void writeNodeDepths() fprintf(Frpt.file, " %-9s ", NodeTypeWords[Node[j].type]); getElapsedTime(NodeStats[j].maxDepthDate, &days, &hrs, &mins); fprintf(Frpt.file, "%7.2f %7.2f %7.2f %4d %02d:%02d %10.2f", - NodeStats[j].avgDepth / ReportStepCount * UCF(LENGTH), //(5.1.015) + NodeStats[j].avgDepth / ReportStepCount * UCF(LENGTH), NodeStats[j].maxDepth * UCF(LENGTH), (NodeStats[j].maxDepth + Node[j].invertElev) * UCF(LENGTH), days, hrs, mins, NodeStats[j].maxRptDepth); @@ -480,8 +474,9 @@ void writeNodeFlooding() FlowUnitWords[FlowUnits], VolUnitsWords[UnitSystem]); if ( RouteModel == DW ) fprintf(Frpt.file, " %6s", PondingUnitsWords[UnitSystem]); - else if ( UnitSystem == US ) fprintf(Frpt.file, " 1000 ft3"); - else fprintf(Frpt.file, " 1000 m3"); + // OWA EDIT - write unicode super script 3 (\xC2\xB3) instead of extended ascii (\xB3) + else if ( UnitSystem == US ) fprintf(Frpt.file, " 1000 ft\xC2\xB3"); + else fprintf(Frpt.file, " 1000 m\xC2\xB3"); fprintf(Frpt.file, "\n --------------------------------------------------------------------------"); n = 1; @@ -542,7 +537,7 @@ void writeStorageVolumes() if ( Node[j].type != STORAGE ) continue; k = Node[j].subIndex; fprintf(Frpt.file, "\n %-20s", Node[j].ID); - avgVol = StorageStats[k].avgVol / (double)ReportStepCount; //(5.1.015) + avgVol = StorageStats[k].avgVol / (double)ReportStepCount; maxVol = StorageStats[k].maxVol; pctMaxVol = 0.0; pctAvgVol = 0.0; @@ -622,7 +617,7 @@ void writeOutfallLoads() { i = UnitSystem; if ( Pollut[p].units == COUNT ) i = 2; - strcpy(units, LoadUnitsWords[i]); + sstrncpy(units, LoadUnitsWords[i], 14); fprintf(Frpt.file, "%14s", units); } fprintf(Frpt.file, @@ -638,7 +633,7 @@ void writeOutfallLoads() // --- print node ID, flow freq., avg. flow, max. flow & flow vol. fprintf(Frpt.file, "\n %-20s", Node[j].ID); - x = 100.*flowCount/(double)ReportStepCount; //(5.1.015) + x = 100.*flowCount/(double)ReportStepCount; fprintf(Frpt.file, "%7.2f", x); freqSum += x; if ( flowCount > 0 ) @@ -781,10 +776,11 @@ void writeFlowClass() // // Input: none // Output: none -// Purpose: writes flow classification fro each conduit to report file. +// Purpose: writes flow classification for each conduit to report file. // { int i, j, k; + double totalSeconds = RoutingTimeSpan; if ( RouteModel != DW ) return; WRITE(""); @@ -808,12 +804,11 @@ void writeFlowClass() for ( i=0; i 0 ) @@ -938,7 +933,7 @@ void writeLinkLoads() { i = UnitSystem; if ( Pollut[p].units == COUNT ) i = 2; - strcpy(units, LoadUnitsWords[i]); + sstrncpy(units, LoadUnitsWords[i], 14); fprintf(Frpt.file, "%14s", units); } fprintf(Frpt.file, "\n %s", linkLine); diff --git a/src/solver/street.c b/src/solver/street.c new file mode 100644 index 000000000..fd05451b7 --- /dev/null +++ b/src/solver/street.c @@ -0,0 +1,162 @@ +//----------------------------------------------------------------------------- +// street.c +// +// Project: EPA SWMM5 +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman +// +// Street Cross-Section Functions +// +//----------------------------------------------------------------------------- +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include "headers.h" + +//----------------------------------------------------------------------------- +// External functions (declared in street.h) +//----------------------------------------------------------------------------- +// street_create called by createObjects in project.c +// street_delete called by deleteObjects in project.c +// street_readParams called by parseLine in input.c + +//============================================================================= + +int street_create(int nStreets) +// +// Input: nStreets = number of Street objects to create +// Output: none +// Purpose: creats a collection of Street objects. +// +{ + Street = NULL; + Nobjects[STREET] = 0; + Street = (TStreet *)calloc(nStreets, sizeof(TStreet)); + if (Street == NULL) return ERR_MEMORY; + Nobjects[STREET] = nStreets; + return 0; +} + +//============================================================================= + +void street_delete() +// +// Input: none +// Output: none +// Purpose: deletes the collection of Street objects. +// +{ + FREE(Street) +} + +//============================================================================= + +int street_readParams(char* tok[], int ntoks) +// +// Format is: +// ID Tcrown Hcurb Sx nRoad (Hdep Wg Sides Tback Sback nBack) +// where +// ID = name assigned to street cross section +// Tcrown = distance from curb to street crown (ft or m) +// Hcurb = curb height (ft or m) +// Sx = roadway cross slope (%) +// nRoad = roadway Manning's n +// Hdep = depressed gutter height (ft or m) +// Wg = depressed gutter width (ft or m) +// Sides = 1 or 2 sided +// Tback = width of street backing (ft or m) +// Sback = slope of street backing (ft or m) +// nBack = Manning's n of street backing +{ + int i, k, sides; + double x[11]; + TStreet *street; + + // --- check for minimum number of tokens + if (ntoks < 5) return error_setInpError(ERR_ITEMS, ""); + + // --- check that street exists in project + i = project_findObject(STREET, tok[0]); + if (i < 0) return error_setInpError(ERR_NAME, tok[0]); + Street[i].ID = project_findID(STREET, tok[0]); + + // --- parse required data + for (k = 0; k <= 9; k++) x[k] = 0.0; + for (k = 1; k <= 4; k++) + if (!getDouble(tok[k], &x[k]) || x[k] <= 0.0) + return error_setInpError(ERR_NUMBER, tok[k]); + + // --- read gutter depression + if (ntoks > 5) + if (!getDouble(tok[5], &x[5]) || x[5] < 0.0) + return error_setInpError(ERR_NUMBER, tok[5]); + + // --- read gutter width + if (ntoks > 6) + if (!getDouble(tok[6], &x[6]) || x[6] < 0.0) + return error_setInpError(ERR_NUMBER, tok[6]); + + // --- read if 1- or 2-sided + sides = 2; + if (ntoks > 7) + if (!getInt(tok[7], &sides) || sides < 1 || sides > 2) + return error_setInpError(ERR_NUMBER, tok[7]); + + // --- read street backing parameters + if (ntoks > 8) + { + if (!getDouble(tok[8], &x[8]) || x[k] < 0.0) + return error_setInpError(ERR_NUMBER, tok[k]); + if (x[8] > 0.0) + { + if (ntoks < 11) return error_setInpError(ERR_ITEMS, ""); + for (k = 9; k <= 10; k++) + if (!getDouble(tok[k], &x[k]) || x[k] <= 0.0) + return error_setInpError(ERR_NUMBER, tok[k]); + } + } + + // --- assign input values to street object + street = &Street[i]; + street->width = x[1] / UCF(LENGTH); + street->curbHeight = x[2] / UCF(LENGTH); + street->slope = x[3] / 100.0; + street->roughness = x[4]; + street->gutterDepression = x[5] / UCF(LENGTH); + street->gutterWidth = x[6] / UCF(LENGTH); + street->sides = sides; + street->backWidth = x[8] / UCF(LENGTH); + street->backSlope = x[9] / 100.0; + street->backRoughness = x[10]; + transect_createStreetTransect(street); + return 0; +} + +//============================================================================= + +double street_getExtentFilled(int link) +// +// Input: link = a link index +// Output: degree to which street is filled +// Purpose: finds the degree to which a street link is filled based on the +// depth (for DW routing) or cross section area at the higher end. +// +{ + int k, t; + double filled; + + t = Link[link].xsect.transect; + if (t < 0) return 0.0; + if (RouteModel == DW) + { + filled = MAX(Node[Link[link].node1].newDepth, + Node[Link[link].node2].newDepth); + } + else + { + k = Link[link].subIndex; + filled = MAX(Conduit[k].a1, Conduit[k].a2); + } + return filled; +} diff --git a/src/solver/street.h b/src/solver/street.h new file mode 100644 index 000000000..bc88fff34 --- /dev/null +++ b/src/solver/street.h @@ -0,0 +1,21 @@ +//----------------------------------------------------------------------------- +// street.h +// +// Project: EPA SWMM5 +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman +// +// Definition of the Street Cross-Section Object +// +//----------------------------------------------------------------------------- +#ifndef STREET_H +#define STREET_H + +// Shared street functions +int street_create(int nStreets); +void street_delete(); +int street_readParams(char* tok[], int ntoks); +double street_getExtentFilled(int link); + +#endif diff --git a/src/solver/subcatch.c b/src/solver/subcatch.c index 659d428fc..7956240cc 100644 --- a/src/solver/subcatch.c +++ b/src/solver/subcatch.c @@ -2,20 +2,14 @@ // subcatch.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/19/14 (Build 5.1.000) -// 04/19/14 (Build 5.1.006) -// 03/19/15 (Build 5.1.008) -// 04/30/15 (Build 5.1.009) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// 05/18/20 (Build 5.1.015) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Subcatchment runoff functions. // +// Update History +// ============== // Build 5.1.008: // - Support added for keeping separate track of drain outflows from LIDs. // - Processing of inflow/outflow volumes over a time step was refactored. @@ -24,28 +18,22 @@ // - Runon now distributed only over non-LID area of a subcatchment, unless // LID covers full area. // - Pollutant buildup and washoff functions were moved to surfqual.c. -// // Build 5.1.009: // - Runon for full LID subcatchment added to statistical summary. -// // Build 5.1.010: // - Fixed a bug introduced in 5.1.008 that forgot to include LID // exfiltration as inflow sent to GW routine. -// // Build 5.1.011: // - Subcatchment percent imperviousness not allowed to exceed 100. -// // Build 5.1.012: // - Subcatchment bottom elevation used instead of aquifer's when // saving water table value to results file. -// // Build 5.1.013: // - Rain gage isUsed property now set in subcatch_validate(). // - Cumulative impervious and pervious area runoff volumes added // to subcatchment statistics. // - Support added for monthly adjustment of subcatchment's depression // storage, pervious N, and infiltration. -// // Build 5.1.015: // - Support added for multiple infiltration methods within a project. // - Only pervious area depression storage receives monthly adjustment. @@ -84,8 +72,8 @@ double VlidReturn; // LID outflow returned to pervious area // Locally shared variables //----------------------------------------------------------------------------- static TSubarea* theSubarea; // subarea to which getDdDt() is applied -static double Dstore; // monthly adjusted depression storage (ft) //(5.1.013) -static double Alpha; // monthly adjusted runoff coeff. // +static double Dstore; // monthly adjusted depression storage (ft) +static double Alpha; // monthly adjusted runoff coeff. static char *RunoffRoutingWords[] = { w_OUTLET, w_IMPERV, w_PERV, NULL}; //----------------------------------------------------------------------------- @@ -109,7 +97,7 @@ static char *RunoffRoutingWords[] = { w_OUTLET, w_IMPERV, w_PERV, NULL}; // subcatch_getFracPerv (called from gwater_initState) // subcatch_getStorage (called from massbal_getRunoffError) // subcatch_getDepth (called from findPondedLoads in surfqual.c) -// subcatch_getBuildup (called from surfqual_getWashoff) +// subcatch_getBuildup (called from surfqual_getWashoff) (OWA Addition) // subcatch_getWtdOutflow (called from addWetWeatherInflows in routing.c) // subcatch_getResults (called from output_saveSubcatchResults) @@ -125,7 +113,7 @@ static double getSubareaInfil(int j, TSubarea* subarea, double precip, static double findSubareaRunoff(TSubarea* subarea, double tRunoff); static void updatePondedDepth(TSubarea* subarea, double* tx); static void getDdDt(double t, double* d, double* dddt); -static void adjustSubareaParams(int subareaType, int subcatch); //(5.1.013) +static void adjustSubareaParams(int subareaType, int subcatch); //============================================================================= @@ -191,9 +179,9 @@ int subcatch_readParams(int j, char* tok[], int ntoks) Subcatch[j].width = x[5] / UCF(LENGTH); Subcatch[j].slope = x[6] / 100.0; Subcatch[j].curbLength = x[7]; - Subcatch[j].nPervPattern = -1; //(5.1.013 - Subcatch[j].dStorePattern = -1; // - Subcatch[j].infilPattern = -1; // + Subcatch[j].nPervPattern = -1; + Subcatch[j].dStorePattern = -1; + Subcatch[j].infilPattern = -1; // --- create the snow pack object if it hasn't already been created if ( x[8] >= 0 ) @@ -421,9 +409,9 @@ void subcatch_validate(int j) } } - // --- set isUsed property of subcatchment's rain gage //(5.1.013) - i = Subcatch[j].gage; // - if (i >= 0) Gage[i].isUsed = TRUE; // + // --- set isUsed property of subcatchment's rain gage + i = Subcatch[j].gage; + if (i >= 0) Gage[i].isUsed = TRUE; } @@ -438,8 +426,6 @@ void subcatch_initState(int j) { int i; -//// isUsed property of subcatchment's rain gage now set in subcatch_validate //(5.1.013) - // --- initialize rainfall, runoff, & snow depth Subcatch[j].rainfall = 0.0; Subcatch[j].oldRunoff = 0.0; @@ -451,7 +437,7 @@ void subcatch_initState(int j) Subcatch[j].infilLoss = 0.0; // --- initialize state of infiltration, groundwater, & snow pack objects - if ( Subcatch[j].infil == j ) infil_initState(j); //(5.1.015) + if ( Subcatch[j].infil == j ) infil_initState(j); if ( Subcatch[j].groundwater ) gwater_initState(j); if ( Subcatch[j].snowpack ) snow_initSnowpack(j); @@ -666,9 +652,9 @@ double subcatch_getRunoff(int j, double tStep) double vOutflow = 0.0; // runoff volume leaving subcatch (ft3) double runoff = 0.0; // total runoff flow on subcatch (cfs) double evapRate = 0.0; // potential evaporation rate (ft/sec) - double subAreaRunoff; // sub-area runoff rate (cfs) //(5.1.013) - double vImpervRunoff = 0.0; // impervious area runoff volume (ft3) // - double vPervRunoff = 0.0; // pervious area runoff volume (ft3) // + double subAreaRunoff; // sub-area runoff rate (cfs) + double vImpervRunoff = 0.0; // impervious area runoff volume (ft3) + double vPervRunoff = 0.0; // pervious area runoff volume (ft3) // --- initialize shared water balance variables Vevap = 0.0; @@ -700,8 +686,8 @@ double subcatch_getRunoff(int j, double tStep) if ( Evap.dryOnly && Subcatch[j].rainfall > 0.0 ) evapRate = 0.0; else evapRate = Evap.rate; - // --- set monthly infiltration adjustment factor //(5.1.013) - infil_setInfilFactor(j); //(5.1.013) + // --- set monthly infiltration adjustment factor + infil_setInfilFactor(j); // --- examine each type of sub-area (impervious w/o depression storage, // impervious w/ depression storage, and pervious) @@ -712,10 +698,10 @@ double subcatch_getRunoff(int j, double tStep) area = nonLidArea * Subcatch[j].subArea[i].fArea; Subcatch[j].subArea[i].runoff = getSubareaRunoff(j, i, area, netPrecip[i], evapRate, tStep); - subAreaRunoff = Subcatch[j].subArea[i].runoff * area; //(5.1.013) - if (i == PERV) vPervRunoff = subAreaRunoff * tStep; // - else vImpervRunoff += subAreaRunoff * tStep; // - runoff += subAreaRunoff; // + subAreaRunoff = Subcatch[j].subArea[i].runoff * area; + if (i == PERV) vPervRunoff = subAreaRunoff * tStep; + else vImpervRunoff += subAreaRunoff * tStep; + runoff += subAreaRunoff; } // --- evaluate any LID treatment provided (updating Vevap, @@ -748,7 +734,7 @@ double subcatch_getRunoff(int j, double tStep) // --- update the cumulative stats for this subcatchment stats_updateSubcatchStats(j, vRain, vRunon, Vevap, Vinfil + VlidInfil, - vImpervRunoff, vPervRunoff, vOutflow + VlidDrain, //(5.1.013) + vImpervRunoff, vPervRunoff, vOutflow + VlidDrain, Subcatch[j].newRunoff + VlidDrain/tStep); // --- include this subcatchment's contribution to overall flow balance @@ -809,7 +795,10 @@ void getNetPrecip(int j, double* netPrecip, double tStep) } //============================================================================= - +// OWA EDIT ################################################################################## +// Function to calculate the total amount of a specified pollutant (summed over land uses) +// This function is NOT the same as the old function of the same name in EPA SWMM that was +// removed in 5.1.008 in c70dd4fe6919a0dafa26975881ec569c463ef161 and replaced with surfqual_getBuildup double subcatch_getBuildup(int j, int p) // // Input: j = subcatchment index @@ -828,7 +817,7 @@ double subcatch_getBuildup(int j, int p) return load; } - +// ########################################################################################### //============================================================================= double subcatch_getDepth(int j) @@ -987,10 +976,10 @@ double getSubareaRunoff(int j, int i, double area, double precip, double evap, if ( i == PERV ) Vpevap += Vevap; Vinfil += infil * area * tStep; - // --- assign adjusted runoff coeff. & storage to shared variables //(5.1.013) - Alpha = subarea->alpha; // - Dstore = subarea->dStore; // - adjustSubareaParams(i, j); // + // --- assign adjusted runoff coeff. & storage to shared variables + Alpha = subarea->alpha; + Dstore = subarea->dStore; + adjustSubareaParams(i, j); // --- if losses exceed available moisture then no ponded water remains if ( surfEvap + infil >= surfMoisture ) @@ -1031,7 +1020,7 @@ double getSubareaInfil(int j, TSubarea* subarea, double precip, double tStep) double infil = 0.0; // actual infiltration rate (ft/sec) // --- compute infiltration rate - infil = infil_getInfil(j, tStep, precip, //(5.1.015) + infil = infil_getInfil(j, tStep, precip, subarea->inflow, subarea->depth); // --- limit infiltration rate by available void space in unsaturated @@ -1054,7 +1043,7 @@ double findSubareaRunoff(TSubarea* subarea, double tRunoff) // Output: returns runoff rate (ft/s) // { - double xDepth = subarea->depth - Dstore; //(5.1.013) + double xDepth = subarea->depth - Dstore; double runoff = 0.0; if ( xDepth > ZERO ) @@ -1062,14 +1051,14 @@ double findSubareaRunoff(TSubarea* subarea, double tRunoff) // --- case where nonlinear routing is used if ( subarea->N > 0.0 ) { - runoff = Alpha * pow(xDepth, MEXP); //(5.1.013) + runoff = Alpha * pow(xDepth, MEXP); } // --- case where no routing is used (Mannings N = 0) else { runoff = xDepth / tRunoff; - subarea->depth = Dstore; //(5.1.013) + subarea->depth = Dstore; } } else @@ -1094,7 +1083,7 @@ void updatePondedDepth(TSubarea* subarea, double* dt) double tx = *dt; // time over which dx > 0 (sec) // --- see if not enough inflow to fill depression storage (dStore) - if ( subarea->depth + ix*tx <= Dstore ) //(5.1.013) + if ( subarea->depth + ix*tx <= Dstore ) { subarea->depth += ix * tx; } @@ -1102,16 +1091,16 @@ void updatePondedDepth(TSubarea* subarea, double* dt) // --- otherwise use the ODE solver to integrate flow depth else { - // --- if depth < Dstore then fill up Dstore & reduce time step //(5.1.013) - dx = Dstore - subarea->depth; // + // --- if depth < Dstore then fill up Dstore & reduce time step + dx = Dstore - subarea->depth; if ( dx > 0.0 && ix > 0.0 ) { tx -= dx / ix; - subarea->depth = Dstore; //(5.1.013) + subarea->depth = Dstore; } // --- now integrate depth over remaining time step tx - if ( Alpha > 0.0 && tx > 0.0 ) //(5.1.013) + if ( Alpha > 0.0 && tx > 0.0 ) { theSubarea = subarea; odesolve_integrate(&(subarea->depth), 1, 0, tx, ODETOL, tx, @@ -1144,28 +1133,26 @@ void getDdDt(double t, double* d, double* dddt) // { double ix = theSubarea->inflow; - double rx = *d - Dstore; //(5.1.013) + double rx = *d - Dstore; if ( rx < 0.0 ) { rx = 0.0; } else { - rx = Alpha * pow(rx, MEXP); //(5.1.013) + rx = Alpha * pow(rx, MEXP); } *dddt = ix - rx; } //============================================================================= -//// New function added to release 5.1.013. //// //(5.1.013) - void adjustSubareaParams(int i, int j) // // Input: i = type of subarea being analyzed // j = index of current subcatchment being analyzed // Output adjusted values of module-level variables Dstore & Alpha -// Purpose: adjusts a pervious subarea's depression storage and its //(5.1.015) +// Purpose: adjusts a pervious subarea's depression storage and its // runoff coeff. by month of the year. // { @@ -1173,7 +1160,7 @@ void adjustSubareaParams(int i, int j) int m; // current month of the year double f; // adjustment factor - if (i == PERV) //(5.1.015) + if (i == PERV) { // --- depression storage adjustment p = Subcatch[j].dStorePattern; @@ -1184,9 +1171,9 @@ void adjustSubareaParams(int i, int j) if (f >= 0.0) Dstore *= f; } - // --- roughness adjustment to runoff coeff. //(5.1.015) + // --- roughness adjustment to runoff coeff. p = Subcatch[j].nPervPattern; - if (p >= 0 && Pattern[p].type == MONTHLY_PATTERN) //(5.1.015) + if (p >= 0 && Pattern[p].type == MONTHLY_PATTERN) { m = datetime_monthOfYear(getDateTime(OldRunoffTime)) - 1; f = Pattern[p].factor[m]; diff --git a/src/solver/surfqual.c b/src/solver/surfqual.c index 6f0dfc4f4..858fcbee5 100644 --- a/src/solver/surfqual.c +++ b/src/solver/surfqual.c @@ -2,18 +2,18 @@ // surfqual.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/19/15 (Build 5.1.008) -// 03/01/20 (Build 5.1.014) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Subcatchment water quality functions. // +// Update History +// ============== // Build 5.1.008: // - Pollutant surface buildup and washoff functions were moved here from // subcatch.c. // - Support for separate accounting of LID drain flows included. -// // Build 5.1.014: // - Fixed bug in computing effective BMP removal by LIDs. //----------------------------------------------------------------------------- @@ -73,8 +73,8 @@ void surfqual_initState(int j) Subcatch[j].oldQual[p] = 0.0; Subcatch[j].newQual[p] = 0.0; Subcatch[j].pondedQual[p] = 0.0; - Subcatch[j].concPonded[p] = 0.0; - Subcatch[j].surfaceBuildup[p] = 0.0; + Subcatch[j].concPonded[p] = 0.0; // (OWA addition) + Subcatch[j].surfaceBuildup[p] = 0.0; // (OWA addition) } // --- initialize pollutant buildup @@ -284,7 +284,7 @@ void surfqual_getWashoff(int j, double runoff, double tStep) if ( !hasOutflow ) cOut = 0.0; Subcatch[j].newQual[p] = cOut / LperFT3; - // --- update surface buildup loads (in lbs or kg) + // OWA Addition --- update surface buildup loads (in lbs or kg) Subcatch[j].surfaceBuildup[p] = subcatch_getBuildup( j, p ); } @@ -382,7 +382,7 @@ void findPondedLoads(int j, double tStep) // --- update ponded mass (using newly computed ponded depth) Subcatch[j].pondedQual[p] = cPonded * subcatch_getDepth(j) * nonLidArea; - Subcatch[j].concPonded[p] = cPonded; + Subcatch[j].concPonded[p] = cPonded; // (OWA Addition) OutflowLoad[p] += wOutflow; } } diff --git a/src/solver/swmm5.c b/src/solver/swmm5.c index 9051da5fc..3ef3f6fc7 100644 --- a/src/solver/swmm5.c +++ b/src/solver/swmm5.c @@ -2,13 +2,8 @@ // swmm5.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/19/14 (Build 5.1.001) -// 03/19/15 (Build 5.1.008) -// 08/01/16 (Build 5.1.011) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// 04/01/20 (Build 5.1.015) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // This is the main module of the computational engine for Version 5 of @@ -18,12 +13,13 @@ // This engine should be compiled into a shared object library whose API // functions are listed in swmm5.h. // +// Update History +// ============== // Build 5.1.008: // - Support added for the MinGW compiler. -// - Reporting of project options moved to swmm_start. +// - Reporting of project options moved to swmm_start. // - Hot start file now read before routing system opened. // - Final routing step adjusted so that total duration not exceeded. -// // Build 5.1.011: // - Made sure that MS exception handling only used with MS C compiler. // - Added name of module handling an exception to error report. @@ -32,16 +28,20 @@ // - Changed WarningCode to Warnings (# warnings issued). // - Added swmm_getWarnings() function to retrieve value of Warnings. // - Fixed error code returned on swmm_xxx functions. -// // Build 5.1.012: // - #include only used when compiled for Windows. -// // Build 5.1.013: // - Support added for saving average results within a reporting period. // - SWMM engine now always compiled to a shared object library. -// // Build 5.1.015: // - Fixes bug in summary statistics when Report Start date > Start Date. +// Build 5.2.0: +// - Added additional API functions. +// - Set max. number of open files to 8192. +// - Changed getElapsedTime function to use report start as base date/time. +// - Prevented possible infinite loop if swmm_step() called when ErrorCode > 0. +// - Prevented early exit from swmm_end() when ErrorCode > 0. +// - Support added for relative file names. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -60,23 +60,27 @@ #ifdef _MSC_VER #define EXH #endif - + // OWA EDIT ####################################################### // Use alias of methods unavailable before VS2015 #if _MSC_VER < 1900 #define snprintf _snprintf #endif + // ############################################################################ #endif // --- include Windows & exception handling headers #ifdef WINDOWS #include #include + #include +#else + #include #endif #ifdef EXH #include #endif - +// OWA EDIT ####################################################### // --- define DLLEXPORT //#ifndef DLLEXPORT @@ -91,6 +95,7 @@ #define DLLEXPORT #endif //#endif +// ################################################################ #include #include @@ -105,26 +110,23 @@ // Note: the directives listed below are also contained in headers.h which // is included at the start of most of SWMM's other code modules. //----------------------------------------------------------------------------- -#include "consts.h" // defined constants #include "macros.h" // macros used throughout SWMM -#include "enums.h" // enumerated variables -#include "error.h" // error message codes -#include "datetime.h" // date/time functions #include "objects.h" // definitions of SWMM's data objects -#include "funcs.h" // declaration of all global functions -#include "text.h" // listing of all text strings #define EXTERN // defined as 'extern' in headers.h #include "globals.h" // declaration of all global variables -#include "version.h" +#include "funcs.h" // declaration of all global functions +#include "error.h" // error message codes +#include "text.h" // listing of all text strings +#include "version.h" // OWA Addition + +#include "swmm5.h" // declaration of SWMM's API functions -#include "swmm5.h" // declaration of exportable functions - // callable from other programs #define MAX_EXCEPTIONS 100 // max. number of exceptions handled //----------------------------------------------------------------------------- // Unit conversion factors //----------------------------------------------------------------------------- -const double Ucf[10][2] = +const double Ucf[10][2] = {// US SI {43200.0, 1097280.0 }, // RAINFALL (in/hr, mm/hr --> ft/sec) {12.0, 304.8 }, // RAINDEPTH (in, mm --> ft) @@ -138,23 +140,26 @@ const double Ucf[10][2] = {43560.0, 3048.0 } // GWFLOW (cfs/ac, cms/ha --> ft/sec) }; +// OWA EDIT ####################################################### #ifdef __cplusplus extern const double Qcf[6] = // Flow Conversion Factors: #else const double Qcf[6] = // Flow Conversion Factors: #endif +// ################################################################ {1.0, 448.831, 0.64632, // cfs, gpm, mgd --> cfs 0.02832, 28.317, 2.4466 }; // cms, lps, mld --> cfs //----------------------------------------------------------------------------- // Shared variables //----------------------------------------------------------------------------- -//static int IsOpenFlag; // TRUE if a project has been opened -//static int IsStartedFlag; // TRUE if a simulation has been started -//static int SaveResultsFlag; // TRUE if output to be saved to binary file -static int ExceptionCount; // number of exceptions handled -static int DoRunoff; // TRUE if runoff is computed -static int DoRouting; // TRUE if flow routing is computed +static int IsOpenFlag; // TRUE if a project has been opened +static int IsStartedFlag; // TRUE if a simulation has been started +static int SaveResultsFlag; // TRUE if output to be saved to binary file +static int ExceptionCount; // number of exceptions handled +static int DoRunoff; // TRUE if runoff is computed +static int DoRouting; // TRUE if flow routing is computed +static double RoutingDuration; // duration of a set of routing steps (msecs) //----------------------------------------------------------------------------- // External API functions (prototyped in swmm5.h) @@ -168,24 +173,54 @@ static int DoRouting; // TRUE if flow routing is computed // swmm_close // swmm_getMassBalErr // swmm_getVersion +// swmm_getError +// swmm_getWarnings +// swmm_getCount +// swmm_getIDname +// swmm_getIndex +// swmm_getStartNode +// swmm_getEndNode +// swmm_getValue +// swmm_setValue +// swmm_getSavedValue +// swmm_writeLine +// swmm_decodeDate //----------------------------------------------------------------------------- // Local functions //----------------------------------------------------------------------------- -static void execRouting(void); +static void execRouting(void); +static void saveResults(void); +static double getGageValue(int index, int property); +static double getSubcatchValue(int index, int property); +static double getNodeValue(int index, int property); +static double getLinkValue(int index, int property); +static double getSavedDate(int period); +static double getSavedSubcatchValue(int index, int property, int period); +static double getSavedNodeValue(int index, int property, int period); +static double getSavedLinkValue(int index, int property, int period); +static double getSystemValue(int property); +static double getMaxRouteStep(); +static void setNodeLatFlow(int index, double value); +static void setOutfallStage(int index, double value); +static void setLinkSetting(int index, double value); +static void setRoutingStep(double value); +static void getAbsolutePath(const char* fname, char* absPath, size_t size); // Exception filtering function #ifdef EXH static int xfilter(int xc, char* module, double elapsedTime, long step); #endif +// OWA EDIT ################################################################### // Forward declaration, defined in toolkit.h extern int swmm_run_cb(const char *f1, const char *f2, const char *f3, void (*callback) (double *)); +// ############################################################################ //============================================================================= -int DLLEXPORT swmm_run(const char* f1, const char* f2, const char* f3) +int DLLEXPORT swmm_run(const char *f1, const char *f2, const char *f3) // // Input: f1 = name of input file // f2 = name of report file @@ -194,9 +229,64 @@ int DLLEXPORT swmm_run(const char* f1, const char* f2, const char* f3) // Purpose: runs a SWMM simulation. // { - return swmm_run_cb(f1, f2, f3, NULL); -} + long newHour, oldHour = 0; + long theDay, theHour; + double elapsedTime = 0.0; + + // --- initialize flags + IsOpenFlag = FALSE; + IsStartedFlag = FALSE; + SaveResultsFlag = TRUE; + // --- open the files & read input data + ErrorCode = 0; + writecon("\n o Retrieving project data"); + swmm_open(f1, f2, f3); + + // --- run the simulation if input data OK + if ( !ErrorCode ) + { + // --- initialize values + swmm_start(TRUE); + + // --- execute each time step until elapsed time is re-set to 0 + if ( !ErrorCode ) + { + writecon("\n o Simulating day: 0 hour: 0"); + do + { + swmm_step(&elapsedTime); + newHour = (long)(elapsedTime * 24.0); + if ( newHour > oldHour ) + { + theDay = (long)elapsedTime; + theHour = (long)((elapsedTime - floor(elapsedTime)) * 24.0); + writecon("\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + snprintf(Msg, MAXMSG, "%-5ld hour: %-2ld", theDay, theHour); + writecon(Msg); + oldHour = newHour; + } + } while ( elapsedTime > 0.0 && !ErrorCode ); + writecon("\b\b\b\b\b\b\b\b\b\b\b\b\b\b" + "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + writecon("Simulation complete "); + } + + // --- clean up + swmm_end(); + } + + // --- report results + if ( !ErrorCode && Fout.mode == SCRATCH_FILE ) + { + writecon("\n o Writing output report"); + swmm_report(); + } + + // --- close the system + swmm_close(); + return ErrorCode; +} //============================================================================= @@ -209,9 +299,10 @@ int DLLEXPORT swmm_open(const char *f1, const char *f2, const char *f3) // Purpose: opens a SWMM project. // { -// --- to be safe, reset the state of the floating point unit //(5.1.013) -#ifdef WINDOWS //(5.1.013) +// --- to be safe, reset the state of the floating point unit +#ifdef WINDOWS _fpreset(); + _setmaxstdio(8192); #endif #ifdef EXH @@ -222,29 +313,27 @@ int DLLEXPORT swmm_open(const char *f1, const char *f2, const char *f3) // --- initialize error & warning codes datetime_setDateFormat(M_D_Y); ErrorCode = 0; - strcpy(ErrorMsg, ""); + ErrorMsg[0] = '\0'; Warnings = 0; IsOpenFlag = FALSE; IsStartedFlag = FALSE; ExceptionCount = 0; // --- open a SWMM project - project_open((char *)f1, (char *)f2, (char *)f3); - if ( ErrorCode ) return error_getCode(ErrorCode); + strcpy(InpDir, ""); + project_open(f1, f2, f3); + getAbsolutePath(f1, InpDir, sizeof(InpDir)); + if ( ErrorCode ) return ErrorCode; IsOpenFlag = TRUE; report_writeLogo(); - writecon(FMT06); // --- retrieve project data from input file project_readInput(); - if ( ErrorCode ) return error_getCode(ErrorCode); + if ( ErrorCode ) return ErrorCode; // --- write project title to report file & validate data report_writeTitle(); project_validate(); - - // --- write input summary to report file if requested - if ( RptFlags.input ) inputrpt_writeInput(); } #ifdef EXH @@ -254,7 +343,7 @@ int DLLEXPORT swmm_open(const char *f1, const char *f2, const char *f3) ErrorCode = ERR_SYSTEM; } #endif - return error_getCode(ErrorCode); + return ErrorCode; } //============================================================================= @@ -267,11 +356,18 @@ int DLLEXPORT swmm_start(int saveResults) // { // --- check that a project is open & no run started - if ( ErrorCode ) return error_getCode(ErrorCode); - if ( !IsOpenFlag || IsStartedFlag ) + if ( ErrorCode ) return ErrorCode; + if ( !IsOpenFlag ) + return (ErrorCode = ERR_API_NOT_OPEN); + if ( IsStartedFlag ) + return (ErrorCode = ERR_API_NOT_ENDED); + + // --- write input summary & project options to report file if requested + if (!RptFlags.disabled) { - report_writeErrorMsg(ERR_NOT_OPEN, ""); - return error_getCode(ErrorCode); + if (RptFlags.input) + inputrpt_writeInput(); + report_writeOptions(); } // --- save saveResults flag to global variable @@ -285,18 +381,19 @@ int DLLEXPORT swmm_start(int saveResults) { // --- initialize elapsed time in decimal days ElapsedTime = 0.0; + RoutingDuration = TotalDuration; // --- initialize runoff, routing & reporting time (in milliseconds) NewRunoffTime = 0.0; NewRoutingTime = 0.0; - ReportTime = (double)(1000 * ReportStep); - TotalStepCount = 0; //(5.1.015) - ReportStepCount = 0; //(5.1.015) + ReportTime = 1000 * (double)ReportStep; + TotalStepCount = 0; + ReportStepCount = 0; NonConvergeCount = 0; IsStartedFlag = TRUE; - // --- initialize external pollutant control - ExtPollutFlag = 0; + // OWA Addition --- initialize external pollutant control for toolkit API + ExtPollutFlag = 0; // --- initialize global continuity errors RunoffError = 0.0; @@ -307,7 +404,7 @@ int DLLEXPORT swmm_start(int saveResults) // --- open rainfall processor (creates/opens a rainfall // interface file and generates any RDII flows) if ( !IgnoreRainfall ) rain_open(); - if ( ErrorCode ) return error_getCode(ErrorCode); + if ( ErrorCode ) return ErrorCode; // --- initialize state of each major system component project_init(); @@ -334,9 +431,9 @@ int DLLEXPORT swmm_start(int saveResults) massbal_open(); stats_open(); - // --- write project options to report file - report_writeOptions(); - if ( RptFlags.controls ) report_writeControlActionsHeading(); + // --- write heading for control actions listing + if (!RptFlags.disabled && RptFlags.controls) + report_writeControlActionsHeading(); } #ifdef EXH @@ -346,11 +443,11 @@ int DLLEXPORT swmm_start(int saveResults) ErrorCode = ERR_SYSTEM; } #endif - return error_getCode(ErrorCode); + return ErrorCode; } //============================================================================= -int DLLEXPORT swmm_step(double* elapsedTime) +int DLLEXPORT swmm_step(double *elapsedTime) // // Input: elapsedTime = current elapsed time in decimal days // Output: updated value of elapsedTime, @@ -359,12 +456,13 @@ int DLLEXPORT swmm_step(double* elapsedTime) // { // --- check that simulation can proceed - if ( ErrorCode ) return error_getCode(ErrorCode); - if ( !IsOpenFlag || !IsStartedFlag ) - { - report_writeErrorMsg(ERR_NOT_OPEN, ""); - return error_getCode(ErrorCode); - } + *elapsedTime = 0.0; + if ( ErrorCode ) + return ErrorCode; + if ( !IsOpenFlag ) + return (ErrorCode = ERR_API_NOT_OPEN); + if ( !IsStartedFlag ) + return (ErrorCode = ERR_API_NOT_STARTED); #ifdef EXH // --- begin exception handling loop here @@ -372,7 +470,7 @@ int DLLEXPORT swmm_step(double* elapsedTime) #endif { // --- if routing time has not exceeded total duration - if ( NewRoutingTime < TotalDuration ) + if ( NewRoutingTime < RoutingDuration ) { // --- route flow & WQ through drainage system // (runoff will be calculated as needed) @@ -380,46 +478,13 @@ int DLLEXPORT swmm_step(double* elapsedTime) execRouting(); } -//// Following code segment modified for release 5.1.013. //// //(5.1.013) // --- if saving results to the binary file if ( SaveResultsFlag ) - { - // --- and it's time to save results - if ( NewRoutingTime >= ReportTime ) - { - // --- if user requested that average results be saved: - if ( RptFlags.averages ) - { - // --- include latest results in current averages - // if current time equals the reporting time - if ( NewRoutingTime == ReportTime ) output_updateAvgResults(); - - // --- save current average results to binary file - // (which will re-set averages to 0) - output_saveResults(ReportTime); - - // --- if current time exceeds reporting period then - // start computing averages for next period - if ( NewRoutingTime > ReportTime ) output_updateAvgResults(); - } - - // --- otherwise save interpolated point results - else output_saveResults(ReportTime); - - // --- advance to next reporting period - ReportTime = ReportTime + (double)(1000 * ReportStep); - } - - // --- not a reporting period so update average results if applicable - else if ( RptFlags.averages ) output_updateAvgResults(); - } -//// + saveResults(); // --- update elapsed time (days) - if ( NewRoutingTime < TotalDuration ) - { + if ( NewRoutingTime < RoutingDuration ) ElapsedTime = NewRoutingTime / MSECperDAY; - } // --- otherwise end the simulation else ElapsedTime = 0.0; @@ -428,12 +493,60 @@ int DLLEXPORT swmm_step(double* elapsedTime) #ifdef EXH // --- end of try loop; handle exception here - __except(xfilter(GetExceptionCode(), "swmm_step", ElapsedTime, TotalStepCount)) //(5.1.015) + __except(xfilter(GetExceptionCode(), "swmm_step", ElapsedTime, TotalStepCount)) { ErrorCode = ERR_SYSTEM; } #endif - return error_getCode(ErrorCode); + return ErrorCode; +} + +//============================================================================= + +int DLLEXPORT swmm_stride(int strideStep, double *elapsedTime) +// +// Input: strideStep = number of seconds to advance the simulation +// elapsedTime = current elapsed time in decimal days +// Output: updated value of elapsedTime, +// returns error code +// Purpose: advances the simulation by a fixed number of seconds. +{ + double realRouteStep = RouteStep; + + // --- check that simulation can proceed + *elapsedTime = 0.0; + if (ErrorCode) + return ErrorCode; + if (!IsOpenFlag) + return (ErrorCode = ERR_API_NOT_OPEN); + if (!IsStartedFlag) + return (ErrorCode = ERR_API_NOT_STARTED); + + // --- modify total duration to be strideStep seconds after current time + RoutingDuration = NewRoutingTime + 1000.0 * strideStep; + RoutingDuration = MIN(TotalDuration, RoutingDuration); + + // --- modify routing step to not exceed stride time step + if (strideStep < RouteStep) RouteStep = strideStep; + + // --- step through simulation until next stride step is reached + do + { + swmm_step(elapsedTime); + } while (*elapsedTime > 0.0 && !ErrorCode); + + // --- restore original routing step and routing duration + RouteStep = realRouteStep; + RoutingDuration = TotalDuration; + + // --- restore actual elapsed time (days) + if (NewRoutingTime < TotalDuration) + { + ElapsedTime = NewRoutingTime / MSECperDAY; + } + else ElapsedTime = 0.0; + *elapsedTime = ElapsedTime; + return ErrorCode; } //============================================================================= @@ -454,7 +567,7 @@ void execRouting() #endif { // --- determine when next routing time occurs - TotalStepCount++; //(5.1.015) + TotalStepCount++; if ( !DoRouting ) routingStep = MIN(WetStep, ReportStep); else routingStep = routing_getRoutingStep(RouteModel, RouteStep); if ( routingStep <= 0.0 ) @@ -465,15 +578,15 @@ void execRouting() nextRoutingTime = NewRoutingTime + 1000.0 * routingStep; // --- adjust routing step so that total duration not exceeded - if ( nextRoutingTime > TotalDuration ) + if ( nextRoutingTime > RoutingDuration ) { - routingStep = (TotalDuration - NewRoutingTime) / 1000.0; + routingStep = (RoutingDuration - NewRoutingTime) / 1000.0; routingStep = MAX(routingStep, 1. / 1000.0); - nextRoutingTime = TotalDuration; + nextRoutingTime = RoutingDuration; } // --- compute runoff until next routing time reached or exceeded - if ( DoRunoff ) while ( NewRunoffTime < nextRoutingTime ) + if ( DoRunoff ) while ( NewRunoffTime < nextRoutingTime) { runoff_execute(); if ( ErrorCode ) return; @@ -481,18 +594,19 @@ void execRouting() // --- if no runoff analysis, update climate state (for evaporation) else climate_setState(getDateTime(NewRoutingTime)); - + // --- route flows & pollutants through drainage system // (while updating NewRoutingTime) - if ( DoRouting ) routing_execute(RouteModel, routingStep); + if ( DoRouting ) + routing_execute(RouteModel, routingStep); else - NewRoutingTime = nextRoutingTime; + NewRoutingTime = nextRoutingTime; } #ifdef EXH // --- end of try loop; handle exception here __except(xfilter(GetExceptionCode(), "execRouting", - ElapsedTime, TotalStepCount)) //(5.1.015) + ElapsedTime, TotalStepCount)) { ErrorCode = ERR_SYSTEM; return; @@ -500,6 +614,45 @@ void execRouting() #endif } +//============================================================================= + +void saveResults() +// +// Input: none +// Output: none +// Purpose: saves current results to binary output file. +{ + if (NewRoutingTime >= ReportTime) + { + // --- if user requested that average results be saved: + if (RptFlags.averages) + { + // --- include latest results in current averages + // if current time equals the reporting time + if (NewRoutingTime == ReportTime) output_updateAvgResults(); + + // --- save current average results to binary file + // (which will re-set averages to 0) + output_saveResults(ReportTime); + + // --- if current time exceeds reporting period then + // start computing averages for next period + if (NewRoutingTime > ReportTime) output_updateAvgResults(); + } + + // --- otherwise save interpolated point results + else output_saveResults(ReportTime); + + // --- advance to next reporting period + ReportTime = ReportTime + 1000 * (double)ReportStep; + } + + // --- not a reporting period so update average results if applicable + else if (RptFlags.averages) output_updateAvgResults(); + +} + + //============================================================================= int DLLEXPORT swmm_end(void) @@ -511,10 +664,7 @@ int DLLEXPORT swmm_end(void) { // --- check that project opened and run started if ( !IsOpenFlag ) - { - report_writeErrorMsg(ERR_NOT_OPEN, ""); - return error_getCode(ErrorCode); - } + return (ErrorCode = ERR_API_NOT_OPEN); if ( IsStartedFlag ) { @@ -522,7 +672,7 @@ int DLLEXPORT swmm_end(void) if ( Fout.file ) output_end(); // --- report mass balance results and system statistics - if ( !ErrorCode ) + if ( !ErrorCode && RptFlags.disabled == 0 ) { massbal_report(); stats_report(); @@ -537,7 +687,7 @@ int DLLEXPORT swmm_end(void) hotstart_close(); IsStartedFlag = FALSE; } - return error_getCode(ErrorCode); + return ErrorCode; } //============================================================================= @@ -546,17 +696,25 @@ int DLLEXPORT swmm_report() // // Input: none // Output: returns an error code -// Purpose: writes simulation results to report file. +// Purpose: writes simulation results to the report file. // { - if ( Fout.mode == SCRATCH_FILE ) output_checkFileSize(); - if ( ErrorCode ) report_writeErrorCode(); - else - { - writecon(FMT07); + if ( !ErrorCode ) report_writeReport(); - } - return error_getCode(ErrorCode); + return ErrorCode; +} + +//============================================================================= + +void DLLEXPORT swmm_writeLine(const char *line) +// +// Input: line = a character string +// Output: returns an error code +// Purpose: writes a line of text to the report file. +// +{ + if (IsOpenFlag) + report_writeLine(line); } //============================================================================= @@ -571,8 +729,10 @@ int DLLEXPORT swmm_close() if ( Fout.file ) output_close(); if ( IsOpenFlag ) project_close(); report_writeSysTime(); - if ( Finp.file != NULL ) fclose(Finp.file); - if ( Frpt.file != NULL ) fclose(Frpt.file); + if ( Finp.file != NULL ) + fclose(Finp.file); + if ( Frpt.file != NULL ) + fclose(Frpt.file); if ( Fout.file != NULL ) { fclose(Fout.file); @@ -585,8 +745,8 @@ int DLLEXPORT swmm_close() //============================================================================= -int DLLEXPORT swmm_getMassBalErr(float* runoffErr, float* flowErr, - float* qualErr) +int DLLEXPORT swmm_getMassBalErr(float *runoffErr, float *flowErr, + float *qualErr) // // Input: none // Output: runoffErr = runoff mass balance error (percent) @@ -611,7 +771,7 @@ int DLLEXPORT swmm_getMassBalErr(float* runoffErr, float* flowErr, //============================================================================= -int DLLEXPORT swmm_getVersion(void) +int DLLEXPORT swmm_getVersion() // // Input: none // Output: returns SWMM engine version number in legacy format @@ -619,24 +779,25 @@ int DLLEXPORT swmm_getVersion(void) // uses a format of xyzzz where x = major version number, // y = minor version number, and zzz = build number. // +// NOTE: Each New Release should be updated in consts.h { return get_version_legacy(); } //============================================================================= -int DLLEXPORT swmm_getWarnings(void) +int DLLEXPORT swmm_getWarnings() // // Input: none // Output: returns number of warning messages issued. -// Purpose: retireves number of warning messages issued during an analysis. +// Purpose: retrieves number of warning messages issued during an analysis. { return Warnings; } //============================================================================= -int DLLEXPORT swmm_getError(char* errMsg, int msgLen) +int DLLEXPORT swmm_getError(char *errMsg, int msgLen) // // Input: errMsg = character array to hold error message text // msgLen = maximum size of errMsg @@ -644,19 +805,610 @@ int DLLEXPORT swmm_getError(char* errMsg, int msgLen) // Purpose: retrieves the code number and text of the error condition that // caused SWMM to abort its analysis. { - size_t errMsgLen = msgLen; - // --- copy text of last error message into errMsg - if ( ErrorCode > 0 && strlen(ErrorMsg) == 0 ) sstrncpy(errMsg, "", 1); - else + if (ErrorCode > 0 && strlen(ErrorMsg) == 0) + error_getMsg(ErrorCode, ErrorMsg); + sstrncpy(errMsg, ErrorMsg, msgLen); + + // --- remove leading line feed from errMsg + if ( msgLen > 0 && errMsg[0] == '\n' ) errMsg[0] = ' '; + return ErrorCode; +} + +//============================================================================= + +int DLLEXPORT swmm_getCount(int objType) +// +// Input: objType = a type of SWMM object +// Output: returns the number of objects; +// Purpose: retrieves the number of objects of a specific type. +{ + if (!IsOpenFlag) + return 0; + if (objType < swmm_GAGE || objType > swmm_LINK) + return 0; + return Nobjects[objType]; +} + +//============================================================================= + +void DLLEXPORT swmm_getName(int objType, int index, char *name, int size) +// +// Input: objType = a type of SWMM object +// index = the object's index in the array of objects +// name = a character array +// size = size of the name array +// Output: name = the object's ID name; +// Purpose: retrieves the ID name of an object. +{ + char *idName = NULL; + + name[0] = '\0'; + if (!IsOpenFlag) + return; + if (objType < swmm_GAGE || objType > swmm_LINK) + return; + if (index < 0 || index >= Nobjects[objType]) + return; + switch (objType) { - errMsgLen = MIN(errMsgLen, strlen(ErrorMsg)); - errMsg = sstrncpy(errMsg, ErrorMsg, errMsgLen); + case GAGE: idName = Gage[index].ID; break; + case SUBCATCH: idName = Subcatch[index].ID; break; + case NODE: idName = Node[index].ID; break; + case LINK: idName = Link[index].ID; break; } + if (idName) + sstrncpy(name, idName, size); +} - // --- remove leading line feed from errMsg - if ( errMsgLen > 0 && errMsg[0] == '\n' ) errMsg[0] = ' '; - return error_getCode(ErrorCode); +//============================================================================= + +int DLLEXPORT swmm_getIndex(int objType, const char *name) +// +// Input: objType = a type of SWMM object +// name = the object's ID name +// Output: returns the object's position in the array of like objects; +// Purpose: retrieves the index of a named object. +{ + if (!IsOpenFlag) + return -1; + if (objType < swmm_GAGE || objType > swmm_LINK) + return -1; + return project_findObject(objType, name); +} + +//============================================================================= + +double DLLEXPORT swmm_getValue(int property, int index) +// +// Input: property = an object's property code +// index = the object's index in the array of like objects +// +// Output: returns the property's current value +// Purpose: retrieves the value of an object's property. +{ + if (!IsOpenFlag) + return 0; + if (property < 100) + return getSystemValue(property); + if (property < 200) + return getGageValue(property, index); + if (property < 300) + return getSubcatchValue(property, index); + if (property < 400) + return getNodeValue(property, index); + if (property < 500) + return getLinkValue(property, index); + return 0; +} + +//============================================================================= + +void DLLEXPORT swmm_setValue(int property, int index, double value) +// +// Input: property = an object's property code +// index = the object's index in the array of like objects +// value = the property's new value +// Output: none +// Purpose: sets the value of an object's property. +{ + if (!IsOpenFlag) + return; + switch (property) + { + case swmm_GAGE_RAINFALL: + if (index < 0 || index >= Nobjects[GAGE]) + return; + if (value >= 0.0) + Gage[index].apiRainfall = value; + return; + case swmm_SUBCATCH_RPTFLAG: + if (!IsStartedFlag && index >= 0 && index < Nobjects[SUBCATCH]) + Subcatch[index].rptFlag = (value > 0.0); + return; + case swmm_NODE_LATFLOW: + setNodeLatFlow(index, value); + return; + case swmm_NODE_HEAD: + setOutfallStage(index, value); + return; + case swmm_NODE_RPTFLAG: + if (!IsStartedFlag && index >= 0 && index < Nobjects[NODE]) + Node[index].rptFlag = (value > 0.0); + return; + case swmm_LINK_SETTING: + setLinkSetting(index, value); + return; + case swmm_LINK_RPTFLAG: + if (!IsStartedFlag && index >= 0 && index < Nobjects[LINK]) + Link[index].rptFlag = (value > 0.0); + return; + case swmm_ROUTESTEP: + setRoutingStep(value); + return; + case swmm_REPORTSTEP: + if (!IsStartedFlag && value > 0) + ReportStep = (int)value; + return; + case swmm_NOREPORT: + if (!IsStartedFlag) + RptFlags.disabled = (value > 0.0); + return; + } +} + +//============================================================================= + +double DLLEXPORT swmm_getSavedValue(int property, int index, int period) +// +// Input: property = an object's property code +// index = the object's index in the array of like objects +// period = a reporting time period (starting from 1) +// Output: returns the property's saved value +// Purpose: retrieves an object's computed value at a specific reporting time period. +{ + if (!IsOpenFlag) + return 0; + if (IsStartedFlag) + return 0; + if (period < 1 || period > Nperiods) + return 0; + if (property == swmm_CURRENTDATE) + return getSavedDate(period); + if (property >= 200 && property < 300) + return getSavedSubcatchValue(property, index, period); + if (property < 400) + return getSavedNodeValue(property, index, period); + if (property < 500) + return getSavedLinkValue(property, index, period); + return 0; +} + +//============================================================================= + +void DLLEXPORT swmm_decodeDate(double date, int *year, int *month, int *day, + int *hour, int *minute, int *second, int *dayOfWeek) +// +// Input: date = an encoded date in decimal days +// Output: date's year, month of year, day of month, time of day (hour, +// minute, second), and day of weeek +// Purpose: retrieves the calendar date and clock time of an encoded date. +{ + datetime_decodeDate(date, year, month, day); + datetime_decodeTime(date, hour, minute, second); + *dayOfWeek = datetime_dayOfWeek(date); +} + +//============================================================================= +// Object property getters and setters +//============================================================================= + +double getGageValue(int property, int index) +// +// Input: property = a rain gage property code +// index = the index of a rain gage +// Output: returns current property value +// Purpose: retrieves current value of a rain gage property. +{ + if (index < 0 || index >= Nobjects[GAGE]) + return 0; + if (property == swmm_GAGE_RAINFALL) + return Gage[index].reportRainfall; + return 0; +} + +//============================================================================= + +double getSubcatchValue(int property, int index) +// +// Input: property = a subcatchment property code +// index = the index of a subcatchment +// Output: returns current property value +// Purpose: retrieves current value of a subcatchment's property. +{ + TSubcatch* subcatch; + if (index < 0 || index >= Nobjects[SUBCATCH]) + return 0; + subcatch = &Subcatch[index]; + switch (property) + { + case swmm_SUBCATCH_AREA: + return subcatch->area * UCF(LANDAREA); + case swmm_SUBCATCH_RAINGAGE: + return subcatch->gage; + case swmm_SUBCATCH_RAINFALL: + if ( subcatch->gage >= 0 ) + return Gage[subcatch->gage].reportRainfall; + else + return 0.0; + case swmm_SUBCATCH_EVAP: + return subcatch->evapLoss * UCF(EVAPRATE); + case swmm_SUBCATCH_INFIL: + return subcatch->infilLoss * UCF(RAINFALL); + case swmm_SUBCATCH_RUNOFF: + return subcatch->newRunoff * UCF(FLOW); + case swmm_SUBCATCH_RPTFLAG: + return (subcatch->rptFlag > 0); + default: + return 0; + } +} + +//============================================================================= + +double getNodeValue(int property, int index) +// +// Input: property = a node property code +// index = the index of a node +// Output: returns current property value +// Purpose: retrieves current value of a node's property. +{ + TNode* node; + if (index < 0 || index >= Nobjects[NODE]) + return 0; + node = &Node[index]; + switch (property) + { + case swmm_NODE_TYPE: + return node->type; + case swmm_NODE_ELEV: + return node->invertElev * UCF(LENGTH); + case swmm_NODE_MAXDEPTH: + return node->fullDepth * UCF(LENGTH); + case swmm_NODE_DEPTH: + return node->newDepth * UCF(LENGTH); + case swmm_NODE_HEAD: + return (node->newDepth + node->invertElev) * UCF(LENGTH); + case swmm_NODE_VOLUME: + return node->newVolume * UCF(VOLUME); + case swmm_NODE_LATFLOW: + return node->newLatFlow * UCF(FLOW); + case swmm_NODE_INFLOW: + return node->inflow * UCF(FLOW); + case swmm_NODE_OVERFLOW: + return node->overflow * UCF(FLOW); + case swmm_NODE_RPTFLAG: + return (node->rptFlag > 0); + default: + return 0; + } +} + +//============================================================================= + +double getLinkValue(int property, int index) +// +// Input: property = a link property code +// index = the index of a link +// Output: returns current property value +// Purpose: retrieves current value of a link's property. +{ + TLink* link; + if (index < 0 || index >= Nobjects[LINK]) + return 0; + link = &Link[index]; + switch (property) + { + case swmm_LINK_TYPE: + return link->type; + case swmm_LINK_NODE1: + return link->node1; + case swmm_LINK_NODE2: + return link->node2; + case swmm_LINK_LENGTH: + if (link->type == CONDUIT) + return Conduit[link->subIndex].length * UCF(LENGTH); + else + return 0; + case swmm_LINK_SLOPE: + if (link->type == CONDUIT) + return Conduit[link->subIndex].slope; + else + return 0; + break; + case swmm_LINK_FULLDEPTH: + return link->xsect.yFull * UCF(LENGTH); + case swmm_LINK_FULLFLOW: + return link->qFull * UCF(FLOW); + case swmm_LINK_FLOW: + return link->newFlow * UCF(FLOW) * (double)link->direction; + case swmm_LINK_VELOCITY: + return link_getVelocity(index, fabs(link->newFlow), link->newDepth) + * UCF(LENGTH); + case swmm_LINK_DEPTH: + return link->newDepth * UCF(LENGTH); + case swmm_LINK_TOPWIDTH: + if (link->type == CONDUIT) + return xsect_getWofY(&link->xsect, link->newDepth) * UCF(LENGTH); + else + return 0; + case swmm_LINK_SETTING: + return link->setting; + case swmm_LINK_TIMEOPEN: + if (link->setting > 0.0) + return (getDateTime(NewRoutingTime) - link->timeLastSet) * 24.; + else + return 0; + case swmm_LINK_TIMECLOSED: + if (link->setting == 0.0) + return (getDateTime(NewRoutingTime) - link->timeLastSet) * 24.; + else + return 0; + case swmm_LINK_RPTFLAG: + return (link->rptFlag > 0); + default: + return 0; + } +} + +//============================================================================= + +double getSystemValue(int property) +// +// Input: property = a system property code +// Output: returns current property value +// Purpose: retrieves current value of a system property. +{ + switch (property) + { + case swmm_STARTDATE: + return StartDateTime; + case swmm_CURRENTDATE: + return StartDateTime + ElapsedTime; + case swmm_ELAPSEDTIME: + return ElapsedTime; + case swmm_ROUTESTEP: + return RouteStep; + case swmm_MAXROUTESTEP: + return getMaxRouteStep(); + case swmm_REPORTSTEP: + return ReportStep; + case swmm_TOTALSTEPS: + return Nperiods; + case swmm_NOREPORT: + return RptFlags.disabled; + case swmm_FLOWUNITS: + return FlowUnits; + default: + return 0; + } +} + +//============================================================================= + +void setNodeLatFlow(int index, double value) +// +// Input: index = the index of a node +// value = the node's external inflow value +// Output: none +// Purpose: sets the value of a node's external inflow. +{ + if (index < 0 || index >= Nobjects[NODE]) + return; + Node[index].apiExtInflow = value / UCF(FLOW); +} + +//============================================================================= + +void setOutfallStage(int index, double value) +// +// Input: index = the index of an outfall node +// value = the outfall's fixed stage elevation +// Output: none +// Purpose: sets the value of an outfall node's fixed stage. +{ + TNode* node; + if (index < 0 || index >= Nobjects[NODE]) + return; + node = &Node[index]; + if (node->type != OUTFALL) + return; + Outfall[node->subIndex].fixedStage = value / UCF(LENGTH); + Outfall[node->subIndex].type = FIXED_OUTFALL; +} + +//============================================================================= + +void setLinkSetting(int index, double value) +// +// Input: index = the index of a link +// value = the link's new setting +// Output: node +// Purpose: sets the value of a link's setting. +{ + TLink* link; + if (index < 0 || index >= Nobjects[LINK]) + return; + link = &Link[index]; + if (value < 0.0 || link->type == CONDUIT) + return; + if (link->type != PUMP && value > 1.0) + value = 1.0; + if (link->targetSetting == value) + return; + link->targetSetting = value; + if (link->targetSetting * link->setting == 0.0) + link->timeLastSet = StartDateTime + ElapsedTime; + link_setSetting(index, 0.0); +} + +//============================================================================= + +double getSavedDate(int period) +// +// Input: period = a reporting period (starting at 1) +// Output: returns the date/time of the reporting period in decimal days +// Purpose: retrieves the date/time of a reporting period. +{ + double days; + output_readDateTime(period, &days); + return days; +} + +//============================================================================= + +double getSavedSubcatchValue(int property, int index, int period) +// +// Input: property = index of a computed property +// index = index of a subcatchment +// period = a reporting period (starting at 1) +// Output: returns the property's value at the recording period +// Purpose: retrieves the computed value of a subcatchment property at a +// specific reporting period. +{ + // --- SubcatchResults array is defined in output.c and contains + // computed results in user's units + extern float* SubcatchResults; + + // --- order in which subcatchment was saved to output results file + int outIndex = Subcatch[index].rptFlag - 1; + if (outIndex < 0) return 0; + + output_readSubcatchResults(period, outIndex); + switch (property) + { + case swmm_SUBCATCH_RAINFALL: + return SubcatchResults[SUBCATCH_RAINFALL]; + case swmm_SUBCATCH_EVAP: + return SubcatchResults[SUBCATCH_EVAP]; + case swmm_SUBCATCH_INFIL: + return SubcatchResults[SUBCATCH_INFIL]; + case swmm_SUBCATCH_RUNOFF: + return SubcatchResults[SUBCATCH_RUNOFF]; + default: + return 0; + } +} + +//============================================================================= + +double getSavedNodeValue(int property, int index, int period) +// +// Input: property = index of a computed property +// index = index of a node +// period = a reporting period (starting at 1) +// Output: returns the property's value at the recording period +// Purpose: retrieves the computed value of a node property at a +// specific reporting period. +{ + // --- NodeResults array is defined in output.c and contains + // computed results in user's units + extern float* NodeResults; + + // --- order in which node was saved to output results file + int outIndex = Node[index].rptFlag - 1; + if (outIndex < 0) return 0; + + output_readNodeResults(period, outIndex); + switch (property) + { + case swmm_NODE_DEPTH: + return NodeResults[NODE_DEPTH]; + case swmm_NODE_HEAD: + return NodeResults[NODE_HEAD]; + case swmm_NODE_VOLUME: + return NodeResults[NODE_VOLUME]; + case swmm_NODE_LATFLOW: + return NodeResults[NODE_LATFLOW]; + case swmm_NODE_INFLOW: + return NodeResults[NODE_INFLOW]; + case swmm_NODE_OVERFLOW: + return NodeResults[NODE_OVERFLOW]; + default: + return 0; + } +} + +//============================================================================= + +double getSavedLinkValue(int property, int index, int period) +// +// Input: property = index of a computed property +// index = index of a link +// period = a reporting period (starting at 1) +// Output: returns the property's value at the recording period +// Purpose: retrieves the computed value of a link property at a +// specific reporting period. +{ + double y, w; + + // --- LinkResults array is defined in output.c and contains + // computed results in user's units + extern float* LinkResults; + + // --- order in which link was saved to output results file + int outIndex = Link[index].rptFlag - 1; + if (outIndex < 0) return 0; + + output_readLinkResults(period, outIndex); + switch (property) + { + case swmm_LINK_FLOW: + return LinkResults[LINK_FLOW]; + case swmm_LINK_DEPTH: + return LinkResults[LINK_DEPTH]; + case swmm_LINK_VELOCITY: + return LinkResults[LINK_VELOCITY]; + case swmm_LINK_TOPWIDTH: + y = LinkResults[LINK_DEPTH] / UCF(LENGTH); + w = xsect_getWofY(&Link[index].xsect, y); + return w * UCF(LENGTH); + case swmm_LINK_SETTING: + return LinkResults[LINK_CAPACITY]; + default: + return 0; + } +} + +//============================================================================= + +double getMaxRouteStep() +{ + double tmpCourantFactor = CourantFactor; + double result = RouteStep; + + if (!IsStartedFlag || RouteModel != DW) + return result; + CourantFactor = 1.0; + result = routing_getRoutingStep(RouteModel, MinRouteStep); + CourantFactor = tmpCourantFactor; + return result; +} + +//============================================================================= + +void setRoutingStep(double value) +// +// Input: value = a routing time step (in decimal seconds) +// Output: none +// Purpose: sets the value of the current flow routing time step. +{ + if (value <= 0.0) + return; + if (value <= MinRouteStep) + value = MinRouteStep; + CourantFactor = 0.0; + RouteStep = value; } //============================================================================= @@ -677,23 +1429,68 @@ double UCF(int u) //============================================================================= -char* sstrncpy(char *dest, const char *src, size_t maxlen) +size_t sstrncpy(char *dest, const char *src, size_t n) // // Input: dest = string to be copied to // src = string to be copied from -// maxlen = number of characters to copy -// Output: returns a pointer to dest -// Purpose: safe version of standard strncpy function +// n = number of bytes to copy +// Output: returns the size of dest +// Purpose: better version of standard strncpy function // { - strncpy(dest, src, maxlen); - dest[maxlen] = '\0'; - return dest; + int offset = 0; + if (n > 0) + { + while (*(src + offset) != '\0') + { + if (offset == n) + break; + *(dest + offset) = *(src + offset); + offset++; + } + } + *(dest + offset) = '\0'; + return strlen(dest); +} + +//============================================================================= + +size_t sstrcat(char* dest, const char* src, size_t size) +// +// Input: dest = string to be appended +// src = string to append to dest +// size = allocated size of dest (including nul terminator) +// Output: returns new size of dest +// Purpose: safe version of standard strcat function +// +{ + size_t dest_len, src_len, offset, src_index; + + // obtain initial sizes + dest_len = strlen(dest); + src_len = strlen(src); + + // get the end of dest + offset = dest_len; + + // append src + src_index = 0; + while (*(src + src_index) != '\0') + { + *(dest + offset) = *(src + src_index); + offset++; + src_index++; + // don't copy more than size - dest_len - 1 characters + if (offset == size - 1) + break; + } + *(dest + offset) = '\0'; + return strlen(dest); } //============================================================================= -int strcomp(char *s1, char *s2) +int strcomp(const char *s1, const char *s2) // // Input: s1 = a character string // s2 = a character string @@ -727,8 +1524,8 @@ char* getTempFileName(char* fname) // --- set dir to user's choice of a temporary directory if (strlen(TempDir) > 0) { - _mkdir(TempDir); - dir = TempDir; + if (_mkdir(TempDir) == 0 || errno == EEXIST) + dir = TempDir; } // --- use _tempnam to get a pointer to an unused file name @@ -736,7 +1533,7 @@ char* getTempFileName(char* fname) if (name == NULL) return NULL; // --- copy the file name to fname - if (strlen(name) < MAXFNAME) strncpy(fname, name, MAXFNAME); + if (strlen(name) <= MAXFNAME) sstrncpy(fname, name, MAXFNAME); else fname = NULL; // --- free the pointer returned by _tempnam @@ -749,7 +1546,7 @@ char* getTempFileName(char* fname) #else // --- use system function mkstemp() to create a temporary file name - strcpy(fname, "swmmXXXXXX"); + sstrncpy(fname, "swmmXXXXXX", MAXFNAME); mkstemp(fname); return fname; @@ -767,7 +1564,7 @@ void getElapsedTime(DateTime aDate, int* days, int* hrs, int* mins) { DateTime x; int secs; - x = aDate - StartDateTime; + x = aDate - ReportStart; if ( x <= 0.0 ) { *days = 0; @@ -796,7 +1593,89 @@ DateTime getDateTime(double elapsedMsec) //============================================================================= -void writecon(char *s) +int isRelativePath(const char* fname) +// +// Input: fname = a file name +// Output: returns 1 if fname's path is relative or 0 if absolute +// Purpose: determines if a file name contains a relative or absolute path. +// +{ + if (strchr(fname, ':')) return 0; + if (fname[0] == '\\') return 0; + if (fname[0] == '/') return 0; + return 1; +} + +//============================================================================= + +void getAbsolutePath(const char* fname, char* absPath, size_t size) +// +// Input: fname = a file name +// absPath = string to hold the absolute path +// size = max. size of absPath +// Output: absPath = string containing absolute path of fname +// (including ending path delimiter) +// Purpose: finds the full path of the directory for file fname +// +{ + char* endOfDir; + + // --- case of empty file anme + if (fname == NULL || strlen(fname) == 0) + return; + + // --- if fname has a relative path then retrieve its full path + if (isRelativePath(fname)) + { + #ifdef WINDOWS + GetFullPathName((LPCSTR)fname, (DWORD)size, (LPSTR)absPath, NULL); + #else + realpath(fname, absPath); + #endif + } + + // --- otherwise copy fname to absPath + else + { + sstrncpy(absPath, fname, strlen(fname)); + } + + // --- trim file name portion of absPath + #ifdef WINDOWS + endOfDir = strrchr(absPath, '\\'); + #else + endOfDir = strrchr(absPath, '/'); + #endif + if (endOfDir) + { + *(endOfDir+1) = '\0'; + } +} + +//============================================================================= + +char* addAbsolutePath(char* fname) +// +// Input: fname = a file name +// Output: returns fname with a full path prepended to it +// Purpose: adds an absolute path name to a file name. +// Note: fname must have been dimensioned to accept MAXFNAME characters. +// +{ + size_t n; + char buffer[MAXFNAME]; + if (isRelativePath(fname)) + { + n = snprintf(buffer, MAXFNAME, "%s%s", InpDir, fname); + if (n > 0) + sstrncpy(fname, buffer, MAXFNAME); + } + return fname; +} + +//============================================================================= + +void writecon(const char *s) // // Input: s = a character string // Output: none @@ -824,7 +1703,7 @@ int xfilter(int xc, char* module, double elapsedTime, long step) int rc; // result code long hour; // current hour of simulation char msg[40]; // exception type text - char xmsg[120]; // error message text + char xmsg[240]; // error message text switch (xc) { case EXCEPTION_ACCESS_VIOLATION: @@ -868,7 +1747,7 @@ int xfilter(int xc, char* module, double elapsedTime, long step) rc = EXCEPTION_EXECUTE_HANDLER; } hour = (long)(elapsedTime / 1000.0 / 3600.0); - sprintf(xmsg, "%sin module %s at step %d, hour %d", + sprintf(xmsg, "%sin module %s at step %ld, hour %ld", msg, module, step, hour); if ( rc == EXCEPTION_EXECUTE_HANDLER || ++ExceptionCount >= MAX_EXCEPTIONS ) @@ -881,6 +1760,9 @@ int xfilter(int xc, char* module, double elapsedTime, long step) } #endif +// OWA EDIT ############################################### +// Additional functions for determining if a simulation is +// open or running. Used in toolkit.c int swmm_IsOpenFlag() // // Check if Project is Open @@ -897,3 +1779,4 @@ int swmm_IsStartedFlag() // TRUE if a simulation has been started return IsStartedFlag; } +// ######################################################## \ No newline at end of file diff --git a/src/solver/table.c b/src/solver/table.c index 5ba5095f6..aced49e13 100644 --- a/src/solver/table.c +++ b/src/solver/table.c @@ -2,10 +2,8 @@ // table.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Table (curve and time series) functions. @@ -16,12 +14,20 @@ // The table_getFirstEntry and table_getNextEntry functions, as well as the // Time Series functions that use them, are not thread safe. // +// Update History +// ============== // Build 5.1.008: // - The lookup functions used for Curve tables (table_lookup, table_lookupEx, // table_intervalLookup, table_inverseLookup, table_getSlope, table_getMaxY, // table_getArea, and table_getInverseArea) were made thread-safe (thanks to // suggestions by CHI). -// +// Build 5.2.0: +// - First line of Curve's input data can contain just the curve name and type. +// - The table_getArea function was renamed table_getStorageVolume and was +// - refactored. +// - The table_getInverseArea function was renamed table_getStorageDepth and +// was refactored. +// - Support added for relative file names. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -68,7 +74,7 @@ int table_readCurve(char* tok[], int ntoks) double x, y; // --- check for minimum number of tokens - if ( ntoks < 3 ) return error_setInpError(ERR_ITEMS, ""); + if ( ntoks < 2 ) return error_setInpError(ERR_ITEMS, ""); // --- check that curve exists in database j = project_findObject(CURVE, tok[0]); @@ -83,6 +89,7 @@ int table_readCurve(char* tok[], int ntoks) m = findmatch(tok[1], CurveTypeWords); if ( m < 0 ) return error_setInpError(ERR_KEYWORD, tok[1]); Curve[j].curveType = m; + if (ntoks == 2) return 0; k1 = 2; } @@ -117,6 +124,7 @@ int table_readTimeseries(char* tok[], int ntoks) double x, y; // time & value table entries DateTime d; // day portion of date/time value DateTime t; // time portion of date/time value + char fname[MAXFNAME + 1]; // --- check for minimum number of tokens if ( ntoks < 3 ) return error_setInpError(ERR_ITEMS, ""); @@ -132,7 +140,8 @@ int table_readTimeseries(char* tok[], int ntoks) // --- check if time series data is in an external file if ( strcomp(tok[1], w_FILE ) ) { - sstrncpy(Tseries[j].file.name, tok[2], MAXFNAME); + sstrncpy(fname, tok[2], MAXFNAME); + sstrncpy(Tseries[j].file.name, addAbsolutePath(fname), MAXFNAME); Tseries[j].file.mode = USE_FILE; return 0; } @@ -573,144 +582,145 @@ double table_getMaxY(TTable *table, double x) //============================================================================= -double table_getArea(TTable* table, double x) +double table_getStorageVolume(TTable *table, double x) // // Input: table = pointer to a TTable structure -// x = an value -// Output: returns area value -// Purpose: finds area under a tabulated curve from 0 to x; -// requires that table's x values be non-negative -// and non-decreasing. -// -// The area within each interval i of the table is given by: -// Integral{ y(x)*dx } from x(i) to x -// where y(x) = y(i) + s*dx -// dx = x - x(i) -// s = [y(i+1) - y(i)] / [x(i+1) - x(i)] -// This results in the following expression for a(i): -// a(i) = y(i)*dx + s*dx*dx/2 +// x = a depth value +// Output: returns a storage volume +// Purpose: finds volume for a given depth in a Storage Curve table. // { - double x1, x2; - double y1, y2; - double dx = 0.0, dy = 0.0; - double a, s = 0.0; + double a, a1, x1, v, dx = 0.0, dy = 0.0, s; TTableEntry* entry; - // --- get area up to first table entry - // and see if x-value lies in this interval + // --- get first entry in table + v = 0.0; entry = table->firstEntry; - if (entry == NULL ) return 0.0; + if (entry == NULL) return 0.0; x1 = entry->x; - y1 = entry->y; - if ( x1 > 0.0 ) s = y1/x1; - if ( x <= x1 ) return s*x*x/2.0; - a = y1*x1/2.0; - - // --- add next table entry to area until target x-value is bracketed - while ( entry->next ) + a1 = entry->y; + + // --- target depth is below first tabulated depth + if (x <= x1) + { + if (x1 < 1.e-6) return 0.0; + return (a1/x1) * x * x / 2.0; + } + + // --- otherwise traverse table entries until target depth is bracketed + while (entry->next) { entry = entry->next; - x2 = entry->x; - y2 = entry->y; - dx = x2 - x1; - dy = y2 - y1; - if ( x <= x2 ) + // --- target is bracketed - apply end area method to interpolated area + if (entry->x >= x) { - if ( dx <= 0.0 ) return a; - y2 = table_interpolate(x, x1, y1, x2, y2); - return a + (x - x1) * (y1 + y2) / 2.0; + a = table_interpolate(x, x1, a1, entry->x, entry->y); + return v + (a1 + a) / 2.0 * (x - x1); + } + // --- target not yet bracketed so update volume using end area method + else + { + dx = entry->x - x1; + dy = entry->y - a1; + v = v + (a1 + entry->y) / 2.0 * dx; + x1 = entry->x; + a1 = entry->y; } - a += (y1 + y2) * dx / 2.0; - x1 = x2; - y1 = y2; } // --- extrapolate area if table limit exceeded - if ( dx > 0.0 ) s = dy/dx; - else s = 0.0; - dx = x - x1; - return a + y1*dx + s*dx*dx/2.0; + if (dx > 1.0e-6) + { + s = dy / dx; + a = a1 + s * (x - x1); + // --- don't extrapolate below 0 in case s is negative + if (a < 0.0) + { + v = v - a1 * a1 / s / 2.0; + } + // --- apply end area method to extrapolated area + else v = v + (a1 + a) / 2.0 * (x - x1); + } + return v; } //============================================================================= -double table_getInverseArea(TTable* table, double a) +double table_getStorageDepth(TTable *table, double v) // // Input: table = pointer to a TTable structure -// a = an area value -// Output: returns an x value -// Purpose: finds x value for given area under a curve. -// -// Refer to table_getArea function to see how area is computed. +// v = a storage volume +// Output: returns a storage depth +// Purpose: finds depth for a given volume in a Storage Curve table. // { - double x1, x2; - double y1, y2; - double dx = 0.0, dy = 0.0; - double a1, a2, s; + double a1, a2, d1, d2, dd = 0.0, da = 0.0, v1, v2, s; TTableEntry* entry; - // --- see if target area is below that of 1st table entry + // --- see if target volume is below that of 1st table entry + if (v == 0.0) return 0.0; entry = table->firstEntry; - if (entry == NULL ) return 0.0; - x1 = entry->x; - y1 = entry->y; - a1 = y1*x1/2.0; - if ( a <= a1 ) + if (entry == NULL) return 0.0; + d1 = entry->x; + a1 = entry->y; + v1 = a1 * d1 / 2.0; + if (v <= v1) { - if ( y1 > 0.0 ) return sqrt(2.0*a*x1/y1); + if (a1 > 0.0) return sqrt(2.0 * v * d1 / a1); else return 0.0; } - // --- add next table entry to area until target area is bracketed - while ( entry->next ) + // --- add next table entry to volume until target volume is bracketed + while (entry->next) { entry = entry->next; - x2 = entry->x; - y2 = entry->y; - dx = x2 - x1; - dy = y2 - y1; - a2 = a1 + y1*dx + dy*dx/2.0; - if ( a <= a2 ) + d2 = entry->x; + a2 = entry->y; + dd = d2 - d1; + da = a2 - a1; + v2 = v1 + (a1 + a2) / 2.0 * dd; + + // target volume is bracketed + if (v <= v2) { - if ( dx <= 0.0 ) return x1; - if ( dy == 0.0 ) + // --- target coincides with point on curve + if (dd <= 0.0) return d1; + if (da == 0.0) { - if ( a2 == a1 ) return x1; - else return x1 + dx * (a - a1) / (a2 - a1); + if (fabs(v2 - v1) < 1.e-6) return d1; + else return d1 + dd * (v - v1) / (v2 - v1); } - - // --- if y decreases with x then replace point 1 with point 2 - if ( dy < 0.0 ) + // --- if area decreases with depth then replace point 1 with point 2 + if (da < 0.0) { - x1 = x2; - y1 = y2; + d1 = d2; a1 = a2; + v1 = v2; } - - s = dy/dx; - dx = (sqrt(y1*y1 + 2.0*s*(a-a1)) - y1) / s; - return x1 + dx; + // --- interpolate between volumes derived from curve + s = da / dd; + return d1 + (sqrt(a1*a1 + 2.0*s*(v-v1)) - a1) / s; } - x1 = x2; - y1 = y2; + + // --- replace point 1 with point 2 + d1 = d2; a1 = a2; + v1 = v2; } - // --- extrapolate area if table limit exceeded - if ( dx == 0.0 || dy == 0.0 ) + // --- extrapolate volume if table limit exceeded + if (dd == 0.0 || da == 0.0) { - if ( y1 > 0.0 ) dx = (a - a1) / y1; - else dx = 0.0; + if (a1 > 0.0) dd = (v - v1) / a1; + else dd = 0.0; } else { - s = dy/dx; - dx = (sqrt(y1*y1 + 2.0*s*(a-a1)) - y1) / s; - if (dx < 0.0) dx = 0.0; + s = da / dd; + dd = (sqrt(a1*a1 + 2.0*s*(v - v1)) - a1) / s; + if (dd < 0.0) dd = 0.0; } - return x1 + dx; + return d1 + dd; } //============================================================================= diff --git a/src/solver/text.h b/src/solver/text.h index 204f6d518..a9c52be33 100644 --- a/src/solver/text.h +++ b/src/solver/text.h @@ -2,25 +2,17 @@ // text.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/19/14 (Build 5.1.001) -// 04/02/14 (Build 5.1.003) -// 04/14/14 (Build 5.1.004) -// 04/23/14 (Build 5.1.005) -// 05/19/14 (Build 5.1.006) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 04/30/15 (Build 5.1.009) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// 03/01/20 (Build 5.1.014) -// 04/01/20 (Build 5.1.015) -// +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Text strings +// +// Update History +// ============== +// Build 5.2.0: +// - Moved strings used in swmm_run() (in swmm5.c) to that function. +// - Added text strings used for storage shapes, streets & inlets. //----------------------------------------------------------------------------- #ifndef TEXT_H @@ -32,13 +24,10 @@ #define FMT03 " There are errors.\n" #define FMT04 " There are warnings.\n" -#define FMT05 "\n" -#define FMT06 "\n o Retrieving project data" -#define FMT07 "\n o Writing output report" #define FMT08 \ - "\n EPA STORM WATER MANAGEMENT MODEL - VERSION 5.1 (Build 5.1.015)" //(5.1.015) + "\n EPA STORM WATER MANAGEMENT MODEL - VERSION 5.2 (Build 5.2.0)" #define FMT09 \ - "\n --------------------------------------------------------------" + "\n ------------------------------------------------------------" #define FMT10 "\n" #define FMT11 "\n Cannot use duplicate file names." #define FMT12 "\n Cannot open input file " @@ -46,8 +35,8 @@ #define FMT14 "\n Cannot open output file " #define FMT15 "\n Cannot open temporary output file" #define FMT16 "\n ERROR %d detected. Execution halted." -#define FMT17 "at line %ld of input file:" //(5.1.013) -#define FMT18 "at line %ld of %s] section:" //(5.1.013) +#define FMT17 "at line %ld of input file:" +#define FMT18 "at line %ld of %s] section:" #define FMT19 "\n Maximum error count exceeded." #define FMT20 "\n\n Analysis begun on: %s" #define FMT20a " Analysis ended on: %s" @@ -64,10 +53,12 @@ #define WARN08 "WARNING 08: elevation drop exceeds length for Conduit" #define WARN09 "WARNING 09: time series interval greater than recording interval for Rain Gage" #define WARN10a \ -"WARNING 10: crest elevation is below downstream invert for regulator Link" //(5.1.013) +"WARNING 10: crest elevation is below downstream invert for regulator Link" #define WARN10b \ -"WARNING 10: crest elevation raised to downstream invert for regulator Link" //(5.1.013) +"WARNING 10: crest elevation raised to downstream invert for regulator Link" #define WARN11 "WARNING 11: non-matching attributes in Control Rule" +#define WARN12 \ +"WARNING 12: inlet removed due to unsupported shape for Conduit" // Analysis Option Keywords #define w_FLOW_UNITS "FLOW_UNITS" @@ -86,7 +77,7 @@ #define w_DRY_STEP "DRY_STEP" #define w_ROUTE_STEP "ROUTING_STEP" #define w_REPORT_STEP "REPORT_STEP" -#define w_RULE_STEP "RULE_STEP" //(5.1.013) +#define w_RULE_STEP "RULE_STEP" #define w_ALLOW_PONDING "ALLOW_PONDING" #define w_INERT_DAMPING "INERTIAL_DAMPING" #define w_SLOPE_WEIGHTING "SLOPE_WEIGHTING" @@ -112,7 +103,7 @@ #define w_IGNORE_RDII "IGNORE_RDII" #define w_MIN_ROUTE_STEP "MINIMUM_STEP" #define w_NUM_THREADS "THREADS" -#define w_SURCHARGE_METHOD "SURCHARGE_METHOD" //(5.1.013) +#define w_SURCHARGE_METHOD "SURCHARGE_METHOD" // Flow Units #define w_CFS "CFS" @@ -133,7 +124,7 @@ #define w_XKINWAVE "XKINWAVE" #define w_DYNWAVE "DYNWAVE" -// Surcharge Methods //(5.1.013) +// Surcharge Methods #define w_EXTRAN "EXTRAN" #define w_SLOT "SLOT" @@ -197,11 +188,18 @@ #define w_CUTOFF "CUTOFF" #define w_OVERFLOW "OVERFLOW" +// Storage Node Shapes +#define w_CYLINDRICAL "CYLINDRICAL" +#define w_CONICAL "CONICAL" +#define w_PARABOLOID "PARABOLIC" +#define w_PYRAMIDAL "PYRAMIDAL" + // Pump Curve Types #define w_TYPE1 "TYPE1" #define w_TYPE2 "TYPE2" #define w_TYPE3 "TYPE3" #define w_TYPE4 "TYPE4" +#define w_TYPE5 "TYPE5" #define w_IDEAL "IDEAL" // Pump Curve Variables @@ -229,6 +227,7 @@ #define w_TRIANGULAR "TRIANGULAR" #define w_PARABOLIC "PARABOLIC" #define w_POWERFUNC "POWER" +#define w_STREET "STREET" #define w_RECT_TRIANG "RECT_TRIANGULAR" #define w_RECT_ROUND "RECT_ROUND" #define w_MOD_BASKET "MODBASKETHANDLE" @@ -317,14 +316,17 @@ #define w_PUMP2 "PUMP2" #define w_PUMP3 "PUMP3" #define w_PUMP4 "PUMP4" +#define w_PUMP5 "PUMP5" +#define w_INLET "INLET" // Reporting Options +#define w_DISABLED "DISABLED" #define w_INPUT "INPUT" #define w_CONTINUITY "CONTINUITY" #define w_FLOWSTATS "FLOWSTATS" #define w_CONTROLS "CONTROL" #define w_NODESTATS "NODESTATS" -#define w_AVERAGES "AVERAGES" //(5.1.013) +#define w_AVERAGES "AVERAGES" // Interface File Types #define w_RAINFALL "RAINFALL" @@ -380,6 +382,9 @@ #define w_ELSE "ELSE" #define w_PRIORITY "PRIORITY" +#define w_VARIABLE "VARIABLE" +#define w_EXPRESSION "EXPRESSION" + // External Inflow Types #define w_FLOW "FLOW" #define w_CONCEN "CONCEN" @@ -450,6 +455,8 @@ #define ws_GWF "[GWF" #define ws_ADJUST "[ADJUSTMENT" #define ws_EVENT "[EVENT" - +#define ws_STREET "[STREET" +#define ws_INLET "[INLET" +#define ws_INLET_USAGE "[INLET_USAGE" #endif //TEXT_H diff --git a/src/solver/toolkit.c b/src/solver/toolkit.c index 50c2f7b79..fff5f4998 100644 --- a/src/solver/toolkit.c +++ b/src/solver/toolkit.c @@ -130,7 +130,7 @@ EXPORT_TOOLKIT int swmm_run_cb(const char* f1, const char* f2, const char* f3, swmm_close(); - return error_getCode(ErrorCode); + return ErrorCode; } @@ -140,26 +140,28 @@ EXPORT_TOOLKIT int swmm_getAPIError(int errorCode, char **errorMsg) /// Output: errmessage String /// Return: API Error /// Purpose: Get an error message -{ - int errorIndex = error_getErrorIndex(errorCode); - cstr_duplicate(errorMsg, error_getMsg(errorIndex)); +{ + char msg[MAXMSG+1]; + error_getMsg(errorCode,msg); + cstr_duplicate(errorMsg, msg); + return 0; } EXPORT_TOOLKIT int swmm_project_findObject(SM_ObjectType type, char *id, int *index) { - int error_code_index = 0; + int error_code = 0; int idx = project_findObject(type, id); if (idx == -1) { index = NULL; - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else *index = idx; - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getSimulationDateTime(SM_TimePropety type, int *year, int *month, int *day, @@ -170,7 +172,7 @@ EXPORT_TOOLKIT int swmm_getSimulationDateTime(SM_TimePropety type, int *year, in /// Return: API Error /// Purpose: Get the simulation start, end and report date times { - int error_code_index = 0; + int error_code = 0; *year = 1900; *month = 1; *day = 1; @@ -181,7 +183,7 @@ EXPORT_TOOLKIT int swmm_getSimulationDateTime(SM_TimePropety type, int *year, in // Check if Open if (swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } else { @@ -194,13 +196,13 @@ EXPORT_TOOLKIT int swmm_getSimulationDateTime(SM_TimePropety type, int *year, in case SM_ENDDATE: _dtime = EndDateTime; break; //ReportStart (globals.h) case SM_REPORTDATE: _dtime = ReportStart; break; - default: return error_getCode(ERR_API_OUTBOUNDS); + default: return ERR_TKAPI_OUTBOUNDS; } datetime_decodeDate(_dtime, year, month, day); datetime_decodeTime(_dtime, hour, minute, second); } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_setSimulationDateTime(SM_TimePropety type, int year, int month, @@ -212,7 +214,7 @@ EXPORT_TOOLKIT int swmm_setSimulationDateTime(SM_TimePropety type, int year, int /// Return: API Error /// Purpose: Get the simulation start, end and report date times { - int error_code_index = 0; + int error_code = 0; DateTime theDate; DateTime theTime; @@ -220,12 +222,12 @@ EXPORT_TOOLKIT int swmm_setSimulationDateTime(SM_TimePropety type, int year, int // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if Simulation is Running else if(swmm_IsStartedFlag() == TRUE) { - error_code_index = ERR_API_SIM_NRUNNING; + error_code = ERR_TKAPI_SIM_NRUNNING; } else { @@ -258,11 +260,11 @@ EXPORT_TOOLKIT int swmm_setSimulationDateTime(SM_TimePropety type, int year, int ReportStartTime = theTime; ReportStart = ReportStartDate + ReportStartTime; break; - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getSimulationUnit(SM_Units type, int *value) @@ -272,12 +274,12 @@ EXPORT_TOOLKIT int swmm_getSimulationUnit(SM_Units type, int *value) /// Returns: API Error /// Purpose: get simulation unit types { - int error_code_index = 0; + int error_code = 0; *value = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } else { @@ -290,11 +292,11 @@ EXPORT_TOOLKIT int swmm_getSimulationUnit(SM_Units type, int *value) // Concentration Unit //case 2: *value = UnitSystem; break; // Type not available - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getSimulationAnalysisSetting(SM_SimOption type, int *value) @@ -304,12 +306,12 @@ EXPORT_TOOLKIT int swmm_getSimulationAnalysisSetting(SM_SimOption type, int *va /// Returns: API Error /// Purpose: get simulation analysis setting { - int error_code_index = 0; + int error_code = 0; *value = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } else { @@ -332,10 +334,10 @@ EXPORT_TOOLKIT int swmm_getSimulationAnalysisSetting(SM_SimOption type, int *va // Analyze water quality (True or False) case SM_IGNORERQUAL: *value = IgnoreQuality; break; // Type not available - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getSimulationParam(SM_SimSetting type, double *value) @@ -345,12 +347,12 @@ EXPORT_TOOLKIT int swmm_getSimulationParam(SM_SimSetting type, double *value) /// Returns: error code /// Purpose: Get simulation analysis parameter { - int error_code_index = 0; + int error_code = 0; *value = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Output setting else @@ -404,10 +406,10 @@ EXPORT_TOOLKIT int swmm_getSimulationParam(SM_SimSetting type, double *value) // Tolerance for steady nodal inflow case SM_LATFLOWTOL: *value = LatFlowTol; break; // Type not available - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_countObjects(SM_ObjectType type, int *count) @@ -417,17 +419,17 @@ EXPORT_TOOLKIT int swmm_countObjects(SM_ObjectType type, int *count) /// Returns: API Error /// Purpose: uses Object Count table to find number of elements of an object { - int error_code_index = 0; + int error_code = 0; *count = 0; if(type >= MAX_OBJ_TYPES) { - error_code_index = ERR_API_OUTBOUNDS; + error_code = ERR_TKAPI_OUTBOUNDS; } else { *count = Nobjects[type]; } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getObjectIndex(SM_ObjectType type, char *id, int *index) @@ -438,15 +440,15 @@ EXPORT_TOOLKIT int swmm_getObjectIndex(SM_ObjectType type, char *id, int *index) /// Return: error /// Purpose: Gets object id index { - int error_code_index = 0; + int error_code = 0; // Check if Open if(swmm_IsOpenFlag() == TRUE) *index = project_findObject(type, id); else - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getObjectId(SM_ObjectType type, int index, char **id) @@ -457,18 +459,18 @@ EXPORT_TOOLKIT int swmm_getObjectId(SM_ObjectType type, int index, char **id) /// Return: API Error /// Purpose: Gets ID for any object { - int error_code_index = 0; + int error_code = 0; TLidProc* lidProc; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[type]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -507,12 +509,12 @@ EXPORT_TOOLKIT int swmm_getObjectId(SM_ObjectType type, int index, char **id) case SM_LID: lidProc = lid_getLidProc(index); if (lidProc != NULL) cstr_duplicate(id, lidProc->ID); - else error_code_index = ERR_API_OUTBOUNDS; + else error_code = ERR_TKAPI_OUTBOUNDS; break; - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getNodeType(int index, SM_NodeType *Ntype) @@ -522,21 +524,21 @@ EXPORT_TOOLKIT int swmm_getNodeType(int index, SM_NodeType *Ntype) /// Return: API Error /// Purpose: Gets Node Type { - int error_code_index = 0; + int error_code = 0; *Ntype = -1; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[NODE]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else *Ntype = Node[index].type; - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getLinkType(int index, SM_LinkType *Ltype) @@ -546,21 +548,21 @@ EXPORT_TOOLKIT int swmm_getLinkType(int index, SM_LinkType *Ltype) /// Return: API Error /// Purpose: Gets Link Type { - int error_code_index = 0; + int error_code = 0; *Ltype = -1; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[LINK]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else *Ltype = Link[index].type; - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getLinkConnections(int index, int *Node1, int *Node2) @@ -570,25 +572,25 @@ EXPORT_TOOLKIT int swmm_getLinkConnections(int index, int *Node1, int *Node2) /// Return: API Error /// Purpose: Gets link Connection ID Indeces { - int error_code_index = 0; + int error_code = 0; *Node1 = -1; *Node2 = -1; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[LINK]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { *Node1 = Link[index].node1; *Node2 = Link[index].node2; } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getLinkDirection(int index, signed char *value) @@ -598,23 +600,23 @@ EXPORT_TOOLKIT int swmm_getLinkDirection(int index, signed char *value) /// Return: API Error /// Purpose: Gets Link Direction { - int error_code_index = 0; + int error_code = 0; *value = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[LINK]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { *value = Link[index].direction; } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getNodeParam(int index, SM_NodeProperty param, double *value) @@ -625,17 +627,17 @@ EXPORT_TOOLKIT int swmm_getNodeParam(int index, SM_NodeProperty param, double *v /// Return: API Error /// Purpose: Gets Node Parameter { - int error_code_index = 0; + int error_code = 0; *value = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[NODE]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -651,10 +653,10 @@ EXPORT_TOOLKIT int swmm_getNodeParam(int index, SM_NodeProperty param, double *v *value = Node[index].pondedArea * UCF(LENGTH) * UCF(LENGTH); break; case SM_INITDEPTH: *value = Node[index].initDepth * UCF(LENGTH); break; - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_setNodeParam(int index, SM_NodeProperty param, double value) @@ -665,21 +667,21 @@ EXPORT_TOOLKIT int swmm_setNodeParam(int index, SM_NodeProperty param, double va /// Return: API Error /// Purpose: Sets Node Parameter { - int error_code_index = 0; + int error_code = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if Simulation is Running else if(swmm_IsStartedFlag() == TRUE) { - error_code_index = ERR_API_SIM_NRUNNING; + error_code = ERR_TKAPI_SIM_NRUNNING; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[NODE]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -695,12 +697,12 @@ EXPORT_TOOLKIT int swmm_setNodeParam(int index, SM_NodeProperty param, double va Node[index].pondedArea = value / ( UCF(LENGTH) * UCF(LENGTH) ); break; case SM_INITDEPTH: Node[index].initDepth = value / UCF(LENGTH); break; - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } // Re-validated a node BEM 1/20/2017 Probably need to re-validate connecting links //node_validate(index) - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getLinkParam(int index, SM_LinkProperty param, double *value) @@ -711,17 +713,17 @@ EXPORT_TOOLKIT int swmm_getLinkParam(int index, SM_LinkProperty param, double *v /// Return: API Error /// Purpose: Gets Link Parameter { - int error_code_index = 0; + int error_code = 0; *value = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[LINK]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -741,10 +743,10 @@ EXPORT_TOOLKIT int swmm_getLinkParam(int index, SM_LinkProperty param, double *v *value = Link[index].cLossOutlet; break; case SM_AVELOSS: *value = Link[index].cLossAvg; break; - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_setLinkParam(int index, SM_LinkProperty param, double value) @@ -755,16 +757,16 @@ EXPORT_TOOLKIT int swmm_setLinkParam(int index, SM_LinkProperty param, double va /// Return: API Error /// Purpose: Sets Link Parameter { - int error_code_index = 0; + int error_code = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[LINK]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -775,14 +777,14 @@ EXPORT_TOOLKIT int swmm_setLinkParam(int index, SM_LinkProperty param, double va // Check if Simulation is Running if(swmm_IsStartedFlag() == TRUE) { - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; } Link[index].offset1 = value / UCF(LENGTH); break; case SM_OFFSET2: // Check if Simulation is Running if(swmm_IsStartedFlag() == TRUE) { - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; } Link[index].offset2 = value / UCF(LENGTH); break; case SM_INITFLOW: @@ -795,13 +797,13 @@ EXPORT_TOOLKIT int swmm_setLinkParam(int index, SM_LinkProperty param, double va Link[index].cLossOutlet = value; break; case SM_AVELOSS: Link[index].cLossAvg = value; break; - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } // re-validated link //link_validate(index); } - return error_getCode(error_code_index); + return error_code; } @@ -813,17 +815,17 @@ EXPORT_TOOLKIT int swmm_getSubcatchParam(int index, SM_SubcProperty param, doubl /// Return: API Error /// Purpose: Gets Subcatchment Parameter { - int error_code_index = 0; + int error_code = 0; *value = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[SUBCATCH]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -839,10 +841,10 @@ EXPORT_TOOLKIT int swmm_getSubcatchParam(int index, SM_SubcProperty param, doubl *value = Subcatch[index].slope; break; case SM_CURBLEN: *value = Subcatch[index].curbLength * UCF(LENGTH); break; - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_setSubcatchParam(int index, SM_SubcProperty param, double value) @@ -853,21 +855,21 @@ EXPORT_TOOLKIT int swmm_setSubcatchParam(int index, SM_SubcProperty param, doubl /// Return: API Error /// Purpose: Sets Subcatchment Parameter { - int error_code_index = 0; + int error_code = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if Simulation is Running else if(swmm_IsStartedFlag() == TRUE) { - error_code_index = ERR_API_SIM_NRUNNING; + error_code = ERR_TKAPI_SIM_NRUNNING; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[SUBCATCH]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -887,13 +889,13 @@ EXPORT_TOOLKIT int swmm_setSubcatchParam(int index, SM_SubcProperty param, doubl Subcatch[index].slope = value; break; case SM_CURBLEN: Subcatch[index].curbLength = value / UCF(LENGTH); break; - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } //re-validate subcatchment subcatch_validate(index); // incorprate callback here } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getSubcatchOutConnection(int index, SM_ObjectType *type, int *out_index) @@ -905,18 +907,18 @@ EXPORT_TOOLKIT int swmm_getSubcatchOutConnection(int index, SM_ObjectType *type, /// Return: API Error /// Purpose: Gets Subcatchment Connection ID Indeces for either Node or Subcatchment { - int error_code_index = 0; + int error_code = 0; *type = -1; *out_index = -1; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[SUBCATCH]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -936,7 +938,7 @@ EXPORT_TOOLKIT int swmm_getSubcatchOutConnection(int index, SM_ObjectType *type, *type = (SM_ObjectType)SUBCATCH; } } - return error_getCode(error_code_index); + return error_code; } @@ -946,24 +948,24 @@ EXPORT_TOOLKIT int swmm_getLidUCount(int index, int *value) // Return: number of lid units for subcatchment // Purpose: count number of lid units for subcatchment { - int error_code_index = 0; + int error_code = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if subcatchment index is within bounds else if(index < 0 || index >= Nobjects[SUBCATCH]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { *value = lid_getLidUnitCount(index); } - return error_getCode(error_code_index); + return error_code; } @@ -976,22 +978,22 @@ EXPORT_TOOLKIT int swmm_getLidUParam(int index, int lidIndex, SM_LidUProperty pa // Return: API Error // Purpose: Gets Lid Unit Parameter { - int error_code_index = 0; + int error_code = 0; TLidUnit* lidUnit; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if subcatchment index is within bounds else if(index < 0 || index >= Nobjects[SUBCATCH]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { - lidUnit = lid_getLidUnit(index, lidIndex, &error_code_index); + lidUnit = lid_getLidUnit(index, lidIndex, &error_code); // There are no Lid Units defined for the subcatchments if(lidUnit) @@ -1012,12 +1014,12 @@ EXPORT_TOOLKIT int swmm_getLidUParam(int index, int lidIndex, SM_LidUProperty pa case SM_FROMPERV: *value = lidUnit->fromPerv * 100; break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } } } - return error_getCode(error_code_index); + return error_code; } @@ -1030,27 +1032,27 @@ EXPORT_TOOLKIT int swmm_setLidUParam(int index, int lidIndex, SM_LidUProperty pa // Return: API Error // Purpose: Gets Lid Unit Parameter { - int error_code_index = 0; + int error_code = 0; TLidUnit* lidUnit; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if subcatchment index is within bounds else if(index < 0 || index >= Nobjects[SUBCATCH]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } // Check if model is running else if(swmm_IsStartedFlag() == TRUE) { - error_code_index = ERR_API_SIM_NRUNNING; + error_code = ERR_TKAPI_SIM_NRUNNING; } else { - lidUnit = lid_getLidUnit(index, lidIndex, &error_code_index); + lidUnit = lid_getLidUnit(index, lidIndex, &error_code); // There are no Lid Units defined for the subcatchments if(lidUnit) @@ -1071,18 +1073,18 @@ EXPORT_TOOLKIT int swmm_setLidUParam(int index, int lidIndex, SM_LidUProperty pa case SM_FROMPERV: lidUnit->fromPerv = value / 100; break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } } } - if(error_code_index == ERR_NONE) + if(error_code == ERR_NONE) { lid_validateLidGroup(index); lid_updateLidGroup(index); } - return error_getCode(error_code_index); + return error_code; } @@ -1095,22 +1097,22 @@ EXPORT_TOOLKIT int swmm_getLidUOption(int index, int lidIndex, SM_LidUOptions pa // Return: API Error // Purpose: Gets Lid Unit Option Parameter { - int error_code_index = 0; + int error_code = 0; TLidUnit* lidUnit; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if subcatchment index is within bounds else if(index < 0 || index >= Nobjects[SUBCATCH]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { - lidUnit = lid_getLidUnit(index, lidIndex, &error_code_index); + lidUnit = lid_getLidUnit(index, lidIndex, &error_code); // There are no Lid Units defined for the subcatchments if(lidUnit) @@ -1129,12 +1131,12 @@ EXPORT_TOOLKIT int swmm_getLidUOption(int index, int lidIndex, SM_LidUOptions pa case SM_DRAINNODE: *value = lidUnit->drainNode; break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } } } - return error_getCode(error_code_index); + return error_code; } @@ -1147,23 +1149,23 @@ EXPORT_TOOLKIT int swmm_setLidUOption(int index, int lidIndex, SM_LidUOptions pa // Return: API Error // Purpose: Sets Lid Unit Option { - int error_code_index = 0; + int error_code = 0; TLidUnit* lidUnit; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if subcatchment index is within bounds else if(index < 0 || index >= Nobjects[SUBCATCH]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } // Check if model is running else if(swmm_IsStartedFlag() == TRUE) { - lidUnit = lid_getLidUnit(index, lidIndex, &error_code_index); + lidUnit = lid_getLidUnit(index, lidIndex, &error_code); // There are no Lid Units defined for the subcatchments if (lidUnit) @@ -1171,11 +1173,11 @@ EXPORT_TOOLKIT int swmm_setLidUOption(int index, int lidIndex, SM_LidUOptions pa switch (param) { case SM_INDEX: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_NUMBER: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_TOPERV: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_DRAINSUB: lidUnit->drainSubcatch = value; lidUnit->drainNode = -1; @@ -1185,13 +1187,13 @@ EXPORT_TOOLKIT int swmm_setLidUOption(int index, int lidIndex, SM_LidUOptions pa lidUnit->drainSubcatch = -1; break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } } } else if(swmm_IsStartedFlag() == FALSE) { - lidUnit = lid_getLidUnit(index, lidIndex, &error_code_index); + lidUnit = lid_getLidUnit(index, lidIndex, &error_code); // There are no Lid Units defined for the subcatchments if(lidUnit) @@ -1214,16 +1216,16 @@ EXPORT_TOOLKIT int swmm_setLidUOption(int index, int lidIndex, SM_LidUOptions pa lidUnit->drainSubcatch = -1; break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } } } else { - error_code_index = ERR_API_OUTBOUNDS; + error_code = ERR_TKAPI_OUTBOUNDS; } - if(error_code_index == ERR_NONE) + if(error_code == ERR_NONE) { lid_validateLidGroup(index); if (swmm_IsStartedFlag() == FALSE) @@ -1232,7 +1234,7 @@ EXPORT_TOOLKIT int swmm_setLidUOption(int index, int lidIndex, SM_LidUOptions pa } } - return error_getCode(error_code_index); + return error_code; } @@ -1243,18 +1245,18 @@ EXPORT_TOOLKIT int swmm_getLidCOverflow(int lidControlIndex, int *condition) // Return: API Error // Purpose: Get Lid Control Surface Immediate Overflow Condition { - int error_code_index = 0; + int error_code = 0; TLidProc* lidProc; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if subcatchment index is within bounds else if(lidControlIndex < 0 || lidControlIndex >= Nobjects[LID]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -1265,7 +1267,7 @@ EXPORT_TOOLKIT int swmm_getLidCOverflow(int lidControlIndex, int *condition) } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getLidCParam(int lidControlIndex, SM_LidLayer layerIndex, SM_LidLayerProperty param, double *value) @@ -1277,18 +1279,18 @@ EXPORT_TOOLKIT int swmm_getLidCParam(int lidControlIndex, SM_LidLayer layerIndex // Return: API Error // Purpose: Gets Lid Control Layer Parameter { - int error_code_index = 0; + int error_code = 0; TLidProc* lidProc; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if subcatchment index is within bounds else if(lidControlIndex < 0 || lidControlIndex >= Nobjects[LID]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -1312,7 +1314,7 @@ EXPORT_TOOLKIT int swmm_getLidCParam(int lidControlIndex, SM_LidLayer layerIndex case SM_ALPHA: *value = lidProc->surface.alpha; break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; case SM_SOIL: @@ -1333,7 +1335,7 @@ EXPORT_TOOLKIT int swmm_getLidCParam(int lidControlIndex, SM_LidLayer layerIndex case SM_SUCTION: *value = lidProc->soil.suction * UCF(RAINDEPTH); break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; case SM_STOR: @@ -1366,7 +1368,7 @@ EXPORT_TOOLKIT int swmm_getLidCParam(int lidControlIndex, SM_LidLayer layerIndex } break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; case SM_PAVE: @@ -1406,7 +1408,7 @@ EXPORT_TOOLKIT int swmm_getLidCParam(int lidControlIndex, SM_LidLayer layerIndex case SM_REGENDEGREE: *value = lidProc->pavement.regenDegree; break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; case SM_DRAIN: @@ -1425,7 +1427,7 @@ EXPORT_TOOLKIT int swmm_getLidCParam(int lidControlIndex, SM_LidLayer layerIndex case SM_HCLOSE: *value = lidProc->drain.hClose * UCF(RAINDEPTH); break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; case SM_DRAINMAT: @@ -1440,14 +1442,14 @@ EXPORT_TOOLKIT int swmm_getLidCParam(int lidControlIndex, SM_LidLayer layerIndex case SM_ALPHA: *value = lidProc->drainMat.alpha; break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } } - return error_getCode(error_code_index); + return error_code; } @@ -1460,18 +1462,18 @@ EXPORT_TOOLKIT int swmm_setLidCParam(int lidControlIndex, SM_LidLayer layerIndex // Return: API Error // Purpose: Sets Lid Control Layer Parameter { - int error_code_index = 0; + int error_code = 0; TLidProc* lidProc; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if subcatchment index is within bounds else if(lidControlIndex < 0 || lidControlIndex >= Nobjects[LID]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else if (swmm_IsStartedFlag() == TRUE) { @@ -1483,69 +1485,69 @@ EXPORT_TOOLKIT int swmm_setLidCParam(int lidControlIndex, SM_LidLayer layerIndex switch (param) { case SM_THICKNESS: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_VOIDFRAC: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_ROUGHNESS: lidProc->surface.roughness = value; break; case SM_SURFSLOPE: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_SIDESLOPE: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; case SM_SOIL: switch (param) { case SM_THICKNESS: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_POROSITY: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_FIELDCAP: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_WILTPOINT: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_KSAT: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_KSLOPE: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_SUCTION: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; case SM_STOR: switch (param) { case SM_THICKNESS: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_VOIDFRAC: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_KSAT: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_CLOGFACTOR: lidProc->storage.clogFactor = value * lidProc->storage.thickness * lidProc->storage.voidFrac; break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; case SM_PAVE: switch (param) { case SM_THICKNESS: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_VOIDFRAC: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_IMPERVFRAC: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_KSAT: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_CLOGFACTOR: lidProc->pavement.clogFactor = value * lidProc->pavement.thickness * @@ -1553,11 +1555,11 @@ EXPORT_TOOLKIT int swmm_setLidCParam(int lidControlIndex, SM_LidLayer layerIndex (1.0 - lidProc->pavement.impervFrac); break; case SM_REGENDAYS: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_REGENDEGREE: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; case SM_DRAIN: @@ -1576,26 +1578,26 @@ EXPORT_TOOLKIT int swmm_setLidCParam(int lidControlIndex, SM_LidLayer layerIndex case SM_HCLOSE: lidProc->drain.hClose = value / UCF(RAINDEPTH); break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; case SM_DRAINMAT: switch (param) { case SM_THICKNESS: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_VOIDFRAC: - error_code_index = ERR_API_SIM_NRUNNING; break; + error_code = ERR_TKAPI_SIM_NRUNNING; break; case SM_ROUGHNESS: lidProc->drainMat.roughness = value; break; //case SM_ALPHA: // lidProc->drainMat.alpha = value; break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } } else if(swmm_IsStartedFlag() == FALSE) @@ -1620,7 +1622,7 @@ EXPORT_TOOLKIT int swmm_setLidCParam(int lidControlIndex, SM_LidLayer layerIndex //case SM_ALPHA: // lidProc->surface.alpha = value; break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; case SM_SOIL: @@ -1641,7 +1643,7 @@ EXPORT_TOOLKIT int swmm_setLidCParam(int lidControlIndex, SM_LidLayer layerIndex case SM_SUCTION: lidProc->soil.suction = value / UCF(RAINDEPTH); break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; case SM_STOR: @@ -1680,7 +1682,7 @@ EXPORT_TOOLKIT int swmm_setLidCParam(int lidControlIndex, SM_LidLayer layerIndex lidProc->storage.voidFrac; break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; case SM_PAVE: @@ -1735,7 +1737,7 @@ EXPORT_TOOLKIT int swmm_setLidCParam(int lidControlIndex, SM_LidLayer layerIndex case SM_REGENDEGREE: lidProc->pavement.regenDegree = value; break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; case SM_DRAIN: @@ -1754,7 +1756,7 @@ EXPORT_TOOLKIT int swmm_setLidCParam(int lidControlIndex, SM_LidLayer layerIndex case SM_HCLOSE: lidProc->drain.hClose = value / UCF(RAINDEPTH); break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; case SM_DRAINMAT: @@ -1769,19 +1771,19 @@ EXPORT_TOOLKIT int swmm_setLidCParam(int lidControlIndex, SM_LidLayer layerIndex //case SM_ALPHA: // lidProc->drainMat.alpha = value; break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } } else { - error_code_index = ERR_API_OUTBOUNDS; + error_code = ERR_TKAPI_OUTBOUNDS; } - if(error_code_index == ERR_NONE) + if(error_code == ERR_NONE) { lid_validateLidProc(lidControlIndex); if (swmm_IsStartedFlag() == FALSE) @@ -1789,7 +1791,7 @@ EXPORT_TOOLKIT int swmm_setLidCParam(int lidControlIndex, SM_LidLayer layerIndex lid_updateAllLidUnit(lidControlIndex); } } - return error_getCode(error_code_index); + return error_code; } //------------------------------- @@ -1805,7 +1807,7 @@ EXPORT_TOOLKIT int swmm_getCurrentDateTime(int *year, int *month, int *day, /// Purpose: Get the simulation start, end and report date times { DateTime currentTime; - int error_code_index = 0; + int error_code = 0; *year = 1900; *month = 1; *day = 1; @@ -1816,7 +1818,7 @@ EXPORT_TOOLKIT int swmm_getCurrentDateTime(int *year, int *month, int *day, // Check if Simulation is Running if(swmm_IsStartedFlag() == FALSE) { - error_code_index = ERR_API_SIM_NRUNNING; + error_code = ERR_TKAPI_SIM_NRUNNING; } else { @@ -1827,7 +1829,7 @@ EXPORT_TOOLKIT int swmm_getCurrentDateTime(int *year, int *month, int *day, datetime_decodeTime(currentTime, hour, minute, second); } - return error_getCode(error_code_index); + return error_code; } @@ -1839,18 +1841,18 @@ EXPORT_TOOLKIT int swmm_getNodeResult(int index, SM_NodeResult type, double *res /// Return: API Error /// Purpose: Gets Node Simulated Value at Current Time { - int error_code_index = 0; + int error_code = 0; *result = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[NODE]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -1875,10 +1877,10 @@ EXPORT_TOOLKIT int swmm_getNodeResult(int index, SM_NodeResult type, double *res *result = Node[index].newLatFlow * UCF(FLOW); break; case SM_HRT: *result = Storage[Node[index].subIndex].hrt; break; - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getNodePollut(int index, SM_NodePollut type, double **pollutArray, int *length) @@ -1890,22 +1892,22 @@ EXPORT_TOOLKIT int swmm_getNodePollut(int index, SM_NodePollut type, double **po /// Purpose: Gets Node Simulated Water Quality Value at Current Time { int p; - int error_code_index = 0; + int error_code = 0; double* result; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[NODE]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else if (MEMCHECK(result = newDoubleArray(Nobjects[POLLUT]))) { - error_code_index = ERR_MEMORY; + error_code = ERR_MEMORY; } else @@ -1938,10 +1940,10 @@ EXPORT_TOOLKIT int swmm_getNodePollut(int index, SM_NodePollut type, double **po } *pollutArray = result; } break; - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_setNodePollut(int index, SM_NodePollut type, int pollutant_index, double pollutant_value) @@ -1953,17 +1955,17 @@ EXPORT_TOOLKIT int swmm_setNodePollut(int index, SM_NodePollut type, int polluta /// Return: API Error /// Purpose: Set pollutant concentration in nodes at the current time step { - int error_code_index = 0; + int error_code = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[NODE]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -1980,12 +1982,12 @@ EXPORT_TOOLKIT int swmm_setNodePollut(int index, SM_NodePollut type, int polluta Node[index].extQual[pollutant_index] = pollutant_value; Node[index].extPollutFlag[pollutant_index] = 1; } break; - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } } - return error_getCode(error_code_index); + return error_code; } @@ -1997,18 +1999,18 @@ EXPORT_TOOLKIT int swmm_getLinkResult(int index, SM_LinkResult type, double *res /// Return: API Error /// Purpose: Gets Link Simulated Value at Current Time { - int error_code_index = 0; + int error_code = 0; *result = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[LINK]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -2030,10 +2032,10 @@ EXPORT_TOOLKIT int swmm_getLinkResult(int index, SM_LinkResult type, double *res *result = Link[index].targetSetting; break; case SM_FROUDE: *result = Link[index].froude; break; - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getLinkPollut(int index, SM_LinkPollut type, double **pollutArray, int *length) @@ -2045,22 +2047,22 @@ EXPORT_TOOLKIT int swmm_getLinkPollut(int index, SM_LinkPollut type, double **po /// Purpose: Gets Link Simulated Water Quality Value at Current Time { int p; - int error_code_index = 0; + int error_code = 0; double* result; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[LINK]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else if (MEMCHECK(result = newDoubleArray(Nobjects[POLLUT]))) { - error_code_index = ERR_MEMORY; + error_code = ERR_MEMORY; } else @@ -2097,10 +2099,10 @@ EXPORT_TOOLKIT int swmm_getLinkPollut(int index, SM_LinkPollut type, double **po } *pollutArray = result; } break; - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } - return error_getCode(error_code_index); + return error_code; } @@ -2113,17 +2115,17 @@ EXPORT_TOOLKIT int swmm_setLinkPollut(int index, SM_LinkPollut type, int polluta /// Return: API error /// Purponse: Set pollutant concentration in links { - int error_code_index = 0; + int error_code = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[LINK]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -2140,11 +2142,11 @@ EXPORT_TOOLKIT int swmm_setLinkPollut(int index, SM_LinkPollut type, int polluta Link[index].extQual[pollutant_index] = pollutant_value; Link[index].extPollutFlag[pollutant_index] = 1; } break; - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getSubcatchResult(int index, SM_SubcResult type, double* result) @@ -2155,18 +2157,18 @@ EXPORT_TOOLKIT int swmm_getSubcatchResult(int index, SM_SubcResult type, double* /// Return: API Error /// Purpose: Gets Subcatchment Simulated Value at Current Time { - int error_code_index = 0; + int error_code = 0; *result = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[SUBCATCH]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -2184,10 +2186,10 @@ EXPORT_TOOLKIT int swmm_getSubcatchResult(int index, SM_SubcResult type, double* *result = Subcatch[index].newRunoff * UCF(FLOW); break; case SM_SUBCSNOW: *result = Subcatch[index].newSnowDepth * UCF(RAINDEPTH); break; - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getSubcatchPollut(int index, SM_SubcPollut type, double **pollutArray, int *length) @@ -2199,23 +2201,23 @@ EXPORT_TOOLKIT int swmm_getSubcatchPollut(int index, SM_SubcPollut type, double /// Purpose: Gets Subcatchment Simulated Pollutant Value at Current Time { int p; - int error_code_index = 0; + int error_code = 0; double a; double* result; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[SUBCATCH]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else if (MEMCHECK(result = newDoubleArray(Nobjects[POLLUT]))) { - error_code_index = ERR_MEMORY; + error_code = ERR_MEMORY; } else @@ -2265,10 +2267,10 @@ EXPORT_TOOLKIT int swmm_getSubcatchPollut(int index, SM_SubcPollut type, double *length = Nobjects[POLLUT]; } break; - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getGagePrecip(int index, SM_GagePrecip type, double* result) @@ -2279,7 +2281,7 @@ EXPORT_TOOLKIT int swmm_getGagePrecip(int index, SM_GagePrecip type, double* res /// Return: API Error /// Purpose: Gets the precipitation value in the gage. { - int error_code_index = 0; + int error_code = 0; double rainfall = 0; double snowfall = 0; double total = 0; @@ -2287,12 +2289,12 @@ EXPORT_TOOLKIT int swmm_getGagePrecip(int index, SM_GagePrecip type, double* res // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[GAGE]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } // Read the rainfall value else @@ -2306,10 +2308,10 @@ EXPORT_TOOLKIT int swmm_getGagePrecip(int index, SM_GagePrecip type, double* res *result = rainfall * UCF(RAINFALL); break; case SM_SNOWFALL: *result = snowfall * UCF(RAINFALL); break; - default: error_code_index = ERR_API_OUTBOUNDS; break; + default: error_code = ERR_TKAPI_OUTBOUNDS; break; } } - return error_getCode(error_code_index); + return error_code; } @@ -2319,27 +2321,27 @@ EXPORT_TOOLKIT int swmm_getNodeStats(int index, SM_NodeStats *nodeStats) /// Return: API Error /// Purpose: Gets Node Stats and Converts Units { - int error_index = 0; + int error_code = 0; // Check if Open if (swmm_IsOpenFlag() == FALSE) - error_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; // Check if Simulation is Running else if (swmm_IsStartedFlag() == FALSE) - error_index = ERR_API_SIM_NRUNNING; + error_code = ERR_TKAPI_SIM_NRUNNING; // Check if object index is within bounds else if (index < 0 || index >= Nobjects[NODE]) - error_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; else if (nodeStats == NULL) - error_index = ERR_API_MEMORY; + error_code = ERR_TKAPI_MEMORY; else stats_getNodeStat(index, (TNodeStats **)&nodeStats); - return error_getCode(error_index); + return error_code; } EXPORT_TOOLKIT int swmm_getNodeTotalInflow(int index, double* value) @@ -2349,20 +2351,20 @@ EXPORT_TOOLKIT int swmm_getNodeTotalInflow(int index, double* value) /// Return: API Error /// Purpose: Get Node Total Inflow Volume. { - int error_index = 0; + int error_code = 0; // Check if Open if (swmm_IsOpenFlag() == FALSE) - error_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; // Check if Simulation is Running else if (swmm_IsStartedFlag() == FALSE) - error_index = ERR_API_SIM_NRUNNING; + error_code = ERR_TKAPI_SIM_NRUNNING; else massbal_getNodeTotalInflow(index, value); - return error_getCode(error_index); + return error_code; } EXPORT_TOOLKIT int swmm_getStorageStats(int index, SM_StorageStats *storageStats) @@ -2371,60 +2373,60 @@ EXPORT_TOOLKIT int swmm_getStorageStats(int index, SM_StorageStats *storageStats /// Return: API Error /// Purpose: Gets Storage Node Stats and Converts Units { - int error_index = 0; + int error_code = 0; // Check if Open if (swmm_IsOpenFlag() == FALSE) - error_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; // Check if Simulation is Running else if (swmm_IsStartedFlag() == FALSE) - error_index = ERR_API_SIM_NRUNNING; + error_code = ERR_TKAPI_SIM_NRUNNING; // Check if object index is within bounds else if (index < 0 || index >= Nobjects[NODE]) - error_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; // Check Node Type is storage else if (Node[index].type != STORAGE) - error_index = ERR_API_WRONG_TYPE; + error_code = ERR_TKAPI_WRONG_TYPE; else if (storageStats == NULL) - error_index = ERR_API_MEMORY; + error_code = ERR_TKAPI_MEMORY; else stats_getStorageStat(index, (TStorageStats **)&storageStats); - return error_getCode(error_index); + return error_code; } EXPORT_TOOLKIT int swmm_getOutfallStats(int index, SM_OutfallStats *outfallStats) { - int error_index = 0; + int error_code = 0; // Check if Open if (swmm_IsOpenFlag() == FALSE) - error_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; // Check if Simulation is Running else if (swmm_IsStartedFlag() == FALSE) - error_index = ERR_API_SIM_NRUNNING; + error_code = ERR_TKAPI_SIM_NRUNNING; // Check if object index is within bounds else if (index < 0 || index >= Nobjects[NODE]) - error_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; // Check Node Type is outfall else if (Node[index].type != OUTFALL) - error_index = ERR_API_WRONG_TYPE; + error_code = ERR_TKAPI_WRONG_TYPE; else if (outfallStats == NULL) - error_index = ERR_API_MEMORY; + error_code = ERR_TKAPI_MEMORY; else stats_getOutfallStat(index, (TOutfallStats **)&outfallStats); - return error_getCode(error_index); + return error_code; } @@ -2434,27 +2436,27 @@ EXPORT_TOOLKIT int swmm_getLinkStats(int index, SM_LinkStats *linkStats) /// Return: API Error /// Purpose: Gets Link Stats and Converts Units { - int error_index = 0; + int error_code = 0; // Check if Open if (swmm_IsOpenFlag() == FALSE) - error_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; // Check if Simulation is Running else if (swmm_IsStartedFlag() == FALSE) - error_index = ERR_API_SIM_NRUNNING; + error_code = ERR_TKAPI_SIM_NRUNNING; // Check if object index is within bounds else if (index < 0 || index >= Nobjects[LINK]) - error_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; else if (linkStats == NULL) - error_index = ERR_API_MEMORY; + error_code = ERR_TKAPI_MEMORY; else stats_getLinkStat(index, (TLinkStats **)&linkStats); - return error_getCode(error_index); + return error_code; } @@ -2464,31 +2466,31 @@ EXPORT_TOOLKIT int swmm_getPumpStats(int index, SM_PumpStats *pumpStats) /// Return: API Error /// Purpose: Gets Pump Link Stats and Converts Units { - int error_index = 0; + int error_code = 0; // Check if Open if (swmm_IsOpenFlag() == FALSE) - error_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; // Check if Simulation is Running else if (swmm_IsStartedFlag() == FALSE) - error_index = ERR_API_SIM_NRUNNING; + error_code = ERR_TKAPI_SIM_NRUNNING; // Check if object index is within bounds else if (index < 0 || index >= Nobjects[LINK]) - error_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; // Check if pump else if (Link[index].type != PUMP) - error_index = ERR_API_WRONG_TYPE; + error_code = ERR_TKAPI_WRONG_TYPE; else if (pumpStats == NULL) - error_index = ERR_API_MEMORY; + error_code = ERR_TKAPI_MEMORY; else stats_getPumpStat(index, (TPumpStats **)&pumpStats); - return error_getCode(error_index); + return error_code; } @@ -2498,27 +2500,27 @@ EXPORT_TOOLKIT int swmm_getSubcatchStats(int index, SM_SubcatchStats *subcatchSt /// Return: API Error /// Purpose: Gets Subcatchment Stats and Converts Units { - int error_index = 0; + int error_code = 0; // Check if Open if (swmm_IsOpenFlag() == FALSE) - error_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; // Check if Simulation is Running else if (swmm_IsStartedFlag() == FALSE) - error_index = ERR_API_SIM_NRUNNING; + error_code = ERR_TKAPI_SIM_NRUNNING; // Check if object index is within bounds else if (index < 0 || index >= Nobjects[SUBCATCH]) - error_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; else if (subcatchStats == NULL) - error_index = ERR_API_MEMORY; + error_code = ERR_TKAPI_MEMORY; else stats_getSubcatchStat(index, (TSubcatchStats **)&subcatchStats); - return error_getCode(error_index); + return error_code; } @@ -2528,23 +2530,23 @@ EXPORT_TOOLKIT int swmm_getSystemRoutingTotals(SM_RoutingTotals *routingTotals) /// Return: API Error /// Purpose: Gets System Flow Routing Totals and Converts Units { - int error_index = 0; + int error_code = 0; // Check if Open if (swmm_IsOpenFlag() == FALSE) - error_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; // Check if Simulation is Running else if (swmm_IsStartedFlag() == FALSE) - error_index = ERR_API_SIM_NRUNNING; + error_code = ERR_TKAPI_SIM_NRUNNING; else if (routingTotals == NULL) - error_index = ERR_API_MEMORY; + error_code = ERR_TKAPI_MEMORY; else massbal_getRoutingTotal((TRoutingTotals **)&routingTotals); - return error_getCode(error_index); + return error_code; } EXPORT_TOOLKIT int swmm_getSystemRunoffTotals(SM_RunoffTotals *runoffTotals) @@ -2553,23 +2555,23 @@ EXPORT_TOOLKIT int swmm_getSystemRunoffTotals(SM_RunoffTotals *runoffTotals) /// Return: API Error /// Purpose: Gets System Runoff Totals and Converts Units { - int error_index = 0; + int error_code = 0; // Check if Open if (swmm_IsOpenFlag() == FALSE) - error_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; // Check if Simulation is Running else if (swmm_IsStartedFlag() == FALSE) - error_index = ERR_API_SIM_NRUNNING; + error_code = ERR_TKAPI_SIM_NRUNNING; else if (runoffTotals == NULL) - error_index = ERR_API_MEMORY; + error_code = ERR_TKAPI_MEMORY; else massbal_getRunoffTotal((TRunoffTotals **)&runoffTotals); - return error_getCode(error_index); + return error_code; } EXPORT_TOOLKIT int swmm_getLidUFluxRates(int index, int lidIndex, SM_LidLayer layerIndex, double* result) @@ -2581,22 +2583,22 @@ EXPORT_TOOLKIT int swmm_getLidUFluxRates(int index, int lidIndex, SM_LidLayer la // Return: API Error // Purpose: Gets Lid Unit Water Balance Simulated Value at Current Time { - int error_code_index = 0; + int error_code = 0; TLidUnit* lidUnit; // Check if Open if (swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[SUBCATCH]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { - lidUnit = lid_getLidUnit(index, lidIndex, &error_code_index); + lidUnit = lid_getLidUnit(index, lidIndex, &error_code); // There are no Lid Units defined for the subcatchments if (lidUnit) @@ -2612,15 +2614,15 @@ EXPORT_TOOLKIT int swmm_getLidUFluxRates(int index, int lidIndex, SM_LidLayer la case SM_PAVE: *result = lidUnit->oldFluxRates[SM_PAVE] * UCF(LENGTH); break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } } else { - error_code_index = ERR_API_UNDEFINED_LID; + error_code = ERR_TKAPI_UNDEFINED_LID; } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getLidGResult(int index, SM_LidResult type, double* result) @@ -2631,18 +2633,18 @@ EXPORT_TOOLKIT int swmm_getLidGResult(int index, SM_LidResult type, double* resu // Return: API Error // Purpose: Gets Lid Group Data at Current Time { - int error_code_index = 0; + int error_code = 0; TLidGroup lidGroup; // Check if Open if (swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[SUBCATCH]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -2661,15 +2663,15 @@ EXPORT_TOOLKIT int swmm_getLidGResult(int index, SM_LidResult type, double* resu case SM_NEWDRAINFLOW: *result = lidGroup->newDrainFlow * UCF(FLOW); break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } } else { - error_code_index = ERR_API_UNDEFINED_LID; + error_code = ERR_TKAPI_UNDEFINED_LID; } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_getLidUResult(int index, int lidIndex, SM_LidResult type, double* result) @@ -2680,23 +2682,23 @@ EXPORT_TOOLKIT int swmm_getLidUResult(int index, int lidIndex, SM_LidResult type // Return: API Error // Purpose: Gets Lid Unit Water Balance Simulated Value at Current Time { - int error_code_index = 0; + int error_code = 0; TLidUnit* lidUnit; double Tstep = 0; // Check if Open if (swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[SUBCATCH]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { - lidUnit = lid_getLidUnit(index, lidIndex, &error_code_index); + lidUnit = lid_getLidUnit(index, lidIndex, &error_code); // There are no Lid Units defined for the subcatchments if (lidUnit) @@ -2760,15 +2762,15 @@ EXPORT_TOOLKIT int swmm_getLidUResult(int index, int lidIndex, SM_LidResult type case SM_STORAGEDRAIN: *result = lidUnit->waterRate.storageDrain * UCF(RAINFALL); break; default: - error_code_index = ERR_API_OUTBOUNDS; break; + error_code = ERR_TKAPI_OUTBOUNDS; break; } } else { - error_code_index = ERR_API_UNDEFINED_LID; + error_code = ERR_TKAPI_UNDEFINED_LID; } } - return error_getCode(error_code_index); + return error_code; } //------------------------------- @@ -2783,18 +2785,18 @@ EXPORT_TOOLKIT int swmm_setLinkSetting(int index, double setting) /// Purpose: Sets Link open fraction (Weir, Orifice, Pump, and Outlet) { DateTime currentTime; - int error_code_index = 0; + int error_code = 0; char _rule_[11] = "ToolkitAPI"; // Check if Open if (swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[LINK]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -2814,7 +2816,7 @@ EXPORT_TOOLKIT int swmm_setLinkSetting(int index, double setting) report_writeControlAction(currentTime, Link[index].ID, setting, _rule_); } } - return error_getCode(error_code_index); + return error_code; } @@ -2825,17 +2827,17 @@ EXPORT_TOOLKIT int swmm_setNodeInflow(int index, double flowrate) /// Output: returns API Error /// Purpose: Sets new node inflow rate and holds until set again { - int error_code_index = 0; + int error_code = 0; // Check if Open if (swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[NODE]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else { @@ -2861,22 +2863,22 @@ EXPORT_TOOLKIT int swmm_setNodeInflow(int index, double flowrate) double baseline = 0.0; // Baseline Inflow Rate // Initializes Inflow Object - error_code_index = inflow_setExtInflow(index, param, type, tSeries, + error_code = inflow_setExtInflow(index, param, type, tSeries, basePat, cf, baseline, sf); // Get The Inflow Object - if ( error_code_index == 0 ) + if ( error_code == 0 ) { inflow = Node[index].extInflow; } } // Assign new flow rate - if ( error_code_index == 0 ) + if ( error_code == 0 ) { inflow -> extIfaceInflow = flowrate; } } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_setOutfallStage(int index, double stage) @@ -2886,32 +2888,32 @@ EXPORT_TOOLKIT int swmm_setOutfallStage(int index, double stage) /// Output: returns API Error /// Purpose: Sets new outfall stage and holds until set again. { - int error_code_index = 0; + int error_code = 0; // Check if Open if (swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if ( index < 0 || index >= Nobjects[NODE] ) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } else if ( Node[index].type != OUTFALL ) { - error_code_index = ERR_API_WRONG_TYPE; + error_code = ERR_TKAPI_WRONG_TYPE; } else { int k = Node[index].subIndex; - if ( Outfall[k].type != STAGED_OUTFALL ) + if ( Outfall[k].type != FIXED_OUTFALL ) { // Change Boundary Conditions Setting Type - Outfall[k].type = STAGED_OUTFALL; + Outfall[k].type = FIXED_OUTFALL; } - Outfall[k].outfallStage = stage / UCF(LENGTH); + Outfall[k].fixedStage = stage / UCF(LENGTH); } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT int swmm_setGagePrecip(int index, double total_precip) @@ -2921,16 +2923,16 @@ EXPORT_TOOLKIT int swmm_setGagePrecip(int index, double total_precip) /// Return: API Error /// Purpose: Sets the precipitation in from the external database { - int error_code_index = 0; + int error_code = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { - error_code_index = ERR_API_INPUTNOTOPEN; + error_code = ERR_TKAPI_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[GAGE]) { - error_code_index = ERR_API_OBJECT_INDEX; + error_code = ERR_TKAPI_OBJECT_INDEX; } // Set the Rainfall rate else @@ -2949,7 +2951,7 @@ EXPORT_TOOLKIT int swmm_setGagePrecip(int index, double total_precip) } Gage[index].externalRain = total_precip; } - return error_getCode(error_code_index); + return error_code; } EXPORT_TOOLKIT void swmm_freeMemory(void *memory) diff --git a/src/solver/toposort.c b/src/solver/toposort.c index 19a92f597..d6c9116cd 100644 --- a/src/solver/toposort.c +++ b/src/solver/toposort.c @@ -2,8 +2,8 @@ // toposort.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Topological sorting of conveyance network links @@ -114,7 +114,10 @@ void toposort_sortLinks(int sortedLinks[]) // --- find number of links entering each node for (i = 0; i < Nobjects[NODE]; i++) InDegree[i] = 0; - for (i = 0; i < Nobjects[LINK]; i++) InDegree[ Link[i].node2 ]++; + for (i = 0; i < Nobjects[LINK]; i++) + { + InDegree[ Link[i].node2 ]++; + } // --- topo sort the links n = topoSort(sortedLinks); @@ -264,7 +267,6 @@ int topoSort(int sortedLinks[]) // --- reduce in-degree of link's downstream node i2 = Link[j].node2; InDegree[i2]--; - // --- add downstream node to stack if its in-degree is zero if ( InDegree[i2] == 0 ) { @@ -290,7 +292,7 @@ void findCycles() int i; // --- allocate arrays - AdjList = (int *) calloc(2*Nobjects[LINK], sizeof(int)); + AdjList = (int *) calloc(2*(size_t)Nobjects[LINK], sizeof(int)); StartPos = (int *) calloc(Nobjects[NODE], sizeof(int)); Stack = (int *) calloc(Nobjects[NODE], sizeof(int)); Examined = (char *) calloc(Nobjects[NODE], sizeof(char)); diff --git a/src/solver/transect.c b/src/solver/transect.c index 2d8fb1cce..831ba017f 100644 --- a/src/solver/transect.c +++ b/src/solver/transect.c @@ -2,11 +2,16 @@ // transect.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Geometry processing for irregular cross-section transects. +// +// Update History +// ============== +// Build 5.2.0: +// - Function added to create a transect for a Street cross-section. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -43,6 +48,7 @@ static double Lfactor; // main channel/flood plain length // transect_delete (called by deleteObjects in project.c) // transect_readParams (called by parseLine in input.c) // transect_validate (called by input_readData) +// transect_createStreetTransect (called by street_readparams) //----------------------------------------------------------------------------- // Local functions @@ -51,10 +57,11 @@ static int setParams(int transect, char* id, double x[]); static int setManning(double n[]); static int addStation(double x, double y); static double getFlow(int k, double a, double wp, int findFlow); -static void getGeometry(int i, int j, double y); +static void createTables(TTransect *transect, double ymin, double ymax); +static void getGeometry(TTransect *transect, int i, double y); static void getSliceGeom(int k, double y, double yu, double yd, double *w, double *a, double *wp); -static void setMaxSectionFactor(int transect); +static void setMaxSectionFactor(TTransect *transect); //============================================================================= @@ -189,8 +196,8 @@ void transect_validate(int j) // Purpose: validates transect data and creates its geometry tables. // { - int i, nLast; - double dy, y, ymin, ymax; + int i; + double ymin, ymax; double oldNchannel = Nchannel; // --- check for valid transect data @@ -234,7 +241,6 @@ void transect_validate(int j) report_writeErrorMsg(ERR_TRANSECT_NO_DEPTH, Transect[j].ID); return; } - Transect[j].yFull = ymax - ymin; // --- add vertical sides to transect to reach full ht. on both ends Station[0] = Station[1]; @@ -243,49 +249,60 @@ void transect_validate(int j) Station[Nstations] = Station[Nstations-1]; Elev[Nstations] = Elev[0]; - // --- determine size & depth increment for geometry tables + // --- create geometry tables Transect[j].nTbl = N_TRANSECT_TBL; - dy = (ymax - ymin) / (double)(Transect[j].nTbl - 1); + createTables(&Transect[j], ymin, ymax); + + // --- save unadjusted main channel roughness + Transect[j].roughness = oldNchannel; +} + +//============================================================================= + +void createTables(TTransect *transect, double ymin, double ymax) +{ + int i, nLast; + double dy, y; + + transect->yFull = ymax - ymin; + transect->wMax = 0.0; // --- set 1st table entries to zero - Transect[j].areaTbl[0] = 0.0; - Transect[j].hradTbl[0] = 0.0; - Transect[j].widthTbl[0] = 0.0; + transect->areaTbl[0] = 0.0; + transect->hradTbl[0] = 0.0; + transect->widthTbl[0] = 0.0; // --- compute geometry for each depth increment + dy = (ymax - ymin) / ((double)(transect->nTbl) - 1); y = ymin; - Transect[j].wMax = 0.0; - for (i = 1; i < Transect[j].nTbl; i++) + for (i = 1; i < transect->nTbl; i++) { y += dy; - Transect[j].areaTbl[i] = 0.0; - Transect[j].hradTbl[i] = 0.0; - Transect[j].widthTbl[i] = 0.0; - getGeometry(i, j, y); + transect->areaTbl[i] = 0.0; + transect->hradTbl[i] = 0.0; + transect->widthTbl[i] = 0.0; + getGeometry(transect, i, y); } // --- determine max. section factor - setMaxSectionFactor(j); + setMaxSectionFactor(transect); // --- normalize geometry table entries // (full cross-section values are last table entries) - nLast = Transect[j].nTbl - 1; - Transect[j].aFull = Transect[j].areaTbl[nLast]; - Transect[j].rFull = Transect[j].hradTbl[nLast]; - Transect[j].wMax = Transect[j].widthTbl[nLast]; + nLast = transect->nTbl - 1; + transect->aFull = transect->areaTbl[nLast]; + transect->rFull = transect->hradTbl[nLast]; + transect->wMax = transect->widthTbl[nLast]; for (i = 1; i <= nLast; i++) { - Transect[j].areaTbl[i] /= Transect[j].aFull; - Transect[j].hradTbl[i] /= Transect[j].rFull; - Transect[j].widthTbl[i] /= Transect[j].wMax; + transect->areaTbl[i] /= transect->aFull; + transect->hradTbl[i] /= transect->rFull; + transect->widthTbl[i] /= transect->wMax; } // --- set width at 0 height equal to width at 4% of max. height - Transect[j].widthTbl[0] = Transect[j].widthTbl[1]; - - // --- save unadjusted main channel roughness - Transect[j].roughness = oldNchannel; + transect->widthTbl[0] = transect->widthTbl[1]; } //============================================================================= @@ -366,10 +383,10 @@ int addStation(double y, double x) //============================================================================= -void getGeometry(int i, int j, double y) +void getGeometry(TTransect *transect, int i, double y) // -// Input: i = index of current entry in geometry tables -// j = transect index +// Input: transect = transect being analyzed +// i = index of current entry in geometry tables // y = depth of current entry in geometry tables // Output: none // Purpose: computes entries in a transect's geometry tables at a given depth. @@ -417,8 +434,8 @@ void getGeometry(int i, int j, double y) // --- update total transect values wpSum += wp; aSum += a; - Transect[j].areaTbl[i] += a; - Transect[j].widthTbl[i] += w; + transect->areaTbl[i] += a; + transect->widthTbl[i] += w; // --- must update flow if station elevation is above water level if ( Elev[k] >= y ) findFlow = TRUE; @@ -437,9 +454,11 @@ void getGeometry(int i, int j, double y) // --- find hyd. radius table entry solving Manning eq. with // total flow, total area, and main channel n - aSum = Transect[j].areaTbl[i]; - if ( aSum == 0.0 ) Transect[j].hradTbl[i] = Transect[j].hradTbl[i-1]; - else Transect[j].hradTbl[i] = pow(qSum * Nchannel / 1.49 / aSum, 1.5); + aSum = transect->areaTbl[i]; + if ( aSum == 0.0 ) + transect->hradTbl[i] = transect->hradTbl[i-1]; + else + transect->hradTbl[i] = pow(qSum * Nchannel / 1.49 / aSum, 1.5); } //============================================================================= @@ -538,6 +557,7 @@ double getFlow(int k, double a, double wp, int findFlow) if ( Station[k] > Xrightbank ) n = Nright; // --- compute flow through flow area + // (PHI is the Manning Eqn. constant defined in consts.h) return PHI / n * a * pow(a/wp, 2./3.); } return 0.0; @@ -545,9 +565,9 @@ double getFlow(int k, double a, double wp, int findFlow) //============================================================================= -void setMaxSectionFactor(int j) +void setMaxSectionFactor(TTransect *transect) // -// Input: j = transect index +// Input: transect = transect being analyzed // Output: none // Purpose: determines the maximum section factor for a transect and the // area where this maxumum occurs. @@ -556,17 +576,103 @@ void setMaxSectionFactor(int j) int i; double sf; - Transect[j].aMax = 0.0; - Transect[j].sMax = 0.0; - for (i=1; iaMax = 0.0; + transect->sMax = 0.0; + for (i = 1; i < transect->nTbl; i++) { - sf = Transect[j].areaTbl[i] * pow(Transect[j].hradTbl[i], 2./3.); - if ( sf > Transect[j].sMax ) + sf = transect->areaTbl[i] * pow(transect->hradTbl[i], 2. / 3.); + if (sf > transect->sMax) { - Transect[j].sMax = sf; - Transect[j].aMax = Transect[j].areaTbl[i]; + transect->sMax = sf; + transect->aMax = transect->areaTbl[i]; } } } //============================================================================= + +void transect_createStreetTransect(TStreet* street) +// +{ + double ymin, ymax, y1, y3, y4; + double w1, w2, w3, w4; + + // Point 0 = top of backing + // Point 1 = top of curb + // Point 2 = bottom of curb + // Point 3 = bottom of depressed gutter + // Point 4 = top of depressed gutter + // Point 5 = street crown + + // --- assign height (y) and width (w) to road & gutter sections + ymin = 0.0; + w1 = street->backWidth; + w2 = street->gutterWidth; + w3 = street->width; + w4 = w3 - w2; + y3 = street->gutterDepression; + y1 = street->curbHeight + y3; + ymax = street->backSlope * street->backWidth + y1; + y4 = y3 + street->slope * w4; + ymax = MAX(ymax, y4); + + // --- assign Station,Elevation points to the street's sections + Station[0] = 0.0; + Elev[0] = ymax; + Station[1] = w1; + Elev[1] = y1; + Station[2] = w1; + Elev[2] = 0.0; + Station[3] = w1 + w2; + Elev[3] = y3; + Station[4] = w1 + w3; + Elev[4] = y4; + + // --- a half street ends here + if (street->sides == 1) + { + Station[5] = Station[4]; + Elev[5] = ymax; + Nstations = 5; + street->transect.nTbl = N_TRANSECT_TBL; + } + + // --- the right side of a full street mirrors the left side + else + { + Station[5] = Station[4] + w4; + Elev[5] = y3; + Station[6] = Station[5] + w2; + Elev[6] = 0.0; + Station[7] = Station[6]; + Elev[7] = y1; + Station[8] = Station[7] + w1; + Elev[8] = ymax; + Nstations = 8; + street->transect.nTbl = N_TRANSECT_TBL; + } + + // --- assign Manning's N to street + Nchannel = street->roughness; + if (street->backWidth == 0.0) + { + Nleft = Nchannel; + Nright = Nchannel; + Xleftbank = Station[0]; + Xrightbank = Station[Nstations]; + } + else + { + Nleft = street->backRoughness; + Nright = Nleft; + Xleftbank = Station[1]; + if (street->sides == 2) + Xrightbank = Station[Nstations - 1]; + else + Xrightbank = Station[Nstations]; + } + + // --- create the street's geometry tables + createTables(&(street->transect), ymin, ymax); + street->transect.roughness = street->roughness; +} diff --git a/src/solver/treatmnt.c b/src/solver/treatmnt.c index 1b1491e49..778cf4d6f 100644 --- a/src/solver/treatmnt.c +++ b/src/solver/treatmnt.c @@ -2,16 +2,18 @@ // treatmnt.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 03/19/15 (Build 5.1.008) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Pollutant treatment functions. // +// Update History +// ============== // Build 5.1.008: // - A bug in evaluating recursive calls to treatment functions was fixed. -// +// Build 5.2.0: +// - Changed enumerated constant used to indicate a math expression error. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -119,11 +121,11 @@ int treatmnt_readExpression(char* tok[], int ntoks) if ( p < 0 ) return error_setInpError(ERR_NAME, tok[1]); // --- concatenate remaining tokens into a single string - strcpy(s, tok[2]); + sstrncpy(s, tok[2], MAXLINE); for ( i=3; iequation == NULL ) R[p] = 0.0; // --- no removal for removal-type expression when there is no inflow - else if ( treatment->treatType == REMOVAL && q <= ZERO ) R[p] = 0.0; + else if ( treatment->treatType == REMOVAL && q <= ZERO ) R[p] = 0.0; - // --- check for external treatment + // OWA EDIT --- check for external treatment, if so internal pollutant removal is set to zero else if ( Node[j].extPollutFlag[p] == 1) R[p] = 0.0; // --- otherwise evaluate the treatment expression to find R[p] @@ -241,15 +247,16 @@ void treatmnt_treat(int j, double q, double v, double tStep) { report_writeErrorMsg(ERR_CYCLIC_TREATMENT, Node[J].ID); } - + // --- update nodal concentrations and mass balances else for ( p = 0; p < Nobjects[POLLUT]; p++ ) { + // OWA EDIT - output conc. can still change if using external treatment if ( R[p] == 0.0 && Node[j].extPollutFlag[p] != 1) continue; treatment = &Node[j].treatment[p]; // --- removal-type treatment equations get applied to inflow stream - + // OWA EDIT - ignore internal treatment calcs if using extenal treatment if ( treatment->treatType == REMOVAL && Node[j].extPollutFlag[p] != 1) { // --- if no pollutant in inflow then cOut is current nodal concen. @@ -263,11 +270,11 @@ void treatmnt_treat(int j, double q, double v, double tStep) cOut = MIN(cOut, Node[j].newQual[p]); } - // --- water quality set externally - else if( Node[j].extPollutFlag[p] == 1) - { - cOut = Node[j].extQual[p]; - } + // OWA EDIT --- water quality set externally + else if( Node[j].extPollutFlag[p] == 1) + { + cOut = Node[j].extQual[p]; + } // --- concentration-type equations get applied to nodal concentration else @@ -275,18 +282,18 @@ void treatmnt_treat(int j, double q, double v, double tStep) cOut = (1.0 - R[p]) * Node[j].newQual[p]; } - // --- store inflow concentration for the timestep - Node[j].inQual[p] = Cin[p]; + // OWA EDIT --- store inflow concentration for the timestep + Node[j].inQual[p] = Cin[p]; - // --- mass lost must account for any initial mass in storage + // --- mass lost must account for any initial mass in storage massLost = (Cin[p]*q*tStep + Node[j].oldQual[p]*Node[j].oldVolume - - cOut*(q*tStep + Node[j].oldVolume)) / tStep; + cOut*(q*tStep + Node[j].oldVolume)) / tStep; - // --- mass can be gained in external treatment - if (Node[j].extPollutFlag[p] != 1) massLost = MAX(0.0, massLost); + // OWA EDIT --- mass can be gained in external treatment + if (Node[j].extPollutFlag[p] != 1) massLost = MAX(0.0, massLost); - // --- reset the flag to default to swmm treatment - if (Node[j].extPollutFlag[p] == 1) Node[j].extPollutFlag[p] = 0; + // OWA EDIT --- reset the flag to default to swmm treatment + if (Node[j].extPollutFlag[p] == 1) Node[j].extPollutFlag[p] = 0; // --- add mass loss to mass balance totals and revise nodal concentration massbal_addReactedMass(p, massLost); @@ -465,5 +472,3 @@ double getRemoval(int p) } return R[p]; } - -//============================================================================= diff --git a/src/solver/xsect.c b/src/solver/xsect.c index 9a4cc6dab..5ebab53c0 100644 --- a/src/solver/xsect.c +++ b/src/solver/xsect.c @@ -2,11 +2,9 @@ // xsect.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // M. Tryby (EPA) // // Cross section geometry functions. @@ -26,11 +24,14 @@ // R = hyd. radius // S = section factor = A*R^(2/3) // +// Update History +// ============== // Build 5.1.012: // - Height at max. width for Modified Baskethandle shape corrected. -// // Build 5.1.013: // - Width at full height set to 0 for closed rectangular shape. +// Build 5.2.0: +// - Support added for Street cross sections. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -74,7 +75,8 @@ double Amax[] = { 0.96, // SEMICIRCULAR 1.0, // IRREGULAR 0.96, // CUSTOM - 0.9756}; // FORCE_MAIN + 0.9756, // FORCE_MAIN + 1.0}; // STREET_XSECT //----------------------------------------------------------------------------- // Shared variables @@ -92,6 +94,7 @@ typedef struct // xsect_isOpen // xsect_setParams // xsect_setIrregXsectParams +// xsect_setStreetXsectParams // xsect_setCustomXsectParams // xsect_getAmax // xsect_getSofA @@ -107,6 +110,8 @@ typedef struct //----------------------------------------------------------------------------- // Local functions //----------------------------------------------------------------------------- +static void getTransectParams(TXsect *xsect, TTransect *transect); + static double generic_getAofS(TXsect* xsect, double s); static void evalSofA(double a, double* f, double* df, void* p); static double tabular_getdSdA(TXsect* xsect, double a, double *table, int nItems); @@ -210,7 +215,7 @@ int xsect_setParams(TXsect *xsect, int type, double p[], double ucf) // // Input: xsect = ptr. to a cross section data structure // type = xsection shape type -// p[] = vector or xsection parameters +// p[] = vector of xsection parameters // ucf = units correction factor // Output: returns TRUE if successful, FALSE if not // Purpose: assigns parameters to a cross section's data structure. @@ -496,7 +501,7 @@ int xsect_setParams(TXsect *xsect, int type, double p[], double ucf) case TRIANGULAR: if ( p[1] <= 0.0 ) return FALSE; xsect->yFull = p[0]/ucf; - xsect->wMax = p[1]/ucf; + xsect->wMax = p[1]/ucf; xsect->ywMax = xsect->yFull; // --- slope of side walls @@ -634,30 +639,20 @@ void xsect_setIrregXsectParams(TXsect *xsect) // { int index = xsect->transect; - int i, iMax; - double wMax; - double* wTbl = Transect[index].widthTbl; - - xsect->yFull = Transect[index].yFull; - xsect->wMax = Transect[index].wMax; - xsect->aFull = Transect[index].aFull; - xsect->rFull = Transect[index].rFull; - xsect->sFull = xsect->aFull * pow(xsect->rFull, 2./3.); - xsect->sMax = Transect[index].sMax; - xsect->aBot = Transect[index].aMax; + getTransectParams(xsect, &Transect[index]); +} - // Search transect's width table up to point where width decreases - iMax = 0; - wMax = wTbl[0]; - for (i = 1; i < N_TRANSECT_TBL; i++) - { - if ( wTbl[i] < wMax ) break; - wMax = wTbl[i]; - iMax = i; - } +//============================================================================= - // Determine height at lowest widest point - xsect->ywMax = xsect->yFull * (double)iMax / (double)(N_TRANSECT_TBL-1); +void xsect_setStreetXsectParams(TXsect *xsect) +// +// Input: xsect = ptr. to a cross section data structure +// Output: none +// Purpose: assigns transect parameters to a street cross section. +// +{ + int index = xsect->transect; + getTransectParams(xsect, &Street[index].transect); } //============================================================================= @@ -687,9 +682,9 @@ void xsect_setCustomXsectParams(TXsect *xsect) wMax = wTbl[0]; for (i = 1; i < N_SHAPE_TBL; i++) { - if ( wTbl[i] < wMax ) break; - wMax = wTbl[i]; - iMax = i; + if ( wTbl[i] < wMax ) break; + wMax = wTbl[i]; + iMax = i; } // Determine height at lowest widest point @@ -823,6 +818,11 @@ double xsect_getYofA(TXsect *xsect, double a) return xsect->yFull * invLookup(alpha, Shape[Curve[xsect->transect].refersTo].areaTbl, N_SHAPE_TBL); + case STREET_XSECT: + return xsect->yFull * invLookup(alpha, + Street[xsect->transect].transect.areaTbl, + Street[xsect->transect].transect.nTbl); + case ARCH: return xsect->yFull * invLookup(alpha, A_Arch, N_A_Arch); @@ -907,6 +907,11 @@ double xsect_getAofY(TXsect *xsect, double y) return xsect->aFull * lookup(yNorm, Shape[Curve[xsect->transect].refersTo].areaTbl, N_SHAPE_TBL); + case STREET_XSECT: + return xsect->aFull * lookup(yNorm, + Street[xsect->transect].transect.areaTbl, + Street[xsect->transect].transect.nTbl); + case RECT_CLOSED: return y * xsect->wMax; case RECT_TRIANG: return rect_triang_getAofY(xsect, y); @@ -988,8 +993,13 @@ double xsect_getWofY(TXsect *xsect, double y) return xsect->wMax * lookup(yNorm, Shape[Curve[xsect->transect].refersTo].widthTbl, N_SHAPE_TBL); + case STREET_XSECT: + return xsect->wMax * lookup(yNorm, + Street[xsect->transect].transect.widthTbl, + Street[xsect->transect].transect.nTbl); + case RECT_CLOSED: - if (yNorm == 1.0) return 0.0; //(5.1.013) + if (yNorm == 1.0) return 0.0; return xsect->wMax; case RECT_TRIANG: return rect_triang_getWofY(xsect, y); @@ -1060,6 +1070,11 @@ double xsect_getRofY(TXsect *xsect, double y) return xsect->rFull * lookup(yNorm, Shape[Curve[xsect->transect].refersTo].hradTbl, N_SHAPE_TBL); + case STREET_XSECT: + return xsect->rFull * lookup(yNorm, + Street[xsect->transect].transect.hradTbl, + Street[xsect->transect].transect.nTbl); + case RECT_TRIANG: return rect_triang_getRofY(xsect, y); case RECT_ROUND: return rect_round_getRofY(xsect, y); @@ -1096,6 +1111,7 @@ double xsect_getRofA(TXsect *xsect, double a) case IRREGULAR: case FILLED_CIRCULAR: case CUSTOM: + case STREET_XSECT: return xsect_getRofY( xsect, xsect_getYofA(xsect, a) ); case RECT_CLOSED: return rect_closed_getRofA(xsect, a); @@ -1214,19 +1230,19 @@ double xsect_getdSdA(TXsect* xsect, double a) return rect_open_getdSdA(xsect, a); case RECT_TRIANG: - return rect_triang_getdSdA(xsect, a); + return rect_triang_getdSdA(xsect, a); case RECT_ROUND: - return rect_round_getdSdA(xsect, a); + return rect_round_getdSdA(xsect, a); case MOD_BASKET: - return mod_basket_getdSdA(xsect, a); + return mod_basket_getdSdA(xsect, a); case TRAPEZOIDAL: - return trapez_getdSdA(xsect, a); + return trapez_getdSdA(xsect, a); case TRIANGULAR: - return triang_getdSdA(xsect, a); + return triang_getdSdA(xsect, a); default: return generic_getdSdA(xsect, a); } @@ -1300,6 +1316,42 @@ double xsect_getYcrit(TXsect* xsect, double q) //============================================================================= +void getTransectParams(TXsect *xsect, TTransect *transect) +// +// Input: xsect = ptr. to a cross section data structure +// transect = ptr. to a transect data structure +// Output: none +// Purpose: gets a cross section's properties from its transect's properties. +// +{ + int i, iMax; + double wMax; + double* wTbl = transect->widthTbl; + + xsect->yFull = transect->yFull; + xsect->wMax = transect->wMax; + xsect->aFull = transect->aFull; + xsect->rFull = transect->rFull; + xsect->sFull = xsect->aFull * pow(xsect->rFull, 2. / 3.); + xsect->sMax = transect->sMax; + xsect->aBot = transect->aMax; + + // Search transect's width table up to point where width decreases + iMax = 0; + wMax = wTbl[0]; + for (i = 1; i < transect->nTbl; i++) + { + if (wTbl[i] < wMax) break; + wMax = wTbl[i]; + iMax = i; + } + + // Determine height at lowest widest point + xsect->ywMax = xsect->yFull * (double)iMax / ((double)(transect->nTbl) - 1); +} + +//============================================================================= + double generic_getAofS(TXsect* xsect, double s) // // Input: xsect = ptr. to a cross section data structure @@ -1378,7 +1430,7 @@ double tabular_getdSdA(TXsect* xsect, double a, double *table, int nItems) { int i; double alpha = a / xsect->aFull; - double delta = 1.0 / (nItems-1); + double delta = 1.0 / ((double)nItems-1); double dSdA; // --- find which segment of table contains alpha @@ -1428,13 +1480,13 @@ double lookup(double x, double *table, int nItems) int i; // --- find which segment of table contains x - delta = 1.0 / (nItems-1); + delta = 1.0 / ((double)nItems-1); i = (int)(x / delta); if ( i >= nItems - 1 ) return table[nItems-1]; // --- compute x at start and end of segment x0 = i * delta; - x1 = (i+1) * delta; + x1 = ((double)i+1) * delta; // --- linearly interpolate a y-value y = table[i] + (x - x0) * (table[i+1] - table[i]) / delta; @@ -1480,7 +1532,7 @@ double invLookup(double y, double *table, int nItems) int i; // lower table index that brackets y // --- compute table's uniform x-increment - dx = 1.0 / (double)(nItems-1); + dx = 1.0 / (double)((double)nItems-1); // --- truncate item count if last 2 table entries are decreasing n = nItems; @@ -1489,14 +1541,14 @@ double invLookup(double y, double *table, int nItems) // --- check if y falls in decreasing portion of table if ( n < nItems && y > table[nItems-1]) { - if ( y >= table[nItems-3] ) return (n-1) * dx; + if ( y >= table[nItems-3] ) return ((double)n-1) * dx; if ( y <= table[nItems-2] ) i = nItems - 2; else i = nItems - 3; } // --- otherwise locate the interval where y falls in the table else i = locate(y, table, n-1); - if ( i >= n - 1 ) return (n-1) * dx; + if ( i >= n - 1 ) return ((double)n-1) * dx; // --- compute x at start and end of segment x0 = i * dx; @@ -1537,13 +1589,13 @@ int locate(double y, double *table, int jLast) // While a portion of the table still remains while ( j2 - j1 > 1) { - // Find midpoint of remaining portion of table + // Find midpoint of remaining portion of table j = (j1 + j2) >> 1; - // Value is greater or equal to midpoint: search from midpoint to j2 + // Value is greater or equal to midpoint: search from midpoint to j2 if ( y >= table[j] ) j1 = j; - // Value is less than midpoint: search from j1 to midpoint + // Value is less than midpoint: search from j1 to midpoint else j2 = j; } @@ -1611,7 +1663,7 @@ double getYcritEnum(TXsect* xsect, double q, double y0) qc = getQcritical(i*dy, &xsectStar); if ( qc >= q ) { - yc = ( (q-q0) / (qc - q0) + (double)(i-1) ) * dy; + yc = ( (q-q0) / (qc - q0) + ((double)i-1) ) * dy; break; } q0 = qc; @@ -2381,7 +2433,7 @@ double circ_getdSdA(TXsect* xsect, double a) if ( a1 < 0.0 ) { a1 = 0.0; - da = alpha + 0.001; + da = alpha + 0.001; } s1 = getScircular(a1); s2 = getScircular(a2); @@ -2560,5 +2612,3 @@ double getThetaOfPsi(double psi) } return theta1; } - -//============================================================================= diff --git a/src/solver/xsect.dat b/src/solver/xsect.dat index b154ea4e5..fa199bb40 100644 --- a/src/solver/xsect.dat +++ b/src/solver/xsect.dat @@ -2,8 +2,8 @@ // xsection.dat // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) +// Version: 5.2 +// Date: 03/24/21 (Build 5.2.0) // Author: L. Rossman // // Tables of relative geometric properties for rounded cross-sections. diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ebb0f397b..ccc4d917e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,8 +1,8 @@ # # CMakeLists.txt - CMake configuration file for swmm-solver/tests # -# Created: Jul 7, 2020 -# Updated: Jan 14, 2021 +# Created: Mar 4, 2020 +# Updated: May 21, 2020 # # Author: Michael E. Tryby # US EPA ORD/CESER diff --git a/tests/solver/test_canonical.cpp b/tests/solver/test_canonical.cpp index fa68bdedc..6bf452160 100644 --- a/tests/solver/test_canonical.cpp +++ b/tests/solver/test_canonical.cpp @@ -150,8 +150,8 @@ BOOST_FIXTURE_TEST_CASE(TStoRpt, FixtureBeforeStart) { BOOST_FIXTURE_TEST_CASE(FailStart403, FixtureBeforeStart) { double elapsedTime = 0.0; int error = swmm_step(&elapsedTime); - BOOST_CHECK_EQUAL(403, error); - BOOST_REQUIRE(error == 403); + BOOST_CHECK_EQUAL(502, error); + BOOST_REQUIRE(error == 502); } diff --git a/tests/solver/test_lid.cpp b/tests/solver/test_lid.cpp index ca41a1454..cc1319d7a 100644 --- a/tests/solver/test_lid.cpp +++ b/tests/solver/test_lid.cpp @@ -21,11 +21,11 @@ #define ERR_NONE 0 -#define ERR_API_OUTBOUNDS 501 -#define ERR_API_INPUTNOTOPEN 502 -#define ERR_API_SIM_NRUNNING 503 -#define ERR_API_OBJECT_INDEX 505 -#define ERR_API_UNDEFINED_LID 511 +#define ERR_TKAPI_OUTBOUNDS 2000 +#define ERR_TKAPI_INPUTNOTOPEN 2001 +#define ERR_TKAPI_SIM_NRUNNING 2002 +#define ERR_TKAPI_OBJECT_INDEX 2004 +#define ERR_TKAPI_UNDEFINED_LID 2010 using namespace std; @@ -112,31 +112,31 @@ BOOST_AUTO_TEST_SUITE(test_lid_toolkitapi) //Lid Control error = swmm_getLidCOverflow(0, &int_value); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_getLidCParam(0, SM_SURFACE, SM_THICKNESS, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_setLidCParam(0, SM_SURFACE, SM_THICKNESS, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); //Lid Unit error = swmm_getLidUCount(0, &int_value); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_getLidUParam(0, 0, SM_UNITAREA, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_setLidUParam(0, 0, SM_UNITAREA, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_getLidUOption(0, 0, SM_INDEX, &int_value); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_setLidUOption(0, 0, SM_INDEX, int_value); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_getLidUFluxRates(0, 0, SM_SURFACE, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_getLidUResult(0, 0, SM_INFLOW, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); //Lid Group error = swmm_getLidGResult(0, SM_INFLOW, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); } BOOST_AUTO_TEST_SUITE_END() @@ -161,65 +161,65 @@ BOOST_AUTO_TEST_SUITE(test_lid_toolkitapi_fixture) //Lid Surface error = swmm_setLidCParam(0, SM_SURFACE, SM_THICKNESS, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_SURFACE, SM_VOIDFRAC, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_SURFACE, SM_ROUGHNESS, db_value); BOOST_CHECK_EQUAL(error, ERR_NONE); error = swmm_setLidCParam(0, SM_SURFACE, SM_SURFSLOPE, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_SURFACE, SM_SIDESLOPE, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_SURFACE, SM_ALPHA, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); //Lid Soil error = swmm_setLidCParam(0, SM_SOIL, SM_THICKNESS, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_SOIL, SM_POROSITY, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_SOIL, SM_FIELDCAP, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_SOIL, SM_WILTPOINT, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_SOIL, SM_SUCTION, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_SOIL, SM_KSAT, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_SOIL, SM_KSLOPE, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_SOIL, SM_CLOGFACTOR, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); //Lid Storage error = swmm_setLidCParam(0, SM_STOR, SM_THICKNESS, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_STOR, SM_VOIDFRAC, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_STOR, SM_KSAT, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_STOR, SM_CLOGFACTOR, db_value); BOOST_CHECK_EQUAL(error, ERR_NONE); error = swmm_setLidCParam(0, SM_STOR, SM_ROUGHNESS, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); //Lid Pavement error = swmm_setLidCParam(0, SM_PAVE, SM_THICKNESS, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_PAVE, SM_VOIDFRAC, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_PAVE, SM_IMPERVFRAC, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_PAVE, SM_KSAT, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_PAVE, SM_CLOGFACTOR, db_value); BOOST_CHECK_EQUAL(error, ERR_NONE); error = swmm_setLidCParam(0, SM_PAVE, SM_REGENDAYS, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_PAVE, SM_REGENDEGREE, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_PAVE, SM_WILTPOINT, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); //Lid Drain error = swmm_setLidCParam(0, SM_DRAIN, SM_COEFF, db_value); @@ -235,17 +235,17 @@ BOOST_AUTO_TEST_SUITE(test_lid_toolkitapi_fixture) error = swmm_setLidCParam(0, SM_DRAIN, SM_HCLOSE, db_value); BOOST_CHECK_EQUAL(error, ERR_NONE); error = swmm_setLidCParam(0, SM_DRAIN, SM_CLOGFACTOR, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); //Lid DrainMat error = swmm_setLidCParam(0, SM_DRAINMAT, SM_THICKNESS, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_DRAINMAT, SM_VOIDFRAC, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidCParam(0, SM_DRAINMAT, SM_ROUGHNESS, db_value); BOOST_CHECK_EQUAL(error, ERR_NONE); error = swmm_setLidCParam(0, SM_DRAINMAT, SM_CLOGFACTOR, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); //Lid Unit error = swmm_getLidUCount(0, &int_value); @@ -253,15 +253,15 @@ BOOST_AUTO_TEST_SUITE(test_lid_toolkitapi_fixture) error = swmm_getLidUParam(0, 0, SM_UNITAREA, &db_value); BOOST_CHECK_EQUAL(error, ERR_NONE); error = swmm_setLidUParam(0, 0, SM_UNITAREA, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_getLidUOption(0, 0, SM_INDEX, &int_value); BOOST_CHECK_EQUAL(error, ERR_NONE); error = swmm_setLidUOption(0, 0, SM_INDEX, int_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidUOption(0, 0, SM_NUMBER, int_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidUOption(0, 0, SM_TOPERV, int_value); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLidUOption(0, 0, SM_DRAINSUB, int_value); BOOST_CHECK_EQUAL(error, ERR_NONE); error = swmm_setLidUOption(0, 0, SM_DRAINNODE, int_value); @@ -288,31 +288,31 @@ BOOST_AUTO_TEST_SUITE(test_lid_toolkitapi_fixture) //Lid Control error = swmm_getLidCOverflow(1, &int_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_getLidCParam(1, SM_SURFACE, SM_THICKNESS, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_setLidCParam(1, SM_SURFACE, SM_THICKNESS, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); //Lid Unit error = swmm_getLidUCount(2, &int_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_getLidUParam(2, 0, SM_UNITAREA, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_setLidUParam(2, 0, SM_UNITAREA, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_getLidUOption(2, 0, SM_INDEX, &int_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_setLidUOption(2, 0, SM_INDEX, int_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_getLidUFluxRates(2, 0, SM_SURFACE, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_getLidUResult(2, 0, SM_INFLOW, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); //Lid Group error = swmm_getLidGResult(2, SM_PERVAREA, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); } // Testing for invalid parameter key @@ -326,31 +326,31 @@ BOOST_AUTO_TEST_SUITE(test_lid_toolkitapi_fixture) //Lid Control error = swmm_getLidCParam(0, SM_SURFACE, static_cast(100), &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_setLidCParam(0, SM_SURFACE, static_cast(100), db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_getLidCParam(0, static_cast(100), SM_THICKNESS, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_setLidCParam(0, static_cast(100), SM_THICKNESS, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); //Lid Unit error = swmm_getLidUParam(0, 0, static_cast(100), &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_setLidUParam(0, 0, static_cast(100), db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_getLidUOption(0, 0, static_cast(100), &int_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_setLidUOption(0, 0, static_cast(100), int_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_getLidUFluxRates(0, 0, static_cast(100), &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_getLidUResult(0, 0, static_cast(100), &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); //Lid Group error = swmm_getLidGResult(0, static_cast(100), &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); } // Testing for undefined lid usage @@ -362,13 +362,13 @@ BOOST_AUTO_TEST_SUITE(test_lid_toolkitapi_fixture) //Lid Unit error = swmm_getLidUResult(0, 1, SM_INFLOW, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_UNDEFINED_LID); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_UNDEFINED_LID); error = swmm_getLidUFluxRates(0, 1, SM_SURFACE, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_UNDEFINED_LID); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_UNDEFINED_LID); //Lid Group error = swmm_getLidGResult(1, SM_PERVAREA, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_UNDEFINED_LID); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_UNDEFINED_LID); } // Testing for Project Settings after Open @@ -2329,31 +2329,31 @@ BOOST_AUTO_TEST_SUITE(test_lid_toolkitapi_fixture) //Lid Control error = swmm_getLidCOverflow(1, &int_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_getLidCParam(1, SM_SURFACE, SM_THICKNESS, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_setLidCParam(1, SM_SURFACE, SM_THICKNESS, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); //Lid Unit error = swmm_getLidUCount(2, &int_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_getLidUParam(2, 0, SM_UNITAREA, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_setLidUParam(2, 0, SM_UNITAREA, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_getLidUOption(2, 0, SM_INDEX, &int_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_setLidUOption(2, 0, SM_INDEX, int_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_getLidUFluxRates(2, 0, SM_SURFACE, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_getLidUResult(2, 0, SM_INFLOW, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); //Lid Group error = swmm_getLidGResult(2, SM_PERVAREA, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); } // Testing for invalid parameter key @@ -2367,27 +2367,27 @@ BOOST_AUTO_TEST_SUITE(test_lid_toolkitapi_fixture) //Lid Control error = swmm_getLidCParam(0, SM_SURFACE, static_cast(100), &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_setLidCParam(0, SM_SURFACE, static_cast(100), db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_getLidCParam(0, static_cast(100), SM_THICKNESS, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_setLidCParam(0, static_cast(100), SM_THICKNESS, db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); //Lid Unit error = swmm_getLidUParam(0, 0, static_cast(100), &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_getLidUOption(0, 0, static_cast(100), &int_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_getLidUFluxRates(0, 0, static_cast(100), &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_getLidUResult(0, 0, static_cast(100), &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); //Lid Group error = swmm_getLidGResult(0, static_cast(100), &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); } // Testing for undefined lid usage @@ -2399,13 +2399,13 @@ BOOST_AUTO_TEST_SUITE(test_lid_toolkitapi_fixture) //Lid Unit error = swmm_getLidUFluxRates(0, 1, SM_SURFACE, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_UNDEFINED_LID); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_UNDEFINED_LID); error = swmm_getLidUResult(0, 1, SM_INFLOW, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_UNDEFINED_LID); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_UNDEFINED_LID); //Lid Group error = swmm_getLidGResult(1, SM_PERVAREA, &db_value); - BOOST_CHECK_EQUAL(error, ERR_API_UNDEFINED_LID); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_UNDEFINED_LID); } BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/solver/test_stats.cpp b/tests/solver/test_stats.cpp index ca41c8090..b5b632d1a 100644 --- a/tests/solver/test_stats.cpp +++ b/tests/solver/test_stats.cpp @@ -17,8 +17,8 @@ #include "test_solver.hpp" #define ERR_NONE 0 -#define ERR_API_MEMORY 512 -#define ERR_API_WRONG_TYPE 504 +#define ERR_TKAPI_MEMORY 2011 +#define ERR_TKAPI_WRONG_TYPE 2003 using namespace std; @@ -39,7 +39,7 @@ BOOST_FIXTURE_TEST_CASE(get_node_stats, FixtureBeforeEnd){ // Test argument checks error = swmm_getNodeStats(index, s); - BOOST_CHECK_EQUAL(error, ERR_API_MEMORY); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_MEMORY); } @@ -54,7 +54,7 @@ BOOST_FIXTURE_TEST_CASE(get_storage_stats, FixtureBeforeEnd){ // Test argument checks error = swmm_getStorageStats(index, s); - BOOST_CHECK_EQUAL(error, ERR_API_WRONG_TYPE); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_WRONG_TYPE); //TODO: Example 1 has no storage nodes } @@ -74,7 +74,7 @@ BOOST_FIXTURE_TEST_CASE(get_outfall_stats, FixtureBeforeEnd){ // Test argument checks error = swmm_getOutfallStats(outfall_index, s); - BOOST_CHECK_EQUAL(error, ERR_API_MEMORY); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_MEMORY); int num_pollut; error = swmm_countObjects(SM_POLLUT, &num_pollut); @@ -110,7 +110,7 @@ BOOST_FIXTURE_TEST_CASE(get_link_stats, FixtureBeforeEnd){ // Test argument checks error = swmm_getLinkStats(index, s); - BOOST_CHECK_EQUAL(error, ERR_API_MEMORY); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_MEMORY); } @@ -128,7 +128,7 @@ BOOST_FIXTURE_TEST_CASE(get_pump_stats, FixtureBeforeEnd){ // Test argument checks error = swmm_getPumpStats(index, s); - BOOST_CHECK_EQUAL(error, ERR_API_WRONG_TYPE); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_WRONG_TYPE); //TODO: Example 1 has no pumps } @@ -147,7 +147,7 @@ BOOST_FIXTURE_TEST_CASE(get_subcatch_stats, FixtureBeforeEnd){ BOOST_REQUIRE(error == ERR_NONE); error = swmm_getSubcatchStats(index, subc_stats); - BOOST_CHECK_EQUAL(error, ERR_API_MEMORY); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_MEMORY); // BOOST_CHECK_SMALL(subc_stats->runon - 0.0, 0.0001); // BOOST_CHECK_SMALL(subc_stats->infil - 42088, 1.0); @@ -164,7 +164,7 @@ BOOST_FIXTURE_TEST_CASE(get_routing_totals, FixtureBeforeEnd){ SM_RoutingTotals *s = NULL; error = swmm_getSystemRoutingTotals(s); - BOOST_CHECK_EQUAL(error, ERR_API_MEMORY); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_MEMORY); SM_RoutingTotals __rtots; SM_RoutingTotals *_rtots = &__rtots; @@ -183,7 +183,7 @@ BOOST_FIXTURE_TEST_CASE(get_runoff_totals, FixtureBeforeEnd){ SM_RunoffTotals *s = NULL; error = swmm_getSystemRunoffTotals(s); - BOOST_CHECK_EQUAL(error, ERR_API_MEMORY); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_MEMORY); SM_RunoffTotals __rtots; SM_RunoffTotals *_rtots = &__rtots; diff --git a/tests/solver/test_toolkit.cpp b/tests/solver/test_toolkit.cpp index 6ffd015c6..5138c666d 100644 --- a/tests/solver/test_toolkit.cpp +++ b/tests/solver/test_toolkit.cpp @@ -17,11 +17,11 @@ #include "test_solver.hpp" #define ERR_NONE 0 -#define ERR_API_OUTBOUNDS 501 -#define ERR_API_INPUTNOTOPEN 502 -#define ERR_API_SIM_NRUNNING 503 -#define ERR_API_WRONG_TYPE 504 -#define ERR_API_OBJECT_INDEX 505 +#define ERR_TKAPI_OUTBOUNDS 2000 +#define ERR_TKAPI_INPUTNOTOPEN 2001 +#define ERR_TKAPI_SIM_NRUNNING 2002 +#define ERR_TKAPI_WRONG_TYPE 2003 +#define ERR_TKAPI_OBJECT_INDEX 2004 using namespace std; @@ -40,67 +40,67 @@ BOOST_AUTO_TEST_CASE(model_not_open) { //Project error = swmm_getObjectIndex(SM_NODE, id, &index); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); //Gage error = swmm_getGagePrecip(0, SM_TOTALPRECIP, &val); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_setGagePrecip(0, input_val); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); //Subcatchment error = swmm_getSubcatchParam(0, SM_WIDTH, &val); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_setSubcatchParam(0, SM_WIDTH, val); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_getSubcatchResult(0, SM_SUBCRAIN, &val); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); //Node error = swmm_getNodeParam(0, SM_INVERTEL, &val); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_setNodeParam(0, SM_INVERTEL, val); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_setNodeInflow(0, input_val); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_getNodeResult(0, SM_TOTALINFLOW, &val); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_setOutfallStage(0, input_val); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); //Link error = swmm_getLinkParam(0, SM_OFFSET1, &val); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_setLinkParam(0, SM_OFFSET1, val); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_getLinkResult(0, SM_LINKFLOW, &val); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_setLinkSetting(0, input_val); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); //Pollutant error = swmm_getSubcatchPollut(0, SM_BUILDUP, &result_array, &length); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_getLinkPollut(0, SM_LINKQUAL, &result_array, &length); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); error = swmm_getNodePollut(0, SM_NODEQUAL, &result_array, &length); - BOOST_CHECK_EQUAL(error, ERR_API_INPUTNOTOPEN); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_INPUTNOTOPEN); } BOOST_AUTO_TEST_SUITE_END() @@ -114,20 +114,20 @@ BOOST_FIXTURE_TEST_CASE(sim_started_check, FixtureBeforeStep) { //Subcatchment error = swmm_setSubcatchParam(0, SM_WIDTH, 1); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); //Node error = swmm_setNodeParam(0, SM_INVERTEL, 1); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); //Link error = swmm_setLinkParam(0, SM_OFFSET1, 1); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLinkParam(0, SM_OFFSET2, 1); - BOOST_CHECK_EQUAL(error, ERR_API_SIM_NRUNNING); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_SIM_NRUNNING); error = swmm_setLinkParam(0, SM_INITFLOW, 1); BOOST_CHECK_EQUAL(error, ERR_NONE); @@ -156,45 +156,45 @@ BOOST_FIXTURE_TEST_CASE(object_bounds_check, FixtureOpenClose) { //Gage error = swmm_getGagePrecip(100, SM_TOTALPRECIP, &val); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); //Subcatchment error = swmm_getSubcatchParam(100, SM_WIDTH, &val); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_setSubcatchParam(100, SM_WIDTH, 1); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); //Node error = swmm_getNodeParam(100, SM_INVERTEL, &val); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_setNodeParam(100, SM_INVERTEL, 1); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_setOutfallStage(100, input_val); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); //Link error = swmm_getLinkParam(100, SM_OFFSET1, &val); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_setLinkParam(100, SM_OFFSET1, 1); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); //Pollutant error = swmm_getSubcatchPollut(100, SM_BUILDUP, &result_array, &length); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_getLinkPollut(100, SM_LINKQUAL, &result_array, &length); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_getNodePollut(100, SM_NODEQUAL, &result_array, &length); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); } @@ -211,26 +211,26 @@ BOOST_FIXTURE_TEST_CASE(key_bounds_check, FixtureOpenClose) { //Subcatchment error = swmm_getSubcatchParam(0, static_cast(100), &val); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_setSubcatchParam(0, static_cast(100), 1); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); //Node error = swmm_getNodeParam(0, static_cast(100), &val); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_setNodeParam(0, static_cast(100), 1); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); //Link error = swmm_getLinkParam(0, static_cast(100), &val); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_setLinkParam(0, static_cast(100), 1); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); } @@ -547,7 +547,7 @@ BOOST_FIXTURE_TEST_CASE(count_objects, FixtureOpenClose){ //return error error = swmm_countObjects(static_cast(999), &count); - BOOST_REQUIRE(error == ERR_API_OUTBOUNDS); + BOOST_REQUIRE(error == ERR_TKAPI_OUTBOUNDS); } @@ -817,32 +817,32 @@ BOOST_FIXTURE_TEST_CASE(sim_after_start_check, FixtureBeforeStep){ // Subcatchment error = swmm_getSubcatchResult(100, SM_SUBCRAIN, &val); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_getSubcatchResult(0, static_cast(100), &val); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); // Node error = swmm_getNodeResult(100, SM_TOTALINFLOW, &val); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_getNodeResult(0, static_cast(100), &val); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_setNodeInflow(100, input_val); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); //Link error = swmm_getLinkResult(100, SM_LINKFLOW, &val); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); error = swmm_getLinkResult(0, static_cast(100), &val); - BOOST_CHECK_EQUAL(error, ERR_API_OUTBOUNDS); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OUTBOUNDS); error = swmm_setLinkSetting(100, input_val); - BOOST_CHECK_EQUAL(error, ERR_API_OBJECT_INDEX); + BOOST_CHECK_EQUAL(error, ERR_TKAPI_OBJECT_INDEX); }