Skip to content

Levelset refactor#1123

Open
danieljvickers wants to merge 85 commits intoMFlowCode:masterfrom
danieljvickers:levelset-refactor
Open

Levelset refactor#1123
danieljvickers wants to merge 85 commits intoMFlowCode:masterfrom
danieljvickers:levelset-refactor

Conversation

@danieljvickers
Copy link
Member

@danieljvickers danieljvickers commented Feb 4, 2026

User description

Description

This PR is a significant refactor of the levelset code. It contains

  1. The removal of IB markers and levelset values from pre-processing. Now all values are computed in simulation
  2. The deletion of the levelset and levelset norm global arrays. The are now tied to ghost points. The levelset code is now parallelized over ghost points instead of grid cells. This reduces the total amount of memory that needs to be allocated for levelsets by an order of magnitude in simple cases, and by several orders of magnitude when using multiple immersed boundaries.
  3. I HAVE DEFERRED GPU STLs TO A SEPARATE PR FOR SIMPLICITY BECAUSE THE SCOPE HAS SIGNIFICANTLY CREEPED ON THIS FEATURE AND IT IS HINDERING DEBUGGING: GPU compute of STL IB levelst values. This is done by loading STL models into global memory, and they are computed separately for IB markers and levelset values. Then each separate loop was parallelized.
  4. Arbitrary rotation of STL models which is compatible with the same local coordiante system as all other IBs

Fixes #1011

How Has This Been Tested?

This currently passes all tests on MacOS and Ubuntu operating systms using the GNU compiler.

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 levelset and levelset_norm arrays with local storage in the ghost_point derived type, reducing memory allocation by orders of magnitude

  • Parallelized 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_levelset module: 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

flowchart LR
  A["Preprocessing<br/>Input Data"] -->|"No levelset<br/>computation"| B["Simulation<br/>Initialization"]
  B -->|"s_instantiate_STL_models"| C["Load STL Models<br/>to Global Memory"]
  C -->|"GPU compute"| D["Ghost Point<br/>Levelset Storage"]
  D -->|"s_apply_levelset"| E["IBM Updates<br/>During Simulation"]
  E -->|"s_write_ib_data_file"| F["Output IB Data<br/>at Runtime"]
Loading

File Walkthrough

Relevant files
Refactor
8 files
m_ib_patches.fpp
Refactor levelset computation from preprocessing to simulation runtime

src/simulation/m_ib_patches.fpp

  • Removed m_compute_levelset module dependency and levelset computation
    calls from s_apply_ib_patches
  • Added new s_instantiate_STL_models() subroutine to load and preprocess
    STL models during initialization
  • Refactored s_ib_model() to use pre-instantiated models from global
    models array instead of computing levelsets inline
  • Removed local eta variable declarations and replaced with local scope
    usage in geometry-specific functions
  • Added offset variable to airfoil subroutines for cleaner centroid
    offset handling
+165/-234
m_data_output.fpp
Remove levelset data output from preprocessing                     

src/pre_process/m_data_output.fpp

  • Removed ib_markers, levelset, and levelset_norm parameters from data
    output subroutine signatures
  • Removed file I/O operations for writing IB markers, levelset, and
    levelset norm data
  • Simplified abstract interface and implementation for
    s_write_abstract_data_files()
+6/-252 
m_ibm.fpp
Refactor IBM module to use ghost-point-based levelsets     

src/simulation/m_ibm.fpp

  • Removed global levelset and levelset_norm arrays and replaced with
    ghost-point-local storage
  • Added call to s_instantiate_STL_models() during IBM setup
  • Replaced s_apply_ib_patches() calls to remove levelset parameters
  • Changed s_compute_image_points() to use levelset values from
    ghost_point structure instead of global arrays
  • Updated s_update_mib() to call new s_apply_levelset() instead of
    computing levelsets inline
  • Removed patch_id_fp array allocation
+38/-44 
m_start_up.fpp
Remove levelset restart file I/O from simulation startup 

src/simulation/m_start_up.fpp

  • Removed code that reads IB markers, levelset, and levelset norm data
    from restart files
  • Removed airfoil grid data reading from preprocessing
  • Added call to s_write_ib_data_file() after IBM setup during
    initialization
  • Simplified s_read_ic_data_files() call signature by removing IB marker
    parameter
+6/-212 
m_start_up.fpp
Remove levelset handling from preprocessing startup           

