Skip to content

Isothermal Boundary Conditions for Reacting Flows#1361

Draft
DimAdam-01 wants to merge 32 commits intoMFlowCode:masterfrom
DimAdam-01:Isothermal_Slip_NoSlip
Draft

Isothermal Boundary Conditions for Reacting Flows#1361
DimAdam-01 wants to merge 32 commits intoMFlowCode:masterfrom
DimAdam-01:Isothermal_Slip_NoSlip

Conversation

@DimAdam-01
Copy link
Copy Markdown
Contributor

Description

Summarize your changes and the motivation behind them.

This PR introduces isothermal wall boundary conditions to support accurate heat transfer modeling in chemically reacting flow simulations, specifically targeting solid fuel combustion geometries/

Fixes #(issue)

Type of change

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • Other: describe

Key Changes & Additions:

Isothermal Boundaries: Added support for isothermal_in and isothermal_out flags alongside T
wall_in and Twall_out parameters across all domain boundaries in m_boundary_common.fpp.

Updated the boundary condition (slip and no-slip) logic to correctly evaluate the ghost cell temperature based on the specified wall temperature and the interior domain state, driving the correct heat flux.

Testing

How did you test your changes?

Test 1: Dual Isothermal Wall.

image

Test 2: 2D Flow in a Channel with Isothermal walls

2D_Thermal_Plate.mp4
image

Checklist

  • I added or updated tests for new behavior
  • I updated documentation if user-facing behavior changed

See the developer guide for full coding standards.

GPU changes (expand if you modified src/simulation/)
  • GPU results match CPU results
  • Tested on NVIDIA GPU or AMD GPU

AI code reviews

Reviews are not triggered automatically. To request a review, comment on the PR:

  • @coderabbitai review — incremental review (new changes only)
  • @coderabbitai full review — full review from scratch
  • /review — Qodo review
  • /improve — Qodo code suggestions
  • @claude full review — Claude full review (also triggers on PR open/reopen/ready)
  • Add label claude-full-review — Claude full review via label

@DimAdam-01 DimAdam-01 changed the title Feature: Add Isothermal Boundary Conditions for Reacting Flows Isothermal Boundary Conditions for Reacting Flows Apr 7, 2026
Dimitrios Adam and others added 2 commits April 7, 2026 15:20
@sbryngelson
Copy link
Copy Markdown
Member

Code review

Found 7 issues:

  1. s_periodic declares q_T_sf as non-optional while all other BC subroutines and its callers treat it as optional — passing an absent optional actual to a non-optional dummy is a Fortran standard violation; any periodic BC run without chemistry will crash or produce undefined behavior.

integer, intent(in) :: k, l
integer :: j, q, i
type(scalar_field), intent(inout) :: q_T_sf
if (bc_dir == 1) then !< x-direction
if (bc_loc == -1) then !< bc_x%beg

  1. q_T_sf is accessed as q_T_sf%sf(...) in routines that declare it optional, guarded only by if (chemistry) without present(q_T_sf) — if any call site omits the argument while chemistry=.true., this dereferences an absent pointer. The correct guard is if (chemistry .and. present(q_T_sf)).

integer, intent(in) :: k, l
integer :: j, i
type(scalar_field), optional, intent(inout) :: q_T_sf
if (bc_dir == 1) then !< x-direction
if (bc_loc == -1) then ! bc_x%beg

  1. Hardcoded 600.0_wp in s_slip_wall y-direction beginning boundary — the isothermal ghost-cell formula should be 2._wp*bc_y%Twall_in - q_T_sf%sf(k, j-1, l), but uses a magic number instead, silently ignoring the user-specified bc_y%Twall_in for any wall temperature other than 300 K. Every other direction in both s_slip_wall and s_no_slip_wall uses the parameterized formula correctly.

if (bc_y%isothermal_in) then
do j = 1, buff_size
q_T_sf%sf(k, -j, l) = 600.0_wp - q_T_sf%sf(k, j - 1, l)
end do
else
do j = 1, buff_size

  1. When igr=T and chemistry=T, q_T_sf is not passed to s_write_serial_boundary_condition_files — the igr branch omits q_T_sf, but s_pack_boundary_condition_buffers accesses q_T_sf%sf(...) under if (chemistry) without a present() guard, causing a NULL dereference.

if (bc_io) then
if (igr) then
call s_write_serial_boundary_condition_files(q_cons_vf, bc_type, t_step_dir, old_grid)
else
call s_write_serial_boundary_condition_files(q_prim_vf, bc_type, t_step_dir, old_grid, q_T_sf)
end if

  1. NaN check in MPI unpack reads the wrong cell index — after writing to q_T_sf%sf(j + unpack_offset, k, l), the NaN check inspects q_T_sf%sf(j, k, l) (without the offset) in all three MPI directions. The correct pattern, used for all other variables in the same function, is to check the cell that was just written. This silently fails to detect NaNs in received data.

do j = -buff_size, -1
r = nVar + v_size*(j + buff_size*((k + 1) + (n + 1)*l))
q_T_sf%sf(j + unpack_offset, k, l) = real(buff_recv(r), kind=stp)
#if defined(__INTEL_COMPILER)
if (ieee_is_nan(q_T_sf%sf(j, k, l))) then
print *, "Error", j, k, l, i
error stop "NaN(s) in recv"
end if
#endif

  1. error stop used instead of call s_mpi_abort() in the three new NaN-detection blocks (one per MPI direction) — CLAUDE.md says "NEVER use stop or error stop. Use call s_mpi_abort()". The existing NaN handlers directly above in the same file use call s_mpi_abort("NaN(s) in recv") correctly.

if (ieee_is_nan(q_T_sf%sf(j, k, l))) then
print *, "Error", j, k, l, i
error stop "NaN(s) in recv"
end if
#endif

  1. New isothermal_in/isothermal_out/Twall_in/Twall_out fields are initialized in src/simulation/m_global_parameters.fpp but not in src/pre_process/ or src/post_process/.claude/rules/parameter-system.md requires all three targets to be updated for shared parameters. The boundary condition code that checks these fields lives in src/common/ and is called by all three executables.

#:for dir in {'x', 'y', 'z'}
bc_${dir}$%isothermal_in = .false.
bc_${dir}$%isothermal_out = .false.
bc_${dir}$%Twall_in = dflt_real
bc_${dir}$%Twall_out = dflt_real
#:endfor

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@sbryngelson sbryngelson added the claude-full-review Trigger Claude Code review label Apr 9, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 9, 2026

Claude Code Review

Head SHA: 2770ad3

Files changed:

  • 28
  • src/common/m_boundary_common.fpp
  • src/common/m_chemistry.fpp
  • src/common/m_mpi_common.fpp
  • src/common/include/2dHardcodedIC.fpp
  • src/common/include/1dHardcodedIC.fpp
  • src/simulation/m_rhs.fpp
  • src/simulation/m_start_up.fpp
  • src/common/m_derived_types.fpp
  • toolchain/mfc/case_validator.py
  • toolchain/mfc/test/cases.py

Findings

1. Spurious chem_params%diffusion guard on z-direction periodic BC for q_T_sf (src/common/m_boundary_common.fpp)

In s_periodic, the ghost-cell fill for q_T_sf in the z-direction beg case is guarded by an extra chem_params%diffusion condition that is absent from all other directions:

! x-beg, x-end, y-beg, y-end — all four use:
if (chemistry .and. present(q_T_sf)) then

! z-beg — uniquely requires diffusion=T:
if (chemistry .and. chem_params%diffusion .and. present(q_T_sf)) then   ! ← added hunk at diff line 683

The z-end case reverts to the plain chemistry .and. present(q_T_sf) guard, matching the other four directions. The asymmetry means that when chemistry=T and diffusion=F, the z-beg periodic ghost cells of q_T_sf are never populated while the z-end cells are, leaving an inconsistent temperature halo at the z-beg boundary.

2. Hardcoded IC 291 (2D Isothermal Flat Plate) initialises only the first and last advected species (src/common/include/2dHardcodedIC.fpp)

q_prim_vf(eqn_idx%species%beg)%sf(i, j, 0) = Y_O2   ! 0.233
q_prim_vf(eqn_idx%species%end)%sf(i, j, 0) = Y_N2   ! 0.767

The case is intended for h2o2.yaml (9 species: H2, O2, H2O, H, O, OH, HO2, H2O2, N2). Assuming N2 is the carrier species, there are 8 advected equations (eqn_idx%species%begbeg+7). This code assigns Y_O2 to H2's equation slot and Y_N2 to H2O2's equation slot, while leaving the 6 intermediate species at zero. The resulting initial state does not represent the intended O2/N2 air mixture and mass fractions will not sum correctly via the carrier relation.

The example examples/2D_Thermal_Flatplate/case.py carefully populates every patch_icpp(1)%Y(i+1) from Cantera, but those values are overridden by the hardcoded IC function when hcid=291 is set, so the per-species assignments in the case file have no effect.

Dimitrios Adam added 2 commits April 9, 2026 23:41
@DimAdam-01 DimAdam-01 marked this pull request as ready for review April 10, 2026 13:39
@DimAdam-01 DimAdam-01 requested a review from sbryngelson as a code owner April 10, 2026 13:39
@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review bot commented Apr 10, 2026

Code Review by Qodo

🐞 Bugs (6)   📘 Rule violations (0)   📎 Requirement gaps (0)   🎨 UX Issues (0)
🐞\ ≡ Correctness (2) ☼ Reliability (4)

Grey Divider


Action required

1. Unsafe optional q_T_sf use🐞
Description
m_boundary_common and MPI comm code write/read q_T_sf under if (chemistry) / `if (chemistry .and.
chem_params%diffusion) without checking present(q_T_sf)`, but several call sites still invoke
buffer population without passing q_T_sf. With chemistry enabled this can dereference an absent
optional argument (undefined behavior: crash or memory corruption).
Code

src/common/m_boundary_common.fpp[R300-304]

+                if (chemistry) then
+                    do j = 1, buff_size
+                        q_T_sf%sf(-j, k, l) = q_T_sf%sf(0, k, l)
+                    end do
+                end if
Evidence
q_T_sf is declared optional but is dereferenced whenever chemistry is true. The repo contains
paths that call s_populate_variables_buffers(...) without q_T_sf (e.g., down_sample in pre_process
and simulation), making the optional argument absent while the callee still dereferences it.

src/common/m_boundary_common.fpp[284-304]
src/common/m_boundary_common.fpp[827-859]
src/common/m_mpi_common.fpp[499-592]
src/pre_process/m_data_output.fpp[409-421]
src/simulation/m_start_up.fpp[741-753]
src/simulation/m_time_steppers.fpp[602-609]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several boundary-condition and MPI-communication routines accept `q_T_sf` as an **optional** argument but dereference it whenever `chemistry` (and sometimes `chem_params%diffusion`) is enabled. There are existing call sites that invoke `s_populate_variables_buffers(...)` without passing `q_T_sf` (e.g., down-sampling output paths), which makes dereferencing the optional argument undefined behavior (can crash or silently corrupt).
### Issue Context
This PR introduced `q_T_sf` support into BC fill + MPI exchange, but the implementation currently relies on an implicit contract (“if chemistry then q_T_sf is always passed”) that the codebase does not consistently satisfy.
### Fix Focus Areas
- Add `present(q_T_sf)` guards around all `q_T_sf%sf(...)` accesses (and similarly in MPI pack/unpack) so the routines are safe when `q_T_sf` is omitted.
- Ensure temperature communication/ghost filling happens whenever it is truly required (e.g., chemistry+diffusion paths that subsequently compute diffusion fluxes).
- src/common/m_boundary_common.fpp[284-365]
- src/common/m_boundary_common.fpp[827-927]
- src/common/m_boundary_common.fpp[1152-1236]
- src/common/m_mpi_common.fpp[499-593]
- src/common/m_mpi_common.fpp[788-830]

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


2. bc_x Twall not broadcast🐞
Description
post_process MPI input broadcast omits bc_x%Twall_in/out, so non-root ranks keep default wall
temperatures and produce incorrect parallel post-processing results for x-direction isothermal
walls. This is silent data corruption in MPI runs.
Code

src/post_process/m_mpi_proxy.fpp[R132-138]

       #:for VAR in [ 'pref', 'rhoref', 'R0ref', 'poly_sigma', 'Web', 'Ca', &
-            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',   &
-            & 'x_output%beg', 'x_output%end', 'y_output%beg', &
-            & 'y_output%end', 'z_output%beg', 'z_output%end']
+            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',      &
+            & 'x_output%beg', 'x_output%end', 'y_output%beg',    &
+            & 'y_output%end', 'z_output%beg', 'z_output%end',    &
+            & 'bc_y%Twall_in', 'bc_y%Twall_out', 'bc_z%Twall_in',&
+            & 'bc_z%Twall_out']
           call MPI_BCAST(${VAR}$, 1, mpi_p, 0, MPI_COMM_WORLD, ierr)
Evidence
The post_process broadcast list includes bc_y/bc_z Twall fields but not bc_x Twall fields, while
defaults are initialized to dflt_real. Simulation does broadcast all Twall fields, highlighting
the inconsistency.

src/post_process/m_mpi_proxy.fpp[132-139]
src/simulation/m_mpi_proxy.fpp[127-142]
src/simulation/m_global_parameters.fpp[767-772]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
In post_process MPI runs, `bc_x%Twall_in` and `bc_x%Twall_out` are not broadcast from rank 0. Non-root ranks therefore keep default values, producing incorrect post-processing for x-direction isothermal boundaries.
### Issue Context
The simulation and pre_process MPI proxies broadcast all 6 Twall values; post_process broadcasts only y/z.
### Fix Focus Areas
- Add `bc_x%Twall_in` and `bc_x%Twall_out` to the real-parameter `MPI_BCAST` list.
- src/post_process/m_mpi_proxy.fpp[132-139]

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


3. q_T_sf stale after smoothing🐞
Description
pre_process computes q_T_sf before elliptic smoothing, but elliptic smoothing overwrites q_prim_vf
and does not recompute q_T_sf afterward. This leaves q_T_sf inconsistent with the final smoothed
initial condition, and any later use/serialization of q_T_sf (e.g., bc_io boundary buffers) will be
wrong.
Code

src/pre_process/m_initial_condition.fpp[R141-149]

+        if (chemistry) call s_compute_T_from_primitives(q_T_sf, q_prim_vf, idwint)

-        call s_convert_primitive_to_conservative_variables(q_prim_vf, q_cons_vf)
+        if (elliptic_smoothing .and. chemistry) then
+            call s_elliptic_smoothing(q_prim_vf, bc_type, q_T_sf)
+        else if (elliptic_smoothing) then
+            call s_elliptic_smoothing(q_prim_vf, bc_type)
+        end if

-        if (chemistry) call s_compute_T_from_primitives(q_T_sf, q_prim_vf, idwint)
+        call s_convert_primitive_to_conservative_variables(q_prim_vf, q_cons_vf)
Evidence
The initial-condition generator now calls s_compute_T_from_primitives before smoothing. The
smoothing routine updates q_prim_vf in-place but never updates/recomputes q_T_sf. Meanwhile,
boundary-condition buffer packing serializes temperature slices from q_T_sf, so stale values can
be written.

src/pre_process/m_initial_condition.fpp[137-150]
src/pre_process/m_perturbation.fpp[86-137]
src/common/m_boundary_common.fpp[2052-2068]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`q_T_sf` is computed from primitives before elliptic smoothing, but smoothing modifies `q_prim_vf` without updating `q_T_sf`. This makes temperature inconsistent with the final smoothed primitives and can lead to incorrect temperature written/used downstream (notably for `bc_io` temperature buffer serialization).
### Issue Context
`m_perturbation:s_elliptic_smoothing` only uses `q_T_sf` to populate ghost/buffer regions; it never recomputes temperature from the updated primitives.
### Fix Focus Areas
- After elliptic smoothing completes (and before any output/serialization that uses `q_T_sf`), recompute `q_T_sf` from the final `q_prim_vf`.
- Keep the pre-smoothing computation if it is needed to apply isothermal BCs during smoothing, but add a post-smoothing recomputation step.
- src/pre_process/m_initial_condition.fpp[137-150]
- src/pre_process/m_perturbation.fpp[86-137]

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


View more (12)
4. MPI q_T_sf presence ignored 🐞
Description
s_mpi_sendrecv_variables_buffers packs/unpacks q_T_sf whenever `chemistry .and.
chem_params%diffusion is true, but q_T_sf` is an optional argument and is dereferenced without
present() checks. If any caller omits q_T_sf (currently true in several places), MPI buffer
packing/unpacking will read/write through an absent optional argument.
Code

src/common/m_mpi_common.fpp[R525-531]

