From 31a001803eba6170a12f14b63f0b1ffcbf6122df Mon Sep 17 00:00:00 2001 From: hnil Date: Fri, 21 Aug 2026 11:46:24 +0200 Subject: [PATCH] Report where time goes inside the well solves Adds an RAII timing scope to WellInterfaceGeneric and collects it in BlackoilWellModel: well solve time split into assembly and linear solve, well potential solves, control/gaslift/group/network/facility time, and well and well-potential iteration counts. Hidden behind --output-extra-convergence-info=performance, off by default. Timings that nothing fills are not printed, so the report only grows where there is something to show. This is the well half of the instrumentation; the preconditioner, tracer, temperature, property-update and output-write timers follow separately. Co-Authored-By: Claude Opus 5 --- .../common/fvbasediscretization.hh | 83 ++++-- opm/simulators/flow/Banners.cpp | 5 +- opm/simulators/flow/Banners.hpp | 3 +- .../flow/ConvergenceOutputConfiguration.cpp | 16 +- .../flow/ConvergenceOutputConfiguration.hpp | 4 + .../flow/FlowGenericProblem_impl.hpp | 22 +- opm/simulators/flow/FlowMain.hpp | 9 +- .../flow/SimulatorFullyImplicit.cpp | 4 +- opm/simulators/flow/Transmissibility.hpp | 20 ++ opm/simulators/flow/Transmissibility_impl.hpp | 47 ++++ .../flow/equil/InitStateEquil_impl.hpp | 5 + .../timestepping/SimulatorReport.cpp | 252 +++++++++++++++++- .../timestepping/SimulatorReport.hpp | 81 +++++- opm/simulators/wells/BlackoilWellModel.hpp | 4 + .../wells/BlackoilWellModelRescoup.hpp | 11 + .../wells/BlackoilWellModelRescoup_impl.hpp | 51 ++-- .../wells/BlackoilWellModel_impl.hpp | 63 +++++ .../wells/MultisegmentWell_impl.hpp | 25 +- opm/simulators/wells/StandardWell_impl.hpp | 13 +- opm/simulators/wells/WellInterfaceGeneric.hpp | 111 ++++++++ .../rescoup/RescoupConstraintsCalculator.cpp | 42 +-- tests/test_convergenceoutputconfiguration.cpp | 38 +++ 22 files changed, 805 insertions(+), 104 deletions(-) diff --git a/opm/models/discretization/common/fvbasediscretization.hh b/opm/models/discretization/common/fvbasediscretization.hh index eae22639c53..53eea027230 100644 --- a/opm/models/discretization/common/fvbasediscretization.hh +++ b/opm/models/discretization/common/fvbasediscretization.hh @@ -67,8 +67,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -726,49 +728,86 @@ public: { invalidateIntensiveQuantitiesCache(timeIdx); + // exceptions must not escape the parallel block below (that calls + // std::terminate()); tuck any exception away and rethrow it after the + // block, so that e.g. a failed flash in the property evaluation leads + // to a time step chop instead of an abort + std::mutex exceptionLock; + std::exception_ptr exceptionPtr = nullptr; + // loop over all elements... ThreadedEntityIterator threadedElemIt(gridView_); #ifdef _OPENMP #pragma omp parallel #endif { - ElementContext elemCtx(simulator_); - ElementIterator elemIt = threadedElemIt.beginParallel(); - for (; !threadedElemIt.isFinished(elemIt); elemIt = threadedElemIt.increment()) { - const Element& elem = *elemIt; - elemCtx.updatePrimaryStencil(elem); - elemCtx.updatePrimaryIntensiveQuantities(timeIdx); + try { + ElementContext elemCtx(simulator_); + for (ElementIterator elemIt = threadedElemIt.beginParallel(); + !threadedElemIt.isFinished(elemIt); + elemIt = threadedElemIt.increment()) + { + const Element& elem = *elemIt; + elemCtx.updatePrimaryStencil(elem); + elemCtx.updatePrimaryIntensiveQuantities(timeIdx); + } + } + catch (...) { + std::lock_guard take(exceptionLock); + exceptionPtr = std::current_exception(); + threadedElemIt.setFinished(); } } + + if (exceptionPtr) { + std::rethrow_exception(exceptionPtr); + } } template void invalidateAndUpdateIntensiveQuantities(unsigned timeIdx, const GridViewType& gridView) const { + // see the overload above for why exceptions are bridged out of the + // parallel block like this + std::mutex exceptionLock; + std::exception_ptr exceptionPtr = nullptr; + // loop over all elements... ThreadedEntityIterator threadedElemIt(gridView); #ifdef _OPENMP #pragma omp parallel #endif { - - ElementContext elemCtx(simulator_); - auto elemIt = threadedElemIt.beginParallel(); - for (; !threadedElemIt.isFinished(elemIt); elemIt = threadedElemIt.increment()) { - if (elemIt->partitionType() != Dune::InteriorEntity) { - continue; - } - const Element& elem = *elemIt; - elemCtx.updatePrimaryStencil(elem); - // Mark cache for this element as invalid. - const std::size_t numPrimaryDof = elemCtx.numPrimaryDof(timeIdx); - for (unsigned dofIdx = 0; dofIdx < numPrimaryDof; ++dofIdx) { - const unsigned globalIndex = elemCtx.globalSpaceIndex(dofIdx, timeIdx); - setIntensiveQuantitiesCacheEntryValidity(globalIndex, timeIdx, false); + try { + ElementContext elemCtx(simulator_); + for (auto elemIt = threadedElemIt.beginParallel(); + !threadedElemIt.isFinished(elemIt); + elemIt = threadedElemIt.increment()) + { + if (elemIt->partitionType() != Dune::InteriorEntity) { + continue; + } + const Element& elem = *elemIt; + elemCtx.updatePrimaryStencil(elem); + // Mark cache for this element as invalid. + const std::size_t numPrimaryDof = elemCtx.numPrimaryDof(timeIdx); + for (unsigned dofIdx = 0; dofIdx < numPrimaryDof; ++dofIdx) { + const unsigned globalIndex = elemCtx.globalSpaceIndex(dofIdx, timeIdx); + setIntensiveQuantitiesCacheEntryValidity(globalIndex, timeIdx, false); + } + // Update for this element. + elemCtx.updatePrimaryIntensiveQuantities(timeIdx); } - // Update for this element. - elemCtx.updatePrimaryIntensiveQuantities(timeIdx); } + catch (...) { + std::lock_guard take(exceptionLock); + exceptionPtr = std::current_exception(); + threadedElemIt.setFinished(); + } + } + + if (exceptionPtr) { + std::rethrow_exception(exceptionPtr); } } diff --git a/opm/simulators/flow/Banners.cpp b/opm/simulators/flow/Banners.cpp index bc1cc80e5e3..d552ad0ae79 100644 --- a/opm/simulators/flow/Banners.cpp +++ b/opm/simulators/flow/Banners.cpp @@ -121,7 +121,8 @@ void printFlowTrailer(int nprocs, const double total_setup_time, const double deck_read_time, const SimulatorReport& report, - const std::string_view extra_summary) + const std::string_view extra_summary, + const bool performance_details) { std::ostringstream ss; ss << "\n\n================ End of simulation ===============\n\n"; @@ -129,7 +130,7 @@ void printFlowTrailer(int nprocs, ss << fmt::format("Threads per MPI process: {:9}\n", nthreads); ss << fmt::format("Setup time: {:9.2f} s\n", total_setup_time); ss << fmt::format(" Deck input: {:9.2f} s\n", deck_read_time); - report.reportFullyImplicit(ss); + report.reportFullyImplicit(ss, performance_details); if (!extra_summary.empty()) { if (extra_summary.front() != '\n') { ss << '\n'; diff --git a/opm/simulators/flow/Banners.hpp b/opm/simulators/flow/Banners.hpp index ef342b6d8fc..bb3fce6e776 100644 --- a/opm/simulators/flow/Banners.hpp +++ b/opm/simulators/flow/Banners.hpp @@ -46,7 +46,8 @@ void printFlowTrailer(int nprocs, const double total_setup_time, const double deck_read_time, const SimulatorReport& report, - std::string_view extra_summary); + std::string_view extra_summary, + const bool performance_details = false); } // namespace Opm diff --git a/opm/simulators/flow/ConvergenceOutputConfiguration.cpp b/opm/simulators/flow/ConvergenceOutputConfiguration.cpp index 74a246ff526..ff0a4e9bf86 100644 --- a/opm/simulators/flow/ConvergenceOutputConfiguration.cpp +++ b/opm/simulators/flow/ConvergenceOutputConfiguration.cpp @@ -57,7 +57,7 @@ namespace { fmt::format("Unsupported convergence output " "option value{}: {}\n" "Supported values are \"none\", " - "\"steps\", and \"iterations\"", + "\"steps\", \"iterations\", and \"performance\"", pl, fmt::join(unsupp.begin(), u, ", ")) }; } @@ -65,7 +65,7 @@ namespace { throw std::invalid_argument { fmt::format("Option {}:\n - Unsupported value{}: {}\n" " - Supported values are \"none\", " - "\"steps\", and \"iterations\"", + "\"steps\", \"iterations\", and \"performance\"", optionName, pl, fmt::join(unsupp.begin(), u, ", ")) }; @@ -79,11 +79,13 @@ namespace { auto opt = std::vector