Irregular Section Channel (Java)
Irregular Section Channel (Java)
hydraulics;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 /**
7 * Irregular section channel
8 * An extension of the class OpenChannel
9 * Used for irregular section channel, usually river channels.
10 */
11 public class IrregularSectionChannel extends OpenChannel {
12 /* **********************************
13 * Properties
14 ***********************************/
15
16 public enum Unknown { // Unknown class
17 DISCHARGE,
18 BED_SLOPE
19 }
20
21
22 private Unknown unknown; // Unknown
23 private List<Point> points; // List of points for the channel profile
24 private float maxWaterElevation;
25 private float waterElevation;
26 private double calculatedDischarge;
27 private double criticalWaterElevation;
28
29 /* **********************************
30 * Setters
31 ***********************************/
32
33 public void setUnknown(Unknown unknown) {
34 this.unknown = unknown;
35 }
36
37 public void setPoints(List<Point> points) {
38 this.points = points;
39 }
40
41 public void setWaterElevation(float waterElevation) {
42 this.waterElevation = waterElevation;
43 }
44
45 /* **********************************
46 * Getters
47 ***********************************/
48
49 public List<Point> getPoints() {
50 return points;
51 }
52
53 public float getMaxWaterElevation() {
54 return maxWaterElevation;
55 }
56
57 public float getWaterElevation() {
58 return waterElevation;
59 }
60
61 public double getCriticalWaterElevation() {
62 return criticalWaterElevation;
63 }
64
65 public Unknown getUnknown() {
66 return unknown;
67 }
68
69 /**
70 * Create an empty {@code IrregularSectionChannel}
71 */
72 public IrregularSectionChannel() {
73 this.unknown = Unknown.DISCHARGE;
74 }
75
76 /**
77 * Creates an {@code IrregularOpenChannel} with given unknown
78 * @param unknown Unknown
79 */
80 public IrregularSectionChannel(Unknown unknown) {
81 this.unknown = unknown;
82 }
83
84 /**
85 * Creates an {@code IrregularSectionChannel} with gicen unknown and points
86 * @param unknown Unknown
87 * @param pts List of Points
88 */
89 public IrregularSectionChannel(Unknown unknown, List<Point> pts) {
90 this.points = pts;
91 }
92
93 /* **********************************
94 * Methods
95 ***********************************/
96
97 public boolean analyze() {
98 if (isValidInputs()) {
99 switch (this.unknown) {
100 case DISCHARGE:
101 solveForDischarge();
102 break;
103 case BED_SLOPE:
104 solveForBedSlope();
105 break;
106 default:
107 try {
108 throw new InvalidValueException("Invalid unknown.");
109 } catch (InvalidValueException e) {
110 this.isCalculationSuccessful = false;
111 this.errMessage = e.getMessage();
112 }
113 }
114 solveForCriticalFlow();
115 return isCalculationSuccessful;
116 }
117 return false;
118 }
119
120 /**
121 * Solve for the unknown bed slope
122 */
123 private void solveForBedSlope() {
124 // Number of waterline intersections
125 int leftIntersections = 0, rightIntersections = 0;
126
127 // Remove points above the waterline intersection at the banks
128 List<Point> newPoints = new ArrayList<>();
129
130 float x1, x2, x3, y1, y2;
131
132 for (int i = 0; i < this.points.size(); i++) {
133 // float x = this.points.get(i).getX();
134 float y = this.points.get(i).getY();
135
136 // Look for the intersection at the left side of the channel
137 if (leftIntersections == 0) {
138 if (y <= this.waterElevation && i > 0) {
139 leftIntersections++;
140 // Solve for the intersection point using interpolation
141 x1 = this.points.get(i - 1).getX();
142 y1 = this.points.get(i - 1).getY();
143 x2 = this.points.get(i).getX();
144 y2 = this.points.get(i).getY();
145 x3 = (this.waterElevation - y1) * (x2 - x1) / (y2 - y1) + x1;
146 newPoints.add(new Point(x3, this.waterElevation));
147 }
148 }
149
150 // Look for the intersection at the right side of the channel
151 if (rightIntersections == 0) {
152 if (y >= this.waterElevation && i > 0) {
153 rightIntersections++;
154 x1 = this.points.get(i - 1).getX();
155 y1 = this.points.get(i - 1).getY();
156 x2 = this.points.get(i).getX();
157 y2 = this.points.get(i).getY();
158 x3 = (this.waterElevation - y1) * (x2 - x1) / (y2 - y1) + x1;
159 newPoints.add(new Point(x3, this.waterElevation));
160 }
161 }
162
163 if (leftIntersections == 1) {
164 if (rightIntersections == 0) {
165 newPoints.add(this.points.get(i));
166 }
167 }
168 }
169
170 double trialSlope = 0;
171 calculatedDischarge = 0;
172
173 while (calculatedDischarge < this.discharge) {
174 trialSlope += SLOPE_TRIAL_INCREMENT;
175 this.wettedArea = polygonArea(newPoints);
176 this.wettedPerimeter = polygonPerimeter(newPoints);
177 this.hydraulicRadius = this.wettedArea / this.wettedPerimeter;
178 this.averageVelocity = (1 / this.manningRoughness) * Math.sqrt(trialSlope) *
179 Math.pow(this.hydraulicRadius, (2.0/3.0));
180 calculatedDischarge = this.averageVelocity * this.wettedArea;
181 }
182
183 this.bedSlope = trialSlope;
184 this.isCalculationSuccessful = true;
185 }
186
187 /**
188 * Solve for the unknown discharge
189 */
190 private void solveForDischarge() {
191 // Number of waterline intersections
192 int leftIntersections = 0, rightIntersections = 0;
193
194 // Remove points above the waterline intersection at the banks
195 List<Point> newPoints = new ArrayList<>();
196
197 float x1, x2, x3, y1, y2;
198
199 for (int i = 0; i < this.points.size(); i++) {
200 // float x = this.points.get(i).getX();
201 float y = this.points.get(i).getY();
202
203 // Look for the intersection at the left side of the channel
204 if (leftIntersections == 0) {
205 if (y <= this.waterElevation && i > 0) {
206 leftIntersections++;
207 // Solve for the intersection point using interpolation
208 x1 = this.points.get(i - 1).getX();
209 y1 = this.points.get(i - 1).getY();
210 x2 = this.points.get(i).getX();
211 y2 = this.points.get(i).getY();
212 x3 = (this.waterElevation - y1) * (x2 - x1) / (y2 - y1) + x1;
213 newPoints.add(new Point(x3, this.waterElevation));
214 }
215 }
216
217 // Look for the intersection at the right side of the channel
218 if (rightIntersections == 0) {
219 if (y >= this.waterElevation && i > 0) {
220 rightIntersections++;
221 x1 = this.points.get(i - 1).getX();
222 y1 = this.points.get(i - 1).getY();
223 x2 = this.points.get(i).getX();
224 y2 = this.points.get(i).getY();
225 x3 = (this.waterElevation - y1) * (x2 - x1) / (y2 - y1) + x1;
226 newPoints.add(new Point(x3, this.waterElevation));
227 }
228 }
229
230 if (leftIntersections == 1) {
231 if (rightIntersections == 0) {
232 newPoints.add(this.points.get(i));
233 }
234 }
235 }
236
237 // Hydraulic elements
238 this.wettedArea = polygonArea(newPoints);
239 this.wettedPerimeter = polygonPerimeter(newPoints);
240 this.hydraulicRadius = this.wettedArea / this.wettedPerimeter;
241 this.averageVelocity = (1 / this.manningRoughness) * Math.sqrt(this.bedSlope) *
242 Math.pow(this.hydraulicRadius, (2.0/3.0));
243 this.discharge = this.averageVelocity * this.wettedArea;
244
245 this.isCalculationSuccessful = true;
246 }
247
248 /**
249 * Implementation of the shoelace formula in computing area of a polygon with
250 * given vertices.
251 * @param points The vertices covered by the cross sectional area.
252 * @return Double Polygon area
253 */
254 private double polygonArea(List<Point> points) {
255 // Number of vertices of the polygon
256 int n = points.size();
257
258 // Initialize area
259 double area = 0;
260 int j;
261
262 for (int i = 0; i < n; i++) {
263 j = (i + 1) % n;
264 area += points.get(i).getX() * points.get(j).getY();
265 area -= points.get(j).getX() * points.get(i).getY();
266 }
267
268 area = Math.abs(area) / 2;
269
270 return area;
271 }
272
273 /**
274 * Get the total distance covered by multiple points
275 * @param points The vertices covered by the cross sectional area.
276 * @return Double Polygon perimeter
277 */
278 private double polygonPerimeter(List<Point> points) {
279 // Initialize perimeter
280 double perimeter = 0;
281
282 // Number of vertices of the polygon
283 int n = points.size();
284
285 Point p1, p2;
286
287 for (int i = 0; i < (n-1); i++) {
288 p1 = points.get(i);
289 p2 = points.get(i + 1);
290 perimeter += distanceBetweenTwoPoints(p1, p2);
291 }
292
293 return perimeter;
294 }
295
296 /**
297 * Calculate the distance between two given points.
298 * @param p1 First point
299 * @param p2 Second point
300 * @return Double The distance between the 2 points
301 */
302 private double distanceBetweenTwoPoints(Point p1, Point p2) {
303 float x1, y1, x2, y2;
304 x1 = p1.getX();
305 y1 = p1.getY();
306 x2 = p2.getX();
307 y2 = p2.getY();
308 return Math.sqrt(Math.pow((y2 - y1), 2) + Math.pow((x2 - x1), 2));
309 }
310
311 /**
312 * Get the lowest point elevation from the list of poits.
313 * @return Double Lowest point
314 */
315 private float calculateLowestPoint() {
316 List<Float> elevations = new ArrayList<Float>();
317 float lowest = 0;
318
319 for (Point p : this.points) {
320 elevations.add(p.getY());
321 }
322
323 for (float el : elevations) {
324 if (lowest > el) {
325 lowest = el;
326 }
327 }
328
329 return lowest;
330 }
331
332 /**
333 * Check for invalid inputs
334 * @return Boolean True if all inputs are valid.
335 */
336 private boolean isValidInputs() {
337 // First, solve for the lowest bank
338 int numberOfPoints = this.points.size();
339 // Elevation of left and right bank
340 float leftBankElevation = this.points.get(0).getY();
341 float rightBankElevation = this.points.get(numberOfPoints - 1).getY();
342
343 // Get the lower of the 2 banks
344 if (leftBankElevation > rightBankElevation) {
345 this.maxWaterElevation = rightBankElevation;
346 } else {
347 this.maxWaterElevation = leftBankElevation;
348 }
349
350 try {
351 if (this.waterElevation > this.maxWaterElevation) {
352 throw new InvalidValueException("Water elevation is above the lowest bank.
Overflow!");
353 }
354
355 if (this.waterElevation < calculateLowestPoint()) {
356 throw new DimensionException("Water surface was set below the lowest
ground.");
357 }
358
359 if (this.points.size() < 3) {
360 throw new DimensionException("Invalid number of points. Minimum is three (3)
points.");
361 }
362
363 if (this.manningRoughness <= 0) {
364 throw new InvalidValueException("Manning's roughness must be greater than
zero.");
365 }
366
367 if (this.unknown != Unknown.DISCHARGE) {
368 if (this.discharge <= 0) {
369 throw new InvalidValueException("Discharge must be greater than zero.");
370 }
371 }
372
373 if (this.unknown != Unknown.BED_SLOPE) {
374 if (this.bedSlope <= 0) {
375 throw new InvalidValueException("Bed slope must not be flat or less than
zero.");
376 }
377 }
378
379 } catch (Exception e) {
380 this.isCalculationSuccessful = false;
381 this.errMessage = e.getMessage();
382 return false;
383 }
384 return true;
385 }
386
387 /**
388 * Solve for critical flow properties (e.g. critical depth, froude number, flow
type ...)
389 */
390 private void solveForCriticalFlow() {
391 double Q2g = Math.pow(this.discharge, 2) / this.GRAVITY_METRIC;
392
393 double tester = 0;
394
395 // Critical depth elevation
396 // Initially at the lowest elevation
397 double yc = this.calculateLowestPoint();
398
399 // Critical area, perimeter, hydraulic radius, critical slope
400 double Ac = 0, Pc, Rc, Sc;
401
402 // Top width
403 double T = 0;
404
405 // Remove points above the waterline intersection at the banks
406 List<Point> newPoints = new ArrayList<>();
407
408 while (tester < Q2g) {
409 yc += this.DEPTH_TRIAL_INCREMENT;
410
411 // Get the new points
412 // Number of waterline intersections
413 int leftIntersections = 0, rightIntersections = 0;
414
415 float x1, x2, x3, y1, y2;
416
417 for (int i = 0; i < this.points.size(); i++) {
418 // float x = this.points.get(i).getX();
419 float y = this.points.get(i).getY();
420
421 // Look for the intersection at the left side of the channel
422 if (leftIntersections == 0) {
423 if (y <= yc && i > 0) {
424 leftIntersections++;
425 // Solve for the intersection point using interpolation
426 x1 = this.points.get(i - 1).getX();
427 y1 = this.points.get(i - 1).getY();
428 x2 = this.points.get(i).getX();
429 y2 = this.points.get(i).getY();
430 x3 = (float) ((yc - y1) * (x2 - x1) / (y2 - y1) + x1);
431 newPoints.add(new Point(x3, (float) yc));
432 }
433 }
434
435 // Look for the intersection at the right side of the channel
436 if (rightIntersections == 0) {
437 if (y >= yc && i > 0) {
438 rightIntersections++;
439 x1 = this.points.get(i - 1).getX();
440 y1 = this.points.get(i - 1).getY();
441 x2 = this.points.get(i).getX();
442 y2 = this.points.get(i).getY();
443 x3 = (float) ((yc - y1) * (x2 - x1) / (y2 - y1) + x1);
444 newPoints.add(new Point(x3, (float) yc));
445 }
446 }
447
448 if (leftIntersections == 1) {
449 if (rightIntersections == 0) {
450 newPoints.add(this.points.get(i));
451 }
452 }
453 }
454
455 tester = Math.pow(Ac, 3) / T;
456 }
457
458 this.criticalWaterElevation = yc;
459 // Calculate the area covered
460 Ac = polygonArea(newPoints);
461 Pc = polygonPerimeter(newPoints);
462 Rc = Ac / Pc;
463 Sc = Math.pow(this.discharge / (Ac * Math.pow(Rc, (2.0/3.0))) *
this.manningRoughness, 2);
464 this.criticalSlope = Sc;
465 T = distanceBetweenTwoPoints(newPoints.get(0), newPoints.get(newPoints.size() -
1));
466 this.hydraulicDepth = this.wettedArea / T;
467 this.froudeNumber = this.averageVelocity / Math.sqrt(this.GRAVITY_METRIC *
this.hydraulicDepth);
468
469 // Select the flow type
470 this.flowType();
471 }
472 }
473