+        else if (chemistry .and. chem_params%diffusion) then
+            v_size = nVar + 1
+            buffer_counts = (/buff_size*v_size*(n + 1)*(p + 1), buff_size*v_size*(m + 2*buff_size + 1)*(p + 1), &
+                             & buff_size*v_size*(m + 2*buff_size + 1)*(n + 2*buff_size + 1)/)
       else
           v_size = nVar
           buffer_counts = (/buff_size*v_size*(n + 1)*(p + 1), buff_size*v_size*(m + 2*buff_size + 1)*(p + 1), &
Evidence
The MPI routine sizes v_size = nVar + 1 under chemistry .and. chem_params%diffusion and then
directly reads/writes q_T_sf%sf(...) under the same condition, but q_T_sf is declared optional
and never checked with present(q_T_sf). This is unsafe when upstream calls omit the argument.

src/common/m_mpi_common.fpp[499-593]
src/common/m_mpi_common.fpp[520-533]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
MPI send/recv for ghost buffers packs/unpacks temperature (`q_T_sf`) based solely on global flags (`chemistry` and `chem_params%diffusion`) even though `q_T_sf` is an optional dummy argument. This can dereference an absent optional argument and crash.
### Issue Context
Some code paths call `s_mpi_sendrecv_variables_buffers` (via `s_populate_variables_buffers`) without providing `q_T_sf` (e.g., conservative-only communications and downsample paths). The MPI routine must not assume `q_T_sf` is present.
### Fix Focus Areas
- src/common/m_mpi_common.fpp[499-593]
- src/common/m_mpi_common.fpp[564-624]
- src/common/m_mpi_common.fpp[625-713]
- src/common/m_mpi_common.fpp[756-974]
### What to change
1. Introduce a local logical like `has_T = present(q_T_sf) .and. chemistry .and. chem_params%diffusion`.
2. Use `has_T` (not just chemistry/diffusion) to:
 - set `v_size` (nVar vs nVar+1)
 - compute `buffer_counts`
 - pack `buff_send` temperature slot
 - unpack into `q_T_sf`
3. Optionally add a defensive abort if `chemistry .and. chem_params%diffusion` is true in a code path that *requires* temperature exchange but `present(q_T_sf)` is false.

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


5. Missing bc_x Twall broadcast🐞
Description
In post_process MPI input broadcast, bc_x%Twall_in/out are not broadcast to non-root ranks,
leaving them at the default dflt_real and making post-processing rank-dependent for x-isothermal
cases. Post_process does call boundary buffer population (which can apply isothermal logic), so
incorrect bc_x%Twall_* affects computed ghost temperatures and derived outputs.
Code

src/post_process/m_mpi_proxy.fpp[R132-138]

       #:for VAR in [ 'pref', 'rhoref', 'R0ref', 'poly_sigma', 'Web', 'Ca', &
-            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',   &
-            & 'x_output%beg', 'x_output%end', 'y_output%beg', &
-            & 'y_output%end', 'z_output%beg', 'z_output%end']
+            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',      &
+            & 'x_output%beg', 'x_output%end', 'y_output%beg',    &
+            & 'y_output%end', 'z_output%beg', 'z_output%end',    &
+            & 'bc_y%Twall_in', 'bc_y%Twall_out', 'bc_z%Twall_in',&
+            & 'bc_z%Twall_out']
           call MPI_BCAST(${VAR}$, 1, mpi_p, 0, MPI_COMM_WORLD, ierr)
Evidence
s_mpi_bcast_user_inputs in post_process broadcasts bc_y%Twall_* and bc_z%Twall_* but omits
bc_x%Twall_*. Post_process then computes temperature and calls s_populate_variables_buffers,
which applies wall temperature logic (e.g., in slip wall) using bc_x%Twall_in/bc_x%Twall_out;
without broadcasting, only rank 0 has the intended values.

src/post_process/m_mpi_proxy.fpp[132-139]
src/post_process/m_start_up.fpp[166-173]
src/common/m_boundary_common.fpp[849-857]
src/pre_process/m_mpi_proxy.fpp[55-66]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Post-process MPI broadcast omits `bc_x%Twall_in` and `bc_x%Twall_out`, leaving defaults on non-root ranks. Post-process uses boundary-condition population that can reference these values, producing incorrect ghost temperatures and rank-dependent derived outputs.
### Issue Context
- pre_process already broadcasts `bc_x%Twall_*` alongside y/z.
- post_process should broadcast the same set for consistency.
### Fix Focus Areas
- src/post_process/m_mpi_proxy.fpp[132-139]
### What to change
Add `bc_x%Twall_in` and `bc_x%Twall_out` to the real-parameter broadcast list in `s_mpi_bcast_user_inputs` (and keep ordering consistent with pre_process/simulation if possible).

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


6. Twall not required by validator 🐞
Description
The case validator permits bc_*%isothermal_in/out = T without requiring the corresponding
bc_*%Twall_in/out to be provided, leaving it at dflt_real = -1e6. Boundary routines use
Twall_* directly to set ghost temperatures, so missing Twall configuration yields wildly
unphysical temperatures.
Code

toolchain/mfc/case_validator.py[R1291-1316]

+        # Fetch global chemistry and diffusion flags
+        chemistry = self.get("chemistry", "F") == "T"
+        diffusion = self.get("chem_params%diffusion", "F") == "T"
+
+        # Define what constitutes a wall (-15 for slip, -16 for no-slip)
+        wall_bcs = [-15, -16]
+
+        for dir in ["x", "y", "z"]:
+            isothermal_in = self.get(f"bc_{dir}%isothermal_in", "F") == "T"
+            isothermal_out = self.get(f"bc_{dir}%isothermal_out", "F") == "T"
+            bc_beg = self.get(f"bc_{dir}%beg")
+            bc_end = self.get(f"bc_{dir}%end")
+
+            if isothermal_in:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal In (bc_{dir}%isothermal_in) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_beg not in wall_bcs, f"Isothermal In (bc_{dir}%isothermal_in) requires a wall. Set bc_{dir}%beg to -15 (slip) or -16 (no-slip).")
+
+            if isothermal_out:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal Out (bc_{dir}%isothermal_out) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_end not in wall_bcs, f"Isothermal Out (bc_{dir}%isothermal_out) requires a wall. Set bc_{dir}%end to -15 (slip) or -16 (no-slip).")
Evidence
check_chemistry validates that isothermal flags require chemistry+diffusion and wall BC types, but
never checks Twall_in/out are set. Meanwhile, global parameters initialize Twall_* to
dflt_real and dflt_real is defined as -1e6, and the wall BC implementation uses bc_x%Twall_in
directly in the ghost-cell temperature formula.

toolchain/mfc/case_validator.py[1284-1316]
src/pre_process/m_global_parameters.fpp[305-310]
src/common/m_constants.fpp[10-13]
src/common/m_boundary_common.fpp[849-857]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The validator allows enabling isothermal walls without specifying `Twall_in/out`, which defaults to `dflt_real=-1e6` and is then used directly by the wall BC implementation to set ghost temperatures.
### Issue Context
- `bc_[xyz]%Twall_in/out` defaults to `dflt_real`.
- `check_chemistry()` already checks compatibility (chemistry+diffusion, wall bc codes) but not presence/validity of Twall.
### Fix Focus Areas
- toolchain/mfc/case_validator.py[1284-1316]
- src/common/m_constants.fpp[10-13]
### What to change
1. When `bc_{dir}%isothermal_in` is true:
 - require `bc_{dir}%Twall_in` is provided
 - require it is numeric (or at least not missing)
 - require it is not equal to the default sentinel (e.g., `-1e6`) and is physically reasonable (e.g., `> 0` K)
2. Similarly, when `bc_{dir}%isothermal_out` is true, validate `bc_{dir}%Twall_out`.
3. Produce clear error messages pointing to the missing Twall parameter.

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


7. Unsafe optional q_T_sf use🐞
Description
m_boundary_common and MPI comm code write/read q_T_sf under if (chemistry) / `if (chemistry .and.
chem_params%diffusion) without checking present(q_T_sf)`, but several call sites still invoke
buffer population without passing q_T_sf. With chemistry enabled this can dereference an absent
optional argument (undefined behavior: crash or memory corruption).
Code

src/common/m_boundary_common.fpp[R300-304]

+                if (chemistry) then
+                    do j = 1, buff_size
+                        q_T_sf%sf(-j, k, l) = q_T_sf%sf(0, k, l)
+                    end do
+                end if
Evidence
q_T_sf is declared optional but is dereferenced whenever chemistry is true. The repo contains
paths that call s_populate_variables_buffers(...) without q_T_sf (e.g., down_sample in pre_process
and simulation), making the optional argument absent while the callee still dereferences it.

src/common/m_boundary_common.fpp[284-304]
src/common/m_boundary_common.fpp[827-859]
src/common/m_mpi_common.fpp[499-592]
src/pre_process/m_data_output.fpp[409-421]
src/simulation/m_start_up.fpp[741-753]
src/simulation/m_time_steppers.fpp[602-609]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several boundary-condition and MPI-communication routines accept `q_T_sf` as an **optional** argument but dereference it whenever `chemistry` (and sometimes `chem_params%diffusion`) is enabled. There are existing call sites that invoke `s_populate_variables_buffers(...)` without passing `q_T_sf` (e.g., down-sampling output paths), which makes dereferencing the optional argument undefined behavior (can crash or silently corrupt).
### Issue Context
This PR introduced `q_T_sf` support into BC fill + MPI exchange, but the implementation currently relies on an implicit contract (“if chemistry then q_T_sf is always passed”) that the codebase does not consistently satisfy.
### Fix Focus Areas
- Add `present(q_T_sf)` guards around all `q_T_sf%sf(...)` accesses (and similarly in MPI pack/unpack) so the routines are safe when `q_T_sf` is omitted.
- Ensure temperature communication/ghost filling happens whenever it is truly required (e.g., chemistry+diffusion paths that subsequently compute diffusion fluxes).
- src/common/m_boundary_common.fpp[284-365]
- src/common/m_boundary_common.fpp[827-927]
- src/common/m_boundary_common.fpp[1152-1236]
- src/common/m_mpi_common.fpp[499-593]
- src/common/m_mpi_common.fpp[788-830]

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


8. bc_x Twall not broadcast🐞
Description
post_process MPI input broadcast omits bc_x%Twall_in/out, so non-root ranks keep default wall
temperatures and produce incorrect parallel post-processing results for x-direction isothermal
walls. This is silent data corruption in MPI runs.
Code

src/post_process/m_mpi_proxy.fpp[R132-138]

      #:for VAR in [ 'pref', 'rhoref', 'R0ref', 'poly_sigma', 'Web', 'Ca', &
-            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',   &
-            & 'x_output%beg', 'x_output%end', 'y_output%beg', &
-            & 'y_output%end', 'z_output%beg', 'z_output%end']
+            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',      &
+            & 'x_output%beg', 'x_output%end', 'y_output%beg',    &
+            & 'y_output%end', 'z_output%beg', 'z_output%end',    &
+            & 'bc_y%Twall_in', 'bc_y%Twall_out', 'bc_z%Twall_in',&
+            & 'bc_z%Twall_out']
          call MPI_BCAST(${VAR}$, 1, mpi_p, 0, MPI_COMM_WORLD, ierr)
Evidence
The post_process broadcast list includes bc_y/bc_z Twall fields but not bc_x Twall fields, while
defaults are initialized to dflt_real. Simulation does broadcast all Twall fields, highlighting
the inconsistency.

src/post_process/m_mpi_proxy.fpp[132-139]
src/simulation/m_mpi_proxy.fpp[127-142]
src/simulation/m_global_parameters.fpp[767-772]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
In post_process MPI runs, `bc_x%Twall_in` and `bc_x%Twall_out` are not broadcast from rank 0. Non-root ranks therefore keep default values, producing incorrect post-processing for x-direction isothermal boundaries.
### Issue Context
The simulation and pre_process MPI proxies broadcast all 6 Twall values; post_process broadcasts only y/z.
### Fix Focus Areas
- Add `bc_x%Twall_in` and `bc_x%Twall_out` to the real-parameter `MPI_BCAST` list.
- src/post_process/m_mpi_proxy.fpp[132-139]

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


9. q_T_sf stale after smoothing🐞
Description
pre_process computes q_T_sf before elliptic smoothing, but elliptic smoothing overwrites q_prim_vf
and does not recompute q_T_sf afterward. This leaves q_T_sf inconsistent with the final smoothed
initial condition, and any later use/serialization of q_T_sf (e.g., bc_io boundary buffers) will be
wrong.
Code

src/pre_process/m_initial_condition.fpp[R141-149]

+        if (chemistry) call s_compute_T_from_primitives(q_T_sf, q_prim_vf, idwint)
-        call s_convert_primitive_to_conservative_variables(q_prim_vf, q_cons_vf)
+        if (elliptic_smoothing .and. chemistry) then
+            call s_elliptic_smoothing(q_prim_vf, bc_type, q_T_sf)
+        else if (elliptic_smoothing) then
+            call s_elliptic_smoothing(q_prim_vf, bc_type)
+        end if
-        if (chemistry) call s_compute_T_from_primitives(q_T_sf, q_prim_vf, idwint)
+        call s_convert_primitive_to_conservative_variables(q_prim_vf, q_cons_vf)
Evidence
The initial-condition generator now calls s_compute_T_from_primitives before smoothing. The
smoothing routine updates q_prim_vf in-place but never updates/recomputes q_T_sf. Meanwhile,
boundary-condition buffer packing serializes temperature slices from q_T_sf, so stale values can
be written.

src/pre_process/m_initial_condition.fpp[137-150]
src/pre_process/m_perturbation.fpp[86-137]
src/common/m_boundary_common.fpp[2052-2068]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`q_T_sf` is computed from primitives before elliptic smoothing, but smoothing modifies `q_prim_vf` without updating `q_T_sf`. This makes temperature inconsistent with the final smoothed primitives and can lead to incorrect temperature written/used downstream (notably for `bc_io` temperature buffer serialization).
### Issue Context
`m_perturbation:s_elliptic_smoothing` only uses `q_T_sf` to populate ghost/buffer regions; it never recomputes temperature from the updated primitives.
### Fix Focus Areas
- After elliptic smoothing completes (and before any output/serialization that uses `q_T_sf`), recompute `q_T_sf` from the final `q_prim_vf`.
- Keep the pre-smoothing computation if it is needed to apply isothermal BCs during smoothing, but add a post-smoothing recomputation step.
- src/pre_process/m_initial_condition.fpp[137-150]
- src/pre_process/m_perturbation.fpp[86-137]

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


10. Unsafe optional q_T_sf use🐞
Description
m_boundary_common and MPI comm code write/read q_T_sf under if (chemistry) / `if (chemistry .and.
chem_params%diffusion) without checking present(q_T_sf)`, but several call sites still invoke
buffer population without passing q_T_sf. With chemistry enabled this can dereference an absent
optional argument (undefined behavior: crash or memory corruption).
Code

src/common/m_boundary_common.fpp[R300-304]

+                if (chemistry) then
+                    do j = 1, buff_size
+                        q_T_sf%sf(-j, k, l) = q_T_sf%sf(0, k, l)
+                    end do
+                end if
Evidence
q_T_sf is declared optional but is dereferenced whenever chemistry is true. The repo contains
paths that call s_populate_variables_buffers(...) without q_T_sf (e.g., down_sample in pre_process
and simulation), making the optional argument absent while the callee still dereferences it.

src/common/m_boundary_common.fpp[284-304]
src/common/m_boundary_common.fpp[827-859]
src/common/m_mpi_common.fpp[499-592]
src/pre_process/m_data_output.fpp[409-421]
src/simulation/m_start_up.fpp[741-753]
src/simulation/m_time_steppers.fpp[602-609]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several boundary-condition and MPI-communication routines accept `q_T_sf` as an **optional** argument but dereference it whenever `chemistry` (and sometimes `chem_params%diffusion`) is enabled. There are existing call sites that invoke `s_populate_variables_buffers(...)` without passing `q_T_sf` (e.g., down-sampling output paths), which makes dereferencing the optional argument undefined behavior (can crash or silently corrupt).
### Issue Context
This PR introduced `q_T_sf` support into BC fill + MPI exchange, but the implementation currently relies on an implicit contract (“if chemistry then q_T_sf is always passed”) that the codebase does not consistently satisfy.
### Fix Focus Areas
- Add `present(q_T_sf)` guards around all `q_T_sf%sf(...)` accesses (and similarly in MPI pack/unpack) so the routines are safe when `q_T_sf` is omitted.
- Ensure temperature communication/ghost filling happens whenever it is truly required (e.g., chemistry+diffusion paths that subsequently compute diffusion fluxes).
- src/common/m_boundary_common.fpp[284-365]
- src/common/m_boundary_common.fpp[827-927]
- src/common/m_boundary_common.fpp[1152-1236]
- src/common/m_mpi_common.fpp[499-593]
- src/common/m_mpi_common.fpp[788-830]

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


11. bc_x Twall not broadcast🐞
Description
post_process MPI input broadcast omits bc_x%Twall_in/out, so non-root ranks keep default wall
temperatures and produce incorrect parallel post-processing results for x-direction isothermal
walls. This is silent data corruption in MPI runs.
Code

src/post_process/m_mpi_proxy.fpp[R132-138]

      #:for VAR in [ 'pref', 'rhoref', 'R0ref', 'poly_sigma', 'Web', 'Ca', &
-            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',   &
-            & 'x_output%beg', 'x_output%end', 'y_output%beg', &
-            & 'y_output%end', 'z_output%beg', 'z_output%end']
+            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',      &
+            & 'x_output%beg', 'x_output%end', 'y_output%beg',    &
+            & 'y_output%end', 'z_output%beg', 'z_output%end',    &
+            & 'bc_y%Twall_in', 'bc_y%Twall_out', 'bc_z%Twall_in',&
+            & 'bc_z%Twall_out']
          call MPI_BCAST(${VAR}$, 1, mpi_p, 0, MPI_COMM_WORLD, ierr)
