diff --git a/opm/simulators/flow/ActionHandler.cpp b/opm/simulators/flow/ActionHandler.cpp index f256daab8..ed97bee32 100644 --- a/opm/simulators/flow/ActionHandler.cpp +++ b/opm/simulators/flow/ActionHandler.cpp @@ -22,6 +22,7 @@ */ #include + #include #include @@ -29,6 +30,7 @@ #include #include + #include #include #include @@ -40,6 +42,8 @@ #include #include + +#include #include #include @@ -128,6 +132,61 @@ namespace { Opm::OpmLog::debug("ACTION_NOT_TRIGGERED", message); } + + template + std::unordered_map + fetchWellPI(const int reportStep, + const Opm::Schedule& schedule, + const WellModel& wellModel, + const Opm::Action::ActionX& action, + const std::vector& matching_wells, + const Opm::Parallel::Communication comm) + { + auto wellpi = std::unordered_map {}; + + const auto wellpi_wells = action.wellpi_wells + (schedule.wellMatcher(reportStep), matching_wells); + + if (wellpi_wells.empty()) { + return wellpi; + } + + const auto num_wells = schedule[reportStep].well_order().size(); + + std::vector wellpi_vector(num_wells); + for (const auto& wname : wellpi_wells) { + if (wellModel.hasWell(wname)) { + const auto& well = schedule.getWell(wname, reportStep); + wellpi_vector[well.seqIndex()] = wellModel.wellPI(wname); + } + } + + if (comm.size() > 1) { + std::vector wellpi_buffer(num_wells * comm.size()); + comm.gather(wellpi_vector.data(), wellpi_buffer.data(), num_wells, 0); + + if (comm.rank() == 0) { + for (int rank = 1; rank < comm.size(); ++rank) { + for (std::size_t well_index = 0; well_index < num_wells; ++well_index) { + const auto global_index = rank*num_wells + well_index; + const auto value = wellpi_buffer[global_index]; + if (value != Scalar{0}) { + wellpi_vector[well_index] = value; + } + } + } + } + + comm.broadcast(wellpi_vector.data(), wellpi_vector.size(), 0); + } + + for (const auto& wname : wellpi_wells) { + const auto& well = schedule.getWell(wname, reportStep); + wellpi.insert_or_assign(wname, wellpi_vector[well.seqIndex()]); + } + + return wellpi; + } } // Anonymous namespace namespace Opm { @@ -180,13 +239,13 @@ applyActions(const int reportStep, logActivePyAction(pyaction->name(), ts); } - this->applySimulatorUpdate(reportStep, sim_update, commit_wellstate, transUp); + this->applySimulatorUpdate(reportStep, sim_update, transUp, commit_wellstate); } auto non_triggered = 0; const auto simTime = asTimeT(now); - for (const auto& action : actions.pending(actionState_, simTime)) { - const auto actionResult = action->eval(context); + for (const auto& action : actions.pending(this->actionState_, simTime)) { + auto actionResult = action->eval(context); if (! actionResult) { ++non_triggered; logInactiveAction(action->name(), ts); @@ -197,13 +256,15 @@ applyActions(const int reportStep, logActiveAction(action->name(), matching_wells, ts); - const auto wellpi = this->fetchWellPI(reportStep, *action, matching_wells); + const auto wellpi = fetchWellPI + (reportStep, this->schedule_, this->wellModel_, + *action, matching_wells, this->comm_); const auto sim_update = this->schedule_ .applyAction(reportStep, *action, matching_wells, wellpi); - this->applySimulatorUpdate(reportStep, sim_update, commit_wellstate, transUp); - actionState_.add_run(*action, simTime, std::move(actionResult)); + this->applySimulatorUpdate(reportStep, sim_update, transUp, commit_wellstate); + this->actionState_.add_run(*action, simTime, std::move(actionResult)); } if (non_triggered > 0) { @@ -223,8 +284,8 @@ template void ActionHandler:: applySimulatorUpdate(const int report_step, const SimulatorUpdate& sim_update, - bool& commit_wellstate, - const TransFunc& updateTrans) + const TransFunc& updateTrans, + bool& commit_wellstate) { OPM_TIMEBLOCK(applySimulatorUpdate); @@ -244,53 +305,6 @@ applySimulatorUpdate(const int report_step, } } -template -std::unordered_map -ActionHandler:: -fetchWellPI(const int reportStep, - const Action::ActionX& action, - const std::vector& matching_wells) const -{ - auto wellpi_wells = action.wellpi_wells - (this->schedule_.wellMatcher(reportStep), matching_wells); - - if (wellpi_wells.empty()) { - return {}; - } - - const auto num_wells = schedule_[reportStep].well_order().size(); - std::vector wellpi_vector(num_wells); - for (const auto& wname : wellpi_wells) { - if (this->wellModel_.hasWell(wname)) { - const auto& well = schedule_.getWell( wname, reportStep ); - wellpi_vector[well.seqIndex()] = this->wellModel_.wellPI(wname); - } - } - - if (comm_.size() > 1) { - std::vector wellpi_buffer(num_wells * comm_.size()); - comm_.gather( wellpi_vector.data(), wellpi_buffer.data(), num_wells, 0 ); - if (comm_.rank() == 0) { - for (int rank = 1; rank < comm_.size(); rank++) { - for (std::size_t well_index=0; well_index < num_wells; well_index++) { - const auto global_index = rank*num_wells + well_index; - const auto value = wellpi_buffer[global_index]; - if (value != 0) - wellpi_vector[well_index] = value; - } - } - } - comm_.broadcast(wellpi_vector.data(), wellpi_vector.size(), 0); - } - - std::unordered_map wellpi; - for (const auto& wname : wellpi_wells) { - const auto& well = schedule_.getWell( wname, reportStep ); - wellpi[wname] = wellpi_vector[ well.seqIndex() ]; - } - return wellpi; -} - template void ActionHandler:: evalUDQAssignments(const unsigned episodeIdx, diff --git a/opm/simulators/flow/ActionHandler.hpp b/opm/simulators/flow/ActionHandler.hpp index 8e1568fa9..f8825c673 100644 --- a/opm/simulators/flow/ActionHandler.hpp +++ b/opm/simulators/flow/ActionHandler.hpp @@ -31,19 +31,20 @@ #include #include +namespace Opm::Action { + class State; +} // namespace Opm::Action + namespace Opm { - -namespace Action { -class ActionX; -class State; -} - template class BlackoilWellModelGeneric; class EclipseState; class Schedule; struct SimulatorUpdate; class SummaryState; class UDQState; +} // namespace Opm + +namespace Opm { //! \brief Class handling Action support in simulator template @@ -53,6 +54,22 @@ public: //! \brief Function handle to update transmissiblities. using TransFunc = std::function; + /// Constructor. + /// + /// \param[in,out] ecl_state Container of static properties such as + /// permeability and transmissibility. + /// + /// \param[in,out] schedule Container of dynamic objects, such as wells. + /// + /// \param[in,out] actionState Dynamic state object for all actions. + /// + /// \param[in,out] summaryState Dynamic state object for all summary + /// vectors. + /// + /// \param[in,out] wellModel Simulation wells on this rank. + /// + /// \param[in] comm MPI communicator object linking all simulation + /// ranks. ActionHandler(EclipseState& ecl_state, Schedule& schedule, Action::State& actionState, @@ -60,36 +77,70 @@ public: BlackoilWellModelGeneric& wellModel, Parallel::Communication comm); + /// Run all pending actions. + /// + /// \param[in] reportStep Zero-based report step index. + /// + /// \param[in] sim_time Elapsed time since simulation start. + /// + /// \param[in] updateTrans Call-back for affecting transmissibility + /// updates. Typically invoked if the action triggers a keyword like + /// MULTZ. void applyActions(int reportStep, double sim_time, const TransFunc& updateTrans); - //! \brief Evaluates UDQ assign statements. + /// \brief Evaluates UDQ assign statements. + /// + /// \param[in] episodeIdx Zero-based report step index. + /// + /// \param[in,out] udq_state Dynamic state of all user-defined + /// quantities. void evalUDQAssignments(const unsigned episodeIdx, UDQState& udq_state); - private: - /* - This function is run after applyAction has been completed in the Schedule - implementation. The sim_update argument should have members & flags for - the simulator properties which need to be updated. This functionality is - probably not complete. - */ +private: + /// Convey dynamic updates triggered by an action block back to the + /// running simulator. + /// + /// This function is run after applyAction has been completed in the + /// Schedule implementation. The sim_update argument should have + /// members & flags for the simulator properties which need to be + /// updated. This functionality is probably not complete. + /// + /// \param[in] report_step Zero-based report step index. + /// + /// \param[in] sim_update Action's resulting simulator update. + /// + /// \param[in] updateTrans Call-back for affecting transmissibility + /// updates. Typically invoked if the action triggers a keyword like + /// MULTZ. + /// + /// \param[out] commit_wellstate Whether or not the action affected any + /// simulation wells which, in turn, may require rebuilding internal + /// data structures in the simulator and therefore would require + /// preserving the dynamic well and group states prior to doing so. void applySimulatorUpdate(int report_step, const SimulatorUpdate& sim_update, - bool& commit_wellstate, - const TransFunc& updateTrans); - - std::unordered_map - fetchWellPI(int reportStep, - const Action::ActionX& action, - const std::vector& matching_wells) const; + const TransFunc& updateTrans, + bool& commit_wellstate); + /// Static properties such as permeability and transmissibility. EclipseState& ecl_state_; + + /// Dynamic objects such as wells. Schedule& schedule_; + + /// Dynamic state for all actions--e.g., their run count. Action::State& actionState_; + + /// Dynamic state for all user-defined quantities. SummaryState& summaryState_; + + /// Simulation wells on this rank. BlackoilWellModelGeneric& wellModel_; + + /// MPI communicator object linking all simulation ranks. Parallel::Communication comm_; };