Skip to content

Make --mixed build again, and fix its multi-rank parallel I/O - #2110

Open
platelett wants to merge 1 commit into
MFlowCode:masterfrom
platelett:codex/fix-mixed-precision
Open

platelett wants to merge 1 commit into
MFlowCode:masterfrom
platelett:codex/fix-mixed-precision

Conversation

@platelett

Copy link
Copy Markdown
Contributor

./mfc.sh build --mixed (half-precision storage stp, single-precision working wp) does not compile on master with NVHPC. Once it does, runs on two or more ranks with parallel_io = 'T' write corrupted initial-condition and restart files. This PR fixes both.

Non-mixed builds are unaffected. There stp == wp, mpi_io_p == mpi_p and mpi_io_type == 1, so every change below is a no-op. The Cray --mixed failure in #1965 happens earlier, in m_derived_types, and is not addressed here.

1. The build

On master, ./mfc.sh build --mixed --no-gpu with NVHPC 26.3 fails in simulation:

NVFORTRAN-S-0155-Illegal POINTER assignment - type mismatch           (m_igr.fpp)
NVFORTRAN-S-0450-Argument number 1 to f_compute_shear_rate_from_components: kind mismatch   (m_viscous.fpp)

--gpu acc also fails on f_bulk_modulus and f_advance_step. The fixes:

  • real(stp) field values passed to real(wp) dummies.
    • Where: m_viscous, m_bubbles_EE, m_acoustic_src, and post_process/m_start_up (s_phase_temperature).
    • Fix: they are converted with real(..., kind=wp) at the call site. The dummies are intent(in), so nothing is written back.
    • Operands first: where an argument combines two field values (the off-diagonal shear rates, and the phase density alpha_rho/max(alpha, sgm_eps)), each operand is converted first. Otherwise the sum, quotient and max would run in half precision, and max would mix stp with the wp constant sgm_eps.
  • jac in m_igr is declared real(wp), but it is exposed through a scalar_field pointer, which is real(stp). Every assignment to jac already converts to stp, so it is now declared real(stp). jac_rhs and jac_old stay wp.
  • integer_field stays a default integer. It used to drop to integer(kind=1) under --mixed (since d290396), but its users need a default integer:
    • s_decode_patch_periodicity takes a default integer. This is the error left in m_ibm.fpp once the real-kind fixes are in.
    • The IB marker MPI-IO view is built from MPI_INTEGER.
    • ib_markers is updated with a GPU_ATOMIC max.
    • An encoded periodic IB id, gbl_id + (num_gbl_ibs+1)*(xp + 3*yp + 9*zp), exceeds 127 with only a few bodies.

2. Parallel I/O

Under --mixed a stored cell is 2 bytes (mpi_io_p = MPI_BYTE, mpi_io_type = 2). The field file views did not follow that:

  • s_initialize_mpi_data and s_initialize_mpi_data_ds built each field's subarray from mpi_p, which is 4 bytes.
  • The MPI_FILE_SET_VIEW calls in pre_process/m_start_up, simulation/m_data_output and post_process/m_data_input used mpi_p as the etype, while the transfer right after uses mpi_io_p. This breaks the Common Pitfalls rule in contributing.md: "mpi_io_p must match stp".
  • pre_process/m_start_up read data_size elements of mpi_p into a buffer of data_size 2-byte cells.

On one rank the layout still comes out right by accident. On two or more, each rank's block lands at the wrong offset.

The fix:

  • Subarrays are now built from a 2-byte contiguous cell type, in s_create_mpi_field_subarray, a private helper in m_mpi_common.
  • Every view uses mpi_io_p as its etype.
  • The pre_process reader reads data_size*mpi_io_type elements of mpi_io_p.

The remaining mpi_p views (Lagrange bubbles and the post_process output) describe real(wp) buffers and are correct as they are.

toolchain/mfc/test_mixed_mpi_views.py:

  • What it does: compiles the production s_initialize_mpi_data and every production field SET_VIEW/transfer pair against 2-byte opaque cells, so it needs no REAL(2) support. It writes from 2 ranks and compares the result with a serially written file.
  • Results: it fails on master and passes with this PR.
  • When it skips: without an MPI compiler, and under MFC_SKIP_COMPILER_TESTS=1, like the other compiler tests.