Evidence
The post_process broadcast list includes bc_y/bc_z Twall fields but not bc_x Twall fields, while
defaults are initialized to dflt_real. Simulation does broadcast all Twall fields, highlighting
the inconsistency.

src/post_process/m_mpi_proxy.fpp[132-139]
src/simulation/m_mpi_proxy.fpp[127-142]
src/simulation/m_global_parameters.fpp[767-772]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
In post_process MPI runs, `bc_x%Twall_in` and `bc_x%Twall_out` are not broadcast from rank 0. Non-root ranks therefore keep default values, producing incorrect post-processing for x-direction isothermal boundaries.
### Issue Context
The simulation and pre_process MPI proxies broadcast all 6 Twall values; post_process broadcasts only y/z.
### Fix Focus Areas
- Add `bc_x%Twall_in` and `bc_x%Twall_out` to the real-parameter `MPI_BCAST` list.
- src/post_process/m_mpi_proxy.fpp[132-139]

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


12. q_T_sf stale after smoothing🐞
Description
pre_process computes q_T_sf before elliptic smoothing, but elliptic smoothing overwrites q_prim_vf
and does not recompute q_T_sf afterward. This leaves q_T_sf inconsistent with the final smoothed
initial condition, and any later use/serialization of q_T_sf (e.g., bc_io boundary buffers) will be
wrong.
Code

src/pre_process/m_initial_condition.fpp[R141-149]

+        if (chemistry) call s_compute_T_from_primitives(q_T_sf, q_prim_vf, idwint)
-        call s_convert_primitive_to_conservative_variables(q_prim_vf, q_cons_vf)
+        if (elliptic_smoothing .and. chemistry) then
+            call s_elliptic_smoothing(q_prim_vf, bc_type, q_T_sf)
+        else if (elliptic_smoothing) then
+            call s_elliptic_smoothing(q_prim_vf, bc_type)
+        end if
-        if (chemistry) call s_compute_T_from_primitives(q_T_sf, q_prim_vf, idwint)
+        call s_convert_primitive_to_conservative_variables(q_prim_vf, q_cons_vf)
Evidence
The initial-condition generator now calls s_compute_T_from_primitives before smoothing. The
smoothing routine updates q_prim_vf in-place but never updates/recomputes q_T_sf. Meanwhile,
boundary-condition buffer packing serializes temperature slices from q_T_sf, so stale values can
be written.

src/pre_process/m_initial_condition.fpp[137-150]
src/pre_process/m_perturbation.fpp[86-137]
src/common/m_boundary_common.fpp[2052-2068]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`q_T_sf` is computed from primitives before elliptic smoothing, but smoothing modifies `q_prim_vf` without updating `q_T_sf`. This makes temperature inconsistent with the final smoothed primitives and can lead to incorrect temperature written/used downstream (notably for `bc_io` temperature buffer serialization).
### Issue Context
`m_perturbation:s_elliptic_smoothing` only uses `q_T_sf` to populate ghost/buffer regions; it never recomputes temperature from the updated primitives.
### Fix Focus Areas
- After elliptic smoothing completes (and before any output/serialization that uses `q_T_sf`), recompute `q_T_sf` from the final `q_prim_vf`.
- Keep the pre-smoothing computation if it is needed to apply isothermal BCs during smoothing, but add a post-smoothing recomputation step.
- src/pre_process/m_initial_condition.fpp[137-150]
- src/pre_process/m_perturbation.fpp[86-137]

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


13. MPI q_T_sf presence ignored🐞
Description
s_mpi_sendrecv_variables_buffers packs/unpacks q_T_sf whenever `chemistry .and.
chem_params%diffusion is true, but q_T_sf` is an optional argument and is dereferenced without
present() checks. If any caller omits q_T_sf (currently true in several places), MPI buffer
packing/unpacking will read/write through an absent optional argument.
Code

src/common/m_mpi_common.fpp[R525-531]

+        else if (chemistry .and. chem_params%diffusion) then
+            v_size = nVar + 1
+            buffer_counts = (/buff_size*v_size*(n + 1)*(p + 1), buff_size*v_size*(m + 2*buff_size + 1)*(p + 1), &
+                             & buff_size*v_size*(m + 2*buff_size + 1)*(n + 2*buff_size + 1)/)
      else
          v_size = nVar
          buffer_counts = (/buff_size*v_size*(n + 1)*(p + 1), buff_size*v_size*(m + 2*buff_size + 1)*(p + 1), &
Evidence
The MPI routine sizes v_size = nVar + 1 under chemistry .and. chem_params%diffusion and then
directly reads/writes q_T_sf%sf(...) under the same condition, but q_T_sf is declared optional
and never checked with present(q_T_sf). This is unsafe when upstream calls omit the argument.

src/common/m_mpi_common.fpp[499-593]
src/common/m_mpi_common.fpp[520-533]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
MPI send/recv for ghost buffers packs/unpacks temperature (`q_T_sf`) based solely on global flags (`chemistry` and `chem_params%diffusion`) even though `q_T_sf` is an optional dummy argument. This can dereference an absent optional argument and crash.
### Issue Context
Some code paths call `s_mpi_sendrecv_variables_buffers` (via `s_populate_variables_buffers`) without providing `q_T_sf` (e.g., conservative-only communications and downsample paths). The MPI routine must not assume `q_T_sf` is present.
### Fix Focus Areas
- src/common/m_mpi_common.fpp[499-593]
- src/common/m_mpi_common.fpp[564-624]
- src/common/m_mpi_common.fpp[625-713]
- src/common/m_mpi_common.fpp[756-974]
### What to change
1. Introduce a local logical like `has_T = present(q_T_sf) .and. chemistry .and. chem_params%diffusion`.
2. Use `has_T` (not just chemistry/diffusion) to:
- set `v_size` (nVar vs nVar+1)
- compute `buffer_counts`
- pack `buff_send` temperature slot
- unpack into `q_T_sf`
3. Optionally add a defensive abort if `chemistry .and. chem_params%diffusion` is true in a code path that *requires* temperature exchange but `present(q_T_sf)` is false.

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


14. Missing bc_x Twall broadcast🐞
Description
In post_process MPI input broadcast, bc_x%Twall_in/out are not broadcast to non-root ranks,
leaving them at the default dflt_real and making post-processing rank-dependent for x-isothermal
cases. Post_process does call boundary buffer population (which can apply isothermal logic), so
incorrect bc_x%Twall_* affects computed ghost temperatures and derived outputs.
Code

src/post_process/m_mpi_proxy.fpp[R132-138]

      #:for VAR in [ 'pref', 'rhoref', 'R0ref', 'poly_sigma', 'Web', 'Ca', &
-            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',   &
-            & 'x_output%beg', 'x_output%end', 'y_output%beg', &
-            & 'y_output%end', 'z_output%beg', 'z_output%end']
+            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',      &
+            & 'x_output%beg', 'x_output%end', 'y_output%beg',    &
+            & 'y_output%end', 'z_output%beg', 'z_output%end',    &
+            & 'bc_y%Twall_in', 'bc_y%Twall_out', 'bc_z%Twall_in',&
+            & 'bc_z%Twall_out']
          call MPI_BCAST(${VAR}$, 1, mpi_p, 0, MPI_COMM_WORLD, ierr)
Evidence
s_mpi_bcast_user_inputs in post_process broadcasts bc_y%Twall_* and bc_z%Twall_* but omits
bc_x%Twall_*. Post_process then computes temperature and calls s_populate_variables_buffers,
which applies wall temperature logic (e.g., in slip wall) using bc_x%Twall_in/bc_x%Twall_out;
without broadcasting, only rank 0 has the intended values.

src/post_process/m_mpi_proxy.fpp[132-139]
src/post_process/m_start_up.fpp[166-173]
src/common/m_boundary_common.fpp[849-857]
src/pre_process/m_mpi_proxy.fpp[55-66]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Post-process MPI broadcast omits `bc_x%Twall_in` and `bc_x%Twall_out`, leaving defaults on non-root ranks. Post-process uses boundary-condition population that can reference these values, producing incorrect ghost temperatures and rank-dependent derived outputs.
### Issue Context
- pre_process already broadcasts `bc_x%Twall_*` alongside y/z.
- post_process should broadcast the same set for consistency.
### Fix Focus Areas
- src/post_process/m_mpi_proxy.fpp[132-139]
### What to change
Add `bc_x%Twall_in` and `bc_x%Twall_out` to the real-parameter broadcast list in `s_mpi_bcast_user_inputs` (and keep ordering consistent with pre_process/simulation if possible).

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


15. Twall not required by validator 🐞
Description
The case validator permits bc_*%isothermal_in/out = T without requiring the corresponding
bc_*%Twall_in/out to be provided, leaving it at dflt_real = -1e6. Boundary routines use
Twall_* directly to set ghost temperatures, so missing Twall configuration yields wildly
unphysical temperatures.
Code

toolchain/mfc/case_validator.py[R1291-1316]

+        # Fetch global chemistry and diffusion flags
+        chemistry = self.get("chemistry", "F") == "T"
+        diffusion = self.get("chem_params%diffusion", "F") == "T"
+
+        # Define what constitutes a wall (-15 for slip, -16 for no-slip)
+        wall_bcs = [-15, -16]
+
+        for dir in ["x", "y", "z"]:
+            isothermal_in = self.get(f"bc_{dir}%isothermal_in", "F") == "T"
+            isothermal_out = self.get(f"bc_{dir}%isothermal_out", "F") == "T"
+            bc_beg = self.get(f"bc_{dir}%beg")
+            bc_end = self.get(f"bc_{dir}%end")
+
+            if isothermal_in:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal In (bc_{dir}%isothermal_in) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_beg not in wall_bcs, f"Isothermal In (bc_{dir}%isothermal_in) requires a wall. Set bc_{dir}%beg to -15 (slip) or -16 (no-slip).")
+
+            if isothermal_out:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal Out (bc_{dir}%isothermal_out) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_end not in wall_bcs, f"Isothermal Out (bc_{dir}%isothermal_out) requires a wall. Set bc_{dir}%end to -15 (slip) or -16 (no-slip).")
Evidence
check_chemistry validates that isothermal flags require chemistry+diffusion and wall BC types, but
never checks Twall_in/out are set. Meanwhile, global parameters initialize Twall_* to
dflt_real and dflt_real is defined as -1e6, and the wall BC implementation uses bc_x%Twall_in
directly in the ghost-cell temperature formula.

toolchain/mfc/case_validator.py[1284-1316]
src/pre_process/m_global_parameters.fpp[305-310]
src/common/m_constants.fpp[10-13]
src/common/m_boundary_common.fpp[849-857]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The validator allows enabling isothermal walls without specifying `Twall_in/out`, which defaults to `dflt_real=-1e6` and is then used directly by the wall BC implementation to set ghost temperatures.
### Issue Context
- `bc_[xyz]%Twall_in/out` defaults to `dflt_real`.
- `check_chemistry()` already checks compatibility (chemistry+diffusion, wall bc codes) but not presence/validity of Twall.
### Fix Focus Areas
- toolchain/mfc/case_validator.py[1284-1316]
- src/common/m_constants.fpp[10-13]
### What to change
1. When `bc_{dir}%isothermal_in` is true:
- require `bc_{dir}%Twall_in` is provided
- require it is numeric (or at least not missing)
- require it is not equal to the default sentinel (e.g., `-1e6`) and is physically reasonable (e.g., `> 0` K)
2. Similarly, when `bc_{dir}%isothermal_out` is true, validate `bc_{dir}%Twall_out`.
3. Produce clear error messages pointing to the missing Twall parameter.

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



Remediation recommended

16. Twall not required 🐞
Description
The validator enforces chemistry/diffusion and wall BC codes for isothermal flags but does not
require Twall_in/out to be provided, so cases can run using default/sentinel wall temperatures and
silently compute incorrect ghost temperatures/heat fluxes. This allows misconfigured isothermal
boundaries to pass validation.
Code

toolchain/mfc/case_validator.py[R1291-1316]

+        # Fetch global chemistry and diffusion flags
+        chemistry = self.get("chemistry", "F") == "T"
+        diffusion = self.get("chem_params%diffusion", "F") == "T"
+
+        # Define what constitutes a wall (-15 for slip, -16 for no-slip)
+        wall_bcs = [-15, -16]
+
+        for dir in ["x", "y", "z"]:
+            isothermal_in = self.get(f"bc_{dir}%isothermal_in", "F") == "T"
+            isothermal_out = self.get(f"bc_{dir}%isothermal_out", "F") == "T"
+            bc_beg = self.get(f"bc_{dir}%beg")
+            bc_end = self.get(f"bc_{dir}%end")
+
+            if isothermal_in:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal In (bc_{dir}%isothermal_in) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_beg not in wall_bcs, f"Isothermal In (bc_{dir}%isothermal_in) requires a wall. Set bc_{dir}%beg to -15 (slip) or -16 (no-slip).")
+
+            if isothermal_out:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal Out (bc_{dir}%isothermal_out) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_end not in wall_bcs, f"Isothermal Out (bc_{dir}%isothermal_out) requires a wall. Set bc_{dir}%end to -15 (slip) or -16 (no-slip).")
Evidence
When isothermal is enabled, ghost temperature is computed using bc_%Twall_*, which defaults to
dflt_real in global parameter initialization. The new validator logic never checks
presence/validity of Twall_in/out, so missing Twall inputs won’t be caught pre-run.

toolchain/mfc/case_validator.py[1291-1316]
src/simulation/m_global_parameters.fpp[767-772]
src/common/m_boundary_common.fpp[849-858]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Cases can enable `bc_%isothermal_in/out` without providing `bc_%Twall_in/out`. The code will then use default/sentinel values for wall temperature in ghost temperature calculations, producing incorrect results without a clear validation error.
### Issue Context
The PR adds validation for chemistry/diffusion and wall BC codes, but it does not validate that the required wall temperature is actually supplied.
### Fix Focus Areas
- When `bc_{dir}%isothermal_in` is true, prohibit missing `bc_{dir}%Twall_in`.
- When `bc_{dir}%isothermal_out` is true, prohibit missing `bc_{dir}%Twall_out`.
- Optionally: validate Twall is numeric and > 0.
- toolchain/mfc/case_validator.py[1291-1316]

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


17. Twall not required 🐞
Description
The validator enforces chemistry/diffusion and wall BC codes for isothermal flags but does not
require Twall_in/out to be provided, so cases can run using default/sentinel wall temperatures and
silently compute incorrect ghost temperatures/heat fluxes. This allows misconfigured isothermal
boundaries to pass validation.
Code

toolchain/mfc/case_validator.py[R1291-1316]

+        # Fetch global chemistry and diffusion flags
+        chemistry = self.get("chemistry", "F") == "T"
+        diffusion = self.get("chem_params%diffusion", "F") == "T"
+
+        # Define what constitutes a wall (-15 for slip, -16 for no-slip)
+        wall_bcs = [-15, -16]
+
+        for dir in ["x", "y", "z"]:
+            isothermal_in = self.get(f"bc_{dir}%isothermal_in", "F") == "T"
+            isothermal_out = self.get(f"bc_{dir}%isothermal_out", "F") == "T"
+            bc_beg = self.get(f"bc_{dir}%beg")
+            bc_end = self.get(f"bc_{dir}%end")
+
+            if isothermal_in:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal In (bc_{dir}%isothermal_in) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_beg not in wall_bcs, f"Isothermal In (bc_{dir}%isothermal_in) requires a wall. Set bc_{dir}%beg to -15 (slip) or -16 (no-slip).")
+
+            if isothermal_out:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal Out (bc_{dir}%isothermal_out) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit i...

Comment thread src/common/m_boundary_common.fpp Outdated
Comment thread src/post_process/m_mpi_proxy.fpp
Comment thread src/pre_process/m_initial_condition.fpp
@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review bot commented Apr 10, 2026

Code Review by Qodo

🐞 Bugs (7)   📘 Rule violations (0)   📎 Requirement gaps (0)   🎨 UX Issues (0)
🐞\ ≡ Correctness (5) ☼ Reliability (2) ⭐ New (3)

Grey Divider


Action required

1. MPI q_T_sf presence ignored 🐞
Description
s_mpi_sendrecv_variables_buffers packs/unpacks q_T_sf whenever `chemistry .and.
chem_params%diffusion is true, but q_T_sf` is an optional argument and is dereferenced without
present() checks. If any caller omits q_T_sf (currently true in several places), MPI buffer
packing/unpacking will read/write through an absent optional argument.
Code

src/common/m_mpi_common.fpp[R525-531]

