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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/documentation/case.md
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ To restart the simulation from $k$-th time step, see @ref running "Restarting Ca
| `alpha_wrt(i)` | Logical | Add the volume fraction of fluid $i$ to the database |
| `gamma_wrt` | Logical | Add the specific heat ratio function to the database |
| `heat_ratio_wrt` | Logical | Add the specific heat ratio to the database |
| `ib_force_wrt` | Logical | Write IB force & torque datafile at each time step |
| `pi_inf_wrt` | Logical | Add the liquid stiffness function to the database |
| `pres_inf_wrt` | Logical | Add the liquid stiffness to the formatted database |
| `c_wrt` | Logical | Add the sound speed to the database |
Expand Down Expand Up @@ -675,6 +676,8 @@ If `file_per_process` is true, then pre_process, simulation, and post_process mu

- `probe_wrt` activates the output of state variables at coordinates specified by `probe(i)%[x;y,z]`.

- `ib_force_wrt` activates the output of data specified by patch_ib(i)%force(:) and patch_ib(i)%torque(:) into a single binary datafile for all IBs at all timesteps. During post_processing, this file is converted into separate force/torque time histories for each IB.

- `output_partial_domain` activates the output of part of the domain specified by `[x,y,z]_output%%beg` and `[x,y,z]_output%%end`.
This is useful for large domains where only a portion of the domain is of interest.
It is not supported when `precision = 1` and `format = 1`.
Expand Down
53 changes: 53 additions & 0 deletions src/post_process/m_data_output.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module m_data_output
s_write_variable_to_formatted_database_file, &
s_write_lag_bubbles_results_to_text, &
s_write_lag_bubbles_to_formatted_database_file, &
s_write_ib_force_files, &
s_write_intf_data_file, &
s_write_energy_data_file, &
s_close_formatted_database_file, &
Expand Down Expand Up @@ -1499,6 +1500,58 @@ contains

end subroutine s_write_lag_variable_to_formatted_database_file

impure subroutine s_write_ib_force_files()

character(len=len_trim(case_dir) + 4*name_len) :: in_file, out_file, file_loc
integer :: iu_in, ios, i, rec_step, rec_id
integer, allocatable, dimension(:) :: iu_out
real(wp) :: rec_time
real(wp), dimension(3) :: rec_force, rec_torque

Comment on lines +1503 to +1510
Copy link
Contributor

Choose a reason for hiding this comment

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

Action required

2. s_write_ib_force_files indentation 📘 Rule violation ✓ Correctness

The newly added post-processing routine is not formatted with the required 2-space indentation. This
violates the mandated Fortran style conventions and reduces consistency across modules.
Agent Prompt
## Issue description
The newly added `s_write_ib_force_files` routine does not follow the required 2-space indentation convention.

## Issue Context
Project style requires 2-space indentation for new/modified Fortran code.

## Fix Focus Areas
- src/post_process/m_data_output.fpp[1503-1553]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

file_loc = trim(case_dir)//'/D'

in_file = trim(file_loc)//'/ib_force.dat'
open (newunit=iu_in, file=trim(in_file), form='unformatted', access='stream', &
status='old', action='read', iostat=ios)
if (ios /= 0) then
print *, 'ERROR: cannot open input file: ', trim(in_file), ' iostat=', ios
return
Comment on lines +1514 to +1518
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fail on bad force-file input.

ios /= 0 here treats corruption as EOF, and the rec_id guard silently drops records that do not match the configured num_ibs. The open-failure paths above also just return. That can leave incomplete per-IB histories while post-process still exits successfully. Only exit on ios < 0; abort on positive ios or an out-of-range ID.

🛠️ Suggested hard-fail read loop
         do
             read (iu_in, iostat=ios) rec_step, rec_time, rec_id, rec_force, rec_torque
