Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
331 changes: 170 additions & 161 deletions docs/documentation/case.md

Large diffs are not rendered by default.

105 changes: 105 additions & 0 deletions examples/2D_mibm_cylinder_in_cross_flow/case.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import json
import math

Mu = 1.84e-05
gam_a = 1.4

# Configuring case dictionary
print(
json.dumps(
{
# Logistics
"run_time_info": "T",
# Computational Domain Parameters
# For these computations, the cylinder is placed at the (0,0,0)
# domain origin.
# axial direction
"x_domain%beg": 0.0e00,
"x_domain%end": 6.0e-03,
# r direction
"y_domain%beg": 0.0e00,
"y_domain%end": 6.0e-03,
"cyl_coord": "F",
"m": 200,
"n": 200,
"p": 0,
"dt": 6.0e-6,
"t_step_start": 0,
"t_step_stop": 10000, # 10000,
"t_step_save": 100,
# Simulation Algorithm Parameters
# Only one patches are necessary, the air tube
"num_patches": 1,
# Use the 5 equation model
"model_eqns": 2,
"alt_soundspeed": "F",
# One fluids: air
"num_fluids": 1,
# time step
"mpp_lim": "F",
# Correct errors when computing speed of sound
"mixture_err": "T",
# Use TVD RK3 for time marching
"time_stepper": 3,
# Use WENO5
"weno_order": 5,
"weno_eps": 1.0e-16,
"weno_Re_flux": "T",
"weno_avg": "T",
"avg_state": 2,
"mapped_weno": "T",
"null_weights": "F",
"mp_weno": "T",
"riemann_solver": 2,
"wave_speeds": 1,
# We use ghost-cell
"bc_x%beg": -3,
"bc_x%end": -3,
"bc_y%beg": -3,
"bc_y%end": -3,
# Set IB to True and add 1 patch
"ib": "T",
"num_ibs": 1,
"viscous": "T",
# Formatted Database Files Structure Parameters
"format": 1,
"precision": 2,
"prim_vars_wrt": "T",
"E_wrt": "T",
"parallel_io": "T",
# Patch: Constant Tube filled with air
# Specify the cylindrical air tube grid geometry
"patch_icpp(1)%geometry": 3,
"patch_icpp(1)%x_centroid": 3.0e-03,
# Uniform medium density, centroid is at the center of the domain
"patch_icpp(1)%y_centroid": 3.0e-03,
"patch_icpp(1)%length_x": 6.0e-03,
"patch_icpp(1)%length_y": 6.0e-03,
# Specify the patch primitive variables
"patch_icpp(1)%vel(1)": 0.05e00,
"patch_icpp(1)%vel(2)": 0.0e00,
"patch_icpp(1)%pres": 1.0e00,
"patch_icpp(1)%alpha_rho(1)": 1.0e00,
"patch_icpp(1)%alpha(1)": 1.0e00,
# Patch: Cylinder Immersed Boundary
"patch_ib(1)%geometry": 2,
"patch_ib(1)%x_centroid": 1.5e-03,
"patch_ib(1)%y_centroid": 4.5e-03,
"patch_ib(1)%radius": 0.3e-03,
"patch_ib(1)%slip": "F",
"patch_ib(1)%moving_ibm": 2,
"patch_ib(1)%vel(2)": -0.1,
"patch_ib(1)%angles(1)": 0.0, # x-axis rotation in radians
"patch_ib(1)%angles(2)": 0.0, # y-axis rotation
"patch_ib(1)%angles(3)": 0.0, # z-axis rotation
"patch_ib(1)%angular_vel(1)": 0.0, # x-axis rotational velocity in radians per second
"patch_ib(1)%angular_vel(2)": 0.0, # y-axis rotation
"patch_ib(1)%angular_vel(3)": 0.0, # z-axis rotation
"patch_ib(1)%mass": 0.0001, # z-axis rotation
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Incorrect comment: Comment says "z-axis rotation" but this line sets the mass parameter. This appears to be a copy-paste error.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/2D_mibm_cylinder_in_cross_flow/case.py, line 98:

<comment>Incorrect comment: Comment says &quot;z-axis rotation&quot; but this line sets the `mass` parameter. This appears to be a copy-paste error.</comment>

<file context>
@@ -0,0 +1,105 @@
+            &quot;patch_ib(1)%angular_vel(1)&quot;: 0.0,  # x-axis rotational velocity in radians per second
+            &quot;patch_ib(1)%angular_vel(2)&quot;: 0.0,  # y-axis rotation
+            &quot;patch_ib(1)%angular_vel(3)&quot;: 0.0,  # z-axis rotation
+            &quot;patch_ib(1)%mass&quot;: 0.0001,  # z-axis rotation
+            # Fluids Physical Parameters
+            &quot;fluid_pp(1)%gamma&quot;: 1.0e00 / (gam_a - 1.0e00),  # 2.50(Not 1.40)
</file context>
Suggested change
"patch_ib(1)%mass": 0.0001, # z-axis rotation
"patch_ib(1)%mass": 0.0001, # mass of the immersed boundary object
Fix with Cubic

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Incorrect comment: The comment # z-axis rotation is wrong for the mass parameter. This appears to be a copy-paste error from the angular velocity comments above.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/2D_mibm_cylinder_in_cross_flow/case.py, line 98:

<comment>Incorrect comment: The comment `# z-axis rotation` is wrong for the `mass` parameter. This appears to be a copy-paste error from the angular velocity comments above.</comment>

<file context>
@@ -0,0 +1,105 @@
+            &quot;patch_ib(1)%angular_vel(1)&quot;: 0.0,  # x-axis rotational velocity in radians per second
+            &quot;patch_ib(1)%angular_vel(2)&quot;: 0.0,  # y-axis rotation
+            &quot;patch_ib(1)%angular_vel(3)&quot;: 0.0,  # z-axis rotation
+            &quot;patch_ib(1)%mass&quot;: 0.0001,  # z-axis rotation
+            # Fluids Physical Parameters
+            &quot;fluid_pp(1)%gamma&quot;: 1.0e00 / (gam_a - 1.0e00),  # 2.50(Not 1.40)
</file context>
Suggested change
"patch_ib(1)%mass": 0.0001, # z-axis rotation
"patch_ib(1)%mass": 0.0001, # mass of the immersed boundary
Fix with Cubic

# Fluids Physical Parameters
"fluid_pp(1)%gamma": 1.0e00 / (gam_a - 1.0e00), # 2.50(Not 1.40)
"fluid_pp(1)%pi_inf": 0,
"fluid_pp(1)%Re(1)": 2500000,
}
)
)
2 changes: 2 additions & 0 deletions src/common/m_derived_types.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,8 @@ module m_derived_types

