Skip to content

Commit bdd6a7e

Browse files
committed
codegen: broadcast muscl_eps and walk the registry for struct broadcasts
muscl_eps was excluded from broadcast generation via _BCAST_EXCLUDE on the incorrect assumption that it is derived post-broadcast. The derivation (in m_weno or m_muscl) only fires under f_is_default(muscl_eps), and default values are assigned on rank 0 only. Every multi-rank MUSCL run therefore had rank-divergent muscl_eps on non-root ranks. Remove it from the exclusion set. Tuple-set delta (var, mpi_type, count) vs. HEAD~: sim: +1 entry: (muscl_eps, mpi_p, 1) pre: no change post: no change _emit_fluid_pp and _emit_lag_params now walk the registry instead of maintaining hardcoded member lists. After Commit 1 deregistered the dead members (mul0/ss/pv/gamma_v/M_v/mu_v/k_v/cp_v/D_v for fluid_pp; T0/Thost/c0/rho0/x0 for lag_params), the registry now matches the Fortran types exactly. Re(1) count=2 remains sim-only via an explicit target check with a comment. G is walked as a regular REAL member. Tests: 3 new tests added — muscl_eps now broadcast in sim, fluid_pp and lag_params registry walks produce exactly the registered members minus documented exclusions, dead members absent.
1 parent 3596cc0 commit bdd6a7e

2 files changed

Lines changed: 93 additions & 11 deletions

File tree

toolchain/mfc/params/generators/fortran_gen.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,11 @@ def generate_case_opt_decls_fpp() -> str:
235235
_STRUCT_ROOTS = frozenset({"bc_x", "bc_y", "bc_z", "x_domain", "y_domain", "z_domain", "x_output", "y_output", "z_output"})
236236

237237
# Variables excluded from broadcast generation (derived post-broadcast or non-namelist).
238-
_BCAST_EXCLUDE = frozenset({"muscl_eps"})
238+
# muscl_eps was previously excluded here on the assumption that it was derived
239+
# post-broadcast, but the derivation only fires under f_is_default(muscl_eps),
240+
# and default values are assigned on rank 0 only. Every multi-rank MUSCL run
241+
# therefore had rank-divergent muscl_eps. Removed from exclusion to fix the bug.
242+
_BCAST_EXCLUDE: frozenset = frozenset()
239243

240244
# Post-process scalars that are namelist-bound but consumed on rank 0 only (reading/init).
241245
# Broadcasting them would be harmless but changes the existing call set, which we preserve.
@@ -318,16 +322,19 @@ def _emit_bcast_group(lines: List[str], vars_list: List[str], mpi_type: str) ->
318322
def _emit_fluid_pp(lines: List[str], target: str) -> None:
319323
"""Emit the fluid_pp(i) member-loop broadcast block.
320324
321-
Members broadcast: all REAL registry members of fluid_pp (gamma, pi_inf, G, cv, qv,
322-
qvp) derived from physical_parameters. Sim additionally: Re(1) with count=2.
325+
Walks the registry for every fluid_pp member, emitting the MPI datatype that
326+
matches each member's registered ParamType (the Herschel-Bulkley merge added
327+
a LOGICAL member, so the datatype must come from the registry, not be assumed
328+
REAL). Sim additionally broadcasts Re(1) with count=2 (kept sim-only to
329+
preserve the historical call set; pre/post never consumed Re).
323330
mul0/ss/pv/gamma_v/M_v/mu_v/k_v/cp_v/D_v were removed from the Fortran type by
324331
upstream #1085/#1093 and are no longer registered.
325332
"""
326-
# Walk the registry for fluid_pp REAL members (Re handled separately; exclude).
327-
fp_real_members = sorted(k.split("%", 1)[1] for k in REGISTRY.all_params if k.startswith("fluid_pp(1)%") and not k.startswith("fluid_pp(1)%Re("))
333+
fp_members = sorted(k.split("%", 1)[1] for k in REGISTRY.all_params if k.startswith("fluid_pp(1)%") and not k.startswith("fluid_pp(1)%Re("))
328334
lines.append(" do i = 1, num_fluids_max")
329-
for mem in fp_real_members:
330-
lines.append(f" call MPI_BCAST(fluid_pp(i)%{mem}, 1, mpi_p, 0, MPI_COMM_WORLD, ierr)")
335+
for mem in fp_members:
336+
ptype = REGISTRY.all_params[f"fluid_pp(1)%{mem}"].param_type
337+
lines.append(f" call MPI_BCAST(fluid_pp(i)%{mem}, 1, {_mpi_type_for(ptype)}, 0, MPI_COMM_WORLD, ierr)")
331338
if target == "sim":
332339
lines.append(" call MPI_BCAST(fluid_pp(i)%Re(1), 2, mpi_p, 0, MPI_COMM_WORLD, ierr)")
333340
lines.append(" end do")

toolchain/mfc/params_tests/test_fortran_gen.py

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -387,8 +387,9 @@ def test_generate_bcast_fpp_case_opt_guard_sim():
387387
assert "num_fluids" in guard_body
388388
assert "mapped_weno" in guard_body
389389

390-
# muscl_eps must NOT appear anywhere (it is excluded — derived post-broadcast)
391-
assert "muscl_eps" not in out
390+
# muscl_eps IS inside the case-opt guard (it is sim-only, non-CASE_OPT_PARAM,
391+
# so it appears in the real-scalars section, which is outside the case-opt block)
392+
assert "call MPI_BCAST(muscl_eps, 1, mpi_p, 0, MPI_COMM_WORLD, ierr)" in out
392393

393394

394395
def test_generate_bcast_fpp_case_opt_not_in_pre_post():
@@ -504,8 +505,6 @@ def test_generate_bcast_fpp_excludes_manual_residue():
504505
assert "m_glb" not in out, f"{target}: m_glb should be manual"
505506
assert "n_glb" not in out, f"{target}: n_glb should be manual"
506507
assert "p_glb" not in out, f"{target}: p_glb should be manual"
507-
# muscl_eps is excluded (derived post-broadcast)
508-
assert "muscl_eps" not in out, f"{target}: muscl_eps should be excluded"
509508

510509
sim = generate_bcast_fpp("sim")
511510
# shear_stress, bulk_stress, bodyForces are derived (non-namelist)
@@ -530,3 +529,79 @@ def test_generate_bcast_fpp_bad_target():
530529

531530
with pytest.raises(ValueError, match="Unknown target"):
532531
generate_bcast_fpp("bad")
532+
533+
534+
def test_generate_bcast_fpp_muscl_eps_now_broadcast():
535+
"""muscl_eps is broadcast for sim (latent-bug fix: derivation is rank-0-only).
536+
537+
Previously excluded via _BCAST_EXCLUDE; every multi-rank MUSCL run had
538+
rank-divergent muscl_eps because f_is_default() only fires on rank 0.
539+
"""
540+
from mfc.params.generators.fortran_gen import generate_bcast_fpp
541+
542+
sim = generate_bcast_fpp("sim")
543+
assert "call MPI_BCAST(muscl_eps, 1, mpi_p, 0, MPI_COMM_WORLD, ierr)" in sim
544+
545+
# muscl_eps is sim-only; must not appear in pre/post
546+
pre = generate_bcast_fpp("pre")
547+
post = generate_bcast_fpp("post")
548+
assert "muscl_eps" not in pre
549+
assert "muscl_eps" not in post
550+
551+
552+
def test_generate_bcast_fpp_fluid_pp_registry_walk():
553+
"""fluid_pp emitter walks registry; no dead members (mul0/ss/pv/gamma_v/M_v/mu_v/k_v/cp_v/D_v removed upstream).
554+
555+
Re(1) count=2 is sim-only; all other registered REAL members appear in all three
556+
targets.
557+
"""
558+
from mfc.params.generators.fortran_gen import generate_bcast_fpp
559+
560+
sim = generate_bcast_fpp("sim")
561+
pre = generate_bcast_fpp("pre")
562+
post = generate_bcast_fpp("post")
563+
564+
# All registered members appear in every target
565+
for mem in ("gamma", "pi_inf", "cv", "qv", "qvp", "G"):
566+
for out, t in [(sim, "sim"), (pre, "pre"), (post, "post")]:
567+
assert f"fluid_pp(i)%{mem}" in out, f"{t}: fluid_pp(i)%{mem} missing"
568+
569+
# Re(1) count=2 is sim-only
570+
assert "fluid_pp(i)%Re(1)" in sim
571+
assert "fluid_pp(i)%Re(1)" not in pre
572+
assert "fluid_pp(i)%Re(1)" not in post
573+
574+
# Dead members must not appear
575+
for dead in ("mul0", "ss", "pv", "gamma_v", "M_v", "mu_v", "k_v", "cp_v", "D_v"):
576+
for out, t in [(sim, "sim"), (pre, "pre"), (post, "post")]:
577+
assert f"fluid_pp(i)%{dead}" not in out, f"{t}: dead member fluid_pp(i)%{dead} present"
578+
579+
580+
def test_generate_bcast_fpp_fluid_pp_member_datatypes():
581+
# The HB merge added a LOGICAL fluid_pp member; datatypes must come from the
582+
# registry, not be assumed REAL.
583+
from mfc.params.generators.fortran_gen import generate_bcast_fpp
584+
585+
sim = generate_bcast_fpp("sim")
586+
assert "call MPI_BCAST(fluid_pp(i)%non_newtonian, 1, MPI_LOGICAL," in sim
587+
assert "call MPI_BCAST(fluid_pp(i)%tau0, 1, mpi_p," in sim
588+
assert "non_newtonian, 1, mpi_p" not in sim
589+
590+
591+
def test_generate_bcast_fpp_lag_params_registry_walk():
592+
"""lag_params emitter walks registry; no dead members (T0/Thost/c0/rho0/x0 removed upstream)."""
593+
from mfc.params.generators.fortran_gen import generate_bcast_fpp
594+
595+
sim = generate_bcast_fpp("sim")
596+
597+
# All registered members must appear
598+
for mem in ("solver_approach", "cluster_type", "smooth_type", "nBubs_glb"):
599+
assert f"lag_params%{mem}" in sim, f"lag_params%{mem} missing from sim"
600+
for mem in ("heatTransfer_model", "massTransfer_model", "pressure_corrector", "write_bubbles", "write_bubbles_stats"):
601+
assert f"lag_params%{mem}" in sim, f"lag_params%{mem} missing from sim"
602+
for mem in ("epsilonb", "charwidth", "valmaxvoid"):
603+
assert f"lag_params%{mem}" in sim, f"lag_params%{mem} missing from sim"
604+
605+
# Dead members must not appear
606+
for dead in ("T0", "Thost", "c0", "rho0", "x0"):
607+
assert f"lag_params%{dead}" not in sim, f"dead member lag_params%{dead} present in sim"

0 commit comments

Comments
 (0)