-            if (ios /= 0) exit  ! EOF (<0) or read error (>0)
-            if (rec_id >= 1 .and. rec_id <= num_ibs) then
-                write (iu_out(rec_id), '(7(ES24.16E3,1X))') rec_time, &
-                    rec_force(1), rec_force(2), rec_force(3), &
-                    rec_torque(1), rec_torque(2), rec_torque(3)
-            end if
+            if (ios < 0) exit
+            if (ios > 0) call s_mpi_abort('Failed while reading '//trim(in_file)//'.')
+            if (rec_id < 1 .or. rec_id > num_ibs) then
+                call s_mpi_abort('IB force record id is out of range for current num_ibs.')
+            end if
+            write (iu_out(rec_id), '(7(ES24.16E3,1X))') rec_time, &
+                rec_force(1), rec_force(2), rec_force(3), &
+                rec_torque(1), rec_torque(2), rec_torque(3)
         end do

Also applies to: 1524-1532, 1537-1544

end if
Comment on lines +1514 to +1519
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

On failure to open the input/output files this routine print *s an error and returns. Elsewhere in post_process, missing/invalid required files typically trigger s_mpi_abort, which avoids silent partial outputs and keeps all ranks in a consistent state. Consider using s_mpi_abort here as well (or otherwise propagate a failure status to the caller).

Copilot uses AI. Check for mistakes.

allocate (iu_out(num_ibs))
do i = 1, num_ibs
write (out_file, '(A,I0,A)') trim(file_loc)//'/ib_', i, '.txt'
Comment on lines +1521 to +1523
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

allocate(iu_out(num_ibs)) will crash if ib_force_wrt is enabled but num_ibs is unset/invalid (it defaults to dflt_int) or if ib is false. Add a guard (e.g., require ib and num_ibs > 0) before allocating/looping, and fail fast with a clear error if ib_force_wrt is inconsistent with the IB configuration.

Copilot uses AI. Check for mistakes.
open (newunit=iu_out(i), file=trim(out_file), form='formatted', status='replace', action='write', iostat=ios)
Comment on lines +1523 to +1524
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

This is whitespace-delimited text, not CSV.

The new files use .txt, a space-separated header, and whitespace-separated rows. If this feature is supposed to be the CSV conversion step described in the PR, downstream tooling will not parse it as advertised.

Based on learnings: Flag modifications to public subroutine signatures, parameter defaults, or output formats - they affect downstream users.

Also applies to: 1534-1543

if (ios /= 0) then
print *, 'ERROR: cannot open output file: ', trim(out_file), ' iostat=', ios
close (iu_in)
do rec_id = 1, i - 1
close (iu_out(rec_id))
end do
deallocate (iu_out)
return
end if
write (iu_out(i), '(A)') 'mytime fx fy fz Tau_x Tau_y Tau_z'
end do
Comment on lines +1523 to +1535
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

The PR description mentions conversion to CSV, but the post-processing output is written as space-separated .txt files with a non-CSV header. Either emit actual CSVs (comma-separated and preferably .csv extension) or update the PR description/docs to match the actual output format.

Copilot uses AI. Check for mistakes.

do
read (iu_in, iostat=ios) rec_step, rec_time, rec_id, rec_force, rec_torque
if (ios /= 0) exit ! EOF (<0) or read error (>0)
if (rec_id >= 1 .and. rec_id <= num_ibs) then
write (iu_out(rec_id), '(7(ES24.16E3,1X))') rec_time, &
rec_force(1), rec_force(2), rec_force(3), &
rec_torque(1), rec_torque(2), rec_torque(3)
end if
end do

close (iu_in)
do i = 1, num_ibs
close (iu_out(i))
end do
deallocate (iu_out)

end subroutine s_write_ib_force_files

!> @brief Extract the volume-fraction interface contour from primitive fields and write the coordinates to the interface data file.
impure subroutine s_write_intf_data_file(q_prim_vf)

Expand Down
2 changes: 2 additions & 0 deletions src/post_process/m_global_parameters.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ module m_global_parameters
logical :: schlieren_wrt
logical :: cf_wrt
logical :: ib
logical :: ib_force_wrt
logical :: chem_wrt_Y(1:num_species)
logical :: chem_wrt_T
logical :: lag_header
Expand Down Expand Up @@ -494,6 +495,7 @@ contains
sim_data = .false.
cf_wrt = .false.
ib = .false.
ib_force_wrt = .false.
lag_txt_wrt = .false.
lag_header = .true.
lag_db_wrt = .false.
Expand Down
2 changes: 1 addition & 1 deletion src/post_process/m_mpi_proxy.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ contains
& 'adv_n', 'ib', 'cfl_adap_dt', 'cfl_const_dt', 'cfl_dt', &
& 'surface_tension', 'hyperelasticity', 'bubbles_lagrange', &
& 'output_partial_domain', 'relativity', 'cont_damage', 'bc_io', &
& 'down_sample','fft_wrt', 'hyper_cleaning' ]
& 'down_sample','fft_wrt', 'hyper_cleaning', 'ib_force_wrt']
call MPI_BCAST(${VAR}$, 1, MPI_LOGICAL, 0, MPI_COMM_WORLD, ierr)
#:endfor

