Merge pull request #1593 from joakim-hove/internalize-pyaction

Internalize pyaction
This commit is contained in:
Joakim Hove
2020-03-20 18:38:29 +01:00
committed by GitHub
20 changed files with 300 additions and 148 deletions

View File

@@ -421,6 +421,10 @@ if(ENABLE_ECL_OUTPUT)
tests/SPE1CASE2.DATA
tests/SPE1CASE2_RESTART.DATA
tests/SPE1CASE2.X0060
tests/PYACTION.DATA
tests/act1.py
tests/EMBEDDED_PYTHON.DATA
tests/wclose.py
)
endif()

View File

@@ -22,6 +22,8 @@
#include <getopt.h>
#include <opm/common/utility/FileSystem.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/I.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/P.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/G.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/Parser/ErrorGuard.hpp>
@@ -151,12 +153,22 @@ int main(int argc, char** argv) {
copy_file(input_arg.parent_path(), restart_file, output_dir);
}
for (std::size_t import_index = 0; import_index < deck.count("IMPORT"); import_index++) {
const auto& import_keyword = deck.getKeyword("IMPORT", import_index);
const auto& fname = import_keyword.getRecord(0).getItem("FILE").get<std::string>(0);
using IMPORT = Opm::ParserKeywords::IMPORT;
for (std::size_t import_index = 0; import_index < deck.count<IMPORT>(); import_index++) {
const auto& import_keyword = deck.getKeyword<IMPORT>(import_index);
const auto& fname = import_keyword.getRecord(0).getItem<IMPORT::FILE>().get<std::string>(0);
copy_file(input_arg.parent_path(), fname, output_dir);
}
using PYACTION = Opm::ParserKeywords::PYACTION;
for (std::size_t pyaction_index = 0; pyaction_index < deck.count<PYACTION>(); pyaction_index++) {
const auto& pyaction_keyword = deck.getKeyword<PYACTION>(pyaction_index);
const auto& fname = pyaction_keyword.getRecord(1).getItem<PYACTION::FILENAME>().get<std::string>(0);
copy_file(input_arg.parent_path(), fname, output_dir);
}
using GDFILE = Opm::ParserKeywords::GDFILE;
if (deck.hasKeyword<GDFILE>()) {
const auto& gdfile_keyword = deck.getKeyword<GDFILE>();

View File

@@ -77,6 +77,10 @@ namespace Opm {
const DeckKeyword& getKeyword( size_t index ) const {
return getKeyword( Keyword::keywordName, index );
}
template< class Keyword >
std::size_t count() const {
return count( Keyword::keywordName );
}
const std::vector< const DeckKeyword* > getKeywordList( const std::string& keyword ) const;
template< class Keyword >

View File

@@ -26,6 +26,7 @@
#include <vector>
#include <opm/parser/eclipse/EclipseState/Schedule/Action/ActionX.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Action/PyAction.hpp>
namespace Opm {
namespace Action {
@@ -38,11 +39,12 @@ namespace Action {
class Actions {
public:
Actions() = default;
Actions(const std::vector<ActionX>& action);
Actions(const std::vector<ActionX>& action, const std::vector<PyAction>& pyactions);
size_t size() const;
int max_input_lines() const;
bool empty() const;
void add(const ActionX& action);
void add(const PyAction& pyaction);
bool ready(std::time_t sim_time) const;
const ActionX& get(const std::string& name) const;
const ActionX& get(std::size_t index) const;
@@ -57,10 +59,12 @@ public:
void serializeOp(Serializer& serializer)
{
serializer.vector(actions);
serializer.vector(pyactions);
}
private:
std::vector<ActionX> actions;
std::vector<PyAction> pyactions;
};
}
}

View File

@@ -25,11 +25,26 @@
#include <string>
namespace Opm {
namespace Action {
class PyAction {
public:
explicit PyAction(const std::string& code_arg);
enum class RunCount {
single,
unlimited,
first_true
};
static RunCount from_string(std::string run_count);
static std::string load(const std::string& input_path, const std::string& fname);
PyAction() = default;
PyAction(const std::string& name, RunCount run_count, const std::string& code);
const std::string& code() const;
const std::string& name() const;
bool operator==(const PyAction& other) const;
PyAction::RunCount run_count() const;
~PyAction();
/*
@@ -43,10 +58,22 @@ public:
between invocations.
*/
void * storage() const;
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(m_name);
serializer(m_run_count);
serializer(input_code);
}
private:
std::string m_name;
RunCount m_run_count;
std::string input_code;
void * m_storage;
void * m_storage = nullptr;
};
}
}

View File

@@ -373,7 +373,7 @@ namespace Opm
void updateUDQActive( std::size_t timeStep, std::shared_ptr<UDQActive> udq );
bool updateWellStatus( const std::string& well, size_t reportStep , Well::Status status, bool update_connections);
void addWellToGroup( const std::string& group_name, const std::string& well_name , size_t timeStep);
void iterateScheduleSection(const ParseContext& parseContext , ErrorGuard& errors, const SCHEDULESection& , const EclipseGrid& grid,
void iterateScheduleSection(const std::string& input_path, const ParseContext& parseContext , ErrorGuard& errors, const SCHEDULESection& , const EclipseGrid& grid,
const FieldPropsManager& fp);
void addACTIONX(const Action::ActionX& action, std::size_t currentStep);
void addGroupToGroup( const std::string& parent_group, const std::string& child_group, size_t timeStep);
@@ -418,6 +418,7 @@ namespace Opm
void handleWEFAC( const DeckKeyword& keyword, size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors);
void handleTUNING( const DeckKeyword& keyword, size_t currentStep);
void handlePYACTION( const std::string& input_path, const DeckKeyword& keyword, size_t currentStep);
void handleNUPCOL( const DeckKeyword& keyword, size_t currentStep);
void handleGRUPTREE( const DeckKeyword& keyword, size_t currentStep, const UnitSystem& unit_system, const ParseContext& parseContext, ErrorGuard& errors);
void handleGRUPNET( const DeckKeyword& keyword, size_t currentStep, const UnitSystem& unit_system);
@@ -437,7 +438,8 @@ namespace Opm
void handleVFPINJ(const DeckKeyword& vfpprodKeyword, const UnitSystem& unit_system, size_t currentStep);
void checkUnhandledKeywords( const SCHEDULESection& ) const;
void checkIfAllConnectionsIsShut(size_t currentStep);
void handleKeyword(size_t currentStep,
void handleKeyword(const std::string& input_path,
size_t currentStep,
const SCHEDULESection& section,
size_t keywordIdx,
const DeckKeyword& keyword,

View File

@@ -59,7 +59,8 @@ public:
Python();
bool exec(const std::string& python_code) const;
bool exec(const std::string& python_code, const Parser& parser, Deck& deck) const;
bool exec(const PyAction& py_action, EclipseState& ecl_state, Schedule& schedule, std::size_t report_step, SummaryState& st) const;
bool exec(const Action::PyAction& py_action, EclipseState& ecl_state, Schedule& schedule, std::size_t report_step, SummaryState& st) const;
static bool enabled();
explicit operator bool() const;
private:
std::shared_ptr<PythonInterp> interp;

View File

@@ -25,8 +25,9 @@ namespace Opm {
namespace Action {
Actions::Actions(const std::vector<ActionX>& action)
: actions(action)
Actions::Actions(const std::vector<ActionX>& action, const std::vector<PyAction>& pyactions)
: actions(action),
pyactions(pyactions)
{}
@@ -48,6 +49,14 @@ void Actions::add(const ActionX& action) {
*iter = action;
}
void Actions::add(const PyAction& pyaction) {
auto iter = std::find_if( this->pyactions.begin(), this->pyactions.end(), [&pyaction](const PyAction& arg) { return arg.name() == pyaction.name(); });
if (iter == this->pyactions.end())
this->pyactions.push_back(pyaction);
else
*iter = pyaction;
}
const ActionX& Actions::get(const std::string& name) const {
const auto iter = std::find_if( this->actions.begin(), this->actions.end(), [&name](const ActionX& action) { return action.name() == name; });
@@ -98,7 +107,8 @@ std::vector<ActionX>::const_iterator Actions::end() const {
bool Actions::operator==(const Actions& data) const {
return actions == data.actions;
return this->actions == data.actions &&
this->pyactions == data.pyactions;
}
}

View File

@@ -16,6 +16,7 @@
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fstream>
#ifdef EMBEDDED_PYTHON
#include <pybind11/embed.h>
@@ -28,12 +29,45 @@ using dict = int;
#endif
#include <opm/parser/eclipse/Utility/String.hpp>
#include <opm/common/utility/FileSystem.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Action/PyAction.hpp>
namespace Opm {
namespace Action {
PyAction::PyAction(const std::string& code_arg) :
input_code(code_arg),
PyAction::RunCount PyAction::from_string(std::string run_count) {
run_count = uppercase(run_count);
if (run_count == "SINGLE")
return RunCount::single;
if (run_count == "UNLIMITED")
return RunCount::unlimited;
if (run_count == "FIRST_TRUE")
return RunCount::first_true;
throw std::invalid_argument("RunCount string: " + run_count + " not recognized ");
}
std::string PyAction::load(const std::string& input_path, const std::string& fname) {
namespace fs = Opm::filesystem;
fs::path code_path = fs::path(input_path) / fs::path(fname);
if (fs::exists(code_path)) {
std::ifstream ifs(code_path);
return std::string{ std::istreambuf_iterator<char>{ifs}, {} };
} else
throw std::invalid_argument("No such file: " + fname);
}
PyAction::PyAction(const std::string& name, RunCount run_count, const std::string& code) :
m_name(name),
m_run_count(run_count),
input_code(code),
m_storage( new py::dict() )
{}
@@ -42,7 +76,13 @@ const std::string& PyAction::code() const {
return this->input_code;
}
const std::string& PyAction::name() const {
return this->m_name;
}
PyAction::RunCount PyAction::run_count() const {
return this->m_run_count;
}
/*
The python variables are reference counted and when the Python dictionary
@@ -63,11 +103,17 @@ PyAction::~PyAction() {
#endif
}
bool PyAction::operator==(const PyAction& other) const {
return this->m_name == other.m_name &&
this->m_run_count == other.m_run_count &&
this->input_code == other.input_code;
}
void * PyAction::storage() const {
return this->m_storage;
}
}
}

View File

@@ -26,6 +26,7 @@
#include <opm/common/OpmLog/LogUtil.hpp>
#include <opm/common/utility/numeric/cmp.hpp>
#include <opm/parser/eclipse/Python/Python.hpp>
#include <opm/parser/eclipse/Utility/String.hpp>
#include <opm/parser/eclipse/Deck/DeckItem.hpp>
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
@@ -37,6 +38,7 @@
#include <opm/parser/eclipse/Parser/ParserKeywords/G.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/L.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/N.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/P.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/V.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/W.hpp>
@@ -162,7 +164,7 @@ std::pair<std::time_t, std::size_t> restart_info(const RestartIO::RstState * rst
}
if (DeckSection::hasSCHEDULE(deck))
iterateScheduleSection( parseContext, errors, SCHEDULESection( deck ), grid, fp);
iterateScheduleSection( deck.getInputPath(), parseContext, errors, SCHEDULESection( deck ), grid, fp);
}
@@ -282,7 +284,8 @@ std::pair<std::time_t, std::size_t> restart_info(const RestartIO::RstState * rst
}
void Schedule::handleKeyword(size_t currentStep,
void Schedule::handleKeyword(const std::string& input_path,
size_t currentStep,
const SCHEDULESection& section,
size_t keywordIdx,
const DeckKeyword& keyword,
@@ -472,6 +475,9 @@ std::pair<std::time_t, std::size_t> restart_info(const RestartIO::RstState * rst
else if (keyword.name() == "NUPCOL")
handleNUPCOL(keyword, currentStep);
else if (keyword.name() == "PYACTION")
handlePYACTION(input_path, keyword, currentStep);
else if (geoModifiers.find( keyword.name() ) != geoModifiers.end()) {
bool supported = geoModifiers.at( keyword.name() );
if (supported) {
@@ -485,7 +491,7 @@ std::pair<std::time_t, std::size_t> restart_info(const RestartIO::RstState * rst
}
void Schedule::iterateScheduleSection(const ParseContext& parseContext , ErrorGuard& errors, const SCHEDULESection& section , const EclipseGrid& grid,
void Schedule::iterateScheduleSection(const std::string& input_path, const ParseContext& parseContext , ErrorGuard& errors, const SCHEDULESection& section , const EclipseGrid& grid,
const FieldPropsManager& fp) {
const auto& unit_system = section.unitSystem();
std::vector<std::pair< const DeckKeyword* , size_t> > rftProperties;
@@ -532,7 +538,7 @@ std::pair<std::time_t, std::size_t> restart_info(const RestartIO::RstState * rst
else {
if (currentStep >= this->m_timeMap.restart_offset())
this->handleKeyword(currentStep, section, keywordIdx, keyword, parseContext, errors, grid, fp, unit_system, rftProperties);
this->handleKeyword(input_path, currentStep, section, keywordIdx, keyword, parseContext, errors, grid, fp, unit_system, rftProperties);
else
OpmLog::info("Skipping keyword: " + keyword.name() + " while loading SCHEDULE section");
}
@@ -630,6 +636,24 @@ std::pair<std::time_t, std::size_t> restart_info(const RestartIO::RstState * rst
}
}
void Schedule::handlePYACTION( const std::string& input_path, const DeckKeyword& keyword, size_t currentStep) {
if (!Python::enabled()) {
const auto& loc = keyword.location();
OpmLog::warning("This version of flow is built without support for Python. Keyword PYACTION in file: " + loc.filename + " line: " + std::to_string(loc.lineno) + " is ignored.");
return;
}
using PY = ParserKeywords::PYACTION;
const auto& name = keyword.getRecord(0).getItem<PY::NAME>().get<std::string>(0);
const auto& run_count = Action::PyAction::from_string( keyword.getRecord(0).getItem<PY::RUN_COUNT>().get<std::string>(0) );
const auto& code = Action::PyAction::load( input_path, keyword.getRecord(1).getItem<PY::FILENAME>().get<std::string>(0) );
Action::PyAction pyaction(name, run_count, code);
auto new_actions = std::make_shared<Action::Actions>( this->actions(currentStep) );
new_actions->add(pyaction);
this->m_actions.update(currentStep, new_actions);
}
void Schedule::handleNUPCOL( const DeckKeyword& keyword, size_t currentStep) {
int nupcol = keyword.getRecord(0).getItem("NUM_ITER").get<int>(0);
if (keyword.getRecord(0).getItem("NUM_ITER").defaultApplied(0)) {

View File

@@ -34,14 +34,20 @@ bool Python::exec(const std::string& python_code) const {
return true;
}
bool Python::enabled() {
#ifdef EMBEDDED_PYTHON
return true;
#else
return false;
#endif
}
bool Python::exec(const std::string& python_code, const Parser& parser, Deck& deck) const {
this->interp->exec(python_code, parser, deck);
return true;
}
bool Python::exec(const PyAction& py_action, EclipseState& ecl_state, Schedule& schedule, std::size_t report_step, SummaryState& st) const {
bool Python::exec(const Action::PyAction& py_action, EclipseState& ecl_state, Schedule& schedule, std::size_t report_step, SummaryState& st) const {
this->interp->exec(py_action, ecl_state, schedule, report_step, st);
return true;
}

View File

@@ -46,7 +46,7 @@ bool PythonInterp::exec(const std::string& python_code, const Parser& parser, De
}
bool PythonInterp::exec(const PyAction& py_action, EclipseState& ecl_state, Schedule& schedule, std::size_t report_step, SummaryState& st) {
bool PythonInterp::exec(const Action::PyAction& py_action, EclipseState& ecl_state, Schedule& schedule, std::size_t report_step, SummaryState& st) {
auto context = py::module::import("context");
context.attr("schedule") = &schedule;

View File

@@ -50,7 +50,7 @@ class __attribute__ ((visibility("hidden"))) PythonInterp {
public:
bool exec(const std::string& python_code);
bool exec(const std::string& python_code, const Parser& parser, Deck& deck);
bool exec(const PyAction& py_action, EclipseState& ecl_state, Schedule& schedule, std::size_t report_step, SummaryState& st);
bool exec(const Action::PyAction& py_action, EclipseState& ecl_state, Schedule& schedule, std::size_t report_step, SummaryState& st);
explicit operator bool() const { return true; }
private:
py::scoped_interpreter guard = {};
@@ -69,7 +69,7 @@ public:
return this->fail();
}
bool exec(const PyAction&, EclipseState&, Schedule&, std::size_t, SummaryState& ) {
bool exec(const Action::PyAction&, EclipseState&, Schedule&, std::size_t, SummaryState& ) {
return this->fail();
}

View File

@@ -1 +1,4 @@
{"name" : "PYACTION", "sections" : ["SCHEDULE"], "code" : {"end" : "PYEND"}}
{"name" : "PYACTION", "sections" : ["SCHEDULE"], "size" : 2, "records": [
[{"name" : "NAME", "value_type" : "STRING"},
{"name" : "RUN_COUNT" , "value_type" : "STRING", "default" : "SINGLE"}],
[{"name" : "FILENAME", "value_type" : "STRING"}]]}

View File

@@ -0,0 +1,70 @@
RUNSPEC
DIMENS
10 10 3 /
GRID
DX
300*1000 /
DY
300*1000 /
DZ
100*20 100*30 100*50 /
TOPS
100*8325 /
PORO
300*0.3 /
PERMX
300*1 /
PERMY
300*1 /
PERMZ
300*1 /
SCHEDULE
PYACTION
WCLOSE UNLIMITED /
'wclose.py' /
WELSPECS
'PROD1' 'G1' 10 10 8400 'OIL' /
'PROD2' 'G1' 5 5 8400 'OIL' /
'INJ' 'G1' 1 1 8335 'GAS' /
/
COMPDAT
'PROD1' 10 10 3 3 'OPEN' 1* 1* 0.5 /
'PROD2' 5 5 3 3 'SHUT' 1* 1* 0.5 /
'INJ' 1 1 1 1 'OPEN' 1* 1* 0.5 /
/
WCONPROD
'PROD1' 'OPEN' 'ORAT' 20000 4* 1000 /
/
WCONINJE
'INJ' 'GAS' 'OPEN' 'RATE' 100000 1* 9014 /
/
TSTEP
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31 /
END

5
tests/PYACTION.DATA Normal file
View File

@@ -0,0 +1,5 @@
SCHEDULE
PYACTION
ACT1 Single /
act1.py /

11
tests/act1.py Normal file
View File

@@ -0,0 +1,11 @@
from math import sin
import random
print("sin(0) = {}".format(sin(0)))
#---
if random.random() > 0.25:
print("Large outcome")
else:
print("Small result")
A = 100
B = A / 10
C = B * 20

View File

@@ -28,6 +28,7 @@
#include <opm/parser/eclipse/Python/Python.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/P.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/SummaryState.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
@@ -39,12 +40,14 @@ BOOST_AUTO_TEST_CASE(INSTANTIATE) {
Python python;
BOOST_CHECK(!python);
BOOST_CHECK_THROW(python.exec("print('Hello world')"), std::logic_error);
BOOST_CHECK(! Python::enabled() );
}
#else
BOOST_AUTO_TEST_CASE(INSTANTIATE) {
Python python;
BOOST_CHECK(Python::enabled());
BOOST_CHECK(python);
BOOST_CHECK_NO_THROW(python.exec("print('Hello world')"));
@@ -99,107 +102,16 @@ BOOST_AUTO_TEST_CASE(PYINPUT_BASIC) {
}
BOOST_AUTO_TEST_CASE(PYACTION) {
const std::string deck_string = R"(
RUNSPEC
DIMENS
10 10 3 /
GRID
DX
300*1000 /
DY
300*1000 /
DZ
100*20 100*30 100*50 /
TOPS
100*8325 /
PORO
300*0.3 /
PERMX
300*1 /
PERMY
300*1 /
PERMZ
300*1 /
SCHEDULE
PYACTION
import sys
sys.stdout.write("Running PYACTION\n")
if "FOPR" in context.sim:
sys.stdout.write("Have FOPR: {}\n".format( context.sim["FOPR"] ))
else:
sys.stdout.write("Missing FOPR\n")
grid = context.state.grid()
sys.stdout.write("Grid dimensions: ({},{},{})\n".format(grid.nx, grid.ny, grid.nz))
prod_well = context.schedule.get_well("PROD1", context.report_step)
sys.stdout.write("Well status: {}\n".format(prod_well.status()))
if not "list" in context.storage:
context.storage["list"] = []
context.storage["list"].append(context.report_step)
if context.sim.well_var("PROD1", "WWCT") > 0.80:
context.schedule.shut_well("PROD1", context.report_step)
context.schedule.open_well("PROD2", context.report_step)
context.sim.update("RUN_COUNT", 1)
print(context.storage["list"])
PYEND
WELSPECS
'PROD1' 'G1' 10 10 8400 'OIL' /
'PROD2' 'G1' 5 5 8400 'OIL' /
'INJ' 'G1' 1 1 8335 'GAS' /
/
COMPDAT
'PROD1' 10 10 3 3 'OPEN' 1* 1* 0.5 /
'PROD2' 5 5 3 3 'SHUT' 1* 1* 0.5 /
'INJ' 1 1 1 1 'OPEN' 1* 1* 0.5 /
/
WCONPROD
'PROD1' 'OPEN' 'ORAT' 20000 4* 1000 /
/
WCONINJE
'INJ' 'GAS' 'OPEN' 'RATE' 100000 1* 9014 /
/
TSTEP
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31 /
END
)";
Parser parser;
auto deck = parser.parseString(deck_string);
auto deck = parser.parseFile("EMBEDDED_PYTHON.DATA");
auto ecl_state = EclipseState(deck);
auto schedule = Schedule(deck, ecl_state);
Python python;
SummaryState st(std::chrono::system_clock::now());
PyAction py_action(deck.getKeyword("PYACTION").getRecord(0).getItem("code").get<std::string>(0));
const auto& pyaction_kw = deck.getKeyword<ParserKeywords::PYACTION>(0);
const std::string& fname = pyaction_kw.getRecord(1).getItem(0).get<std::string>(0);
Action::PyAction py_action("WCLOSE", Action::PyAction::RunCount::unlimited, Action::PyAction::load(deck.getInputPath(), fname));
st.update_well_var("PROD1", "WWCT", 0);
python.exec(py_action, ecl_state, schedule, 10, st);

View File

@@ -22,12 +22,24 @@
#include <boost/test/unit_test.hpp>
#include <iostream>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/P.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Action/PyAction.hpp>
using namespace Opm;
BOOST_AUTO_TEST_CASE(ParsePYACTION) {
const std::string input_code = R"(from math import sin
Parser parser;
auto deck = parser.parseFile("PYACTION.DATA");
auto keyword = deck.getKeyword<ParserKeywords::PYACTION>(0);
const auto& record0 = keyword.getRecord(0);
const auto& record1 = keyword.getRecord(1);
const auto& name = record0.getItem(0).get<std::string>(0);
auto run_count = Action::PyAction::from_string(record0.getItem(1).get<std::string>(0));
std::string code = Action::PyAction::load(deck.getInputPath(), record1.getItem(0).get<std::string>(0));
std::string literal_code =R"(from math import sin
import random
print("sin(0) = {}".format(sin(0)))
#---
@@ -40,31 +52,8 @@ B = A / 10
C = B * 20
)";
const std::string deck_string1 = R"(
SCHEDULE
PYACTION Her comes an ignored comment
)" + input_code + "PYEND";
const std::string deck_string2 = R"(
SCHEDULE
PYACTION -- Comment
)" + input_code + "PYEND" + "\nGRID";
Parser parser;
{
auto deck = parser.parseString(deck_string1);
const auto& parsed_code = deck.getKeyword("PYACTION").getRecord(0).getItem("code").get<std::string>(0);
BOOST_CHECK_EQUAL(parsed_code, input_code);
}
{
auto deck = parser.parseString(deck_string2);
const auto& parsed_code = deck.getKeyword("PYACTION").getRecord(0).getItem("code").get<std::string>(0);
BOOST_CHECK_EQUAL(parsed_code, input_code);
BOOST_CHECK( deck.hasKeyword("GRID"));
}
PyAction pyact(input_code);
Action::PyAction pyaction("ACT1", run_count, code);
BOOST_CHECK_EQUAL(pyaction.name(), "ACT1");
BOOST_CHECK_EQUAL(pyaction.code(), literal_code);
BOOST_CHECK(pyaction.run_count() == Action::PyAction::RunCount::single);
}

22
tests/wclose.py Normal file
View File

@@ -0,0 +1,22 @@
import sys
sys.stdout.write("Running PYACTION\n")
if "FOPR" in context.sim:
sys.stdout.write("Have FOPR: {}\n".format( context.sim["FOPR"] ))
else:
sys.stdout.write("Missing FOPR\n")
grid = context.state.grid()
sys.stdout.write("Grid dimensions: ({},{},{})\n".format(grid.nx, grid.ny, grid.nz))
prod_well = context.schedule.get_well("PROD1", context.report_step)
sys.stdout.write("Well status: {}\n".format(prod_well.status()))
if not "list" in context.storage:
context.storage["list"] = []
context.storage["list"].append(context.report_step)
if context.sim.well_var("PROD1", "WWCT") > 0.80:
context.schedule.shut_well("PROD1", context.report_step)
context.schedule.open_well("PROD2", context.report_step)
context.sim.update("RUN_COUNT", 1)
print(context.storage["list"])