!! Patch conditions for moving imersed boundaries
integer :: moving_ibm ! 0 for no moving, 1 for moving, 2 for moving on forced path
real(wp) :: mass, moment ! mass and moment of inertia of object used to compute forces in 2-way coupling
real(wp), dimension(1:3) :: force, torque ! vectors for the computed force and torque values applied to an IB
real(wp), dimension(1:3) :: vel
real(wp), dimension(1:3) :: step_vel ! velocity array used to store intermediate steps in the time_stepper module
real(wp), dimension(1:3) :: angular_vel
Expand Down
24 changes: 24 additions & 0 deletions src/common/m_mpi_common.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,30 @@ contains

end subroutine s_mpi_allreduce_sum

!> This subroutine follows the behavior of the s_mpi_allreduce_sum subroutine
!> with the additional feature that it reduces an array of vectors.
impure subroutine s_mpi_allreduce_vectors_sum(var_loc, var_glb, num_vectors, vector_length)

integer, intent(in) :: num_vectors, vector_length
real(wp), dimension(:, :), intent(in) :: var_loc
real(wp), dimension(:, :), intent(out) :: var_glb

#ifdef MFC_MPI
integer :: ierr !< Generic flag used to identify and report MPI errors

! Performing the reduction procedure
if (loc(var_loc) == loc(var_glb)) then
call MPI_Allreduce(MPI_IN_PLACE, var_glb, num_vectors*vector_length, &
mpi_p, MPI_SUM, MPI_COMM_WORLD, ierr)
else
call MPI_Allreduce(var_loc, var_glb, num_vectors*vector_length, &
mpi_p, MPI_SUM, MPI_COMM_WORLD, ierr)
end if

#endif
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Missing non-MPI fallback: when MFC_MPI is not defined, var_glb is never assigned, leaving it uninitialized. Similar subroutine s_mpi_allreduce_integer_sum includes an #else block to copy var_loc to var_glb for serial execution.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/common/m_mpi_common.fpp, line 495:

<comment>Missing non-MPI fallback: when `MFC_MPI` is not defined, `var_glb` is never assigned, leaving it uninitialized. Similar subroutine `s_mpi_allreduce_integer_sum` includes an `#else` block to copy `var_loc` to `var_glb` for serial execution.</comment>

<file context>
@@ -472,6 +472,30 @@ contains
+                               mpi_p, MPI_SUM, MPI_COMM_WORLD, ierr)
+        end if
+
+#endif
+
+    end subroutine s_mpi_allreduce_vectors_sum
</file context>
Suggested change
#endif
#else
var_glb = var_loc
#endif
Fix with Cubic


end subroutine s_mpi_allreduce_vectors_sum

!> The following subroutine takes the input local variable
!! from all processors and reduces to the sum of all
!! values. The reduced variable is recorded back onto the
Expand Down
2 changes: 2 additions & 0 deletions src/pre_process/m_global_parameters.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,8 @@ contains
patch_ib(i)%vel(:) = 0._wp
patch_ib(i)%angles(:) = 0._wp
patch_ib(i)%angular_vel(:) = 0._wp
patch_ib(i)%mass = dflt_real
patch_ib(i)%moment = dflt_real

! sets values of a rotation matrix which can be used when calculating rotations
patch_ib(i)%rotation_matrix = 0._wp
Expand Down
159 changes: 158 additions & 1 deletion src/simulation/m_ibm.fpp
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High-level Suggestion

The force and torque calculations in s_compute_ib_forces are incorrect because the pressure gradient and cell volume are computed using cell coordinates instead of grid spacing and cell dimensions. This invalidates the fluid-structure interaction results. [High-level, importance: 9]

Solution Walkthrough:

Before:

subroutine s_compute_ib_forces(pressure)
    ...
    do i = 0, m
        do j = 0, n
            do k = 0, p
                ...
                ! Incorrect pressure gradient using cell-center coordinates in denominator
                pressure_divergence(1) = (pressure(i+1,j,k) - pressure(i-1,j,k)) / (2._wp * x_cc(i))
                pressure_divergence(2) = (pressure(i,j+1,k) - pressure(i,j-1,k)) / (2._wp * y_cc(j))
                
                ! Incorrect cell volume calculation using coordinates
                cell_volume = x_cc(i) * y_cc(j)
                if (num_dims == 3) then
                    pressure_divergence(3) = (pressure(i,j,k+1) - pressure(i,j,k-1)) / (2._wp * z_cc(k))
                    cell_volume = cell_volume * z_cc(k)
                end if

                forces(ib_idx, :) = forces(ib_idx, :) - (pressure_divergence * cell_volume)
                ...
            end do
        end do
    end do
end subroutine

After:

subroutine s_compute_ib_forces(pressure)
    ...
    ! Grid spacings (dx, dy, dz) should be available or computed
    ! e.g., dx = x_cc(1) - x_cc(0) for a uniform grid
    
    do i = 0, m
        do j = 0, n
            do k = 0, p
                ...
                ! Correct pressure gradient using grid spacing
                pressure_divergence(1) = (pressure(i+1,j,k) - pressure(i-1,j,k)) / (2._wp * dx)
                pressure_divergence(2) = (pressure(i,j+1,k) - pressure(i,j-1,k)) / (2._wp * dy)
                
                ! Correct cell volume calculation
                cell_volume = dx * dy
                if (num_dims == 3) then
                    pressure_divergence(3) = (pressure(i,j,k+1) - pressure(i,j,k-1)) / (2._wp * dz)
                    cell_volume = cell_volume * dz
                end if

                forces(ib_idx, :) = forces(ib_idx, :) - (pressure_divergence * cell_volume)
                ...
            end do
        end do
    end do
end subroutine

Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ contains
end if
call s_update_ib_rotation_matrix(i)
end do

$:GPU_ENTER_DATA(copyin='[patch_ib]')

! Allocating the patch identities bookkeeping variable
Expand Down Expand Up @@ -197,6 +196,21 @@ contains
type(ghost_point) :: gp
type(ghost_point) :: innerp

! set the Moving IBM interior Pressure Values
do patch_id = 1, num_ibs
if (patch_ib(patch_id)%moving_ibm == 2) then
do j = 0, m
do k = 0, n
do l = 0, p
if (ib_markers%sf(j, k, l) == patch_id) then
q_prim_vf(E_idx)%sf(j, k, l) = 1._wp
end if
end do
end do
end do
end if
end do

if (num_gps > 0) then
$: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]')
do i = 1, num_gps
Expand Down Expand Up @@ -244,6 +258,17 @@ contains
if (surface_tension) then
q_prim_vf(c_idx)%sf(j, k, l) = c_IP
end if

! set the pressure
do q = 1, num_fluids
if (patch_ib(patch_id)%moving_ibm == 0) then
q_prim_vf(E_idx)%sf(j, k, l) = pres_IP
else
! TODO :: improve for two fluid
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, :)))
end if
end do

if (model_eqns /= 4) then
! If in simulation, use acc mixture subroutines
if (elasticity) then
Expand Down Expand Up @@ -969,6 +994,67 @@ contains

end subroutine s_update_mib

! compute the surface integrals of the IB via a volume integraion method described in
! "A coupled IBM/Euler-Lagrange framework for simulating shock-induced particle size segregation"
! by Archana Sridhar and Jesse Capecelatro
subroutine s_compute_ib_forces(pressure)

real(wp), dimension(0:m, 0:n, 0:p), intent(in) :: pressure

integer :: i, j, k, ib_idx
real(wp), dimension(num_ibs, 3) :: forces, torques
real(wp), dimension(1:3) :: pressure_divergence, radial_vector
real(wp) :: cell_volume

forces = 0._wp
torques = 0._wp

! TODO :: This is currently only valid inviscid, and needs to be extended to add viscocity
$:GPU_PARALLEL_LOOP(private='[ib_idx,radial_vector,pressure_divergence,cell_volume]', copy='[forces,torques]', copyin='[ib_markers]', collapse=3)
do i = 0, m
do j = 0, n
do k = 0, p
ib_idx = ib_markers%sf(i, j, k)
if (ib_idx /= 0) then ! only need to compute the gradient for cells inside a IB
if (patch_ib(ib_idx)%moving_ibm == 2) then
if (num_dims == 3) then
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] ! get the vector pointing to the grid cell
else
radial_vector = [x_cc(i), y_cc(j), 0._wp] - [patch_ib(ib_idx)%x_centroid, patch_ib(ib_idx)%y_centroid, 0._wp] ! get the vector pointing to the grid cell
end if
pressure_divergence(1) = (pressure(i + 1, j, k) - pressure(i - 1, j, k))/(2._wp*x_cc(i))
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Pressure gradient calculation uses x_cc(i) (cell center coordinate) instead of grid spacing dx(i) in denominator. For central difference: (P[i+1]-P[i-1])/(2*dx). Similarly, cell_volume should use grid spacings dx(i)*dy(j), not coordinates.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/simulation/m_ibm.fpp, line 1025:

<comment>Pressure gradient calculation uses `x_cc(i)` (cell center coordinate) instead of grid spacing `dx(i)` in denominator. For central difference: `(P[i+1]-P[i-1])/(2*dx)`. Similarly, `cell_volume` should use grid spacings `dx(i)*dy(j)`, not coordinates.</comment>