Expand Down
2 changes: 1 addition & 1 deletion src/post_process/m_start_up.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ contains
lag_rad_wrt, lag_rvel_wrt, lag_r0_wrt, lag_rmax_wrt, &
lag_rmin_wrt, lag_dphidt_wrt, lag_pres_wrt, lag_mv_wrt, &
lag_mg_wrt, lag_betaT_wrt, lag_betaC_wrt, &
alpha_rho_e_wrt
alpha_rho_e_wrt, ib_force_wrt

! Inquiring the status of the post_process.inp file
file_loc = 'post_process.inp'
Expand Down
4 changes: 4 additions & 0 deletions src/post_process/p_main.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ program p_main
end do
! END: Time-Marching Loop

if (proc_rank == 0 .and. ib_force_wrt) then
call s_write_ib_force_files()
end if
Comment on lines +96 to +98
Copy link
Contributor

Choose a reason for hiding this comment

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

Action required

5. Post_process can crash 🐞 Bug ⛯ Reliability

post_process calls s_write_ib_force_files() whenever ib_force_wrt is true, even if ib is false and
num_ibs remains at its sentinel default. This can lead to invalid allocation sizes
(allocate(iu_out(num_ibs))) and crashes for misconfigured inputs.
Agent Prompt
### Issue description
`post_process/p_main.fpp` calls the IB force conversion whenever `ib_force_wrt` is enabled, but the conversion allocates arrays based on `num_ibs`, which can be left at a sentinel value if `ib` is false or inputs are inconsistent.

### Issue Context
Current behavior can crash on `allocate(iu_out(num_ibs))`.

### Fix Focus Areas
- src/post_process/p_main.fpp[96-98]
- src/post_process/m_data_output.fpp[1503-1524]
- src/post_process/m_global_parameters.fpp[497-499]

### Suggested approach
- Change the call site to:
  - `if (proc_rank == 0 .and. ib .and. ib_force_wrt) call s_write_ib_force_files()`
- Inside `s_write_ib_force_files`, add a defensive check:
  - if `num_ibs <= 0` then print a clear error and return (or abort).
- Optionally also check the existence of the input file before allocating output units.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


close (11)

call s_finalize_modules()
Expand Down
44 changes: 40 additions & 4 deletions src/simulation/m_data_output.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,20 @@ module m_data_output
s_open_run_time_information_file, &
s_open_com_files, &
s_open_probe_files, &
s_open_ib_force_file, &
s_write_run_time_information, &
s_write_data_files, &
s_write_serial_data_files, &
s_write_parallel_data_files, &
s_write_ib_data_file, &
s_write_com_files, &
s_write_probe_files, &
s_write_ib_force_file, &
s_close_run_time_information_file, &
s_close_com_files, &
s_close_probe_files, &
s_finalize_data_output_module, &
s_write_ib_data_file
s_close_ib_force_file, &
s_finalize_data_output_module

real(wp), allocatable, dimension(:, :, :) :: icfl_sf !< ICFL stability criterion
real(wp), allocatable, dimension(:, :, :) :: vcfl_sf !< VCFL stability criterion
Expand Down Expand Up @@ -254,6 +257,18 @@ contains

end subroutine s_open_probe_files

impure subroutine s_open_ib_force_file
character(LEN=path_len + 2*name_len) :: file_loc

write (file_loc, '(A)') 'ib_force.dat'
file_loc = trim(case_dir)//'/D/'//trim(file_loc)
open (92, FILE=trim(file_loc), &
FORM='unformatted', &
ACCESS='stream', &
STATUS='replace', &
POSITION='append')
Comment on lines +262 to +269
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

