GCC Code Coverage Report


Directory: src/solver/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 83.3% 563 / 0 / 676
Functions: 92.1% 35 / 0 / 38
Branches: 56.6% 260 / 0 / 459

inlet.c
Line Branch Exec Source
1 //-----------------------------------------------------------------------------
2 // inlet.c
3 //
4 // Project: EPA SWMM5
5 // Version: 5.2
6 // Date: 07/13/23 (Build 5.2.4)
7 // Author: L. Rossman
8 //
9 // Street/Channel Inlet Functions
10 //
11 // Computes capture efficiency of inlets placed in Street conduits
12 // or Rectangular/Trapezoidal channels using FHWA HEC-22 methods (see
13 // Brown, S.A. et al., Urban Drainage Design Manual, Federal Highway
14 // Administration Hydraulic Engineering Circular No. 22, 3rd Edition,
15 // FHWA-NHI-10-009, August 2013).
16 //
17 // Build 5.2.1:
18 // - Substitutes the constant BIG for HUGE.
19 // Build 5.2.2:
20 // - Additional statistics added to Street Flow Summary table.
21 // Build 5.2.4:
22 // - Fixed expression for equivalent gutter slope in getCurbInletCapture.
23 // - Corrected sign in equation for effective head in a curb inlet
24 // with an inclined throat opening in getCurbOrificeFlow.
25 //-----------------------------------------------------------------------------
26 #define _CRT_SECURE_NO_DEPRECATE
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <math.h>
31 #include "headers.h"
32
33 // Grate inlet
34 typedef struct
35 {
36 int type; // type of grate used
37 double length; // length (parallel to flow) (ft)
38 double width; // width (perpendicular to flow) (ft)
39 double fracOpenArea; // fraction of grate area that is open
40 double splashVeloc; // splash-over velocity (ft/s)
41 } TGrateInlet;
42
43 // Slotted drain inlet
44 typedef struct
45 {
46 double length; // length (parallel to flow) (ft)
47 double width; // width (perpendicular to flow) (ft)
48 } TSlottedInlet;
49
50 // Curb opening inlet
51 typedef struct
52 {
53 double length; // length of curb opening (ft)
54 double height; // height of curb opening (ft)
55 int throatAngle; // type of throat angle
56 } TCurbInlet;
57
58 // Custom inlet
59 typedef struct
60 {
61 int onGradeCurve; // flow diversion curve index
62 int onSagCurve; // flow rating curve index
63 } TCustomInlet;
64
65 // Inlet design object
66 typedef struct
67 {
68 char * ID; // name assigned to inlet design
69 int type; // type of inlet used (grate, curb, etc)
70 TGrateInlet grateInlet; // length = 0 if not used
71 TSlottedInlet slottedInlet; // length = 0 if not used
72 TCurbInlet curbInlet; // length = 0 if not used
73 int customCurve; // curve index = -1 if not used
74 } TInletDesign;
75
76
77 // Inlet performance statistics
78 typedef struct
79 {
80 int flowPeriods; // # periods with approach flow
81 int capturePeriods; // # periods with captured flow
82 int backflowPeriods; // # periods with backflow
83 double peakFlow; // peak flow seen by inlet (cfs)
84 double peakFlowCapture; // capture efficiency at peak flow
85 double avgFlowCapture; // average capture efficiency
86 double bypassFreq; // frequency of bypass flow
87 } TInletStats;
88
89 // Inlet list object
90 struct TInlet
91 {
92 int linkIndex; // index of conduit link with the inlet
93 int designIndex; // index of inlet's design
94 int nodeIndex; // index of node receiving captured flow
95 int numInlets; // # inlets on each side of street or in channel
96 int placement; // whether inlet is on-grade or on-sag
97 double clogFactor; // fractional degree of inlet clogging
98 double flowLimit; // inlet flow restriction (cfs)
99 double localDepress; // local gutter depression (ft)
100 double localWidth; // local depression width (ft)
101
102 double flowFactor; // flow = flowFactor * (flow spread)^2.67
103 double flowCapture; // captured flow rate (cfs)
104 double backflow; // backflow from capture node (cfs)
105 double backflowRatio; // inlet backflow / capture node overflow
106 TInletStats stats; // inlet performance statistics
107 TInlet * nextInlet; // next inlet in list
108 };
109
110 // Shared inlet variables
111 TInletDesign * InletDesigns; // array of available inlet designs
112 int InletDesignCount; // number of inlet designs
113 int UsesInlets; // TRUE if project uses inlets
114
115 //-----------------------------------------------------------------------------
116 // Enumerations
117 //-----------------------------------------------------------------------------
118
119 enum InletType {
120 GRATE_INLET, CURB_INLET, COMBO_INLET, SLOTTED_INLET,
121 DROP_GRATE_INLET, DROP_CURB_INLET, CUSTOM_INLET
122 };
123
124 enum GrateType {
125 P50, P50x100, P30, CURVED_VANE, TILT_BAR_45,
126 TILT_BAR_30, RETICULINE, GENERIC
127 };
128
129 enum InletPlacementType { AUTOMATIC, ON_GRADE, ON_SAG };
130
131 enum ThroatAngleType { HORIZONTAL_THROAT, INCLINED_THROAT, VERTICAL_THROAT };
132
133 //-----------------------------------------------------------------------------
134 // Constants
135 //-----------------------------------------------------------------------------
136 static char* InletTypeWords[] =
137 {"GRATE", "CURB", "", "SLOTTED", "DROP_GRATE", "DROP_CURB", "CUSTOM", NULL};
138
139 static char* GrateTypeWords[] =
140 {"P_BAR-50", "P_BAR-50x100", "P_BAR-30", "CURVED_VANE", "TILT_BAR-45", "TILT_BAR-30",
141 "RETICULINE", "GENERIC", NULL};
142
143 static char* ThroatAngleWords[] =
144 {"HORIZONTAL", "INCLINED", "VERTICAL", NULL};
145
146 static char *PlacementTypeWords[] =
147 {"AUTOMATIC", "ON_GRADE", "ON_SAG"};
148
149 // Coefficients for cubic polynomials fitted to Splash Over Velocity v.
150 // Grate Length curves in Chart 5B of HEC-22 manual taken from Denver
151 // UDFCD manual.
152 static const double SplashCoeffs[][4] = {
153 {2.22, 4.03, 0.65, 0.06}, //P_BAR-50
154 {0.74, 2.44, 0.27, 0.02}, //P_BAR-50x100
155 {1.76, 3.12, 0.45, 0.03}, //P_BAR-30
156 {0.30, 4.85, 1.31, 0.15}, //Curved_Vane
157 {0.99, 2.64, 0.36, 0.03}, //Tilt_Bar-45
158 {0.51, 2.34, 0.2, 0.01}, //Tilt_Bar-30
159 {0.28, 2.28, 0.18, 0.01}}; //Reticuline
160
161 // Grate opening ratios (Chart 9B of HEC-22 manual)
162 static const double GrateOpeningRatios[] = {
163 0.90, //P_BAR-50
164 0.80, //P_BAR-50x100
165 0.60, //P_BAR-30
166 0.35, //Curved_Vane
167 0.17, //Tilt_Bar-45 (assumed)
168 0.34, //Tilt_Bar-30
169 0.80, //Reticuline
170 1.00}; //Generic
171
172 //-----------------------------------------------------------------------------
173 // Imported Variables
174 //-----------------------------------------------------------------------------
175 extern TLinkStats* LinkStats; // defined in STATS.C
176 extern TNodeStats* NodeStats; // defined in STATS.C
177
178 //-----------------------------------------------------------------------------
179 // Local Shared Variables
180 //-----------------------------------------------------------------------------
181 // Variables as named in the HEC-22 manual.
182 static double Sx; // street cross slope
183 static double SL; // conduit longitudinal slope
184 static double Sw; // gutter + cross slope
185 static double a; // street gutter depression (ft)
186 static double W; // street gutter width (ft)
187 static double T; // top width of flow spread (ft)
188 static double n; // Manning's roughness coeff.
189
190 // Additional variables
191 static int Nsides; // 1- or 2-sided street
192 static double Tcrown; // distance from street curb to crown (ft)
193 static double Beta; // = 1.486 * sqrt(SL) / n
194 static double Qfactor; // factor f in Izzard's eqn. Q = f*T^2.67
195 static TXsect* xsect; // cross-section data of inlet's conduit
196 static double* InletFlow; // captured inlet flow received by each node
197 static TInlet* FirstInlet; // head of list of deployed inlets
198
199 //-----------------------------------------------------------------------------
200 // External functions (declared in inlet.h)
201 //-----------------------------------------------------------------------------
202 // inlet_create called by createObjects in project.c
203 // inlet_delete called by deleteObjects in project.c
204 // inlet_readDesignParams called by parseLine in input.c
205 // inlet_readUsageParams called by parseLine in input.c
206 // inlet_validate called by project_validate
207 // inlet_findCapturedFlows called by routing_execute
208 // inlet_adjustQualInflows called by routing_execute
209 // inlet_adjustQualOutflows called by routing execute
210 // inlet_writeStatsReport called by statsrpt_writeReport
211 // inlet_capturedFlow called by findLinkMassFlow in qualrout.c
212
213 //-----------------------------------------------------------------------------
214 // Local functions
215 //-----------------------------------------------------------------------------
216 static int readGrateInletParams(int inletIndex, char* tok[], int ntoks);
217 static int readCurbInletParams(int inletIndex, char* tok[], int ntoks);
218 static int readSlottedInletParams(int inletIndex, char* tok[], int ntoks);
219 static int readCustomInletParams(int inletIndex, char* tok[], int ntoks);
220
221 static void initInletStats(TInlet* inlet);
222 static void updateInletStats(TInlet* inlet, double q);
223 static void writeStreetStatsHeader();
224 static void writeStreetStats(int link);
225
226 static void getBackflowRatios();
227 static double getInletArea(TInlet* inlet);
228
229 static int getInletPlacement(TInlet* inlet, int node);
230 static void getConduitGeometry(TInlet* inlet);
231 static double getFlowSpread(double flow);
232 static double getEo(double slopeRatio, double spread, double gutterWidth);
233
234 static double getCustomCapturedFlow(TInlet* inlet, double flow, double depth);
235 static double getOnGradeCapturedFlow(TInlet* inlet, double flow, double depth);
236 static double getOnGradeInletCapture(int inletIndex, double flow, double depth);
237 static double getGrateInletCapture(int inletIndex, double flow);
238 static double getCurbInletCapture(double flow, double length);
239
240 static double getGutterFlowRatio(double gutterWidth);
241 static double getGutterAreaRatio(double grateWidth, double area);
242 static double getSplashOverVelocity(int grateType, double grateLength);
243
244 static double getOnSagCapturedFlow(TInlet* inlet, double flow, double depth);
245 static double getOnSagInletCapture(int inletIndex, double depth);
246 static void findOnSagGrateFlows(int inletIndex, double depth,
247 double *weirFlow, double *orificeFlow);
248 static void findOnSagCurbFlows(int inletIndex, double depth,
249 double openingLength, double *weirFlow,
250 double *orificeFlow);
251 static double getCurbOrificeFlow(double flowDepth, double openingHeight,
252 double openingLength, int throatAngle);
253 static double getOnSagSlottedFlow(int inletIndex, double depth);
254
255 //=============================================================================
256
257 58 int inlet_create(int numInlets)
258 //
259 // Input: numInlets = number of inlet designs to create
260 // Output: none
261 // Purpose: creats a collection of inlet designs.
262 //
263 {
264 int i;
265
266 58 InletDesigns = NULL;
267 58 InletFlow = NULL;
268 58 InletDesignCount = 0;
269 58 UsesInlets = FALSE;
270 58 FirstInlet = NULL;
271 58 InletDesigns = (TInletDesign *)calloc(numInlets, sizeof(TInletDesign));
272
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
58 if (InletDesigns == NULL) return ERR_MEMORY;
273 58 InletDesignCount = numInlets;
274
275 58 InletFlow = (double *)calloc(Nobjects[NODE], sizeof(double));
276
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
58 if (InletFlow == NULL) return ERR_MEMORY;
277
278
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 58 times.
65 for (i = 0; i < InletDesignCount; i++)
279 {
280 7 InletDesigns[i].customCurve = -1;
281 7 InletDesigns[i].curbInlet.length = 0.0;
282 7 InletDesigns[i].grateInlet.length = 0.0;
283 7 InletDesigns[i].slottedInlet.length = 0.0;
284 7 InletDesigns[i].type = CUSTOM_INLET;
285 }
286 58 return 0;
287 }
288
289 //=============================================================================
290
291 58 void inlet_delete()
292 //
293 // Input: none
294 // Output: none
295 // Purpose: frees all memory allocated for inlet analysis.
296 //
297 {
298 58 TInlet* inlet = FirstInlet;
299 TInlet* nextInlet;
300
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 58 times.
72 while (inlet)
301 {
302 14 nextInlet = inlet->nextInlet;
303 14 free(inlet);
304 14 inlet = nextInlet;
305 }
306 58 FirstInlet = NULL;
307
1/2
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
58 FREE(InletFlow);
308
1/2
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
58 FREE(InletDesigns);
309 58 }
310
311 //=============================================================================
312
313 8 int inlet_readDesignParams(char* tok[], int ntoks)
314 //
315 // Input: tok[] = array of string tokens
316 // ntoks = number of tokens
317 // Output: returns an error code
318 // Purpose: extracts a set of inlet design parameters from a tokenized line
319 // of the [INLETS] section of a SWMM input file.
320 //
321 // Format of input line is:
322 // ID GRATE Length Width GrateType (OpenArea) (SplashVeloc)
323 // ID CURB Length Height (ThroatType)
324 // ID SLOTTED Length Width
325 // ID DROP_GRATE Length Width GrateType (OpenArea) (SplashVeloc)
326 // ID DROP_CURB Length Height
327 // ID CUSTOM CurveID
328 //
329 {
330 int i;
331
332 // --- check for minimum number of tokens
333
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if ( ntoks < 3 ) return error_setInpError(ERR_ITEMS, "");
334
335 // --- check that design ID already registered in project
336 8 i = project_findObject(INLET, tok[0]);
337
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if ( i < 0 ) return error_setInpError(ERR_NAME, tok[0]);
338 8 InletDesigns[i].ID = project_findID(INLET, tok[0]);
339
340 // --- retrieve type of inlet design
341 8 InletDesigns[i].type = findmatch(tok[1], InletTypeWords);
342
343 // --- read inlet's design parameters
344
4/5
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
8 switch (InletDesigns[i].type)
345 {
346 3 case GRATE_INLET:
347 case DROP_GRATE_INLET:
348 3 return readGrateInletParams(i, tok, ntoks);
349 3 case CURB_INLET:
350 case DROP_CURB_INLET:
351 3 return readCurbInletParams(i, tok, ntoks);
352 1 case SLOTTED_INLET:
353 1 return readSlottedInletParams(i, tok, ntoks);
354 1 case CUSTOM_INLET:
355 1 return readCustomInletParams(i, tok, ntoks);
356 default: return error_setInpError(ERR_KEYWORD, tok[1]);
357 }
358 return 0;
359 }
360 //=============================================================================
361
362 14 int inlet_readUsageParams(char* tok[], int ntoks)
363 //
364 // Input: tok[] = array of string tokens
365 // ntoks = number of tokens
366 // Output: returns an error code
367 // Purpose: extracts inlet usage parameters from a tokenized line
368 // of the [INLET_USAGE] section of a SWMM input file.
369 //
370 // Format of input line is:
371 // linkID inletID nodeID (#Inlets %Clog Qmax aLocal wLocal placement)
372 // where
373 // linkID = ID name of link containing the inlet
374 // inletID = ID name of inlet design being used
375 // nodeID = ID name of node receiving captured flow
376 // #Inlets = number of identical inlets used (default = 1)
377 // %Clog = percent that inlet is clogged
378 // Qmax = maximum flow that inlet can capture (default = 0 (no limit))
379 // aLocal = local gutter depression (ft or m) (default = 0)
380 // wLocal = width of local gutter depression (ft or m) (default = 0)
381 // placement = ON_GRADE, ON_SAG, or AUTO (the default)
382 //
383 {
384 14 int linkIndex, designIndex, nodeIndex, numInlets = 1;
385 14 int placement = AUTOMATIC;
386 14 double flowLimit = 0.0, pctClogged = 0.0;
387 14 double aLocal = 0.0, wLocal = 0.0;
388 TInlet* inlet;
389
390 // --- check that inlet's link exists
391
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (ntoks < 3) return error_setInpError(ERR_ITEMS, "");
392 14 linkIndex = project_findObject(LINK, tok[0]);
393
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (linkIndex < 0) return error_setInpError(ERR_NAME, tok[0]);
394
395 // --- check that inlet design type exists
396 14 designIndex = project_findObject(INLET, tok[1]);
397
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (designIndex < 0) return error_setInpError(ERR_NAME, tok[1]);
398
399 // --- check that receiving node exists
400 14 nodeIndex = project_findObject(NODE, tok[2]);
401
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (nodeIndex < 0) return error_setInpError(ERR_NAME, tok[2]);
402
403 // --- get number of inlets
404
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (ntoks > 3)
405
2/4
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
14 if (!getInt(tok[3], &numInlets) || numInlets < 1)
406 return error_setInpError(ERR_NUMBER, tok[3]);
407
408 // --- get flow limit & percent clogged
409
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (ntoks > 4)
410 {
411
2/4
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
14 if (!getDouble(tok[4], &pctClogged) || pctClogged < 0.0
412
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 || pctClogged > 99.)
413 return error_setInpError(ERR_NUMBER, tok[4]);
414 }
415
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (ntoks > 5)
416
2/4
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
14 if (!getDouble(tok[5], &flowLimit) || flowLimit < 0.0)
417 return error_setInpError(ERR_NUMBER, tok[5]);
418
419 // --- get local depression parameters
420
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (ntoks > 6)
421
2/4
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
14 if (!getDouble(tok[6], &aLocal) || aLocal < 0.0)
422 return error_setInpError(ERR_NUMBER, tok[6]);
423
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (ntoks > 7)
424
2/4
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
14 if (!getDouble(tok[7], &wLocal) || wLocal < 0.0)
425 return error_setInpError(ERR_NUMBER, tok[7]);
426
427 // --- get inlet placement
428
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (ntoks > 8)
429 {
430 14 placement = findmatch(tok[8], PlacementTypeWords);
431
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (placement < 0) return error_setInpError(ERR_KEYWORD, tok[8]);
432 }
433
434 // --- create an inlet usage object for the link
435 14 inlet = Link[linkIndex].inlet;
436
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (inlet == NULL)
437 {
438 14 inlet = (TInlet *)malloc(sizeof(TInlet));
439
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (!inlet) return error_setInpError(ERR_MEMORY, "");
440 14 Link[linkIndex].inlet = inlet;
441 14 inlet->nextInlet = FirstInlet;
442 14 FirstInlet = inlet;
443 }
444
445 // --- save inlet usage parameters
446 14 inlet->linkIndex = linkIndex;
447 14 inlet->designIndex = designIndex;
448 14 inlet->nodeIndex = nodeIndex;
449 14 inlet->numInlets = numInlets;
450 14 inlet->placement = placement;
451 14 inlet->clogFactor = 1.0 - (pctClogged / 100.);
452 14 inlet->flowLimit = flowLimit / UCF(FLOW);
453 14 inlet->localDepress = aLocal / UCF(LENGTH);
454 14 inlet->localWidth = wLocal / UCF(LENGTH);
455 14 inlet->flowFactor = 0.0;
456 14 inlet->backflowRatio = 0.0;
457 14 initInletStats(inlet);
458 14 UsesInlets = TRUE;
459 14 return 0;
460 }
461
462 //=============================================================================
463
464 58 void inlet_validate()
465 //
466 // Input: none
467 // Output: none
468 // Purpose: checks that inlets have been assigned to conduits with proper
469 // cross section shapes and counts the number of inlets that each
470 // node receives either bypased or captured flow from.
471 //
472 {
473 int i, j, inletType, inletValid;
474 TInlet* inlet;
475 TInlet* prevInlet;
476
477 // --- traverse the list of inlets placed in conduits
478
2/2
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 3 times.
58 if (!UsesInlets) return;
479 3 prevInlet = FirstInlet;
480 3 inlet = FirstInlet;
481
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 3 times.
17 while (inlet)
482 {
483 // --- check that inlet's conduit can accept the inlet's type
484 14 inletValid = FALSE;
485 14 i = inlet->linkIndex;
486 14 xsect = &Link[i].xsect;
487 14 inletType = InletDesigns[inlet->designIndex].type;
488
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 13 times.
14 if (inletType == CUSTOM_INLET)
489 {
490 1 j = InletDesigns[inlet->designIndex].customCurve;
491
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if (j >= 0)
492 {
493
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if (Curve[j].curveType == DIVERSION_CURVE ||
494 Curve[j].curveType == RATING_CURVE)
495 1 inletValid = TRUE;
496 }
497 }
498
5/6
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 1 time.
13 else if ((xsect->type == TRAPEZOIDAL || xsect->type == RECT_OPEN) &&
499
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 (inletType == DROP_GRATE_INLET ||
500 inletType == DROP_CURB_INLET))
501 2 inletValid = TRUE;
502
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 else if (xsect->type == STREET_XSECT &&
503
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 inletType != DROP_GRATE_INLET &&
504 inletType != DROP_CURB_INLET)
505 11 inletValid = TRUE;
506
507 // --- if inlet placement is valid then
508
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (inletValid)
509 {
510 // --- record that receptor node has inlets
511 14 Node[Link[i].node2].inlet = BYPASS;
512 14 Node[inlet->nodeIndex].inlet = CAPTURE;
513
514 // --- initialize inlet's backflow
515 14 inlet->backflow = 0.0;
516
517 // --- compute street inlet's flow factor for Izzard's eqn.
518 // (used in Q = flowFactor * Spread^2.67 equation)
519 14 getConduitGeometry(inlet);
520 14 inlet->flowFactor = (0.56/n) * pow(SL,0.5) * pow(Sx,1.67);
521
522 // --- save reference to current inlet & continue to next inlet
523 14 prevInlet = inlet;
524 14 inlet = inlet->nextInlet;
525 }
526
527 // --- if inlet placement is not valid then issue a warning message
528 // and remove the inlet from the conduit
529 else
530 {
531 report_writeWarningMsg(WARN12, Link[i].ID);
532 if (inlet == FirstInlet)
533 {
534 FirstInlet = inlet->nextInlet;
535 prevInlet = FirstInlet;
536 free(inlet);
537 inlet = FirstInlet;
538 }
539 else
540 {
541 prevInlet->nextInlet = inlet->nextInlet;
542 free(inlet);
543 inlet = prevInlet->nextInlet;
544 }
545 Link[i].inlet = NULL;
546 }
547 }
548
549 // --- determine how capture node's overflow is split between its inlets
550 3 getBackflowRatios();
551 }
552
553 //=============================================================================
554
555 1030493 void inlet_findCapturedFlows(double tStep)
556 //
557 // Input: tStep = current flow routing time step (sec)
558 // Output: none
559 // Purpose: computes flow captured by each inlet and adjusts the
560 // lateral flows of the inlet's bypass and capture nodes accordingly.
561 //
562 // This function is called after regular lateral flows to all nodes have been
563 // set but before a flow routing step has been taken.
564 {
565 int i, j, m, placement;
566 double q;
567 TInlet *inlet;
568
569 // --- For non-DW routing find conduit flow into each node
570 // (used to limit max. amount of on-sag capture)
571
2/2
✓ Branch 0 taken 1028471 times.
✓ Branch 1 taken 2022 times.
1030493 if (!UsesInlets) return;
572 2022 memset(InletFlow, 0, Nobjects[NODE]*sizeof(double));
573
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2022 times.
2022 if (RouteModel != DW)
574 {
575 for (j = 0; j < Nobjects[NODE]; j++)
576 Node[j].inflow = MAX(0., Node[j].newLatFlow);
577 for (i = 0; i < Nobjects[LINK]; i++)
578 Node[Link[i].node2].inflow += MAX(0.0, Link[i].newFlow);
579 }
580
581 // --- loop through each inlet
582
2/2
✓ Branch 0 taken 8308 times.
✓ Branch 1 taken 2022 times.
10330 for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet)
583 {
584 // --- identify indexes of inlet's bypass (j) and capture (m) nodes
585 8308 i = inlet->linkIndex;
586 8308 j = Link[i].node2;
587 8308 m = inlet->nodeIndex;
588
589 // --- get inlet's placement (ON_GRADE or ON_SAG)
590 8308 placement = getInletPlacement(inlet, j);
591
592 // --- find flow captured by a Custom inlet
593
2/2
✓ Branch 0 taken 482 times.
✓ Branch 1 taken 7826 times.
8308 if (InletDesigns[inlet->designIndex].type == CUSTOM_INLET)
594 {
595 482 q = fabs(Link[i].newFlow);
596 482 inlet->flowCapture = getCustomCapturedFlow(inlet, q, Node[j].newDepth);
597 }
598
599 // --- find flow captured by on-grade inlet
600
2/2
✓ Branch 0 taken 4400 times.
✓ Branch 1 taken 3426 times.
7826 else if (placement == ON_GRADE)
601 {
602 4400 q = fabs(Link[i].newFlow);
603 4400 inlet->flowCapture = getOnGradeCapturedFlow(inlet, q, Node[j].newDepth);
604 }
605
606 // --- find flow captured by on-sag inlet
607 else
608 {
609 3426 q = Node[j].inflow;
610 3426 inlet->flowCapture = getOnSagCapturedFlow(inlet, q, Node[j].newDepth);
611 }
612
2/2
✓ Branch 0 taken 229 times.
✓ Branch 1 taken 8079 times.
8308 if (fabs(inlet->flowCapture) < FUDGE) inlet->flowCapture = 0.0;
613
614 // --- add to total flow captured by inlet's node
615 8308 InletFlow[j] += inlet->flowCapture;
616
617 // --- capture node's overflow becomes inlet's backflow
618 8308 inlet->backflow = Node[m].overflow * inlet->backflowRatio;
619
2/2
✓ Branch 0 taken 6559 times.
✓ Branch 1 taken 1749 times.
8308 if (fabs(inlet->backflow) < FUDGE) inlet->backflow = 0.0;
620 }
621
622 // --- make second pass through each inlet
623
2/2
✓ Branch 0 taken 8308 times.
✓ Branch 1 taken 2022 times.
10330 for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet)
624 {
625 // --- identify indexes of inlet's bypass (j) and capture (m) nodes
626 8308 i = inlet->linkIndex;
627 8308 j = Link[i].node2;
628 8308 m = inlet->nodeIndex;
629
630 // --- for on-sag placement under non-DW routing, captured flow
631 // is limited to inlet's share of bypass node's inflow plus
632 // any stored volume
633
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 8308 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
8308 if (RouteModel != DW && getInletPlacement(inlet, j) == ON_SAG)
634 {
635 q = Node[j].newVolume / tStep;
636 q += MAX(Node[j].inflow, 0.0);
637 if (InletFlow[j] > q)
638 inlet->flowCapture *= q / InletFlow[j];
639 }
640
641 // --- adjust lateral flows at bypass and capture nodes
642 // (subtract captured flow from bypass node, add it to capture
643 // node, and add any backflow to bypass node)
644 8308 Node[j].newLatFlow -= (inlet->flowCapture - inlet->backflow);
645 8308 Node[m].newLatFlow += inlet->flowCapture;
646
647 // --- update inlet's performance if reporting has begun
648
1/2
✓ Branch 1 taken 8308 times.
✗ Branch 2 not taken.
8308 if (getDateTime(NewRoutingTime) > ReportStart)
649 8308 updateInletStats(inlet, fabs(Link[i].newFlow));
650 }
651 }
652
653 //=============================================================================
654
655 201935 void inlet_adjustQualInflows()
656 //
657 // Input: none
658 // Output: none
659 // Purpose: adjusts accumulated flow rates and pollutant mass inflows at each
660 // inlet's bypass and capture nodes after a flow routing step has
661 // been taken prior to a quality routing step.
662 //
663 {
664 int i, j, m, p;
665 double qNet;
666 TInlet* inlet;
667
668
2/2
✓ Branch 0 taken 200945 times.
✓ Branch 1 taken 990 times.
201935 if (!UsesInlets) return;
669
2/4
✓ Branch 0 taken 990 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 990 times.
990 if (IgnoreQuality || Nobjects[POLLUT] == 0) return;
670
2/2
✓ Branch 0 taken 1980 times.
✓ Branch 1 taken 990 times.
2970 for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet)
671 {
672 // --- identify indexes of inlet's bypass (j) and capture (m) nodes
673 1980 i = inlet->linkIndex;
674 1980 j = Link[i].node2;
675 1980 m = inlet->nodeIndex;
676
677 // --- there's a net flow from the bypass to the capture node
678 1980 qNet = inlet->flowCapture - inlet->backflow;
679
2/2
✓ Branch 0 taken 1851 times.
✓ Branch 1 taken 129 times.
1980 if (qNet > 0.0)
680 {
681 // --- add net capture flow to capture node's accumulated flow
682 // inflow for quality routing
683 1851 Node[m].qualInflow += qNet;
684
685 // --- and do the same for pollutant mass flows
686 // (Node[m].newQual is the mass inflow accumulator for node m)
687
2/2
✓ Branch 0 taken 1851 times.
✓ Branch 1 taken 1851 times.
3702 for (p = 0; p < Nobjects[POLLUT]; p++)
688 1851 Node[m].newQual[p] += qNet * Node[j].oldQual[p];
689 }
690
691 // --- there's a net backflow from the capture to the bypass node
692 else
693 {
694 // --- add the backflow flow rate and pollutant mass flow to the
695 // bypass node's accumulated flow and pollutant mass inflow
696 129 qNet = -qNet;
697 129 Node[j].qualInflow += qNet;
698
2/2
✓ Branch 0 taken 129 times.
✓ Branch 1 taken 129 times.
258 for (p = 0; p < Nobjects[POLLUT]; p++)
699 129 Node[j].newQual[p] += qNet * Node[m].oldQual[p];
700 }
701 }
702 }
703
704 //=============================================================================
705
706 1030493 void inlet_adjustQualOutflows()
707 //
708 // Input: none
709 // Output: none
710 // Purpose: adjusts mass balance totals after a complete routing step has been
711 // taken so as not to treat inlet transfer flows as system outflows.
712 //
713 {
714 int j, p;
715 double q, w;
716 TInlet* inlet;
717
718 // --- these variables, declared in massbal.c, accumulate system-wide flow and
719 // pollutant mass fluxes over a time step to use in mass balances
720 extern TRoutingTotals StepFlowTotals;
721 extern TRoutingTotals* StepQualTotals;
722
723 // --- examine each node
724
2/2
✓ Branch 0 taken 37894467 times.
✓ Branch 1 taken 1030493 times.
38924960 for (j = 0; j < Nobjects[NODE]; j++)
725 {
726 // --- node receives captured flow from an inlet
727
2/2
✓ Branch 0 taken 8308 times.
✓ Branch 1 taken 37886159 times.
37894467 if (Node[j].inlet == CAPTURE)
728 {
729 // --- node also has an overflow (e.g., it's a surcharged sewer node)
730 8308 q = Node[j].overflow;
731
2/2
✓ Branch 0 taken 1753 times.
✓ Branch 1 taken 6555 times.
8308 if (q > 0.0)
732 {
733 // --- remove overflow from system flooding total since it does
734 // not leave the system (it is sent to inlet's bypass node)
735 1753 StepFlowTotals.flooding -= q;
736
737 // --- also remove pollutant overflow mass from system totals
738
1/2
✓ Branch 0 taken 1753 times.
✗ Branch 1 not taken.
1753 if (!IgnoreQuality)
739
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1753 times.
1753 for (p = 0; p < Nobjects[POLLUT]; p++)
740 {
741 w = q * Node[j].newQual[p];
742 StepQualTotals[p].flooding -= w;
743 }
744 }
745 }
746 }
747
748 // --- for WQ analysis, examine each inlet's bypass node
749
4/4
✓ Branch 0 taken 1018206 times.
✓ Branch 1 taken 12287 times.
✓ Branch 2 taken 201935 times.
✓ Branch 3 taken 816271 times.
1030493 if (!IgnoreQuality && Nobjects[POLLUT] > 0)
750 {
751
2/2
✓ Branch 0 taken 1980 times.
✓ Branch 1 taken 201935 times.
203915 for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet)
752 {
753 1980 j = Link[inlet->linkIndex].node2;
754
755 // --- inlet has net positive flow capture leading to
756 // node having a net negative lateral inflow
757 1980 q = inlet->flowCapture - inlet->backflow;
758
3/4
✓ Branch 0 taken 1851 times.
✓ Branch 1 taken 129 times.
✓ Branch 2 taken 1851 times.
✗ Branch 3 not taken.
1980 if (q > 0.0 && Node[j].newLatFlow < 0.0)
759
760 // --- remove the pollutant mass in the captured flow from
761 // the system totals since it does not leave the system
762 // (it is sent to the inlet's capture node)
763
2/2
✓ Branch 0 taken 1851 times.
✓ Branch 1 taken 1851 times.
3702 for (p = 0; p < Nobjects[POLLUT]; p++)
764 {
765 1851 w = q * Node[j].newQual[p];
766 1851 StepQualTotals[p].outflow -= w;
767 }
768 }
769 }
770 1030493 }
771
772 //=============================================================================
773
774 48 void inlet_writeStatsReport()
775 //
776 // Input: none
777 // Output: none
778 // Purpose: writes table of street & inlet flow statistics to SWMM's report file.
779 //
780 {
781 48 int j, header = FALSE;
782
783
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 3 times.
48 if (Nobjects[STREET] == 0) return;
784
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 3 times.
22 for (j = 0; j < Nobjects[LINK]; j++)
785 {
786
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 7 times.
19 if (Link[j].xsect.type == STREET_XSECT)
787 {
788
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 10 times.
12 if (!header)
789 {
790 2 writeStreetStatsHeader();
791 2 header = TRUE;
792 }
793 12 writeStreetStats(j);
794 }
795 }
796 3 report_writeLine("");
797 }
798
799 //=============================================================================
800
801 double inlet_capturedFlow(int i)
802 //
803 // Input: i = a link index
804 // Output: returns captured flow rate (cfs)
805 // Purpose: gets the current flow captured by an inlet.
806 //
807 {
808 if (Link[i].inlet) return Link[i].inlet->flowCapture;
809 return 0.0;
810 }
811
812 //=============================================================================
813
814 3 int readGrateInletParams(int i, char* tok[], int ntoks)
815 {
816 //
817 // Input: i = inlet index
818 // tok[] = array of string tokens
819 // ntoks = number of tokens
820 // Output: returns an error code
821 // Purpose: extracts a grate's inlet parameters from a set of string tokens.
822 //
823 int grateType;
824 3 double width, length, areaRatio = 0.0, vSplash = 0.0;
825
826 // --- check for enough tokens
827
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (ntoks < 5) return error_setInpError(ERR_ITEMS, "");
828
829 // --- retrieve length & width
830
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 if (!getDouble(tok[2], &length) || length <= 0.0)
831 return error_setInpError(ERR_NUMBER, tok[2]);
832
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 if (!getDouble(tok[3], &width) || width <= 0.0)
833 return error_setInpError(ERR_NUMBER, tok[3]);
834
835 // --- retrieve grate type
836 3 grateType = findmatch(tok[4], GrateTypeWords);
837
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (grateType < 0) return error_setInpError(ERR_KEYWORD, tok[4]);
838
839 // --- only read open area & splash velocity for GENERIC type grate
840
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (grateType == GENERIC)
841 {
842 if (ntoks < 6) return error_setInpError(ERR_ITEMS, "");
843 if (!getDouble(tok[5], &areaRatio) || areaRatio <= 0.0
844 || areaRatio > 1.0) return error_setInpError(ERR_NUMBER, tok[5]);
845 if (ntoks > 6)
846 {
847 if (!getDouble(tok[6], &vSplash) || vSplash < 0.0)
848 return error_setInpError(ERR_NUMBER, tok[6]);
849 }
850 }
851
852 // --- save grate inlet parameters
853 3 InletDesigns[i].grateInlet.length = length / UCF(LENGTH);
854 3 InletDesigns[i].grateInlet.width = width / UCF(LENGTH);
855 3 InletDesigns[i].grateInlet.type = grateType;
856 3 InletDesigns[i].grateInlet.fracOpenArea = areaRatio;
857 3 InletDesigns[i].grateInlet.splashVeloc = vSplash / UCF(LENGTH);
858
859 // --- check if grate is part of a combo inlet
860
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 if (InletDesigns[i].type == GRATE_INLET &&
861
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 InletDesigns[i].curbInlet.length > 0.0)
862 InletDesigns[i].type = COMBO_INLET;
863 3 return 0;
864 }
865
866 //=============================================================================
867
868 3 int readCurbInletParams(int i, char* tok[], int ntoks)
869 //
870 // Input: i = inlet index
871 // tok[] = array of string tokens
872 // ntoks = number of tokens
873 // Output: returns an error code
874 // Purpose: extracts curb opening inlet parameters from a set of string tokens.
875 //
876 {
877 int throatAngle;
878 double height, length;
879
880 // --- check for enough tokens
881
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (ntoks < 4) return error_setInpError(ERR_ITEMS, "");
882
883 // --- retrieve length & width of opening
884
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 if (!getDouble(tok[2], &length) || length <= 0.0)
885 return error_setInpError(ERR_NUMBER, tok[2]);
886
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 if (!getDouble(tok[3], &height) || height <= 0.0)
887 return error_setInpError(ERR_NUMBER, tok[3]);
888
889 // --- retrieve type of throat angle for curb inlet
890 3 throatAngle = VERTICAL_THROAT;
891
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
3 if (InletDesigns[i].type == CURB_INLET && ntoks > 4)
892 {
893 2 throatAngle = findmatch(tok[4], ThroatAngleWords);
894
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (throatAngle < 0) return error_setInpError(ERR_KEYWORD, tok[4]);
895 }
896
897 // ---- save curb opening inlet parameters
898 3 InletDesigns[i].curbInlet.length = length / UCF(LENGTH);
899 3 InletDesigns[i].curbInlet.height = height / UCF(LENGTH);
900 3 InletDesigns[i].curbInlet.throatAngle = throatAngle;
901
902 // --- check if curb inlet is part of a combo inlet
903
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 if (InletDesigns[i].type == CURB_INLET &&
904
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 InletDesigns[i].grateInlet.length > 0.0)
905 1 InletDesigns[i].type = COMBO_INLET;
906 3 return 0;
907 }
908
909 //=============================================================================
910
911 1 int readSlottedInletParams(int i, char* tok[], int ntoks)
912 //
913 // Input: i = inlet index
914 // tok[] = array of string tokens
915 // ntoks = number of tokens
916 // Output: returns an error code
917 // Purpose: extracts slotted drain inlet parameters from a set of string tokens.
918 //
919 {
920 double width, length;
921
922 // --- check for enough tokens
923
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if (ntoks < 4) return error_setInpError(ERR_ITEMS, "");
924
925 // --- retrieve length and width
926
2/4
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
1 if (!getDouble(tok[2], &length) || length <= 0.0)
927 return error_setInpError(ERR_NUMBER, tok[2]);
928
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], &width) || width <= 0.0)
929 return error_setInpError(ERR_NUMBER, tok[3]);
930
931 // --- save slotted inlet parameters
932 1 InletDesigns[i].slottedInlet.length = length / UCF(LENGTH);
933 1 InletDesigns[i].slottedInlet.width = width / UCF(LENGTH);
934 1 return 0;
935 }
936
937 //=============================================================================
938
939 1 int readCustomInletParams(int i, char* tok[], int ntoks)
940 //
941 // Input: i = inlet index
942 // tok[] = array of string tokens
943 // ntoks = number of tokens
944 // Output: returns an error code
945 // Purpose: extracts custom inlet parameters from a set of string tokens.
946 //
947 {
948 int c; // capture curve index
949
950
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if (ntoks < 3) return error_setInpError(ERR_ITEMS, "");
951 else
952 {
953 1 c = project_findObject(CURVE, tok[2]);
954
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if (c < 0) return error_setInpError(ERR_NAME, tok[2]);
955 }
956 1 InletDesigns[i].customCurve = c;
957 1 return 0;
958 }
959
960 //=============================================================================
961
962 14 void initInletStats(TInlet* inlet)
963 //
964 // Input: inlet = an inlet object placed in a conduit link
965 // Output: none
966 // Purpose: initializes the performance statistics of an inlet.
967 //
968 {
969
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (inlet)
970 {
971 14 inlet->flowCapture = 0.0;
972 14 inlet->backflow = 0.0;
973 14 inlet->stats.flowPeriods = 0;
974 14 inlet->stats.capturePeriods = 0;
975 14 inlet->stats.backflowPeriods = 0;
976 14 inlet->stats.peakFlow = 0.0;
977 14 inlet->stats.peakFlowCapture = 0;
978 14 inlet->stats.avgFlowCapture = 0;
979 14 inlet->stats.bypassFreq = 0;
980 }
981 14 }
982
983 //=============================================================================
984
985 8308 void updateInletStats(TInlet* inlet, double q)
986 //
987 // Input: inlet = an inlet object placed in a conduit link
988 // q = inlet's approach flow (cfs)
989 // Output: none
990 // Purpose: updates the performance statistics of an inlet.
991 //
992 {
993 8308 double qCapture = inlet->flowCapture,
994 8308 qBackflow = inlet->backflow,
995 8308 qNet = qCapture - qBackflow,
996 8308 qBypass = q - qNet,
997 8308 fCapture = 0.0;
998
999 // --- check for no flow condition
1000
3/4
✓ Branch 0 taken 488 times.
✓ Branch 1 taken 7820 times.
✓ Branch 2 taken 488 times.
✗ Branch 3 not taken.
8308 if (q < MIN_RUNOFF_FLOW && qBackflow <= 0.0) return;
1001 7820 inlet->stats.flowPeriods++;
1002
1003 // --- there is positive net flow from inlet to capture node
1004
2/2
✓ Branch 0 taken 6414 times.
✓ Branch 1 taken 1406 times.
7820 if (qNet > 0.0)
1005 {
1006 6414 inlet->stats.capturePeriods++;
1007 6414 fCapture = qNet / q;
1008
2/2
✓ Branch 0 taken 6409 times.
✓ Branch 1 taken 5 times.
6414 fCapture = MIN(fCapture, 1.0);
1009 6414 inlet->stats.avgFlowCapture += fCapture;
1010
2/2
✓ Branch 0 taken 6142 times.
✓ Branch 1 taken 272 times.
6414 if (qBypass > MIN_RUNOFF_FLOW) inlet->stats.bypassFreq++;
1011 }
1012
1013 // --- otherwise inlet receives backflow from capture node
1014 1406 else inlet->stats.backflowPeriods++;
1015
1016 // --- update peak flow stats
1017
2/2
✓ Branch 0 taken 1943 times.
✓ Branch 1 taken 5877 times.
7820 if (q > inlet->stats.peakFlow)
1018 {
1019 1943 inlet->stats.peakFlow = q;
1020 1943 inlet->stats.peakFlowCapture = fCapture * 100.0;
1021 }
1022 }
1023
1024 //=============================================================================
1025
1026 2 void writeStreetStatsHeader()
1027 //
1028 // Input: none
1029 // Output: none
1030 // Purpose: writes column headers for Street Flow Summary table to SWMM's report file.
1031 //
1032 {
1033 2 report_writeLine("");
1034 2 report_writeLine("*******************");
1035 2 report_writeLine("Street Flow Summary");
1036 2 report_writeLine("*******************");
1037 2 report_writeLine("");
1038 2 fprintf(Frpt.file,
1039 "\n ---------------------------------------------------------------------------------------------------------------------------------------"
1040 "\n Peak Avg. Bypass Back Peak Peak"
1041 "\n Peak Maximum Maximum Flow Flow Flow Flow Capture Bypass"
1042 "\n Flow Spread Depth Inlet Inlet Inlet Capture Capture Freq Freq / Inlet Flow");
1043
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (UnitSystem == US) fprintf(Frpt.file,
1044 "\n Street Conduit %3s ft ft Design Location Count Pcnt Pcnt Pcnt Pcnt %3s %3s",
1045 FlowUnitWords[FlowUnits], FlowUnitWords[FlowUnits], FlowUnitWords[FlowUnits]);
1046 else fprintf(Frpt.file,
1047 "\n Street Conduit %3s m m Design Location Pcnt Pcnt Pcnt Pcnt %3s %3s",
1048 FlowUnitWords[FlowUnits], FlowUnitWords[FlowUnits], FlowUnitWords[FlowUnits]);
1049 2 fprintf(Frpt.file,
1050 "\n ---------------------------------------------------------------------------------------------------------------------------------------");
1051 2 }
1052
1053 //=============================================================================
1054
1055 12 void writeStreetStats(int link)
1056 //
1057 // Input: link = index of a conduit link containing an inlet
1058 // Output: none
1059 // Purpose: writes flow statistics for a Street conduit and its inlet to
1060 // SWMM's report file.
1061 //
1062 {
1063 int k, t, placement;
1064 double maxSpread, maxDepth, maxFlow;
1065 12 double fp, cp, afc = 0.0, bpf = 0.0;
1066 TInlet* inlet;
1067
1068 // --- retrieve street parameters
1069 12 k = Link[link].subIndex;
1070 12 t = Link[link].xsect.transect;
1071 12 inlet = Link[link].inlet;
1072
1073 // --- get recorded max flow and depth
1074 12 maxFlow = LinkStats[link].maxFlow;
1075 12 maxDepth = LinkStats[link].maxDepth;
1076
1077 // --- SWMM's spread (flow width) at max depth
1078 12 maxSpread = xsect_getWofY(&Link[link].xsect, maxDepth) / Street[t].sides;
1079
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 maxSpread = MIN(maxSpread, Street[t].width);
1080 /*
1081 // HEC-22's spread based on max flow (doesn't account for backwater)
1082 Sx = Street[t].slope;
1083 a = Street[t].gutterDepression;
1084 W = Street[t].gutterWidth;
1085 n = Street[t].roughness;
1086 Qfactor = (0.56 / n) * sqrt(Conduit[k].slope) * pow(Sx, 1.67);
1087 maxSpread = getFlowSpread(maxFlow / Street[t].sides);
1088 maxSpread = MIN(maxSpread, Street[t].width);
1089 */
1090 // --- write street stats
1091 12 fprintf(Frpt.file, "\n %-16s", Link[link].ID);
1092 12 fprintf(Frpt.file, " %9.3f", maxFlow * UCF(FLOW));
1093 12 fprintf(Frpt.file, " %9.3f", maxSpread * UCF(LENGTH));
1094 12 fprintf(Frpt.file, " %9.3f", maxDepth * UCF(LENGTH));
1095
1096 // --- write inlet stats
1097
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (inlet)
1098 {
1099 12 fprintf(Frpt.file, " %-16s", InletDesigns[inlet->designIndex].ID);
1100 12 placement = getInletPlacement(inlet, Link[inlet->linkIndex].node2);
1101
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
12 if (placement == ON_GRADE)
1102 8 fprintf(Frpt.file, " ON-GRADE");
1103 else
1104 4 fprintf(Frpt.file, " ON-SAG ");
1105 12 fprintf(Frpt.file, " %5d", inlet->numInlets);
1106 12 fp = inlet->stats.flowPeriods / 100.0;
1107
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (fp > 0.0)
1108 {
1109 12 cp = inlet->stats.capturePeriods / 100.0;
1110 12 fprintf(Frpt.file, " %7.2f", inlet->stats.peakFlowCapture);
1111
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (cp > 0.0)
1112 {
1113 12 afc = inlet->stats.avgFlowCapture / cp;
1114 12 bpf = inlet->stats.bypassFreq / cp;
1115 }
1116 12 fprintf(Frpt.file, " %7.2f", afc);
1117 12 fprintf(Frpt.file, " %7.2f", bpf);
1118 12 fprintf(Frpt.file, " %7.2f", inlet->stats.backflowPeriods / fp);
1119 12 fprintf(Frpt.file, " %7.2f", (maxFlow / Street[t].sides) * UCF(FLOW) *
1120 12 0.01 * inlet->stats.peakFlowCapture / inlet->numInlets);
1121 12 fprintf(Frpt.file, " %7.2f", maxFlow * UCF(FLOW) * 0.01 *
1122 12 (100.0 - inlet->stats.peakFlowCapture));
1123 }
1124 }
1125 12 }
1126
1127 //=============================================================================
1128
1129 8320 int getInletPlacement(TInlet* inlet, int j)
1130 //
1131 // Input: inlet = an inlet object placed in a conduit link
1132 // j = index of inlet's bypass node
1133 // Output: returns type of inlet placement
1134 // Purpose: determines actual placement for an inlet with AUTOMATIC placement.
1135 //
1136 {
1137
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8320 times.
8320 if (inlet->placement == AUTOMATIC)
1138 {
1139 if (Node[j].degree > 0) return ON_GRADE;
1140 else return ON_SAG;
1141 }
1142 8320 else return inlet->placement;
1143 }
1144
1145 //=============================================================================
1146
1147 7778 void getConduitGeometry(TInlet* inlet)
1148 //
1149 // Input: inlet = an inlet object placed in a conduit link
1150 // Output: none
1151 // Purpose: assigns properties of an inlet's conduit to
1152 // module-level shared variables used by other functions.
1153 //
1154 {
1155 7778 int linkIndex = inlet->linkIndex;
1156 7778 int t, k = Link[linkIndex].subIndex;
1157
1158 7778 SL = Conduit[k].slope; // longitudinal slope
1159 7778 Beta = Conduit[k].beta; // 1.486 * sqrt(SL) / n
1160 7778 xsect = &Link[linkIndex].xsect;
1161
1162 // --- if conduit has a Street cross section
1163
2/2
✓ Branch 0 taken 5796 times.
✓ Branch 1 taken 1982 times.
7778 if (xsect->type == STREET_XSECT)
1164 {
1165 5796 t = xsect->transect;
1166 5796 Sx = Street[t].slope; // street cross slope
1167 5796 a = Street[t].gutterDepression; // gutter depression
1168 5796 W = Street[t].gutterWidth; // gutter width
1169 5796 n = Street[t].roughness; // street roughness
1170 5796 Nsides = Street[t].sides; // 1 or 2 sided street
1171 5796 Tcrown = Street[t].width; // distance from curb to crown
1172 5796 Qfactor = inlet->flowFactor; // factor used in Izzard's eqn.
1173
1174 // --- add inlet's local depression to street's continuous depression
1175
3/4
✓ Branch 0 taken 5796 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 483 times.
✓ Branch 3 taken 5313 times.
5796 if (inlet && inlet->localDepress * inlet->localWidth > 0)
1176 {
1177 483 a += inlet->localDepress; // inlet depression
1178 483 W = inlet->localWidth; // inlet depressed width
1179 }
1180
1181 // --- slope of depressed gutter section
1182
2/2
✓ Branch 0 taken 483 times.
✓ Branch 1 taken 5313 times.
5796 if (W * a > 0.0) Sw = Sx + a / W;
1183 5313 else Sw = Sx;
1184 }
1185
1186 // --- conduit has rectangular or trapezoidal cross section
1187 else
1188 {
1189 1982 a = 0.0;
1190 1982 W = 0.0;
1191 1982 n = Conduit[k].roughness;
1192 1982 Nsides = 1;
1193 1982 Sx = 0.01;
1194 1982 Sw = Sx;
1195 }
1196 7778 }
1197
1198 //=============================================================================
1199
1200 8676 double getFlowSpread(double Q)
1201 //
1202 // Input: Q = conduit flow rate (cfs)
1203 // Output: returns width of flow spread (ft)
1204 // Purpose: computes width of flow spread across a Street cross section using
1205 // HEC-22 equations derived from Izzard's form of the Manning eqn.
1206 //
1207 {
1208 int iter;
1209 double f, f1, Sr, Ts1, Ts2, Tw, Qs, Eo;
1210
1211 8676 f = Qfactor; // = (0.56/n) * SL^0.5 * Sx^1.67
1212
1213 // --- no depressed curb
1214
1/2
✓ Branch 0 taken 8676 times.
✗ Branch 1 not taken.
8676 if (a == 0.0)
1215 {
1216 8676 Ts1 = pow(Q / f, 0.375); //HEC-22 Eq(4-2)
1217 }
1218 else
1219 {
1220 // --- check if spread is within curb width
1221 f1 = f * pow((a / W) / Sx, 1.67);
1222 Tw = pow(Q / f1, 0.375); //HEC-22 Eq(4-2)
1223 if (Tw <= W) Ts1 = Tw;
1224 else
1225 {
1226 // --- spread extends beyond curb width
1227 Sr = (Sx + a / W) / Sx;
1228 iter = 1;
1229 Ts1 = pow(Q / f, 0.375) - W;
1230 if (Ts1 <= 0) Ts1 = Tw - W;
1231 while (iter < 11)
1232 {
1233 Eo = getEo(Sr, Ts1, W);
1234 Qs = (1.0 - Eo) * Q; //HEC-22 Eq(4-6)
1235 Ts2 = pow(Qs / f, 0.375); //HEC-22 Eq(4-2)
1236 if (fabs(Ts2 - Ts1) < 0.01) break;
1237 Ts1 = Ts2;
1238 iter++;
1239 }
1240 Ts1 = Ts2 + W;
1241 }
1242 }
1243
2/2
✓ Branch 0 taken 264 times.
✓ Branch 1 taken 8412 times.
8676 return MIN(Ts1, Tcrown);
1244 }
1245
1246 //=============================================================================
1247
1248 double getEo(double Sr, double Ts, double w)
1249 //
1250 // Input: Sr = ratio of gutter slope to street cross slope
1251 // Ts = amount of flow spread outside of gutter width (ft)
1252 // w = gutter width (ft)
1253 // Output: returns ratio of gutter flow to total flow in street cross section
1254 // Purpose: solves HEC-22 Eq. (4-4) for Eo with Ts/w substituted for
1255 // (T/w) - 1 where Ts = T - w.
1256 //
1257 {
1258 double x;
1259 x = Sr / (Ts / w);
1260 x = pow((1.0 + x), 2.67) - 1.0;
1261 x = 1.0 + Sr / x;
1262 return 1.0 / x;
1263 }
1264
1265 //=============================================================================
1266
1267 4400 double getOnGradeCapturedFlow(TInlet* inlet, double q, double d)
1268 //
1269 // Input: inlet = an inlet object placed in a conduit link
1270 // q = flow in link prior to any inlet capture (cfs)
1271 // d = flow depth seen by inlet (ft)
1272 // Output: returns flow captured by the inlet (cfs)
1273 // Purpose: computes flow captured by an inlet placed on-grade.
1274 //
1275 // An inlet object placed in a conduit can have multiple inlets of
1276 // the same type distributed along the conduit's length that all
1277 // send their captured flow to the same sewer node. This function
1278 // finds the total captured flow as each individual inlet is analyzed
1279 // sequentially, where its approach flow has been reduced by the
1280 // amount of flow captured by prior inlets.
1281 {
1282 int i,
1283 linkIndex; // index of link containing inlets
1284 double qApproach, // single inlet's approach flow (cfs)
1285 qc, // single inlet's captured flow (cfs)
1286 qCaptured, // total flow captured by link's inlets (cfs)
1287 qBypassed, // total flow bypassed by link's inlets (cfs)
1288 qMax; // max. flow that a single inlet can capture (cfs)
1289
1290
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4400 times.
4400 if (inlet->numInlets == 0) return 0.0;
1291 4400 linkIndex = inlet->linkIndex;
1292
1293 // --- check that link has flow
1294 4400 qApproach = q;
1295
2/2
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 4338 times.
4400 if (qApproach < MIN_RUNOFF_FLOW) return 0.0;
1296
1297 // --- store conduit geometry in shared variables
1298 4338 getConduitGeometry(inlet);
1299
1300 // --- adjust flow for 2-sided street
1301 4338 qApproach /= Nsides;
1302 4338 qBypassed = qApproach;
1303 4338 qCaptured = 0.0;
1304
1305 // --- set limit on max. flow captured per inlet
1306 4338 qMax = BIG;
1307
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4338 times.
4338 if (inlet->flowLimit > 0.0) qMax = inlet->flowLimit;
1308
1309 // --- evaluate each inlet
1310
2/2
✓ Branch 0 taken 4338 times.
✓ Branch 1 taken 4226 times.
8564 for (i = 1; i <= inlet->numInlets; i++)
1311 {
1312 4338 qc = getOnGradeInletCapture(inlet->designIndex, qBypassed, d) *
1313 4338 inlet->clogFactor;
1314
1/2
✓ Branch 0 taken 4338 times.
✗ Branch 1 not taken.
4338 qc = MIN(qc, qMax);
1315
1/2
✓ Branch 0 taken 4338 times.
✗ Branch 1 not taken.
4338 qc = MIN(qc, qBypassed);
1316 4338 qCaptured += qc;
1317 4338 qBypassed -= qc;
1318
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 4226 times.
4338 if (qBypassed < MIN_RUNOFF_FLOW) break;
1319 }
1320 4338 return qCaptured *= Nsides;
1321 }
1322
1323 //=============================================================================
1324
1325 4338 double getOnGradeInletCapture(int i, double Q, double d)
1326 //
1327 // Input: i = an InletDesigns index
1328 // Q = flow rate seen by inlet (cfs)
1329 // d = flow depth seen by inlet (ft)
1330 // Output: returns captured flow rate (cfs)
1331 // Purpose: finds the flow captured by a single on-grade inlet.
1332 //
1333 {
1334 4338 double Q1 = Q, Qc = 0.0, Lsweep = 0.0, Lcurb = 0.0, Lgrate = 0.0;
1335
1336 // --- drop curb inlet (in non-Street conduit) only operates in on sag mode
1337
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4338 times.
4338 if (InletDesigns[i].type == DROP_CURB_INLET)
1338 {
1339 Qc = getOnSagInletCapture(i, d);
1340 return MIN(Qc, Q);
1341 }
1342
1343 // --- drop grate inlet (in non-Street conduit)
1344
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4338 times.
4338 if (InletDesigns[i].type == DROP_GRATE_INLET)
1345 {
1346 Qc = getGrateInletCapture(i, Q);
1347 return MIN(Qc, Q);
1348 }
1349
1350 // --- Remaining inlet types apply to Street conduits
1351
1352 // --- find flow spread
1353 4338 T = getFlowSpread(Q);
1354
1355 // --- slotted inlet (behaves as a curb opening inlet per HEC-22)
1356
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4338 times.
4338 if (InletDesigns[i].type == SLOTTED_INLET)
1357 {
1358 Qc = getCurbInletCapture(Q, InletDesigns[i].slottedInlet.length);
1359 return MIN(Qc, Q);
1360 }
1361
1362 4338 Lcurb = InletDesigns[i].curbInlet.length;
1363 4338 Lgrate = InletDesigns[i].grateInlet.length;
1364
1365 // --- curb opening inlet
1366
1/2
✓ Branch 0 taken 4338 times.
✗ Branch 1 not taken.
4338 if (Lcurb > 0.0)
1367 {
1368 4338 Lsweep = Lcurb - Lgrate;
1369
1/2
✓ Branch 0 taken 4338 times.
✗ Branch 1 not taken.
4338 if (Lsweep > 0.0)
1370 {
1371 4338 Qc = getCurbInletCapture(Q1, Lsweep);
1372 4338 Q1 -= Qc;
1373 }
1374 }
1375
1376 // --- grate inlet
1377
2/4
✓ Branch 0 taken 4338 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4338 times.
✗ Branch 3 not taken.
4338 if (Lgrate > 0.0 && Q1 > 0.0)
1378 {
1379
1/2
✓ Branch 0 taken 4338 times.
✗ Branch 1 not taken.
4338 if (Q1 != Q) T = getFlowSpread(Q1);
1380 4338 Qc += getGrateInletCapture(i, Q1);
1381 }
1382 4338 return Qc;
1383 }
1384
1385 //=============================================================================
1386
1387 4338 double getGrateInletCapture(int i, double Q)
1388 //
1389 // Input: i = inlet type index
1390 // Q = flow rate seen by inlet (cfs)
1391 // Output: returns captured flow rate (cfs)
1392 // Purpose: finds the flow captured by an on-grade grate inlet.
1393 //
1394 {
1395 int grateType;
1396 double Lg, // grate length (ft)
1397 Wg, // grate width (ft)
1398 A, // total cross section flow area (ft2)
1399 Y, // flow depth (ft)
1400 Eo, // ratio of gutter to total flow
1401 V, // flow velocity (ft/s)
1402 Vo, // splash-over velocity (ft/s)
1403 4338 Qo = Q, // flow over street area (cfs)
1404 4338 Rf = 1.0, // ratio of intercepted to total frontal flow
1405 4338 Rs = 0.0; // ratio of intercepted to total side flow
1406
1407 // xsect, a, W, & Sx were from getConduitGeometry(). T was from getFlowSpread().
1408
1409 4338 Lg = InletDesigns[i].grateInlet.length;
1410 4338 Wg = InletDesigns[i].grateInlet.width;
1411
1412 // --- flow ratio for drop inlet
1413
2/4
✓ Branch 0 taken 4338 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4338 times.
4338 if (xsect->type == TRAPEZOIDAL || xsect->type == RECT_OPEN)
1414 {
1415 A = xsect_getAofS(xsect, Q / Beta);
1416 Y = xsect_getYofA(xsect, A);
1417 T = xsect_getWofY(xsect, Y);
1418 Eo = Beta * pow(Y*Wg, 1.67) / pow(Wg + 2*Y, 0.67) / Q;
1419 if (Wg > 0.99*xsect->yBot && xsect->type == TRAPEZOIDAL && xsect->sBot > 0.0)
1420 {
1421 Wg = xsect->yBot;
1422 Sx = 1.0 / xsect->sBot;
1423 }
1424 }
1425
1426 // --- flow ratio & area for conventional street gutter
1427
1/2
✓ Branch 0 taken 4338 times.
✗ Branch 1 not taken.
4338 else if (a == 0.0)
1428 {
1429 4338 A = T * T * Sx / 2.0;
1430 4338 Eo = getGutterFlowRatio(Wg); // flow ratio based on grate width
1431
2/2
✓ Branch 0 taken 4204 times.
✓ Branch 1 taken 134 times.
4338 if (T >= Tcrown) Qo = Qfactor * pow(Tcrown, 2.67);
1432 }
1433
1434 // --- flow ratio & area for composite street gutter
1435 else
1436 {
1437 // --- spread confined to gutter
1438 if (T <= W) A = T * T * Sw / 2.0;
1439
1440 // --- spread beyond gutter width
1441 else A = (T * T * Sx + a * W) / 2.0;
1442
1443 // flow ratio based on gutter width corrected for grate width
1444 Eo = getGutterFlowRatio(W);
1445 if (Eo < 1.0)
1446 {
1447 if (T >= Tcrown)
1448 Qo = Qfactor * pow(Tcrown, 2.67) / (1.0 - Eo);
1449 Eo = Eo * getGutterAreaRatio(Wg, A); //HEC-22 Eq(4-20a)
1450 }
1451 }
1452
1453 // --- flow and splash-over velocities
1454 4338 V = Qo / A;
1455 4338 grateType = InletDesigns[i].grateInlet.type;
1456
2/4
✓ Branch 0 taken 4338 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4338 times.
4338 if (grateType < 0 || grateType == GENERIC)
1457 Vo = InletDesigns[i].grateInlet.splashVeloc;
1458 else
1459 4338 Vo = getSplashOverVelocity(grateType, Lg);
1460
1461 // --- frontal flow capture efficiency
1462
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4338 times.
4338 if (V > Vo) Rf = 1.0 - 0.09 * (V - Vo); //HEC-22 Eq(4-18)
1463
1464 // --- side flow capture efficiency
1465
1/2
✓ Branch 0 taken 4338 times.
✗ Branch 1 not taken.
4338 if (Eo < 1.0)
1466 {
1467 4338 Rs = 1.0 / (1.0 + (0.15 * pow(V, 1.8) /
1468 4338 Sx / pow(Lg, 2.3))); //HEC-22 Eq(4-19)
1469 }
1470
1471 // --- return total flow captured
1472 4338 return Q * (Rf * Eo + Rs * (1.0 - Eo)); //HEC-22 Eq(4-21)
1473 }
1474
1475 //=============================================================================
1476
1477 4338 double getCurbInletCapture(double Q, double L)
1478 //
1479 // Input: Q = flow rate seen by inlet (cfs)
1480 // L = length of inlet opening (ft)
1481 // Output: returns captured flow rate (cfs)
1482 // Purpose: finds the flow captured by an on-grade curb opening inlet.
1483 //
1484 {
1485 4338 double Se = Sx, // equivalent gutter slope
1486 Lt, // length for full capture
1487 Sr, // ratio of gutter slope to cross slope
1488 4338 Eo = 0.0, // ratio of gutter to total flow
1489 4338 E = 1.0; // capture efficiency
1490
1491 // a, W, Sx, Sw, SL, & n were from getConduitGeometry(). T was from getFlowSpread().
1492
1493 // --- for depressed gutter section
1494
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4338 times.
4338 if (a > 0.0)
1495 {
1496 Sr = Sw / Sx;
1497 Eo = getEo(Sr, T-W, W);
1498 Se = Sx + (a/W) * Eo; //HEC-22 Eq(4-24)
1499 }
1500
1501 // --- opening length for full capture
1502 4338 Lt = 0.6 * pow(Q, 0.42) * pow(SL, 0.3) *
1503 4338 pow(1.0/(n*Se), 0.6); //HEC-22 Eq(4-22a)
1504
1505 // --- capture efficiency for actual opening length
1506
1/2
✓ Branch 0 taken 4338 times.
✗ Branch 1 not taken.
4338 if (L < Lt)
1507 {
1508 4338 E = 1.0 - (L/Lt);
1509 4338 E = 1 - pow(E, 1.8); //HEC-22 Eq(4-23)
1510 }
1511
1/2
✓ Branch 0 taken 4338 times.
✗ Branch 1 not taken.
4338 E = MIN(E, 1.0);
1512
1/2
✓ Branch 0 taken 4338 times.
✗ Branch 1 not taken.
4338 E = MAX(E, 0.0);
1513 4338 return E * Q;
1514 }
1515
1516 //=============================================================================
1517
1518 4338 double getGutterFlowRatio(double w)
1519 //
1520 // Input: w = gutter width (ft)
1521 // Output: returns a flow ratio
1522 // Purpose: computes the ratio of flow over a width of gutter to the total
1523 // flow in a street cross section.
1524 //
1525 {
1526
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4338 times.
4338 if (T <= w) return 1.0;
1527
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4338 times.
4338 else if (a > 0.0)
1528 return getEo(Sw / Sx, T - w, w);
1529 else
1530 4338 return 1.0 - pow((1.0 - w / T), 2.67); //HEC-22 Eq(4-16)
1531 }
1532
1533 //=============================================================================
1534
1535 double getGutterAreaRatio(double Wg, double A)
1536 //
1537 // Input: Wg = width of grate inlet (ft)
1538 // A = total flow area (ft2)
1539 // Output: returns an area ratio
1540 // Purpose: computes the ratio of the flow area above a grate to the flow
1541 // area above depressed gutter in a street cross section.
1542 //
1543 {
1544 double As, // flow area beyond gutter width (ft2)
1545 Ag; // flow area over grate width (ft2)
1546
1547 if (Wg >= W) return 1.0;
1548 if (T <= Wg) return 1.0;
1549 if (T <= W) return Wg / T;
1550 As = 0.5 * SQR((T - W)) * Sx;
1551 Ag = Wg * ( (T * Sx) + a - (Wg * Sw / 2.) );
1552 return Ag / (A - As);
1553 }
1554
1555 //=============================================================================
1556
1557 4338 double getSplashOverVelocity(int grateType, double L)
1558 //
1559 // Input: grateType = grate inlet type code
1560 // L = length of grate inlet (ft)
1561 // Output: returns a splash over velocity
1562 // Purpose: computes the splash over velocity for a standard type of grate
1563 // inlet as a function of its length.
1564 //
1565 {
1566 4338 return SplashCoeffs[grateType][0] +
1567 4338 SplashCoeffs[grateType][1] * L -
1568 8676 SplashCoeffs[grateType][2] * L * L +
1569 4338 SplashCoeffs[grateType][3] * L * L * L;
1570 }
1571
1572 //=============================================================================
1573
1574 3426 double getOnSagCapturedFlow(TInlet* inlet, double q, double d)
1575 //
1576 // Input: inlet = an inlet object placed in a conduit link
1577 // q = flow in link prior to any inlet capture (cfs)
1578 // d = flow depth seen by inlet (ft)
1579 // Output: returns flow captured by the inlet (cfs)
1580 // Purpose: computes flow captured by an inlet placed on-sag.
1581 //
1582 {
1583 int linkIndex, designIndex, totalInlets;
1584 3426 double qCaptured = 0.0, qMax = BIG;
1585
1586
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3426 times.
3426 if (inlet->numInlets == 0) return 0.0;
1587 3426 totalInlets = Nsides * inlet->numInlets;
1588 3426 linkIndex = inlet->linkIndex;
1589 3426 designIndex = inlet->designIndex;
1590
1591 // --- store conduit geometry in shared variables
1592 3426 getConduitGeometry(inlet);
1593
1594 // --- set flow limit per inlet
1595
1/2
✓ Branch 0 taken 3426 times.
✗ Branch 1 not taken.
3426 if (inlet->flowLimit > 0.0)
1596 3426 qMax = inlet->flowLimit;
1597
1598 // --- find nominal flow captured by inlet
1599 3426 qCaptured = getOnSagInletCapture(designIndex, fabs(d));
1600
1601 // --- find actual flow captured by the inlet
1602 3426 qCaptured *= inlet->clogFactor;
1603
2/2
✓ Branch 0 taken 2663 times.
✓ Branch 1 taken 763 times.
3426 qCaptured = MIN(qCaptured, qMax);
1604 3426 qCaptured *= (double)totalInlets;
1605 3426 return qCaptured;
1606 }
1607
1608 //=============================================================================
1609
1610 3426 double getOnSagInletCapture(int i, double d)
1611 //
1612 // Input: i = inlet type index
1613 // d = water level seen by inlet (ft)
1614 // Output: returns captured flow rate (cfs)
1615 // Purpose: finds the flow captured by an on-sag inlet.
1616 //
1617 {
1618 3426 double Lsweep = 0.0, Lcurb = 0.0, Lgrate = 0.0;
1619 3426 double Qsw = 0.0, //Sweeper curb opening weir flow
1620 3426 Qso = 0.0, //Sweeper curb opening orifice flow
1621 3426 Qgw = 0.0, //Grate weir flow
1622 3426 Qgo = 0.0, //Grate orifice flow
1623 3426 Qcw = 0.0, //Curb opening weir flow
1624 3426 Qco = 0.0; //Curb opening orifice flow
1625
1626
2/2
✓ Branch 0 taken 482 times.
✓ Branch 1 taken 2944 times.
3426 if (InletDesigns[i].slottedInlet.length > 0.0)
1627 482 return getOnSagSlottedFlow(i, d);
1628
1629 2944 Lgrate = InletDesigns[i].grateInlet.length;
1630
2/2
✓ Branch 0 taken 1472 times.
✓ Branch 1 taken 1472 times.
2944 if (Lgrate > 0.0) findOnSagGrateFlows(i, d, &Qgw, &Qgo);
1631
1632 2944 Lcurb = InletDesigns[i].curbInlet.length;
1633
2/2
✓ Branch 0 taken 1472 times.
✓ Branch 1 taken 1472 times.
2944 if (Lcurb > 0.0)
1634 {
1635 1472 Lsweep = Lcurb - Lgrate;
1636
1/2
✓ Branch 0 taken 1472 times.
✗ Branch 1 not taken.
1472 if (Lsweep > 0.0) findOnSagCurbFlows(i, d, Lsweep, &Qsw, &Qso);
1637
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1472 times.
1472 if (Qgo > 0.0) findOnSagCurbFlows(i, d, Lgrate, &Qcw, &Qco);
1638 }
1639 2944 return Qgw + Qgo + Qsw + Qso + Qco;
1640 }
1641
1642 //=============================================================================
1643
1644 1472 void findOnSagGrateFlows(int i, double d, double *Qw, double *Qo)
1645 //
1646 // Input: i = inlet type index
1647 // d = water level seen by inlet (ft)
1648 // Output: Qw = flow captured in weir mode (cfs)
1649 // Qo = flow captured in orifice mode (cfs)
1650 // Purpose: finds the flow captured by an on-sag grate inlet.
1651 //
1652 {
1653 1472 int grateType = InletDesigns[i].grateInlet.type;
1654 1472 double Lg = InletDesigns[i].grateInlet.length;
1655 1472 double Wg = InletDesigns[i].grateInlet.width;
1656 double P, // grate perimeter (ft)
1657 Ao, // grate opening area (ft2)
1658 di; // average flow depth across grate (ft)
1659
1660 // --- for drop grate inlets
1661
2/2
✓ Branch 0 taken 990 times.
✓ Branch 1 taken 482 times.
1472 if (InletDesigns[i].type == DROP_GRATE_INLET)
1662 {
1663 990 di = d;
1664 990 P = 2.0 * (Lg + Wg);
1665 }
1666
1667 // --- for gutter grate inlets:
1668 else
1669 {
1670 // --- check for spread within grate width
1671
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 469 times.
482 if (d <= Wg * Sw)
1672 13 Wg = d / Sw;
1673
1674 // --- avergage depth over grate
1675 482 di = d - (Wg / 2.0) * Sw;
1676
1677 // --- effective grate perimeter
1678 482 P = Lg + 2.0 * Wg;
1679 }
1680
1681
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1472 times.
1472 if (grateType == GENERIC)
1682 Ao = Lg * Wg * InletDesigns[i].grateInlet.fracOpenArea;
1683 else
1684 1472 Ao = Lg * Wg * GrateOpeningRatios[grateType];
1685
1686 // --- weir flow applies (based on depth where result of
1687 // weir eqn. equals result of orifice eqn.)
1688
1689
1/2
✓ Branch 0 taken 1472 times.
✗ Branch 1 not taken.
1472 if (d <= 1.79 * Ao / P)
1690 {
1691 1472 *Qw = 3.0 * P * pow(di, 1.5); //HEC-22 Eq(4-26)
1692 }
1693
1694 // --- orifice flow applies
1695 else
1696 {
1697 *Qo = 0.67 * Ao * sqrt(2.0 * 32.16 * di); //HEC-22 Eq(4-27)
1698 }
1699 1472 }
1700
1701 //=============================================================================
1702
1703 1472 void findOnSagCurbFlows(int i, double d, double L, double *Qw, double *Qo)
1704 //
1705 // Input: i = inlet type index
1706 // d = water level seen by inlet (ft)
1707 // L = length of curb opening (ft)
1708 // Output: Qw = flow captured in weir mode (cfs)
1709 // Qo = flow captured in orifice mode (cfs)
1710 // Purpose: finds the flow captured by an on-sag curb opening inlet.
1711 //
1712 {
1713 1472 int throatAngle = InletDesigns[i].curbInlet.throatAngle;
1714 1472 double h = InletDesigns[i].curbInlet.height;
1715 double Qweir, Qorif, P;
1716 double dweir, dorif, r;
1717
1718 // --- check for orifice flow
1719
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1472 times.
1472 if (L <= 0.0) return;
1720
2/2
✓ Branch 0 taken 990 times.
✓ Branch 1 taken 482 times.
1472 if (InletDesigns[i].type == DROP_CURB_INLET) L = L * 4.0;
1721 1472 dorif = 1.4 * h;
1722
2/2
✓ Branch 0 taken 420 times.
✓ Branch 1 taken 1052 times.
1472 if (d > dorif)
1723 {
1724 420 *Qo = getCurbOrificeFlow(d, h, L, throatAngle);
1725 420 return;
1726 }
1727
1728 // --- for uniform cross slope or very long opening
1729
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1052 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1052 if (a == 0.0 || L > 12.0)
1730 {
1731 // --- check for weir flow
1732 1052 dweir = h;
1733
2/2
✓ Branch 0 taken 1045 times.
✓ Branch 1 taken 7 times.
1052 if (d < dweir)
1734 {
1735 1045 *Qw = 3.0 * L * pow(d, 1.5); //HEC-22 Eq(4-30)
1736 1045 return;
1737 }
1738 7 else Qweir = 3.0 * L * pow(dweir, 1.5);
1739 }
1740
1741 // --- for depressed gutter
1742 else
1743 {
1744 // --- check for weir flow
1745 P = L + 1.8 * W;
1746 dweir = h + a;
1747 if (d < dweir)
1748 {
1749 *Qw = 2.3 * P * pow(d, 1.5); //HEC-22 Eq(4-28)
1750 return;
1751 }
1752 else Qweir = 2.3 * P * pow(dweir, 1.5);
1753 }
1754
1755 // --- interpolate between Qweir at depth dweir and Qorif at depth dorif
1756 7 Qorif = getCurbOrificeFlow(dorif, h, L, throatAngle);
1757 7 r = (d - dweir) / (dorif - dweir);
1758 7 *Qw = (1.0 -r) * Qweir;
1759 7 *Qo = r * Qorif;
1760 }
1761
1762 //=============================================================================
1763
1764 427 double getCurbOrificeFlow(double di, double h, double L, int throatAngle)
1765 //
1766 // Input: di = water level at lip of inlet opening (ft)
1767 // h = height of curb opening (ft)
1768 // L = length of curb opening (ft)
1769 // throatAngle = type of throat angle in curb opening
1770 // Output: return flow captured by inlet (cfs)
1771 // Purpose: finds the flow captured by an on-sag curb opening inlet under
1772 // orifice flow conditions.
1773 //
1774 {
1775 427 double d = di;
1776
1/2
✓ Branch 0 taken 427 times.
✗ Branch 1 not taken.
427 if (throatAngle == HORIZONTAL_THROAT)
1777 427 d = di - h / 2.0;
1778 else if (throatAngle == INCLINED_THROAT)
1779 d = di - (h / 2.0) * 0.7071;
1780 427 return 0.67 * h * L * sqrt(2.0 * 32.16 * d); //HEC-22 Eq(4-31a)
1781 }
1782
1783 //=============================================================================
1784
1785 482 double getOnSagSlottedFlow(int i, double d)
1786 //
1787 // Input: i = inlet type index
1788 // d = water level seen by inlet (ft)
1789 // Output: returns captured flow rate (cfs)
1790 // Purpose: finds the flow captured by an on-sag slotted inlet.
1791 //
1792 // Note: weir flow = orifice flow at d = 2.587 * inlet width
1793 {
1794 482 double L = InletDesigns[i].slottedInlet.length;
1795 482 double w = InletDesigns[i].slottedInlet.width;
1796
1797
1/2
✓ Branch 0 taken 482 times.
✗ Branch 1 not taken.
482 if (d <= 2.587 * w)
1798 482 return 2.48 * L * pow(d, 1.5); //HEC-22 Eq(4-32)
1799 else
1800 return 0.8 * L * w * sqrt(64.32 * d); //HEC-22 Eq(4-33)
1801 }
1802
1803 //=============================================================================
1804
1805 3 void getBackflowRatios()
1806 //
1807 // Input: none
1808 // Output: overflow ratio for each inlet
1809 // Purpose: finds the fraction of the overflow produced by an inlet's capture
1810 // node that becomes backflow into the inlet.
1811 //
1812 // Note: when a capture node receives flow from two or more inlets
1813 // its backflow is divided among the inlets based on:
1814 // i) the fraction of total open area for standard inlets
1815 // ii) the fraction of total number of inlets for custom inlets
1816 {
1817 TInlet* inlet;
1818 double area;
1819 double f;
1820 int n;
1821
1822 // --- info for each node receiving flow from an inlet
1823 typedef struct
1824 {
1825 int numInletLinks; // total # inlet links
1826 int numStdInletLinks; // total # standard inlet links
1827 int numCustomInlets; // # custom inlets
1828 double totalInletArea; // open area of standard inlets
1829 } TInletNode;
1830 3 TInletNode* inletNodes = (TInletNode *) calloc(Nobjects[NODE], sizeof(TInletNode));
1831
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (inletNodes == NULL) return;
1832
1833 // --- Finds each inlet's contribution to its capture node
1834
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 3 times.
17 for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet)
1835 {
1836 14 n = inlet->nodeIndex;
1837 14 inletNodes[n].numInletLinks++;
1838 14 area = getInletArea(inlet);
1839
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1 time.
14 if (area > 0.0)
1840 {
1841 13 inletNodes[n].numStdInletLinks++;
1842 13 inletNodes[n].totalInletArea += area;
1843 }
1844 else
1845 1 inletNodes[n].numCustomInlets += inlet->numInlets;
1846 }
1847
1848 // --- find fraction of capture node's overflow that becomes inlet backflow
1849
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 3 times.
17 for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet)
1850 {
1851 // --- f is ratio of links with standard inlets to all inlet links
1852 // connected to receptor node n
1853 14 n = inlet->nodeIndex;
1854 14 f = (double) inletNodes[n].numStdInletLinks /
1855 14 (double) inletNodes[n].numInletLinks;
1856
1857 // --- backflow ratio depends if inlet is standard or custom (area = 0)
1858 14 area = getInletArea(inlet);
1859
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 13 times.
14 if (area == 0.0)
1860 1 inlet->backflowRatio = (double)inlet->numInlets /
1861 1 (double)inletNodes[n].numCustomInlets * (1. - f);
1862 else
1863 13 inlet->backflowRatio = area / inletNodes[n].totalInletArea * f;
1864 }
1865 3 free(inletNodes);
1866 }
1867
1868 //=============================================================================
1869
1870 28 double getInletArea(TInlet* inlet)
1871 //
1872 // Input: inlet = an inlet object placed in a conduit link
1873 // Output: returns the unclogged open area of the inlet (ft2)
1874 // Purpose: finds the total open flow area inlets placed in a conduit.
1875 //
1876 {
1877 28 double area = 0.0;
1878 double curbLength;
1879 28 int i = inlet->designIndex;
1880 28 int grateType = InletDesigns[i].grateInlet.type;
1881
1882
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 8 times.
28 if (InletDesigns[i].grateInlet.length > 0.0)
1883 {
1884 20 area = InletDesigns[i].grateInlet.length * InletDesigns[i].grateInlet.width;
1885
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (grateType == GENERIC)
1886 area *= InletDesigns[i].grateInlet.fracOpenArea;
1887 else
1888 20 area *= GrateOpeningRatios[grateType];
1889 }
1890
1891 28 curbLength = InletDesigns[i].curbInlet.length - InletDesigns[i].grateInlet.length;
1892
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 8 times.
28 if (curbLength > 0.0)
1893 20 area += curbLength * InletDesigns[i].curbInlet.height;
1894
1895
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 26 times.
28 if (InletDesigns[i].slottedInlet.length > 0.0)
1896 2 area = InletDesigns[i].slottedInlet.length * InletDesigns[i].slottedInlet.width;
1897 28 return area * inlet->numInlets * inlet->clogFactor;
1898 }
1899
1900 //=============================================================================
1901
1902 482 double getCustomCapturedFlow(TInlet* inlet, double q, double d)
1903 {
1904 482 int i = inlet->designIndex; // inlet's position in InletDesigns array
1905 int j; // counter for replicate inlets
1906 482 int sides = 1; // number of sides for inlet's street (1 or 2)
1907 int c; // an index into the Curve array
1908 double qApproach, // inlet's approach flow (cfs)
1909 qBypassed, // inlet's bypassed flow (cfs)
1910 qCaptured, // inlet's captured flow (cfs)
1911 qIncrement, // increment to captured flow (cfs)
1912 482 qMax = BIG; // user-supplied flow capture limit (cfs)
1913
1914
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 482 times.
482 if (inlet->numInlets == 0) return 0.0;
1915
1916 // --- set limit on max. flow captured per inlet
1917 482 qMax = BIG;
1918
1/2
✓ Branch 0 taken 482 times.
✗ Branch 1 not taken.
482 if (inlet->flowLimit > 0.0) qMax = inlet->flowLimit;
1919
1920 // --- get number of sides to a street xsection
1921 482 xsect = &Link[inlet->linkIndex].xsect;
1922
1/2
✓ Branch 0 taken 482 times.
✗ Branch 1 not taken.
482 if (xsect->type == STREET_XSECT)
1923 482 sides = Street[xsect->transect].sides;
1924
1925 // --- adjust flow for 2-sided street
1926 482 qApproach = q / sides;
1927 482 qBypassed = qApproach;
1928 482 qCaptured = 0.0;
1929
1930 // --- get index of inlet's capture curve
1931 482 c = InletDesigns[i].customCurve;
1932
1/2
✓ Branch 0 taken 482 times.
✗ Branch 1 not taken.
482 if (c >= 0)
1933 {
1934 // --- curve is captured flow v. approach flow
1935
1/2
✓ Branch 0 taken 482 times.
✗ Branch 1 not taken.
482 if (Curve[c].curveType == DIVERSION_CURVE)
1936 {
1937 // --- add up incrmental capture of each replicate inlet
1938
2/2
✓ Branch 0 taken 482 times.
✓ Branch 1 taken 476 times.
958 for (j = 1; j <= inlet->numInlets; j++)
1939 {
1940 1446 qIncrement = inlet->clogFactor *
1941 482 table_lookupEx(&Curve[c], qBypassed * UCF(FLOW)) / UCF(FLOW);
1942
1/2
✓ Branch 0 taken 482 times.
✗ Branch 1 not taken.
482 qIncrement = MIN(qIncrement, qMax);
1943
1/2
✓ Branch 0 taken 482 times.
✗ Branch 1 not taken.
482 qIncrement = MIN(qIncrement, qBypassed);
1944 482 qCaptured += qIncrement;
1945 482 qBypassed -= qIncrement;
1946
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 476 times.
482 if (qBypassed < MIN_RUNOFF_FLOW) break;
1947 }
1948 }
1949
1950 // --- curve is captured flow v. downstream node depth
1951 else if (Curve[c].curveType == RATING_CURVE)
1952 {
1953 qCaptured = inlet->numInlets * inlet->clogFactor *
1954 table_lookupEx(&Curve[c], d * UCF(LENGTH)) / UCF(FLOW);
1955 }
1956 482 qCaptured *= sides;
1957 }
1958 482 return qCaptured;
1959 }
1960