GCC Code Coverage Report


Directory: src/solver/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 88.8% 245 / 0 / 276
Functions: 92.3% 12 / 0 / 13
Branches: 63.9% 149 / 0 / 233

landuse.c
Line Branch Exec Source
1 //-----------------------------------------------------------------------------
2 // landuse.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 // Pollutant buildup and washoff functions.
10 //
11 // Update History
12 // ==============
13 // Build 5.1.008:
14 // - landuse_getWashoffMass() re-named to landuse_getWashoffQual() and
15 // modified to return concentration instead of mass load.
16 // - landuse_getRunoffLoad() re-named to landuse_getWashoffLoad() and
17 // modified to work with landuse_getWashoffQual().
18 //-----------------------------------------------------------------------------
19 #define _CRT_SECURE_NO_DEPRECATE
20
21 #include <math.h>
22 #include <string.h>
23 #include "headers.h"
24
25 //-----------------------------------------------------------------------------
26 // External functions (declared in funcs.h)
27 //-----------------------------------------------------------------------------
28 // landuse_readParams (called by parseLine in input.c)
29 // landuse_readPollutParams (called by parseLine in input.c)
30 // landuse_readBuildupParams (called by parseLine in input.c)
31 // landuse_readWashoffParams (called by parseLine in input.c)
32
33 // landuse_getInitBuildup (called by subcatch_initState)
34 // landuse_getBuildup (called by surfqual_getBuildup)
35 // landuse_getWashoffLoad (called by surfqual_getWashoff)
36 // landuse_getCoPollutLoad (called by surfqual_getwashoff));
37 // landuse_getAvgBMPEffic (called by updatePondedQual in surfqual.c)
38
39 //-----------------------------------------------------------------------------
40 // Function declarations
41 //-----------------------------------------------------------------------------
42 static double landuse_getBuildupDays(int landuse, int pollut, double buildup);
43 static double landuse_getBuildupMass(int landuse, int pollut, double days);
44 static double landuse_getWashoffQual(int landuse, int pollut, double buildup,
45 double runoff, double area);
46 static double landuse_getExternalBuildup(int i, int p, double buildup,
47 double tStep);
48
49 //=============================================================================
50
51 10 int landuse_readParams(int j, char* tok[], int ntoks)
52 //
53 // Input: j = land use index
54 // tok[] = array of string tokens
55 // ntoks = number of tokens
56 // Output: returns an error code
57 // Purpose: reads landuse parameters from a tokenized line of input.
58 //
59 // Data format is:
60 // landuseID (sweepInterval sweepRemoval sweepDays0)
61 //
62 {
63 char *id;
64
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if ( ntoks < 1 ) return error_setInpError(ERR_ITEMS, "");
65 10 id = project_findID(LANDUSE, tok[0]);
66
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if ( id == NULL ) return error_setInpError(ERR_NAME, tok[0]);
67 10 Landuse[j].ID = id;
68
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 if ( ntoks > 1 )
69 {
70
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if ( ntoks < 4 ) return error_setInpError(ERR_ITEMS, "");
71
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if ( ! getDouble(tok[1], &Landuse[j].sweepInterval) )
72 return error_setInpError(ERR_NUMBER, tok[1]);
73
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if ( ! getDouble(tok[2], &Landuse[j].sweepRemoval) )
74 return error_setInpError(ERR_NUMBER, tok[2]);
75
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if ( ! getDouble(tok[3], &Landuse[j].sweepDays0) )
76 return error_setInpError(ERR_NUMBER, tok[3]);
77 }
78 else
79 {
80 4 Landuse[j].sweepInterval = 0.0;
81 4 Landuse[j].sweepRemoval = 0.0;
82 4 Landuse[j].sweepDays0 = 0.0;
83 }
84
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if ( Landuse[j].sweepRemoval < 0.0
85
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 || Landuse[j].sweepRemoval > 1.0 )
86 return error_setInpError(ERR_NUMBER, tok[2]);
87 10 return 0;
88 }
89
90 //=============================================================================
91
92 47 int landuse_readPollutParams(int j, char* tok[], int ntoks)
93 //
94 // Input: j = pollutant index
95 // tok[] = array of string tokens
96 // ntoks = number of tokens
97 // Output: returns an error code
98 // Purpose: reads pollutant parameters from a tokenized line of input.
99 //
100 // Data format is:
101 // ID Units cRain cGW cRDII kDecay (snowOnly coPollut coFrac cDWF cInit)
102 //
103 {
104 int i, k, coPollut, snowFlag;
105 double x[4], coFrac, cDWF, cInit;
106 char *id;
107
108 // --- extract pollutant name & units
109
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
47 if ( ntoks < 6 ) return error_setInpError(ERR_ITEMS, "");
110 47 id = project_findID(POLLUT, tok[0]);
111
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
47 if ( id == NULL ) return error_setInpError(ERR_NAME, tok[0]);
112 47 k = findmatch(tok[1], QualUnitsWords);
113
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
47 if ( k < 0 ) return error_setInpError(ERR_KEYWORD, tok[1]);
114
115 // --- extract concen. in rain, gwater, & I&I
116
2/2
✓ Branch 0 taken 141 times.
✓ Branch 1 taken 47 times.
188 for ( i = 2; i <= 4; i++ )
117 {
118
2/4
✓ Branch 1 taken 141 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 141 times.
141 if ( ! getDouble(tok[i], &x[i-2]) || x[i-2] < 0.0 )
119 {
120 return error_setInpError(ERR_NUMBER, tok[i]);
121 }
122 }
123
124 // --- extract decay coeff. (which can be negative for growth)
125
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 47 times.
47 if ( ! getDouble(tok[5], &x[3]) )
126 {
127 return error_setInpError(ERR_NUMBER, tok[5]);
128 }
129
130 // --- set defaults for snow only flag & co-pollut. parameters
131 47 snowFlag = 0;
132 47 coPollut = -1;
133 47 coFrac = 0.0;
134 47 cDWF = 0.0;
135 47 cInit = 0.0;
136
137 // --- check for snow only flag
138
1/2
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
47 if ( ntoks >= 7 )
139 {
140 47 snowFlag = findmatch(tok[6], NoYesWords);
141
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
47 if ( snowFlag < 0 ) return error_setInpError(ERR_KEYWORD, tok[6]);
142 }
143
144 // --- check for co-pollutant
145
1/2
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
47 if ( ntoks >= 9 )
146 {
147
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 44 times.
47 if ( !strcomp(tok[7], "*") )
148 {
149 3 coPollut = project_findObject(POLLUT, tok[7]);
150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if ( coPollut < 0 ) return error_setInpError(ERR_NAME, tok[7]);
151
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 if ( ! getDouble(tok[8], &coFrac) || coFrac < 0.0 )
152 return error_setInpError(ERR_NUMBER, tok[8]);
153 }
154 }
155
156 // --- check for DWF concen.
157
1/2
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
47 if ( ntoks >= 10 )
158 {
159
2/4
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 47 times.
47 if ( ! getDouble(tok[9], &cDWF) || cDWF < 0.0)
160 return error_setInpError(ERR_NUMBER, tok[9]);
161 }
162
163 // --- check for initial concen.
164
1/2
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
47 if ( ntoks >= 11 )
165 {
166
2/4
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 47 times.
47 if ( ! getDouble(tok[10], &cInit) || cInit < 0.0 )
167 return error_setInpError(ERR_NUMBER, tok[9]);
168 }
169
170 // --- save values for pollutant object
171 47 Pollut[j].ID = id;
172 47 Pollut[j].units = k;
173
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 2 times.
47 if ( Pollut[j].units == MG ) Pollut[j].mcf = UCF(MASS);
174
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 else if ( Pollut[j].units == UG ) Pollut[j].mcf = UCF(MASS) / 1000.0;
175 else Pollut[j].mcf = 1.0;
176 47 Pollut[j].pptConcen = x[0];
177 47 Pollut[j].gwConcen = x[1];
178 47 Pollut[j].rdiiConcen = x[2];
179 47 Pollut[j].kDecay = x[3]/SECperDAY;
180 47 Pollut[j].snowOnly = snowFlag;
181 47 Pollut[j].coPollut = coPollut;
182 47 Pollut[j].coFraction = coFrac;
183 47 Pollut[j].dwfConcen = cDWF;
184 47 Pollut[j].initConcen = cInit;
185 47 return 0;
186 }
187
188 //=============================================================================
189
190 25 int landuse_readBuildupParams(char* tok[], int ntoks)
191 //
192 // Input: tok[] = array of string tokens
193 // ntoks = number of tokens
194 // Output: returns an error code
195 // Purpose: reads pollutant buildup parameters from a tokenized line of input.
196 //
197 // Data format is:
198 // landuseID pollutID buildupType c1 c2 c3 normalizerType
199 //
200 {
201 int i, j, k, n, p;
202 25 double c[3] = {0, 0, 0}, tmax;
203
204
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 if ( ntoks < 3 ) return 0;
205 25 j = project_findObject(LANDUSE, tok[0]);
206
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 if ( j < 0 ) return error_setInpError(ERR_NAME, tok[0]);
207 25 p = project_findObject(POLLUT, tok[1]);
208
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 if ( p < 0 ) return error_setInpError(ERR_NAME, tok[1]);
209 25 k = findmatch(tok[2], BuildupTypeWords);
210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 if ( k < 0 ) return error_setInpError(ERR_KEYWORD, tok[2]);
211 25 Landuse[j].buildupFunc[p].funcType = k;
212
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 8 times.
25 if ( k > NO_BUILDUP )
213 {
214
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if ( ntoks < 7 ) return error_setInpError(ERR_ITEMS, "");
215
4/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 48 times.
✓ Branch 3 taken 16 times.
65 if ( k != EXTERNAL_BUILDUP ) for (i=0; i<3; i++)
216 {
217
2/4
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 48 times.
48 if ( ! getDouble(tok[i+3], &c[i]) || c[i] < 0.0 )
218 {
219 return error_setInpError(ERR_NUMBER, tok[i+3]);
220 }
221 }
222 17 n = findmatch(tok[6], NormalizerWords);
223
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (n < 0 ) return error_setInpError(ERR_KEYWORD, tok[6]);
224 17 Landuse[j].buildupFunc[p].normalizer = n;
225 }
226
227 // Find time until max. buildup (or time series for external buildup)
228
5/5
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 8 times.
25 switch (Landuse[j].buildupFunc[p].funcType)
229 {
230 11 case POWER_BUILDUP:
231 // --- check for too small or large an exponent
232
3/6
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 11 times.
11 if ( c[2] > 0.0 && (c[2] < 0.01 || c[2] > 10.0) )
233 return error_setInpError(ERR_KEYWORD, tok[5]);
234
235 // --- find time to reach max. buildup
236 // --- use zero if coeffs. are 0
237
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if ( c[1]*c[2] == 0.0 ) tmax = 0.0;
238
239 // --- use 10 years if inverse power function tends to blow up
240
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 8 times.
11 else if ( log10(c[0]) / c[2] > 3.5 ) tmax = 3650.0;
241
242 // --- otherwise use inverse power function
243 8 else tmax = pow(c[0]/c[1], 1.0/c[2]);
244 11 break;
245
246 1 case EXPON_BUILDUP:
247
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if ( c[1] == 0.0 ) tmax = 0.0;
248 1 else tmax = -log(0.001)/c[1];
249 1 break;
250
251 4 case SATUR_BUILDUP:
252 4 tmax = 1000.0*c[2];
253 4 break;
254
255 1 case EXTERNAL_BUILDUP:
256
2/4
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
1 if ( !getDouble(tok[3], &c[0]) || c[0] < 0.0 ) //max. buildup
257 return error_setInpError(ERR_NUMBER, tok[3]);
258
2/4
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
1 if ( !getDouble(tok[4], &c[1]) || c[1] < 0.0 ) //scaling factor
259 return error_setInpError(ERR_NUMBER, tok[3]);
260 1 n = project_findObject(TSERIES, tok[5]); //time series
261
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if ( n < 0 ) return error_setInpError(ERR_NAME, tok[4]);
262 1 Tseries[n].refersTo = EXTERNAL_BUILDUP;
263 1 c[2] = n;
264 1 tmax = 0.0;
265 1 break;
266
267 8 default:
268 8 tmax = 0.0;
269 }
270
271 // Assign parameters to buildup object
272 25 Landuse[j].buildupFunc[p].coeff[0] = c[0];
273 25 Landuse[j].buildupFunc[p].coeff[1] = c[1];
274 25 Landuse[j].buildupFunc[p].coeff[2] = c[2];
275 25 Landuse[j].buildupFunc[p].maxDays = tmax;
276 25 return 0;
277 }
278
279 //=============================================================================
280
281 25 int landuse_readWashoffParams(char* tok[], int ntoks)
282 //
283 // Input: tok[] = array of string tokens
284 // ntoks = number of tokens
285 // Output: returns an error code
286 // Purpose: reads pollutant washoff parameters from a tokenized line of input.
287 //
288 // Data format is:
289 // landuseID pollutID washoffType c1 c2 sweepEffic bmpRemoval
290 {
291 int i, j, p;
292 int func;
293 double x[4];
294
295
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 if ( ntoks < 3 ) return 0;
296
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 25 times.
125 for (i=0; i<4; i++) x[i] = 0.0;
297 25 func = NO_WASHOFF;
298 25 j = project_findObject(LANDUSE, tok[0]);
299
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 if ( j < 0 ) return error_setInpError(ERR_NAME, tok[0]);
300 25 p = project_findObject(POLLUT, tok[1]);
301
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 if ( p < 0 ) return error_setInpError(ERR_NAME, tok[1]);
302
1/2
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
25 if ( ntoks > 2 )
303 {
304 25 func = findmatch(tok[2], WashoffTypeWords);
305
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 if ( func < 0 ) return error_setInpError(ERR_KEYWORD, tok[2]);
306
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 3 times.
25 if ( func != NO_WASHOFF )
307 {
308
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if ( ntoks < 5 ) return error_setInpError(ERR_ITEMS, "");
309
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
22 if ( ! getDouble(tok[3], &x[0]) )
310 return error_setInpError(ERR_NUMBER, tok[3]);
311
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
22 if ( ! getDouble(tok[4], &x[1]) )
312 return error_setInpError(ERR_NUMBER, tok[4]);
313
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 if ( ntoks >= 6 )
314 {
315
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
22 if ( ! getDouble(tok[5], &x[2]) )
316 return error_setInpError(ERR_NUMBER, tok[5]);
317 }
318
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 if ( ntoks >= 7 )
319 {
320
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
22 if ( ! getDouble(tok[6], &x[3]) )
321 return error_setInpError(ERR_NUMBER, tok[6]);
322 }
323 }
324 }
325
326 // --- check for valid parameter values
327 // x[0] = washoff coeff.
328 // x[1] = washoff expon.
329 // x[2] = sweep effic.
330 // x[3] = BMP effic.
331
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 if ( x[0] < 0.0 ) return error_setInpError(ERR_NUMBER, tok[3]);
332
2/4
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 25 times.
25 if ( x[1] < -10.0 || x[1] > 10.0 )
333 return error_setInpError(ERR_NUMBER, tok[4]);;
334
2/4
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 25 times.
25 if ( x[2] < 0.0 || x[2] > 100.0 )
335 return error_setInpError(ERR_NUMBER, tok[5]);
336
2/4
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 25 times.
25 if ( x[3] < 0.0 || x[3] > 100.0 )
337 return error_setInpError(ERR_NUMBER, tok[6]);
338
339 // --- convert units of washoff coeff.
340
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 9 times.
25 if ( func == EXPON_WASHOFF ) x[0] /= 3600.0;
341
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 24 times.
25 if ( func == RATING_WASHOFF ) x[0] *= pow(UCF(FLOW), x[1]);
342
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 20 times.
25 if ( func == EMC_WASHOFF ) x[0] *= LperFT3;
343
344 // --- assign washoff parameters to washoff object
345 25 Landuse[j].washoffFunc[p].funcType = func;
346 25 Landuse[j].washoffFunc[p].coeff = x[0];
347 25 Landuse[j].washoffFunc[p].expon = x[1];
348 25 Landuse[j].washoffFunc[p].sweepEffic = x[2] / 100.0;
349 25 Landuse[j].washoffFunc[p].bmpEffic = x[3] / 100.0;
350 25 return 0;
351 }
352
353 //=============================================================================
354
355 2393 void landuse_getInitBuildup(TLandFactor* landFactor, double* initBuildup,
356 double area, double curb)
357 //
358 // Input: landFactor = array of land use factors
359 // initBuildup = total initial buildup of each pollutant
360 // area = subcatchment's area (ft2)
361 // curb = subcatchment's curb length (users units)
362 // Output: modifies each land use factor's initial pollutant buildup
363 // Purpose: determines the initial buildup of each pollutant on
364 // each land use for a given subcatchment.
365 //
366 // Notes: Contributions from co-pollutants to initial buildup are not
367 // included since the co-pollutant mechanism only applies to
368 // washoff.
369 //
370 {
371 int i, p;
372 double startDrySeconds; // antecedent dry period (sec)
373 double f; // faction of total land area
374 double fArea; // area of land use (ft2)
375 double fCurb; // curb length of land use
376 double buildup; // pollutant mass buildup
377
378 // --- convert antecedent dry days into seconds
379 2393 startDrySeconds = StartDryDays*SECperDAY;
380
381 // --- examine each land use
382
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 2393 times.
2444 for (i = 0; i < Nobjects[LANDUSE]; i++)
383 {
384 // --- initialize date when last swept
385 51 landFactor[i].lastSwept = StartDateTime - Landuse[i].sweepDays0;
386
387 // --- determine area and curb length covered by land use
388 51 f = landFactor[i].fraction;
389 51 fArea = f * area * UCF(LANDAREA);
390 51 fCurb = f * curb;
391
392 // --- determine buildup of each pollutant
393
2/2
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 51 times.
175 for (p = 0; p < Nobjects[POLLUT]; p++)
394 {
395 // --- if an initial loading was supplied, then use it to
396 // find the starting buildup over the land use
397 124 buildup = 0.0;
398
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
124 if ( initBuildup[p] > 0.0 ) buildup = initBuildup[p] * fArea;
399
400 // --- otherwise use the land use's buildup function to
401 // compute a buildup over the antecedent dry period
402 124 else buildup = landuse_getBuildup(i, p, fArea, fCurb, buildup,
403 startDrySeconds);
404 124 landFactor[i].buildup[p] = buildup;
405 }
406 }
407 2393 }
408
409 //=============================================================================
410
411 162007 double landuse_getBuildup(int i, int p, double area, double curb, double buildup,
412 double tStep)
413 //
414 // Input: i = land use index
415 // p = pollutant index
416 // area = land use area (ac or ha)
417 // curb = land use curb length (users units)
418 // buildup = current pollutant buildup (lbs or kg)
419 // tStep = time increment for buildup (sec)
420 // Output: returns new buildup mass (lbs or kg)
421 // Purpose: computes new pollutant buildup on a landuse after a time increment.
422 //
423 {
424 int n; // normalizer code
425 double days; // accumulated days of buildup
426 double perUnit; // normalizer value (area or curb length)
427
428 // --- return current buildup if no buildup function or time increment
429
3/4
✓ Branch 0 taken 80652 times.
✓ Branch 1 taken 81355 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 80652 times.
162007 if ( Landuse[i].buildupFunc[p].funcType == NO_BUILDUP || tStep == 0.0 )
430 {
431 81355 return buildup;
432 }
433
434 // --- see what buildup is normalized to
435 80652 n = Landuse[i].buildupFunc[p].normalizer;
436 80652 perUnit = 1.0;
437
1/2
✓ Branch 0 taken 80652 times.
✗ Branch 1 not taken.
80652 if ( n == PER_AREA ) perUnit = area;
438
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80652 times.
80652 if ( n == PER_CURB ) perUnit = curb;
439
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 80634 times.
80652 if ( perUnit == 0.0 ) return 0.0;
440
441 // --- buildup determined by loading time series
442
2/2
✓ Branch 0 taken 222 times.
✓ Branch 1 taken 80412 times.
80634 if ( Landuse[i].buildupFunc[p].funcType == EXTERNAL_BUILDUP )
443 {
444 222 return landuse_getExternalBuildup(i, p, buildup/perUnit, tStep) *
445 perUnit;
446 }
447
448 // --- determine equivalent days of current buildup
449 80412 days = landuse_getBuildupDays(i, p, buildup/perUnit);
450
451 // --- compute buildup after adding on time increment
452 80412 days += tStep / SECperDAY;
453 80412 return landuse_getBuildupMass(i, p, days) * perUnit;
454 }
455
456 //=============================================================================
457
458 80412 double landuse_getBuildupDays(int i, int p, double buildup)
459 //
460 // Input: i = land use index
461 // p = pollutant index
462 // buildup = amount of pollutant buildup
463 // Output: returns number of days it takes for buildup to reach a given level
464 // Purpose: finds the number of days corresponding to a pollutant buildup.
465 //
466 {
467 80412 double c0 = Landuse[i].buildupFunc[p].coeff[0];
468 80412 double c1 = Landuse[i].buildupFunc[p].coeff[1];
469 80412 double c2 = Landuse[i].buildupFunc[p].coeff[2];
470
471
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 80326 times.
80412 if ( buildup == 0.0 ) return 0.0;
472
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80326 times.
80326 if ( buildup >= c0 ) return Landuse[i].buildupFunc[p].maxDays;
473
3/4
✓ Branch 0 taken 417 times.
✓ Branch 1 taken 221 times.
✓ Branch 2 taken 79688 times.
✗ Branch 3 not taken.
80326 switch (Landuse[i].buildupFunc[p].funcType)
474 {
475 417 case POWER_BUILDUP:
476
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 417 times.
417 if ( c1*c2 == 0.0 ) return 0.0;
477 417 else return pow( (buildup/c1), (1.0/c2) );
478
479 221 case EXPON_BUILDUP:
480
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 221 times.
221 if ( c0*c1 == 0.0 ) return 0.0;
481 221 else return -log(1. - buildup/c0) / c1;
482
483 79688 case SATUR_BUILDUP:
484
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 79688 times.
79688 if ( c0 == 0.0 ) return 0.0;
485 79688 else return buildup*c2 / (c0 - buildup);
486
487 default:
488 return 0.0;
489 }
490 }
491
492 //=============================================================================
493
494 80412 double landuse_getBuildupMass(int i, int p, double days)
495 //
496 // Input: i = land use index
497 // p = pollutant index
498 // days = time over which buildup has occurred (days)
499 // Output: returns mass of pollutant buildup (lbs or kg per area or curblength)
500 // Purpose: finds amount of buildup of pollutant on a land use.
501 //
502 {
503 double b;
504 80412 double c0 = Landuse[i].buildupFunc[p].coeff[0];
505 80412 double c1 = Landuse[i].buildupFunc[p].coeff[1];
506 80412 double c2 = Landuse[i].buildupFunc[p].coeff[2];
507
508
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80412 times.
80412 if ( days == 0.0 ) return 0.0;
509
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80412 times.
80412 if ( days >= Landuse[i].buildupFunc[p].maxDays ) return c0;
510
3/4
✓ Branch 0 taken 482 times.
✓ Branch 1 taken 222 times.
✓ Branch 2 taken 79708 times.
✗ Branch 3 not taken.
80412 switch (Landuse[i].buildupFunc[p].funcType)
511 {
512 482 case POWER_BUILDUP:
513 482 b = c1 * pow(days, c2);
514
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 482 times.
482 if ( b > c0 ) b = c0;
515 482 break;
516
517 222 case EXPON_BUILDUP:
518 222 b = c0*(1.0 - exp(-days*c1));
519 222 break;
520
521 79708 case SATUR_BUILDUP:
522 79708 b = days*c0/(c2 + days);
523 79708 break;
524
525 default: b = 0.0;
526 }
527 80412 return b;
528 }
529
530 //=============================================================================
531
532 8844804 double landuse_getAvgBmpEffic(int j, int p)
533 //
534 // Input: j = subcatchment index
535 // p = pollutant index
536 // Output: returns a BMP removal fraction for pollutant p
537 // Purpose: finds the overall average BMP removal achieved for pollutant p
538 // treated in subcatchment j.
539 //
540 {
541 int i;
542 8844804 double r = 0.0;
543
2/2
✓ Branch 0 taken 304290 times.
✓ Branch 1 taken 8844804 times.
9149094 for (i = 0; i < Nobjects[LANDUSE]; i++)
544 {
545 304290 r += Subcatch[j].landFactor[i].fraction *
546 304290 Landuse[i].washoffFunc[p].bmpEffic;
547 }
548 8844804 return r;
549 }
550
551 //=============================================================================
552
553 33651 double landuse_getWashoffLoad(int i, int p, double area,
554 TLandFactor landFactor[], double runoff, double vOutflow)
555 //
556 // Input: i = land use index
557 // p = pollut. index
558 // area = sucatchment area (ft2)
559 // landFactor[] = array of land use data for subcatchment
560 // runoff = runoff flow generated by subcatchment (ft/sec)
561 // vOutflow = runoff volume leaving the subcatchment (ft3)
562 // Output: returns pollutant runoff load (mass)
563 // Purpose: computes pollutant load generated by a land use over a time step.
564 //
565 {
566 double landuseArea; // area of current land use (ft2)
567 double buildup; // current pollutant buildup (lb or kg)
568 double washoffQual; // pollutant concentration in washoff (mass/ft3)
569 double washoffLoad; // pollutant washoff load over time step (lb or kg)
570 double bmpRemoval; // pollutant load removed by BMP treatment (lb or kg)
571
572 // --- compute concen. of pollutant in washoff (mass/ft3)
573 33651 buildup = landFactor[i].buildup[p];
574 33651 landuseArea = landFactor[i].fraction * area;
575 33651 washoffQual = landuse_getWashoffQual(i, p, buildup, runoff, landuseArea);
576
577 // --- compute washoff load exported (lbs or kg) from landuse
578 // (Pollut[].mcf converts from mg (or ug) mass units to lbs (or kg)
579 33651 washoffLoad = washoffQual * vOutflow * landuseArea / area * Pollut[p].mcf;
580
581 // --- if buildup modelled, reduce it by amount of washoff
582
3/4
✓ Branch 0 taken 16131 times.
✓ Branch 1 taken 17520 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16131 times.
33651 if ( Landuse[i].buildupFunc[p].funcType != NO_BUILDUP ||
583 buildup > washoffLoad )
584 {
585
2/2
✓ Branch 0 taken 17496 times.
✓ Branch 1 taken 24 times.
17520 washoffLoad = MIN(washoffLoad, buildup);
586 17520 buildup -= washoffLoad;
587 17520 landFactor[i].buildup[p] = buildup;
588 }
589
590 // --- otherwise add washoff to buildup mass balance totals
591 // so that things will balance
592 else
593 {
594 16131 massbal_updateLoadingTotals(BUILDUP_LOAD, p, washoffLoad);
595 16131 landFactor[i].buildup[p] = 0.0;
596 }
597
598 // --- apply any BMP removal to washoff
599 33651 bmpRemoval = Landuse[i].washoffFunc[p].bmpEffic * washoffLoad;
600
2/2
✓ Branch 0 taken 348 times.
✓ Branch 1 taken 33303 times.
33651 if ( bmpRemoval > 0.0 )
601 {
602 348 massbal_updateLoadingTotals(BMP_REMOVAL_LOAD, p, bmpRemoval);
603 348 washoffLoad -= bmpRemoval;
604 }
605
606 // --- return washoff load converted back to mass (mg or ug)
607 33651 return washoffLoad / Pollut[p].mcf;
608 }
609
610 //=============================================================================
611
612 33651 double landuse_getWashoffQual(int i, int p, double buildup, double runoff,
613 double area)
614 //
615 // Input: i = land use index
616 // p = pollutant index
617 // buildup = current buildup over land use (lbs or kg)
618 // runoff = current runoff on subcatchment (ft/sec)
619 // area = area devoted to land use (ft2)
620 // Output: returns pollutant concentration in washoff (mass/ft3)
621 // Purpose: finds concentration of pollutant washed off a land use.
622 //
623 // Notes: "coeff" for each washoff function was previously adjusted to
624 // result in units of mass/sec
625 //
626 {
627 33651 double cWashoff = 0.0;
628 33651 double coeff = Landuse[i].washoffFunc[p].coeff;
629 33651 double expon = Landuse[i].washoffFunc[p].expon;
630 33651 int func = Landuse[i].washoffFunc[p].funcType;
631
632 // --- if no washoff function or no runoff, return 0
633
3/4
✓ Branch 0 taken 33303 times.
✓ Branch 1 taken 348 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 33303 times.
33651 if ( func == NO_WASHOFF || runoff == 0.0 ) return 0.0;
634
635 // --- if buildup function exists but no current buildup, return 0
636
4/4
✓ Branch 0 taken 17520 times.
✓ Branch 1 taken 15783 times.
✓ Branch 2 taken 464 times.
✓ Branch 3 taken 17056 times.
33303 if ( Landuse[i].buildupFunc[p].funcType != NO_BUILDUP && buildup == 0.0 )
637 464 return 0.0;
638
639 // --- Exponential Washoff function
640
2/2
✓ Branch 0 taken 16940 times.
✓ Branch 1 taken 15899 times.
32839 if ( func == EXPON_WASHOFF )
641 {
642 // --- evaluate washoff eqn. with runoff in in/hr (or mm/hr)
643 // and buildup converted from lbs (or kg) to concen. mass units
644 16940 cWashoff = coeff * pow(runoff * UCF(RAINFALL), expon) *
645 16940 buildup / Pollut[p].mcf;
646 16940 cWashoff /= runoff * area;
647 }
648
649 // --- Rating Curve Washoff function
650
2/2
✓ Branch 0 taken 116 times.
✓ Branch 1 taken 15783 times.
15899 else if ( func == RATING_WASHOFF )
651 {
652 116 cWashoff = coeff * pow(runoff * area, expon-1.0);
653 }
654
655 // --- Event Mean Concentration Washoff
656
1/2
✓ Branch 0 taken 15783 times.
✗ Branch 1 not taken.
15783 else if ( func == EMC_WASHOFF )
657 {
658 15783 cWashoff = coeff; // coeff includes LperFT3 factor
659 }
660 32839 return cWashoff;
661 }
662
663 //=============================================================================
664
665 double landuse_getCoPollutLoad(int p, double washoff[])
666 //
667 // Input: p = pollutant index
668 // washoff = pollut. washoff rate (mass/sec)
669 // Output: returns washoff mass added by co-pollutant relation (mass)
670 // Purpose: finds washoff mass added by a co-pollutant of a given pollutant.
671 //
672 {
673 int k;
674 double w;
675
676 // --- check if pollutant p has a co-pollutant k
677 k = Pollut[p].coPollut;
678 if ( k >= 0 )
679 {
680 // --- compute addition to washoff from co-pollutant
681 w = Pollut[p].coFraction * washoff[k];
682
683 // --- add washoff to buildup mass balance totals
684 // so that things will balance
685 massbal_updateLoadingTotals(BUILDUP_LOAD, p, w * Pollut[p].mcf);
686 return w;
687 }
688 return 0.0;
689 }
690
691 //=============================================================================
692
693 222 double landuse_getExternalBuildup(int i, int p, double buildup, double tStep)
694 //
695 // Input: i = landuse index
696 // p = pollutant index
697 // buildup = buildup at start of time step (mass/unit)
698 // tStep = time step (sec)
699 // Output: returns pollutant buildup at end of time interval (mass/unit)
700 // Purpose: finds pollutant buildup contributed by external loading over a
701 // given time step.
702 //
703 {
704 222 double maxBuildup = Landuse[i].buildupFunc[p].coeff[0];
705 222 double sf = Landuse[i].buildupFunc[p].coeff[1]; // scaling factor
706 222 int ts = (int)floor(Landuse[i].buildupFunc[p].coeff[2]); // time series index
707 222 double rate = 0.0;
708
709 // --- no buildup increment at start of simulation
710
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 221 times.
222 if (NewRunoffTime == 0.0) return 0.0;
711
712 // --- get buildup rate (mass/unit/day) over the interval
713
1/2
✓ Branch 0 taken 221 times.
✗ Branch 1 not taken.
221 if ( ts >= 0 )
714 {
715 221 rate = sf * table_tseriesLookup(&Tseries[ts],
716 getDateTime(NewRunoffTime), FALSE);
717 }
718
719 // --- compute buildup at end of time interval
720 221 buildup = buildup + rate * tStep / SECperDAY;
721
1/2
✓ Branch 0 taken 221 times.
✗ Branch 1 not taken.
221 buildup = MIN(buildup, maxBuildup);
722 221 return buildup;
723 }
724