The IB force output uses a hard-coded unit number (92) for open/write/close. This can collide with other units already used in this module (e.g., integrals use i+70, so num_integrals>=22 will also use unit 92), leading to failed opens or writing/closing the wrong file. Use open(newunit=...) and store the resulting unit in a module variable used by the write/close routines (and consider checking iostat on open).

Suggested change
write (file_loc, '(A)') 'ib_force.dat'
file_loc = trim(case_dir)//'/D/'//trim(file_loc)
open (92, FILE=trim(file_loc), &
FORM='unformatted', &
ACCESS='stream', &
STATUS='replace', &
POSITION='append')
integer, save :: ib_force_unit = -1
integer :: istat
write (file_loc, '(A)') 'ib_force.dat'
file_loc = trim(case_dir)//'/D/'//trim(file_loc)
open (newunit=ib_force_unit, FILE=trim(file_loc), &
FORM='unformatted', &
ACCESS='stream', &
STATUS='replace', &
POSITION='append', &
iostat=istat)
if (istat /= 0) then
write (*,*) 'Error opening IB force file: ', trim(file_loc), ' iostat = ', istat
end if

Copilot uses AI. Check for mistakes.
Comment on lines +260 to +269
Copy link
Contributor

Choose a reason for hiding this comment

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

Action required

1. Uppercase keywords in open 📘 Rule violation ✓ Correctness

New Fortran code uses uppercase keywords (e.g., LEN, FILE, FORM, ACCESS, STATUS,
POSITION) and non-2-space indentation, violating the required style conventions. This reduces
consistency and can hinder maintainability across the codebase.
Agent Prompt
## Issue description
New Fortran code violates the required style conventions by using uppercase keywords (e.g., `LEN=`, `FILE=`, `FORM=`) and non-2-space indentation.

## Issue Context
Project style requires 2-space indentation and lowercase Fortran keywords for new/modified code.

## Fix Focus Areas
- src/simulation/m_data_output.fpp[260-269]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

end subroutine s_open_ib_force_file

!> The goal of the procedure is to output to the run-time
!! information file the stability criteria extrema in the
!! entire computational domain and at the given time-step.
Expand Down Expand Up @@ -1071,7 +1086,7 @@ contains
write (t_step_dir, '(A,I0,A,I0)') trim(case_dir)//'/p_all'
write (t_step_dir, '(a,i0,a,i0)') trim(case_dir)//'/p_all/p', &
proc_rank, '/', time_step
write (file_path, '(A,I0,A)') trim(t_step_dir)//'/ib.dat'
write (file_path, '(A,I0,A)') trim(t_step_dir)//'/ib_data.dat'
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

s_write_serial_ib_data now writes ib_data.dat, but the post-process reader still looks for ib.dat (see src/post_process/m_data_input.f90). This will break post-processing (and possibly restarts) for serial I/O IB marker data. Either keep the original filename, or update the post-process input logic to accept the new name (ideally supporting both for backward compatibility).

Suggested change
write (file_path, '(A,I0,A)') trim(t_step_dir)//'/ib_data.dat'
write (file_path, '(A,I0,A)') trim(t_step_dir)//'/ib.dat'

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find all references to ib_data.dat and ib.dat in the repo
echo "=== All references to ib_data.dat and ib.dat ==="
rg -n --no-heading -C2 '\bib(?:_data)?\.dat\b'

Repository: MFlowCode/MFC

Length of output: 2384


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Examine the file around line 1089 and find the parallel I/O path
echo "=== Context around line 1089 in m_data_output.fpp ==="
sed -n '1080,1100p' src/simulation/m_data_output.fpp

echo -e "\n=== Search for parallel IB output (ib.dat) ==="
rg -n 'ib\.dat' src/simulation/m_data_output.fpp -C3

Repository: MFlowCode/MFC

Length of output: 2082


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if there's conditional logic for serial vs parallel IB output
echo "=== Search for s_write_serial_ib_data and related routines ==="
rg -n 's_write.*ib' src/simulation/m_data_output.fpp -A5

