1 /** 2 * trapezoidal_open_channel module. 3 * Contains class for the analysis of trapezoidal and triangular channels. 4 */ 5 module libs.trapezoidal_open_channel; 6 7 // Standard modules 8 import std.math : pow, sqrt, pow, isNaN, abs; 9 import std.algorithm : canFind; 10 11 // Custom modules 12 import libs.openchannel; 13 14 /** 15 * Class for the analysis of trapezoidal and triangular channels. 16 */ 17 class TrapezoidalOpenChannel : OpenChannel 18 { 19 //++++++++++++++++++++++++++++++++++++++++++++++ 20 // Properties + 21 //+++++++++++++++++++++++++++++++++++++++++++++/ 22 /// Base width or the channel width for rectangular sections. 23 protected double baseWidth; 24 25 /// Sideslope 26 protected float sideSlope; 27 28 /// Available unknowns for this class. 29 protected Unknown[] availableUnknowns = [ 30 Unknown.DISCHARGE, Unknown.WATER_DEPTH, Unknown.BED_SLOPE, Unknown.BASE_WIDTH 31 ]; 32 33 //++++++++++++++++++++++++++++++++++++++++++++++ 34 // Setters + 35 //+++++++++++++++++++++++++++++++++++++++++++++/ 36 /** 37 * Sets the width at the smaller base of the channel. 38 * Params: 39 * b = Base width 40 */ 41 void setBaseWidth(double b) 42 { 43 if (unit == Units.ENGLISH) 44 { 45 baseWidth = b / 3.28; 46 } 47 else 48 { 49 baseWidth = b; 50 } 51 } 52 53 /** 54 * Sets the side slope of the channel. Assumming symmetrical section. 55 * Params: 56 * ss = Side slope 57 */ 58 void setSideSlope(float ss) 59 { 60 sideSlope = ss; 61 } 62 63 //++++++++++++++++++++++++++++++++++++++++++++++ 64 // Getters + 65 //+++++++++++++++++++++++++++++++++++++++++++++/ 66 /// Returns the base width of the channel. 67 double getBaseWidth() 68 { 69 if (unit == Units.ENGLISH) 70 { 71 return baseWidth * 3.28; 72 } 73 return baseWidth; 74 } 75 76 //++++++++++++++++++++++++++++++++++++++++++++++ 77 // Methods + 78 //+++++++++++++++++++++++++++++++++++++++++++++/ 79 // Solution summary. 80 // To be called in the application API 81 /// Method to be called for the analysis regardless of the unknown. 82 bool solve() 83 { 84 if (!canFind(availableUnknowns, unknown)) 85 { 86 errorMessage = "The specified unknown is not included in the available unknowns."; 87 return false; 88 } 89 90 switch (this.unknown) 91 { 92 case Unknown.DISCHARGE: 93 if (solveForDischarge) 94 { 95 solveForCriticalFlow(); 96 return true; 97 } 98 break; 99 case Unknown.WATER_DEPTH: 100 if (solveForWaterDepth) 101 { 102 solveForCriticalFlow(); 103 return true; 104 } 105 break; 106 case Unknown.BASE_WIDTH: 107 if (solveForBaseWidth) 108 { 109 solveForCriticalFlow(); 110 return true; 111 } 112 break; 113 case Unknown.BED_SLOPE: 114 if (solveForBedSlope) 115 { 116 solveForCriticalFlow(); 117 return true; 118 } 119 break; 120 default: 121 break; 122 } 123 124 return false; 125 } 126 127 /// Solve for the unknown discharge. 128 protected bool solveForDischarge() 129 { 130 if (isValidInputs(isValidBaseWidth(Unknown.DISCHARGE), isValidBedSlope(Unknown.DISCHARGE), 131 isValidWaterDepth(Unknown.DISCHARGE), 132 isValidSideslope(Unknown.DISCHARGE), isValidManning)) 133 { 134 wettedArea = (baseWidth + waterDepth * sideSlope) * waterDepth; 135 wettedPerimeter = 2 * waterDepth * sqrt(pow(sideSlope, 2) + 1) + baseWidth; 136 137 // Check if wetted perimeter is zero. 138 // Cancel the calculation is so, which will yield infinity in calculation 139 // of hydraulic radius, R. 140 if (wettedPerimeter == 0.0) 141 { 142 errorMessage = "Perimeter shall be non-zero positive result. Please check your dimensions"; 143 return false; 144 } 145 146 hydraulicRadius = wettedArea / wettedPerimeter; 147 averageVelocity = (1.0 / manningRoughness) * sqrt(bedSlope) * pow(hydraulicRadius, 148 (2.0 / 3)); 149 discharge = averageVelocity * wettedArea; 150 151 return true; 152 } 153 else 154 { 155 return false; 156 } 157 } 158 159 /// Solve for the unknown water depth 160 protected bool solveForWaterDepth() 161 { 162 if (isValidInputs(isValidBaseWidth(Unknown.WATER_DEPTH), isValidBedSlope(Unknown.WATER_DEPTH), 163 isValidDischarge(Unknown.WATER_DEPTH), 164 isValidSideslope(Unknown.WATER_DEPTH), isValidManning)) 165 { 166 167 double trialDischarge = 0, increment = 0.0001; 168 waterDepth = 0; 169 170 const allowedDiff = discharge * ERROR; 171 172 // Start of trial and error 173 while (abs(discharge - trialDischarge) > allowedDiff) 174 { 175 waterDepth += increment; 176 wettedArea = (baseWidth + waterDepth * sideSlope) * waterDepth; 177 wettedPerimeter = 2 * waterDepth * sqrt(pow(sideSlope, 2) + 1) + baseWidth; 178 179 // Check if wetted perimeter is zero. 180 // Cancel the calculation is so, which will yield infinity in calculation 181 // of hydraulic radius, R. 182 if (wettedPerimeter == 0.0) 183 { 184 errorMessage = "Perimeter shall be non-zero positive result. Please check your dimensions"; 185 return false; 186 } 187 188 hydraulicRadius = wettedArea / wettedPerimeter; 189 averageVelocity = (1.0 / manningRoughness) * sqrt(bedSlope) * pow(hydraulicRadius, 190 (2.0 / 3)); 191 trialDischarge = averageVelocity * wettedArea; 192 193 /+ 194 + My root finding algorithm 195 +/ 196 if (trialDischarge < discharge) 197 { 198 increment *= 2.1; 199 } 200 201 if (trialDischarge > discharge) 202 { 203 waterDepth -= increment; 204 increment *= .75; 205 } 206 /+ 207 + End of root finding algorithm 208 +/ 209 } 210 return true; 211 } 212 else 213 { 214 return false; 215 } 216 } 217 218 /// Solve for the unknown base width 219 protected bool solveForBaseWidth() 220 { 221 if (isValidInputs(isValidWaterDepth(Unknown.BASE_WIDTH), isValidBedSlope(Unknown.BASE_WIDTH), 222 isValidDischarge(Unknown.BASE_WIDTH), 223 isValidSideslope(Unknown.BASE_WIDTH), isValidManning)) 224 { 225 226 double trialDischarge = 0, increment = 0.0001; 227 baseWidth = 0; 228 229 const allowedDiff = discharge * ERROR; 230 231 // Start of trial and error 232 while (abs(discharge - trialDischarge) > allowedDiff) 233 { 234 baseWidth += increment; 235 wettedArea = (baseWidth + waterDepth * sideSlope) * waterDepth; 236 wettedPerimeter = 2 * waterDepth * sqrt(pow(sideSlope, 2) + 1) + baseWidth; 237 238 // Check if wetted perimeter is zero. 239 // Cancel the calculation is so, which will yield infinity in calculation 240 // of hydraulic radius, R. 241 if (wettedPerimeter == 0.0) 242 { 243 errorMessage = "Perimeter shall be non-zero positive result. Please check your dimensions"; 244 return false; 245 } 246 247 hydraulicRadius = wettedArea / wettedPerimeter; 248 averageVelocity = (1.0 / manningRoughness) * sqrt(bedSlope) * pow(hydraulicRadius, 249 (2.0 / 3)); 250 trialDischarge = averageVelocity * wettedArea; 251 252 /+ 253 + My root finding algorithm 254 +/ 255 if (trialDischarge < discharge) 256 { 257 increment *= 2.1; 258 } 259 260 if (trialDischarge > discharge) 261 { 262 baseWidth -= increment; 263 increment *= .75; 264 } 265 /+ 266 + End of root finding algorithm 267 +/ 268 } 269 return true; 270 } 271 else 272 { 273 return false; 274 } 275 } 276 277 /// Solve for the unknown bed slope 278 protected bool solveForBedSlope() 279 { 280 if (isValidInputs(isValidWaterDepth(Unknown.BED_SLOPE), isValidBaseWidth(Unknown.BED_SLOPE), 281 isValidDischarge(Unknown.BED_SLOPE), 282 isValidSideslope(Unknown.BED_SLOPE), isValidManning)) 283 { 284 285 double trialDischarge = 0, increment = 0.0000001; 286 bedSlope = 0; 287 288 const allowedDiff = discharge * ERROR; 289 290 // Start of trial and error 291 while (abs(discharge - trialDischarge) > allowedDiff) 292 { 293 bedSlope += increment; 294 wettedArea = (baseWidth + waterDepth * sideSlope) * waterDepth; 295 wettedPerimeter = 2 * waterDepth * sqrt(pow(sideSlope, 2) + 1) + baseWidth; 296 297 // Check if wetted perimeter is zero. 298 // Cancel the calculation is so, which will yield infinity in calculation 299 // of hydraulic radius, R. 300 if (wettedPerimeter == 0.0) 301 { 302 errorMessage = "Perimeter shall be non-zero positive result. Please check your dimensions"; 303 return false; 304 } 305 306 hydraulicRadius = wettedArea / wettedPerimeter; 307 averageVelocity = (1.0 / manningRoughness) * sqrt(bedSlope) * pow(hydraulicRadius, 308 (2.0 / 3)); 309 trialDischarge = averageVelocity * wettedArea; 310 311 // Start of my root finding algorithm 312 if (trialDischarge < discharge) 313 { 314 increment *= 2.1; 315 } 316 317 if (trialDischarge > discharge) 318 { 319 bedSlope -= increment; 320 increment *= .75; 321 } 322 // End of root finding algorithm 323 } 324 return true; 325 } 326 else 327 { 328 return false; 329 } 330 } 331 332 //++++++++++++++++++++++++++++++++++++++++++++++ 333 // Error handling + 334 //+++++++++++++++++++++++++++++++++++++++++++++/ 335 /** 336 * Base width error checking. 337 * Params: 338 * u = Unknown 339 * Returns: 340 * Returns true if the base width given is valid. 341 */ 342 protected bool isValidBaseWidth(Unknown u) 343 { 344 if (isNaN(baseWidth) && (u != Unknown.BASE_WIDTH)) 345 { 346 errorMessage = "Base width must be numeric."; 347 return false; 348 } 349 350 if (baseWidth < 0.0 && u != Unknown.BASE_WIDTH) 351 { 352 errorMessage = "Base width must be greater than zero."; 353 return false; 354 } 355 356 errorMessage = "Calculation successful."; 357 return true; 358 } 359 360 /** 361 * Sideslope error checking 362 * Params: 363 * u = Unknown 364 * Returns: 365 * Returns true if the side slope given is valid. 366 */ 367 protected bool isValidSideslope(Unknown u) 368 { 369 if (isNaN(sideSlope)) 370 { 371 errorMessage = "Sideslope must be numeric."; 372 return false; 373 } 374 375 if (sideSlope < 0) 376 { 377 errorMessage = "Sideslope must be greater than zero."; 378 return false; 379 } 380 381 errorMessage = "Calculation successful."; 382 return true; 383 } 384 385 /** 386 * Critical flow analysis. 387 */ 388 protected void solveForCriticalFlow() 389 { 390 const Q2g = pow(discharge, 2) / GRAVITY_METRIC; 391 392 double tester = 0; 393 394 // Critical depth 395 double yc = 0; 396 397 // Critical area, perimeter, hydraulic radius, critical slope 398 double Ac = 0, Pc, Rc, Sc; 399 400 // Top width 401 double T = 0; 402 403 while (tester < Q2g) 404 { 405 yc += 0.00001; 406 T = baseWidth + 2 * sideSlope * yc; 407 Ac = (T + baseWidth) / 2 * yc; 408 tester = pow(Ac, 3) / T; 409 } 410 411 criticalDepth = yc; 412 413 Pc = 2 * yc * sqrt(pow(this.sideSlope, 2) + 1) + this.baseWidth; 414 Rc = Ac / Pc; 415 Sc = pow(this.discharge / (Ac * pow(Rc, (2.0 / 3.0))) * this.manningRoughness, 2); 416 this.criticalSlope = Sc; 417 418 // Solve for froude number 419 const topWidth = this.baseWidth + 2 * this.sideSlope * this.waterDepth; 420 this.hydraulicDepth = this.wettedArea / topWidth; 421 this.froudeNumber = this.averageVelocity / sqrt( 422 this.GRAVITY_METRIC * this.hydraulicDepth); 423 424 // Select the flow type 425 calculateFlowType(); 426 } 427 }