Abort when post_process is given a different grid than the restart files - #1858
Conversation
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Pull request overview
Adds safety checks and new runtime features around restart handling, boundary-condition ramps, and immersed-boundary (IB) output/kinematics.
Changes:
- Abort post-processing early when restart grid resolution (from
x_cb.datsize) doesn’t match the case file’sm. - Add ramped inflow support (
vel_in_ramp,vel_in_t0,vel_in_frac0) for GRCBC and Dirichlet-style inflows. - Add prescribed IB kinematics options and buffered per-step IB force/kinematic logging (
D/ib<id>_forces.dat) withib_force_stride.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| toolchain/mfc/params/descriptions.py | Adds parameter descriptions for IB force stride and IB kinematics. |
| toolchain/mfc/params/definitions.py | Registers new params/hints (inflow ramp, IB force stride, IB kinematics) and raises probe max. |
| toolchain/mfc/case_validator.py | Validates inflow ramp constraints and IB stride/kinematics constraints. |
| src/simulation/m_time_steppers.fpp | Calls inflow-ramp updater and IB force logging; integrates prescribed kinematics into RK stages. |
| src/simulation/m_start_up.fpp | Flushes buffered IB force records on finalize. |
| src/simulation/m_mpi_proxy.fpp | Broadcasts new BC ramp parameters and IB kinematics parameters. |
| src/simulation/m_ibm.fpp | Initializes prescribed kinematics at start; adds kinematics implementation and adjusts GPU/MPI data movement. |
| src/simulation/m_global_parameters.fpp | Initializes new global/BC/IB parameters (ib_force_stride, inflow ramp fields, kinematics fields). |
| src/simulation/m_data_output.fpp | Implements buffered per-step IB force/kinematics output and adjusts probe output formatting. |
| src/simulation/m_cbc.fpp | Stores “final” inflow velocity and applies time ramp factor to inflow + exposes updater. |
| src/pre_process/m_global_parameters.fpp | Initializes new IB kinematics fields for preprocessing. |
| src/post_process/m_data_input.f90 | Adds restart grid mismatch detection via x_cb.dat file size. |
| src/common/m_derived_types.fpp | Extends BC and IB derived types with inflow ramp + kinematics parameters. |
| src/common/m_constants.fpp | Raises num_probes_max to 64. |
| src/common/m_boundary_primitives.fpp | Applies ramp factor to Dirichlet boundary ghost velocities via bc_vel_ramp. |
| docs/documentation/case.md | Documents new IB kinematics, IB force logging, and inflow ramp parameters. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| do i = 1, ib_force_buf_n | ||
| ib_id = nint(ib_force_buf(1, i)) | ||
| if (ib_id < 0) cycle ! already written as part of an earlier body's pass | ||
|
|
||
| write (file_loc, '(A,I0,A)') '/D/ib', ib_id, '_forces.dat' | ||
| file_loc = trim(case_dir) // trim(file_loc) | ||
| inquire (file=trim(file_loc), exist=file_exist) | ||
| if (file_exist) then | ||
| open (newunit=file_unit, file=trim(file_loc), form='formatted', status='old', position='append') | ||
| else | ||
| open (newunit=file_unit, file=trim(file_loc), form='formatted', status='new') | ||
| write (file_unit, '(A)') '# t_step time Fx Fy Fz Tx Ty Tz vx vy vz wx wy wz ax ay az xc yc zc' | ||
| end if | ||
|
|
||
| do j = i, ib_force_buf_n ! all rows for this body, in time order | ||
| if (nint(ib_force_buf(1, j)) /= ib_id) cycle | ||
| write (file_unit, '(I10,19(1X,ES17.9E3))') nint(ib_force_buf(2, j)), ib_force_buf(3:21,j) | ||
| ib_force_buf(1, j) = -1._wp | ||
| end do | ||
|
|
||
| close (file_unit) |
| ! do all set up for moving immersed boundaries; prescribed kinematics are evaluated at the initial time so the | ||
| ! first stage already sees the correct body state, velocity and angular velocity | ||
| t_init = t_step_start*dt | ||
| if (cfl_dt) t_init = t_save*n_start |
| integer, intent(in) :: s | ||
| integer :: i | ||
| integer :: gbl_id ! used for analytic ib patch motion | ||
| real(wp) :: t_stage ! time of the state produced by RK stage s (used by prescribed kinematics) |
| t_stage = mytime + dt | ||
| if (time_stepper == time_stepper_rk3 .and. s == 2) t_stage = mytime + 0.5_wp*dt | ||
|
|
||
| $:GPU_PARALLEL_LOOP(private='[i, gbl_id]', copyin='[s, t_stage]') |
|
|
||
| file_loc = trim(case_dir) // '/restart_data' // trim(mpiiofs) // 'x_cb.dat' | ||
| inquire (FILE=trim(file_loc), EXIST=file_exist) | ||
| inquire (FILE=trim(file_loc), EXIST=file_exist, SIZE=file_bytes) |
| !> Buffered immersed-boundary force records: (id, t_step, time, force, torque, vel, angular_vel, angles, centroid) | ||
| integer, parameter :: ib_force_buf_len = 1024 | ||
| real(wp), dimension(21, ib_force_buf_len) :: ib_force_buf |
post_process checks that the restart files exist but never that they hold the grid the case file asks for. Give it a case whose resolution no longer matches the run and it reads past the end of every file, post-processes the overrun, and exits 0 with NaN-filled output. Nothing reports a problem until someone plots it, or until a diagnostic built on that output starts returning NaN and gets blamed instead. That is what happened here: a 349 M cell run (m = 1644) post-processed with a case file that had since moved to m = 2056, so 681 M cells were read from files holding 349 M. x_cb.dat holds one value per cell boundary, so its size states which grid wrote the restart. The check costs one inquire on a file already being opened, fires before any bulk read, and names both grids. The size is taken from storage_size(0._wp) rather than a literal 8, since x_cb.dat is written with mpi_p and a --single build writes four-byte reals. down_sample reads a full-resolution file with a stride of three, touching stride*(m_glb + 1) + 1 boundaries, so it needs more of the file than m_glb + 2 and only the un-strided read can pin the size exactly -- three source grids of different size can down-sample to the same m_glb.
4007be2 to
731dd59
Compare
|
Rebased onto Two real bugs in the check itself, both fixed: Hardcoded 8 bytes.
|
|
Claude Code Review Head SHA: 4007be2 Files changed:
Findings:
|
Lines of Code
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1858 +/- ##
==========================================
- Coverage 61.26% 61.25% -0.01%
==========================================
Files 84 84
Lines 22330 22335 +5
Branches 3265 3266 +1
==========================================
+ Hits 13680 13681 +1
- Misses 6207 6210 +3
- Partials 2443 2444 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
post_processchecks that the restart files exist but never that they hold the grid the case file asks for. Give it a case whose resolution no longer matches the run and it reads past the end of every file, post-processes the overrun, and exits 0 with NaN-filled output. Nothing reports a problem until someone plots it — or until a diagnostic built on that output starts returning NaN and gets blamed instead, which is how I found it.Reproduction
A 349 M cell run (
m = 1644) post-processed with a case file that had since moved tom = 2056:681 M cells read from files holding 349 M. Exit code 0, silo written, every field NaN.
The check
x_cb.datholds exactlym_glb + 2cell boundaries, so its size states which grid wrote the restart. Oneinquireon a file already being opened, before any bulk read:Verified against the failing data: 2058 boundaries would need 16464 bytes, the file holds 13168, so it aborts where it previously ran to completion.
Builds clean (
./mfc.sh build --no-gpu -t post_process).https://claude.ai/code/session_01HMJ7cycfo7kTFSFq5yhHLG