+        else if (chemistry .and. chem_params%diffusion) then
+            v_size = nVar + 1
+            buffer_counts = (/buff_size*v_size*(n + 1)*(p + 1), buff_size*v_size*(m + 2*buff_size + 1)*(p + 1), &
+                             & buff_size*v_size*(m + 2*buff_size + 1)*(n + 2*buff_size + 1)/)
        else
            v_size = nVar
            buffer_counts = (/buff_size*v_size*(n + 1)*(p + 1), buff_size*v_size*(m + 2*buff_size + 1)*(p + 1), &
Evidence
The MPI routine sizes v_size = nVar + 1 under chemistry .and. chem_params%diffusion and then
directly reads/writes q_T_sf%sf(...) under the same condition, but q_T_sf is declared optional
and never checked with present(q_T_sf). This is unsafe when upstream calls omit the argument.

src/common/m_mpi_common.fpp[499-593]
src/common/m_mpi_common.fpp[520-533]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
MPI send/recv for ghost buffers packs/unpacks temperature (`q_T_sf`) based solely on global flags (`chemistry` and `chem_params%diffusion`) even though `q_T_sf` is an optional dummy argument. This can dereference an absent optional argument and crash.

### Issue Context
Some code paths call `s_mpi_sendrecv_variables_buffers` (via `s_populate_variables_buffers`) without providing `q_T_sf` (e.g., conservative-only communications and downsample paths). The MPI routine must not assume `q_T_sf` is present.

### Fix Focus Areas
- src/common/m_mpi_common.fpp[499-593]
- src/common/m_mpi_common.fpp[564-624]
- src/common/m_mpi_common.fpp[625-713]
- src/common/m_mpi_common.fpp[756-974]

### What to change
1. Introduce a local logical like `has_T = present(q_T_sf) .and. chemistry .and. chem_params%diffusion`.
2. Use `has_T` (not just chemistry/diffusion) to:
  - set `v_size` (nVar vs nVar+1)
  - compute `buffer_counts`
  - pack `buff_send` temperature slot
  - unpack into `q_T_sf`
3. Optionally add a defensive abort if `chemistry .and. chem_params%diffusion` is true in a code path that *requires* temperature exchange but `present(q_T_sf)` is false.

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


2. Missing bc_x Twall broadcast 🐞
Description
In post_process MPI input broadcast, bc_x%Twall_in/out are not broadcast to non-root ranks,
leaving them at the default dflt_real and making post-processing rank-dependent for x-isothermal
cases. Post_process does call boundary buffer population (which can apply isothermal logic), so
incorrect bc_x%Twall_* affects computed ghost temperatures and derived outputs.
Code

src/post_process/m_mpi_proxy.fpp[R132-138]

        #:for VAR in [ 'pref', 'rhoref', 'R0ref', 'poly_sigma', 'Web', 'Ca', &
-            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',   &
-            & 'x_output%beg', 'x_output%end', 'y_output%beg', &
-            & 'y_output%end', 'z_output%beg', 'z_output%end']
+            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',      &
+            & 'x_output%beg', 'x_output%end', 'y_output%beg',    &
+            & 'y_output%end', 'z_output%beg', 'z_output%end',    &
+            & 'bc_y%Twall_in', 'bc_y%Twall_out', 'bc_z%Twall_in',&
+            & 'bc_z%Twall_out']
            call MPI_BCAST(${VAR}$, 1, mpi_p, 0, MPI_COMM_WORLD, ierr)
Evidence
s_mpi_bcast_user_inputs in post_process broadcasts bc_y%Twall_* and bc_z%Twall_* but omits
bc_x%Twall_*. Post_process then computes temperature and calls s_populate_variables_buffers,
which applies wall temperature logic (e.g., in slip wall) using bc_x%Twall_in/bc_x%Twall_out;
without broadcasting, only rank 0 has the intended values.

src/post_process/m_mpi_proxy.fpp[132-139]
src/post_process/m_start_up.fpp[166-173]
src/common/m_boundary_common.fpp[849-857]
src/pre_process/m_mpi_proxy.fpp[55-66]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Post-process MPI broadcast omits `bc_x%Twall_in` and `bc_x%Twall_out`, leaving defaults on non-root ranks. Post-process uses boundary-condition population that can reference these values, producing incorrect ghost temperatures and rank-dependent derived outputs.

### Issue Context
- pre_process already broadcasts `bc_x%Twall_*` alongside y/z.
- post_process should broadcast the same set for consistency.

### Fix Focus Areas
- src/post_process/m_mpi_proxy.fpp[132-139]

### What to change
Add `bc_x%Twall_in` and `bc_x%Twall_out` to the real-parameter broadcast list in `s_mpi_bcast_user_inputs` (and keep ordering consistent with pre_process/simulation if possible).

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


3. Twall not required by validator 🐞
Description
The case validator permits bc_*%isothermal_in/out = T without requiring the corresponding
bc_*%Twall_in/out to be provided, leaving it at dflt_real = -1e6. Boundary routines use
Twall_* directly to set ghost temperatures, so missing Twall configuration yields wildly
unphysical temperatures.
Code

toolchain/mfc/case_validator.py[R1291-1316]

+        # Fetch global chemistry and diffusion flags
+        chemistry = self.get("chemistry", "F") == "T"
+        diffusion = self.get("chem_params%diffusion", "F") == "T"
+
+        # Define what constitutes a wall (-15 for slip, -16 for no-slip)
+        wall_bcs = [-15, -16]
+
+        for dir in ["x", "y", "z"]:
+            isothermal_in = self.get(f"bc_{dir}%isothermal_in", "F") == "T"
+            isothermal_out = self.get(f"bc_{dir}%isothermal_out", "F") == "T"
+            bc_beg = self.get(f"bc_{dir}%beg")
+            bc_end = self.get(f"bc_{dir}%end")
+
+            if isothermal_in:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal In (bc_{dir}%isothermal_in) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_beg not in wall_bcs, f"Isothermal In (bc_{dir}%isothermal_in) requires a wall. Set bc_{dir}%beg to -15 (slip) or -16 (no-slip).")
+
+            if isothermal_out:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal Out (bc_{dir}%isothermal_out) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_end not in wall_bcs, f"Isothermal Out (bc_{dir}%isothermal_out) requires a wall. Set bc_{dir}%end to -15 (slip) or -16 (no-slip).")
Evidence
check_chemistry validates that isothermal flags require chemistry+diffusion and wall BC types, but
never checks Twall_in/out are set. Meanwhile, global parameters initialize Twall_* to
dflt_real and dflt_real is defined as -1e6, and the wall BC implementation uses bc_x%Twall_in
directly in the ghost-cell temperature formula.

toolchain/mfc/case_validator.py[1284-1316]
src/pre_process/m_global_parameters.fpp[305-310]
src/common/m_constants.fpp[10-13]
src/common/m_boundary_common.fpp[849-857]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The validator allows enabling isothermal walls without specifying `Twall_in/out`, which defaults to `dflt_real=-1e6` and is then used directly by the wall BC implementation to set ghost temperatures.

### Issue Context
- `bc_[xyz]%Twall_in/out` defaults to `dflt_real`.
- `check_chemistry()` already checks compatibility (chemistry+diffusion, wall bc codes) but not presence/validity of Twall.

### Fix Focus Areas
- toolchain/mfc/case_validator.py[1284-1316]
- src/common/m_constants.fpp[10-13]

### What to change
1. When `bc_{dir}%isothermal_in` is true:
  - require `bc_{dir}%Twall_in` is provided
  - require it is numeric (or at least not missing)
  - require it is not equal to the default sentinel (e.g., `-1e6`) and is physically reasonable (e.g., `> 0` K)
2. Similarly, when `bc_{dir}%isothermal_out` is true, validate `bc_{dir}%Twall_out`.
3. Produce clear error messages pointing to the missing Twall parameter.

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


View more (3)
4. Unsafe optional q_T_sf use 🐞
Description
m_boundary_common and MPI comm code write/read q_T_sf under if (chemistry) / `if (chemistry .and.
chem_params%diffusion) without checking present(q_T_sf)`, but several call sites still invoke
buffer population without passing q_T_sf. With chemistry enabled this can dereference an absent
optional argument (undefined behavior: crash or memory corruption).
Code

src/common/m_boundary_common.fpp[R300-304]

+                if (chemistry) then
+                    do j = 1, buff_size
+                        q_T_sf%sf(-j, k, l) = q_T_sf%sf(0, k, l)
+                    end do
+                end if
Evidence
q_T_sf is declared optional but is dereferenced whenever chemistry is true. The repo contains
paths that call s_populate_variables_buffers(...) without q_T_sf (e.g., down_sample in pre_process
and simulation), making the optional argument absent while the callee still dereferences it.

src/common/m_boundary_common.fpp[284-304]
src/common/m_boundary_common.fpp[827-859]
src/common/m_mpi_common.fpp[499-592]
src/pre_process/m_data_output.fpp[409-421]
src/simulation/m_start_up.fpp[741-753]
src/simulation/m_time_steppers.fpp[602-609]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several boundary-condition and MPI-communication routines accept `q_T_sf` as an **optional** argument but dereference it whenever `chemistry` (and sometimes `chem_params%diffusion`) is enabled. There are existing call sites that invoke `s_populate_variables_buffers(...)` without passing `q_T_sf` (e.g., down-sampling output paths), which makes dereferencing the optional argument undefined behavior (can crash or silently corrupt).
### Issue Context
This PR introduced `q_T_sf` support into BC fill + MPI exchange, but the implementation currently relies on an implicit contract (“if chemistry then q_T_sf is always passed”) that the codebase does not consistently satisfy.
### Fix Focus Areas
- Add `present(q_T_sf)` guards around all `q_T_sf%sf(...)` accesses (and similarly in MPI pack/unpack) so the routines are safe when `q_T_sf` is omitted.
- Ensure temperature communication/ghost filling happens whenever it is truly required (e.g., chemistry+diffusion paths that subsequently compute diffusion fluxes).
- src/common/m_boundary_common.fpp[284-365]
- src/common/m_boundary_common.fpp[827-927]
- src/common/m_boundary_common.fpp[1152-1236]
- src/common/m_mpi_common.fpp[499-593]
- src/common/m_mpi_common.fpp[788-830]

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


5. bc_x Twall not broadcast 🐞
Description
post_process MPI input broadcast omits bc_x%Twall_in/out, so non-root ranks keep default wall
temperatures and produce incorrect parallel post-processing results for x-direction isothermal
walls. This is silent data corruption in MPI runs.
Code

src/post_process/m_mpi_proxy.fpp[R132-138]

       #:for VAR in [ 'pref', 'rhoref', 'R0ref', 'poly_sigma', 'Web', 'Ca', &
-            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',   &
-            & 'x_output%beg', 'x_output%end', 'y_output%beg', &
-            & 'y_output%end', 'z_output%beg', 'z_output%end']
+            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',      &
+            & 'x_output%beg', 'x_output%end', 'y_output%beg',    &
+            & 'y_output%end', 'z_output%beg', 'z_output%end',    &
+            & 'bc_y%Twall_in', 'bc_y%Twall_out', 'bc_z%Twall_in',&
+            & 'bc_z%Twall_out']
           call MPI_BCAST(${VAR}$, 1, mpi_p, 0, MPI_COMM_WORLD, ierr)
Evidence
The post_process broadcast list includes bc_y/bc_z Twall fields but not bc_x Twall fields, while
defaults are initialized to dflt_real. Simulation does broadcast all Twall fields, highlighting
the inconsistency.

src/post_process/m_mpi_proxy.fpp[132-139]
src/simulation/m_mpi_proxy.fpp[127-142]
src/simulation/m_global_parameters.fpp[767-772]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
In post_process MPI runs, `bc_x%Twall_in` and `bc_x%Twall_out` are not broadcast from rank 0. Non-root ranks therefore keep default values, producing incorrect post-processing for x-direction isothermal boundaries.
### Issue Context
The simulation and pre_process MPI proxies broadcast all 6 Twall values; post_process broadcasts only y/z.
### Fix Focus Areas
- Add `bc_x%Twall_in` and `bc_x%Twall_out` to the real-parameter `MPI_BCAST` list.
- src/post_process/m_mpi_proxy.fpp[132-139]

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


6. q_T_sf stale after smoothing 🐞
Description
pre_process computes q_T_sf before elliptic smoothing, but elliptic smoothing overwrites q_prim_vf
and does not recompute q_T_sf afterward. This leaves q_T_sf inconsistent with the final smoothed
initial condition, and any later use/serialization of q_T_sf (e.g., bc_io boundary buffers) will be
wrong.
Code

src/pre_process/m_initial_condition.fpp[R141-149]

+        if (chemistry) call s_compute_T_from_primitives(q_T_sf, q_prim_vf, idwint)

-        call s_convert_primitive_to_conservative_variables(q_prim_vf, q_cons_vf)
+        if (elliptic_smoothing .and. chemistry) then
+            call s_elliptic_smoothing(q_prim_vf, bc_type, q_T_sf)
+        else if (elliptic_smoothing) then
+            call s_elliptic_smoothing(q_prim_vf, bc_type)
+        end if

-        if (chemistry) call s_compute_T_from_primitives(q_T_sf, q_prim_vf, idwint)
+        call s_convert_primitive_to_conservative_variables(q_prim_vf, q_cons_vf)
Evidence
The initial-condition generator now calls s_compute_T_from_primitives before smoothing. The
smoothing routine updates q_prim_vf in-place but never updates/recomputes q_T_sf. Meanwhile,
boundary-condition buffer packing serializes temperature slices from q_T_sf, so stale values can
be written.

src/pre_process/m_initial_condition.fpp[137-150]
src/pre_process/m_perturbation.fpp[86-137]
src/common/m_boundary_common.fpp[2052-2068]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`q_T_sf` is computed from primitives before elliptic smoothing, but smoothing modifies `q_prim_vf` without updating `q_T_sf`. This makes temperature inconsistent with the final smoothed primitives and can lead to incorrect temperature written/used downstream (notably for `bc_io` temperature buffer serialization).
### Issue Context
`m_perturbation:s_elliptic_smoothing` only uses `q_T_sf` to populate ghost/buffer regions; it never recomputes temperature from the updated primitives.
### Fix Focus Areas
- After elliptic smoothing completes (and before any output/serialization that uses `q_T_sf`), recompute `q_T_sf` from the final `q_prim_vf`.
- Keep the pre-smoothing computation if it is needed to apply isothermal BCs during smoothing, but add a post-smoothing recomputation step.
- src/pre_process/m_initial_condition.fpp[137-150]
- src/pre_process/m_perturbation.fpp[86-137]

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



Remediation recommended

7. Twall not required 🐞
Description
The validator enforces chemistry/diffusion and wall BC codes for isothermal flags but does not
require Twall_in/out to be provided, so cases can run using default/sentinel wall temperatures and
silently compute incorrect ghost temperatures/heat fluxes. This allows misconfigured isothermal
boundaries to pass validation.
Code

toolchain/mfc/case_validator.py[R1291-1316]

+        # Fetch global chemistry and diffusion flags
+        chemistry = self.get("chemistry", "F") == "T"
+        diffusion = self.get("chem_params%diffusion", "F") == "T"
+
+        # Define what constitutes a wall (-15 for slip, -16 for no-slip)
+        wall_bcs = [-15, -16]
+
+        for dir in ["x", "y", "z"]:
+            isothermal_in = self.get(f"bc_{dir}%isothermal_in", "F") == "T"
+            isothermal_out = self.get(f"bc_{dir}%isothermal_out", "F") == "T"
+            bc_beg = self.get(f"bc_{dir}%beg")
+            bc_end = self.get(f"bc_{dir}%end")
+
+            if isothermal_in:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal In (bc_{dir}%isothermal_in) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_beg not in wall_bcs, f"Isothermal In (bc_{dir}%isothermal_in) requires a wall. Set bc_{dir}%beg to -15 (slip) or -16 (no-slip).")
+
+            if isothermal_out:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal Out (bc_{dir}%isothermal_out) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_end not in wall_bcs, f"Isothermal Out (bc_{dir}%isothermal_out) requires a wall. Set bc_{dir}%end to -15 (slip) or -16 (no-slip).")
Evidence
When isothermal is enabled, ghost temperature is computed using bc_%Twall_*, which defaults to
dflt_real in global parameter initialization. The new validator logic never checks
presence/validity of Twall_in/out, so missing Twall inputs won’t be caught pre-run.

toolchain/mfc/case_validator.py[1291-1316]
src/simulation/m_global_parameters.fpp[767-772]
src/common/m_boundary_common.fpp[849-858]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Cases can enable `bc_%isothermal_in/out` without providing `bc_%Twall_in/out`. The code will then use default/sentinel values for wall temperature in ghost temperature calculations, producing incorrect results without a clear validation error.
### Issue Context
The PR adds validation for chemistry/diffusion and wall BC codes, but it does not validate that the required wall temperature is actually supplied.
### Fix Focus Areas
- When `bc_{dir}%isothermal_in` is true, prohibit missing `bc_{dir}%Twall_in`.
- When `bc_{dir}%isothermal_out` is true, prohibit missing `bc_{dir}%Twall_out`.
- Optionally: validate Twall is numeric and > 0.
- toolchain/mfc/case_validator.py[1291-1316]

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


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 10, 2026

📝 Walkthrough

Walkthrough

