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