Skip to content

Commit

Permalink
Add a submodule opm.embedded to the opm library
Browse files Browse the repository at this point in the history
Now, ther are again only two modules created
- opmcommon_python: all classes, with a submodule opm.embedded that has the following attributes
  - current_ecl_state, current_summary_state, current_schedule, current_report_step
  - OpmLog
- opm_embedded, as embedded Python module, needed for PYACTION and PYINPUT

Still to do:
- In the embedded python code, one can now do import opm.io and use methods that are not meant for embedded python code
- the autocompletion does again not work as nicely as in the commit before :(
  • Loading branch information
lisajulia committed Apr 22, 2024
1 parent 3054426 commit 085fdf2
Show file tree
Hide file tree
Showing 23 changed files with 991 additions and 195 deletions.
21 changes: 1 addition & 20 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -404,34 +404,15 @@ if (OPM_ENABLE_PYTHON)

target_link_libraries(opmcommon_python PRIVATE
opmcommon)
if(OPM_ENABLE_EMBEDDED_PYTHON)
pybind11_add_module(opm_embedded
${PYTHON_CXX_SOURCE_FILES}
${PROJECT_BINARY_DIR}/python/cxx/builtin_pybind11.cpp)
target_link_libraries(opm_embedded PRIVATE
opmcommon)
endif()
if(TARGET pybind11::pybind11)
target_link_libraries(opmcommon_python PRIVATE
pybind11::pybind11)
if(OPM_ENABLE_EMBEDDED_PYTHON)
target_link_libraries(opm_embedded PRIVATE
pybind11::pybind11)
endif()
else()
target_include_directories(opmcommon_python SYSTEM PRIVATE ${pybind11_INCLUDE_DIRS})
if(OPM_ENABLE_EMBEDDED_PYTHON)
target_include_directories(opm_embedded SYSTEM PRIVATE ${pybind11_INCLUDE_DIRS})
endif()
endif()
set_target_properties(opmcommon_python PROPERTIES
LIBRARY_OUTPUT_DIRECTORY python/opm)
add_dependencies(opmcommon_python copy_python)
if(OPM_ENABLE_EMBEDDED_PYTHON)
set_target_properties(opm_embedded PROPERTIES
LIBRARY_OUTPUT_DIRECTORY python)
add_dependencies(opm_embedded copy_python)
endif()

# Generate versioned setup.py
configure_file(${PROJECT_SOURCE_DIR}/python/setup.py.in
Expand Down Expand Up @@ -459,7 +440,7 @@ if (OPM_ENABLE_PYTHON)

# Install the convenience library for PYACTION script development
if (OPM_ENABLE_EMBEDDED_PYTHON)
install(TARGETS opm_embedded DESTINATION ${DEST_PREFIX}${CMAKE_INSTALL_PREFIX}/${PYTHON_INSTALL_PREFIX})
install(TARGETS opmcommon_python DESTINATION ${DEST_PREFIX}${CMAKE_INSTALL_PREFIX}/${PYTHON_INSTALL_PREFIX})
endif()
endif()

Expand Down
2 changes: 2 additions & 0 deletions CMakeLists_files.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -706,13 +706,15 @@ if(ENABLE_ECL_OUTPUT)
tests/ACTIONX_M1.X0010
tests/wclose.py
tests/msim/MSIM_PYACTION.DATA
tests/msim/MSIM_PYACTION_RETRIEVE_INFO.DATA
tests/msim/MSIM_PYACTION_CHANGING_SCHEDULE.DATA
tests/msim/MSIM_PYACTION_CHANGING_SCHEDULE_ACTIONX_CALLBACK.DATA
tests/msim/MSIM_PYACTION_INSERT_KEYWORD.DATA
tests/msim/MSIM_PYACTION_INSERT_INVALID_KEYWORD.DATA
tests/msim/MSIM_PYACTION_NO_RUN_FUNCTION.DATA
tests/msim/MSIM_PYACTION_OPEN_WELL_AT_PAST_REPORT_STEP.DATA
tests/msim/MSIM_PYACTION_OPEN_WELL_AT_TOO_LATE_REPORT_STEP.DATA
tests/msim/retrieve_info.py
tests/msim/action1.py
tests/msim/action2.py
tests/msim/action2_no_run_function.py
Expand Down
1 change: 1 addition & 0 deletions build-cmake/python/opm/_embedded.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .opmcommon_python.embedded import current_ecl_state, current_summary_state, current_schedule, current_report_step
6 changes: 6 additions & 0 deletions build-cmake/python/opm/embedded/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""
This is the opm_embedded module for embedding python code in PYACTION.
"""

from opm._embedded import current_ecl_state, current_summary_state, current_schedule, current_report_step
from opm._common import OpmLog
19 changes: 10 additions & 9 deletions opm/input/eclipse/Python/PyRunModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ PyRunModule::PyRunModule(std::shared_ptr<const Python> python, const std::string

// opm_embedded needs to be loaded before user defined module.
try {
this->opm_embedded = py::module::import("opm_embedded");
this->opm_embedded = py::module::import("opm.embedded");
} catch (const std::exception& e) {
OpmLog::error(fmt::format("Exception thrown when loading Python module opm_embedded: {}"), e.what());
OpmLog::error(fmt::format("Exception thrown when loading Python module opm.embedded: {}"), e.what());
throw e;
} catch (...) {
OPM_THROW(std::runtime_error, "General exception thrown when loading Python module opm_embedded!");
OPM_THROW(std::runtime_error, "General exception thrown when loading Python module opm.embedded!");
}
}

Expand Down Expand Up @@ -86,6 +86,7 @@ bool PyRunModule::run(EclipseState& ecl_state, Schedule& sched, std::size_t repo
// The attributes need to be set before the user defined module is loaded; it needs to be set in every call.
this->opm_embedded.attr("current_report_step") = report_step;


if (!this->module) {
// The attributes need to be set before the user defined module is loaded; they need to be set only once since they are shared pointers.
this->opm_embedded.attr("current_schedule") = &sched;
Expand Down Expand Up @@ -121,14 +122,14 @@ bool PyRunModule::run(EclipseState& ecl_state, Schedule& sched, std::size_t repo
this->module.attr("storage") = this->storage;

if (py::hasattr(this->module, "run")) {
OpmLog::info(R"(PYACTION can be used without a run(ecl_state, schedule, report_step, summary_state, actionx_callback) function, its arguments are available as attributes of the module opm_embedded, try the following in your python script:
OpmLog::info(R"(PYACTION can be used without a run(ecl_state, schedule, report_step, summary_state, actionx_callback) function, its arguments are available as attributes of the module opm.embedded, try the following in your python script:
import opm_embedded
import opm.embedded
help(opm_embedded.current_ecl_state)
help(opm_embedded.current_schedule)
help(opm_embedded.current_report_step)
help(opm_embedded.current_summary_state)
help(opm.embedded.current_ecl_state)
help(opm.embedded.current_schedule)
help(opm.embedded.current_report_step)
help(opm.embedded.current_summary_state)
)");
this->run_function = this->module.attr("run");
return this->executeInnerRunFunction(actionx_callback);
Expand Down
9 changes: 5 additions & 4 deletions opm/input/eclipse/Python/PythonInterp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ namespace Opm {
The same module is created as a Pybind 11 module in export.cpp
*/

OPM_EMBEDDED_MODULE(opm_embedded, module) {
python::common::export_all_opm_embedded(module);
OPM_EMBEDDED_MODULE(opm, module) {
pybind11::module submodule = module.def_submodule("embedded");
python::common::export_all_opm_embedded(submodule);
}


Expand All @@ -60,7 +61,7 @@ bool PythonInterp::exec(const std::string& python_code, const Parser& parser, De
if (!this->guard)
throw std::logic_error("Python interpreter not enabled");

auto context = py::module::import("opm_embedded");
auto context = py::module::import("opm.embedded");
context.attr("deck") = &deck;
context.attr("parser") = &parser;
return this->exec(python_code, context);
Expand All @@ -72,7 +73,7 @@ bool PythonInterp::exec(const std::string& python_code) {
if (!this->guard)
throw std::logic_error("Python interpreter not enabled");

auto context = py::module::import("opm_embedded");
auto context = py::module::import("opm.embedded");
return this->exec(python_code, context);
}

Expand Down
67 changes: 45 additions & 22 deletions python/cxx/eclipse_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,33 +92,56 @@ namespace {
return state.fieldProps();
}

/**
* @brief Function to export methods that are available for both embedded Python code and Python bindings for the Python class c.
*
* @param c Reference to the python class.
*/
void add_shared_EclipseState_methods(py::class_< EclipseState, std::shared_ptr<EclipseState> >& c) {
c.def_property_readonly( "title", &EclipseState::getTitle )
.def( "tables", &EclipseState::getTableManager, ref_internal)
.def( "field_props", &get_field_props, ref_internal)
.def( "grid", &EclipseState::getInputGrid, ref_internal)
.def( "has_input_nnc", &EclipseState::hasInputNNC)
.def( "input_nnc", &getNNC)
.def( "faultNames", &faultNames)
.def( "faultFaces", &faultFaces)
.def( "jfunc", &jfunc)
.def( "simulation", &EclipseState::getSimulationConfig, ref_internal)
;
}
}

void python::common::export_EclipseState(py::module& module) {
/**
* @brief Function to export the EclipseState class and some methods to the opm_embedded python module.
*
* In the below class we use std::shared_ptr as the holder type, see https://pybind11.readthedocs.io/en/stable/advanced/smart_ptrs.html
* this makes it possible to share the EclipseState (which is created only once per simulation) in the opm_embedded module.
*
* @param module Reference to the python module.
*/
void python::common::export_EclipseState_embedded(py::module& module) {
auto c = py::class_< EclipseState, std::shared_ptr<EclipseState> >( module, "EclipseState", R"pbdoc(
The Opm::EclipseState class - this is a representation of all static properties in the model,
ranging from porosity to relperm tables. The content of the EclipseState is immutable and may not
be changed at runtime.)pbdoc");

add_shared_EclipseState_methods(c);
}

// Note: In the below class we std::shared_ptr as the holder type, see:
//
// https://pybind11.readthedocs.io/en/stable/advanced/smart_ptrs.html
//
// this makes it possible to share the returned object with e.g. and
// opm.simulators.BlackOilSimulator Python object
//
py::class_< EclipseState, std::shared_ptr<EclipseState> >( module, "EclipseState", R"pbdoc(
The Opm::EclipseState class - this is a representation of all static properties in the model,
ranging from porosity to relperm tables. The content of the EclipseState is immutable and may not
be changed at runtime.)pbdoc")
/**
* @brief Function to export the EclipseState class and some methods to the opmcommon_python module, for the Python bindings.
*
* In the below class we use std::shared_ptr as the holder type, see https://pybind11.readthedocs.io/en/stable/advanced/smart_ptrs.html
* this makes it possible to share the EclipseState (which is created only once per simulation) with e.g. the opm.simulators.BlackOilSimulator Python object.
*
* @param module Reference to the python module.
*/
void python::common::export_EclipseState(py::module& module) {
auto c = py::class_< EclipseState, std::shared_ptr<EclipseState> >( module, "EclipseState")
.def(py::init<const Deck&>())
.def_property_readonly( "title", &EclipseState::getTitle )
.def( "field_props", &get_field_props, ref_internal)
.def( "grid", &EclipseState::getInputGrid, ref_internal)
.def( "config", &EclipseState::cfg, ref_internal)
.def( "tables", &EclipseState::getTableManager, ref_internal)
.def( "has_input_nnc", &EclipseState::hasInputNNC )
.def( "simulation", &EclipseState::getSimulationConfig, ref_internal)
.def( "input_nnc", &getNNC )
.def( "faultNames", &faultNames )
.def( "faultFaces", &faultFaces )
.def( "jfunc", &jfunc )
;

add_shared_EclipseState_methods(c);
}
46 changes: 35 additions & 11 deletions python/cxx/export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,49 @@ void python::common::export_all(py::module& module) {
export_ParserKeywords(module);
}


PYBIND11_MODULE(opmcommon_python, module) {
python::common::export_all(module);
pybind11::module submodule = module.def_submodule("embedded");
submodule.attr("current_ecl_state") = std::make_shared<EclipseState>();
submodule.attr("current_summary_state") = std::make_shared<SummaryState>();
submodule.attr("current_schedule") = std::make_shared<Schedule>();
submodule.attr("current_report_step") = 0;
submodule.doc() = R"pbdoc(This is the opm_embedded module for embedding python code in PYACTION.)pbdoc";
//python::common::export_all_opm_embedded(submodule); << This gives me an error when calling import opm.embedded
}

/**
* @brief Function to export several classes to the opm.embedded module.
* This embedded module is created as an embedded python module in PythonInterp.cpp
*
* @param module Reference to the python module.
*/
void python::common::export_all_opm_embedded(py::module& module) {
python::common::export_all(module);

export_ParseContext(module);
export_Parser(module);
export_Deck(module);
export_DeckKeyword(module);
export_UnitSystem(module);
export_ParserKeywords(module);

export_Schedule_embedded(module);
export_EclipseState_embedded(module);
export_SummaryState_embedded(module);

export_Connection(module);
export_FieldProperties(module);
export_EclipseGrid(module);
export_Group(module);
export_Log(module);
export_ScheduleState(module);
export_SimulationConfig(module);
export_TableManager(module);
export_Well(module);

module.attr("current_ecl_state") = std::make_shared<EclipseState>();
module.attr("current_summary_state") = std::make_shared<SummaryState>();
module.attr("current_schedule") = std::make_shared<Schedule>();
module.attr("current_report_step") = 0;
module.doc() = R"pbdoc(This is the opm_embedded module for embedding python code in PYACTION.)pbdoc";
}

/*
PYBIND11_MODULE create a Python of all the Python/C++ classes which are
generated in the python::common::export_all_opm_embedded() function in the wrapping code.
The same module is created as an embedded python module in PythonInterp.cpp
*/
PYBIND11_MODULE(opm_embedded, module) {
python::common::export_all_opm_embedded(module);
}
3 changes: 3 additions & 0 deletions python/cxx/export.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,21 @@ void export_FieldProperties(py::module& module);
void export_EclipseConfig(py::module& module);
void export_EclipseGrid(py::module& module);
void export_EclipseState(py::module& module);
void export_EclipseState_embedded(py::module& module);
void export_Group(py::module& module);
void export_ParseContext(py::module& module);
void export_Parser(py::module& module);
void export_SimulationConfig(py::module& module);
void export_Schedule(py::module& module);
void export_Schedule_embedded(py::module& module);
void export_ScheduleState(py::module& module);
void export_TableManager(py::module& module);
void export_Well(py::module& module);
void export_Log(py::module& module);
void export_IO(py::module& module);
void export_EModel(py::module& module);
void export_SummaryState(py::module& module);
void export_SummaryState_embedded(py::module& module);

// The export_ParserKeywords() function is implemented in the source file
// ${BUILD}/builtin_pybind11.cpp which is generated by the build system.
Expand Down

0 comments on commit 085fdf2

Please sign in to comment.