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