Repository: MFlowCode/MFC

Length of output: 2628


Serial IB output filename breaks post-process reader compatibility.

s_write_serial_ib_data() writes ib_data.dat (line 1089), but src/post_process/m_data_input.f90 reads ib.dat for serial mode (lines 166–167). This mismatch will cause post-process to fail with "file not found" for IB data in serial-I/O mode. Either revert the serial filename to ib.dat to match the reader, or update the post-process reader to expect ib_data.dat in serial mode.


open (2, FILE=trim(file_path), &
FORM='unformatted', &
Comment on lines 1086 to 1092
Copy link
Contributor

Choose a reason for hiding this comment

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

Action required

3. Ib marker file rename breaks post_process 🐞 Bug ✓ Correctness

Simulation serial IB marker output was renamed to ib_data.dat, but post_process still looks for
ib.dat and aborts if it’s missing. This will break post-processing for cases that rely on the
per-timestep serial IB marker files.
Agent Prompt
### Issue description
Simulation now writes per-timestep serial IB marker files as `ib_data.dat`, but post-processing still requires `ib.dat` and aborts if not found.

### Issue Context
This breaks post-processing for IB cases in the serial-per-timestep path (`p_all/p<rank>/<tstep>/...`).

### Fix Focus Areas
- src/post_process/m_data_input.f90[164-208]
- src/simulation/m_data_output.fpp[1085-1093]

### Suggested approach
- In `s_read_ib_data_files`, when `parallel_io` is false, first try `ib_data.dat`; if missing, fall back to `ib.dat` (or switch fully to `ib_data.dat` if backward compatibility isn’t needed).
- Keep the `parallel_io` branch reading `restart_data/.../ib.dat` unchanged unless you also renamed that output.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Expand Down Expand Up @@ -1136,7 +1151,22 @@ contains
call s_write_serial_ib_data(time_step)
end if

end subroutine
end subroutine s_write_ib_data_file

!> @brief Writes IB force/torque records to D/ib_force.dat on rank 0.
impure subroutine s_write_ib_force_file(time_step)

integer, intent(in) :: time_step
if (proc_rank == 0) then
block
integer :: i
do i = 1, num_ibs
write (92) time_step, mytime, i, patch_ib(i)%force, patch_ib(i)%torque
end do
end block
end if

end subroutine s_write_ib_force_file

!> This writes a formatted data file where the root processor
!! can write out the CoM information
Expand Down Expand Up @@ -1907,6 +1937,12 @@ contains

end subroutine s_close_probe_files

impure subroutine s_close_ib_force_file

close (92)

end subroutine s_close_ib_force_file

!> The computation of parameters, the allocation of memory,
!! the association of pointers and/or the execution of any
!! other procedures that are necessary to setup the module.
Expand Down
2 changes: 2 additions & 0 deletions src/simulation/m_global_parameters.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ module m_global_parameters
!> @{
logical :: ib
integer :: num_ibs
logical :: ib_force_wrt

type(ib_patch_parameters), dimension(num_patches_max) :: patch_ib
type(vec3_dt), allocatable, dimension(:) :: airfoil_grid_u, airfoil_grid_l
Expand Down Expand Up @@ -715,6 +716,7 @@ contains
! Immersed Boundaries
ib = .false.
num_ibs = dflt_int
ib_force_wrt = .false.

! Bubble modeling
bubbles_euler = .false.
Expand Down
2 changes: 1 addition & 1 deletion src/simulation/m_mpi_proxy.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ contains
& 'cfl_adap_dt', 'cfl_const_dt', 'cfl_dt', 'surface_tension', &
& 'shear_stress', 'bulk_stress', 'bubbles_lagrange', &
& 'hyperelasticity', 'down_sample', 'int_comp','fft_wrt', &
& 'hyper_cleaning' ]
& 'hyper_cleaning', 'ib_force_wrt']
call MPI_BCAST(${VAR}$, 1, MPI_LOGICAL, 0, MPI_COMM_WORLD, ierr)
#:endfor