<file context>
@@ -969,6 +994,67 @@ contains
+                            else
+                                radial_vector = [x_cc(i), y_cc(j), 0._wp] - [patch_ib(ib_idx)%x_centroid, patch_ib(ib_idx)%y_centroid, 0._wp] ! get the vector pointing to the grid cell
+                            end if
+                            pressure_divergence(1) = (pressure(i + 1, j, k) - pressure(i - 1, j, k))/(2._wp*x_cc(i))
+                            pressure_divergence(2) = (pressure(i, j + 1, k) - pressure(i, j - 1, k))/(2._wp*y_cc(j))
+                            cell_volume = x_cc(i)*y_cc(j)
</file context>
Fix with Cubic

pressure_divergence(2) = (pressure(i, j + 1, k) - pressure(i, j - 1, k))/(2._wp*y_cc(j))
cell_volume = x_cc(i)*y_cc(j)
if (num_dims == 3) then
pressure_divergence(3) = (pressure(i, j, k + 1) - pressure(i, j, k - 1))/(2._wp*z_cc(k))
cell_volume = cell_volume*z_cc(k)
Comment on lines +1025 to +1030
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical bug: Pressure gradient uses cell centers instead of cell widths.

The pressure divergence calculation divides by x_cc(i) and y_cc(j) (cell center coordinates), but should divide by dx(i) and dy(j) (cell widths). Similarly, cell_volume should use cell widths, not center coordinates.

-pressure_divergence(1) = (pressure(i + 1, j, k) - pressure(i - 1, j, k))/(2._wp*x_cc(i))
-pressure_divergence(2) = (pressure(i, j + 1, k) - pressure(i, j - 1, k))/(2._wp*y_cc(j))
-cell_volume = x_cc(i)*y_cc(j)
+pressure_divergence(1) = (pressure(i + 1, j, k) - pressure(i - 1, j, k))/(x_cc(i + 1) - x_cc(i - 1))
+pressure_divergence(2) = (pressure(i, j + 1, k) - pressure(i, j - 1, k))/(y_cc(j + 1) - y_cc(j - 1))
+cell_volume = dx(i)*dy(j)
 if (num_dims == 3) then
-    pressure_divergence(3) = (pressure(i, j, k + 1) - pressure(i, j, k - 1))/(2._wp*z_cc(k))
-    cell_volume = cell_volume*z_cc(k)
+    pressure_divergence(3) = (pressure(i, j, k + 1) - pressure(i, j, k - 1))/(z_cc(k + 1) - z_cc(k - 1))
+    cell_volume = cell_volume*dz(k)

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/simulation/m_ibm.fpp around lines 1025 to 1030, the pressure_divergence
and cell_volume calculations incorrectly use cell center coordinates (x_cc,
y_cc, z_cc); replace those with the cell widths (dx, dy, dz) in the denominators
so pressure_divergence(1) = (pressure(i+1,...) - pressure(i-1,...)) /
(2._wp*dx(i)) and similarly for index 2 and 3, and compute cell_volume =
dx(i)*dy(j) and if num_dims == 3 then cell_volume = dx(i)*dy(j)*dz(k); ensure
the variable names and precision suffix (_wp) match existing conventions.

else
pressure_divergence(3) = 0._wp
end if

forces(ib_idx, :) = forces(ib_idx, :) - (pressure_divergence*cell_volume)
torques(ib_idx, :) = torques(ib_idx, :) + (cross_product(radial_vector, pressure_divergence)*cell_volume)
end if
end if
end do
end do
end do
$:END_GPU_PARALLEL_LOOP()

! reduce the forces across all MPI ranks
call s_mpi_allreduce_vectors_sum(forces, forces, num_ibs, 3)
call s_mpi_allreduce_vectors_sum(torques, torques, num_ibs, 3)

! apply the summed forces
do i = 1, num_ibs
patch_ib(i)%force(:) = forces(i, :)
patch_ib(i)%torque(:) = matmul(patch_ib(i)%rotation_matrix_inverse, torques(i, :)) ! torques must be computed in the local coordinates of the IB
end do

