Record and act on return value from PYACTION

This commit is contained in:
Joakim Hove 2022-01-28 09:28:07 +01:00
parent c37c2b2d49
commit 343f14dacf
16 changed files with 104 additions and 22 deletions

View File

@ -551,6 +551,7 @@ if(ENABLE_ECL_OUTPUT)
tests/msim/action1.py tests/msim/action1.py
tests/msim/action2.py tests/msim/action2.py
tests/msim/action3.py tests/msim/action3.py
tests/msim/action_count.py
tests/VFP_CASE.DATA) tests/VFP_CASE.DATA)
endif() endif()

View File

@ -89,8 +89,8 @@ void msim::post_step(data::Solution& /* sol */, data::Wells& /* well_data */, da
this->schedule.applyAction(report_step, *action, result.wells(), {}); this->schedule.applyAction(report_step, *action, result.wells(), {});
} }
for (const auto& pyaction : actions.pending_python()) for (const auto& pyaction : actions.pending_python(this->action_state))
this->schedule.runPyAction(report_step, *pyaction, this->state, this->st); this->schedule.runPyAction(report_step, *pyaction, this->action_state, this->state, this->st);
} }

View File

@ -55,7 +55,7 @@ public:
const ActionX& operator[](const std::string& name) const; const ActionX& operator[](const std::string& name) const;
const ActionX& operator[](std::size_t index) const; const ActionX& operator[](std::size_t index) const;
std::vector<const ActionX *> pending(const State& state, std::time_t sim_time) const; std::vector<const ActionX *> pending(const State& state, std::time_t sim_time) const;
std::vector<const PyAction *> pending_python() const; std::vector<const PyAction *> pending_python(const State& state) const;
bool has(const std::string& name) const; bool has(const std::string& name) const;
std::vector<ActionX>::const_iterator begin() const; std::vector<ActionX>::const_iterator begin() const;

View File