This pull request adds support for chemistry-specific isothermal boundary conditions by introducing a separate temperature scalar field (q_T_sf) that is managed independently from the primary fluid state. New boundary parameters (isothermal_in, isothermal_out, Twall_in, Twall_out) are added to each directional boundary (bc_x, bc_y, bc_z) to control temperature at slip/no-slip walls. The changes extend MPI communication, boundary condition application, data serialization, and initial condition logic to propagate this temperature field throughout pre-processing, simulation, and post-processing phases. Two new test cases demonstrate the functionality: a 1D dual isothermal wall gradient scenario and a 2D isothermal flat plate. Documentation and parameter validation are updated accordingly.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is incomplete and missing critical information. It lacks proper structure, has a typo ('geometries/'), incomplete sentences, and fails to comprehensively explain the implementation details and design decisions. Complete the description by fixing the typo, removing placeholder text like 'Fixes #(issue)', providing clear motivation, and explaining implementation approach and key design decisions.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main feature being added: isothermal boundary conditions for reacting flows.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/simulation/m_rhs.fpp (1)

582-589: ⚠️ Potential issue | 🔴 Critical

Pass q_T_sf through the IGR communication path as well.

Line 588 fixes the finite-volume branch, but when igr = .true. the earlier s_populate_variables_buffers(...) call still executes without q_T_sf. That leaves the chemistry temperature field out of the boundary/MPI population path for IGR cases, so isothermal-wall chemistry runs can still fail or use stale temperature halos.

Suggested fix
         if (igr .or. dummy) then
             call nvtxStartRange("RHS-COMMUNICATION")
-            call s_populate_variables_buffers(bc_type, q_cons_vf, pb_in, mv_in)
+            call s_populate_variables_buffers(bc_type, q_cons_vf, pb_in, mv_in, q_T_sf)
             call nvtxEndRange
         end if

Based on learnings: Applies to src/{common,simulation}/**/*.{fpp,f90} : Ensure MPI pack/unpack logic is updated consistently across all sweep directions (e.g., x, y, z dimensions).

src/common/m_mpi_common.fpp (1)

499-512: ⚠️ Potential issue | 🔴 Critical

Guard q_T_sf with present(...) before using it.

q_T_sf is optional here, but the new chemistry-diffusion path is enabled from global flags alone. Every added pack/unpack block below dereferences q_T_sf%sf(...), so any caller that omits the actual argument will hit undefined behavior.

💡 Suggested fix
-        else if (chemistry .and. chem_params%diffusion) then
+        else if (chemistry .and. chem_params%diffusion .and. present(q_T_sf)) then
             v_size = nVar + 1
             buffer_counts = (/buff_size*v_size*(n + 1)*(p + 1), buff_size*v_size*(m + 2*buff_size + 1)*(p + 1), &
                              & buff_size*v_size*(m + 2*buff_size + 1)*(n + 2*buff_size + 1)/)
+        if (chemistry .and. chem_params%diffusion .and. .not. present(q_T_sf)) then
+            call s_mpi_abort('q_T_sf must be present when chemistry diffusion halo exchange is enabled')
+        end if

Also applies to: 525-528

src/pre_process/m_data_output.fpp (1)

87-92: ⚠️ Potential issue | 🔴 Critical

Pass q_T_sf through the igr boundary-output path too.

Only the non-igr calls were updated. In chemistry/isothermal cases with igr = T, the boundary writers still get called without q_T_sf, which leaves the downstream BC packing path without the temperature field.

💡 Suggested fix
         if (bc_io) then
             if (igr) then
-                call s_write_serial_boundary_condition_files(q_cons_vf, bc_type, t_step_dir, old_grid)
+                call s_write_serial_boundary_condition_files(q_cons_vf, bc_type, t_step_dir, old_grid, q_T_sf)
             else
                 call s_write_serial_boundary_condition_files(q_prim_vf, bc_type, t_step_dir, old_grid, q_T_sf)
             end if
         end if
         if (bc_io) then
             if (igr) then
-                call s_write_parallel_boundary_condition_files(q_cons_vf, bc_type)
+                call s_write_parallel_boundary_condition_files(q_cons_vf, bc_type, q_T_sf)
             else
                 call s_write_parallel_boundary_condition_files(q_prim_vf, bc_type, q_T_sf)
             end if
         end if

Also applies to: 565-570

🧹 Nitpick comments (1)
toolchain/mfc/case_validator.py (1)

1298-1298: Rename dir to avoid Ruff A001.

Using dir here shadows the Python builtin and already triggers the linter. direction or axis would keep this clean.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dea48334-e1e4-4f3b-b300-e56f569f927b

📥 Commits

Reviewing files that changed from the base of the PR and between 23bbd4f and c0c2f90.

📒 Files selected for processing (31)
  • CMakeLists.txt
  • docs/documentation/case.md
  • examples/2D_Thermal_Flatplate/case.py
  • src/common/include/1dHardcodedIC.fpp
  • src/common/include/2dHardcodedIC.fpp
  • src/common/m_boundary_common.fpp
  • src/common/m_chemistry.fpp
  • src/common/m_derived_types.fpp
  • src/common/m_mpi_common.fpp
  • src/post_process/m_global_parameters.fpp
  • src/post_process/m_mpi_proxy.fpp
  • src/post_process/m_start_up.fpp
  • src/pre_process/m_data_output.fpp
  • src/pre_process/m_global_parameters.fpp
  • src/pre_process/m_initial_condition.fpp
  • src/pre_process/m_mpi_proxy.fpp
  • src/pre_process/m_perturbation.fpp
  • src/pre_process/m_start_up.fpp
  • src/simulation/m_data_output.fpp
  • src/simulation/m_global_parameters.fpp
  • src/simulation/m_mpi_proxy.fpp
  • src/simulation/m_rhs.fpp
  • src/simulation/m_start_up.fpp
  • tests/4587725A/golden-metadata.txt
  • tests/4587725A/golden.txt
  • tests/8E56ACC1/golden-metadata.txt
  • tests/8E56ACC1/golden.txt
  • toolchain/mfc/case_validator.py
  • toolchain/mfc/params/definitions.py
  • toolchain/mfc/params/descriptions.py
  • toolchain/mfc/test/cases.py

Comment thread docs/documentation/case.md Outdated
Comment thread examples/2D_Thermal_Flatplate/case.py
Comment thread src/common/include/1dHardcodedIC.fpp Outdated
Comment thread src/common/m_boundary_common.fpp Outdated
Comment thread src/common/m_mpi_common.fpp
Comment thread src/post_process/m_mpi_proxy.fpp Outdated
Comment thread src/pre_process/m_perturbation.fpp
Comment thread toolchain/mfc/case_validator.py
Comment thread toolchain/mfc/params/definitions.py Outdated
Comment thread toolchain/mfc/params/descriptions.py Outdated
Comment thread src/common/m_mpi_common.fpp Outdated
Comment thread src/post_process/m_mpi_proxy.fpp
Comment thread toolchain/mfc/case_validator.py
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 10, 2026

Codecov Report

❌ Patch coverage is 63.93443% with 88 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.60%. Comparing base (649d71a) to head (f39b1fc).

Files with missing lines Patch % Lines
src/common/m_boundary_common.fpp 61.67% 49 Missing and 15 partials ⚠️
src/common/m_mpi_common.fpp 54.76% 19 Missing ⚠️
src/pre_process/m_initial_condition.fpp 57.14% 2 Missing and 1 partial ⚠️
src/common/m_chemistry.fpp 0.00% 1 Missing ⚠️
src/simulation/m_data_output.fpp 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1361      +/-   ##
==========================================
- Coverage   64.62%   64.60%   -0.02%     
==========================================
  Files          71       71              
  Lines       18407    18566     +159     
  Branches     1516     1541      +25     
==========================================
+ Hits        11895    11995     +100     
- Misses       5555     5620      +65     
+ Partials      957      951       -6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sbryngelson sbryngelson marked this pull request as draft April 11, 2026 02:23
@DimAdam-01 DimAdam-01 marked this pull request as ready for review April 11, 2026 17:30
@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review bot commented Apr 11, 2026

Code Review by Qodo

🐞 Bugs (7)   📘 Rule violations (0)   📎 Requirement gaps (0)   🎨 UX Issues (0)
🐞\ ≡ Correctness (5) ☼ Reliability (2)

Grey Divider


Action required

1. Unsafe optional q_T_sf use 🐞
Description
m_boundary_common and MPI comm code write/read q_T_sf under if (chemistry) / `if (chemistry .and.
chem_params%diffusion) without checking present(q_T_sf)`, but several call sites still invoke
buffer population without passing q_T_sf. With chemistry enabled this can dereference an absent
optional argument (undefined behavior: crash or memory corruption).
Code

src/common/m_boundary_common.fpp[R300-304]

+                if (chemistry) then
+                    do j = 1, buff_size
+                        q_T_sf%sf(-j, k, l) = q_T_sf%sf(0, k, l)
+                    end do
+                end if
Evidence
q_T_sf is declared optional but is dereferenced whenever chemistry is true. The repo contains
paths that call s_populate_variables_buffers(...) without q_T_sf (e.g., down_sample in pre_process
and simulation), making the optional argument absent while the callee still dereferences it.

src/common/m_boundary_common.fpp[284-304]
src/common/m_boundary_common.fpp[827-859]
src/common/m_mpi_common.fpp[499-592]
src/pre_process/m_data_output.fpp[409-421]
src/simulation/m_start_up.fpp[741-753]
src/simulation/m_time_steppers.fpp[602-609]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several boundary-condition and MPI-communication routines accept `q_T_sf` as an **optional** argument but dereference it whenever `chemistry` (and sometimes `chem_params%diffusion`) is enabled. There are existing call sites that invoke `s_populate_variables_buffers(...)` without passing `q_T_sf` (e.g., down-sampling output paths), which makes dereferencing the optional argument undefined behavior (can crash or silently corrupt).
### Issue Context
This PR introduced `q_T_sf` support into BC fill + MPI exchange, but the implementation currently relies on an implicit contract (“if chemistry then q_T_sf is always passed”) that the codebase does not consistently satisfy.
### Fix Focus Areas
- Add `present(q_T_sf)` guards around all `q_T_sf%sf(...)` accesses (and similarly in MPI pack/unpack) so the routines are safe when `q_T_sf` is omitted.
- Ensure temperature communication/ghost filling happens whenever it is truly required (e.g., chemistry+diffusion paths that subsequently compute diffusion fluxes).
- src/common/m_boundary_common.fpp[284-365]
- src/common/m_boundary_common.fpp[827-927]
- src/common/m_boundary_common.fpp[1152-1236]
- src/common/m_mpi_common.fpp[499-593]
- src/common/m_mpi_common.fpp[788-830]

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


2. bc_x Twall not broadcast 🐞
Description
post_process MPI input broadcast omits bc_x%Twall_in/out, so non-root ranks keep default wall
temperatures and produce incorrect parallel post-processing results for x-direction isothermal
walls. This is silent data corruption in MPI runs.
Code

src/post_process/m_mpi_proxy.fpp[R132-138]

       #:for VAR in [ 'pref', 'rhoref', 'R0ref', 'poly_sigma', 'Web', 'Ca', &
-            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',   &
-            & 'x_output%beg', 'x_output%end', 'y_output%beg', &
-            & 'y_output%end', 'z_output%beg', 'z_output%end']
+            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',      &
+            & 'x_output%beg', 'x_output%end', 'y_output%beg',    &
+            & 'y_output%end', 'z_output%beg', 'z_output%end',    &
+            & 'bc_y%Twall_in', 'bc_y%Twall_out', 'bc_z%Twall_in',&
+            & 'bc_z%Twall_out']
           call MPI_BCAST(${VAR}$, 1, mpi_p, 0, MPI_COMM_WORLD, ierr)
Evidence
The post_process broadcast list includes bc_y/bc_z Twall fields but not bc_x Twall fields, while
defaults are initialized to dflt_real. Simulation does broadcast all Twall fields, highlighting
the inconsistency.

src/post_process/m_mpi_proxy.fpp[132-139]
src/simulation/m_mpi_proxy.fpp[127-142]
src/simulation/m_global_parameters.fpp[767-772]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
In post_process MPI runs, `bc_x%Twall_in` and `bc_x%Twall_out` are not broadcast from rank 0. Non-root ranks therefore keep default values, producing incorrect post-processing for x-direction isothermal boundaries.
### Issue Context
The simulation and pre_process MPI proxies broadcast all 6 Twall values; post_process broadcasts only y/z.
### Fix Focus Areas
- Add `bc_x%Twall_in` and `bc_x%Twall_out` to the real-parameter `MPI_BCAST` list.
- src/post_process/m_mpi_proxy.fpp[132-139]

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


3. q_T_sf stale after smoothing 🐞
Description
pre_process computes q_T_sf before elliptic smoothing, but elliptic smoothing overwrites q_prim_vf
and does not recompute q_T_sf afterward. This leaves q_T_sf inconsistent with the final smoothed
initial condition, and any later use/serialization of q_T_sf (e.g., bc_io boundary buffers) will be
wrong.
Code

src/pre_process/m_initial_condition.fpp[R141-149]

+        if (chemistry) call s_compute_T_from_primitives(q_T_sf, q_prim_vf, idwint)

-        call s_convert_primitive_to_conservative_variables(q_prim_vf, q_cons_vf)
+        if (elliptic_smoothing .and. chemistry) then
+            call s_elliptic_smoothing(q_prim_vf, bc_type, q_T_sf)
+        else if (elliptic_smoothing) then
+            call s_elliptic_smoothing(q_prim_vf, bc_type)
+        end if

-        if (chemistry) call s_compute_T_from_primitives(q_T_sf, q_prim_vf, idwint)
+        call s_convert_primitive_to_conservative_variables(q_prim_vf, q_cons_vf)
Evidence
The initial-condition generator now calls s_compute_T_from_primitives before smoothing. The
smoothing routine updates q_prim_vf in-place but never updates/recomputes q_T_sf. Meanwhile,
boundary-condition buffer packing serializes temperature slices from q_T_sf, so stale values can
be written.

src/pre_process/m_initial_condition.fpp[137-150]
src/pre_process/m_perturbation.fpp[86-137]
src/common/m_boundary_common.fpp[2052-2068]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`q_T_sf` is computed from primitives before elliptic smoothing, but smoothing modifies `q_prim_vf` without updating `q_T_sf`. This makes temperature inconsistent with the final smoothed primitives and can lead to incorrect temperature written/used downstream (notably for `bc_io` temperature buffer serialization).
### Issue Context
`m_perturbation:s_elliptic_smoothing` only uses `q_T_sf` to populate ghost/buffer regions; it never recomputes temperature from the updated primitives.
### Fix Focus Areas
- After elliptic smoothing completes (and before any output/serialization that uses `q_T_sf`), recompute `q_T_sf` from the final `q_prim_vf`.
- Keep the pre-smoothing computation if it is needed to apply isothermal BCs during smoothing, but add a post-smoothing recomputation step.
- src/pre_process/m_initial_condition.fpp[137-150]
- src/pre_process/m_perturbation.fpp[86-137]

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


View more (3)
4. MPI q_T_sf presence ignored 🐞
Description
s_mpi_sendrecv_variables_buffers packs/unpacks q_T_sf whenever `chemistry .and.
chem_params%diffusion is true, but q_T_sf` is an optional argument and is dereferenced without
present() checks. If any caller omits q_T_sf (currently true in several places), MPI buffer
packing/unpacking will read/write through an absent optional argument.
Code

src/common/m_mpi_common.fpp[R525-531]

+        else if (chemistry .and. chem_params%diffusion) then
+            v_size = nVar + 1
+            buffer_counts = (/buff_size*v_size*(n + 1)*(p + 1), buff_size*v_size*(m + 2*buff_size + 1)*(p + 1), &
+                             & buff_size*v_size*(m + 2*buff_size + 1)*(n + 2*buff_size + 1)/)
       else
           v_size = nVar
           buffer_counts = (/buff_size*v_size*(n + 1)*(p + 1), buff_size*v_size*(m + 2*buff_size + 1)*(p + 1), &
Evidence
The MPI routine sizes v_size = nVar + 1 under chemistry .and. chem_params%diffusion and then
directly reads/writes q_T_sf%sf(...) under the same condition, but q_T_sf is declared optional
and never checked with present(q_T_sf). This is unsafe when upstream calls omit the argument.

src/common/m_mpi_common.fpp[499-593]
src/common/m_mpi_common.fpp[520-533]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
MPI send/recv for ghost buffers packs/unpacks temperature (`q_T_sf`) based solely on global flags (`chemistry` and `chem_params%diffusion`) even though `q_T_sf` is an optional dummy argument. This can dereference an absent optional argument and crash.
### Issue Context
Some code paths call `s_mpi_sendrecv_variables_buffers` (via `s_populate_variables_buffers`) without providing `q_T_sf` (e.g., conservative-only communications and downsample paths). The MPI routine must not assume `q_T_sf` is present.
### Fix Focus Areas
- src/common/m_mpi_common.fpp[499-593]
- src/common/m_mpi_common.fpp[564-624]
- src/common/m_mpi_common.fpp[625-713]
- src/common/m_mpi_common.fpp[756-974]
### What to change
1. Introduce a local logical like `has_T = present(q_T_sf) .and. chemistry .and. chem_params%diffusion`.
2. Use `has_T` (not just chemistry/diffusion) to:
 - set `v_size` (nVar vs nVar+1)
 - compute `buffer_counts`
 - pack `buff_send` temperature slot
 - unpack into `q_T_sf`
3. Optionally add a defensive abort if `chemistry .and. chem_params%diffusion` is true in a code path that *requires* temperature exchange but `present(q_T_sf)` is false.

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


5. Missing bc_x Twall broadcast 🐞
Description
In post_process MPI input broadcast, bc_x%Twall_in/out are not broadcast to non-root ranks,
leaving them at the default dflt_real and making post-processing rank-dependent for x-isothermal
cases. Post_process does call boundary buffer population (which can apply isothermal logic), so
incorrect bc_x%Twall_* affects computed ghost temperatures and derived outputs.
Code

src/post_process/m_mpi_proxy.fpp[R132-138]

       #:for VAR in [ 'pref', 'rhoref', 'R0ref', 'poly_sigma', 'Web', 'Ca', &
-            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',   &
-            & 'x_output%beg', 'x_output%end', 'y_output%beg', &
-            & 'y_output%end', 'z_output%beg', 'z_output%end']
+            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',      &
+            & 'x_output%beg', 'x_output%end', 'y_output%beg',    &
+            & 'y_output%end', 'z_output%beg', 'z_output%end',    &
+            & 'bc_y%Twall_in', 'bc_y%Twall_out', 'bc_z%Twall_in',&
+            & 'bc_z%Twall_out']
           call MPI_BCAST(${VAR}$, 1, mpi_p, 0, MPI_COMM_WORLD, ierr)