print *, patch_ib(1)%force(1:2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove debug print statement.

This debug print statement will produce excessive output in production runs and should be removed or guarded by a debug flag.

-print *, patch_ib(1)%force(1:2)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
print *, patch_ib(1)%force(1:2)
🤖 Prompt for AI Agents
In src/simulation/m_ibm.fpp around line 1054, there is a debug print statement
"print *, patch_ib(1)%force(1:2)" that will produce excessive output in
production; remove this statement or wrap it with a runtime debug flag check.
Replace the unconditional print with either deletion or a conditional such as
checking a module-level logical debug variable (e.g., if (debug) print ...) or a
verbosity level before printing, ensuring the debug flag is initialized and
documented.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Debug print statement should be removed from production code.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/simulation/m_ibm.fpp, line 1054:

<comment>Debug print statement should be removed from production code.</comment>

<file context>
@@ -969,6 +994,67 @@ contains
+            patch_ib(i)%torque(:) = matmul(patch_ib(i)%rotation_matrix_inverse, torques(i, :)) ! torques must be computed in the local coordinates of the IB
+        end do
+
+        print *, patch_ib(1)%force(1:2)
+
+    end subroutine s_compute_ib_forces
</file context>
Fix with Cubic

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Debug print statement should be removed from production code.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/simulation/m_ibm.fpp, line 1054:

<comment>Debug print statement should be removed from production code.</comment>

<file context>
@@ -969,6 +994,67 @@ contains
+            patch_ib(i)%torque(:) = matmul(patch_ib(i)%rotation_matrix_inverse, torques(i, :)) ! torques must be computed in the local coordinates of the IB
+        end do
+
+        print *, patch_ib(1)%force(1:2)
+
+    end subroutine s_compute_ib_forces
</file context>
Fix with Cubic


end subroutine s_compute_ib_forces

!> Subroutine to deallocate memory reserved for the IBM module
impure subroutine s_finalize_ibm_module()

Expand All @@ -978,6 +1064,77 @@ contains

end subroutine s_finalize_ibm_module

subroutine s_compute_moment_of_inertia(ib_marker, axis)

real(wp), dimension(3), optional :: axis !< the axis about which we compute the moment. Only required in 3D.
integer, intent(in) :: ib_marker

real(wp) :: moment, distance_to_axis, cell_volume
real(wp), dimension(3) :: position, closest_point_along_axis, vector_to_axis
integer :: i, j, k, count

if (p == 0) then
axis = [0, 1, 0]
else if (sqrt(sum(axis**2)) == 0) then
! if the object is not actually rotating at this time, return a dummy value and exit
patch_ib(ib_marker)%moment = 1._wp
return
else
axis = axis/sqrt(sum(axis))
end if
Comment on lines +1076 to +1084
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Optional argument handling issue in 2D case.

When p == 0 (2D), the code assigns to axis without checking if it was provided. Since axis is declared optional, assigning to it when not present is undefined behavior. Additionally, the axis parameter should have intent(inout) if it's being modified.

-subroutine s_compute_moment_of_inertia(ib_marker, axis)
+subroutine s_compute_moment_of_inertia(ib_marker, axis_in)
 
-    real(wp), dimension(3), optional :: axis !< the axis about which we compute the moment. Only required in 3D.
+    real(wp), dimension(3), intent(in), optional :: axis_in !< the axis about which we compute the moment. Only required in 3D.
     integer, intent(in) :: ib_marker
 
     real(wp) :: moment, distance_to_axis, cell_volume
-    real(wp), dimension(3) :: position, closest_point_along_axis, vector_to_axis
+    real(wp), dimension(3) :: position, closest_point_along_axis, vector_to_axis, axis
     integer :: i, j, k, count
 
     if (p == 0) then
-        axis = [0, 1, 0]
-    else if (sqrt(sum(axis**2)) == 0) then
+        axis = [0._wp, 0._wp, 1._wp]
+    else if (.not. present(axis_in) .or. sqrt(sum(axis_in**2)) < 1.e-16_wp) then
         ! if the object is not actually rotating at this time, return a dummy value and exit
         patch_ib(ib_marker)%moment = 1._wp
         return
     else
-        axis = axis/sqrt(sum(axis))
+        axis = axis_in/sqrt(sum(axis_in**2))
     end if

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/simulation/m_ibm.fpp around lines 1076 to 1084, the branch for p == 0
assigns to the optional argument axis (undefined if not present) and axis should
be declared intent(inout) if modified; fix by (a) guarding any write/read of
axis with present(axis) (e.g., only assign axis = [0,1,0] when present(axis) is
true), (b) avoid touching axis when not present (use a local dummy variable
instead), and (c) update the routine signature to declare axis as intent(inout)
if the caller is expected to receive the modified axis. Ensure other uses in
this block also check present(axis) before reading or normalizing it.


! if the IB is in 2D or a 3D sphere, we can compute this exactly
if (patch_ib(ib_marker)%geometry == 2) then ! circle
patch_ib(ib_marker)%moment = 0.5*patch_ib(ib_marker)%mass*(patch_ib(ib_marker)%radius)**2
elseif (patch_ib(ib_marker)%geometry == 3) then ! rectangle
patch_ib(ib_marker)%moment = patch_ib(i)%mass*(patch_ib(ib_marker)%length_x**2 + patch_ib(ib_marker)%length_y**2)/6._wp
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Variable name typo: Uses i instead of ib_marker.

Line 1090 references patch_ib(i) but i is a loop iterator declared later. This should be patch_ib(ib_marker).

 elseif (patch_ib(ib_marker)%geometry == 3) then ! rectangle
-    patch_ib(ib_marker)%moment = patch_ib(i)%mass*(patch_ib(ib_marker)%length_x**2 + patch_ib(ib_marker)%length_y**2)/6._wp
+    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
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
patch_ib(ib_marker)%moment = patch_ib(i)%mass*(patch_ib(ib_marker)%length_x**2 + patch_ib(ib_marker)%length_y**2)/6._wp
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
🤖 Prompt for AI Agents
In src/simulation/m_ibm.fpp around line 1090, the assignment uses
patch_ib(i)%mass but the loop index there should be ib_marker; change the
reference from patch_ib(i) to patch_ib(ib_marker) so the mass and lengths come
from the same element (patch_ib(ib_marker)). Ensure the variable ib_marker is in
scope at this line and remove any accidental dependence on i.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0: Wrong index variable: patch_ib(i)%mass should be patch_ib(ib_marker)%mass. Variable i is undefined at this point in the code path - it's only used as a loop variable in the numerical approximation section below.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/simulation/m_ibm.fpp, line 1090:

<comment>Wrong index variable: `patch_ib(i)%mass` should be `patch_ib(ib_marker)%mass`. Variable `i` is undefined at this point in the code path - it&#39;s only used as a loop variable in the numerical approximation section below.</comment>

<file context>
@@ -978,6 +1064,77 @@ contains
+        if (patch_ib(ib_marker)%geometry == 2) then ! circle
+            patch_ib(ib_marker)%moment = 0.5*patch_ib(ib_marker)%mass*(patch_ib(ib_marker)%radius)**2
+        elseif (patch_ib(ib_marker)%geometry == 3) then ! rectangle
+            patch_ib(ib_marker)%moment = patch_ib(i)%mass*(patch_ib(ib_marker)%length_x**2 + patch_ib(ib_marker)%length_y**2)/6._wp
+        elseif (patch_ib(ib_marker)%geometry == 8) then ! sphere
+            patch_ib(ib_marker)%moment = 0.4*patch_ib(ib_marker)%mass*(patch_ib(ib_marker)%radius)**2
</file context>
Suggested change
patch_ib(ib_marker)%moment = patch_ib(i)%mass*(patch_ib(ib_marker)%length_x**2 + patch_ib(ib_marker)%length_y**2)/6._wp
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
Fix with Cubic

elseif (patch_ib(ib_marker)%geometry == 8) then ! sphere
patch_ib(ib_marker)%moment = 0.4*patch_ib(ib_marker)%mass*(patch_ib(ib_marker)%radius)**2

else ! we do not have an analytic moment of inertia calculation and need to approximate it directly
count = 0
moment = 0._wp
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
if (p /= 0) then
cell_volume = cell_volume*(z_cc(1) - z_cc(0))
end if

$: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)
do i = 0, m
do j = 0, j
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Loop bound typo in the moment-of-inertia grid scan: the inner loop is written do j = 0, j which uses the loop variable j as its own bound and is almost certainly incorrect (will either be a no-op or undefined behavior); it should use the grid size (e.g., n) instead. [logic error]