src/pre_process/m_start_up.fpp

  • Removed m_ib_patches module import
  • Removed ib_markers, levelset, and levelset_norm variable declarations
    and allocations
  • Removed IB marker reading from preprocessing startup
  • Simplified s_read_ic_data_files() interface by removing IB marker
    parameter
+6/-70   
m_mpi_common.fpp
Simplify MPI data initialization for IBM                                 

src/common/m_mpi_common.fpp

  • Removed levelset and levelset_norm parameters from
    s_initialize_mpi_data() subroutine
  • Removed MPI type creation for levelset and levelset norm data
    structures
  • Removed airfoil grid MPI I/O setup code
  • Simplified MPI data initialization for IB markers only
+2/-66   
m_initial_condition.fpp
Remove IBM-related variables from preprocessing                   

src/pre_process/m_initial_condition.fpp

  • Removed m_ib_patches module import
  • Removed ib_markers, levelset, and levelset_norm variable declarations
  • Removed IB patch allocation and initialization code
  • Removed s_apply_ib_patches() call from preprocessing
+1/-31   
m_time_steppers.fpp
Remove levelset parameters from immersed boundary update 

src/simulation/m_time_steppers.fpp

  • Simplified the s_update_mib subroutine call by removing levelset and
    levelset_norm parameters
  • Reflects the refactoring where levelset values are now computed in
    simulation rather than passed as arguments
+1/-1     
Enhancement
5 files
m_compute_levelset.fpp
New module for ghost-point-based levelset computation       

src/simulation/m_compute_levelset.fpp

  • New module created to handle all levelset computations for immersed
    boundaries
  • Implements s_apply_levelset() subroutine that computes levelsets for
    ghost points instead of grid cells
  • Contains geometry-specific levelset functions for circles, rectangles,
    ellipses, spheres, cylinders, airfoils, and STL models
  • Levelset and normal vector computations now tied to ghost_point
    derived type instead of global arrays
  • Parallelized over ghost points using GPU macros for improved memory
    efficiency
+723/-0 
m_data_output.fpp
Add dedicated IB data output subroutines                                 

src/simulation/m_data_output.fpp

  • Added new subroutines s_write_serial_ib_data() and
    s_write_parallel_ib_data() for IB data output
  • Created s_write_ib_data_file() wrapper to handle both serial and
    parallel I/O
  • Removed levelset and levelset norm output from main data writing
    routines
  • Simplified MPI data initialization calls by removing levelset
    parameters
+96/-21 
m_global_parameters.fpp
Add default initialization for IB patch parameters             

src/simulation/m_global_parameters.fpp

  • Added initialization loop for patch_ib() array with default values for
    all IB patch parameters
  • Sets proper defaults for STL model transformation parameters (scale,
    translate, rotate)
  • Initializes rotation matrices and moving boundary parameters
+41/-0   
m_derived_types.fpp
Add derived types for model storage and ghost point levelsets

src/common/m_derived_types.fpp

  • Added new t_model_array derived type to store STL model data with
    boundary vertices and interpolation information
  • Extended ghost_point derived type with levelset and levelset_norm
    fields for local storage
  • Removed global levelset array dependencies by moving data to ghost
    point structure
+13/-0   
case_dicts.py
Add STL model and transformation parameters to IB configuration

toolchain/mfc/run/case_dicts.py

  • Added three new immersed boundary patch parameters: model_filepath,
    model_spc, and model_threshold
  • Added three new model transformation parameters: model_translate and
    model_rotate for each spatial direction
  • Commented out model_scale parameter for potential future use
+6/-1     
Tests
7 files
cases.py
Adjust grid resolution for circle test cases                         

toolchain/mfc/test/cases.py

  • Updated Circle test case to use n=49 grid resolution for improved
    precision
  • Added comment explaining machine-level precision sensitivity for
    circular geometries
  • Minor formatting adjustment to boundary condition test setup
+6/-2     
golden.txt
Remove IB markers from golden test output                               

tests/7FA04E95/golden.txt

  • Removed the ib_markers output line from golden test data
  • Reflects the refactoring where IB markers are no longer pre-computed
    and stored globally
+1/-2     
golden.txt
Remove IB markers output from test golden file                     

tests/5600D63B/golden.txt

  • Removed the last line containing D/ib_markers.00.dat with all zero
    values
  • Kept the D/cons.5.00.000050.dat line with updated values
  • Test golden file updated to reflect removal of IB markers from output
