Skip to content

Commit c6a802c

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 4862a12 commit c6a802c

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():
@@ -500,8 +501,6 @@ def test_generate_bcast_fpp_excludes_manual_residue():
500501
assert "m_glb" not in out, f"{target}: m_glb should be manual"
501502
assert "n_glb" not in out, f"{target}: n_glb should be manual"
502503
assert "p_glb" not in out, f"{target}: p_glb should be manual"
503-
# muscl_eps is excluded (derived post-broadcast)
504-
assert "muscl_eps" not in out, f"{target}: muscl_eps should be excluded"
505504

506505
sim = generate_bcast_fpp("sim")
507506
# shear_stress, bulk_stress, bodyForces are derived (non-namelist)
@@ -526,3 +525,79 @@ def test_generate_bcast_fpp_bad_target():
526525

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

0 commit comments

Comments
 (0)