Evidence
s_mpi_bcast_user_inputs in post_process broadcasts bc_y%Twall_* and bc_z%Twall_* but omits
bc_x%Twall_*. Post_process then computes temperature and calls s_populate_variables_buffers,
which applies wall temperature logic (e.g., in slip wall) using bc_x%Twall_in/bc_x%Twall_out;
without broadcasting, only rank 0 has the intended values.

src/post_process/m_mpi_proxy.fpp[132-139]
src/post_process/m_start_up.fpp[166-173]
src/common/m_boundary_common.fpp[849-857]
src/pre_process/m_mpi_proxy.fpp[55-66]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Post-process MPI broadcast omits `bc_x%Twall_in` and `bc_x%Twall_out`, leaving defaults on non-root ranks. Post-process uses boundary-condition population that can reference these values, producing incorrect ghost temperatures and rank-dependent derived outputs.
### Issue Context
- pre_process already broadcasts `bc_x%Twall_*` alongside y/z.
- post_process should broadcast the same set for consistency.
### Fix Focus Areas
- src/post_process/m_mpi_proxy.fpp[132-139]
### What to change
Add `bc_x%Twall_in` and `bc_x%Twall_out` to the real-parameter broadcast list in `s_mpi_bcast_user_inputs` (and keep ordering consistent with pre_process/simulation if possible).

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


6. Twall not required by validator 🐞
Description
The case validator permits bc_*%isothermal_in/out = T without requiring the corresponding
bc_*%Twall_in/out to be provided, leaving it at dflt_real = -1e6. Boundary routines use
Twall_* directly to set ghost temperatures, so missing Twall configuration yields wildly
unphysical temperatures.
Code

toolchain/mfc/case_validator.py[R1291-1316]

+        # Fetch global chemistry and diffusion flags
+        chemistry = self.get("chemistry", "F") == "T"
+        diffusion = self.get("chem_params%diffusion", "F") == "T"
+
+        # Define what constitutes a wall (-15 for slip, -16 for no-slip)
+        wall_bcs = [-15, -16]
+
+        for dir in ["x", "y", "z"]:
+            isothermal_in = self.get(f"bc_{dir}%isothermal_in", "F") == "T"
+            isothermal_out = self.get(f"bc_{dir}%isothermal_out", "F") == "T"
+            bc_beg = self.get(f"bc_{dir}%beg")
+            bc_end = self.get(f"bc_{dir}%end")
+
+            if isothermal_in:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal In (bc_{dir}%isothermal_in) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_beg not in wall_bcs, f"Isothermal In (bc_{dir}%isothermal_in) requires a wall. Set bc_{dir}%beg to -15 (slip) or -16 (no-slip).")
+
+            if isothermal_out:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal Out (bc_{dir}%isothermal_out) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_end not in wall_bcs, f"Isothermal Out (bc_{dir}%isothermal_out) requires a wall. Set bc_{dir}%end to -15 (slip) or -16 (no-slip).")
Evidence
check_chemistry validates that isothermal flags require chemistry+diffusion and wall BC types, but
never checks Twall_in/out are set. Meanwhile, global parameters initialize Twall_* to
dflt_real and dflt_real is defined as -1e6, and the wall BC implementation uses bc_x%Twall_in
directly in the ghost-cell temperature formula.

toolchain/mfc/case_validator.py[1284-1316]
src/pre_process/m_global_parameters.fpp[305-310]
src/common/m_constants.fpp[10-13]
src/common/m_boundary_common.fpp[849-857]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The validator allows enabling isothermal walls without specifying `Twall_in/out`, which defaults to `dflt_real=-1e6` and is then used directly by the wall BC implementation to set ghost temperatures.
### Issue Context
- `bc_[xyz]%Twall_in/out` defaults to `dflt_real`.
- `check_chemistry()` already checks compatibility (chemistry+diffusion, wall bc codes) but not presence/validity of Twall.
### Fix Focus Areas
- toolchain/mfc/case_validator.py[1284-1316]
- src/common/m_constants.fpp[10-13]
### What to change
1. When `bc_{dir}%isothermal_in` is true:
 - require `bc_{dir}%Twall_in` is provided
 - require it is numeric (or at least not missing)
 - require it is not equal to the default sentinel (e.g., `-1e6`) and is physically reasonable (e.g., `> 0` K)
2. Similarly, when `bc_{dir}%isothermal_out` is true, validate `bc_{dir}%Twall_out`.
3. Produce clear error messages pointing to the missing Twall parameter.

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



Remediation recommended

7. Twall not required 🐞
Description
The validator enforces chemistry/diffusion and wall BC codes for isothermal flags but does not
require Twall_in/out to be provided, so cases can run using default/sentinel wall temperatures and
silently compute incorrect ghost temperatures/heat fluxes. This allows misconfigured isothermal
boundaries to pass validation.
Code

toolchain/mfc/case_validator.py[R1291-1316]

+        # Fetch global chemistry and diffusion flags
+        chemistry = self.get("chemistry", "F") == "T"
+        diffusion = self.get("chem_params%diffusion", "F") == "T"
+
+        # Define what constitutes a wall (-15 for slip, -16 for no-slip)
+        wall_bcs = [-15, -16]
+
+        for dir in ["x", "y", "z"]:
+            isothermal_in = self.get(f"bc_{dir}%isothermal_in", "F") == "T"
+            isothermal_out = self.get(f"bc_{dir}%isothermal_out", "F") == "T"
+            bc_beg = self.get(f"bc_{dir}%beg")
+            bc_end = self.get(f"bc_{dir}%end")
+
+            if isothermal_in:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal In (bc_{dir}%isothermal_in) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_beg not in wall_bcs, f"Isothermal In (bc_{dir}%isothermal_in) requires a wall. Set bc_{dir}%beg to -15 (slip) or -16 (no-slip).")
+
+            if isothermal_out:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal Out (bc_{dir}%isothermal_out) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_end not in wall_bcs, f"Isothermal Out (bc_{dir}%isothermal_out) requires a wall. Set bc_{dir}%end to -15 (slip) or -16 (no-slip).")
Evidence
When isothermal is enabled, ghost temperature is computed using bc_%Twall_*, which defaults to
dflt_real in global parameter initialization. The new validator logic never checks
presence/validity of Twall_in/out, so missing Twall inputs won’t be caught pre-run.

toolchain/mfc/case_validator.py[1291-1316]
src/simulation/m_global_parameters.fpp[767-772]
src/common/m_boundary_common.fpp[849-858]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Cases can enable `bc_%isothermal_in/out` without providing `bc_%Twall_in/out`. The code will then use default/sentinel values for wall temperature in ghost temperature calculations, producing incorrect results without a clear validation error.
### Issue Context
The PR adds validation for chemistry/diffusion and wall BC codes, but it does not validate that the required wall temperature is actually supplied.
### Fix Focus Areas
- When `bc_{dir}%isothermal_in` is true, prohibit missing `bc_{dir}%Twall_in`.
- When `bc_{dir}%isothermal_out` is true, prohibit missing `bc_{dir}%Twall_out`.
- Optionally: validate Twall is numeric and > 0.
- toolchain/mfc/case_validator.py[1291-1316]

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


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review bot commented Apr 11, 2026

Code Review by Qodo

🐞 Bugs (9)   📘 Rule violations (1)   📎 Requirement gaps (0)   🎨 UX Issues (0)
🐞\ ≡ Correctness (6) ☼ Reliability (3) ⭐ New (2)
📘\ ☼ Reliability (1) ⭐ New (1)

Grey Divider


Action required

1. bc_buffers missing @:DEALLOCATE 📘
Description
This PR modifies/introduces @:ALLOCATE calls for bc_buffers storage, but the corresponding
teardown still uses raw deallocate instead of @:DEALLOCATE. This violates the project
requirement to pair macro allocations/deallocations, risking inconsistent resource handling across
builds/backends.
Code

src/common/m_boundary_common.fpp[R45-62]

+        integer :: i, j, sys_size_alloc

        @:ALLOCATE(bc_buffers(1:3, 1:2))

        if (bc_io) then
-            @:ALLOCATE(bc_buffers(1, 1)%sf(1:sys_size, 0:n, 0:p))
-            @:ALLOCATE(bc_buffers(1, 2)%sf(1:sys_size, 0:n, 0:p))
+            sys_size_alloc = sys_size
+            if (chemistry) sys_size_alloc = sys_size + 1
+
+            @:ALLOCATE(bc_buffers(1, 1)%sf(1:sys_size_alloc, 0:n, 0:p))
+            @:ALLOCATE(bc_buffers(1, 2)%sf(1:sys_size_alloc, 0:n, 0:p))
            #:if not MFC_CASE_OPTIMIZATION or num_dims > 1
                if (n > 0) then
-                    @:ALLOCATE(bc_buffers(2,1)%sf(-buff_size:m+buff_size,1:sys_size,0:p))
-                    @:ALLOCATE(bc_buffers(2,2)%sf(-buff_size:m+buff_size,1:sys_size,0:p))
+                    @:ALLOCATE(bc_buffers(2,1)%sf(-buff_size:m+buff_size,1:sys_size_alloc,0:p))
+                    @:ALLOCATE(bc_buffers(2,2)%sf(-buff_size:m+buff_size,1:sys_size_alloc,0:p))
                    #:if not MFC_CASE_OPTIMIZATION or num_dims > 2
                        if (p > 0) then
-                            @:ALLOCATE(bc_buffers(3,1)%sf(-buff_size:m+buff_size,-buff_size:n+buff_size,1:sys_size))
-                            @:ALLOCATE(bc_buffers(3,2)%sf(-buff_size:m+buff_size,-buff_size:n+buff_size,1:sys_size))
+                            @:ALLOCATE(bc_buffers(3,1)%sf(-buff_size:m+buff_size,-buff_size:n+buff_size,1:sys_size_alloc))
+                            @:ALLOCATE(bc_buffers(3,2)%sf(-buff_size:m+buff_size,-buff_size:n+buff_size,1:sys_size_alloc))
Evidence
PR Compliance ID 6 requires every @:ALLOCATE to have a matching @:DEALLOCATE. The updated code
allocates bc_buffers(*,*)%sf(...) via @:ALLOCATE, while module finalization deallocates the same
storage via raw deallocate, not @:DEALLOCATE.

CLAUDE.md
src/common/m_boundary_common.fpp[45-62]
src/common/m_boundary_common.fpp[2326-2346]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR updates `@:ALLOCATE` usage for `bc_buffers` storage, but `s_finalize_boundary_common_module()` still deallocates those buffers using raw `deallocate` instead of the required `@:DEALLOCATE` macro.

## Issue Context
Compliance requires macro-based allocation/deallocation pairing for consistent resource management.

## Fix Focus Areas
- src/common/m_boundary_common.fpp[45-62]
- src/common/m_boundary_common.fpp[2326-2346]

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


2. Unsafe optional q_T_sf use 🐞
Description
m_boundary_common and MPI comm code write/read q_T_sf under if (chemistry) / `if (chemistry .and.
chem_params%diffusion) without checking present(q_T_sf)`, but several call sites still invoke
buffer population without passing q_T_sf. With chemistry enabled this can dereference an absent
optional argument (undefined behavior: crash or memory corruption).
Code

src/common/m_boundary_common.fpp[R300-304]

+                if (chemistry) then
+                    do j = 1, buff_size
+                        q_T_sf%sf(-j, k, l) = q_T_sf%sf(0, k, l)
+                    end do
+                end if
Evidence
q_T_sf is declared optional but is dereferenced whenever chemistry is true. The repo contains
paths that call s_populate_variables_buffers(...) without q_T_sf (e.g., down_sample in pre_process
and simulation), making the optional argument absent while the callee still dereferences it.

src/common/m_boundary_common.fpp[284-304]
src/common/m_boundary_common.fpp[827-859]
src/common/m_mpi_common.fpp[499-592]
src/pre_process/m_data_output.fpp[409-421]
src/simulation/m_start_up.fpp[741-753]
src/simulation/m_time_steppers.fpp[602-609]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several boundary-condition and MPI-communication routines accept `q_T_sf` as an **optional** argument but dereference it whenever `chemistry` (and sometimes `chem_params%diffusion`) is enabled. There are existing call sites that invoke `s_populate_variables_buffers(...)` without passing `q_T_sf` (e.g., down-sampling output paths), which makes dereferencing the optional argument undefined behavior (can crash or silently corrupt).
### Issue Context
This PR introduced `q_T_sf` support into BC fill + MPI exchange, but the implementation currently relies on an implicit contract (“if chemistry then q_T_sf is always passed”) that the codebase does not consistently satisfy.
### Fix Focus Areas
- Add `present(q_T_sf)` guards around all `q_T_sf%sf(...)` accesses (and similarly in MPI pack/unpack) so the routines are safe when `q_T_sf` is omitted.
- Ensure temperature communication/ghost filling happens whenever it is truly required (e.g., chemistry+diffusion paths that subsequently compute diffusion fluxes).
- src/common/m_boundary_common.fpp[284-365]
- src/common/m_boundary_common.fpp[827-927]
- src/common/m_boundary_common.fpp[1152-1236]
- src/common/m_mpi_common.fpp[499-593]
- src/common/m_mpi_common.fpp[788-830]

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


3. bc_x Twall not broadcast 🐞
Description
post_process MPI input broadcast omits bc_x%Twall_in/out, so non-root ranks keep default wall
temperatures and produce incorrect parallel post-processing results for x-direction isothermal
walls. This is silent data corruption in MPI runs.
Code

src/post_process/m_mpi_proxy.fpp[R132-138]

       #:for VAR in [ 'pref', 'rhoref', 'R0ref', 'poly_sigma', 'Web', 'Ca', &
-            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',   &
-            & 'x_output%beg', 'x_output%end', 'y_output%beg', &
-            & 'y_output%end', 'z_output%beg', 'z_output%end']
+            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',      &
+            & 'x_output%beg', 'x_output%end', 'y_output%beg',    &
+            & 'y_output%end', 'z_output%beg', 'z_output%end',    &
+            & 'bc_y%Twall_in', 'bc_y%Twall_out', 'bc_z%Twall_in',&
+            & 'bc_z%Twall_out']
           call MPI_BCAST(${VAR}$, 1, mpi_p, 0, MPI_COMM_WORLD, ierr)
Evidence
The post_process broadcast list includes bc_y/bc_z Twall fields but not bc_x Twall fields, while
defaults are initialized to dflt_real. Simulation does broadcast all Twall fields, highlighting
the inconsistency.

src/post_process/m_mpi_proxy.fpp[132-139]
src/simulation/m_mpi_proxy.fpp[127-142]
src/simulation/m_global_parameters.fpp[767-772]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
In post_process MPI runs, `bc_x%Twall_in` and `bc_x%Twall_out` are not broadcast from rank 0. Non-root ranks therefore keep default values, producing incorrect post-processing for x-direction isothermal boundaries.
### Issue Context
The simulation and pre_process MPI proxies broadcast all 6 Twall values; post_process broadcasts only y/z.
### Fix Focus Areas
- Add `bc_x%Twall_in` and `bc_x%Twall_out` to the real-parameter `MPI_BCAST` list.
- src/post_process/m_mpi_proxy.fpp[132-139]

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


View more (4)
4. q_T_sf stale after smoothing 🐞
Description
pre_process computes q_T_sf before elliptic smoothing, but elliptic smoothing overwrites q_prim_vf
and does not recompute q_T_sf afterward. This leaves q_T_sf inconsistent with the final smoothed
initial condition, and any later use/serialization of q_T_sf (e.g., bc_io boundary buffers) will be
wrong.
Code

src/pre_process/m_initial_condition.fpp[R141-149]

+        if (chemistry) call s_compute_T_from_primitives(q_T_sf, q_prim_vf, idwint)