+1/-2     
golden-metadata.txt
Update test metadata with new build environment details   

tests/7F70E665/golden-metadata.txt

  • Updated timestamp from 2025-01-20 to 2026-02-03
  • Changed Git commit hash and branch information
  • Updated CMake version and compiler information (AppleClang to GNU)
  • Changed system from macOS (Apple M1 Pro) to Linux (Intel i7-12700K)
  • Reordered build configuration sections (post_process moved to top)
  • Added OpenMP : OFF configuration line
  • Expanded CPU information with detailed lscpu output
+84/-41 
golden-metadata.txt
Update test metadata with new build environment details   

tests/F60D6594/golden-metadata.txt

  • Updated timestamp from 2025-01-20 to 2026-02-03
  • Changed Git commit hash and branch information
  • Updated CMake version and compiler information (AppleClang to GNU)
  • Changed system from macOS (Apple M1 Pro) to Linux (Intel i7-12700K)
  • Reordered build configuration sections
  • Added OpenMP : OFF configuration line
  • Expanded CPU information with detailed lscpu output
+84/-41 
golden-metadata.txt
Update test metadata with new build environment details   

tests/4F5A5E32/golden-metadata.txt

  • Updated timestamp from 2025-01-20 to 2026-02-03
  • Changed Git commit hash and branch information
  • Updated CMake version and compiler information (AppleClang to GNU)
  • Changed system from macOS (Apple M1 Pro) to Linux (Intel i7-12700K)
  • Reordered build configuration sections
  • Added OpenMP : OFF configuration line
  • Expanded CPU information with detailed lscpu output
+81/-38 
golden-metadata.txt
Update test metadata with new build environment details   

tests/8D8F6424/golden-metadata.txt

  • Updated timestamp from 2025-01-20 to 2026-02-03
  • Changed Git commit hash and branch information
  • Updated CMake version and compiler information (AppleClang to GNU)
  • Changed system from macOS (Apple M1 Pro) to Linux (Intel i7-12700K)
  • Reordered build configuration sections
  • Added OpenMP : OFF configuration line
  • Expanded CPU information with detailed lscpu output
+81/-38 
Miscellaneous
3 files
golden-metadata.txt
Update golden test metadata and build environment               

tests/B0CE19C5/golden-metadata.txt

  • Updated test metadata with new generation timestamp and git commit
    hash
  • Changed build system configuration details (CMake version, compiler
    paths, system information)
  • Updated CPU information from macOS to Linux system specifications
+93/-102
golden-metadata.txt
Update golden test metadata and build environment               

tests/7DCE34B4/golden-metadata.txt

  • Updated test metadata with new generation timestamp and git commit
    hash
  • Changed build system configuration details (CMake version, compiler
    paths, system information)
  • Updated CPU information from macOS to Linux system specifications
+90/-99 
golden-metadata.txt
Update golden test metadata and build environment               

tests/6171E9D4/golden-metadata.txt

  • Updated test metadata with new generation timestamp and git commit
    hash
  • Changed build system configuration details (CMake version, compiler
    paths, system information)
  • Updated CPU information from macOS M1 Pro to Linux x86_64 system
    specifications
+84/-41 
Additional files
63 files
m_compute_levelset.fpp +0/-625 
m_model.fpp +0/-1     
m_global_parameters.fpp +0/-10   
m_icpp_patches.fpp +0/-6     
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +0/-3     
golden.txt +0/-1     
golden.txt +0/-1     
golden-metadata.txt +78/-35 
golden.txt +10/-11 
golden.txt +0/-1     
golden.txt +1/-2     
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +1/-2     
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +10/-11 
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +10/-11 
golden-metadata.txt +81/-38 
golden.txt +14/-15 
golden-metadata.txt +81/-38 
golden.txt +14/-15 
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +18/-19 
golden.txt +12/-13 
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +18/-19 
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +1/-2     
golden.txt +0/-1     
golden.txt +12/-13 
golden-metadata.txt +81/-38 
golden.txt +10/-11 
golden.txt +1/-2     
golden.txt +1/-2     
golden.txt +0/-1     
golden-metadata.txt +81/-38 
golden.txt +14/-15 
golden.txt +1/-2     
golden.txt +1/-2     
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +0/-1     
golden.txt +14/-15 
golden.txt +1/-2     

