gage.c
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | //----------------------------------------------------------------------------- | ||
| 2 | // gage.c | ||
| 3 | // | ||
| 4 | // Project: EPA SWMM5 | ||
| 5 | // Version: 5.2 | ||
| 6 | // Date: 11/01/21 (Build 5.2.0) | ||
| 7 | // Author: L. Rossman | ||
| 8 | // | ||
| 9 | // Rain gage functions. | ||
| 10 | // | ||
| 11 | // Update History | ||
| 12 | // ============== | ||
| 13 | // Build 5.1.007: | ||
| 14 | // - Support for monthly rainfall adjustments added. | ||
| 15 | // Build 5.1.013: | ||
| 16 | // - Validation no longer performed on unused gages. | ||
| 17 | // Build 5.2.0: | ||
| 18 | // - Support added for tracking a gage's prior n-hour rainfall total. | ||
| 19 | // - Support added for relative file names. | ||
| 20 | // - Support added for setting rainfall through API call. | ||
| 21 | //----------------------------------------------------------------------------- | ||
| 22 | #define _CRT_SECURE_NO_DEPRECATE | ||
| 23 | |||
| 24 | #include <string.h> | ||
| 25 | #include <math.h> | ||
| 26 | #include "headers.h" | ||
| 27 | |||
| 28 | //----------------------------------------------------------------------------- | ||
| 29 | // Constants | ||
| 30 | //----------------------------------------------------------------------------- | ||
| 31 | const double OneSecond = 1.1574074e-5; | ||
| 32 | |||
| 33 | //----------------------------------------------------------------------------- | ||
| 34 | // External functions (declared in funcs.h) | ||
| 35 | //----------------------------------------------------------------------------- | ||
| 36 | // gage_readParams (called by input_readLine) | ||
| 37 | // gage_validate (called by project_validate) | ||
| 38 | // gage_initState (called by project_init) | ||
| 39 | // gage_setState (called by runoff_execute & getRainfall in rdii.c) | ||
| 40 | // gage_getPrecip (called by subcatch_getRunoff) | ||
| 41 | // gage_getNextRainDate (called by runoff_getTimeStep) | ||
| 42 | // gage_updatePastRain (called by runoff_execute) | ||
| 43 | // gage_getPastRain (called by getRainValue in controls.c) | ||
| 44 | // gage_setReportRainfall (called by output_saveSubcatchResults) | ||
| 45 | |||
| 46 | //----------------------------------------------------------------------------- | ||
| 47 | // Local functions | ||
| 48 | //----------------------------------------------------------------------------- | ||
| 49 | static int readGageSeriesFormat(char* tok[], int ntoks, double x[]); | ||
| 50 | static int readGageFileFormat(char* tok[], int ntoks, double x[]); | ||
| 51 | static int getFirstRainfall(int gage); | ||
| 52 | static int getNextRainfall(int gage); | ||
| 53 | static double convertRainfall(int gage, double rain); | ||
| 54 | static void initPastRain(int gage); | ||
| 55 | |||
| 56 | //============================================================================= | ||
| 57 | |||
| 58 | 52 | int gage_readParams(int j, char* tok[], int ntoks) | |
| 59 | // | ||
| 60 | // Input: j = rain gage index | ||
| 61 | // tok[] = array of string tokens | ||
| 62 | // ntoks = number of tokens | ||
| 63 | // Output: returns an error code | ||
| 64 | // Purpose: reads rain gage parameters from a line of input data | ||
| 65 | // | ||
| 66 | // Data formats are: | ||
| 67 | // Name RainType RecdFreq SCF TIMESERIES SeriesName | ||
| 68 | // Name RainType RecdFreq SCF FILE FileName Station Units StartDate | ||
| 69 | // | ||
| 70 | { | ||
| 71 | int k, err; | ||
| 72 | char *id; | ||
| 73 | char fname[MAXFNAME+1]; | ||
| 74 | char staID[MAXMSG+1]; | ||
| 75 | double x[7]; | ||
| 76 | |||
| 77 | // --- check that gage exists | ||
| 78 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
|
52 | if ( ntoks < 2 ) return error_setInpError(ERR_ITEMS, ""); |
| 79 | 52 | id = project_findID(GAGE, tok[0]); | |
| 80 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
|
52 | if ( id == NULL ) return error_setInpError(ERR_NAME, tok[0]); |
| 81 | |||
| 82 | // --- assign default parameter values | ||
| 83 | 52 | x[0] = -1.0; // No time series index | |
| 84 | 52 | x[1] = 1.0; // Rain type is volume | |
| 85 | 52 | x[2] = 3600.0; // Recording freq. is 3600 sec | |
| 86 | 52 | x[3] = 1.0; // Snow catch deficiency factor | |
| 87 | 52 | x[4] = NO_DATE; // Default is no start/end date | |
| 88 | 52 | x[5] = NO_DATE; | |
| 89 | 52 | x[6] = 0.0; // US units | |
| 90 | 52 | fname[0] = '\0'; | |
| 91 | 52 | staID[0] = '\0'; | |
| 92 | |||
| 93 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
|
52 | if ( ntoks < 5 ) return error_setInpError(ERR_ITEMS, ""); |
| 94 | 52 | k = findmatch(tok[4], GageDataWords); | |
| 95 |
2/2✓ Branch 0 taken 39 times.
✓ Branch 1 taken 13 times.
|
52 | if ( k == RAIN_TSERIES ) |
| 96 | { | ||
| 97 | 39 | err = readGageSeriesFormat(tok, ntoks, x); | |
| 98 | } | ||
| 99 |
1/2✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
|
13 | else if ( k == RAIN_FILE ) |
| 100 | { | ||
| 101 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
|
13 | if ( ntoks < 8 ) return error_setInpError(ERR_ITEMS, ""); |
| 102 | 13 | sstrncpy(fname, tok[5], MAXFNAME); | |
| 103 | 13 | sstrncpy(staID, tok[6], MAXMSG); | |
| 104 | 13 | err = readGageFileFormat(tok, ntoks, x); | |
| 105 | } | ||
| 106 | ✗ | else return error_setInpError(ERR_KEYWORD, tok[4]); | |
| 107 | |||
| 108 | // --- save parameters to rain gage object | ||
| 109 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
|
52 | if ( err > 0 ) return err; |
| 110 | 52 | Gage[j].ID = id; | |
| 111 | 52 | Gage[j].tSeries = (int)x[0]; | |
| 112 | 52 | Gage[j].rainType = (int)x[1]; | |
| 113 | 52 | Gage[j].rainInterval = (int)x[2]; | |
| 114 | 52 | Gage[j].snowFactor = x[3]; | |
| 115 | 52 | Gage[j].rainUnits = (int)x[6]; | |
| 116 |
2/2✓ Branch 0 taken 39 times.
✓ Branch 1 taken 13 times.
|
52 | if ( Gage[j].tSeries >= 0 ) Gage[j].dataSource = RAIN_TSERIES; |
| 117 | 13 | else Gage[j].dataSource = RAIN_FILE; | |
| 118 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 39 times.
|
52 | if ( Gage[j].dataSource == RAIN_FILE ) |
| 119 | { | ||
| 120 | 13 | sstrncpy(Gage[j].fname, addAbsolutePath(fname), MAXFNAME); | |
| 121 | 13 | sstrncpy(Gage[j].staID, staID, MAXMSG); | |
| 122 | 13 | Gage[j].startFileDate = x[4]; | |
| 123 | 13 | Gage[j].endFileDate = x[5]; | |
| 124 | } | ||
| 125 | 52 | Gage[j].unitsFactor = 1.0; | |
| 126 | 52 | Gage[j].coGage = -1; | |
| 127 | 52 | Gage[j].isUsed = FALSE; | |
| 128 | 52 | return 0; | |
| 129 | } | ||
| 130 | |||
| 131 | //============================================================================= | ||
| 132 | |||
| 133 | 39 | int readGageSeriesFormat(char* tok[], int ntoks, double x[]) | |
| 134 | { | ||
| 135 | int m, ts; | ||
| 136 | DateTime aTime; | ||
| 137 | |||
| 138 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
|
39 | if ( ntoks < 6 ) return error_setInpError(ERR_ITEMS, ""); |
| 139 | |||
| 140 | // --- determine type of rain data | ||
| 141 | 39 | m = findmatch(tok[1], RainTypeWords); | |
| 142 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
|
39 | if ( m < 0 ) return error_setInpError(ERR_KEYWORD, tok[1]); |
| 143 | 39 | x[1] = (double)m; | |
| 144 | |||
| 145 | // --- get data time interval & convert to seconds | ||
| 146 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 2 taken 31 times.
|
39 | if ( getDouble(tok[2], &x[2]) ) x[2] = floor(x[2]*3600 + 0.5); |
| 147 |
1/2✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
|
31 | else if ( datetime_strToTime(tok[2], &aTime) ) |
| 148 | { | ||
| 149 | 31 | x[2] = floor(aTime*SECperDAY + 0.5); | |
| 150 | } | ||
| 151 | ✗ | else return error_setInpError(ERR_DATETIME, tok[2]); | |
| 152 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
|
39 | if ( x[2] <= 0.0 ) return error_setInpError(ERR_DATETIME, tok[2]); |
| 153 | |||
| 154 | // --- get snow catch deficiency factor | ||
| 155 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
|
39 | if ( !getDouble(tok[3], &x[3]) ) |
| 156 | ✗ | return error_setInpError(ERR_DATETIME, tok[3]);; | |
| 157 | |||
| 158 | // --- get time series index | ||
| 159 | 39 | ts = project_findObject(TSERIES, tok[5]); | |
| 160 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
|
39 | if ( ts < 0 ) return error_setInpError(ERR_NAME, tok[5]); |
| 161 | 39 | x[0] = (double)ts; | |
| 162 | 39 | sstrncpy(tok[2], "", 0); | |
| 163 | 39 | return 0; | |
| 164 | } | ||
| 165 | |||
| 166 | //============================================================================= | ||
| 167 | |||
| 168 | 13 | int readGageFileFormat(char* tok[], int ntoks, double x[]) | |
| 169 | { | ||
| 170 | int m, u; | ||
| 171 | DateTime aDate; | ||
| 172 | DateTime aTime; | ||
| 173 | |||
| 174 | // --- determine type of rain data | ||
| 175 | 13 | m = findmatch(tok[1], RainTypeWords); | |
| 176 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
|
13 | if ( m < 0 ) return error_setInpError(ERR_KEYWORD, tok[1]); |
| 177 | 13 | x[1] = (double)m; | |
| 178 | |||
| 179 | // --- get data time interval & convert to seconds | ||
| 180 |
2/2✓ Branch 1 taken 7 times.
✓ Branch 2 taken 6 times.
|
13 | if ( getDouble(tok[2], &x[2]) ) x[2] *= 3600; |
| 181 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | else if ( datetime_strToTime(tok[2], &aTime) ) |
| 182 | { | ||
| 183 | 6 | x[2] = floor(aTime*SECperDAY + 0.5); | |
| 184 | } | ||
| 185 | ✗ | else return error_setInpError(ERR_DATETIME, tok[2]); | |
| 186 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
|
13 | if ( x[2] <= 0.0 ) return error_setInpError(ERR_DATETIME, tok[2]); |
| 187 | |||
| 188 | // --- get snow catch deficiency factor | ||
| 189 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
|
13 | if ( !getDouble(tok[3], &x[3]) ) |
| 190 | ✗ | return error_setInpError(ERR_NUMBER, tok[3]); | |
| 191 | |||
| 192 | // --- get rain depth units | ||
| 193 | 13 | u = findmatch(tok[7], RainUnitsWords); | |
| 194 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
|
13 | if ( u < 0 ) return error_setInpError(ERR_KEYWORD, tok[7]); |
| 195 | 13 | x[6] = (double)u; | |
| 196 | |||
| 197 | // --- get start date (if present) | ||
| 198 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
13 | if ( ntoks > 8 && *tok[8] != '*') |
| 199 | { | ||
| 200 | ✗ | if ( !datetime_strToDate(tok[8], &aDate) ) | |
| 201 | ✗ | return error_setInpError(ERR_DATETIME, tok[8]); | |
| 202 | ✗ | x[4] = (float) aDate; | |
| 203 | } | ||
| 204 | 13 | return 0; | |
| 205 | } | ||
| 206 | |||
| 207 | //============================================================================= | ||
| 208 | |||
| 209 | 52 | void gage_validate(int j) | |
| 210 | // | ||
| 211 | // Input: j = rain gage index | ||
| 212 | // Output: none | ||
| 213 | // Purpose: checks for valid rain gage parameters | ||
| 214 | // | ||
| 215 | // NOTE: assumes that any time series used by a rain gage has been | ||
| 216 | // previously validated. | ||
| 217 | // | ||
| 218 | { | ||
| 219 | int i, k; | ||
| 220 | int gageInterval; | ||
| 221 | |||
| 222 | // --- for gage with time series data: | ||
| 223 |
2/2✓ Branch 0 taken 39 times.
✓ Branch 1 taken 13 times.
|
52 | if ( Gage[j].dataSource == RAIN_TSERIES ) |
| 224 | { | ||
| 225 | // --- no validation for an unused gage | ||
| 226 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
|
39 | if ( !Gage[j].isUsed ) return; |
| 227 | |||
| 228 | // --- see if gage uses same time series as another gage | ||
| 229 | 39 | k = Gage[j].tSeries; | |
| 230 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 39 times.
|
58 | for (i=0; i<j; i++) |
| 231 | { | ||
| 232 |
2/4✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
|
19 | if ( Gage[i].dataSource == RAIN_TSERIES && Gage[i].tSeries == k |
| 233 | ✗ | && Gage[i].isUsed ) | |
| 234 | { | ||
| 235 | ✗ | Gage[j].coGage = i; | |
| 236 | |||
| 237 | // --- check that both gages record same type of data | ||
| 238 | ✗ | if ( Gage[j].rainType != Gage[i].rainType ) | |
| 239 | { | ||
| 240 | ✗ | report_writeErrorMsg(ERR_RAIN_GAGE_FORMAT, Gage[j].ID); | |
| 241 | } | ||
| 242 | ✗ | return; | |
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | // --- check gage's recording interval against that of time series | ||
| 247 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
|
39 | if ( Tseries[k].refersTo >= 0 ) |
| 248 | { | ||
| 249 | ✗ | report_writeErrorMsg(ERR_RAIN_GAGE_TSERIES, Gage[j].ID); | |
| 250 | } | ||
| 251 | 39 | gageInterval = (int)(floor(Tseries[k].dxMin*SECperDAY + 0.5)); | |
| 252 |
2/4✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 39 times.
|
39 | if ( gageInterval > 0 && Gage[j].rainInterval > gageInterval ) |
| 253 | { | ||
| 254 | ✗ | report_writeErrorMsg(ERR_RAIN_GAGE_INTERVAL, Gage[j].ID); | |
| 255 | } | ||
| 256 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 38 times.
|
39 | if ( Gage[j].rainInterval < gageInterval ) |
| 257 | { | ||
| 258 | 1 | report_writeWarningMsg(WARN09, Gage[j].ID); | |
| 259 | } | ||
| 260 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 38 times.
|
39 | if ( Gage[j].rainInterval < WetStep ) |
| 261 | { | ||
| 262 | 1 | report_writeWarningMsg(WARN01, Gage[j].ID); | |
| 263 | 1 | WetStep = Gage[j].rainInterval; | |
| 264 | } | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | //============================================================================= | ||
| 269 | |||
| 270 | 61 | void gage_initState(int j) | |
| 271 | // | ||
| 272 | // Input: j = rain gage index | ||
| 273 | // Output: none | ||
| 274 | // Purpose: initializes state of rain gage. | ||
| 275 | // | ||
| 276 | { | ||
| 277 | // --- initialize actual and reported rainfall | ||
| 278 | 61 | Gage[j].rainfall = 0.0; | |
| 279 | 61 | Gage[j].apiRainfall = MISSING; | |
| 280 | 61 | Gage[j].reportRainfall = 0.0; | |
| 281 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 61 times.
|
61 | if ( IgnoreRainfall ) return; |
| 282 | |||
| 283 | // --- for gage with file data: | ||
| 284 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 48 times.
|
61 | if ( Gage[j].dataSource == RAIN_FILE ) |
| 285 | { | ||
| 286 | // --- set current file position to start of period of record | ||
| 287 | 13 | Gage[j].currentFilePos = Gage[j].startFilePos; | |
| 288 | |||
| 289 | // --- assign units conversion factor | ||
| 290 | // (rain depths on interface file are in inches) | ||
| 291 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
|
13 | if ( UnitSystem == SI ) Gage[j].unitsFactor = MMperINCH; |
| 292 | } | ||
| 293 | |||
| 294 | // --- get first & next rainfall values | ||
| 295 |
1/2✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
|
61 | if ( getFirstRainfall(j) ) |
| 296 | { | ||
| 297 | // --- find date at end of starting rain interval | ||
| 298 | 122 | Gage[j].endDate = datetime_addSeconds( | |
| 299 | 61 | Gage[j].startDate, Gage[j].rainInterval); | |
| 300 | |||
| 301 | // --- if rainfall record begins after start of simulation, | ||
| 302 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 48 times.
|
61 | if ( Gage[j].startDate > StartDateTime ) |
| 303 | { | ||
| 304 | // --- make next rainfall date the start of the rain record | ||
| 305 | 13 | Gage[j].nextDate = Gage[j].startDate; | |
| 306 | 13 | Gage[j].nextRainfall = Gage[j].rainfall; | |
| 307 | |||
| 308 | // --- make start of current rain interval the simulation start | ||
| 309 | 13 | Gage[j].startDate = StartDateTime; | |
| 310 | 13 | Gage[j].endDate = Gage[j].nextDate; | |
| 311 | 13 | Gage[j].rainfall = 0.0; | |
| 312 | } | ||
| 313 | |||
| 314 | // --- otherwise find next recorded rainfall | ||
| 315 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 47 times.
|
48 | else if ( !getNextRainfall(j) ) Gage[j].nextDate = NO_DATE; |
| 316 | } | ||
| 317 | ✗ | else Gage[j].startDate = NO_DATE; | |
| 318 | 61 | initPastRain(j); | |
| 319 | } | ||
| 320 | |||
| 321 | //============================================================================= | ||
| 322 | |||
| 323 | 37574 | void gage_setState(int j, DateTime t) | |
| 324 | // | ||
| 325 | // Input: j = rain gage index | ||
| 326 | // t = a calendar date/time | ||
| 327 | // Output: none | ||
| 328 | // Purpose: updates state of rain gage for specified date. | ||
| 329 | // | ||
| 330 | { | ||
| 331 | // --- return if gage not used by any subcatchment | ||
| 332 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 37574 times.
|
37574 | if ( Gage[j].isUsed == FALSE ) return; |
| 333 | |||
| 334 | // --- set rainfall to zero if disabled | ||
| 335 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 37574 times.
|
37574 | if ( IgnoreRainfall ) |
| 336 | { | ||
| 337 | ✗ | Gage[j].rainfall = 0.0; | |
| 338 | ✗ | return; | |
| 339 | } | ||
| 340 | |||
| 341 | // --- use rainfall from co-gage (gage with lower index that uses | ||
| 342 | // same rainfall time series or file) if it exists | ||
| 343 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 37574 times.
|
37574 | if ( Gage[j].coGage >= 0) |
| 344 | { | ||
| 345 | ✗ | Gage[j].rainfall = Gage[Gage[j].coGage].rainfall; | |
| 346 | ✗ | return; | |
| 347 | } | ||
| 348 | |||
| 349 | // --- use rainfall supplied by API function call | ||
| 350 | // (where constant ZERO (1.e-10) is used for 0 rainfall) | ||
| 351 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 37574 times.
|
37574 | if (Gage[j].apiRainfall != MISSING) |
| 352 | { | ||
| 353 | ✗ | Gage[j].rainfall = Gage[j].apiRainfall; | |
| 354 | ✗ | return; | |
| 355 | } | ||
| 356 | |||
| 357 | // --- otherwise march through rainfall record until date t is bracketed | ||
| 358 | 37574 | t += OneSecond; | |
| 359 | for (;;) | ||
| 360 | { | ||
| 361 | // --- no rainfall if no interval start date | ||
| 362 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 126345 times.
|
126345 | if ( Gage[j].startDate == NO_DATE ) |
| 363 | { | ||
| 364 | ✗ | Gage[j].rainfall = 0.0; | |
| 365 | ✗ | return; | |
| 366 | } | ||
| 367 | |||
| 368 | // --- no rainfall if time is before interval start date | ||
| 369 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 126345 times.
|
126345 | if ( t < Gage[j].startDate ) |
| 370 | { | ||
| 371 | ✗ | Gage[j].rainfall = 0.0; | |
| 372 | ✗ | return; | |
| 373 | } | ||
| 374 | |||
| 375 | // --- use current rainfall if time is before interval end date | ||
| 376 |
2/2✓ Branch 0 taken 5802 times.
✓ Branch 1 taken 120543 times.
|
126345 | if ( t < Gage[j].endDate ) |
| 377 | { | ||
| 378 | 5802 | return; | |
| 379 | } | ||
| 380 | |||
| 381 | // --- no rainfall if t >= interval end date & no next interval exists | ||
| 382 |
2/2✓ Branch 0 taken 13851 times.
✓ Branch 1 taken 106692 times.
|
120543 | if ( Gage[j].nextDate == NO_DATE ) |
| 383 | { | ||
| 384 | 13851 | Gage[j].rainfall = 0.0; | |
| 385 | 13851 | return; | |
| 386 | } | ||
| 387 | |||
| 388 | // --- no rainfall if t > interval end date & < next interval date | ||
| 389 |
2/2✓ Branch 0 taken 17921 times.
✓ Branch 1 taken 88771 times.
|
106692 | if ( t < Gage[j].nextDate ) |
| 390 | { | ||
| 391 | 17921 | Gage[j].rainfall = 0.0; | |
| 392 | 17921 | return; | |
| 393 | } | ||
| 394 | |||
| 395 | // --- otherwise update next rainfall interval date | ||
| 396 | 88771 | Gage[j].startDate = Gage[j].nextDate; | |
| 397 | 177542 | Gage[j].endDate = datetime_addSeconds(Gage[j].startDate, | |
| 398 | 88771 | Gage[j].rainInterval); | |
| 399 | 88771 | Gage[j].rainfall = Gage[j].nextRainfall; | |
| 400 |
2/2✓ Branch 1 taken 35 times.
✓ Branch 2 taken 88736 times.
|
88771 | if ( !getNextRainfall(j) ) Gage[j].nextDate = NO_DATE; |
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | //============================================================================= | ||
| 405 | |||
| 406 | 61 | void initPastRain(int j) | |
| 407 | { | ||
| 408 | // --- initialize past hourly rain accumulation | ||
| 409 | int i; | ||
| 410 |
2/2✓ Branch 0 taken 2989 times.
✓ Branch 1 taken 61 times.
|
3050 | for (i = 0; i <= MAXPASTRAIN; i++) |
| 411 | 2989 | Gage[j].pastRain[i] = 0.0; | |
| 412 | 61 | Gage[j].pastInterval = 0; | |
| 413 | 61 | } | |
| 414 | |||
| 415 | //============================================================================= | ||
| 416 | |||
| 417 | 33826 | void gage_updatePastRain(int j, int tStep) | |
| 418 | // | ||
| 419 | // Input: j = rain gage index | ||
| 420 | // tStep = current runoff time step (sec) | ||
| 421 | // Output: none | ||
| 422 | // Purpose: updates past MAXPASTRAIN hourly rain totals. | ||
| 423 | // | ||
| 424 | // Note: pastRain[0] is past rain volume prior to 1 hour, | ||
| 425 | // pastRain[n] is past rain volume after n hours, | ||
| 426 | // pastInterval is time since last hour was reached. | ||
| 427 | { | ||
| 428 | int i, t; | ||
| 429 | double r; | ||
| 430 | |||
| 431 | // --- current rainfall intensity (in/sec or mm/sec) | ||
| 432 | 33826 | r = Gage[j].rainfall / 3600.; | |
| 433 | |||
| 434 | // --- process each hourly interval of current time step | ||
| 435 |
2/2✓ Branch 0 taken 38293 times.
✓ Branch 1 taken 33826 times.
|
72119 | while (tStep > 0) |
| 436 | { | ||
| 437 | // --- time for most recent rainfall interval to reach 1 hr | ||
| 438 | 38293 | t = 3600 - Gage[j].pastInterval; | |
| 439 | |||
| 440 | // --- remaining time step is greater than this time | ||
| 441 |
2/2✓ Branch 0 taken 4467 times.
✓ Branch 1 taken 33826 times.
|
38293 | if (tStep > t) |
| 442 | { | ||
| 443 | // --- add current rain to most recent interval | ||
| 444 | 4467 | Gage[j].pastRain[0] += t * r; | |
| 445 | |||
| 446 | // --- shift all prior hourly rain amounts by 1 hour | ||
| 447 |
2/2✓ Branch 0 taken 214416 times.
✓ Branch 1 taken 4467 times.
|
218883 | for (i = MAXPASTRAIN; i > 0; i-- ) |
| 448 | 214416 | Gage[j].pastRain[i] = Gage[j].pastRain[i-1]; | |
| 449 | |||
| 450 | // --- begin a new most recent interval | ||
| 451 | 4467 | Gage[j].pastInterval = 0; | |
| 452 | 4467 | Gage[j].pastRain[0] = 0.0; | |
| 453 | 4467 | tStep -= t; | |
| 454 | } | ||
| 455 | // --- time to reach 1 hr in most recent interval is greater | ||
| 456 | // than remaining time step so update most recent interval | ||
| 457 | else | ||
| 458 | { | ||
| 459 | 33826 | Gage[j].pastRain[0] += tStep * r; | |
| 460 | 33826 | Gage[j].pastInterval += tStep; | |
| 461 | 33826 | tStep = 0; | |
| 462 | } | ||
| 463 | } | ||
| 464 | 33826 | } | |
| 465 | |||
| 466 | //============================================================================= | ||
| 467 | |||
| 468 | 240 | double gage_getPastRain(int j, int n) | |
| 469 | // | ||
| 470 | // Input: j = rain gage index | ||
| 471 | // n = number of hours prior to current date | ||
| 472 | // Output: cumulative rain volume (inches or mm) in last n hours | ||
| 473 | // Purpose: retrieves rainfall total over some previous number of hours. | ||
| 474 | // | ||
| 475 | { | ||
| 476 | int i; | ||
| 477 | 240 | double result = 0.0; | |
| 478 |
2/4✓ Branch 0 taken 240 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 240 times.
|
240 | if (n < 1 || n > MAXPASTRAIN) return 0.0; |
| 479 |
2/2✓ Branch 0 taken 240 times.
✓ Branch 1 taken 240 times.
|
480 | for (i = 1; i <= n; i++) |
| 480 | 240 | result += Gage[j].pastRain[i]; | |
| 481 | 240 | return result; | |
| 482 | } | ||
| 483 | |||
| 484 | //============================================================================= | ||
| 485 | |||
| 486 | 33826 | DateTime gage_getNextRainDate(int j, DateTime aDate) | |
| 487 | // | ||
| 488 | // Input: j = rain gage index | ||
| 489 | // aDate = calendar date/time | ||
| 490 | // Output: next date with rainfall occurring | ||
| 491 | // Purpose: finds the next date from specified date when rainfall occurs. | ||
| 492 | // | ||
| 493 | { | ||
| 494 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33826 times.
|
33826 | if ( Gage[j].isUsed == FALSE ) return aDate; |
| 495 | 33826 | aDate += OneSecond; | |
| 496 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33826 times.
|
33826 | if ( aDate < Gage[j].startDate ) return Gage[j].startDate; |
| 497 |
2/2✓ Branch 0 taken 5125 times.
✓ Branch 1 taken 28701 times.
|
33826 | if ( aDate < Gage[j].endDate ) return Gage[j].endDate; |
| 498 | 28701 | return Gage[j].nextDate; | |
| 499 | } | ||
| 500 | |||
| 501 | //============================================================================= | ||
| 502 | |||
| 503 | 6344757 | double gage_getPrecip(int j, double *rainfall, double *snowfall) | |
| 504 | // | ||
| 505 | // Input: j = rain gage index | ||
| 506 | // Output: rainfall = rainfall rate (ft/sec) | ||
| 507 | // snowfall = snow fall rate (ft/sec) | ||
| 508 | // returns total precipitation (ft/sec) | ||
| 509 | // Purpose: determines whether gage's recorded rainfall is rain or snow. | ||
| 510 | // | ||
| 511 | { | ||
| 512 | 6344757 | *rainfall = 0.0; | |
| 513 | 6344757 | *snowfall = 0.0; | |
| 514 |
4/4✓ Branch 0 taken 6230126 times.
✓ Branch 1 taken 114631 times.
✓ Branch 2 taken 3544782 times.
✓ Branch 3 taken 2685344 times.
|
6344757 | if ( !IgnoreSnowmelt && Temp.ta <= Snow.snotmp ) |
| 515 | { | ||
| 516 | 3544782 | *snowfall = Gage[j].rainfall * Gage[j].snowFactor / UCF(RAINFALL); | |
| 517 | } | ||
| 518 | 2799975 | else *rainfall = Gage[j].rainfall / UCF(RAINFALL); | |
| 519 | 6344757 | return (*rainfall) + (*snowfall); | |
| 520 | } | ||
| 521 | |||
| 522 | //============================================================================= | ||
| 523 | |||
| 524 | 15515 | void gage_setReportRainfall(int j, DateTime reportDate) | |
| 525 | // | ||
| 526 | // Input: j = rain gage index | ||
| 527 | // reportDate = date/time value of current reporting time | ||
| 528 | // Output: none | ||
| 529 | // Purpose: sets the rainfall value reported at the current reporting time. | ||
| 530 | // | ||
| 531 | { | ||
| 532 | double result; | ||
| 533 | |||
| 534 | // --- use value from co-gage if it exists | ||
| 535 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15515 times.
|
15515 | if ( Gage[j].coGage >= 0) |
| 536 | { | ||
| 537 | ✗ | Gage[j].reportRainfall = Gage[Gage[j].coGage].reportRainfall; | |
| 538 | ✗ | return; | |
| 539 | } | ||
| 540 | |||
| 541 | // --- rainfall set by API call | ||
| 542 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15515 times.
|
15515 | if (Gage[j].apiRainfall != MISSING) |
| 543 | { | ||
| 544 | ✗ | Gage[j].reportRainfall = Gage[j].apiRainfall; | |
| 545 | ✗ | return; | |
| 546 | } | ||
| 547 | |||
| 548 | // --- otherwise increase reporting time by 1 second to avoid | ||
| 549 | // roundoff problems | ||
| 550 | 15515 | reportDate += OneSecond; | |
| 551 | |||
| 552 | // --- use current rainfall if report date/time is before end | ||
| 553 | // of current rain interval | ||
| 554 |
2/2✓ Branch 0 taken 2934 times.
✓ Branch 1 taken 12581 times.
|
15515 | if ( reportDate < Gage[j].endDate ) result = Gage[j].rainfall; |
| 555 | |||
| 556 | // --- use 0.0 if report date/time is before start of next rain interval | ||
| 557 |
2/2✓ Branch 0 taken 8292 times.
✓ Branch 1 taken 4289 times.
|
12581 | else if ( reportDate < Gage[j].nextDate ) result = 0.0; |
| 558 | |||
| 559 | // --- otherwise report date/time falls right on end of current rain | ||
| 560 | // interval and start of next interval so use next interval's rainfall | ||
| 561 | 4289 | else result = Gage[j].nextRainfall; | |
| 562 | 15515 | Gage[j].reportRainfall = result; | |
| 563 | } | ||
| 564 | |||
| 565 | //============================================================================= | ||
| 566 | |||
| 567 | 61 | int getFirstRainfall(int j) | |
| 568 | // | ||
| 569 | // Input: j = rain gage index | ||
| 570 | // Output: returns TRUE if successful | ||
| 571 | // Purpose: positions rainfall record to date with first rainfall. | ||
| 572 | // | ||
| 573 | { | ||
| 574 | int k; // time series index | ||
| 575 | float vFirst; // first rain volume (ft or m) | ||
| 576 | double rFirst; // first rain intensity (in/hr or mm/hr) | ||
| 577 | |||
| 578 | // --- assign default values to date & rainfall | ||
| 579 | 61 | Gage[j].startDate = NO_DATE; | |
| 580 | 61 | Gage[j].rainfall = 0.0; | |
| 581 | |||
| 582 | // --- initialize internal cumulative rainfall value | ||
| 583 | 61 | Gage[j].rainAccum = 0; | |
| 584 | |||
| 585 | // --- use rain interface file if applicable | ||
| 586 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 48 times.
|
61 | if ( Gage[j].dataSource == RAIN_FILE ) |
| 587 | { | ||
| 588 |
2/4✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
|
13 | if ( Frain.file && Gage[j].endFilePos > Gage[j].startFilePos ) |
| 589 | { | ||
| 590 | // --- retrieve 1st date & rainfall volume from file | ||
| 591 | 13 | fseek(Frain.file, Gage[j].startFilePos, SEEK_SET); | |
| 592 | 13 | fread(&Gage[j].startDate, sizeof(DateTime), 1, Frain.file); | |
| 593 | 13 | fread(&vFirst, sizeof(float), 1, Frain.file); | |
| 594 | 13 | Gage[j].currentFilePos = ftell(Frain.file); | |
| 595 | |||
| 596 | // --- convert rainfall to intensity | ||
| 597 | 13 | Gage[j].rainfall = convertRainfall(j, (double)vFirst); | |
| 598 | 13 | return 1; | |
| 599 | } | ||
| 600 | ✗ | return 0; | |
| 601 | } | ||
| 602 | |||
| 603 | // --- otherwise access user-supplied rainfall time series | ||
| 604 | else | ||
| 605 | { | ||
| 606 | 48 | k = Gage[j].tSeries; | |
| 607 |
1/2✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
|
48 | if ( k >= 0 ) |
| 608 | { | ||
| 609 | // --- retrieve first rainfall value from time series | ||
| 610 |
1/2✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
|
48 | if ( table_getFirstEntry(&Tseries[k], &Gage[j].startDate, |
| 611 | &rFirst) ) | ||
| 612 | { | ||
| 613 | // --- convert rainfall to intensity | ||
| 614 | 48 | Gage[j].rainfall = convertRainfall(j, rFirst); | |
| 615 | 48 | return 1; | |
| 616 | } | ||
| 617 | } | ||
| 618 | ✗ | return 0; | |
| 619 | } | ||
| 620 | } | ||
| 621 | |||
| 622 | //============================================================================= | ||
| 623 | |||
| 624 | 88819 | int getNextRainfall(int j) | |
| 625 | // | ||
| 626 | // Input: j = rain gage index | ||
| 627 | // Output: returns 1 if successful; 0 if not | ||
| 628 | // Purpose: positions rainfall record to date with next non-zero rainfall | ||
| 629 | // while updating the gage's next rain intensity value. | ||
| 630 | // | ||
| 631 | // Note: zero rainfall values explicitly entered into a rain file or | ||
| 632 | // time series are skipped over so that a proper accounting of | ||
| 633 | // wet and dry periods can be maintained. | ||
| 634 | // | ||
| 635 | { | ||
| 636 | int k; // time series index | ||
| 637 | float vNext; // next rain volume (ft or m) | ||
| 638 | double rNext; // next rain intensity (in/hr or mm/hr) | ||
| 639 | |||
| 640 | 88819 | Gage[j].nextRainfall = 0.0; | |
| 641 | do | ||
| 642 | { | ||
| 643 |
2/2✓ Branch 0 taken 83255 times.
✓ Branch 1 taken 5649 times.
|
88904 | if ( Gage[j].dataSource == RAIN_FILE ) |
| 644 | { | ||
| 645 |
3/4✓ Branch 0 taken 83255 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 83246 times.
✓ Branch 3 taken 9 times.
|
83255 | if ( Frain.file && Gage[j].currentFilePos < Gage[j].endFilePos ) |
| 646 | { | ||
| 647 | 83246 | fseek(Frain.file, Gage[j].currentFilePos, SEEK_SET); | |
| 648 | 83246 | fread(&Gage[j].nextDate, sizeof(DateTime), 1, Frain.file); | |
| 649 | 83246 | fread(&vNext, sizeof(float), 1, Frain.file); | |
| 650 | 83246 | Gage[j].currentFilePos = ftell(Frain.file); | |
| 651 | 83246 | rNext = convertRainfall(j, (double)vNext); | |
| 652 | } | ||
| 653 | 9 | else return 0; | |
| 654 | } | ||
| 655 | |||
| 656 | else | ||
| 657 | { | ||
| 658 | 5649 | k = Gage[j].tSeries; | |
| 659 |
1/2✓ Branch 0 taken 5649 times.
✗ Branch 1 not taken.
|
5649 | if ( k >= 0 ) |
| 660 | { | ||
| 661 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 5622 times.
|
5649 | if ( !table_getNextEntry(&Tseries[k], |
| 662 | 5676 | &Gage[j].nextDate, &rNext) ) return 0; | |
| 663 | 5622 | rNext = convertRainfall(j, rNext); | |
| 664 | } | ||
| 665 | ✗ | else return 0; | |
| 666 | } | ||
| 667 |
2/2✓ Branch 0 taken 85 times.
✓ Branch 1 taken 88783 times.
|
88868 | } while (rNext == 0.0); |
| 668 | 88783 | Gage[j].nextRainfall = rNext; | |
| 669 | 88783 | return 1; | |
| 670 | } | ||
| 671 | |||
| 672 | //============================================================================= | ||
| 673 | |||
| 674 | 88929 | double convertRainfall(int j, double r) | |
| 675 | // | ||
| 676 | // Input: j = rain gage index | ||
| 677 | // r = rainfall value (user units) | ||
| 678 | // Output: returns rainfall intensity (user units) | ||
| 679 | // Purpose: converts rainfall value to an intensity (depth per hour). | ||
| 680 | // | ||
| 681 | { | ||
| 682 | double r1; | ||
| 683 |
3/4✓ Branch 0 taken 505 times.
✓ Branch 1 taken 88399 times.
✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
|
88929 | switch ( Gage[j].rainType ) |
| 684 | { | ||
| 685 | 505 | case RAINFALL_INTENSITY: | |
| 686 | 505 | r1 = r; | |
| 687 | 505 | break; | |
| 688 | |||
| 689 | 88399 | case RAINFALL_VOLUME: | |
| 690 | 88399 | r1 = r / Gage[j].rainInterval * 3600.0; | |
| 691 | 88399 | break; | |
| 692 | |||
| 693 | 25 | case CUMULATIVE_RAINFALL: | |
| 694 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
|
25 | if ( r < Gage[j].rainAccum ) |
| 695 | ✗ | r1 = r / Gage[j].rainInterval * 3600.0; | |
| 696 | 25 | else r1 = (r - Gage[j].rainAccum) / Gage[j].rainInterval * 3600.0; | |
| 697 | 25 | Gage[j].rainAccum = r; | |
| 698 | 25 | break; | |
| 699 | |||
| 700 | ✗ | default: r1 = r; | |
| 701 | } | ||
| 702 | 88929 | return r1 * Gage[j].unitsFactor * Adjust.rainFactor; | |
| 703 | } | ||
| 704 | |||
| 705 | //============================================================================= | ||
| 706 |