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/action2.py
tests/msim/action3.py
tests/msim/action_count.py
tests/VFP_CASE.DATA)
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(), {});
}
for (const auto& pyaction : actions.pending_python())
this->schedule.runPyAction(report_step, *pyaction, this->state, this->st);
for (const auto& pyaction : actions.pending_python(this->action_state))
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[](std::size_t index) 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;
std::vector<ActionX>::const_iterator begin() const;

View File

@ -36,6 +36,7 @@ class SummaryState;
class PyRunModule;
namespace Action {
class State;
class PyAction {
public:
@ -53,7 +54,7 @@ public:
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::string& name() const;
bool active() const;
bool ready(const State& state) const;
bool operator==(const PyAction& other) const;
template<class Serializer>

View File

@ -35,6 +35,8 @@ namespace Action {
class ActionX;
class Actions;
class PyAction;
class State {
struct RunState {
@ -79,9 +81,11 @@ struct RunState {
public:
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::time_t run_time(const ActionX& 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);
template<class Serializer>
@ -89,6 +93,7 @@ public:
{
serializer.map(this->run_state);
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);
std::map<action_id, RunState> run_state;
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
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;

View File

@ -109,10 +109,10 @@ bool Actions::ready(const State& state, std::time_t sim_time) const {
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;
for (const auto& pyaction : this->pyactions) {
if (pyaction.active())
if (pyaction.ready(state))
pyaction_vector.push_back( &pyaction );
}
return pyaction_vector;

View File

@ -31,6 +31,7 @@ namespace py = pybind11;
#include <opm/input/eclipse/Python/Python.hpp>
#include <opm/input/eclipse/Schedule/Action/PyAction.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/Schedule/Action/State.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
@ -64,8 +65,18 @@ PyAction PyAction::serializeObject()
return result;
}
bool PyAction::active() const {
return this->m_active;
bool PyAction::ready(const State& state) const {
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;
}
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 {
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));
}
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 {
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
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 {
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;
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.m_python_result.insert( std::make_pair("PYACTION", false) );
return st;
}

View File

@ -58,6 +58,7 @@
#include <opm/input/eclipse/EclipseState/TracerConfig.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/ActionResult.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;
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);
};
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;
}

View File

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

View File

@ -447,7 +447,7 @@ PYACTION
PYACTION
ACTION3 FIRST_TRUE /
'action2.py' /
'action_count.py' /
DATES
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_EQUAL( sim.st.get("run_count"), 13);
}
BOOST_AUTO_TEST_CASE(PYTHON_ACTIONX) {

View File

@ -276,7 +276,7 @@ BOOST_AUTO_TEST_CASE(TestActions) {
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);
}
@ -977,6 +977,7 @@ BOOST_AUTO_TEST_CASE(ActionState) {
auto res = st.result("NAME-HIDDEN");
BOOST_CHECK(!res.has_value());
}
BOOST_AUTO_TEST_CASE(MANUAL4_QUOTE) {

View File

@ -32,6 +32,7 @@
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/common/utility/TimeService.hpp>
#include <opm/input/eclipse/Schedule/Action/State.hpp>
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);
Action::PyAction py_action(python, "WCLOSE", Action::PyAction::RunCount::unlimited, deck.makeDeckPath(fname));
auto actionx_callback = [] (const std::string&, const std::vector<std::string>&) { ;};
Action::State action_state;
st.update_well_var("PROD1", "WWCT", 0);
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( well2.getStatus() == Well::Status::OPEN );
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);
}