-        call s_convert_primitive_to_conservative_variables(q_prim_vf, q_cons_vf)
+        if (elliptic_smoothing .and. chemistry) then
+            call s_elliptic_smoothing(q_prim_vf, bc_type, q_T_sf)
+        else if (elliptic_smoothing) then
+            call s_elliptic_smoothing(q_prim_vf, bc_type)
+        end if

-        if (chemistry) call s_compute_T_from_primitives(q_T_sf, q_prim_vf, idwint)
+        call s_convert_primitive_to_conservative_variables(q_prim_vf, q_cons_vf)
Evidence
The initial-condition generator now calls s_compute_T_from_primitives before smoothing. The
smoothing routine updates q_prim_vf in-place but never updates/recomputes q_T_sf. Meanwhile,
boundary-condition buffer packing serializes temperature slices from q_T_sf, so stale values can
be written.

src/pre_process/m_initial_condition.fpp[137-150]
src/pre_process/m_perturbation.fpp[86-137]
src/common/m_boundary_common.fpp[2052-2068]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`q_T_sf` is computed from primitives before elliptic smoothing, but smoothing modifies `q_prim_vf` without updating `q_T_sf`. This makes temperature inconsistent with the final smoothed primitives and can lead to incorrect temperature written/used downstream (notably for `bc_io` temperature buffer serialization).
### Issue Context
`m_perturbation:s_elliptic_smoothing` only uses `q_T_sf` to populate ghost/buffer regions; it never recomputes temperature from the updated primitives.
### Fix Focus Areas
- After elliptic smoothing completes (and before any output/serialization that uses `q_T_sf`), recompute `q_T_sf` from the final `q_prim_vf`.
- Keep the pre-smoothing computation if it is needed to apply isothermal BCs during smoothing, but add a post-smoothing recomputation step.
- src/pre_process/m_initial_condition.fpp[137-150]
- src/pre_process/m_perturbation.fpp[86-137]

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


5. MPI q_T_sf presence ignored 🐞
Description
s_mpi_sendrecv_variables_buffers packs/unpacks q_T_sf whenever `chemistry .and.
chem_params%diffusion is true, but q_T_sf` is an optional argument and is dereferenced without
present() checks. If any caller omits q_T_sf (currently true in several places), MPI buffer
packing/unpacking will read/write through an absent optional argument.
Code

src/common/m_mpi_common.fpp[R525-531]

+        else if (chemistry .and. chem_params%diffusion) then
+            v_size = nVar + 1
+            buffer_counts = (/buff_size*v_size*(n + 1)*(p + 1), buff_size*v_size*(m + 2*buff_size + 1)*(p + 1), &
+                             & buff_size*v_size*(m + 2*buff_size + 1)*(n + 2*buff_size + 1)/)
       else
           v_size = nVar
           buffer_counts = (/buff_size*v_size*(n + 1)*(p + 1), buff_size*v_size*(m + 2*buff_size + 1)*(p + 1), &
Evidence
The MPI routine sizes v_size = nVar + 1 under chemistry .and. chem_params%diffusion and then
directly reads/writes q_T_sf%sf(...) under the same condition, but q_T_sf is declared optional
and never checked with present(q_T_sf). This is unsafe when upstream calls omit the argument.

src/common/m_mpi_common.fpp[499-593]
src/common/m_mpi_common.fpp[520-533]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
MPI send/recv for ghost buffers packs/unpacks temperature (`q_T_sf`) based solely on global flags (`chemistry` and `chem_params%diffusion`) even though `q_T_sf` is an optional dummy argument. This can dereference an absent optional argument and crash.
### Issue Context
Some code paths call `s_mpi_sendrecv_variables_buffers` (via `s_populate_variables_buffers`) without providing `q_T_sf` (e.g., conservative-only communications and downsample paths). The MPI routine must not assume `q_T_sf` is present.
### Fix Focus Areas
- src/common/m_mpi_common.fpp[499-593]
- src/common/m_mpi_common.fpp[564-624]
- src/common/m_mpi_common.fpp[625-713]
- src/common/m_mpi_common.fpp[756-974]
### What to change
1. Introduce a local logical like `has_T = present(q_T_sf) .and. chemistry .and. chem_params%diffusion`.
2. Use `has_T` (not just chemistry/diffusion) to:
 - set `v_size` (nVar vs nVar+1)
 - compute `buffer_counts`
 - pack `buff_send` temperature slot
 - unpack into `q_T_sf`
3. Optionally add a defensive abort if `chemistry .and. chem_params%diffusion` is true in a code path that *requires* temperature exchange but `present(q_T_sf)` is false.

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


6. Missing bc_x Twall broadcast 🐞
Description
In post_process MPI input broadcast, bc_x%Twall_in/out are not broadcast to non-root ranks,
leaving them at the default dflt_real and making post-processing rank-dependent for x-isothermal
cases. Post_process does call boundary buffer population (which can apply isothermal logic), so
incorrect bc_x%Twall_* affects computed ghost temperatures and derived outputs.
Code

src/post_process/m_mpi_proxy.fpp[R132-138]

       #:for VAR in [ 'pref', 'rhoref', 'R0ref', 'poly_sigma', 'Web', 'Ca', &
-            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',   &
-            & 'x_output%beg', 'x_output%end', 'y_output%beg', &
-            & 'y_output%end', 'z_output%beg', 'z_output%end']
+            & 'Re_inv', 'Bx0', 'sigma', 't_save', 't_stop',      &
+            & 'x_output%beg', 'x_output%end', 'y_output%beg',    &
+            & 'y_output%end', 'z_output%beg', 'z_output%end',    &
+            & 'bc_y%Twall_in', 'bc_y%Twall_out', 'bc_z%Twall_in',&
+            & 'bc_z%Twall_out']
           call MPI_BCAST(${VAR}$, 1, mpi_p, 0, MPI_COMM_WORLD, ierr)
Evidence
s_mpi_bcast_user_inputs in post_process broadcasts bc_y%Twall_* and bc_z%Twall_* but omits
bc_x%Twall_*. Post_process then computes temperature and calls s_populate_variables_buffers,
which applies wall temperature logic (e.g., in slip wall) using bc_x%Twall_in/bc_x%Twall_out;
without broadcasting, only rank 0 has the intended values.

src/post_process/m_mpi_proxy.fpp[132-139]
src/post_process/m_start_up.fpp[166-173]
src/common/m_boundary_common.fpp[849-857]
src/pre_process/m_mpi_proxy.fpp[55-66]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Post-process MPI broadcast omits `bc_x%Twall_in` and `bc_x%Twall_out`, leaving defaults on non-root ranks. Post-process uses boundary-condition population that can reference these values, producing incorrect ghost temperatures and rank-dependent derived outputs.
### Issue Context
- pre_process already broadcasts `bc_x%Twall_*` alongside y/z.
- post_process should broadcast the same set for consistency.
### Fix Focus Areas
- src/post_process/m_mpi_proxy.fpp[132-139]
### What to change
Add `bc_x%Twall_in` and `bc_x%Twall_out` to the real-parameter broadcast list in `s_mpi_bcast_user_inputs` (and keep ordering consistent with pre_process/simulation if possible).

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


7. Twall not required by validator 🐞
Description
The case validator permits bc_*%isothermal_in/out = T without requiring the corresponding
bc_*%Twall_in/out to be provided, leaving it at dflt_real = -1e6. Boundary routines use
Twall_* directly to set ghost temperatures, so missing Twall configuration yields wildly
unphysical temperatures.
Code

toolchain/mfc/case_validator.py[R1291-1316]

+        # Fetch global chemistry and diffusion flags
+        chemistry = self.get("chemistry", "F") == "T"
+        diffusion = self.get("chem_params%diffusion", "F") == "T"
+
+        # Define what constitutes a wall (-15 for slip, -16 for no-slip)
+        wall_bcs = [-15, -16]
+
+        for dir in ["x", "y", "z"]:
+            isothermal_in = self.get(f"bc_{dir}%isothermal_in", "F") == "T"
+            isothermal_out = self.get(f"bc_{dir}%isothermal_out", "F") == "T"
+            bc_beg = self.get(f"bc_{dir}%beg")
+            bc_end = self.get(f"bc_{dir}%end")
+
+            if isothermal_in:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal In (bc_{dir}%isothermal_in) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_beg not in wall_bcs, f"Isothermal In (bc_{dir}%isothermal_in) requires a wall. Set bc_{dir}%beg to -15 (slip) or -16 (no-slip).")
+
+            if isothermal_out:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal Out (bc_{dir}%isothermal_out) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_end not in wall_bcs, f"Isothermal Out (bc_{dir}%isothermal_out) requires a wall. Set bc_{dir}%end to -15 (slip) or -16 (no-slip).")
Evidence
check_chemistry validates that isothermal flags require chemistry+diffusion and wall BC types, but
never checks Twall_in/out are set. Meanwhile, global parameters initialize Twall_* to
dflt_real and dflt_real is defined as -1e6, and the wall BC implementation uses bc_x%Twall_in
directly in the ghost-cell temperature formula.

toolchain/mfc/case_validator.py[1284-1316]
src/pre_process/m_global_parameters.fpp[305-310]
src/common/m_constants.fpp[10-13]
src/common/m_boundary_common.fpp[849-857]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The validator allows enabling isothermal walls without specifying `Twall_in/out`, which defaults to `dflt_real=-1e6` and is then used directly by the wall BC implementation to set ghost temperatures.
### Issue Context
- `bc_[xyz]%Twall_in/out` defaults to `dflt_real`.
- `check_chemistry()` already checks compatibility (chemistry+diffusion, wall bc codes) but not presence/validity of Twall.
### Fix Focus Areas
- toolchain/mfc/case_validator.py[1284-1316]
- src/common/m_constants.fpp[10-13]
### What to change
1. When `bc_{dir}%isothermal_in` is true:
 - require `bc_{dir}%Twall_in` is provided
 - require it is numeric (or at least not missing)
 - require it is not equal to the default sentinel (e.g., `-1e6`) and is physically reasonable (e.g., `> 0` K)
2. Similarly, when `bc_{dir}%isothermal_out` is true, validate `bc_{dir}%Twall_out`.
3. Produce clear error messages pointing to the missing Twall parameter.

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



Remediation recommended

8. QBMM skips temperature exchange 🐞
Description
When qbmm_comm is active, MPI packing uses the QBMM branch and does not include q_T_sf in the
communicated buffer even if chemistry diffusion is enabled. This can leave q_T_sf ghost regions
stale across MPI boundaries, breaking diffusion fluxes that now compute T_L/T_R from q_T_sf.
Code

src/common/m_mpi_common.fpp[R521-533]

        if (present(pb_in) .and. present(mv_in) .and. qbmm .and. .not. polytropic) then
            qbmm_comm = .true.
            v_size = nVar + 2*nb*nnode
            buffer_counts = (/buff_size*v_size*(n + 1)*(p + 1), buff_size*v_size*(m + 2*buff_size + 1)*(p + 1), &
                             & buff_size*v_size*(m + 2*buff_size + 1)*(n + 2*buff_size + 1)/)
+        else if (chemistry .and. chem_params%diffusion) then
+            chem_diff_comm = .true.
+            v_size = nVar + 1
+            buffer_counts = (/buff_size*v_size*(n + 1)*(p + 1), buff_size*v_size*(m + 2*buff_size + 1)*(p + 1), &
+                             & buff_size*v_size*(m + 2*buff_size + 1)*(n + 2*buff_size + 1)/)
        else
            v_size = nVar
            buffer_counts = (/buff_size*v_size*(n + 1)*(p + 1), buff_size*v_size*(m + 2*buff_size + 1)*(p + 1), &
Evidence
The send/recv routine’s QBMM path and chemistry-diffusion path are mutually exclusive due to an
else-if, so QBMM communication never includes temperature. Chemistry diffusion flux computation
explicitly uses q_T_sf values at interface-adjacent cells, so stale ghost temperatures across MPI
partitions can yield incorrect diffusion/heat fluxes.

src/common/m_mpi_common.fpp[521-535]
src/common/m_chemistry.fpp[160-245]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
In `s_mpi_sendrecv_variables_buffers`, the QBMM communication branch prevents packing/unpacking of `q_T_sf` even when `chemistry .and. chem_params%diffusion` is enabled.

### Issue Context
Diffusion fluxes now use `q_T_sf` for `T_L/T_R`. If QBMM cases are allowed to also enable chemistry diffusion, temperature ghost cells must be communicated across MPI boundaries.

### Fix Focus Areas
- src/common/m_mpi_common.fpp[521-535]
- src/common/m_mpi_common.fpp[583-714] (pack paths)
- src/common/m_mpi_common.fpp[814-976] (unpack paths)

### What to change
Choose one:
1) **Implement combined packing**: when `qbmm_comm` and `chem_diff_comm` are both true, set `v_size = nVar + 2*nb*nnode + 1` and pack/unpack `q_T_sf` at the last slot for each spatial index.
2) **Explicitly forbid the configuration**: add a CaseValidator prohibition for `qbmm='T'` with `chem_params%diffusion='T'` (and/or `chemistry='T'`) if it is not supported.

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


9. Restart BC temp uninitialized 🐞
Description
bc_buffers are allocated with sys_size+1 entries whenever chemistry is enabled and the restart
writer writes the entire bc_buffers array. In the IGR output path, q_T_sf is not provided, so the
extra temperature slot is never packed and bc_buffers.dat can contain uninitialized temperature
data.
Code

src/pre_process/m_data_output.fpp[R88-92]

            if (igr) then
                call s_write_serial_boundary_condition_files(q_cons_vf, bc_type, t_step_dir, old_grid)
            else
-                call s_write_serial_boundary_condition_files(q_prim_vf, bc_type, t_step_dir, old_grid)
+                call s_write_serial_boundary_condition_files(q_prim_vf, bc_type, t_step_dir, old_grid, q_T_sf)
            end if
Evidence
bc_buffers allocation adds an extra slot (sys_size+1) under chemistry, but packing fills that slot
only when q_T_sf is present. The IGR branch calls the restart BC writer without q_T_sf, and the
writer unconditionally writes the full bc_buffers(dir,loc)%sf array to disk.

src/common/m_boundary_common.fpp[43-63]
src/common/m_boundary_common.fpp[2053-2068]
src/common/m_boundary_common.fpp[1846-1881]
src/pre_process/m_data_output.fpp[87-92]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
When `chemistry` is enabled, `bc_buffers` include an extra slot intended for temperature. In the IGR output path, `q_T_sf` is not passed, so that extra slot is never packed, but the full array is still written to restart.

### Issue Context
This can lead to nondeterministic restart behavior if the written temperature slot is later read/used.

### Fix Focus Areas
- src/pre_process/m_data_output.fpp[87-92]
- src/common/m_boundary_common.fpp[43-63]
- src/common/m_boundary_common.fpp[1846-1881]
- src/common/m_boundary_common.fpp[2053-2068]

### What to change
Pick one (preferred: robust in writer):
- **Writer-side**: In `s_write_serial_boundary_condition_files` (and parallel version), write only `1:sys_size` when `(.not. present(q_T_sf))` (i.e., do not write the `sys_size+1` slice).
- **Caller-side**: In the IGR path, compute temperature and pass `q_T_sf` into `s_write_serial_boundary_condition_files` so packing initializes `sys_size+1`.
- **Allocation-side**: Allocate `sys_size+1` only when temperature will be serialized (i.e., when `present(q_T_sf)` at pack/write time), though this may be harder due to module-level allocation timing.

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


10. Twall not required 🐞
Description
The validator enforces chemistry/diffusion and wall BC codes for isothermal flags but does not
require Twall_in/out to be provided, so cases can run using default/sentinel wall temperatures and
silently compute incorrect ghost temperatures/heat fluxes. This allows misconfigured isothermal
boundaries to pass validation.
Code

toolchain/mfc/case_validator.py[R1291-1316]

+        # Fetch global chemistry and diffusion flags
+        chemistry = self.get("chemistry", "F") == "T"
+        diffusion = self.get("chem_params%diffusion", "F") == "T"
+
+        # Define what constitutes a wall (-15 for slip, -16 for no-slip)
+        wall_bcs = [-15, -16]
+
+        for dir in ["x", "y", "z"]:
+            isothermal_in = self.get(f"bc_{dir}%isothermal_in", "F") == "T"
+            isothermal_out = self.get(f"bc_{dir}%isothermal_out", "F") == "T"
+            bc_beg = self.get(f"bc_{dir}%beg")
+            bc_end = self.get(f"bc_{dir}%end")
+
+            if isothermal_in:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal In (bc_{dir}%isothermal_in) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_beg not in wall_bcs, f"Isothermal In (bc_{dir}%isothermal_in) requires a wall. Set bc_{dir}%beg to -15 (slip) or -16 (no-slip).")
+
+            if isothermal_out:
+                # Prohibit isothermal boundaries if chemistry or diffusion are disabled
+                self.prohibit(not chemistry or not diffusion, f"Isothermal Out (bc_{dir}%isothermal_out) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
+
+                # Prohibit if neither beg nor end is set to a valid wall condition
+                self.prohibit(bc_end not in wall_bcs, f"Isothermal Out (bc_{dir}%isothermal_out) requires a wall. Set bc_{dir}%end to -15 (slip) or -16 (no-slip).")
Evidence
When isothermal is enabled, ghost temperature is computed using bc_%Twall_*, which defaults to
dflt_real in global parameter initialization. The new validator logic never checks
presence/validity of Twall_in/out, so missing Twall inputs won’t be caught pre-run.

toolchain/mfc/case_validator.py[1291-1316]
src/simulation/m_global_parameters.fpp[767-772]
src/common/m_boundary_common.fpp[849-858]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Cases can enable `bc_%isothermal_in/out` without providing `bc_%Twall_in/out`. The code will then use default/sentinel values for wall temperature in ghost temperature calculations, producing incorrect results without a clear validation error.
### Issue Context
The PR adds validation for chemistry/diffusion and wall BC codes, but it does not validate that the required wall temperature is actually supplied.
### Fix Focus Areas
- When `bc_{dir}%isothermal_in` is true, prohibit missing `bc_{dir}%Twall_in`.
- When `bc_{dir}%isothermal_out` is true, prohibit missing `bc_{dir}%Twall_out`.
- Optionally: validate Twall is numeric and > 0.
- toolchain/mfc/case_validator.py[1291-1316]

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


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +45 to +62
integer :: i, j, sys_size_alloc

@:ALLOCATE(bc_buffers(1:3, 1:2))

if (bc_io) then
@:ALLOCATE(bc_buffers(1, 1)%sf(1:sys_size, 0:n, 0:p))
@:ALLOCATE(bc_buffers(1, 2)%sf(1:sys_size, 0:n, 0:p))
sys_size_alloc = sys_size
if (chemistry) sys_size_alloc = sys_size + 1

@:ALLOCATE(bc_buffers(1, 1)%sf(1:sys_size_alloc, 0:n, 0:p))
@:ALLOCATE(bc_buffers(1, 2)%sf(1:sys_size_alloc, 0:n, 0:p))
#:if not MFC_CASE_OPTIMIZATION or num_dims > 1
if (n > 0) then
@:ALLOCATE(bc_buffers(2,1)%sf(-buff_size:m+buff_size,1:sys_size,0:p))
@:ALLOCATE(bc_buffers(2,2)%sf(-buff_size:m+buff_size,1:sys_size,0:p))
@:ALLOCATE(bc_buffers(2,1)%sf(-buff_size:m+buff_size,1:sys_size_alloc,0:p))
@:ALLOCATE(bc_buffers(2,2)%sf(-buff_size:m+buff_size,1:sys_size_alloc,0:p))
#:if not MFC_CASE_OPTIMIZATION or num_dims > 2
if (p > 0) then
@:ALLOCATE(bc_buffers(3,1)%sf(-buff_size:m+buff_size,-buff_size:n+buff_size,1:sys_size))
@:ALLOCATE(bc_buffers(3,2)%sf(-buff_size:m+buff_size,-buff_size:n+buff_size,1:sys_size))
@:ALLOCATE(bc_buffers(3,1)%sf(-buff_size:m+buff_size,-buff_size:n+buff_size,1:sys_size_alloc))
@:ALLOCATE(bc_buffers(3,2)%sf(-buff_size:m+buff_size,-buff_size:n+buff_size,1:sys_size_alloc))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

1. bc_buffers missing @:deallocate 📘 Rule violation ☼ Reliability

This PR modifies/introduces @:ALLOCATE calls for bc_buffers storage, but the corresponding
teardown still uses raw deallocate instead of @:DEALLOCATE. This violates the project
requirement to pair macro allocations/deallocations, risking inconsistent resource handling across
builds/backends.
Agent Prompt
## Issue description
The PR updates `@:ALLOCATE` usage for `bc_buffers` storage, but `s_finalize_boundary_common_module()` still deallocates those buffers using raw `deallocate` instead of the required `@:DEALLOCATE` macro.

## Issue Context
Compliance requires macro-based allocation/deallocation pairing for consistent resource management.

## Fix Focus Areas
- src/common/m_boundary_common.fpp[45-62]
- src/common/m_boundary_common.fpp[2326-2346]

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

@sbryngelson sbryngelson marked this pull request as draft April 11, 2026 18:22
@DimAdam-01 DimAdam-01 marked this pull request as ready for review April 11, 2026 23:35
@sbryngelson
Copy link
Copy Markdown
Member

Code review

Found 2 issues:

  1. chem_diff_comm set without checking present(q_T_sf), causing segfault when callers omit q_T_sf -- In s_mpi_sendrecv_variables_buffers, chem_diff_comm is set to .true. based solely on chemistry .and. chem_params%diffusion (line 526), without verifying present(q_T_sf). When chem_diff_comm is true, q_T_sf%sf(...) is dereferenced unconditionally in all three MPI direction pack/unpack blocks. Multiple callers invoke this subroutine without passing q_T_sf: s_populate_capillary_buffers (lines 1335, 1354, 1377, 1396, 1420, 1439 of m_boundary_common.fpp) and s_populate_F_igr_buffers (lines 1657, 1682, 1710, 1735, 1764, 1789). Any chemistry+diffusion simulation that also uses surface tension or IGR on multiple MPI ranks will segfault. Fix: else if (chemistry .and. chem_params%diffusion .and. present(q_T_sf)) then.

& buff_size*v_size*(m + 2*buff_size + 1)*(n + 2*buff_size + 1)/)
else if (chemistry .and. chem_params%diffusion) then
chem_diff_comm = .true.
v_size = nVar + 1

  1. Undefined variable i in NaN-detection print statements -- The three new chem_diff_comm unpack blocks (one per MPI direction) contain print *, "Error", j, k, l, i, but i is not a loop variable in these blocks. The loops iterate over j, k, l only. The variable i exists in the separate do i = 1, nVar loop for q_comm above, but is not in scope for the chem_diff_comm section. Under Intel ifx (a CI-gated compiler where this #if defined(__INTEL_COMPILER) block compiles), i will have an undefined or stale value. The same bug appears in all three directions.

#if defined(__INTEL_COMPILER)
if (ieee_is_nan(q_T_sf%sf(j + unpack_offset, k, l))) then
print *, "Error", j, k, l, i
call s_mpi_abort("NaN(s) in recv")
end if
#endif

Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@sbryngelson
Copy link
Copy Markdown
Member

Code review (additional findings)

8 additional issues found during review, listed by estimated severity:


1. q_T_sf allocated without ghost cells in pre_process — out-of-bounds crash

In m_initial_condition.fpp, q_T_sf%sf is allocated with interior-only bounds:

allocate (q_T_sf%sf(0:m, 0:n, 0:p))

When elliptic_smoothing=T and chemistry=T, the code passes q_T_sf to s_elliptic_smoothing, which calls s_populate_variables_buffers -> s_ghost_cell_extrapolation, which writes to ghost-cell indices:

q_T_sf%sf(-j, k, l) = q_T_sf%sf(0, k, l)   ! j=1..buff_size -> index -1..-buff_size

Since the array lower bound is 0, accessing index -1 is out-of-bounds. The simulation target allocates correctly with idwbuff bounds in m_time_steppers.fpp, but pre_process does not. Fix: allocate with ghost-cell bounds matching the simulation target.

if (chemistry) then
allocate (q_T_sf%sf(0:m,0:n,0:p))
end if


2. Validator does not require Twall_in/out when isothermal_in/out is enabled — silent -1e6 K wall temperature

check_chemistry() validates that isothermal_in/out requires chemistry + diffusion + a wall BC type, but does not check that Twall_in/out is set to a physical value. The default is dflt_real = -1e6. A user who writes 'bc_y%isothermal_in': 'T' but omits 'bc_y%Twall_in' will silently get -1,000,000 K as the wall temperature, corrupting results with no error. A prohibit check like Twall_in == dflt_real when isothermal is enabled would catch this.

if isothermal_in:
# Prohibit isothermal boundaries if chemistry or diffusion are disabled
self.prohibit(not chemistry or not diffusion, f"Isothermal In (bc_{dir}%isothermal_in) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
# Prohibit if neither beg nor end is set to a valid wall condition
self.prohibit(bc_beg not in wall_bcs, f"Isothermal In (bc_{dir}%isothermal_in) requires a wall. Set bc_{dir}%beg to -15 (slip) or -16 (no-slip).")
if isothermal_out:
# Prohibit isothermal boundaries if chemistry or diffusion are disabled
self.prohibit(not chemistry or not diffusion, f"Isothermal Out (bc_{dir}%isothermal_out) requires both chemistry='T' and chem_params%diffusion='T' to calculate heat conduction.")
# Prohibit if neither beg nor end is set to a valid wall condition
self.prohibit(bc_end not in wall_bcs, f"Isothermal Out (bc_{dir}%isothermal_out) requires a wall. Set bc_{dir}%end to -15 (slip) or -16 (no-slip).")


3. Adiabatic temperature ghost-cell uses reflection instead of zero-gradient extrapolation

In s_slip_wall and s_no_slip_wall, when isothermal_in/out = .false. (adiabatic wall), temperature ghost cells are filled with a reflective/mirror stencil:

q_T_sf%sf(-j, k, l) = q_T_sf%sf(j - 1, k, l)   ! mirror

But every other scalar in the same routines uses constant extrapolation from the boundary cell:

q_prim_vf(i)%sf(-j, k, l) = q_prim_vf(i)%sf(0, k, l)   ! zero-gradient

And s_ghost_cell_extrapolation also uses zero-gradient for temperature:

q_T_sf%sf(-j, k, l) = q_T_sf%sf(0, k, l)

The comment on s_slip_wall says "extrapolating scalars." For buff_size > 1 (WENO5 typically has buff_size=4), cells -2, -3, -4 get different values between the two stencils. This affects all 6 directions in both s_slip_wall and s_no_slip_wall.

end do
else
do j = 1, buff_size
q_T_sf%sf(-j, k, l) = q_T_sf%sf(j - 1, k, l)
end do
end if


4. s_periodic guard inconsistent with other BC routines

Most BC routines guard temperature ghost-cell updates with:

if (chemistry .and. present(q_T_sf)) then

But s_periodic uses a stricter condition:

if (chemistry .and. chem_params%diffusion .and. present(q_T_sf)) then

With chemistry=T, diffusion=F, all other BCs (extrapolation, symmetry, slip wall, no-slip wall) propagate temperature ghost cells but periodic BCs do not. This asymmetry could produce incorrect ghost-cell values in that configuration.

if (chemistry .and. chem_params%diffusion .and. present(q_T_sf)) then
do j = 1, buff_size


5. check_chemistry() only called from validate_pre_process()

check_chemistry() validates the isothermal BC parameters but is only invoked from validate_pre_process(). It is not called from validate_simulation(), validate_post_process(), or validate_common(). A user running simulation directly from existing initial condition data (e.g., a restart) bypasses this validation entirely, even though the isothermal BC logic executes in simulation and post_process.

self.check_perturb_density()
self.check_chemistry()
self.check_misc_pre_process()


6. Missing @:PROHIBIT checks in Fortran checker files

CLAUDE.md says "Every new parameter MUST be added in at least 3 places (4 if it has constraints)." .claude/rules/parameter-system.md says "When adding parameters with physics constraints, add Fortran-side checks here in addition to case_validator.py."

The new isothermal_in/out and Twall_in/out parameters have physics constraints in case_validator.py (require chemistry, diffusion, wall BC type -15 or -16), but no corresponding @:PROHIBIT() checks were added to src/simulation/m_checker.fpp or src/common/m_checker_common.fpp.


7. isothermal_out description incomplete in descriptions.py

The description reads "Enable isothermal at {0}-outlet" -- missing the word "wall" for consistency with isothermal_in which reads "Enable isothermal wall at {0}-inlet".

(r"bc_([xyz])%isothermal_out", "Enable isothermal at {0}-outlet"),


8. Double parentheses in s_no_slip_wall

if ((bc_x%isothermal_in)) at line 1012 uses double parentheses while every other isothermal check in the file uses single parentheses. Cosmetic only.

if (chemistry .and. present(q_T_sf)) then
if ((bc_x%isothermal_in)) then
do j = 1, buff_size


Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@sbryngelson sbryngelson added claude-full-review Trigger Claude Code review and removed claude-full-review Trigger Claude Code review labels Apr 15, 2026
@sbryngelson
Copy link
Copy Markdown
Member

sbryngelson commented Apr 16, 2026

Code review (post-update check)

Checked the changes since the previous review — the "AI Suggestion" commit (2770ad31) addressed most of the prior findings. Status below, followed by two items to revisit.


Prior issues — status

Fixed:

  • q_T_sf now allocated with ghost cells (idwbuff) in pre_process
  • ✅ Adiabatic fallback in s_slip_wall/s_no_slip_wall now uses zero-gradient extrapolation (q_T_sf(-j,k,l) = q_T_sf(0,k,l)) instead of mirror stencil
  • check_chemistry() now called from validate_simulation() and validate_post_process()
  • isothermal_out description now reads "Enable isothermal wall at {0}-outlet"
  • ✅ Double parentheses in s_no_slip_wall isothermal check removed
  • chem_diff_comm guard now includes present(q_T_sf) in m_mpi_common.fpp
  • ✅ Debug NaN print statements no longer reference out-of-scope i

Still not addressed:

  • No validation that Twall_in/out != dflt_real when isothermal_in/out is enabled (prior item Update m_global_parameters.f90 #2). check_chemistry() still skips this check. A user enabling bc_y%isothermal_in=T without setting bc_y%Twall_in will silently get T_wall = -1e6 K and corrupted output. Add the check to check_chemistry() in case_validator.py — since that function now runs for all three stages, the toolchain is the right place for this.

(Retracted: the earlier suggestion to add @:PROHIBIT Fortran-side checks for isothermal BCs was over-defensive. The toolchain check_chemistry() now covers all three validation stages, so a duplicate Fortran-side check isn't needed. @:PROHIBIT should be reserved for constraints the toolchain can't see — compiler quirks, per-rank cell counts, runtime-set quantities.)


New issues introduced in this update

1. CRITICAL — s_periodic z-beg guard not harmonized (one of six branches missed)

The AI Suggestion dropped chem_params%diffusion from five of the six periodic branches but left line 733 (the bc_z%beg branch) untouched:

! x-beg (line 637): chemistry .and. present(q_T_sf)
! x-end (line 660): chemistry .and. present(q_T_sf)
! y-beg (line 685): chemistry .and. present(q_T_sf)
! y-end (line 708): chemistry .and. present(q_T_sf)
! z-beg (line 733): chemistry .and. chem_params%diffusion .and. present(q_T_sf)   <-- MISSED
! z-end (line 756): chemistry .and. present(q_T_sf)

This is exactly the bug the prior review flagged (item #4, "s_periodic guard inconsistent with other BC routines") — the fix was applied to every periodic branch except the z-beg one. For bc_z%beg periodic with chemistry=T, diffusion=F, temperature ghost cells along the z-lower face will not be periodically wrapped while all other faces will. This asymmetry silently produces incorrect T in the ghost region and shows up as a z-direction asymmetry in reacting flow results.

if (chemistry .and. chem_params%diffusion .and. present(q_T_sf)) then


2. MODERATE — s_compute_T_from_primitives(q_T_sf, q_prim_vf, idwbuff) reads uninitialized primitives

The AI Suggestion changed two calls from idwint to idwbuff:

if (chemistry) call s_compute_T_from_primitives(q_T_sf, q_prim_vf, idwbuff)   ! line 141
...
    call s_compute_T_from_primitives(q_T_sf, q_prim_vf, idwbuff)              ! line 145

At the time of the line-141 call, ghost cells of q_prim_vf have not been populated — in pre_process, only s_apply_icpp_patches (interior only) runs before this. The ghost cells still hold the sentinel initialization value set at line 64:

q_prim_vf(i)%sf = -1.e-6_stp   ! real(dflt_real, kind=stp)

Inside the compute, for each (x,y,z) in the ghost region:

q_T_sf%sf(x, y, z) = q_prim_vf(eqn_idx%E)%sf(x, y, z) * mix_mol_weight &
                     / (gas_constant * q_prim_vf(1)%sf(x, y, z))

With negative -1e-6 partial density, negative -1e-6 internal energy, and Ys = -1e-6 for every species, this produces a finite but physically meaningless T (and a potentially negative mixture molecular weight from get_mixture_molecular_weight). The garbage T then gets written to disk by s_write_data_files(..., q_T_sf) in the parallel output path.

In the non-elliptic_smoothing path this garbage is never overwritten before output. Simulation restart will re-populate the buffer via its own BC fill, so the bug is silent there — but any consumer that reads T ghost cells directly from the pre_process output (post_process visualization near boundaries, diagnostic scripts) will see corrupt values.

The allocation change alone (using idwbuff bounds) is sufficient to fix the original out-of-bounds bug; keeping the compute call at idwint is safer. Alternatively, move the s_compute_T_from_primitives call to after a buffer-population step.

if (chemistry) call s_compute_T_from_primitives(q_T_sf, q_prim_vf, idwbuff)

call s_compute_T_from_primitives(q_T_sf, q_prim_vf, idwbuff)


Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@DimAdam-01 DimAdam-01 marked this pull request as draft April 17, 2026 00:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

claude-full-review Trigger Claude Code review

Development

Successfully merging this pull request may close these issues.

2 participants