-
Notifications
You must be signed in to change notification settings - Fork 1
/
Source1V2.f90
463 lines (397 loc) · 20.6 KB
/
Source1V2.f90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
use dflib
!Author : globalpolicy
!Blog : c0dew0rth.blogspot.com
!GitHub : globalpolicy
!Date : 06:05PM - 12th August, 2019
!Purpose: To draw flownet (revised)
!Changes:
!-Head at dam heel is water depth as should be(was assumed 0 before)
!-Dam was fixed to be the size of a single grid box. Now, width of dam can be specified.
!-Dam's heel and toe are Dirichlet boundaries i.e. known heads but any node between are treated as unknowns.
!Parameters : contourInterval, numDys, numDxs, DxsTillDam, DamWidthInDxs, reservoirHead, downstreamBoundaryHead
!Built by observing structure of an example coefficient matrix from my Fortran Simulation notes
!https://photos.app.goo.gl/BS5C2tp4MNMFp9Dh6 -> example grid
!https://photos.app.goo.gl/GecBS62BS1hTPNri9 -> matrix construction
!https://photos.app.goo.gl/WgKvzMWf63CRNBwZA -> final matrix
!Also, during revision of code, the following problems were taken as a reference:
!https://photos.app.goo.gl/1p7NWiWeYKTRTBEh6
!https://photos.app.goo.gl/LF51G7jisALXweCL7
!Coefficient matrices were constructed for them and solved in Excel:
!https://drive.google.com/open?id=1I513Eq-tPaADIw_MF5oiTIGo4es4k2f8
!Great lectures I made my notes off of:
!https://www.youtube.com/watch?v=FgR3KS51mbA - Prof. Wen Shen, Penn State University
!https://www.youtube.com/watch?v=i0f2DegLBN8 - Sandip Mazumder
implicit none
integer i,j,col,row
character(10) char
real tmp,distance,smallestDistanceIndex,smallestDistance
integer numDys,numDxs,numUnknowns,DxsTillDam,DamWidthInDxs
integer(2) viewPortTopLeftx,viewPortTopLefty,viewPortBottomRightx,viewPortBottomRighty,viewPortWidth !viewPortWidth=viewPortHeight
real reservoirHead,downstreamBoundaryHead
real A(500,500),B(500,500),X(500,500),AInverse(500,500)
logical status
TYPE(wxycoord) wxy
integer ret
real H(500,500)
real EquipotentialX(500,500),EquipotentialY(500,500) !one column for one head, same column's row element from each matrix give the equipotential point x,y
real searchHead,currentHead,rightHead,upHead,contourInterval
real interpolatedX,interpolatedY
integer numberOfContours, contourId
integer contourPointsCounters(500) !stores the number of equipotential points obtained for each contour
contourInterval=10
numDys=3 !total grid boxes along Y axis, enclosed within boundaries top and bottom
numDxs=4 !total grid boxes along X axis, enclosed within boundaries left and right
DxsTillDam=2
DamWidthInDxs=2
reservoirHead=100
downstreamBoundaryHead=50
viewPortWidth = 400 !width(=height) of the viewport
viewPortTopLeftx = 200
viewPortTopLefty = 80
numUnknowns=numDys*numDxs+(DamWidthInDxs-1)
numberOfContours=reservoirHead/contourInterval + 1
!generate coefficient matrix A
!the rows, columns and nodes in the comments are referencing the full domain grid including the boundaries(https://photos.app.goo.gl/nToWZ9p3znyGxvwy8), not the array/matrix used here
do i=1,numUnknowns !row index
do j=1,numUnknowns !column index
if(i.EQ.j) then
A(i,j)=-4
!for immediate right element of diagonal's row (right blob of Laplace computational stencil)
if(i.LT.numDxs*numDys.AND.MODULO(i,numDxs).EQ.1) then !if current node is on u/s boundary
A(i,j+1)=2
elseif(i.LE.numDxs*numDys.AND.MODULO(i,numDxs).EQ.0) then !if current node is on second-last column (before d/s boundary) and below the surface row
A(i,j+1)=0
B(i,1)=B(i,1)-downstreamBoundaryHead
elseif(i.GT.numDxs*numDys.AND.i.EQ.numUnknowns) then !if current node is the final unknown i.e. surface node right beneath dam, right before toe of dam(d/s boundary)
A(i,j+1)=0
B(i,1)=B(i,1)-0
else
A(i,j+1)=1
endif
!for immediate left element of diagonal's row (left blob of Laplace computational stencil)
if(j.GT.1) then !if current node is not the bottom-left one
if(i.LT.numDxs*numDys.AND.MODULO(i,numDxs).EQ.1) then !if current node is on u/s boundary
A(i,j-1)=0
elseif(DamWidthInDxs.GE.2.AND.i.EQ.numDxs*numDys+1) then !if current node is right beneath the dam, one node to right of heel
A(i,j-1)=0
B(i,1)=B(i,1)-reservoirHead
else
A(i,j-1)=1
endif
endif
!for far right element of diagonal's row (top blob of Laplace computational stencil)
if(i.LE.numDxs) then !if current node is in the bottom row
A(i,j+numDxs)=2
elseif(j+numDxs.LE.numDxs*numDys) then !if current row is below the second-top row
A(i,j+numDxs)=1
elseif(j.LE.numDxs*numDys) then !if current row is second-top row(row immediately below surface)
if(j.LE.numUnknowns-(DamWidthInDxs-1+numDxs-DxsTillDam-1)) then !if this node is u/s of heel of dam, heel inclusive
B(i,1)=B(i,1)+(-reservoirHead)
elseif(j.GT.numDxs*numDys-(DamWidthInDxs-1).AND.j.LE.numDxs*numDys) then !if node is between(exclusive) heel and toe
A(i,j+(DamWidthInDxs-1))=1
endif
else !if current row is topmost row(unknown nodes directly beneath the dam)
B(i,1)=B(i,1)+0
endif
!for far left element of diagonal's row (bottom blob of Laplace computational stencil)
if(j.GT.numDxs) then
if(j.LE.numDxs*numDys) then
A(i,j-numDxs)=1
else
A(i,j-(DamWIdthInDxs-1))=2
endif
endif
endif
end do
end do
!generated coefficient matrix A written to file for inspection
open(1,file='A.txt')
do i=1,numUnknowns
write(1,'(F5.1,$)') (A(i,j),j=1,numUnknowns)
write(1,*)
end do
close(1)
!generated B vector written to file for inspection
open(1,file='B.txt')
do i=1,numUnknowns
write(1,*) B(i,1)
end do
close(1)
call Inverse(A,AInverse,numUnknowns)
X=matmul(AInverse,B)
!calculated X vector
open(1,file='X.txt')
do i=1,numUnknowns
write(1,*) X(i,1)
end do
close(1)
!setup viewport
viewPortBottomRightx = int2(viewPortTopLeftx+real(numDxs)/real(numDys)*viewPortWidth)
viewPortBottomRighty = int2(viewPortTopLefty+viewPortWidth)
call SETVIEWPORT(200,80,viewPortBottomRightx,viewPortBottomRighty)
status = SETCOLOR(4) !red
status = FLOODFILL(51,81,4) !flood fill viewport
status = SETWINDOW(.TRUE.,real(0,8),real(0,8),real(numDxs,8),real(numDys,8))
status = SETCOLOR(1)
!draw horizontal lines from bottom to top
do i=0,numDys
call MOVETO_W(0.0_8,real(i,8),wxy)
ret = LINETO_W(real(numDxs,8),real(i,8))
end do
!draw vertical lines from left to right
do i=0,numDxs
call MOVETO_W(real(i,8),0.0_8,wxy)
ret = LINETO_W(real(i,8),real(numDys,8))
end do
!create Head matrix for whole domain from calculated heads at nodes and the boundary condition values
!populate non-last row(below surface) of Head matrix from the computed X vector
do i=1,numDys
do j=1,numDxs+1
if(j.EQ.numDxs+1) then !if final column
H(i,j)=downstreamBoundaryHead
else
H(i,j)=X((i-1)*numDxs+j,1)
endif
end do
end do
!populate the last row (corresponding to the surface heads)
i=1
do j=1,numDxs+1
if(j.LE.DxsTillDam+1) then
H(numDys+1,j)=reservoirHead
elseif(j.GT.DxsTillDam+1.AND.j.LT.numDxs+1) then
H(numDys+1,j)=X(numDxs*numDys+i,1)
i=i+1
else
H(numDys+1,j)=0
endif
end do
!output H matrix to file for inspection
!observe that it is of the form (taking the referenced example at the top)
! h1 h2 h3 h4 downstreamBoundaryHead
! h5 h6 h7 h8 downstreamBoundaryHead
! h9 h10 h11 h12 downstreamBoundaryHead
! reservoirHead reservoirHead reservoirHead h13 0
!where h1,h2,... are computed heads from X vector
!also observe that H matrix has exactly the same number of elements as nodes in our whole domain grid(i.e. numDys+1 by numDxs+1)
open(1,file='H.txt')
do i=1,numDys+1
write(1,'(F5.1,$," ")') (H(i,j),j=1,numDxs+1)
write(1,*)
end do
close(1)
contourId=0
do searchHead=0,reservoirHead,contourInterval
contourId=contourId+1
!iterate through grid nodes one column at a time, left to right, looking for same potential(searchHead) right and top
!note that correspondence between our grid coordinate and H matrix's element is
!Head at coordinate (col,row) of grid = (row+1,col+1) element of H matrix
!Makes use of the fact that in a row, head decreases from left to right.
!Depending on column on grid, in a column, head decreases top to bottom for col<damX and increases for col>=damX
do col=0,numDxs
10 do row=0,numDys
currentHead=H(row+1,col+1)
if(currentHead.NE.searchHead) then
if(col<numDxs) then !look toward east node
rightHead=H(row+1,col+2)
if(searchHead.LE.currentHead.AND.searchHead.GE.rightHead) then
interpolatedX=col+1/(rightHead-currentHead)*(searchHead-currentHead)
contourPointsCounters(contourId)=contourPointsCounters(contourId)+1
EquipotentialX(contourPointsCounters(contourId),contourId)=interpolatedX
EquipotentialY(contourPointsCounters(contourId),contourId)=row
endif
endif
if(row<numDys) then !look toward north node
upHead=H(row+2,col+1)
if(col<DxsTillDam) then !if we're left of dam
!head increases towards up
if(searchHead.GE.currentHead.AND.searchHead.LE.upHead) then
interpolatedY=row+1/(upHead-currentHead)*(searchHead-currentHead)
contourPointsCounters(contourId)=contourPointsCounters(contourId)+1
EquipotentialY(contourPointsCounters(contourId),contourId)=interpolatedY
EquipotentialX(contourPointsCounters(contourId),contourId)=col
endif
else !if we're at or right of dam's heel
!head decreases towards up
if(searchHead.LE.currentHead.AND.searchHead.GE.upHead) then
interpolatedY=row+1/(upHead-currentHead)*(searchHead-currentHead)
contourPointsCounters(contourId)=contourPointsCounters(contourId)+1
EquipotentialY(contourPointsCounters(contourId),contourId)=interpolatedY
EquipotentialX(contourPointsCounters(contourId),contourId)=col
endif
endif
endif
else
!we're standing at our searchHead!!
contourPointsCounters(contourId)=contourPointsCounters(contourId)+1
EquipotentialY(contourPointsCounters(contourId),contourId)=row
EquipotentialX(contourPointsCounters(contourId),contourId)=col
endif
end do
end do
end do
!!----DEPRECATED EQUIPOTENTIAL POINT SORTING METHOD----
!!sort equipotential points in ascending (descending works too) order of x coordinate for proper equipotential line
!!sketching (left to right in ascending, right to left in descending).
!!EDIT : Equipotential curves that are improper functions (two y's for a x) don't behave nicely with
!!this method though. Only a couple of curves, generally towards the center of the flownet are as such
!!in my experience.
!do contourId=1,numberOfContours
! do i=1,contourPointsCounters(contourId)
! do j=i,contourPointsCounters(contourId)
! if(EquipotentialX(j,contourId).LT.EquipotentialX(i,contourId)) then
! tmp=EquipotentialY(i,contourId)
! EquipotentialY(i,contourId)=EquipotentialY(j,contourId)
! EquipotentialY(j,contourId)=tmp
!
! tmp=EquipotentialX(i,contourId)
! EquipotentialX(i,contourId)=EquipotentialX(j,contourId)
! EquipotentialX(j,contourId)=tmp
! endif
! end do
! end do
!end do
!equipotential points sorting for proper equipotential line sketching
!first sort equipotential points in descending order of Y coordinate(topmost to bottom-most)
!then sort based on shortest consecutive distance.
!better than sorting by x coordinate but not totally devoid of some weird artifacts
do contourId=1,numberOfContours
!sort descending in Y coordinate
do i=1,contourPointsCounters(contourId)
do j=i,contourPointsCounters(contourId)
if(EquipotentialY(j,contourId).GT.EquipotentialY(i,contourId)) then
!swap Y coordinate
tmp=EquipotentialY(i,contourId)
EquipotentialY(i,contourId)=EquipotentialY(j,contourId)
EquipotentialY(j,contourId)=tmp
!swap X coordinate
tmp=EquipotentialX(i,contourId)
EquipotentialX(i,contourId)=EquipotentialX(j,contourId)
EquipotentialX(j,contourId)=tmp
endif
end do
end do
!sort based on shortest consecutive distance (basically ordering the shortest path downward, downward coz the array is descending inY)
do i=1,contourPointsCounters(contourId)-1
smallestDistance=10000 !very large number
do j=i+1,contourPointsCounters(contourId)
!distance between i and j point
distance = (EquipotentialX(i,contourId)-EquipotentialX(j,contourId))**2 + (EquipotentialY(i,contourId)-EquipotentialY(j,contourId))**2
if(distance.LT.smallestDistance) then
smallestDistance=distance
smallestDistanceIndex=j
endif
end do
!swap i+1 point and point at smallestDistanceIndex
!X
tmp = EquipotentialX(i+1,contourId)
EquipotentialX(i+1,contourId) = EquipotentialX(smallestDistanceIndex,contourId)
EquipotentialX(smallestDistanceIndex,contourId) = tmp
!Y
tmp = EquipotentialY(i+1,contourId)
EquipotentialY(i+1,contourId) = EquipotentialY(smallestDistanceIndex,contourId)
EquipotentialY(smallestDistanceIndex,contourId) = tmp
end do
end do
!write equipotential points to file for observation
open(1,file='Isobars.txt')
do contourId=1,numberOfContours
write(1,*) (contourId-1)*contourInterval
write(1,'("(",F5.2,",",F5.2,") ",$)') (EquipotentialX(i,contourId),EquipotentialY(i,contourId),i=1,contourPointsCounters(contourId))
write(1,*) ''
end do
close(1)
!draw equipotential lines
do contourId=1,numberOfContours
call MOVETO_W(real(EquipotentialX(1,contourId),8),real(EquipotentialY(1,contourId),8),wxy)
!draw a circle on the equipotential point(not necessary)
status = SETCOLOR(6)
ret = ELLIPSE_W($GFILLINTERIOR,real(EquipotentialX(1,contourId)-0.015,8),real(EquipotentialY(1,contourId)+0.015,8),real(EquipotentialX(1,contourId)+0.015,8),real(EquipotentialY(1,contourId)-0.015,8))
status = SETCOLOR(2)
do i=2,contourPointsCounters(contourId)
!draw a circle on the equipotential point(not necessary)
status = SETCOLOR(6)
ret = ELLIPSE_W($GFILLINTERIOR,real(EquipotentialX(i,contourId)-0.015,8),real(EquipotentialY(i,contourId)+0.015,8),real(EquipotentialX(i,contourId)+0.015,8),real(EquipotentialY(i,contourId)-0.015,8))
status = SETCOLOR(2)
!avoids jumping lines
call GETCURRENTPOSITION_W(wxy)
if(abs(wxy.wx-EquipotentialX(i,contourId)).GT.4.OR.abs(wxy.wy-EquipotentialY(i,contourId)).GT.4) then
call MOVETO_W(real(EquipotentialX(i,contourId),8),real(EquipotentialY(i,contourId),8),wxy)
endif
ret = LINETO_W(real(EquipotentialX(i,contourId),8),real(EquipotentialY(i,contourId),8))
end do
end do
!draw one grid length horizontal line on grid top representing base of dam
status = SETCOLOR(8)
call MOVETO_W(real(DxsTillDam,8),real(numDys,8),wxy)
ret = LINETO_W(real(DxsTillDam+DamWidthInDxs,8),real(numDys,8))
!write heads on grid nodes
!note that correspondence between our grid coordinate and H matrix's element is
!head at coordinate (col,row) of grid = (row+1,col+1) element of H matrix
status = SETCOLOR(8)
status = INITIALIZEFONTS()
status = SETFONT('t''Arial''h12w8pvb')
!loop for except the bottom and right boundaries and bottom-right node (else they're clipped)
do i=0,numDxs-1
do j=1,numDys
call MOVETO_W(real(i,8),real(j,8),wxy)
write(char,'(F6.2)') H(j+1,i+1)
call OUTGTEXT(char)
end do
end do
!for bottom boundary
do i=0,numDxs-1
call MOVETO_W(real(i,8),real(0,8)+0.1,wxy)
write(char,'(F6.2)') H(0+1,i+1)
call OUTGTEXT(char)
end do
!for right boundary
do i=1,numDys
call MOVETO_W(real(numDxs,8)-0.3,real(i,8),wxy)
write(char,'(F6.2)') H(i+1,numDxs+1)
call OUTGTEXT(char)
end do
!for bottom right node
call MOVETO_W(real(numDxs,8)-0.3,real(0,8)+0.1,wxy)
write(char,'(F6.2)') H(0+1,numDxs+1)
call OUTGTEXT(char)
end program
subroutine Inverse(X,XI,size)
dimension X(500,500),XI(500,500),originalX(500,500)
integer rowindex,colindex,size,tmpi,tmpj,i,j
real pivotelement
originalX=X
do i=1,size
do j=1,size
if(i.EQ.j) then
XI(i,j)=1
else
XI(i,j)=0 !this is for good measure coz by default, XI may have been changed by the caller itself though it is 0 by default
endif
end do
end do
do rowindex=1,size !loop over rows of X
pivotelement=X(rowindex,rowindex) !this is crucial!
!divide the row by pivot element
do colindex=1,size
X(rowindex,colindex)=X(rowindex,colindex)/pivotelement
end do
do colindex=1,size
XI(rowindex,colindex)=XI(rowindex,colindex)/pivotelement
end do
!perform row operations to make non-pivot elements of this column zero
do i=1,size !i gives the row number being manipulated
pivotelement=X(i,rowindex) !pivot element for the currently manipulated row. this i crucial
if(i.NE.rowindex) then !row operations are for non-current rows only
do j=1,size !column index for ith row of X matrix
X(i,j)=X(i,j)-pivotelement*X(rowindex,j)
end do
do j=1,size !column index for ith row of XI matrix
XI(i,j)=XI(i,j)-pivotelement*XI(rowindex,j)
end do
endif
end do
end do
!restore X to original X since upon inversion completion, X has been reduced to an Identity matrix. good practice
X=originalX
end subroutine