Side effects

  • Non-mixed builds: no change. The conversions are no-ops, and the helper builds exactly the old subarray. No goldens change.
  • --mixed builds:
    • ib_markers and bc_type take 4 bytes per cell instead of 1.
    • IB-marker files written by an older --mixed build cannot be read back. NVHPC could not produce such a build on master.
    • jac (IGR) is stored in stp, which is what every assignment to it already produced.
  • Overlap with SCC 26: bug: fix parallel I/O failures and performance overhead #2085 (%sf → %sf(:,:,:) in the same MPI-IO calls), in pre_process/m_start_up, simulation/m_data_output and post_process/m_data_input:
  • Overlap with SCC26: Use nonblocking MPI for halo exchange #2090: one private :: line in m_mpi_common, keep both.
Verification

Hardware: Intel Core i7-14700F, RTX 4060 Ti (sm_89). Compiler: NVHPC 26.3 with its bundled HPC-X MPI, built in clean build directories.

Build:

Build master this PR
--mixed --no-gpu, pre/sim/post simulation fails (above) builds
--mixed --gpu acc, pre/sim/post simulation fails (also f_bulk_modulus, f_advance_step) builds

Parallel I/O:

  • Case: examples/2D_riemann_test at 64², 20 steps, parallel_io = 'T', --mixed.
  • Comparison: the 1-rank and 2-rank restart_data/lustre_<step>.dat files, byte for byte. Each is 40,960 bytes (64·64·5 fields·2 bytes).
lustre_0 (initial condition) lustre_10 lustre_20
build fixes only, without the view fix: 1 rank vs 2 ranks differ: 3,272 of 20,480 values; density off by up to 0.97 (range 0.14–1.5) differ differ
this PR, CPU: 1 rank vs 2 ranks identical identical identical
this PR, GPU (--gpu acc): 1 rank vs 2 ranks identical — identical

Density at step 20, 2 ranks, against a double-precision run of the same case:

--mixed Max relative difference
this PR, CPU 1.1e-3 (half-precision storage)
this PR, GPU 1.1e-3
build fixes only, without the view fix, CPU 6.5e-1

The final code writes restart files byte-identical to the runs above, on CPU and GPU. This inviscid case does not reach the operand-first conversions, which were added after those runs.

The operand-first conversions:

Check Result
post_process temperature (T_wrt = 'T' on the case above), --mixed, CPU and GPU, against double max relative difference 1.0e-3 (initial condition) and 2.5e-3 (step 20), the same level as density and pressure
Same case, 1 rank vs 2 ranks, CPU and GPU restart files identical
examples/2D_poiseuille_nn (non-Newtonian), --mixed, CPU, 2,730 steps runs, all fields finite. Its velocities (~1e-5) are below half precision's normal range, so it cannot be compared meaningfully with double
simulation, --mixed --gpu acc --unified (the UVM jac_host path) builds

Tests:

  • test_mixed_mpi_views.py: fails on master. Passes with this PR on Open MPI 4.1.6 + gfortran 13.3 and on HPC-X + nvfortran 26.3. Also passes merged with SCC 26: bug: fix parallel I/O failures and performance overhead #2085.
  • Full regression suite, non-mixed: final code on current master ee99151b, GNU 13.3 + Open MPI 4.1.6, CPU: 725 passed, 0 failed (39 convergence cases skipped by default). No golden changes.
  • Lint gate: ./mfc.sh lint, including the compiler tests, passes.

Contribution Policy

All contributions are expected to demonstrate:

  • A clear understanding of the codebase
  • Alignment with product direction
  • Thoughtful reasoning behind changes
  • Evidence of real-world usage or hands-on experience with the problem

If these expectations are not met, we would prefer to implement the changes ourselves rather than spend time reviewing low-effort submissions.

Acknowledgement

  • I confirm this PR meets the above expectations and reflects my own understanding and real-world context.

This PR is from Diablo (Tsinghua University) participating in the SC26 Student Cluster Competition.

Motivation
Mixed-precision builds have incompatible storage and working types, and parallel field views use the wrong cell size.

Expectation
Make the affected interfaces and MPI I/O agree with storage precision while preserving non-mixed behavior.