Severity Level: Minor ⚠️

Suggested change
do j = 0, j
do j = 0, n
Why it matters? ⭐

The inner loop bound "do j = 0, j" is clearly a typo and will not iterate over the intended grid (uses the loop variable as its own bound). Changing it to "0, n" (or appropriate grid size) fixes a logic bug that would otherwise make the loop a no-op or unpredictable.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/simulation/m_ibm.fpp
**Line:** 1105:1105
**Comment:**
	*Logic Error: Loop bound typo in the moment-of-inertia grid scan: the inner loop is written `do j = 0, j` which uses the loop variable `j` as its own bound and is almost certainly incorrect (will either be a no-op or undefined behavior); it should use the grid size (e.g., `n`) instead.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0: Loop variable j is used as its own upper bound. This should be do j = 0, n to iterate over the y-dimension grid.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/simulation/m_ibm.fpp, line 1105:

<comment>Loop variable `j` is used as its own upper bound. This should be `do j = 0, n` to iterate over the y-dimension grid.</comment>

<file context>
@@ -978,6 +1064,78 @@ contains
+            
+            $:GPU_PARALLEL_LOOP(private=&#39;[position,closest_point_along_axis,vector_to_axis,distance_to_axis]&#39;, copy=&#39;[moment,count]&#39;, copyin=&#39;[ib_marker,cell_volume,axis]&#39;, collapse=3)
+            do i = 0, m
+                do j = 0, j
+                    do k = 0, p
+                        if (ib_markers%sf(i, j, k) == ib_marker) then
</file context>
Suggested change
do j = 0, j
do j = 0, n
Fix with Cubic

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0: Loop bound error: do j = 0, j uses j as both loop variable and upper bound. This will cause incorrect iteration since j is uninitialized. Should be do j = 0, n to iterate over the y-dimension.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/simulation/m_ibm.fpp, line 1104:

<comment>Loop bound error: `do j = 0, j` uses `j` as both loop variable and upper bound. This will cause incorrect iteration since `j` is uninitialized. Should be `do j = 0, n` to iterate over the y-dimension.</comment>

<file context>
@@ -978,6 +1064,77 @@ contains
+
+            $:GPU_PARALLEL_LOOP(private=&#39;[position,closest_point_along_axis,vector_to_axis,distance_to_axis]&#39;, copy=&#39;[moment,count]&#39;, copyin=&#39;[ib_marker,cell_volume,axis]&#39;, collapse=3)
+            do i = 0, m
+                do j = 0, j
+                    do k = 0, p
+                        if (ib_markers%sf(i, j, k) == ib_marker) then
</file context>
Suggested change
do j = 0, j
do j = 0, n
Fix with Cubic

do k = 0, p
Comment on lines +1102 to +1105
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Loop uses j as both iterator and upper bound - causes infinite/incorrect loop.

On line 1104, the loop do j = 0, j uses j as both the iterator variable and the upper bound. This should be do j = 0, n.

 $: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)
 do i = 0, m
-    do j = 0, j
+    do j = 0, n
         do k = 0, p
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$: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)
do i = 0, m
do j = 0, j
do k = 0, p
$: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)
do i = 0, m
do j = 0, n
do k = 0, p
🤖 Prompt for AI Agents
In src/simulation/m_ibm.fpp around lines 1102 to 1105, the middle loop is
written as "do j = 0, j" which uses the loop variable as its own upper bound and
will produce incorrect/infinite iteration; change that line to "do j = 0, n"
(i.e., use the intended upper-bound variable n), and confirm that n is correctly
declared/initialized in this scope and matches the intended dimension for the
loop to ensure the GPU_PARALLEL_LOOP pragma and private/copy lists remain valid.

