1 /** 2 * irregular_section_open_channel module. 3 * Contains class for the analysis of irregular shaped sections 4 * of open channel. 5 * Authors: 6 * Alexius Academia 7 * License: 8 * MIT 9 * Copyright: 10 * Alexius Academia, 2019 11 */ 12 module libs.irregular_section_open_channel; 13 14 // Standard modules 15 import std.math; 16 import std.stdio; 17 import std.algorithm; 18 19 // Custom modules 20 import libs.openchannel; 21 import libs.utils.point; 22 import libs.utils.geometry_calculators; 23 24 /** 25 * Class for the analysis of irregular shaped sections 26 * of open channel.\ 27 * Unit is disregarded in this class as the inputs can all be 28 * unit independent. 29 */ 30 class IrregularSectionOpenChannel : OpenChannel 31 { 32 //++++++++++++++++++++++++++++++++++++++++++++++ 33 // Properties + 34 //+++++++++++++++++++++++++++++++++++++++++++++/ 35 36 /** 37 * List of points that defines the channel shape. 38 */ 39 protected Point[] points; 40 /// Part of the <b>points</b> variable that is used and 41 /// manipulated during the calculation. 42 protected Point[] newPoints; 43 /// Maximum allowed water elevation based on the lowest bank. 44 protected float maxWaterElevation; 45 /// Water elevation. 46 protected float waterElevation; 47 private double trialDischarge; 48 /// Available unknowns for this class. 49 protected Unknown[] availableUnknowns = [Unknown.DISCHARGE]; 50 51 //++++++++++++++++++++++++++++++++++++++++++++++ 52 // Setters + 53 //+++++++++++++++++++++++++++++++++++++++++++++/ 54 /** 55 * Set the points with an array of Point objects. 56 * Params: 57 * pts = List of Point objects that defines the shape of the channel. 58 */ 59 void setPoints(Point[] pts) 60 { 61 points = pts; 62 } 63 64 /** 65 * Sets the water elevation. 66 * Params: 67 * we = Water elevation either in meter or in foot. 68 */ 69 void setWaterElevation(float we) 70 { 71 waterElevation = we; 72 } 73 74 //++++++++++++++++++++++++++++++++++++++++++++++ 75 // Getters + 76 //+++++++++++++++++++++++++++++++++++++++++++++/ 77 78 /** 79 * Returns the list of points 80 * Returns: 81 * List of Point objects that defines the shape of the channel. 82 */ 83 Point[] getPoints() 84 { 85 return points; 86 } 87 88 /** Returns the adjusted points. */ 89 Point[] getNewPoints() 90 { 91 return newPoints; 92 } 93 94 /** 95 * Get the elevation of the lower bank. Either from left or right. 96 * Returns: 97 * maxWaterElevation = Lowest bank elevation either in meter or foot. 98 */ 99 float getMaxWaterElevation() 100 { 101 return maxWaterElevation; 102 } 103 104 /** 105 * Returns the water elevation. 106 */ 107 float getWaterElevation() 108 { 109 return waterElevation; 110 } 111 112 /// Returns all the available unknowns for this class. 113 Unknown[] getAvailableUnknowns() 114 { 115 return availableUnknowns; 116 } 117 118 //++++++++++++++++++++++++++++++++++++++++++++++ 119 // Constructors + 120 //+++++++++++++++++++++++++++++++++++++++++++++/ 121 /// Empty constructor 122 this() 123 { 124 unknown = Unknown.DISCHARGE; 125 } 126 127 /// Constructor specifying the unknown. 128 this(Unknown u) 129 { 130 unknown = u; 131 } 132 133 /+++++++++++++++++++++++++++++++++++++++++++++++ 134 + Methods + 135 +++++++++++++++++++++++++++++++++++++++++++++++/ 136 /// Add a single point 137 /// Params: 138 /// p = Point to be added to the end of shape definition. 139 void addPoint(Point p) 140 { 141 int lastIndex = cast(int) points.length; 142 points.length = points.length + 1; 143 points[lastIndex] = p; 144 } 145 146 // Solution summary. 147 // To be called in the application API 148 /// Method to be called for the analysis regardless of the unknown. 149 bool solve() 150 { 151 newPoints = null; // Reset newPoints array 152 newPoints.length = 1; // Set length to 1 to give room for the first element of points array 153 154 if (!canFind(availableUnknowns, unknown)) 155 { 156 errorMessage = "The specified unknown is not included in the available unknowns."; 157 return false; 158 } 159 160 switch (unknown) 161 { 162 case Unknown.DISCHARGE: 163 if (solveForDischarge) 164 return true; 165 break; 166 default: 167 break; 168 } 169 return false; 170 } 171 172 /// Solve for the unknown discharge 173 private bool solveForDischarge() 174 { 175 if (isValidInputs(isValidBedSlope(Unknown.DISCHARGE))) 176 { 177 // Number of intersections 178 int leftIntersection = 0, rightIntersection = 0; 179 180 float x1, y1, x2, y2, x3; 181 182 // Collect all points including original ones 183 // and the intersection between section and waterElevation. 184 185 newPoints[0] = points[0]; 186 187 for (int i = 1; i < points.length; i++) 188 { 189 // Look for the intersection at the left side of the channel 190 if (leftIntersection == 0) 191 { 192 if (points[i].y <= waterElevation && i > 0) 193 { 194 leftIntersection++; 195 // Solve for the intersection point using interpolation 196 x1 = points[i - 1].x; 197 y1 = points[i - 1].y; 198 x2 = points[i].x; 199 y2 = points[i].y; 200 x3 = (waterElevation - y1) * (x2 - x1) / (y2 - y1) + x1; 201 newPoints.length = newPoints.length + 1; 202 newPoints[cast(int) newPoints.length - 1] = new Point(x3, 203 this.waterElevation); 204 } 205 } 206 207 // Get the water intersection at right bank 208 if (rightIntersection == 0) 209 { 210 if (points[i].y >= waterElevation && i > 0) 211 { 212 rightIntersection++; 213 // Solve for the intersection point using interpolation 214 x1 = points[i - 1].x; 215 y1 = points[i - 1].y; 216 x2 = points[i].x; 217 y2 = points[i].y; 218 x3 = (waterElevation - y1) * (x2 - x1) / (y2 - y1) + x1; 219 newPoints.length = newPoints.length + 1; 220 newPoints[cast(int) newPoints.length - 1] = new Point(x3, 221 this.waterElevation); 222 } 223 } 224 225 newPoints.length = newPoints.length + 1; 226 newPoints[cast(int) newPoints.length - 1] = points[i]; 227 } 228 229 // Now, remove all points above waterElevation 230 for (int i = 0; i < newPoints.length; i++) 231 { 232 if (newPoints[i].y > waterElevation) 233 { 234 // Using remove from std.algorithm 235 newPoints = newPoints.remove(i); 236 } 237 } 238 239 // Hydraulic elements 240 wettedArea = polygonArea(newPoints); 241 wettedPerimeter = polygonPerimeter(newPoints); 242 hydraulicRadius = wettedArea / wettedPerimeter; 243 244 averageVelocity = (1.0 / manningRoughness) * sqrt(bedSlope) * pow(hydraulicRadius, 245 (2.0 / 3)); 246 discharge = averageVelocity * wettedArea; 247 248 return true; 249 250 } 251 252 return false; 253 } 254 255 /** 256 * Critical flow analysis. 257 */ 258 private void solveForCriticalFlow() 259 { 260 // Top width 261 double T; 262 263 T = distanceBetweenTwoPoints(newPoints[0], 264 newPoints[cast(int)newPoints.length - 1]); 265 266 hydraulicDepth = wettedArea / T; 267 froudeNumber = averageVelocity / sqrt( 268 GRAVITY_METRIC * hydraulicDepth); 269 270 // Select the flow type 271 calculateFlowType(); 272 } 273 274 /// Finds the elevation of the lowest point from the cross section. 275 private float calculateLowestElevation() 276 { 277 float[] elevations; 278 float lowest = points[0].y; 279 280 // Collect all elevations 281 foreach (Point p; points) 282 { 283 elevations.length = elevations.length + 1; 284 elevations[cast(int) elevations.length - 1] = p.y; 285 } 286 287 // Compare each elevation 288 foreach (float el; elevations) 289 { 290 if (lowest > el) 291 { 292 lowest = el; 293 } 294 } 295 return lowest; 296 } 297 298 /** 299 * Calculates the distance between two given coordinates. 300 * Params: 301 * p1 = object Point, x and y coordinate 302 * p2 = another point 303 * Returns: 304 * The distance between p1 and p2. 305 */ 306 private double distanceBetweenTwoPoints(Point p1, Point p2) 307 { 308 float x1, y1, x2, y2; 309 x1 = p1.x; 310 y1 = p1.y; 311 x2 = p2.x; 312 y2 = p2.y; 313 return sqrt(pow((y2 - y1), 2) + pow((x2 - x1), 2)); 314 } 315 }