-
Notifications
You must be signed in to change notification settings - Fork 137
Expand file tree
/
Copy pathm_compute_levelset.fpp
More file actions
626 lines (516 loc) · 28.5 KB
/
m_compute_levelset.fpp
File metadata and controls
626 lines (516 loc) · 28.5 KB
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
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
!>
!! @file m_compute_levelset.fpp
!! @brief Contains module m_compute_levelset
#:include 'macros.fpp'
!> @brief This module is used to handle all operations related to immersed
!! boundary methods (IBMs)
module m_compute_levelset
use m_derived_types !< Definitions of the derived types
use m_global_parameters !< Definitions of the global parameters
use m_mpi_proxy !< Message passing interface (MPI) module proxy
use m_helper_basic !< Functions to compare floating point numbers
implicit none
private; public :: s_cylinder_levelset, s_circle_levelset, &
s_airfoil_levelset, &
s_3D_airfoil_levelset, &
s_rectangle_levelset, &
s_cuboid_levelset, &
s_sphere_levelset, &
s_ellipse_levelset
contains
subroutine s_circle_levelset(ib_patch_id, levelset, levelset_norm)
type(levelset_field), intent(INOUT), optional :: levelset
type(levelset_norm_field), intent(INOUT), optional :: levelset_norm
integer, intent(IN) :: ib_patch_id
real(wp) :: radius, dist
real(wp), dimension(2) :: center
real(wp), dimension(3) :: dist_vec
integer :: i, j !< Loop index variables
radius = patch_ib(ib_patch_id)%radius
center(1) = patch_ib(ib_patch_id)%x_centroid
center(2) = patch_ib(ib_patch_id)%y_centroid
$:GPU_PARALLEL_LOOP(private='[i,j,dist_vec,dist]', &
& copyin='[ib_patch_id,center,radius]', collapse=2)
do i = 0, m
do j = 0, n
dist_vec(1) = x_cc(i) - center(1)
dist_vec(2) = y_cc(j) - center(2)
dist_vec(3) = 0._wp
dist = sqrt(sum(dist_vec**2))
levelset%sf(i, j, 0, ib_patch_id) = dist - radius
if (f_approx_equal(dist, 0._wp)) then
levelset_norm%sf(i, j, 0, ib_patch_id, :) = 0
else
levelset_norm%sf(i, j, 0, ib_patch_id, :) = &
dist_vec(:)/dist
end if
end do
end do
$:END_GPU_PARALLEL_LOOP()
end subroutine s_circle_levelset
subroutine s_airfoil_levelset(ib_patch_id, levelset, levelset_norm)
type(levelset_field), intent(inout), optional :: levelset
type(levelset_norm_field), intent(inout), optional :: levelset_norm
integer, intent(in) :: ib_patch_id
real(wp) :: dist, global_dist
integer :: global_id
real(wp), dimension(3) :: dist_vec
real(wp), dimension(1:3) :: xy_local !< x and y coordinates in local IB frame
real(wp), dimension(1:2) :: center
real(wp), dimension(1:3, 1:3) :: rotation, inverse_rotation
integer :: i, j, k !< Loop index variables
center(1) = patch_ib(ib_patch_id)%x_centroid
center(2) = patch_ib(ib_patch_id)%y_centroid
inverse_rotation(:, :) = patch_ib(ib_patch_id)%rotation_matrix_inverse(:, :)
rotation(:, :) = patch_ib(ib_patch_id)%rotation_matrix(:, :)
$:GPU_PARALLEL_LOOP(private='[i,j,xy_local,k,dist_vec,dist,global_dist,global_id]', &
& copyin='[ib_patch_id,center,rotation,inverse_rotation,airfoil_grid_u,airfoil_grid_l]', collapse=2)
do i = 0, m
do j = 0, n
xy_local = [x_cc(i) - center(1), y_cc(j) - center(2), 0._wp] ! get coordinate frame centered on IB
xy_local = matmul(inverse_rotation, xy_local) ! rotate the frame into the IB's coordinate
xy_local = xy_local - patch_ib(ib_patch_id)%centroid_offset ! airfoils are a patch that require a centroid offset
if (xy_local(2) >= 0._wp) then
! finds the location on the airfoil grid with the minimum distance (closest)
do k = 1, Np
dist_vec(1) = xy_local(1) - airfoil_grid_u(k)%x
dist_vec(2) = xy_local(2) - airfoil_grid_u(k)%y
dist_vec(3) = 0._wp
dist = sqrt(sum(dist_vec**2))
if (k == 1) then
global_dist = dist
global_id = k
else
if (dist < global_dist) then
global_dist = dist
global_id = k
end if
end if
end do
dist_vec(1) = xy_local(1) - airfoil_grid_u(global_id)%x
dist_vec(2) = xy_local(2) - airfoil_grid_u(global_id)%y
dist_vec(3) = 0
dist = global_dist
else
! TODO :: This looks identical to the above code but using the lower array. Refactor this.
do k = 1, Np
dist_vec(1) = xy_local(1) - airfoil_grid_l(k)%x
dist_vec(2) = xy_local(2) - airfoil_grid_l(k)%y
dist_vec(3) = 0
dist = sqrt(sum(dist_vec**2))
if (k == 1) then
global_dist = dist
global_id = k
else
if (dist < global_dist) then
global_dist = dist
global_id = k
end if
end if
end do
dist_vec(1) = xy_local(1) - airfoil_grid_l(global_id)%x
dist_vec(2) = xy_local(2) - airfoil_grid_l(global_id)%y
dist_vec(3) = 0
dist = global_dist
end if
levelset%sf(i, j, 0, ib_patch_id) = dist
if (f_approx_equal(dist, 0._wp)) then
levelset_norm%sf(i, j, 0, ib_patch_id, :) = 0._wp
else
levelset_norm%sf(i, j, 0, ib_patch_id, :) = &
matmul(rotation, dist_vec(:))/dist ! convert the normal vector back to global grid coordinates
end if
end do
end do
$:END_GPU_PARALLEL_LOOP()
end subroutine s_airfoil_levelset
subroutine s_3D_airfoil_levelset(ib_patch_id, levelset, levelset_norm)
type(levelset_field), intent(INOUT), optional :: levelset
type(levelset_norm_field), intent(INOUT), optional :: levelset_norm
integer, intent(IN) :: ib_patch_id
real(wp) :: dist, dist_surf, dist_side, global_dist
integer :: global_id
real(wp) :: lz, z_max, z_min
real(wp), dimension(3) :: dist_vec
real(wp), dimension(1:3) :: xyz_local, center !< x, y, z coordinates in local IB frame
real(wp), dimension(1:3, 1:3) :: rotation, inverse_rotation
real(wp) :: length_z
integer :: i, j, k, l !< Loop index variables
center(1) = patch_ib(ib_patch_id)%x_centroid
center(2) = patch_ib(ib_patch_id)%y_centroid
center(3) = patch_ib(ib_patch_id)%z_centroid
lz = patch_ib(ib_patch_id)%length_z
inverse_rotation(:, :) = patch_ib(ib_patch_id)%rotation_matrix_inverse(:, :)
rotation(:, :) = patch_ib(ib_patch_id)%rotation_matrix(:, :)
z_max = center(3) + lz/2
z_min = center(3) - lz/2
$:GPU_PARALLEL_LOOP(private='[i,j,l,xyz_local,k,dist_vec,dist,global_dist,global_id,dist_side,dist_surf]', &
& copyin='[ib_patch_id,center,rotation,inverse_rotation,airfoil_grid_u,airfoil_grid_l,z_min,z_max]', collapse=3)
do l = 0, p
do j = 0, n
do i = 0, m
xyz_local = [x_cc(i) - center(1), y_cc(j) - center(2), z_cc(l) - center(3)] ! get coordinate frame centered on IB
xyz_local = matmul(inverse_rotation, xyz_local) ! rotate the frame into the IB's coordinates
xyz_local = xyz_local - patch_ib(ib_patch_id)%centroid_offset ! airfoils are a patch that require a centroid offset
if (xyz_local(2) >= center(2)) then
do k = 1, Np
dist_vec(1) = xyz_local(1) - airfoil_grid_u(k)%x
dist_vec(2) = xyz_local(2) - airfoil_grid_u(k)%y
dist_vec(3) = 0
dist_surf = sqrt(sum(dist_vec**2))
if (k == 1) then
global_dist = dist_surf
global_id = k
else
if (dist_surf < global_dist) then
global_dist = dist_surf
global_id = k
end if
end if
end do
dist_vec(1) = xyz_local(1) - airfoil_grid_u(global_id)%x
dist_vec(2) = xyz_local(2) - airfoil_grid_u(global_id)%y
dist_vec(3) = 0
dist_surf = global_dist
else
do k = 1, Np
dist_vec(1) = xyz_local(1) - airfoil_grid_l(k)%x
dist_vec(2) = xyz_local(2) - airfoil_grid_l(k)%y
dist_vec(3) = 0
dist_surf = sqrt(sum(dist_vec**2))
if (k == 1) then
global_dist = dist_surf
global_id = k
else
if (dist_surf < global_dist) then
global_dist = dist_surf
global_id = k
end if
end if
end do
dist_vec(1) = xyz_local(1) - airfoil_grid_l(global_id)%x
dist_vec(2) = xyz_local(2) - airfoil_grid_l(global_id)%y
dist_vec(3) = 0
dist_surf = global_dist
end if
dist_side = min(abs(z_cc(l) - z_min), abs(z_max - z_cc(l)))
if (dist_side < dist_surf) then
levelset%sf(i, j, l, ib_patch_id) = dist_side
if (f_approx_equal(dist_side, abs(z_cc(l) - z_min))) then
levelset_norm%sf(i, j, l, ib_patch_id, :) = (/0, 0, -1/)
else
levelset_norm%sf(i, j, l, ib_patch_id, :) = (/0, 0, 1/)
end if
levelset_norm%sf(i, j, l, ib_patch_id, :) = &
matmul(rotation, levelset_norm%sf(i, j, l, ib_patch_id, :)/dist_surf)
else
levelset%sf(i, j, l, ib_patch_id) = dist_surf
if (f_approx_equal(dist_surf, 0._wp)) then
levelset_norm%sf(i, j, l, ib_patch_id, :) = 0
else
levelset_norm%sf(i, j, l, ib_patch_id, :) = &
matmul(rotation, dist_vec(:)/dist_surf)
end if
end if
end do
end do
end do
$:END_GPU_PARALLEL_LOOP()
end subroutine s_3D_airfoil_levelset
!> Initialize IBM module
subroutine s_rectangle_levelset(ib_patch_id, levelset, levelset_norm)
type(levelset_field), intent(INOUT), optional :: levelset
type(levelset_norm_field), intent(INOUT), optional :: levelset_norm
integer, intent(in) :: ib_patch_id
real(wp) :: top_right(2), bottom_left(2)
real(wp) :: min_dist
real(wp) :: side_dists(4)
real(wp) :: length_x, length_y
real(wp), dimension(1:3) :: xy_local, dist_vec !< x and y coordinates in local IB frame
real(wp), dimension(2) :: center !< x and y coordinates in local IB frame
real(wp), dimension(1:3, 1:3) :: rotation, inverse_rotation
integer :: i, j, k !< Loop index variables
integer :: idx !< Shortest path direction indicator
length_x = patch_ib(ib_patch_id)%length_x
length_y = patch_ib(ib_patch_id)%length_y
center(1) = patch_ib(ib_patch_id)%x_centroid
center(2) = patch_ib(ib_patch_id)%y_centroid
inverse_rotation(:, :) = patch_ib(ib_patch_id)%rotation_matrix_inverse(:, :)
rotation(:, :) = patch_ib(ib_patch_id)%rotation_matrix(:, :)
top_right(1) = length_x/2
top_right(2) = length_y/2
bottom_left(1) = -length_x/2
bottom_left(2) = -length_y/2
$:GPU_PARALLEL_LOOP(private='[i,j,k,min_dist,idx,side_dists,xy_local,dist_vec]', &
& copyin='[ib_patch_id,center,bottom_left,top_right,inverse_rotation,rotation]', collapse=2)
do i = 0, m
do j = 0, n
xy_local = [x_cc(i) - center(1), y_cc(j) - center(2), 0._wp]
xy_local = matmul(inverse_rotation, xy_local)
if ((xy_local(1) > bottom_left(1) .and. xy_local(1) < top_right(1)) .or. &
(xy_local(2) > bottom_left(2) .and. xy_local(2) < top_right(2))) then
side_dists(1) = bottom_left(1) - xy_local(1)
side_dists(2) = top_right(1) - xy_local(1)
side_dists(3) = bottom_left(2) - xy_local(2)
side_dists(4) = top_right(2) - xy_local(2)
min_dist = side_dists(1)
idx = 1
do k = 2, 4
if (abs(side_dists(k)) < abs(min_dist)) then
idx = k
min_dist = side_dists(idx)
end if
end do
levelset%sf(i, j, 0, ib_patch_id) = side_dists(idx)
dist_vec = 0._wp
if (.not. f_approx_equal(side_dists(idx), 0._wp)) then
if (idx == 1 .or. idx == 2) then
! vector points along the x axis
dist_vec(1) = side_dists(idx)/abs(side_dists(idx))
else
! vector points along the y axis
dist_vec(2) = side_dists(idx)/abs(side_dists(idx))
end if
! convert the normal vector back into the global coordinate system
levelset_norm%sf(i, j, 0, ib_patch_id, :) = matmul(rotation, dist_vec)
else
levelset_norm%sf(i, j, 0, ib_patch_id, :) = 0._wp
end if
end if
end do
end do
$:END_GPU_PARALLEL_LOOP()
end subroutine s_rectangle_levelset
subroutine s_ellipse_levelset(ib_patch_id, levelset, levelset_norm)
type(levelset_field), intent(INOUT), optional :: levelset
type(levelset_norm_field), intent(INOUT), optional :: levelset_norm
integer, intent(in) :: ib_patch_id
real(wp) :: ellipse_coeffs(2) ! a and b in the ellipse equation
real(wp) :: quadratic_coeffs(3) ! A, B, C in the quadratic equation to compute levelset
real(wp) :: length_x, length_y
real(wp), dimension(1:3) :: xy_local, normal_vector !< x and y coordinates in local IB frame
real(wp), dimension(2) :: center !< x and y coordinates in local IB frame
real(wp), dimension(1:3, 1:3) :: rotation, inverse_rotation
integer :: i, j, k !< Loop index variables
integer :: idx !< Shortest path direction indicator
length_x = patch_ib(ib_patch_id)%length_x
length_y = patch_ib(ib_patch_id)%length_y
center(1) = patch_ib(ib_patch_id)%x_centroid
center(2) = patch_ib(ib_patch_id)%y_centroid
inverse_rotation(:, :) = patch_ib(ib_patch_id)%rotation_matrix_inverse(:, :)
rotation(:, :) = patch_ib(ib_patch_id)%rotation_matrix(:, :)
ellipse_coeffs(1) = 0.5_wp*length_x
ellipse_coeffs(2) = 0.5_wp*length_y
$:GPU_PARALLEL_LOOP(private='[i,j,k,idx,quadratic_coeffs,xy_local,normal_vector]', &
& copyin='[ib_patch_id,center,ellipse_coeffs,inverse_rotation,rotation]', collapse=2)
do i = 0, m
do j = 0, n
xy_local = [x_cc(i) - center(1), y_cc(j) - center(2), 0._wp]
xy_local = matmul(inverse_rotation, xy_local)
! we will get NaNs in the levelset if we compute this outside the ellipse
if ((xy_local(1)/ellipse_coeffs(1))**2 + (xy_local(2)/ellipse_coeffs(2))**2 <= 1._wp) then
normal_vector = xy_local
normal_vector(2) = normal_vector(2)*(ellipse_coeffs(1)/ellipse_coeffs(2))**2._wp ! get the normal direction via the coordinate transformation method
normal_vector = normal_vector/sqrt(dot_product(normal_vector, normal_vector)) ! normalize the vector
levelset_norm%sf(i, j, 0, ib_patch_id, :) = matmul(rotation, normal_vector) ! save after rotating the vector to the global frame
! use the normal vector to set up the quadratic equation for the levelset, using A, B, and C in indices 1, 2, and 3
quadratic_coeffs(1) = (normal_vector(1)/ellipse_coeffs(1))**2 + (normal_vector(2)/ellipse_coeffs(2))**2
quadratic_coeffs(2) = 2._wp*((xy_local(1)*normal_vector(1)/(ellipse_coeffs(1)**2)) + (xy_local(2)*normal_vector(2)/(ellipse_coeffs(2)**2)))
quadratic_coeffs(3) = (xy_local(1)/ellipse_coeffs(1))**2._wp + (xy_local(2)/ellipse_coeffs(2))**2._wp - 1._wp
! compute the levelset with the quadratic equation [ -B + sqrt(B^2 - 4AC) ] / 2A
levelset%sf(i, j, 0, ib_patch_id) = -0.5_wp*(-quadratic_coeffs(2) + sqrt(quadratic_coeffs(2)**2._wp - 4._wp*quadratic_coeffs(1)*quadratic_coeffs(3)))/quadratic_coeffs(1)
end if
end do
end do
$:END_GPU_PARALLEL_LOOP()
end subroutine s_ellipse_levelset
subroutine s_cuboid_levelset(ib_patch_id, levelset, levelset_norm)
type(levelset_field), intent(INOUT), optional :: levelset
type(levelset_norm_field), intent(INOUT), optional :: levelset_norm
integer, intent(IN) :: ib_patch_id
real(wp) :: Right, Left, Bottom, Top, Front, Back
real(wp) :: min_dist
real(wp) :: side_dists(6)
real(wp), dimension(3) :: center
real(wp) :: length_x, length_y, length_z
real(wp), dimension(1:3) :: xyz_local, dist_vec !< x and y coordinates in local IB frame
real(wp), dimension(1:3, 1:3) :: rotation, inverse_rotation
integer :: i, j, k !< Loop index variables
length_x = patch_ib(ib_patch_id)%length_x
length_y = patch_ib(ib_patch_id)%length_y
length_z = patch_ib(ib_patch_id)%length_z
center(1) = patch_ib(ib_patch_id)%x_centroid
center(2) = patch_ib(ib_patch_id)%y_centroid
center(3) = patch_ib(ib_patch_id)%z_centroid
inverse_rotation(:, :) = patch_ib(ib_patch_id)%rotation_matrix_inverse(:, :)
rotation(:, :) = patch_ib(ib_patch_id)%rotation_matrix(:, :)
Right = length_x/2
Left = -length_x/2
Top = length_y/2
Bottom = -length_y/2
Front = length_z/2
Back = -length_z/2
$:GPU_PARALLEL_LOOP(private='[i,j,k,min_dist,side_dists,xyz_local,dist_vec]', &
& copyin='[ib_patch_id,center,inverse_rotation,rotation,Right,Left,Top,Bottom,Front,Back]', collapse=3)
do i = 0, m
do j = 0, n
do k = 0, p
xyz_local = [x_cc(i), y_cc(j), z_cc(k)] - center ! get coordinate frame centered on IB
xyz_local = matmul(inverse_rotation, xyz_local) ! rotate the frame into the IB's coordinate
if ((xyz_local(1) > Left .and. xyz_local(1) < Right) .or. &
(xyz_local(2) > Bottom .and. xyz_local(2) < Top) .or. &
(xyz_local(3) > Back .and. xyz_local(3) < Front)) then
side_dists(1) = Left - xyz_local(1)
side_dists(2) = xyz_local(1) - Right
side_dists(3) = Bottom - xyz_local(2)
side_dists(4) = xyz_local(2) - Top
side_dists(5) = Back - xyz_local(3)
side_dists(6) = xyz_local(3) - Front
min_dist = minval(abs(side_dists))
! TODO :: The way that this is written, it looks like we will
! trigger at the first size that is close to the minimum distance,
! meaning corners where side_dists are the same will
! trigger on what may not actually be the minimum,
! leading to undesired behavior. This should be resolved
! and this code should be cleaned up. It also means that
! rotating the box 90 degrees will cause tests to fail.
dist_vec = 0._wp
if (f_approx_equal(min_dist, abs(side_dists(1)))) then
levelset%sf(i, j, k, ib_patch_id) = side_dists(1)
if (.not. f_approx_equal(side_dists(1), 0._wp)) then
dist_vec(1) = side_dists(1)/abs(side_dists(1))
end if
else if (f_approx_equal(min_dist, abs(side_dists(2)))) then
levelset%sf(i, j, k, ib_patch_id) = side_dists(2)
if (.not. f_approx_equal(side_dists(2), 0._wp)) then
dist_vec(1) = -side_dists(2)/abs(side_dists(2))
end if
else if (f_approx_equal(min_dist, abs(side_dists(3)))) then
levelset%sf(i, j, k, ib_patch_id) = side_dists(3)
if (.not. f_approx_equal(side_dists(3), 0._wp)) then
dist_vec(2) = side_dists(3)/abs(side_dists(3))
end if
else if (f_approx_equal(min_dist, abs(side_dists(4)))) then
levelset%sf(i, j, k, ib_patch_id) = side_dists(4)
if (.not. f_approx_equal(side_dists(4), 0._wp)) then
dist_vec(2) = -side_dists(4)/abs(side_dists(4))
end if
else if (f_approx_equal(min_dist, abs(side_dists(5)))) then
levelset%sf(i, j, k, ib_patch_id) = side_dists(5)
if (.not. f_approx_equal(side_dists(5), 0._wp)) then
dist_vec(3) = side_dists(5)/abs(side_dists(5))
end if
else if (f_approx_equal(min_dist, abs(side_dists(6)))) then
levelset%sf(i, j, k, ib_patch_id) = side_dists(6)
if (.not. f_approx_equal(side_dists(6), 0._wp)) then
dist_vec(3) = -side_dists(6)/abs(side_dists(6))
end if
end if
levelset_norm%sf(i, j, k, ib_patch_id, :) = matmul(rotation, dist_vec)
end if
end do
end do
end do
$:END_GPU_PARALLEL_LOOP()
end subroutine s_cuboid_levelset
subroutine s_sphere_levelset(ib_patch_id, levelset, levelset_norm)
type(levelset_field), intent(INOUT), optional :: levelset
type(levelset_norm_field), intent(INOUT), optional :: levelset_norm
integer, intent(IN) :: ib_patch_id
real(wp) :: radius, dist
real(wp), dimension(3) :: dist_vec, center
integer :: i, j, k !< Loop index variables
radius = patch_ib(ib_patch_id)%radius
center(1) = patch_ib(ib_patch_id)%x_centroid
center(2) = patch_ib(ib_patch_id)%y_centroid
center(3) = patch_ib(ib_patch_id)%z_centroid
$:GPU_PARALLEL_LOOP(private='[i,j,k,dist_vec,dist]', &
& copyin='[ib_patch_id,center,radius]', collapse=3)
do i = 0, m
do j = 0, n
do k = 0, p
dist_vec(1) = x_cc(i) - center(1)
dist_vec(2) = y_cc(j) - center(2)
dist_vec(3) = z_cc(k) - center(3)
dist = sqrt(sum(dist_vec**2))
levelset%sf(i, j, k, ib_patch_id) = dist - radius
if (f_approx_equal(dist, 0._wp)) then
levelset_norm%sf(i, j, k, ib_patch_id, :) = (/1, 0, 0/)
else
levelset_norm%sf(i, j, k, ib_patch_id, :) = dist_vec(:)/dist
end if
end do
end do
end do
$:END_GPU_PARALLEL_LOOP()
end subroutine s_sphere_levelset
subroutine s_cylinder_levelset(ib_patch_id, levelset, levelset_norm)
type(levelset_field), intent(INOUT), optional :: levelset
type(levelset_norm_field), intent(INOUT), optional :: levelset_norm
integer, intent(IN) :: ib_patch_id
real(wp) :: radius
real(wp), dimension(3) :: dist_sides_vec, dist_surface_vec, length
real(wp), dimension(2) :: boundary
real(wp) :: dist_side, dist_surface, side_pos
integer :: i, j, k !< Loop index variables
real(wp), dimension(1:3) :: xyz_local, center !< x and y coordinates in local IB frame
real(wp), dimension(1:3, 1:3) :: rotation, inverse_rotation
radius = patch_ib(ib_patch_id)%radius
center(1) = patch_ib(ib_patch_id)%x_centroid
center(2) = patch_ib(ib_patch_id)%y_centroid
center(3) = patch_ib(ib_patch_id)%z_centroid
length(1) = patch_ib(ib_patch_id)%length_x
length(2) = patch_ib(ib_patch_id)%length_y
length(3) = patch_ib(ib_patch_id)%length_z
inverse_rotation(:, :) = patch_ib(ib_patch_id)%rotation_matrix_inverse(:, :)
rotation(:, :) = patch_ib(ib_patch_id)%rotation_matrix(:, :)
if (.not. f_approx_equal(length(1), 0._wp)) then
boundary(1) = -0.5_wp*length(1)
boundary(2) = 0.5_wp*length(1)
dist_sides_vec = (/1, 0, 0/)
dist_surface_vec = (/0, 1, 1/)
else if (.not. f_approx_equal(length(2), 0._wp)) then
boundary(1) = -0.5_wp*length(2)
boundary(2) = 0.5_wp*length(2)
dist_sides_vec = (/0, 1, 0/)
dist_surface_vec = (/1, 0, 1/)
else if (.not. f_approx_equal(length(3), 0._wp)) then
boundary(1) = -0.5_wp*length(3)
boundary(2) = 0.5_wp*length(3)
dist_sides_vec = (/0, 0, 1/)
dist_surface_vec = (/1, 1, 0/)
end if
$:GPU_PARALLEL_LOOP(private='[i,j,k,side_pos,dist_side,dist_surface,xyz_local]', &
& copyin='[ib_patch_id,center,radius,inverse_rotation,rotation,dist_sides_vec,dist_surface_vec]', collapse=3)
do i = 0, m
do j = 0, n
do k = 0, p
xyz_local = [x_cc(i), y_cc(j), z_cc(k)] - center ! get coordinate frame centered on IB
xyz_local = matmul(inverse_rotation, xyz_local) ! rotate the frame into the IB's coordinates
! get distance to flat edge of cylinder
side_pos = dot_product(xyz_local, dist_sides_vec)
dist_side = min(abs(side_pos - boundary(1)), &
abs(boundary(2) - side_pos))
! get distance to curved side of cylinder
dist_surface = norm2(xyz_local*dist_surface_vec) &
- radius
if (dist_side < abs(dist_surface)) then
! if the closest edge is flat
levelset%sf(i, j, k, ib_patch_id) = -dist_side
if (f_approx_equal(dist_side, abs(side_pos - boundary(1)))) then
levelset_norm%sf(i, j, k, ib_patch_id, :) = matmul(rotation, -dist_sides_vec)
else
levelset_norm%sf(i, j, k, ib_patch_id, :) = matmul(rotation, dist_sides_vec)
end if
else
levelset%sf(i, j, k, ib_patch_id) = dist_surface
xyz_local = xyz_local*dist_surface_vec
xyz_local = xyz_local/norm2(xyz_local)
levelset_norm%sf(i, j, k, ib_patch_id, :) = matmul(rotation, xyz_local)
end if
end do
end do
end do
$:END_GPU_PARALLEL_LOOP()
end subroutine s_cylinder_levelset
end module m_compute_levelset