From 8c7a747b473d98b0f064af1413a7c879b2ce5899 Mon Sep 17 00:00:00 2001 From: Spencer Bryngelson Date: Thu, 26 Feb 2026 19:23:25 -0500 Subject: [PATCH 1/4] Fix post_process non-MPI builds never reading input file In s_initialize_mpi_domain, the entire body including s_assign_default_values_to_user_inputs(), s_read_input_file(), and s_check_input_file() was guarded by #ifdef MFC_MPI. Non-MPI builds therefore ran with all parameters at Fortran defaults (case_dir blank, m/n/p = 0), causing a misleading "Time-step folder missing" error. pre_process and simulation already call these unconditionally; the individual MPI functions (s_mpi_bcast_user_inputs, etc.) handle the #ifdef MFC_MPI guard internally as stubs when MPI is absent. Co-Authored-By: Claude Sonnet 4.6 --- src/post_process/m_start_up.fpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/post_process/m_start_up.fpp b/src/post_process/m_start_up.fpp index ecc0d70cef..74a15d04db 100644 --- a/src/post_process/m_start_up.fpp +++ b/src/post_process/m_start_up.fpp @@ -1149,7 +1149,6 @@ contains num_dims = 1 + min(1, n) + min(1, p) -#ifdef MFC_MPI ! Initialization of the MPI environment call s_mpi_initialize() @@ -1167,14 +1166,12 @@ contains ! Broadcasting the user inputs to all of the processors and performing the ! parallel computational domain decomposition. Neither procedure has to be - ! carried out if the simulation is in fact not truly executed in parallel. + ! carried out if the post-process is in fact not truly executed in parallel. call s_mpi_bcast_user_inputs() call s_initialize_parallel_io() call s_mpi_decompose_computational_domain() call s_check_inputs_fft() -#endif - end subroutine s_initialize_mpi_domain !> @brief Destroy FFTW plans, free MPI communicators, and finalize all post-process sub-modules. From 233a1f6420a36dd3d5f4cbde607b841b85a46a8d Mon Sep 17 00:00:00 2001 From: Spencer Bryngelson Date: Thu, 26 Feb 2026 19:39:33 -0500 Subject: [PATCH 2/4] Add build-flag compatibility checks to Python case validator Move three Fortran @:PROHIBIT checks that test case parameters against build configuration flags into case_validator.py, so they fire before any binary is invoked rather than at Fortran runtime: - parallel_io = T requires --mpi (all stages, via check_build_flags) - precision = 2 requires no --single (post_process, in check_output_format) - 3D cylindrical + p > 0 requires no --single (simulation only, via check_geometry_precision_simulation) CFG() from state.py carries the active build config (mpi, single, mixed). During standalone './mfc.sh validate', CFG() holds defaults (mpi=T, single=F) so these checks do not produce false positives. Co-Authored-By: Claude Sonnet 4.6 --- toolchain/mfc/case_validator.py | 34 +++++++++++++++++++++++++++++++++ toolchain/mfc/lint_docs.py | 2 ++ 2 files changed, 36 insertions(+) diff --git a/toolchain/mfc/case_validator.py b/toolchain/mfc/case_validator.py index 2c29c75e7f..15acc58120 100644 --- a/toolchain/mfc/case_validator.py +++ b/toolchain/mfc/case_validator.py @@ -19,6 +19,7 @@ from functools import lru_cache from .common import MFCException from .params.definitions import CONSTRAINTS +from .state import CFG # Physics documentation for check methods. @@ -1794,6 +1795,10 @@ def check_output_format(self): if precision is not None: self.prohibit(precision not in [1, 2], "precision must be 1 or 2") + self.prohibit( + precision == 2 and CFG().single, + "precision = 2 (double output) requires MFC built without --single" + ) def check_vorticity(self): """Checks vorticity parameters (post-process)""" @@ -2330,6 +2335,33 @@ def check_velocity_components(self): self.prohibit(vel3 != 0, f"patch_icpp({i})%vel(3) = {vel3} but p = 0 (1D/2D simulation)") + # =================================================================== + # Build-Flag Compatibility Checks + # =================================================================== + + def check_build_flags(self): + """Checks case parameters against the active build configuration. + + These catch incompatibilities between case settings and how the + MFC binaries were compiled (--mpi/--no-mpi, --single, etc.) + before any binary is invoked. + """ + parallel_io = self.get('parallel_io', 'F') == 'T' + self.prohibit( + parallel_io and not CFG().mpi, + "parallel_io = T requires MFC built with --mpi" + ) + + def check_geometry_precision_simulation(self): + """Checks that 3D cylindrical geometry is not used with --single builds.""" + cyl_coord = self.get('cyl_coord', 'F') == 'T' + p = self.get('p', 0) + self.prohibit( + CFG().single and cyl_coord and p is not None and p > 0, + "Fully 3D cylindrical geometry (cyl_coord = T, p > 0) is not supported " + "in single precision (--single)" + ) + # =================================================================== # Main Validation Entry Points # =================================================================== @@ -2337,6 +2369,7 @@ def check_velocity_components(self): def validate_common(self): """Validate parameters common to all stages""" self.check_parameter_types() # Type validation first + self.check_build_flags() self.check_simulation_domain() self.check_domain_bounds() self.check_model_eqns_and_num_fluids() @@ -2359,6 +2392,7 @@ def validate_common(self): def validate_simulation(self): """Validate simulation-specific parameters""" self.validate_common() + self.check_geometry_precision_simulation() self.check_finite_difference() self.check_time_stepping() self.check_riemann_solver() diff --git a/toolchain/mfc/lint_docs.py b/toolchain/mfc/lint_docs.py index 4e5fdf3461..b6e195ecc3 100644 --- a/toolchain/mfc/lint_docs.py +++ b/toolchain/mfc/lint_docs.py @@ -423,6 +423,8 @@ def check_physics_docs_coverage(repo_root: Path) -> list[str]: "check_output_format", # output format selection "check_restart", # restart file logistics "check_parallel_io_pre_process", # parallel I/O settings + "check_build_flags", # build-flag compatibility (no physics meaning) + "check_geometry_precision_simulation", # build-flag compatibility (no physics meaning) "check_misc_pre_process", # miscellaneous pre-process flags "check_bc_patches", # boundary patch geometry "check_grid_stretching", # grid stretching parameters From 8627b414ca9639b568a68bb6c636140d88cd0492 Mon Sep 17 00:00:00 2001 From: Spencer Bryngelson Date: Thu, 26 Feb 2026 19:42:53 -0500 Subject: [PATCH 3/4] Remove Fortran PROHIBIT checks now handled by Python validator The three build-flag compatibility checks moved to case_validator.py in the previous commit no longer need Fortran-side enforcement. Remove them and their now-empty subroutines: - pre_process/m_checker.fpp: s_check_parallel_io (parallel_io + --no-mpi) - simulation/m_checker.fpp: s_check_inputs_geometry_precision (3D cyl + --single) - post_process/m_checker.fpp: s_check_inputs_output_format (precision=2 + --single) Co-Authored-By: Claude Sonnet 4.6 --- src/post_process/m_checker.fpp | 8 -------- src/pre_process/m_checker.fpp | 10 ---------- src/simulation/m_checker.fpp | 9 --------- 3 files changed, 27 deletions(-) diff --git a/src/post_process/m_checker.fpp b/src/post_process/m_checker.fpp index 2116fb3d39..13587887a8 100644 --- a/src/post_process/m_checker.fpp +++ b/src/post_process/m_checker.fpp @@ -24,16 +24,8 @@ contains !> Checks compatibility of parameters in the input file. !! Used by the post_process stage impure subroutine s_check_inputs - - call s_check_inputs_output_format - end subroutine s_check_inputs - !> Checks constraints on output format parameters - impure subroutine s_check_inputs_output_format - @:PROHIBIT(precision == 2 .and. wp == sp) - end subroutine s_check_inputs_output_format - !> Checks constraints on fft_wrt impure subroutine s_check_inputs_fft integer :: num_procs_y, num_procs_z diff --git a/src/pre_process/m_checker.fpp b/src/pre_process/m_checker.fpp index 56ceb430d5..63ed19199b 100644 --- a/src/pre_process/m_checker.fpp +++ b/src/pre_process/m_checker.fpp @@ -24,16 +24,6 @@ contains !> Checks compatibility of parameters in the input file. !! Used by the pre_process stage impure subroutine s_check_inputs - - call s_check_parallel_io - end subroutine s_check_inputs - !> Checks if mpi is enabled with parallel_io - impure subroutine s_check_parallel_io -#ifndef MFC_MPI - @:PROHIBIT(parallel_io, "MFC built with --no-mpi requires parallel_io=F") -#endif - end subroutine s_check_parallel_io - end module m_checker diff --git a/src/simulation/m_checker.fpp b/src/simulation/m_checker.fpp index 4c5ff1ed99..ed694c449d 100644 --- a/src/simulation/m_checker.fpp +++ b/src/simulation/m_checker.fpp @@ -36,7 +36,6 @@ contains else if (recon_type == MUSCL_TYPE) then call s_check_inputs_muscl end if - call s_check_inputs_geometry_precision end if call s_check_inputs_time_stepping @@ -76,14 +75,6 @@ contains "For 3D simulation, p must be greater than or equal to (num_stcls_min*muscl_order - 1), whose value is "//trim(numStr)) end subroutine s_check_inputs_muscl - !> Checks constraints on geometry and precision - impure subroutine s_check_inputs_geometry_precision - ! Prevent spherical geometry in single precision -#ifdef MFC_SINGLE_PRECISION - @:PROHIBIT(.not. (cyl_coord .neqv. .true. .or. (cyl_coord .and. p == 0)), "Fully 3D cylindrical grid (geometry = 3) is not supported in single precision.") -#endif - end subroutine s_check_inputs_geometry_precision - !> Checks constraints on time stepping parameters impure subroutine s_check_inputs_time_stepping if (.not. cfl_dt) then From 1c41100b7a89c95d353fe9d0ee34a78d9bf7bf1e Mon Sep 17 00:00:00 2001 From: Spencer Bryngelson Date: Thu, 26 Feb 2026 19:51:27 -0500 Subject: [PATCH 4/4] Remove dead p is not None guard in check_geometry_precision_simulation self.get('p', 0) always returns an int (0 when absent), so the p is not None check was always True and never needed. Co-Authored-By: Claude Sonnet 4.6 --- toolchain/mfc/case_validator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolchain/mfc/case_validator.py b/toolchain/mfc/case_validator.py index 15acc58120..4c47588501 100644 --- a/toolchain/mfc/case_validator.py +++ b/toolchain/mfc/case_validator.py @@ -2357,7 +2357,7 @@ def check_geometry_precision_simulation(self): cyl_coord = self.get('cyl_coord', 'F') == 'T' p = self.get('p', 0) self.prohibit( - CFG().single and cyl_coord and p is not None and p > 0, + CFG().single and cyl_coord and p > 0, "Fully 3D cylindrical geometry (cyl_coord = T, p > 0) is not supported " "in single precision (--single)" )