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 }