Change
A --mixed build (stp = half, wp = single) did not compile with
nvfortran, and once it did, runs on two or more ranks with
parallel_io wrote corrupted initial-condition and restart files.

Build:
- m_viscous, m_bubbles_EE, m_acoustic_src and post_process m_start_up
  passed real(stp) field values to real(wp) dummies. Convert them at
  the call site, converting each operand before any sum or quotient so
  the arithmetic does not run in half precision.
- m_igr exposes jac through a scalar_field pointer, which is real(stp),
  while jac was declared real(wp). Every write to jac already converts
  to stp, so declare it real(stp).
- integer_field dropped to integer(kind=1) under --mixed, but
  s_decode_patch_periodicity, the MPI_INTEGER marker I/O and the
  GPU_ATOMIC max expect default integers, and encoded periodic IB ids
  exceed 127. Keep it a default integer.

Parallel I/O: a stored cell is 2 bytes under --mixed (mpi_io_p =
MPI_BYTE, mpi_io_type = 2), but the field subarrays were built from
mpi_p and several file views used mpi_p as their etype, so each rank's
block landed at the wrong offset. Build the subarrays from a 2-byte
cell type (s_create_mpi_field_subarray), use mpi_io_p as the etype
wherever the transfer uses it, and read data_size*mpi_io_type elements
in pre_process. test_mixed_mpi_views.py checks the production views
from 2 ranks against a serially written file.

Non-mixed builds are unchanged: stp == wp, mpi_io_p == mpi_p and
mpi_io_type == 1 there, so every change is a no-op.

Result
The type conversions, integer metadata and file-view fixes are retained. The precheck passes and regression reference files are unchanged. Compiler tests are skipped by the standard precheck; the documented numerical and compiler verification is not rerun by this preparation.

Co-authored-by: ruru25254649 <ruru.2525.4649@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WPhkY8p1EHhGY7jScizJmC
@platelett
platelett requested a review from a team as a code owner October 25, 2026 09:56
@platelett
platelett requested review from sbryngelson and removed request for a team October 25, 2026 09:56
@github-actions

Copy link
Copy Markdown

Lines of Code

File Lines Diff
src/common/m_mpi_common.fpp 1503 +9
src/common/m_derived_types.fpp 479 -4
src/simulation/m_viscous.fpp 1023 +2
src/simulation/m_igr.fpp 2290 +1
Directory Lines Diff
common 10541 +5
simulation 28566 +3
total 47640 +8

@github-actions

Copy link
Copy Markdown

Claude Code Review

Head SHA: 910f07b

Files changed:

  • 11
  • src/common/m_derived_types.fpp
  • src/common/m_mpi_common.fpp
  • src/post_process/m_data_input.f90
  • src/post_process/m_start_up.fpp
  • src/pre_process/m_start_up.fpp
  • src/simulation/m_acoustic_src.fpp
  • src/simulation/m_bubbles_EE.fpp
  • src/simulation/m_data_output.fpp
  • src/simulation/m_igr.fpp
  • src/simulation/m_viscous.fpp

Findings:
No issues found. Checked the changed hunks in m_mpi_common, m_data_input, both m_start_up files, m_acoustic_src, m_bubbles_EE, m_viscous and m_derived_types. The m_igr jac declaration change could not be checked against the other jac uses, because m_igr has no file in the context directory.

@codecov

codecov Bot commented Oct 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 65.21739% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.96%. Comparing base (ee99151) to head (910f07b).

Files with missing lines Patch % Lines
src/pre_process/m_start_up.fpp 0.00% 4 Missing ⚠️
src/simulation/m_viscous.fpp 0.00% 2 Missing ⚠️
src/common/m_mpi_common.fpp 87.50% 1 Missing ⚠️
src/post_process/m_start_up.fpp 0.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master    #2110   +/-   ##
=======================================
  Coverage   61.95%   61.96%           
=======================================
  Files          86       86           
  Lines       22906    22908    +2     
  Branches     3378     3378           
=======================================
+ Hits        14192    14195    +3     
+ Misses       6206     6205    -1     
  Partials     2508     2508           

☔ View full report in Codecov by Harness.
📢 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.

This branch has not been deployed

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant