Merge pull request #1593 from joakim-hove/internalize-pyaction
Internalize pyaction
This commit is contained in:
@@ -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()
|
||||
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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 >
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
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"])
|
||||
|
||||
Reference in New Issue
Block a user