diff --git a/src/climate.c b/src/climate.c index aba36aa67..a44c5faae 100644 --- a/src/climate.c +++ b/src/climate.c @@ -8,6 +8,7 @@ // 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) // Author: L. Rossman // // Climate related functions. @@ -27,6 +28,10 @@ // // 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. ///----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -41,7 +46,7 @@ //----------------------------------------------------------------------------- enum ClimateFileFormats {UNKNOWN_FORMAT, USER_PREPARED, // SWMM 5's own user format - GHCND, // NCDC GHCN Daily format //(5.1.007) + GHCND, // NCDC GHCN Daily format TD3200, // NCDC TD3200 format DLY0204}; // Canadian DLY02 or DLY04 format static const int MAXCLIMATEVARS = 4; @@ -49,11 +54,10 @@ static const int MAXDAYSPERMONTH = 32; // These variables are used when processing climate files. enum ClimateVarType {TMIN, TMAX, EVAP, WIND}; -enum WindSpeedType {WDMV, AWND}; //(5.1.007) -static char* ClimateVarWords[] = {"TMIN", "TMAX", "EVAP", "WDMV", "AWND", //(5.1.007) +enum WindSpeedType {WDMV, AWND}; +static char* ClimateVarWords[] = {"TMIN", "TMAX", "EVAP", "WDMV", "AWND", NULL}; -//// Added for release 5.1.010. //// //(5.1.010) //----------------------------------------------------------------------------- // Data Structures //----------------------------------------------------------------------------- @@ -67,7 +71,7 @@ typedef struct int maxCount; // maximum length of moving average window int front; // index of front of moving average window } TMovAve; -//// + //----------------------------------------------------------------------------- // Shared variables @@ -84,7 +88,7 @@ static double Hrday; // avg. of min/max temp times static double Dhrdy; // hrs. between min. & max. temp. times static double Dydif; // hrs. between max. & min. temp. times static DateTime LastDay; // date of last day with temp. data -static TMovAve Tma; // moving average of daily temperatures //(5.1.010) +static TMovAve Tma; // moving average of daily temperatures // Evaporation variables static DateTime NextEvapDate; // next date when evap. rate changes @@ -101,9 +105,9 @@ static double FileValue[4]; // current day's values of climate data static double FileData[4][32]; // month's worth of daily climate data static char FileLine[MAXLINE+1]; // line from climate data file -static int FileFieldPos[4]; // start of data fields for file record //(5.1.007) -static int FileDateFieldPos; // start of date field for file record //(5.1.007) -static int FileWindType; // wind speed type; //(5.1.007) +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 //----------------------------------------------------------------------------- // External functions (defined in funcs.h) @@ -114,7 +118,7 @@ static int FileWindType; // wind speed type; / // climate_openFile // called by runoff_open // climate_initState // called by project_init // climate_setState // called by runoff_execute -// climate_getNextEvapDate // called by runoff_getTimeStep //(5.1.008) +// climate_getNextEvapDate // called by runoff_getTimeStep //----------------------------------------------------------------------------- // Local functions @@ -126,13 +130,13 @@ static void readTD3200FileLine(int *year, int *month); static void readDLY0204FileLine(int *year, int *month); static void readFileValues(void); -static void setNextEvapDate(DateTime thedate); //(5.1.008) +static void setNextEvapDate(DateTime thedate); static void setEvap(DateTime theDate); static void setTemp(DateTime theDate); static void setWind(DateTime theDate); static void updateTempTimes(int day); -static void updateTempMoveAve(double tmin, double tmax); //(5.1.010) -static double getTempEvap(int day, double ta, double tr); //(5.1.010) +static void updateTempMoveAve(double tmin, double tmax); +static double getTempEvap(int day, double ta, double tr); static void updateFileValues(DateTime theDate); static void parseUserFileLine(void); @@ -140,9 +144,9 @@ static void parseTD3200FileLine(void); static void parseDLY0204FileLine(void); static void setTD3200FileValues(int param); -static int isGhcndFormat(char* line); //(5.1.007) -static void readGhcndFileLine(int *year, int *month); //(5.1.007) -static void parseGhcndFileLine(void); //(5.1.007) +static int isGhcndFormat(char* line); +static void readGhcndFileLine(int *year, int *month); +static void parseGhcndFileLine(void); //============================================================================= @@ -356,8 +360,6 @@ int climate_readEvapParams(char* tok[], int ntoks) //============================================================================= -//// New function added to release 5.1.007. //// //(5.1.007) - int climate_readAdjustments(char* tok[], int ntoks) // // Input: tok[] = array of string tokens @@ -370,9 +372,13 @@ int climate_readAdjustments(char* tok[], int ntoks) // TEMPERATURE v1 ... v12 // EVAPORATION v1 ... v12 // RAINFALL v1 ... v12 -// CONDUCTIVITY v1 ... v12 //(5.1.008) +// CONDUCTIVITY v1 ... v12 +// N-PERV subcatchID patternID //(5.1.013 +// DSTORE subcatchID patternID // +// INFIL subcatchID patternID // { - int i; + int i, j; //(5.1.013) + if (ntoks == 1) return 0; if ( match(tok[0], "TEMP") ) @@ -408,8 +414,6 @@ int climate_readAdjustments(char* tok[], int ntoks) return 0; } -//// Following code segment added for release 5.1.008. //// //(5.1.008) -//// if ( match(tok[0], "CONDUCT") ) { if ( ntoks < 13 ) return error_setInpError(ERR_ITEMS, ""); @@ -417,10 +421,44 @@ int climate_readAdjustments(char* tok[], int ntoks) { if ( !getDouble(tok[i], &Adjust.hydcon[i-1]) ) return error_setInpError(ERR_NUMBER, tok[i]); - if ( Adjust.hydcon[i-1] <= 0.0 ) Adjust.hydcon[i-1] = 1.0; //(5.1.011) + if ( Adjust.hydcon[i-1] <= 0.0 ) Adjust.hydcon[i-1] = 1.0; } 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, ""); + i = project_findObject(SUBCATCH, tok[1]); + if (i < 0) return error_setInpError(ERR_NAME, tok[1]); + j = project_findObject(TIMEPATTERN, tok[2]); + if (j < 0) return error_setInpError(ERR_NAME, tok[2]); + Subcatch[i].nPervPattern = j; + return 0; + } + + if ( match(tok[0], "DSTORE") ) + { + if (ntoks < 3) return error_setInpError(ERR_ITEMS, ""); + i = project_findObject(SUBCATCH, tok[1]); + if (i < 0) return error_setInpError(ERR_NAME, tok[1]); + j = project_findObject(TIMEPATTERN, tok[2]); + if (j < 0) return error_setInpError(ERR_NAME, tok[2]); + Subcatch[i].dStorePattern = j; + return 0; + } + + if (match(tok[0], "INFIL")) + { + if (ntoks < 3) return error_setInpError(ERR_ITEMS, ""); + i = project_findObject(SUBCATCH, tok[1]); + if (i < 0) return error_setInpError(ERR_NAME, tok[1]); + j = project_findObject(TIMEPATTERN, tok[2]); + if (j < 0) return error_setInpError(ERR_NAME, tok[2]); + Subcatch[i].infilPattern = j; + return 0; + } //// return error_setInpError(ERR_KEYWORD, tok[0]); } @@ -434,10 +472,10 @@ void climate_validate() // Purpose: validates climatological variables // { - int i; //(5.1.007) + int i; double a, z, pa; - // --- check if climate data comes from external data file //(5.1.007) + // --- check if climate data comes from external data file if ( Wind.type == FILE_WIND || Evap.type == FILE_EVAP || Evap.type == TEMPERATURE_EVAP ) { @@ -447,8 +485,8 @@ void climate_validate() } } - // --- open the climate data file //(5.1.007) - if ( Fclimate.mode == USE_FILE ) climate_openFile(); //(5.1.007) + // --- open the climate data file + if ( Fclimate.mode == USE_FILE ) climate_openFile(); // --- snow melt parameters tipm & rnm must be fractions if ( Snow.tipm < 0.0 || @@ -468,7 +506,7 @@ void climate_validate() else pa = 29.9 - 1.02*z + 0.0032*pow(z, 2.4); // atmos. pressure Temp.gamma = 0.000359 * pa; - // --- convert units of monthly temperature & evap adjustments //(5.1.007) + // --- convert units of monthly temperature & evap adjustments for (i = 0; i < 12; i++) { if (UnitSystem == SI) Adjust.temp[i] *= 9.0/5.0; @@ -545,8 +583,6 @@ void climate_openFile() //============================================================================= -//// This function was re-written for release 5.1.008. //// //(5.1.008) - void climate_initState() // // Input: none @@ -577,7 +613,6 @@ void climate_initState() setNextEvapDate(NextEvapDate); } -//// Following section added to release 5.1.010. //// //(5.1.010) // --- initialize variables for temperature evaporation if ( Evap.type == TEMPERATURE_EVAP ) { @@ -587,7 +622,6 @@ void climate_initState() Tma.tAve = 0.0; Tma.tRng = 0.0; } -//// } //============================================================================= @@ -603,15 +637,13 @@ void climate_setState(DateTime theDate) if ( Temp.dataSource != NO_TEMP ) setTemp(theDate); setEvap(theDate); setWind(theDate); - Adjust.rainFactor = Adjust.rain[datetime_monthOfYear(theDate)-1]; //(5.1.007) - Adjust.hydconFactor = Adjust.hydcon[datetime_monthOfYear(theDate)-1]; //(5.1.008) - setNextEvapDate(theDate); //(5.1.008) + Adjust.rainFactor = Adjust.rain[datetime_monthOfYear(theDate)-1]; + Adjust.hydconFactor = Adjust.hydcon[datetime_monthOfYear(theDate)-1]; + setNextEvapDate(theDate); } //============================================================================= -//// New function added to release 5.1.008. //// //(5.1.008) - DateTime climate_getNextEvapDate() // // Input: none @@ -624,8 +656,6 @@ DateTime climate_getNextEvapDate() //============================================================================= -//// Modified from what was previously named climate_getNextEvap. //// //(5.1.008) - void setNextEvapDate(DateTime theDate) // // Input: theDate = current simulation date @@ -746,14 +776,14 @@ void setTemp(DateTime theDate) { int j; // snow data object index int k; // time series index - int mon; // month of year //(5.1.007) + int mon; // month of year int day; // day of year DateTime theDay; // calendar day double hour; // hour of day double tmp; // temporary temperature // --- see if a new day has started - mon = datetime_monthOfYear(theDate); //(5.1.007) + mon = datetime_monthOfYear(theDate); theDay = floor(theDate); if ( theDay > LastDay ) { @@ -761,8 +791,8 @@ void setTemp(DateTime theDate) day = datetime_dayOfYear(theDate); if ( Temp.dataSource == FILE_TEMP ) { - Tmin = FileValue[TMIN] + Adjust.temp[mon-1]; //(5.1.007) - Tmax = FileValue[TMAX] + Adjust.temp[mon-1]; //(5.1.007) + Tmin = FileValue[TMIN] + Adjust.temp[mon-1]; + Tmax = FileValue[TMAX] + Adjust.temp[mon-1]; if ( Tmin > Tmax ) { tmp = Tmin; @@ -772,8 +802,8 @@ void setTemp(DateTime theDate) updateTempTimes(day); if ( Evap.type == TEMPERATURE_EVAP ) { - updateTempMoveAve(Tmin, Tmax); //(5.1.010) - FileValue[EVAP] = getTempEvap(day, Tma.tAve, Tma.tRng); //(5.1.010) + updateTempMoveAve(Tmin, Tmax); + FileValue[EVAP] = getTempEvap(day, Tma.tAve, Tma.tRng); } } @@ -816,8 +846,8 @@ void setTemp(DateTime theDate) Temp.ta = (9./5.) * Temp.ta + 32.0; } - // --- apply climate change adjustment factor //(5.1.007) - Temp.ta += Adjust.temp[mon-1]; //(5.1.007) + // --- apply climate change adjustment factor + Temp.ta += Adjust.temp[mon-1]; } } @@ -835,7 +865,7 @@ void setEvap(DateTime theDate) // { int k; - int mon = datetime_monthOfYear(theDate); //(5.1.007) + int mon = datetime_monthOfYear(theDate); switch ( Evap.type ) { @@ -864,15 +894,15 @@ void setEvap(DateTime theDate) default: Evap.rate = 0.0; } - // --- apply climate change adjustment //(5.1.007) - Evap.rate += Adjust.evap[mon-1]; //(5.1.007) + // --- apply climate change adjustment + Evap.rate += Adjust.evap[mon-1]; // --- set soil recovery factor Evap.recoveryFactor = 1.0; k = Evap.recoveryPattern; if ( k >= 0 && Pattern[k].type == MONTHLY_PATTERN ) { - Evap.recoveryFactor = Pattern[k].factor[mon-1]; //(5.1.007) + Evap.recoveryFactor = Pattern[k].factor[mon-1]; } } @@ -936,8 +966,6 @@ void updateTempTimes(int day) //============================================================================= -//// This function was modified for release 5.1.010. //// //(5.1.010) - double getTempEvap(int day, double tave, double trng) // // Input: day = day of year @@ -1004,8 +1032,8 @@ int getFileFormat() n = sscanf(line, "%s %d %d %d %s", staID, &y, &m, &d, s); if ( n == 5 ) return USER_PREPARED; - // --- check for GHCND format //(5.1.007) - if ( isGhcndFormat(line) ) return GHCND; //(5.1.007) + // --- check for GHCND format + if ( isGhcndFormat(line) ) return GHCND; return UNKNOWN_FORMAT; } @@ -1033,7 +1061,7 @@ void readFileLine(int *y, int *m) case USER_PREPARED: readUserFileLine(y, m); break; case TD3200: readTD3200FileLine(y,m); break; case DLY0204: readDLY0204FileLine(y,m); break; - case GHCND: readGhcndFileLine(y,m); break; //(5.1.007) + case GHCND: readGhcndFileLine(y,m); break; } } @@ -1154,7 +1182,7 @@ void readFileValues() case USER_PREPARED: parseUserFileLine(); break; case TD3200: parseTD3200FileLine(); break; case DLY0204: parseDLY0204FileLine(); break; - case GHCND: parseGhcndFileLine(); break; //(5.1.007) + case GHCND: parseGhcndFileLine(); break; } strcpy(FileLine, ""); } @@ -1364,8 +1392,6 @@ void parseDLY0204FileLine() //============================================================================= -//// This function was added to release 5.1.007. //// //(5.1.007) - int isGhcndFormat(char* line) // // Input: line = first line of text from a climate file @@ -1410,8 +1436,6 @@ int isGhcndFormat(char* line) //============================================================================= -//// This function was added to release 5.1.007. //// //(5.1.007) - void readGhcndFileLine(int* y, int* m) // // Input: none @@ -1430,8 +1454,6 @@ void readGhcndFileLine(int* y, int* m) //============================================================================= -//// This function was added to release 5.1.007. //// //(5.1.007) - void parseGhcndFileLine() // // Input: none @@ -1499,8 +1521,6 @@ void parseGhcndFileLine() //============================================================================= -//// New function added to release 5.1.010. //// //(5.1.010) - void updateTempMoveAve(double tmin, double tmax) // // Input: tmin = minimum daily temperature (deg F) diff --git a/src/consts.h b/src/consts.h index 7bdc8c8f4..789eddea2 100644 --- a/src/consts.h +++ b/src/consts.h @@ -5,6 +5,7 @@ // 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) // Author: L. Rossman // // Various Constants @@ -14,7 +15,7 @@ // General Constants //------------------ -#define VERSION 51011 //(5.1.011) +#define VERSION 51013 #define MAGICNUMBER 516114522 #define EOFMARK 0x1A // Use 0x04 for UNIX systems #define MAXTITLE 3 // Max. # title lines diff --git a/src/controls.c b/src/controls.c index becb3dba0..33e1b2155 100644 --- a/src/controls.c +++ b/src/controls.c @@ -54,7 +54,7 @@ #define _CRT_SECURE_NO_DEPRECATE #include -#include +#include #include #include "headers.h" @@ -65,9 +65,9 @@ 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, //(5.1.008) - r_SETTING, r_TIMEOPEN, r_TIMECLOSED, r_TIME, r_DATE, //(5.1.010) - r_CLOCKTIME, r_DAYOFYEAR, r_DAY, r_MONTH}; //(5.1.011) +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}; enum RuleRelation {EQ, NE, LT, LE, GT, GE}; enum RuleSetting {r_CURVE, r_TIMESERIES, r_PID, r_NUMERIC}; @@ -75,9 +75,9 @@ static char* ObjectWords[] = {"NODE", "LINK", "CONDUIT", "PUMP", "ORIFICE", "WEIR", "OUTLET", "SIMULATION", NULL}; static char* AttribWords[] = - {"DEPTH", "HEAD", "VOLUME", "INFLOW", "FLOW", "STATUS", "SETTING", //(5.1.008) - "TIMEOPEN", "TIMECLOSED","TIME", "DATE", "CLOCKTIME", "DAYOFYEAR", //(5.1.011) - "DAY", "MONTH", NULL}; //(5.1.011) + {"DEPTH", "HEAD", "VOLUME", "INFLOW", "FLOW", "STATUS", "SETTING", + "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}; @@ -98,8 +98,8 @@ struct TVariable struct TPremise { int type; // clause type (IF/AND/OR) - struct TVariable lhsVar; // left hand side variable //(5.1.008) - struct TVariable rhsVar; // right hand side variable //(5.1.008) + struct TVariable lhsVar; // left hand side variable + struct TVariable rhsVar; // right hand side variable int relation; // relational operator (>, <, =, etc) double value; // right hand side value struct TPremise *next; // next premise clause of rule @@ -148,7 +148,6 @@ double ControlValue; // value of controller variable double SetPoint; // value of controller setpoint DateTime CurrentDate; // current date in whole days DateTime CurrentTime; // current time of day (decimal) -DateTime ElapsedTime; // elasped simulation time (decimal days) //----------------------------------------------------------------------------- // External functions (declared in funcs.h) @@ -180,7 +179,7 @@ void deleteRules(void); int findExactMatch(char *s, char *keyword[]); int setActionSetting(char* tok[], int nToks, int* curve, int* tseries, - int* attrib, double* value); + int* attrib, double value[]); void updateActionValue(struct TAction* a, DateTime currentTime, double dt); double getPIDSetting(struct TAction* a, double dt); @@ -345,8 +344,6 @@ int controls_evaluate(DateTime currentTime, DateTime elapsedTime, double tStep) //============================================================================= -// This function was revised to add support for r.h.s. premise variables. // //(5.1.008) - int addPremise(int r, int type, char* tok[], int nToks) // // Input: r = control rule index @@ -389,9 +386,9 @@ int addPremise(int r, int type, char* tok[], int nToks) if ( findmatch(tok[n], ObjectWords) >= 0 && n + 3 >= nToks ) { err = getPremiseVariable(tok, &n, &v2); - if ( err > 0 ) return ERR_RULE; //(5.1.009) - if ( v1.attribute != v2.attribute) //(5.1.009) - report_writeWarningMsg(WARN11, Rules[r].ID); //(5.1.009) + if ( err > 0 ) return ERR_RULE; + if ( v1.attribute != v2.attribute) + report_writeWarningMsg(WARN11, Rules[r].ID); } // --- otherwise get value to which LHS variable is compared to @@ -478,12 +475,11 @@ int getPremiseVariable(char* tok[], int* k, struct TVariable* v) { case r_DEPTH: case r_HEAD: - case r_VOLUME: //(5.1.008) + case r_VOLUME: case r_INFLOW: break; default: return error_setInpError(ERR_KEYWORD, tok[n]); } -//// Added to release 5.1.010. //// //(5.1.010) // --- check for link TIMEOPEN & TIMECLOSED attributes else if ( link >= 0 && ( (attrib == r_TIMEOPEN || @@ -492,7 +488,6 @@ int getPremiseVariable(char* tok[], int* k, struct TVariable* v) { } -//// else if ( obj == r_LINK || obj == r_CONDUIT ) switch (attrib) { @@ -520,7 +515,7 @@ int getPremiseVariable(char* tok[], int* k, struct TVariable* v) case r_CLOCKTIME: case r_DAY: case r_MONTH: - case r_DAYOFYEAR: break; //(5.1.011) + case r_DAYOFYEAR: break; default: return error_setInpError(ERR_KEYWORD, tok[n]); } @@ -544,7 +539,7 @@ int getPremiseValue(char* token, int attrib, double* value) // in the premise clause of a control rule. // { - char strDate[25]; //(5.1.011) + char strDate[25]; switch (attrib) { case r_STATUS: @@ -555,8 +550,8 @@ int getPremiseValue(char* token, int attrib, double* value) case r_TIME: case r_CLOCKTIME: - case r_TIMEOPEN: //(5.1.010) - case r_TIMECLOSED: //(5.1.010) + case r_TIMEOPEN: + case r_TIMECLOSED: if ( !datetime_strToTime(token, value) ) return error_setInpError(ERR_DATETIME, token); break; @@ -580,7 +575,6 @@ int getPremiseValue(char* token, int attrib, double* value) return error_setInpError(ERR_DATETIME, token); break; -//// This code block added to release 5.1.011. //// //(5.1.011) case r_DAYOFYEAR: strncpy(strDate, token, 6); strcat(strDate, "/1947"); @@ -591,7 +585,6 @@ int getPremiseValue(char* token, int attrib, double* value) else if ( !getDouble(token, value) || *value < 1 || *value > 365 ) return error_setInpError(ERR_DATETIME, token); break; -//////////////////////////////////////////////////// default: if ( !getDouble(token, value) ) return error_setInpError(ERR_NUMBER, token); @@ -940,8 +933,8 @@ int executeActionList(DateTime currentTime) if ( Link[a1->link].targetSetting != a1->value ) { Link[a1->link].targetSetting = a1->value; - if ( RptFlags.controls && a1->curve < 0 //(5.1.011) - && a1->tseries < 0 && a1->attribute != r_PID ) //(5.1.011) + if ( RptFlags.controls && a1->curve < 0 + && a1->tseries < 0 && a1->attribute != r_PID ) report_writeControlAction(currentTime, Link[a1->link].ID, a1->value, Rules[a1->rule].ID); count++; @@ -955,8 +948,6 @@ int executeActionList(DateTime currentTime) //============================================================================= -//// This function was re-written for release 5.1.011. //// //(5.1.011) - int evaluatePremise(struct TPremise* p, double tStep) // // Input: p = a control rule premise condition @@ -1011,8 +1002,8 @@ double getVariableValue(struct TVariable v) case r_MONTH: return datetime_monthOfYear(CurrentDate); - case r_DAYOFYEAR: //(5.1.011) - return datetime_dayOfYear(CurrentDate); //(5.1.011) + case r_DAYOFYEAR: + return datetime_dayOfYear(CurrentDate); case r_STATUS: if ( j < 0 || @@ -1038,7 +1029,7 @@ double getVariableValue(struct TVariable v) if ( i < 0 ) return MISSING; return (Node[i].newDepth + Node[i].invertElev) * UCF(LENGTH); - case r_VOLUME: //(5.1.008) + case r_VOLUME: if ( i < 0 ) return MISSING; return (Node[i].newVolume * UCF(VOLUME)); @@ -1046,7 +1037,6 @@ double getVariableValue(struct TVariable v) if ( i < 0 ) return MISSING; else return Node[i].newLatFlow*UCF(FLOW); -//// This section added to release 5.1.010. //// //(5.1.010) case r_TIMEOPEN: if ( j < 0 ) return MISSING; if ( Link[j].setting <= 0.0 ) return MISSING; @@ -1056,7 +1046,6 @@ double getVariableValue(struct TVariable v) if ( j < 0 ) return MISSING; if ( Link[j].setting > 0.0 ) return MISSING; return CurrentDate + CurrentTime - Link[j].timeLastSet; -//// default: return MISSING; } diff --git a/src/culvert.c b/src/culvert.c index 55c43f15f..37c7c8d56 100644 --- a/src/culvert.c +++ b/src/culvert.c @@ -4,6 +4,7 @@ // Project: EPA SWMM5 // Version: 5.1 // Date: 03/20/14 (Build 5.1.001) +// 05/10/18 (Build 5.1.013) // Author: L. Rossman // // Culvert equations for SWMM5 @@ -11,6 +12,8 @@ // Computes flow reduction in a culvert-type conduit due to // inlet control using equations from the FHWA HEC-5 circular. // +// Build 5.1.013: +// - C parameter corrected for Arch, Corrugated Metal, Mitered culvert. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -105,7 +108,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.0463, 0.75}, //Mitered to slope + {1.0, 0.0300, 1.00, 0.0473, 0.75}, //Mitered to slope //(5.1.013) {1.0, 0.0340, 1.50, 0.0496, 0.57}, //Thin wall projecting // Circular Culvert diff --git a/src/datetime.c b/src/datetime.c index 6373c8aa8..e139ff207 100644 --- a/src/datetime.c +++ b/src/datetime.c @@ -231,9 +231,9 @@ void datetime_decodeTime(DateTime time, int* h, int* m, int* s) { int secs; int mins; - double fracDay = (time - floor(time)) * SecsPerDay; //(5.1.011) - secs = (int)(floor(fracDay + 0.5)); //(5.1.011) - if ( secs >= 86400 ) secs = 86399; //(5.1.011) + double fracDay = (time - floor(time)) * SecsPerDay; + secs = (int)(floor(fracDay + 0.5)); + if ( secs >= 86400 ) secs = 86399; divMod(secs, 60, &mins, s); divMod(mins, 60, h, m); if ( *h > 23 ) *h = 0; @@ -512,8 +512,6 @@ int datetime_daysPerMonth(int year, int month) //============================================================================= -//// New function added to release 5.1.011. //// //(5.1.011) - void datetime_getTimeStamp(int fmt, DateTime aDate, int stampSize, char* timeStamp) // Input: fmt = desired date format code diff --git a/src/datetime.h b/src/datetime.h index 5dfee5bc4..397f3180c 100644 --- a/src/datetime.h +++ b/src/datetime.h @@ -46,8 +46,8 @@ int datetime_daysPerMonth(int year, int month); // Functions for converting a DateTime value to a string void datetime_dateToStr(DateTime date, char* s); void datetime_timeToStr(DateTime time, char* s); -void datetime_getTimeStamp(int fmt, DateTime aDate, int stampSize, //5.1.011 - char* timeStamp); //5.1.011 +void datetime_getTimeStamp(int fmt, DateTime aDate, int stampSize, + char* timeStamp); // Functions for converting a string date or time to a DateTime value int datetime_findMonth(char* s); diff --git a/src/dwflow.c b/src/dwflow.c index 4ef2464f6..420a6898d 100644 --- a/src/dwflow.c +++ b/src/dwflow.c @@ -6,6 +6,7 @@ // 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) // Author: L. Rossman (EPA) // M. Tryby (EPA) // R. Dickinson (CDM) @@ -18,6 +19,10 @@ // // 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. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -35,7 +40,8 @@ static double findLocalLosses(int link, double a1, double a2, double aMid, double q); static double getWidth(TXsect* xsect, double y); -static double getArea(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 getHydRad(TXsect* xsect, double y); static double checkNormalFlow(int j, double q, double y1, double y2, @@ -70,6 +76,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 dq1, dq2, dq3, dq4, dq5, // terms in momentum eqn. dq6; // term for evap and infil losses double denom; // denominator of flow update formula @@ -107,9 +114,12 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) y1 = MAX(y1, FUDGE); y2 = MAX(y2, FUDGE); - // --- flow depths can't exceed full depth of conduit - y1 = MIN(y1, xsect->yFull); - y2 = MIN(y2, xsect->yFull); + // --- flow depths can't exceed full depth of conduit if slot not used + if ( SurchargeMethod != SLOT ) //(5.1.013) + { + y1 = MIN(y1, xsect->yFull); + y2 = MIN(y2, xsect->yFull); + } // -- get area from solution at previous time step aOld = Conduit[k].a2; @@ -123,13 +133,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 - a1 = getArea(xsect, y1); - a2 = getArea(xsect, y2); + wSlot = getSlotWidth(xsect, y1); //(5.1.013) + a1 = getArea(xsect, y1, wSlot); //(5.1.013) r1 = getHydRad(xsect, y1); + wSlot = getSlotWidth(xsect, y2); //(5.1.013) + a2 = getArea(xsect, y2, wSlot); //(5.1.013) // --- compute area & hyd. radius at midpoint yMid = 0.5 * (y1 + y2); - aMid = getArea(xsect, yMid); + wSlot = getSlotWidth(xsect, yMid); //(5.1.013) + aMid = getArea(xsect, yMid, wSlot); //(5.1.013) rMid = getHydRad(xsect, yMid); // --- alternate approach not currently used, but might produce better @@ -213,11 +226,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, qOld, dt) * 2.5 * dt * v / link_getLength(j); //(5.1.012) + dq6 = link_getLossRate(j, qOld, dt) * 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; + q = (qOld - dq2 + dq3 + dq4 + dq6) / denom; //(5.1.013) // --- compute derivative of flow w.r.t. head Link[j].dqdh = 1.0 / denom * GRAVITY * dt * aWtd / length * barrels; @@ -267,8 +280,8 @@ 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); - Conduit[k].fullState = link_getFullState(a1, a2, xsect->aFull); //(5.1.008) +// aMid = MIN(aMid, xsect->aFull); //Slot can have aMid > aFull //(5.1.013) + Conduit[k].fullState = link_getFullState(a1, a2, xsect->aFull); Link[j].newVolume = aMid * link_getLength(j) * barrels; Link[j].newFlow = q * barrels; } @@ -276,7 +289,7 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) //============================================================================= int getFlowClass(int j, double q, double h1, double h2, double y1, double y2, - double *yC, double *yN, double* fasnh) + double *yC, double *yN, double* fasnh) // // Input: j = conduit link index // q = current conduit flow (cfs) @@ -420,7 +433,8 @@ void findSurfArea(int j, double q, double length, double* h1, double* h2, double surfArea2 = 0.0; // surface area st downstrm node (ft2) double criticalDepth; // critical flow depth (ft) double normalDepth; // normal flow depth (ft) - double fasnh; // fraction between norm. & crit. depth + double fullDepth; // full depth (ft) //(5.1.013) + double fasnh = 1.0; // fraction between norm. & crit. depth //(5.1.013) TXsect* xsect = &Link[j].xsect; // pointer to cross-section data // --- get node indexes & current flow depths @@ -432,9 +446,16 @@ 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 - Link[j].flowClass = getFlowClass(j, q, *h1, *h2, *y1, *y2, - &criticalDepth, &normalDepth, &fasnh); + fullDepth = xsect->yFull; + if (flowDepth1 >= fullDepth && flowDepth2 >= fullDepth) + { + Link[j].flowClass = SUBCRITICAL; + } + 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 ) @@ -547,6 +568,27 @@ 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; + + // --- return 0.0 if slot surcharge method not used + if (SurchargeMethod != SLOT || xsect_isOpen(xsect->type) || + yNorm < CrownCutoff) return 0.0; + + // --- for depth > 1.78 * pipe depth, slot width = 1% of max. width + if (yNorm > 1.78) return 0.01 * xsect->wMax; + + // --- otherwise use the Sjoberg formula + return xsect->wMax * 0.5423 * exp(-pow(yNorm, 2.4)); +} + +//============================================================================= + +//// 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 @@ -555,15 +597,18 @@ double getWidth(TXsect* xsect, double y) // Purpose: computes top width of flow surface in conduit. // { - double yNorm = y/xsect->yFull; - if ( yNorm > 0.96 && - !xsect_isOpen(xsect->type) ) y = 0.96*xsect->yFull; + double wSlot = getSlotWidth(xsect, y); + if (wSlot > 0.0) return wSlot; + if (y / xsect->yFull >= CrownCutoff && !xsect_isOpen(xsect->type)) + y = CrownCutoff * xsect->yFull; return xsect_getWofY(xsect, y); } //============================================================================= -double getArea(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 // y = flow depth (ft) @@ -571,14 +616,14 @@ double getArea(TXsect* xsect, double y) // Purpose: computes area of flow cross-section in a conduit. // { - double area; // flow area (ft2) - y = MIN(y, xsect->yFull); - area = xsect_getAofY(xsect, y); - return area; + if ( y >= xsect->yFull ) return xsect->aFull + (y - xsect->yFull) * wSlot; + return xsect_getAofY(xsect, y); } //============================================================================= +//// 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 @@ -587,10 +632,8 @@ double getHydRad(TXsect* xsect, double y) // Purpose: computes hydraulic radius of flow cross-section in a conduit. // { - double hRadius; // hyd. radius (ft) - y = MIN(y, xsect->yFull); - hRadius = xsect_getRofY(xsect, y); - return hRadius; + if (y >= xsect->yFull) return xsect->rFull; + return xsect_getRofY(xsect, y); } //============================================================================= diff --git a/src/dynwave.c b/src/dynwave.c index 76c043623..40b6a71a6 100644 --- a/src/dynwave.c +++ b/src/dynwave.c @@ -8,6 +8,7 @@ // 09/15/14 (5.1.007) // 03/19/15 (5.1.008) // 08/01/16 (5.1.011) +// 05/10/18 (5.1.013) // Author: L. Rossman (EPA) // M. Tryby (EPA) // R. Dickinson (CDM) @@ -38,24 +39,32 @@ // - 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. +// //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE #include "headers.h" -#include +#include #include -#include //(5.1.008) +#if defined(_OPENMP) //(5.1.013) +#include +#endif //----------------------------------------------------------------------------- // Constants //----------------------------------------------------------------------------- -static const double MINTIMESTEP = 0.001; // min. time step (sec) //(5.1.008) -static const double OMEGA = 0.5; // under-relaxation parameter - -// Constants moved here from project.c // //(5.1.008) -const double DEFAULT_SURFAREA = 12.566; // Min. nodal surface area (~4 ft diam.) -const double DEFAULT_HEADTOL = 0.005; // Default head tolerance (ft) -const int DEFAULT_MAXTRIALS = 8; // Max. trials per time step +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 //----------------------------------------------------------------------------- @@ -105,8 +114,6 @@ static double getNodeStep(double tMin, int *minNode); //============================================================================= -//// This function was modified for release 5.1.008. //// //(5.1.008) - void dynwave_init() // // Input: none @@ -119,15 +126,12 @@ void dynwave_init() VariableStep = 0.0; Xnode = (TXnode *) calloc(Nobjects[NODE], sizeof(TXnode)); - -//// Added to release 5.1.011. //// //(5.1.011) if ( Xnode == NULL ) { report_writeErrorMsg(ERR_MEMORY, " Not enough memory for dynamic wave routing."); return; } -////////////////////////////////////// // --- initialize node surface areas & crown elev. for (i = 0; i < Nobjects[NODE]; i++ ) @@ -137,7 +141,7 @@ void dynwave_init() Node[i].crownElev = Node[i].invertElev; } - // --- update node crown elev. & initialize links + // --- initialize links & update node crown elevations for (i = 0; i < Nobjects[LINK]; i++) { j = Link[i].node1; @@ -149,6 +153,10 @@ void dynwave_init() Link[i].flowClass = DRY; 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) } //============================================================================= @@ -165,8 +173,6 @@ void dynwave_close() //============================================================================= -//// New function added to release 5.1.008. //// //(5.1.008) - void dynwave_validate() // // Input: none @@ -176,11 +182,11 @@ void dynwave_validate() { if ( MinRouteStep > RouteStep ) MinRouteStep = RouteStep; if ( MinRouteStep < MINTIMESTEP ) MinRouteStep = MINTIMESTEP; - if ( MinSurfArea == 0.0 ) MinSurfArea = DEFAULT_SURFAREA; - else MinSurfArea /= UCF(LENGTH) * UCF(LENGTH); + if ( MinSurfArea == 0.0 ) MinSurfArea = DEFAULT_SURFAREA; + else MinSurfArea /= UCF(LENGTH) * UCF(LENGTH); if ( HeadTol == 0.0 ) HeadTol = DEFAULT_HEADTOL; else HeadTol /= UCF(LENGTH); - if ( MaxTrials == 0 ) MaxTrials = DEFAULT_MAXTRIALS; + if ( MaxTrials == 0 ) MaxTrials = DEFAULT_MAXTRIALS; } //============================================================================= @@ -201,7 +207,7 @@ double dynwave_getRoutingStep(double fixedStep) // use the minimum allowable time step if ( VariableStep == 0.0 ) { - VariableStep = MinRouteStep; //(5.1.008) + VariableStep = MinRouteStep; } // --- otherwise compute variable step based on current flow solution @@ -297,12 +303,13 @@ 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; } - -//// Following code section modified for release 5.1.007 //// //(5.1.007) +*/ // --- initialize nodal inflow & outflow Node[i].inflow = 0.0; Node[i].outflow = Node[i].losses; @@ -347,7 +354,7 @@ void findLimitedLinks() for (j = 0; j < Nobjects[LINK]; j++) { // ---- check only non-dummy conduit links - if ( !isTrueConduit(j) ) continue; //(5.1.008) + if ( !isTrueConduit(j) ) continue; // --- check that upstream end is full k = Link[j].subIndex; @@ -372,9 +379,9 @@ void findLinkFlows(double dt) int i; // --- find new flow in each non-dummy conduit -#pragma omp parallel num_threads(NumThreads) //(5.1.008) +#pragma omp parallel num_threads(NumThreads) { - #pragma omp for //(5.1.008) + #pragma omp for for ( i = 0; i < Nobjects[LINK]; i++) { if ( isTrueConduit(i) && !Link[i].bypassed ) @@ -502,14 +509,8 @@ void findNonConduitSurfArea(int i) } // --- no surface area for weirs to maintain SWMM 4 compatibility -/* - else if ( Link[i].type == WEIR ) - { - Xlink[i].surfArea1 = Weir[Link[i].subIndex].surfArea / 2.; - } -*/ - else Link[i].surfArea1 = 0.0; + Link[i].surfArea2 = Link[i].surfArea1; if ( Link[i].flowClass == UP_CRITICAL || Node[Link[i].node1].type == STORAGE ) Link[i].surfArea1 = 0.0; @@ -527,7 +528,7 @@ void updateNodeFlows(int i) // Purpose: updates cumulative inflow & outflow at link's end nodes. // { - int k; //(5.1.011) + int k; int barrels = 1; int n1 = Link[i].node1; int n2 = Link[i].node2; @@ -563,7 +564,7 @@ void updateNodeFlows(int i) if ( Link[i].type == PUMP ) { k = Link[i].subIndex; - if ( Pump[k].type != TYPE4_PUMP ) //(5.1.011) + if ( Pump[k].type != TYPE4_PUMP ) { Xnode[n2].sumdqdh += Link[i].dqdh; } @@ -585,9 +586,9 @@ 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) //(5.1.008) +#pragma omp parallel num_threads(NumThreads) { - #pragma omp for private(yOld) //(5.1.008) + #pragma omp for private(yOld) for ( i = 0; i < Nobjects[NODE]; i++ ) { if ( Node[i].type == OUTFALL ) continue; @@ -600,7 +601,7 @@ int findNodeDepths(double dt) Xnode[i].converged = FALSE; } } -} //(5.1.008) +} return converged; } @@ -616,6 +617,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) double dQ; // inflow minus outflow at node (cfs) double dV; // change in node volume (ft3) double dy; // change in node depth (ft) @@ -639,19 +641,39 @@ 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) // --- 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) + { + // --- ponded nodes don't surcharge + if (isPonded) isSurcharged = FALSE; + + // --- closed storage units that are full are in surcharge + else if (Node[i].type == STORAGE) + { + isSurcharged = (Node[i].surDepth > 0.0 && + yLast > Node[i].fullDepth); + } + + // --- 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 ( yLast <= yCrown || Node[i].type == STORAGE || isPonded ) + if (!isSurcharged) //(5.1.013) { dy = dV / surfArea; yNew = yOld + dy; - // --- save non-ponded surface area for use in surcharge algorithm //(5.1.002) - if ( !isPonded ) Xnode[i].oldSurfArea = surfArea; //(5.1.002) + // --- save non-ponded surface area for use in surcharge algorithm + if ( !isPonded ) Xnode[i].oldSurfArea = surfArea; // --- apply under-relaxation to new depth estimate if ( Steps > 0 ) @@ -781,7 +803,7 @@ double getVariableStep(double maxStep) stats_updateCriticalTimeCount(minNode, minLink); // --- don't let time step go below an absolute minimum - if ( tMin < MinRouteStep ) tMin = MinRouteStep; //(5.1.008) + if ( tMin < MinRouteStep ) tMin = MinRouteStep; return tMin; } @@ -809,7 +831,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 <= 0.05 * Link[i].qFull + if ( q <= FUDGE //(5.1.013) || Conduit[k].a1 <= FUDGE || Link[i].froude <= 0.01 ) continue; diff --git a/src/enums.h b/src/enums.h index 568b22d92..e76f27519 100644 --- a/src/enums.h +++ b/src/enums.h @@ -9,6 +9,7 @@ // 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) // Author: L. Rossman // // Enumerated variables @@ -32,6 +33,10 @@ // 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. +// //----------------------------------------------------------------------------- //------------------------------------- @@ -213,7 +218,7 @@ //------------------------------------- // System-wide flow quantities //------------------------------------- -#define MAX_SYS_RESULTS 15 //(5.1.010) +#define MAX_SYS_RESULTS 15 enum SysFlowType { SYS_TEMPERATURE, // air temperature SYS_RAINFALL, // rainfall intensity @@ -229,12 +234,11 @@ enum SysFlowType { SYS_OUTFLOW, // outfall outflow SYS_STORAGE, // storage volume SYS_EVAP, // evaporation - SYS_PET}; // potential ET //(5.1.010) + SYS_PET}; // potential ET //------------------------------------- // Conduit flow classifications //------------------------------------- -// #define MAX_FLOW_CLASSES 7 //(5.1.008) enum FlowClassType { DRY, // dry conduit UP_DRY, // upstream end is dry @@ -243,13 +247,11 @@ enum SysFlowType { SUPCRITICAL, // super-critical flow UP_CRITICAL, // free-fall at upstream end DN_CRITICAL, // free-fall at downstream end - MAX_FLOW_CLASSES, // number of distinct flow classes //(5.1.008) - UP_FULL, // upstream end is full //(5.1.008) - DN_FULL, // downstream end is full //(5.1.008) - ALL_FULL}; // completely full //(5.1.008) - + MAX_FLOW_CLASSES, // number of distinct flow classes + UP_FULL, // upstream end is full + DN_FULL, // downstream end is full + ALL_FULL}; // completely full -//// Added to release 5.1.008. //// //(5.1.008) //------------------------ // Runoff flow categories //------------------------ @@ -360,6 +362,11 @@ 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 + enum InflowType { EXTERNAL_INFLOW, // user-supplied external inflow DRY_WEATHER_INFLOW, // user-supplied dry weather inflow @@ -417,7 +424,7 @@ enum CompatibilityType { SIDEFLOW_WEIR, // side flow weir VNOTCH_WEIR, // V-notch (triangular) weir TRAPEZOIDAL_WEIR, // trapezoidal weir - ROADWAY_WEIR}; // FHWA HDS-5 roadway weir //(5.1.010) + ROADWAY_WEIR}; // FHWA HDS-5 roadway weir enum CurveType { STORAGE_CURVE, // surf. area v. depth for storage node @@ -426,6 +433,7 @@ 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) 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) @@ -444,24 +452,24 @@ enum CompatibilityType { s_TREATMENT, s_CURVE, s_TIMESERIES, s_REPORT, 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, //(5.1.007) - s_ADJUST, s_EVENT}; //(5.1.011) + s_MAP, s_LID_CONTROL, s_LID_USAGE, s_GWF, + s_ADJUST, s_EVENT}; 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, - REPORT_STEP, ALLOW_PONDING, INERT_DAMPING, - SLOPE_WEIGHTING, VARIABLE_STEP, NORMAL_FLOW_LTD, - LENGTHENING_STEP, MIN_SURFAREA, COMPATIBILITY, - SKIP_STEADY_STATE, TEMPDIR, IGNORE_RAINFALL, - FORCE_MAIN_EQN, LINK_OFFSETS, MIN_SLOPE, - IGNORE_SNOWMELT, IGNORE_GWATER, IGNORE_ROUTING, - IGNORE_QUALITY, MAX_TRIALS, HEAD_TOL, - SYS_FLOW_TOL, LAT_FLOW_TOL, IGNORE_RDII, //(5.1.004) - MIN_ROUTE_STEP, NUM_THREADS}; //(5.1.008) + 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) + REPORT_STEP, ALLOW_PONDING, INERT_DAMPING, + SLOPE_WEIGHTING, VARIABLE_STEP, NORMAL_FLOW_LTD, + LENGTHENING_STEP, MIN_SURFAREA, COMPATIBILITY, + SKIP_STEADY_STATE, TEMPDIR, IGNORE_RAINFALL, + FORCE_MAIN_EQN, LINK_OFFSETS, MIN_SLOPE, + 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) enum NoYesType { NO, diff --git a/src/error.c b/src/error.c index ad92d0bf5..843823878 100644 --- a/src/error.c +++ b/src/error.c @@ -168,6 +168,17 @@ "\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" + //////////////////////////////////////////////////////////////////////////// // NOTE: Need to update ErrorMsgs[], ErrorCodes[], and ErrorType // (in error.h) whenever a new error message is added. @@ -185,7 +196,8 @@ char* ErrorMsgs[] = 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}; + ERR363, ERR401, ERR402, ERR403, ERR405, ERR501, ERR502, ERR503, ERR504, + ERR505, ERR506, ERR507, ERR508, ERR509}; int ErrorCodes[] = { 0, 101, 103, 105, 107, 108, 109, 110, 111, @@ -199,7 +211,8 @@ int ErrorCodes[] = 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}; + 363, 401, 402, 403, 405, 501, 502, 503, 504, + 505, 506, 507, 508, 509}; char ErrString[256]; diff --git a/src/error.h b/src/error.h index 48ca8e606..7ffef1b7e 100644 --- a/src/error.h +++ b/src/error.h @@ -160,6 +160,16 @@ enum ErrorType { 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 MAXERRMSG}; char* error_getMsg(int i); diff --git a/src/exfil.c b/src/exfil.c index 11edb25b9..58ef6f5b2 100644 --- a/src/exfil.c +++ b/src/exfil.c @@ -24,7 +24,7 @@ #define _CRT_SECURE_NO_DEPRECATE #include -#include +#include #include "headers.h" #include "infil.h" #include "exfil.h" @@ -119,14 +119,11 @@ void exfil_initState(int k) alast = a; } -//// Following code block added to release 5.1.011 //// //(5.1.011) // --- 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 @@ -159,10 +156,10 @@ double exfil_getLoss(TExfil* exfil, double tStep, double depth, double area) // --- find infiltration through bottom of unit if ( exfil->btmExfil->IMDmax == 0.0 ) { - exfilRate = exfil->btmExfil->Ks * Adjust.hydconFactor; //(5.1.008) + exfilRate = exfil->btmExfil->Ks * Adjust.hydconFactor; } else exfilRate = grnampt_getInfil(exfil->btmExfil, tStep, 0.0, depth, - MOD_GREEN_AMPT); //(5.1.010) + MOD_GREEN_AMPT); exfilRate *= exfil->btmArea; // --- find infiltration through sloped banks @@ -175,7 +172,7 @@ double exfil_getLoss(TExfil* exfil, double tStep, double depth, double area) // --- if infil. rate not a function of depth if ( exfil->btmExfil->IMDmax == 0.0 ) { - exfilRate += area * exfil->btmExfil->Ks * Adjust.hydconFactor; //(5.1.008) + exfilRate += area * exfil->btmExfil->Ks * Adjust.hydconFactor; } // --- infil. rate depends on depth above bank @@ -194,7 +191,7 @@ double exfil_getLoss(TExfil* exfil, double tStep, double depth, double area) // --- use Green-Ampt function for bank infiltration exfilRate += area * grnampt_getInfil(exfil->bankExfil, - tStep, 0.0, depth, MOD_GREEN_AMPT); //(5.1.010) + tStep, 0.0, depth, MOD_GREEN_AMPT); } } } diff --git a/src/flowrout.c b/src/flowrout.c index f1a3b1b10..7b2503d40 100644 --- a/src/flowrout.c +++ b/src/flowrout.c @@ -6,7 +6,7 @@ // Date: 03/19/14 (Build 5.1.001) // 09/15/14 (Build 5.1.007) // 03/19/15 (Build 5.1.008) -// 03/14/11 (Build 5.1.012) +// 03/14/17 (Build 5.1.012) // Author: L. Rossman (EPA) // M. Tryby (EPA) // @@ -53,7 +53,7 @@ static const double STOPTOL = 0.005; // storage updating stopping tolerance static void initLinkDepths(void); static void initNodeDepths(void); static void initNodes(void); -static void initLinks(int routingModel); //(5.1.008) +static void initLinks(int routingModel); static void validateTreeLayout(void); static void validateGeneralLayout(void); static void updateStorageState(int i, int j, int links[], double dt); @@ -95,7 +95,7 @@ void flowrout_init(int routingModel) // --- initialize node & link volumes initNodes(); - initLinks(routingModel); //(5.1.008) + initLinks(routingModel); } //============================================================================= @@ -417,8 +417,6 @@ void initLinkDepths() //============================================================================= -//// This function was modified for release 5.1.008. //// //(5.1.008) - void initNodes() // // Input: none @@ -466,8 +464,6 @@ void initNodes() //============================================================================= -//// This function was modified for release 5.1.008. //// //(5.1.008) - void initLinks(int routingModel) // // Input: none @@ -555,7 +551,7 @@ void updateStorageState(int i, int j, int links[], double dt) // that do not depend on storage depth at end of time step vFixed = Node[i].oldVolume + 0.5 * (Node[i].oldNetInflow + Node[i].inflow - - Node[i].outflow) * dt; //(5.1.007) + Node[i].outflow) * dt; d1 = Node[i].newDepth; // --- iterate finding outflow (which depends on depth) and subsequent @@ -565,7 +561,7 @@ void updateStorageState(int i, int j, int links[], double dt) while ( iter < MAXITER && !stopped ) { // --- find new volume from flow balance eqn. - v2 = vFixed - 0.5 * getStorageOutflow(i, j, links, dt) * dt; //(5.1.007) + v2 = vFixed - 0.5 * getStorageOutflow(i, j, links, dt) * dt; // --- limit volume to full volume if no ponding // and compute overflow rate @@ -575,7 +571,7 @@ void updateStorageState(int i, int j, int links[], double dt) { Node[i].overflow = (v2 - MAX(Node[i].oldVolume, Node[i].fullVolume)) / dt; - if ( Node[i].overflow < FUDGE ) Node[i].overflow = 0.0; //(5.1.012) + if ( Node[i].overflow < FUDGE ) Node[i].overflow = 0.0; if ( !AllowPonding || Node[i].pondedArea == 0.0 ) v2 = Node[i].fullVolume; } @@ -638,7 +634,6 @@ void setNewNodeState(int j, double dt) int canPond; // TRUE if ponding can occur at node double newNetInflow; // inflow - outflow at node (cfs) -//// Following section revised for release 5.1.012. //// //(5.1.012) // --- update terminal storage nodes if ( Node[j].type == STORAGE ) { @@ -646,10 +641,9 @@ void setNewNodeState(int j, double dt) updateStorageState(j, Nobjects[LINK], NULL, dt); return; } -////////////////////////////////////////////////////////// // --- update stored volume using mid-point integration - newNetInflow = Node[j].inflow - Node[j].outflow - Node[j].losses; //(5.1.007) + newNetInflow = Node[j].inflow - Node[j].outflow - Node[j].losses; Node[j].newVolume = Node[j].oldVolume + 0.5 * (Node[j].oldNetInflow + newNetInflow) * dt; if ( Node[j].newVolume < FUDGE ) Node[j].newVolume = 0.0; @@ -705,12 +699,12 @@ void setNewLinkState(int j) if ( Conduit[k].a1 >= Link[j].xsect.aFull ) { Conduit[k].capacityLimited = TRUE; - Conduit[k].fullState = ALL_FULL; //(5.1.008) + Conduit[k].fullState = ALL_FULL; } else { Conduit[k].capacityLimited = FALSE; - Conduit[k].fullState = 0; //(5.1.008) + Conduit[k].fullState = 0; } } } @@ -771,10 +765,9 @@ int steadyflow_execute(int j, double* qin, double* qout, double tStep) if ( Link[j].xsect.type == DUMMY ) Conduit[k].a1 = 0.0; else { - // --- subtract evap and infil losses from inflow - q -= link_getLossRate(j, q, tStep); //(5.1.008) - if ( q < 0.0 ) q = 0.0; - + // --- adjust flow for evap and infil losses + q -= link_getLossRate(j, q, tStep); + // --- flow can't exceed full flow if ( q > Link[j].qFull ) { @@ -791,11 +784,10 @@ int steadyflow_execute(int j, double* qin, double* qout, double tStep) } } Conduit[k].a2 = Conduit[k].a1; - + Conduit[k].q1Old = Conduit[k].q1; Conduit[k].q2Old = Conduit[k].q2; - - + Conduit[k].q1 = q; Conduit[k].q2 = q; (*qout) = q * Conduit[k].barrels; diff --git a/src/funcs.h b/src/funcs.h index 5d7f9f4b9..620c03f7f 100644 --- a/src/funcs.h +++ b/src/funcs.h @@ -7,6 +7,7 @@ // 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) // Author: L. Rossman (EPA) // M. Tryby (EPA) // @@ -23,10 +24,9 @@ // Build 5.1.010: // - New roadway_getInflow() function added. // -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// Project Manager Methods +// Build 5.1.013: +// - Additional arguments added to function stats_updateSubcatchStats. +// //----------------------------------------------------------------------------- void project_open(char *f1, char *f2, char *f3); void project_close(void); @@ -93,12 +93,12 @@ void statsrpt_writeReport(void); //----------------------------------------------------------------------------- int climate_readParams(char* tok[], int ntoks); int climate_readEvapParams(char* tok[], int ntoks); -int climate_readAdjustments(char* tok[], int ntoks); //(5.1.007) +int climate_readAdjustments(char* tok[], int ntoks); void climate_validate(void); void climate_openFile(void); void climate_initState(void); void climate_setState(DateTime aDate); -DateTime climate_getNextEvapDate(void); //(5.1.008) +DateTime climate_getNextEvapDate(void); //----------------------------------------------------------------------------- // Rainfall Processing Methods @@ -148,6 +148,7 @@ 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); @@ -197,8 +198,8 @@ void landuse_getInitBuildup(TLandFactor* landFactor, double* initBuildup, double landuse_getBuildup(int landuse, int pollut, double area, double curb, double buildup, double tStep); -double landuse_getWashoffLoad(int landuse, int p, double area, //(5.1.008) - TLandFactor landFactor[], double runoff, double vOutflow); //(5.1.008) +double landuse_getWashoffLoad(int landuse, int p, double area, + TLandFactor landFactor[], double runoff, double vOutflow); double landuse_getAvgBmpEffic(int j, int p); double landuse_getCoPollutLoad(int p, double washoff[]); @@ -213,7 +214,7 @@ int flowrout_execute(int links[], int routingModel, double tStep); void toposort_sortLinks(int links[]); int kinwave_execute(int link, double* qin, double* qout, double tStep); -void dynwave_validate(void); //(5.1.008) +void dynwave_validate(void); void dynwave_init(void); void dynwave_close(void); double dynwave_getRoutingStep(double fixedStep); @@ -240,7 +241,7 @@ int massbal_open(void); void massbal_close(void); void massbal_report(void); -void massbal_updateRunoffTotals(int type, double v); //(5.1.008) +void massbal_updateRunoffTotals(int type, double v); void massbal_updateLoadingTotals(int type, int pollut, double w); void massbal_updateGwaterTotals(double vInfil, double vUpperEvap, double vLowerEvap, double vLowerPerc, double vGwater); @@ -254,9 +255,11 @@ void massbal_addOutflowQual(int pollut, double mass, int isFlooded); void massbal_addNodeLosses(double evapLoss, double infilLoss); void massbal_addLinkLosses(double evapLoss, double infilLoss); void massbal_addReactedMass(int pollut, double mass); -void massbal_addSeepageLoss(int pollut, double seepLoss); //(5.1.008) -void massbal_addToFinalStorage(int pollut, double mass); //(5.1.008) +void massbal_addSeepageLoss(int pollut, double seepLoss); +void massbal_addToFinalStorage(int pollut, double mass); double massbal_getStepFlowError(void); +double massbal_getRunoffError(void); +double massbal_getFlowError(void); //----------------------------------------------------------------------------- // Simulation Statistics Methods @@ -268,13 +271,14 @@ 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, double runonVol, - double evapVol, double infilVol, double runoffVol, double runoff); -void stats_updateGwaterStats(int j, double infil, double evap, //(5.1.008) - double latFlow, double deepFlow, double theta, double waterTable, //(5.1.008) - double tStep); //(5.1.008) +void stats_updateSubcatchStats(int subcatch, double rainVol, + double runonVol, double evapVol, double infilVol, + double impervVol, double pervVol, double runoffVol, double runoff); //(5.1.013) +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); //(5.1.008) +void stats_updateMaxNodeDepth(int node, double depth); //----------------------------------------------------------------------------- // Raingage Methods @@ -304,13 +308,12 @@ double subcatch_getStorage(int subcatch); double subcatch_getDepth(int subcatch); void subcatch_getRunon(int subcatch); -void subcatch_addRunonFlow(int subcatch, double flow); //(5.1.008) +void subcatch_addRunonFlow(int subcatch, double flow); double subcatch_getRunoff(int subcatch, double tStep); double subcatch_getWtdOutflow(int subcatch, double wt); void subcatch_getResults(int subcatch, double wt, float x[]); -//// New functions added to release 5.1.008. //// //(5.1.008) //----------------------------------------------------------------------------- // Surface Pollutant Buildup/Washoff Methods //----------------------------------------------------------------------------- @@ -337,7 +340,6 @@ void node_setDividerCutoff(int node, int link); double node_getSurfArea(int node, double depth); double node_getDepth(int node, double volume); double node_getVolume(int node, double depth); -//double node_getPondedDepth(int node, double volume); removed //(5.1.008) double node_getPondedArea(int node, double depth); double node_getOutflow(int node, int link); @@ -352,7 +354,12 @@ 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_validate(int param, int type, int tSeries, + int basePat, double *cf); + void inflow_initDwfInflow(TDwfInflow* inflow); void inflow_initDwfPattern(int pattern); @@ -405,8 +412,8 @@ 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, double tStep); //(5.1.008) -char link_getFullState(double a1, double a2, double aFull); //(5.1.008) +double link_getLossRate(int link, double q, double tStep); +char link_getFullState(double a1, double a2, double aFull); void link_getResults(int link, double wt, float x[]); @@ -430,11 +437,11 @@ double xsect_getWofY(TXsect* xsect, double y); double xsect_getYcrit(TXsect* xsect, double q); //----------------------------------------------------------------------------- -// Culvert/Roadway Methods //(5.1.010) +// Culvert/Roadway Methods //----------------------------------------------------------------------------- double culvert_getInflow(int link, double q, double h); double roadway_getInflow(int link, double dir, double hcrest, double h1, - double h2); //(5.1.010) + double h2); //----------------------------------------------------------------------------- // Force Main Methods @@ -478,7 +485,6 @@ void table_deleteEntries(TTable* table); void table_init(TTable* table); int table_validate(TTable* table); -// table_interpolate now defined in table.c //(5.1.008) double table_lookup(TTable* table, double x); double table_lookupEx(TTable* table, double x); diff --git a/src/gage.c b/src/gage.c index d45a0a9b7..b8b0e65d0 100644 --- a/src/gage.c +++ b/src/gage.c @@ -5,6 +5,7 @@ // 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) // Author: L. Rossman // // Rain gage functions. @@ -12,6 +13,9 @@ // Build 5.1.007: // - Support for monthly rainfall adjustments added. // +// Build 5.1.013: +// - Validation no longer performed on unused gages. +// //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -213,8 +217,28 @@ void gage_validate(int j) // --- for gage with time series data: if ( Gage[j].dataSource == RAIN_TSERIES ) { - // --- check gage's recording interval against that of time series + // --- no validation for an unused gage + if ( !Gage[j].isUsed ) return; + + // --- see if gage uses same time series as another gage k = Gage[j].tSeries; + for (i=0; i= 0 ) { report_writeErrorMsg(ERR_RAIN_GAGE_TSERIES, Gage[j].ID); @@ -233,22 +257,6 @@ void gage_validate(int j) report_writeWarningMsg(WARN01, Gage[j].ID); WetStep = Gage[j].rainInterval; } - - // --- see if gage uses same time series as another gage - for (i=0; ioldFlow = 0.0; gw->newFlow = 0.0; - gw->evapLoss = 0.0; //(5.1.008) + gw->evapLoss = 0.0; // ... initial available infiltration volume into upper zone gw->maxInfilVol = (gw->surfElev - gw->waterTableElev) * @@ -489,9 +487,6 @@ void gwater_getGroundwater(int j, double evap, double infil, double tStep) // tStep = time step (sec) // Output: none // - -// Note: local "area" variable was replaced with shared variable "Area". // //(5.1.008) - { int n; // node exchanging groundwater double x[2]; // upper moisture content & lower depth @@ -502,8 +497,8 @@ void gwater_getGroundwater(int j, double evap, double infil, double tStep) // shared variables GW = Subcatch[j].groundwater; if ( GW == NULL ) return; - LatFlowExpr = Subcatch[j].gwLatFlowExpr; //(5.1.007) - DeepFlowExpr = Subcatch[j].gwDeepFlowExpr; //(5.1.007) + LatFlowExpr = Subcatch[j].gwLatFlowExpr; + DeepFlowExpr = Subcatch[j].gwDeepFlowExpr; A = Aquifer[GW->aquifer]; // --- get fraction of total area that is pervious @@ -604,9 +599,9 @@ void gwater_getGroundwater(int j, double evap, double infil, double tStep) // --- update GW mass balance updateMassBal(Area, tStep); - // --- update GW statistics //(5.1.008) - stats_updateGwaterStats(j, infil, GW->evapLoss, GWFlow, LowerLoss, //(5.1.008) - GW->theta, GW->lowerDepth + GW->bottomElev, tStep); //(5.1.008) + // --- update GW statistics + stats_updateGwaterStats(j, infil, GW->evapLoss, GWFlow, LowerLoss, + GW->theta, GW->lowerDepth + GW->bottomElev, tStep); } //============================================================================= @@ -637,8 +632,6 @@ void updateMassBal(double area, double tStep) //============================================================================= -//// This function was re-written for release 5.1.007. //// //(5.1.007) - void getFluxes(double theta, double lowerDepth) // // Input: upperVolume = vol. depth of upper zone (ft) @@ -801,7 +794,7 @@ double getUpperPerc(double theta, double upperDepth) dhdz = 1.0 + A.tensionSlope * 2.0 * delta / upperDepth; // --- compute upper zone percolation rate - HydCon = hydcon; //(5.1.010) + HydCon = hydcon; return hydcon * dhdz; } @@ -872,12 +865,12 @@ double getVariableValue(int varIndex) case gwvHCB: return Hstar * UCF(LENGTH); case gwvHGS: return TotalDepth * UCF(LENGTH); case gwvKS: return A.conductivity * UCF(RAINFALL); - case gwvK: return HydCon * UCF(RAINFALL); //(5.1.010) - case gwvTHETA:return Theta; //(5.1.008) - case gwvPHI: return A.porosity; //(5.1.008) - case gwvFI: return Infil * UCF(RAINFALL); //(5.1.008) - case gwvFU: return UpperPerc * UCF(RAINFALL); //(5.1.008) - case gwvA: return Area * UCF(LANDAREA); //(5.1.008) + case gwvK: return HydCon * UCF(RAINFALL); + case gwvTHETA:return Theta; + case gwvPHI: return A.porosity; + case gwvFI: return Infil * UCF(RAINFALL); + case gwvFU: return UpperPerc * UCF(RAINFALL); + case gwvA: return Area * UCF(LANDAREA); default: return 0.0; } } diff --git a/src/hash.c b/src/hash.c index 5c73a4153..4915733cb 100644 --- a/src/hash.c +++ b/src/hash.c @@ -15,7 +15,7 @@ // HTfree() - frees a hash table //----------------------------------------------------------------------------- -#include +#include #include #include "hash.h" #define UCHAR(x) (((x) >= 'a' && (x) <= 'z') ? ((x)&~32) : (x)) diff --git a/src/hotstart.c b/src/hotstart.c index 2227b30b4..be81670bd 100644 --- a/src/hotstart.c +++ b/src/hotstart.c @@ -55,7 +55,7 @@ static int fileVersion; // External functions (declared in funcs.h) //----------------------------------------------------------------------------- // hotstart_open (called by swmm_start in swmm5.c) -// hotstart_close (called by swmm_end in swmm5.c) //(5.1.005) +// hotstart_close (called by swmm_end in swmm5.c) //----------------------------------------------------------------------------- // Function declarations @@ -76,15 +76,6 @@ int hotstart_open() // --- open hot start files if ( !openHotstartFile1() ) return FALSE; //input hot start file if ( !openHotstartFile2() ) return FALSE; //output hot start file - - //// Following lines removed. //// //(5.1.005) - //if ( Fhotstart1.file ) - //{ - // readRunoff(); - // readRouting(); - // fclose(Fhotstart1.file); - //} - return TRUE; } @@ -120,7 +111,7 @@ int openHotstartFile1() char fStampx[] = "SWMM5-HOTSTARTx"; char fileStamp2[] = "SWMM5-HOTSTART2"; char fileStamp3[] = "SWMM5-HOTSTART3"; - char fileStamp4[] = "SWMM5-HOTSTART4"; //(5.1.008) + char fileStamp4[] = "SWMM5-HOTSTART4"; // --- try to open the file if ( Fhotstart1.mode != USE_FILE ) return TRUE; @@ -132,7 +123,7 @@ int openHotstartFile1() // --- check that file contains proper header records fread(fStampx, sizeof(char), strlen(fileStamp2), Fhotstart1.file); - if ( strcmp(fStampx, fileStamp4) == 0 ) fileVersion = 4; //(5.1.008) + if ( strcmp(fStampx, fileStamp4) == 0 ) fileVersion = 4; else if ( strcmp(fStampx, fileStamp3) == 0 ) fileVersion = 3; else if ( strcmp(fStampx, fileStamp2) == 0 ) fileVersion = 2; else @@ -153,12 +144,12 @@ int openHotstartFile1() nPollut = -1; nLandUses = -1; flowUnits = -1; - if ( fileVersion >= 2 ) //(5.1.002) + if ( fileVersion >= 2 ) { fread(&nSubcatch, sizeof(int), 1, Fhotstart1.file); } else nSubcatch = Nobjects[SUBCATCH]; - if ( fileVersion >= 3 ) //(5.1.008) + if ( fileVersion >= 3 ) { fread(&nLandUses, sizeof(int), 1, Fhotstart1.file); } @@ -179,7 +170,7 @@ int openHotstartFile1() } // --- read contents of the file and close it - if ( fileVersion >= 3 ) readRunoff(); //(5.1.008) + if ( fileVersion >= 3 ) readRunoff(); readRouting(); fclose(Fhotstart1.file); if ( ErrorCode ) return FALSE; @@ -201,7 +192,7 @@ int openHotstartFile2() int nLinks; int nPollut; int flowUnits; - char fileStamp[] = "SWMM5-HOTSTART4"; //(5.1.008) + char fileStamp[] = "SWMM5-HOTSTART4"; // --- try to open file if ( Fhotstart2.mode != SAVE_FILE ) return TRUE; @@ -246,14 +237,12 @@ void saveRouting() x[1] = (float)Node[i].newLatFlow; fwrite(x, sizeof(float), 2, Fhotstart2.file); -//// New code added to release 5.1.008. //// //(5.1.008) if ( Node[i].type == STORAGE ) { j = Node[i].subIndex; x[0] = (float)Storage[j].hrt; fwrite(&x[0], sizeof(float), 1, Fhotstart2.file); } -//// for (j = 0; j < Nobjects[POLLUT]; j++) { @@ -317,14 +306,12 @@ void readRouting() if ( !readFloat(&x, f) ) return; Node[i].newLatFlow = x; -//// New code added to release 5.1.008. //// //(5.1.008) if ( fileVersion >= 4 && Node[i].type == STORAGE ) { if ( !readFloat(&x, f) ) return; j = Node[i].subIndex; Storage[j].hrt = x; } -//// for (j = 0; j < Nobjects[POLLUT]; j++) { @@ -352,12 +339,11 @@ void readRouting() if ( !readFloat(&x, f) ) return; Link[i].setting = x; -//// Following code section moved to here. //// //(5.1.011) // --- set link's target setting to saved setting Link[i].targetSetting = x; link_setTargetSetting(i); link_setSetting(i, 0.0); -//// + for (j = 0; j < Nobjects[POLLUT]; j++) { if ( !readFloat(&x, f) ) return; @@ -413,7 +399,7 @@ void saveRunoff(void) } // Water quality - if ( Nobjects[POLLUT] > 0 ) //(5.1.008) + if ( Nobjects[POLLUT] > 0 ) { // Runoff quality for (j=0; j 0 ) //(5.1.008) + if ( Nobjects[POLLUT] > 0 ) { // Runoff quality for (j=0; j -#include "malloc.h" -#include "stdlib.h" +#include #include "headers.h" #include "infil.h" @@ -45,6 +49,7 @@ TGrnAmpt* GAInfil = NULL; TCurveNum* CNInfil = NULL; static double Fumax; // saturated water volume in upper soil zone (ft) +static double InfilFactor; //(5.1.013) //----------------------------------------------------------------------------- // External Functions (declared in infil.h) @@ -74,11 +79,10 @@ static double horton_getInfil(THorton *infil, double tstep, double irate, static double modHorton_getInfil(THorton *infil, double tstep, double irate, double depth); -//// Revised set of functions for Green-Ampt infiltration //// //(5.1.007) static void grnampt_getState(TGrnAmpt *infil, double x[]); static void grnampt_setState(TGrnAmpt *infil, double x[]); static double grnampt_getUnsatInfil(TGrnAmpt *infil, double tstep, - double irate, double depth, int modelType); //(5.1.010) + double irate, double depth, int modelType); static double grnampt_getSatInfil(TGrnAmpt *infil, double tstep, double irate, double depth); static double grnampt_getF2(double f1, double c1, double ks, double ts); @@ -108,7 +112,7 @@ void infil_create(int subcatchCount, int model) if ( HortInfil == NULL ) ErrorCode = ERR_MEMORY; break; case GREEN_AMPT: - case MOD_GREEN_AMPT: //(5.1.010) + case MOD_GREEN_AMPT: GAInfil = (TGrnAmpt *) calloc(subcatchCount, sizeof(TGrnAmpt)); if ( GAInfil == NULL ) ErrorCode = ERR_MEMORY; break; @@ -118,6 +122,7 @@ void infil_create(int subcatchCount, int model) break; default: ErrorCode = ERR_MEMORY; } + InfilFactor = 1.0; //(5.1.013) } //============================================================================= @@ -155,11 +160,11 @@ int infil_readParams(int m, char* tok[], int ntoks) if ( j < 0 ) return error_setInpError(ERR_NAME, tok[0]); // --- number of input tokens depends on infiltration model m - if ( m == HORTON ) n = 5; - else if ( m == MOD_HORTON ) n = 5; - else if ( m == GREEN_AMPT ) n = 4; - else if ( m == MOD_GREEN_AMPT ) n = 4; //(5.1.010) - else if ( m == CURVE_NUMBER ) n = 4; + if ( m == HORTON ) n = 5; + else if ( m == MOD_HORTON ) n = 5; + else if ( m == GREEN_AMPT ) n = 4; + else if ( m == MOD_GREEN_AMPT ) n = 4; + else if ( m == CURVE_NUMBER ) n = 4; else return 0; if ( ntoks < n ) return error_setInpError(ERR_ITEMS, ""); @@ -186,7 +191,7 @@ int infil_readParams(int m, char* tok[], int ntoks) case MOD_HORTON: status = horton_setParams(&HortInfil[j], x); break; case GREEN_AMPT: - case MOD_GREEN_AMPT: //(5.1.010) + case MOD_GREEN_AMPT: status = grnampt_setParams(&GAInfil[j], x); break; case CURVE_NUMBER: status = curvenum_setParams(&CNInfil[j], x); @@ -212,7 +217,7 @@ void infil_initState(int j, int m) case HORTON: case MOD_HORTON: horton_initState(&HortInfil[j]); break; case GREEN_AMPT: - case MOD_GREEN_AMPT: //(5.1.010) + case MOD_GREEN_AMPT: grnampt_initState(&GAInfil[j]); break; case CURVE_NUMBER: curvenum_initState(&CNInfil[j]); break; } @@ -233,7 +238,7 @@ void infil_getState(int j, int m, double x[]) case HORTON: case MOD_HORTON: horton_getState(&HortInfil[j], x); break; case GREEN_AMPT: - case MOD_GREEN_AMPT: //(5.1.010) + case MOD_GREEN_AMPT: grnampt_getState(&GAInfil[j],x); break; case CURVE_NUMBER: curvenum_getState(&CNInfil[j], x); break; } @@ -254,7 +259,7 @@ void infil_setState(int j, int m, double x[]) case HORTON: case MOD_HORTON: horton_setState(&HortInfil[j], x); break; case GREEN_AMPT: - case MOD_GREEN_AMPT: //(5.1.010) + case MOD_GREEN_AMPT: grnampt_setState(&GAInfil[j],x); break; case CURVE_NUMBER: curvenum_setState(&CNInfil[j], x); break; } @@ -262,6 +267,34 @@ void infil_setState(int j, int m, double x[]) //============================================================================= +//// New function added for release 5.1.013. //// //(5.1.013) + +void infil_setInfilFactor(int j) +// +// Input: j = subcatchment index +// Output: none +// Purpose: assigns a value to the infiltration adjustment factor. +{ + int m; + int p; + + // ... set factor to the global conductivity adjustment factor + InfilFactor = Adjust.hydconFactor; + + // ... override global factor with subcatchment's adjustment if assigned + if (j >= 0) + { + p = Subcatch[j].infilPattern; + if (p >= 0 && Pattern[p].type == MONTHLY_PATTERN) + { + m = datetime_monthOfYear(getDateTime(OldRunoffTime)) - 1; + InfilFactor = Pattern[p].factor[m]; + } + } +} + +//============================================================================= + double infil_getInfil(int j, int m, double tstep, double rainfall, double runon, double depth) // @@ -285,8 +318,8 @@ double infil_getInfil(int j, int m, double tstep, double rainfall, depth); case GREEN_AMPT: - case MOD_GREEN_AMPT: //(5.1.010) - return grnampt_getInfil(&GAInfil[j], tstep, rainfall+runon, depth, m); //(5.1.010) + case MOD_GREEN_AMPT: + return grnampt_getInfil(&GAInfil[j], tstep, rainfall+runon, depth, m); case CURVE_NUMBER: depth += runon / tstep; @@ -374,11 +407,11 @@ double horton_getInfil(THorton *infil, double tstep, double irate, double depth) double fa, fp = 0.0; double Fp, F1, t1, tlim, ex, kt; double FF, FF1, r; - double f0 = infil->f0 * Adjust.hydconFactor; //(5.1.008) - double fmin = infil->fmin * Adjust.hydconFactor; //(5.1.008) + double f0 = infil->f0 * InfilFactor; //(5.1.013) + double fmin = infil->fmin * InfilFactor; //(5.1.013) double Fmax = infil->Fmax; double tp = infil->tp; - double df = f0 - fmin; //(5.1.008) + double df = f0 - fmin; double kd = infil->decay; double kr = infil->regen * Evap.recoveryFactor; @@ -386,7 +419,7 @@ double horton_getInfil(THorton *infil, double tstep, double irate, double depth) if ( df < 0.0 || kd < 0.0 || kr < 0.0 ) return 0.0; if ( df == 0.0 || kd == 0.0 ) { - fp = f0; //(5.1.008) + fp = f0; fa = irate + depth / tstep; if ( fp > fa ) fp = fa; return MAX(0.0, fp); @@ -412,7 +445,7 @@ double horton_getInfil(THorton *infil, double tstep, double irate, double depth) F1 = fmin * t1 + df / kd * (1.0 - exp(-kd * t1)); } fp = (F1 - Fp) / tstep; - fp = MAX(fp, fmin); //(5.1.011) + fp = MAX(fp, fmin); // --- limit infil rate to available infil if ( fp > fa ) fp = fa; @@ -485,9 +518,9 @@ double modHorton_getInfil(THorton *infil, double tstep, double irate, // --- assign local variables double f = 0.0; double fp, fa; - double f0 = infil->f0 * Adjust.hydconFactor; //(5.1.008) - double fmin = infil->fmin * Adjust.hydconFactor; //(5.1.008) - double df = f0 - fmin; //(5.1.008) + double f0 = infil->f0 * InfilFactor; //(5.1.013) + double fmin = infil->fmin * InfilFactor; //(5.1.013) + double df = f0 - fmin; double kd = infil->decay; double kr = infil->regen * Evap.recoveryFactor; @@ -495,7 +528,7 @@ double modHorton_getInfil(THorton *infil, double tstep, double irate, if ( df < 0.0 || kd < 0.0 || kr < 0.0 ) return 0.0; if ( df == 0.0 || kd == 0.0 ) { - fp = f0; //(5.1.008) + fp = f0; fa = irate + depth / tstep; if ( fp > fa ) fp = fa; return MAX(0.0, fp); @@ -511,21 +544,21 @@ double modHorton_getInfil(THorton *infil, double tstep, double irate, if ( infil->Fmax > 0.0 && infil->Fe >= infil->Fmax ) return 0.0; // --- potential infiltration - fp = f0 - kd * infil->Fe; //(5.1.008) - fp = MAX(fp, fmin); //(5.1.011) + fp = f0 - kd * infil->Fe; + fp = MAX(fp, fmin); // --- actual infiltration f = MIN(fa, fp); // --- new cumulative infiltration minus seepage - infil->Fe += MAX((f - fmin), 0.0) * tstep; //(5.1.008) + infil->Fe += MAX((f - fmin), 0.0) * tstep; if ( infil->Fmax > 0.0 ) infil->Fe = MAX(infil->Fe, infil->Fmax); } // --- reduce cumulative infiltration for dry condition else if (kr > 0.0) { - infil->Fe *= exp(-kr * tstep); //(5.1.007) + infil->Fe *= exp(-kr * tstep); infil->Fe = MAX(infil->Fe, 0.0); } return f; @@ -543,7 +576,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; //(5.1.007) + if ( p[0] < 0.0 || p[1] <= 0.0 || p[2] < 0.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 @@ -565,10 +598,10 @@ void grnampt_initState(TGrnAmpt *infil) { if (infil == NULL) return; infil->IMD = infil->IMDmax; - infil->Fu = 0.0; //(5.1.007) + infil->Fu = 0.0; infil->F = 0.0; infil->Sat = FALSE; - infil->T = 0.0; //(5.1.010) + infil->T = 0.0; } void grnampt_getState(TGrnAmpt *infil, double x[]) @@ -591,10 +624,8 @@ void grnampt_setState(TGrnAmpt *infil, double x[]) //============================================================================= -//// This function was re-written for release 5.1.007 //// //(5.1.007) - double grnampt_getInfil(TGrnAmpt *infil, double tstep, double irate, - double depth, int modelType) //(5.1.010) + double depth, int modelType) // // Input: infil = ptr. to Green-Ampt infiltration object // tstep = time step (sec), @@ -602,14 +633,14 @@ double grnampt_getInfil(TGrnAmpt *infil, double tstep, double irate, // = rainfall + snowmelt + runon, // does not include ponded water (added on below) // depth = depth of ponded water (ft) -// modelType = either GREEN_AMPT or MOD_GREEN_AMPT //(5.1.010) +// modelType = either GREEN_AMPT or MOD_GREEN_AMPT // Output: returns infiltration rate (ft/sec) // Purpose: computes Green-Ampt infiltration for a subcatchment // or a storage node. // { // --- find saturated upper soil zone water volume - Fumax = infil->IMDmax * infil->Lu * sqrt(Adjust.hydconFactor); //(5.1.011) + Fumax = infil->IMDmax * infil->Lu * sqrt(InfilFactor); //(5.1.013) // --- reduce time until next event infil->T -= tstep; @@ -621,8 +652,6 @@ double grnampt_getInfil(TGrnAmpt *infil, double tstep, double irate, //============================================================================= -//// New function added to release 5.1.007 //// //(5.1.007) - double grnampt_getUnsatInfil(TGrnAmpt *infil, double tstep, double irate, double depth, int modelType) // @@ -632,15 +661,15 @@ double grnampt_getUnsatInfil(TGrnAmpt *infil, double tstep, double irate, // = rainfall + snowmelt + runon, // does not include ponded water (added on below) // depth = depth of ponded water (ft) -// modelType = either GREEN_AMPT or MOD_GREEN_AMPT //(5.1.010) +// modelType = either GREEN_AMPT or MOD_GREEN_AMPT // Output: returns infiltration rate (ft/sec) // Purpose: computes Green-Ampt infiltration when upper soil zone is // unsaturated. // { double ia, c1, F2, dF, Fs, kr, ts; - double ks = infil->Ks * Adjust.hydconFactor; //(5.1.008) - double lu = infil->Lu * sqrt(Adjust.hydconFactor); //(5.1.011) + double ks = infil->Ks * InfilFactor; //(5.1.013) + double lu = infil->Lu * sqrt(InfilFactor); //(5.1.013) // --- get available infiltration rate (rainfall + ponded water) ia = irate + depth / tstep; @@ -650,7 +679,7 @@ double grnampt_getUnsatInfil(TGrnAmpt *infil, double tstep, double irate, if ( ia == 0.0 ) { if ( infil->Fu <= 0.0 ) return 0.0; - kr = lu / 90000.0 * Evap.recoveryFactor; //(5.1.011) + kr = lu / 90000.0 * Evap.recoveryFactor; dF = kr * Fumax * tstep; infil->F -= dF; infil->Fu -= dF; @@ -665,32 +694,32 @@ double grnampt_getUnsatInfil(TGrnAmpt *infil, double tstep, double irate, // --- if new wet event begins then reset IMD & F if ( infil->T <= 0.0 ) { - infil->IMD = (Fumax - infil->Fu) / lu; //(5.1.011) + infil->IMD = (Fumax - infil->Fu) / lu; infil->F = 0.0; } return 0.0; } // --- rainfall does not exceed Ksat - if ( ia <= ks ) //(5.1.008) + if ( ia <= ks ) { dF = ia * tstep; infil->F += dF; infil->Fu += dF; infil->Fu = MIN(infil->Fu, Fumax); - if ( modelType == GREEN_AMPT && infil->T <= 0.0 ) //(5.1.010) + if ( modelType == GREEN_AMPT && infil->T <= 0.0 ) { - infil->IMD = (Fumax - infil->Fu) / lu; //(5.1.011) + infil->IMD = (Fumax - infil->Fu) / lu; infil->F = 0.0; } return ia; } // --- rainfall exceeds Ksat; renew time to drain upper zone - infil->T = 5400.0 / lu / Evap.recoveryFactor; //(5.1.011) + infil->T = 5400.0 / lu / Evap.recoveryFactor; // --- find volume needed to saturate surface layer - Fs = ks * (infil->S + depth) * infil->IMD / (ia - ks); //(5.1.008) + Fs = ks * (infil->S + depth) * infil->IMD / (ia - ks); // --- surface layer already saturated if ( infil->F > Fs ) @@ -716,7 +745,7 @@ double grnampt_getUnsatInfil(TGrnAmpt *infil, double tstep, double irate, // --- compute new total volume infiltrated c1 = (infil->S + depth) * infil->IMD; - F2 = grnampt_getF2(Fs, c1, ks, ts); //(5.1.008) + F2 = grnampt_getF2(Fs, c1, ks, ts); if ( F2 > Fs + ia*ts ) F2 = Fs + ia*ts; // --- compute infiltration rate @@ -730,8 +759,6 @@ double grnampt_getUnsatInfil(TGrnAmpt *infil, double tstep, double irate, //============================================================================= -//// New function added to release 5.1.007 //// //(5.1.007) - double grnampt_getSatInfil(TGrnAmpt *infil, double tstep, double irate, double depth) // @@ -747,19 +774,19 @@ double grnampt_getSatInfil(TGrnAmpt *infil, double tstep, double irate, // { double ia, c1, dF, F2; - double ks = infil->Ks * Adjust.hydconFactor; //(5.1.008) - double lu = infil->Lu * sqrt(Adjust.hydconFactor); //(5.1.011) + double ks = infil->Ks * InfilFactor; //(5.1.013) + double lu = infil->Lu * sqrt(InfilFactor); //(5.1.013) // --- get available infiltration rate (rainfall + ponded water) ia = irate + depth / tstep; if ( ia < ZERO ) return 0.0; // --- re-set new event recovery time - infil->T = 5400.0 / lu / Evap.recoveryFactor; //(5.1.011) + infil->T = 5400.0 / lu / Evap.recoveryFactor; // --- solve G-A equation for new cumulative infiltration volume (F2) c1 = (infil->S + depth) * infil->IMD; - F2 = grnampt_getF2(infil->F, c1, ks, tstep); //(5.1.008) + F2 = grnampt_getF2(infil->F, c1, ks, tstep); dF = F2 - infil->F; // --- all available water infiltrates -- set saturated state to false @@ -778,8 +805,6 @@ double grnampt_getSatInfil(TGrnAmpt *infil, double tstep, double irate, //============================================================================= -//// This function was re-written for release 5.1.007. //// //(5.1.007) - double grnampt_getF2(double f1, double c1, double ks, double ts) // // Input: f1 = old infiltration volume (ft) @@ -843,10 +868,6 @@ int curvenum_setParams(TCurveNum *infil, double p[]) infil->Smax = (1000.0 / p[0] - 10.0) / 12.0; if ( infil->Smax < 0.0 ) return FALSE; -//// ---- linear regeneration constant and inter-event --- //// -//// time now computed directly from drying time; //// -//// hydraulic conductivity no longer used. //// - // --- convert drying time (days) to a regeneration const. (1/sec) if ( p[2] > 0.0 ) infil->regen = 1.0 / (p[2] * SECperDAY); else return FALSE; @@ -978,5 +999,3 @@ double curvenum_getInfil(TCurveNum *infil, double tstep, double irate, infil->f = f1; return f1; } - -//============================================================================= diff --git a/src/infil.h b/src/infil.h index 2e999571c..cf208e234 100644 --- a/src/infil.h +++ b/src/infil.h @@ -6,12 +6,16 @@ // 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) // Author: L. Rossman (US EPA) // // Public interface for infiltration functions. // // Build 5.1.010: // - New Modified Green Ampt infiltration option added. +// +// Build 5.1.013: +// - New function infil_setInfilFactor() added. //----------------------------------------------------------------------------- #ifndef INFIL_H @@ -24,7 +28,7 @@ enum InfilType { HORTON, // Horton infiltration MOD_HORTON, // Modified Horton infiltration GREEN_AMPT, // Green-Ampt infiltration - MOD_GREEN_AMPT, // Modified Green-Ampt infiltration //(5.1.010) + MOD_GREEN_AMPT, // Modified Green-Ampt infiltration CURVE_NUMBER}; // SCS Curve Number infiltration //--------------------- @@ -46,8 +50,6 @@ typedef struct //------------------------- // Green-Ampt Infiltration //------------------------- - -// ---- Some variable names changed for release 5.1.007 ---- //(5.1.007) typedef struct { double S; // avg. capillary suction (ft) @@ -97,12 +99,13 @@ int infil_readParams(int model, char* tok[], int ntoks); void infil_initState(int area, int model); void infil_getState(int j, int m, double x[]); void infil_setState(int j, int m, double x[]); +void infil_setInfilFactor(int j); //(5.1.013) double infil_getInfil(int area, int model, double tstep, double rainfall, double runon, double depth); int grnampt_setParams(TGrnAmpt *infil, double p[]); void grnampt_initState(TGrnAmpt *infil); double grnampt_getInfil(TGrnAmpt *infil, double tstep, double irate, - double depth, int modelType); //(5.1.010) + double depth, int modelType); #endif diff --git a/src/inflow.c b/src/inflow.c index bf70344d7..e11e15086 100644 --- a/src/inflow.c +++ b/src/inflow.c @@ -49,7 +49,6 @@ int inflow_readExtInflow(char* tok[], int ntoks) double cf = 1.0; // units conversion factor double sf = 1.0; // scaling factor double baseline = 0.0; // baseline value - TExtInflow* inflow; // external inflow object // --- find index of node receiving the inflow if ( ntoks < 3 ) return error_setInpError(ERR_ITEMS, ""); @@ -72,15 +71,14 @@ int inflow_readExtInflow(char* tok[], int ntoks) Tseries[tseries].refersTo = EXTERNAL_INFLOW; } - // --- assign type & cf values for a FLOW inflow - if ( param == -1 ) - { - type = FLOW_INFLOW; - cf = 1.0/UCF(FLOW); - } + // --- assign type & cf values for a FLOW inflow + if (param == -1) + { + type = FLOW_INFLOW; + } // --- do the same for a pollutant inflow - else if ( ntoks >= 4 ) + if ( ntoks >= 4 && param > -1) { if ( match(tok[3], w_CONCEN) ) type = CONCEN_INFLOW; else if ( match(tok[3], w_MASS) ) type = MASS_INFLOW; @@ -116,37 +114,117 @@ int inflow_readExtInflow(char* tok[], int ntoks) { basePat = project_findObject(TIMEPATTERN, tok[7]); if ( basePat < 0 ) return error_setInpError(ERR_NAME, tok[7]); - } + } + + return(inflow_setExtInflow(j, param, type, tseries, basePat, + cf, baseline, sf)); +} - // --- include LperFT3 term in conversion factor for MASS_INFLOW - if ( type == MASS_INFLOW ) cf /= LperFT3; +int inflow_validate(int param, int type, int tseries, int basePat, double *cf) +// +// Purpose: Validates Inflow +// Input: param = -1 for Flow or Index of Pollutant +// type = FLOW_INFLOW, CONCEN_INFLOW or MASS_INFLOW +// tSeries = Time Series Index +// basePat = Base Pattern Index +// Output: cf = Unit Conversion +// Return: returns Error Code +{ + int errcode = 0; + // Validate param + if (param >= Nobjects[POLLUT]) + { + errcode = ERR_API_POLLUT_INDEX; + } + // Validate Type + else if (type != FLOW_INFLOW + && type != CONCEN_INFLOW + && type != MASS_INFLOW) + { + errcode = ERR_KEYWORD; + } + // Validate Timeseries Index + else if (tseries >= Nobjects[TSERIES]) + { + errcode = ERR_API_TSERIES_INDEX; + } + // Validate Timepattern Index + else if (basePat >= Nobjects[TIMEPATTERN]) + { + errcode = ERR_API_PATTERN_INDEX; + } + else + { + // --- assign type & cf values for a FLOW inflow + if ( type == FLOW_INFLOW ) + { + *cf = 1.0/UCF(FLOW); + } + // --- include LperFT3 term in conversion factor for MASS_INFLOW + else if ( type == MASS_INFLOW ) + { + *cf /= LperFT3; + } + } + + return(errcode); +} - // --- check if an external inflow object for this constituent already exists - inflow = Node[j].extInflow; - while ( inflow ) - { - if ( inflow->param == param ) break; - inflow = inflow->next; - } - // --- if it doesn't exist, then create it - if ( inflow == NULL ) - { - inflow = (TExtInflow *) malloc(sizeof(TExtInflow)); - if ( inflow == NULL ) return error_setInpError(ERR_MEMORY, ""); - inflow->next = Node[j].extInflow; - Node[j].extInflow = inflow; - } +int inflow_setExtInflow(int j, int param, int type, int tseries, int basePat, + double cf, double baseline, double sf) +// Purpose: This function assigns property values to the inflow object +// Inputs: j = Node index +// param = FLOW (-1) or pollutant index +// type = FLOW, CONCEN or MASS inflow +// tSeries = time series index +// basePat = baseline pattern +// cf = units conversion factor +// baseline = baseline inflow value +// sf = scaling factor +// Return: returns Error Code - // --- assign property values to the inflow object - inflow->param = param; - inflow->type = type; - inflow->tSeries = tseries; - inflow->cFactor = cf; - inflow->sFactor = sf; - inflow->baseline = baseline; - inflow->basePat = basePat; - return 0; +{ + int errcode = 0; + + // Validate Inflow + errcode = inflow_validate(param, type, tseries, basePat, &cf); + + if (errcode == 0) + { + TExtInflow* inflow; // external inflow object + + // --- check if an external inflow object for this constituent already exists + inflow = Node[j].extInflow; + while ( inflow ) + { + if ( inflow->param == param ) break; + inflow = inflow->next; + } + + // --- if it doesn't exist, then create it + if ( inflow == NULL ) + { + inflow = (TExtInflow *) malloc(sizeof(TExtInflow)); + if ( inflow == NULL ) + { + return error_setInpError(ERR_MEMORY, ""); + } + inflow->next = Node[j].extInflow; + Node[j].extInflow = inflow; + } + + // Assigning Values to the inflow object + inflow->param = param; + inflow->type = type; + inflow->tSeries = tseries; + inflow->cFactor = cf; + inflow->sFactor = sf; + inflow->baseline = baseline; + inflow->basePat = basePat; + inflow->extIfaceInflow = 0.0; + } + return(errcode); } //============================================================================= @@ -187,6 +265,7 @@ 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 if ( p >= 0 ) { @@ -196,7 +275,7 @@ double inflow_getExtInflow(TExtInflow* inflow, DateTime aDate) blv *= inflow_getPatternFactor(p, month, day, hour); } if ( k >= 0 ) tsv = table_tseriesLookup(&Tseries[k], aDate, FALSE) * sf; - return cf * (tsv + blv); + return cf * (tsv + blv) + cf * extIfaceInflow; } //============================================================================= @@ -450,4 +529,4 @@ double inflow_getPatternFactor(int p, int month, int day, int hour) return 1.0; } -//============================================================================= +//============================================================================= \ No newline at end of file diff --git a/src/input.c b/src/input.c index c2bcbf2b0..b36fd4289 100644 --- a/src/input.c +++ b/src/input.c @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include "headers.h" #include "lid.h" @@ -39,7 +39,7 @@ static int Ntokens; // Number of tokens in line of input static int Mobjects[MAX_OBJ_TYPES]; // Working number of objects of each type static int Mnodes[MAX_NODE_TYPES]; // Working number of node objects static int Mlinks[MAX_LINK_TYPES]; // Working number of link objects -static int Mevents; // Working number of event periods //(5.1.011) +static int Mevents; // Working number of event periods //----------------------------------------------------------------------------- // External Functions (declared in funcs.h) @@ -58,8 +58,7 @@ static int readTitle(char* line); static int readControl(char* tok[], int ntoks); static int readNode(int type); static int readLink(int type); -static int readEvent(char* tok[], int ntoks); //(5.1.011) - +static int readEvent(char* tok[], int ntoks); //============================================================================= @@ -158,7 +157,7 @@ int input_readData() for (i = 0; i < MAX_OBJ_TYPES; i++) Mobjects[i] = 0; for (i = 0; i < MAX_NODE_TYPES; i++) Mnodes[i] = 0; for (i = 0; i < MAX_LINK_TYPES; i++) Mlinks[i] = 0; - Mevents = 0; //(5.1.011) + Mevents = 0; // --- initialize starting date for all time series for ( i = 0; i < Nobjects[TSERIES]; i++ ) @@ -433,7 +432,7 @@ int addObject(int objType, char* id) } break; - case s_EVENT: NumEvents++; break; //(5.1.011) + case s_EVENT: NumEvents++; break; } return errcode; } @@ -466,8 +465,8 @@ int parseLine(int sect, char *line) case s_EVAP: return climate_readEvapParams(Tok, Ntokens); - case s_ADJUST: //(5.1.007) - return climate_readAdjustments(Tok, Ntokens); //(5.1.007) + case s_ADJUST: + return climate_readAdjustments(Tok, Ntokens); case s_SUBCATCH: j = Mobjects[SUBCATCH]; @@ -596,7 +595,7 @@ int parseLine(int sect, char *line) return lid_readGroupParams(Tok, Ntokens); case s_EVENT: - return readEvent(Tok, Ntokens); //(5.1.011) + return readEvent(Tok, Ntokens); default: return 0; } @@ -717,8 +716,6 @@ int readLink(int type) //============================================================================= -//// This function was added to release 5.1.011. //// //(5.1.011) - int readEvent(char* tok[], int ntoks) { DateTime x[4]; diff --git a/src/keywords.c b/src/keywords.c index f240aecc7..7ae5b2d5a 100644 --- a/src/keywords.c +++ b/src/keywords.c @@ -9,6 +9,7 @@ // 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) // Author: L. Rossman // // Exportable keyword dictionary @@ -33,6 +34,9 @@ // 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. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -41,7 +45,7 @@ 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_CONTROLS, w_SHAPE, w_WEIR, //(5.1.013) w_PUMP1, w_PUMP2, w_PUMP3, w_PUMP4, NULL}; char* DividerTypeWords[] = { w_CUTOFF, w_TABULAR, w_WEIR, w_OVERFLOW, NULL}; char* EvapTypeWords[] = { w_CONSTANT, w_MONTHLY, w_TIMESERIES, @@ -54,7 +58,7 @@ char* FlowUnitWords[] = { w_CFS, w_GPM, w_MGD, w_CMS, w_LPS, w_MLD, NULL}; char* ForceMainEqnWords[] = { w_H_W, w_D_W, NULL}; char* GageDataWords[] = { w_TIMESERIES, w_FILE, NULL}; char* InfilModelWords[] = { w_HORTON, w_MOD_HORTON, w_GREEN_AMPT, - w_MOD_GREEN_AMPT, w_CURVE_NUMEBR, NULL}; //(5.1.010) + w_MOD_GREEN_AMPT, w_CURVE_NUMEBR, NULL}; char* InertDampingWords[] = { w_NONE, w_PARTIAL, w_FULL, NULL}; char* LinkOffsetWords[] = { w_DEPTH, w_ELEVATION, NULL}; char* LinkTypeWords[] = { w_CONDUIT, w_PUMP, w_ORIFICE, @@ -75,7 +79,8 @@ 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_REPORT_STEP, + w_ROUTE_STEP, w_RULE_STEP, //(5.1.013) + w_REPORT_STEP, w_ALLOW_PONDING, w_INERT_DAMPING, w_SLOPE_WEIGHTING, w_VARIABLE_STEP, w_NORMAL_FLOW_LTD, w_LENGTHENING_STEP, @@ -87,8 +92,9 @@ char* OptionWords[] = { w_FLOW_UNITS, w_INFIL_MODEL, w_IGNORE_ROUTING, w_IGNORE_QUALITY, w_MAX_TRIALS, w_HEAD_TOL, w_SYS_FLOW_TOL, w_LAT_FLOW_TOL, - w_IGNORE_RDII, w_MIN_ROUTE_STEP, //(5.1.008) - w_NUM_THREADS, NULL}; //(5.1.008) + w_IGNORE_RDII, w_MIN_ROUTE_STEP, + w_NUM_THREADS, w_SURCHARGE_METHOD, //(5.1.013) + NULL }; char* OrificeTypeWords[] = { w_SIDE, w_BOTTOM, NULL}; char* OutfallTypeWords[] = { w_FREE, w_NORMAL, w_FIXED, w_TIDAL, w_TIMESERIES, NULL}; @@ -102,7 +108,7 @@ 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, NULL}; + w_NODESTATS, w_AVERAGES, NULL}; //(5.1.013) 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, @@ -132,10 +138,11 @@ char* SectWords[] = { ws_TITLE, ws_OPTION, ws_SYMBOL, ws_BACKDROP, ws_TAG, ws_PROFILE, ws_MAP, ws_LID_CONTROL, - ws_LID_USAGE, ws_GWF, //(5.1.007) - ws_ADJUST, ws_EVENT, //(5.1.011) + ws_LID_USAGE, ws_GWF, + ws_ADJUST, ws_EVENT, NULL}; char* SnowmeltWords[] = { w_PLOWABLE, w_IMPERV, w_PERV, w_REMOVAL, NULL}; +char* SurchargeWords[] = { w_EXTRAN, w_SLOT, NULL}; //(5.1.013) char* TempKeyWords[] = { w_TIMESERIES, w_FILE, w_WINDSPEED, w_SNOWMELT, w_ADC, NULL}; char* TransectKeyWords[] = { w_NC, w_X1, w_GR, NULL}; @@ -145,7 +152,7 @@ char* VolUnitsWords[] = { w_MGAL, w_MLTRS }; char* VolUnitsWords2[] = { w_GAL, w_LTR }; char* WashoffTypeWords[] = { w_NONE, w_EXP, w_RC, w_EMC, NULL}; char* WeirTypeWords[] = { w_TRANSVERSE, w_SIDEFLOW, w_VNOTCH, - w_TRAPEZOIDAL, w_ROADWAY, NULL}; //(5.1.010) + w_TRAPEZOIDAL, w_ROADWAY, NULL}; char* XsectTypeWords[] = { w_DUMMY, w_CIRCULAR, w_FILLED_CIRCULAR, w_RECT_CLOSED, w_RECT_OPEN, w_TRAPEZOIDAL, diff --git a/src/keywords.h b/src/keywords.h index 0caee2280..3724a0c89 100644 --- a/src/keywords.h +++ b/src/keywords.h @@ -5,12 +5,16 @@ // 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) // Author: L. Rossman // // Exportable keyword dictionary // // Build 5.1.008: // - Keyword arrays listed in alphabetical order. +// +// Build 5.1.013: +// - New keyword array defined for surcharge method. //----------------------------------------------------------------------------- extern char* BuildupTypeWords[]; @@ -51,6 +55,7 @@ extern char* RouteModelWords[]; extern char* RuleKeyWords[]; extern char* SectWords[]; extern char* SnowmeltWords[]; +extern char* SurchargeWords[]; //(5.1.013) extern char* TempKeyWords[]; extern char* TransectKeyWords[]; extern char* TreatTypeWords[]; diff --git a/src/kinwave.c b/src/kinwave.c index b442239c4..d13662b68 100644 --- a/src/kinwave.c +++ b/src/kinwave.c @@ -94,11 +94,11 @@ int kinwave_execute(int j, double* qinflow, double* qoutflow, double tStep) q1 = Conduit[k].q1 / Qfull; q2 = Conduit[k].q2 / Qfull; - // --- normalize inflow //(5.1.008) + // --- normalize inflow qin = (*qinflow) / Conduit[k].barrels / Qfull; // --- compute evaporation and infiltration loss rate - q3 = link_getLossRate(j, qin*Qfull, tStep) / Qfull; //(5.1.008) + q3 = link_getLossRate(j, qin*Qfull, tStep) / Qfull; // --- normalize previous areas a1 = Conduit[k].a1 / Afull; @@ -155,7 +155,7 @@ int kinwave_execute(int j, double* qinflow, double* qoutflow, double tStep) Conduit[k].q2 = qout * Qfull; Conduit[k].a2 = aout * Afull; Conduit[k].fullState = - link_getFullState(Conduit[k].a1, Conduit[k].a2, Afull); //(5.1.008) + link_getFullState(Conduit[k].a1, Conduit[k].a2, Afull); (*qinflow) = Conduit[k].q1 * Conduit[k].barrels; (*qoutflow) = Conduit[k].q2 * Conduit[k].barrels; return result; diff --git a/src/landuse.c b/src/landuse.c index 01b11729b..380605f66 100644 --- a/src/landuse.c +++ b/src/landuse.c @@ -43,7 +43,7 @@ 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, //(5.1.008) +static double landuse_getWashoffQual(int landuse, int pollut, double buildup, double runoff, double area); static double landuse_getExternalBuildup(int i, int p, double buildup, double tStep); @@ -552,8 +552,6 @@ double landuse_getAvgBmpEffic(int j, int p) //============================================================================= -//// This function was re-named and modified for release 5.1.008. //// //(5.1.008) - double landuse_getWashoffLoad(int i, int p, double area, TLandFactor landFactor[], double runoff, double vOutflow) // @@ -613,8 +611,6 @@ double landuse_getWashoffLoad(int i, int p, double area, //============================================================================= -//// This function was re-named and modified for release 5.1.008. //// //(5.1.008) - double landuse_getWashoffQual(int i, int p, double buildup, double runoff, double area) // diff --git a/src/lid.c b/src/lid.c index a689ad177..f204cc5fb 100644 --- a/src/lid.c +++ b/src/lid.c @@ -11,6 +11,7 @@ // 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 (US EPA) // // This module handles all data processing involving LID (Low Impact @@ -64,6 +65,13 @@ // // 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. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -71,13 +79,13 @@ #include "headers.h" #include "lid.h" -//// New error messages added to release 5.1.008. //// //(5.1.008) #define ERR_PAVE_LAYER " - check pavement layer parameters" #define ERR_SOIL_LAYER " - check soil layer parameters" #define ERR_STOR_LAYER " - check storage layer parameters" #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_SWALE_WIDTH " - invalid swale width" //----------------------------------------------------------------------------- @@ -89,13 +97,15 @@ enum LidLayerTypes { STOR, // storage layer PAVE, // pavement layer DRAINMAT, // drainage mat layer - DRAIN}; // underdrain system + DRAIN, // underdrain system + REMOVALS}; // pollutant removals //(5.1.013) //// 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", NULL}; + {"SURFACE", "SOIL", "STORAGE", "PAVEMENT", "DRAINMAT", "DRAIN", + "REMOVALS", NULL}; //(5.1.013) char* LidTypeWords[] = {"BC", //bio-retention cell @@ -121,7 +131,6 @@ struct LidList typedef struct LidList TLidList; // LID Group - collection of LID units applied to a specific subcatchment -//// Re-defined for release 5.1.008. //// //(5.1.008) struct LidGroup { double pervArea; // amount of pervious area in group (ft2) @@ -148,7 +157,7 @@ static double MaxNativeInfil; // native soil infil. rate limit (ft/s) //----------------------------------------------------------------------------- // Imported Variables (from SUBCATCH.C) //----------------------------------------------------------------------------- -// Volumes (ft3) for a subcatchment over a time step //(5.1.008) +// Volumes (ft3) for a subcatchment over a time step extern double Vevap; // evaporation extern double Vpevap; // pervious area evaporation extern double Vinfil; // non-LID infiltration @@ -157,12 +166,8 @@ extern double VlidIn; // impervious area flow to LID units extern double VlidOut; // surface outflow from LID units extern double VlidDrain; // drain outflow from LID units extern double VlidReturn; // LID outflow returned to pervious area -extern char HasWetLids; // TRUE if any LIDs are wet //(5.1.010) - // (from RUNOFF.C) //(5.1.010) - -//// Deleted for release 5.1.008. //// //(5.1.008) -//static double NextReportTime; -//static int SaveResults; +extern char HasWetLids; // TRUE if any LIDs are wet + // (from RUNOFF.C) //----------------------------------------------------------------------------- // External Functions (prototyped in lid.h) @@ -176,8 +181,8 @@ extern char HasWetLids; // TRUE if any LIDs are wet / // lid_readGroupParams called by parseLine in input.c // lid_setOldGroupState called by subcatch_setOldState -// lid_setReturnQual called by findLidLoads in surfqual.c //(5.1.008) -// lid_getReturnQual called by subcatch_getRunon //(5.1.008) +// lid_setReturnQual called by findLidLoads in surfqual.c +// lid_getReturnQual called by subcatch_getRunon // lid_getPervArea called by subcatch_getFracPerv // lid_getFlowToPerv called by subcatch_getRunon @@ -187,7 +192,6 @@ extern char HasWetLids; // TRUE if any LIDs are wet / // lid_getRunon called by subcatch_getRunon // lid_getRunoff called by subcatch_getRunoff -//// Added to release 5.1.008. //// //(5.1.008) // lid_addDrainRunon called by subcatch_getRunon // lid_addDrainLoads called by surfqual_getWashoff // lid_addDrainInflow called by addLidDrainInflows in routing.c @@ -206,9 +210,10 @@ 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 addLidUnit(int j, int k, int n, double x[], char* fname, - int drainSubcatch, int drainNode); //(5.1.008) + int drainSubcatch, int drainNode); static int createLidRptFile(TLidUnit* lidUnit, char* fname); static void initLidRptFile(char* title, char* lidID, char* subcatchID, TLidUnit* lidUnit); @@ -216,11 +221,11 @@ static void validateLidProc(int j); static void validateLidGroup(int j); static int isLidPervious(int k); -static double getImpervAreaRunoff(int j); //(5.1.008) -static double getSurfaceDepth(int subcatch); //(5.1.008) -static void findNativeInfil(int j, double tStep); //(5.1.008) +static double getImpervAreaRunoff(int j); +static double getPervAreaRunoff(int j); //(5.1.013) +static double getSurfaceDepth(int subcatch); +static void findNativeInfil(int j, double tStep); -//// Re-definition of evalLidUnit. //// //(5.1.008) static void evalLidUnit(int j, TLidUnit* lidUnit, double lidArea, double lidInflow, double tStep, double *qRunoff, double *qDrain, double *qReturn); @@ -279,6 +284,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; // + } // } } @@ -294,6 +307,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) FREE(LidProcs); GroupCount = 0; LidCount = 0; @@ -351,6 +365,7 @@ int lid_readProcParams(char* toks[], int ntoks) // LID_ID STORAGE // LID_ID DRAIN // LID_ID DRAINMAT +// LID_ID REMOVALS //(5.1.013) // { int j, m; @@ -386,6 +401,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) } return error_setInpError(ERR_KEYWORD, toks[1]); } @@ -401,7 +417,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) //(5.1.008) +// (RptFile DrainTo FromPerv) //(5.1.013) // where: // Subcatch_ID = name of subcatchment // LID_ID = name of LID process @@ -411,14 +427,15 @@ int lid_readGroupParams(char* toks[], int ntoks) // InitSat (x[2]) = % that LID is initially saturated // FromImp (x[3]) = % of impervious runoff sent to LID // ToPerv (x[4]) = 1 if outflow goes to pervious sub-area; 0 if not -// RptFile = name of detailed results file (optional) //(5.1.008) -// DrainTo = name of subcatch/node for drain flow (optional) //(5.1.008) +// 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) // { int i, j, k, n; - double x[5]; - char* fname = NULL; //(5.1.008) - int drainSubcatch = -1, drainNode = -1; //(5.1.008) + double x[6]; //(5.1.013) + char* fname = NULL; + int drainSubcatch = -1, drainNode = -1; //... check for valid number of input tokens if ( ntoks < 8 ) return error_setInpError(ERR_ITEMS, ""); @@ -450,8 +467,6 @@ int lid_readGroupParams(char* toks[], int ntoks) //... read optional report file name if ( ntoks >= 9 && strcmp(toks[8], "*") != 0 ) fname = toks[8]; -//// ---- Following code segment added to release 5.1.008. ---- //// //(5.1.008) -//// //... read optional underdrain outlet if ( ntoks >= 10 && strcmp(toks[9], "*") != 0 ) { @@ -462,7 +477,14 @@ int lid_readGroupParams(char* toks[], int ntoks) if ( drainNode < 0 ) return error_setInpError(ERR_NAME, toks[9]); } } -//// + + //... 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]); // + } // //... create a new LID unit and add it to the subcatchment's LID group return addLidUnit(j, k, n, x, fname, drainSubcatch, drainNode); @@ -471,7 +493,7 @@ int lid_readGroupParams(char* toks[], int ntoks) //============================================================================= int addLidUnit(int j, int k, int n, double x[], char* fname, - int drainSubcatch, int drainNode) //(5.1.008) + int drainSubcatch, int drainNode) // // Purpose: adds an LID unit to a subcatchment's LID group. // Input: j = subcatchment index @@ -479,8 +501,8 @@ int addLidUnit(int j, int k, int n, double x[], char* fname, // n = number of replicate units // x = LID unit's parameters // fname = name of detailed performance report file -// drainSubcatch = index of subcatchment receiving underdrain flow //(5.1.008) -// drainNode = index of node receiving underdrain flow //(5.1.008) +// drainSubcatch = index of subcatchment receiving underdrain flow +// drainNode = index of node receiving underdrain flow // Output: returns an error code // { @@ -523,8 +545,9 @@ 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->drainSubcatch = drainSubcatch; //(5.1.008) - lidUnit->drainNode = drainNode; //(5.1.008) + lidUnit->fromPerv = x[5] / 100.0; //(5.1.013) + lidUnit->drainSubcatch = drainSubcatch; + lidUnit->drainNode = drainNode; //... open report file if it was supplied if ( fname != NULL ) @@ -597,10 +620,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) // { int i; - double x[5]; + double x[7]; //(5.1.013) if ( ntoks < 7 ) return error_setInpError(ERR_ITEMS, ""); for (i = 2; i < 7; i++) @@ -609,6 +633,20 @@ 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]); // + } // + //... convert void ratio to void fraction x[1] = x[1]/(x[1] + 1.0); @@ -617,6 +655,8 @@ int readPavementData(int j, char* toks[], int ntoks) LidProcs[j].pavement.impervFrac = x[2]; LidProcs[j].pavement.kSat = x[3] / UCF(RAINFALL); LidProcs[j].pavement.clogFactor = x[4]; + LidProcs[j].pavement.regenDays = x[5]; //(5.1.013) + LidProcs[j].pavement.regenDegree = x[6]; // return 0; } @@ -703,25 +743,36 @@ int readDrainData(int j, char* toks[], int ntoks) // Output: returns error code // // Format of data is: -// LID_ID DRAIN coeff expon offset delay +// LID_ID DRAIN coeff expon offset delay hOpen hClose curve //(5.1.013) // { int i; - double x[4]; + double x[6]; //(5.1.013) //... read numerical parameters if ( ntoks < 6 ) return error_setInpError(ERR_ITEMS, ""); - for (i = 2; i < 6; i++) + for (i = 0; i < 6; i++) x[i] = 0.0; //(5.1.013) + for (i = 2; i < 8; i++) // { - if ( ! getDouble(toks[i], &x[i-2]) || x[i-2] < 0.0 ) + if ( ntoks > i && ! getDouble(toks[i], &x[i-2]) || x[i-2] < 0.0 ) //(5.1.013) 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]); // + } // + //... 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; // return 0; } @@ -761,6 +812,49 @@ 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 +// data file +// Input: j = LID process index +// toks = array of string tokens +// ntoks = number of tokens +// Output: returns error code +// +// Format of data is: +// LID_ID REMOVALS pollut1 %removal1 pollut2 %removal2 ... +// +{ + int i = 2; + int p; + double rmvl; + + //... start with 3rd token + if (ntoks < 4) return error_setInpError(ERR_ITEMS, ""); + while (ntoks > i) + { + //... find pollutant index from its name + p = project_findObject(POLLUT, toks[i]); + if (p < 0) return error_setInpError(ERR_NAME, toks[i]); + + //... check that a next token exists + i++; + if (ntoks == i) return error_setInpError(ERR_ITEMS, ""); + + //... get the % removal value from the next token + if (!getDouble(toks[i], &rmvl) || rmvl < 0.0 || rmvl > 100.0) + return error_setInpError(ERR_NUMBER, toks[i]); + + //... save the pollutant removal for the LID process as a fraction + LidProcs[j].drainRmvl[p] = rmvl / 100.0; + i++; + } + return 0; +} +//============================================================================= + void lid_writeSummary() // // Purpose: writes summary of LID processes used to report file. @@ -779,12 +873,15 @@ void lid_writeSummary() fprintf(Frpt.file, "\n *******************"); fprintf(Frpt.file, "\n LID Control Summary"); fprintf(Frpt.file, "\n *******************"); + + fprintf(Frpt.file, -"\n No. of Unit Unit %% Area %% Imperv"); - fprintf(Frpt.file, -"\n Subcatchment LID Control Units Area Width Covered Treated"); - fprintf(Frpt.file, -"\n ---------------------------------------------------------------------------------------"); +"\n No. of Unit Unit %% Area %% Imperv %% Perv"); //(5.1.013) + fprintf(Frpt.file, // +"\n Subcatchment LID Control Units Area Width Covered Treated Treated"); // + fprintf(Frpt.file, // +"\n ---------------------------------------------------------------------------------------------------"); // + for (j = 0; j < GroupCount; j++) { lidGroup = LidGroups[j]; @@ -796,10 +893,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", + fprintf(Frpt.file, "%6d %10.2f %10.2f %10.2f %10.2f %10.2f", //(5.1.013) lidUnit->number, lidUnit->area * SQR(UCF(LENGTH)), lidUnit->fullWidth * UCF(LENGTH), pctArea, - lidUnit->fromImperv*100.0); + lidUnit->fromImperv*100.0, lidUnit->fromPerv*100.0); //(5.1.013) lidList = lidList->nextLidUnit; } } @@ -870,13 +967,11 @@ void validateLidProc(int j) || LidProcs[j].pavement.voidFrac > 1.0 || LidProcs[j].pavement.impervFrac > 1.0 ) -//// Modified for release 5.1.008. //// //(5.1.008) { strcpy(Msg, LidProcs[j].ID); strcat(Msg, ERR_PAVE_LAYER); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } -//// } //... check soil layer parameters @@ -887,14 +982,11 @@ void validateLidProc(int j) || LidProcs[j].soil.wiltPoint >= LidProcs[j].soil.fieldCap || LidProcs[j].soil.kSat <= 0.0 || LidProcs[j].soil.kSlope < 0.0 ) - -//// Modified for release 5.1.008. //// //(5.1.008) { strcpy(Msg, LidProcs[j].ID); strcat(Msg, ERR_SOIL_LAYER); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } -//// } //... check storage layer parameters @@ -902,37 +994,28 @@ void validateLidProc(int j) { if ( LidProcs[j].storage.voidFrac <= 0.0 || LidProcs[j].storage.voidFrac > 1.0 ) - -//// Modified for release 5.1.008. //// //(5.1.008) { strcpy(Msg, LidProcs[j].ID); strcat(Msg, ERR_STOR_LAYER); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } -//// } - //... if no storage layer adjust void fraction and drain offset //(5.1.007) + //... if no storage layer adjust void fraction and drain offset else { LidProcs[j].storage.voidFrac = 1.0; LidProcs[j].drain.offset = 0.0; } -//// Removed for release 5.1.011 to allow for upturned drain pipes. //// //(5.1.011) -/* - //... check underdrain parameters - if ( LidProcs[j].drain.offset > LidProcs[j].storage.thickness ) -//// - -//// Modified for release 5.1.008. //// //(5.1.008) - { - strcpy(Msg, LidProcs[j].ID); - strcat(Msg, ERR_DRAIN_OFFSET); - report_writeErrorMsg(ERR_LID_PARAMS, Msg); - } -*/ -//// + //... 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); // + } // //... compute the surface layer's overland flow constant (alpha) if ( LidProcs[j].lidType == VEG_SWALE ) @@ -941,14 +1024,11 @@ void validateLidProc(int j) LidProcs[j].surface.surfSlope <= 0.0 || LidProcs[j].surface.thickness == 0.0 ) - -//// Modified for release 5.1.008. //// //(5.1.008) { strcpy(Msg, LidProcs[j].ID); strcat(Msg, ERR_SWALE_SURF); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } -//// else LidProcs[j].surface.alpha = 1.49 * sqrt(LidProcs[j].surface.surfSlope) / LidProcs[j].surface.roughness; @@ -983,14 +1063,14 @@ void validateLidProc(int j) LidProcs[j].storage.clogFactor *= LidProcs[j].storage.thickness * LidProcs[j].storage.voidFrac; } - else LidProcs[j].storage.clogFactor = 0.0; //(5.1.007) + else LidProcs[j].storage.clogFactor = 0.0; //... 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; switch (LidProcs[j].lidType) { - case ROOF_DISCON: LidProcs[j].surface.canOverflow = FALSE; break; //(5.1.008) + case ROOF_DISCON: LidProcs[j].surface.canOverflow = FALSE; break; case INFIL_TRENCH: case POROUS_PAVEMENT: case BIO_CELL: @@ -1031,6 +1111,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) TLidUnit* lidUnit; TLidList* lidList; TLidGroup lidGroup; @@ -1046,6 +1127,7 @@ void validateLidGroup(int j) //... update contributing fractions totalLidArea += (lidUnit->area * lidUnit->number); fromImperv += lidUnit->fromImperv; + fromPerv += lidUnit->fromPerv; //(5.1.013) //... assign biocell soil layer infiltration parameters lidUnit->soilInfil.Ks = 0.0; @@ -1057,43 +1139,35 @@ void validateLidGroup(int j) (1.0 - lidUnit->initSat); if ( grnampt_setParams(&(lidUnit->soilInfil), p) == FALSE ) { -//// Modified for release 5.1.008. //// //(5.1.008) strcpy(Msg, LidProcs[k].ID); strcat(Msg, ERR_SOIL_LAYER); report_writeErrorMsg(ERR_LID_PARAMS, Msg); -//// } } //... assign vegetative swale infiltration parameters if ( LidProcs[k].lidType == VEG_SWALE ) { - if ( InfilModel == GREEN_AMPT || InfilModel == MOD_GREEN_AMPT ) //(5.1.010) + if ( InfilModel == GREEN_AMPT || InfilModel == MOD_GREEN_AMPT ) { p[0] = GAInfil[j].S * UCF(RAINDEPTH); p[1] = GAInfil[j].Ks * UCF(RAINFALL); p[2] = GAInfil[j].IMDmax; if ( grnampt_setParams(&(lidUnit->soilInfil), p) == FALSE ) { -//// Modified for release 5.1.008. //// //(5.1.008) strcpy(Msg, LidProcs[k].ID); strcat(Msg, ERR_GREEN_AMPT); report_writeErrorMsg(ERR_LID_PARAMS, Msg); -//// } } if ( lidUnit->fullWidth <= 0.0 ) { -//// Modified for release 5.1.008. //// //(5.1.008) strcpy(Msg, LidProcs[k].ID); strcat(Msg, ERR_SWALE_WIDTH); report_writeErrorMsg(ERR_LID_PARAMS, Msg); -//// } } -//// Added to release 5.1.008. //// //(5.1.008) -//// //... LID unit cannot send outflow back to subcatchment's // pervious area if none exists if ( Subcatch[j].fracImperv >= 0.999 ) lidUnit->toPerv = 0; @@ -1104,7 +1178,6 @@ void validateLidGroup(int j) lidUnit->drainNode = Subcatch[j].outNode; lidUnit->drainSubcatch = Subcatch[j].outSubcatch; } -//// lidList = lidList->nextLidUnit; } @@ -1113,7 +1186,7 @@ void validateLidGroup(int j) { report_writeErrorMsg(ERR_LID_AREAS, Subcatch[j].ID); } - if ( fromImperv > 1.001 ) + if ( fromImperv > 1.001 || fromPerv > 1.001 ) //(5.1.013) { report_writeErrorMsg(ERR_LID_CAPTURE_AREA, Subcatch[j].ID); } @@ -1139,8 +1212,7 @@ void lid_initState() double initVol; double initDryTime = StartDryDays * SECperDAY; - //NextReportTime = (double) (ReportStep * 1000.0); //(5.1.008) - HasWetLids = FALSE; //(5.1.010) + HasWetLids = FALSE; for (j = 0; j < GroupCount; j++) { //... check if group exists @@ -1150,8 +1222,8 @@ void lid_initState() //... initialize group variables lidGroup->pervArea = 0.0; lidGroup->flowToPerv = 0.0; - lidGroup->oldDrainFlow = 0.0; //(5.1.008) - lidGroup->newDrainFlow = 0.0; //(5.1.008) + lidGroup->oldDrainFlow = 0.0; + lidGroup->newDrainFlow = 0.0; //... examine each LID in the group lidList = lidGroup->lidList; @@ -1163,8 +1235,10 @@ void lid_initState() lidUnit->surfaceDepth = 0.0; lidUnit->storageDepth = 0.0; lidUnit->soilMoisture = 0.0; - lidUnit->paveDepth = 0.0; //(5.1.011) + lidUnit->paveDepth = 0.0; lidUnit->dryTime = initDryTime; + lidUnit->volTreated = 0.0; //(5.1.013) + lidUnit->nextRegenDay = LidProcs[k].pavement.regenDays; // initVol = 0.0; if ( LidProcs[k].soil.thickness > 0.0 ) { @@ -1185,10 +1259,11 @@ void lid_initState() LidProcs[k].drainMat.thickness; initVol += lidUnit->storageDepth * LidProcs[k].drainMat.voidFrac; } - if ( lidUnit->initSat > 0.0 ) HasWetLids = TRUE; //(5.1.010) + if ( lidUnit->initSat > 0.0 ) HasWetLids = TRUE; //... initialize water balance totals lidproc_initWaterBalance(lidUnit, initVol); + lidUnit->volTreated = 0.0; //... initialize report file for the LID if ( lidUnit->rptFile ) @@ -1196,12 +1271,12 @@ void lid_initState() initLidRptFile(Title[0], LidProcs[k].ID, Subcatch[j].ID, lidUnit); } - //... initialize drain flows //(5.1.008) - lidUnit->oldDrainFlow = 0.0; //(5.1.008) - lidUnit->newDrainFlow = 0.0; //(5.1.008) + //... initialize drain flows + lidUnit->oldDrainFlow = 0.0; + lidUnit->newDrainFlow = 0.0; //... set previous flux rates to 0 - for (i = 0; i < MAX_LAYERS; i++) //(5.1.008) + for (i = 0; i < MAX_LAYERS; i++) { lidUnit->oldFluxRates[i] = 0.0; } @@ -1220,8 +1295,6 @@ void lid_initState() //============================================================================= -//// New function added to release 5.1.008. //// //(5.1.008) - void lid_setOldGroupState(int j) // // Purpose: saves the current drain flow rate for the LIDs in a subcatchment. @@ -1259,7 +1332,7 @@ int isLidPervious(int k) //============================================================================= -double getSurfaceDepth(int j) //(5.1.008) +double getSurfaceDepth(int j) // // Purpose: computes the depth (volume per unit area) of ponded water on the // surface of all LIDs within a subcatchment. @@ -1344,8 +1417,6 @@ double lid_getStoredVolume(int j) //============================================================================= -//// New function added to release 5.1.008. //// //(5.1.008) - double lid_getDrainFlow(int j, int timePeriod) // // Purpose: returns flow from all of a subcatchment's LID drains for @@ -1364,7 +1435,7 @@ double lid_getDrainFlow(int j, int timePeriod) //============================================================================= -//// New function added to release 5.1.008. //// //(5.1.008) +//// This function was modified for relelase 5.1.013. //// //(5.1.013) void lid_addDrainLoads(int j, double c[], double tStep) // @@ -1376,7 +1447,9 @@ void lid_addDrainLoads(int j, double c[], double tStep) // Output: none. // { + int isRunoffLoad; // true if drain becomes external runoff load int p; // pollutant index + double r; // pollutant fractional removal double w; // pollutant mass load (lb or kg) TLidUnit* lidUnit; TLidList* lidList; @@ -1386,26 +1459,36 @@ void lid_addDrainLoads(int j, double c[], double tStep) lidGroup = LidGroups[j]; if ( lidGroup != NULL ) { - //... examine each LID in the group + //... examine each LID unit in the group lidList = lidGroup->lidList; while ( lidList ) { - //... see if LID's drain goes to a conveyance system node lidUnit = lidList->lidUnit; - if ( lidUnit->drainNode >= 0 - || lidUnit->drainSubcatch == j ) + + //... skip LID unit if it sends its drain flow onto + // its subcatchment's pervious area + if (lidUnit->toPerv) continue; + + //... see if unit's drain flow becomes external runoff + isRunoffLoad = (lidUnit->drainNode >= 0 || + lidUnit->drainSubcatch == j); + + //... for each pollutant + for (p = 0; p < Nobjects[POLLUT]; p++) { - //... For each pollutant - for (p = 0; p < Nobjects[POLLUT]; p++) - { - //... get drain's mass load - w = lidUnit->newDrainFlow * c[p] * tStep * - LperFT3 * Pollut[p].mcf; + //... get mass load flowing through the drain + w = lidUnit->newDrainFlow * c[p] * tStep * LperFT3 * Pollut[p].mcf; - //... update system mass balance totals - massbal_updateLoadingTotals(RUNOFF_LOAD, p, w); - } + //... get fractional removal for this load + r = LidProcs[lidUnit->lidIndex].drainRmvl[p]; + + //... update system mass balance totals + massbal_updateLoadingTotals(BMP_REMOVAL_LOAD, p, r*w); + if (isRunoffLoad) + massbal_updateLoadingTotals(RUNOFF_LOAD, p, w*(1.0-r)); } + + // process next LID unit in the group lidList = lidList->nextLidUnit; } } @@ -1413,8 +1496,6 @@ void lid_addDrainLoads(int j, double c[], double tStep) //============================================================================= -//// New function added to release 5.1.008. //// //(5.1.008) - void lid_addDrainRunon(int j) // // Purpose: adds drain flows from LIDs in a given subcatchment to the @@ -1423,9 +1504,11 @@ void lid_addDrainRunon(int j) // Output: none. // { + int i; // index of an LID unit's LID process //(5.1.013) 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) TLidUnit* lidUnit; TLidList* lidList; TLidGroup lidGroup; @@ -1440,6 +1523,7 @@ void lid_addDrainRunon(int j) { //... see if LID's drain discharges to another subcatchment lidUnit = lidList->lidUnit; + i = lidUnit->lidIndex; //(5.1.013) k = lidUnit->drainSubcatch; if ( k >= 0 && k != j ) { @@ -1452,8 +1536,9 @@ void lid_addDrainRunon(int j) // point which is converted later on to a concentration) for (p = 0; p < Nobjects[POLLUT]; p++) { - Subcatch[k].newQual[p] += - q * Subcatch[j].oldQual[p] * LperFT3; + w = q * Subcatch[j].oldQual[p] * LperFT3; //(5.1.013) + w = w * (1.0 - LidProcs[i].drainRmvl[p]); // + Subcatch[k].newQual[p] += w; // } } lidList = lidList->nextLidUnit; @@ -1463,8 +1548,6 @@ void lid_addDrainRunon(int j) //============================================================================= -//// New function added to release 5.1.008. //// //(5.1.008) - void lid_addDrainInflow(int j, double f) // // Purpose: adds LID drain flow to conveyance system nodes @@ -1476,7 +1559,8 @@ 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 k, // node index + int i, // LID process index //(5.1.013) + k, // node index p; // pollutant index double q, // drain flow (cfs) w, w1, w2; // pollutant mass loads (mass/sec) @@ -1494,6 +1578,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) k = lidUnit->drainNode; if ( k >= 0 ) { @@ -1511,6 +1596,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) Node[k].newQual[p] += w; massbal_addInflowQual(WET_WEATHER_INFLOW, p, w); } @@ -1522,8 +1608,6 @@ void lid_addDrainInflow(int j, double f) //============================================================================= -//// This function was re-written for release 5.1.008. //// //(5.1.008) - void lid_getRunoff(int j, double tStep) // // Purpose: computes runoff and drain flows from the LIDs in a subcatchment. @@ -1538,6 +1622,7 @@ 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 qRunoff = 0.0; // surface runoff from all LID units (cfs) double qDrain = 0.0; // drain flow from all LID units (cfs) @@ -1556,10 +1641,12 @@ void lid_getRunoff(int j, double tStep) //... find subcatchment's infiltration rate into native soil findNativeInfil(j, tStep); - //... get runoff from impervious, non-LID subarea of subcatchment (cfs) + //... get impervious and pervious area runoff from non-LID + // portion of subcatchment (cfs) if ( Subcatch[j].area > Subcatch[j].lidArea ) { qImperv = getImpervAreaRunoff(j); + qPerv = getPervAreaRunoff(j); //(5.1.013) } //... evaluate performance of each LID unit placed in the subcatchment @@ -1573,7 +1660,8 @@ 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 / lidArea; + lidInflow = (qImperv * lidUnit->fromImperv + //(5.1.013) + qPerv * lidUnit->fromPerv) / lidArea; // //... update total runoff volume treated VlidIn += lidInflow * lidArea * tStep; @@ -1645,8 +1733,6 @@ void findNativeInfil(int j, double tStep) //============================================================================= -//// This function was re-named and modified for release 5.1.008. //// //(5.1.008) - double getImpervAreaRunoff(int j) // // Purpose: computes runoff from impervious area of a subcatchment that @@ -1677,7 +1763,33 @@ double getImpervAreaRunoff(int j) //============================================================================= -//// This function was re-named and re-written for release 5.1.008. //// //(5.1.008) +//// 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 +// is available for LID treatment. +// Input: j = subcatchment index +// Output: returns runoff flow rate (cfs) +// +{ + double q = 0.0, // runoff rate (ft/sec) + nonLidArea; // non-LID area (ft2) + + // --- runoff from pervious area + q = Subcatch[j].subArea[PERV].runoff * Subcatch[j].subArea[PERV].fArea; + + // --- adjust for any fraction of runoff sent to impervious area + if (Subcatch[j].subArea[PERV].routeTo == TO_IMPERV && + Subcatch[j].fracImperv > 0.0) + { + q *= Subcatch[j].subArea[PERV].fOutlet; + } + nonLidArea = Subcatch[j].area - Subcatch[j].lidArea; + return q * nonLidArea; +} + +//============================================================================= void evalLidUnit(int j, TLidUnit* lidUnit, double lidArea, double lidInflow, double tStep, double *qRunoff, double *qDrain, double *qReturn) @@ -1715,7 +1827,7 @@ void evalLidUnit(int j, TLidUnit* lidUnit, double lidArea, double lidInflow, lidDrain *= lidArea; //... revise flows if LID outflow returned to pervious area - if ( lidUnit->toPerv && Subcatch[j].area > Subcatch[j].lidArea ) //(5.1.009) + if ( lidUnit->toPerv && Subcatch[j].area > Subcatch[j].lidArea ) { //... surface runoff is always returned *qReturn += lidRunoff; @@ -1753,7 +1865,7 @@ void evalLidUnit(int j, TLidUnit* lidUnit, double lidArea, double lidInflow, else lidUnit->dryTime += tStep; //... update LID water balance and save results - lidproc_saveResults(lidUnit, UCF(RAINFALL), UCF(RAINDEPTH)); //(5.1.011) + lidproc_saveResults(lidUnit, UCF(RAINFALL), UCF(RAINDEPTH)); //... update LID group totals *qRunoff += lidRunoff; @@ -1793,7 +1905,6 @@ void lid_writeWaterBalance() "\n LID Performance Summary" "\n ***********************\n"); -//// Headings modified for release 5.1.008. //// //(5.1.008) fprintf(Frpt.file, "\n --------------------------------------------------------------------------------------------------------------------" "\n Total Evap Infil Surface Drain Initial Final Continuity" @@ -1837,7 +1948,7 @@ void lid_writeWaterBalance() lidUnit->waterBalance.drainFlow; if ( inflow > 0.0 ) err = (inflow - outflow) / inflow; else err = 1.0; - fprintf(Frpt.file, " %10.2f", err*100.0); //(5.1.008) + fprintf(Frpt.file, " %10.2f", err*100.0); lidList = lidList->nextLidUnit; } } @@ -1845,8 +1956,6 @@ void lid_writeWaterBalance() //============================================================================= -//// This function was re-written for release 5.1.011. //// //(5.1.011) - void initLidRptFile(char* title, char* lidID, char* subcatchID, TLidUnit* lidUnit) // // Purpose: initializes the report file used for a specific LID unit @@ -1902,6 +2011,6 @@ void initLidRptFile(char* title, char* lidID, char* subcatchID, TLidUnit* lidUni for ( i = 1; i < colCount; i++) fprintf(f, "\t%s", line9); //... initialize LID dryness state - lidUnit->rptFile->wasDry = 1; //(5.1.012) - strcpy(lidUnit->rptFile->results, ""); //(5.1.012) + lidUnit->rptFile->wasDry = 1; + strcpy(lidUnit->rptFile->results, ""); } diff --git a/src/lid.h b/src/lid.h index f41fd3976..93ee2b779 100644 --- a/src/lid.h +++ b/src/lid.h @@ -7,6 +7,7 @@ // 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) // // Public interface for LID functions. @@ -23,6 +24,18 @@ // 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. +// - New members hOpen and hClose addded to TDrainLayer to open/close +// drain when certain heads are reached. +// - New member qCurve added to TDrainLayer to allow underdrain flow to +// be adjusted by a curve of multiplier v. head. +// - New array drainRmvl added to TLidProc to allow for underdrain +// pollutant removal values. +// - New members added to TPavementLayer and TLidUnit to support +// unclogging permeable pavement at fixed intervals. +// //----------------------------------------------------------------------------- #ifndef LID_H @@ -44,11 +57,11 @@ enum LidTypes { POROUS_PAVEMENT, // porous pavement RAIN_BARREL, // rain barrel VEG_SWALE, // vegetative swale - ROOF_DISCON}; // roof disconnection //(5.1.008) + ROOF_DISCON}; // roof disconnection enum TimePeriod { - PREVIOUS, // previous time period //(5.1.008) - CURRENT}; // current time period //(5.1.008) + PREVIOUS, // previous time period + CURRENT}; // current time period //----------------------------------------------------------------------------- // Data Structures @@ -75,6 +88,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 // } TPavementLayer; // LID Soil Layer @@ -105,6 +120,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) // } TDrainLayer; // Drainage Mat Layer (for green roofs) @@ -127,6 +145,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) } TLidProc; // Water Balance Statistics @@ -145,8 +164,8 @@ typedef struct typedef struct { FILE* file; // file pointer - int wasDry; // number of successive dry periods //(5.1.012) - char results[256]; // results for current time period //(5.1.008) + int wasDry; // number of successive dry periods + char results[256]; // results for current time period } TLidRptFile; // LID Unit - specific LID process applied over a given area @@ -159,14 +178,15 @@ 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) int toPerv; // 1 if outflow sent to pervious area; 0 if not - int drainSubcatch; // subcatchment receiving drain flow //(5.1.008) - int drainNode; // node receiving drain flow //(5.1.008) + int drainSubcatch; // subcatchment receiving drain flow + int drainNode; // node receiving drain flow TLidRptFile* rptFile; // pointer to detailed report file TGrnAmpt soilInfil; // infil. object for biocell soil layer double surfaceDepth; // depth of ponded water on surface layer (ft) - double paveDepth; // depth of water in porous pavement layer //(5.1.011) + double paveDepth; // depth of water in porous pavement layer double soilMoisture; // moisture content of biocell soil layer double storageDepth; // depth of water in storage layer (ft) @@ -174,8 +194,10 @@ typedef struct double oldFluxRates[MAX_LAYERS]; double dryTime; // time since last rainfall (sec) - double oldDrainFlow; // previous drain flow (cfs) //(5.1.008) - double newDrainFlow; // current drain flow (cfs) //(5.1.008) + 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 // TWaterBalance waterBalance; // water balance quantites } TLidUnit; @@ -190,22 +212,16 @@ int lid_readGroupParams(char* tok[], int ntoks); void lid_validate(void); void lid_initState(void); -void lid_setOldGroupState(int subcatch); //(5.1.008) +void lid_setOldGroupState(int subcatch); double lid_getPervArea(int subcatch); double lid_getFlowToPerv(int subcatch); -double lid_getDrainFlow(int subcatch, int timePeriod); //(5.1.008) +double lid_getDrainFlow(int subcatch, int timePeriod); double lid_getStoredVolume(int subcatch); - -//double lid_getSurfaceDepth(int subcatch); //(5.1.008) -//double lid_getDepthOnPavement(int subcatch, double impervDepth); //(5.1.008) - -void lid_addDrainLoads(int subcatch, double c[], double tStep); //(5.1.008) -void lid_addDrainRunon(int subcatch); //(5.1.008) -void lid_addDrainInflow(int subcatch, double f); //(5.1.008) - -void lid_getRunoff(int subcatch, double tStep); //(5.1.008) - +void lid_addDrainLoads(int subcatch, double c[], double tStep); +void lid_addDrainRunon(int subcatch); +void lid_addDrainInflow(int subcatch, double f); +void lid_getRunoff(int subcatch, double tStep); void lid_writeSummary(void); void lid_writeWaterBalance(void); @@ -214,10 +230,10 @@ void lid_writeWaterBalance(void); void lidproc_initWaterBalance(TLidUnit *lidUnit, double initVol); double lidproc_getOutflow(TLidUnit* lidUnit, TLidProc* lidProc, - double inflow, double evap, double infil, double maxInfil, //(5.1.008) - double tStep, double* lidEvap, double* lidInfil, double* lidDrain); //(5.1.008) + double inflow, double evap, double infil, double maxInfil, + double tStep, double* lidEvap, double* lidInfil, double* lidDrain); -void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, //(5.1.011) +void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDepth); #endif diff --git a/src/lidproc.c b/src/lidproc.c index 693783447..1c7d8d216 100644 --- a/src/lidproc.c +++ b/src/lidproc.c @@ -11,6 +11,7 @@ // 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 (US EPA) // // This module computes the hydrologic performance of an LID (Low Impact @@ -53,6 +54,11 @@ // - 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. +// //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -82,18 +88,17 @@ enum LidRptVars { SURF_INFLOW, // inflow to surface layer TOTAL_EVAP, // evaporation rate from all layers SURF_INFIL, // infiltration into surface layer - PAVE_PERC, // percolation through pavement layer //(5.1.008) + PAVE_PERC, // percolation through pavement layer SOIL_PERC, // percolation through soil layer - STOR_EXFIL, // exfiltration out of storage layer //(5.1.011) + STOR_EXFIL, // exfiltration out of storage layer SURF_OUTFLOW, // outflow from surface layer STOR_DRAIN, // outflow from storage layer SURF_DEPTH, // ponded depth on surface layer - PAVE_DEPTH, // water level in pavement layer //(5.1.011) + PAVE_DEPTH, // water level in pavement layer SOIL_MOIST, // moisture content of soil layer STOR_DEPTH, // water level in storage layer MAX_RPT_VARS}; -//// Added to release 5.1.008. //// //(5.1.008) //----------------------------------------------------------------------------- // Imported variables //----------------------------------------------------------------------------- @@ -106,7 +111,6 @@ static TLidUnit* theLidUnit; // ptr. to a subcatchment's LID unit static TLidProc* theLidProc; // ptr. to a LID process static double Tstep; // current time step (sec) -//static double Rainfall; // current rainfall rate (ft/s) //(5.1.008) static double EvapRate; // evaporation rate (ft/s) static double MaxNativeInfil; // native soil infil. rate limit (ft/s) @@ -116,21 +120,21 @@ static double SurfaceEvap; // evap. rate from surface layer (ft/s) static double SurfaceOutflow; // outflow from surface layer (ft/s) static double SurfaceVolume; // volume in surface storage (ft) -static double PaveEvap; // evap. from pavement layer (ft/s) //(5.1.008) -static double PavePerc; // percolation from pavement layer (ft/s) //(5.1.008) -static double PaveVolume; // volume stored in pavement layer (ft) //(5.1.008) +static double PaveEvap; // evap. from pavement layer (ft/s) +static double PavePerc; // percolation from pavement layer (ft/s) +static double PaveVolume; // volume stored in pavement layer (ft) static double SoilEvap; // evap. from soil layer (ft/s) static double SoilPerc; // percolation from soil layer (ft/s) static double SoilVolume; // volume in soil/pavement storage (ft) static double StorageInflow; // inflow rate to storage layer (ft/s) -static double StorageExfil; // exfil. rate from storage layer (ft/s) //(5.1.011) +static double StorageExfil; // exfil. rate from storage layer (ft/s) static double StorageEvap; // evap.rate from storage layer (ft/s) static double StorageDrain; // underdrain flow rate layer (ft/s) static double StorageVolume; // volume in storage layer (ft) -static double Xold[MAX_LAYERS]; // previous moisture level in LID layers //(5.1.008) +static double Xold[MAX_LAYERS]; // previous moisture level in LID layers //----------------------------------------------------------------------------- // External Functions (declared in lid.h) @@ -148,18 +152,18 @@ static void greenRoofFluxRates(double x[], double f[]); static void pavementFluxRates(double x[], double f[]); static void trenchFluxRates(double x[], double f[]); static void swaleFluxRates(double x[], double f[]); -static void roofFluxRates(double x[], double f[]); //(5.1.008) +static void roofFluxRates(double x[], double f[]); static double getSurfaceOutflowRate(double depth); static double getSurfaceOverflowRate(double* surfaceDepth); static double getPavementPermRate(void); -static double getSoilPercRate(double theta); //(5.1.007) -static double getStorageExfilRate(void); //(5.1.011) -static double getStorageDrainRate(double storageDepth, double soilTheta, //(5.1.011) - double paveDepth, double surfaceDepth); //(5.1.011) +static double getSoilPercRate(double theta); +static double getStorageExfilRate(void); +static double getStorageDrainRate(double storageDepth, double soilTheta, + double paveDepth, double surfaceDepth); static double getDrainMatOutflow(double depth); -static void getEvapRates(double surfaceVol, double paveVol, //(5.1.008) - double soilVol, double storageVol, double pervFrac); //(5.1.011) +static void getEvapRates(double surfaceVol, double paveVol, + double soilVol, double storageVol, double pervFrac); static void updateWaterBalance(TLidUnit *lidUnit, double inflow, double evap, double infil, double surfFlow, @@ -167,7 +171,7 @@ static void updateWaterBalance(TLidUnit *lidUnit, double inflow, 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, //(5.1.007) + double* qOld, double* q, double dt, double omega, void (*derivs)(double*, double*)); @@ -187,13 +191,11 @@ void lidproc_initWaterBalance(TLidUnit *lidUnit, double initVol) lidUnit->waterBalance.surfFlow = 0.0; lidUnit->waterBalance.drainFlow = 0.0; lidUnit->waterBalance.initVol = initVol; - lidUnit->waterBalance.finalVol = initVol; //(5.1.008) + lidUnit->waterBalance.finalVol = initVol; } //============================================================================= -//// This function was modified for release 5.1.008. //// //(5.1.008) - double lidproc_getOutflow(TLidUnit* lidUnit, TLidProc* lidProc, double inflow, double evap, double infil, double maxInfil, double tStep, double* lidEvap, @@ -255,7 +257,7 @@ double lidproc_getOutflow(TLidUnit* lidUnit, TLidProc* lidProc, double inflow, SoilEvap = 0.0; SoilPerc = 0.0; StorageInflow = 0.0; - StorageExfil = 0.0; //(5.1.011) + StorageExfil = 0.0; StorageEvap = 0.0; StorageDrain = 0.0; for (i = 0; i < MAX_LAYERS; i++) @@ -274,7 +276,7 @@ double lidproc_getOutflow(TLidUnit* lidUnit, TLidProc* lidProc, double inflow, SurfaceInfil = grnampt_getInfil(&theLidUnit->soilInfil, Tstep, SurfaceInflow, theLidUnit->surfaceDepth, - MOD_GREEN_AMPT); //(5.1.010) + MOD_GREEN_AMPT); } else SurfaceInfil = infil; @@ -286,7 +288,7 @@ double lidproc_getOutflow(TLidUnit* lidUnit, TLidProc* lidProc, double inflow, } if ( theLidProc->pavement.thickness > 0.0 ) { - xMax[PAVE] = theLidProc->pavement.thickness; //(5.1.011) + xMax[PAVE] = theLidProc->pavement.thickness; } if ( theLidProc->storage.thickness > 0.0 ) { @@ -337,7 +339,7 @@ double lidproc_getOutflow(TLidUnit* lidUnit, TLidProc* lidProc, double inflow, //... save updated results theLidUnit->surfaceDepth = x[SURF]; - theLidUnit->paveDepth = x[PAVE]; //(5.1.011) + theLidUnit->paveDepth = x[PAVE]; theLidUnit->soilMoisture = x[SOIL]; theLidUnit->storageDepth = x[STOR]; for (i = 0; i < MAX_LAYERS; i++) theLidUnit->oldFluxRates[i] = f[i]; @@ -353,8 +355,6 @@ double lidproc_getOutflow(TLidUnit* lidUnit, TLidProc* lidProc, double inflow, //============================================================================= -//// This function was re-written for release 5.1.011. //// //(5.1.011) - void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDepth) // // Purpose: updates the mass balance for an LID unit and saves @@ -392,8 +392,8 @@ void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDe //... update status of HasWetLids if ( !isDry ) HasWetLids = TRUE; - //... write results to LID report file //(5.1.012) - if ( lidUnit->rptFile ) //(5.1.012) + //... write results to LID report file + if ( lidUnit->rptFile ) { //... convert rate results to original units (in/hr or mm/hr) ucf = ucfRainfall; @@ -409,14 +409,14 @@ 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; //(5.1.011) + rptVars[PAVE_DEPTH] = theLidUnit->paveDepth; rptVars[SOIL_MOIST] = theLidUnit->soilMoisture; rptVars[STOR_DEPTH] = theLidUnit->storageDepth*ucf; //... if the current LID state is wet but the previous state was dry - // for more than one period then write the saved previous results //(5.1.012) - // to the report file thus marking the end of a dry period //(5.10012) - if ( !isDry && theLidUnit->rptFile->wasDry > 1) //(5.1.012) + // for more than one period then write the saved previous results + // to the report file thus marking the end of a dry period + if ( !isDry && theLidUnit->rptFile->wasDry > 1) { fprintf(theLidUnit->rptFile->file, "%s", theLidUnit->rptFile->results); @@ -438,14 +438,14 @@ void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDe { //... if the previous state was wet then write the current // results to file marking the start of a dry period - if ( theLidUnit->rptFile->wasDry == 0 ) //(5.1.012) + if ( theLidUnit->rptFile->wasDry == 0 ) { fprintf(theLidUnit->rptFile->file, "%s", theLidUnit->rptFile->results); } - //... increment the number of successive dry periods //(5.1.012) - theLidUnit->rptFile->wasDry++; //(5.1.012) + //... increment the number of successive dry periods + theLidUnit->rptFile->wasDry++; } //... if the current LID state is wet @@ -455,16 +455,14 @@ void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDe fprintf(theLidUnit->rptFile->file, "%s", theLidUnit->rptFile->results); - //... re-set the number of successive dry periods to 0 //(5.1.012) - theLidUnit->rptFile->wasDry = 0; //(5.1.012) + //... re-set the number of successive dry periods to 0 + theLidUnit->rptFile->wasDry = 0; } } } //============================================================================= -//// New function for release 5.1.008. //// //(5.1.008) - void roofFluxRates(double x[], double f[]) // // Purpose: computes flux rates for roof disconnection. @@ -474,7 +472,7 @@ void roofFluxRates(double x[], double f[]) { double surfaceDepth = x[SURF]; - getEvapRates(surfaceDepth, 0.0, 0.0, 0.0, 1.0); //(5.1.011) + getEvapRates(surfaceDepth, 0.0, 0.0, 0.0, 1.0); SurfaceVolume = surfaceDepth; SurfaceInfil = 0.0; if ( theLidProc->surface.alpha > 0.0 ) @@ -487,8 +485,6 @@ void roofFluxRates(double x[], double f[]) //============================================================================= -//// This function was re-written for release 5.1.011. //// //(5.1.011) - void greenRoofFluxRates(double x[], double f[]) // // Purpose: computes flux rates from the layers of a green roof. @@ -533,7 +529,7 @@ void greenRoofFluxRates(double x[], double f[]) //... limit perc rate by available water availVolume = (soilTheta - soilFieldCap) * soilThickness; - maxRate = MAX(availVolume, 0.0) / Tstep - SoilEvap; //(5.1.012) + maxRate = MAX(availVolume, 0.0) / Tstep - SoilEvap; SoilPerc = MIN(SoilPerc, maxRate); SoilPerc = MAX(SoilPerc, 0.0); @@ -557,8 +553,8 @@ void greenRoofFluxRates(double x[], double f[]) else { //... limit drainmat outflow by available storage volume - maxRate = storageDepth * storageVoidFrac / Tstep - StorageEvap; //(5.1.012) - if ( storageDepth >= storageThickness ) maxRate += SoilPerc; //(5.1.012) + maxRate = storageDepth * storageVoidFrac / Tstep - StorageEvap; + if ( storageDepth >= storageThickness ) maxRate += SoilPerc; maxRate = MAX(maxRate, 0.0); StorageDrain = MIN(StorageDrain, maxRate); @@ -587,8 +583,6 @@ void greenRoofFluxRates(double x[], double f[]) //============================================================================= -//// This function was re-written for release 5.1.011. //// //(5.1.011) - void biocellFluxRates(double x[], double f[]) // // Purpose: computes flux rates from the layers of a bio-retention cell LID. @@ -633,7 +627,7 @@ void biocellFluxRates(double x[], double f[]) //... limit perc rate by available water availVolume = (soilTheta - soilFieldCap) * soilThickness; - maxRate = MAX(availVolume, 0.0) / Tstep - SoilEvap; //(5.1.012) + maxRate = MAX(availVolume, 0.0) / Tstep - SoilEvap; SoilPerc = MIN(SoilPerc, maxRate); SoilPerc = MAX(SoilPerc, 0.0); @@ -656,12 +650,10 @@ void biocellFluxRates(double x[], double f[]) SoilPerc = maxRate; StorageExfil = maxRate; -//// Following code segment added to release 5.1.012 //// //(5.1.012) //... limit surface infil. by unused soil volume maxRate = (soilPorosity - soilTheta) * soilThickness / Tstep + SoilPerc + SoilEvap; SurfaceInfil = MIN(SurfaceInfil, maxRate); -////////////////////////////////////////////////////////// } @@ -697,8 +689,8 @@ void biocellFluxRates(double x[], double f[]) //... limit underdrain flow by volume above drain offset if ( StorageDrain > 0.0 ) { - maxRate = -StorageExfil - StorageEvap; //(5.1.012) - if ( storageDepth >= storageThickness) maxRate += SoilPerc; //(5.1.012) + maxRate = -StorageExfil - StorageEvap; + if ( storageDepth >= storageThickness) maxRate += SoilPerc; if ( theLidProc->drain.offset <= storageDepth ) { maxRate += (storageDepth - theLidProc->drain.offset) * @@ -735,8 +727,6 @@ void biocellFluxRates(double x[], double f[]) //============================================================================= -//// This function was re-written for release 5.1.011. //// //(5.1.011) - void trenchFluxRates(double x[], double f[]) // // Purpose: computes flux rates from the layers of an infiltration trench LID. @@ -793,8 +783,8 @@ void trenchFluxRates(double x[], double f[]) //... limit underdrain flow by volume above drain offset if ( StorageDrain > 0.0 ) { - maxRate = -StorageExfil - StorageEvap; //(5.1.012) - if (storageDepth >= storageThickness ) maxRate += StorageInflow; //(5.1.012) + maxRate = -StorageExfil - StorageEvap; + if (storageDepth >= storageThickness ) maxRate += StorageInflow; if ( theLidProc->drain.offset <= storageDepth ) { maxRate += (storageDepth - theLidProc->drain.offset) * @@ -825,8 +815,6 @@ void trenchFluxRates(double x[], double f[]) //============================================================================= -//// This function was re-written for release 5.1.011. //// //(5.1.011) - void pavementFluxRates(double x[], double f[]) // // Purpose: computes flux rates for the layers of a porous pavement LID. @@ -884,6 +872,9 @@ void pavementFluxRates(double x[], double f[]) //... find perc rate out of pavement layer PavePerc = getPavementPermRate(); + //... surface infiltration can't exceed pavement permeability //(5.1.013) + SurfaceInfil = MIN(SurfaceInfil, PavePerc); // + //... limit pavement perc by available water maxRate = PaveVolume/Tstep + SurfaceInfil - PaveEvap; maxRate = MAX(maxRate, 0.0); @@ -894,7 +885,7 @@ void pavementFluxRates(double x[], double f[]) { SoilPerc = getSoilPercRate(soilTheta); availVolume = (soilTheta - soilFieldCap) * soilThickness; - maxRate = MAX(availVolume, 0.0) / Tstep - SoilEvap; //(5.1.012) + maxRate = MAX(availVolume, 0.0) / Tstep - SoilEvap; SoilPerc = MIN(SoilPerc, maxRate); SoilPerc = MAX(SoilPerc, 0.0); } @@ -1007,8 +998,8 @@ void pavementFluxRates(double x[], double f[]) //... limit underdrain flow by volume above drain offset if ( StorageDrain > 0.0 ) { - maxRate = -StorageExfil - StorageEvap; //(5.1.012) - if (storageDepth >= storageThickness ) maxRate += SoilPerc; //(5.1.012) + maxRate = -StorageExfil - StorageEvap; + if (storageDepth >= storageThickness ) maxRate += SoilPerc; if ( theLidProc->drain.offset <= storageDepth ) { maxRate += (storageDepth - theLidProc->drain.offset) * @@ -1122,7 +1113,7 @@ void swaleFluxRates(double x[], double f[]) SurfaceEvap = MIN(SurfaceEvap, volume/Tstep); //... infiltration rate to native soil in cfs - StorageExfil = SurfaceInfil * surfArea; //(5.1.011) + StorageExfil = SurfaceInfil * surfArea; //... no surface outflow if depth below depression storage xDepth = depth - dStore; @@ -1149,7 +1140,7 @@ void swaleFluxRates(double x[], double f[]) } //... net flux rate (dV/dt) in cfs - dVdT = surfInflow - SurfaceEvap - StorageExfil - SurfaceOutflow; //(5.1.011) + dVdT = surfInflow - SurfaceEvap - StorageExfil - SurfaceOutflow; //... when full, any net positive inflow becomes spillage if ( depth == theLidProc->surface.thickness && dVdT > 0.0 ) @@ -1160,7 +1151,7 @@ void swaleFluxRates(double x[], double f[]) //... convert flux rates to ft/s SurfaceEvap /= lidArea; - StorageExfil /= lidArea; //(5.1.011) + StorageExfil /= lidArea; SurfaceOutflow /= lidArea; f[SURF] = dVdT / surfArea; f[SOIL] = 0.0; @@ -1174,8 +1165,6 @@ void swaleFluxRates(double x[], double f[]) //============================================================================= -//// This function was re-written for release 5.1.007. //// //(5.1.007) - void barrelFluxRates(double x[], double f[]) // // Purpose: computes flux rates for a rain barrel LID. @@ -1259,23 +1248,40 @@ double getPavementPermRate() // Output: returns the reduced permeability of the pavement layer (ft/s). // { - double permRate; - double permReduction; + double permReduction = 0.0; + double clogFactor= theLidProc->pavement.clogFactor; + double regenDays = theLidProc->pavement.regenDays; - permReduction = theLidProc->pavement.clogFactor; - if ( permReduction > 0.0 ) + // ... find permeability reduction due to clogging + if ( clogFactor > 0.0 ) { - permReduction = theLidUnit->waterBalance.inflow / permReduction; + // ... see if permeability regeneration has occurred + // (regeneration is assumed to reduce the total + // volumetric loading that the pavement has received) + if ( regenDays > 0.0 ) + { + if ( OldRunoffTime / 1000.0 / SECperDAY >= theLidUnit->nextRegenDay ) + { + // ... reduce total volume treated by degree of regeneration + theLidUnit->volTreated *= + (1.0 - theLidProc->pavement.regenDegree); + + // ... update next day that regenration occurs + theLidUnit->nextRegenDay += regenDays; + } + } + + // ... find permeabiity reduction factor + permReduction = theLidUnit->volTreated / clogFactor; permReduction = MIN(permReduction, 1.0); } - permRate = theLidProc->pavement.kSat * (1.0 - permReduction); - return permRate; + + // ... return the effective pavement permeability + return theLidProc->pavement.kSat * (1.0 - permReduction); } //============================================================================= -//// This function was modified for release 5.1.011. //// //(5.1.011) - double getSoilPercRate(double theta) // // Purpose: computes percolation rate of water through a LID's soil layer. @@ -1296,9 +1302,9 @@ double getSoilPercRate(double theta) //============================================================================= -double getStorageExfilRate() //(5.1.011) +double getStorageExfilRate() // -// Purpose: computes exfiltration rate from storage zone into //(5.1.011) +// Purpose: computes exfiltration rate from storage zone into // native soil beneath a LID. // Input: depth = depth of water storage zone (ft) // Output: returns infiltration rate (ft/s) @@ -1327,8 +1333,6 @@ double getStorageExfilRate() / //============================================================================= -//// This function was modified for release 5.1.011. //// //(5.1.011) - double getStorageDrainRate(double storageDepth, double soilTheta, double paveDepth, double surfaceDepth) // @@ -1344,6 +1348,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) double head = storageDepth; double outflow = 0.0; double paveThickness = theLidProc->pavement.thickness; @@ -1383,16 +1388,32 @@ 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; // + // --- make head relative to drain offset head -= theLidProc->drain.offset; - // ... compute drain outflow from underdrain flow equation in user units + // --- compute drain outflow from underdrain flow equation in user units // (head in inches or mm, flow rate in in/hr or mm/hr) if ( head > ZERO ) { + // --- convert head to user units head *= UCF(RAINDEPTH); + + // --- compute drain outflow in user units outflow = theLidProc->drain.coeff * pow(head, theLidProc->drain.expon); + + // --- apply user-supplied control curve to outflow + if (curve >= 0) outflow *= table_lookup(&Curve[curve], head); //(5.1.013) + + // --- convert outflow to ft/s outflow /= UCF(RAINFALL); } return outflow; @@ -1400,8 +1421,6 @@ double getStorageDrainRate(double storageDepth, double soilTheta, //============================================================================= -//// This function was modified for release 5.1.007. //// //(5.1.007) - double getDrainMatOutflow(double depth) // // Purpose: computes flow rate through a green roof's drainage mat. @@ -1417,24 +1436,22 @@ double getDrainMatOutflow(double depth) { result = theLidProc->drainMat.alpha * pow(depth, 5.0/3.0) * theLidUnit->fullWidth / theLidUnit->area * - theLidProc->drainMat.voidFrac; //(5.1.008) + theLidProc->drainMat.voidFrac; } return result; } //============================================================================= -//// This function was re-written for release 5.1.008. //// //(5.1.008) - void getEvapRates(double surfaceVol, double paveVol, double soilVol, - double storageVol, double pervFrac) //(5.1.011) + double storageVol, double pervFrac) // // Purpose: computes surface, pavement, soil, and storage evaporation rates. // Input: surfaceVol = volume/area of ponded water on surface layer (ft) // paveVol = volume/area of water in pavement pores (ft) // soilVol = volume/area of water in soil (or pavement) pores (ft) // storageVol = volume/area of water in storage layer (ft) -// pervFrac = fraction of surface layer that is pervious //(5.1.011) +// pervFrac = fraction of surface layer that is pervious // Output: none // { @@ -1445,7 +1462,7 @@ void getEvapRates(double surfaceVol, double paveVol, double soilVol, SurfaceEvap = MIN(availEvap, surfaceVol/Tstep); SurfaceEvap = MAX(0.0, SurfaceEvap); availEvap = MAX(0.0, (availEvap - SurfaceEvap)); - availEvap *= pervFrac; //(5.1.011) + availEvap *= pervFrac; //... no subsurface evap if water is infiltrating if ( SurfaceInfil > 0.0 ) @@ -1501,6 +1518,7 @@ void updateWaterBalance(TLidUnit *lidUnit, double inflow, double evap, // Output: none // { + lidUnit->volTreated += inflow * Tstep; //(5.1.013) lidUnit->waterBalance.inflow += inflow * Tstep; lidUnit->waterBalance.evap += evap * Tstep; lidUnit->waterBalance.infil += infil * Tstep; @@ -1513,7 +1531,7 @@ void updateWaterBalance(TLidUnit *lidUnit, double inflow, double evap, 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, //(5.1.007) + double* qOld, double* q, double dt, double omega, void (*derivs)(double*, double*)) // // Purpose: solves system of equations dx/dt = q(x) for x at end of time step @@ -1528,8 +1546,8 @@ int modpuls_solve(int n, double* x, double* xOld, double* xPrev, // qOld = flux rates at start of time step // q = flux rates at end of time step // dt = time step (sec) -// omega = time weighting parameter (use 0 for Euler method //(5.1.007) -// or 0.5 for modified Puls method) //(5.1.007) +// omega = time weighting parameter (use 0 for Euler method +// or 0.5 for modified Puls method) // derivs = pointer to function that computes flux rates q as a // function of state variables x // Output: returns number of steps required for convergence (or 0 if @@ -1562,7 +1580,7 @@ int modpuls_solve(int n, double* x, double* xOld, double* xPrev, x[i] = MIN(x[i], xMax[i]); x[i] = MAX(x[i], xMin[i]); - if ( omega > 0.0 && //(5.1.007) + if ( omega > 0.0 && fabs(x[i] - xPrev[i]) > xTol[i] ) canStop = 0; xPrev[i] = x[i]; } diff --git a/src/link.c b/src/link.c index f2e207ae4..c2e618291 100644 --- a/src/link.c +++ b/src/link.c @@ -9,6 +9,7 @@ // 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) // M. Tryby (EPA) // @@ -38,6 +39,12 @@ // - 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. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -90,7 +97,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, double tstep); //(5.1.008) +static double conduit_getLossRate(int j, double q, double tstep); static int pump_readParams(int j, int k, char* tok[], int ntoks); static void pump_validate(int j, int k); @@ -107,12 +114,12 @@ static double orifice_getFlow(int j, int k, double head, double f, static int weir_readParams(int j, int k, char* tok[], int ntoks); static void weir_validate(int j, int k); -static void weir_setSetting(int j); //(5.1.007) +static void weir_setSetting(int j); static double weir_getInflow(int j); static double weir_getOpenArea(int j, double y); static void weir_getFlow(int j, int k, double head, double dir, int hasFlapGate, double* q1, double* q2); -static double weir_getOrificeFlow(int j, double head, double y, double cOrif); //(5.1.007) +static double weir_getOrificeFlow(int j, double head, double y, double cOrif); static double weir_getdqdh(int k, double dir, double h, double q1, double q2); static int outlet_readParams(int j, int k, char* tok[], int ntoks); @@ -203,14 +210,12 @@ int link_readXsectParams(char* tok[], int ntoks) return error_setInpError(ERR_NUMBER, tok[i]); } -//// Following code segment added to release 5.1.011. //// //(5.1.011) // --- ignore extra parameters for non-conduit open rectangular shapes if ( Link[j].type != CONDUIT && k == RECT_OPEN ) { x[2] = 0.0; x[3] = 0.0; } -//// if ( !xsect_setParams(&Link[j].xsect, k, x, UCF(LENGTH)) ) { return error_setInpError(ERR_NUMBER, ""); @@ -246,7 +251,7 @@ int link_readLossParams(char* tok[], int ntoks) // Purpose: reads local loss parameters for a link from a tokenized // line of input data. // -// Format: LinkID cInlet cOutlet cAvg FlapGate(YES/NO) SeepRate //(5.1.007) +// Format: LinkID cInlet cOutlet cAvg FlapGate(YES/NO) SeepRate // { int i, j, k; @@ -347,10 +352,10 @@ void link_setParams(int j, int type, int n1, int n2, int k, double x[]) Link[j].hasFlapGate = (x[3] > 0.0) ? 1 : 0; Weir[k].endCon = x[4]; Weir[k].cDisch2 = x[5]; - Weir[k].canSurcharge = (int)x[6]; //(5.1.007) - Weir[k].roadWidth = x[7] / UCF(LENGTH); //(5.1.011) - Weir[k].roadSurface = (int)x[8]; //(5.1.010) -// Weir[k].shape = -(int)x[9]; //DELETED// //(5.1.011) + 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) break; case OUTLET: @@ -397,9 +402,13 @@ void link_validate(int j) if ( Node[Link[j].node1].invertElev + Link[j].offset1 < Node[Link[j].node2].invertElev ) { - Link[j].offset1 = Node[Link[j].node2].invertElev - //(5.1.011) - Node[Link[j].node1].invertElev; //(5.1.011) - report_writeWarningMsg(WARN10, Link[j].ID); + if (RouteModel == DW) //(5.1.013) + { + Link[j].offset1 = Node[Link[j].node2].invertElev - + Node[Link[j].node1].invertElev; + report_writeWarningMsg(WARN10b, Link[j].ID); //(5.1.013) + } + else report_writeWarningMsg(WARN10a, Link[j].ID); //(5.1.013) } } @@ -413,7 +422,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 ) + if ( Node[n].type != STORAGE || Node[n].surDepth > 0.0 ) //(5.1.013) { Node[n].fullDepth = MAX(Node[n].fullDepth, Link[j].offset1 + Link[j].xsect.yFull); @@ -421,7 +430,8 @@ void link_validate(int j) // --- do same for downstream node only for conduit links n = Link[j].node2; - if ( Node[n].type != STORAGE && Link[j].type == CONDUIT ) + if ( (Node[n].type != STORAGE || Node[n].surDepth > 0.0) && //(5.1.013) + Link[j].type == CONDUIT ) { Node[n].fullDepth = MAX(Node[n].fullDepth, Link[j].offset2 + Link[j].xsect.yFull); @@ -488,7 +498,7 @@ void link_initState(int j) Link[j].newVolume = 0.0; Link[j].setting = 1.0; Link[j].targetSetting = 1.0; - Link[j].timeLastSet = StartDate; //(5.1.010) + Link[j].timeLastSet = StartDate; Link[j].inletControl = FALSE; Link[j].normalFlow = FALSE; if ( Link[j].type == CONDUIT ) conduit_initState(j, Link[j].subIndex); @@ -526,8 +536,6 @@ double link_getInflow(int j) //============================================================================= -//// This function has been modified for release 5.1.008. //// //(5.1.008) - void link_setOldHydState(int j) // // Input: j = link index @@ -601,7 +609,7 @@ void link_setSetting(int j, double tstep) // { if ( Link[j].type == ORIFICE ) orifice_setSetting(j, tstep); - else if ( Link[j].type == WEIR ) weir_setSetting(j); //(5.1.007) + else if ( Link[j].type == WEIR ) weir_setSetting(j); else Link[j].setting = Link[j].targetSetting; } @@ -857,24 +865,22 @@ double link_getPower(int j) //============================================================================= -double link_getLossRate(int j, double q, double tStep) //(5.1.008) +double link_getLossRate(int j, double q, double tStep) // // Input: j = link index -// q = flow rate (ft3/sec) //(5.1.008) +// q = flow rate (ft3/sec) // tstep = time step (sec) // Output: returns uniform loss rate in link (ft3/sec) -// Purpose: computes rate at which flow volume is lost in a link due to //(5.1.008) +// Purpose: computes rate at which flow volume is lost in a link due to // evaporation and seepage. // { - if ( Link[j].type == CONDUIT ) return conduit_getLossRate(j, q, tStep); //(5.1.008) + if ( Link[j].type == CONDUIT ) return conduit_getLossRate(j, q, tStep); else return 0.0; } //============================================================================= -//// New function added to release 5.1.008. //// //(5.1.008) - char link_getFullState(double a1, double a2, double aFull) // // Input: a1 = upstream link area (ft2) @@ -968,7 +974,7 @@ void conduit_validate(int j, int k) double lengthFactor, roughness, slope; // --- a storage node cannot have a dummy outflow link - if ( Link[j].xsect.type == DUMMY && RouteModel == DW ) //(5.1.007) + if ( Link[j].xsect.type == DUMMY && RouteModel == DW ) { if ( Node[Link[j].node1].type == STORAGE ) { @@ -1289,20 +1295,21 @@ double conduit_getInflow(int j) //============================================================================= -double conduit_getLossRate(int j, double q, double tStep) //(5.1.008) +//// This function was modified for relese 5.1.013. //// //(5.1.013) + +double conduit_getLossRate(int j, double q, double tStep) // // Input: j = link index // tStep = time step (sec) -// Output: returns rate of evaporation & seepage losses (ft3/sec) //(5.1.008) -// Purpose: computes volumetric rate of water evaporation & seepage //(5.1.008) -// from a conduit (per barrel). //(5.1.008) +// Output: returns rate of evaporation & seepage losses (ft3/sec) +// Purpose: computes volumetric rate of water evaporation & seepage +// from a conduit (per barrel). // { TXsect *xsect; double depth = 0.5 * (Link[j].oldDepth + Link[j].newDepth); double length; double topWidth; - //double wettedPerimeter; //DEPRECATED //(5.1.012) double maxLossRate; double evapLossRate = 0.0, seepLossRate = 0.0, @@ -1326,30 +1333,20 @@ double conduit_getLossRate(int j, double q, double tStep) / // limit depth to depth at max width if ( depth >= xsect->ywMax ) depth = xsect->ywMax; -//// The following section was deprecated for release 5.1.012. //// //(5.1.012) - // get wetted perimeter -// wettedPerimeter = 0.0; -// if ( depth > 0.0 ) -// { -// wettedPerimeter = xsect_getAofY(xsect, depth) / -// xsect_getRofY(xsect, depth); -// } -///////////////////////////////////////////////////////////////// - // compute seepage loss rate across length of conduit - seepLossRate = Link[j].seepRate * xsect_getWofY(xsect, depth) * //(5.1.012) + seepLossRate = Link[j].seepRate * xsect_getWofY(xsect, depth) * length; - seepLossRate *= Adjust.hydconFactor; //(5.1.008) + seepLossRate *= Adjust.hydconFactor; } // --- compute total loss rate totalLossRate = evapLossRate + seepLossRate; - // --- total loss rate cannot exceed flow volume or flow rate //(5.1.008) + // --- total loss rate cannot exceed current volume if ( totalLossRate > 0.0 ) { maxLossRate = 0.5 * (Link[j].oldVolume + Link[j].newVolume) / tStep; - maxLossRate = MIN(maxLossRate, fabs(q)); //(5.1.008) + maxLossRate = MIN(maxLossRate, fabs(q)); if ( totalLossRate > maxLossRate ) { evapLossRate = evapLossRate * maxLossRate / totalLossRate; @@ -1358,6 +1355,7 @@ double conduit_getLossRate(int j, double q, double tStep) / } } } + Conduit[Link[j].subIndex].evapLossRate = evapLossRate; Conduit[Link[j].subIndex].seepLossRate = seepLossRate; return totalLossRate; @@ -1984,7 +1982,7 @@ int weir_readParams(int j, int k, char* tok[], int ntoks) { int m; int n1, n2; - double x[9]; //(5.1.010) + double x[10]; //(5.1.013) char* id; // --- check for valid ID and end node IDs @@ -2008,36 +2006,34 @@ int weir_readParams(int j, int k, char* tok[], int ntoks) x[3] = 0.0; x[4] = 0.0; x[5] = 0.0; - x[6] = 1.0; //(5.1.007) - x[7] = 0.0; //(5.1.010) - x[8] = 0.0; //(5.1.010) - if ( ntoks >= 7 && *tok[6] != '*' ) //(5.1.007) + x[6] = 1.0; + x[7] = 0.0; + x[8] = 0.0; + x[9] = -1.0; //(5.1.013) + if ( ntoks >= 7 && *tok[6] != '*' ) { m = findmatch(tok[6], NoYesWords); if ( m < 0 ) return error_setInpError(ERR_KEYWORD, tok[6]); x[3] = m; // flap gate } - if ( ntoks >= 8 && *tok[7] != '*' ) //(5.1.007) + if ( ntoks >= 8 && *tok[7] != '*' ) { if ( ! getDouble(tok[7], &x[4]) || x[4] < 0.0 ) // endCon return error_setInpError(ERR_NUMBER, tok[7]); } - if ( ntoks >= 9 && *tok[8] != '*' ) //(5.1.007) + if ( ntoks >= 9 && *tok[8] != '*' ) { if ( ! getDouble(tok[8], &x[5]) || x[5] < 0.0 ) // cDisch2 return error_setInpError(ERR_NUMBER, tok[8]); } -//// Following segment added for release 5.1.007. //// //(5.1.007) if ( ntoks >= 10 && *tok[9] != '*' ) { m = findmatch(tok[9], NoYesWords); if ( m < 0 ) return error_setInpError(ERR_KEYWORD, tok[9]); x[6] = m; // canSurcharge } -//// -//// Following segment added for release 5.1.010. //// //(5.1.010) if ( (m = (int)x[0]) == ROADWAY_WEIR ) { if ( ntoks >= 11 ) // road width @@ -2051,7 +2047,13 @@ int weir_readParams(int j, int k, char* tok[], int ntoks) else if ( strcomp(tok[11], "GRAVEL") ) x[8] = 2.0; } } -//// + + 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; // + } // // --- add parameters to weir object Link[j].ID = id; @@ -2070,14 +2072,14 @@ void weir_validate(int j, int k) // { int err = 0; - double q, q1, q2, head; //(5.1.008) + double q, q1, q2, head; // --- check for valid cross section switch ( Weir[k].type) { case TRANSVERSE_WEIR: case SIDEFLOW_WEIR: - case ROADWAY_WEIR: //(5.1.010) + case ROADWAY_WEIR: if ( Link[j].xsect.type != RECT_OPEN ) err = ERR_REGULATOR_SHAPE; Weir[k].slope = 0.0; break; @@ -2112,13 +2114,10 @@ void weir_validate(int j, int k) Weir[k].length = MAX(200.0, Weir[k].length); Weir[k].surfArea = 0.0; -//// Following code segment added to release 5.1.008. //// //(5.1.008) - // --- find flow through weir when water level equals weir height head = Link[j].xsect.yFull; weir_getFlow(j, k, head, 1.0, FALSE, &q1, &q2); q = q1 + q2; -//// // --- compute equivalent orifice coeff. (for CFS flow units) head = head / 2.0; // head seen by equivalent orifice @@ -2127,8 +2126,6 @@ void weir_validate(int j, int k) //============================================================================= -//// New function added to release 5.1.007. //// //(5.1.007) - void weir_setSetting(int j) // // Input: j = link index @@ -2142,7 +2139,7 @@ void weir_setSetting(int j) // --- adjust weir setting Link[j].setting = Link[j].targetSetting; if ( !Weir[k].canSurcharge ) return; - if ( Weir[k].type == ROADWAY_WEIR ) return; //(5.1.010) + if ( Weir[k].type == ROADWAY_WEIR ) return; // --- find orifice coeff. for surcharged flow if ( Link[j].setting == 0.0 ) Weir[k].cSurcharge = 0.0; @@ -2213,11 +2210,9 @@ double weir_getInflow(int j) hcrest = Node[n1].invertElev + Link[j].offset1; hcrown = hcrest + Link[j].xsect.yFull; -//// Added to release 5.1.010. //// //(5.1.010) // --- treat a roadway weir as a special case if ( Weir[k].type == ROADWAY_WEIR ) return roadway_getInflow(j, dir, hcrest, h1, h2); -//// // --- adjust crest ht. for partially open weir hcrest += (1.0 - Link[j].setting) * Link[j].xsect.yFull; @@ -2247,8 +2242,6 @@ double weir_getInflow(int j) y = Link[j].xsect.yFull - (hcrown - MIN(h1, hcrown)); Weir[k].surfArea = xsect_getWofY(&Link[j].xsect, y) * Weir[k].length; -//// New section added to release 5.1.007. //// //(5.1.007) - // --- head is above crown if ( h1 >= hcrown ) { @@ -2267,7 +2260,6 @@ double weir_getInflow(int j) // --- otherwise limit head to height of weir opening else head = hcrown - hcrest; } -//// // --- use weir eqn. to find flows through central (q1) // and end sections (q2) of weir @@ -2309,6 +2301,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; // // --- q1 = flow through central portion of weir, // q2 = flow through end sections of trapezoidal weir @@ -2321,11 +2315,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); -//// Following code segment re-located. //// //(5.1.012) - // --- reduce length when end contractions present - //length -= 0.1 * Weir[k].endCon * h; - //length = MAX(length, 0.0); -///////////////////////////////////////////// + // --- lookup tabulated discharge coeff. //(5.1.013) + if ( cdCurve >= 0 ) cDisch1 = table_lookup(&Curve[cdCurve], h); // // --- use appropriate formula for weir flow wType = Weir[k].type; @@ -2335,42 +2326,37 @@ void weir_getFlow(int j, int k, double head, double dir, int hasFlapGate, { case TRANSVERSE_WEIR: - // --- reduce length when end contractions present //(5.1.012) - length -= 0.1 * Weir[k].endCon * h; //(5.1.012) - length = MAX(length, 0.0); //(5.1.012) - *q1 = Weir[k].cDisch1 * length * pow(h, 1.5); + // --- 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) break; case SIDEFLOW_WEIR: - // --- reduce length when end contractions present //(5.1.012) - length -= 0.1 * Weir[k].endCon * h; //(5.1.012) - length = MAX(length, 0.0); //(5.1.012) + // --- reduce length when end contractions present + length -= 0.1 * Weir[k].endCon * h; + length = MAX(length, 0.0); // --- weir behaves as a transverse weir under reverse flow if ( dir < 0.0 ) - *q1 = Weir[k].cDisch1 * length * pow(h, 1.5); + *q1 = cDisch1 * length * pow(h, 1.5); //(5.1.013) else -//// Corrected formula //// //(5.1.012) -// (see Metcalf & Eddy, Inc., Wastewater Engineering, McGraw-Hill, 1972 p. 164). - *q1 = Weir[k].cDisch1 * pow(length, 0.83) * pow(h, 1.67); + // 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) break; case VNOTCH_WEIR: - *q1 = Weir[k].cDisch1 * Weir[k].slope * pow(h, 2.5); + *q1 = cDisch1 * Weir[k].slope * pow(h, 2.5); //(5.1.013) break; case TRAPEZOIDAL_WEIR: y = (1.0 - Link[j].setting) * Link[j].xsect.yFull; length = xsect_getWofY(&Link[j].xsect, y) * UCF(LENGTH); - -//// End contractions don't apply to trapezoidal weirs //// //(5.1.012) - //length -= 0.1 * Weir[k].endCon * h; //(5.1.012) - //length = MAX(length, 0.0); //(5.1.012) - - *q1 = Weir[k].cDisch1 * length * pow(h, 1.5); + *q1 = cDisch1 * length * pow(h, 1.5); //(5.1.013) *q2 = Weir[k].cDisch2 * Weir[k].slope * pow(h, 2.5); } @@ -2482,7 +2468,7 @@ double weir_getdqdh(int k, double dir, double h, double q1, double q2) case SIDEFLOW_WEIR: // --- weir behaves as a transverse weir under reverse flow if ( dir < 0.0 ) return 1.5 * q1h; - else return 1.67 * q1h; //(5.1.012) + else return 1.67 * q1h; case VNOTCH_WEIR: if ( q2h == 0.0 ) return 2.5 * q1h; // Fully open diff --git a/src/main.c b/src/main.c new file mode 100644 index 000000000..b4af1f112 --- /dev/null +++ b/src/main.c @@ -0,0 +1,103 @@ +//----------------------------------------------------------------------------- +// main.c +// +// Project: EPA SWMM5 +// Version: 5.1 +// Date: 05/10/2018 +// Author: L. Rossman + +// Main stub for the command line version of EPA SWMM 5.1 +// to be run with swmm5.dll. + +#include +#include +#include "swmm5.h" + +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. +// +// Command line is: swmm5 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). +// +{ + char *inputFile; + char *reportFile; + char *binaryFile; + char *arg1; + char blank[] = ""; + int version, vMajor, vMinor, vRelease; + char errMsg[128]; + int msgLen = 127; + time_t start; + double runTime; + + version = swmm_getVersion(); + vMajor = version / 10000; + vMinor = (version - 10000 * vMajor) / 1000; + vRelease = (version - 10000 * vMajor - 1000 * vMinor); + start = time(0); + + // --- check for proper number of command line arguments + if (argc == 1) + { + printf("\nNot Enough Arguments (See Help --help)\n\n"); + } + else if (argc == 2) + { + // --- extract first argument + arg1 = argv[1]; + + if (strcmp(arg1, "--help") == 0 || strcmp(arg1, "-h") == 0) + { + // Help + printf("\n\nSTORMWATER MANAGEMENT MODEL (SWMM5) HELP\n\n"); + printf("COMMANDS:\n"); + printf("\t--help (-h) Help Docs\n"); + printf("\t--version (-v) Build Version\n"); + printf("\nRUNNING A SIMULATION:\n"); + printf("\t swmm5 \n\n"); + } + else if (strcmp(arg1, "--version") == 0 || strcmp(arg1, "-v") == 0) + { + // Output version number + printf("\n%d.%d.%0d\n\n", vMajor, vMinor, vRelease); + } + else + { + printf("\nUnknown Argument (See Help --help)\n\n"); + } + } + else + { + // --- extract file names from command line arguments + inputFile = argv[1]; + reportFile = argv[2]; + if (argc > 3) binaryFile = argv[3]; + else binaryFile = blank; + printf("\n... EPA-SWMM %d.%d (Build %d.%d.%0d)\n", vMajor, vMinor, + vMajor, vMinor, vRelease); + + // --- run SWMM + swmm_run(inputFile, reportFile, binaryFile); + + // Display closing status on console + runTime = difftime(time(0), start); + printf("\n\n... EPA-SWMM completed in %.2f seconds.", runTime); + if ( swmm_getError(errMsg, msgLen) > 0 ) printf(" There are errors.\n"); + else if ( swmm_getWarnings() > 0 ) printf(" There are warnings.\n"); + else printf("\n"); + } + +// --- Use the code below if you need to keep the console window visible +/* + printf(" Press Enter to continue..."); + getchar(); +*/ + + return 0; +} \ No newline at end of file diff --git a/src/massbal.c b/src/massbal.c index a5e8c3ccc..c5a02903d 100644 --- a/src/massbal.c +++ b/src/massbal.c @@ -9,6 +9,7 @@ // 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) // M. Tryby (EPA) // @@ -33,12 +34,17 @@ // 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. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE +#include #include #include #include "headers.h" +#include "swmm5.h" //----------------------------------------------------------------------------- // Constants @@ -72,7 +78,7 @@ double TotalArea; // total drainage area (ft2) // massbal_close (called from swmm_end in swmm5.c) // massbal_report (called from swmm_end in swmm5.c) // massbal_updateRunoffTotals (called from subcatch_getRunoff) -// massbal_updateDrainTotals (called from evalLidUnit in lid.c) //(5.1.008) +// massbal_updateDrainTotals (called from evalLidUnit in lid.c) // massbal_updateLoadingTotals (called from subcatch_getBuildup) // massbal_updateGwaterTotals (called from updateMassBal in gwater.c) // massbal_updateRoutingTotals (called from routing_execute) @@ -84,8 +90,8 @@ double TotalArea; // total drainage area (ft2) // massbal_addNodeLosses (called from removeStorageLosses in routing.c) // massbal_addLinkLosses (called from removeConduitLosses in routing.c) // massbal_addReactedMass (called from qualrout.c & treatmnt.c) -// massbal_addSeepageLoss (called from routing.c) //(5.1.008) -// massbal_addToFinalStorage (called from qualrout.c) //(5.1.008) +// massbal_addSeepageLoss (called from routing.c) +// massbal_addToFinalStorage (called from qualrout.c) // massbal_getStepFlowError (called from routing.c) //----------------------------------------------------------------------------- @@ -94,10 +100,8 @@ double TotalArea; // total drainage area (ft2) double massbal_getBuildup(int pollut); double massbal_getStorage(char isFinalStorage); double massbal_getStoredMass(int pollut); -double massbal_getRunoffError(void); double massbal_getLoadingError(void); double massbal_getGwaterError(void); -double massbal_getFlowError(void); double massbal_getQualError(void); @@ -123,8 +127,8 @@ int massbal_open() RunoffTotals.evap = 0.0; RunoffTotals.infil = 0.0; RunoffTotals.runoff = 0.0; - RunoffTotals.runon = 0.0; //(5.1.008) - RunoffTotals.drains = 0.0; //(5.1.008) + RunoffTotals.runon = 0.0; + RunoffTotals.drains = 0.0; RunoffTotals.snowRemoved = 0.0; RunoffTotals.initStorage = 0.0; RunoffTotals.initSnowCover = 0.0; @@ -167,18 +171,6 @@ int massbal_open() FlowTotals.initStorage += Link[j].newVolume; StepFlowTotals = FlowTotals; - // --- add contribution of minimum surface area (i.e., manhole area) - // to initial storage under dynamic wave routing - if ( RouteModel == DW ) - { - for (j = 0; j < Nobjects[NODE]; j++) - { - if ( Node[j].type != STORAGE && - Node[j].initDepth <= Node[j].crownElev - Node[j].invertElev ) //(5.1.007) - FlowTotals.initStorage += Node[j].initDepth * MinSurfArea; - } - } - // --- initialize arrays to null LoadingTotals = NULL; QualTotals = NULL; @@ -354,8 +346,6 @@ double massbal_getBuildup(int p) //============================================================================= -//// This function was re-written for release 5.1.008. //// //(5.1.008) - void massbal_updateRunoffTotals(int flowType, double v) // // Input: flowType = type of flow @@ -427,9 +417,9 @@ void massbal_initTimeStepTotals() StepQualTotals[j].flooding = 0.0; StepQualTotals[j].outflow = 0.0; StepQualTotals[j].reacted = 0.0; - StepQualTotals[j].seepLoss = 0.0; //(5.1.008) - StepQualTotals[j].initStorage = 0.0; //(5.1.010) - StepQualTotals[j].finalStorage = 0.0; //(5.1.010) + StepQualTotals[j].seepLoss = 0.0; + StepQualTotals[j].initStorage = 0.0; + StepQualTotals[j].finalStorage = 0.0; } } @@ -500,8 +490,6 @@ void massbal_addInflowQual(int type, int p, double w) //============================================================================= -//// This function was modified for release 5.1.007. //// //(5.1.007) - void massbal_addOutflowFlow(double q, int isFlooded) // // Input: q = outflow flow rate (cfs) @@ -550,8 +538,6 @@ void massbal_addReactedMass(int p, double w) //============================================================================= -//// New function added to release 5.1.008. //// //(5.1.008) - void massbal_addSeepageLoss(int p, double w) // // Input: p = pollutant index @@ -566,8 +552,6 @@ void massbal_addSeepageLoss(int p, double w) //============================================================================= -//// New function added to release 5.1.008. //// //(5.1.008) - void massbal_addToFinalStorage(int p, double w) // // Input: p = pollutant index @@ -638,15 +622,15 @@ void massbal_updateRoutingTotals(double tStep) QualTotals[j].flooding += StepQualTotals[j].flooding * tStep; QualTotals[j].outflow += StepQualTotals[j].outflow * tStep; QualTotals[j].reacted += StepQualTotals[j].reacted * tStep; - QualTotals[j].seepLoss += StepQualTotals[j].seepLoss * tStep; //(5.1.008) - QualTotals[j].finalStorage += StepQualTotals[j].finalStorage; //(5.1.010) + QualTotals[j].seepLoss += StepQualTotals[j].seepLoss * tStep; + QualTotals[j].finalStorage += StepQualTotals[j].finalStorage; } 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) ) //(5.1.012) + (Node[j].degree == 0 && Node[j].type != STORAGE) ) { NodeOutflow[j] += Node[j].inflow * tStep; } @@ -680,18 +664,6 @@ double massbal_getStorage(char isFinalStorage) totalStorage += nodeStorage; } - // --- add contribution from minimum surface area (i.e., manhole diameter) - // to final storage under dynamic wave routing - if ( isFinalStorage && RouteModel == DW ) - { - for (j = 0; j < Nobjects[NODE]; j++) - { - if ( Node[j].type != STORAGE && - Node[j].newDepth <= Node[j].crownElev - Node[j].invertElev ) //(5.1.007) - totalStorage += Node[j].newDepth * MinSurfArea; - } - } - // --- skip final link storage for Steady Flow routing if ( isFinalStorage && RouteModel == SF ) return totalStorage; @@ -756,13 +728,13 @@ double massbal_getRunoffError() // --- compute % difference between total inflow and outflow totalInflow = RunoffTotals.rainfall + - RunoffTotals.runon + //(5.1.008) + RunoffTotals.runon + RunoffTotals.initStorage + RunoffTotals.initSnowCover; totalOutflow = RunoffTotals.evap + RunoffTotals.infil + RunoffTotals.runoff + - RunoffTotals.drains + //(5.1.008) + RunoffTotals.drains + RunoffTotals.snowRemoved + RunoffTotals.finalStorage + RunoffTotals.finalSnowCover; @@ -893,8 +865,6 @@ double massbal_getGwaterError() //============================================================================= -//// The following function was re-written for release 5.1.008. //// //(5.1.008) - double massbal_getFlowError() // // Input: none @@ -960,7 +930,7 @@ double massbal_getQualError() for (p = 0; p < Nobjects[POLLUT]; p++) { // --- get final mass stored in nodes and links - QualTotals[p].finalStorage += massbal_getStoredMass(p); //(5.1.008) + QualTotals[p].finalStorage += massbal_getStoredMass(p); // --- compute % difference between total inflow and outflow totalInflow = QualTotals[p].dwInflow + @@ -972,7 +942,7 @@ double massbal_getQualError() totalOutflow = QualTotals[p].flooding + QualTotals[p].outflow + QualTotals[p].reacted + - QualTotals[p].seepLoss + //(5.1.008) + QualTotals[p].seepLoss + QualTotals[p].finalStorage; QualTotals[p].pctError = 0.0; if ( fabs(totalInflow - totalOutflow) < 0.001 ) @@ -1006,7 +976,7 @@ double massbal_getQualError() QualTotals[p].flooding = LOG10(cf * QualTotals[p].flooding); QualTotals[p].outflow = LOG10(cf * QualTotals[p].outflow); QualTotals[p].reacted = LOG10(cf * QualTotals[p].reacted); - QualTotals[p].seepLoss = LOG10(cf * QualTotals[p].seepLoss); //(5.1.008) + QualTotals[p].seepLoss = LOG10(cf * QualTotals[p].seepLoss); QualTotals[p].initStorage = LOG10(cf * QualTotals[p].initStorage); QualTotals[p].finalStorage = LOG10(cf * QualTotals[p].finalStorage); } @@ -1053,10 +1023,10 @@ double massbal_getStepFlowError() StepFlowTotals.evapLoss + StepFlowTotals.seepLoss + StepFlowTotals.reacted; - if ( fabs(totalInflow) > 0.0 ) //(5.1.007) + if ( fabs(totalInflow) > 0.0 ) return 1.0 - totalOutflow / totalInflow; else if ( fabs(totalOutflow) > 0.0 ) - return totalInflow / totalOutflow - 1.0; //(5.1.007) + return totalInflow / totalOutflow - 1.0; else return 0.0; } @@ -1077,12 +1047,10 @@ double massbal_getStoredMass(int p) storedMass += Node[j].newVolume * Node[j].newQual[p]; // --- get mass stored in links (except for Steady Flow routing) - if ( RouteModel != SF ) //(5.1.011) + if ( RouteModel != SF ) { for (j = 0; j < Nobjects[LINK]; j++) storedMass += Link[j].newVolume * Link[j].newQual[p]; } return storedMass; } - -//============================================================================= diff --git a/src/mathexpr.c b/src/mathexpr.c index aac10c877..d5e944b85 100644 --- a/src/mathexpr.c +++ b/src/mathexpr.c @@ -44,7 +44,7 @@ #define _CRT_SECURE_NO_DEPRECATE #include -#include +#include #include #include #include @@ -529,8 +529,8 @@ void deleteTree(ExprTree *tree) //============================================================================= -// Turn on "precise" floating point option //(5.1.008) -#pragma float_control(precise, on, push) //(5.1.008) +// Turn on "precise" floating point option +#pragma float_control(precise, on, push) double mathexpr_eval(MathExpr *expr, double (*getVariableValue) (int)) // Mathematica expression evaluation using a stack @@ -730,14 +730,14 @@ double mathexpr_eval(MathExpr *expr, double (*getVariableValue) (int)) } r1 = ExprStack[stackindex]; - // Set result to 0 if it is NaN due to an illegal math op //(5.1.008) - if ( r1 != r1 ) r1 = 0.0; //(5.1.008) + // Set result to 0 if it is NaN due to an illegal math op + if ( r1 != r1 ) r1 = 0.0; return r1; } -// Turn off "precise" floating point option //(5.1.008) -#pragma float_control(pop) //(5.1.008) +// Turn off "precise" floating point option +#pragma float_control(pop) //============================================================================= diff --git a/src/mempool.c b/src/mempool.c index 367b49b2d..495447801 100644 --- a/src/mempool.c +++ b/src/mempool.c @@ -17,7 +17,7 @@ #include -#include +#include #include "mempool.h" /* diff --git a/src/node.c b/src/node.c index 5a0ceb34f..2917b5749 100644 --- a/src/node.c +++ b/src/node.c @@ -7,6 +7,7 @@ // 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) // Author: L. Rossman // // Conveyance system node functions. @@ -23,10 +24,13 @@ // 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. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE -#include +#include #include #include #include "headers.h" @@ -51,7 +55,7 @@ typedef struct // node_setOldQualState (called from routing_execute) // node_initInflow (called from routing_execute) // node_setOutletDepth (called from routing_execute) -// node_getLosses (called from routing_execute) //(5.1.008) +// node_getLosses (called from routing_execute) // node_getSystemOutflow (called from removeOutflows in routing.c) // node_getResults (called from output_saveNodeResults) // node_getPondedArea (called from initNodeStates in dynwave.c) @@ -60,7 +64,6 @@ typedef struct // node_getSurfArea // node_getDepth // node_getVolume -// node_getPondedDepth removed //(5.1.008) //----------------------------------------------------------------------------- // Local functions @@ -145,9 +148,6 @@ void node_setParams(int j, int type, int k, double x[]) Outfall[k].tideCurve = (int)x[3]; Outfall[k].stageSeries = (int)x[4]; Outfall[k].hasFlapGate = (char)x[5]; - -//// Following code segment added to release 5.1.008. //// //(5.1.008) - Outfall[k].routeTo = (int)x[6]; Outfall[k].wRouted = NULL; if ( Outfall[k].routeTo >= 0 ) @@ -155,7 +155,6 @@ void node_setParams(int j, int type, int k, double x[]) Outfall[k].wRouted = (double *) calloc(Nobjects[POLLUT], sizeof(double)); } -//// break; case STORAGE: @@ -165,7 +164,10 @@ void node_setParams(int j, int type, int k, double x[]) Storage[k].aExpon = x[4]; Storage[k].aConst = x[5]; Storage[k].aCurve = (int)x[6]; - // x[7] (ponded depth) is deprecated. //(5.1.007) + + // Surcharge depth replaces ponded area //(5.1.013) + Node[j].surDepth = x[7] / UCF(LENGTH); // + Storage[k].fEvap = x[8]; break; @@ -225,7 +227,7 @@ void node_initState(int j) // Purpose: initializes a node's state variables at start of simulation. // { - int p, k; //(5.1.007) + int p, k; // --- initialize depth Node[j].oldDepth = Node[j].initDepth; @@ -246,10 +248,7 @@ void node_initState(int j) // --- initialize any inflow Node[j].oldLatFlow = 0.0; Node[j].newLatFlow = 0.0; - Node[j].losses = 0.0; //(5.1.007) - - -//// Following code section added to release 5.1.007. //// //(5.1.007) + Node[j].losses = 0.0; // --- initialize storage nodes if ( Node[j].type == STORAGE ) @@ -261,9 +260,6 @@ void node_initState(int j) // --- initialize exfiltration properties if ( Storage[k].exfil ) exfil_initState(k); } -//// - -//// Following code section added to release 5.1.008. //// //(5.1.008) // --- initialize flow stream routed from outfall onto a subcatchment if ( Node[j].type == OUTFALL ) @@ -275,7 +271,6 @@ void node_initState(int j) for (p = 0; p < Nobjects[POLLUT]; p++) Outfall[k].wRouted[p] = 0.0; } } -//// } //============================================================================= @@ -288,7 +283,6 @@ void node_setOldHydState(int j) // { Node[j].oldDepth = Node[j].newDepth; - Node[j].oldLatFlow = Node[j].newLatFlow; Node[j].oldVolume = Node[j].newVolume; } @@ -323,7 +317,7 @@ void node_initInflow(int j, double tStep) 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; //(5.1.007) + Node[j].outflow = Node[j].losses; // --- set overflow to any excess stored volume if ( Node[j].newVolume > Node[j].fullVolume ) @@ -448,8 +442,6 @@ double node_getSystemOutflow(int j, int *isFlooded) // --- node sends flow into outfall conduit // (therefore it has a negative outflow) else - -//// Following code segment modified for release 5.1.007. //// //(5.1.007) { if ( Node[j].inflow == 0.0 ) { @@ -655,8 +647,8 @@ 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 aPond fEvap (infil) -// nodeID elev maxDepth initDepth TABULAR curveID aPond fEvap (infil) +// nodeID elev maxDepth initDepth FUNCTIONAL a1 a2 a0 surDepth fEvap (infil) //(5.1.013) +// nodeID elev maxDepth initDepth TABULAR curveID surDepth fEvap (infil) // // { int i, m, n; @@ -708,7 +700,7 @@ int storage_readParams(int j, int k, char* tok[], int ntoks) n = 6; } - // --- ignore next token if present (deprecated ponded area property) //(5.1.007) + // --- ponded area replaced by surcharge depth //(5.1.013) if ( ntoks > n) { if ( ! getDouble(tok[n], &x[7]) ) @@ -729,7 +721,7 @@ int storage_readParams(int j, int k, char* tok[], int ntoks) node_setParams(j, STORAGE, k, x); // --- read exfiltration parameters if present - if ( ntoks > n ) return exfil_readStorageParams(k, tok, ntoks, n); //(5.1.007) + if ( ntoks > n ) return exfil_readStorageParams(k, tok, ntoks, n); return 0; } @@ -906,8 +898,6 @@ double storage_getOutflow(int j, int i) //============================================================================= -//// This function was modified for release 5.1.008. //// //(5.1.008) - double storage_getLosses(int j, double tStep) // // Input: j = node index @@ -927,7 +917,7 @@ double storage_getLosses(int j, double tStep) TExfil* exfil; // --- if node has some stored volume - if ( Node[j].newVolume > FUDGE ) //(5.1.010) + if ( Node[j].newVolume > FUDGE ) { // --- get node's evap. rate (ft/s) & exfiltration object k = Node[j].subIndex; @@ -938,7 +928,7 @@ double storage_getLosses(int j, double tStep) if ( evapRate > 0.0 || exfil != NULL) { // --- obtain storage depth & surface area - depth = Node[j].newDepth; //(5.1.010) + depth = Node[j].newDepth; area = storage_getSurfArea(j, depth); // --- compute evap rate over this area (cfs) @@ -952,9 +942,9 @@ double storage_getLosses(int j, double tStep) // --- total loss over time step cannot exceed stored volume totalLoss = (evapRate + exfilRate) * tStep; - if ( totalLoss > Node[j].newVolume ) //(5.1.010) + if ( totalLoss > Node[j].newVolume ) { - lossRatio = Node[j].newVolume / totalLoss; //(5.1.010) + lossRatio = Node[j].newVolume / totalLoss; evapRate *= lossRatio; exfilRate *= lossRatio; } @@ -1210,7 +1200,7 @@ int outfall_readParams(int j, int k, char* tok[], int ntoks) // { int i, m, n; - double x[7]; //(5.1.008) + double x[7]; char* id; if ( ntoks < 3 ) return error_setInpError(ERR_ITEMS, ""); @@ -1258,14 +1248,12 @@ int outfall_readParams(int j, int k, char* tok[], int ntoks) x[5] = m; } -//// Added for release 5.1.008. //// //(5.1.008) if ( ntoks == n+1) { m = project_findObject(SUBCATCH, tok[n]); if ( m < 0 ) return error_setInpError(ERR_NAME, tok[n]); x[6] = m; } -//// Node[j].ID = id; node_setParams(j, OUTFALL, k, x); diff --git a/src/objects.h b/src/objects.h index 047473eae..9fd738067 100644 --- a/src/objects.h +++ b/src/objects.h @@ -8,6 +8,8 @@ // 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) +// // Author: L. Rossman (EPA) // M. Tryby (EPA) // R. Dickinson (CDM) @@ -46,11 +48,17 @@ // - 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. //----------------------------------------------------------------------------- #include "mathexpr.h" #include "infil.h" -#include "exfil.h" //(5.1.007) +#include "exfil.h" //----------------- // FILE INFORMATION @@ -74,7 +82,6 @@ struct TableEntry }; typedef struct TableEntry TTableEntry; - //------------------------- // CURVE/TIME SERIES OBJECT //------------------------- @@ -93,7 +100,6 @@ typedef struct TFile file; // external data file } TTable; - //----------------- // RAIN GAGE OBJECT //----------------- @@ -127,7 +133,6 @@ typedef struct int isCurrent; // TRUE if gage's rainfall is current } TGage; - //------------------- // TEMPERATURE OBJECT //------------------- @@ -147,7 +152,6 @@ typedef struct double tanAnglat; // tangent of latitude angle } TTemp; - //----------------- // WINDSPEED OBJECT //----------------- @@ -159,7 +163,6 @@ typedef struct double ws; // wind speed (mph) } TWind; - //------------ // SNOW OBJECT //------------ @@ -174,7 +177,6 @@ typedef struct double removed; // total snow plowed out of system (ft3) } TSnow; - //------------------- // EVAPORATION OBJECT //------------------- @@ -191,7 +193,6 @@ typedef struct double recoveryFactor; // current soil recovery factor } TEvap; -//// Added for release 5.1.007 //// //(5.1.007) //------------------- // ADJUSTMENTS OBJECT //------------------- @@ -200,14 +201,12 @@ typedef struct double temp[12]; // monthly temperature adjustments (deg F) double evap[12]; // monthly evaporation adjustments (ft/s) double rain[12]; // monthly rainfall adjustment multipliers - double hydcon[12]; // hyd. conductivity adjustment multipliers //(5.1.008) + double hydcon[12]; // hyd. conductivity adjustment multipliers //---------------------------- double rainFactor; // current rainfall adjustment multiplier - double hydconFactor; // current conductivity multiplier //(5.1.008) + double hydconFactor; // current conductivity multiplier } TAdjust; - -//// Added for release 5.1.011 //// //(5.1.011) //------------- // EVENT OBJECT //------------- @@ -217,7 +216,6 @@ typedef struct DateTime end; // event end date } TEvent; - //------------------- // AQUIFER OBJECT //------------------- @@ -239,8 +237,6 @@ typedef struct int upperEvapPat; // monthly upper evap. adjustment factors } TAquifer; - -//// Added to release 5.1.008. //// //(5.1.008) //----------------------- // GROUNDWATER STATISTICS //----------------------- @@ -257,7 +253,6 @@ typedef struct double maxFlow; // max. lateral outflow (cfs) } TGWaterStats; - //------------------------ // GROUNDWATER OBJECT //------------------------ @@ -277,14 +272,13 @@ typedef struct //---------------------------- double theta; // upper zone moisture content double lowerDepth; // depth of saturated zone (ft) - double oldFlow; // gw outflow from previous time period (fps) //(5.1.011) - double newFlow; // gw outflow from current time period (fps) //(5.1.011) + double oldFlow; // gw outflow from previous time period (fps) + double newFlow; // gw outflow from current time period (fps) double evapLoss; // evaporation loss rate (ft/sec) double maxInfilVol; // max. infil. upper zone can accept (ft) - TGWaterStats stats; // gw statistics //(5.1.008) + TGWaterStats stats; // gw statistics } TGroundwater; - //---------------- // SNOWMELT OBJECT //---------------- @@ -311,7 +305,6 @@ typedef struct double dhm[3]; // melt coeff. for each surface (ft/sec-F) } TSnowmelt; - //---------------- // SNOWPACK OBJECT //---------------- @@ -331,7 +324,6 @@ typedef struct double imelt[3]; // immediate melt (ft) } TSnowpack; - //--------------- // SUBAREA OBJECT //--------------- @@ -354,7 +346,6 @@ typedef struct double depth; // depth of surface runoff (ft) } TSubarea; - //------------------------- // LAND AREA LANDUSE FACTOR //------------------------- @@ -365,7 +356,6 @@ typedef struct DateTime lastSwept; // date/time of last street sweeping } TLandFactor; - //-------------------- // SUBCATCHMENT OBJECT //-------------------- @@ -386,9 +376,12 @@ typedef struct double* initBuildup; // initial pollutant buildup (mass/ft2) TLandFactor* landFactor; // array of land use factors TGroundwater* groundwater; // associated groundwater data - MathExpr* gwLatFlowExpr; // user-supplied lateral outflow expression //(5.1.007) - MathExpr* gwDeepFlowExpr; // user-supplied deep percolation expression //(5.1.007) + 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 // //----------------------------- double lidArea; // area devoted to LIDs (ft2) double rainfall; // current rainfall (ft/sec) @@ -405,7 +398,6 @@ typedef struct double* totalLoad; // total washoff load (lbs or kg) } TSubcatch; - //----------------------- // TIME PATTERN DATA //----------------------- @@ -417,7 +409,6 @@ typedef struct double factor[24]; // time pattern factors } TPattern; - //------------------------------ // DIRECT EXTERNAL INFLOW OBJECT //------------------------------ @@ -430,11 +421,11 @@ 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 struct ExtInflow* next; // pointer to next inflow data object }; typedef struct ExtInflow TExtInflow; - //------------------------------- // DRY WEATHER FLOW INFLOW OBJECT //------------------------------- @@ -447,7 +438,6 @@ struct DwfInflow }; typedef struct DwfInflow TDwfInflow; - //------------------- // RDII INFLOW OBJECT //------------------- @@ -457,7 +447,6 @@ typedef struct double area; // area of sewershed (ft2) } TRdiiInflow; - //----------------------------- // UNIT HYDROGRAPH GROUP OBJECT //----------------------------- @@ -473,7 +462,6 @@ typedef struct long tPeak[12][3]; // time to peak of each UH in each month (sec) } TUnitHyd; - //----------------- // TREATMENT OBJECT //----------------- @@ -483,7 +471,6 @@ typedef struct MathExpr* equation; // treatment eqn. as tokenized math terms } TTreatment; - //------------ // NODE OBJECT //------------ @@ -505,10 +492,10 @@ typedef struct //----------------------------- int degree; // number of outflow links char updated; // true if state has been updated - double crownElev; // top of highest connecting conduit (ft) + 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); //(5.1.007) + double losses; // evap + exfiltration loss (ft3) double oldVolume; // previous volume (ft3) double newVolume; // current volume (ft3) double fullVolume; // max. storage available (ft3) @@ -523,7 +510,6 @@ typedef struct double oldNetInflow; // previous net inflow } TNode; - //--------------- // OUTFALL OBJECT //--------------- @@ -534,12 +520,11 @@ typedef struct 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 //(5.1.008) - double vRouted; // flow volume routed (ft3) //(5.1.008) - double* wRouted; // pollutant load routed (mass) //(5.1.008) + int routeTo; // subcatchment index routed onto + double vRouted; // flow volume routed (ft3) + double* wRouted; // pollutant load routed (mass) } TOutfall; - //-------------------- // STORAGE UNIT OBJECT //-------------------- @@ -550,14 +535,13 @@ typedef struct double aCoeff; // coeff. of area v. height curve double aExpon; // exponent of area v. height curve int aCurve; // index of tabulated area v. height curve - TExfil* exfil; // ptr. to exfiltration object //(5.1.007) + TExfil* exfil; // ptr. to exfiltration object //----------------------------- double hrt; // hydraulic residence time (sec) double evapLoss; // evaporation loss (ft3) - double exfilLoss; // exfiltration loss (ft3) //(5.1.007) + double exfilLoss; // exfiltration loss (ft3) } TStorage; - //-------------------- // FLOW DIVIDER OBJECT //-------------------- @@ -572,7 +556,6 @@ typedef struct int flowCurve; // index of inflow v. diverted flow curve } TDivider; - //----------------------------- // CROSS SECTION DATA STRUCTURE //----------------------------- @@ -596,7 +579,6 @@ typedef struct double rBot; // radius of bottom section } TXsect; - //-------------------------------------- // CROSS SECTION TRANSECT DATA STRUCTURE //-------------------------------------- @@ -620,7 +602,6 @@ typedef struct int nTbl; // size of geometry tables } TTransect; - //------------------------------------- // CUSTOM CROSS SECTION SHAPE STRUCTURE //------------------------------------- @@ -639,7 +620,6 @@ typedef struct double widthTbl[N_SHAPE_TBL]; // table of top width v. depth } TShape; - //------------ // LINK OBJECT //------------ @@ -673,7 +653,7 @@ typedef struct double qFull; // flow when full (cfs) double setting; // current control setting double targetSetting; // target control setting - double timeLastSet; // time when setting was last changed //(5.1.010) + double timeLastSet; // time when setting was last changed double froude; // Froude number double* oldQual; // previous quality state double* newQual; // current quality state @@ -686,7 +666,6 @@ typedef struct char inletControl; // culvert inlet control flag } TLink; - //--------------- // CONDUIT OBJECT //--------------- @@ -703,16 +682,15 @@ typedef struct double qMax; // max. flow (cfs) double a1, a2; // upstream & downstream areas (ft2) double q1, q2; // upstream & downstream flows per barrel (cfs) - double q1Old, q2Old; // previous values of q1 & q2 (cfs) //(5.1.010) + double q1Old, q2Old; // previous values of q1 & q2 (cfs) double evapLossRate; // evaporation rate (cfs) double seepLossRate; // seepage rate (cfs) char capacityLimited; // capacity limited flag char superCritical; // super-critical flow flag char hasLosses; // local losses flag - char fullState; // determines if either or both ends full //(5.1.008) + char fullState; // determines if either or both ends full } TConduit; - //------------ // PUMP OBJECT //------------ @@ -745,28 +723,26 @@ typedef struct double surfArea; // equivalent surface area (ft2) } TOrifice; - //------------ // WEIR OBJECT //------------ typedef struct { int type; // weir type code -// int shape; // weir shape code //DELETED// //(5.1.011) double cDisch1; // discharge coeff. double cDisch2; // discharge coeff. for ends double endCon; // end contractions - int canSurcharge; // true if weir can surcharge //(5.1.007) - double roadWidth; // width for ROADWAY weir //(5.1.010) - int roadSurface; // road surface material //(5.1.010) + 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) //----------------------------- - double cSurcharge; // orifice coeff. for surcharge //(5.1.007) + double cSurcharge; // orifice coeff. for surcharge double length; // equivalent length (ft) double slope; // slope for Vnotch & Trapezoidal weirs double surfArea; // equivalent surface area (ft2) } TWeir; - //--------------------- // OUTLET DEVICE OBJECT //--------------------- @@ -778,7 +754,6 @@ typedef struct int curveType; // rating curve type } TOutlet; - //----------------- // POLLUTANT OBJECT //----------------- @@ -798,7 +773,6 @@ typedef struct int snowOnly; // TRUE if buildup occurs only under snow } TPollut; - //------------------------ // BUILDUP FUNCTION OBJECT //------------------------ @@ -810,7 +784,6 @@ typedef struct double maxDays; // time to reach max. buildup (days) } TBuildup; - //------------------------ // WASHOFF FUNCTION OBJECT //------------------------ @@ -823,7 +796,6 @@ typedef struct double bmpEffic; // best mgt. practice fractional removal } TWashoff; - //--------------- // LANDUSE OBJECT //--------------- @@ -837,7 +809,6 @@ typedef struct TWashoff* washoffFunc; // array of washoff functions for pollutants } TLanduse; - //-------------------------- // REPORTING FLAGS STRUCTURE //-------------------------- @@ -852,21 +823,21 @@ typedef struct 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) int linesPerPage; // number of lines printed per page } TRptFlags; - //------------------------------- // CUMULATIVE RUNOFF TOTALS //------------------------------- typedef struct -{ // All volume totals are in ft3. //(5.1.008) +{ // All volume totals are in ft3. double rainfall; // rainfall volume double evap; // evaporation loss double infil; // infiltration loss double runoff; // runoff volume - double drains; // LID drains //(5.1.008) - double runon; // runon from outfalls //(5.1.008) + double drains; // LID drains + double runon; // runon from outfalls double initStorage; // inital surface storage double finalStorage; // final surface storage double initSnowCover; // initial snow cover @@ -875,7 +846,6 @@ typedef struct double pctError; // continuity error (%) } TRunoffTotals; - //-------------------------- // CUMULATIVE LOADING TOTALS //-------------------------- @@ -892,7 +862,6 @@ typedef struct double pctError; // continuity error (%) } TLoadingTotals; - //------------------------------ // CUMULATIVE GROUNDWATER TOTALS //------------------------------ @@ -908,7 +877,6 @@ typedef struct double pctError; // continuity error (%) } TGwaterTotals; - //---------------------------- // CUMULATIVE ROUTING TOTALS //---------------------------- @@ -929,7 +897,6 @@ typedef struct double pctError; // continuity error } TRoutingTotals; - //----------------------- // SYSTEM-WIDE STATISTICS //----------------------- @@ -942,7 +909,6 @@ typedef struct double steadyStateCount; } TSysStats; - //-------------------- // RAINFALL STATISTICS //-------------------- @@ -955,7 +921,6 @@ typedef struct long periodsMalfunc; } TRainStats; - //------------------------ // SUBCATCHMENT STATISTICS //------------------------ @@ -967,6 +932,8 @@ typedef struct double infil; double runoff; double maxFlow; + double impervRunoff; //(5.1.013) + double pervRunoff; // } TSubcatchStats; //---------------- @@ -977,7 +944,7 @@ typedef struct double avgDepth; double maxDepth; DateTime maxDepthDate; - double maxRptDepth; //(5.1.008) + double maxRptDepth; double volFlooded; double timeFlooded; double timeSurcharged; @@ -991,7 +958,6 @@ typedef struct DateTime maxOverflowDate; } TNodeStats; - //------------------- // STORAGE STATISTICS //------------------- @@ -1006,7 +972,6 @@ typedef struct DateTime maxVolDate; } TStorageStats; - //------------------- // OUTFALL STATISTICS //------------------- @@ -1018,7 +983,6 @@ typedef struct int totalPeriods; } TOutfallStats; - //---------------- // PUMP STATISTICS //---------------- @@ -1036,7 +1000,6 @@ typedef struct int totalPeriods; } TPumpStats; - //---------------- // LINK STATISTICS //---------------- @@ -1045,7 +1008,6 @@ typedef struct double maxFlow; DateTime maxFlowDate; double maxVeloc; - //DateTime maxVelocDate; //deprecated //(5.1.008) double maxDepth; double timeNormalFlow; double timeInletControl; @@ -1060,7 +1022,6 @@ typedef struct int flowTurnSign; } TLinkStats; - //------------------------- // MAXIMUM VALUE STATISTICS //------------------------- @@ -1071,7 +1032,6 @@ typedef struct double value; // value of node or link statistic } TMaxStats; - //------------------ // REPORT FIELD INFO //------------------ diff --git a/src/output.c b/src/output.c index 2cd7db269..186a3979d 100644 --- a/src/output.c +++ b/src/output.c @@ -6,6 +6,7 @@ // Date: 03/20/14 (Build 5.1.001) // 03/19/15 (Build 5.1.008) // 08/05/15 (Build 5.1.010) +// 05/10/18 (Build 5.1.013) // Author: L. Rossman (EPA) // // Binary output file access functions. @@ -17,6 +18,12 @@ // Build 5.1.010: // - Potentional ET added to list of system-wide variables saved to file. // +// Build 5.1.013: +// - Names NsubcatchVars, NnodeVars & NlinkVars replaced with +// NumSubcatchVars, NumNodeVars & NumLinkVars +// - Support added for saving average node & link routing results to +// binary file in each reporting period. +// //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -34,6 +41,11 @@ enum InputDataType {INPUT_TYPE_CODE, INPUT_AREA, INPUT_INVERT, INPUT_MAX_DEPTH, INPUT_OFFSET, INPUT_LENGTH}; +typedef struct //(5.1.013) +{ // + REAL4* xAvg; // +} TAvgResults; // + //----------------------------------------------------------------------------- // Shared variables //----------------------------------------------------------------------------- @@ -41,15 +53,19 @@ 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 INT4 NsubcatchResults; // number of subcatchment output variables -static INT4 NnodeResults; // number of node output variables -static INT4 NlinkResults; // number of link output variables +static INT4 NumSubcatchVars; // number of subcatchment output variables +static INT4 NumNodeVars; // number of node output variables +static INT4 NumLinkVars; // number of link output variables 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; // + //----------------------------------------------------------------------------- // Exportable variables (shared with report.c) //----------------------------------------------------------------------------- @@ -67,12 +83,19 @@ 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); // + + //----------------------------------------------------------------------------- // External functions (declared in funcs.h) //----------------------------------------------------------------------------- // 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_saveResults (called by swmm_step in swmm5.c) // output_checkFileSize (called by swmm_report) // output_readDateTime (called by routines in report.c) @@ -106,15 +129,15 @@ int output_open() // --- subcatchment results consist of Rainfall, Snowdepth, Evap, // Infil, Runoff, GW Flow, GW Elev, GW Sat, and Washoff - NsubcatchResults = MAX_SUBCATCH_RESULTS - 1 + NumPolluts; + NumSubcatchVars = MAX_SUBCATCH_RESULTS - 1 + NumPolluts; // --- node results consist of Depth, Head, Volume, Lateral Inflow, // Total Inflow, Overflow and Quality - NnodeResults = MAX_NODE_RESULTS - 1 + NumPolluts; + NumNodeVars = MAX_NODE_RESULTS - 1 + NumPolluts; - // --- link results consist of Depth, Flow, Velocity, Froude No., + // --- link results consist of Depth, Flow, Velocity, Volume, //(5.1.013) // Capacity and Quality - NlinkResults = MAX_LINK_RESULTS - 1 + NumPolluts; + NumLinkVars = MAX_LINK_RESULTS - 1 + NumPolluts; // --- get number of objects reported on NumSubcatch = 0; @@ -125,24 +148,33 @@ int output_open() for (j=0; j 0) output_saveSubcatchResults(reportTime, Fout.file); - if (Nobjects[NODE] > 0) - output_saveNodeResults(reportTime, Fout.file); - if (Nobjects[LINK] > 0) - output_saveLinkResults(reportTime, Fout.file); + + // --- save average routing results over reporting period if called for //(5.1.013) + if ( RptFlags.averages ) output_saveAvgResults(Fout.file); // + + // --- otherwise save interpolated point routing results //(5.1.013) + else // + { + if (Nobjects[NODE] > 0) + output_saveNodeResults(reportTime, Fout.file); + if (Nobjects[LINK] > 0) + output_saveLinkResults(reportTime, Fout.file); + } + + // --- update & save system-wide flows + SysResults[SYS_FLOODING] = (REAL4)(StepFlowTotals.flooding * UCF(FLOW)); + SysResults[SYS_OUTFLOW] = (REAL4)(StepFlowTotals.outflow * UCF(FLOW)); + SysResults[SYS_DWFLOW] = (REAL4)(StepFlowTotals.dwInflow * UCF(FLOW)); + SysResults[SYS_GWFLOW] = (REAL4)(StepFlowTotals.gwInflow * UCF(FLOW)); + SysResults[SYS_IIFLOW] = (REAL4)(StepFlowTotals.iiInflow * UCF(FLOW)); + SysResults[SYS_EXFLOW] = (REAL4)(StepFlowTotals.exInflow * UCF(FLOW)); + SysResults[SYS_INFLOW] = SysResults[SYS_RUNOFF] + + SysResults[SYS_DWFLOW] + + SysResults[SYS_GWFLOW] + + SysResults[SYS_IIFLOW] + + SysResults[SYS_EXFLOW]; fwrite(SysResults, sizeof(REAL4), MAX_SYS_RESULTS, Fout.file); + + // --- save outfall flows to interface file if called for if ( Foutflows.mode == SAVE_FILE && !IgnoreRouting ) iface_saveOutletResults(reportDate, Foutflows.file); Nperiods++; @@ -472,6 +533,7 @@ void output_close() FREE(SubcatchResults); FREE(NodeResults); FREE(LinkResults); + output_closeAvgResults(); //(5.1.013) } //============================================================================= @@ -520,7 +582,7 @@ void output_saveSubcatchResults(double reportTime, FILE* file) // --- retrieve interpolated results for reporting time & write to file subcatch_getResults(j, f, SubcatchResults); if ( Subcatch[j].rptFlag ) - fwrite(SubcatchResults, sizeof(REAL4), NsubcatchResults, file); + fwrite(SubcatchResults, sizeof(REAL4), NumSubcatchVars, file); // --- update system-wide results area = Subcatch[j].area * UCF(LANDAREA); @@ -539,24 +601,27 @@ void output_saveSubcatchResults(double reportTime, FILE* file) } // --- normalize system-wide results to catchment area - if ( UnitSystem == SI ) f = (5./9.) * (Temp.ta - 32.0); - else f = Temp.ta; - SysResults[SYS_TEMPERATURE] = (REAL4)f; - - f = Evap.rate * UCF(EVAPRATE); //(5.1.010) - SysResults[SYS_PET] = (REAL4)f; //(5.1.010) - - if ( totalArea > 0.0 ) //(5.1.008) + if ( totalArea > 0.0 ) { SysResults[SYS_EVAP] /= totalArea; SysResults[SYS_RAINFALL] /= totalArea; SysResults[SYS_SNOWDEPTH] /= totalArea; SysResults[SYS_INFIL] /= totalArea; } + + // --- update system temperature and PET + if ( UnitSystem == SI ) f = (5./9.) * (Temp.ta - 32.0); + else f = Temp.ta; + SysResults[SYS_TEMPERATURE] = (REAL4)f; + f = Evap.rate * UCF(EVAPRATE); + SysResults[SYS_PET] = (REAL4)f; + } //============================================================================= +//// 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) @@ -565,7 +630,6 @@ void output_saveNodeResults(double reportTime, FILE* file) // Purpose: writes computed node results to binary file. // { - extern TRoutingTotals StepFlowTotals; // defined in massbal.c int j; // --- find where current reporting time lies between latest routing times @@ -578,25 +642,12 @@ void output_saveNodeResults(double reportTime, FILE* file) // --- retrieve interpolated results for reporting time & write to file node_getResults(j, f, NodeResults); if ( Node[j].rptFlag ) - fwrite(NodeResults, sizeof(REAL4), NnodeResults, file); - stats_updateMaxNodeDepth(j, NodeResults[NODE_DEPTH]); //(5.1.008) + fwrite(NodeResults, sizeof(REAL4), NumNodeVars, file); + stats_updateMaxNodeDepth(j, NodeResults[NODE_DEPTH]); // --- update system-wide storage volume SysResults[SYS_STORAGE] += NodeResults[NODE_VOLUME]; } - - // --- update system-wide flows - SysResults[SYS_FLOODING] = (REAL4) (StepFlowTotals.flooding * UCF(FLOW)); - SysResults[SYS_OUTFLOW] = (REAL4) (StepFlowTotals.outflow * UCF(FLOW)); - SysResults[SYS_DWFLOW] = (REAL4)(StepFlowTotals.dwInflow * UCF(FLOW)); - SysResults[SYS_GWFLOW] = (REAL4)(StepFlowTotals.gwInflow * UCF(FLOW)); - SysResults[SYS_IIFLOW] = (REAL4)(StepFlowTotals.iiInflow * UCF(FLOW)); - SysResults[SYS_EXFLOW] = (REAL4)(StepFlowTotals.exInflow * UCF(FLOW)); - SysResults[SYS_INFLOW] = SysResults[SYS_RUNOFF] + - SysResults[SYS_DWFLOW] + - SysResults[SYS_GWFLOW] + - SysResults[SYS_IIFLOW] + - SysResults[SYS_EXFLOW]; } //============================================================================= @@ -620,9 +671,11 @@ void output_saveLinkResults(double reportTime, FILE* file) for (j=0; j #include -#include -#include //(5.1.008) -#include //(5.1.008) +#include +#include + +#if defined(_OPENMP) //(5.1.013) + #include // +#else // + int omp_get_num_threads(void) { return 1;} // +#endif // + #include "headers.h" #include "lid.h" #include "hash.h" #include "mempool.h" -//----------------------------------------------------------------------------- -// Constants -//----------------------------------------------------------------------------- -//// Constants for DYNWAVE flow routing moved to dynwave.c. //// //(5.1.008) - //----------------------------------------------------------------------------- // Shared variables //----------------------------------------------------------------------------- @@ -151,8 +160,6 @@ void project_readInput() } else { -//// Following code segment was modified for release 5.1.009. //// //(5.1.009) -//// // --- compute total duration of simulation in seconds TotalDuration = floor((EndDateTime - StartDateTime) * SECperDAY); @@ -171,7 +178,6 @@ void project_readInput() // --- convert total duration to milliseconds TotalDuration *= 1000.0; } -//// } //============================================================================= @@ -205,9 +211,9 @@ void project_validate() lid_validate(); if ( Nobjects[SNOWMELT] == 0 ) IgnoreSnowmelt = TRUE; if ( Nobjects[AQUIFER] == 0 ) IgnoreGwater = TRUE; - for ( i=0; i 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); // + 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) } break; @@ -557,7 +573,7 @@ int project_readOption(char* s1, char* s2) case IGNORE_GWATER: case IGNORE_ROUTING: case IGNORE_QUALITY: - case IGNORE_RDII: //(5.1.004) + case IGNORE_RDII: m = findmatch(s2, NoYesWords); if ( m < 0 ) return error_setInpError(ERR_KEYWORD, s2); switch ( k ) @@ -570,13 +586,12 @@ int project_readOption(char* s1, char* s2) case IGNORE_GWATER: IgnoreGwater = m; break; case IGNORE_ROUTING: IgnoreRouting = m; break; case IGNORE_QUALITY: IgnoreQuality = m; break; - case IGNORE_RDII: IgnoreRDII = m; break; //(5.1.004) + case IGNORE_RDII: IgnoreRDII = m; break; } break; case NORMAL_FLOW_LTD: m = findmatch(s2, NormalFlowWords); - //if ( m < 0 ) m = findmatch(s2, NoYesWords); DEPRECATED //(5.1.012) if ( m < 0 ) return error_setInpError(ERR_KEYWORD, s2); NormalFlowLtd = m; break; @@ -630,8 +645,6 @@ int project_readOption(char* s1, char* s2) else LengtheningStep = MAX(0.0, tStep); break; -//// Following code section added to release 5.1.008. //// //(5.1.008) - // --- minimum variable time step for dynamic wave routing case MIN_ROUTE_STEP: if ( !getDouble(s2, &MinRouteStep) || MinRouteStep < 0.0 ) @@ -643,7 +656,6 @@ int project_readOption(char* s1, char* s2) if ( m < 0 ) return error_setInpError(ERR_NUMBER, s2); NumThreads = m; break; - //// // --- safety factor applied to variable time step estimates under // dynamic wave flow routing (value of 0 indicates that variable @@ -658,7 +670,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: - MinSurfArea = atof(s2); + 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) break; // --- minimum conduit slope (%) @@ -703,6 +718,13 @@ int project_readOption(char* s1, char* s2) LatFlowTol /= 100.0; break; + // --- method used for surcharging in dynamic wave flow routing //(5.1.013) + case SURCHARGE_METHOD: + m = findmatch(s2, SurchargeWords); + if (m < 0) return error_setInpError(ERR_KEYWORD, s2); + SurchargeMethod = m; + break; + case TEMPDIR: // Temporary Directory sstrncpy(TempDir, s2, MAXFNAME); break; @@ -742,7 +764,7 @@ void initPointers() Aquifer = NULL; UnitHyd = NULL; Snowmelt = NULL; - Event = NULL; //(5.1.011) + Event = NULL; MemPoolAllocated = FALSE; } @@ -786,6 +808,8 @@ void setDefaults() 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) AllowPonding = FALSE; // No ponding at nodes InertDamping = SOME; // Partial inertial damping NormalFlowLtd = BOTH; // Default normal flow limitation @@ -794,18 +818,19 @@ void setDefaults() LengtheningStep = 0; // No lengthening of conduits CourantFactor = 0.0; // No variable time step MinSurfArea = 0.0; // Force use of default min. surface area - MinSlope = 0.0; // No user supplied minimum conduit slope //(5.1.012) + MinSlope = 0.0; // No user supplied minimum conduit slope SkipSteadyState = FALSE; // Do flow routing in steady state periods IgnoreRainfall = FALSE; // Analyze rainfall/runoff - IgnoreRDII = FALSE; // Analyze RDII //(5.1.004) + IgnoreRDII = FALSE; // Analyze RDII IgnoreSnowmelt = FALSE; // Analyze snowmelt IgnoreGwater = FALSE; // Analyze groundwater IgnoreRouting = FALSE; // Analyze flow routing IgnoreQuality = FALSE; // Analyze water quality 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) - MinRouteStep = 0.5; // Minimum variable time step (sec) //(5.1.008) + MinRouteStep = 0.5; // Minimum variable time step (sec) ReportStep = 900; // Reporting time step (secs) StartDryDays = 0.0; // Antecedent dry days MaxTrials = 0; // Force use of default max. trials @@ -813,7 +838,7 @@ void setDefaults() SysFlowTol = 0.05; // System flow tolerance for steady state LatFlowTol = 0.05; // Lateral flow tolerance for steady state NumThreads = 0; // Number of parallel threads to use - NumEvents = 0; // Number of detailed routing events //(5.1.011) + NumEvents = 0; // Number of detailed routing events // Deprecated options SlopeWeighting = TRUE; // Use slope weighting @@ -839,6 +864,7 @@ void setDefaults() RptFlags.nodes = FALSE; RptFlags.links = FALSE; RptFlags.nodeStats = FALSE; + RptFlags.averages = FALSE; // Temperature data Temp.dataSource = NO_TEMP; @@ -876,19 +902,16 @@ void setDefaults() Evap.tSeries = -1; Evap.dryOnly = FALSE; -//// Following code segment added to release 5.1.007. //// //(5.1.007) -//// // Climate adjustments for (i = 0; i < 12; i++) { Adjust.temp[i] = 0.0; // additive adjustments Adjust.evap[i] = 0.0; // additive adjustments Adjust.rain[i] = 1.0; // multiplicative adjustments - Adjust.hydcon[i] = 1.0; // hyd. conductivity adjustments //(5.1.008) + Adjust.hydcon[i] = 1.0; // hyd. conductivity adjustments } Adjust.rainFactor = 1.0; - Adjust.hydconFactor = 1.0; //(5.1.008) -//// + Adjust.hydconFactor = 1.0; } //============================================================================= @@ -974,12 +997,10 @@ void createObjects() Snowmelt = (TSnowmelt *) calloc(Nobjects[SNOWMELT], sizeof(TSnowmelt)); Shape = (TShape *) calloc(Nobjects[SHAPE], sizeof(TShape)); -//// Added to release 5.1.011. //// //(5.1.011) // --- create array of detailed routing event periods Event = (TEvent *) calloc(NumEvents+1, sizeof(TEvent)); Event[NumEvents].start = BIG; Event[NumEvents].end = BIG + 1.0; -//// // --- create LID objects lid_create(Nobjects[LID], Nobjects[SUBCATCH]); @@ -1067,8 +1088,8 @@ void createObjects() Subcatch[j].outNode = -1; Subcatch[j].infil = -1; Subcatch[j].groundwater = NULL; - Subcatch[j].gwLatFlowExpr = NULL; //(5.1.007) - Subcatch[j].gwDeepFlowExpr = NULL; //(5.1.007) + Subcatch[j].gwLatFlowExpr = NULL; + Subcatch[j].gwDeepFlowExpr = NULL; Subcatch[j].snowpack = NULL; Subcatch[j].lidArea = 0.0; for (k = 0; k < Nobjects[POLLUT]; k++) @@ -1083,8 +1104,8 @@ void createObjects() // --- initialize snowmelt properties for ( j = 0; j < Nobjects[SNOWMELT]; j++ ) snow_initSnowmelt(j); - // --- initialize storage node exfiltration //(5.1.007) - for (j = 0; j < Nnodes[STORAGE]; j++) Storage[j].exfil = NULL; //(5.1.007) + // --- initialize storage node exfiltration + for (j = 0; j < Nnodes[STORAGE]; j++) Storage[j].exfil = NULL; // --- initialize link properties for (j = 0; j < Nobjects[LINK]; j++) @@ -1167,8 +1188,6 @@ void deleteObjects() // --- free memory used for rainfall infiltration infil_delete(); -//// Added for release 5.1.007. //// //(5.1.007) -//// // --- free memory used for storage exfiltration if ( Node ) for (j = 0; j < Nnodes[STORAGE]; j++) { @@ -1179,11 +1198,10 @@ void deleteObjects() FREE(Storage[j].exfil); } } -//// - // --- free memory used for outfall pollutants loads //(5.1.008) - if ( Node ) for (j = 0; j < Nnodes[OUTFALL]; j++) //(5.1.008) - FREE(Outfall[j].wRouted); //(5.1.008) + // --- free memory used for outfall pollutants loads + if ( Node ) for (j = 0; j < Nnodes[OUTFALL]; j++) + FREE(Outfall[j].wRouted); // --- free memory used for nodal inflows & treatment functions if ( Node ) for (j = 0; j < Nobjects[NODE]; j++) @@ -1231,7 +1249,7 @@ void deleteObjects() FREE(UnitHyd); FREE(Snowmelt); FREE(Shape); - FREE(Event); //(5.1.011) + FREE(Event); } //============================================================================= diff --git a/src/rain.c b/src/rain.c index 47cc73661..03af41187 100644 --- a/src/rain.c +++ b/src/rain.c @@ -6,6 +6,7 @@ // 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) // Author: L. Rossman // // Places rainfall data from external files into a SWMM rainfall @@ -44,6 +45,9 @@ // // 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(). //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -98,7 +102,7 @@ static void readFile(FILE *f, int fileFormat, int hdrLines, DateTime day1, DateTime day2); static int readNWSLine(char *line, int fileFormat, DateTime day1, DateTime day2); -static int readNwsOnlineValue(char* s, long* v, char* flag); //(5.1.011) +static int readNwsOnlineValue(char* s, long* v, char* flag); static int readCMCLine(char *line, int fileFormat, DateTime day1, DateTime day2); static int readStdLine(char *line, DateTime day1, DateTime day2); @@ -773,11 +777,11 @@ int readNWSLine(char *line, int fileFormat, DateTime day1, DateTime day2) // --- read each recorded rainfall time, value, & codes from line while ( k < lineLength ) { - flag1 = 0; + flag1 = 0; flag2 = 0; - v = 99999; - hour = 25; - minute = 0; + v = 99999; + hour = 25; + minute = 0; switch ( fileFormat ) { case NWS_TAPE: @@ -801,7 +805,7 @@ int readNWSLine(char *line, int fileFormat, DateTime day1, DateTime day2) case NWS_ONLINE_60: case NWS_ONLINE_15: n = sscanf(&line[k], " %2d:%2d", &hour, &minute); - n += readNwsOnlineValue(&line[ValueOffset], &v, &flag1); //(5.1.011) + n += readNwsOnlineValue(&line[ValueOffset], &v, &flag1); // --- ending hour 0 is really hour 24 of previous day if ( hour == 0 ) @@ -855,8 +859,6 @@ int readNWSLine(char *line, int fileFormat, DateTime day1, DateTime day2) //============================================================================= -//// New function added to release 5.1.011 //// //(5.1.011) - int readNwsOnlineValue(char* s, long* v, char* flag) // // Input: s = portion of rainfall record in NWS online format @@ -867,21 +869,21 @@ int readNwsOnlineValue(char* s, long* v, char* flag) // rainfall record. // { - int n; - float x = 99.99; + int n; + float x = 99.99f; //(5.1.013) - // --- check for newer format of decimal inches - if ( strchr(s, '.') ) + // --- check for newer format of decimal inches + if ( strchr(s, '.') ) { - n = sscanf(s, "%f %c", &x, flag); + n = sscanf(s, "%f %c", &x, flag); - // --- convert to integer hundreths of an inch - *v = (long)(100.0f * x + 0.5f); - } + // --- convert to integer hundreths of an inch + *v = (long)(100.0f * x + 0.5f); + } - // --- older format of hundreths of an inch - else n = sscanf(s, "%ld %c", v, flag); - return n; + // --- older format of hundreths of an inch + else n = sscanf(s, "%ld %c", v, flag); + return n; } //============================================================================= @@ -1017,7 +1019,7 @@ int readStdLine(char *line, DateTime day1, DateTime day2) date2 = date1 + datetime_encodeTime(hour, minute, 0); if ( date2 <= PreviousDate ) { - report_writeErrorMsg(ERR_RAIN_FILE_SEQUENCE, Gage[GageIndex].fname); //(5.1.010) + report_writeErrorMsg(ERR_RAIN_FILE_SEQUENCE, Gage[GageIndex].fname); report_writeLine(line); return -1; } diff --git a/src/rdii.c b/src/rdii.c index 28a0c418f..6da1d7a23 100644 --- a/src/rdii.c +++ b/src/rdii.c @@ -24,7 +24,7 @@ #include #include -#include +#include #include "headers.h" //----------------------------------------------------------------------------- @@ -75,7 +75,7 @@ static TUHGroup* UHGroup; // processing data for each UH group static int RdiiStep; // RDII time step (sec) static int NumRdiiNodes; // number of nodes w/ RDII data static int* RdiiNodeIndex; // indexes of nodes w/ RDII data -static REAL4* RdiiNodeFlow; // inflows for nodes with RDII //(5.1.003) +static REAL4* RdiiNodeFlow; // inflows for nodes with RDII static int RdiiFlowUnits; // RDII flow units code static DateTime RdiiStartDate; // start date of RDII inflow period static DateTime RdiiEndDate; // end date of RDII inflow period @@ -86,8 +86,14 @@ static int RdiiFileType; // type (binary/text) of RDII file //----------------------------------------------------------------------------- // Imported Variables //----------------------------------------------------------------------------- -extern double Qcf[]; // flow units conversion factors +#ifdef __cplusplus +extern const double Qcf[]; // flow units conversion factors // (see swmm5.c) +#else +extern double Qcf[]; // flow units conversion factors + // (see swmm5.c) +#endif + //----------------------------------------------------------------------------- // External functions (declared in funcs.h) //----------------------------------------------------------------------------- @@ -413,7 +419,7 @@ void rdii_openRdii() RdiiStartDate = NO_DATE; // --- create the RDII file if existing file not being used - if ( IgnoreRDII ) return; //(5.1.004) + if ( IgnoreRDII ) return; if ( Frdii.mode != USE_FILE ) createRdiiFile(); if ( Frdii.mode == NO_FILE || ErrorCode ) return; @@ -568,7 +574,7 @@ int readRdiiFileHeader() // --- allocate memory for RdiiNodeIndex & RdiiNodeFlow arrays RdiiNodeIndex = (int *) calloc(NumRdiiNodes, sizeof(int)); if ( !RdiiNodeIndex ) return ERR_MEMORY; - RdiiNodeFlow = (REAL4 *) calloc(NumRdiiNodes, sizeof(REAL4)); //(5.1.003) + RdiiNodeFlow = (REAL4 *) calloc(NumRdiiNodes, sizeof(REAL4)); if ( !RdiiNodeFlow ) return ERR_MEMORY; // --- read indexes of RDII nodes @@ -627,7 +633,7 @@ int readRdiiTextFileHeader() // --- allocate memory for RdiiNodeIndex & RdiiNodeFlow arrays RdiiNodeIndex = (int *) calloc(NumRdiiNodes, sizeof(int)); if ( !RdiiNodeIndex ) return ERR_MEMORY; - RdiiNodeFlow = (REAL4 *) calloc(NumRdiiNodes, sizeof(REAL4)); //(5.1.003) + RdiiNodeFlow = (REAL4 *) calloc(NumRdiiNodes, sizeof(REAL4)); if ( !RdiiNodeFlow ) return ERR_MEMORY; // --- read names of RDII nodes from file & save their indexes @@ -662,7 +668,7 @@ void readRdiiFlows() if ( feof(Frdii.file) ) return; fread(&RdiiStartDate, sizeof(DateTime), 1, Frdii.file); if ( RdiiStartDate == NO_DATE ) return; - if ( fread(RdiiNodeFlow, sizeof(REAL4), NumRdiiNodes, Frdii.file) //(5.1.003) + if ( fread(RdiiNodeFlow, sizeof(REAL4), NumRdiiNodes, Frdii.file) < (size_t)NumRdiiNodes ) RdiiStartDate = NO_DATE; else RdiiEndDate = datetime_addSeconds(RdiiStartDate, RdiiStep); } @@ -680,7 +686,7 @@ void readRdiiTextFlows() int i, n; int yr = 0, mon = 0, day = 0, hr = 0, min = 0, sec = 0; // year, month, day, hour, minute, second - double x; // RDII flow in original units //(5.1.003) + double x; // RDII flow in original units char line[MAXLINE+1]; // line from RDII data file char s[MAXLINE+1]; // node ID label (not used) @@ -689,10 +695,10 @@ void readRdiiTextFlows() { if ( feof(Frdii.file) ) return; fgets(line, MAXLINE, Frdii.file); - n = sscanf(line, "%s %d %d %d %d %d %d %f", + n = sscanf(line, "%s %d %d %d %d %d %d %lf", s, &yr, &mon, &day, &hr, &min, &sec, &x); if ( n < 8 ) return; - RdiiNodeFlow[i] = (REAL4)(x / Qcf[RdiiFlowUnits]); //(5.1.003) + RdiiNodeFlow[i] = (REAL4)(x / Qcf[RdiiFlowUnits]); } RdiiStartDate = datetime_encodeDate(yr, mon, day) + datetime_encodeTime(hr, min, sec); @@ -947,7 +953,7 @@ int allocRdiiMemory() // --- allocate memory for RDII indexes & inflow at each node w/ RDII data RdiiNodeIndex = (int *) calloc(NumRdiiNodes, sizeof(int)); if ( !RdiiNodeIndex ) return FALSE; - RdiiNodeFlow = (REAL4 *) calloc(NumRdiiNodes, sizeof(REAL4)); //(5.1.003) + RdiiNodeFlow = (REAL4 *) calloc(NumRdiiNodes, sizeof(REAL4)); if ( !RdiiNodeFlow ) return FALSE; return TRUE; } @@ -1181,7 +1187,7 @@ void getRainfall(DateTime currentDate) // --- get rainfall volume over gage's recording interval // at gage'a current date (in original depth units) gageDate = UHGroup[j].gageDate; - Adjust.rainFactor = Adjust.rain[datetime_monthOfYear(gageDate)-1]; //(5.1.007) + Adjust.rainFactor = Adjust.rain[datetime_monthOfYear(gageDate)-1]; if (!Gage[g].isCurrent) { gage_setState(g, gageDate); @@ -1478,7 +1484,7 @@ void saveRdiiFlows(DateTime currentDate) // { fwrite(¤tDate, sizeof(DateTime), 1, Frdii.file); - fwrite(RdiiNodeFlow, sizeof(REAL4), NumRdiiNodes, Frdii.file); //(5.1.003) + fwrite(RdiiNodeFlow, sizeof(REAL4), NumRdiiNodes, Frdii.file); } //============================================================================= diff --git a/src/report.c b/src/report.c index f6183ea2d..a78adb232 100644 --- a/src/report.c +++ b/src/report.c @@ -9,6 +9,7 @@ // 04/02/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 (EPA) // // Report writing functions. @@ -34,10 +35,15 @@ // Build 5.1.012: // - System time step statistics adjusted for time in steady state. // +// Build 5.1.013: +// - Parsing of AVERAGES report option added to report_readOptions(). +// - Name of surcharge method reported in report_writeOptions(). +// - Missing format specifier added to fprintf() in report_writeErrorCode. +// //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE -#include +#include #include #include #include @@ -135,6 +141,13 @@ int report_readOptions(char* tok[], int ntoks) 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]); } k = (char)findmatch(tok[1], NoneAllWords); @@ -236,14 +249,14 @@ void report_writeTitle() // { int i; - int lineCount = 0; //(5.1.011) + int lineCount = 0; if ( ErrorCode ) return; for (i=0; i 0 ) { WRITE(Title[i]); - lineCount++; //(5.1.011) + lineCount++; } - if ( lineCount > 0 ) WRITE(""); //(5.1.011) + if ( lineCount > 0 ) WRITE(""); } //============================================================================= @@ -274,7 +287,7 @@ void report_writeOptions() fprintf(Frpt.file, "NO"); else fprintf(Frpt.file, "YES"); - fprintf(Frpt.file, "\n RDII ................... "); //(5.1.004) + fprintf(Frpt.file, "\n RDII ................... "); if ( IgnoreRDII || Nobjects[UNITHYD] == 0 ) fprintf(Frpt.file, "NO"); else fprintf(Frpt.file, "YES"); @@ -308,6 +321,11 @@ void report_writeOptions() if ( Nobjects[LINK] > 0 ) 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) + datetime_dateToStr(StartDate, str); fprintf(Frpt.file, "\n Starting Date ............ %s", str); datetime_timeToStr(StartTime, str); @@ -335,9 +353,9 @@ void report_writeOptions() 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); //(5.1.008) + fprintf(Frpt.file, "\n Number of Threads ........ %d", NumThreads); fprintf(Frpt.file, "\n Head Tolerance ........... %.6f ", - HeadTol*UCF(LENGTH)); //(5.1.008) + HeadTol*UCF(LENGTH)); if ( UnitSystem == US ) fprintf(Frpt.file, "ft"); else fprintf(Frpt.file, "m"); } @@ -517,14 +535,12 @@ void report_writeRunoffError(TRunoffTotals* totals, double totalArea) totals->rainfall * UCF(LENGTH) * UCF(LANDAREA), totals->rainfall / totalArea * UCF(RAINDEPTH)); -//// Following code segment added to release 5.1.008. //// //(5.1.008) if ( totals->runon > 0.0 ) { fprintf(Frpt.file, "\n Outfall Runon ............%14.3f%14.3f", totals->runon * UCF(LENGTH) * UCF(LANDAREA), totals->runon / totalArea * UCF(RAINDEPTH)); } -//// fprintf(Frpt.file, "\n Evaporation Loss .........%14.3f%14.3f", totals->evap * UCF(LENGTH) * UCF(LANDAREA), @@ -538,7 +554,6 @@ void report_writeRunoffError(TRunoffTotals* totals, double totalArea) totals->runoff * UCF(LENGTH) * UCF(LANDAREA), totals->runoff / totalArea * UCF(RAINDEPTH)); -//// Following code segment added to release 5.1.008. //// //(5.1.008) if ( totals->drains > 0.0 ) { fprintf(Frpt.file, "\n LID Drainage .............%14.3f%14.3f", @@ -556,7 +571,7 @@ void report_writeRunoffError(TRunoffTotals* totals, double totalArea) totals->finalSnowCover / totalArea * UCF(RAINDEPTH)); } - fprintf(Frpt.file, "\n Final Storage ............%14.3f%14.3f", //(5.1.008) + fprintf(Frpt.file, "\n Final Storage ............%14.3f%14.3f", totals->finalStorage * UCF(LENGTH) * UCF(LANDAREA), totals->finalStorage / totalArea * UCF(RAINDEPTH)); @@ -768,13 +783,13 @@ void report_writeFlowError(TRoutingTotals *totals) fprintf(Frpt.file, "\n External Outflow .........%14.3f%14.3f", totals->outflow * ucf1, totals->outflow * ucf2); - fprintf(Frpt.file, "\n Flooding Loss ............%14.3f%14.3f", //(5.1.008) + fprintf(Frpt.file, "\n Flooding Loss ............%14.3f%14.3f", totals->flooding * ucf1, totals->flooding * ucf2); fprintf(Frpt.file, "\n Evaporation Loss .........%14.3f%14.3f", totals->evapLoss * ucf1, totals->evapLoss * ucf2); - fprintf(Frpt.file, "\n Exfiltration Loss ........%14.3f%14.3f", //(5.1.007) + fprintf(Frpt.file, "\n Exfiltration Loss ........%14.3f%14.3f", totals->seepLoss * ucf1, totals->seepLoss * ucf2); fprintf(Frpt.file, "\n Initial Stored Volume ....%14.3f%14.3f", @@ -811,7 +826,7 @@ void report_writeQualError(TRoutingTotals QualTotals[]) //============================================================================= -void report_QualErrors(int p1, int p2, TRoutingTotals QualTotals[]) +void report_QualErrors(int p1, int p2, TRoutingTotals* QualTotals) { int i; int p; @@ -873,20 +888,17 @@ void report_QualErrors(int p1, int p2, TRoutingTotals QualTotals[]) fprintf(Frpt.file, "%14.3f", QualTotals[p].outflow); } - fprintf(Frpt.file, "\n Flooding Loss ............"); //(5.1.008) + fprintf(Frpt.file, "\n Flooding Loss ............"); for (p = p1; p <= p2; p++) { fprintf(Frpt.file, "%14.3f", QualTotals[p].flooding); } -//// Following code segment added to release 5.1.008. //// //(5.1.008) -//// fprintf(Frpt.file, "\n Exfiltration Loss ........"); for (p = p1; p <= p2; p++) { fprintf(Frpt.file, "%14.3f", QualTotals[p].seepLoss); } -//// fprintf(Frpt.file, "\n Mass Reacted ............."); for (p = p1; p <= p2; p++) @@ -1012,10 +1024,10 @@ void report_writeSysStats(TSysStats* sysStats) // { double x; - double eventStepCount = (double)StepCount - sysStats->steadyStateCount; //(5.1.012) + double eventStepCount = (double)StepCount - sysStats->steadyStateCount; if ( Nobjects[LINK] == 0 || StepCount == 0 - || eventStepCount == 0.0 ) return; //(5.1.012) + || eventStepCount == 0.0 ) return; WRITE(""); WRITE("*************************"); WRITE("Routing Time Step Summary"); @@ -1025,19 +1037,19 @@ void report_writeSysStats(TSysStats* sysStats) sysStats->minTimeStep); fprintf(Frpt.file, "\n Average Time Step : %7.2f sec", - sysStats->avgTimeStep / eventStepCount); //(5.1.012) + sysStats->avgTimeStep / eventStepCount); fprintf(Frpt.file, "\n Maximum Time Step : %7.2f sec", sysStats->maxTimeStep); - x = (1.0 - sysStats->avgTimeStep * 1000.0 / NewRoutingTime) * 100.0; //(5.1.012) + x = (1.0 - sysStats->avgTimeStep * 1000.0 / NewRoutingTime) * 100.0; fprintf(Frpt.file, "\n Percent in Steady State : %7.2f", MIN(x, 100.0)); fprintf(Frpt.file, "\n Average Iterations per Step : %7.2f", - sysStats->avgStepCount / eventStepCount); //(5.1.012) + sysStats->avgStepCount / eventStepCount); fprintf(Frpt.file, "\n Percent Not Converging : %7.2f", - 100.0 * (double)NonConvergeCount / eventStepCount); //(5.1.012) + 100.0 * (double)NonConvergeCount / eventStepCount); WRITE(""); } @@ -1373,13 +1385,11 @@ void report_writeErrorMsg(int code, char* s) } ErrorCode = code; -//// Following code segment added to release 5.1.011. //// //(5.1.011) // --- 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); } -//// } //============================================================================= @@ -1396,7 +1406,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, error_getMsg(ErrorCode)); + fprintf(Frpt.file, "%s", error_getMsg(ErrorCode)); //(5.1.013) } } @@ -1432,7 +1442,7 @@ void report_writeWarningMsg(char* msg, char* id) // { fprintf(Frpt.file, "\n %s %s", msg, id); - Warnings++; //(5.1.011) + Warnings++; } //============================================================================= diff --git a/src/roadway.c b/src/roadway.c index 4a1248e01..4530f4fa3 100644 --- a/src/roadway.c +++ b/src/roadway.c @@ -24,7 +24,7 @@ #include #include "headers.h" -static enum RoadSurface {PAVED = 1, GRAVEL}; +enum RoadSurface {PAVED = 1, GRAVEL = 2}; //----------------------------------------------------------------------------- // Constants @@ -43,7 +43,7 @@ static const double Cr_Low_Paved[4][2] = { static const int N_Cr_Low_Gravel = 8; static const double Cr_Low_Gravel[8][2] = { {0.0, 2.5}, {0.5, 2.7}, {1.0, 2.8}, {1.5, 2.9}, {2.0, 2.98}, - {2.5, 3.02}, {3.0, 3.03}, {4.0, 3.05} }; //(5.1.012) + {2.5, 3.02}, {3.0, 3.03}, {4.0, 3.05} }; // Discharge Coefficients for (head / road width) > 0.15 static const int N_Cr_High_Paved = 2; diff --git a/src/routing.c b/src/routing.c index 0c70cb912..9882746c4 100644 --- a/src/routing.c +++ b/src/routing.c @@ -9,6 +9,7 @@ // 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) // M. Tryby (EPA) // @@ -37,6 +38,12 @@ // - 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. +// //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -45,14 +52,14 @@ #include #include #include "headers.h" -#include "lid.h" //(5.1.008) - +#include "lid.h" //----------------------------------------------------------------------------- // Shared variables //----------------------------------------------------------------------------- static int* SortedLinks; -static int NextEvent; //(5.1.011) -static int BetweenEvents; //(5.1.012) +static int NextEvent; +static int BetweenEvents; +static double NewRuleTime; //(5.1.013) //----------------------------------------------------------------------------- // External functions (declared in funcs.h) @@ -71,12 +78,12 @@ static void addWetWeatherInflows(double routingTime); static void addGroundwaterInflows(double routingTime); static void addRdiiInflows(DateTime currentDate); static void addIfaceInflows(DateTime currentDate); -static void addLidDrainInflows(double routingTime); //(5.1.008) +static void addLidDrainInflows(double routingTime); static void removeStorageLosses(double tStep); static void removeConduitLosses(void); -static void removeOutflows(double tStep); //(5.1.008) +static void removeOutflows(double tStep); static int inflowHasChanged(void); -static void sortEvents(void); //(5.1.011) +static void sortEvents(void); //============================================================================= @@ -107,15 +114,15 @@ int routing_open() // --- open any routing interface files iface_openRoutingFiles(); - // --- initialize flow and quality routing systems //(5.1.008) - flowrout_init(RouteModel); //(5.1.008) - if ( Fhotstart1.mode == NO_FILE ) qualrout_init(); //(5.1.008) + // --- initialize flow and quality routing systems + flowrout_init(RouteModel); + if ( Fhotstart1.mode == NO_FILE ) qualrout_init(); - // --- initialize routing events //(5.1.011) - if ( NumEvents > 0 ) sortEvents(); //(5.1.011) - NextEvent = 0; //(5.1.011) - //InSteadyState = (NumEvents > 0); //(5.1.012) - BetweenEvents = (NumEvents > 0); //(5.1.012) + // --- initialize routing events + if ( NumEvents > 0 ) sortEvents(); + NextEvent = 0; + BetweenEvents = (NumEvents > 0); + NewRuleTime = 0.0; //(5.1.013) return ErrorCode; } @@ -139,7 +146,7 @@ void routing_close(int routingModel) //============================================================================= -//// This function was re-written for release 5.1.011. //// //(5.1.011) +//// This function was modified for release 5.1.013. //// //(5.1.013) double routing_getRoutingStep(int routingModel, double fixedStep) // @@ -150,18 +157,19 @@ double routing_getRoutingStep(int routingModel, double fixedStep) // { double date1, date2, nextTime; + double routingStep = 0.0, nextRuleTime, nextRoutingTime; if ( Nobjects[LINK] == 0 ) return fixedStep; // --- find largest step possible if between routing events - if ( NumEvents > 0 && BetweenEvents ) //(5.1.012) + if ( NumEvents > 0 && BetweenEvents ) { nextTime = MIN(NewRunoffTime, ReportTime); date1 = getDateTime(NewRoutingTime); date2 = getDateTime(nextTime); if ( date2 > date1 && date2 < Event[NextEvent].start ) { - return (nextTime - NewRoutingTime) / 1000.0; + routingStep = (nextTime - NewRoutingTime) / 1000.0; } else { @@ -171,13 +179,26 @@ double routing_getRoutingStep(int routingModel, double fixedStep) } // --- otherwise use a regular flow-routing based time step - return flowrout_getRoutingStep(routingModel, fixedStep); + if (routingStep == 0.0) + { + routingStep = flowrout_getRoutingStep(routingModel, fixedStep); + } + + // --- determine if control rule time interval reached + if (RuleStep > 0) + { + nextRuleTime = NewRuleTime + 1000. * RuleStep; + nextRoutingTime = NewRoutingTime + 1000. * routingStep; + if (nextRoutingTime >= nextRuleTime) + { + routingStep = (nextRuleTime - NewRoutingTime) / 1000.0; + } + } + return routingStep; } //============================================================================= -//// This function was re-written for release 5.1.012. //// //(5.1.012) - void routing_execute(int routingModel, double routingStep) // // Input: routingModel = routing method code @@ -202,10 +223,15 @@ void routing_execute(int routingModel, double routingStep) // --- control rules (e.g., pump on/off depth limits) for (j=0; j 0.0 ) //(5.1.013) { massbal_addOutflowFlow(q, isFlooded); for ( p = 0; p < Nobjects[POLLUT]; p++ ) @@ -811,6 +839,7 @@ void removeOutflows(double tStep) massbal_addOutflowQual(p, w, isFlooded); } } + else massbal_addInflowFlow(EXTERNAL_INFLOW, -q); //(5.1.013) // --- update mass balance with mass leaving system through negative // lateral inflows (lateral flow was previously accounted for) @@ -829,8 +858,6 @@ void removeOutflows(double tStep) //============================================================================= -//// New function added for release 5.1.011. //// //(5.1.011) - void sortEvents() // // Input: none diff --git a/src/runoff.c b/src/runoff.c index fb9401bc0..bad45a44a 100644 --- a/src/runoff.c +++ b/src/runoff.c @@ -34,7 +34,7 @@ #include #include -#include +#include #include "headers.h" #include "odesolve.h" @@ -52,7 +52,7 @@ static long MaxStepsPos; // position in Runoff interface file //----------------------------------------------------------------------------- // Exportable variables //----------------------------------------------------------------------------- -char HasWetLids; // TRUE if any LIDs are wet (used in lidproc.c) //(5.1.008) +char HasWetLids; // TRUE if any LIDs are wet (used in lidproc.c) double* OutflowLoad; // exported pollutant mass load (used in surfqual.c) //----------------------------------------------------------------------------- @@ -74,7 +74,7 @@ static double runoff_getTimeStep(DateTime currentDate); static void runoff_initFile(void); static void runoff_readFromFile(void); static void runoff_saveToFile(float tStep); -static void runoff_getOutfallRunon(double tStep); //(5.1.008) +static void runoff_getOutfallRunon(double tStep); //============================================================================= @@ -93,7 +93,7 @@ int runoff_open() // --- open the Ordinary Differential Equation solver if ( !odesolve_open(MAXODES) ) report_writeErrorMsg(ERR_ODE_SOLVER, ""); - // --- allocate memory for pollutant runoff loads //(5.1.008) + // --- allocate memory for pollutant runoff loads OutflowLoad = NULL; if ( Nobjects[POLLUT] > 0 ) { @@ -115,8 +115,6 @@ int runoff_open() else runoff_initFile(); break; } - -//// Call to climate_openFile() moved to climate_validate(). //// //(5.1.007) return ErrorCode; } @@ -132,7 +130,7 @@ void runoff_close() // --- close the ODE solver odesolve_close(); - // --- free memory for pollutant runoff loads //(5.1.008) + // --- free memory for pollutant runoff loads FREE(OutflowLoad); // --- close runoff interface file if in use @@ -163,15 +161,15 @@ void runoff_execute() int j; // object index int day; // day of calendar year double runoffStep; // runoff time step (sec) - double oldRunoffStep; // previous runoff time step (sec) //(5.1.011) + double oldRunoffStep; // previous runoff time step (sec) double runoff; // subcatchment runoff (ft/sec) DateTime currentDate; // current date/time char canSweep; // TRUE if street sweeping can occur if ( ErrorCode ) return; - // --- find previous runoff time step in sec //(5.1.011) - oldRunoffStep = (NewRunoffTime - OldRunoffTime) / 1000.0; //(5.1.011) + // --- find previous runoff time step in sec + oldRunoffStep = (NewRunoffTime - OldRunoffTime) / 1000.0; // --- convert elapsed runoff time in milliseconds to a calendar date currentDate = getDateTime(NewRunoffTime); @@ -184,7 +182,7 @@ void runoff_execute() { OldRunoffTime = NewRunoffTime; NewRunoffTime += (double)(1000 * DryStep); - NewRunoffTime = MIN(NewRunoffTime, TotalDuration); //(5.1.008) + NewRunoffTime = MIN(NewRunoffTime, TotalDuration); return; } @@ -222,26 +220,23 @@ void runoff_execute() OldRunoffTime = NewRunoffTime; NewRunoffTime += (double)(1000 * runoffStep); -//// Following code segment added to release 5.1.008. //// //(5.1.008) -//// // --- adjust runoff step so that total duration not exceeded if ( NewRunoffTime > TotalDuration ) { runoffStep = (TotalDuration - OldRunoffTime) / 1000.0; NewRunoffTime = TotalDuration; } -//// // --- update old state of each subcatchment, for (j = 0; j < Nobjects[SUBCATCH]; j++) subcatch_setOldState(j); - // --- determine any runon from drainage system outfall nodes //(5.1.008) - if ( oldRunoffStep > 0.0 ) runoff_getOutfallRunon(oldRunoffStep); //(5.1.011) + // --- determine any runon from drainage system outfall nodes + if ( oldRunoffStep > 0.0 ) runoff_getOutfallRunon(oldRunoffStep); // --- determine runon from upstream subcatchments, and implement snow removal for (j = 0; j < Nobjects[SUBCATCH]; j++) { - if ( Subcatch[j].area == 0.0 ) continue; //(5.1.008) + if ( Subcatch[j].area == 0.0 ) continue; subcatch_getRunon(j); if ( !IgnoreSnowmelt ) snow_plowSnow(j, runoffStep); } @@ -249,13 +244,13 @@ void runoff_execute() // --- determine runoff and pollutant buildup/washoff in each subcatchment HasSnow = FALSE; HasRunoff = FALSE; - HasWetLids = FALSE; //(5.1.008) + HasWetLids = FALSE; for (j = 0; j < Nobjects[SUBCATCH]; j++) { // --- find total runoff rate (in ft/sec) over the subcatchment // (the amount that actually leaves the subcatchment (in cfs) // is also computed and is stored in Subcatch[j].newRunoff) - if ( Subcatch[j].area == 0.0 ) continue; //(5.1.008) + if ( Subcatch[j].area == 0.0 ) continue; runoff = subcatch_getRunoff(j, runoffStep); // --- update state of study area surfaces @@ -305,8 +300,8 @@ double runoff_getTimeStep(DateTime currentDate) // --- find shortest time until next evaporation or rainfall value // (this represents the maximum possible time step) - timeStep = datetime_timeDiff(climate_getNextEvapDate(), currentDate); //(5.1.008) - if ( timeStep > 0.0 && timeStep < maxStep ) maxStep = timeStep; //(5.1.008) + timeStep = datetime_timeDiff(climate_getNextEvapDate(), currentDate); + if ( timeStep > 0.0 && timeStep < maxStep ) maxStep = timeStep; for (j = 0; j < Nobjects[GAGE]; j++) { timeStep = datetime_timeDiff(gage_getNextRainDate(j, currentDate), @@ -314,13 +309,11 @@ double runoff_getTimeStep(DateTime currentDate) if ( timeStep > 0 && timeStep < maxStep ) maxStep = timeStep; } -//// Following code segment modified for release 5.1.012. //// //(5.1.012) // --- determine whether wet or dry time step applies if ( IsRaining || HasSnow || HasRunoff || HasWetLids ) { timeStep = WetStep; } -//// else timeStep = DryStep; // --- limit time step if necessary @@ -479,20 +472,19 @@ void runoff_readFromFile(void) // --- update runoff time clock OldRunoffTime = NewRunoffTime; NewRunoffTime = OldRunoffTime + (double)(tStep)*1000.0; - NewRunoffTime = MIN(NewRunoffTime, TotalDuration); //(5.1.008) + NewRunoffTime = MIN(NewRunoffTime, TotalDuration); Nsteps++; } //============================================================================= -//// New function added for release 5.1.008. //// //(5.1.008) void runoff_getOutfallRunon(double tStep) // -// Input: tStep = previous runoff time step (sec) //(5.1.011) +// Input: tStep = previous runoff time step (sec) // Output: none -// Purpose: adds flow and pollutant loads leaving drainage system outfalls //(5.1.011) -// during the previous runoff time step to designated subcatchments. //(5.1.011) +// Purpose: adds flow and pollutant loads leaving drainage system outfalls +// during the previous runoff time step to designated subcatchments. // { int i, k, p; @@ -523,4 +515,4 @@ void runoff_getOutfallRunon(double tStep) Outfall[i].wRouted[p] = 0.0; } } -} \ No newline at end of file +} diff --git a/src/shape.c b/src/shape.c index d3061db53..ebf38ad85 100644 --- a/src/shape.c +++ b/src/shape.c @@ -27,19 +27,19 @@ static double Ptotal; //----------------------------------------------------------------------------- // Local functions //----------------------------------------------------------------------------- -static int computeShapeTables(TShape *shape, TTable *curve); -static void getSmax(TShape *shape); -static int normalizeShapeTables(TShape *shape); -static int getNextInterval(TTable *curve, double y, double yLast, - double wLast, double *y1, double *y2, double *w1, double *w2, - double *wMax); +static int computeShapeTables(TShape *shape, TTable *curve); +static void getSmax(TShape *shape); +static int normalizeShapeTables(TShape *shape); +static int getNextInterval(TTable *curve, double y, double yLast, double wLast, + double *y1, double *y2, double *w1, double *w2, + double *wMax); static double getWidth(double y, double y1, double y2, double w1, double w2); static double getArea(double y, double w, double y1, double w1); static double getPerim(double y, double w, double y1, double w1); //============================================================================= -int shape_validate(TShape *shape, TTable *curve) +int shape_validate(TShape *shape, TTable *curve) // // Input: shape = pointer to a custom x-section TShape object // curve = pointer to shape's table of width v. height @@ -48,14 +48,20 @@ int shape_validate(TShape *shape, TTable *curve) // tables from its user-supplied width v. height curve. // { - if ( !computeShapeTables(shape, curve) ) return FALSE; - if ( !normalizeShapeTables(shape) ) return FALSE; + if (!computeShapeTables(shape, curve)) { + return FALSE; + } + + if (!normalizeShapeTables(shape)) { + return FALSE; + } + return TRUE; } //============================================================================= -int computeShapeTables(TShape *shape, TTable *curve) +int computeShapeTables(TShape *shape, TTable *curve) // // Input: shape = pointer to a TShape object // curve = pointer to shape's table of width v. depth @@ -69,80 +75,102 @@ int computeShapeTables(TShape *shape, TTable *curve) { int i, n; double dy, y, y1, y2, w, w1, w2; - double yLast, wLast, wMax; + double yLast, wLast, wMax; // --- get first entry of user's shape curve - if ( !table_getFirstEntry(curve, &y1, &w1) ) return FALSE; - if ( y1 < 0.0 || y1 >= 1.0 || w1 < 0.0 ) return FALSE; + if (!table_getFirstEntry(curve, &y1, &w1)) { + return FALSE; + } + + if (y1 < 0.0 || y1 >= 1.0 || w1 < 0.0) { + return FALSE; + } + wMax = w1; - + // --- if first entry not at zero ht. then add an initial entry - if ( y1 != 0.0 ) - { + if (y1 != 0.0) { y2 = y1; w2 = w1; y1 = 0.0; w1 = 0.0; } - // --- otherwise get next entry in the user's shape curve - else - { - if ( !table_getNextEntry(curve, &y2, &w2) ) return FALSE; - if ( y2 < y1 || w2 < 0.0 ) return FALSE; - if ( y2 > 1.0 ) y2 = 1.0; - if ( w2 > wMax ) wMax = w2; + else { + if (!table_getNextEntry(curve, &y2, &w2)) { + return FALSE; + } + + if (y2 < y1 || w2 < 0.0) { + return FALSE; + } + + if (y2 > 1.0) { + y2 = 1.0; + } + + if (w2 > wMax) { + wMax = w2; + } } // --- determine number of entries & interval size in geom. tables shape->nTbl = N_SHAPE_TBL; - n = shape->nTbl - 1; - dy = 1.0 / (double)(n); + n = shape->nTbl - 1; + dy = 1.0 / (double)(n); // --- initialize geometry tables - shape->areaTbl[0] = 0.0; - shape->hradTbl[0] = 0.0; + shape->areaTbl[0] = 0.0; + shape->hradTbl[0] = 0.0; shape->widthTbl[0] = w1; - Ptotal = w1; - Atotal = 0.0; + Ptotal = w1; + Atotal = 0.0; // --- fill in rest of geometry tables y = 0.0; w = w1; - for ( i = 1; i <= n; i++ ) - { + + for (i = 1; i <= n; i++) { // --- advance to next relative height level yLast = y; wLast = w; - y = y + dy; + y = y + dy; // --- do not allow height to exceed 1.0 - if ( fabs(y - 1.0) < TINY ) y = 1.0; + if (fabs(y - 1.0) < TINY) { + y = 1.0; + } // --- if height exceeds current shape curve interval, // move to next interval of shape curve - if ( y > y2 ) - { - if ( !getNextInterval(curve, y, yLast, wLast, &y1, &y2, &w1, - &w2, &wMax) ) + if (y > y2) { + if (!getNextInterval(curve, y, yLast, wLast, &y1, &y2, &w1, &w2, + &wMax)) { return FALSE; + } + yLast = y1; wLast = w1; } // --- get top width, area, & perimeter of current interval w = getWidth(y, y1, y2, w1, w2); - Atotal += getArea(y, w, yLast, wLast); + Atotal += getArea(y, w, yLast, wLast); Ptotal += getPerim(y, w, yLast, wLast); // --- add top width to total perimeter if at top of shape - if ( y == 1.0 ) Ptotal += w2; + if (y == 1.0) { + Ptotal += w2; + } // --- update table values shape->widthTbl[i] = w; - shape->areaTbl[i] = Atotal; - if ( Ptotal > 0.0) shape->hradTbl[i] = Atotal / Ptotal; - else shape->hradTbl[i] = 0.0; + shape->areaTbl[i] = Atotal; + if (Ptotal > 0.0) { + shape->hradTbl[i] = Atotal / Ptotal; + } else { + shape->hradTbl[i] = 0.0; + } } // --- assign values to shape'a area and hyd. radius when full @@ -150,14 +178,15 @@ int computeShapeTables(TShape *shape, TTable *curve) shape->rFull = shape->hradTbl[n]; // --- assign values to shape's max. width and section factor - shape->wMax = wMax; + shape->wMax = wMax; getSmax(shape); + return TRUE; } //============================================================================= -void getSmax(TShape *shape) +void getSmax(TShape *shape) // // Input: shape = pointer to a TShape object // Output: none @@ -171,11 +200,11 @@ void getSmax(TShape *shape) shape->sMax = 0.0; shape->aMax = 0.0; - for ( i = 1; i <= n; i++ ) - { - sf = shape->areaTbl[i] * pow(shape->hradTbl[i], 2./3.); - if ( sf > shape->sMax ) - { + + for (i = 1; i <= n; i++) { + sf = shape->areaTbl[i] * pow(shape->hradTbl[i], 2. / 3.); + + if (sf > shape->sMax) { shape->sMax = sf; shape->aMax = shape->areaTbl[i]; } @@ -184,7 +213,7 @@ void getSmax(TShape *shape) //============================================================================= -int normalizeShapeTables(TShape *shape) +int normalizeShapeTables(TShape *shape) // // Input: shape = pointer to a TShape object // Output: returns TRUE if successful. FALSE if not @@ -192,28 +221,30 @@ int normalizeShapeTables(TShape *shape) // { int i; - int n = shape->nTbl - 1; // highest table entry index + int n = shape->nTbl - 1; // highest table entry index double aFull = shape->aFull; // area when full double rFull = shape->rFull; // hyd. radius when full - double wMax = shape->wMax; // max. width + double wMax = shape->wMax; // max. width // --- check that normalizing factors are non-zero - if ( aFull == 0.0 || rFull == 0.0 || wMax == 0.0 ) return FALSE; + if (aFull == 0.0 || rFull == 0.0 || wMax == 0.0) { + return FALSE; + } // --- normalize entries in each table by their respective factors - for ( i = 0; i <= n; i++ ) - { + for (i = 0; i <= n; i++) { shape->areaTbl[i] /= aFull; shape->hradTbl[i] /= rFull; shape->widthTbl[i] /= wMax; } + return TRUE; } //============================================================================= -int getNextInterval(TTable *curve, double y, double yLast, double wLast, - double *y1, double *y2, double *w1, double *w2, +int getNextInterval(TTable *curve, double y, double yLast, double wLast, + double *y1, double *y2, double *w1, double *w2, double *wMax) // // Input: curve = pointer to a user-supplied shape curve table @@ -235,13 +266,11 @@ int getNextInterval(TTable *curve, double y, double yLast, double wLast, { // --- keep advancing while the current geom. table height is // above the end of the curve table interval - while ( y > *y2 ) - { + while (y > *y2) { // --- move start of geom. table interval up to the end of // the current curve table interval - if ( *y2 > yLast ) - { - Atotal += getArea(*y2, *w2, yLast, wLast); + if (*y2 > yLast) { + Atotal += getArea(*y2, *w2, yLast, wLast); Ptotal += getPerim(*y2, *w2, yLast, wLast); yLast = *y2; wLast = *w2; @@ -250,25 +279,32 @@ int getNextInterval(TTable *curve, double y, double yLast, double wLast, // --- move to the next curve table interval *y1 = *y2; *w1 = *w2; - if ( !table_getNextEntry(curve, y2, w2) ) - { + if (!table_getNextEntry(curve, y2, w2)) { *y2 = 1.0; return TRUE; } // --- update curve table's max. width - if ( *w2 > *wMax ) *wMax = *w2; + if (*w2 > *wMax) { + *wMax = *w2; + } // --- check for valid curve table values - if ( *y2 < *y1 || *w2 < 0.0 ) return FALSE; - if ( *y2 > 1.0 ) *y2 = 1.0; + if (*y2 < *y1 || *w2 < 0.0) { + return FALSE; + } + + if (*y2 > 1.0) { + *y2 = 1.0; + } } + return TRUE; } //============================================================================= -double getWidth(double y, double y1, double y2, double w1, double w2) +double getWidth(double y, double y1, double y2, double w1, double w2) // // Input: y = height along a shape curve // y1 = height at start of a shape curve interval @@ -280,7 +316,10 @@ double getWidth(double y, double y1, double y2, double w1, double w2) // x-section's shape curve. // { - if ( y2 == y1 ) return w2; + if (y2 == y1) { + return w2; + } + return w1 + (y - y1) / (y2 - y1) * (w2 - w1); } @@ -297,17 +336,16 @@ double getArea(double y, double w, double y1, double w1) // { double wMin, wMax; - if ( w > w1 ) - { + + if (w > w1) { wMin = w1; wMax = w; - } - else - { + } else { wMin = w; wMax = w1; } - return (wMin + (wMax - wMin)/2.) * (y - y1); + + return (wMin + (wMax - wMin) / 2.0) * (y - y1); } //============================================================================= @@ -324,7 +362,8 @@ double getPerim(double y, double w, double y1, double w1) // interval along a x-section's shape curve. // { - double dy = y - y1; + double dy = y - y1; double dw = fabs(w - w1) / 2.0; - return 2.0 * sqrt(dy*dy + dw*dw); + + return 2.0 * sqrt(dy * dy + dw * dw); } diff --git a/src/snow.c b/src/snow.c index 21e3021b7..e188f8402 100644 --- a/src/snow.c +++ b/src/snow.c @@ -503,12 +503,9 @@ double snow_getSnowMelt(int j, double rainfall, double snowfall, double tStep, smelt = routeSnowmelt(snowpack, i, smelt, asc, rainfall, tStep); } -//// Following section revised for release 5.1.008. //// //(5.1.008) -//// // --- find net precip. over entire subcatch area netPrecip[i] = smelt + snowpack->imelt[i] // snow pack melt + rainfall*(1.0 - asc); // rainfall on non-snow area -//// // --- add to total snow depth on subcatchment snowDepth += snowpack->wsnow[i] * snowpack->fArea[i]; @@ -705,8 +702,8 @@ double meltSnowpack(TSnowpack* snowpack, int i, double rmelt, double asc, return 0.0; } - // --- adjust snowmelt for area of snow cover //(5.1.008) - smelt *= asc; //(5.1.008) + // --- adjust snowmelt for area of snow cover + smelt *= asc; // --- reduce cold content of melting pack ccFactor = tStep * Snow.rnm * asc; @@ -837,7 +834,6 @@ double routeSnowmelt(TSnowpack* snowpack, int i, double smelt, double asc, // Output: returns rate of liquid snow melt leaving a snow pack (ft/sec) // Purpose: routes snow melt through free water holding capacity of snow pack. // -//// Additional comments added for release 5.1.008. //// //(5.1.008) { int k; // snowmelt parameter set index double vmelt; // snow melt volume (ft) @@ -852,7 +848,7 @@ double routeSnowmelt(TSnowpack* snowpack, int i, double smelt, double asc, // --- add snowmelt volume and any rainfall on snow // covered area of sub-area to snow pack's free water content - snowpack->fw[i] += vmelt + rainfall * tStep * asc; //(5.1.008) + snowpack->fw[i] += vmelt + rainfall * tStep * asc; // --- excess free water becomes liquid melt that leaves the pack vmelt = snowpack->fw[i] - Snowmelt[k].fwfrac[i] * snowpack->wsnow[i]; diff --git a/src/stats.c b/src/stats.c index 6b7ac06a0..7d6cb8b90 100644 --- a/src/stats.c +++ b/src/stats.c @@ -8,6 +8,7 @@ // 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 (EPA) // R. Dickinson (CDM) // @@ -30,13 +31,22 @@ // - 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. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE #include +#include #include -#include //(5.1.008) #include "headers.h" +#include "swmm5.h" +#if defined(_OPENMP) //(5.1.013) +#include +#endif //----------------------------------------------------------------------------- // Shared variables @@ -73,10 +83,10 @@ extern double* NodeOutflow; // defined in massbal.c // stats_close (called from swmm_end in swmm5.c) // stats_report (called from swmm_end in swmm5.c) // stats_updateSubcatchStats (called from subcatch_getRunoff) -// stats_updateGwaterStats (called from gwater_getGroundwater) //(5.1.008) +// stats_updateGwaterStats (called from gwater_getGroundwater) // stats_updateFlowStats (called from routing_execute) // stats_updateCriticalTimeCount (called from getVariableStep in dynwave.c) -// stats_updateMaxNodeDepth (called from output_saveNodeResults) //(5.1.008) +// stats_updateMaxNodeDepth (called from output_saveNodeResults) //----------------------------------------------------------------------------- // Local functions @@ -123,10 +133,10 @@ 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; // } -//// Added to release 5.1.008. //// //(5.1.008) -//// for (j=0; jstats.evap = 0.0; Subcatch[j].groundwater->stats.maxFlow = 0.0; } -//// } // --- allocate memory for node & link stats @@ -159,7 +168,7 @@ int stats_open() NodeStats[j].avgDepth = 0.0; NodeStats[j].maxDepth = 0.0; NodeStats[j].maxDepthDate = StartDateTime; - NodeStats[j].maxRptDepth = 0.0; //(5.1.008) + NodeStats[j].maxRptDepth = 0.0; NodeStats[j].volFlooded = 0.0; NodeStats[j].timeFlooded = 0.0; NodeStats[j].timeSurcharged = 0.0; @@ -210,7 +219,7 @@ int stats_open() StorageStats[j].maxVol = 0.0; StorageStats[j].maxFlow = 0.0; StorageStats[j].evapLosses = 0.0; - StorageStats[j].exfilLosses = 0.0; //(5.1.007) + StorageStats[j].exfilLosses = 0.0; StorageStats[j].maxVolDate = StartDateTime; } } @@ -330,6 +339,7 @@ void stats_report() void stats_updateSubcatchStats(int j, double rainVol, double runonVol, double evapVol, double infilVol, + double impervVol, double pervVol, double runoffVol, double runoff) // // Input: j = subcatchment index @@ -337,6 +347,8 @@ void stats_updateSubcatchStats(int j, double rainVol, double runonVol, // runonVol = runon volume from other subcatchments (ft3) // evapVol = evaporation volume (ft3) // infilVol = infiltration volume (ft3) +// impervVol = impervious runoff volume (ft3) +// pervVol = pervious runoff volume (ft3) // runoffVol = runoff volume (ft3) // runoff = runoff rate (cfs) // Output: none @@ -347,14 +359,14 @@ void stats_updateSubcatchStats(int j, double rainVol, double runonVol, SubcatchStats[j].runon += runonVol; SubcatchStats[j].evap += evapVol; SubcatchStats[j].infil += infilVol; - SubcatchStats[j].runoff += runoffVol; + SubcatchStats[j].runoff += runoffVol; SubcatchStats[j].maxFlow = MAX(SubcatchStats[j].maxFlow, runoff); + SubcatchStats[j].impervRunoff += impervVol; //(5.1.013) + SubcatchStats[j].pervRunoff += pervVol; // } //============================================================================= -//// New function added to release 5.1.008. //// //(5.1.008) - void stats_updateGwaterStats(int j, double infil, double evap, double latFlow, double deepFlow, double theta, double waterTable, double tStep) @@ -391,8 +403,6 @@ void stats_updateMaxRunoff() //============================================================================= -//// New function added for release 5.1.008. //// //(5.1.008) - void stats_updateMaxNodeDepth(int j, double depth) // // Input: j = node index @@ -425,18 +435,16 @@ void stats_updateFlowStats(double tStep, DateTime aDate, int stepCount, SysOutfallFlow = 0.0; // --- update node & link stats -#pragma omp parallel num_threads(NumThreads) //(5.1.008) +#pragma omp parallel num_threads(NumThreads) { - #pragma omp for //(5.1.008) + #pragma omp for for ( j=0; j 0.0); // --- update depth statistics @@ -513,12 +518,15 @@ void stats_updateNodeStats(int j, double tStep, DateTime aDate) (newVolume - Node[j].fullVolume)); } - // --- for dynamic wave routing, classify a non-storage node as //(5.1.011) - // surcharged if its water level exceeds its crown elev. //(5.1.011) - if ( RouteModel == DW && Node[j].type != STORAGE && //(5.1.011) - newDepth + Node[j].invertElev + FUDGE >= Node[j].crownElev ) + // --- for dynamic wave routing, classify a node as //(5.1.013) + // surcharged if its water level exceeds its crown elev. + if (RouteModel == DW) //(5.1.013) { - NodeStats[j].timeSurcharged += tStep; + if ((Node[j].type != STORAGE || Node[j].surDepth > 0.0) && //(5.1.013) + newDepth + Node[j].invertElev + FUDGE >= Node[j].crownElev) + { + NodeStats[j].timeSurcharged += tStep; + } } } @@ -554,7 +562,7 @@ void stats_updateNodeStats(int j, double tStep, DateTime aDate) for (p=0; p LinkStats[j].maxVeloc ) { LinkStats[j].maxVeloc = v; - //LinkStats[j].maxVelocDate = aDate; //(5.1.008) } // --- update max. depth @@ -657,13 +664,11 @@ void stats_updateLinkStats(int j, double tStep, DateTime aDate) // --- update time conduit is full k = Link[j].subIndex; - if ( q >= Link[j].qFull * (double)Conduit[k].barrels ) //(5.1.012) + if ( q >= Link[j].qFull * (double)Conduit[k].barrels ) LinkStats[j].timeFullFlow += tStep; if ( Conduit[k].capacityLimited ) LinkStats[j].timeCapacityLimited += tStep; -//// Following section modified for release 5.1.008. //// //(5.1.008) -//// switch (Conduit[k].fullState) { case ALL_FULL: @@ -677,7 +682,6 @@ void stats_updateLinkStats(int j, double tStep, DateTime aDate) case DN_FULL: LinkStats[j].timeFullDnstream += tStep; } -//// } // --- update flow turn count @@ -743,7 +747,7 @@ void stats_findMaxStats() if ( RouteModel != DW || CourantFactor == 0.0 ) return; // --- find nodes most frequently Courant critical - if ( StepCount == 0 ) return; //(5.1.008) + if ( StepCount == 0 ) return; for (j=0; j +#include #include #include #include @@ -50,7 +54,7 @@ extern double* NodeOutflow; // defined in massbal.c // Local functions //----------------------------------------------------------------------------- void writeSubcatchRunoff(void); -void writeGroundwater(void); //(5.1.008) +void writeGroundwater(void); void writeSubcatchLoads(void); void writeNodeDepths(void); void writeNodeFlows(void); @@ -95,7 +99,7 @@ void statsrpt_writeReport() { writeSubcatchRunoff(); lid_writeWaterBalance(); - if ( !IgnoreGwater ) writeGroundwater(); //(5.1.008) + if ( !IgnoreGwater ) writeGroundwater(); if ( Nobjects[POLLUT] > 0 && !IgnoreQuality) writeSubcatchLoads(); } } @@ -105,7 +109,7 @@ void statsrpt_writeReport() { writeNodeDepths(); writeNodeFlows(); - if ( RouteModel == DW ) writeNodeSurcharge(); //(5.1.011) + if ( RouteModel == DW ) writeNodeSurcharge(); writeNodeFlooding(); writeStorageVolumes(); writeOutfallLoads(); @@ -132,17 +136,21 @@ void writeSubcatchRunoff() WRITE(""); fprintf(Frpt.file, -"\n --------------------------------------------------------------------------------------------------------" -"\n Total Total Total Total Total Total Peak Runoff" -"\n Precip Runon Evap Infil Runoff Runoff Runoff Coeff"); +//////// 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"); if ( UnitSystem == US ) fprintf(Frpt.file, -"\n Subcatchment in in in in in %8s %3s", +"\n Subcatchment in in in in in in in %8s %3s", VolUnitsWords[UnitSystem], FlowUnitWords[FlowUnits]); else fprintf(Frpt.file, -"\n Subcatchment mm mm mm mm mm %8s %3s", +"\n Subcatchment mm mm mm mm mm mm mm %8s %3s", VolUnitsWords[UnitSystem], FlowUnitWords[FlowUnits]); fprintf(Frpt.file, -"\n --------------------------------------------------------------------------------------------------------"); +"\n ------------------------------------------------------------------------------------------------------------------------------"); + +///////////////////////////////////////////////////////////////// for ( j = 0; j < Nobjects[SUBCATCH]; j++ ) { @@ -157,10 +165,14 @@ 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].runoff * UCF(RAINDEPTH); fprintf(Frpt.file, " %10.2f", x/a); x = SubcatchStats[j].runoff * Vcf; - fprintf(Frpt.file, "%12.2f", x); + fprintf(Frpt.file, "%12.2f", x); x = SubcatchStats[j].maxFlow * UCF(FLOW); fprintf(Frpt.file, " %8.2f", x); r = SubcatchStats[j].precip + SubcatchStats[j].runon; @@ -172,8 +184,6 @@ void writeSubcatchRunoff() //============================================================================= -//// New function added to release 5.1.008. //// //(5.1.008) - void writeGroundwater(void) { int i, j; @@ -273,7 +283,7 @@ void writeSubcatchLoads() x = Subcatch[j].totalLoad[p]; totals[p] += x; if ( Pollut[p].units == COUNT ) x = LOG10(x); - fprintf(Frpt.file, "%14.3f", x); + fprintf(Frpt.file, "%14.3f", x); } } @@ -285,7 +295,7 @@ void writeSubcatchLoads() { x = totals[p]; if ( Pollut[p].units == COUNT ) x = LOG10(x); - fprintf(Frpt.file, "%14.3f", x); + fprintf(Frpt.file, "%14.3f", x); } free(totals); WRITE(""); @@ -294,8 +304,6 @@ void writeSubcatchLoads() //============================================================================= -//// Function modified for release 5.1.008. //// //(5.1.008) - void writeNodeDepths() // // Input: none @@ -332,7 +340,7 @@ void writeNodeDepths() NodeStats[j].avgDepth / StepCount * UCF(LENGTH), NodeStats[j].maxDepth * UCF(LENGTH), (NodeStats[j].maxDepth + Node[j].invertElev) * UCF(LENGTH), - days, hrs, mins, NodeStats[j].maxRptDepth); //(5.1.011) + days, hrs, mins, NodeStats[j].maxRptDepth); } WRITE(""); } @@ -362,7 +370,7 @@ void writeNodeFlows() "\n Inflow Inflow Occurrence Volume Volume Error" "\n Node Type %3s %3s days hr:min %8s %8s Percent", FlowUnitWords[FlowUnits], FlowUnitWords[FlowUnits], VolUnitsWords[UnitSystem], - VolUnitsWords[UnitSystem]); //(5.1.009) + VolUnitsWords[UnitSystem]); fprintf(Frpt.file, "\n -------------------------------------------------------------------------------------------------"); @@ -374,13 +382,13 @@ void writeNodeFlows() fprintf(Frpt.file, FlowFmt, NodeStats[j].maxLatFlow * UCF(FLOW)); fprintf(Frpt.file, FlowFmt, NodeStats[j].maxInflow * UCF(FLOW)); fprintf(Frpt.file, " %4d %02d:%02d", days1, hrs1, mins1); - fprintf(Frpt.file, "%12.3g", NodeStats[j].totLatFlow * Vcf); - fprintf(Frpt.file, "%12.3g", NodeInflow[j] * Vcf); - if ( fabs(NodeOutflow[j]) < 1.0 ) + fprintf(Frpt.file, "%12.3g", NodeStats[j].totLatFlow * Vcf); + fprintf(Frpt.file, "%12.3g", NodeInflow[j] * Vcf); + if ( fabs(NodeOutflow[j]) < 1.0 ) fprintf(Frpt.file, "%12.3f %s", (NodeInflow[j]-NodeOutflow[j])*Vcf*1.0e6, VolUnitsWords2[UnitSystem]); - else + else fprintf(Frpt.file, "%12.3f", (NodeInflow[j]-NodeOutflow[j]) / NodeOutflow[j]*100.); } @@ -407,7 +415,7 @@ void writeNodeSurcharge() t = MAX(0.01, (NodeStats[j].timeSurcharged / 3600.0)); if ( n == 0 ) { - WRITE("Surcharging occurs when water rises above the top of the highest conduit."); + WRITE("Surcharging occurs when water rises above the top of the highest conduit."); fprintf(Frpt.file, "\n ---------------------------------------------------------------------" "\n Max. Height Min. Depth" @@ -479,7 +487,7 @@ void writeNodeFlooding() fprintf(Frpt.file, FlowFmt, NodeStats[j].maxOverflow * UCF(FLOW)); getElapsedTime(NodeStats[j].maxOverflowDate, &days, &hrs, &mins); fprintf(Frpt.file, " %4d %02d:%02d", days, hrs, mins); - fprintf(Frpt.file, "%12.3f", NodeStats[j].volFlooded * Vcf); + fprintf(Frpt.file, "%12.3f", NodeStats[j].volFlooded * Vcf); if ( RouteModel == DW ) fprintf(Frpt.file, " %9.3f", (NodeStats[j].maxDepth - Node[j].fullDepth) * UCF(LENGTH)); @@ -515,7 +523,7 @@ void writeStorageVolumes() fprintf(Frpt.file, "\n --------------------------------------------------------------------------------------------------" -"\n Average Avg Evap Exfil Maximum Max Time of Max Maximum" //(5.1.007) +"\n Average Avg Evap Exfil Maximum Max Time of Max Maximum" "\n Volume Pcnt Pcnt Pcnt Volume Pcnt Occurrence Outflow"); if ( UnitSystem == US ) fprintf(Frpt.file, "\n Storage Unit 1000 ft3 Full Loss Loss 1000 ft3 Full days hr:min "); @@ -545,7 +553,7 @@ void writeStorageVolumes() if ( addedVol > 0.0 ) { pctEvapLoss = StorageStats[k].evapLosses / addedVol * 100.0; - pctSeepLoss = StorageStats[k].exfilLosses / addedVol * 100.0; //(5.1.007) + pctSeepLoss = StorageStats[k].exfilLosses / addedVol * 100.0; } fprintf(Frpt.file, "%10.3f %4.0f %4.0f %4.0f %10.3f %4.0f", @@ -583,7 +591,7 @@ void writeOutfallLoads() for (p=0; p 0.0) + if (Link[j].type == PUMP && Link[j].qFull > 0.0) { fprintf(Frpt.file, " "); fprintf(Frpt.file, " %6.2f", @@ -734,29 +742,29 @@ void writeLinkFlows() } // --- stop printing for dummy conduits - if ( Link[j].xsect.type == DUMMY ) continue; + if (Link[j].xsect.type == DUMMY) continue; // --- stop printing for outlet links (since they don't have xsections) - if ( Link[j].type == OUTLET ) continue; + if (Link[j].type == OUTLET) continue; // --- print max velocity & max/full flow for conduits - if ( Link[j].type == CONDUIT ) + if (Link[j].type == CONDUIT) { v = LinkStats[j].maxVeloc*UCF(LENGTH); - if ( v > 50.0 ) fprintf(Frpt.file, " >50.00"); + if (v > 50.0) fprintf(Frpt.file, " >50.00"); else fprintf(Frpt.file, " %7.2f", v); fprintf(Frpt.file, " %6.2f", LinkStats[j].maxFlow / Link[j].qFull / - (double)Conduit[k].barrels); + (double)Conduit[k].barrels); } else fprintf(Frpt.file, " "); // --- print max/full depth fullDepth = Link[j].xsect.yFull; - if ( Link[j].type == ORIFICE && - Orifice[k].type == BOTTOM_ORIFICE ) fullDepth = 0.0; - if ( fullDepth > 0.0 ) + if (Link[j].type == ORIFICE && + Orifice[k].type == BOTTOM_ORIFICE) fullDepth = 0.0; + if (fullDepth > 0.0) { - fprintf(Frpt.file, " %6.2f", LinkStats[j].maxDepth / fullDepth); + fprintf(Frpt.file, " %6.2f", LinkStats[j].maxDepth / fullDepth); } else fprintf(Frpt.file, " "); } @@ -821,7 +829,7 @@ void writeLinkSurcharge() for ( j = 0; j < Nobjects[LINK]; j++ ) { if ( Link[j].type != CONDUIT || - Link[j].xsect.type == DUMMY ) continue; + Link[j].xsect.type == DUMMY ) continue; t[0] = LinkStats[j].timeSurcharged / 3600.0; t[1] = LinkStats[j].timeFullUpstream / 3600.0; t[2] = LinkStats[j].timeFullDnstream / 3600.0; diff --git a/src/subcatch.c b/src/subcatch.c index 1a03cd46c..175d5d41c 100644 --- a/src/subcatch.c +++ b/src/subcatch.c @@ -10,6 +10,7 @@ // 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 // // Subcatchment runoff functions. @@ -37,6 +38,13 @@ // - 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. +// //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -56,7 +64,7 @@ const double ODETOL = 0.0001; // acceptable error for ODE solver //----------------------------------------------------------------------------- // Globally shared variables //----------------------------------------------------------------------------- -// Volumes (ft3) for a subcatchment over a time step //(5.1.008) +// Volumes (ft3) for a subcatchment over a time step double Vevap; // evaporation double Vpevap; // pervious area evaporation double Vinfil; // non-LID infiltration @@ -72,6 +80,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 char *RunoffRoutingWords[] = { w_OUTLET, w_IMPERV, w_PERV, NULL}; //----------------------------------------------------------------------------- @@ -88,7 +98,7 @@ static char *RunoffRoutingWords[] = { w_OUTLET, w_IMPERV, w_PERV, NULL}; // subcatch_setOldState (called from runoff_execute) // subcatch_getRunon (called from runoff_execute) // subcatch_addRunon (called from subcatch_getRunon, -// lid_addDrainRunon, & runoff_getOutfallRunon) //(5.1.008) +// lid_addDrainRunon, & runoff_getOutfallRunon) // subcatch_getRunoff (called from runoff_execute) // subcatch_hadRunoff (called from runoff_execute) @@ -103,13 +113,14 @@ static char *RunoffRoutingWords[] = { w_OUTLET, w_IMPERV, w_PERV, NULL}; // Function declarations //----------------------------------------------------------------------------- static void getNetPrecip(int j, double* netPrecip, double tStep); -static double getSubareaRunoff(int subcatch, int subarea, double area, //(5.1.008) +static double getSubareaRunoff(int subcatch, int subarea, double area, double rainfall, double evap, double tStep); static double getSubareaInfil(int j, TSubarea* subarea, double precip, double tStep); -static double findSubareaRunoff(TSubarea* subarea, double tRunoff); //(5.1.008) +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) //============================================================================= @@ -171,10 +182,13 @@ int subcatch_readParams(int j, char* tok[], int ntoks) Subcatch[j].outNode = (int)x[1]; Subcatch[j].outSubcatch = (int)x[2]; Subcatch[j].area = x[3] / UCF(LANDAREA); - Subcatch[j].fracImperv = MIN(x[4], 100.0) / 100.0; //(5.1.011) + Subcatch[j].fracImperv = MIN(x[4], 100.0) / 100.0; 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; // // --- create the snow pack object if it hasn't already been created if ( x[8] >= 0 ) @@ -392,7 +406,7 @@ void subcatch_validate(int j) Subcatch[j].subArea[i].alpha = 0.0; //// Possible change to how sub-area width should be assigned. //// -//// area = nonLidArea; //(5.1.011) +//// area = nonLidArea; ///////////////////////////////////////////////////////////////////// if ( area > 0.0 && Subcatch[j].subArea[i].N > 0.0 ) @@ -401,6 +415,11 @@ void subcatch_validate(int j) sqrt(Subcatch[j].slope) / Subcatch[j].subArea[i].N; } } + + // --- set isUsed property of subcatchment's rain gage //(5.1.013) + i = Subcatch[j].gage; // + if (i >= 0) Gage[i].isUsed = TRUE; // + } //============================================================================= @@ -414,6 +433,8 @@ 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; @@ -421,16 +442,8 @@ void subcatch_initState(int j) Subcatch[j].oldSnowDepth = 0.0; Subcatch[j].newSnowDepth = 0.0; Subcatch[j].runon = 0.0; - Subcatch[j].evapLoss = 0.0; //(5.1.008) - Subcatch[j].infilLoss = 0.0; //(5.1.008) - - // --- set isUsed property of subcatchment's rain gage - i = Subcatch[j].gage; - if ( i >= 0 ) - { - Gage[i].isUsed = TRUE; - if ( Gage[i].coGage >= 0 ) Gage[Gage[i].coGage].isUsed = TRUE; - } + Subcatch[j].evapLoss = 0.0; + Subcatch[j].infilLoss = 0.0; // --- initialize state of infiltration, groundwater, & snow pack objects if ( Subcatch[j].infil == j ) infil_initState(j, InfilModel); @@ -470,7 +483,7 @@ void subcatch_setOldState(int j) Subcatch[j].oldQual[i] = Subcatch[j].newQual[i]; Subcatch[j].newQual[i] = 0.0; } - lid_setOldGroupState(j); //(5.1.008) + lid_setOldGroupState(j); } //============================================================================= @@ -517,8 +530,6 @@ double subcatch_getStorage(int j) //============================================================================= -//// This function was modified for release 5.1.008. //// //(5.1.008) - void subcatch_getRunon(int j) // // Input: j = subcatchment index @@ -596,8 +607,6 @@ void subcatch_getRunon(int j) //============================================================================= -//// New function added to release 5.1.008. //// //(5.1.008) - void subcatch_addRunonFlow(int k, double q) // // Input: k = subcatchment index @@ -627,8 +636,6 @@ void subcatch_addRunonFlow(int k, double q) //============================================================================= -//// This function was modified for release 5.1.008. //// //(5.1.008) - double subcatch_getRunoff(int j, double tStep) // // Input: j = subcatchment index @@ -654,6 +661,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) // // --- initialize shared water balance variables Vevap = 0.0; @@ -673,11 +683,9 @@ double subcatch_getRunoff(int j, double tStep) vRunon = Subcatch[j].runon * tStep * nonLidArea; Vinflow = vRunon + subcatch_getDepth(j) * nonLidArea; -//// Added to release 5.1.009. //// //(5.1.009) // --- find LID runon only if LID occupies full subcatchment if ( nonLidArea == 0.0 ) vRunon = Subcatch[j].runon * tStep * Subcatch[j].area; -//// // --- get net precip. (rainfall + snowfall + snowmelt) on the 3 types // of subcatchment sub-areas and update Vinflow with it @@ -687,6 +695,9 @@ 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) + // --- examine each type of sub-area (impervious w/o depression storage, // impervious w/ depression storage, and pervious) if ( nonLidArea > 0.0 ) for (i = IMPERV0; i <= PERV; i++) @@ -696,7 +707,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); - runoff += Subcatch[j].subArea[i].runoff * area; + subAreaRunoff = Subcatch[j].subArea[i].runoff * area; //(5.1.013) + if (i == PERV) vPervRunoff = subAreaRunoff * tStep; // + else vImpervRunoff += subAreaRunoff * tStep; // + runoff += subAreaRunoff; // } // --- evaluate any LID treatment provided (updating Vevap, @@ -709,7 +723,7 @@ double subcatch_getRunoff(int j, double tStep) // --- update groundwater levels & flows if applicable if ( !IgnoreGwater && Subcatch[j].groundwater ) { - gwater_getGroundwater(j, Vpevap, Vinfil+VlidInfil, tStep); //(5.1.010) + gwater_getGroundwater(j, Vpevap, Vinfil+VlidInfil, tStep); } // --- save subcatchment's total loss rates (ft/s) @@ -729,8 +743,8 @@ double subcatch_getRunoff(int j, double tStep) // --- update the cumulative stats for this subcatchment stats_updateSubcatchStats(j, vRain, vRunon, Vevap, Vinfil + VlidInfil, - vOutflow + VlidDrain, - Subcatch[j].newRunoff + VlidDrain/tStep); + vImpervRunoff, vPervRunoff, vOutflow + VlidDrain, //(5.1.013) + Subcatch[j].newRunoff + VlidDrain/tStep); // --- include this subcatchment's contribution to overall flow balance // only if its outlet is a drainage system node @@ -791,8 +805,6 @@ void getNetPrecip(int j, double* netPrecip, double tStep) //============================================================================= -//// This function was modified for release 5.1.008. //// //(5.1.008) - double subcatch_getDepth(int j) // // Input: j = subcatchment index @@ -859,18 +871,15 @@ void subcatch_getResults(int j, double f, float x[]) x[SUBCATCH_INFIL] = (float)(Subcatch[j].infilLoss * UCF(RAINFALL)); runoff = f1 * Subcatch[j].oldRunoff + f * Subcatch[j].newRunoff; -//// Following code segement added to release 5.1.008. //// //(5.1.008) -//// // --- add any LID drain flow to reported runoff if ( Subcatch[j].lidArea > 0.0 ) { runoff += f1 * lid_getDrainFlow(j, PREVIOUS) + f * lid_getDrainFlow(j, CURRENT); } -//// // --- if runoff is really small, report it as zero - if ( runoff < MIN_RUNOFF * Subcatch[j].area ) runoff = 0.0; //(5.1.008) + if ( runoff < MIN_RUNOFF * Subcatch[j].area ) runoff = 0.0; x[SUBCATCH_RUNOFF] = (float)(runoff * UCF(FLOW)); // --- retrieve groundwater results @@ -879,7 +888,7 @@ void subcatch_getResults(int j, double f, float x[]) { z = (f1 * gw->oldFlow + f * gw->newFlow) * Subcatch[j].area * UCF(FLOW); x[SUBCATCH_GW_FLOW] = (float)z; - z = (gw->bottomElev + gw->lowerDepth) * UCF(LENGTH); //(5.1.012) + z = (gw->bottomElev + gw->lowerDepth) * UCF(LENGTH); x[SUBCATCH_GW_ELEV] = (float)z; z = gw->theta; x[SUBCATCH_SOIL_MOIST] = (float)z; @@ -905,8 +914,6 @@ void subcatch_getResults(int j, double f, float x[]) // SUB-AREA METHODS //============================================================================= -//// This function was modified for release 5.1.008. //// //(5.1.008) - double getSubareaRunoff(int j, int i, double area, double precip, double evap, double tStep) // @@ -954,6 +961,11 @@ 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); // + // --- if losses exceed available moisture then no ponded water remains if ( surfEvap + infil >= surfMoisture ) { @@ -1008,8 +1020,6 @@ double getSubareaInfil(int j, TSubarea* subarea, double precip, double tStep) //============================================================================= -//// This function was modified for release 5.1.008. //// //(5.1.008) - double findSubareaRunoff(TSubarea* subarea, double tRunoff) // // Purpose: computes runoff (ft/s) from subarea after current time step. @@ -1018,7 +1028,7 @@ double findSubareaRunoff(TSubarea* subarea, double tRunoff) // Output: returns runoff rate (ft/s) // { - double xDepth = subarea->depth - subarea->dStore; + double xDepth = subarea->depth - Dstore; //(5.1.013) double runoff = 0.0; if ( xDepth > ZERO ) @@ -1026,14 +1036,14 @@ double findSubareaRunoff(TSubarea* subarea, double tRunoff) // --- case where nonlinear routing is used if ( subarea->N > 0.0 ) { - runoff = subarea->alpha * pow(xDepth, MEXP); + runoff = Alpha * pow(xDepth, MEXP); //(5.1.013) } // --- case where no routing is used (Mannings N = 0) else { runoff = xDepth / tRunoff; - subarea->depth = subarea->dStore; + subarea->depth = Dstore; //(5.1.013) } } else @@ -1053,12 +1063,12 @@ void updatePondedDepth(TSubarea* subarea, double* dt) // Purpose: computes new ponded depth over subarea after current time step. // { - double ix = subarea->inflow; // excess inflow to subarea (ft/sec) //(5.1.008) + double ix = subarea->inflow; // excess inflow to subarea (ft/sec) double dx; // depth above depression storage (ft) double tx = *dt; // time over which dx > 0 (sec) - + // --- see if not enough inflow to fill depression storage (dStore) - if ( subarea->depth + ix*tx <= subarea->dStore ) + if ( subarea->depth + ix*tx <= Dstore ) //(5.1.013) { subarea->depth += ix * tx; } @@ -1066,16 +1076,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 - dx = subarea->dStore - subarea->depth; + // --- if depth < Dstore then fill up Dstore & reduce time step //(5.1.013) + dx = Dstore - subarea->depth; // if ( dx > 0.0 && ix > 0.0 ) { tx -= dx / ix; - subarea->depth = subarea->dStore; + subarea->depth = Dstore; //(5.1.013) } // --- now integrate depth over remaining time step tx - if ( subarea->alpha > 0.0 && tx > 0.0 ) + if ( Alpha > 0.0 && tx > 0.0 ) //(5.1.013) { theSubarea = subarea; odesolve_integrate(&(subarea->depth), 1, 0, tx, ODETOL, tx, @@ -1107,17 +1117,52 @@ void getDdDt(double t, double* d, double* dddt) // for the subarea whose runoff is being computed. // { - double ix = theSubarea->inflow; //(5.1.008) - double rx = *d - theSubarea->dStore; + double ix = theSubarea->inflow; + double rx = *d - Dstore; //(5.1.013) if ( rx < 0.0 ) { rx = 0.0; } else { - rx = theSubarea->alpha * pow(rx, MEXP); + rx = Alpha * pow(rx, MEXP); //(5.1.013) } *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 subarea's depression storage and its pervious +// runoff coeff. by month of the year. +// +{ + int p; // monthly pattern index + int m; // current month of the year + double f; // adjustment factor + + // --- depression storage adjustment + p = Subcatch[j].dStorePattern; + if (p >= 0 && Pattern[p].type == MONTHLY_PATTERN) + { + m = datetime_monthOfYear(getDateTime(OldRunoffTime)) - 1; + f = Pattern[p].factor[m]; + if (f >= 0.0) Dstore *= f; + } + + // --- pervious area roughness + p = Subcatch[j].nPervPattern; + if (i == PERV && p >= 0 && Pattern[p].type == MONTHLY_PATTERN) + { + m = datetime_monthOfYear(getDateTime(OldRunoffTime)) - 1; + f = Pattern[p].factor[m]; + if (f <= 0.0) Alpha = 0.0; + else Alpha /= f; + } +} diff --git a/src/swmm5.c b/src/swmm5.c index 41fa120ee..2917cb5e3 100644 --- a/src/swmm5.c +++ b/src/swmm5.c @@ -7,16 +7,15 @@ // 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 // // This is the main module of the computational engine for Version 5 of // the U.S. Environmental Protection Agency's Storm Water Management Model // (SWMM). It contains functions that control the flow of computations. // -// Depending on how it is compiled, this engine can be executed either as -// a command line executable or through a series of calls made to functions -// in a dynamic link library. -// +// This engine should be compiled into a shared object library whose API +// functions are listed in swmm5.h. // // Build 5.1.008: // - Support added for the MinGW compiler. @@ -35,18 +34,14 @@ // // 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. // //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE -//********************************************************** -// Leave only one of the following 3 lines un-commented, -// depending on the choice of compilation target -//********************************************************** -//#define CLE /* Compile as a command line executable */ -//#define SOL /* Compile as a shared object library */ -#define DLL /* Compile as a Windows DLL */ - // --- define WINDOWS #undef WINDOWS #ifdef _WIN32 @@ -56,26 +51,22 @@ #define WINDOWS #endif -//// ---- following section modified for release 5.1.011. //// //(5.1.011) -//// // --- define EXH (MS Windows exception handling) #undef EXH // indicates if exception handling included #ifdef WINDOWS #ifdef _MSC_VER - #define EXH + #define EXH #endif #endif - // --- include Windows & exception handling headers #ifdef WINDOWS #include - #include //(5.1.012) + #include #endif #ifdef EXH #include #endif -//// #include #include @@ -101,8 +92,8 @@ #define EXTERN // defined as 'extern' in headers.h #include "globals.h" // declaration of all global variables -#include "swmm5.h" // declaration of exportable functions - // callable from other programs +#include "swmm5.h" // declaration of SWMM's API functions + #define MAX_EXCEPTIONS 100 // max. number of exceptions handled //----------------------------------------------------------------------------- @@ -122,8 +113,8 @@ const double Ucf[10][2] = {43560.0, 3048.0 } // GWFLOW (cfs/ac, cms/ha --> ft/sec) }; const double Qcf[6] = // Flow Conversion Factors: - { 1.0, 448.831, 0.64632, // cfs, gpm, mgd --> cfs - 0.02832, 28.317, 2.4466 }; // cms, lps, mld --> cfs + {1.0, 448.831, 0.64632, // cfs, gpm, mgd --> cfs + 0.02832, 28.317, 2.4466 }; // cms, lps, mld --> cfs //----------------------------------------------------------------------------- // Shared variables @@ -136,7 +127,7 @@ static int DoRunoff; // TRUE if runoff is computed static int DoRouting; // TRUE if flow routing is computed //----------------------------------------------------------------------------- -// External functions (prototyped in swmm5.h) +// External API functions (prototyped in swmm5.h) //----------------------------------------------------------------------------- // swmm_run // swmm_open @@ -151,73 +142,11 @@ static int DoRouting; // TRUE if flow routing is computed //----------------------------------------------------------------------------- // Local functions //----------------------------------------------------------------------------- -static void execRouting(void); //(5.1.011) +static void execRouting(void); // Exception filtering function -#ifdef EXH //(5.1.011) -static int xfilter(int xc, char* module, double elapsedTime, long step); //(5.1.011) -#endif - -//----------------------------------------------------------------------------- -// Entry point used to compile a stand-alone executable. -//----------------------------------------------------------------------------- -#ifdef CLE -int main(int argc, char *argv[]) -// -// Input: argc = number of command line arguments -// argv = array of command line arguments -// Output: returns error status -// Purpose: processes command line arguments. -// -// Command line for stand-alone operation is: swmm5 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). -// -{ - char *inputFile; - char *reportFile; - char *binaryFile; - char blank[] = ""; - time_t start; - double runTime; - - // --- initialize flags - IsOpenFlag = FALSE; - IsStartedFlag = FALSE; - SaveResultsFlag = TRUE; - - // --- check for proper number of command line arguments - start = time(0); - if (argc < 3) writecon(FMT01); - else - { - // --- extract file names from command line arguments - inputFile = argv[1]; - reportFile = argv[2]; - if (argc > 3) binaryFile = argv[3]; - else binaryFile = blank; - writecon(FMT02); - - // --- run SWMM - swmm_run(inputFile, reportFile, binaryFile); - - // Display closing status on console - runTime = difftime(time(0), start); - sprintf(Msg, "\n\n... EPA-SWMM completed in %.2f seconds.", runTime); - writecon(Msg); - if ( ErrorCode ) writecon(FMT03); - else if ( Warnings ) writecon(FMT04); //(5.1.011) - else writecon(FMT05); - } - -// --- Use the code below if you need to keep the console window visible -/* - writecon(" Press Enter to continue..."); - getchar(); -*/ - - return 0; -} /* End of main */ +#ifdef EXH +static int xfilter(int xc, char* module, double elapsedTime, long step); #endif //============================================================================= @@ -233,7 +162,12 @@ int DLLEXPORT swmm_run(char* f1, char* f2, char* f3) { long newHour, oldHour = 0; long theDay, theHour; - double elapsedTime = 0.0; //(5.1.011) + double elapsedTime = 0.0; + + // --- initialize flags //(5.1.013) + IsOpenFlag = FALSE; // + IsStartedFlag = FALSE; // + SaveResultsFlag = TRUE; // // --- open the files & read input data ErrorCode = 0; @@ -258,7 +192,7 @@ int DLLEXPORT swmm_run(char* f1, char* f2, char* f3) 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"); - sprintf(Msg, "%-5d hour: %-2d", theDay, theHour); + sprintf(Msg, "%-5ld hour: %-2ld", theDay, theHour); //(5.1.013) writecon(Msg); oldHour = newHour; } @@ -277,7 +211,7 @@ int DLLEXPORT swmm_run(char* f1, char* f2, char* f3) // --- close the system swmm_close(); - return error_getCode(ErrorCode); //(5.1.011) + return error_getCode(ErrorCode); } //============================================================================= @@ -291,11 +225,12 @@ int DLLEXPORT swmm_open(char* f1, char* f2, char* f3) // Purpose: opens a SWMM project. // { -#ifdef DLL - _fpreset(); +// --- to be safe, reset the state of the floating point unit //(5.1.013) +#ifdef WINDOWS //(5.1.013) + _fpreset(); #endif -#ifdef EXH //(5.1.011) +#ifdef EXH // --- begin exception handling here __try #endif @@ -311,14 +246,14 @@ int DLLEXPORT swmm_open(char* f1, char* f2, char* f3) // --- open a SWMM project project_open(f1, f2, f3); - if ( ErrorCode ) return error_getCode(ErrorCode); //(5.1.011) + if ( ErrorCode ) return error_getCode(ErrorCode); IsOpenFlag = TRUE; report_writeLogo(); writecon(FMT06); // --- retrieve project data from input file project_readInput(); - if ( ErrorCode ) return error_getCode(ErrorCode); //(5.1.011) + if ( ErrorCode ) return error_getCode(ErrorCode); // --- write project title to report file & validate data report_writeTitle(); @@ -328,14 +263,14 @@ int DLLEXPORT swmm_open(char* f1, char* f2, char* f3) if ( RptFlags.input ) inputrpt_writeInput(); } -#ifdef EXH //(5.1.011) +#ifdef EXH // --- end of try loop; handle exception here - __except(xfilter(GetExceptionCode(), "swmm_open", 0.0, 0)) //(5.1.011) + __except(xfilter(GetExceptionCode(), "swmm_open", 0.0, 0)) { ErrorCode = ERR_SYSTEM; } #endif - return error_getCode(ErrorCode); //(5.1.011) + return error_getCode(ErrorCode); } //============================================================================= @@ -348,24 +283,24 @@ int DLLEXPORT swmm_start(int saveResults) // { // --- check that a project is open & no run started - if ( ErrorCode ) return error_getCode(ErrorCode); //(5.1.011) + if ( ErrorCode ) return error_getCode(ErrorCode); if ( !IsOpenFlag || IsStartedFlag ) { report_writeErrorMsg(ERR_NOT_OPEN, ""); - return error_getCode(ErrorCode); //(5.1.011) + return error_getCode(ErrorCode); } - // --- save saveResults flag to global variable //(5.1.011) - SaveResultsFlag = saveResults; //(5.1.011) + // --- save saveResults flag to global variable + SaveResultsFlag = saveResults; ExceptionCount = 0; -#ifdef EXH //(5.1.011) +#ifdef EXH // --- begin exception handling loop here __try #endif { - // --- initialize elapsed time in decimal days //(5.1.011) - ElapsedTime = 0.0; //(5.1.011) + // --- initialize elapsed time in decimal days + ElapsedTime = 0.0; // --- initialize runoff, routing & reporting time (in milliseconds) NewRunoffTime = 0.0; @@ -384,7 +319,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); //(5.1.011) + if ( ErrorCode ) return error_getCode(ErrorCode); // --- initialize state of each major system component project_init(); @@ -395,8 +330,6 @@ int DLLEXPORT swmm_start(int saveResults) if ( Nobjects[NODE] > 0 && !IgnoreRouting ) DoRouting = TRUE; else DoRouting = FALSE; -//// Following section modified for release 5.1.008. //// //(5.1.008) -//// // --- open binary output file output_open(); @@ -416,21 +349,20 @@ int DLLEXPORT swmm_start(int saveResults) // --- write project options to report file report_writeOptions(); if ( RptFlags.controls ) report_writeControlActionsHeading(); -//// } -#ifdef EXH //(5.1.011) +#ifdef EXH // --- end of try loop; handle exception here - __except(xfilter(GetExceptionCode(), "swmm_start", 0.0, 0)) //(5.1.011) + __except(xfilter(GetExceptionCode(), "swmm_start", 0.0, 0)) { ErrorCode = ERR_SYSTEM; } #endif - return error_getCode(ErrorCode); //(5.1.011) + return error_getCode(ErrorCode); } //============================================================================= -int DLLEXPORT swmm_step(double* elapsedTime) //(5.1.011) +int DLLEXPORT swmm_step(double* elapsedTime) // // Input: elapsedTime = current elapsed time in decimal days // Output: updated value of elapsedTime, @@ -439,14 +371,14 @@ int DLLEXPORT swmm_step(double* elapsedTime) / // { // --- check that simulation can proceed - if ( ErrorCode ) return error_getCode(ErrorCode); //(5.1.011) + if ( ErrorCode ) return error_getCode(ErrorCode); if ( !IsOpenFlag || !IsStartedFlag ) { report_writeErrorMsg(ERR_NOT_OPEN, ""); - return error_getCode(ErrorCode); //(5.1.011) + return error_getCode(ErrorCode); } -#ifdef EXH //(5.1.011) +#ifdef EXH // --- begin exception handling loop here __try #endif @@ -457,42 +389,70 @@ int DLLEXPORT swmm_step(double* elapsedTime) / // --- route flow & WQ through drainage system // (runoff will be calculated as needed) // (NewRoutingTime is updated) - execRouting(); //(5.1.011) + execRouting(); } - // --- save results at next reporting time - if ( NewRoutingTime >= ReportTime ) +//// Following code segment modified for release 5.1.013. //// //(5.1.013) + // --- if saving results to the binary file + if ( SaveResultsFlag ) { - if ( SaveResultsFlag ) output_saveResults(ReportTime); - ReportTime = ReportTime + (double)(1000 * ReportStep); + // --- 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(); } +//// // --- update elapsed time (days) if ( NewRoutingTime < TotalDuration ) { - ElapsedTime = NewRoutingTime / MSECperDAY; //(5.1.011) + ElapsedTime = NewRoutingTime / MSECperDAY; } // --- otherwise end the simulation - else ElapsedTime = 0.0; //(5.1.011) - *elapsedTime = ElapsedTime; //(5.1.011) + else ElapsedTime = 0.0; + *elapsedTime = ElapsedTime; } -#ifdef EXH //(5.1.011) +#ifdef EXH // --- end of try loop; handle exception here - __except(xfilter(GetExceptionCode(), "swmm_step", ElapsedTime, StepCount)) //(5.1.011) + __except(xfilter(GetExceptionCode(), "swmm_step", ElapsedTime, StepCount)) { ErrorCode = ERR_SYSTEM; } #endif - return error_getCode(ErrorCode); //(5.1.011) + return error_getCode(ErrorCode); } //============================================================================= -void execRouting() //(5.1.011) +void execRouting() // -// Input: none //(5.1.011) +// Input: none // Output: none // Purpose: routes flow & WQ through drainage system over a single time step. // @@ -500,7 +460,7 @@ void execRouting() / double nextRoutingTime; // updated elapsed routing time (msec) double routingStep; // routing time step (sec) -#ifdef EXH //(5.1.011) +#ifdef EXH // --- begin exception handling loop here __try #endif @@ -516,8 +476,6 @@ void execRouting() / } nextRoutingTime = NewRoutingTime + 1000.0 * routingStep; -//// Following section added to release 5.1.008. //// //(5.1.008) -//// // --- adjust routing step so that total duration not exceeded if ( nextRoutingTime > TotalDuration ) { @@ -525,7 +483,6 @@ void execRouting() / routingStep = MAX(routingStep, 1./1000.0); nextRoutingTime = TotalDuration; } -//// // --- compute runoff until next routing time reached or exceeded if ( DoRunoff ) while ( NewRunoffTime < nextRoutingTime ) @@ -537,16 +494,17 @@ void execRouting() / // --- if no runoff analysis, update climate state (for evaporation) else climate_setState(getDateTime(NewRoutingTime)); - // --- route flows & pollutants through drainage system //(5.1.008) - // (while updating NewRoutingTime) //(5.1.008) + // --- route flows & pollutants through drainage system + // (while updating NewRoutingTime) if ( DoRouting ) routing_execute(RouteModel, routingStep); - else NewRoutingTime = nextRoutingTime; + else + NewRoutingTime = nextRoutingTime; } -#ifdef EXH //(5.1.011) +#ifdef EXH // --- end of try loop; handle exception here - __except(xfilter(GetExceptionCode(), "execRouting", //(5.1.011) - ElapsedTime, StepCount)) //(5.1.011) + __except(xfilter(GetExceptionCode(), "execRouting", + ElapsedTime, StepCount)) { ErrorCode = ERR_SYSTEM; return; @@ -567,7 +525,7 @@ int DLLEXPORT swmm_end(void) if ( !IsOpenFlag ) { report_writeErrorMsg(ERR_NOT_OPEN, ""); - return error_getCode(ErrorCode); //(5.1.011) + return error_getCode(ErrorCode); } if ( IsStartedFlag ) @@ -591,7 +549,7 @@ int DLLEXPORT swmm_end(void) hotstart_close(); IsStartedFlag = FALSE; } - return error_getCode(ErrorCode); //(5.1.011) + return error_getCode(ErrorCode); } //============================================================================= @@ -610,7 +568,7 @@ int DLLEXPORT swmm_report() writecon(FMT07); report_writeReport(); } - return error_getCode(ErrorCode); //(5.1.011) + return error_getCode(ErrorCode); } //============================================================================= @@ -673,14 +631,13 @@ 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 VERSION; } //============================================================================= -//// New function added to release 5.1.011. //// //(5.1.011) - int DLLEXPORT swmm_getWarnings(void) // // Input: none @@ -692,8 +649,6 @@ int DLLEXPORT swmm_getWarnings(void) //============================================================================= -//// New function added to release 5.1.011. //// //(5.1.011) - int DLLEXPORT swmm_getError(char* errMsg, int msgLen) // // Input: errMsg = character array to hold error message text @@ -708,13 +663,13 @@ int DLLEXPORT swmm_getError(char* errMsg, int msgLen) if ( ErrorCode > 0 && strlen(ErrorMsg) == 0 ) sstrncpy(errMsg, "", 1); else { - errMsgLen = MIN(errMsgLen, strlen(ErrorMsg)); - errMsg = sstrncpy(errMsg, ErrorMsg, errMsgLen); + errMsgLen = MIN(errMsgLen, strlen(ErrorMsg)); + errMsg = sstrncpy(errMsg, ErrorMsg, errMsgLen); } // --- remove leading line feed from errMsg if ( errMsgLen > 0 && errMsg[0] == '\n' ) errMsg[0] = ' '; - return error_getCode(ErrorCode); //(5.1.011) + return error_getCode(ErrorCode); } //============================================================================= @@ -861,19 +816,17 @@ void writecon(char *s) // Purpose: writes string of characters to the console. // { -#ifdef CLE - fprintf(stdout,s); - fflush(stdout); -#endif + fprintf(stdout,s); + fflush(stdout); } //============================================================================= -#ifdef EXH //(5.1.011) -int xfilter(int xc, char* module, double elapsedTime, long step) //(5.1.011) +#ifdef EXH +int xfilter(int xc, char* module, double elapsedTime, long step) // // Input: xc = exception code -// module = name of code module where exception was handled //(5.1.011) +// module = name of code module where exception was handled // elapsedTime = simulation time when exception occurred (days) // step = step count at time when exception occurred // Output: returns an exception handling code @@ -924,12 +877,12 @@ int xfilter(int xc, char* module, double elapsedTime, long step) / rc = EXCEPTION_CONTINUE_EXECUTION; break; default: - sprintf(msg, "\n Exception %d", xc); + sprintf(msg, "\n Exception %d ", xc); rc = EXCEPTION_EXECUTE_HANDLER; } hour = (long)(elapsedTime / 1000.0 / 3600.0); - sprintf(xmsg, "%sin module %s at step %d, hour %d", //(5.1.011) - msg, module, step, hour); //(5.1.011) + sprintf(xmsg, "%sin module %s at step %d, hour %d", + msg, module, step, hour); if ( rc == EXCEPTION_EXECUTE_HANDLER || ++ExceptionCount >= MAX_EXCEPTIONS ) { @@ -940,6 +893,3 @@ int xfilter(int xc, char* module, double elapsedTime, long step) / return rc; } #endif - -//============================================================================= - \ No newline at end of file diff --git a/src/swmm5.h b/src/swmm5.h index 510d13c1b..f2eaf9872 100644 --- a/src/swmm5.h +++ b/src/swmm5.h @@ -10,6 +10,7 @@ // Prototypes for SWMM5 functions exported to swmm5.dll. // //----------------------------------------------------------------------------- + #ifndef SWMM5_H #define SWMM5_H @@ -26,9 +27,9 @@ // --- define DLLEXPORT #ifdef WINDOWS - #define DLLEXPORT __declspec(dllexport) __stdcall + #define DLLEXPORT __declspec(dllexport) __stdcall #else - #define DLLEXPORT + #define DLLEXPORT #endif // --- use "C" linkage for C++ programs @@ -47,8 +48,8 @@ int DLLEXPORT swmm_getMassBalErr(float* runoffErr, float* flowErr, float* qualErr); int DLLEXPORT swmm_close(void); int DLLEXPORT swmm_getVersion(void); -int DLLEXPORT swmm_getError(char* errMsg, int msgLen); //(5.1.011) -int DLLEXPORT swmm_getWarnings(void); //(5.1.011) +int DLLEXPORT swmm_getError(char* errMsg, int msgLen); +int DLLEXPORT swmm_getWarnings(void); #ifdef __cplusplus } // matches the linkage specification from above */ diff --git a/src/table.c b/src/table.c index 9cb250329..5ba5095f6 100644 --- a/src/table.c +++ b/src/table.c @@ -35,7 +35,7 @@ //----------------------------------------------------------------------------- int table_getNextFileEntry(TTable* table, double* x, double* y); int table_parseFileLine(char* line, TTable* table, double* x, double* y); -double table_interpolate(double x, double x1, double y1, double x2, double y2);//(5.1.008) +double table_interpolate(double x, double x1, double y1, double x2, double y2); //============================================================================= @@ -381,8 +381,6 @@ int table_getNextEntry(TTable *table, double *x, double *y) //============================================================================= -//// Revised for release 5.1.008 //// //(5.1.008) - double table_lookup(TTable *table, double x) // // Input: table = pointer to a TTable structure @@ -418,8 +416,6 @@ double table_lookup(TTable *table, double x) //============================================================================= -//// Revised for release 5.1.008. //// //(5.1.008) - double table_getSlope(TTable *table, double x) // // Input: table = pointer to a TTable structure @@ -454,8 +450,6 @@ double table_getSlope(TTable *table, double x) //============================================================================= -//// Revised for release 5.1.008. //// //(5.1.008) - double table_lookupEx(TTable *table, double x) // // Input: table = pointer to a TTable structure @@ -495,8 +489,6 @@ double table_lookupEx(TTable *table, double x) //============================================================================= -//// Revised for release 5.1.008. //// //(5.1.008) - double table_intervalLookup(TTable *table, double x) // // Input: table = pointer to a TTable structure @@ -521,8 +513,6 @@ double table_intervalLookup(TTable *table, double x) //============================================================================= -//// Revised for release 5.1.008. //// //(5.1.008) - double table_inverseLookup(TTable *table, double y) // // Input: table = pointer to a TTable structure @@ -558,8 +548,6 @@ double table_inverseLookup(TTable *table, double y) //============================================================================= -//// Revised for release 5.1.008. //// //(5.1.008) - double table_getMaxY(TTable *table, double x) // // Input: table = pointer to a TTable structure @@ -585,8 +573,6 @@ double table_getMaxY(TTable *table, double x) //============================================================================= -//// Revised for release 5.1.008. //// //(5.1.008) - double table_getArea(TTable* table, double x) // // Input: table = pointer to a TTable structure @@ -649,8 +635,6 @@ double table_getArea(TTable* table, double x) //============================================================================= -//// Revised for release 5.1.008. //// //(5.1.008) - double table_getInverseArea(TTable* table, double a) // // Input: table = pointer to a TTable structure diff --git a/src/text.h b/src/text.h index 05075db9d..6f3100613 100644 --- a/src/text.h +++ b/src/text.h @@ -14,14 +14,14 @@ // 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 // // Text strings //----------------------------------------------------------------------------- #define FMT01 \ - "\n Correct syntax is:\n swmm5 \n" -#define FMT02 "\n... EPA-SWMM 5.1 (Build 5.1.012)\n" //(5.1.012) + "\tswmm5 \n" #define FMT03 " There are errors.\n" #define FMT04 " There are warnings.\n" @@ -29,7 +29,7 @@ #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.012)" //(5.1.012) + "\n EPA STORM WATER MANAGEMENT MODEL - VERSION 5.1 (Build 5.1.013)" //(5.1.013) #define FMT09 \ "\n --------------------------------------------------------------" #define FMT10 "\n" @@ -39,8 +39,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 %d of input file:" -#define FMT18 "at line %d of %s] section:" +#define FMT17 "at line %ld of input file:" //(5.1.013) +#define FMT18 "at line %ld of %s] section:" //(5.1.013) #define FMT19 "\n Maximum error count exceeded." #define FMT20 "\n\n Analysis begun on: %s" #define FMT20a " Analysis ended on: %s" @@ -56,10 +56,11 @@ #define WARN07 "WARNING 07: routing time step reduced to the wet weather time step" #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 WARN10 "WARNING 10: crest elevation is below downstream invert for regulator Link" -#define WARN10 \ -"WARNING 10: crest elevation raised to downstream invert for regulator Link" //(5.1.011) -#define WARN11 "WARNING 11: non-matching attributes in Control Rule" //(5.1.009) +#define WARN10a \ +"WARNING 10: crest elevation is below downstream invert for regulator Link" //(5.1.013) +#define WARN10b \ +"WARNING 10: crest elevation raised to downstream invert for regulator Link" //(5.1.013) +#define WARN11 "WARNING 11: non-matching attributes in Control Rule" // Analysis Option Keywords #define w_FLOW_UNITS "FLOW_UNITS" @@ -78,6 +79,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_ALLOW_PONDING "ALLOW_PONDING" #define w_INERT_DAMPING "INERTIAL_DAMPING" #define w_SLOPE_WEIGHTING "SLOPE_WEIGHTING" @@ -100,9 +102,10 @@ #define w_HEAD_TOL "HEAD_TOLERANCE" #define w_SYS_FLOW_TOL "SYS_FLOW_TOL" #define w_LAT_FLOW_TOL "LAT_FLOW_TOL" -#define w_IGNORE_RDII "IGNORE_RDII" //(5.1.004) -#define w_MIN_ROUTE_STEP "MINIMUM_STEP" //(5.1.008) -#define w_NUM_THREADS "THREADS" //(5.1.008) +#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) // Flow Units #define w_CFS "CFS" @@ -123,11 +126,15 @@ #define w_XKINWAVE "XKINWAVE" #define w_DYNWAVE "DYNWAVE" +// Surcharge Methods //(5.1.013) +#define w_EXTRAN "EXTRAN" +#define w_SLOT "SLOT" + // Infiltration Methods #define w_HORTON "HORTON" #define w_MOD_HORTON "MODIFIED_HORTON" #define w_GREEN_AMPT "GREEN_AMPT" -#define w_MOD_GREEN_AMPT "MODIFIED_GREEN_AMPT" //(5.1.010) +#define w_MOD_GREEN_AMPT "MODIFIED_GREEN_AMPT" #define w_CURVE_NUMEBR "CURVE_NUMBER" // Normal Flow Criteria @@ -203,7 +210,7 @@ #define w_TRANSVERSE "TRANSVERSE" #define w_SIDEFLOW "SIDEFLOW" #define w_VNOTCH "V-NOTCH" -#define w_ROADWAY "ROADWAY" //(5.1.010) +#define w_ROADWAY "ROADWAY" // Conduit Cross-Section Shapes #define w_DUMMY "DUMMY" @@ -310,6 +317,7 @@ #define w_FLOWSTATS "FLOWSTATS" #define w_CONTROLS "CONTROL" #define w_NODESTATS "NODESTATS" +#define w_AVERAGES "AVERAGES" //(5.1.013) // Interface File Types #define w_RAINFALL "RAINFALL" @@ -431,7 +439,7 @@ #define ws_PROFILE "[PROFILE" #define ws_LID_CONTROL "[LID_CONTROL" #define ws_LID_USAGE "[LID_USAGE" -#define ws_GW_FLOW "[GW_FLOW" //Deprecated //(5.1.007) -#define ws_GWF "[GWF" //(5.1.007) -#define ws_ADJUST "[ADJUSTMENT" //(5.1.007) -#define ws_EVENT "[EVENT" //(5.1.011) +#define ws_GW_FLOW "[GW_FLOW" //Deprecated +#define ws_GWF "[GWF" +#define ws_ADJUST "[ADJUSTMENT" +#define ws_EVENT "[EVENT" diff --git a/src/treatmnt.c b/src/treatmnt.c index 313cab3ce..0843b5f96 100644 --- a/src/treatmnt.c +++ b/src/treatmnt.c @@ -39,7 +39,6 @@ static double Q; // node inflow (cfs) static double V; // node volume (ft3) static double* R; // array of pollut. removals static double* Cin; // node inflow concentrations -//static TTreatment* Treatment; // defined locally in treatmnt_treat() //(5.1.008) //----------------------------------------------------------------------------- // External functions (declared in funcs.h) @@ -208,7 +207,7 @@ void treatmnt_treat(int j, double q, double v, double tStep) int p; // pollutant index double cOut; // concentration after treatment double massLost; // mass lost by treatment per time step - TTreatment* treatment; // pointer to treatment object //(5.1.008) + TTreatment* treatment; // pointer to treatment object // --- set locally shared variables for node j if ( Node[j].treatment == NULL ) return; @@ -225,11 +224,11 @@ void treatmnt_treat(int j, double q, double v, double tStep) for ( p = 0; p < Nobjects[POLLUT]; p++) { // --- removal is zero if there is no treatment equation - treatment = &Node[j].treatment[p]; //(5.1.008) - if ( treatment->equation == NULL ) R[p] = 0.0; //(5.1.008) + treatment = &Node[j].treatment[p]; + if ( treatment->equation == 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; //(5.1.008) + else if ( treatment->treatType == REMOVAL && q <= ZERO ) R[p] = 0.0; // --- otherwise evaluate the treatment expression to find R[p] else getRemoval(p); @@ -245,11 +244,11 @@ void treatmnt_treat(int j, double q, double v, double tStep) else for ( p = 0; p < Nobjects[POLLUT]; p++ ) { if ( R[p] == 0.0 ) continue; - treatment = &Node[j].treatment[p]; //(5.1.008) + treatment = &Node[j].treatment[p]; // --- removal-type treatment equations get applied to inflow stream - if ( treatment->treatType == REMOVAL ) //(5.1.008) + if ( treatment->treatType == REMOVAL ) { // --- if no pollutant in inflow then cOut is current nodal concen. if ( Cin[p] == 0.0 ) cOut = Node[j].newQual[p]; @@ -344,7 +343,7 @@ double getVariableValue(int varCode) { int p; double a1, a2, y; - TTreatment* treatment; //(5.1.008) + TTreatment* treatment; // --- variable is a process variable if ( varCode < PVMAX ) @@ -381,8 +380,8 @@ double getVariableValue(int varCode) else if ( varCode < PVMAX + Nobjects[POLLUT] ) { p = varCode - PVMAX; - treatment = &Node[J].treatment[p]; //(5.1.008) - if ( treatment->treatType == REMOVAL ) return Cin[p]; //(5.1.008) + treatment = &Node[J].treatment[p]; + if ( treatment->treatType == REMOVAL ) return Cin[p]; return Node[J].newQual[p]; } @@ -406,7 +405,7 @@ double getRemoval(int p) { double c0 = Node[J].newQual[p]; // initial node concentration double r; // removal value - TTreatment* treatment; //(5.1.008) + TTreatment* treatment; // --- case where removal already being computed for another pollutant if ( R[p] > 1.0 || ErrCode ) @@ -431,12 +430,12 @@ double getRemoval(int p) } // --- apply treatment eqn. - treatment = &Node[J].treatment[p]; //(5.1.008) - r = mathexpr_eval(treatment->equation, getVariableValue); //(5.1.008) + treatment = &Node[J].treatment[p]; + r = mathexpr_eval(treatment->equation, getVariableValue); r = MAX(0.0, r); // --- case where treatment eqn. is for removal - if ( treatment->treatType == REMOVAL ) //(5.1.008) + if ( treatment->treatType == REMOVAL ) { r = MIN(1.0, r); R[p] = r; diff --git a/src/xsect.c b/src/xsect.c index f09b2a35c..9a4cc6dab 100644 --- a/src/xsect.c +++ b/src/xsect.c @@ -5,6 +5,7 @@ // 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) // M. Tryby (EPA) // @@ -27,6 +28,9 @@ // // 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. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -446,7 +450,7 @@ int xsect_setParams(TXsect *xsect, int type, double p[], double ucf) // --- height of circular arc xsect->yBot = xsect->rBot * (1.0 - cos(theta/2.0)); - xsect->ywMax = xsect->yFull - xsect->yBot; //(5.1.012) + xsect->ywMax = xsect->yFull - xsect->yBot; // --- area of circular arc xsect->aBot = xsect->rBot * xsect->rBot / @@ -537,10 +541,10 @@ int xsect_setParams(TXsect *xsect, int type, double p[], double ucf) break; case HORIZ_ELLIPSE: - if ( p[1] == 0.0 ) p[2] = p[0]; //(5.1.008) - if ( p[2] > 0.0 ) // std. ellipse pipe //(5.1.008) + if ( p[1] == 0.0 ) p[2] = p[0]; + if ( p[2] > 0.0 ) // std. ellipse pipe { - index = (int)floor(p[2]) - 1; // size code //(5.1.008) + index = (int)floor(p[2]) - 1; // size code if ( index < 0 || index >= NumCodesEllipse ) return FALSE; xsect->yFull = MinorAxis_Ellipse[index]/12.; @@ -565,10 +569,10 @@ int xsect_setParams(TXsect *xsect, int type, double p[], double ucf) break; case VERT_ELLIPSE: - if ( p[1] == 0.0 ) p[2] = p[0]; //(5.1.008) - if ( p[2] > 0.0 ) // std. ellipse pipe //(5.1.008) + if ( p[1] == 0.0 ) p[2] = p[0]; + if ( p[2] > 0.0 ) // std. ellipse pipe { - index = (int)floor(p[2]) - 1; // size code //(5.1.008) + index = (int)floor(p[2]) - 1; // size code if ( index < 0 || index >= NumCodesEllipse ) return FALSE; xsect->yFull = MajorAxis_Ellipse[index]/12.; @@ -593,10 +597,10 @@ int xsect_setParams(TXsect *xsect, int type, double p[], double ucf) break; case ARCH: - if ( p[1] == 0.0 ) p[2] = p[0]; //(5.1.008) - if ( p[2] > 0.0 ) // std. arch pipe //(5.1.008) + if ( p[1] == 0.0 ) p[2] = p[0]; + if ( p[2] > 0.0 ) // std. arch pipe { - index = (int)floor(p[2]) - 1; // size code //(5.1.008) + index = (int)floor(p[2]) - 1; // size code if ( index < 0 || index >= NumCodesArch ) return FALSE; xsect->yFull = Yfull_Arch[index]/12.; // Yfull units are inches @@ -984,7 +988,9 @@ double xsect_getWofY(TXsect *xsect, double y) return xsect->wMax * lookup(yNorm, Shape[Curve[xsect->transect].refersTo].widthTbl, N_SHAPE_TBL); - case RECT_CLOSED: return xsect->wMax; + case RECT_CLOSED: + if (yNorm == 1.0) return 0.0; //(5.1.013) + return xsect->wMax; case RECT_TRIANG: return rect_triang_getWofY(xsect, y);