GCC Code Coverage Report


Directory: src/solver/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 86.2% 219 / 0 / 254
Functions: 100.0% 15 / 0 / 15
Branches: 66.9% 111 / 0 / 166

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