You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Particle-cloud generation (src/simulation/m_particle_cloud.fpp) is geometry preprocessing, not flow solving — rejection sampling, triangular/FCC lattices, hemisphere-shell CDF sampling, a spatial hash grid, and periodic minimum-image wrapping have nothing to do with the solver. It currently lives in the simulation startup path only because IBM marker computation (m_ibm.fpp, s_ibm_setup) is simulation-side and the packer was placed next to it.
Proposal: expand the compact particle_cloud spec in pre_process, write the resulting IB-patch list to a file, and have the solver just read it. The solver becomes geometry-agnostic about beds and simulation/ sheds the whole subsystem.
This is a follow-up to the review on #1667 — not part of it. #1667 is a feature PR; this is a cross-executable refactor and should land after it merges, so we relocate a finished subsystem rather than a moving target.
Boundary
pre_process expands each particle_cloud into a global IB-patch list and writes restart_data/particle_cloud_ibs.dat; the solver reads that list at fresh start, then reduces to each rank's neighborhood (unchanged). ib_patch_parameters already lives in src/common/m_derived_types.fpp, so both stages share the type.
Reduction stays solver-side.f_neighborhood_ranks_own_location / s_reduce_ib_patch_array depend on the simulation's domain decomposition, so pre_process writes the global list and the solver reduces on read. The lattice packer's inline neighborhood filter disappears (it packs global now) — a simplification.
Scaling win, not loss. Today every rank redundantly recomputes the full deterministic placement; under the file boundary it is computed once. The list is bounded by num_ib_patches_max_namelist (54000) -> tiny file, so the old "avoid broadcasting N patches" concern goes away.
Restart path. On restart (t_step_start > 0) the solver already skips generation and reads ib_state; keep that branch. The .dat read fires only on fresh start (mirrors the current if/else in m_start_up.fpp:903-913).
RNG / goldens. Packing runs in a different binary, so regenerate the 6 particle-cloud goldens rather than assume a bit-identical xorshift stream. The ib_state geometric verifier in test.py is unchanged (reads solver output either way).
Add a ppn=2 case (classic "did the broadcast/decomposition survive" catch, per .claude/rules/common-pitfalls.md).
Keep the geometric ib_state verifier.
Effort
Medium — ~80% mechanical relocation, ~20% genuinely new (file writer/reader + pre_process param plumbing). The real risk surface is parallel correctness, not the algorithms.
Summary
Particle-cloud generation (
src/simulation/m_particle_cloud.fpp) is geometry preprocessing, not flow solving — rejection sampling, triangular/FCC lattices, hemisphere-shell CDF sampling, a spatial hash grid, and periodic minimum-image wrapping have nothing to do with the solver. It currently lives in the simulation startup path only because IBM marker computation (m_ibm.fpp,s_ibm_setup) is simulation-side and the packer was placed next to it.Proposal: expand the compact
particle_cloudspec in pre_process, write the resulting IB-patch list to a file, and have the solver just read it. The solver becomes geometry-agnostic about beds andsimulation/sheds the whole subsystem.This is a follow-up to the review on #1667 — not part of it. #1667 is a feature PR; this is a cross-executable refactor and should land after it merges, so we relocate a finished subsystem rather than a moving target.
Boundary
pre_process expands each
particle_cloudinto a global IB-patch list and writesrestart_data/particle_cloud_ibs.dat; the solver reads that list at fresh start, then reduces to each rank's neighborhood (unchanged).ib_patch_parametersalready lives insrc/common/m_derived_types.fpp, so both stages share the type.What moves
src/simulation/m_particle_cloud.fppsrc/simulation/m_start_up.fpps_generate_particle_clouds(...)->s_read_particle_cloud_ibs(...)src/pre_process/m_particle_cloud.fppsrc/pre_process/m_start_up.fppsrc/pre_process/m_global_parameters.fpp,m_mpi_proxy.fppparticle_cloudnamelist awareness + broadcast (pre_process has none today)src/common/Net:
simulation/loses ~525 lines (556-line subsystem -> ~30-line reader).Design decisions
f_neighborhood_ranks_own_location/s_reduce_ib_patch_arraydepend on the simulation's domain decomposition, so pre_process writes the global list and the solver reduces on read. The lattice packer's inline neighborhood filter disappears (it packs global now) — a simplification.num_ib_patches_max_namelist(54000) -> tiny file, so the old "avoid broadcasting N patches" concern goes away.t_step_start > 0) the solver already skips generation and readsib_state; keep that branch. The.datread fires only on fresh start (mirrors the currentif/elseinm_start_up.fpp:903-913).ib_stategeometric verifier intest.pyis unchanged (reads solver output either way).case_validator.pyis untouched — it validates the compact user spec. This is the payoff of the input-only/runtime split from Move input-only checks to case_validator, delete dead params_tests, enforce the split #1717.Test plan
ppn=2case (classic "did the broadcast/decomposition survive" catch, per.claude/rules/common-pitfalls.md).ib_stateverifier.Effort
Medium — ~80% mechanical relocation, ~20% genuinely new (file writer/reader + pre_process param plumbing). The real risk surface is parallel correctness, not the algorithms.