Skip to content

Commit 9a3093b

Browse files
danieljvickersDaniel VickersDaniel J VickersDaniel Vickerssbryngelson
authored
Invicid two-way fluid-structure interaction (#1075)
Co-authored-by: Daniel Vickers <danieljvickers@login08.frontier.olcf.ornl.gov> Co-authored-by: Daniel J Vickers <dvickers6@atl1-1-01-003-34-0.pace.gatech.edu> Co-authored-by: Daniel Vickers <danieljvickers@login04.frontier.olcf.ornl.gov> Co-authored-by: Spencer Bryngelson <sbryngelson@gmail.com>
1 parent b220ebf commit 9a3093b

File tree

13 files changed

+676
-181
lines changed

13 files changed

+676
-181
lines changed

docs/documentation/case.md

Lines changed: 170 additions & 161 deletions
Large diffs are not rendered by default.
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import json
2+
import math
3+
4+
Mu = 1.84e-05
5+
gam_a = 1.4
6+
7+
# Configuring case dictionary
8+
print(
9+
json.dumps(
10+
{
11+
# Logistics
12+
"run_time_info": "T",
13+
# Computational Domain Parameters
14+
# For these computations, the cylinder is placed at the (0,0,0)
15+
# domain origin.
16+
# axial direction
17+
"x_domain%beg": 0.0e00,
18+
"x_domain%end": 6.0e-03,
19+
# r direction
20+
"y_domain%beg": 0.0e00,
21+
"y_domain%end": 6.0e-03,
22+
"cyl_coord": "F",
23+
"m": 200,
24+
"n": 200,
25+
"p": 0,
26+
"dt": 6.0e-6,
27+
"t_step_start": 0,
28+
"t_step_stop": 10000, # 10000,
29+
"t_step_save": 100,
30+
# Simulation Algorithm Parameters
31+
# Only one patches are necessary, the air tube
32+
"num_patches": 1,
33+
# Use the 5 equation model
34+
"model_eqns": 2,
35+
"alt_soundspeed": "F",
36+
# One fluids: air
37+
"num_fluids": 1,
38+
# time step
39+
"mpp_lim": "F",
40+
# Correct errors when computing speed of sound
41+
"mixture_err": "T",
42+
# Use TVD RK3 for time marching
43+
"time_stepper": 3,
44+
# Use WENO5
45+
"weno_order": 5,
46+
"weno_eps": 1.0e-16,
47+
"weno_Re_flux": "T",
48+
"weno_avg": "T",
49+
"avg_state": 2,
50+
"mapped_weno": "T",
51+
"null_weights": "F",
52+
"mp_weno": "T",
53+
"riemann_solver": 2,
54+
"wave_speeds": 1,
55+
# We use ghost-cell
56+
"bc_x%beg": -3,
57+
"bc_x%end": -3,
58+
"bc_y%beg": -3,
59+
"bc_y%end": -3,
60+
# Set IB to True and add 1 patch
61+
"ib": "T",
62+
"num_ibs": 1,
63+
"viscous": "T",
64+
# Formatted Database Files Structure Parameters
65+
"format": 1,
66+
"precision": 2,
67+
"prim_vars_wrt": "T",
68+
"E_wrt": "T",
69+
"parallel_io": "T",
70+
# Patch: Constant Tube filled with air
71+
# Specify the cylindrical air tube grid geometry
72+
"patch_icpp(1)%geometry": 3,
73+
"patch_icpp(1)%x_centroid": 3.0e-03,
74+
# Uniform medium density, centroid is at the center of the domain
75+
"patch_icpp(1)%y_centroid": 3.0e-03,
76+
"patch_icpp(1)%length_x": 6.0e-03,
77+
"patch_icpp(1)%length_y": 6.0e-03,
78+
# Specify the patch primitive variables
79+
"patch_icpp(1)%vel(1)": 0.05e00,
80+
"patch_icpp(1)%vel(2)": 0.0e00,
81+
"patch_icpp(1)%pres": 1.0e00,
82+
"patch_icpp(1)%alpha_rho(1)": 1.0e00,
83+
"patch_icpp(1)%alpha(1)": 1.0e00,
84+
# Patch: Cylinder Immersed Boundary
85+
"patch_ib(1)%geometry": 2,
86+
"patch_ib(1)%x_centroid": 1.5e-03,
87+
"patch_ib(1)%y_centroid": 4.5e-03,
88+
"patch_ib(1)%radius": 0.3e-03,
89+
"patch_ib(1)%slip": "F",
90+
"patch_ib(1)%moving_ibm": 2,
91+
"patch_ib(1)%vel(2)": -0.1,
92+
"patch_ib(1)%angles(1)": 0.0, # x-axis rotation in radians
93+
"patch_ib(1)%angles(2)": 0.0, # y-axis rotation
94+
"patch_ib(1)%angles(3)": 0.0, # z-axis rotation
95+
"patch_ib(1)%angular_vel(1)": 0.0, # x-axis rotational velocity in radians per second
96+
"patch_ib(1)%angular_vel(2)": 0.0, # y-axis rotation
97+
"patch_ib(1)%angular_vel(3)": 0.0, # z-axis rotation
98+
"patch_ib(1)%mass": 1.0e-6, # z-axis rotation
99+
# Fluids Physical Parameters
100+
"fluid_pp(1)%gamma": 1.0e00 / (gam_a - 1.0e00), # 2.50(Not 1.40)
101+
"fluid_pp(1)%pi_inf": 0,
102+
"fluid_pp(1)%Re(1)": 2500000,
103+
}
104+
)
105+
)

src/common/m_derived_types.fpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,8 @@ module m_derived_types
332332

333333
!! Patch conditions for moving imersed boundaries
334334
integer :: moving_ibm ! 0 for no moving, 1 for moving, 2 for moving on forced path
335+
real(wp) :: mass, moment ! mass and moment of inertia of object used to compute forces in 2-way coupling
336+
real(wp), dimension(1:3) :: force, torque ! vectors for the computed force and torque values applied to an IB
335337
real(wp), dimension(1:3) :: vel
336338
real(wp), dimension(1:3) :: step_vel ! velocity array used to store intermediate steps in the time_stepper module
337339
real(wp), dimension(1:3) :: angular_vel

src/common/m_mpi_common.fpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,32 @@ contains
472472
473473
end subroutine s_mpi_allreduce_sum
474474
475+
!> This subroutine follows the behavior of the s_mpi_allreduce_sum subroutine
476+
!> with the additional feature that it reduces an array of vectors.
477+
impure subroutine s_mpi_allreduce_vectors_sum(var_loc, var_glb, num_vectors, vector_length)
478+
479+
integer, intent(in) :: num_vectors, vector_length
480+
real(wp), dimension(:, :), intent(in) :: var_loc
481+
real(wp), dimension(:, :), intent(out) :: var_glb
482+
483+
#ifdef MFC_MPI
484+
integer :: ierr !< Generic flag used to identify and report MPI errors
485+
486+
! Performing the reduction procedure
487+
if (loc(var_loc) == loc(var_glb)) then
488+
call MPI_Allreduce(MPI_IN_PLACE, var_glb, num_vectors*vector_length, &
489+
mpi_p, MPI_SUM, MPI_COMM_WORLD, ierr)
490+
else
491+
call MPI_Allreduce(var_loc, var_glb, num_vectors*vector_length, &
492+
mpi_p, MPI_SUM, MPI_COMM_WORLD, ierr)
493+
end if
494+
495+
#else
496+
var_glb(1:num_vectors, 1:vector_length) = var_loc(1:num_vectors, 1:vector_length)
497+
#endif
498+
499+
end subroutine s_mpi_allreduce_vectors_sum
500+
475501
!> The following subroutine takes the input local variable
476502
!! from all processors and reduces to the sum of all
477503
!! values. The reduced variable is recorded back onto the

src/pre_process/m_global_parameters.fpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,8 @@ contains
560560
patch_ib(i)%vel(:) = 0._wp
561561
patch_ib(i)%angles(:) = 0._wp
562562
patch_ib(i)%angular_vel(:) = 0._wp
563+
patch_ib(i)%mass = dflt_real
564+
patch_ib(i)%moment = dflt_real
563565

564566
! sets values of a rotation matrix which can be used when calculating rotations
565567
patch_ib(i)%rotation_matrix = 0._wp

src/simulation/m_ibm.fpp

Lines changed: 180 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ contains
101101
end if
102102
call s_update_ib_rotation_matrix(i)
103103
end do
104-
105104
$:GPU_ENTER_DATA(copyin='[patch_ib]')
106105

107106
! Allocating the patch identities bookkeeping variable
@@ -197,6 +196,19 @@ contains
197196
type(ghost_point) :: gp
198197
type(ghost_point) :: innerp
199198

199+
! set the Moving IBM interior Pressure Values
200+
$:GPU_PARALLEL_LOOP(private='[i,j,k]', copyin='[E_idx]', collapse=3)
201+
do l = 0, p
202+
do k = 0, n
203+
do j = 0, m
204+
if (ib_markers%sf(j, k, l) /= 0) then
205+
q_prim_vf(E_idx)%sf(j, k, l) = 1._wp
206+
end if
207+
end do
208+
end do
209+
end do
210+
$:END_GPU_PARALLEL_LOOP()
211+
200212
if (num_gps > 0) then
201213
$:GPU_PARALLEL_LOOP(private='[i,physical_loc,dyn_pres,alpha_rho_IP, alpha_IP,pres_IP,vel_IP,vel_g,vel_norm_IP,r_IP, v_IP,pb_IP,mv_IP,nmom_IP,presb_IP,massv_IP,rho, gamma,pi_inf,Re_K,G_K,Gs,gp,innerp,norm,buf, radial_vector, rotation_velocity, j,k,l,q,qv_K,c_IP,nbub,patch_id]')
202214
do i = 1, num_gps
@@ -244,6 +256,19 @@ contains
244256
if (surface_tension) then
245257
q_prim_vf(c_idx)%sf(j, k, l) = c_IP
246258
end if
259+
260+
! set the pressure
261+
if (patch_ib(patch_id)%moving_ibm <= 1) then
262+
q_prim_vf(E_idx)%sf(j, k, l) = pres_IP
263+
else
264+
q_prim_vf(E_idx)%sf(j, k, l) = 0._wp
265+
$:GPU_LOOP(parallelism='[seq]')
266+
do q = 1, num_fluids
267+
! Se the pressure inside a moving immersed boundary based upon the pressure of the image point. acceleration, and normal vector direction
268+
q_prim_vf(E_idx)%sf(j, k, l) = q_prim_vf(E_idx)%sf(j, k, l) + pres_IP/(1._wp - 2._wp*abs(levelset%sf(j, k, l, patch_id)*alpha_rho_IP(q)/pres_IP)*dot_product(patch_ib(patch_id)%force/patch_ib(patch_id)%mass, levelset_norm%sf(j, k, l, patch_id, :)))
269+
end do
270+
end if
271+
247272
if (model_eqns /= 4) then
248273
! If in simulation, use acc mixture subroutines
249274
if (elasticity) then
@@ -266,7 +291,7 @@ contains
266291
! compute the linear velocity of the ghost point due to rotation
267292
radial_vector = physical_loc - [patch_ib(patch_id)%x_centroid, &
268293
patch_ib(patch_id)%y_centroid, patch_ib(patch_id)%z_centroid]
269-
rotation_velocity = cross_product(matmul(patch_ib(patch_id)%rotation_matrix, patch_ib(patch_id)%angular_vel), radial_vector)
294+
call s_cross_product(matmul(patch_ib(patch_id)%rotation_matrix, patch_ib(patch_id)%angular_vel), radial_vector, rotation_velocity)
270295

271296
! add only the component of the IB's motion that is normal to the surface
272297
vel_g = vel_g + sum((patch_ib(patch_id)%vel + rotation_velocity)*norm)*norm
@@ -280,7 +305,7 @@ contains
280305
radial_vector = physical_loc - [patch_ib(patch_id)%x_centroid, &
281306
patch_ib(patch_id)%y_centroid, patch_ib(patch_id)%z_centroid]
282307
! convert the angular velocity from the inertial reference frame to the fluids frame, then convert to linear velocity
283-
rotation_velocity = cross_product(matmul(patch_ib(patch_id)%rotation_matrix, patch_ib(patch_id)%angular_vel), radial_vector)
308+
call s_cross_product(matmul(patch_ib(patch_id)%rotation_matrix, patch_ib(patch_id)%angular_vel), radial_vector, rotation_velocity)
284309
do q = 1, 3
285310
! if mibm is 1 or 2, then the boundary may be moving
286311
vel_g(q) = patch_ib(patch_id)%vel(q) ! add the linear velocity
@@ -969,6 +994,83 @@ contains
969994
970995
end subroutine s_update_mib
971996
997+
! compute the surface integrals of the IB via a volume integraion method described in
998+
! "A coupled IBM/Euler-Lagrange framework for simulating shock-induced particle size segregation"
999+
! by Archana Sridhar and Jesse Capecelatro
1000+
subroutine s_compute_ib_forces(pressure)
1001+
1002+
! real(wp), dimension(idwbuff(1)%beg:idwbuff(1)%end, &
1003+
! idwbuff(2)%beg:idwbuff(2)%end, &
1004+
! idwbuff(3)%beg:idwbuff(3)%end), intent(in) :: pressure
1005+
type(scalar_field), intent(in) :: pressure
1006+
1007+
integer :: i, j, k, l, ib_idx
1008+
real(wp), dimension(num_ibs, 3) :: forces, torques
1009+
real(wp), dimension(1:3) :: pressure_divergence, radial_vector, local_torque_contribution
1010+
real(wp) :: cell_volume, dx, dy, dz
1011+
1012+
forces = 0._wp
1013+
torques = 0._wp
1014+
1015+
! TODO :: This is currently only valid inviscid, and needs to be extended to add viscocity
1016+
$:GPU_PARALLEL_LOOP(private='[ib_idx,radial_vector,pressure_divergence,cell_volume,local_torque_contribution, dx, dy, dz]', copy='[forces,torques]', copyin='[ib_markers,patch_ib]', collapse=3)
1017+
do i = 0, m
1018+
do j = 0, n
1019+
do k = 0, p
1020+
ib_idx = ib_markers%sf(i, j, k)
1021+
if (ib_idx /= 0) then ! only need to compute the gradient for cells inside a IB
1022+
if (patch_ib(ib_idx)%moving_ibm == 2) then ! make sure that this IB has 2-way coupling enabled
1023+
! get the vector pointing to the grid cell from the IB centroid
1024+
if (num_dims == 3) then
1025+
radial_vector = [x_cc(i), y_cc(j), z_cc(k)] - [patch_ib(ib_idx)%x_centroid, patch_ib(ib_idx)%y_centroid, patch_ib(ib_idx)%z_centroid]
1026+
else
1027+
radial_vector = [x_cc(i), y_cc(j), 0._wp] - [patch_ib(ib_idx)%x_centroid, patch_ib(ib_idx)%y_centroid, 0._wp]
1028+
end if
1029+
dx = x_cc(i + 1) - x_cc(i)
1030+
dy = y_cc(j + 1) - y_cc(j)
1031+
1032+
! use a finite difference to compute the 2D components of the gradient of the pressure and cell volume
1033+
pressure_divergence(1) = (pressure%sf(i + 1, j, k) - pressure%sf(i - 1, j, k))/(2._wp*dx)
1034+
pressure_divergence(2) = (pressure%sf(i, j + 1, k) - pressure%sf(i, j - 1, k))/(2._wp*dy)
1035+
cell_volume = dx*dy
1036+
1037+
! add the 3D component, if we are working in 3 dimensions
1038+
if (num_dims == 3) then
1039+
dz = z_cc(k + 1) - z_cc(k)
1040+
pressure_divergence(3) = (pressure%sf(i, j, k + 1) - pressure%sf(i, j, k - 1))/(2._wp*dz)
1041+
cell_volume = cell_volume*dz
1042+
else
1043+
pressure_divergence(3) = 0._wp
1044+
end if
1045+
1046+
! Update the force values atomically to prevent race conditions
1047+
call s_cross_product(radial_vector, pressure_divergence, local_torque_contribution) ! separate out to make atomics safe
1048+
local_torque_contribution = local_torque_contribution*cell_volume
1049+
do l = 1, 3
1050+
$:GPU_ATOMIC(atomic='update')
1051+
forces(ib_idx, l) = forces(ib_idx, l) - (pressure_divergence(l)*cell_volume)
1052+
$:GPU_ATOMIC(atomic='update')
1053+
torques(ib_idx, l) = torques(ib_idx, l) - local_torque_contribution(l)
1054+
end do
1055+
end if
1056+
end if
1057+
end do
1058+
end do
1059+
end do
1060+
$:END_GPU_PARALLEL_LOOP()
1061+
1062+
! reduce the forces across all MPI ranks
1063+
call s_mpi_allreduce_vectors_sum(forces, forces, num_ibs, 3)
1064+
call s_mpi_allreduce_vectors_sum(torques, torques, num_ibs, 3)
1065+
1066+
! apply the summed forces
1067+
do i = 1, num_ibs
1068+
patch_ib(i)%force(:) = forces(i, :)
1069+
patch_ib(i)%torque(:) = matmul(patch_ib(i)%rotation_matrix_inverse, torques(i, :)) ! torques must be computed in the local coordinates of the IB
1070+
end do
1071+
1072+
end subroutine s_compute_ib_forces
1073+
9721074
!> Subroutine to deallocate memory reserved for the IBM module
9731075
impure subroutine s_finalize_ibm_module()
9741076
@@ -978,14 +1080,85 @@ contains
9781080
9791081
end subroutine s_finalize_ibm_module
9801082
981-
function cross_product(a, b) result(c)
982-
implicit none
1083+
subroutine s_compute_moment_of_inertia(ib_marker, axis)
1084+
1085+
real(wp), dimension(3), optional :: axis !< the axis about which we compute the moment. Only required in 3D.
1086+
integer, intent(in) :: ib_marker
1087+
1088+
real(wp) :: moment, distance_to_axis, cell_volume
1089+
real(wp), dimension(3) :: position, closest_point_along_axis, vector_to_axis
1090+
integer :: i, j, k, count
1091+
1092+
if (p == 0) then
1093+
axis = [0, 1, 0]
1094+
else if (sqrt(sum(axis**2)) == 0) then
1095+
! if the object is not actually rotating at this time, return a dummy value and exit
1096+
patch_ib(ib_marker)%moment = 1._wp
1097+
return
1098+
else
1099+
axis = axis/sqrt(sum(axis))
1100+
end if
1101+
1102+
! if the IB is in 2D or a 3D sphere, we can compute this exactly
1103+
if (patch_ib(ib_marker)%geometry == 2) then ! circle
1104+
patch_ib(ib_marker)%moment = 0.5*patch_ib(ib_marker)%mass*(patch_ib(ib_marker)%radius)**2
1105+
elseif (patch_ib(ib_marker)%geometry == 3) then ! rectangle
1106+
patch_ib(ib_marker)%moment = patch_ib(ib_marker)%mass*(patch_ib(ib_marker)%length_x**2 + patch_ib(ib_marker)%length_y**2)/6._wp
1107+
elseif (patch_ib(ib_marker)%geometry == 8) then ! sphere
1108+
patch_ib(ib_marker)%moment = 0.4*patch_ib(ib_marker)%mass*(patch_ib(ib_marker)%radius)**2
1109+
1110+
else ! we do not have an analytic moment of inertia calculation and need to approximate it directly
1111+
count = 0
1112+
moment = 0._wp
1113+
cell_volume = (x_cc(1) - x_cc(0))*(y_cc(1) - y_cc(0)) ! computed without grid stretching. Update in the loop to perform with stretching
1114+
if (p /= 0) then
1115+
cell_volume = cell_volume*(z_cc(1) - z_cc(0))
1116+
end if
1117+
1118+
$:GPU_PARALLEL_LOOP(private='[position,closest_point_along_axis,vector_to_axis,distance_to_axis]', copy='[moment,count]', copyin='[ib_marker,cell_volume,axis]', collapse=3)
1119+
do i = 0, m
1120+
do j = 0, n
1121+
do k = 0, p
1122+
if (ib_markers%sf(i, j, k) == ib_marker) then
1123+
$:GPU_ATOMIC(atomic='update')
1124+
count = count + 1 ! increment the count of total cells in the boundary
1125+
1126+
! get the position in local coordinates so that the axis passes through 0, 0, 0
1127+
if (p == 0) then
1128+
position = [x_cc(i), y_cc(j), 0._wp] - [patch_ib(ib_marker)%x_centroid, patch_ib(ib_marker)%y_centroid, 0._wp]
1129+
else
1130+
position = [x_cc(i), y_cc(j), z_cc(k)] - [patch_ib(ib_marker)%x_centroid, patch_ib(ib_marker)%y_centroid, patch_ib(ib_marker)%z_centroid]
1131+
end if
1132+
1133+
! project the position along the axis to find the closest distance to the rotation axis
1134+
closest_point_along_axis = axis*dot_product(axis, position)
1135+
vector_to_axis = position - closest_point_along_axis
1136+
distance_to_axis = dot_product(vector_to_axis, vector_to_axis) ! saves the distance to the axis squared
1137+
1138+
! compute the position component of the moment
1139+
$:GPU_ATOMIC(atomic='update')
1140+
moment = moment + distance_to_axis
1141+
end if
1142+
end do
1143+
end do
1144+
end do
1145+
$:END_GPU_PARALLEL_LOOP()
1146+
1147+
! write the final moment assuming the points are all uniform density
1148+
patch_ib(ib_marker)%moment = moment*patch_ib(ib_marker)%mass/(count*cell_volume)
1149+
$:GPU_UPDATE(device='[patch_ib(ib_marker)%moment]')
1150+
end if
1151+
1152+
end subroutine s_compute_moment_of_inertia
1153+
1154+
subroutine s_cross_product(a, b, c)
1155+
$:GPU_ROUTINE(parallelism='[seq]')
9831156
real(wp), intent(in) :: a(3), b(3)
984-
real(wp) :: c(3)
1157+
real(wp), intent(out) :: c(3)
9851158
9861159
c(1) = a(2)*b(3) - a(3)*b(2)
9871160
c(2) = a(3)*b(1) - a(1)*b(3)
9881161
c(3) = a(1)*b(2) - a(2)*b(1)
989-
end function cross_product
1162+
end subroutine s_cross_product
9901163
9911164
end module m_ibm

0 commit comments

Comments
 (0)