Adds a method get_injection_properties()

Adds a method get_injection_properties() to the Python interface to
Opm::Schedule. This method can be used to get information about
Well::WellInjectionProperties for a given report step.

In order to avoid code duplication between opm-simulators and opm-common,
logic in CMakeLists.txt was refactored out into
cmake/Modules/PyInstallPrefix.cmake
This commit is contained in:
Håkon Hægland 2022-05-11 17:31:38 +02:00
parent 84ef4f6aa3
commit fa740678e0
4 changed files with 83 additions and 36 deletions

View File

@ -334,34 +334,7 @@ install(FILES cmake/OPM-CMake.md
install(FILES etc/opm_bash_completion.sh.in DESTINATION share/opm/etc)
if (OPM_ENABLE_PYTHON)
execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "
import site, sys
try:
sys.stdout.write(site.getsitepackages()[-1])
except e:
sys.stdout.write('')" OUTPUT_VARIABLE PYTHON_SITE_PACKAGES_PATH)
# -------------------------------------------------------------------------
# 1: Wrap C++ functionality in Python
if (PYTHON_SITE_PACKAGES_PATH MATCHES ".*/dist-packages/?" AND
CMAKE_INSTALL_PREFIX MATCHES "^/usr.*")
# dist-packages is only used if we install below /usr and python's site packages
# path matches dist-packages
set(PYTHON_PACKAGE_PATH "dist-packages")
else()
set(PYTHON_PACKAGE_PATH "site-packages")
endif()
if(PYTHON_VERSION_MAJOR)
set(PY_MAJOR ${PYTHON_VERSION_MAJOR})
else()
set(PY_MAJOR ${Python3_VERSION_MAJOR})
endif()
if(PYTHON_VERSION_MINOR)
set(PY_MINOR ${PYTHON_VERSION_MINOR})
else()
set(PY_MINOR ${Python3_VERSION_MINOR})
endif()
set(PYTHON_INSTALL_PREFIX "lib/python${PY_MAJOR}.${PY_MINOR}/${PYTHON_PACKAGE_PATH}" CACHE STRING "Subdirectory to install Python modules in")
include(PyInstallPrefix)
make_directory(${PROJECT_BINARY_DIR}/python)
get_target_property(_opmcommon_include_dirs opmcommon INCLUDE_DIRECTORIES)
list(APPEND _opmcommon_include_dirs ${_ecl_include_dirs})

View File

@ -0,0 +1,28 @@
# We make this a cmake module so it can be used from opm-simulators' CMakeLists.txt also
execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "
import site, sys
try:
sys.stdout.write(site.getsitepackages()[-1])
except e:
sys.stdout.write('')" OUTPUT_VARIABLE PYTHON_SITE_PACKAGES_PATH)
# -------------------------------------------------------------------------
# 1: Wrap C++ functionality in Python
if (PYTHON_SITE_PACKAGES_PATH MATCHES ".*/dist-packages/?" AND
CMAKE_INSTALL_PREFIX MATCHES "^/usr.*")
# dist-packages is only used if we install below /usr and python's site packages
# path matches dist-packages
set(PYTHON_PACKAGE_PATH "dist-packages")
else()
set(PYTHON_PACKAGE_PATH "site-packages")
endif()
if(PYTHON_VERSION_MAJOR)
set(PY_MAJOR ${PYTHON_VERSION_MAJOR})
else()
set(PY_MAJOR ${Python3_VERSION_MAJOR})
endif()
if(PYTHON_VERSION_MINOR)
set(PY_MINOR ${PYTHON_VERSION_MINOR})
else()
set(PY_MINOR ${Python3_VERSION_MINOR})
endif()
set(PYTHON_INSTALL_PREFIX "lib/python${PY_MAJOR}.${PY_MINOR}/${PYTHON_PACKAGE_PATH}" CACHE STRING "Subdirectory to install Python modules in")

View File

@ -59,13 +59,13 @@ namespace {
}
std::map<std::string, double> get_production_properties(
const Schedule& sch, const std::string& name, const size_t& timestep)
const Schedule& sch, const std::string& well_name, const size_t& report_step)
{
const Well* well = nullptr;
try{
well = &(sch.getWell( name, timestep ));
} catch( const std::invalid_argument& e ) {
throw py::key_error( fmt::format("well {} is not defined", name ));
well = &(sch.getWell( well_name, report_step ));
} catch( const std::out_of_range& e ) {
throw py::index_error( fmt::format("well {} is not defined", well_name ));
}
if (well->isProducer()) {
auto& prod_prop = well->getProductionProperties();
@ -81,9 +81,34 @@ namespace {
};
}
else {
throw py::key_error( fmt::format("well {} is not a producer", name) );
throw py::key_error( fmt::format("well {} is not a producer", well_name) );
}
}
std::map<std::string, double> get_injection_properties(
const Schedule& sch, const std::string& well_name, const size_t& report_step)
{
const Well* well = nullptr;
try{
well = &(sch.getWell( well_name, report_step ));
} catch( const std::out_of_range& e ) {
throw py::index_error( fmt::format("well {}: invalid well name", well_name ));
}
if (well->isInjector()) {
auto& inj_prop = well->getInjectionProperties();
return {
{ "surf_inj_rate", inj_prop.surfaceInjectionRate.get<double>() },
{ "resv_inj_rate", inj_prop.reservoirInjectionRate.get<double>() },
{ "bhp_target", inj_prop.BHPTarget.get<double>() },
{ "thp_target", inj_prop.THPTarget.get<double>() },
};
}
else {
throw py::key_error( fmt::format("well {} is not an injector", well_name) );
}
}
system_clock::time_point get_start_time( const Schedule& s ) {
return datetime(s.posixStartTime());
}
@ -185,7 +210,8 @@ void python::common::export_Schedule(py::module& module) {
.def( "open_well", &Schedule::open_well)
.def( "stop_well", &Schedule::stop_well)
.def( "get_wells", &Schedule::getWells)
.def( "get_production_properties", &get_production_properties)
.def( "get_injection_properties", &get_injection_properties, py::arg("well_name"), py::arg("report_step"))
.def( "get_production_properties", &get_production_properties, py::arg("well_name"), py::arg("report_step"))
.def("well_names", py::overload_cast<const std::string&>(&Schedule::wellNames, py::const_))
.def( "get_well", &get_well)
.def( "insert_keywords",

View File

@ -57,6 +57,22 @@ class TestSchedule(unittest.TestCase):
with self.assertRaises(Exception):
self.sch[0].group('foo')
def test_injection_properties(self):
deck = Parser().parse(test_path('spe3/SPE3CASE1.DATA'))
state = EclipseState(deck)
sch = Schedule( deck, state )
report_step = 4
well_name = 'INJ'
prop = sch.get_injection_properties(well_name, report_step)
self.assertEqual(prop['surf_inj_rate'], 4700.0) # Mscf/day
self.assertEqual(prop['resv_inj_rate'], 0.0) # rb/day
self.assertEqual(prop['bhp_target'], 4000.0) # psi
self.assertEqual(prop['thp_target'], 0.0)
with self.assertRaises(IndexError):
prop = sch.get_injection_properties("UNDEF", report_step)
with self.assertRaises(KeyError):
prop = sch.get_injection_properties("PROD", report_step)
def test_production_properties(self):
deck = Parser().parse(test_path('spe3/SPE3CASE1.DATA'))
state = EclipseState(deck)
@ -67,11 +83,15 @@ class TestSchedule(unittest.TestCase):
self.assertEqual(prop['alq_value'], 0.0)
self.assertEqual(prop['bhp_target'], 500.0)
self.assertEqual(prop['gas_rate'], 6200.0)
self.assertEqual(prop['liquid_rate'], 0.0)
self.assertEqual(prop['oil_rate'], 0.0)
self.assertEqual(prop['water_rate'], 0.0)
self.assertEqual(prop['liquid_rate'], 0.0)
self.assertEqual(prop['resv_rate'], 0.0)
self.assertEqual(prop['thp_target'], 0.0)
self.assertEqual(prop['water_rate'], 0.0)
with self.assertRaises(IndexError):
prop = sch.get_production_properties("UNDEF", report_step)
with self.assertRaises(KeyError):
prop = sch.get_production_properties("INJ", report_step)
def test_well_names(self):
deck = Parser().parse(test_path('spe3/SPE3CASE1.DATA'))