if (ib_markers%sf(i, j, k) == ib_marker) then
$:GPU_ATOMIC(atomic='update')
count = count + 1 ! increment the count of total cells in the boundary

! get the position in local coordinates so that the axis passes through 0, 0, 0
if (p == 0) then
position = [x_cc(i), y_cc(j), 0._wp] - [patch_ib(ib_marker)%x_centroid, patch_ib(ib_marker)%y_centroid, 0._wp]
else
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]
end if

! project the position along the axis to find the closest distance to the rotation axis
closest_point_along_axis = axis*dot_product(axis, position)
vector_to_axis = position - closest_point_along_axis
distance_to_axis = sum(vector_to_axis) ! saves the distance to the axis squared
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Incorrect distance calculation - missing square root and squaring.

The distance_to_axis should be the squared distance (for moment of inertia) or the actual distance. Currently it uses sum(vector_to_axis) which sums the components, not their squares. The moment of inertia formula requires .

-distance_to_axis = sum(vector_to_axis) ! saves the distance to the axis squared
+distance_to_axis = sum(vector_to_axis**2) ! distance squared to the rotation axis
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
distance_to_axis = sum(vector_to_axis) ! saves the distance to the axis squared
distance_to_axis = sum(vector_to_axis**2) ! distance squared to the rotation axis
🤖 Prompt for AI Agents
In src/simulation/m_ibm.fpp around line 1120, the assignment uses
sum(vector_to_axis) which incorrectly sums components instead of computing r or
r^2; replace this with the squared distance computed as the sum of each
component squared (e.g., sum of vector_to_axis[i]*vector_to_axis[i]) so
distance_to_axis holds r^2 for moment-of-inertia calculations; if you actually
need the linear distance elsewhere, compute sqrt(r^2) and use a different
variable name to avoid confusion.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Incorrect distance calculation: sum(vector_to_axis) sums components, not computing squared distance. Should be sum(vector_to_axis**2) per the comment.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/simulation/m_ibm.fpp, line 1121:

<comment>Incorrect distance calculation: `sum(vector_to_axis)` sums components, not computing squared distance. Should be `sum(vector_to_axis**2)` per the comment.</comment>

<file context>
@@ -978,6 +1064,78 @@ contains
+                            ! project the position along the axis to find the closest distance to the rotation axis
+                            closest_point_along_axis = axis*dot_product(axis, position)
+                            vector_to_axis = position - closest_point_along_axis
+                            distance_to_axis = sum(vector_to_axis) ! saves the distance to the axis squared
+
+                            ! compute the position component of the moment
</file context>
Suggested change
distance_to_axis = sum(vector_to_axis) ! saves the distance to the axis squared
distance_to_axis = sum(vector_to_axis**2) ! saves the distance to the axis squared
Fix with Cubic

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Incorrect distance calculation: sum(vector_to_axis) computes the sum of components, not the squared distance. For moment of inertia, this should be sum(vector_to_axis**2) to get the squared perpendicular distance from the rotation axis.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/simulation/m_ibm.fpp, line 1120:

<comment>Incorrect distance calculation: `sum(vector_to_axis)` computes the sum of components, not the squared distance. For moment of inertia, this should be `sum(vector_to_axis**2)` to get the squared perpendicular distance from the rotation axis.</comment>

<file context>
@@ -978,6 +1064,77 @@ contains
+                            ! project the position along the axis to find the closest distance to the rotation axis
+                            closest_point_along_axis = axis*dot_product(axis, position)
+                            vector_to_axis = position - closest_point_along_axis
+                            distance_to_axis = sum(vector_to_axis) ! saves the distance to the axis squared
+
+                            ! compute the position component of the moment
</file context>
Suggested change
distance_to_axis = sum(vector_to_axis) ! saves the distance to the axis squared
distance_to_axis = sum(vector_to_axis**2) ! saves the distance to the axis squared
Fix with Cubic


! compute the position component of the moment
$:GPU_ATOMIC(atomic='update')
moment = moment + distance_to_axis
end if
end do
end do
end do
$:END_GPU_PARALLEL_LOOP()

! write the final moment assuming the points are all uniform density
patch_ib(ib_marker)%moment = moment*patch_ib(ib_marker)%mass/(count*cell_volume)
$:GPU_UPDATE(device='[patch_ib(ib_marker)%moment]')
end if

end subroutine s_compute_moment_of_inertia

function cross_product(a, b) result(c)
implicit none
real(wp), intent(in) :: a(3), b(3)
Expand Down
2 changes: 1 addition & 1 deletion src/simulation/m_mpi_proxy.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ contains

