From cf3a8b50469fbadabea3440d064d7bea4f121706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Wed, 14 Jan 2026 08:29:48 +0100 Subject: [PATCH] Add do_mpi_gather parameter to pushLogger() for NLDD support --- .../wells/BlackoilWellModelNldd_impl.hpp | 4 +- opm/simulators/wells/GroupStateHelper.hpp | 41 ++++++++++++++----- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/opm/simulators/wells/BlackoilWellModelNldd_impl.hpp b/opm/simulators/wells/BlackoilWellModelNldd_impl.hpp index 919524b006..4135342745 100644 --- a/opm/simulators/wells/BlackoilWellModelNldd_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModelNldd_impl.hpp @@ -105,8 +105,8 @@ recoverWellSolutionAndUpdateWellState(const BVector& x, // Note: no point in trying to do a parallel gathering // try/catch here, as this function is not called in // parallel but for each individual domain of each rank. - // The ScopedLoggerGuard handles logging on destruction. - auto loggerGuard = wellModel_.groupStateHelper().pushLogger(); + // Use do_mpi_gather=false to avoid MPI collective operations. + auto loggerGuard = wellModel_.groupStateHelper().pushLogger(/*do_mpi_gather=*/false); for (const auto& well : wellModel_.localNonshutWells()) { if (this->well_domain().at(well->name()) == domainIdx) { const auto& cells = well->cells(); diff --git a/opm/simulators/wells/GroupStateHelper.hpp b/opm/simulators/wells/GroupStateHelper.hpp index 0858f1c209..b5010af663 100644 --- a/opm/simulators/wells/GroupStateHelper.hpp +++ b/opm/simulators/wells/GroupStateHelper.hpp @@ -126,9 +126,15 @@ public: class ScopedLoggerGuard { public: - explicit ScopedLoggerGuard(const GroupStateHelper& helper) + /// @brief Constructor for scoped logger guard + /// @param helper Reference to the GroupStateHelper + /// @param do_mpi_gather If true, gather messages across MPI ranks on destruction. + /// Set to false when called from contexts where MPI synchronization is not + /// possible (e.g., NLDD domain-local operations). + explicit ScopedLoggerGuard(const GroupStateHelper& helper, bool do_mpi_gather = true) : helper_(&helper) , previous_(helper.deferred_logger_) + , do_mpi_gather_(do_mpi_gather) { helper_->deferred_logger_ = &logger_; } @@ -136,12 +142,19 @@ public: ~ScopedLoggerGuard() { if (helper_) { - // 1. Gather messages across MPI ranks - DeferredLogger global = gatherDeferredLogger(logger_, helper_->comm()); + if (do_mpi_gather_) { + // 1. Gather messages across MPI ranks + DeferredLogger global = gatherDeferredLogger(logger_, helper_->comm()); - // 2. Log on rank 0 (if terminal_output enabled) - if (helper_->terminalOutput()) { - global.logMessages(); + // 2. Log on rank 0 (if terminal_output enabled) + if (helper_->terminalOutput()) { + global.logMessages(); + } + } else { + // Just log locally without MPI gather + if (helper_->terminalOutput()) { + logger_.logMessages(); + } } // 3. Restore previous logger @@ -160,6 +173,7 @@ public: : helper_(other.helper_) , logger_(std::move(other.logger_)) , previous_(other.previous_) + , do_mpi_gather_(other.do_mpi_gather_) { // Update the helper's pointer to our moved logger if (helper_) { @@ -174,6 +188,7 @@ public: const GroupStateHelper* helper_{nullptr}; DeferredLogger logger_; // Owned logger DeferredLogger* previous_{nullptr}; // For restore + bool do_mpi_gather_{true}; // Whether to gather messages across MPI ranks }; GroupStateHelper(WellState& well_state, @@ -290,18 +305,22 @@ public: return GroupStateGuard(*this, group_state); } - /// @brief Push a new logger onto the stack with auto-gather on destruction + /// @brief Push a new logger onto the stack with auto-cleanup on destruction /// /// @details Creates a new DeferredLogger and pushes it onto the logger stack. /// When the returned guard goes out of scope: - /// 1. Messages are gathered across MPI ranks via gatherDeferredLogger() - /// 2. Messages are logged on rank 0 (if terminal_output is enabled) + /// 1. If do_mpi_gather is true: Messages are gathered across MPI ranks via gatherDeferredLogger() + /// and logged on rank 0 (if terminal_output is enabled) + /// 2. If do_mpi_gather is false: Messages are logged locally without MPI gather /// 3. The previous logger is restored /// + /// @param do_mpi_gather If true (default), gather messages across MPI ranks on destruction. + /// Set to false when called from contexts where MPI synchronization is not + /// possible (e.g., NLDD domain-local operations called at different times on different ranks). /// @return RAII guard that owns the logger and handles cleanup - ScopedLoggerGuard pushLogger() const + ScopedLoggerGuard pushLogger(bool do_mpi_gather = true) const { - return ScopedLoggerGuard(*this); + return ScopedLoggerGuard(*this, do_mpi_gather); } WellStateGuard pushWellState(WellState& well_state)