@@ -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
9911164end module m_ibm
0 commit comments