1 module libs.irregular_section_open_channel;
2 
3 /// Standard modules
4 import std.math;
5 import std.stdio;
6 import std.algorithm;
7 
8 // Custom modules
9 import libs.openchannel;
10 import libs.utils.point;
11 import libs.utils.geometry_calculators;
12 
13 class IrregularSectionOpenChannel : OpenChannel
14 {
15     /+++++++++++++++++++++++++++++++++++++++++++++++
16     +                 Properties                   +
17     +++++++++++++++++++++++++++++++++++++++++++++++/
18     private Point[] points;
19     private Point[] newPoints;
20     private float maxWaterElevation;
21     private float waterElevation;
22     private double trialDischarge;
23 
24     Unknown[] availableUnknowns = [Unknown.DISCHARGE];
25 
26     /+++++++++++++++++++++++++++++++++++++++++++++++ 
27     +                  Setters                     +
28     +++++++++++++++++++++++++++++++++++++++++++++++/
29     /**
30     *   Set the points with an array of Point object.
31     */
32     void setPoints(Point[] pts)
33     {
34         points = pts;
35     }
36 
37     /**
38     *   Sets the water elevation.
39     */
40     void setWaterElevation(float we)
41     {
42         waterElevation = we;
43     }
44 
45     /+++++++++++++++++++++++++++++++++++++++++++++++ 
46     +                  Getters                     +
47     +++++++++++++++++++++++++++++++++++++++++++++++/
48 
49     /** Returns the list of points */
50     Point[] getPoints()
51     {
52         return points;
53     }
54 
55     /** Returns the adjusted points. */
56     Point[] getNewPoints()
57     {
58         return newPoints;
59     }
60 
61     float getMaxWaterElevation()
62     {
63         return maxWaterElevation;
64     }
65 
66     float getWaterElevation()
67     {
68         return waterElevation;
69     }
70 
71     Unknown[] getAvailableUnknowns()
72     {
73         return availableUnknowns;
74     }
75 
76     /+++++++++++++++++++++++++++++++++++++++++++++++ 
77     +                Constructors                  +
78     +++++++++++++++++++++++++++++++++++++++++++++++/
79     /// Empty constructor
80     this()
81     {
82         unknown = Unknown.DISCHARGE;
83     }
84 
85     /// Constructor specifying the unknown.
86     this(Unknown u)
87     {
88         unknown = u;
89     }
90 
91     /+++++++++++++++++++++++++++++++++++++++++++++++
92     +                   Methods                    +
93     +++++++++++++++++++++++++++++++++++++++++++++++/
94     /// Add a single point
95     void addPoint(Point p)
96     {
97         int lastIndex = cast(int) points.length;
98         points.length = points.length + 1;
99         points[lastIndex] = p;
100     }
101 
102     /// Solution summary.
103     /// To be called in the application API
104     bool solve()
105     {
106         newPoints = null; // Reset newPoints array
107         newPoints.length = 1; // Set length to 1 to give room for the first element of points array
108 
109         if (!canFind(availableUnknowns, unknown))
110         {
111             errorMessage = "The specified unknown is not included in the available unknowns.";
112             return false;
113         }
114 
115         switch (unknown)
116         {
117         case Unknown.DISCHARGE:
118             if (solveForDischarge)
119                 return true;
120             break;
121         default:
122             break;
123         }
124         return false;
125     }
126 
127     /// Solve for the unknown discharge
128     private bool solveForDischarge()
129     {
130         if (isValidInputs(isValidBedSlope(Unknown.DISCHARGE)))
131         {
132             // Number of intersections
133             int leftIntersection = 0, rightIntersection = 0;
134 
135             float x1, y1, x2, y2, x3;
136 
137             // Collect all points including original ones
138             // and the intersection between section and waterElevation.
139 
140             newPoints[0] = points[0];
141 
142             for (int i = 1; i < points.length; i++)
143             {
144                 // Look for the intersection at the left side of the channel
145                 if (leftIntersection == 0)
146                 {
147                     if (points[i].y <= waterElevation && i > 0)
148                     {
149                         leftIntersection++;
150                         // Solve for the intersection point using interpolation
151                         x1 = points[i - 1].x;
152                         y1 = points[i - 1].y;
153                         x2 = points[i].x;
154                         y2 = points[i].y;
155                         x3 = (waterElevation - y1) * (x2 - x1) / (y2 - y1) + x1;
156                         newPoints.length = newPoints.length + 1;
157                         newPoints[cast(int) newPoints.length - 1] = new Point(x3,
158                                 this.waterElevation);
159                     }
160                 }
161 
162                 // Get the water intersection at right bank
163                 if (rightIntersection == 0)
164                 {
165                     if (points[i].y >= waterElevation && i > 0)
166                     {
167                         rightIntersection++;
168                         // Solve for the intersection point using interpolation
169                         x1 = points[i - 1].x;
170                         y1 = points[i - 1].y;
171                         x2 = points[i].x;
172                         y2 = points[i].y;
173                         x3 = (waterElevation - y1) * (x2 - x1) / (y2 - y1) + x1;
174                         newPoints.length = newPoints.length + 1;
175                         newPoints[cast(int) newPoints.length - 1] = new Point(x3,
176                                 this.waterElevation);
177                     }
178                 }
179 
180                 newPoints.length = newPoints.length + 1;
181                 newPoints[cast(int) newPoints.length - 1] = points[i];
182             }
183 
184             // Now, remove all points above waterElevation
185             for (int i = 0; i < newPoints.length; i++)
186             {
187                 if (newPoints[i].y > waterElevation)
188                 {
189                     // Using remove from std.algorithm
190                     newPoints = newPoints.remove(i);
191                 }
192             }
193 
194             // Hydraulic elements
195             wettedArea = polygonArea(newPoints);
196             wettedPerimeter = polygonPerimeter(newPoints);
197             hydraulicRadius = wettedArea / wettedPerimeter;
198 
199             averageVelocity = (1.0 / manningRoughness) * sqrt(bedSlope) * pow(hydraulicRadius,
200                     (2.0 / 3));
201             discharge = averageVelocity * wettedArea;
202 
203             return true;
204 
205         }
206 
207         return false;
208     }
209 
210     /// Finds the elevation of the lowest point from the cross section.
211     private float calculateLowestElevation()
212     {
213         float[] elevations;
214         float lowest = points[0].y;
215 
216         // Collect all elevations
217         foreach (Point p; points)
218         {
219             elevations.length = elevations.length + 1;
220             elevations[cast(int) elevations.length - 1] = p.y;
221         }
222 
223         // Compare each elevation
224         foreach (float el; elevations)
225         {
226             if (lowest > el)
227             {
228                 lowest = el;
229             }
230         }
231         return lowest;
232     }
233 
234 }