do i = 1, num_ibs
#:for VAR in [ 'radius', 'length_x', 'length_y', 'length_z', &
& 'x_centroid', 'y_centroid', 'z_centroid', 'c', 'm', 'p', 't', 'theta', 'slip']
& 'x_centroid', 'y_centroid', 'z_centroid', 'c', 'm', 'p', 't', 'theta', 'slip', 'mass']
call MPI_BCAST(patch_ib(i)%${VAR}$, 1, mpi_p, 0, MPI_COMM_WORLD, ierr)
#:endfor
#:for VAR in ['vel', 'angular_vel', 'angles']
Expand Down
24 changes: 17 additions & 7 deletions src/simulation/m_time_steppers.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,8 @@ contains
if (ib) then
! check if any IBMS are moving, and if so, update the markers, ghost points, levelsets, and levelset norms
if (moving_immersed_boundary_flag) then
call s_compute_ib_forces(q_prim_vf(E_idx)%sf(0:m, 0:n, 0:p)) ! compute the force and torque on the IB from the fluid
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Unsafe use of q_prim_vf when computing IB forces: q_prim_vf is only allocated in initialization when .not. igr. Calling s_compute_ib_forces(q_prim_vf(E_idx)%sf(...)) without guarding for igr can dereference an unallocated array and crash when igr is true. Wrap the call in a guard so it's only executed when q_prim_vf is available. [possible bug]

Severity Level: Critical 🚨

Suggested change
call s_compute_ib_forces(q_prim_vf(E_idx)%sf(0:m, 0:n, 0:p)) ! compute the force and torque on the IB from the fluid
if (.not. igr) then
call s_compute_ib_forces(q_prim_vf(E_idx)%sf(0:m, 0:n, 0:p)) ! compute the force and torque on the IB from the fluid
end if
Why it matters? ⭐

This is a valid runtime-safety concern: q_prim_vf is only allocated in initialization when (.not. igr).
The PR added an unconditional call that will dereference q_prim_vf when igr is true, which can crash.
Guarding the call is the right minimal fix — though note the IB block later still passes q_prim_vf into s_ibm_correct_state
unconditionally, so fixing just this line avoids one crash but doesn't fully resolve the broader inconsistency.
The suggestion is correct and actionable.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/simulation/m_time_steppers.fpp
**Line:** 614:614
**Comment:**
	*Possible Bug: Unsafe use of `q_prim_vf` when computing IB forces: `q_prim_vf` is only allocated in initialization when `.not. igr`. Calling `s_compute_ib_forces(q_prim_vf(E_idx)%sf(...))` without guarding for `igr` can dereference an unallocated array and crash when `igr` is true. Wrap the call in a guard so it's only executed when `q_prim_vf` is available.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.


do i = 1, num_ibs
if (s == 1) then
patch_ib(i)%step_vel = patch_ib(i)%vel
Expand All @@ -621,14 +623,22 @@ contains
patch_ib(i)%step_z_centroid = patch_ib(i)%z_centroid
end if

if (patch_ib(i)%moving_ibm == 1) then
do j = 1, 3
patch_ib(i)%vel(j) = (rk_coef(s, 1)*patch_ib(i)%step_vel(j) + rk_coef(s, 2)*patch_ib(i)%vel(j) + rk_coef(s, 3)*0._wp*dt)/rk_coef(s, 4) ! 0.0 is a placeholder for accelerations
patch_ib(i)%angular_vel(j) = (rk_coef(s, 1)*patch_ib(i)%step_angular_vel(j) + rk_coef(s, 2)*patch_ib(i)%angular_vel(j) + rk_coef(s, 3)*0._wp*dt)/rk_coef(s, 4)
if (patch_ib(i)%moving_ibm > 0) then
patch_ib(i)%vel = (rk_coef(s, 1)*patch_ib(i)%step_vel + rk_coef(s, 2)*patch_ib(i)%vel)/rk_coef(s, 4)
patch_ib(i)%angular_vel = (rk_coef(s, 1)*patch_ib(i)%step_angular_vel + rk_coef(s, 2)*patch_ib(i)%angular_vel)/rk_coef(s, 4)

! Update the angle of the IB
patch_ib(i)%angles(j) = (rk_coef(s, 1)*patch_ib(i)%step_angles(j) + rk_coef(s, 2)*patch_ib(i)%angles(j) + rk_coef(s, 3)*patch_ib(i)%angular_vel(j)*dt)/rk_coef(s, 4)
end do
if (patch_ib(i)%moving_ibm == 2) then ! if we are using two-way coupling, apply force and torque
! update the velocity from the force value
patch_ib(i)%vel = patch_ib(i)%vel + rk_coef(s, 3)*dt*(patch_ib(i)%force/patch_ib(i)%mass)/rk_coef(s, 4)

! update the angular velocity with the torque value
patch_ib(i)%angular_vel = (patch_ib(i)%angular_vel*patch_ib(i)%moment) + (rk_coef(s, 3)*dt*patch_ib(i)%torque/rk_coef(s, 4)) ! add the torque to the angular momentum
call s_compute_moment_of_inertia(i, patch_ib(i)%angular_vel) ! update the moment of inertia to be based on the direction of the angular momentum
patch_ib(i)%angular_vel = patch_ib(i)%angular_vel/patch_ib(i)%moment ! convert back to angular velocity with the new moment of inertia
end if

! Update the angle of the IB
patch_ib(i)%angles = (rk_coef(s, 1)*patch_ib(i)%step_angles + rk_coef(s, 2)*patch_ib(i)%angles + rk_coef(s, 3)*patch_ib(i)%angular_vel*dt)/rk_coef(s, 4)

! Update the position of the IB
patch_ib(i)%x_centroid = (rk_coef(s, 1)*patch_ib(i)%step_x_centroid + rk_coef(s, 2)*patch_ib(i)%x_centroid + rk_coef(s, 3)*patch_ib(i)%vel(1)*dt)/rk_coef(s, 4)
Expand Down
Loading
Loading