Let a reservoir coupling master spawn its slaves through a wrapper - #7414
Conversation
|
jenkins build this please |
|
jenkins build this serial please |
There was a problem hiding this comment.
🟢 Approval recommended
No unresolved issues were identified, and the existing spawn path remains unchanged when the option is unset.
Pull request overview
Adds an optional hidden wrapper executable for reservoir-coupling slave processes, enabling debugger integration while preserving existing behavior.
Changes:
- Adds and hides
--rescoup-spawn-wrapper. - Prepends the simulator path to wrapper arguments and logs wrapper usage.
- Updates documentation for slave output redirection.
File summaries
| File | Summary |
|---|---|
opm/simulators/timestepping/EclTimeSteppingParams.hpp |
Declares the wrapper parameter. |
opm/simulators/timestepping/EclTimeSteppingParams.cpp |
Registers and hides the parameter. |
opm/simulators/flow/rescoup/ReservoirCouplingSpawnSlaves.cpp |
Implements wrapper-based spawning and updates output documentation. |
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
blattms
left a comment
There was a problem hiding this comment.
Looks good. Maybe be more detailed about how such a wrapper would look in help and comment
| // simulator, for example under a debugger for one selected rank, and must keep | ||
| // running as the spawned process until the simulator exits, because the MPI | ||
| // runtime watches the process it started. The slaves receive the parameter too, | ||
| // as they receive every other one, and ignore it: they never spawn. |
There was a problem hiding this comment.
I think it would help if you would put some more information about how the wrapper should look here. E.g. from the PR descritpion
There was a problem hiding this comment.
Good idea, in b3aaadb the comment now shows the exact command line the wrapper receives, where it finds the slave name and the rank, and a minimal wrapper script that starts one rank of one slave under gdb and every other rank as usual.
Debugging a reservoir coupling slave under gdb is awkward because the master starts its slaves itself, at runtime, with MPI_Comm_spawn(): a debugger cannot be attached to a slave before it exists, and nothing outside the simulator can wrap a spawned process. Add a hidden parameter, --rescoup-spawn-wrapper=<path>. When set, the master passes that executable to MPI_Comm_spawn() instead of its own binary, with the binary path prepended to the slave's arguments. The wrapper then decides how to start the simulator: plainly, or under a debugger for the one rank the user wants to look at, while every other process runs untouched. It must run as the spawned process until the simulator exits, since the MPI runtime watches the process it started. The slaves receive the parameter like every other one and ignore it. With the parameter unset nothing changes.
The comment above MPI_Comm_spawn() said that the handling of the slaves' output was undecided and might need a custom solution. It has been decided for a long time: every slave rank redirects its own stdout and stderr to <slave name>.<rank>.log in Main::initMPI(), from the --slave-log-file argument the master puts first on its command line. Say that instead, and point at the function that does it.
The review asked for the parameter's help text and the code comment to say how a wrapper for --rescoup-spawn-wrapper should look, not only that one can be given. The help text now states the command line the wrapper receives, that it must start the simulator with exactly those arguments and keep running until the simulator exits, and where the slave name and the rank come from. The code comment says the same and adds a minimal wrapper that starts one rank of one slave under gdb and every other rank as usual.
442fe9c to
b3aaadb
Compare
|
@blattms Thanks for the review. I rebased onto master and added b3aaadb which describes the wrapper in both places you mentioned: the help text (shown by |
|
jenkins build this please |
A reservoir coupling master starts its slaves itself, at runtime, with
MPI_Comm_spawn(). That makes a slave hard to debug: a debugger cannot be attached to a process before it exists, and nothing outside the simulator (not the MPI launcher, not gdb'sfollow-fork-mode) can wrap a process that the MPI runtime spawns on the master's behalf. This PR adds one small hook so that the master can spawn a wrapper executable instead offlow, leaving it to the wrapper to start the simulator plainly or under a debugger.Add the hidden parameter
--rescoup-spawn-wrapper(commit 1)--rescoup-spawn-wrapper=<path>, declared inEclTimeSteppingParams.hppnext to the other reservoir coupling developer flag (--rescoup-sync-at-report-steps), registered and hidden inEclTimeSteppingParams.cpplike--slave.ReservoirCouplingSpawnSlaves::spawnSlaveProcesses_()passes that executable toMPI_Comm_spawn()instead of the simulator binary, with the binary path prepended to the slave's usual arguments:<wrapper> <flow> --slave-log-file=<name> <deck> ... --slave=true. The master logsSpawning slave <name> through wrapper <path>at info level, once per slave.exec flow "$@"for every rank except the one to debug, andgdb --args flow "$@"for that one — the slave name is in--slave-log-file=…, the rank inOMPI_COMM_WORLD_RANK), and it must keep running as the spawned process until the simulator exits, because the MPI runtime watches the process it started.Replace a stale TODO about slave output (commit 2)
MPI_Comm_spawn()said the handling of the slaves' output was undecided and might need a custom solution. It has been decided for a long time: every slave rank redirects its own stdout and stderr to<slave name>.<rank>.loginMain::initMPI(), from the--slave-log-fileargument the master puts first on its command line. The comment now says so and points atMain::maybeRedirectReservoirCouplingSlaveOutput_().Testing
Tested on a two-slave case (4 + 1 ranks) with a Debug build. With the parameter, rank 2 of the first slave came up under gdb in its own terminal and stopped at a breakpoint in
BlackoilWellModel::beginTimeStep()while the coupled run continued and completed; the master log shows both slaves spawned through the wrapper. Without the parameter the same case runs exactly as before. There is nothing to unit-test here without MPI spawning, so the change is covered by the coupled runs only.