Expand Down
1 change: 1 addition & 0 deletions src/simulation/m_start_up.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ contains
x_domain, y_domain, z_domain, &
hypoelasticity, &
ib, num_ibs, patch_ib, &
ib_force_wrt, &
fluid_pp, bub_pp, probe_wrt, prim_vars_wrt, &
Comment on lines 146 to 148
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Reject ib_force_wrt unless IB is enabled.

s_initialize_modules() only initializes IBM under if (ib), but the force-output path in src/simulation/m_time_steppers.fpp:467-469 and src/simulation/m_time_steppers.fpp:637-641 is gated by ib_force_wrt alone. This new namelist entry therefore exposes a configuration that can reach IB force routines with uninitialized IB state.

🛡️ Suggested validation hook
     call s_check_inputs_common()
     call s_check_inputs()
+    if (ib_force_wrt .and. .not. ib) then
+        call s_mpi_abort('ib_force_wrt requires ib = T. Exiting.')
+    end if

fd_order, probe, num_probes, t_step_old, &
alt_soundspeed, mixture_err, weno_Re_flux, &
Expand Down
18 changes: 18 additions & 0 deletions src/simulation/m_time_steppers.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,11 @@ contains
call s_open_run_time_information_file()
end if

! Opening and writing the header of the ib data file
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

This comment says the routine is "opening and writing the header" of the IB force file, but s_open_ib_force_file only opens the stream and doesn't write any header record. Please either write an actual header (and update the post-process reader accordingly) or adjust the comment to avoid misleading future maintainers.

Suggested change
! Opening and writing the header of the ib data file
! Opening the IB force data file

Copilot uses AI. Check for mistakes.
if (proc_rank == 0 .and. ib_force_wrt) then
call s_open_ib_force_file()
end if
Comment on lines +466 to +469
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Gate the IB force file lifetime on ib too.

With ib_force_wrt = T and ib = F, Line 467 still opens and Line 1073 still closes the dedicated IB file, but the only write path is inside if (ib) at Lines 634-645. That leaves a misleading empty artifact unless this combination is rejected upstream.

Suggested fix
-        if (proc_rank == 0 .and. ib_force_wrt) then
+        if (proc_rank == 0 .and. ib .and. ib_force_wrt) then
             call s_open_ib_force_file()
         end if
@@
-        if (proc_rank == 0 .and. ib_force_wrt) then
+        if (proc_rank == 0 .and. ib .and. ib_force_wrt) then
             call s_close_ib_force_file()
         end if

Based on learnings, "Every new parameter must be added in at least 3 places: (1) toolchain/mfc/params/definitions.py, (2) Fortran variable declaration in src//m_global_parameters.fpp, (3) Fortran namelist in src//m_start_up.fpp, and optionally (4) toolchain/mfc/case_validator.py if the parameter has physics constraints".

Also applies to: 1072-1075


if (cfl_dt) then
@:ALLOCATE(max_dt(0:m, 0:n, 0:p))
end if
Expand Down Expand Up @@ -630,6 +635,14 @@ contains
! check if any IBMS are moving, and if so, update the markers, ghost points, levelsets, and levelset norms
if (moving_immersed_boundary_flag) then
call s_propagate_immersed_boundaries(s)
! compute ib forces for fixed immersed boundaries if requested for output
else if (ib_force_wrt .and. s == nstage) then
call s_compute_ib_forces(q_prim_vf, fluid_pp)
end if

! Write IB forces to file if requested and at the RK final stage
if (ib_force_wrt .and. s == nstage) then
call s_write_ib_force_file(t_step)
Comment on lines +638 to +645
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Moving-IB force output can be stale or missing.

When moving_immersed_boundary_flag is true, Lines 636-641 bypass the fixed-IB force path and rely on s_propagate_immersed_boundaries(). Later in this file, that routine only calls s_compute_ib_forces() for moving_ibm == 2, so one-way moving IBs—and mixed fixed + one-way cases—reach Lines 643-645 with old or never-populated patch_ib(:)%force/torque.

Suggested fix
                 if (moving_immersed_boundary_flag) then
                     call s_propagate_immersed_boundaries(s)
-                    ! compute ib forces for fixed immersed boundaries if requested for output
-                else if (ib_force_wrt .and. s == nstage) then
-                    call s_compute_ib_forces(q_prim_vf, fluid_pp)
                 end if