Summary by CodeRabbit

  • New Features

    • STL/model-based immersed-boundary support with on-demand model instantiation
    • New dedicated immersed-boundary data export routine
  • Refactor

    • Simplified IB/level‑set interfaces and internal plumbing
    • Switched to a model-driven IB workflow and removed legacy per-point level‑set outputs
  • Bug Fixes

    • Improved circle geometry precision by adjusting default sampling
  • Tests

    • Updated golden outputs to reflect IB/model changes and removed legacy marker data

CodeAnt-AI Description

Compute levelsets at runtime and simplify immersed-boundary I/O

What Changed

  • STL models are loaded once at startup and used to compute levelset and normal values on-demand for ghost points during the simulation instead of storing global levelset arrays from preprocessing.
  • Immersed-boundary markers are generated in simulation, written to restart/output files (including time-step 0), and parallel/serial IB I/O has dedicated routines; pre/post-process no longer read or write large levelset and normal arrays.
  • Levelset and normal values are attached to ghost points and used directly when computing image points and IBM corrections, removing the global levelset and levelset-norm shared arrays.
  • Airfoil/airfoil-grid and STL interpolation are handled during model instantiation; moving IB centroid offsets are computed after IB markers are generated.

Impact

✅ Much lower memory use for simulations with STLs or multiple immersed boundaries
✅ Restart files no longer include bulky levelset arrays (smaller restart size)
✅ IB markers available immediately in simulation outputs (including t=0)

💡 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:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

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:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

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.

danieljvickers and others added 30 commits December 12, 2025 13:48
@codeant-ai
Copy link
Contributor

codeant-ai bot commented Feb 11, 2026

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 ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai bot added size:XXL This PR changes 1000+ lines, ignoring generated files and removed size:XXL This PR changes 1000+ lines, ignoring generated files labels Feb 11, 2026
@codeant-ai
Copy link
Contributor

codeant-ai bot commented Feb 11, 2026

CodeAnt AI Incremental review completed.

@danieljvickers
Copy link
Member Author

Thanks for the response. It isn't the level set. It is either model reading or in marker generation. Because if you manually go into the in_patch file and comment out on marker generation for models, you get this exact but. So for one reason or another, it just never generates a single in marker and therefore never gets to the level set, but that's the most I know for certain.

I am doing models on the CPU because they are the only patch not supported by GPU compute. The model data structure is causing problems. So instead I opted to just do host to device copies on that one case. I'd have thought that would be acceptable, and it is for NVHPC and Cray. But maybe AMD is off. I will go through that more closely in a bit. My current theory is that the random number generator in the runners is broken unless it is seeded in the program and it's causing some spurious behavior. I just pushed that, so if that's the issue then we will see in a day or so.

Thanks for the time.

@danieljvickers I looked through it and there was nothing too obvious, but it seems like I did find one potential bug with MPI and another one that may be causing issues with AMD

First, when you call s_ib_model (STL case) in m_ib_patches.fpp; for some reason you're not calculating ib_markers on GPUs (all other non-STL cases seem to do this). Then, you call the MPI function assuming it's on GPUs already, so this will cause problems in multi-rank STL cases. And finally, there's an update to device instead of update to host for ib_markers, seems like this would almost certainly cause bugs ? Since ib_markers seems to be computed on GPUs before this.

Secondly, I'll say for AMD you might want to look at s_model_levelset in m_compute_levelset.fpp. In particular, the call to f_normals, I've had issues with nested sequential loops inside parallel loops on certain occasions (particularly with a while loop); and it seems like this is done only for the STL cases. You might try inlining these function calls, although I can't guarantee this will work

1 similar comment
@danieljvickers
Copy link
Member Author

Thanks for the response. It isn't the level set. It is either model reading or in marker generation. Because if you manually go into the in_patch file and comment out on marker generation for models, you get this exact but. So for one reason or another, it just never generates a single in marker and therefore never gets to the level set, but that's the most I know for certain.

I am doing models on the CPU because they are the only patch not supported by GPU compute. The model data structure is causing problems. So instead I opted to just do host to device copies on that one case. I'd have thought that would be acceptable, and it is for NVHPC and Cray. But maybe AMD is off. I will go through that more closely in a bit. My current theory is that the random number generator in the runners is broken unless it is seeded in the program and it's causing some spurious behavior. I just pushed that, so if that's the issue then we will see in a day or so.

Thanks for the time.

@danieljvickers I looked through it and there was nothing too obvious, but it seems like I did find one potential bug with MPI and another one that may be causing issues with AMD

