Conversation
…o levelset-refactor
…o levelset-refactor
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
src/common/m_model.fpp (4)
1125-1136:⚠️ Potential issue | 🟠 MajorAssert
boundary_edge_countmatchesboundary_vsize to prevent OOB.With assumed-shape
boundary_v, a mismatch withboundary_edge_countcan overrundist_bufferand the loop bounds. Add a size assertion at entry.🔧 Proposed fix (size guard)
function f_distance(boundary_v, boundary_edge_count, point) result(distance) integer, intent(in) :: boundary_edge_count real(wp), intent(in), dimension(:, :, :) :: boundary_v real(wp), dimension(1:3), intent(in) :: point + + @:ASSERT(boundary_edge_count == size(boundary_v, 1), & + & "boundary_edge_count must match size(boundary_v,1)")As per coding guidelines, use the fypp ASSERT macro for validating conditions.
1159-1169:⚠️ Potential issue | 🟠 MajorAdd a size guard for
boundary_vinf_normals.
boundary_edge_countis now decoupled from the assumed-shape array; a mismatch risks out-of-bounds access.🔧 Proposed fix (size guard)
subroutine f_normals(boundary_v, boundary_edge_count, point, normals) integer, intent(in) :: boundary_edge_count real(wp), intent(in), dimension(:, :, :) :: boundary_v real(wp), dimension(1:3), intent(in) :: point real(wp), dimension(1:3), intent(out) :: normals + + @:ASSERT(boundary_edge_count == size(boundary_v, 1), & + & "boundary_edge_count must match size(boundary_v,1)")As per coding guidelines, use the fypp ASSERT macro for validating conditions.
1221-1230:⚠️ Potential issue | 🟠 MajorGuard
total_verticesvsinterpolated_boundary_vsize.After switching to assumed-shape, a mismatch can overrun array bounds in the loop.
🔧 Proposed fix (size guard)
function f_interpolated_distance(interpolated_boundary_v, total_vertices, point) result(distance) integer, intent(in) :: total_vertices real(wp), intent(in), dimension(:, :) :: interpolated_boundary_v real(wp), dimension(1:3), intent(in) :: point + + @:ASSERT(total_vertices == size(interpolated_boundary_v, 1), & + & "total_vertices must match size(interpolated_boundary_v,1)")As per coding guidelines, use the fypp ASSERT macro for validating conditions.
488-520:⚠️ Potential issue | 🟠 MajorResolve GPU incompatibility in
f_model_is_insideto re-enable GPU acceleration.The
random_number()call prevents GPU compilation, and GPU parallelization is already disabled inm_ib_patches.fpp(line 926–927, see TODO). Replace the RNG with a deterministic GPU-safe sampler such as the Fibonacci sphere (as documented in the function's TODO) and re-enable the GPU routine annotation andGPU_PARALLEL_LOOPmacro. The preprocessing call site inm_icpp_patches.fppis CPU-only and unaffected.
🤖 Fix all issues with AI agents
In `@src/simulation/m_compute_levelset.fpp`:
- Line 681: The code uses an undeclared variable inverse_rotation in the matmul
call inside the subroutine (line showing "xyz_local = matmul(inverse_rotation,
xyz_local"); fix by using the correct rotation-inverse from the patch structure
(replace inverse_rotation with patch_ib(patch_id)%rotation_matrix_inverse) or
alternatively declare and assign inverse_rotation from
patch_ib(patch_id)%rotation_matrix_inverse before the matmul; ensure the symbol
patch_ib(patch_id)%rotation_matrix_inverse is used consistently in this
subroutine (or that inverse_rotation is properly declared and set from it).
- Around line 709-722: The code uses an undeclared variable point causing a
compile error; replace uses of point with the local coordinate variable
xyz_local where the levelset and normals are computed: update the calls to
f_interpolated_distance(models(patch_id)%interpolated_boundary_v,
total_vertices, point), f_distance(models(patch_id)%boundary_v,
boundary_edge_count, point), and f_normals(models(patch_id)%boundary_v,
boundary_edge_count, point, normals) to pass xyz_local instead of point and
ensure gp%levelset assignment that follows uses the corrected input.
In `@src/simulation/m_ib_patches.fpp`:
- Around line 901-931: s_ib_model currently uses inverse_rotation in the matmul
but never declares or initializes it; declare a local variable real(wp),
dimension(3,3) :: inverse_rotation in s_ib_model and initialize it the same way
as s_ib_airfoil / s_ib_3D_airfoil do (e.g., set inverse_rotation =
model%inverse_rotation or call the same routine that builds the inverse rotation
from model), ensuring model is the t_model pointer used earlier; this mirrors
the pattern in s_ib_airfoil and s_ib_3D_airfoil so matmul(inverse_rotation,
xyz_local) is valid.
🧹 Nitpick comments (2)
src/simulation/m_compute_levelset.fpp (2)
218-218: Remove unused variablelength_z.The variable
length_zis declared but never used. Line 230 useslzinstead.🧹 Suggested fix
- real(wp) :: length_z
392-393: Remove unused variableskandidx.These variables are declared but never used in
s_ellipse_levelset.🧹 Suggested fix
- integer :: i, j, k !< Loop index variables - integer :: idx !< Shortest path direction indicator + integer :: i, j !< Loop index variables
There was a problem hiding this comment.
2 issues found across 3 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="src/simulation/m_compute_levelset.fpp">
<violation number="1" location="src/simulation/m_compute_levelset.fpp:681">
P0: Undeclared variable `inverse_rotation` will cause a compilation error. This variable is used in the `matmul` call but is never declared or initialized in this subroutine. Replace with `patch_ib(patch_id)%rotation_matrix_inverse`.</violation>
</file>
<file name="src/simulation/m_ib_patches.fpp">
<violation number="1" location="src/simulation/m_ib_patches.fpp:931">
P0: Undeclared variable `inverse_rotation` will cause a compilation error. This variable is used in the `matmul` call but is never declared or initialized in `s_ib_model`. Compare to `s_ib_airfoil` and `s_ib_3D_airfoil` which correctly declare and initialize this variable from `patch_ib(patch_id)%rotation_matrix_inverse`.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/simulation/m_compute_levelset.fpp`:
- Around line 419-424: The levelset assignment using quadratic_coeffs and
gp%levelset must guard the discriminant before calling sqrt: compute the
discriminant (disc = quadratic_coeffs(2)**2._wp -
4._wp*quadratic_coeffs(1)*quadratic_coeffs(3)), check disc >= 0._wp and only
then set gp%levelset with the existing formula using sqrt(disc); otherwise call
s_mpi_abort with a clear message (e.g., "Negative discriminant in ellipse
levelset: point may be outside surface") so you fail fast instead of producing
NaN.
🧹 Nitpick comments (1)
src/simulation/m_ib_patches.fpp (1)
482-537: Use@:ALLOCATEmacro for device-aware allocation.Lines 483-484 use standard
allocateinstead of the@:ALLOCATEfypp macro. Compare withs_ib_airfoil(lines 328-329) which correctly uses@:ALLOCATE(airfoil_grid_u(1:Np)).This inconsistency could cause GPU memory management issues since these arrays are used in GPU loops.
♻️ Proposed fix
if (.not. allocated(airfoil_grid_u)) then - allocate (airfoil_grid_u(1:Np)) - allocate (airfoil_grid_l(1:Np)) + @:ALLOCATE(airfoil_grid_u(1:Np)) + @:ALLOCATE(airfoil_grid_l(1:Np))Also add
$:GPU_UPDATE(device='[airfoil_grid_l,airfoil_grid_u]')after the initialization loop (before line 538), similar to what's done ins_ib_airfoilat line 382.As per coding guidelines: "Use fypp macro
@:ALLOCATE(var1, var2)for device-aware allocation instead of standard Fortranallocate".
| 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 | ||
| gp%levelset = -0.5_wp*(-quadratic_coeffs(2) + sqrt(quadratic_coeffs(2)**2._wp - 4._wp*quadratic_coeffs(1)*quadratic_coeffs(3)))/quadratic_coeffs(1) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, find and read the m_compute_levelset.fpp file to see the full context
fd -e fpp "m_compute_levelset" src/simulation/Repository: MFlowCode/MFC
Length of output: 94
🏁 Script executed:
# Get line count and read the ellipse subroutine section
wc -l src/simulation/m_compute_levelset.fppRepository: MFlowCode/MFC
Length of output: 98
🏁 Script executed:
# Read the ellipse levelset subroutine (lines 378-426+)
sed -n '370,450p' src/simulation/m_compute_levelset.fppRepository: MFlowCode/MFC
Length of output: 3609
🏁 Script executed:
# Search for ghost point setup, initialization, or validation related to ellipse
rg -n "ghost\|ellipse" src/simulation/m_compute_levelset.fpp -A 3 -B 1Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Look for any assertions or validation on point coordinates before ellipse computation
rg -n "ASSERT\|assert\|valid" src/simulation/m_compute_levelset.fppRepository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Search across the simulation directory for how ghost points are initialized
rg -n "ghost.*inside\|inside.*ellipse\|allocate.*ghost" src/simulation/ -iRepository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Search for where ghost points are allocated or created
rg -n "allocate.*ghost\|ghost_point" src/simulation/ --type f90 --type fpp | head -30Repository: MFlowCode/MFC
Length of output: 84
🏁 Script executed:
# Look for the ghost_point type definition
rg -n "type.*ghost_point\|ghost_point.*type" src/ --type f90 --type fpp -A 10 | head -40Repository: MFlowCode/MFC
Length of output: 84
🏁 Script executed:
# Search for any validation or guards related to points inside/outside surfaces
rg -n "inside\|outside\|boundary" src/simulation/m_compute_levelset.fpp -iRepository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Check the algorithm/comments around ghost point usage in the levelset file
sed -n '1,100p' src/simulation/m_compute_levelset.fppRepository: MFlowCode/MFC
Length of output: 3219
🏁 Script executed:
# Search for where ghost_point type is defined and where gps array is allocated
rg -n "type.*ghost_point" src/simulation/ -A 8Repository: MFlowCode/MFC
Length of output: 12335
🏁 Script executed:
# Search for where ghost points are populated/initialized before being passed to s_apply_levelset
rg -n "s_apply_levelset\|gps\(" src/simulation/*.fpp src/simulation/*.f90 2>/dev/null | head -30Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Look for any marking/flagging of ghost points or validation of their location
rg -n "flag_gp\|valid.*gp\|check.*point" src/simulation/ -iRepository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Check if there's documentation or comments about ghost point creation strategy
rg -n "ghost.*point\|ghost.*cell" src/simulation/ -B 2 -A 2 -i | head -50Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Search for where ghost_points are initially identified or marked
rg -n "ghost_points\|num_gps" src/simulation/m_ibm.fpp -B 2 -A 2 | head -60Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Look for initialization of ghost_points array and marking algorithm
rg -n "do.*i.*num_gps\|ghost_points\(i\)" src/simulation/m_ibm.fpp -B 1 -A 1 | head -40Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Check the beginning of s_ibm to understand ghost point creation
sed -n '1,100p' src/simulation/m_ibm.fppRepository: MFlowCode/MFC
Length of output: 3044
🏁 Script executed:
# Find the s_find_ghost_points and s_find_num_ghost_points subroutines
rg -n "subroutine s_find_ghost_points\|subroutine s_find_num_ghost_points" src/simulation/m_ibm.fpp -A 50 | head -120Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Look for how ghost points are identified relative to surfaces
rg -n "levelset\|distance\|surface" src/simulation/m_ibm.fpp -B 1 -A 1 | head -60Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Search for the marking of ghost points in relation to IB surfaces
sed -n '300,600p' src/simulation/m_ibm.fppRepository: MFlowCode/MFC
Length of output: 13967
🏁 Script executed:
# Look for where ib_markers are set and understand the marking algorithm
rg -n "ib_markers.*sf\|marking" src/simulation/ -B 2 -A 2 | head -80Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Check if there's any documentation or algorithm comment about the ghost point identification
rg -n "ghost.*boundary\|interface\|inside\|outside" src/simulation/m_ibm.fpp -i -B 1 -A 1 | head -50Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Look at the levelset computation again and see if there's any handling for outside points
sed -n '413,428p' src/simulation/m_compute_levelset.fppRepository: MFlowCode/MFC
Length of output: 1293
🏁 Script executed:
# Check how levelset is used after computation - is there NaN handling?
rg -n "gp%levelset\|levelset" src/simulation/m_ibm.fpp -A 2 -B 1 | head -100Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Check if there's any NaN checking or validation after levelset computation
rg -n "nan\|isnan\|check\|valid" src/simulation/ -i | grep -i levelsetRepository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Look for how the levelset distance is used in image point computation
sed -n '435,470p' src/simulation/m_ibm.fppRepository: MFlowCode/MFC
Length of output: 1302
🏁 Script executed:
# Look for documentation or comments about ghost point placement relative to surfaces
rg -n "ghost.*point\|levelset" src/simulation/m_compute_levelset.fpp -B 3 -A 3 | head -80Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Check if there's any mathematical guarantee that ghost points are inside
rg -n "inside\|outside\|distance" src/simulation/m_ibm.fpp -B 2 -A 2 | grep -i "ghost\|point" -B 2 -A 2 | head -50Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Look at what happens when levelset is computed - trace the usage
sed -n '30,80p' src/simulation/m_compute_levelset.fppRepository: MFlowCode/MFC
Length of output: 1960
Add a guard against negative discriminant in the ellipse levelset computation.
The quadratic discriminant can be negative if a ghost point is outside the ellipse, causing sqrt() to return NaN. Add a check before the sqrt:
if (quadratic_coeffs(2)**2._wp - 4._wp*quadratic_coeffs(1)*quadratic_coeffs(3) >= 0._wp) then
gp%levelset = -0.5_wp*(-quadratic_coeffs(2) + sqrt(quadratic_coeffs(2)**2._wp - 4._wp*quadratic_coeffs(1)*quadratic_coeffs(3)))/quadratic_coeffs(1)
else
call s_mpi_abort("Negative discriminant in ellipse levelset: point may be outside surface")
end if
🤖 Prompt for AI Agents
In `@src/simulation/m_compute_levelset.fpp` around lines 419 - 424, The levelset
assignment using quadratic_coeffs and gp%levelset must guard the discriminant
before calling sqrt: compute the discriminant (disc = quadratic_coeffs(2)**2._wp
- 4._wp*quadratic_coeffs(1)*quadratic_coeffs(3)), check disc >= 0._wp and only
then set gp%levelset with the existing formula using sqrt(disc); otherwise call
s_mpi_abort with a clear message (e.g., "Negative discriminant in ellipse
levelset: point may be outside surface") so you fail fast instead of producing
NaN.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/simulation/m_ib_patches.fpp`:
- Around line 230-243: The GPU update only copied the models array descriptor,
leaving nested allocatable components like models%model, models%boundary_v,
models%interpolated_boundary_v (and associated sizes such as
models%total_vertices and models%boundary_edge_count) untransferred; fix by,
immediately after assigning those members inside the loop (where
models(patch_id)%model, %boundary_v, %interpolated_boundary_v, %total_vertices
are set), perform explicit device updates for each allocatable component (e.g.,
issue device copy/update for models(patch_id)%model, models(patch_id)%boundary_v
and, if interpolate,
models(patch_id)%interpolated_boundary_v/total_vertices/boundary_edge_count) or
replace the single $:GPU_UPDATE(device='[models]') with a $:GPU_ENTER_DATA /
copyin clause that copies the actual allocatable arrays into persistent device
memory so device-side pointers are valid before any GPU use.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/simulation/m_ib_patches.fpp`:
- Around line 244-254: The fypp loop uses an undefined Fypp symbol
MAX_NUM_PATCHES causing preprocessing to fail; fix by either replacing
MAX_NUM_PATCHES with the literal 10 in the loop range (i.e. change the range to
1..11) or define the Fypp variable at top of this file with #:set
MAX_NUM_PATCHES = 10 so the loop (`#:for patch_id in range(1,
MAX_NUM_PATCHES+1)`) can expand; ensure this addresses the references to
models(${patch_id}$)%boundary_v, %interpolated_boundary_v, and %model%trs so the
GPU update macros (`GPU_UPDATE`) generate for all patches.
src/simulation/m_ib_patches.fpp
Outdated
| #:for patch_id in range(1, MAX_NUM_PATCHES+1) | ||
| if (allocated(models(${patch_id}$)%boundary_v)) then | ||
| $:GPU_UPDATE(device='[models(${patch_id}$)%boundary_v]') | ||
| end if | ||
| if (allocated(models(${patch_id}$)%interpolated_boundary_v)) then | ||
| $:GPU_UPDATE(device='[models(${patch_id}$)%interpolated_boundary_v]') | ||
| end if | ||
| if (allocated(models(${patch_id}$)%model%trs)) then | ||
| $:GPU_UPDATE(device='[models(${patch_id}$)%model%trs]') | ||
| end if | ||
| #:endfor |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for MAX_NUM_PATCHES definition across the codebase
rg -n "MAX_NUM_PATCHES" --type-add 'fortran:*.{f90,fpp,F90}' --type fortran -B 2 -A 2Repository: MFlowCode/MFC
Length of output: 520
🏁 Script executed:
#!/bin/bash
# Check the includes and imports at the top of m_ib_patches.fpp
head -40 src/simulation/m_ib_patches.fppRepository: MFlowCode/MFC
Length of output: 1370
🏁 Script executed:
#!/bin/bash
# Search for MAX_NUM_PATCHES definition (not usage)
rg -n "^\s*(integer|parameter.*MAX_NUM_PATCHES|#:set.*MAX_NUM_PATCHES)" --type-add 'fortran:*.{f90,fpp,F90}' --type fortranRepository: MFlowCode/MFC
Length of output: 50370
🏁 Script executed:
#!/bin/bash
# Check m_global_parameters to see if MAX_NUM_PATCHES is defined there
fd -e fpp -e f90 m_global_parameters | head -5 | xargs -I {} grep -n "MAX_NUM_PATCHES" {}Repository: MFlowCode/MFC
Length of output: 41
🏁 Script executed:
#!/bin/bash
# Check what's in macros.fpp which is included
fd -e fpp macros.fpp | xargs cat | head -100Repository: MFlowCode/MFC
Length of output: 3018
🏁 Script executed:
#!/bin/bash
# Narrow search: find any line containing MAX_NUM_PATCHES definition
rg "MAX_NUM_PATCHES\s*=" --type-add 'fortran:*.{f90,fpp,F90}' --type fortranRepository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
#!/bin/bash
# Search in include directories
fd -e fpp . src/simulation/include src/common/include 2>/dev/null | xargs grep -l "MAX_NUM_PATCHES" 2>/dev/null || echo "Not found in include dirs"Repository: MFlowCode/MFC
Length of output: 82
🏁 Script executed:
#!/bin/bash
# Check if MAX_NUM_PATCHES is in m_global_parameters.fpp
grep -n "MAX_NUM_PATCHES" src/simulation/m_global_parameters.fpp || echo "Not in m_global_parameters"Repository: MFlowCode/MFC
Length of output: 83
🏁 Script executed:
#!/bin/bash
# Check if there's a related constant or if it should be defined in case.fpp or similar
fd -e fpp . src/simulation | xargs grep -l "NUM_PATCHES\|PATCHES" | head -5Repository: MFlowCode/MFC
Length of output: 88
🏁 Script executed:
#!/bin/bash
# Look at the context around line 244 in m_ib_patches.fpp to understand intent
sed -n '230,260p' src/simulation/m_ib_patches.fppRepository: MFlowCode/MFC
Length of output: 1479
🏁 Script executed:
#!/bin/bash
# Search for 'models' declaration and size to understand the patch array bounds
grep -n "models\s*(" src/simulation/m_ib_patches.fpp | head -20Repository: MFlowCode/MFC
Length of output: 1142
🏁 Script executed:
#!/bin/bash
# Check module-level declarations for models array
sed -n '1,100p' src/simulation/m_ib_patches.fpp | grep -A5 -B5 "models"Repository: MFlowCode/MFC
Length of output: 960
🏁 Script executed:
#!/bin/bash
# Search for parameter or constant that might relate to the loop
rg "patch.*id.*range|range.*patch|num.*patch" --type-add 'fortran:*.{f90,fpp}' --type fortran -i | head -20Repository: MFlowCode/MFC
Length of output: 1933
🏁 Script executed:
#!/bin/bash
# Search for num_patches_max definition
grep -n "num_patches_max" src/simulation/m_global_parameters.fpp | head -10Repository: MFlowCode/MFC
Length of output: 252
🏁 Script executed:
#!/bin/bash
# Check if there's a Fypp #:set or #:def for MAX_NUM_PATCHES in case.fpp
cat src/simulation/case.fpp | head -50Repository: MFlowCode/MFC
Length of output: 112
🏁 Script executed:
#!/bin/bash
# Find case.fpp file - check include directories per learnings
fd case.fpp src/simulation/include src/common/include 2>/dev/nullRepository: MFlowCode/MFC
Length of output: 84
🏁 Script executed:
#!/bin/bash
# Search for num_patches_max definition (with = sign)
rg "num_patches_max\s*=" --type-add 'fortran:*.{f90,fpp,F90}' --type fortranRepository: MFlowCode/MFC
Length of output: 130
🏁 Script executed:
#!/bin/bash
# Look at where case.fpp is included from
grep -r "#:include.*case" src/simulation/ 2>/dev/null | head -5Repository: MFlowCode/MFC
Length of output: 320
🏁 Script executed:
#!/bin/bash
# Check case.fpp content
cat src/common/include/case.fppRepository: MFlowCode/MFC
Length of output: 423
🏁 Script executed:
#!/bin/bash
# Check m_constants.fpp for related definitions
grep -n "num_patches\|MAX_NUM" src/common/m_constants.fppRepository: MFlowCode/MFC
Length of output: 106
🏁 Script executed:
#!/bin/bash
# Check if m_constants.fpp is included in m_ib_patches.fpp
grep -n "#:include\|use.*constants" src/simulation/m_ib_patches.fppRepository: MFlowCode/MFC
Length of output: 240
🏁 Script executed:
#!/bin/bash
# Check what m_constants.fpp looks like and if it exports num_patches_max
head -50 src/common/m_constants.fppRepository: MFlowCode/MFC
Length of output: 3461
🏁 Script executed:
#!/bin/bash
# Verify m_constants.fpp is part of the module search path
grep -r "m_constants" src/simulation/m_ib_patches.fppRepository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
#!/bin/bash
# Check if any Fypp file defines MAX_NUM_PATCHES or similar
fd -e fpp . src/common/include src/simulation/include | xargs grep -l "MAX_NUM_PATCHES\|num_patches" 2>/dev/null || echo "Not found"Repository: MFlowCode/MFC
Length of output: 66
🏁 Script executed:
#!/bin/bash
# Check the exact Fypp syntax and see if there's a pattern for using constants
grep -r "#:set\|#:def" src/common/include/macros.fpp | head -20Repository: MFlowCode/MFC
Length of output: 747
🏁 Script executed:
#!/bin/bash
# Check if macros.fpp includes or defines anything that would define MAX_NUM_PATCHES
fd macros.fpp src/common/include src/simulation/include | xargs cat | grep -i "MAX_NUM_PATCHES\|num_patches" || echo "Not found in macros"Repository: MFlowCode/MFC
Length of output: 76
🏁 Script executed:
#!/bin/bash
# Verify the build actually fails with this undefined constant
# Check if there's a CMake or build log showing the error
fd -e log -e txt . | xargs grep -l "MAX_NUM_PATCHES" 2>/dev/null || echo "No build logs found"Repository: MFlowCode/MFC
Length of output: 76
🏁 Script executed:
#!/bin/bash
# Check what num_patches_max is used for elsewhere to understand the pattern
grep -n "num_patches_max" src/simulation/m_global_parameters.fpp -B2 -A2Repository: MFlowCode/MFC
Length of output: 730
🏁 Script executed:
#!/bin/bash
# Check if there's a pattern for defining Fypp constants in other files
fd -e fpp . src/simulation | xargs grep "#:set.*=" | grep -i "num\|count\|max" | head -15Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
#!/bin/bash
# Look at the entire s_instantiate_STL_models subroutine to understand context better
sed -n '118,260p' src/simulation/m_ib_patches.fpp | tail -50Repository: MFlowCode/MFC
Length of output: 2599
🏁 Script executed:
#!/bin/bash
# Check if there's any Fypp #:set definition that should be present
fd -e fpp . src/ | xargs grep "#:set.*=" | grep -i "patch\|max" | head -20Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
#!/bin/bash
# Check the entire beginning of the subroutine to see all loop context
sed -n '118,230p' src/simulation/m_ib_patches.fpp | head -60Repository: MFlowCode/MFC
Length of output: 2779
🏁 Script executed:
#!/bin/bash
# Check what's in macros.fpp completely
cat src/common/include/macros.fpp | head -100Repository: MFlowCode/MFC
Length of output: 4119
🏁 Script executed:
#!/bin/bash
# Check if case.fpp or included files export num_patches_max as a Fypp constant
cat src/common/include/case.fpp
cat src/common/include/ExtrusionHardcodedIC.fpp 2>/dev/null | head -20 || echo "File not found"Repository: MFlowCode/MFC
Length of output: 1376
🏁 Script executed:
#!/bin/bash
# Check if m_constants is a Fortran module or just Fypp
file src/common/m_constants.fpp
head -5 src/common/m_constants.fppRepository: MFlowCode/MFC
Length of output: 228
🏁 Script executed:
#!/bin/bash
# Check what m_global_parameters imports or uses from m_constants
grep -n "m_constants\|num_patches_max" src/simulation/m_global_parameters.fpp | head -20Repository: MFlowCode/MFC
Length of output: 252
🏁 Script executed:
#!/bin/bash
# Look at what Fypp provides access to - check if num_patches_max is accessible in m_ib_patches
# by looking at what happens when Fypp processes a file that includes case.fpp
grep -r "num_patches_max" src/common/include/ 2>/dev/null || echo "Not in include dir"Repository: MFlowCode/MFC
Length of output: 75
🏁 Script executed:
#!/bin/bash
# Check how Fypp gets access to constants - look for #:set or #:def patterns in initialization
grep -r "#:set\|#:def" src/common/include/ src/simulation/include/ 2>/dev/null | grep -i "num\|max\|patch" | head -20Repository: MFlowCode/MFC
Length of output: 259
🏁 Script executed:
#!/bin/bash
# Try to understand the build pipeline - check if there's a CMakeLists that preprocesses Fypp
fd CMakeLists.txt | xargs grep -l "fypp\|fpp" 2>/dev/null | head -3Repository: MFlowCode/MFC
Length of output: 71
Critical: MAX_NUM_PATCHES is undefined in Fypp context, causing preprocessing failure.
The Fypp loop at lines 244–254 references MAX_NUM_PATCHES which is not defined as a Fypp variable. The Fortran constant num_patches_max = 10 exists in src/common/m_constants.fpp, but is not accessible to the Fypp preprocessor.
Replace MAX_NUM_PATCHES with the hardcoded value 10:
#:for patch_id in range(1, 11)
Alternatively, add a Fypp variable definition at the top of the file:
#:set MAX_NUM_PATCHES = 10
🧰 Tools
🪛 GitHub Actions: Cleanliness
[error] 244-244: FyppFatalError: name 'MAX_NUM_PATCHES' is not defined
🤖 Prompt for AI Agents
In `@src/simulation/m_ib_patches.fpp` around lines 244 - 254, The fypp loop uses
an undefined Fypp symbol MAX_NUM_PATCHES causing preprocessing to fail; fix by
either replacing MAX_NUM_PATCHES with the literal 10 in the loop range (i.e.
change the range to 1..11) or define the Fypp variable at top of this file with
#:set MAX_NUM_PATCHES = 10 so the loop (`#:for patch_id in range(1,
MAX_NUM_PATCHES+1)`) can expand; ensure this addresses the references to
models(${patch_id}$)%boundary_v, %interpolated_boundary_v, and %model%trs so the
GPU update macros (`GPU_UPDATE`) generate for all patches.
There was a problem hiding this comment.
2 issues found across 1 file (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="src/simulation/m_ib_patches.fpp">
<violation number="1" location="src/simulation/m_ib_patches.fpp:244">
P0: The GPU update loop iterates up to `MAX_NUM_PATCHES` (compile-time constant) while the `models` array is allocated to `num_ibs` (runtime variable). If `num_ibs < MAX_NUM_PATCHES`, accessing `models(patch_id)` for `patch_id > num_ibs` causes an out-of-bounds memory access/crash.
Verify if `MAX_NUM_PATCHES` is guaranteed to be equal to `num_ibs` (unlikely). Use a standard Fortran loop up to `num_ibs` instead of the preprocessor unrolled loop, or allocate `models` to `MAX_NUM_PATCHES`.</violation>
<violation number="2" location="src/simulation/m_ib_patches.fpp:244">
P1: Define a Fypp constant for `MAX_NUM_PATCHES` (or replace it with a literal range) so the preprocessor can expand this loop without a fatal undefined-name error.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
|
CodeAnt AI is running Incremental review Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
| ! 3D Patch Geometries | ||
| if (p > 0) then | ||
|
|
||
| $:GPU_PARALLEL_LOOP(private='[i,patch_id,patch_geometry]', copy='[gps]', copyin='[patch_ib,Np]') |
There was a problem hiding this comment.
Suggestion: The GPU parallel loop over ghost points in the 3D branch calls the STL model levelset routine, which accesses the global models array, but models is not included in the loop's copyin list, so on GPU offload the kernel will read models from host memory or uninitialized device memory, causing incorrect results or runtime errors; models should be explicitly mapped to the device. [possible bug]
Severity Level: Major ⚠️
- ❌ STL model levelset incorrect on GPU.
- ⚠️ GPU-enabled simulations using STL IB unreliable.
- ⚠️ s_model_levelset (models reads) produces garbage.| $:GPU_PARALLEL_LOOP(private='[i,patch_id,patch_geometry]', copy='[gps]', copyin='[patch_ib,Np]') | |
| $:GPU_PARALLEL_LOOP(private='[i,patch_id,patch_geometry]', copy='[gps]', copyin='[patch_ib,models,Np]') |
Steps of Reproduction ✅
1. Build and run with GPU offload enabled so the $:GPU_PARALLEL_LOOP is executed on device
(the loop is declared in src/simulation/m_compute_levelset.fpp around lines 39-40).
2. Ensure at least one ghost point belongs to an STL model patch (patch_geometry == 12) so
s_apply_levelset invokes s_model_levelset from inside the GPU-parallel loop
(s_model_levelset uses models(...) at lines ~661-666 in the s_model_levelset routine).
3. At runtime the GPU kernel will execute s_model_levelset but the loop's copyin list does
not include the global models array, so the kernel will attempt to read models on device
without an explicit device mapping.
4. Observe either incorrect gp%levelset/gp%levelset_norm values for STL patches or offload
runtime errors depending on the compiler/device mapping behavior; the incorrect values
originate from reads of unmapped/invalid device memory for models.Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src/simulation/m_compute_levelset.fpp
**Line:** 39:39
**Comment:**
*Possible Bug: The GPU parallel loop over ghost points in the 3D branch calls the STL model levelset routine, which accesses the global `models` array, but `models` is not included in the loop's `copyin` list, so on GPU offload the kernel will read `models` from host memory or uninitialized device memory, causing incorrect results or runtime errors; `models` should be explicitly mapped to the device.
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.|
|
||
| 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 |
There was a problem hiding this comment.
Suggestion: In the ellipse levelset computation, the normal vector is normalized without checking for zero length; when the ghost point lies exactly at the ellipse centroid, this makes the dot product zero and causes a division by zero, leading to NaNs in both the normal and the levelset value. [division by zero]
Severity Level: Major ⚠️
- ❌ Ellipse IB levelset computation returns NaN.
- ⚠️ s_apply_levelset for ellipse patches unreliable.
- ⚠️ Downstream IB operations consume NaN normals.| normal_vector = normal_vector/sqrt(dot_product(normal_vector, normal_vector)) ! normalize the vector | |
| if (f_approx_equal(dot_product(normal_vector, normal_vector), 0._wp)) then | |
| gp%levelset = 0._wp | |
| gp%levelset_norm = 0._wp | |
| return | |
| end if |
Steps of Reproduction ✅
1. Ensure a ghost point is processed by s_apply_levelset in file
src/simulation/m_compute_levelset.fpp (s_apply_levelset branches call s_ellipse_levelset
for patch_geometry==6; see s_apply_levelset around lines 61-66 in the PR hunk).
2. Configure an ellipse IB patch (patch_ib(...)%geometry == 6) whose centroid equals a
grid cell center so that xy_local computed in s_ellipse_levelset becomes exactly zero
(s_ellipse_levelset computes xy_local at lines ~410-412, then uses it at lines 413-416).
3. Run the simulation/path that triggers s_apply_levelset; execution enters
s_ellipse_levelset (file src/simulation/m_compute_levelset.fpp, around lines 410-426) and
reaches the normalization at lines 413-415.
4. At line 415 the code divides by sqrt(dot_product(normal_vector, normal_vector)) which
is zero, producing a division-by-zero and resulting NaNs in gp%levelset_norm (line 416)
and potentially in subsequent gp%levelset computation (line 424). Observe NaN values in
gp%levelset_norm/gp%levelset for that ghost point.Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src/simulation/m_compute_levelset.fpp
**Line:** 415:415
**Comment:**
*Division By Zero: In the ellipse levelset computation, the normal vector is normalized without checking for zero length; when the ghost point lies exactly at the ellipse centroid, this makes the dot product zero and causes a division by zero, leading to NaNs in both the normal and the levelset value.
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.| 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 | ||
| gp%levelset = -0.5_wp*(-quadratic_coeffs(2) + sqrt(quadratic_coeffs(2)**2._wp - 4._wp*quadratic_coeffs(1)*quadratic_coeffs(3)))/quadratic_coeffs(1) |
There was a problem hiding this comment.
Suggestion: The quadratic formula used to compute the ellipse levelset has an extra leading minus sign, so instead of computing (-B + sqrt(B^2 - 4AC)) / (2A) as indicated by the comment, it returns the negative of that value, flipping the sign of the distance and potentially inverting the inside/outside convention. [logic error]
Severity Level: Critical 🚨
- ❌ Ellipse levelset sign inverted.
- ⚠️ Ghost-point boundary classification incorrect.
- ⚠️ IB boundary condition application affected.| gp%levelset = -0.5_wp*(-quadratic_coeffs(2) + sqrt(quadratic_coeffs(2)**2._wp - 4._wp*quadratic_coeffs(1)*quadratic_coeffs(3)))/quadratic_coeffs(1) | |
| gp%levelset = 0.5_wp*(-quadratic_coeffs(2) + sqrt(quadratic_coeffs(2)**2._wp - 4._wp*quadratic_coeffs(1)*quadratic_coeffs(3)))/quadratic_coeffs(1) |
Steps of Reproduction ✅
1. Trigger s_apply_levelset in src/simulation/m_compute_levelset.fpp for a 2D ghost point
whose patch_ib geometry is an ellipse (s_apply_levelset calls s_ellipse_levelset for
geometry==6; see the s_apply_levelset call in the 2D branch around lines 61-66).
2. Inside s_ellipse_levelset (file src/simulation/m_compute_levelset.fpp, around lines
410-426) the code computes quadratic_coeffs and then evaluates gp%levelset at line 424
using the quadratic formula.
3. Because the assignment at line 424 includes a leading negative sign, the computed
gp%levelset is the negative of the intended root; run a unit test that places a point
known to be outside the ellipse and verify the sign of gp%levelset is inverted compared to
mathematical expectation.
4. Observe that the levelset sign is flipped for all ellipse ghost points (incorrect
inside/outside convention for ellipses).Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src/simulation/m_compute_levelset.fpp
**Line:** 424:424
**Comment:**
*Logic Error: The quadratic formula used to compute the ellipse levelset has an extra leading minus sign, so instead of computing (-B + sqrt(B^2 - 4AC)) / (2A) as indicated by the comment, it returns the negative of that value, flipping the sign of the distance and potentially inverting the inside/outside convention.
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.
src/simulation/m_ib_patches.fpp
Outdated
| ! update allocatables | ||
| $:GPU_UPDATE(device='[models]') | ||
| $:GPU_UPDATE(device='[models(patch_id)%boundary_v]') | ||
| $:GPU_UPDATE(device='[models(patch_id)%interpolated_boundary_v]') |
There was a problem hiding this comment.
✅ Suggestion: The GPU update macro is invoked on models(patch_id)%interpolated_boundary_v even when no interpolation was performed, so the allocatable component may be unallocated and any internal size/shape query in the macro will trigger a runtime error when interpolate is false. [possible bug]
Severity Level: Major ⚠️
- ❌ STL model initialization may crash on startup.
- ⚠️ Some STL models skip interpolation frequently.
- ⚠️ GPU device update macros interact with allocatables.
- ❌ Loading of 2D/3D STL patches may be aborted.| $:GPU_UPDATE(device='[models(patch_id)%interpolated_boundary_v]') | |
| if (allocated(models(patch_id)%interpolated_boundary_v)) then | |
| $:GPU_UPDATE(device='[models(patch_id)%interpolated_boundary_v]') | |
| end if |
Steps of Reproduction ✅
1. Prepare an input case that includes an STL patch but where interpolation is not
required (patch_ib(...)%model_interpolate → f_check_interpolation_2D/3D sets interpolate =
.false()). This path executes inside s_instantiate_STL_models in
src/simulation/m_ib_patches.fpp (see the s_instantiate_STL_models block around lines
236-247, specifically the update allocatables region at lines 239-243).
2. Run the simulation initialization so s_instantiate_STL_models executes: f_model_read →
f_check_interpolation_* sets interpolate = .false() and the code does NOT allocate
models(patch_id)%interpolated_boundary_v (allocation only happens inside the IF
interpolate branch just above lines 239-243).
3. When execution reaches the unguarded GPU update sequence at lines 239-243, the macro
$:GPU_UPDATE(device='[models(patch_id)%interpolated_boundary_v]') is invoked while
models(patch_id)%interpolated_boundary_v remains unallocated.
4. Depending on the macro expansion and GPU toolchain behavior, the macro may query
allocation/shape or attempt device update and trigger a runtime error (allocation query or
segfault) during initialization. If compilation emits only warnings, no runtime failure
occurs; otherwise a crash/reported runtime error will reproduce the problem.Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src/simulation/m_ib_patches.fpp
**Line:** 242:242
**Comment:**
*Possible Bug: The GPU update macro is invoked on `models(patch_id)%interpolated_boundary_v` even when no interpolation was performed, so the allocatable component may be unallocated and any internal size/shape query in the macro will trigger a runtime error when `interpolate` is false.
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.| xyz_local = [x_cc(i) - center(1), y_cc(j) - center(2), 0._wp] | ||
| if (p > 0) then | ||
| point(3) = z_cc(k) | ||
| xyz_local(3) = z_cc(k) - center(3) | ||
| end if | ||
| xyz_local = matmul(inverse_rotation, xyz_local) | ||
|
|
||
| if (grid_geometry == 3) then | ||
| point = f_convert_cyl_to_cart(point) | ||
| xyz_local = f_convert_cyl_to_cart(xyz_local) |
There was a problem hiding this comment.
Suggestion: In the STL IB patch, the code rotates coordinates in the native grid frame and only then applies a cylindrical-to-Cartesian conversion via f_convert_cyl_to_cart, which assumes [x,r,θ] input; this mismatched order and interpretation will misplace the geometry for cylindrical grids and produce incorrect immersed boundary marking. [logic error]
Severity Level: Critical 🚨
- ❌ Incorrect immersed-boundary placement in cylindrical grids.
- ⚠️ s_ib_model incorrectly transforms coordinates.
- ⚠️ s_apply_ib_patches produces wrong ib_markers_sf.
- ❌ Cylindrical STL simulations yield invalid geometry.| xyz_local = [x_cc(i) - center(1), y_cc(j) - center(2), 0._wp] | |
| if (p > 0) then | |
| point(3) = z_cc(k) | |
| xyz_local(3) = z_cc(k) - center(3) | |
| end if | |
| xyz_local = matmul(inverse_rotation, xyz_local) | |
| if (grid_geometry == 3) then | |
| point = f_convert_cyl_to_cart(point) | |
| xyz_local = f_convert_cyl_to_cart(xyz_local) | |
| if (grid_geometry == 3) then | |
| call s_convert_cylindrical_to_cartesian_coord(y_cc(j), z_cc(k)) | |
| xyz_local = [x_cc(i) - center(1), cart_y - center(2), 0._wp] | |
| if (p > 0) then | |
| xyz_local(3) = cart_z - center(3) | |
| end if | |
| else | |
| xyz_local = [x_cc(i) - center(1), y_cc(j) - center(2), 0._wp] | |
| if (p > 0) then | |
| xyz_local(3) = z_cc(k) - center(3) | |
| end if | |
| end if | |
| xyz_local = matmul(inverse_rotation, xyz_local) |
Steps of Reproduction ✅
1. Configure a simulation using cylindrical coordinates (grid_geometry == 3) and include
an STL patch (patch_ib geometry 5 or 12) so s_ib_model is executed. The relevant code
resides in src/simulation/m_ib_patches.fpp inside subroutine s_ib_model around lines
930-941 (xyz_local handling).
2. Start the simulation so s_apply_ib_patches calls s_ib_model for the STL patch
(s_apply_ib_patches dispatches to s_ib_model for geometry 5/12; s_ib_model contains the
xyz_local formation at lines 933-940).
3. In the current code path the cell coordinates are translated and rotated
(matmul(inverse_rotation, xyz_local)) with cylindrical components still in native [x, r,
theta] ordering, and only afterwards f_convert_cyl_to_cart is applied (line 940). This
converts a vector whose components have already been rotated, misplacing the geometry.
4. Observe incorrect IB marking: ib_markers_sf(i,j,k) assignments (later in s_ib_model)
will not match the physical STL location in cylindrical runs — reproduce by comparing
expected STL position vs ib_markers_sf mask output for a simple STL centered at known
radius/angle.Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src/simulation/m_ib_patches.fpp
**Line:** 933:940
**Comment:**
*Logic Error: In the STL IB patch, the code rotates coordinates in the native grid frame and only then applies a cylindrical-to-Cartesian conversion via `f_convert_cyl_to_cart`, which assumes `[x,r,θ]` input; this mismatched order and interpretation will misplace the geometry for cylindrical grids and produce incorrect immersed boundary marking.
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.|
CodeAnt AI Incremental review completed. |
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/common/m_model.fpp (3)
1060-1114:⚠️ Potential issue | 🟡 MinorPotential out-of-bounds access when
model%ntrs == 0.If
model%ntrsis zero, the loop at line 1081 never executes andtri_idxremains0. Line 1111 then accessesmodel%trs(0)%n(1), which is an out-of-bounds array access. While an empty model is unlikely in practice, this will cause undefined behavior or a crash with bounds-checking enabled.🛡️ Proposed guard
+ if (tri_idx == 0) then + normals = 0._wp + distance = 0._wp + return + end if + normals(1) = model%trs(tri_idx)%n(1) normals(2) = model%trs(tri_idx)%n(2) normals(3) = model%trs(tri_idx)%n(3)
1159-1194:⚠️ Potential issue | 🟡 MinorSame zero-index risk:
idx_bufferstays0whenboundary_edge_count == 0.Analogous to
tri_idxinf_distance_normals_3D, ifboundary_edge_countis zero, the loop never executes andidx_bufferremains0, causingboundary_v(0, 3, 1)at line 1190 — an out-of-bounds access.🛡️ Proposed guard
+ if (idx_buffer == 0) then + normals = 0._wp + return + end if + normals(1) = boundary_v(idx_buffer, 3, 1)
1125-1152:⚠️ Potential issue | 🟠 MajorRemove VLA and simplify to scalar running minimum in GPU
[seq]routine.The automatic array
dist_buffer(1:boundary_edge_count)(line 1136) is a variable-length array whose size depends on a runtime argument. In GPU device code, nvfortran enforces a 512 KiB compile-time stack-frame limit per procedure, and automatic arrays of unknown size easily exceed this or cause runtime stack overflow. Since the array is only used to computeminval, eliminate it entirely and track the minimum with a scalar accumulator in the loop. This pattern is already used in similar functions in this file (e.g.,f_interpolated_distance).Also remove the commented-out old declaration on line 1131.
⚡ Proposed simplification
function f_distance(boundary_v, boundary_edge_count, point) result(distance) $:GPU_ROUTINE(parallelism='[seq]') integer, intent(in) :: boundary_edge_count real(wp), intent(in), dimension(:, :, :) :: boundary_v - ! real(wp), intent(in), dimension(1:boundary_edge_count, 1:3, 1:2) :: boundary_v real(wp), dimension(1:3), intent(in) :: point integer :: i real(wp) :: dist_buffer1, dist_buffer2 - real(wp), dimension(1:boundary_edge_count) :: dist_buffer real(wp) :: distance - distance = 0._wp + distance = huge(1._wp) do i = 1, boundary_edge_count dist_buffer1 = sqrt((point(1) - boundary_v(i, 1, 1))**2 + & & (point(2) - boundary_v(i, 1, 2))**2) dist_buffer2 = sqrt((point(1) - boundary_v(i, 2, 1))**2 + & & (point(2) - boundary_v(i, 2, 2))**2) - dist_buffer(i) = minval((/dist_buffer1, dist_buffer2/)) + distance = min(distance, min(dist_buffer1, dist_buffer2)) end do - distance = minval(dist_buffer) - end function f_distance
🧹 Nitpick comments (2)
src/common/m_model.fpp (2)
490-511: GPU directive is intentionally disabled; the TODO should be tracked.The
! $:GPU_ROUTINEis commented out becauserandom_numberis not callable from GPU device code. The fibonacci-sphere approach in the comment block is a viable deterministic replacement that would also eliminate the non-reproducibility of the ray-casting test.Consider opening an issue to track this so the TODO doesn't go stale, especially since the PR objective explicitly targets GPU-parallelized levelset computation and this function (
f_model_is_inside) remains a CPU-only bottleneck for STL models.Would you like me to open an issue to track replacing
random_numberwith the fibonacci-sphere approach to enable GPU execution off_model_is_inside?
8-26: File exceeds the 1000-line guideline limit (1251 lines).While this is pre-existing, the refactor could be an opportunity to split geometry-specific helpers (e.g., interpolation routines, boundary-check routines) into a separate module. This would also align with the PR's broader goal of consolidating levelset logic into dedicated modules. As per coding guidelines: "Keep … files ≤ 1000."
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="src/simulation/m_ib_patches.fpp">
<violation number="1" location="src/simulation/m_ib_patches.fpp:242">
P2: Guard the GPU update on interpolated_boundary_v with an allocated() check to avoid touching an unallocated array when interpolation is skipped.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@src/simulation/m_compute_levelset.fpp`:
- Around line 239-240: z_min/z_max are computed in global coordinates (using
center(3)) but later compared to the already-translated local coordinate
xyz_local(3), causing wrong distances and normals; change the code so z bounds
are expressed in the local frame (e.g. z_min = -lz/2 and z_max = +lz/2) or
compute z_min/z_max after subtracting center (so they are relative to the
translated origin) and then use those when computing dist_side and normals for
xyz_local(3). Update the analogous occurrence around line ~288 as well (keep
references: z_min, z_max, center, lz, xyz_local, dist_side).
- Around line 64-66: The 2D GPU_PARALLEL_LOOP uses copy='[gps]' and
patch_ib(1:num_ibs) which is inconsistent with the 3D path; change the 2D loop's
data clauses to use present='[gps]' (since gps is already on-device via
GPU_UPDATE) and make the patch_ib clause consistent with the 3D loop (use the
full patch_ib array form rather than the sliced (1:num_ibs)), leaving
private='[i,patch_id,patch_geometry]' and copyin='[Np,patch_ib]' (or the same
copyin form used in the 3D branch) so the kernel uses present gps and a
consistent patch_ib specifier.
- Around line 39-60: The GPU loops call s_model_levelset which dereferences the
host array-of-structs models (e.g. models(patch_id)%interpolate,
%boundary_edge_count, %total_vertices, %model, %interpolated_boundary_v,
%boundary_v) but models is not included in the GPU data clauses; add models to
device data so the kernel won't fault. Best fix: after
s_instantiate_STL_models() returns, create a persistent GPU data region (using
$:GPU_ENTER_DATA(present='[models]', copyin='[models]') / $:END_GPU_ENTER_DATA()
or the project macro equivalent) to keep models on-device for reuse;
alternatively, add models to the copyin list of the $:GPU_PARALLEL_LOOP(...)
that calls s_model_levelset (and the corresponding 2D loop) so models is present
during the loops. Ensure the same symbol name models is used in the data clause
and that its lifetime covers both loops.
In `@src/simulation/m_ibm.fpp`:
- Around line 72-73: s_finalize_ibm_module must free the allocatable array
models and each of its allocatable components to avoid host/device leaks: in
s_finalize_ibm_module iterate over models (if allocated(models)) and for each
element check and deallocate its allocatable fields model, boundary_v, and
interpolated_boundary_v (use ALLOCATED() guards), then deallocate the models
array itself (again guarding with ALLOCATED()) and handle any device-specific
deallocation if these components were moved to the device; update
s_finalize_ibm_module to perform these checks and DEALLOCATE calls for model,
boundary_v, interpolated_boundary_v, then DEALLOCATE(models).
🧹 Nitpick comments (3)
src/simulation/m_compute_levelset.fpp (2)
557-559: Usewpreal literals in array constructors for type consistency.
gp%levelset_norm = (/1, 0, 0/)creates an integer array that gets implicitly converted. While Fortran handles this correctly for these values, using explicit real literals is more consistent with the rest of the file (e.g., line 292 uses0._wp).The same pattern appears in
s_cylinder_levelset(lines 601–612) with(/1, 0, 0/),(/0, 1, 0/), etc.✏️ Example fix for sphere
- gp%levelset_norm = (/1, 0, 0/) + gp%levelset_norm = (/1._wp, 0._wp, 0._wp/)
310-311: Stale docstring:s_rectangle_levelsetis labeled "Initialize IBM module".The
!> Initialize IBM modulecomment on line 310 is copy-paste from a different subroutine and does not describe what this routine does.✏️ Fix docstring
- !> Initialize IBM module - subroutine s_rectangle_levelset(gp) + !> Computes the levelset distance and normal for a rectangle patch + subroutine s_rectangle_levelset(gp)src/simulation/m_ibm.fpp (1)
97-98: Remove the commented-outGPU_ENTER_DATAline.The commented-out directive on line 97 is dead code. The
GPU_UPDATEon line 98 is the intended replacement—clean up the leftover.🧹 Proposed fix
- ! $:GPU_ENTER_DATA(copyin='[patch_ib(1:num_ibs)]') $:GPU_UPDATE(device='[patch_ib(1:num_ibs)]')
| if (p > 0) then | ||
|
|
||
| $:GPU_PARALLEL_LOOP(private='[i,patch_id,patch_geometry]', present='[gps]', copyin='[patch_ib,Np]') | ||
| do i = 1, num_gps | ||
|
|
||
| patch_id = gps(i)%ib_patch_id | ||
| patch_geometry = patch_ib(patch_id)%geometry | ||
|
|
||
| if (patch_geometry == 8) then | ||
| call s_sphere_levelset(gps(i)) | ||
| elseif (patch_geometry == 9) then | ||
| call s_cuboid_levelset(gps(i)) | ||
| elseif (patch_geometry == 10) then | ||
| call s_cylinder_levelset(gps(i)) | ||
| elseif (patch_geometry == 11) then | ||
| call s_3d_airfoil_levelset(gps(i)) | ||
| ! STL+IBM patch | ||
| elseif (patch_geometry == 12) then | ||
| call s_model_levelset(gps(i)) | ||
| end if | ||
| end do | ||
| $:END_GPU_PARALLEL_LOOP() |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Step 1: Locate m_compute_levelset.fpp and check GPU loops around lines 39-60
echo "=== Checking m_compute_levelset.fpp GPU loops (target: lines 39-60) ==="
head -70 src/simulation/m_compute_levelset.fpp | tail -40
echo ""
echo "=== Searching for all GPU_PARALLEL_LOOP in m_compute_levelset.fpp ==="
grep -n "GPU_PARALLEL_LOOP" src/simulation/m_compute_levelset.fpp
echo ""
echo "=== Checking if models is accessed in GPU loops ==="
rg -n "models" src/simulation/m_compute_levelset.fpp -B2 -A2Repository: MFlowCode/MFC
Length of output: 3781
🏁 Script executed:
#!/bin/bash
# Step 2: Find s_model_levelset implementation and check what it accesses
echo "=== Locating s_model_levelset subroutine ==="
rg -n "subroutine s_model_levelset" src/simulation/
echo ""
echo "=== Checking s_model_levelset for models access ==="
rg -n "subroutine s_model_levelset" src/simulation/ -A30 | head -50Repository: MFlowCode/MFC
Length of output: 3090
🏁 Script executed:
#!/bin/bash
# Step 3: Check if models has GPU data management (GPU_ENTER_DATA, copyin, etc.)
echo "=== Searching for GPU_ENTER_DATA in simulation directory ==="
rg -n "GPU_ENTER_DATA\|GPU_UPDATE" src/simulation/ | head -20
echo ""
echo "=== Checking @:ALLOCATE for models ==="
rg -n "@:ALLOCATE.*models\|allocate.*models" src/simulation/ -B1 -A1 | head -30
echo ""
echo "=== Searching for models declaration ==="
rg -n "^\\s*(type|class|real|integer).*models" src/simulation/ -B1 | head -20Repository: MFlowCode/MFC
Length of output: 328
🏁 Script executed:
#!/bin/bash
# Verify geometry ID for s_model_levelset in 2D case
echo "=== Checking 2D loop for geometry handling ==="
sed -n '66,90p' src/simulation/m_compute_levelset.fpp
echo ""
echo "=== Full s_model_levelset call context in both loops ==="
grep -n "s_model_levelset" src/simulation/m_compute_levelset.fpp -B3 -A1Repository: MFlowCode/MFC
Length of output: 1927
🏁 Script executed:
#!/bin/bash
# Check if models is used elsewhere with GPU data management patterns
echo "=== Searching for any persistent GPU data region setup ==="
rg -n "persistent\|enter data\|GPU_ENTER_DATA" src/simulation/ | head -20
echo ""
echo "=== Checking m_ib_patches for s_instantiate_STL_models ==="
rg -n "subroutine s_instantiate_STL_models" src/simulation/m_ib_patches.fpp -A30 | head -40Repository: MFlowCode/MFC
Length of output: 2178
models variable not in GPU data clauses—both GPU loops calling s_model_levelset will fault on device.
The GPU loops at lines 41 (3D, geometry 12) and 66 (2D, geometry 5) only copyin patch_ib and Np. The s_model_levelset subroutine (marked GPU_ROUTINE) accesses models(patch_id)%interpolate, models(patch_id)%boundary_edge_count, models(patch_id)%total_vertices, models(patch_id)%model, models(patch_id)%interpolated_boundary_v, and models(patch_id)%boundary_v. With default(present) semantics from the macro, any unresolved symbol triggers a runtime fault on the GPU.
Add models to a persistent $:GPU_ENTER_DATA(...) region after s_instantiate_STL_models() returns (recommended, since models are large and reused), or add to both loop copyin clauses.
🤖 Prompt for AI Agents
In `@src/simulation/m_compute_levelset.fpp` around lines 39 - 60, The GPU loops
call s_model_levelset which dereferences the host array-of-structs models (e.g.
models(patch_id)%interpolate, %boundary_edge_count, %total_vertices, %model,
%interpolated_boundary_v, %boundary_v) but models is not included in the GPU
data clauses; add models to device data so the kernel won't fault. Best fix:
after s_instantiate_STL_models() returns, create a persistent GPU data region
(using $:GPU_ENTER_DATA(present='[models]', copyin='[models]') /
$:END_GPU_ENTER_DATA() or the project macro equivalent) to keep models on-device
for reuse; alternatively, add models to the copyin list of the
$:GPU_PARALLEL_LOOP(...) that calls s_model_levelset (and the corresponding 2D
loop) so models is present during the loops. Ensure the same symbol name models
is used in the data clause and that its lifetime covers both loops.
| elseif (n > 0) then | ||
|
|
||
| $:GPU_PARALLEL_LOOP(private='[i,patch_id,patch_geometry]', copy='[gps]', copyin='[Np,patch_ib(1:num_ibs)]') |
There was a problem hiding this comment.
GPU data clause mismatch: 2D loop uses copy for gps while 3D loop uses present.
The 3D path (line 41) correctly uses present='[gps]' since gps is already on the device via the GPU_UPDATE(device=...) at line 36 (and the persistent allocation from the caller). The 2D path uses copy='[gps]', which triggers redundant allocation/copy semantics and may interfere with the unstructured data region depending on the compiler. Use present for consistency and correctness.
Also, the 3D copyin uses patch_ib (full array) while 2D uses patch_ib(1:num_ibs) (slice)—minor inconsistency but worth aligning.
🔧 Proposed fix
- $:GPU_PARALLEL_LOOP(private='[i,patch_id,patch_geometry]', copy='[gps]', copyin='[Np,patch_ib(1:num_ibs)]')
+ $:GPU_PARALLEL_LOOP(private='[i,patch_id,patch_geometry]', present='[gps]', copyin='[patch_ib,Np]')📝 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.
| elseif (n > 0) then | |
| $:GPU_PARALLEL_LOOP(private='[i,patch_id,patch_geometry]', copy='[gps]', copyin='[Np,patch_ib(1:num_ibs)]') | |
| elseif (n > 0) then | |
| $:GPU_PARALLEL_LOOP(private='[i,patch_id,patch_geometry]', present='[gps]', copyin='[patch_ib,Np]') |
🤖 Prompt for AI Agents
In `@src/simulation/m_compute_levelset.fpp` around lines 64 - 66, The 2D
GPU_PARALLEL_LOOP uses copy='[gps]' and patch_ib(1:num_ibs) which is
inconsistent with the 3D path; change the 2D loop's data clauses to use
present='[gps]' (since gps is already on-device via GPU_UPDATE) and make the
patch_ib clause consistent with the 3D loop (use the full patch_ib array form
rather than the sliced (1:num_ibs)), leaving
private='[i,patch_id,patch_geometry]' and copyin='[Np,patch_ib]' (or the same
copyin form used in the 3D branch) so the kernel uses present gps and a
consistent patch_ib specifier.
| z_max = center(3) + lz/2 | ||
| z_min = center(3) - lz/2 |
There was a problem hiding this comment.
Critical: z_min/z_max are in global coordinates but compared against local xyz_local(3).
After line 242, center is subtracted from the global position, then rotation and offset are applied (lines 243–244), so xyz_local(3) is in the local frame. But z_min = center(3) - lz/2 and z_max = center(3) + lz/2 still include center(3). This produces incorrect dist_side and wrong normal direction whenever center(3) ≠ 0.
Concrete example: airfoil at z-center=5, lz=2. Ghost point at global z=5.5 → local z=0.5. Code computes dist_side = min(|0.5 - 4|, |6 - 0.5|) = 3.5 instead of the correct min(|0.5 + 1|, |1 - 0.5|) = 0.5.
Compare with s_cuboid_levelset (lines 466–471) which correctly uses ±length/2 without the centroid.
🐛 Proposed fix
- z_max = center(3) + lz/2
- z_min = center(3) - lz/2
+ z_max = lz/2
+ z_min = -lz/2Also applies to: 288-288
🤖 Prompt for AI Agents
In `@src/simulation/m_compute_levelset.fpp` around lines 239 - 240, z_min/z_max
are computed in global coordinates (using center(3)) but later compared to the
already-translated local coordinate xyz_local(3), causing wrong distances and
normals; change the code so z bounds are expressed in the local frame (e.g.
z_min = -lz/2 and z_max = +lz/2) or compute z_min/z_max after subtracting center
(so they are relative to the translated origin) and then use those when
computing dist_side and normals for xyz_local(3). Update the analogous
occurrence around line ~288 as well (keep references: z_min, z_max, center, lz,
xyz_local, dist_side).
| @:ALLOCATE(models(num_ibs)) | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, find the file and check its size
wc -l src/simulation/m_ibm.fppRepository: MFlowCode/MFC
Length of output: 86
🏁 Script executed:
# Check the allocation at line 72 and surrounding context
sed -n '65,80p' src/simulation/m_ibm.fppRepository: MFlowCode/MFC
Length of output: 581
🏁 Script executed:
# Check the finalization routine
sed -n '1175,1190p' src/simulation/m_ibm.fppRepository: MFlowCode/MFC
Length of output: 674
🏁 Script executed:
# Search for all DEALLOCATE statements in the file
rg -n "DEALLOCATE.*models|deallocate.*models" src/simulation/m_ibm.fppRepository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Find the type definition of models
rg -n "models.*allocatable|type.*::|real.*::\s*models" src/simulation/m_ibm.fpp | head -10Repository: MFlowCode/MFC
Length of output: 695
🏁 Script executed:
# Check m_ib_patches for related cleanup
rg -n "finalize|DEALLOCATE" src/simulation/m_ib_patches.fpp | head -30Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Search for models declaration in the module
rg -n "models" src/simulation/m_ibm.fpp | grep -E "allocatable|type|dimension" | head -20Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Get the module variable declarations section
sed -n '1,100p' src/simulation/m_ibm.fpp | rg -n "models|allocatable"Repository: MFlowCode/MFC
Length of output: 231
🏁 Script executed:
# Search for type definition that models uses
rg -n "type.*ib_patch|type.*patch" src/simulation/m_ibm.fpp | head -10Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Search for all lines containing 'models' to understand its scope
rg -n "models" src/simulation/m_ibm.fppRepository: MFlowCode/MFC
Length of output: 248
🏁 Script executed:
# Look at the module variable declarations section (first 150 lines)
sed -n '1,150p' src/simulation/m_ibm.fppRepository: MFlowCode/MFC
Length of output: 5154
🏁 Script executed:
# Check what's exported from m_ib_patches module
rg -n "public.*models|models.*allocatable" src/simulation/m_ib_patches.fppRepository: MFlowCode/MFC
Length of output: 187
🏁 Script executed:
# Search for models variable in all simulation files to understand its scope
rg -n "models.*allocatable|allocatable.*models" src/simulation/Repository: MFlowCode/MFC
Length of output: 149
🏁 Script executed:
# Check the exact context around line 72 - is it inside or outside a subroutine?
sed -n '60,85p' src/simulation/m_ibm.fppRepository: MFlowCode/MFC
Length of output: 851
🏁 Script executed:
# Find the type definition of t_model_array
rg -n "type.*t_model_array|t_model_array.*contains" src/simulation/m_ib_patches.fpp -A 20 | head -50Repository: MFlowCode/MFC
Length of output: 693
🏁 Script executed:
# Check for finalization routines in m_ib_patches
rg -n "finalize|DEALLOCATE.*models" src/simulation/m_ib_patches.fppRepository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Look for the complete t_model_array type definition
sed -n '1,200p' src/simulation/m_ib_patches.fpp | rg -n "type.*t_model_array" -A 30Repository: MFlowCode/MFC
Length of output: 1266
🏁 Script executed:
# Search for t_model_array type definition in m_ib_patches.fpp
rg -n "type\s*::\s*t_model_array|type\s*,\s*public\s*::\s*t_model_array" src/simulation/m_ib_patches.fpp -A 30Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Look at the full structure - search around where models is declared
sed -n '1,150p' src/simulation/m_ib_patches.fpp | grep -n "type.*t_model" -A 30Repository: MFlowCode/MFC
Length of output: 2158
🏁 Script executed:
# Search for t_model type definition
rg -n "type\s*::\s*t_model\b" src/simulation/m_ib_patches.fpp -A 40Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Search for t_model_array definition
rg -n "type\s*::\s*t_model_array" src/simulation/ -A 10Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Check m_derived_types for these type definitions
rg -n "type\s*::\s*t_model" src/simulation/m_derived_types.fpp -A 20Repository: MFlowCode/MFC
Length of output: 131
🏁 Script executed:
# List Fortran/Fypp files in src/simulation
ls -la src/simulation/*.fpp | head -20Repository: MFlowCode/MFC
Length of output: 1718
🏁 Script executed:
# Search for t_model in all files
rg -n "type.*t_model" src/Repository: MFlowCode/MFC
Length of output: 1795
🏁 Script executed:
# Check what's imported at the top of m_ib_patches.fpp
sed -n '1,50p' src/simulation/m_ib_patches.fppRepository: MFlowCode/MFC
Length of output: 1838
🏁 Script executed:
# Get the t_model and t_model_array type definitions
sed -n '180,193p' src/common/m_derived_types.fppRepository: MFlowCode/MFC
Length of output: 546
🏁 Script executed:
# Check if there's any finalization for models or s_instantiate_STL_models
rg -n "s_instantiate_STL_models" src/simulation/m_ib_patches.fpp -A 30 | head -60Repository: MFlowCode/MFC
Length of output: 3152
🏁 Script executed:
# Check if s_instantiate_STL_models deallocates models or any cleanup happens
rg -n "s_instantiate_STL_models|DEALLOCATE.*models|models.*boundary_v" src/simulation/m_ib_patches.fpp | head -30Repository: MFlowCode/MFC
Length of output: 592
🏁 Script executed:
# Verify the full extent of s_instantiate_STL_models
sed -n '118,250p' src/simulation/m_ib_patches.fpp | tail -100Repository: MFlowCode/MFC
Length of output: 4884
🏁 Script executed:
# Check if models is deallocated anywhere in the entire codebase
rg -n "DEALLOCATE.*models" src/Repository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
# Check if there's any other finalization routine that might clean up models
rg -n "finalize|cleanup" src/simulation/m_ib_patches.fppRepository: MFlowCode/MFC
Length of output: 39
🏁 Script executed:
#!/bin/bash
# Confirm models is not deallocated anywhere
rg -n "DEALLOCATE.*models" src/Repository: MFlowCode/MFC
Length of output: 39
Deallocate models and its components in s_finalize_ibm_module.
models is allocated at line 72 but never deallocated. The finalization routine (lines 1178–1186) omits cleanup of the models array and its allocatable fields (model, boundary_v, interpolated_boundary_v), causing memory leaks on both host and device.
🤖 Prompt for AI Agents
In `@src/simulation/m_ibm.fpp` around lines 72 - 73, s_finalize_ibm_module must
free the allocatable array models and each of its allocatable components to
avoid host/device leaks: in s_finalize_ibm_module iterate over models (if
allocated(models)) and for each element check and deallocate its allocatable
fields model, boundary_v, and interpolated_boundary_v (use ALLOCATED() guards),
then deallocate the models array itself (again guarding with ALLOCATED()) and
handle any device-specific deallocation if these components were moved to the
device; update s_finalize_ibm_module to perform these checks and DEALLOCATE
calls for model, boundary_v, interpolated_boundary_v, then DEALLOCATE(models).
User description
User description
Description
This PR is a significant refactor of the levelset code. It contains
Fixes #1011
Type of change
Scope
If you cannot check the above box, please split your PR into multiple PRs that each have a common goal.
How Has This Been Tested?
This currently passes all tests on MacOS and Ubuntu operating systms using the GNU compiler.
Checklist
docs/)examples/that demonstrate my new feature performing as expected.They run to completion and demonstrate "interesting physics"
./mfc.sh formatbefore committing my codeIf your code changes any code source files (anything in
src/simulation)To make sure the code is performing as expected on GPU devices, I have:
nvtxranges so that they can be identified in profiles./mfc.sh run XXXX --gpu -t simulation --nsys, and have attached the output file (.nsys-rep) and plain text results to this PR./mfc.sh run XXXX --gpu -t simulation --rsys --hip-trace, and have attached the output file and plain text results to this PR.PR Type
Enhancement, Refactor
Description
Removed levelset computation from preprocessing: IB markers and levelset values are no longer computed during pre-processing; all computations now occur during simulation runtime
Eliminated global levelset arrays: Replaced global
levelsetandlevelset_normarrays with local storage in theghost_pointderived type, reducing memory allocation by orders of magnitudeParallelized levelset computation over ghost points: Refactored levelset code to parallelize over ghost points instead of grid cells, improving memory efficiency and GPU performance
GPU-accelerated STL model processing: Implemented GPU compute for STL IB markers and levelset values by loading STL models into global memory with separate parallelized loops
New
m_compute_levelsetmodule: Created dedicated module for all levelset computations with geometry-specific functions (circles, rectangles, ellipses, spheres, cylinders, airfoils, STL models)Added STL model configuration parameters: Extended IB patch parameters to support
model_filepath,model_spc,model_threshold, and transformation parameters (model_translate,model_rotate)Simplified data I/O: Removed levelset and levelset norm from preprocessing output; added dedicated IB data output subroutines for simulation runtime
Updated test infrastructure: Adjusted grid resolution for circle test cases and updated golden test files to reflect removal of pre-computed IB markers
Diagram Walkthrough
File Walkthrough
8 files
m_ib_patches.fpp
Refactor levelset computation from preprocessing to simulation runtimesrc/simulation/m_ib_patches.fpp
m_compute_levelsetmodule dependency and levelset computationcalls from
s_apply_ib_patchess_instantiate_STL_models()subroutine to load and preprocessSTL models during initialization
s_ib_model()to use pre-instantiated models from globalmodelsarray instead of computing levelsets inlineetavariable declarations and replaced with local scopeusage in geometry-specific functions
offsetvariable to airfoil subroutines for cleaner centroidoffset handling
m_data_output.fpp
Remove levelset data output from preprocessingsrc/pre_process/m_data_output.fpp
ib_markers,levelset, andlevelset_normparameters from dataoutput subroutine signatures
levelset norm data
s_write_abstract_data_files()m_ibm.fpp
Refactor IBM module to use ghost-point-based levelsetssrc/simulation/m_ibm.fpp
levelsetandlevelset_normarrays and replaced withghost-point-local storage
s_instantiate_STL_models()during IBM setups_apply_ib_patches()calls to remove levelset parameterss_compute_image_points()to use levelset values fromghost_pointstructure instead of global arrayss_update_mib()to call news_apply_levelset()instead ofcomputing levelsets inline
patch_id_fparray allocationm_start_up.fpp
Remove levelset restart file I/O from simulation startupsrc/simulation/m_start_up.fpp
from restart files
s_write_ib_data_file()after IBM setup duringinitialization
s_read_ic_data_files()call signature by removing IB markerparameter
m_start_up.fpp
Remove levelset handling from preprocessing startupsrc/pre_process/m_start_up.fpp
m_ib_patchesmodule importib_markers,levelset, andlevelset_normvariable declarationsand allocations
s_read_ic_data_files()interface by removing IB markerparameter
m_mpi_common.fpp
Simplify MPI data initialization for IBMsrc/common/m_mpi_common.fpp
levelsetandlevelset_normparameters froms_initialize_mpi_data()subroutinestructures
m_initial_condition.fpp
Remove IBM-related variables from preprocessingsrc/pre_process/m_initial_condition.fpp
m_ib_patchesmodule importib_markers,levelset, andlevelset_normvariable declarationss_apply_ib_patches()call from preprocessingm_time_steppers.fpp
Remove levelset parameters from immersed boundary updatesrc/simulation/m_time_steppers.fpp
s_update_mibsubroutine call by removinglevelsetandlevelset_normparameterssimulation rather than passed as arguments
5 files
m_compute_levelset.fpp
New module for ghost-point-based levelset computationsrc/simulation/m_compute_levelset.fpp
boundaries
s_apply_levelset()subroutine that computes levelsets forghost points instead of grid cells
ellipses, spheres, cylinders, airfoils, and STL models
ghost_pointderived type instead of global arrays
efficiency
m_data_output.fpp
Add dedicated IB data output subroutinessrc/simulation/m_data_output.fpp
s_write_serial_ib_data()ands_write_parallel_ib_data()for IB data outputs_write_ib_data_file()wrapper to handle both serial andparallel I/O
routines
parameters
m_global_parameters.fpp
Add default initialization for IB patch parameterssrc/simulation/m_global_parameters.fpp
patch_ib()array with default values forall IB patch parameters
translate, rotate)
m_derived_types.fpp
Add derived types for model storage and ghost point levelsetssrc/common/m_derived_types.fpp
t_model_arrayderived type to store STL model data withboundary vertices and interpolation information
ghost_pointderived type withlevelsetandlevelset_normfields for local storage
point structure
case_dicts.py
Add STL model and transformation parameters to IB configurationtoolchain/mfc/run/case_dicts.py
model_filepath,model_spc, andmodel_thresholdmodel_translateandmodel_rotatefor each spatial directionmodel_scaleparameter for potential future use7 files
cases.py
Adjust grid resolution for circle test casestoolchain/mfc/test/cases.py
n=49grid resolution for improvedprecision
circular geometries
golden.txt
Remove IB markers from golden test outputtests/7FA04E95/golden.txt
ib_markersoutput line from golden test dataand stored globally
golden.txt
Remove IB markers output from test golden filetests/5600D63B/golden.txt
D/ib_markers.00.datwith all zerovalues
D/cons.5.00.000050.datline with updated valuesgolden-metadata.txt
Update test metadata with new build environment detailstests/7F70E665/golden-metadata.txt
OpenMP : OFFconfiguration linegolden-metadata.txt
Update test metadata with new build environment detailstests/F60D6594/golden-metadata.txt
OpenMP : OFFconfiguration linegolden-metadata.txt
Update test metadata with new build environment detailstests/4F5A5E32/golden-metadata.txt
OpenMP : OFFconfiguration linegolden-metadata.txt
Update test metadata with new build environment detailstests/8D8F6424/golden-metadata.txt
OpenMP : OFFconfiguration line3 files
golden-metadata.txt
Update golden test metadata and build environmenttests/B0CE19C5/golden-metadata.txt
hash
paths, system information)
golden-metadata.txt
Update golden test metadata and build environmenttests/7DCE34B4/golden-metadata.txt
hash
paths, system information)
golden-metadata.txt
Update golden test metadata and build environmenttests/6171E9D4/golden-metadata.txt
hash
paths, system information)
specifications
63 files
Summary by CodeRabbit
New Features
Refactor
Bug Fixes
Tests
CodeAnt-AI Description
Refactor immersed-boundary levelset handling and STL model use so levelsets are computed at runtime per ghost point
What Changed
Impact
✅ Lower memory during simulation for cases with immersed boundaries✅ No precomputed levelset files required at startup or restart✅ Clearer IB restart behavior (IB markers written and read from the unified runtime writers)💡 Usage Guide
Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.
Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.