PYACTION: The actual python code is in an external file
With this commit the PYACTION keyword is changed, instead of embedding the Python code directly in the .DATA file the keyword now points to an external file which is loaded verbatim into the PyAction keyword. In addition the PYACTION keyword has now got a name and a string indicating how many times it should run.
This commit is contained in:
parent
db72ff80ed
commit
7852203d39
@ -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()
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#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>
|
||||
@ -159,6 +160,15 @@ int main(int argc, char** argv) {
|
||||
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>();
|
||||
|
@ -28,8 +28,20 @@ namespace Opm {
|
||||
|
||||
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(const std::string& name, RunCount run_count, const std::string& code);
|
||||
const std::string& code() const;
|
||||
const std::string& name() const;
|
||||
PyAction::RunCount run_count() const;
|
||||
~PyAction();
|
||||
|
||||
/*
|
||||
@ -44,6 +56,8 @@ public:
|
||||
*/
|
||||
void * storage() const;
|
||||
private:
|
||||
std::string m_name;
|
||||
RunCount m_run_count;
|
||||
std::string input_code;
|
||||
void * m_storage;
|
||||
};
|
||||
|
@ -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 {
|
||||
|
||||
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.c_str(), std::ios::in);
|
||||
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
|
||||
|
@ -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"}]]}
|
||||
|
70
tests/EMBEDDED_PYTHON.DATA
Normal file
70
tests/EMBEDDED_PYTHON.DATA
Normal 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
5
tests/PYACTION.DATA
Normal file
@ -0,0 +1,5 @@
|
||||
SCHEDULE
|
||||
|
||||
PYACTION
|
||||
ACT1 Single /
|
||||
act1.py /
|
11
tests/act1.py
Normal file
11
tests/act1.py
Normal 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
|
@ -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>
|
||||
|
||||
@ -99,107 +100,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);
|
||||
PyAction py_action("WCLOSE", PyAction::RunCount::unlimited, PyAction::load(deck.getInputPath(), fname));
|
||||
st.update_well_var("PROD1", "WWCT", 0);
|
||||
python.exec(py_action, ecl_state, schedule, 10, st);
|
||||
|
||||
|
@ -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 = PyAction::from_string(record0.getItem(1).get<std::string>(0));
|
||||
std::string code = 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);
|
||||
PyAction pyaction("ACT1", run_count, code);
|
||||
BOOST_CHECK_EQUAL(pyaction.name(), "ACT1");
|
||||
BOOST_CHECK_EQUAL(pyaction.code(), literal_code);
|
||||
BOOST_CHECK(pyaction.run_count() == PyAction::RunCount::single);
|
||||
}
|
||||
|
22
tests/wclose.py
Normal file
22
tests/wclose.py
Normal 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"])
|
||||
|
Loading…
Reference in New Issue
Block a user