-                ! Write IB forces to file if requested and at the RK final stage
                 if (ib_force_wrt .and. s == nstage) then
+                    call s_compute_ib_forces(q_prim_vf, fluid_pp)
                     call s_write_ib_force_file(t_step)
                 end if

end if
Comment on lines 635 to 646
Copy link
Contributor

Choose a reason for hiding this comment

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

Action required

4. Forces may be stale/zero 🐞 Bug ✓ Correctness

When any IB is moving, the new logic skips s_compute_ib_forces in the main RK loop, yet still writes
patch_ib(i)%force/torque for all IBs. This can produce incorrect force/torque output for fixed IBs
in mixed cases and for moving_ibm==1 (analytic motion) where forces are never computed.
Agent Prompt
### Issue description
`m_time_steppers` writes IB forces at the final RK stage whenever `ib_force_wrt` is enabled, but it only computes forces in the non-moving case (or for `moving_ibm==2`). This can leave `patch_ib(:)%force/torque` unset/stale in mixed moving/fixed IB cases or for `moving_ibm==1`.

### Issue Context
`moving_immersed_boundary_flag` becomes true if *any* IB has `moving_ibm /= 0`, which prevents the fixed-IB compute path from running.

### Fix Focus Areas
- src/simulation/m_time_steppers.fpp[634-646]
- src/simulation/m_time_steppers.fpp[835-856]

### Suggested approach
- At `s == nstage` and `ib_force_wrt`, call `s_compute_ib_forces(q_prim_vf, fluid_pp)` unconditionally before `s_write_ib_force_file(t_step)`.
- Optionally avoid double-computation by tracking whether forces were already computed during this stage (e.g., a stage-local flag set by `s_propagate_immersed_boundaries`).
- Keep `s_propagate_immersed_boundaries` behavior for two-way coupling, but ensure final-stage output always reflects current state.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


! update the ghost fluid properties point values based on IB state
Expand Down Expand Up @@ -1056,6 +1069,11 @@ contains
call s_close_run_time_information_file()
end if

! Writing the footer of and closing the IB data file
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

This comment says a footer is written for the IB force file, but s_close_ib_force_file only closes the unit and does not write any footer record. Please either implement a footer (and adjust the post-process reader) or update the comment to match the actual behavior.

Suggested change
! Writing the footer of and closing the IB data file
! Closing the IB data file

Copilot uses AI. Check for mistakes.
if (proc_rank == 0 .and. ib_force_wrt) then
call s_close_ib_force_file()
end if

end subroutine s_finalize_time_steppers_module

end module m_time_steppers
3 changes: 2 additions & 1 deletion toolchain/mfc/params/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@
"prim_vars_wrt": "Write primitive variables",
"cons_vars_wrt": "Write conservative variables",
"run_time_info": "Print runtime info",
"ib_force_wrt": "Write IB force/torque data",
# Misc
"case_dir": "Case directory path",
"cantera_file": "Cantera mechanism file",
Expand Down Expand Up @@ -932,7 +933,7 @@ def _load(): # pylint: disable=too-many-locals,too-many-statements
_r("format", INT, {"output"})
_r("schlieren_alpha", REAL, {"output"})
for n in ["parallel_io", "file_per_process", "run_time_info", "prim_vars_wrt",
"cons_vars_wrt", "fft_wrt"]:
"cons_vars_wrt", "fft_wrt", "ib_force_wrt"]:
_r(n, LOG, {"output"})
Comment on lines 935 to 937
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

This new parameter is correctly added to the registry, but the toolchain’s fallback namelist parameter sets (used when Fortran sources aren’t available, e.g. Homebrew installs) are defined in toolchain/mfc/params/namelist_parser.py and currently do not include ib_force_wrt for simulation/post_process. Please regenerate/update those fallback sets so ib_force_wrt is accepted in source-less installations too.

Copilot uses AI. Check for mistakes.
for n in ["schlieren_wrt", "alpha_rho_wrt", "rho_wrt", "mom_wrt", "vel_wrt",
"flux_wrt", "E_wrt", "pres_wrt", "alpha_wrt", "kappa_wrt", "gamma_wrt",
Expand Down
Loading