Repository navigation
Euler-Lagrange capabilities - Phase 1 - #1926
thierrydaoud wants to merge 68 commits into
Conversation
sbryngelson
left a comment
There was a problem hiding this comment.
Thanks for this. It is a large, carefully tested port, and the verification table and the list of bugs found along the way are genuinely useful. I reviewed CI, the toolchain, the tests and the examples myself, and read the two new Fortran modules and the shared-code changes in full. Several issues need fixing before this can merge; inline comments carry the specifics. Nothing below was built or run locally except where stated; the CI observations come from the job logs of the current run.
Commit attribution. Please rewrite the branch so the commits carry no Co-Authored-By: Claude ... <noreply@anthropic.com> trailers (12 of the 13 non-merge commits have one), and drop the "Generated with Claude Code" footer from the description. We do not list AI tools as MFC contributors. The AI disclosure paragraph is fine to keep. While you are at it, the commits are authored as t.daoud <TDAOUD@MAE-RYX75DJ.local>, a machine-local address, so GitHub does not credit them to your account; squashing into commits authored with your GitHub-linked email fixes both.
CI: 38 failing jobs, three causes
- Both new examples fail case validation on every lane:
fluid_pp(1)%eos = 'ideal_gas' has no stiffness; do not set fluid_pp(1)%pi_inf. That rule was already in the master merged into this branch, so the examples were not rerun after the merge. They also have no goldens (tests 242025F6, 45F842CA) and are not in the Example skip list, so they will fail again once they validate. 2D -> Lagrange Particles -> Two-way Coupling -> qs_fluct_force(EDD3540A) fails on NVHPC CPU (e.g. 23.11, 26.3) with a relative error of 8e3 inbeta.6. See the inline notes on the seed overflow and the per-stage OU update; either would make this non-portable.- The NVHPC GPU builds crash the compiler on
m_particles_EL.fpp(fort2 TERMINATED by signal 11, on 26.3 OpenACC and 25.5 OpenMP on Phoenix). The GPU verification in the description was done with CCE only; NVHPC is our main GPU compiler, so this has to build there.
Blockers (physics and correctness)
- Momentum is not conserved in two-way coupling: the source weights are normalized by sum(G V alpha_f) and the RHS divides by alpha_f again, so the fluid receives about F/<alpha_f> for a particle force F (inline).
- Energy is not conserved:
SEis always zero and the fluid energy source is S.u_f, so the drag dissipation beta|u_f - u_p|^2 disappears instead of heating the gas (inline). - A quiescent cloud (uniform p, u = 0, fixed particles, nonuniform alpha_f) is not an equilibrium: the -(p/alpha_f) grad(alpha_f) momentum term is unbalanced (inline).
fd_orderunset or 1 is accepted for particle runs and gives a negative or empty gradient stencil (inline).- One-way coupling with added mass runs with du/dt = 0 because
rhs_oldis filled only in the two-way path. This is listed under known limitations; the validator should reject the combination rather than run it (inline). - The new t = 0 save also runs on restarts and rewrites the checkpoint being restarted from (inline).
- Restarts assign particles by rank index (
part_id = proc_particle_counts(proc_rank + 1)), so a restart on a different rank count reads out of range or loses particles, andparticle_seed/fqs_fluctare not saved. Either support it or abort when the rank count changes.
Should fix
- The drag correlation details flagged inline (Ma and Ahmadi coefficient, Osnes Mach floor, undocumented Loth modifications, stochastic force advanced every RK stage).
- Non-finite forces are silently zeroed (kernels ~648-667), and interpolated rho/p/alpha_f are unbounded near shocks. Please count or abort instead of hiding them.
- Cost and memory: six full
sys_sizearrays of reconstructed states are allocated and copied every stage (inline); particle arrays andp_send_idsare sized bynparticles_glbon every rank; the particle state moves between host and device several times per stage. None of this is a problem at 20 particles; all of it is at 1e6. lag_voidfrac_wrtis not restricted to particle runs; on a bubble run it writes the bubblervelcolumn labelled as void fraction.cyl_coordhas a partial code path but no 1/r terms or velocity conversion; please prohibit it in Phase 1.- Two small behavior changes to non-particle runs should be mentioned in the description: the
nt <= 1timing fix, and the bubble Silo multimesh name length (16 -> 11).
Cleanliness
- Registered but never read:
particle_pp%cp_particle,ksp_col,nu_col,E_col,cor_col(the tests even setcp_particle). Please register them with the collision PR instead. - Dead code: the periodic and reflective wrap paths (forbidden by the validator),
particle_in_domain,s_check_celloutside,s_get_cell, the never-writtenkahan_comparrays, and the bubble leftovers that mean nothing for rigid particles (particle_draddt, the radius RK update,Rmax/Rmin_stats_part, the stats file). Commented-out code should be deleted (e.g. kernels 826-877, m_particles_EL 1899-1901). - About 330 lines of
m_particles_ELare verbatim copies ofm_bubbles_EL, ands_mpi_sendrecv_solid_particlesduplicates the bubble routine's structure. A shared Lagrangian layer would remove most of this. - Gidaspow, Parmar et al. and Osnes et al. are not in
docs/references.bib; please cite them with the equation numbers each kernel implements.
What would make this excellent
- A conservation regression (one particle in a closed box, total momentum and energy to round-off) and a quiescent-cloud regression. Either would have caught blockers 1-3.
- Validation of the curtain example against Wagner et al. (2012): Mach 1.66, a 2 mm curtain at 21 percent volume fraction of 115 um glass, 82.7 kPa ambient is that experiment. Plotting the upstream and downstream curtain fronts against the measured ones would be a strong result, and the example should cite it.
- Unit tests of the drag correlations against published tables, including the limits (Stokes, M -> 0, phi -> 0) and continuity at every switch. For the record, Parmar 2010 checks out, including continuity at M = 0.6, 1.0 and 1.75, and the Gidaspow dilute limit matches Schiller-Naumann.
- A restart regression: 2N steps must equal N steps, a restart, then N more.
- Longer term, one Lagrangian container shared with the bubble model (handover, compaction, restart I/O, RK through
rk_coef) that stays on the device.
wilfonba
left a comment
There was a problem hiding this comment.
This PR needs to introduce a shared m_euler_lagrange.fpp to house shared utilities for bubbles and particles. As it stands, there are hundreds of lines of almost verbatim duplicate code, and several hundred more of very similar code.
|
Hello all, I appreciate you taking the time for this pull request, and provide detailed comments and feedback. I am going over them and will answer/resolve them one by one. This might take a day or more, but hopefully will be resolved soon. After resolving most/all comments, I will push the code changes/commits and squash them here, instead of bugging users with notifications about the various commits. I'll be doing them instead on my local branch Thanks for your patience and assistance. |
7b829d6 to
34a89cb
Compare
Lagrangian solid-particle model (particles_lagrange) next to the Lagrangian bubbles. Rigid spheres are tracked individually; the gas acts on them through quasi-steady drag (Gidaspow, Parmar et al., Osnes et al.), optional pressure-gradient and added-mass forces and an optional stochastic drag fluctuation. With two-way coupling the particles act back on the gas through momentum and energy sources projected with a Gaussian kernel. No collisions in phase 1. - Solver: src/simulation/m_particles_EL.fpp and m_particles_EL_kernels.fpp. - Shared module m_euler_lagrange.fpp for bubbles and particles: cell location, domain and cell-volume helpers, input parsing and start-up, void-fraction and evolution files, MPI-IO restart read/write. Bubble results are unchanged. - Coupling: force deposit normalized by sum(G V); the gas receives the particle momentum change m du/dt (including the added-mass reaction) and the work -F.u_p; pressure terms -(alpha_p/alpha_f) grad p and -(alpha_p/alpha_f) div(p u) as in the bubble solver, so a quiescent cloud stays at rest. - Inputs registered in toolchain/mfc/params/definitions.py, validated by check_particles_lagrange (unsupported combinations rejected), documented in docs/documentation/case.md with references in docs/references.bib. - Examples 2D_particle_curtain and 2D_particle_hemisphere with goldens; 12 particle regression tests, including a quiescent cloud and a closed box. - Shared-code changes: halo width and buffers, beta exchange, solid-particle MPI handover, particle gradients inside the RHS direction loop, parallel-I/O volume-fraction slot, post-process naming and rank-count handling.
34a89cb to
2c148cd
Compare
SummaryThis PR adds a Lagrangian solid-particle model ( The solver is ported from our research fork (MFC-EL) and adapted to current Existing cases are unaffected: every Lagrangian-bubble golden passes unchanged, bit for bit. Usage and ApplicationsThe model simulates particle-laden compressible flows in the Euler–Lagrange framework, such as shock–particle-curtain interaction and blast waves through particle clouds. Two examples are included and run in CI. What's included
Changes to shared code
Physics changes made during reviewThese change two-way results; all particle goldens were regenerated.
In a closed box with slip walls (moving particle cloud, gas at rest), total momentum drift went from +14.9% to +1.9% after 100 steps, and from +25.7% to +2.4% after 1000 steps in a longer box, where it no longer grows once the exchange is over. The energy lost early in the exchange went from 78% of the particles' kinetic energy to a few percent. The remainder comes from discretizing the α_f terms and shrinks as the α_f field gets smoother. Verification
Runs before the review's physics fixes, kept for reference: GPU vs CPU on LLNL Tuolumne (Cray CCE, OpenACC) agreed to 5e-9 in the flow on the hemisphere case; NVHPC 25.9 OpenACC on a HiPerGator GPU node passed the 8 particle tests then present; 1 vs 2 ranks agreed to 1e-16 (2D) and bit for bit (3D) with particles crossing ranks; and a debug build with bounds checking ran clean on multiple ranks. Bugs found and fixedDuring the port: gradient weights computed on the GPU with run-time-sized private arrays (particles about 5% slow on GPU); an unset z-velocity entering the added-mass force in 2D; zero-slip drag NaNs that zeroed the whole particle force; a halo-buffer overflow in the multi-rank beta exchange; unclosed GPU loops and missing During review: the four coupling errors above; interpolation overshoot (the barycentric interpolant is now clamped to its stencil range, which removed negative interpolated pressures at blast fronts); a non-finite force now aborts with a report of the particle, its Reynolds and Mach numbers and the force term responsible, instead of being zeroed silently; seed overflow (undefined behaviour in Fortran); restart files that lost the drag-fluctuation state and the seed; and post-process missing particles when run on a different rank count. Known limitationsThe validator rejects the unsupported combinations.
Acknowledgement
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #1926 +/- ##
==========================================
+ Coverage 61.90% 63.69% +1.79%
==========================================
Files 86 89 +3
Lines 22885 23736 +851
Branches 3369 3433 +64
==========================================
+ Hits 14166 15118 +952
+ Misses 6220 6004 -216
- Partials 2499 2614 +115 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
sbryngelson
left a comment
There was a problem hiding this comment.
A few issues from a read of the particle module and kernels. These come from reading the code; I have not built or run them.
…ange, seed round trip - The particle evolution file resumes its t_save cadence at the restart time instead of writing every step until it caught up from t = 0. - Added mass with several fluids: dρ/dt and the density gradient sum all partial densities, matching the mixture density (single fluid unchanged). - f_xorshift_rand divides by 2^31, so the state -2^31 maps to 1 and the uniform stays in [0, 1] (Box-Muller no longer sees log(u) > 0). - The restart file stores the seed as two 16-bit halves (columns 20-21), exact in single and double precision; older files still read correctly. - The validator requires fluid_pp(l)%cv > 0 when particle_params%suth(l) is set (Sutherland's law needs the temperature). The qs_fluct_force golden is regenerated (RNG rescaling, changes up to 2e-9).
Several test particles started exactly on cell faces of the test grids, and x = 0.5 is also the 2-rank boundary, so round-off decided their cell and rank. That made the goldens compiler- and precision-dependent: qs_fluct_force failed on every NVHPC CPU lane, and the 2-rank test failed in single precision, where particles at x = 0.5 stayed on the other rank. The particles now start off every face and boundary; the moving ones still cross ranks, now at definite times (in 3D they also move across the z = 0.5 split). Particle goldens are regenerated; a debug build and a single-precision build now match them.
# Conflicts: # src/common/m_helper.fpp
On NVHPC CPU builds the two-pass inliner (-Mextract/-Minline) inlined the RNG into s_compute_qs_fluctuations and returned the last of the five draws for all five, so Z1 = Z2 and the drag and lift fluctuations were neither independent nor Gaussian. This is why qs_fluct_force failed on every NVHPC CPU lane. gfortran, Intel, Cray and NVHPC GPU builds drew correct numbers, so the goldens are unchanged. - s_xorshift_rand is excluded from -Minline, as other miscompiled routines are. - The RNG is a subroutine, s_xorshift_rand(var, seed), like s_prng, instead of a function that updates its argument. Verified on NVHPC 25.9 CPU (HiPerGator): the five draws match gfortran and EDD3540A passes; all 10 particle tests pass with gfortran. Committed with --no-verify: precheck steps run by hand on macOS (all pass except the SLURM-monitor/preflight pytest files that fail on clean master here).
|
NVHPC CPU lanes: root cause and fix (f82c096) The NVHPC CPU lanes (23.11–26.3) had failed on every commit of this PR, always on Cause. The two-pass cross-file inliner ( So Fix.
Verified. gfortran: all 10 Lagrange Particles tests pass. |
The xorshift seeds (particle ID + 16 xorshift rounds) gave correlated streams for neighboring IDs: the first draws of consecutive particles had a correlation of +0.35, because xorshift is linear and warm-up rounds keep it so. The fluctuation model now uses the existing s_prng_splitmix32, whose hashed Weyl sequence gives unrelated streams per seed (correlation -0.009), and s_xorshift_rand is removed. - Each particle's seed is its global ID; particle_seed is the 64-bit splitmix state, sent between ranks as MPI_INTEGER8 (buffer size updated) and still saved as two 16-bit halves in restart columns 20-21. - s_prng_splitmix32 and f_mulmod32 are GPU seq routines; the -Minline exception moves to s_prng_splitmix32. - EDD3540A golden regenerated (new random stream); the other particle goldens are unchanged. Checked with gfortran: 10/10 particle tests; 20 steps vs 10 + restart + 10 give bit-identical checkpoints (flow and particles, with seeds); 1 vs 2 ranks with particles crossing the boundary: identical seeds, states within 1e-16. Committed with --no-verify: precheck steps run by hand on macOS.
…ameter array
CCE OpenACC rejects the duidxj_id(3, 3) parameter array in s_get_particle_force
("Global in accelerator routine without declare"); NVHPC places it on the
device implicitly. du_i/dx_j is field dufxdx_id + 3*(i - 1) + j - 1, so both
uses compute it like the neighboring dPx_id + dir - 1. Results unchanged.
Committed with --no-verify: format and lint run by hand on macOS; 10/10
particle tests pass with gfortran against the existing goldens.
With CCE OpenMP offload, simulation aborted at startup with "Present Table Collision": m_bubbles_EL and m_particles_EL each declare a device array named kahan_comp, and the runtime registered the second at the first one's address. Every test on that build failed, not only particle tests. A module-specific name, as m_muscl uses for is1_muscl, avoids the clash. Results unchanged. Committed with --no-verify: format and lint run by hand on macOS; 10/10 particle tests pass with gfortran against the existing goldens.
p_owner_rank was written at init, every stage and on receive but never read; lag_recv_ranks only fed it. With it gone the second n_recv count in s_begin_lag_entity_exchange duplicated the first. (The deallocation part of the review comment was already fixed by the allocation change.) Lagrangian tests pass (47/47 incl. five 2-rank cases, macOS CPU+MPI). Committed with --no-verify: precheck hangs on macOS.
- coupling = two_way requires pressure_gradient_force = T: the gas momentum source carries the full -grad p, so without the force the gas also takes the particles' share. - Reject body forces (particles get no gravity; added mass would read g as a fluid acceleration), bubbles_euler, ib, a state-dependent EOS, and the bubble-only lag_*_wrt outputs, which wrote other quantities for particles. - Require particle_pp%rho0ref_particle > 0. - Default particle input_path is input/particles.dat, the name every example, test and doc uses. Validator tests pass; all examples validate; Lagrangian tests pass (47/47, macOS CPU+MPI). Committed with --no-verify: precheck hangs on macOS.
Unknown names stopped at the JSON schema check with a bare "data must not contain" message, so the "did you mean" suggestions never showed. Check them first. A renamed parameter names its replacement; the first entry is lag_params%solver_approach -> lag_params%coupling. Toolchain tests pass. Committed with --no-verify: precheck hangs on macOS.
The particle kernel width is fixed in grid cells by kernel_alpha; the parameter came over with the bubble set (where it scales the kernel standard deviation) and was never read. lag_params%epsilonb is kept. Toolchain tests pass; all examples validate; Lagrangian tests pass (47/47, macOS CPU+MPI). Committed with --no-verify: precheck hangs on macOS.
The particle kernel spans mapCells_loc cells past the host cell and its normalization includes ghost cells, but only periodic and reflective boundaries folded the ghost share back, so at slip and no-slip walls it was dropped. Measured on a 2D slip-wall box (kernel_alpha = ln 10), the interior kept 0.91667 of alpha_p, alpha_p*u_p, Sm and SE for a particle centered in a wall-adjacent cell and 0.72028 for one at y = r_p. Wall boundaries now use the reflective fold (ghost share added to the mirror cell, then ghost = mirror) when particles_lagrange is on; the interior then keeps 1.00000 in both cases and in a corner. Bubble runs are unchanged. No golden changes: no test particle's kernel reaches a wall ghost cell. Lagrangian tests pass (47/47, macOS CPU+MPI). Committed with --no-verify: precheck hangs on macOS.
The beta halo was sized for num_beta_vars_max = 11 fields on every Euler-Lagrange run, including bubble runs that exchange 2-3 and pre/post processing, which exchange none. s_initialize_mpi_common_module now takes num_beta_vars_in (simulation: the larger of bub_q_idx%num and part_q_idx%num for the active models; pre/post: 0). Abort if it exceeds num_beta_vars_max, or if s_populate_beta_buffers is asked for more fields than the halo holds; 3D two-way particles already use all 11. Lagrangian tests pass (47/47 incl. 2-rank, macOS CPU+MPI). Committed with --no-verify: precheck hangs on macOS.
Committed with --no-verify: precheck hangs on macOS.
2D -> Lagrange Particles -> Two-way Coupling -> Closed Box -> Wall Layer: six particles in two clusters one radius off the y = 0 and x = 0 slip walls, moving into them, with valmaxvoid = 0.1 (the clusters' cells reach alpha_p = 0.124). It covers the wall ghost-cell fold, the radius wall clamp and sources in cells at the void limit, which no test reached. Reverting each fix alone fails it: 4288288 (rel 9.1e-6), 8e69a80 (rel 4.3e-3), de6b02d (rel 3.4e-4). Golden generated on macOS, GNU 16.2, MPI. Lagrangian tests pass (48/48). Committed with --no-verify: precheck hangs on macOS.
|
I am adding particle restart tests for the lagrange particles. Although the lagrange bubbles do have restart tests, they are not triggered with a full test. The full test requires I will make that fix for I think the bubbles also previously failed at runtime if the serial output was enabled. that is also now checked in the validator. |
The Lagrangian restart file is its own MPI-IO file, but it was only written and read with parallel_io because mpiiofs and mpi_info_int were set up only then (the gate came with the bubble writer). Set mpiiofs and mpi_info_int = MPI_INFO_NULL in both modes (hints still only with parallel_io), drop the gate in the shared reader and the particle writer, and create restart_data when missing. The file name is the same in both modes. Validator: particle restarts no longer need parallel_io = T; bubble restarts now do (their writer keeps the gate), which was only caught at run time before. A 2D two-way qs_fluct_force run restarted at step 25 with serial I/O matches the straight run bitwise (cons, beta, particle evolution). Toolchain tests pass; all examples validate; 113 tests pass (all Lagrangian and restart cases plus 60 random, macOS CPU+MPI). Committed with --no-verify: precheck hangs on macOS.
The --test-all template set fd_order = 1 for 3D vorticity output, which the particle validator rejects, so post_process never ran for 3D particle tests (test.py does not check the post_process return code). Particles use fd_order 2 or 4, which also suits the vorticity output. 3C74B315 now runs pre_process, simulation and post_process and writes its Silo files. Committed with --no-verify: precheck hangs on macOS.
2D -> Lagrange Particles -> Two-way Coupling -> qs_fluct_force -> Restart runs to step 25 with serial I/O, restarts from the particle restart file and must match the straight run, so the saved seed and fluctuation state are covered. Added mass is off: its fluid acceleration is not saved. Skipped without MPI, since the restart file is MPI-IO. Replacing the seed restore with a constant fails it (cons.4 mismatch at step 50). Golden generated on macOS, GNU 16.2, MPI. Committed with --no-verify: precheck hangs on macOS.
Both examples have particles on the y = 0 slip wall (the hemisphere's symmetry plane, the curtain's particles at y = r_p), whose wall ghost-cell deposit 8e69a80 now folds back instead of dropping. Step-0 alpha_f moves by up to 1.3e-3 (hemisphere) and 2.2e-3 (curtain) relative near the wall; after one step cons.4 differs by up to 2.3e-2. Reverting 8e69a80 alone restores the old goldens. Regenerated on macOS, GNU 16.2, MPI. Lagrangian tests incl. examples pass (52/52). Committed with --no-verify: precheck hangs on macOS.
fd_order = 4 sets the widest particle halo; interpolation_order = 6 is the widest stencil the validator allows with the gradient forces at fd_order = 2. Both run in 2D and 3D on 2 ranks, so the stencils cross a rank boundary. Each matches its 1-rank run to round-off (max difference / field max 5e-13 to 2e-12, as for the existing fd_order = 2 2-rank cases), and each differs from the order-2 runs (fields 2e-3 to 4e-2). Goldens generated on macOS, GNU 16.2, MPI. Lagrangian tests incl. examples pass (56/56). Committed with --no-verify: precheck hangs on macOS.
- Remove dead code: use m_ibm and the adap_dt_tol default in m_particles_EL, the particle cyl_coord branch (the validator forbids it), the unused locals of s_write_lag_bubble_evol, FMT in s_open_lag_bubble_stats (the header is written with *), and the empty pre_process comment. - s_prng_splitmix32 and f_mulmod32 use seed_kind instead of local int64_kind parameters. - Size the beta halo and the Lagrangian MPI buffers through integer(kind=8) variables instead of _8 literals and int(..., 8). - s_read_lag_input opens with newunit and status='old', and aborts if the file cannot be opened (it read zero entities before). - s_sync_bubbles_for_save keeps the bubble host updates and radius check in m_bubbles_EL, as s_sync_particles_for_save does for particles; the second host update after the data write was redundant. - Renumber the force-kernel step comments. Lagrangian, restart and 2-rank tests pass (66/66, macOS CPU+MPI). Committed with --no-verify: precheck hangs on macOS.
From the review: QS_Parmar/QS_Osnes/QS_Gidaspow -> f_qs_parmar/ f_qs_osnes/f_qs_gidaspow; the viscosity argument of s_get_particle_force Re -> mu_fluid (and myRe -> my_mu in its caller); s_add_particles -> s_add_particle; s_compute_particle_EL_dynamics -> s_compute_particles_EL_dynamics, to match s_compute_particles_EL_source. Also s_finalize_particle_lagrangian_solver -> s_finalize_particles_EL_module, to mirror s_initialize_particles_EL_module. examples/3D_moving_lag_particles is a bubble case: renamed to examples/3D_moving_lag_bubbles (it is on the example-test skip list, so no golden moves). Lagrangian tests incl. examples pass (56/56); the example validates. Committed with --no-verify: precheck hangs on macOS.
…_bcast The four per-struct emitters were variants of _emit_struct_bcast with the MPI types hard-coded (bub_pp all REAL, chem_params LOG/INT only, rburn and lag_params split by type). _emit_struct_bcast takes each member's type from the registry, and _mpi_type_for raises on an unmapped type, so a new member can no longer be silently skipped. Generated broadcasts: pre and post identical; sim has the same 313 calls, reordered alphabetically within lag_params and chem_params. Toolchain tests pass; 85 Lagrangian, chemistry, reactive-burn and multi-rank tests pass. Committed with --no-verify: precheck hangs on macOS.
b55ff34 only carried the example directory move; this adds the rest of that change: QS_Parmar/QS_Osnes/QS_Gidaspow -> f_qs_parmar/f_qs_osnes/ f_qs_gidaspow, the Re argument of s_get_particle_force -> mu_fluid (myRe -> my_mu in its caller), s_add_particles -> s_add_particle, s_compute_particle_EL_dynamics -> s_compute_particles_EL_dynamics, s_finalize_particle_lagrangian_solver -> s_finalize_particles_EL_module, and the example-test skip list entry for examples/3D_moving_lag_bubbles. Lagrangian tests incl. examples pass (56/56). Committed with --no-verify: precheck hangs on macOS.
The three branches differed only in the moving index and the weights array, and narrowed their loops by a hard-coded 2. One #:for body now generates the three GPU loops, with the narrowing taken from npts (fd_order/2) along dir only. With fd_order = 2 the loops now also fill the outermost ghost layer, where weights exist; nothing reads it: 2D and 3D two-way runs are bitwise identical to before. Lagrangian tests incl. examples pass (56/56). Committed with --no-verify: precheck hangs on macOS.
s_beta_periodic, s_beta_extrapolation and s_beta_reflective declared q_beta and kahan_comp as dimension(num_dims + 1), the bubble field count. Since 8e69a80 slip walls call s_beta_reflective for particles, whose fields go up to index 8 in 2D and 11 in 3D: the reldebug CI job aborted on both particle examples ("Index '6' of dimension 1 of array 'q_beta' above upper bound of 3"). Release builds touched the right memory, so results were correct, but the access was out of the declared bounds. Two-way bubbles with cluster_type >= 4 (up to 5 fields) exceeded it too. dimension(*) keeps the explicit-shape calling convention (no descriptor in the device routines) and fits any field count. Reproduced locally in reldebug, then fixed: particle examples and all 56 Lagrangian tests pass in reldebug and release. Committed with --no-verify: precheck hangs on macOS.
Lines of Code
|
| end subroutine s_set_lag_comm_coords | ||
|
|
||
| !> Create case_dir/D on rank 0 if it does not exist. | ||
| impure subroutine s_create_D_dir() |
There was a problem hiding this comment.
i'm pretty sure this function already exists somewhere else, also i dont see how it's EL specific
|
|
||
| !> Read the initial Lagrangian entities from path, one per line with ncols values, and return those in this rank's physical | ||
| !! domain: rows(1:n_loc, :) and their global IDs ids(1:n_loc), which are the line numbers among the n_read lines read. | ||
| impure subroutine s_read_lag_input(path, ncols, n_glb, rows, ids, n_loc, n_read) |
There was a problem hiding this comment.
should this not go in m_data_input / m_startup?
| end subroutine s_read_lag_input | ||
|
|
||
| !> Sum the local count n_loc over the ranks into n_glb and abort if no entity lies in the domain. | ||
| impure subroutine s_count_lag_entities_glb(n_loc, n_glb, path) |
There was a problem hiding this comment.
usually global routines go elsewhere (like module startup calls)
|
|
||
| !> Open this rank's evolution file D/lag_<lag_kind>_evol_<rank>.dat: on a fresh start replace it and write the column headers | ||
| !! (first header width 14, the others width), on a restart append to it. | ||
| impure subroutine s_open_lag_evol(lag_kind, width, headers) |
There was a problem hiding this comment.
you seem to be mixing i/o with start up with computation
| call MPI_TYPE_FREE(view, ierr) | ||
| #endif | ||
|
|
||
| end subroutine s_write_lag_restart |
There was a problem hiding this comment.
isn't this what we have m-data-output for?
|
|
||
| end subroutine s_write_lag_restart | ||
|
|
||
| !> Read restart_data/lag_<lag_kind>_<save_count>.dat written by s_write_lag_restart: set mytime and dt from the header and |
There was a problem hiding this comment.
more stuff cluttering the code/algorithm?
|
I went over the most recent comments. I just want to be clear first that these exist for the lagrangian bubbles, and were copied over to be shared with the lagrangian solid particles in this shared module. The one i commented on "can be fixed" is something we can instead fix by creating The rest of the comments can be worked on in a follow-up PR, in which the input and restart headers are moved to start-up, since that will need restructuring the bubble init. |
Summary
This PR adds a Lagrangian solid-particle model (
particles_lagrange) next to the existing Lagrangian bubble model. Rigid spherical particles are tracked individually. The gas acts on them through quasi-steady drag (Gidaspow, Parmar et al., or Osnes et al.), optional pressure-gradient and added-mass forces, and an optional stochastic drag-fluctuation force. With two-way coupling, the particles act back on the gas through momentum and energy source terms, projected onto the grid with the Gaussian kernel of Maeda & Colonius (2018).The solver is ported from our research fork (MFC-EL) and adapted to current
master(generated parameters,m_eos, the refactored Riemann-state reconstruction). Phase 1 has no particle–particle collisions.Existing cases are unaffected, with one exception:
periodic_bcis now broadcast to every rank (onmasteronly rank 0 set it), which changes multi-rank periodicbubbles_lagrangeresults. Every other non-particle code path, including the Lagrangian bubble model, behaves as before; no existing golden file is modified.Usage and Applications
This code capability is essential to simulate complex flows in the Euler-Lagrange framework. Problems such as a hemi-sphere blast, a planar particle curtain shock, and many other were tested and can be simulated using the developed capability.
What's included
src/simulation/m_particles_EL.fpp(driver, dynamics, sources, RK update, boundaries, MPI handover, I/O) andsrc/simulation/m_particles_EL_kernels.fpp(GPUseqkernels: Gaussian projection, interpolation, force model, drag correlations). The module isprivateby default and exports only what the time stepper, RHS and start-up need. Per-particle arrays are sized by the rank's own particle count plus 50 % spare and grow during the MPI handover, instead ofnparticles_glbon every rank.particles_lagrange,particle_pp%…(physical properties),particle_params%…(solver settings) andlag_voidfrac_wrt. They are registered intoolchain/mfc/params/definitions.py, so the namelists, broadcasts and declarations are generated.couplingtakes"one_way"/"two_way"for both bubbles and particles; the oldlag_params%solver_approachis reported as renamed, naming its replacement.check_particles_lagrangeincase_validator.py, with 37 unit tests. It rejects the combinations the particle path does not support (see Known limitations), requirespressure_gradient_forcewith two-way coupling, and requiresrho0ref_particle > 0.docs/documentation/case.md.examples/2D_particle_hemisphere(Mach 10 blast through a half-ring of particles) andexamples/2D_particle_curtain(planar shock through a dense particle curtain between slip walls). Both generate their particles incase.py.alter_particlesincases.py, plus the two examples). 2D and 3D: one-way, tracer, two-way, no added mass, drag fluctuations, and on 2 ranks one-way, two-way, drag fluctuations,fd_order = 4andinterpolation_order = 6. 2D only: Gidaspow, Parmar, a quiescent cloud (gas must stay at rest), a closed slip-wall box, particles pressed against two walls with cells at the void limit, and a restart roundtrip. The goldens include every particle's position, velocity and force at every step.Changes to shared code
These are the parts reviewers of other models may want to look at. Bubble call sites keep their previous behavior unless stated.
m_helper_basic.fpp):s_configure_coordinate_boundstakesparticles_lagrangealongsidebubbles_lagrange, andfd_numberis set for particles.s_populate_beta_bufferstakes the list of fields to exchange (bubbles pass their 2–3, particles up to 11). The halo buffer (m_mpi_common.fpp) is sized from the largest field count of the active model (num_beta_vars_in; 0 in pre/post-process) instead of a fixed 11, with an abort if a call exceeds it.m_mpi_proxy.fpp): solid-particle send/receive. The allocation and count exchange shared by bubbles and particles ares_allocate_lag_entity_commands_exchange_lag_entity_counts; the buffers grow on demand.periodic_bcbroadcast (m_mpi_proxy.fpp): now broadcast to all ranks. This is a fix (onmasteronly rank 0 set it), but it changes multi-rank periodicbubbles_lagrangeresults.mpiiofsandmpi_info_int(MPI_INFO_NULLwithout parallel I/O) are now set in both I/O modes, and the shared reader no longer requiresparallel_io, so particle restarts work with serial I/O. The bubble writer keeps itsparallel_iogate, and the validator now rejects bubble restarts withoutparallel_io(previously a run-time abort).m_rhs.fpp): each direction's reconstructed states are copied at the end ofs_reconstruct_riemann_statesfor the particle gradient fields.MPI_IO_DATAsizedsys_size + 1), and a t = 0 save for particle runs so post-process sees the real volume fraction.lag_namereplaces 14 hard-codedlag_bubblesnames in the text and Silo writers. The Silo calls now pass the real name length (one existing call passed 16 for an 11-character name).--test-alltemplate keepsfd_orderfor 3D particle cases (it setfd_order = 1, which the validator rejects, so post-process never ran for them), and restart tests of Lagrangian particles are skipped without MPI.s_prng_splitmix32inm_helper.fpp(the drag-fluctuation generator: exact integer arithmetic, so the stream is compiler-independent),_emit_struct_bcastin the Fortran generator, a packer rule solag_particleoutput files skip their header line likelag_bubblefiles, and the bubble restart IDs are written at working precision (they collided above 2^24).Verification
After the October 24 review (macOS, GNU 16.2, CPU + MPI):
fd_order = 4andinterpolation_order = 6, 1 vs 2 ranks (2D and 3D)fd_order = 22-rank testsy = r_pand in a corner: 1.00000 (was 0.917 and 0.720 at the wall)./mfc.sh validateon every example./mfc.sh precheckon HiPerGator (Linux)09b8873b)Before the October 24 review (not rerun since; GPU runs on LLNL Tuolumne, Cray CCE, OpenACC):
Bugs found and fixed during verification
Running the GPU and debug builds against the CPU reference turned up several bugs, all fixed in this branch:
qs_fluct_force(found with bounds checking).GPU_PARALLEL_LOOPs, and two scalars missing fromprivatelists.MPI_IO_DATAtoo small for the volume-fraction slot).From the October 24 review: cells clamped to the void limit received no sources; kernel deposits in wall ghost cells were dropped; particles were clamped
verysmall(not their radius) inside a wall, which in single precision left them out of bounds; restart IDs went through default-kindreal(); a nonzerowin a 2D input heated the gas every step.Known limitations
These are features the phase-1 solver does not support yet. None of them is a regression, and the validator rejects the unsupported combinations.
particle_pp%*_colconstants are accepted but unused until then.cyl_coord,bubbles_lagrange,bubbles_euler,ib,igr, body forces (the particles get no gravity), state-dependent equations of state (the particle forces use the stiffened-gasgamma/pi_inf), one-way coupling with added mass, and the bubble-onlylag_*_wrtoutputs.added_mass_force = 1the first stage after a restart uses zero fluid acceleration, which is not saved.lag_db_wrtstill needsparallel_io = T.mapCells = 3, although the particle kernel only reaches one cell.Notes for reviewers
masteratdeb640eb(master961cde5e).--no-verifyon macOS, where four upstream toolchain tests (test_monitor_*,test_bench_preflight,test_submit_requeue) hang or fail without these changes too. Every precheck step was run manually for each commit, and the full precheck passes on Linux.--test-all, the post-process run's return code is not checked (toolchain/mfc/test/test.py), so a failing post-process run goes unnoticed; this is how the 3D particlefd_orderproblem above stayed hidden. Checking it may expose failures elsewhere in the suite, so it is left for a separate PR.