First, when you call s_ib_model (STL case) in m_ib_patches.fpp; for some reason you're not calculating ib_markers on GPUs (all other non-STL cases seem to do this). Then, you call the MPI function assuming it's on GPUs already, so this will cause problems in multi-rank STL cases. And finally, there's an update to device instead of update to host for ib_markers, seems like this would almost certainly cause bugs ? Since ib_markers seems to be computed on GPUs before this.

Secondly, I'll say for AMD you might want to look at s_model_levelset in m_compute_levelset.fpp. In particular, the call to f_normals, I've had issues with nested sequential loops inside parallel loops on certain occasions (particularly with a while loop); and it seems like this is done only for the STL cases. You might try inlining these function calls, although I can't guarantee this will work

@anandrdbz
Copy link
Contributor

anandrdbz commented Feb 11, 2026

@danieljvickers that should be fine but your order of operations seems to be incorrect, you should do an update to device only for the STL cases immediately after s_apply_ib_patches (or s_ib_model). Then it should complete the MPI transfer using populate_ib_buffers, and then finally the update to device on line 113 in m_ibm.fpp should be changed to update to host since you already have the correct values on device.

There could be potential implicit copies to host in NVHPC/CRAY that are preventing this from causing problems but I would def fix this and try again

@danieljvickers
Copy link
Member Author

@anandrdbz I do not believe this order is incorrect. The subroutine s_apply_ib_patches takes a copy of the host-side in markets array, which is initialized to be all zero on the line before. It performs a copy of this array inside the subroutine for the kernel, which makes all of the IB markers on the CPU after execution. I then send over MPI to fill the halo regions. Up to this point, some compute was done on the GPU, but all results are on the host. That is why a device update is required then.

I know this seems odd, but I was forced to do it this way because the subroutine was previously split between preprocess and sim. I plan to correct this after this refactor goes through. Also the model subroutine is CPU based anyways. So all of the compute is CPU and the device update is just an afterthought that doesn't matter until level sets are computed. Please telle of this doesn't make sense, because I'm perhaps missing something.

@anandrdbz
Copy link
Contributor

@danieljvickers but MPI transfer function assumes it's on GPUs and not CPUs ? I did miss the copy function (thought it was only copyin first) , so it seems like after apply_ib_pathes every case should have the correct value on host, but the STL cases won't have it on device. So the update to device should be done before the call to MPI I believe. This still won't cause problems in the test suite since there is no multirank STL case, but I think this is the right order.

Check Line 329 in m_mpi_proxy.fpp in subroutine s_mpi_sendrecv_ib_buffers, it assumes everything's on the GPU (which should be fine in general but not for STL)

Regarding this particular case though seems like the failure is random if you can't reproduce it.

@danieljvickers
Copy link
Member Author

@anandrdbz that may be possible. I will investigate when I return

Either way, as you said that is not the source of error. The problem here is that when only the AMD test runs it fails on the runner and not locally. And the specific error looks like no IB markers get made in the center of the domain on one rank. I am curious if there is something special about the AMD runner environment or something

@anandrdbz
Copy link
Contributor

anandrdbz commented Feb 11, 2026