@ -36,6 +36,7 @@ class SummaryState;
class PyRunModule; class PyRunModule;
namespace Action { namespace Action {
class State;
class PyAction { class PyAction {
public: public:
@ -53,7 +54,7 @@ public:
bool run(EclipseState& ecl_state, Schedule& schedule, std::size_t report_step, SummaryState& st, bool run(EclipseState& ecl_state, Schedule& schedule, std::size_t report_step, SummaryState& st,
const std::function<void(const std::string&, const std::vector<std::string>&)>& actionx_callback) const; const std::function<void(const std::string&, const std::vector<std::string>&)>& actionx_callback) const;
const std::string& name() const; const std::string& name() const;
bool active() const; bool ready(const State& state) const;
bool operator==(const PyAction& other) const; bool operator==(const PyAction& other) const;
template<class Serializer> template<class Serializer>

View File

@ -35,6 +35,8 @@ namespace Action {
class ActionX; class ActionX;
class Actions; class Actions;
class PyAction;
class State { class State {
struct RunState { struct RunState {
@ -79,9 +81,11 @@ struct RunState {
public: public:
void add_run(const ActionX& action, std::time_t sim_time, Result result); void add_run(const ActionX& action, std::time_t sim_time, Result result);
void add_run(const PyAction& action, bool result);
std::size_t run_count(const ActionX& action) const; std::size_t run_count(const ActionX& action) const;
std::time_t run_time(const ActionX& action) const; std::time_t run_time(const ActionX& action) const;
std::optional<Result> result(const std::string& action) const; std::optional<Result> result(const std::string& action) const;
std::optional<bool> python_result(const std::string& action) const;
void load_rst(const Actions& action_config, const RestartIO::RstState& rst_state); void load_rst(const Actions& action_config, const RestartIO::RstState& rst_state);
template<class Serializer> template<class Serializer>
@ -89,6 +93,7 @@ public:
{ {
serializer.map(this->run_state); serializer.map(this->run_state);
serializer.map(this->last_result); serializer.map(this->last_result);
serializer.template map<std::map<std::string,bool>, false>(this->m_python_result);
} }
@ -100,6 +105,7 @@ private:
static action_id make_id(const ActionX& action); static action_id make_id(const ActionX& action);
std::map<action_id, RunState> run_state; std::map<action_id, RunState> run_state;
std::map<std::string, Result> last_result; std::map<std::string, Result> last_result;
std::map<std::string, bool> m_python_result;
}; };

View File

@ -295,7 +295,7 @@ namespace Opm
Python code. he return value from runPyAction() comes from such a Python code. he return value from runPyAction() comes from such a
internal ACTIONX. internal ACTIONX.
*/ */
SimulatorUpdate runPyAction(std::size_t reportStep, const Action::PyAction& pyaction, EclipseState& ecl_state, SummaryState& summary_state); SimulatorUpdate runPyAction(std::size_t reportStep, const Action::PyAction& pyaction, Action::State& action_state, EclipseState& ecl_state, SummaryState& summary_state);
const GasLiftOpt& glo(std::size_t report_step) const; const GasLiftOpt& glo(std::size_t report_step) const;

View File

@ -109,10 +109,10 @@ bool Actions::ready(const State& state, std::time_t sim_time) const {
return false; return false;
} }
std::vector<const PyAction *> Actions::pending_python() const { std::vector<const PyAction *> Actions::pending_python(const State& state) const {
std::vector<const PyAction *> pyaction_vector; std::vector<const PyAction *> pyaction_vector;
for (const auto& pyaction : this->pyactions) { for (const auto& pyaction : this->pyactions) {
if (pyaction.active()) if (pyaction.ready(state))
pyaction_vector.push_back( &pyaction ); pyaction_vector.push_back( &pyaction );
} }
return pyaction_vector; return pyaction_vector;

View File

@ -31,6 +31,7 @@ namespace py = pybind11;
#include <opm/input/eclipse/Python/Python.hpp> #include <opm/input/eclipse/Python/Python.hpp>
#include <opm/input/eclipse/Schedule/Action/PyAction.hpp> #include <opm/input/eclipse/Schedule/Action/PyAction.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp> #include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/Schedule/Action/State.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp> #include <opm/input/eclipse/Schedule/Schedule.hpp>
@ -64,8 +65,18 @@ PyAction PyAction::serializeObject()
return result; return result;
} }
bool PyAction::active() const { bool PyAction::ready(const State& state) const {
return this->m_active; if (this->m_run_count == RunCount::unlimited)
return true;
auto last_result = state.python_result(this->m_name);
if (!last_result.has_value())
return true;
if (this->m_run_count == RunCount::first_true && last_result.value() == false)
return true;
return false;
} }
@ -73,13 +84,6 @@ const std::string& PyAction::name() const {
return this->m_name; return this->m_name;
} }
void PyAction::update(bool result) const {
if (this->m_run_count == RunCount::single)
this->m_active = false;
if (this->m_run_count == RunCount::first_true && result)
this->m_active = false;
}
bool PyAction::operator==(const PyAction& other) const { bool PyAction::operator==(const PyAction& other) const {
return this->m_name == other.m_name && return this->m_name == other.m_name &&

View File

@ -59,6 +59,10 @@ void State::add_run(const ActionX& action, std::time_t run_time, Result result)
this->last_result.insert_or_assign(action.name(), std::move(result)); this->last_result.insert_or_assign(action.name(), std::move(result));
} }
void State::add_run(const PyAction& action, bool result) {
this->m_python_result.insert_or_assign( action.name(), result );
}
std::optional<Result> State::result(const std::string& action) const { std::optional<Result> State::result(const std::string& action) const {
auto iter = this->last_result.find(action); auto iter = this->last_result.find(action);
@ -69,6 +73,16 @@ std::optional<Result> State::result(const std::string& action) const {
} }
std::optional<bool> State::python_result(const std::string& action) const {
auto iter = this->m_python_result.find(action);
if (iter == this->m_python_result.end())
return std::nullopt;
return iter->second;
}
/* /*
When restoring from restart file we initialize the number of times it has run When restoring from restart file we initialize the number of times it has run
and the last run time. From the evaluation only the 'true' evaluation is and the last run time. From the evaluation only the 'true' evaluation is
@ -86,7 +100,8 @@ void State::load_rst(const Actions& action_config, const RestartIO::RstState& rs
bool State::operator==(const State& other) const { bool State::operator==(const State& other) const {
return this->run_state == other.run_state && return this->run_state == other.run_state &&
this->last_result == other.last_result; this->last_result == other.last_result &&
this->m_python_result == other.m_python_result;
} }
@ -94,6 +109,7 @@ State State::serializeObject() {
State st; State st;
st.run_state.insert(std::make_pair( std::make_pair("ACTION", 100), RunState::serializeObject())); st.run_state.insert(std::make_pair( std::make_pair("ACTION", 100), RunState::serializeObject()));
st.last_result.insert( std::make_pair("ACTION", Result::serializeObject())); st.last_result.insert( std::make_pair("ACTION", Result::serializeObject()));
st.m_python_result.insert( std::make_pair("PYACTION", false) );
return st; return st;
} }

View File

@ -58,6 +58,7 @@
#include <opm/input/eclipse/EclipseState/TracerConfig.hpp> #include <opm/input/eclipse/EclipseState/TracerConfig.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp> #include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/Schedule/Action/State.hpp>
#include <opm/input/eclipse/Schedule/Action/ActionX.hpp> #include <opm/input/eclipse/Schedule/Action/ActionX.hpp>
#include <opm/input/eclipse/Schedule/Action/ActionResult.hpp> #include <opm/input/eclipse/Schedule/Action/ActionResult.hpp>
#include <opm/input/eclipse/Schedule/MSW/SICD.hpp> #include <opm/input/eclipse/Schedule/MSW/SICD.hpp>
@ -1428,14 +1429,15 @@ File {} line {}.)", pattern, location.keyword, location.filename, location.linen
*/ */
SimulatorUpdate Schedule::runPyAction(std::size_t reportStep, const Action::PyAction& pyaction, EclipseState& ecl_state, SummaryState& summary_state) { SimulatorUpdate Schedule::runPyAction(std::size_t reportStep, const Action::PyAction& pyaction, Action::State& action_state, EclipseState& ecl_state, SummaryState& summary_state) {
SimulatorUpdate sim_update; SimulatorUpdate sim_update;
auto apply_action_callback = [&sim_update, &reportStep, this](const std::string& action_name, const std::vector<std::string>& matching_wells) { auto apply_action_callback = [&sim_update, &reportStep, this](const std::string& action_name, const std::vector<std::string>& matching_wells) {
sim_update = this->applyAction(reportStep, action_name, matching_wells); sim_update = this->applyAction(reportStep, action_name, matching_wells);
}; };
pyaction.run(ecl_state, *this, reportStep, summary_state, apply_action_callback); auto result = pyaction.run(ecl_state, *this, reportStep, summary_state, apply_action_callback);
action_state.add_run(pyaction, result);
return sim_update; return sim_update;
} }

View File

@ -34,6 +34,18 @@ PYACTION
WCLOSE UNLIMITED / WCLOSE UNLIMITED /
'wclose.py' / 'wclose.py' /
PYACTION
UNLIMITED UNLIMITED /
'wclose.py' /
PYACTION
SINGLE SINGLE /
'wclose.py' /
PYACTION
FIRST_TRUE FIRST_TRUE /
'wclose.py' /
WELSPECS WELSPECS
'PROD1' 'G1' 10 10 8400 'OIL' / 'PROD1' 'G1' 10 10 8400 'OIL' /
'PROD2' 'G1' 5 5 8400 'OIL' / 'PROD2' 'G1' 5 5 8400 'OIL' /

View File

@ -447,7 +447,7 @@ PYACTION
PYACTION PYACTION
ACTION3 FIRST_TRUE / ACTION3 FIRST_TRUE /
'action2.py' / 'action_count.py' /
DATES DATES
1 'JAN' 2015 / 1 'JAN' 2015 /

View File

@ -0,0 +1,11 @@
import math
def run(ecl_state, schedule, report_step, summary_state, actionx_callback):
if not "run_count" in summary_state:
summary_state["run_count"] = 0
summary_state["run_count"] += 1
if summary_state.elapsed() > (365 + 15)*24*3600:
return True
return False

View File

@ -524,6 +524,7 @@ BOOST_AUTO_TEST_CASE(PYTHON_WELL_CLOSE_EXAMPLE) {
BOOST_CHECK(w4_11.getStatus() == Well::Status::SHUT ); BOOST_CHECK(w4_11.getStatus() == Well::Status::SHUT );
} }
} }
BOOST_CHECK_EQUAL( sim.st.get("run_count"), 13);
} }
BOOST_AUTO_TEST_CASE(PYTHON_ACTIONX) { BOOST_AUTO_TEST_CASE(PYTHON_ACTIONX) {

View File

@ -276,7 +276,7 @@ BOOST_AUTO_TEST_CASE(TestActions) {
BOOST_CHECK(!action2.eval(context)); BOOST_CHECK(!action2.eval(context));
const auto& python_actions = config.pending_python(); const auto& python_actions = config.pending_python(action_state);
BOOST_CHECK_EQUAL(python_actions.size(), 2U); BOOST_CHECK_EQUAL(python_actions.size(), 2U);
} }
@ -977,6 +977,7 @@ BOOST_AUTO_TEST_CASE(ActionState) {
auto res = st.result("NAME-HIDDEN"); auto res = st.result("NAME-HIDDEN");
BOOST_CHECK(!res.has_value()); BOOST_CHECK(!res.has_value());
} }
BOOST_AUTO_TEST_CASE(MANUAL4_QUOTE) { BOOST_AUTO_TEST_CASE(MANUAL4_QUOTE) {

View File

@ -32,6 +32,7 @@
#include <opm/input/eclipse/Schedule/SummaryState.hpp> #include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp> #include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/common/utility/TimeService.hpp> #include <opm/common/utility/TimeService.hpp>
#include <opm/input/eclipse/Schedule/Action/State.hpp>
using namespace Opm; using namespace Opm;
@ -124,7 +125,7 @@ BOOST_AUTO_TEST_CASE(PYACTION) {
const std::string& fname = pyaction_kw.getRecord(1).getItem(0).get<std::string>(0); const std::string& fname = pyaction_kw.getRecord(1).getItem(0).get<std::string>(0);
Action::PyAction py_action(python, "WCLOSE", Action::PyAction::RunCount::unlimited, deck.makeDeckPath(fname)); Action::PyAction py_action(python, "WCLOSE", Action::PyAction::RunCount::unlimited, deck.makeDeckPath(fname));
auto actionx_callback = [] (const std::string&, const std::vector<std::string>&) { ;}; auto actionx_callback = [] (const std::string&, const std::vector<std::string>&) { ;};
Action::State action_state;
st.update_well_var("PROD1", "WWCT", 0); st.update_well_var("PROD1", "WWCT", 0);
py_action.run(ecl_state, schedule, 10, st, actionx_callback); py_action.run(ecl_state, schedule, 10, st, actionx_callback);
@ -141,6 +142,32 @@ BOOST_AUTO_TEST_CASE(PYACTION) {
BOOST_CHECK( well1.getStatus() == Well::Status::SHUT ); BOOST_CHECK( well1.getStatus() == Well::Status::SHUT );
BOOST_CHECK( well2.getStatus() == Well::Status::OPEN ); BOOST_CHECK( well2.getStatus() == Well::Status::OPEN );
BOOST_CHECK( st.has("RUN_COUNT") ); BOOST_CHECK( st.has("RUN_COUNT") );
std::map<std::string, Action::PyAction> action_map;
for (const auto * p : schedule[0].actions().pending_python(action_state))
action_map.emplace( p->name(), *p );
const auto& pyaction_unlimited = action_map.at("UNLIMITED");
const auto& pyaction_single = action_map.at("SINGLE");
const auto& pyaction_first_true = action_map.at("FIRST_TRUE");
auto actions = schedule[0].actions();
BOOST_CHECK( actions.pending_python(action_state).size() == 4);
action_state.add_run( py_action, true);
BOOST_CHECK( actions.pending_python(action_state).size() == 4);
action_state.add_run( pyaction_unlimited, true);
BOOST_CHECK( actions.pending_python(action_state).size() == 4);
action_state.add_run( pyaction_single, false);
BOOST_CHECK( actions.pending_python(action_state).size() == 3);
action_state.add_run( pyaction_first_true, false);
BOOST_CHECK( actions.pending_python(action_state).size() == 3);
action_state.add_run( pyaction_first_true, true);
BOOST_CHECK( actions.pending_python(action_state).size() == 2);
} }