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