@danieljvickers I was able to reproduce the error on frontier on an interactive node (I reran it 3 times to make sure it's not random)
I used . ./mfc.sh load -c famd -m g followed by ./mfc.sh test -o 0A362971 -j 8 --gpu mp -- -c frontier_amd

Make sure you've deleted the build directory and build from scratch using these commands

@danieljvickers
Copy link
Member Author

Oh that's interesting. You can see earlier that I was able to run without changes... But I did miss the --comoyer flag. Maybe that has done it. I'll try in a bit

@danieljvickers
Copy link
Member Author

Oh that's interesting. You can see earlier that I was able to run without changes... But I did miss the --computer flag. Maybe that has done it. I'll try in a bit

@sbryngelson
Copy link
Member

Frontier has been sporadically in maintenance yesterday and today. Sometimes allowing login, others not. Could be part of it.

@danieljvickers
Copy link
Member Author

Yeah I will take a look at it tonight on frontier.

@anandrdbz I don't think your suggestion is relevant to this bug, but I think it is relevant to a different bug I've been working on today that has been in the code for a while.

We will have to wait fore to see

@codeant-ai
Copy link
Contributor

codeant-ai bot commented Feb 12, 2026

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 ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai bot added size:XXL This PR changes 1000+ lines, ignoring generated files and removed size:XXL This PR changes 1000+ lines, ignoring generated files labels Feb 12, 2026
@:DEALLOCATE(ib_markers%sf)
@:DEALLOCATE(levelset%sf)
@:DEALLOCATE(levelset_norm%sf)
if (allocated(airfoil_grid_u)) then
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: Add a deallocation of models in the finalize routine to match its allocation and prevent a memory leak. [custom_rule]

Severity Level: Minor ⚠️

Suggested change
if (allocated(airfoil_grid_u)) then
@:DEALLOCATE(models)
Why it matters? ⭐

The PR introduces an allocation of models in s_initialize_ibm_module but the finalize routine does not free it — this matches a documented PR-pattern trigger ("PR adds @:ALLOCATE calls without matching @:DEALLOCATE in the corresponding finalization subroutine"). Adding @:DEALLOCATE(models) directly addresses that rule and prevents a memory leak. The proposed change is minimal and aligns with existing deallocation patterns in the file.

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

**Path:** src/simulation/m_ibm.fpp
**Line:** 1183:1183
**Comment:**
	*Custom Rule: Add a deallocation of `models` in the finalize routine to match its allocation and prevent a memory leak.

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 patch_id = 1, num_ibs
if (patch_ib(patch_id)%geometry == 5 .or. patch_ib(patch_id)%geometry == 12) then
allocate (models(patch_id)%model)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: The allocatable module array holding STL models is never allocated before individual elements are accessed and their model component is allocated, which will cause a runtime error when models(patch_id) is referenced if models itself has not been allocated; you should allocate models to size num_ibs before entering the loop. [possible bug]

Severity Level: Critical 🚨
- ❌ STL immersed-boundary cases crash during model instantiation.
- ❌ All geometry 5/12 patches unusable in simulations.
- ⚠️ Blocks testing of new STL refactor functionality.
Suggested change
allocate (models(patch_id)%model)
if (.not. allocated(models)) then
allocate(models(1:num_ibs))
end if
Steps of Reproduction ✅
1. Configure an immersed boundary patch with STL geometry by setting
`patch_ib(patch_id)%geometry` to 5 (2D STL) or 12 (3D STL); these values are checked in
`s_instantiate_STL_models` at `src/simulation/m_ib_patches.fpp:145-146`.

2. Start a simulation that calls `s_instantiate_STL_models()` (defined at
`src/simulation/m_ib_patches.fpp:120`) during initialization before any other code
allocates the module array `models(:)` (declared at
`src/simulation/m_ib_patches.fpp:58-60`).

3. Execution reaches `allocate (models(patch_id)%model)` at
`src/simulation/m_ib_patches.fpp:147`, which indexes `models(patch_id)` even though the
allocatable array `models(:)` itself has never been allocated.

4. At this point the Fortran runtime attempts to access an unallocated allocatable array,
leading to a runtime error (typically segmentation fault or "allocatable array is not
allocated") and preventing any STL-based immersed boundary setup from running.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/simulation/m_ib_patches.fpp
**Line:** 145:145
**Comment:**
	*Possible Bug: The allocatable module array holding STL models is never allocated before individual elements are accessed and their `model` component is allocated, which will cause a runtime error when `models(patch_id)` is referenced if `models` itself has not been allocated; you should allocate `models` to size `num_ibs` before entering the loop.

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.
👍 | 👎

Comment on lines 320 to 323
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))

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: In the 2D airfoil routine the airfoil grid arrays are only allocated once and never resized, but Np is recomputed per patch and used in indexing and loop bounds; if a later airfoil patch needs a larger Np than the first, the code will index past the end of airfoil_grid_u/l, causing out-of-bounds access and incorrect geometry, so the arrays should be reallocated when their required size changes. [possible bug]

Severity Level: Critical 🚨
- ❌ Multi-airfoil 2D setups can crash or corrupt memory.
- ❌ Incorrect immersed-boundary markers for later 2D airfoils.
- ⚠️ Results unreliable for complex 2D airfoil configurations.
Suggested change
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))
if (.not. allocated(airfoil_grid_u) .or. size(airfoil_grid_u) /= Np) then
if (allocated(airfoil_grid_u)) then
deallocate(airfoil_grid_u)
end if
if (allocated(airfoil_grid_l)) then
deallocate(airfoil_grid_l)
end if
Steps of Reproduction ✅
1. Define at least two 2D immersed-boundary patches with airfoil geometry by setting
`patch_ib(i)%geometry == 4` for multiple `i` in the case configuration; these are iterated
in `s_apply_ib_patches` at `src/simulation/m_ib_patches.fpp:98-110`, which calls
`s_ib_airfoil` for each such patch.

2. Choose patch parameters so that `Np = Np1+Np2+1` (computed from `ca_in`, `pa`, and
`dx(0)` at `src/simulation/m_ib_patches.fpp:315-317`) for the second airfoil patch is
larger than for the first (e.g. larger chord `c` or different `p`).

3. Run the simulation so that `s_ib_airfoil` (defined at
`src/simulation/m_ib_patches.fpp:292-437`) is invoked first for patch 1:
`airfoil_grid_u/l` are unallocated, so the block at `315-380` allocates them with size
`1:Np_first` and fills them with airfoil coordinates.

4. When `s_ib_airfoil` is called for patch 2, `Np` is recomputed (now `Np_second >
Np_first`), but `airfoil_grid_u` and `airfoil_grid_l` are already allocated, so the
allocation block at `315-380` is skipped; later in the marker loop at
`src/simulation/m_ib_patches.fpp:379-435`, the search `do while (airfoil_grid_u(k)%x <
xy_local(1) .and. k <= Np)` can increment `k` up to `Np_second`, causing out-of-bounds
access on arrays of size `Np_first` and leading to memory corruption or a runtime crash
with incorrect IB marker placement.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/simulation/m_ib_patches.fpp
**Line:** 320:323
**Comment:**
	*Possible Bug: In the 2D airfoil routine the airfoil grid arrays are only allocated once and never resized, but `Np` is recomputed per patch and used in indexing and loop bounds; if a later airfoil patch needs a larger `Np` than the first, the code will index past the end of `airfoil_grid_u/l`, causing out-of-bounds access and incorrect geometry, so the arrays should be reallocated when their required size changes.

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
Copy link
Contributor

codeant-ai bot commented Feb 12, 2026

CodeAnt AI Incremental review completed.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue 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_ibm.fpp">

<violation number="1" location="src/simulation/m_ibm.fpp:114">
P2: patch_ib is modified after its device copy is updated, so subsequent GPU levelset kernels will see stale centroid/offset data. Add a device update after s_compute_centroid_offset to keep GPU state consistent.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment on lines +114 to 116
if (patch_ib(i)%moving_ibm /= 0) call s_compute_centroid_offset(i) ! offsets are computed after IB markers are generated
end do

Copy link
Contributor

Choose a reason for hiding this comment

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

P2: patch_ib is modified after its device copy is updated, so subsequent GPU levelset kernels will see stale centroid/offset data. Add a device update after s_compute_centroid_offset to keep GPU state consistent.

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 114:

<comment>patch_ib is modified after its device copy is updated, so subsequent GPU levelset kernels will see stale centroid/offset data. Add a device update after s_compute_centroid_offset to keep GPU state consistent.</comment>

<file context>
@@ -109,8 +107,12 @@ contains
+        call s_populate_ib_buffers()
+        $:GPU_UPDATE(host='[ib_markers%sf]')
+        do i = 1, num_ibs
+            if (patch_ib(i)%moving_ibm /= 0) call s_compute_centroid_offset(i) ! offsets are computed after IB markers are generated
+        end do
 
</file context>
Suggested change
if (patch_ib(i)%moving_ibm /= 0) call s_compute_centroid_offset(i) ! offsets are computed after IB markers are generated
end do
do i = 1, num_ibs
if (patch_ib(i)%moving_ibm /= 0) call s_compute_centroid_offset(i) ! offsets are computed after IB markers are generated
end do
$:GPU_UPDATE(device='[patch_ib(1:num_ibs)]')

@danieljvickers
Copy link
Member Author

Would I be able to get approval for benchmark on this?

@danieljvickers
Copy link
Member Author

I don't see the benchmarks running and I also don't see it listed as a check to perform anymore. Was there something that modifies when benchmark is run in the toolchain rework?

Copy link
Member

@sbryngelson sbryngelson left a comment

Choose a reason for hiding this comment

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

Triggering benchmark CI

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Review effort 4/5 size:XXL This PR changes 1000+ lines, ignoring generated files

Development

Successfully merging this pull request may close these issues.

Remove Levelset and Levelset Norm Arrays

3 participants