Compare commits

..

8 Commits

Author SHA1 Message Date
Arne Morten Kvarving
0a1691fbba bump to final 2021-05-04 10:08:35 +02:00
Arne Morten Kvarving
049394497b bump packaging versions 2021-04-27 09:55:02 +02:00
Arne Morten Kvarving
ca27588d5a fix cherry-picking error 2021-04-26 14:44:58 +02:00
Arne Morten Kvarving
678676484b fixed: missing member initialization
if deck has no rockcomp keyword, variable is not written
2021-04-26 13:35:03 +02:00
Joakim Hove
1415d849de Merge pull request #2434 from akva2/fix_serialize_valgrind
Fix some valgrind errors in test_ParallelRestart
2021-04-26 12:42:45 +02:00
Tor Harald Sandve
7f24108d09 Raise OPM Version to 2021.04-rc2 2021-04-20 10:30:59 +02:00
Bård Skaflestad
bcc96eda2c Merge pull request #2432 from goncalvesmachadoc/welltemp_output
Fix Well Temperature Output
2021-04-19 08:37:29 +02:00
Joakim Hove
75b81a16a5 Merge pull request #2429 from blattms/fix-compile-g++10.2
Include stdexcept header to fix compilation with g++-10
2021-04-19 08:37:23 +02:00
2068 changed files with 19752 additions and 144249 deletions

View File

@@ -9,7 +9,7 @@ option(ENABLE_ECL_INPUT "Enable eclipse input support?" ON)
option(ENABLE_ECL_OUTPUT "Enable eclipse output support?" ON)
option(ENABLE_MOCKSIM "Build the mock simulator for io testing" ON)
option(OPM_ENABLE_PYTHON "Enable python bindings?" OFF)
option(OPM_INSTALL_PYTHON "Install python bindings?" ON)
option(OPM_INSTALL_PYTHON "Enable python bindings?" OFF)
option(OPM_ENABLE_EMBEDDED_PYTHON "Enable embedded python?" OFF)
# Output implies input
@@ -42,13 +42,11 @@ set(OPM_PROJECT_EXTRA_CODE_INTREE "#ENABLE_ECL_INPUT is needed by opm-common-pre
if(ENABLE_ECL_OUTPUT)
set(OPM_PROJECT_EXTRA_CODE_INSTALLED "${OPM_PROJECT_EXTRA_CODE_INSTALLED}
set(COMPARE_ECL_COMMAND ${CMAKE_INSTALL_PREFIX}/bin${${name}_VER_DIR}/compareECL)
set(OPM_PACK_COMMAND ${CMAKE_INSTALL_PREFIX}/bin${${name}_VER_DIR}/opmpack)
set(RST_DECK_COMMAND ${CMAKE_INSTALL_PREFIX}/bin${${name}_VER_DIR}/rst_deck)")
set(OPM_PACK_COMMAND ${CMAKE_INSTALL_PREFIX}/bin${${name}_VER_DIR}/opmpack)")
set(OPM_PROJECT_EXTRA_CODE_INTREE "${OPM_PROJECT_EXTRA_CODE_INTREE}
set(COMPARE_ECL_COMMAND ${PROJECT_BINARY_DIR}/bin/compareECL)
set(OPM_PACK_COMMAND ${PROJECT_BINARY_DIR}/bin/opmpack)
set(RST_DECK_COMMAND ${PROJECT_BINARY_DIR}/bin/rst_deck)")
set(OPM_PACK_COMMAND ${PROJECT_BINARY_DIR}/bin/opmpack)")
endif()
# project information is in dune.module. Read this file and set variables.
@@ -80,13 +78,6 @@ if(ENABLE_ECL_INPUT)
endif()
endif()
if(OPM_ENABLE_EMBEDDED_PYTHON AND NOT OPM_ENABLE_PYTHON)
# This needs to be here to run before source_hook
message(WARNING "Inconsistent settings: OPM_ENABLE_PYTHON=OFF and "
"OPM_ENABLE_EMBEDDED_PYTHON=ON. Please use OPM_ENABLE_PYTHON=ON to "
"activate. Will disable embedded python in this run.")
set(OPM_ENABLE_EMBEDDED_PYTHON OFF CACHE BOOL "Enable embedded python?" FORCE)
endif()
# read the list of components from this file (in the project directory);
# it should set various lists with the names of the files to include
include (CMakeLists_files.cmake)
@@ -116,7 +107,8 @@ macro (config_hook)
set(HAVE_ECL_OUTPUT 1)")
endif()
find_package(Boost COMPONENTS filesystem regex system unit_test_framework)
# Configure boost targets for old cmake
include(cmake/Modules/BoostTargets.cmake)
if (HAVE_DYNAMIC_BOOST_TEST)
set_target_properties(Boost::unit_test_framework PROPERTIES INTERFACE_COMPILE_DEFINITIONS BOOST_TEST_DYN_LINK=1)
@@ -137,11 +129,11 @@ macro (sources_hook)
foreach (name A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
list(INSERT opm-common_SOURCES 0 ${PROJECT_BINARY_DIR}/ParserKeywords/${name}.cpp)
endforeach()
if (OPM_ENABLE_EMBEDDED_PYTHON)
if (OPM_ENABLE_PYTHON)
list(INSERT opm-common_SOURCES 0 ${PROJECT_BINARY_DIR}/python/cxx/builtin_pybind11.cpp)
endif()
endif()
set_source_files_properties(src/opm/input/eclipse/Python/Python.cpp
set_source_files_properties(src/opm/parser/eclipse/Python/Python.cpp
PROPERTIES COMPILE_FLAGS -Wno-shadow)
endmacro (sources_hook)
@@ -199,18 +191,6 @@ if (OPM_ENABLE_PYTHON)
else()
find_package(Python3 REQUIRED COMPONENTS Interpreter)
endif()
# Make sure we fail gracefully here without setuptool
execute_process(COMMAND ${Python3_EXECUTABLE} -c "import setuptools"
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
ERROR_VARIABLE SETUPTOOL_ERROR OUTPUT_VARIABLE SETUPTOOL_OUT
RESULT_VARIABLE SETUPTOOL_RESULT)
if(SETUPTOOL_RESULT GREATER 0)
message(WARNING "Trying to test setuptools resulted in error message: ${SETUPTOOL_ERROR}")
message(SEND_ERROR "To build the python bindings you need to install setuptool. "
"Either use \"apt-get install python3-setuptools\" (on Debian/Ubuntu) "
"or \"pip install setuptools\"")
endif()
# Compatibility settings for PythonInterp and PythonLibs
# used e.g. in FindCwrap, pybind11
set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE})
@@ -262,6 +242,10 @@ if (ENABLE_MOCKSIM)
endif()
endif()
if (ENABLE_SCHEDULE_DEBUG)
add_compile_definitions( SCHEDULE_DEBUG )
endif()
# Build the compare utilities
if(ENABLE_ECL_INPUT)
add_executable(compareECL
@@ -278,15 +262,11 @@ if(ENABLE_ECL_INPUT)
test_util/summary.cpp
)
add_executable(arraylist
test_util/arraylist.cpp
)
add_executable(rewriteEclFile
test_util/rewriteEclFile.cpp
)
foreach(target compareECL convertECL summary rewriteEclFile arraylist)
foreach(target compareECL convertECL summary rewriteEclFile)
target_link_libraries(${target} opmcommon)
install(TARGETS ${target} DESTINATION bin)
endforeach()
@@ -320,47 +300,28 @@ if(ENABLE_ECL_INPUT)
${PROJECT_BINARY_DIR}/tests
)
foreach(test test_EclIO test_EGrid test_ERft test_ERst test_ESmry test_EInit)
opm_add_test(${test} CONDITION ENABLE_ECL_INPUT AND Boost_UNIT_TEST_FRAMEWORK_FOUND
LIBRARIES ${_libs}
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/tests)
endforeach()
endif()
# Install build system files and documentation
install(DIRECTORY cmake
DESTINATION share/opm USE_SOURCE_PERMISSIONS
PATTERN "OPM-CMake.md" EXCLUDE)
install(FILES cmake/OPM-CMake.md
DESTINATION ${CMAKE_INSTALL_DOCDIR})
# Install build system files
install(DIRECTORY cmake DESTINATION share/opm USE_SOURCE_PERMISSIONS)
# Install tab completion skeleton
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
if (EXISTS "/etc/debian_version")
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")
set(PYTHON_INSTALL_PREFIX "lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/${PYTHON_PACKAGE_PATH}" CACHE STRING "Subdirectory to install Python modules in")
make_directory(${PROJECT_BINARY_DIR}/python)
get_target_property(_opmcommon_include_dirs opmcommon INCLUDE_DIRECTORIES)
@@ -384,12 +345,7 @@ except e:
endif()
if (BUILD_SHARED_LIBS)
if (OPM_INSTALL_PYTHON)
list(APPEND _python_rpath_list "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
list(APPEND _python_rpath_list "${PROJECT_BINARY_DIR}/lib")
else()
list(APPEND _python_rpath_list "${PROJECT_BINARY_DIR}/lib")
endif()
list(APPEND _python_rpath_list "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
endif()
if (_python_rpath_list)
@@ -421,16 +377,6 @@ except e:
set(SETUP_PY_FMT_FLAGS "'-DFMT_HEADER_ONLY'")
endif()
if(cjson_FOUND)
list(APPEND opm-common_PYTHON_LINKAGE "'${cjson_LIBRARIES}'")
endif()
# opm-common_PYTHON_LINKAGE is used verbatim in a listin python.
# Hence items need to be comma separated.
if (opm-common_PYTHON_LINKAGE)
string(REPLACE ";" "," SETUP_PY_LINKAGE "${opm-common_PYTHON_LINKAGE}")
endif()
configure_file (${PROJECT_SOURCE_DIR}/python/setup.py.in ${PROJECT_BINARY_DIR}/python/setup.py)
file(COPY ${PROJECT_SOURCE_DIR}/python/README.md DESTINATION ${PROJECT_BINARY_DIR}/python)
execute_process(COMMAND ${Python3_EXECUTABLE} target_name.py
@@ -513,6 +459,11 @@ except e:
target_link_libraries(opmcommon PUBLIC ${PYTHON_LIBRARY})
add_definitions(-DEMBEDDED_PYTHON)
endif()
else()
if (OPM_ENABLE_EMBEDDED_PYTHON)
message(WARNING "Inconsistent settings OPM_ENABLE_PYTHON / OPM_ENABLE_EMBEDDED_PYTHON")
set(OPM_ENABLE_EMBEDDED_PYTHON OFF CACHE BOOL "Enable embedded python?" FORCE)
endif()
endif()
install(DIRECTORY docs/man1 DESTINATION ${CMAKE_INSTALL_MANDIR}

File diff suppressed because it is too large Load Diff

View File

@@ -13,17 +13,17 @@ if (EXISTS ${BASE_DIR}/python/cxx)
endif()
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
${BASE_DIR}/tmp_gen/include/opm/input/eclipse/Parser/ParserKeywords/Builtin.hpp
${BASE_DIR}/include/opm/input/eclipse/Parser/ParserKeywords/Builtin.hpp)
${BASE_DIR}/tmp_gen/include/opm/parser/eclipse/Parser/ParserKeywords/Builtin.hpp
${BASE_DIR}/include/opm/parser/eclipse/Parser/ParserKeywords/Builtin.hpp)
file(GLOB HDRS ${BASE_DIR}/tmp_gen/include/opm/input/eclipse/Parser/ParserKeywords/*.hpp)
file(GLOB HDRS ${BASE_DIR}/tmp_gen/include/opm/parser/eclipse/Parser/ParserKeywords/*.hpp)
foreach(HDR ${HDRS})
file(RELATIVE_PATH hdr ${BASE_DIR}/tmp_gen/include/opm/input/eclipse/Parser/ParserKeywords ${HDR})
file(RELATIVE_PATH hdr ${BASE_DIR}/tmp_gen/include/opm/parser/eclipse/Parser/ParserKeywords ${HDR})
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
${HDR}
${BASE_DIR}/include/opm/input/eclipse/Parser/ParserKeywords/${hdr})
${BASE_DIR}/include/opm/parser/eclipse/Parser/ParserKeywords/${hdr})
endforeach()

View File

@@ -18,13 +18,13 @@ set(_testdir ${PROJECT_SOURCE_DIR}/tests/parser/data)
opm_add_test(ParserTests
SOURCES tests/parser/ParserTests.cpp
LIBRARIES ${TEST_LIBS}
TEST_ARGS -- ${_testdir}/)
TEST_ARGS ${_testdir}/)
list(APPEND EXTRA_TESTS ParserTests)
opm_add_test(ParserIncludeTests
SOURCES tests/parser/ParserIncludeTests.cpp
LIBRARIES ${TEST_LIBS}
TEST_ARGS -- ${_testdir}/parser/)
TEST_ARGS ${_testdir}/parser/)
target_compile_definitions(ParserIncludeTests PRIVATE
-DHAVE_CASE_SENSITIVE_FILESYSTEM=${HAVE_CASE_SENSITIVE_FILESYSTEM})
list(APPEND EXTRA_TESTS ParserIncludeTests)
@@ -32,13 +32,13 @@ list(APPEND EXTRA_TESTS ParserIncludeTests)
opm_add_test(PvtxTableTests
SOURCES tests/parser/PvtxTableTests.cpp
LIBRARIES ${TEST_LIBS}
TEST_ARGS -- ${_testdir}/integration_tests/)
TEST_ARGS ${_testdir}/integration_tests/)
list(APPEND EXTRA_TESTS PvtxTableTests)
opm_add_test(EclipseStateTests
SOURCES tests/parser/EclipseStateTests.cpp
LIBRARIES ${TEST_LIBS}
TEST_ARGS -- ${_testdir}/integration_tests/)
TEST_ARGS ${_testdir}/integration_tests/)
list(APPEND EXTRA_TESTS EclipseStateTests)
foreach (test BoxTest
@@ -55,7 +55,7 @@ foreach (test BoxTest
opm_add_test(${test}
SOURCES tests/parser/integration/${test}.cpp
LIBRARIES ${TEST_LIBS}
TEST_ARGS -- ${_testdir}/integration_tests/)
TEST_ARGS ${_testdir}/integration_tests/)
list(APPEND EXTRA_TESTS ${test})
endforeach ()
@@ -69,20 +69,6 @@ opm_add_test( rst_msw
LIBRARIES ${TEST_LIBS}
TEST_ARGS tests/MSW.DATA tests/MSW_RESTART.DATA )
add_test( NAME rst_deck_test
COMMAND ${PROJECT_SOURCE_DIR}/tests/rst_test_driver.sh ${PROJECT_BINARY_DIR}/bin/rst_deck ${PROJECT_BINARY_DIR}/bin/opmhash
${PROJECT_SOURCE_DIR}/tests/SPE1CASE2_INCLUDE.DATA)
add_test( NAME rst_deck_test2
COMMAND ${PROJECT_SOURCE_DIR}/tests/rst_test_driver2.sh
${PROJECT_BINARY_DIR}/bin/rst_deck
${PROJECT_BINARY_DIR}/bin/opmi
${PROJECT_SOURCE_DIR}/tests/ACTIONX_M1.DATA
${PROJECT_SOURCE_DIR}/tests/ACTIONX_M1_MULTIPLE.DATA
${PROJECT_SOURCE_DIR}/tests/ACTIONX_M1.UNRST
${PROJECT_SOURCE_DIR}/tests/ACTIONX_M1.X0010 )
# opm-tests dependent tests
if(HAVE_OPM_TESTS)
opm_add_test(parse_write ONLY_COMPILE
@@ -103,6 +89,7 @@ if(HAVE_OPM_TESTS)
${OPM_TESTS_ROOT}/spe9/SPE9_CP.DATA
${OPM_TESTS_ROOT}/spe9/SPE9_CP_GROUP.DATA
${OPM_TESTS_ROOT}/spe9/SPE9_CP_SHORT.DATA
${OPM_TESTS_ROOT}/spe9/SPE9_CP_SHORT_RESTART.DATA
${OPM_TESTS_ROOT}/spe9/SPE9.DATA
${OPM_TESTS_ROOT}/spe10model1/SPE10_MODEL1.DATA
${OPM_TESTS_ROOT}/spe10model2/SPE10_MODEL2.DATA
@@ -126,17 +113,8 @@ if(HAVE_OPM_TESTS)
opm_add_test("SPE9_CP_GROUP2" NO_COMPILE EXE_NAME parse_write TEST_ARGS "${OPM_TESTS_ROOT}/spe9group/SPE9_CP_GROUP.DATA")
set_property(TEST NORNE_ATW2013
PROPERTY ENVIRONMENT "OPM_ERRORS_IGNORE=PARSE_RANDOM_SLASH")
add_test( NAME rst_deck_test_norne
COMMAND ${PROJECT_SOURCE_DIR}/tests/rst_test_driver.sh ${PROJECT_BINARY_DIR}/bin/rst_deck ${PROJECT_BINARY_DIR}/bin/opmhash
${OPM_TESTS_ROOT}/norne/NORNE_ATW2013.DATA)
set_property(TEST rst_deck_test_norne
PROPERTY ENVIRONMENT "OPM_ERRORS_IGNORE=PARSE_RANDOM_SLASH")
endif()
# JSON tests
opm_add_test(jsonTests
SOURCES tests/json/jsonTests.cpp

View File

@@ -1,29 +1,26 @@
set(genkw_SOURCES src/opm/json/JsonObject.cpp
src/opm/input/eclipse/Parser/createDefaultKeywordList.cpp
src/opm/input/eclipse/Deck/UDAValue.cpp
src/opm/input/eclipse/Deck/DeckTree.cpp
src/opm/input/eclipse/Deck/DeckValue.cpp
src/opm/input/eclipse/Deck/Deck.cpp
src/opm/input/eclipse/Deck/DeckView.cpp
src/opm/input/eclipse/Deck/DeckItem.cpp
src/opm/input/eclipse/Deck/DeckKeyword.cpp
src/opm/input/eclipse/Deck/DeckRecord.cpp
src/opm/input/eclipse/Deck/DeckOutput.cpp
src/opm/input/eclipse/Generator/KeywordGenerator.cpp
src/opm/input/eclipse/Generator/KeywordLoader.cpp
src/opm/input/eclipse/Parser/ErrorGuard.cpp
src/opm/input/eclipse/Parser/ParseContext.cpp
src/opm/input/eclipse/Parser/ParserEnums.cpp
src/opm/input/eclipse/Parser/ParserItem.cpp
src/opm/input/eclipse/Parser/ParserKeyword.cpp
src/opm/input/eclipse/Parser/ParserRecord.cpp
src/opm/input/eclipse/Parser/raw/RawKeyword.cpp
src/opm/input/eclipse/Parser/raw/RawRecord.cpp
src/opm/input/eclipse/Parser/raw/StarToken.cpp
src/opm/input/eclipse/Units/Dimension.cpp
src/opm/input/eclipse/Units/UnitSystem.cpp
src/opm/parser/eclipse/Parser/createDefaultKeywordList.cpp
src/opm/parser/eclipse/Deck/UDAValue.cpp
src/opm/parser/eclipse/Deck/DeckValue.cpp
src/opm/parser/eclipse/Deck/Deck.cpp
src/opm/parser/eclipse/Deck/DeckItem.cpp
src/opm/parser/eclipse/Deck/DeckKeyword.cpp
src/opm/parser/eclipse/Deck/DeckRecord.cpp
src/opm/parser/eclipse/Deck/DeckOutput.cpp
src/opm/parser/eclipse/Generator/KeywordGenerator.cpp
src/opm/parser/eclipse/Generator/KeywordLoader.cpp
src/opm/parser/eclipse/Parser/ErrorGuard.cpp
src/opm/parser/eclipse/Parser/ParseContext.cpp
src/opm/parser/eclipse/Parser/ParserEnums.cpp
src/opm/parser/eclipse/Parser/ParserItem.cpp
src/opm/parser/eclipse/Parser/ParserKeyword.cpp
src/opm/parser/eclipse/Parser/ParserRecord.cpp
src/opm/parser/eclipse/Parser/raw/RawKeyword.cpp
src/opm/parser/eclipse/Parser/raw/RawRecord.cpp
src/opm/parser/eclipse/Parser/raw/StarToken.cpp
src/opm/parser/eclipse/Units/Dimension.cpp
src/opm/parser/eclipse/Units/UnitSystem.cpp
src/opm/common/utility/OpmInputError.cpp
src/opm/common/utility/shmatch.cpp
src/opm/common/OpmLog/OpmLog.cpp
src/opm/common/OpmLog/Logger.cpp
src/opm/common/OpmLog/StreamLog.cpp
@@ -38,9 +35,9 @@ add_executable(genkw ${genkw_SOURCES})
target_link_libraries(genkw ${opm-common_LIBRARIES})
# Generate keyword list
include(src/opm/input/eclipse/share/keywords/keyword_list.cmake)
string(REGEX REPLACE "([^;]+)" "${PROJECT_SOURCE_DIR}/src/opm/input/eclipse/share/keywords/\\1" keyword_files "${keywords}")
configure_file(src/opm/input/eclipse/keyword_list.argv.in keyword_list.argv)
include(src/opm/parser/eclipse/share/keywords/keyword_list.cmake)
string(REGEX REPLACE "([^;]+)" "${PROJECT_SOURCE_DIR}/src/opm/parser/eclipse/share/keywords/\\1" keyword_files "${keywords}")
configure_file(src/opm/parser/eclipse/keyword_list.argv.in keyword_list.argv)
# Generate keyword source
@@ -48,7 +45,7 @@ set( genkw_argv keyword_list.argv
${PROJECT_BINARY_DIR}/tmp_gen/ParserKeywords
${PROJECT_BINARY_DIR}/tmp_gen/ParserInit.cpp
${PROJECT_BINARY_DIR}/tmp_gen/include/
opm/input/eclipse/Parser/ParserKeywords
opm/parser/eclipse/Parser/ParserKeywords
${PROJECT_BINARY_DIR}/tmp_gen/TestKeywords.cpp)
set( _tmp_output
@@ -124,7 +121,7 @@ endif()
add_custom_command( OUTPUT
${_tmp_output}
COMMAND genkw ${genkw_argv}
DEPENDS genkw ${keyword_files} src/opm/input/eclipse/share/keywords/keyword_list.cmake)
DEPENDS genkw ${keyword_files} src/opm/parser/eclipse/share/keywords/keyword_list.cmake)
# To avoid some rebuilds
add_custom_command(OUTPUT

View File

@@ -1,23 +1,3 @@
# opm-common: Tools for Eclipse reservoir simulation files
The Open Porous Media (OPM) software suite provides libraries and
tools for modeling and simulation of porous media processes,
especially for simulating CO2 sequestration and improved and enhanced
oil recovery. The Eclipse file format is widely used in the reservoir
simulation community. This package provides a library containing code
for processing files in Eclipse format, Python binding for accessing
Eclipse files and utility code used by other OPM modules (e.g. CMake
modules used).
License
-------
The library is distributed under the GNU General Public License,
version 3 or later (GPLv3+).
Requirements
-----------
- C++17 compatible compiler
- CMake >= 3.10
- Posix compatibility
# opm-common
Contains common components used throughout all of OPM,
in particular CMake modules for the build system.

View File

@@ -0,0 +1,49 @@
# make targets for boost if find module did not do the job
if(NOT TARGET Boost::system)
add_library(Boost::system UNKNOWN IMPORTED)
set_target_properties(Boost::system PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}"
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}"
IMPORTED_LOCATION "${Boost_SYSTEM_LIBRARY}"
IMPORTED_LOCATION_DEBUG "${Boost_SYSTEM_LIBRARY_DEBUG}"
IMPORTED_LOCATION_RELEASE "${Boost_SYSTEM_LIBRARY_RELEASE}"
)
endif()
if(NOT TARGET Boost::filesystem)
add_library(Boost::filesystem UNKNOWN IMPORTED)
set_target_properties(Boost::filesystem PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}"
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}"
INTERFACE_COMPILE_DEFINITIONS BOOST_FILESYSTEM_VERSION=3
INTERFACE_LINK_LIBRARIES "${boost_system}"
IMPORTED_LOCATION "${Boost_FILESYSTEM_LIBRARY}"
IMPORTED_LOCATION_DEBUG "${Boost_FILESYSTEM_LIBRARY_DEBUG}"
IMPORTED_LOCATION_RELEASE "${Boost_FILESYSTEM_LIBRARY_RELEASE}"
)
endif()
if(NOT TARGET Boost::regex)
add_library(Boost::regex UNKNOWN IMPORTED)
set_target_properties(Boost::regex PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}"
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES "${boost_system}"
IMPORTED_LOCATION "${Boost_REGEX_LIBRARY}"
IMPORTED_LOCATION_DEBUG "${Boost_REGEX_LIBRARY_DEBUG}"
IMPORTED_LOCATION_RELEASE "${Boost_REGEX_LIBRARY_RELEASE}"
)
endif()
if(NOT TARGET Boost::unit_test_framework)
add_library(Boost::unit_test_framework UNKNOWN IMPORTED)
set_target_properties(Boost::unit_test_framework PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}"
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES "${boost_system}"
IMPORTED_LOCATION "${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}"
IMPORTED_LOCATION_DEBUG "${Boost_UNIT_TEST_FRAMEWORK_LIBRARY_DEBUG}"
IMPORTED_LOCATION_RELEASE "${Boost_UNIT_TEST_FRAMEWORK_LIBRARY_RELEASE}"
)
endif()

View File

@@ -1,31 +0,0 @@
# - Find the GNU Multiprecision library.
#
# Will define the following imported target for usage:
# - GMP::gmp Target for linking/compiling with C library
# - GMP::gmpxx Target for linking/compiling with C++ library
find_path(GMP_INCLUDE_DIR gmp.h)
find_library(GMP_LIBRARY gmp)
find_path(GMPXX_INCLUDE_DIR gmpxx.h)
find_library(GMPXX_LIBRARY gmpxx)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GMP
DEFAULT_MSG
GMPXX_LIBRARY GMPXX_INCLUDE_DIR GMP_INCLUDE_DIR GMP_LIBRARY)
if(GMP_FOUND)
if(NOT TARGET GMP::gmp)
add_library(GMP::gmp UNKNOWN IMPORTED GLOBAL)
set_target_properties(GMP::gmp PROPERTIES
IMPORTED_LOCATION ${GMP_LIBRARY}
INTERFACE_INCLUDE_DIRECTORIES ${GMP_INCLUDE_DIR})
endif()
if(NOT TARGET GMP::gmpxx)
add_library(GMP::gmpxx UNKNOWN IMPORTED GLOBAL)
set_target_properties(GMP::gmpxx PROPERTIES
IMPORTED_LOCATION ${GMPXX_LIBRARY}
INTERFACE_INCLUDE_DIRECTORIES ${GMPXX_INCLUDE_DIR}
TARGET_LINK_LIBRARIES GMP::gmp)
endif()
endif()

View File

@@ -8,11 +8,7 @@
# HAVE_METIS - like METIS_FOUND, but for the inclusion in config.h
# METIS_INCLUDE_DIRS - incude paths to use libMETIS
# METIS_LIBRARIES - Link these to use libMETIS
# METIS::METIS - Imported metis target needed for DUNE 2.8.0
# METIS_API_VERSION - The METIS api version scotch is supporting.
set(METIS_API_VERSION 0 CACHE STRING
"METIS API version provided by METIS or scotch-metis library")
set(METIS_SEARCH_PATH "/usr" "/usr/local" "/opt" "/opt/local")
set(METIS_NO_DEFAULT_PATH "")
if(METIS_ROOT)
@@ -39,27 +35,9 @@ find_library(METIS_LIBRARIES
${METIS_NO_DEFAULT_PATH})
set (METIS_FOUND FALSE)
if (METIS_INCLUDE_DIRS OR METIS_LIBRARIES)
set(METIS_FOUND TRUE)
set(HAVE_METIS TRUE)
file(READ "${METIS_INCLUDE_DIRS}/metis.h" metisheader)
string(REGEX MATCH "#define METIS_VER_MAJOR[ ]+([0-9]+)" METIS_MAJOR_VERSION ${metisheader})
if(NOT METIS_API_VERSION AND METIS_MAJOR_VERSION)
# string(REGEX REPLACE ".*#define METIS_VER_MAJOR[ ]+([0-9]+).*" "\\1"
# METIS_MAJOR_VERSION "${metisheader}")
if(METIS_MAJOR_VERSION GREATER_EQUAL 3 AND METIS_MAJOR_VERSION LESS 5)
set(METIS_API_VERSION "3")
else()
set(METIS_API_VERSION "${METIS_MAJOR_VERSION}")
endif()
endif()
add_library(METIS::METIS UNKNOWN IMPORTED)
set_target_properties(METIS::METIS PROPERTIES
IMPORTED_LOCATION ${METIS_LIBRARIES}
INTERFACE_INCLUDE_DIRECTORIES ${METIS_INCLUDE_DIRS}
INTERFACE_COMPILE_DEFINITIONS METIS_API_VERSION=${METIS_API_VERSION})
endif()
# print a message to indicate status of this package

View File

@@ -70,20 +70,6 @@ if(PTSCOTCH_FOUND)
set(PTSCOCH_LINK_FLAGS "${DUNE_MPI_LINK_FLAGS}"
CACHE STRING "PT-Scotch link flags")
set(HAVE_PTSCOTCH 1)
if(NOT TARGET PTScotch::Scotch)
add_library(PTScotch::Scotch UNKNOWN IMPORTED GLOBAL)
set_target_properties(PTScotch::Scotch PROPERTIES
IMPORTED_LOCATION "${SCOTCH_LIBRARY}"
INCLUDE_DIRECTORIES "${PTSCOTCH_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES "${PTSCOTCHERR_LIBRARY}")
endif()
if(NOT TARGET PTScotch::PTScotch)
add_library(PTScotch::PTScotch UNKNOWN IMPORTED GLOBAL)
set_target_properties(PTScotch::PTScotch PROPERTIES
IMPORTED_LOCATION "${PTSCOTCH_LIBRARY}"
INCLUDE_DIRECTORIES "${PTSCOTCH_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES "PTScotch::Scotch;${MPI_DUNE_LIBRARIES}")
endif()
# log result
file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
"Determing location of PT-Scotch succeded:\n"

View File

@@ -102,13 +102,6 @@ if(PARMETIS_FOUND)
"Include directory: ${PARMETIS_INCLUDE_DIRS}\n"
"Library directory: ${PARMETIS_LIBRARIES}\n\n")
if(NOT TARGET ParMETIS::ParMETIS)
add_library(ParMETIS::ParMETIS UNKNOWN IMPORTED GLOBAL)
set_target_properties(ParMETIS::ParMETIS PROPERTIES
IMPORTED_LOCATION ${PARMETIS_LIBRARY}
INCLUDE_DIRECTORIES "${PARMETIS_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES "${METIS_LIBRARIES};${MPI_C_LIBRARIES}")
endif()
endif(PARMETIS_FOUND)
mark_as_advanced(PARMETIS_INCLUDE_DIRS PARMETIS_LIBRARIES HAVE_PARMETIS)

View File

@@ -34,16 +34,11 @@ int main(void){
if (QUADMATH_FOUND)
set(QUADMATH_LIBRARIES "quadmath")
set(HAVE_QUAD "${QUADMATH_FOUND}")
add_library(QuadMath::QuadMath INTERFACE IMPORTED)
set_target_properties(QuadMath::QuadMath PROPERTIES
INTERFACE_LINK_LIBRARIES quadmath
INTERFACE_COMPILE_DEFINITIONS _GLIBCXX_USE_FLOAT128
INTERFACE_COMPILE_OPTIONS $<$<CXX_COMPILER_ID:GNU>:-fext-numeric-literals>)
endif()
endif()
if (USE_QUADMATH AND NOT QUADMATH_FOUND)
message(STATUS "Quadruple precision math support is unavailable! Skipping it.")
message(FATAL_ERROR "Quadruple precision math support was explicitly requested but is unavailable!")
endif()
include(FindPackageHandleStandardArgs)

View File

@@ -259,9 +259,9 @@ foreach (module IN LISTS SuiteSparse_FIND_COMPONENTS)
set (SuiteSparse_FOUND FALSE)
# use empty string instead of zero, so it can be tested with #ifdef
# as well as #if in the source code
set (HAVE_SUITESPARSE_${MODULE}_H "" CACHE STRING "Is ${module} header present?")
set (HAVE_SUITESPARSE_${MODULE}_H "" CACHE INT "Is ${module} header present?")
else (NOT SuiteSparse_${MODULE}_FOUND)
set (HAVE_SUITESPARSE_${MODULE}_H 1 CACHE STRING "Is ${module} header present?")
set (HAVE_SUITESPARSE_${MODULE}_H 1 CACHE INT "Is ${module} header present?")
list (APPEND SuiteSparse_LIBRARIES "${${MODULE}_LIBRARIES}")
list (APPEND SuiteSparse_LINKER_FLAGS "${${MODULE}_LINKER_FLAGS}")
list (APPEND SuiteSparse_INCLUDE_DIRS "${${MODULE}_INCLUDE_DIRS}")
@@ -280,32 +280,6 @@ if (SuiteSparse_LIBRARIES)
list (REVERSE SuiteSparse_LIBRARIES)
endif (SuiteSparse_LIBRARIES)
if(SuiteSparse_FOUND)
if(NOT TARGET SuiteSparse::SuiteSparse)
add_library(SuiteSparse::SuiteSparse INTERFACE IMPORTED GLOBAL)
set_property(TARGET SuiteSparse::SuiteSparse PROPERTY
INTERFACE_INCLUDE_DIRECTORIES ${SuiteSparse_INCLUDE_DIRS})
if(config_LIBRARY)
set_property(TARGET SuiteSparse::SuiteSparse PROPERTY
INTERFACE_LINK_LIBRARIES ${config_LIBRARY})
endif()
endif()
foreach(_module ${SuiteSparse_MODULES})
string (TOUPPER ${_module} _MODULE)
if(SuiteSparse_${_MODULE}_FOUND)
if(NOT TARGET SuiteSparse::${_module})
message(STATUS "Creating target SuitSparse::${_module}")
add_library(SuiteSparse::${_module} UNKNOWN IMPORTED GLOBAL)
set_target_properties(SuiteSparse::${_module} PROPERTIES
IMPORTED_LOCATION ${${_MODULE}_LIBRARY}
INCLUDE_DIRECTORIES ${${_MODULE}_INCLUDE_DIRS}
INTERFACE_LINK_LIBRARIES "${config_LIBRARY}")
target_link_libraries(SuiteSparse::SuiteSparse
INTERFACE SuiteSparse::${_module})
endif()
endif()
endforeach(_module)
endif()
# print a message to indicate status of this package
include (FindPackageHandleStandardArgs)
find_package_handle_standard_args (SuiteSparse

View File

@@ -180,16 +180,6 @@ if(SUPERLU_FOUND)
if (SUPERLU_BLAS_LIBRARY)
list(APPEND SUPERLU_LIBRARIES ${SUPERLU_BLAS_LIBRARY})
endif()
if(NOT TARGET SuperLU::SuperLU)
add_library(SuperLU::SuperLU UNKNOWN IMPORTED GLOBAL)
set_target_properties(SuperLU::SuperLU PROPERTIES
IMPORTED_LOCATION ${SUPERLU_LIBRARY}
INCLUDE_DIRECTORIES ${SUPERLU_INCLUDE_DIRS})
if(SUPERLU_BLAS_LIBRARY)
set_property(TARGET SuperLU::SuperLU PROPERTY
INTERFACE_LINK_LIBRARIES ${SUPERLU_BLAS_LIBRARY})
endif()
endif()
endif()
cmake_pop_check_state()

View File

@@ -1,34 +0,0 @@
# Poor man's FindTBB that will create the CMake targets
# used by DUNE.
# If the TBB version is new enough it will ship its own
# TBBConfig.cmake and we are good
find_package(TBB QUIET CONFIG)
if(NOT TBB_FOUND)
# Fall back to using pkgconfig
find_package(PkgConfig QUIET)
if (PKG_CONFIG_FOUND)
pkg_check_modules(PkgConfigTBB IMPORTED_TARGET GLOBAL tbb QUIET)
if(NOT TARGET PkgConfig::PkgConfigTBB)
# workaround bug in old FindPkgConfig.cmake which adds
# pkgcfg_lib_PkgConfigTBB_atomic-NOTFOUND because it cannot
# find the atomic lib of the compiler (not in platforms default
# library path. It will therefore not create the target and we
# try that manually.
string(REPLACE ";pkgcfg_lib_PkgConfigTBB_atomic-NOTFOUND" "" _find_tbb_libs "${PkgConfigTBB_LINK_LIBRARIES}")
if(_find_tbb_libs)
add_library(PkgConfig::PkgConfigTBB INTERFACE IMPORTED GLOBAL)
set_property(TARGET PkgConfig::PkgConfigTBB PROPERTY
INTERFACE_LINK_LIBRARIES "${_find_tbb_libs}")
endif()
endif()
if(TARGET PkgConfig::PkgConfigTBB)
if(NOT TARGET TBB::tbb)
message(STATUS "Found TBB library using pkg config")
add_library(TBB::tbb ALIAS PkgConfig::PkgConfigTBB)
endif()
endif()
endif(PKG_CONFIG_FOUND)
else()
message(STATUS "Found TBB library using config mode")
endif(NOT TBB_FOUND)

View File

@@ -36,8 +36,8 @@ find_library (CJSON_LIBRARY
${_no_default_path} )
# setup list of all required libraries to link with cjson
set (cjson_INCLUDE_DIRS ${CJSON_INCLUDE_DIR})
set (cjson_LIBRARIES ${CJSON_LIBRARY})
set (CJSON_INCLUDE_DIRS ${CJSON_INCLUDE_DIR})
set (CJSON_LIBRARIES ${CJSON_LIBRARY})
# math library (should exist on all unices; automatically linked on Windows)
if (UNIX)
@@ -72,7 +72,10 @@ endif ()
# if the test program didn't compile, but was required to do so, bail
# out now and display an error; otherwise limp on
find_package_handle_standard_args (cjson
set (CJSON_FIND_REQUIRED ${cjson_FIND_REQUIRED})
set (CJSON_FIND_QUIETLY ${cjson_FIND_QUIETLY})
find_package_handle_standard_args (CJSON
DEFAULT_MSG
cjson_INCLUDE_DIRS cjson_LIBRARIES HAVE_CJSON
CJSON_INCLUDE_DIRS CJSON_LIBRARIES HAVE_CJSON
)
set (cjson_FOUND ${CJSON_FOUND})

View File

@@ -19,9 +19,7 @@ find_opm_package (
"BLAS REQUIRED;
LAPACK REQUIRED;
CxaDemangle;
MPI;
TBB;
GMP
MPI
"
# header to search for
"dune/common/fvector.hh"

View File

@@ -24,8 +24,7 @@ find_opm_package (
dune-alugrid;
ZLIB;
ZOLTAN;
METIS;
Quadmath
METIS
"
# header to search for
"dune/fem/space/shapefunctionset/legendre.hh"

View File

@@ -19,7 +19,7 @@ find_opm_package (
"dune-common REQUIRED;
ParMETIS;
SuperLU;
SuiteSparse COMPONENTS umfpack REQUIRED
SuiteSparse COMPONENTS umfpack
"
# header to search for
"dune/istl/bcrsmatrix.hh"

View File

@@ -1,16 +1,6 @@
# Installs bash tab completion for a product
macro(opm_add_bash_completion binary)
option(USE_BASH_COMPLETIONS_DIR
"Whether to use the new bash completion dir (/usr/share/bash-completion/completions) with load on demand"
OFF)
if(USE_BASH_COMPLETIONS_DIR)
set(_BASH_COMPLETION_FILE ${binary})
set(_BASH_COMPLETION_INSTALL_DIR ${CMAKE_INSTALL_DATAROOTDIR}/bash-completion/completions)
else()
set(_BASH_COMPLETION_FILE ${binary}_bash_completion.sh)
set(_BASH_COMPLETION_INSTALL_DIR ${CMAKE_INSTALL_SYSCONFDIR}/bash_completion.d)
endif()
set(PRODUCT ${binary})
configure_file(${OPM_MACROS_ROOT}/etc/opm_bash_completion.sh.in ${_BASH_COMPLETION_FILE} @ONLY)
install(FILES ${PROJECT_BINARY_DIR}/${_BASH_COMPLETION_FILE} DESTINATION ${_BASH_COMPLETION_INSTALL_DIR})
configure_file(${OPM_MACROS_ROOT}/etc/opm_bash_completion.sh.in ${binary}_bash_completion.sh @ONLY)
install(FILES ${PROJECT_BINARY_DIR}/${binary}_bash_completion.sh DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/bash_completion.d)
endmacro()

View File

@@ -130,18 +130,9 @@ macro (find_and_append_package_to prefix name)
# and the likes which is only done via opm_find_package
if ( (NOT DEFINED ${name}_FOUND AND NOT DEFINED ${NAME}_FOUND )
OR _search_components GREATER -1)
string(REGEX MATCH "(opm)-.*" _is_opm ${name})
string(REGEX MATCH "(opm)-.*" _is_opm ${name})
if(NOT _is_opm)
# When using Boost >= 1.70 and e.g. CMake 3.18 we need to make sure that
# subsequent searches are using config mode too. Otherwise the library
# list will be completely messed up. We use a set Boost_Dir to detect that
# previous searches were done using config mode.
if("${name}" STREQUAL "Boost" AND Boost_DIR)
set(_CONFIG_MODE CONFIG)
else()
set(_CONFIG_MODE "")
endif()
find_package (${name} ${ARGN} ${_CONFIG_MODE})
find_package (${name} ${ARGN})
else()
if(${name}_DIR)
find_package (${name} ${${prefix}_VERSION_MAJOR}.${${prefix}_VERSION_MINOR} ${ARGN} NO_MODULE PATHS ${${name}_DIR} NO_DEFAULT_PATH)

View File

@@ -130,7 +130,7 @@ endif ()
# parallel computing must be explicitly enabled
# This needs to be in OpmInit as prereqs is called before OpmLibMain is included.
option (USE_MPI "Use Message Passing Interface for parallel computing" ON)
option (USE_MPI "Use Message Passing Interface for parallel computing" OFF)
if (NOT USE_MPI)
set (CMAKE_DISABLE_FIND_PACKAGE_MPI TRUE)
endif ()
@@ -138,13 +138,18 @@ endif ()
# Compiler standard version needs to be requested here as prereqs is included
# before OpmLibMain and some tests need/use CXX_STANDARD_VERSION (e.g. pybind11)
# Languages and global compiler settings
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
if(CMAKE_VERSION VERSION_LESS 3.8)
message(WARNING "CMake version does not support c++17, guessing -std=c++17")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
else()
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
endif()
# quadmath must be explicitly enabled
# This needs to be in OpmInit as prereqs is called before OpmLibMain is included.
option (USE_QUADMATH "Search for high precision floating point library (normally not used)" ON)
option (USE_QUADMATH "Use high precision floating point library (slow)" OFF)
if (NOT USE_QUADMATH)
set (CMAKE_DISABLE_FIND_PACKAGE_QuadMath TRUE)
endif ()

View File

@@ -64,7 +64,7 @@ include (UseThreads)
find_threads (${project})
# SuperLU is optional
option (USE_SUPERLU "Use SuperLU direct solvers for AMG (if umfpack is not found)" ON)
option (USE_SUPERLU "Use SuperLU direct solvers" OFF)
# PETSc is optional
option (USE_PETSC "Use PETSc iterative solvers" OFF)

View File

@@ -54,12 +54,6 @@ function (configure_cmake_file name variant version)
foreach (suffix IN LISTS variable_suffices)
set (opm-project_${suffix} "${${name}_${suffix}}")
endforeach (suffix)
if (BUILD_SHARED_LIBS)
# No need to list shared libraries as the linker information is alread
# in the shared lib
string(REGEX REPLACE ";?[^;]*.so" "" opm-project_LIBRARIES "${opm-project_LIBRARIES}")
endif()
set (opm-project_NAME "${${name}_NAME}")
set (opm-project_NAME_UC "${${name}_NAME}")
string(TOUPPER "${opm-project_NAME}" opm-project_NAME_UC)

View File

@@ -338,7 +338,7 @@ macro(opm_add_test TestName)
# has been specified to supervise the test binary, use it else
# run the test binary "naked".
if (CURTEST_DRIVER)
set(CURTEST_COMMAND ${CURTEST_DRIVER} ${CURTEST_DRIVER_ARGS} -e ${CURTEST_EXE_NAME} -- ${CURTEST_TEST_ARGS})
set(CURTEST_COMMAND ${CURTEST_DRIVER} ${CURTEST_DRIVER_ARGS} ${CURTEST_EXE_NAME} ${CURTEST_TEST_ARGS})
else()
set(CURTEST_COMMAND ${PROJECT_BINARY_DIR}/bin/${CURTEST_EXE_NAME})
if (CURTEST_TEST_ARGS)

View File

@@ -1,11 +1,4 @@
# When using Boost >= 1.70 and e.g. CMake 3.18 we need to make sure that
# subsequent searches are using config mode too. Otherwise the library
# list will be completely messed up. We use a set Boost_Dir to detect that
# previous searches were done using config mode.
if(Boost_DIR)
set(_Boost_CONFIG_MODE CONFIG)
endif()
find_package (Boost 1.44.0 COMPONENTS unit_test_framework QUIET ${_Boost_CONFIG_MODE})
find_package (Boost 1.44.0 COMPONENTS unit_test_framework QUIET)
if (Boost_UNIT_TEST_FRAMEWORK_FOUND)
# setup to do a test compile

View File

@@ -63,9 +63,9 @@ if (CXX_COMPAT_GCC)
# use these options for release builds - full optimization
add_options (ALL_LANGUAGES "${_prof_RELEASE}" ${_opt_rel} ${_opt_flags})
option(WITH_NDEBUG "Disable asserts in release mode" OFF)
if(NOT WITH_NDEBUG)
add_options (ALL_LANGUAGES "${_prof_RELEASE}" -UNDEBUG)
option(WITH_NDEBUG "Disable asserts in release mode" ON)
if(WITH_NDEBUG)
add_options (ALL_LANGUAGES "${_prof_RELEASE}" -DNDEBUG)
endif()
else ()

View File

@@ -17,9 +17,6 @@ message("-- Writing version information to local header project-version.h")
string (TIMESTAMP build_timestamp "%Y-%m-%d at %H:%M:%S hrs")
string (TOUPPER "${CMAKE_BUILD_TYPE}" cmake_build_type_upper_)
set(OPM_BINARY_PACKAGE_VERSION "" CACHE STRING
"Version of the binary Linux package built (will be printed in PRT file of flow if not empty)")
string(LENGTH "${OPM_BINARY_PACKAGE_VERSION}" _BINARY_PACKAGE_VERSION_LENGTH)
if (cmake_build_type_upper_ MATCHES DEBUG)
file (WRITE "${PROJECT_BINARY_DIR}/project-version.h"
"#ifndef OPM_GENERATED_OPM_VERSION_HEADER_INCLUDED\n"
@@ -46,30 +43,22 @@ else ()
# system, so there is "no" way we can update the SHA. notice
# that this is a slightly different version of the label than
# above.
if (NOT GIT_FOUND OR NOT EXISTS ${PROJECT_SOURCE_DIR}/.git)
if(_BINARY_PACKAGE_VERSION_LENGTH GREATER 0)
set(_PROJECT_VERSION_HASH "${OPM_BINARY_PACKAGE_VERSION}")
else()
set(_PROJECT_VERSION_HASH "unknown git version")
endif()
file (WRITE "${PROJECT_BINARY_DIR}/project-version.h"
"#ifndef OPM_GENERATED_OPM_VERSION_HEADER_INCLUDED\n"
"#define OPM_GENERATED_OPM_VERSION_HEADER_INCLUDED\n"
"#define PROJECT_VERSION_NAME \"${${project}_LABEL}\"\n"
"#define PROJECT_VERSION_HASH \"${_PROJECT_VERSION_HASH}\"\n"
"#define PROJECT_VERSION \"${${project}_LABEL} (${_PROJECT_VERSION_HASH})\"\n"
"#endif // OPM_GENERATED_OPM_VERSION_HEADER_INCLUDED\n"
)
# Write header file with build timestamp
file (WRITE "${PROJECT_BINARY_DIR}/project-timestamp.h"
if (NOT GIT_FOUND)
file (WRITE "${PROJECT_BINARY_DIR}/project-version.h"
"#ifndef OPM_GENERATED_OPM_VERSION_HEADER_INCLUDED\n"
"#define OPM_GENERATED_OPM_VERSION_HEADER_INCLUDED\n"
"#define PROJECT_VERSION_NAME \"${${project}_LABEL}\"\n"
"#define PROJECT_VERSION_HASH \"unknown git version\"\n"
"#define PROJECT_VERSION \"${${project}_LABEL} (unknown git version)\"\n"
"#endif // OPM_GENERATED_OPM_VERSION_HEADER_INCLUDED\n"
)
# Write header file with build timestamp
file (WRITE "${PROJECT_BINARY_DIR}/project-timestamp.h"
"#ifndef OPM_GENERATED_OPM_TIMESTAMP_HEADER_INCLUDED\n"
"#define OPM_GENERATED_OPM_TIMESTAMP_HEADER_INCLUDED\n")
if (_BINARY_PACKAGE_VERSION_LENGTH EQUAL 0)
file(APPEND "${PROJECT_BINARY_DIR}/project-timestamp.h"
"#define BUILD_TIMESTAMP \"${build_timestamp}\"\n")
endif()
file(APPEND "${PROJECT_BINARY_DIR}/project-timestamp.h"
"#endif // OPM_GENERATED_OPM_TIMESTAMP_HEADER_INCLUDED\n")
"#define OPM_GENERATED_OPM_TIMESTAMP_HEADER_INCLUDED\n"
"#define BUILD_TIMESTAMP \"${build_timestamp}\"\n"
"#endif // OPM_GENERATED_OPM_TIMESTAMP_HEADER_INCLUDED\n"
)
else ()
add_custom_target (update-version ALL
COMMAND ${CMAKE_COMMAND}

View File

@@ -816,7 +816,7 @@ HTML_COLORSTYLE_GAMMA = 80
# page will contain the date and time when the page was generated. Setting
# this to NO can help when comparing the output of multiple runs.
HTML_TIMESTAMP = NO
HTML_TIMESTAMP = YES
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the

View File

@@ -93,16 +93,6 @@ if(NOT @opm-project_NAME@_FOUND)
# this is the contents of config.h as far as our probes can tell:
# Require correct CMake standard. Needed for user modules as
# some software will add incompatible compile switches like
# -std=gnu++11 otherwise when search for (I guess because of
# imported targets using INTERFACE_COMPILE_FEATURES), and will
# break compilation because of missing c++17 features.
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD @CMAKE_CXX_STANDARD@)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
endif()
# The settings in this block do not mix well with the DEST_PREFIX
# setting.
@@ -114,7 +104,6 @@ if(NOT @opm-project_NAME@_FOUND)
@OPM_PROJECT_EXTRA_CODE@
# end extra code
include(OpmPackage)
include(@opm-project_NAME@-prereqs)
endif()
endif()

View File

@@ -9,6 +9,5 @@ Name: @name@
Description: @description@ @major@.@minor@
Version: @major@.@minor@
URL: http://opm-project.org
Libs.private: @libs@
Libs: @target@
Libs: @target@ @libs@
Cflags: @includes@ @defs@

5
debian/changelog vendored Normal file
View File

@@ -0,0 +1,5 @@
opm-common (2021.04-rfinal-1~bionic) bionic; urgency=medium
* New release
-- Arne Morten Kvarving <arne.morten.kvarving@sintef.no> Mon, 19 Oct 2015 10:49:00 +0200

1
debian/compat vendored Normal file
View File

@@ -0,0 +1 @@
9

64
debian/control vendored Normal file
View File

@@ -0,0 +1,64 @@
Source: opm-common
Priority: extra
Maintainer: Arne Morten Kvarving <arne.morten.kvarving@sintef.no>
Build-Depends: build-essential, debhelper (>= 9),
pkg-config, cmake, git, libtool, doxygen, graphviz,
texlive-latex-extra, texlive-latex-recommended,
ghostscript, libboost-system-dev, libboost-test-dev,
zlib1g-dev, libpython3-dev, python3-numpy, python3-distutils,
python3-setuptools, python3-setuptools-scm, python3-pytest-runner,
python3-decorator
Standards-Version: 3.9.2
Section: libs
Homepage: http://opm-project.org
Vcs-Git: git://github.com/OPM/opm-common.git
Vcs-Browser: https://github.com/OPM/opm-common
Package: libopm-common1
Section: libs
Pre-Depends: ${misc:Pre-Depends}
Architecture: any
Multi-Arch: same
Depends: ${shlibs:Depends}, ${misc:Depends}
Provides: libopm-common
Description: OPM common library
The OPM common library contains generic code shared across all OPM modules.
Package: libopm-common1-bin
Section: libs
Pre-Depends: ${misc:Pre-Depends}
Architecture: any
Multi-Arch: same
Depends: ${shlibs:Depends}, ${misc:Depends}
Provides: libopm-common-bin
Description: OPM common binaries
The OPM common binaries.
Package: libopm-common1-dev
Section: libdevel
Architecture: any
Multi-Arch: foreign
Provides: libopm-common-dev
Suggests: libopm-common1-doc
Depends: libopm-common1 (= ${binary:Version})
Description: OPM common library -- development files
The OPM common library contains the shared buildsystem
and helpers shared across all OPM modules.
Package: libopm-common1-doc
Section: doc
Architecture: all
Multi-Arch: foreign
Provides: libopm-common-doc
Description: OPM common library -- documentation
The OPM common library contains the shared buildsystem
and helpers shared across all OPM modules.
Package: python3-opm-common
Section: libs
Pre-Depends: ${misc:Pre-Depends}
Architecture: any
Multi-Arch: same
Depends: ${shlibs:Depends}, ${misc:Depends}, libopm-common1, python3-numpy, python3-decorator
Description: OPM common python bindings
Python package for opm-common

13
debian/copyright vendored Normal file
View File

@@ -0,0 +1,13 @@
License: GPL-3+
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>

2
debian/docs vendored Normal file
View File

@@ -0,0 +1,2 @@
LICENSE
README.md

1
debian/libopm-common1-bin.install vendored Normal file
View File

@@ -0,0 +1 @@
usr/bin/*

6
debian/libopm-common1-dev.install vendored Normal file
View File

@@ -0,0 +1,6 @@
usr/include/*
usr/lib/dunecontrol/*
usr/lib/*/pkgconfig/*
usr/share/cmake/*
usr/share/opm/*
usr/lib/*/lib*.so

1
debian/libopm-common1-doc.install vendored Normal file
View File

@@ -0,0 +1 @@
usr/share/doc/*

1
debian/libopm-common1.install vendored Normal file
View File

@@ -0,0 +1 @@
usr/lib/*/lib*.so.*

1
debian/python3-opm-common.install vendored Normal file
View File

@@ -0,0 +1 @@
usr/lib/python*/*

29
debian/rules vendored Executable file
View File

@@ -0,0 +1,29 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Sample debian/rules that uses debhelper.
# This file was originally written by Joey Hess and Craig Small.
# As a special exception, when this file is copied by dh-make into a
# dh-make output file, you may use that output file without restriction.
# This special exception was added by Craig Small in version 0.37 of dh-make.
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
%:
dh $@ --parallel
override_dh_auto_clean:
dh_auto_clean --buildsystem=cmake
override_dh_auto_build:
dh_auto_build --buildsystem=cmake
# consider using -DUSE_VERSIONED_DIR=ON if backporting
override_dh_auto_configure:
dh_auto_configure --buildsystem=cmake -- -DCMAKE_BUILD_TYPE=RelWithDebInfo -DSTRIP_DEBUGGING_SYMBOLS=ON -DBUILD_SHARED_LIBS=1 -DCMAKE_INSTALL_DOCDIR=share/doc/libopm-common1 -DWHOLE_PROG_OPTIM=ON -DUSE_RUNPATH=OFF -DWITH_NATIVE=OFF -DOPM_ENABLE_PYTHON=1 -DOPM_INSTALL_PYTHON=1 -DPYTHON_EXECUTABLE=/usr/bin/python3 -DOPM_ENABLE_EMBEDDED_PYTHON=1
override_dh_auto_install:
dh_auto_install -- install-html
override_dh_installdocs:
dh_installdocs --link-doc=libopm-common1

1
debian/source/format vendored Normal file
View File

@@ -0,0 +1 @@
1.0

View File

@@ -153,7 +153,7 @@ we currently have five different categories:
To infer the number of records in the keyword based on an
internal calculation is not supported, hence for these keywords
size is given as unknown, and the keywords are terminated when the
size is given as unkown, and the keywords are terminated when the
next valid keyword is found:
{"name" : "VFPPROD" , "size" : "UNKNOWN", ....
@@ -297,7 +297,7 @@ composite units based on the dimension of the composite quantity. As a
consequence the list of dimensions supported by the parser is long,
and growing. The current list can be found in the source file:
opm/input/eclipse/Units/UnitSystem.cpp
opm/parser/eclipse/Units/UnitSystem.cpp

View File

@@ -1,4 +1,5 @@
.TH COMPAREECL "1" "April 2022" "compareECL 2022.04" "User Commands"
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.8.
.TH COMPAREECL "1" "April 2021" "compareECL 2020.04" "User Commands"
.SH NAME
compareECL \- Comparator for Eclipse files
.SH DESCRIPTION
@@ -10,54 +11,55 @@ The program takes four arguments:
3. Absolute tolerance
4. Relative tolerance (between 0 and 1)
.PP
.SH OPTIONS
.HP
In addition, the program takes these options (which must be given before the arguments):
.PP
\fB\-a\fR Run a full analysis of errors.
.TP
\fB\-h\fR Print help and exit.
.TP
\fB\-d\fR Use report steps only when comparing results from summary files.
.TP
\fB\-i\fR Execute integration test (regression test is default).
.IP
The integration test compares SGAS, SWAT and PRESSURE in unified restart files, and WOPR, WGPR, WWPR and WBHP (all wells) in summary file.
.PP
\fB\-k\fR Specify specific keyword to compare (capitalized), for examples \fB\-k\fR PRESSURE or \fB\-k\fR WOPR:A\-1H
.TP
\fB\-l\fR Only do comparison for the last Report Step. This option is only valid for restart files.
.TP
\fB\-n\fR Do not throw on errors.
.TP
\fB\-p\fR Print keywords in both cases and exit.
.TP
\fB\-r\fR compare a spesific report time step number in a restart file.
.TP
\fB\-t\fR Specify ECLIPSE filetype to compare, (default behaviour is that all files are compared if found). Different possible arguments are:
.IP
.TP
\fB\-t\fR UNRST
Compare two unified restart files (.UNRST). This the default value, so it is the same as not passing option \fB\-t\fR.
.IP
.TP
\fB\-t\fR EGRID
Compare two EGrid files (.EGRID).
.IP
.TP
\fB\-t\fR INIT
Compare two initial files (.INIT).
.IP
.TP
\fB\-t\fR RFT
Compare two RFT files (.RFT).
.IP
.TP
\fB\-t\fR SMRY
Compare two cases consistent of (unified) summary files.
.PP
\fB\-x\fR Allow extra keywords in case number 2. These additional keywords (not found in case number1) will be ignored in the comparison.
.PP
.SH Example usage of the program:
Example usage of the program:
.PP
compareECL \fB\-k\fR PRESSURE <path to first casefile> <path to second casefile> 1e\-3 1e\-5
compareECL \fB\-t\fR INIT \fB\-k\fR PORO <path to first casefile> <path to second casefile> 1e\-3 1e\-5
compareECL \fB\-i\fR <path to first casefile> <path to second casefile> 0.01 1e\-6
.PP
Exceptions are thrown (and hence program exits) when deviations are larger than the specified tolerances, or when the number of cells does not match \fB\-\-\fR either in the grid file or for a specific keyword. Information about the keyword, keyword occurrence (zero based) and cell coordinate is printed when an exception is thrown. For more information about how the cases are compared, see the documentation of the EclFilesComparator class.
.SH "SEE ALSO"
The full documentation for
.B compareECL
is maintained as a Texinfo manual. If the
.B info
and
.B compareECL
programs are properly installed at your site, the command
.IP
.B info compareECL
.PP
should give you access to the complete manual.

View File

@@ -1,4 +1,5 @@
.TH CONVERTECL "1" "April 2022" "convertECL 2022.04" "User Commands"
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.8.
.TH CONVERTECL "1" "April 2021" "convertECL 2020.04" "User Commands"
.SH NAME
convertECL \- Converter for Eclipse files (binary <-> formatted format)
.SH DESCRIPTION
@@ -6,13 +7,18 @@ convertECL needs one argument which is the input file to be converted. If this i
.PP
In addition, the program takes these options (which must be given before the arguments):
.PP
.SH OPTIONS
.HP
\fB\-h\fR Print help and exit.
.TP
\fB\-l\fR list report step numbers in the selected restart file.
.TP
\fB\-i\fR Enforce IX standard on output file.
.TP
\fB\-r\fR extract and convert a spesific report time step number from a unified restart file.
.SH "SEE ALSO"
The full documentation for
.B convertECL
is maintained as a Texinfo manual. If the
.B info
and
.B convertECL
programs are properly installed at your site, the command
.IP
.B info convertECL
.PP
should give you access to the complete manual.

View File

@@ -1,4 +1,5 @@
.TH OPMHASH "1" "April 2022" "opmhash 2022.04" "User Commands"
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.8.
.TH OPMHASH "1" "April 2021" "opmhash 2020.04" "User Commands"
.SH NAME
opmhash \- Hasher for summary keywords in Eclipse files
.SH DESCRIPTION
@@ -39,3 +40,15 @@ depends on the keyword order.
\fB\-l\fR : Add filename and linenumber information to each keyword.
.HP
\fB\-s\fR : Short form \- only print the hash of the complete deck.
.SH "SEE ALSO"
The full documentation for
.B opmhash
is maintained as a Texinfo manual. If the
.B info
and
.B opmhash
programs are properly installed at your site, the command
.IP
.B info opmhash
.PP
should give you access to the complete manual.

View File

@@ -1,7 +1,10 @@
.TH OPMPACK "1" "April 2022" "opmpack 2022.04" "User Commands"
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.8.
.TH OPMPACK "1" "April 2021" "opmpack 2020.04" "User Commands"
.SH NAME
opmpack \- Validator and printer of deck in Eclipse files without comments
.SH DESCRIPTION
opmpack: invalid option \fB\-\-\fR 'h'
.PP
The opmpack program will load a deck, resolve all include
files and then print it out again on stdout. All comments
will be stripped and the value types will be validated.
@@ -25,4 +28,15 @@ opmpack \fB\-o\fR NEW_CASE.DATA path/to/MY_CASE.DATA
As an alternative to the \fB\-o\fR option you can use \fB\-c\fR; that is equivalent to \fB\-o\fR \-
but restart and import files referred to in the deck are also copied. The \fB\-o\fR and
\fB\-c\fR options are mutually exclusive.
.SH "SEE ALSO"
The full documentation for
.B opmpack
is maintained as a Texinfo manual. If the
.B info
and
.B opmpack
programs are properly installed at your site, the command
.IP
.B info opmpack
.PP
should give you access to the complete manual.

View File

@@ -1,180 +0,0 @@
.TH RST_DECK: "1" "April 2022" "rst_deck 2022.04" "User Commands"
.SH NAME
rst_deck \- Convert simulation deck to a deck ready for restart
.SH DESCRIPTION
The rst_deck program will load a simulation deck and parameters for a restart
and reformat the deck to become a restart deck. Before the updated deck is
output the program will update the SOLUTION and SCHEDULE sections. All keywords
from the SOLUTION section will be cleared out(1) and a RESTART keyword will be
inserted. In the SCHEDULE section the program can either remove all keywords up
until the restart date, or alternatively insert SKIPREST immediately following
the SCHEDULE keyword.
.PP
When creating the updated restart deck the program can either link to unmodified
include files with INCLUDE statements, create a copy of deck structure in an
alternative location or create one large file with all keywords in the same
file. Apart from the alterations to support restart the output deck will be
equivalent to the input deck, but formatting is not retained and comments have
been stripped away.
.PP
Arguments:
.PP
1. The data file we are starting with.
.PP
2. The restart source; this can either be a basename with an optional path
.IP
prefix and a :N to restart from step N; alternatively you can point to an
existing restart file. If you point to an existing restart file the input
will be validated in several ways:
.IP
a) Unified/multiple files will be checked against the UNIFIN setting of
.IP
the deck.
.IP
b) Formatted/unformatted will be checked against the FMTIn setting of the
.IP
deck.
.IP
c) If a single file like \fI\,/path/to/case/HISTORY.X0067\/\fP is given as argument the
.IP
:N notation to denote report step should not be used.
.IP
If the restart argument is given as the path to an existing file the content
of the RESTART keyword will be updated to contain the correct path from the
location of the restart deck to the location of the restart file. This path
awareness will be fooled if the restart deck is redirected from stdout to a
path different from cwd. If the restart argument is given as an absolute
filename the RESTART keyword will have an absolute path, if the restart
argument is a relative path the RESTART keyword will get a relative path \-
although an absolute path will be used if the restart file and the output
deck have different roots. If the restart argument is given as a string not
pointing to an existing file it will be inserted verbatim in the restart
deck.
.IP
A restart step value of 0 is interpreted as a dry run \- a deck which has not
been set up for restart will be written out.
.PP
3. Basename of the restart deck we create, can optionally contain a path prefix;
.IP
the path will be created if it does not already exist. This argument is
optional, if it is not provided the program will dump a restart deck on
stdout. If the argument corresponds to an existing directory the restart case
will get the same name as the base case.
.SH OPTIONS
\fB\-s\fR: Manipulate the SCHEDULE section by inserting a SKIPREST keyword immediately
.IP
following the SCHEDULE keyword. If the \fB\-s\fR option is not used the SCHEDULE
section will be modified by removing all keywords until we reach the restart
date.
.PP
\fB\-m\fR: [share|inline|copy] The restart deck can reuse the unmodified include files
.IP
from the base case, this is mode 'share' and is the default. With mode
\&'inline' the restart deck will be one long file and with mode 'copy' the
file structure of the base case will be retained. The default if no \fB\-m\fR
option is given is the 'share' mode.
.IP
In the case of 'share' and 'copy' the correct path to include files will be
negotiated based on the path given to the output case in the third argument.
If the restart deck is passed to stdout the include files will be resolved
based on output in cwd.
.PP
Example:
.IP
rst_deck \fI\,/path/to/history/HISTORY.DATA\/\fP rst/HISTORY:30 \fI\,/path/to/rst/RESTART\/\fP \fB\-s\fR
.PP
1: The program has a compiled list of keywords which will be retained in the
.IP
SOLUTION section. The current value of that list is: RPTRST
.PP
\&../../opm\-parallel/bin/rst_deck: invalid option \fB\-\-\fR 'v'
\&../../opm\-parallel/bin/rst_deck: invalid option \fB\-\-\fR 'e'
\&../../opm\-parallel/bin/rst_deck: invalid option \fB\-\-\fR 'r'
\&../../opm\-parallel/bin/rst_deck: invalid option \fB\-\-\fR 'i'
\&../../opm\-parallel/bin/rst_deck: invalid option \fB\-\-\fR 'o'
\&../../opm\-parallel/bin/rst_deck: invalid option \fB\-\-\fR 'n'
.PP
The rst_deck program will load a simulation deck and parameters for a restart
and reformat the deck to become a restart deck. Before the updated deck is
output the program will update the SOLUTION and SCHEDULE sections. All keywords
from the SOLUTION section will be cleared out(1) and a RESTART keyword will be
inserted. In the SCHEDULE section the program can either remove all keywords up
until the restart date, or alternatively insert SKIPREST immediately following
the SCHEDULE keyword.
.PP
When creating the updated restart deck the program can either link to unmodified
include files with INCLUDE statements, create a copy of deck structure in an
alternative location or create one large file with all keywords in the same
file. Apart from the alterations to support restart the output deck will be
equivalent to the input deck, but formatting is not retained and comments have
been stripped away.
.PP
Arguments:
.PP
1. The data file we are starting with.
.PP
2. The restart source; this can either be a basename with an optional path
.IP
prefix and a :N to restart from step N; alternatively you can point to an
existing restart file. If you point to an existing restart file the input
will be validated in several ways:
.IP
a) Unified/multiple files will be checked against the UNIFIN setting of
.IP
the deck.
.IP
b) Formatted/unformatted will be checked against the FMTIn setting of the
.IP
deck.
.IP
c) If a single file like \fI\,/path/to/case/HISTORY.X0067\/\fP is given as argument the
.IP
:N notation to denote report step should not be used.
.IP
If the restart argument is given as the path to an existing file the content
of the RESTART keyword will be updated to contain the correct path from the
location of the restart deck to the location of the restart file. This path
awareness will be fooled if the restart deck is redirected from stdout to a
path different from cwd. If the restart argument is given as an absolute
filename the RESTART keyword will have an absolute path, if the restart
argument is a relative path the RESTART keyword will get a relative path \-
although an absolute path will be used if the restart file and the output
deck have different roots. If the restart argument is given as a string not
pointing to an existing file it will be inserted verbatim in the restart
deck.
.IP
A restart step value of 0 is interpreted as a dry run \- a deck which has not
been set up for restart will be written out.
.PP
3. Basename of the restart deck we create, can optionally contain a path prefix;
.IP
the path will be created if it does not already exist. This argument is
optional, if it is not provided the program will dump a restart deck on
stdout. If the argument corresponds to an existing directory the restart case
will get the same name as the base case.
.PP
\fB\-s\fR: Manipulate the SCHEDULE section by inserting a SKIPREST keyword immediately
.IP
following the SCHEDULE keyword. If the \fB\-s\fR option is not used the SCHEDULE
section will be modified by removing all keywords until we reach the restart
date.
.PP
\fB\-m\fR: [share|inline|copy] The restart deck can reuse the unmodified include files
.IP
from the base case, this is mode 'share' and is the default. With mode
\&'inline' the restart deck will be one long file and with mode 'copy' the
file structure of the base case will be retained. The default if no \fB\-m\fR
option is given is the 'share' mode.
.IP
In the case of 'share' and 'copy' the correct path to include files will be
negotiated based on the path given to the output case in the third argument.
If the restart deck is passed to stdout the include files will be resolved
based on output in cwd.
.PP
Example:
.IP
rst_deck \fI\,/path/to/history/HISTORY.DATA\/\fP rst/HISTORY:30 \fI\,/path/to/rst/RESTART\/\fP \fB\-s\fR
.PP
1: The program has a compiled list of keywords which will be retained in the
.IP
SOLUTION section. The current value of that list is: RPTRST

View File

@@ -1,4 +1,5 @@
.TH SUMMARY "1" "April 2022" "summary 2022.04" "User Commands"
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.8.
.TH SUMMARY "1" "April 2021" "summary 2020.04" "User Commands"
.SH NAME
summary \- Printer for summary keys in Eclipse summary files
.SH DESCRIPTION
@@ -6,10 +7,18 @@ summary needs a minimum of two arguments. First is smspec filename and then list
.PP
In addition, the program takes these options (which must be given before the arguments):
.PP
.SH OPTIONS
\fB\-h\fR Print help and exit.
.TP
\fB\-l\fR list all summary vectors.
.TP
\fB\-r\fR extract data only for report steps.
.SH "SEE ALSO"
The full documentation for
.B summary
is maintained as a Texinfo manual. If the
.B info
and
.B summary
programs are properly installed at your site, the command
.IP
.B info summary
.PP
should give you access to the complete manual.

View File

@@ -1,27 +0,0 @@
\documentclass[a4paper,11pt]{book}
\usepackage{verbatim}
\newcommand{\kw}[1]{\texttt{#1}}
\newcommand{\inlinecode}[1]{\texttt{#1}}
\newcommand{\flow}[0]{\texttt{flow}}
\newcommand{\eclipse}[0]{\texttt{Eclipse}}
\newcommand{\path}[1]{\texttt{#1}}
\newenvironment{deck}[0]{\verbatim}{\endverbatim}
\newenvironment{code}[0]{\verbatim}{\endverbatim}
\begin{document}
\begin{titlepage}
\begin{center}
\Huge
\textbf{Technical manual}
\end{center}
\end{titlepage}
\tableofcontents
\input{udq_actionx/udq}
\input{udq_actionx/actionx}
\input{udq_actionx/pyaction}
\end{document}

View File

@@ -1,437 +0,0 @@
\newcommand{\actionx}{\kw{ACTIONX}}
\chapter{Programming in the deck: \actionx}
\label{actionx}
The \actionx{} keyword is the most direct way to \emph{program} in the deck. The
\actionx{} functionality consist of the \actionx{} keyword itself, with some
metadata and a condition and then a list of keywords which are injected into the
in-memory representation of the \kw{SCHEDULE} section at the point in time where
the condition evaluates to true. The \actionx{} statement is evaluated at the
end of every timestep, and if it evaluates to true the new keywords should take
effect immediately. The \actionx{} conditions are less sophisticated than the
expressions used in \udq{}, this implies that a common pattern is to make
involved calculations as \udq{} expressions, and then use a simple test as
\actionx{} condition.
The \actionx{} keyword is also documented in section 12.3.6 in the \flow{}
reference manual.
\section{Structure of the \actionx{} keyword}
\label{actionx_structure}
The \actionx{} keyword itself consist of multiple records. The first record is
metadata with name of the action, the number of times the action can be
triggered and the minimum time elapsed before an action is eligible for a second
run. The subsequent records are \emph{conditions}, all the conditions are of the
same form
\begin{code}
lhs comparison rhs
\end{code}
and subsequent conditions are combined with \inlinecode{AND} or \inlinecode{OR}.
The \inlinecode{lhs} is a field, well or group quantity, in addition you can use
time variables \kw{DAY}, \kw{MNTH} and \kw{YEAR} as left hand
side\footnote{\eclipse{} supports a wider list of summary variables like region,
block and aquifer quantities on both left and right hand side.}. As with the
\udq{} variables the well and group variables are \emph{sets}, and the
evaluation status is maintained individually for each well and group.
The comparison is one of the ordinary mathematical comparison operators
\inlinecode{>,<,=,!=, <=} and \inlinecode{>=}. Numerical comparisons are done
with the corresponding plain C++ operators, this is in contrast to the \udq{}
implementation where an epsilon defined in \kw{UDQPARAMS} is used in floating
point comparisons.
The \inlinecode{rhs} is a numerical scalar, or a field, well or group quantity.
If your \inlinecode{rhs} is a well or group quantity the \inlinecode{lhs} and
\inlinecode{rhs} must be of the same type. If you use the symbol \kw{MNTH} as
\inlinecode{lhs} you can compare with named months, i.e. the following will
trigger on leap days
\begin{deck}
ACTIONX
LEAP 1000 /
MNTH=FEB AND /
DAY=29 /
/
...
...
ENDACTIO
\end{deck}
When there is a well/group quantity as \inlinecode{lhs} the evaluation status is
maintained individually for each well/group. The complete condition evaluates to
true if \emph{any} of the wells/groups satisfy the condition. In the case of
wells the wells matching the condition can subsequently be accessed with
wellname '?' in the \kw{ACTIONX} keywords, this is a quite common pattern to
e.g. close the well with highest watercut.
If there are more conditions they must be joined with a trailing \kw{AND} or
\kw{OR}, furthermore conditions can be grouped with paranthesis. The \actionx{}
expressions can only contain the four arithmetic operators $+,-,*,/$ and not
mathematical functions like $\log()$, for more advanced expressions the natural
approach is to first define a \udq{} and then use the \udq{} symbol in the
\actionx{}, this is illustrated in section \ref{uda}. When multiple conditions
involving the same well set are evaluated, the list of matching wells available
in '?' will contain all the wells from the final condition, i.e. for
\begin{code}
WWCT = {"OP1": 0.25, "OP2": 0.50, "OP3": 0.75}
\end{code}
and the action
\begin{deck}
ACTIONX
WWCT /
WWCT > 0.33 AND /
WWCT < 0.66 /
/
...
ENDACTIO
\end{deck}
the set of wells available for further use in '?' are \emph{all} the wells
matching the condition \inlinecode{WWCT < 0.66} i.e. OP1 and OP2 and \emph{not}
the wells matching the combined expression \inlinecode{0.33 < WWCT < 0.66}. In
order to select wells in a range as attempted here you will have to create an
indicator variable with \udq{} first and then select based on that indicator -
e.g. something like
\begin{deck}
UDQ
DEFINE WUCTR (WWCT < 0.66) * (WWCT > 0.33) /
/
ACTIONX
WUCTR /
WUCTR = 1 /
/
...
ENDACTIO
\end{deck}
The \actionx{} implementation is located in
\path{opm/input/eclipse/Schedule/Action} and all the classes are in namespace
\inlinecode{Action::}. As with the \udq{} the input parser needs some special
case to handle '/' and '*' as division operator and multiplier respectively, but
that is the only code shared between the \udq{} and the \actionx{}
implementation\footnote{It might be possible to share more code between the two,
in particular both have an internal recursive descent parser, but both \udq{}
and \actionx{} have so much ``personality'' that at least initially separate
implementations was the simplest.}.
The condition part of the \actionx{} keyword is internalized while the
\kw{SCHEDULE} section is parsed, the final product is maintained in a class
\inlinecode{Action::ActionX} which has a \inlinecode{eval()} method waiting to
be called. The keywords in the \actionx{} block are stored in the
\inlinecode{Action::ActionX} keyword for future use. All of the \actionx{}
keywords are stored in a container \inlinecode{Action::Actions} which will
eventually manage the book keeping of which actions are eligible for evaluation.
\section{The structure of the \inlinecode{Schedule} implementation}
\label{schedule_design}
\flow{} internalizes all keywords from the input deck and passes fully baked
datastructures to the simulator, whereas our impression is that \eclipse{} works
more like a reservoir model interepreter, executing one keywords at a time.
Mostly the \flow{} approach has worked out well, however for the \actionx{}
functionality the difference in execution model is quite acute, and the nature
of the \actionx{} keyword has had quite strong influence on the final Schedule
implementation. Although not required for use of \actionx{} it is valuable to
understand how the \actionx{} functionality has influenced the design of the
Schedule class, that way you will hopefully better understand problems or bugs
which might arise in the future.
At the very first pass the \kw{SCHEDULE} section is split in \emph{blocks}, with
one block for each report step. The blocks are implemented with the class
\inlinecode{ScheduleBlock}. Each block has a starting time and a list of
keywords, the keywords are maintained in the input format
\inlinecode{DeckKeyword}. Then the entire \kw{SCHEDULE} section is internalized
in the class \inlinecode{ScheduleDeck} which essentially contains a list of
\inlinecode{ScheduleBlock} instances. Consider the \kw{SCHEDULE} section
\begin{deck}
START
1 'JAN' 2020 /
...
...
SCHEDULE
WELSPECS
'PROD' 'G1' 10 10 8400 'OIL' /
'INJ' 'G1' 1 1 8335 'GAS' /
/
COMPDAT
'PROD' 10 10 3 3 'OPEN' 1* 1* 0.5 /
'INJ' 1 1 1 1 'OPEN' 1* 1* 0.5 /
/
-- End of block 0
DATES
1 'FEB' 2020 /
/
WCONPROD
'PROD' 'OPEN' 'ORAT' 20000 4* 1000 /
/
WCONINJE
'INJ' 'GAS' 'OPEN' 'RATE' 100000 1* 9014 /
/
-- End of block 1
DATES
1 'MAR' 2020 /
/
-- End of block 2
END
\end{deck}
When this is internalized we get a \inlinecode{ScheduleDeck} instance with three
\inlinecode{ScheduleBlock} values:
\begin{code}
ScheduleDeck sched_deck = [
ScheduleBlock {
start = "2020-01-01",
keywords = ["WELSPECS","COMPDAT"]
},
ScheduleBlock {
start = "2020-02-01",
keywords = ["WCONPROD","WCONINJE"]
},
ScheduleBlock {
start = "2020-03-01",
keywords = []
}
]
\end{code}
The \inlinecode{Schedule} class has a \inlinecode{ScheduleDeck} member. The
processed content of the \inlinecode{Schedule} class is managed in vector of
\inlinecode{ScheduleState} instances, where one \inlinecode{ScheduleState}
represents the complete dynamic input state at a particular report step. The
processed \kw{SCHEDULE} code is created with the method
\begin{code}
Schedule::iterateScheduleSection().
\end{code}
When \inlinecode{Schedule::iterateScheduleSection(report\_step)} is called it
starts by clearing the vector of \inlinecode{ScheduleState} instances from
\inlinecode{report\_step} to the end of the simulation, and then recreates those
by treating the \inlinecode{ScheduleBlock}. The advantage of this approach is
that the \inlinecode{Schedule::iterateScheduleSection(report\_step)} method is
idempotent - it can be called repeatedly, from an arbitrary point in the
timeseries.
The implementation of the \actionx{} functionality is just to append the
\actionx{} keywords in the \inlinecode{ScheduleBlock} instance corresponding to
the current report step and then rerun the
\inlinecode{Schedule::iterateScheduleSection()} from this report step.
\section{Forward references of wells and groups}
When a well or group is defined as an \actionx{} keyword and then
unconditionally referenced in the deck we get a challenge at the first pass
through the \kw{SCHEDULE} section. In the example below a new well \kw{W1} is
defined with the \kw{WELSPECS} keyword when the action \inlinecode{NEW\_WELL}
evaluates to true. At 1.st of January 2025 the well \kw{W1} is opened with the
\kw{WCONPROD} keyword. The engineer making this model assumes that the
\inlinecode{NEW\_WELL} action will evaluate to true sometime before 1.st of
January 2025, and thereby ensure that the well is fully defined when it is
eventually opened with \kw{WCONPROD}:
\begin{deck}
ACTIONX
'NEW_WELL/
WWCT OPX > 0.75 /
/
WELSPECS
'W1' 'OP' 1 1 3.33 'OIL' 7*/
/
ENDACTIO
TSTEP
10*30 /
DATES
1 'JAN' 2025 /
/
WCONPROD
'W1' 'OPEN' 'ORAT' 0.000 0.000 0.000 5* /
/
TSTEP
10*30 /
\end{deck}
For \flow{} this creates problems because the entire \kw{SCHEDULE} section is
parsed when the simulator starts, and at first pass the well \kw{W1} is unknown
in the \kw{WCONPROD} keyword. This is ``solved'' in the following way:
\begin{enumerate}
\item At first pass we inspect the keywords inside the \actionx{} block and if
we discover \kw{WELSPECS} we store the name of the well which will be defined
at a later stage through \actionx{}.
\item When we parse further on as part of the first pass and said well is
referenced e.g. in a \kw{WCONPROD} keyword, we verify that the well will
eventually appear via \actionx{} - we issue a warning and ignore the well in
the \kw{WCONPROD} keyword\footnote{If the well is not registered as ``will
appear through \actionx{}'' there will be a runtime error with unknown well
name when parsing \kw{WCONPROD}.}.
\item When the \actionx{} evaluates to true the well will be properly defined,
and when reiterating over the Schedule keywords the \kw{WCONPROD} keyword will
now be properly internalized. If the \actionx{} never evaluates to true the
\kw{WCONPROD} keyword will never be applied, and the warning from point 2 will
be the only trace of this well.
\end{enumerate}
It should be mentioned that the functionality with forward referencing of well
names is quite new\footnote{In January 2022}, there might be well keywords in
the \kw{SCHEDULE} section where the implementation is not yet prepared for this.
Furthermore the forward referencing is not at all implemented for groups. The
relevant data structure is the member \inlinecode{Action::WGNames
action\_wgnames} in the \inlinecode{Schedule} class.
\section{To enable a new keyword for \actionx}
The keywords must be explicitly enabled to be available in an \actionx{} block,
and enabling a new keyword requires recompiling \flow{}. The keywords available
as \actionx{} keywords are listed in the static method
\inlinecode{ActionX::valid\_keyword()} in
\path{opm/input/eclipse/Schedule/Action/ActionX.cpp}. In principle it should
just be to add the keyword to the \inlinecode{ActionX::valid\_keyword()} method
and rebuild \flow{}, but experience has unfortunately shown that problems of
various kinds have had a tendency to pop up when new keywords are tried out as
\actionx{} keywords. Most commonly the problems have been in the interaction
between the \inlinecode{Schedule} class in opm-common and the simulator - things
have a tendency to go out of sync.
\section{Running \actionx{} during simulation}
The first part of the \actionx{} treatment is parsing the keyword and conditions
and assemble a syntax tree which can be used to evaluate the conditions. This
parsing takes place when the \kw{SCHEDULE} section is parsed for the first time.
This takes place fully within the realms of the opm-common codebase, and is
quite mature.
When the simulation actually runs the \actionx{} behavior consists of three
distinct parts, taking place in the simulator, in opm-common and again in the
simulator. The simulator will manage an instance of \inlinecode{Action::State}
which will hold on to the time of last run and the latest results for the
various actions.
\subsection{Prepare and evaluate}
As with \udq{} the \inlinecode{SummaryState} instance is the most important
variable to provide context to the \actionx{} evaluation, i.e. the
\inlinecode{SummaryState} variable must be evaluated before \actionx{}. In
addition the \udq{} variables must be evaluated and available in the
\inlinecode{SummaryState} instance before we invoke the \actionx{}
functionality.
The evaluation of actions is called from the method \inlinecode{applyActions()}
in \path{eclproblem.hh}. The method will evaluate which actions are eligible for
running by inspecting the \inlinecode{Action::Actions} variable and call the
\inlinecode{ActionX::eval()} method.
\subsection{Recreate \inlinecode{Schedule}}
When an \actionx{} has evaluated to \inlinecode{true} the simulator will call
into the opm-common method \inlinecode{Schedule::applyAction(report\_step)}.
That function will add the keywords from the \actionx{} keyword to the
\inlinecode{ScheduleBlock} for the correct report step, and then reiterate
through the \kw{SCHEDULE} section to the end of the simulation.
This reiterate process will recreate all internal members in the
\inlinecode{Schedule} class, i.e. if the simulator was holding on to a reference
to an internal \inlinecode{Schedule} datastructure that will be invalidated.
While recreating the \inlinecode{Schedule} instance there is some book keeping
as to which datastructures need to be recalculated in the simulator as a
consequence of the \actionx{}. That information is maintained in the data
structure \inlinecode{Action::SimulatorUpdate} which is returned back to the
simulator.
\subsection{Updating simulator data structures}
The \actionx{} implementation is in the module opm-common, whereas when the
simulation is proceeding it is the simulator code which is clearly in control.
If an action has evaluated to true and new keywords are injected in the Schedule
object the complete simulator state is updated. This is complex, and many of the
bugs in \actionx{} functionality have been in the interaction between the
simulator and opm-common, in particular when an action has evaluated to true.
An assumption permeating the simulator code is that changes to the well and
group configuration only take place at report steps, and in between those the
simulator ``owns'' the well and group data. Unfortunately this is no longer the
case when \actionx{} is active, and depending on the keywords in the \actionx{}
block we need to update the simulator data structures after \actionx{} has been
evaluated to true. Some details of what is currently updated is decsribed below.
This update mechanism will probably need to be continously updated in the
future.
The simulator code makes \emph{copies} of many of the objects like wells and
connections from the \inlinecode{Schedule} class, and also assembles many
simulator specific data structures which to a large extent consist of extracts
of information from the internals of the \inlinecode{Schedule} object. Some of
the interaction between the simulator and the input layer could probably be
simplified if the simulator would call the \inlinecode{Schedule} object when
e.g. a well or connection is needed, instead of storing references or copies to
the \inlinecode{Schedule} objects internally.
There are currently three categories of changes that can take place due to
\actionx{}:
\subsubsection{General changes in well status}
\label{actionx_change_well}
When there is well related keyword in the \actionx{} block - e.g. \kw{WELOPEN}
to open or close a well or \kw{WCONPROD} to adjust rates, the simulator is
required update it's internal data structures with the updated input information.
This will be communicated by flagging all the wells which need an update in the
\inlinecode{Action::SimulatorUpdate} instance which is passed from the
simulator.
\subsubsection{Changes in geo properties}
The grid keywords are in general not permitted in the \kw{SCHEDULE} section,
however there are a few geo multipliers like \kw{MULTZ} and \kw{MULTFLT} which
are allowed in the \kw{SCHEDULE} section, and thereby also in \actionx{}. If one
of these keywords are encountered in the \actionx{} block we set the flag
\inlinecode{Action::SimulatorUpdate::tran\_update = true} to encourage the
simulator to recalculate the transmissibilities.
\subsubsection{WELPI}
\label{actionx_welpi}
The \kw{WELPI} keyword is quite complex from the outset, when it is included as
an \actionx{} keyword it gets even more complicated. In order to support
\kw{WELPI} in \actionx{} the simulator needs to inspect the \actionx{} keywords
before invoking them, and if \kw{WELPI} is included the PI values must be
assembled from the simulator and passed to the
\inlinecode{Schedule::applyAction()}. This is implemented and works, but it is
complex and the special treatment in order to support the combination \kw{WELPI}
+ \actionx{} is quite considerable.
\section{Problems in parallel}
\label{actionx_paralle}
The operation environment for \actionx{} is quite similar to \udq{} when it
comes to parallel behavior - see section \ref{udq_parallel}, in addition we
need to be aware of parallel challenges after the \actionx{} has completed. If
the \actionx{} keyword changes the well structure there will be problems with
with the parallel well distribution in the simulator. \emph{As of Januarry 2022
this will fail undetected.}
\section{\actionx{} restart output}
As with the \udq{} keyword some of the structure and complexity of the
\actionx{} datastructures are there primarily to enable \eclipse{} compatible
restart. Regarding restart of \actionx{} related data:
\begin{enumerate}
\item The restart output contains the result of parsing an \actionx{}
condition in an intermediate representation which has been reverse
engineered, this is complex and might not be 100\% correct.
\item When restarting \flow{} from an \eclipse{} formatted restart file the
\actionx{} conditions are reparsed based on string data in the restart file
and the intermediate representation mentioned in point 1 is not utilised.
\end{enumerate}

View File

@@ -1,405 +0,0 @@
\newcommand{\pyaction}{\kw{PYACTION}}
\chapter{Programming in the deck: \pyaction{}}
\label{pyaction}
The \pyaction{} keyword is a \flow{} specific keyword which allows for Python
programming in the \kw{SCHEDULE} section. The \pyaction{} keyword is inspired by
the \actionx{} keyword, but instead of a \inlinecode{.DATA} formatted condition
you are allowed to implement the condition with a general Python script. The
\actionx{} keywords are very clearly separated in a condition part and an action
part in the form of a list of keywords which are effectively injected in the
\kw{SCHEDULE} section when the condition evaluates to true. This is not so for
\pyaction{} where there is only one Python script which can both evaluate
conditions and apply changes. In principle the script can run arbitrary code,
but due to the complexity of the \kw{SCHEDULE} datamodel the ``current best''
way to actually change the course of the simulation is through the use of an
additional dummy \actionx{} keyword.
In order to enable the \pyaction{} keyword \flow{} must be compiled with the
\path{cmake} switches \inlinecode{-DOPM\_ENABLE\_EMBEDDED\_PYTHON=ON} and
\inlinecode{-DOPM\_ENABLE\_PYTHON=ON}, the default is to build with these switches
set to \inlinecode{OFF}. Before you enable \pyaction{} in your \flow{}
installation please read carefully through section \ref{pyaction_security} for
security implications of \pyaction{}.
\section{Python - wrapping and embedding}
Python is present in the \flow{} codebase in two different ways. For many of
the classes in the \flow{} codebase - in particular in opm-common, there are
\emph{Python wrappers} available. That means that you can invoke the C++
functionality in \flow{} classes from Python - e.g. this Python script can be used to
load a deck and print all the keywords:
\begin{code}
import sys
from opm.io.parser import Parser
input_file = sys.argv[1]
parser = Parser()
deck = parser.parse_file(input_file)
for kw in deck:
print(kw.name)
\end{code}
When used this way the Python interpreter is the main program running, and the
\flow{} classes like \inlinecode{Opm::Parser} are loaded to extend the Python
interpreter. This can also be flipped around, the Python interpreter can be
\emph{embedded} in the \flow{} executable. When Python is embedded, \flow{} is
the main program running, and with help of the embedded interpreter the \flow{}
program can be extended with Python plugins. The \pyaction{} keyword can be
perceived as a Python plugin. To really interact with the state of the \flow{}
simulation the plugin needs to utilize the functionality which wraps the C++
functionality, so for \pyaction{} both wrapping and embedding is at play.
Exporting more functionality from C++ to Python in the form of new and updated
wrappers is a quite simple and mechanical process. If you need a particular
functionality which is already available in C++ also in Python it will probably
be a quite limited effort for a developer who is already familiar with the code.
\section{The \pyaction{} keyword}
The \pyaction{} keyword is in the \kw{SCHEDULE} section like \actionx{}. The
first record is the name of the action and a string identifier for how many
times the action should run, then there is a path to a Python module:
\begin{deck}
PYACTION
PYTEST 'FIRST_TRUE' /
'pytest.py' /
\end{deck}
This keyword defines a \pyaction{} called \kw{PYTEST} which will run at the end
of every timestep until the first time a \inlinecode{true} value is returned. In
addition to \kw{FIRST\_TRUE} you can choose \kw{SINGLE} to run exactly once and
\kw{UNLIMITED} to continue running at the end of every timestep for the entire
simulation. The second record is the path to a file with Python code which will
run when this \pyaction{} is invoked. The path to the module will be interpreted
relative to the location of the \path{.DATA} file.
The python module can be quite arbitrary, but it must contain a function
\inlinecode{run} with the correct signature:
\begin{code}
def run(ecl_state, schedule, report_step, summary_state, actionx_callback):
print('Running python code in PYACTION')
return True
\end{code}
The \pyaction{} machinery is not as robust as the simulator proper: while
loading the \kw{PYACTION} keyword \flow{} will check that the Python module
contains syntactically valid Python code, and that it contains a
\inlinecode{run()} function, but it will \emph{not} check the signature of the
\inlinecode{run()} function. If the signature is wrong you will get a hard to
diagnose runtime error.
When the Python module is loaded it does so in an environment where the path to
the \path{.DATA} file has been appended to the Python load path by manipulating
the internal \inlinecode{sys.path} variable.
\subsection{The different arguments}
The \inlinecode{run()} function will be called with exactly five arguments which
your implementation can use. These arguments point to datastructures in the
simulator, and is the way to interact with the state of the simulation. The five
arguments are:
\begin{description}
\item[\inlinecode{ecl\_state}:] An instance of the \inlinecode{Opm::EclipseState}
class - this is a representation of \emph{all static properties} in the model,
ranging from porosity to relperm tables. The content of the
\inlinecode{ecl\_state} is immutable - you are not allowed to change the static
properties at runtime\footnote{This could certainly be interesting, but this
is beyond the scope of the \pyaction{} keyword.}.
\item[\inlinecode{schedule}:] An instance of the \inlinecode{Opm::Schedule}
class - this is a representation of all the content from the \kw{SCHEDULE}
section, notably all well and group information and the timestepping. Being
able to change the \kw{SCHEDULE} information runtime is certainly one of the
main motivations for this functionality, however due to the complexity of
the \inlinecode{Opm::Schedule} class (section \ref{schedule_design})
the recommended way to actually mutate the \inlinecode{Opm::Schedule} is
through the use of a dummy \actionx{} keyword (section
\ref{pyaction_actionx}).
\item[\inlinecode{report\_step}:] This is an integer for the report step we
are currently working on. Observe that the \pyaction{} is called for every
simulator timestep, i.e. it will typically be called multiple times with
the same value for the $\mathrm{report\_step}$ argument.
\item[\inlinecode{summary\_state}:] An instance of the
\inlinecode{Opm::SummaryState} class, this is where the current summary
results of the simulator are stored. The \inlinecode{SummaryState} class has
methods to get hold of well, group and general variables
\begin{code}
# Print all well names
for well in summary_state.wells:
print(well)
# Assign all group names to the variable group_names
group_names = summary_state.groups
# Sum the oil rate from all wells.
sum_wopr = 0
for well in summary_state.wells:
sum_wopr += summary_state.well_var(well, 'WOPR')
# Directly fetch the FOPR from the summary_state
fopr = summary_state['FOPR']
\end{code}
The \inlinecode{summary\_state} variable can also be updated with the
\inlinecode{update()}, \inlinecode{update\_well\_var()} and
\inlinecode{update\_group\_var()} methods.
\item[\inlinecode{actionx\_callback}:] The \inlinecode{actionx\_callback} is a
specialized function which is used to update the \inlinecode{Schedule} object
by applying the keywords from a normal \actionx{} keyword. This is described
in detail in section \ref{pyaction_actionx}.
\end{description}
\subsection{Holding state}
The \pyaction{} keywords will often be invoked multiple times, a Python
dictionary \inlinecode{state} has been injected in the module - that dictionary
can be used to maintain state between invocations. Let us assume we want to
detect when the field oil production starts curving down - i.e. when
$\partial^2_{t} \mathrm{FOPR} < 0$, in order to calculate that we need to keep
track of the timesteps and the $\mathrm{FOPR}$ as function of time - this is one
possible implementation:
\begin{code}
def diff(pair1, pair2):
return (pair1[0] - pair2[0], pair1[1] - pair2[1])
def fopr_diff2(summary_state):
fopr = summary_state.get('FOPR')
sim_time = summary_state.get('TIME')
if not 'fopr' in state:
state['fopr'] = []
fopr_series = state['fopr']
fopr_series.append( (sim_time, fopr) )
if len(fopr_series) < 2:
return None
pair0 = fopr_series[-1]
pair1 = fopr_series[-2]
pair2 = fopr_series[-3]
dt1, df1 = diff(pair0, pair1)
dt2, df2 = diff(pair1, pair2)
return 2*(df1/dt1 - df2/dt2)/(dt1 + dt2)
def run(ecl_state, schedule, report_step, summary_state, actionx_callback):
fopr_d2 = fopr_diff2(summary_state)
if not fopr_d2 is None:
if fopr_d2 < 0:
print('Hmmm - this is going the wrong way')
else:
print('All good - sky is the limit!')
\end{code}
\section{Changing the \inlinecode{Schedule} object - using a ``normal'' \actionx{}}
\label{pyaction_actionx}
Before reading this section you should make sure to understand the
\inlinecode{Schedule} design described in section \ref{schedule_design}. The
initial plan when implementing the \pyaction{} keyword was to be able to make
function calls like
\begin{code}
schedule.close_well(w1, report_step)
schedule.set_orat(w2, 1000, report_step)
\end{code}
to close a well and set the oil rate of another well. Unfortunately it proved
very complex to get good semantics for combining such runtime changes with the
keyword based model for \kw{SCHEDULE} section. The current recommendation is to
apply changes to the \kw{SCHEDULE} section using callbacks to \kw{ACTIONX}
keywords from Python code, this is illustrated in the example
below\footnote{From a programmers point of view the solution seems very
unsatisfactory, but it works and it plays nicely with the \kw{ACTIONX} behavior.
If/when the underlying \inlinecode{Schedule} implementation changes there is
nothing per se in the \pyaction{} design which inhibits use of a better
\inlinecode{Schedule} api in the future.}.
The recommended way to achieve this is to create a normal \actionx{} keyword
which is set up to run zero times, and then explicitly invoke that from the
Python \inlinecode{run()} function. In the example below we create an \actionx{}
\inlinecode{CLOSEWELLS} which will close all matching wells (the wellname '?')
\begin{deck}
ACTIONX
CLOSEWELLS 0 /
/
/
WELOPEN
'?' 'CLOSE' /
/
ENDACTIO
\end{deck}
The \inlinecode{CLOSEWELLS} action is set up to run zero times, so the normal
\actionx{} machinery will never run this action\footnote{The \kw{CLOSEWELL}
action has an \emph{empty condition}, the \actionx{} keywords with empty
condition will always evaluate as false.}. Then in the Python run function we go
through all the wells and call the \inlinecode{CLOSEWELL} action to close those
with \inlinecode{OPR < 1000}:
\begin{code}
def run(ecl_state, schedule, report_step, summary_state, actionx_callback):
close_wells = []
for well in summary_state.wells:
if summary_state.well_var(well, 'WOPR') < 1000:
close_wells.append(well)
if close_wells:
actionx_callback('CLOSEWELLS', close_wells)
\end{code}
The implementation of this is quite complex with thread of execution going from
C++ to Python, then invoking a callback to C++ which will call
\inlinecode{Schedule::iterateScheduleSection()}, going back to Python to
complete the \inlinecode{run()} method before the function pointers pops back to
C++ and continues the simulator execution\footnote{This is documented in some
detail as code comments of \inlinecode{Schedule::applyPyAction()} in the
\path{Schedule.cpp} file.}.
\section{Implementing \udq{} like behavior}
The \udq{} keyword has three different purposes - all based on defining
complex quantities from the current state of the simulation:
\begin{enumerate}
\item Define a complex quantity to be used in a \actionx{} condition.
\item Define a complex quantity for reporting in the summary file.
\item Define a quantity which can used as a control in \kw{UDA}.
\end{enumerate}
All of these can be achieved by using the \pyaction{} keyword, although for the
two latter alternatives you must specify the \udq{} keyword in the deck first,
but you can let the \pyaction{} implementation override the value:
\begin{deck}
-- Observe that this UDQ will be assigned from a PYACTION keyword,
-- the value used in the ASSIGN statement below is pure dummy.
UDQ
ASSIGN WUGOOD 1 /
ASSIGN FUGOOD 1 /
/
\end{deck}
\subsection{Using \pyaction{} instead of \udq{} + \actionx{}}
Towards the end of section \ref{actionx_structure} it is demonstrated how \udq{}
and \actionx{} can be combined to implement an action in case a complicated
condition applies. As described in section \ref{pyaction_actionx} the best way
to actually invoke changes on the \kw{SCHEDULE} section is through the use of a
dummy \actionx{} keyword, but \pyaction{} is very well suited to evaluate
complex conditions. In the example below we close all wells which have
consistently produced less than 1000 $\mathrm{m^3/day}$ for more than 60 days:
\begin{code}
wopr_limit = 1000
time_limit = 60 * 3600 * 24
def init_state(summary_state):
if 'closed_wells' in state:
return
state['closed_wells'] = set()
bad_wells = {}
for well in summary_state.wells:
bad_wells[well] = None
state['bad_wells'] = bad_wells
def run(ecl_state, schedule, report_step, summary_state, actionx_callback):
shut_wells = []
init_state(summary_state)
for well in summary_state.wells:
if well in state['closed_wells']:
continue
if summary_state.well_var(well, 'WOPR') < wopr_limit:
elapsed = summary_state.elapsed()
if state['bad_wells'][well] is None:
state['bad_wells'][well] = elapsed
else:
bad_time = elapsed - state['bad_wells'][well]
if bad_time > time_limit:
shut_wells.append(well)
state['closed_wells'].add( well )
else:
state['bad_wells'][well] = None
if shut_wells:
actionx_callback(shut_wells)
\end{code}
\subsection{Using \pyaction{} to report to the summary file}
The important point when using \pyaction{} to report complex results to the
summary file is just that the \inlinecode{summary\_state} argument to the
\inlinecode{run()} function is \emph{writable} with \inlinecode{updata\_xxx}
calls. Assuming dummy \udq{} variables \kw{WUGOOD} and \kw{FUGOOD} have been
defined as per the example above, we can use \pyaction{} to set variable
\kw{FUGOOD} to one for all wells with rate above a limit, and the \kw{FUGOOD}
variable can be the count of such wells:
\begin{code}
def run(ecl_state, schedule, report_step, summary_state, actionx_callback):
good_count = 0
opr_limit = 1000
for wname in schedule.well_names():
if summary_state.well_var(wname, 'FOPR') > opr_limit:
good_count += 1
summary_state.update_well_var(wname, 'WUGOOD', 1)
else:
summary_state.update_well_var(wname, 'WUGOOD', 0)
summary_state.update_var('FUGOOD', good_count)
\end{code}
\subsection{Using \pyaction{} to set a \kw{UDA} control}
Using \pyaction{} to set \kw{UDA} controls is quite simple. Again the \udq{}
keyword must have been defined with a dummy value in the \kw{SCHEDULE} section,
and the \kw{UDA} keyword used in e.g. a \kw{WCONDPROD} keyword. Then the
\inlinecode{run()} function can just be used to assign to the \udq{} variable.
In the example below we use a \kw{UDA} to control the oil production rate, and
the value is set to the average value of the producing wells:
\begin{deck}
-- Define dummy UDQ WUOPR to be used as control in the WCONPROD
-- keyword. The actual value for this UDQ is assigned in a PYACTION
-- keyword
UDQ
ASSIGN WUOPR 0 /
/
...
...
-- Need to define a well list with all the production wells.
-- This is to ensure that the WCONPROD keyword is only applied
-- to producers.
WLIST
'PROD' P1 P2 P3 .../
WCONPROD
'*PROD' 'OPEN' 'ORAT' 'WUOPR' /
/
\end{deck}
This can then be combined with the python code:
\begin{code}
def run(ecl_state, schedule, report_step, summary_state, actionx_callback):
num_prod_wells = 0
for wname in schedule.well_names():
if summary_state.well_var(wname, 'WOPR') > 0:
num_prod_wells += 1
fopr = summary_state['FOPR']
new_rate = fopr / num_prod_wells
for wname in schedule.well_names():
summary_state.update_well_var(wname, 'WUOPR', new_rate)
\end{code}
\section{Security implications of \pyaction{}}
\label{pyaction_security}
The \pyaction{} keyword allows for execution of arbitrary user supplied Python
code, with the priviliges of the user actually running \flow{}. If you have a
setup where \flow{} runs with a different user account than the person
submitting the simulation you should be \emph{very careful} about enabling the
embedded Python functionality and the \pyaction{} keyword. As a scary example
this script will wipe your disks:
\begin{code}
import shutil
def run(ecl_state, schedule, report_step, summary_state, actionx_callback):
shutil.rmtree('/')
\end{code}
If the user running \flow{} has different security credentials than the user
submits the job, this has significant security implications.

View File

@@ -1,599 +0,0 @@
\newcommand{\udq}{\kw{UDQ}}
\chapter{Programming in the deck: \udq{}}
\udq{} and \kw{ACTIONX} are two keywords which offer a sort of
\emph{programming} in the input deck. \udq{} is an acronym for \emph{User
Defined Quantity}, and the essence of the \udq{} keyword is the ability to
define arithmetic expressions based on the result vectors of the ongoing
simulation. The quantites evaluated with \udq{} can then be output as summary
variables, and they can be used as controls in keywords like \kw{WCONPROD} and
\kw{GCONINJE}. When used as controls the \udq{} variables are called \emph{User
Defined Arguments} (UDA).
For both the \udq{} and \kw{ACTIONX} keywords evaluating an arithmetic
expression based on the current results of the ongoing simulation is an
important part of the concept, and they are often referenced in pair as
\udq{}/\kw{ACTIONX}, this is slightly misleading as the two are fully
independent and share very little both as concepts in \flow{} and in the C++
implementation. The \kw{ACTIONX} keyword is described in chapter \ref{actionx}.
The \udq{} keyword is also documented in section 12.3.233 in the \flow{}
reference manual.
\section{Defining a new \udq{} keyword}
New \udq{} variables are defined with the \udq{} keyword in the \kw{SCHEDULE}
section. The \udq{} keyword is a sort of a super keyword with four additional
subcommands: \kw{DEFINE}, \kw{ASSIGN}, \kw{UNIT} and \kw{UPDATE}. The
\kw{DEFINE} subcommand is the most important command, that is used to
\emph{define} an arithmetic expression for a \udq{} variable, which is evaluated
at the end of every simulator time step. \kw{ASSIGN} is used to define a \udq{}
variable with a constant numerical value, in addition the \kw{ASSIGN} subcommand
is often used as a ``forward reference'' to enable using a \udq{} keyword in
another \kw{DEFINE} expression. The \kw{UNIT} command is used to assign a unit
string to a \udq{} variable, see section \ref{udq_units} for more details about
\udq{} variables and units. The \kw{UPDATE ON} and \kw{UPDATE OFF} commands can
be used to switch updating of \udq{} variables on and off. The \udq{} variables
are created in mode \kw{ON}.
All \udq{} keywords must have the letter \emph{U} as their second character, the
first character should be 'F', 'G' or 'W' to indicate whether this is a field,
group or well quantity\footnote{In \eclipse{} also the characters 'S', 'C', 'A'
and 'B' are used to denote \emph{segment}, \emph{connection} \emph{aquifer} and
\emph{block} variables respectively. None of these are supported in \flow{}.}.
The simplest way to define a \udq{} is just using the \kw{ASSIGN} subcommand of
the \udq{} keyword:
\begin{deck}
UDQ
ASSIGN FUVAR1 123 /
ASSIGN FUVAR2 456 /
/
\end{deck}
This way we will assign to variables \inlinecode{FUVAR1} and \inlinecode{FUVAR2}
with values 123 and 456 respectively. These values can output to the summary
file, be used as control values in a control keyword like \kw{WELTARG} and be
used to recursively define another \udq{} keyword.
The more interesting \udq{} subcommand is \kw{DEFINE} which is used to define an
arithmetic expression for a \udq{} variable, the arithmetic expression will be
evaluated at the end of every timestep. The expressions are built from the
normal arithmetic operators +,-,*,/, a predefined set of available functions
(see \ref{udq_functions}) and results from the ongoing simulation.
In the example below we create \udq{} variables \kw{FUWCT1} and \kw{FUWCT2} as
user defined field water cut, one based on the summary variables \kw{FWPR} and
\kw{FOPR} and one based on summing \kw{WOPR} and \kw{WWPR} over all wells
\begin{deck}
UDQ
DEFINE FUWCT1 FWPR/(FWPR + FOPR) /
DEFINE FUWCT2 SUM(WWPR)/(SUM(WWPR) + SUM(WOPR)) /
/
\end{deck}
A \udq{} variable can be redefined during the simulation, and also change from
constant \kw{ASSIGN} to variable \kw{DEFINE}. Observe that \udq{} values are
always evaluated in order of occurence in the input deck. For instance for this
input
\begin{deck}
UDQ
ASSIGN FU1 100 /
DEFINE FU2 FU1 + FOPR /
DEFINE FU1 FWPR /
/
\end{deck}
the evaluation order will be \{\kw{FU1}, \kw{FU2}\}\footnote{Maintaining the
input order and whether a certain \udq{} symbol is now a \kw{ASSIGN} or
\kw{DEFINE} keyword is also very important for the restart code.}. \kw{ASSIGN}
statements take effect at input time, making the newly defined symbol
immediately available for reuse in a subsequent defintion. This is utilized in
the \udq{} keyword above where the symbol \kw{FU1} is referenced in the
defintion of \kw{FU2}. As used in this example the \kw{ASSIGN FU1 100} can be
seen as a \emph{forward reference}. When we have completed the first timestep
and it is time to evalute the \udq{} expressions they will be evaluated in order
of \emph{first appearance}, i.e. \{\kw{FU1}, \kw{FU2}\}. At this stage the
active definition of \kw{FU1} is \inlinecode{FU1 = FWPR}, i.e. the value
\inlinecode{100} from the initial definition \inlinecode{FU1 = 100} is never
actually used.
\section{Different types of \udq{} - field, group and well}
The \udq{} keywords can be of different types, \flow{} supports \emph{field},
\emph{well} and \emph{group} keywords\footnote{\eclipse{} also supports
connection, segment and aquifer variables.}. The field keywords are scalar,
whereas the well and group keywords are sets with values for every well/group.
The type of a \udq{} keyword is inferred from the name in the same manner as the
summary keywords, i.e. for this \udq{} keyword
\begin{deck}
UDQ
ASSIGN FU1 100 /
ASSIGN WU1 200 /
ASSIGN GU1 300 /
/
\end{deck}
we will define one scalar keyword \kw{FU1}, one well set \kw{WU1} and one group
set \kw{GU1}. The well sets will have one slot for each well in the model, it is
not possible to create a well set with only a subset of wells, but the well set
can have \emph{undefined} value for a subset of wells. The set maintains a
\inlinecode{is\_defined()} status for each element and most operations only
apply to the defined elements\footnote{The C++ implementation of the \udq{}
value set is the equivalent of \texttt{std::map<std::string,
std::optional<double>>}.}.
As indicated with \kw{ASSIGN WU1 200} you can assign a scalar value to a set
keyword, then all elements in the set will have the same value. Assuming we have
a model with wells OP1, OP2, WI and GI the \kw{WU1} will look like
\begin{code}
WU1 = {"OP1": 100, "OP2": 100, "WI": 100, "GI": 100}.
\end{code}
When defining a well \udq{} it is natural to refer to well summary variables, in
the example below we define \kw{WUBHP} which is the bottom hole pressure
for each well, \kw{WUOPR1} which is the oil production rate for a subset of
wells and \kw{WUOPR2} which is the oil production rate for well OP1 \emph{broadcasted to all wells}.
\begin{deck}
UDQ
DEFINE WUBHP WBHP /
DEFINE WUOPR1 WOPR 'OP*' /
DEFINE WUOPR2 WOPR OP1 /
/
\end{deck}
After evaluation these \udq{} values will be\footnote{The numerical values are
arbitrary, just to illustrate that they are \emph{defined}, in contrast to the
[ ] which is used to illustrate an \emph{undefined} value.}:
\begin{code}
WUBHP = {"OP1": 20, "OP2": 30, "WI": 10, "GI": 15}
WUOPR1 = {"OP1": 13, "OP2": 17, "WI": [ ], "GI": [ ]}
WUOPR2 = {"OP1": 13, "OP2": 13, "WI": 13, "GI": 13}
\end{code}
Observe how well variables like \kw{WBHP} and \kw{WOPR} can be qualified with a
wellname pattern. If no wellname is supplied the expression will be evaluated
for all wells, as for \kw{WUBHP}, for \kw{WUOPR1} the pattern 'OP*' will select
wells \{OP1, OP2\} and leave the injectors \{WI, GI\} undefined. For \kw{WUOPR2}
the well pattern 'OP1' specifies a well completely, i.e. this will be evaluated
as a scalar, and then the scalar value \inlinecode{WOPR:OP1 == 13} is
broadcasted to all the wells in \kw{WUOPR2}.
\udq{} sets with different sets of defined wells can be combined, in most cases
the operations will be applied to the intersection of all defined wells,
consider for example\label{udq_diff}:
\begin{deck}
UDQ
DEFINE WUBHP WBHP /
DEFINE WUTHP WTHP OP* /
DEFINE WUPDIFF WUBHP - WUTHP
/
\end{deck}
When these \udq{} statements are evaluated we will get:
\begin{code}
WUBHP = {"OP1": 20, "OP2": 30, "WI": 10, "GI": 15}
WUTHP = {"OP1": 13, "OP2": 17, "WI": [ ], "GI": [ ]}
WUPDIFF = {"OP1": 7, "OP2": 13, "WI": [ ], "GI": [ ]}
\end{code}
As we see the undefined property for wells GI and WI in \kw{WUTHP} is contagious
when the two expressions are combined with \inlinecode{WUPDIFF = WUBHP - WUTHP},
see however section \ref{udq_union_functions} for a collection of functions
which operate on the union of values.
When an undefined variable is output to the summary file it will get a value
given as item 3 of the \kw{UDQPARAM} keyword. Summary output is the \emph{only}
point where the undefined value can be dereferenced, the numerical value given
in \kw{UDQPARAM} will not be available either for \udq{} nor \kw{ACTIONX}
evaluations.
\section{Functions available in \udq{} defintions}
\label{udq_functions}
\flow{} supports all the \udq{} functions available in \eclipse{}, for the
future it would be a quite simple C++ task to extend the list of available
functions, although that will affect \eclipse{} compatibility.
\subsection{Ordinary binary functions}
The \udq{} framework supports all the ordinary arithmetic operators +,-,*,/ and
\^{} and the comparison operators $>, \ge, <, \le, \ne, ==$. For all of these
operations sets and scalars can be combined freely. When combining a scalar and
a set the scalar will be promoted to a set with all values equal, i.e. for
\begin{deck}
UDQ
ASSIGN WULIMIT 100 /
/
\end{deck}
the numerical value 100 will be broadcasted to all wells:
\begin{code}
WULIMIT = {"OP1": 100, "OP2": 100, "WI": 100, "GI": 100}.
\end{code}
In the case of set arguments the operations are only applied to the union of
values which are defined in both argument sets. The comparison operators produce
numerical 0 or 1 depending on the result of the comparison, the result from a
comparison operation can be used numerically in further computations. In the
following example \kw{FUWCNT} will be the number of wells producing with oil
production rate above 1000\footnote{Remember: 1000 has the units of the
deck, i.e. $\mathrm{stb/day}$ in FIELD units and $\mathrm{m^3/day}$ in metric
units.}.
\begin{deck}
UDQ
DEFINE FUWCNT SUM( WOPR > 1000 ) /
/
\end{deck}
\subsection{Functions over a set returning a scalar}
The \udq{} framework has several functions which iterate over all the defined
members of a set and return a scalar. In the example
\begin{deck}
UDQ
DEFINE FU1 SUM(WOPR 'OP*') /
/
\end{deck}
The scalar variable \kw{FU1} will be equal to the sum of oil production rates
for all wells matching the pattern \kw{OP*}, the wells with a name not matching
\kw{OP*} will not contribute to the sum. The complete list of functions iterating
over a set and returning a scalar are:
\begin{description}
\item[SUM] Sum all defined elements in the argument set and return a scalar.
\item[AVEA] The arithmetic average of the elements in the set.
\item[AVEG] The geometric average of the elements in the set.
\item[AVEH] The harmonic average of the elements in the set.
\item[MAX] The maximum element in the set.
\item[MIN] The minimum element in the set.
\item[NORM1] The $\mathrm{L^1}$ norm of the elements in the set.
\item[NORM2] The $\mathrm{L^2}$ norm of the elements in the set.
\item[NORMI] The $\mathrm{L^\infty}$ norm of the elements in the set.
\item[PROD] The product of all the elements in the set.
\end{description}
\subsection{Elemental functions}
There is a family of functions which take a vector or scalar as argument, run
through all the elements in the argument and return a result of the same shape
as the argument, where a function has been applied to all the defined elements.
Assume that the \kw{WU1} has the following value:
\begin{code}
WU1 = {"OP1": -2, "OP2": -1, "WI": 1, "GI": []},
\end{code}
then \kw{DEFINE WUABS ABS(WU1)} will give
\begin{code}
WUABS = {"OP1": 2, "OP2": 1, "WI": 1, "GI": []}.
\end{code}
The available elemental functions of this kind come in different categories:
\subsubsection*{Mathematical functions}
\begin{description}
\item[EXP] Return a new set where all defined elements have been exponentiated.
\item[ABS] Return a new set with the absolute value of all elements.
\item[LN] Return a new set with the \emph{natural logarithm} of all elements.
\item[LOG] Return a new set with $\log_{10}$ of all elements.
\item[NINT] Return a new set where all elements have been converted to the
nearest integer.
\end{description}
\subsubsection*{Sorting functions}
The \udq{} functionality supports functions \kw{SORTA} and \kw{SORTD} to sort a
set in ascending or descending order respectively. Observe that these functions
do not sort the sets in place - the udqset does not have any notion of ordering,
rather they create a permutation set with values 1,2,3, ... The following
combination of \udq{} and \kw{ACTIONX} will use the \kw{SORTD} function to close
the two wells with the highest watercut:
\begin{deck}
UDQ
DEFINE WUWCTS SORTD(WWCT OP*) /
/
\end{deck}
assuming the \kw{WWCT} looks like
\begin{code}
WWCT = {"OP1": 0.5, "OP2": 0.2, "OP3": 0.1, "OP4":0.7, "WI": 0, "GI": 0}
\end{code}
then the \kw{WUWCTS} set will look like
\begin{code}
WUWCTS = {"OP1": 2, "OP2": 3, "OP3": 4, "OP4":1 "WI": [], "GI": []}
\end{code}
When this is combined with the \kw{ACTIONX}(see chapter \ref{actionx})
\begin{code}
ACTIONX
CW /
WUWCTS <= 2 /
/
WELOPEN
'?' 'CLOSE' /
/
ENDACTIO
\end{code}
the two wells with highest watercut will be selected in the \inlinecode{WUWCTS
<= 2} statement and that will be expanded to \{OP4, OP1\} by the '?'
expression in the \kw{WELOPEN} keyword.
\subsubsection*{Random functions}
The \udq{} machinery has functionality to sample random numbers, there are one
set of random number functions which are seeded deterministically by item 1 of
\kw{UDQPARAMS} and an alternative set which is seeded by the clock. The random
number functions take a \udq{} variable as argument, that is only to ensure that
the shape of the result value is correct.
\begin{description}
\item[RANDN] Random numbers from distribution $N(0,1)$ seeded deterministically
by item 1 in \kw{UDQPARAMS}.
\item[RANDU] Random numbers from distribution $U(-1,1)$ seeded deterministically
by item 1 in \kw{UDQPARAMS}.
\item[RRNDN] Random numbers from distribution $N(0,1)$ seeded by the clock.
\item[RRNDU] Random numbers from distribution $U(-1,1)$ seeded by the clock.
\end{description}
Assuming the argument vector \kw{WU1} looks like
\begin{code}
WU1 = {"OP1": -2, "OP2": [], "WI": 1, "GI": []},
\end{code}
the result of \kw{DEFINE WURAND RANDU(WU1)} could be like
\begin{code}
WURAND = {"OP1": 0.576, "OP2": [], "WI": -0.132, "GI": []}.
\end{code}
\subsubsection*{Work with defined status}
The functions \kw{DEF}, \kw{UNDEF} and \kw{IDV} can be used to inspect the
defined/not defined status of the elements in a \udq{} set.
\begin{description}
\item[DEF] Return a set with value 1 for all defined elements.
\item[UNDEF] Return a set with value 1 for all undefined elements.
\item[IDV] Return a set with value 1 for all defined elements and value 0 for
all undefined elements.
\end{description}
Assuming the input argument
\begin{code}
WU1 = {"OP1": -2, "OP2": [], "WI": 1, "GI": []},
\end{code}
the \udq{} assignments
\begin{deck}
UDQ
DEFINE WUDEF DEF(WU1) /
DEFINE WUUNDEF UNDEF(WU1) /
DEFINE WUIDV IDV(WU1) /
/
\end{deck}
will produce:
\begin{code}
WUDEF = {"OP1": 1, "OP2": [], "WI": 1, "GI": []},
WUUNDEF = {"OP1": [], "OP2": 1, "WI": [], "GI": 1},
WUIDV = {"OP1": 1, "OP2": 0, "WI": 1, "GI": 0},
\end{code}
\subsection{Union functions}
\label{udq_union_functions}
There are a list of functions \kw{Uxxx} which operate on the union of the values
found in the two sets, i.e. a result is assigned if at at least one set has
defined value for this well/group.
\begin{description}
\item[UADD] Will add the items from the two sets.
\item[UMUL] Will multiply the items from the two sets.
\item[UMAX] The maximum value from the two sets.
\item[UMIN] The minumum value from the two sets.
\end{description}
In the case where only one of the sets has a defined value the operation will be
performed \emph{as if} the function \kw{Uxxx} is the identity function.
As an example consider the two sets:
\begin{code}
WU1 = {"OP1": 1, "OP2": [], "WI": 2, "GI": []},
WU2 = {"OP1": 6, "OP2": 5, "WI": [], "GI": []},
\end{code}
and the \udq{} expression \kw{DEFINE WUUADD WU1 UADD WU2}, then the resulting
set \kw{WUUADD} will be
\begin{code}
WUUADD = {"OP1": 7, "OP2": 5, "WI": 2, "GI": []}.
\end{code}
This in contrast to a normal $+$ which only operates on the intersection of the
two sets, only well OP1 would have a defined value in this case.
\section{Used as a control: UDA}
\label{uda}
Probably one of the most important uses of the \udq{} functionality is the
ability to use a \udq{} as control in e.g. the \kw{WCONINJE} keyword. In the
example below we calculate the produced liquid volume from a group of wells and
use that as injection target for a water injector:
\begin{deck}
UDQ
DEFINE FULPR (WOPR P* + WWPR P*) * 1.25 /
/
...
...
WCONINJE
WI 'WATER' 'OPEN' 'RATE' 'FULPR' /
/
\end{deck}
In the following insane example we distribute the current oil production rate
randomly among the producers and use that as target for the next timestep - do
not try this at home:
\begin{deck}
UDQ
-- Create a 0 / 1 mask with 0 for injectors and 1 for producers.
DEFINE WUPROD WOPR > 0 /
-- Create a vector of random numbers [0,1] for all producers
DEFINE WURAND 0.5 * (RANDU(WUPROD) + 1) * WUPROD /
-- Create a vector where all producers get a random fraction of
-- the current total oil production rate
DEFINE WUOPR FOPR * WURAND / SUM(WURAND) /
/
-- Need to have a well list or similar to select all producers for
-- the WCONPROD keyword.
WLIST
'P' 'ADD' ..... /
WCONPROD
'*P' 'OPEN' 'ORAT' 'WUOPR' /
/
\end{deck}
One point about this example is that the \udq{} variable \kw{WUOPR} which is
used as as control in the \kw{WCONPROD} is a well set, the lookup machinery will
automatically use the correct well index when assigning control value to a
particular well.
From an implementation point of view the \kw{UDA} functionality creates a
significant complexity, because the actual rate to use in the simulation must be
evaluated \emph{just in time}.
\section{\udq{} units}
\label{udq_units}
The \udq{} subcommand \kw{UNIT} can be used to assign a string which is used as
output unit when the \udq{} variable is output to the summary file. This is
\emph{only} a string and does not induce any unit conversion. All \udq{}
evaluations are in terms of the corre deck units - irrespective of the \kw{UNIT}
subcommand. Consider the following
\begin{deck}
RUNSPEC
FIELD
...
...
SCHEDULE
UDQ
DEFINE WUI WWIR - 100 /
/
\end{deck}
\begin{enumerate}
\item The simulation runs in SI units; hence the water injection rate is
calculated in $\mathrm{m^3/s}$.
\item As part of the summary evaluation the \inlinecode{SummaryState} will
contain the water injection rate converted to field units.
\item The \udq{} variable \kw{WUI} is evaluated as the water injection rate from
\inlinecode{SummaryState} subtracted numerical value 100, \emph{it is solely the users
responsability that the numerical value 100 represents water injection rate in
field units.}
\end{enumerate}
Observe that the units are complicated, and non intuitive for \kw{UDA} values.
The \kw{UDA} evaluation is based on the \inlinecode{SummaryState} class which is
in deck units, the controls determined by \kw{UDA} evaluation must therefor be
converted to SI units just when it is passed to the simulator. The initial
design ambition was to block the deck units at the IO boundary, keeping the
internals fully in SI. The \kw{UDA} concept is the exception which manages to
inject deck units quite far into the code.
\section{Implementation details}
The significant part of the UDQ implementation is in classes located in
\path{opm/input/eclipse/Schedule/UDQ}, in addition the restart output of
\udq{}/\kw{UDA} values is in \path{opm/output/eclipse/AggregateUDQData.cpp}.
Finally the \udq{} parser makes use of the type \inlinecode{RawString} to treat
literal ``/'' and ``*'' different from ordinary parsing where ``/'' signifies
end of line and ``*'' is either a default or part of a multiplier expression.
\subsection*{\inlinecode{UDQConfig}}
The class \inlinecode{UDQConfig} internalizes the parsing of the \udq{} keywords
from the input deck. The \inlinecode{UDQConfig} instance is time-versioned and
managed by the \inlinecode{ScheduleState} class. The \inlinecode{UDQConfig} is
immutable while the simulator is executing. The \inlinecode{UDQConfig} contains
two main classes \inlinecode{UDQDefine} and \inlinecode{UDQAssign} in addition
to book-keeping code to keep track of what type of expression a \udq{} keyword
is at the time. A significant part of the book-keeping is only required to be
able to output restart files in \eclipse{} format.
\subsection*{\inlinecode{UDQDefine} and \inlinecode{UDQASTNode}}
The \inlinecode{UDQDefine} manages a parsed \udq{} expresssion along with a
chunck of metadata. The parsed \udq{} expression is managed in an instance of
\inlinecode{UDQASTNode}. The \inlinecode{UDQASTNode} contains a parsed tree
representation of the \udq{} input expression. The parsing of \udq{} expressions
happens at input time, and for the rest of the simulation the
\inlinecode{UDQASTnode} instances are immutable.
Many scalar \udq{} types are defined in the file \path{UDQEenums.hpp} and in the
namespace \inlinecode{UDQ} there are many small utility functions to work with
these enums.
\subsection*{\inlinecode{SummaryState}}
\label{summarystate}
The \inlinecode{SummaryState} class is not part of the \udq{} implementation,
but it is a very important class for the \udq{} functionality. At the end of
every timestep the simulator will call the method \inlinecode{evalSummary} which
will call into opm-common and evaluate all summary variables and store them in a
\inlinecode{SummaryState} instance\footnote{Observe that during initialization
the \udq{} expressions are inspected, and we make sure that all summary
variables needed to evaulate \udq{} expressions are evaluated in the Summary
evaluation.}. The \inlinecode{SummaryState} class manages a set of maps with
well, group and field variables, when evaluating e.g. the \kw{WOPR} the results
will be stored in a two level map first indexed with keyword \kw{WOPR} and then
with well name. Afterwards the \udq{} layer can fetch values with
\inlinecode{SummaryState::get\_well\_var()}. The values in the
\inlinecode{SummaryState} have been converted to output units, this is important
for the \kw{UDA} evaluation.
At the end of every timestep the \inlinecode{UDQConfig::eval()} method is called
to evaluate all the \udq{} expressions, the evaluated values will end up in the
active \inlinecode{SummaryState} instance, i.e. for this \udq{}
\begin{deck}
UDQ
DEFINE FUOPR SUM(WOPR) /
DEFINE WUGWR WGPR / WWPR /
/
\end{deck}
we will get \inlinecode{SummaryState} entries for \kw{FUOPR} and \kw{WUGWR} for
\emph{all} wells in the model. These \inlinecode{SummaryState} values can then
be output to the summary file, be used when evaluating \kw{ACTIONX} keywords or
to evaluate \kw{UDA} control values.
In addition to the \inlinecode{SummaryState} variable there is an instance of
type \inlinecode{UDQState} which is updated runtime, the \inlinecode{UDQState}
instance holds on to the results of \udq{} evaluations with more context than
\inlinecode{SummaryState} and is used to output \udq{} and \kw{UDA} state to the
restart files. While evaluating the simulator will create a \kw{UDQContext}
variable which will manage both the \inlinecode{SummaryState},
\inlinecode{UDQState} and also some \udq{} parameters from the \kw{RUNSPEC}
section. The lifetime of the \kw{UDQContext} instance is only one \udq{}
evaluation.
While evaluating the \udq{} expressions the results will be instances of
\inlinecode{UDQSet} which is a small container class which keeps track of
well/group names and whether a value is defined or not. The \inlinecode{UDQSet}
class overrides the arithmetic operators like \inlinecode{UDQSet::operator+()}
so that expressions like \kw{2 * WOPR 'OP*'} can be easily evaluated in code.
\subsection*{Paralell awareness}
\label{udq_parallel}
The opm-common code where the \udq{} functionality is implemented is totally
unaware of parallel execution, so to be certain that this works for a parallel
simulator care must be taken. In flow this is handled as:
\begin{enumerate}
\item The \inlinecode{Schedule} class as a whole is identical on all
processes.
\item The state variables \inlinecode{UDQState} and \inlinecode{SummaryState}
are distributed so they are equal on all processes before the \udq{}
evaluation. This communication is performed in the simulator.
\end{enumerate}
The \udq{} evaluation is invoked from
\path{opm-simulators/ebos/eclgenericwriter.cc} function
\inlinecode{evalSummary()}.

View File

@@ -5,8 +5,8 @@
Module: opm-common
Description: Open Porous Media Initiative shared infrastructure
Version: 2022.04-rc1
Label: 2022.04-rc1
Version: 2021.04
Label: 2021.04
Maintainer: opm@opm-project.org
MaintainerName: OPM community
Url: http://opm-project.org

View File

@@ -18,7 +18,6 @@
*/
#include <filesystem>
#include <iostream>
#include <getopt.h>
#include <string.h>
@@ -32,6 +31,7 @@
#include <opm/io/eclipse/ESmry.hpp>
#include <opm/io/eclipse/EclUtil.hpp>
#include <opm/common/utility/FileSystem.hpp>
static void printHelp() {
@@ -39,7 +39,7 @@ static void printHelp() {
std::cout << "\nThis program create one or more lodsmry files, designed for effective load on the demand. \n"
<< "These files are created with input from the smspec and unsmry file. \n"
<< "\nIn addition, the program takes these options (which must be given before the arguments):\n\n"
<< "-f if ESMRY file exist, this will be replaced. Default behaviour is that existing file is kept.\n"
<< "-f if LODSMRY file exist, this will be replaced. Default behaviour is that existing file is kept.\n"
<< "-n Maximum number of threads to be used if mulitple files should be created.\n"
<< "-h Print help and exit.\n\n";
}
@@ -48,9 +48,7 @@ static void printHelp() {
int main(int argc, char **argv) {
int c = 0;
#ifdef _OPENMP
int max_threads = -1;
#endif
int max_threads [[maybe_unused]] = -1;
bool force = false;
while ((c = getopt(argc, argv, "fn:h")) != -1) {
@@ -62,11 +60,7 @@ int main(int argc, char **argv) {
printHelp();
return 0;
case 'n':
#ifdef _OPENMP
max_threads = atoi(optarg);
#else
std::cerr << "OpenMP is disabled - using single thread only\n";
#endif
break;
default:
return EXIT_FAILURE;
@@ -93,23 +87,23 @@ int main(int argc, char **argv) {
#pragma omp parallel for
for (int f = argOffset; f < argc; f ++){
std::filesystem::path inputFileName = argv[f];
Opm::filesystem::path inputFileName = argv[f];
std::filesystem::path esmryFileName = inputFileName.parent_path() / inputFileName.stem();
esmryFileName = esmryFileName += ".ESMRY";
Opm::filesystem::path lodFileName = inputFileName.parent_path() / inputFileName.stem();
lodFileName = lodFileName += ".LODSMRY";
if (Opm::EclIO::fileExists(esmryFileName) && (force))
remove (esmryFileName);
if (Opm::EclIO::fileExists(lodFileName) && (force))
remove (lodFileName);
Opm::EclIO::ESmry smryFile(argv[f]);
if (!smryFile.make_esmry_file()){
if (!smryFile.make_lodsmry_file()){
std::cout << "\n! Warning, smspec already have one lod file, existing kept use option -f to replace this" << std::endl;
}
}
auto lap1 = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds1 = lap1-lap0;
std::cout << "\nruntime for creating " << (argc-argOffset) << " ESMRY files: " << elapsed_seconds1.count() << " seconds\n" << std::endl;
std::cout << "\nruntime for creating " << (argc-argOffset) << " LODSMRY files: " << elapsed_seconds1.count() << " seconds\n" << std::endl;
return 0;
}

View File

@@ -19,14 +19,14 @@
#include <opm/output/eclipse/EclipseIO.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/Parser/ParseContext.hpp>
#include <opm/input/eclipse/Parser/ErrorGuard.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
#include <opm/parser/eclipse/Parser/ErrorGuard.hpp>
#include <opm/input/eclipse/Python/Python.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
#include <opm/parser/eclipse/Python/Python.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
#include <opm/common/OpmLog/OpmLog.hpp>
#include <opm/msim/msim.hpp>
@@ -51,8 +51,8 @@ int main(int /* argc */, char** argv) {
error_guard.terminate();
}
Opm::msim msim(state, schedule);
Opm::msim msim(state);
Opm::EclipseIO io(state, state.getInputGrid(), schedule, summary_config);
msim.run(io, false);
msim.run(schedule, io, false);
}

View File

@@ -20,18 +20,16 @@
#include <vector>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <fmt/format.h>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/Parser/ParseContext.hpp>
#include <opm/input/eclipse/Parser/ErrorGuard.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
#include <opm/parser/eclipse/Parser/ErrorGuard.hpp>
#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
struct keyword {
@@ -51,7 +49,7 @@ struct keyword {
};
std::vector<keyword> load_deck(const std::string& deck_file) {
std::vector<keyword> load_deck(const char * deck_file) {
Opm::ParseContext parseContext;
Opm::ErrorGuard errors;
Opm::Parser parser;
@@ -75,7 +73,7 @@ std::vector<keyword> load_deck(const std::string& deck_file) {
}
std::size_t make_deck_hash(const std::vector<keyword>& keywords) {
std::size_t deck_hash(const std::vector<keyword>& keywords) {
std::stringstream ss;
for (const auto& kw : keywords)
ss << kw.content_hash;
@@ -84,14 +82,14 @@ std::size_t make_deck_hash(const std::vector<keyword>& keywords) {
}
void print_keywords(const std::vector<keyword>& keywords, std::size_t deck_hash, bool location_info) {
void print_keywords(const std::vector<keyword>& keywords, bool location_info) {
for (const auto& kw : keywords) {
if (location_info)
fmt::print("{:8s} : {}:{} {} \n", kw.name, kw.filename, kw.line_number, kw.content_hash);
else
fmt::print("{:8s} : {} \n", kw.name, kw.content_hash);
}
fmt::print("\n{:8s} : {}\n", "Total", deck_hash);
fmt::print("\n{:8s} : {}\n", "Total", deck_hash(keywords));
}
@@ -120,12 +118,6 @@ Options:
-l : Add filename and linenumber information to each keyword.
-s : Short form - only print the hash of the complete deck.
-S : Silent form - will not print any deck output.
It is possible to add multiple deck arguments, they are then scanned repeatedly,
and the decks are compared. In the case of multiple deck arguments the exit
status of the program will be zero if all are equal and nonzero in case of
differences.
)";
std::cerr << help_text << std::endl;
@@ -137,11 +129,10 @@ int main(int argc, char** argv) {
int arg_offset = 1;
bool location_info = false;
bool short_form = false;
bool silent = false;
while (true) {
int c;
c = getopt(argc, argv, "lsS");
c = getopt(argc, argv, "ls");
if (c == -1)
break;
@@ -152,53 +143,16 @@ int main(int argc, char** argv) {
case 's':
short_form = true;
break;
case 'S':
silent = true;
break;
}
}
arg_offset = optind;
if (arg_offset >= argc)
print_help_and_exit();
std::vector<std::pair<std::string, std::size_t>> deck_hash_table;
for (int iarg = arg_offset; iarg < argc; iarg++) {
const std::string deck_file = argv[iarg];
auto keywords = load_deck(deck_file);
auto deck_hash = make_deck_hash(keywords);
deck_hash_table.emplace_back(deck_file, deck_hash);
if (silent)
continue;
if (short_form)
std::cout << deck_hash << std::endl;
else
print_keywords(keywords, deck_hash, location_info);
}
if (deck_hash_table.size() > 1) {
bool equal = true;
const auto& [first_deck, first_hash] = deck_hash_table[0];
for (std::size_t index = 1; index < deck_hash_table.size(); index++) {
const auto& [deck, hash] = deck_hash_table[index];
if (first_hash != hash)
equal = false;
if (silent)
continue;
fmt::print("{} {} {}\n",
first_deck,
(first_hash == hash) ? "==" : "!=",
deck);
}
if (equal)
std::exit(EXIT_SUCCESS);
else
std::exit(EXIT_FAILURE);
}
auto keywords = load_deck(argv[arg_offset]);
if (short_form)
std::cout << deck_hash(keywords) << std::endl;
else
print_keywords(keywords, location_info);
}

View File

@@ -21,15 +21,15 @@
#include <iomanip>
#include <chrono>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/Parser/ParseContext.hpp>
#include <opm/input/eclipse/Parser/ErrorGuard.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Python/Python.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
#include <opm/parser/eclipse/Parser/ErrorGuard.hpp>
#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/Python/Python.hpp>
#include <opm/common/OpmLog/OpmLog.hpp>
#include <opm/common/OpmLog/StreamLog.hpp>

View File

@@ -17,21 +17,21 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <filesystem>
#include <fstream>
#include <iostream>
#include <getopt.h>
#include <opm/input/eclipse/Parser/ParserKeywords/I.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/P.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/G.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/Parser/ErrorGuard.hpp>
#include <opm/input/eclipse/Parser/ParseContext.hpp>
#include <opm/input/eclipse/Parser/InputErrorAction.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#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>
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
#include <opm/parser/eclipse/Parser/InputErrorAction.hpp>
#include <opm/parser/eclipse/Deck/Deck.hpp>
namespace fs = std::filesystem;
namespace fs = Opm::filesystem;
Opm::Deck pack_deck( const char * deck_file, std::ostream& os) {
Opm::ParseContext parseContext(Opm::InputError::WARN);
@@ -154,14 +154,16 @@ int main(int argc, char** argv) {
}
using IMPORT = Opm::ParserKeywords::IMPORT;
for (const auto& import_keyword : deck.get<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 (const auto& pyaction_keyword : deck.get<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);
}
@@ -169,7 +171,7 @@ int main(int argc, char** argv) {
using GDFILE = Opm::ParserKeywords::GDFILE;
if (deck.hasKeyword<GDFILE>()) {
const auto& gdfile_keyword = deck.get<GDFILE>().back();
const auto& gdfile_keyword = deck.getKeyword<GDFILE>();
const auto& fname = gdfile_keyword.getRecord(0).getItem<GDFILE::filename>().get<std::string>(0);
copy_file(input_arg.parent_path(), fname, output_dir);
}

View File

@@ -1,369 +0,0 @@
/*
Copyright 2021 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <getopt.h>
#include <fmt/format.h>
#include <unordered_set>
#include <utility>
#include <opm/input/eclipse/Deck/DeckKeyword.hpp>
#include <opm/input/eclipse/Units/UnitSystem.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/I.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/P.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/G.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/R.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/S.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/Parser/ErrorGuard.hpp>
#include <opm/input/eclipse/Parser/ParseContext.hpp>
#include <opm/input/eclipse/Parser/InputErrorAction.hpp>
#include <opm/input/eclipse/EclipseState/IOConfig/IOConfig.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Deck/FileDeck.hpp>
namespace fs = std::filesystem;
const std::unordered_set<std::string> remove_from_solution = {"EQUIL", "PRESSURE", "SWAT", "SGAS"};
void print_help_and_exit(const std::optional<std::string> error_msg = {}) {
if (error_msg.has_value()) {
std::cerr << "Error:" << std::endl;
std::cerr << error_msg.value() << std::endl;
std::cerr << "------------------------------------------------------" << std::endl;
}
std::string keep_keywords;
for (const auto& kw : Opm::FileDeck::rst_keep_in_solution)
keep_keywords += kw + " ";
const std::string help_text = fmt::format(R"(
The rst_deck program will load a simulation deck and parameters for a restart
and reformat the deck to become a restart deck. Before the updated deck is
output the program will update the SOLUTION and SCHEDULE sections. All keywords
from the SOLUTION section will be cleared out(1) and a RESTART keyword will be
inserted. In the SCHEDULE section the program can either remove all keywords up
until the restart date, or alternatively insert SKIPREST immediately following
the SCHEDULE keyword.
When creating the updated restart deck the program can either link to unmodified
include files with INCLUDE statements, create a copy of deck structure in an
alternative location or create one large file with all keywords in the same
file. Apart from the alterations to support restart the output deck will be
equivalent to the input deck, but formatting is not retained and comments have
been stripped away.
Arguments:
1. The data file we are starting with.
2. The restart source; this can either be a basename with an optional path
prefix and a :N to restart from step N; alternatively you can point to an
existing restart file. If you point to an existing restart file the input
will be validated in several ways:
a) Unified/multiple files will be checked against the UNIFIN setting of
the deck.
b) Formatted/unformatted will be checked against the FMTIn setting of the
deck.
c) If a single file like /path/to/case/HISTORY.X0067 is given as argument the
:N notation to denote report step should not be used.
If the restart argument is given as the path to an existing file the content
of the RESTART keyword will be updated to contain the correct path from the
location of the restart deck to the location of the restart file. This path
awareness will be fooled if the restart deck is redirected from stdout to a
path different from cwd. If the restart argument is given as an absolute
filename the RESTART keyword will have an absolute path, if the restart
argument is a relative path the RESTART keyword will get a relative path -
although an absolute path will be used if the restart file and the output
deck have different roots. If the restart argument is given as a string not
pointing to an existing file it will be inserted verbatim in the restart
deck.
A restart step value of 0 is interpreted as a dry run - a deck which has not
been set up for restart will be written out.
3. Basename of the restart deck we create, can optionally contain a path prefix;
the path will be created if it does not already exist. This argument is
optional, if it is not provided the program will dump a restart deck on
stdout. If the argument corresponds to an existing directory the restart case
will get the same name as the base case.
Options:
-s: Manipulate the SCHEDULE section by inserting a SKIPREST keyword immediately
following the SCHEDULE keyword. If the -s option is not used the SCHEDULE
section will be modified by removing all keywords until we reach the restart
date.
-m: [share|inline|copy] The restart deck can reuse the unmodified include files
from the base case, this is mode 'share' and is the default. With mode
'inline' the restart deck will be one long file and with mode 'copy' the
file structure of the base case will be retained. The default if no -m
option is given is the 'share' mode.
In the case of 'share' and 'copy' the correct path to include files will be
negotiated based on the path given to the output case in the third argument.
If the restart deck is passed to stdout the include files will be resolved
based on output in cwd.
Example:
rst_deck /path/to/history/HISTORY.DATA rst/HISTORY:30 /path/to/rst/RESTART -s
1: The program has a compiled list of keywords which will be retained in the
SOLUTION section. The current value of that list is: {}
)", keep_keywords);
std::cerr << help_text << std::endl;
if (error_msg.has_value())
std::exit(EXIT_FAILURE);
std::exit(EXIT_SUCCESS);
}
struct Options {
std::string input_deck;
std::string restart_base;
int restart_step;
std::optional<std::string> target_path;
std::optional<std::string> target_fname;
Opm::FileDeck::OutputMode mode{Opm::FileDeck::OutputMode::SHARE};
bool skiprest{false};
};
Opm::Deck load_deck(const Options& opt) {
Opm::ParseContext parseContext(Opm::InputError::WARN);
Opm::ErrorGuard errors;
Opm::Parser parser;
/* Use the same default ParseContext as flow. */
parseContext.update(Opm::ParseContext::PARSE_RANDOM_SLASH, Opm::InputError::IGNORE);
parseContext.update(Opm::ParseContext::PARSE_MISSING_DIMS_KEYWORD, Opm::InputError::WARN);
parseContext.update(Opm::ParseContext::SUMMARY_UNKNOWN_WELL, Opm::InputError::WARN);
parseContext.update(Opm::ParseContext::SUMMARY_UNKNOWN_GROUP, Opm::InputError::WARN);
return parser.parseFile(opt.input_deck, parseContext, errors);
}
Opm::FileDeck::OutputMode mode(const std::string& mode_arg) {
if (mode_arg == "inline")
return Opm::FileDeck::OutputMode::INLINE;
if (mode_arg == "share")
return Opm::FileDeck::OutputMode::SHARE;
if (mode_arg == "copy")
return Opm::FileDeck::OutputMode::COPY;
print_help_and_exit(fmt::format("Mode argument: \'{}\' not recognized. Valid options are inline|share|copy", mode_arg));
return Opm::FileDeck::OutputMode::INLINE;
}
std::optional<std::size_t> verify_extension(const std::string& extension, bool unified, bool formatted) {
if (unified) {
if (formatted) {
if (extension == ".FUNRST")
return std::nullopt;
print_help_and_exit("Deck has specified formatted unified input - expected restart extension: .FUNRST");
}
if (extension == ".UNRST")
return std::nullopt;
print_help_and_exit("Deck has expected unformatted unified input - expected restart extension: .UNRST");
}
std::size_t report_step;
if ((formatted && (extension[1] == 'F')) || (!formatted && (extension[1] == 'X'))) {
try {
report_step = std::stoi(extension.substr(2));
return report_step;
}
catch (...) {}
}
print_help_and_exit("Deck has specified multiple input files - expected restart extension: .Xnnnn / .Fnnnn");
return std::nullopt;
}
bool same_mount(const fs::path& p1, const fs::path& p2) {
auto abs1 = fs::absolute(p1);
auto abs2 = fs::absolute(p2);
auto iter1 = abs1.begin(); iter1++;
auto iter2 = abs2.begin(); iter2++;
auto mnt1 = *iter1;
auto mnt2 = *iter2;
return (mnt1 == mnt2);
}
void update_restart_path(Options& opt, const std::string& restart_arg, const Opm::IOConfig& io_config) {
std::string base;
std::optional<std::size_t> rst_step;
auto sep_pos = restart_arg.rfind(':');
auto base_arg = restart_arg.substr(0, sep_pos);
if (fs::exists(base_arg)) {
auto unif = io_config.getUNIFIN();
auto fmt = io_config.getFMTIN();
auto path = fs::path(base_arg);
auto extension = path.extension();
rst_step = verify_extension(extension, unif, fmt);
if (path.is_absolute()) {
path.replace_extension();
base = path;
} else {
auto target_path = fs::current_path();
if (opt.target_path.has_value())
target_path = fs::path(opt.target_path.value());
if (same_mount(path, target_path))
base = fs::relative(path, target_path).replace_extension();
else
base = fs::canonical(fs::absolute(path)).replace_extension();
}
} else
base = base_arg;
if (!rst_step.has_value()) {
if (sep_pos == std::string::npos)
print_help_and_exit(fmt::format("Expected restart argument on the form: BASE:NUMBER - e.g. HISTORY:60"));
rst_step = std::stoi(restart_arg.substr(sep_pos + 1));
}
opt.restart_step = rst_step.value();
opt.restart_base = base;
}
std::pair<Options, std::string> load_options(int argc, char **argv) {
Options opt;
while (true) {
int c;
c = getopt(argc, argv, "hm:s");
if (c == -1)
break;
switch(c) {
case 'm':
opt.mode = mode(optarg);
break;
case 's':
opt.skiprest = true;
break;
case 'h':
print_help_and_exit();
break;
}
}
auto arg_offset = optind;
if (arg_offset >= argc)
print_help_and_exit();
opt.input_deck = argv[arg_offset];
std::string restart_arg = argv[arg_offset + 1];
if ((argc - arg_offset) >= 3) {
auto target_arg = argv[arg_offset + 2];
if (fs::is_directory(target_arg)) {
opt.target_path = target_arg;
opt.target_fname = fs::path(opt.input_deck).filename();
} else {
auto target_path = fs::path( fs::absolute(target_arg) );
opt.target_path = fs::absolute(target_path.parent_path());
opt.target_fname = target_path.filename();
}
if (opt.mode == Opm::FileDeck::OutputMode::COPY) {
auto target = fs::path(target_arg).parent_path();
if (fs::exists(target)) {
auto input = fs::path(opt.input_deck).parent_path();
if (fs::equivalent(target, input))
opt.mode = Opm::FileDeck::OutputMode::SHARE;
}
}
} else {
if (opt.mode == Opm::FileDeck::OutputMode::COPY)
print_help_and_exit("When writing output to stdout you must use inline|share mode");
}
return {opt, restart_arg};
}
void update_solution(const Options& opt, Opm::FileDeck& file_deck)
{
if (opt.restart_step == 0)
return;
const auto solution = file_deck.find("SOLUTION");
if (!solution.has_value())
print_help_and_exit(fmt::format("Could not find SOLUTION section in input deck: {}", opt.input_deck));
auto summary = file_deck.find("SUMMARY");
if (!summary.has_value())
print_help_and_exit(fmt::format("Could not find SUMMARY section in input deck: {}", opt.input_deck));
file_deck.rst_solution(opt.restart_base, opt.restart_step);
}
void update_schedule(const Options& opt, Opm::FileDeck& file_deck)
{
if (opt.restart_step == 0)
return;
if (opt.skiprest)
file_deck.insert_skiprest();
else
file_deck.skip(opt.restart_step);
}
int main(int argc, char** argv) {
auto [options, restart_arg] = load_options(argc, argv);
auto deck = load_deck(options);
Opm::FileDeck file_deck(deck);
update_restart_path(options, restart_arg, Opm::IOConfig(deck));
update_solution(options, file_deck);
update_schedule(options, file_deck);
if (!options.target_path.has_value())
file_deck.dump_stdout(fs::current_path(), options.mode);
else
file_deck.dump( options.target_path.value(), options.target_fname.value(), options.mode);
}

View File

@@ -17,24 +17,23 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <chrono>
#include <sstream>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/Parser/ParseContext.hpp>
#include <opm/input/eclipse/Parser/ErrorGuard.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Python/Python.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
#include <opm/parser/eclipse/Parser/ErrorGuard.hpp>
#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/Python/Python.hpp>
#include <opm/common/OpmLog/OpmLog.hpp>
#include <opm/common/OpmLog/StreamLog.hpp>
#include <opm/common/OpmLog/LogUtil.hpp>
#include <opm/common/utility/FileSystem.hpp>
inline void createDot(const Opm::Schedule& schedule, const std::string& casename)
@@ -85,7 +84,6 @@ inline void createDot(const Opm::Schedule& schedule, const std::string& casename
}
os << "}\n";
std::cout << "complete." << std::endl;
std::cout << "Convert output to PDF with 'dot -Tpdf " << casename << ".gv > " << casename << ".pdf'\n" << std::endl;
}
@@ -124,7 +122,7 @@ int main(int argc, char** argv)
for (int iarg = 1; iarg < argc; iarg++) {
const std::string filename = argv[iarg];
const auto sched = loadSchedule(filename);
const auto casename = std::filesystem::path(filename).stem();
const auto casename = Opm::filesystem::path(filename).stem();
createDot(sched, casename);
}
} catch (const std::exception& e) {

View File

@@ -3,7 +3,7 @@
declare -A configurations
declare -A EXTRA_MODULE_FLAGS
EXTRA_MODULE_FLAGS[opm-simulators]="-DBUILD_FLOW_VARIANTS=ON -DOPM_ENABLE_PYTHON=ON -DBUILD_FLOW_POLY_GRID=ON"
EXTRA_MODULE_FLAGS[opm-simulators]="-DBUILD_EBOS_EXTENSIONS=ON -DBUILD_EBOS_DEBUG_EXTENSIONS=ON -DBUILD_FLOW_VARIANTS=ON -DOPM_ENABLE_PYTHON=ON -DBUILD_FLOW_POLY_GRID=ON"
EXTRA_MODULE_FLAGS[opm-common]="-DOPM_ENABLE_PYTHON=ON -DOPM_ENABLE_EMBEDDED_PYTHON=ON -DOPM_INSTALL_PYTHON=ON"
# Parse revisions from trigger comment and setup arrays
@@ -16,12 +16,7 @@ function parseRevisions {
do
if grep -qi "$upstream=" <<< $ghprbCommentBody
then
if test -n "$absolute_revisions"
then
upstreamRev[$upstream]=`echo $ghprbCommentBody | sed -r "s/.*${upstream,,}=([^ ]+).*/\1/g"`
else
upstreamRev[$upstream]=pull/`echo $ghprbCommentBody | sed -r "s/.*${upstream,,}=([0-9]+).*/\1/g"`/merge
fi
upstreamRev[$upstream]=pull/`echo $ghprbCommentBody | sed -r "s/.*${upstream,,}=([0-9]+).*/\1/g"`/merge
fi
done
if grep -q "with downstreams" <<< $ghprbCommentBody
@@ -30,13 +25,8 @@ function parseRevisions {
do
if grep -qi "$downstream=" <<< $ghprbCommentBody
then
if test -n "$absolute_revisions"
then
downstreamRev[$downstream]=`echo $ghprbCommentBody | sed -r "s/.*${downstream,,}=([^ ]+).*/\1/g"`
else
downstreamRev[$downstream]=pull/`echo $ghprbCommentBody | sed -r "s/.*${downstream,,}=([0-9]+).*/\1/g"`/merge
fi
fi
downstreamRev[$downstream]=pull/`echo $ghprbCommentBody | sed -r "s/.*${downstream,,}=([0-9]+).*/\1/g"`/merge
fi
done
fi
@@ -232,12 +222,8 @@ function build_downstreams {
# $1 = Name of main module
function build_module_full {
PY_MAJOR=`python3 --version | awk -F ' ' '{print $2}' | awk -F '.' '{print $1}'`
PY_MINOR=`python3 --version | awk -F ' ' '{print $2}' | awk -F '.' '{print $2}'`
for configuration in ${!configurations[@]}
do
export PYTHONPATH="$WORKSPACE/$configuration/install/lib/python$PY_MAJOR.$PY_MINOR/site-packages"
# Build upstream modules
build_upstreams

View File

@@ -14,9 +14,6 @@ then
if ! test -d $WORKSPACE/deps/opm-tests
then
cp $OPM_TESTS_ROOT_PREDEFINED $WORKSPACE/deps/opm-tests -R
pushd $WORKSPACE/deps/opm-tests
echo "opm-tests revision: `git rev-parse HEAD`"
popd
fi
else
# We need a full repo checkout
@@ -32,9 +29,6 @@ else
if ! test -d $WORKSPACE/deps/opm-tests
then
cp $OPM_TESTS_ROOT $WORKSPACE/deps/opm-tests -R
pushd $WORKSPACE/deps/opm-tests
echo "opm-tests-revision: `git rev-parse HEAD`"
popd
fi
fi
OPM_TESTS_ROOT=$WORKSPACE/deps/opm-tests

View File

@@ -22,7 +22,6 @@ upstreamRev[opm-grid]=master
upstreamRev[opm-models]=master
upstreamRev[opm-simulators]=master
upstreamRev[opm-upscaling]=master
upstreamRev[opm-tests]=master
# Setup revision tables
parseRevisions
@@ -38,20 +37,14 @@ do
prnumber=${rev//[!0-9]/}
BRANCH_NAME="${BRANCH_NAME}_${repo}_$prnumber"
test -n "$REASON" && REASON+=" "
REASON+="PR https://github.com/OPM/$repo/pull/$prnumber\n"
REASON+="https://github.com/OPM/$repo/pull/$prnumber\n"
fi
done
# Do the commit
export REASON
if [ "${upstreamRev[opm-tests]}" == "master" ]
then
export BRANCH_BASE=origin/master
else
export BRANCH_BASE=${upstreamRev[opm-tests]}
fi
export BRANCH_NAME
$WORKSPACE/deps/opm-simulators/tests/update_reference_data.sh $OPM_TESTS_ROOT $WORKSPACE/$configuration/build-opm-simulators $WORKSPACE/$configuration/install/bin/convertECL
$WORKSPACE/deps/opm-simulators/tests/update_reference_data.sh $OPM_TESTS_ROOT
if test $? -eq 5
then
echo "No tests failed - no data to update. Exiting"
@@ -97,7 +90,7 @@ fi
if [ -n "$DATA_PR" ]
then
curl -d "{ \"body\": \"Existing PR https://github.com/OPM/opm-tests/pull/$DATA_PR was updated\" }" -H "Authorization: token ${GH_TOKEN}" -X POST https://api.github.com/repos/OPM/$MAIN_REPO/issues/$PRNUMBER/comments
curl -d "{ \"body\": \"Existing PR https://github.com/OPM/opm-tests/pull/$DATA_PR was updated\" }" -X POST https://api.github.com/repos/OPM/$MAIN_REPO/issues/$PRNUMBER/comments?access_token=$GH_TOKEN
else
git-open-pull -u jenkins4opm --base-account OPM --base-repo opm-tests -r /tmp/cmsg $BRANCH_NAME
fi

View File

@@ -6,19 +6,17 @@
#include <map>
#include <string>
#include <opm/input/eclipse/Parser/ErrorGuard.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/Action/State.hpp>
#include <opm/parser/eclipse/Parser/ErrorGuard.hpp>
#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/output/data/Solution.hpp>
#include <opm/output/data/Wells.hpp>
#include <opm/output/data/Groups.hpp>
#include <opm/input/eclipse/Deck/UDAValue.hpp>
#include <opm/parser/eclipse/Deck/UDAValue.hpp>
namespace Opm {
@@ -29,7 +27,10 @@ class Parser;
class Python;
class SummaryState;
class UDQState;
class WellTestState;
namespace Action {
class State;
}
class msim {
@@ -37,30 +38,24 @@ public:
using well_rate_function = double(const EclipseState&, const Schedule&, const SummaryState& st, const data::Solution&, size_t report_step, double seconds_elapsed);
using solution_function = void(const EclipseState&, const Schedule&, data::Solution&, size_t report_step, double seconds_elapsed);
msim(const EclipseState& state, const Schedule& schedule_arg);
msim(const EclipseState& state);
Opm::UDAValue uda_val();
void well_rate(const std::string& well, data::Rates::opt rate, std::function<well_rate_function> func);
void solution(const std::string& field, std::function<solution_function> func);
void run(EclipseIO& io, bool report_only);
void post_step(data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, const time_point& sim_time);
void run(Schedule& schedule, EclipseIO& io, bool report_only);
void post_step(Schedule& schedule, Action::State& action_state, SummaryState& st, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, const time_point& sim_time);
private:
void run_step(WellTestState& wtest_state, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, EclipseIO& io);
void run_step(WellTestState& wtest_state, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double dt, EclipseIO& io);
void output(WellTestState& wtest_state, const UDQState& udq_state, size_t report_step, bool substep, double seconds_elapsed, const data::Solution& sol, const data::Wells& well_data, const data::GroupAndNetworkValues& group_data, EclipseIO& io);
void simulate(data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double seconds_elapsed, double time_step);
void run_step(const Schedule& schedule, Action::State& action_state, SummaryState& st, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, EclipseIO& io) const;
void run_step(const Schedule& schedule, Action::State& action_state, SummaryState& st, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double dt, EclipseIO& io) const;
void output(Action::State& action_state, SummaryState& st, const UDQState& udq_state, size_t report_step, bool substep, double seconds_elapsed, const data::Solution& sol, const data::Wells& well_data, const data::GroupAndNetworkValues& group_data, EclipseIO& io) const;
void simulate(const Schedule& schedule, const SummaryState& st, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double seconds_elapsed, double time_step) const;
EclipseState state;
std::map<std::string, std::map<data::Rates::opt, std::function<well_rate_function>>> well_rates;
std::map<std::string, std::function<solution_function>> solutions;
public:
Schedule schedule;
Action::State action_state;
SummaryState st;
};
}

View File

@@ -18,7 +18,6 @@
*/
#include <iostream>
#include <utility>
#include <opm/output/eclipse/Inplace.hpp>
#include <opm/output/eclipse/EclipseIO.hpp>
@@ -27,30 +26,30 @@
#include <opm/output/data/Solution.hpp>
#include <opm/output/data/Wells.hpp>
#include <opm/output/data/Groups.hpp>
#include <opm/input/eclipse/Python/Python.hpp>
#include <opm/parser/eclipse/Python/Python.hpp>
#include <opm/input/eclipse/Schedule/Action/ActionContext.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQState.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQConfig.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTestState.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/Parser/ParseContext.hpp>
#include <opm/input/eclipse/Parser/ErrorGuard.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/SummaryState.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Action/ActionContext.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Action/State.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQState.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQConfig.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
#include <opm/parser/eclipse/Parser/ErrorGuard.hpp>
#include <opm/msim/msim.hpp>
namespace Opm {
msim::msim(const EclipseState& state_arg, const Schedule& schedule_arg)
: state(state_arg)
, schedule(schedule_arg)
, st(TimeService::from_time_t(this->schedule.getStartTime()))
msim::msim(const EclipseState& state_arg) :
state(state_arg)
{}
void msim::run(EclipseIO& io, bool report_only) {
void msim::run(Schedule& schedule, EclipseIO& io, bool report_only) {
const double week = 7 * 86400;
data::Solution sol;
UDQState udq_state(this->schedule.getUDQConfig(0).params().undefinedValue());
WellTestState wtest_state;
SummaryState st(TimeService::from_time_t(schedule.getStartTime()));
UDQState udq_state(schedule.getUDQConfig(0).params().undefinedValue());
Action::State action_state;
Python python;
io.writeInitial();
@@ -58,13 +57,13 @@ void msim::run(EclipseIO& io, bool report_only) {
data::Wells well_data;
data::GroupAndNetworkValues group_nwrk_data;
if (report_only)
run_step(wtest_state, udq_state, sol, well_data, group_nwrk_data, report_step, io);
run_step(schedule, action_state, st, udq_state, sol, well_data, group_nwrk_data, report_step, io);
else {
double time_step = std::min(week, 0.5*schedule.stepLength(report_step - 1));
run_step(wtest_state, udq_state, sol, well_data, group_nwrk_data, report_step, time_step, io);
run_step(schedule, action_state, st, udq_state, sol, well_data, group_nwrk_data, report_step, time_step, io);
}
auto sim_time = TimeService::from_time_t( schedule.simTime(report_step) );
post_step(sol, well_data, group_nwrk_data, report_step, sim_time);
post_step(schedule, action_state, st, sol, well_data, group_nwrk_data, report_step, sim_time);
const auto& exit_status = schedule.exitStatus();
if (exit_status.has_value())
return;
@@ -76,33 +75,33 @@ UDAValue msim::uda_val() {
}
void msim::post_step(data::Solution& /* sol */, data::Wells& /* well_data */, data::GroupAndNetworkValues& /* grp_nwrk_data */, size_t report_step, const time_point& sim_time) {
const auto& actions = this->schedule[report_step].actions.get();
void msim::post_step(Schedule& schedule, Action::State& action_state, SummaryState& st, data::Solution& /* sol */, data::Wells& /* well_data */, data::GroupAndNetworkValues& /* grp_nwrk_data */, size_t report_step, const time_point& sim_time) {
const auto& actions = schedule[report_step].actions.get();
if (actions.empty())
return;
Action::Context context( this->st , this->schedule[report_step].wlist_manager.get());
Action::Context context( st , schedule[report_step].wlist_manager.get());
for (const auto& action : actions.pending(this->action_state, std::chrono::system_clock::to_time_t(sim_time))) {
for (const auto& action : actions.pending(action_state, std::chrono::system_clock::to_time_t(sim_time))) {
auto result = action->eval(context);
if (result)
this->schedule.applyAction(report_step, *action, result.wells(), {});
schedule.applyAction(report_step, sim_time, *action, result, {});
}
for (const auto& pyaction : actions.pending_python(this->action_state))
this->schedule.runPyAction(report_step, *pyaction, this->action_state, this->state, this->st);
for (const auto& pyaction : actions.pending_python())
pyaction->run(this->state, schedule, report_step, st);
}
void msim::run_step(WellTestState& wtest_state, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& grp_nwrk_data, size_t report_step, EclipseIO& io) {
this->run_step(wtest_state, udq_state, sol, well_data, grp_nwrk_data, report_step, schedule.stepLength(report_step - 1), io);
void msim::run_step(const Schedule& schedule, Action::State& action_state, SummaryState& st, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& grp_nwrk_data, size_t report_step, EclipseIO& io) const {
this->run_step(schedule, action_state, st, udq_state, sol, well_data, grp_nwrk_data, report_step, schedule.stepLength(report_step - 1), io);
}
void msim::run_step(WellTestState& wtest_state, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double dt, EclipseIO& io) {
double start_time = this->schedule.seconds(report_step - 1);
double end_time = this->schedule.seconds(report_step);
void msim::run_step(const Schedule& schedule, Action::State& action_state, SummaryState& st, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double dt, EclipseIO& io) const {
double start_time = schedule.seconds(report_step - 1);
double end_time = schedule.seconds(report_step);
double seconds_elapsed = start_time;
while (seconds_elapsed < end_time) {
@@ -110,11 +109,11 @@ void msim::run_step(WellTestState& wtest_state, UDQState& udq_state, data::Solut
if ((seconds_elapsed + time_step) > end_time)
time_step = end_time - seconds_elapsed;
this->simulate(sol, well_data, group_nwrk_data, report_step, seconds_elapsed, time_step);
this->simulate(schedule, st, sol, well_data, group_nwrk_data, report_step, seconds_elapsed, time_step);
seconds_elapsed += time_step;
io.summary().eval(this->st,
io.summary().eval(st,
report_step,
seconds_elapsed,
well_data,
@@ -124,9 +123,10 @@ void msim::run_step(WellTestState& wtest_state, UDQState& udq_state, data::Solut
{},
{});
this->schedule.getUDQConfig( report_step ).eval(report_step, schedule.wellMatcher(report_step), this->st, udq_state);
schedule.getUDQConfig( report_step ).eval(report_step, schedule.wellMatcher(report_step), st, udq_state);
this->output(wtest_state,
this->output(action_state,
st,
udq_state,
report_step,
(seconds_elapsed < end_time),
@@ -140,28 +140,27 @@ void msim::run_step(WellTestState& wtest_state, UDQState& udq_state, data::Solut
void msim::output(WellTestState& wtest_state, const UDQState& udq_state, size_t report_step, bool substep, double seconds_elapsed, const data::Solution& sol, const data::Wells& well_data, const data::GroupAndNetworkValues& group_nwrk_data, EclipseIO& io) {
RestartValue value(sol, well_data, group_nwrk_data, {});
io.writeTimeStep(this->action_state,
wtest_state,
this->st,
void msim::output(Action::State& action_state, SummaryState& st, const UDQState& udq_state, size_t report_step, bool substep, double seconds_elapsed, const data::Solution& sol, const data::Wells& well_data, const data::GroupAndNetworkValues& group_nwrk_data, EclipseIO& io) const {
RestartValue value(sol, well_data, group_nwrk_data);
io.writeTimeStep(action_state,
st,
udq_state,
report_step,
substep,
seconds_elapsed,
std::move(value));
value);
}
void msim::simulate(data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& /* group_nwrk_data */, size_t report_step, double seconds_elapsed, double time_step) {
void msim::simulate(const Schedule& schedule, const SummaryState& st, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& /* group_nwrk_data */, size_t report_step, double seconds_elapsed, double time_step) const {
for (const auto& sol_pair : this->solutions) {
auto func = sol_pair.second;
func(this->state, this->schedule, sol, report_step, seconds_elapsed + time_step);
func(this->state, schedule, sol, report_step, seconds_elapsed + time_step);
}
for (const auto& well_pair : this->well_rates) {
const std::string& well_name = well_pair.first;
const auto& sched_well = this->schedule.getWell(well_name, report_step);
const auto& sched_well = schedule.getWell(well_name, report_step);
bool well_open = (sched_well.getStatus() == Well::Status::OPEN);
data::Well& well = well_data[well_name];
@@ -170,7 +169,7 @@ void msim::simulate(data::Solution& sol, data::Wells& well_data, data::GroupAndN
auto func = rate_pair.second;
if (well_open)
well.rates.set(rate, func(this->state, this->schedule, this->st, sol, report_step, seconds_elapsed + time_step));
well.rates.set(rate, func(this->state, schedule, st, sol, report_step, seconds_elapsed + time_step));
else
well.rates.set(rate, 0.0);
}

View File

@@ -53,7 +53,7 @@
do { \
std::ostringstream oss__; \
oss__ << "[" << __FILE__ << ":" << __LINE__ << "] " << message; \
::Opm::OpmLog::error(oss__.str()); \
Opm::OpmLog::error(oss__.str()); \
throw Exception(oss__.str()); \
} while (false)

View File

@@ -23,9 +23,9 @@
#include <cstdint>
#include <string>
namespace Opm {
#include <opm/common/OpmLog/KeywordLocation.hpp>
class KeywordLocation;
namespace Opm {
namespace Log {
namespace MessageType {

View File

@@ -88,8 +88,6 @@ public:
}
static bool stdoutIsTerminal();
private:
static std::shared_ptr<Logger> getLogger();
static std::shared_ptr<Logger> m_logger;

View File

@@ -19,7 +19,7 @@
#ifndef ACTIVEGRIDCELLS_HPP
#define ACTIVEGRIDCELLS_HPP
#include <opm/input/eclipse/EclipseState/Grid/GridDims.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/GridDims.hpp>
#include <array>

View File

@@ -19,15 +19,28 @@
#ifndef OPM_FILESYSTEM_HPP
#define OPM_FILESYSTEM_HPP
#if __cplusplus < 201703L || \
(defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
#include <experimental/filesystem>
#else
#include <filesystem>
#endif
#include <string>
namespace Opm
{
#if __cplusplus < 201703L || \
(defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
namespace filesystem = std::experimental::filesystem;
#else
namespace filesystem = std::filesystem;
#endif
// A poor man's filesystem::unique_path
std::string unique_path(const std::string& input);
} // end namespace Opm

View File

@@ -17,14 +17,14 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_SERIALIZER_HPP
#define OPM_SERIALIZER_HPP
#include <cstring>
#include <string>
#include <unordered_map>
#include <vector>
#ifndef OPM_SERIALIZER_HPP
#define OPM_SERIALIZER_HPP
namespace Opm {
/*
This is a very basic serialization class used to support serialization of

View File

@@ -3,10 +3,6 @@
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <optional>
#include <sstream>
#include <string>
#include <vector>
@@ -107,25 +103,6 @@ inline std::vector<std::string> split_string(const std::string& input,
return result;
}
inline std::string format_double(double d) {
double integral_part;
const double decimal_part = std::modf(d, &integral_part);
if (decimal_part == 0)
return std::to_string(static_cast<int>(d));
else
return std::to_string(d);
}
inline std::optional<double> try_parse_double(const std::string& token) {
char * end_ptr;
auto value = std::strtod(token.c_str(), &end_ptr);
if (std::strlen(end_ptr) == 0)
return value;
return std::nullopt;
}
}
#endif //OPM_UTILITY_STRING_HPP

View File

@@ -24,7 +24,6 @@
#include <ctime>
#include <string>
#include <unordered_map>
#include <opm/input/eclipse/Deck/DeckRecord.hpp>
namespace Opm {
@@ -39,13 +38,7 @@ namespace Opm {
std::time_t advance(const std::time_t tp, const double sec);
std::time_t makeUTCTime(std::tm timePoint);
const std::unordered_map<std::string , int>& eclipseMonthIndices();
const std::unordered_map<int, std::string>& eclipseMonthNames();
int eclipseMonth(const std::string& name);
bool valid_month(const std::string& month_name);
std::time_t mkdatetime(int in_year, int in_month, int in_day, int hour, int minute, int second);
std::time_t mkdate(int in_year, int in_month, int in_day);
std::time_t timeFromEclipse(const DeckRecord &dateRecord);
}
class TimeStampUTC
@@ -122,8 +115,6 @@ namespace Opm {
TimeStampUTC operator+(const TimeStampUTC& lhs, std::chrono::duration<double> delta);
std::time_t asTimeT(const TimeStampUTC& tp);
std::time_t asLocalTimeT(const TimeStampUTC& tp);
time_point asTimePoint(const TimeStampUTC& tp);
} // namespace Opm

View File

@@ -5,7 +5,7 @@
// Created: Mon Jun 29 15:28:59 2009
//
// Author(s): Atgeirr F Rasmussen <atgeirr@sintef.no>
// Bård Skaflestad <bard.skaflestad@sintef.no>
// B<EFBFBD>rd Skaflestad <bard.skaflestad@sintef.no>
//
// $Date$
//

0
opm/common/utility/numeric/calculateCellVol.hpp Normal file → Executable file
View File

View File

@@ -4,7 +4,7 @@
//
// Created: Tue Jun 2 16:00:21 2009
//
// Author(s): Bård Skaflestad <bard.skaflestad@sintef.no>
// Author(s): B<EFBFBD>rd Skaflestad <bard.skaflestad@sintef.no>
// Atgeirr F Rasmussen <atgeirr@sintef.no>
//
// $Date$

View File

@@ -4,7 +4,7 @@
//
// Created: Tue Jun 2 19:11:11 2009
//
// Author(s): Bård Skaflestad <bard.skaflestad@sintef.no>
// Author(s): B<EFBFBD>rd Skaflestad <bard.skaflestad@sintef.no>
// Atgeirr F Rasmussen <atgeirr@sintef.no>
//
// $Date$

View File

@@ -4,7 +4,7 @@
//
// Created: Tue Jun 2 19:06:46 2009
//
// Author(s): Bård Skaflestad <bard.skaflestad@sintef.no>
// Author(s): B<EFBFBD>rd Skaflestad <bard.skaflestad@sintef.no>
// Atgeirr F Rasmussen <atgeirr@sintef.no>
//
// $Date$
@@ -142,7 +142,7 @@ namespace Opm {
assignments.push_back(std::make_pair(name, value));
continue;
}
OpmLog::warning("Too many assignments (' "
OpmLog::warning("Too many assignements (' "
+ ID_delimiter_assignment
+ "') detected in argument " + to_string(i));
}

View File

@@ -4,7 +4,7 @@
//
// Created: Tue Jun 2 19:05:54 2009
//
// Author(s): Bård Skaflestad <bard.skaflestad@sintef.no>
// Author(s): B<EFBFBD>rd Skaflestad <bard.skaflestad@sintef.no>
// Atgeirr F Rasmussen <atgeirr@sintef.no>
//
// $Date$

View File

@@ -4,7 +4,7 @@
//
// Created: Tue Jun 2 19:05:02 2009
//
// Author(s): Bård Skaflestad <bard.skaflestad@sintef.no>
// Author(s): B<EFBFBD>rd Skaflestad <bard.skaflestad@sintef.no>
// Atgeirr F Rasmussen <atgeirr@sintef.no>
//
// $Date$

View File

@@ -4,7 +4,7 @@
//
// Created: Tue Jun 2 19:04:15 2009
//
// Author(s): Bård Skaflestad <bard.skaflestad@sintef.no>
// Author(s): B<EFBFBD>rd Skaflestad <bard.skaflestad@sintef.no>
// Atgeirr F Rasmussen <atgeirr@sintef.no>
//
// $Date$

View File

@@ -4,7 +4,7 @@
//
// Created: Tue Jun 2 19:02:19 2009
//
// Author(s): Bård Skaflestad <bard.skaflestad@sintef.no>
// Author(s): B<EFBFBD>rd Skaflestad <bard.skaflestad@sintef.no>
// Atgeirr F Rasmussen <atgeirr@sintef.no>
//
// $Date$

View File

@@ -1,37 +0,0 @@
/*
Copyright 2022 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_UTILITY_SHMATCH_HPP
#define OPM_UTILITY_SHMATCH_HPP
#include <string>
namespace Opm {
/*
The shmatch() function is an implementation of the shell matching algorithm
used in posix function fnmatch(). It is implemented here based on std::regex
to avoid using a posix function.
*/
bool shmatch(const std::string& pattern, const std::string& symbol);
}
#endif //OPM_UTILITY_STRING_HPP

View File

@@ -1,72 +0,0 @@
/*
Copyright 2021 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DECK_TREE_HPP
#define DECK_TREE_HPP
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <optional>
namespace Opm {
/*
The purpose of the DeckTree class is to maintain a minimal relationship
between the include files in the deck; the sole purpose of this class is to
support writing of decks with the keywords in the correct files.
*/
class DeckTree {
public:
DeckTree() = default;
DeckTree(const std::string&);
const std::string& parent(const std::string& fname) const;
bool includes(const std::string& parent_file, const std::string& include_file) const;
void add_include(std::string parent_file, std::string include_file);
void add_root(const std::string& fname);
bool has_include(const std::string& fname) const;
const std::string& root() const;
private:
class TreeNode {
public:
explicit TreeNode(const std::string& fn);
TreeNode(const std::string& pn, const std::string& fn);
void add_include(const std::string& include_file);
bool includes(const std::string& include_file) const;
std::string fname;
std::optional<std::string> parent;
std::unordered_set<std::string> include_files;
};
std::string add_node(const std::string& fname);
std::optional<std::string> root_file;
std::unordered_map<std::string, TreeNode> nodes;
};
}
#endif /* DECKRECORD_HPP */

View File

@@ -1,91 +0,0 @@
/*
Copyright 2021 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DECKVIEW_HPP
#define DECKVIEW_HPP
#include <opm/input/eclipse/Deck/DeckKeyword.hpp>
#include <unordered_map>
namespace Opm {
class DeckView {
public:
typedef std::vector<std::reference_wrapper<const DeckKeyword>> storage_type;
struct Iterator : public storage_type::iterator {
Iterator(storage_type::const_iterator inner_iter) :
inner(inner_iter)
{}
const DeckKeyword& operator*() { return this->inner->get(); }
const DeckKeyword* operator->() { return &this->inner->get(); }
Iterator& operator++() { ++this->inner; return *this; }
Iterator operator++(int) { auto tmp = *this; ++this->inner; return tmp; }
Iterator& operator--() { --this->inner; return *this; }
Iterator operator--(int) { auto tmp = *this; --this->inner; return tmp; }
Iterator::difference_type operator-(const Iterator &other) { return this->inner - other.inner; }
Iterator operator+(Iterator::difference_type shift) { Iterator tmp = *this; tmp.inner += shift; return tmp;}
friend bool operator== (const Iterator& a, const Iterator& b) { return a.inner == b.inner; };
friend bool operator!= (const Iterator& a, const Iterator& b) { return a.inner != b.inner; };
private:
storage_type::const_iterator inner;
};
Iterator begin() const { return Iterator(this->keywords.begin()); }
Iterator end() const { return Iterator(this->keywords.end()); }
const DeckKeyword& operator[](std::size_t index) const;
DeckView operator[](const std::string& keyword) const;
std::vector<std::size_t> index(const std::string& keyword) const;
std::size_t count(const std::string& keyword) const;
const DeckKeyword& front() const;
const DeckKeyword& back() const;
DeckView() = default;
void add_keyword(const DeckKeyword& kw);
bool has_keyword(const std::string& kw) const;
bool empty() const;
std::size_t size() const;
template<class Keyword>
bool has_keyword() const {
return this->has_keyword( Keyword::keywordName );
}
template<class Keyword>
DeckView get() const {
return this->operator[](Keyword::keywordName);
}
private:
storage_type keywords;
std::unordered_map<std::string, std::vector<std::size_t>> keyword_index;
};
}
#endif

View File

@@ -1,164 +0,0 @@
/*
Copyright 2021 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FILE_DECK_HPP
#define FILE_DECK_HPP
#include <optional>
#include <string>
#include <unordered_set>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <fmt/format.h>
#include <opm/input/eclipse/Deck/DeckKeyword.hpp>
#include <opm/input/eclipse/Deck/FileDeck.hpp>
namespace fs = std::filesystem;
namespace Opm {
class Deck;
class FileDeck {
public:
static const std::unordered_set<std::string> rst_keep_in_solution;
static const std::unordered_set<std::string> rst_keep_in_schedule;
enum class OutputMode {
INLINE = 1,
SHARE = 2,
COPY = 3
};
struct Index {
std::size_t file_index;
std::size_t keyword_index;
Index(std::size_t file_index_arg, std::size_t keyword_index_arg, const FileDeck* deck_arg)
: file_index(file_index_arg)
, keyword_index(keyword_index_arg)
, deck(deck_arg)
{}
Index& operator--();
Index operator--(int);
Index& operator++();
Index operator++(int);
bool operator==(const Index& other) const;
bool operator!=(const Index& other) const;
bool operator<(const Index& other) const;
Index operator+(std::size_t shift) const;
private:
const FileDeck * deck;
};
class Block {
public:
explicit Block(const std::string& filename);
std::size_t size() const;
void load(const Deck& deck, std::size_t deck_index);
std::optional<std::size_t> find(const std::string& keyword, std::size_t keyword_index) const;
bool empty() const;
void erase(const FileDeck::Index& index);
void insert(std::size_t keyword_index, const DeckKeyword& keyword);
void dump(DeckOutput& out) const;
private:
std::string fname;
std::vector<DeckKeyword> keywords;
friend FileDeck;
};
explicit FileDeck(const Deck& deck);
std::optional<Index> find(const std::string& keyword, const Index& offset) const;
std::optional<Index> find(const std::string& keyword) const;
std::size_t count(const std::string& keyword) const;
void erase(const Index& index);
void erase(const Index& begin, const Index& end);
void insert(const Index& index, const DeckKeyword& keyword);
void dump_stdout(const std::string& output_dir, OutputMode mode) const;
void dump(const std::string& dir, const std::string& fname, OutputMode mode) const;
const DeckKeyword& operator[](const Index& index) const;
const Index start() const;
const Index stop() const;
void rst_solution(const std::string& rst_base, int report_step);
void insert_skiprest();
void skip(int report_step);
private:
std::vector<Block> blocks;
std::string input_directory;
std::unordered_set<std::string> modified_files;
DeckTree deck_tree;
struct DumpContext {
std::unordered_map<std::string, std::ofstream> stream_map;
std::unordered_map<std::string, std::string> file_map;
bool has_file(const std::string& fname) const {
return this->file_map.count(fname) > 0;
}
std::optional<std::ofstream *> get_stream(const std::string& deck_name) {
auto name_iter = this->file_map.find(deck_name);
if (name_iter == this->file_map.end())
return {};
return &this->stream_map.at(name_iter->second);
}
std::ofstream& open_file(const std::string& deck_name, const fs::path& output_file)
{
if (this->stream_map.count(output_file.string()) == 0) {
this->file_map.insert(std::make_pair( deck_name, output_file.string() ));
if (!fs::is_directory(output_file.parent_path()))
fs::create_directories(output_file.parent_path());
std::ofstream stream{output_file};
if (!stream)
throw std::logic_error(fmt::format("Opening {} for writing failed", output_file.string()));
this->stream_map.insert(std::make_pair(output_file.string(), std::move(stream)));
}
return this->stream_map.at(output_file.string());
}
};
void dump(std::ostream& os) const;
void dump_shared(std::ostream& stream, const std::string& output_dir) const;
void dump_inline() const;
std::string dump_block(const Block& block, const std::string& dir, const std::optional<std::string>& fname, DumpContext& context) const;
void include_block(const std::string& source_file, const std::string& target_file, const std::string& dir, DumpContext& context) const;
};
}
#endif

View File

@@ -1,154 +0,0 @@
/*
Copyright (C) 2017 TNO
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_AQUIFERCT_HPP
#define OPM_AQUIFERCT_HPP
/*
The AquiferCT which stands for AquiferCarterTracy is a data container object meant to hold the data for the aquifer carter tracy model.
This includes the logic for parsing as well as the associated tables. It is meant to be used by opm-grid and opm-simulators in order to
implement the Carter Tracy analytical aquifer model in OPM Flow.
*/
#include <cstddef>
#include <optional>
#include <vector>
namespace Opm {
class Deck;
class DeckRecord;
class TableManager;
}
namespace Opm { namespace RestartIO {
class RstAquifer;
}} // Opm::RestartIO
namespace Opm {
class AquiferCT {
public:
struct AQUCT_data
{
friend class AquiferCT;
AQUCT_data() = default;
AQUCT_data(const DeckRecord& record, const TableManager& tables);
AQUCT_data(const int aqID,
const int infID,
const int pvtID,
const double phi_aq_,
const double d0_,
const double C_t_,
const double r_o_,
const double k_a_,
const double h_,
const double theta_,
const double p0_,
const double T0_);
int aquiferID{};
int inftableID{};
int pvttableID{};
double porosity{};
double datum_depth{};
double total_compr{};
double inner_radius{};
double permeability{};
double thickness{};
double angle_fraction{};
std::optional<double> initial_pressure{};
std::optional<double> initial_temperature{};
std::vector<double> dimensionless_time{};
std::vector<double> dimensionless_pressure{};
static AQUCT_data serializeObject();
double timeConstant() const { return this->time_constant_; }
double influxConstant() const { return this->influx_constant_; }
double waterDensity() const { return this->water_density_; }
double waterViscosity() const { return this->water_viscosity_; }
bool operator==(const AQUCT_data& other) const;
void finishInitialisation(const TableManager& tables);
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(this->aquiferID);
serializer(this->inftableID);
serializer(this->pvttableID);
serializer(this->porosity);
serializer(this->datum_depth);
serializer(this->total_compr);
serializer(this->inner_radius);
serializer(this->permeability);
serializer(this->thickness);
serializer(this->angle_fraction);
serializer(this->initial_pressure);
serializer(this->initial_temperature);
serializer(this->dimensionless_time);
serializer(this->dimensionless_pressure);
serializer(this->time_constant_);
serializer(this->influx_constant_);
serializer(this->water_density_);
serializer(this->water_viscosity_);
}
private:
double time_constant_{};
double influx_constant_{};
double water_density_{};
double water_viscosity_{};
};
AquiferCT() = default;
AquiferCT(const TableManager& tables, const Deck& deck);
AquiferCT(const std::vector<AquiferCT::AQUCT_data>& data);
void loadFromRestart(const RestartIO::RstAquifer& rst,
const TableManager& tables);
static AquiferCT serializeObject();
std::size_t size() const;
std::vector<AquiferCT::AQUCT_data>::const_iterator begin() const;
std::vector<AquiferCT::AQUCT_data>::const_iterator end() const;
const std::vector<AquiferCT::AQUCT_data>& data() const;
bool operator==(const AquiferCT& other) const;
bool hasAquifer(const int aquID) const;
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer.vector(m_aquct);
}
private:
std::vector<AquiferCT::AQUCT_data> m_aquct;
};
}
#endif

View File

@@ -1,133 +0,0 @@
/*
Copyright (C) 2017 TNO
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_AQUIFERFETP_HPP
#define OPM_AQUIFERFETP_HPP
/*
The Aquiferfetp which stands for AquiferFetkovich is a data container object meant to hold the data for the fetkovich aquifer model.
This includes the logic for parsing as well as the associated tables. It is meant to be used by opm-grid and opm-simulators in order to
implement the Fetkovich analytical aquifer model in OPM Flow.
*/
#include <cstddef>
#include <optional>
#include <vector>
namespace Opm {
class Deck;
class DeckRecord;
class TableManager;
}
namespace Opm { namespace RestartIO {
class RstAquifer;
}} // Opm::RestartIO
namespace Opm {
class Aquifetp {
public:
struct AQUFETP_data
{
friend class Aquifetp;
AQUFETP_data() = default;
AQUFETP_data(const DeckRecord& record, const TableManager& tables);
AQUFETP_data(const int aquiferID_,
const int pvttableID_,
const double J_,
const double C_t_,
const double V0_,
const double d0_,
const double p0_);
int aquiferID{};
int pvttableID{};
double prod_index{};
double total_compr{};
double initial_watvolume{};
double datum_depth{};
std::optional<double> initial_pressure{};
static AQUFETP_data serializeObject();
double timeConstant() const { return this->time_constant_; }
double waterDensity() const { return this->water_density_; }
double waterViscosity() const { return this->water_viscosity_; }
bool operator==(const AQUFETP_data& other) const;
void finishInitialisation(const TableManager& tables);
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(this->aquiferID);
serializer(this->pvttableID);
serializer(this->prod_index);
serializer(this->total_compr);
serializer(this->initial_watvolume);
serializer(this->datum_depth);
serializer(this->initial_pressure);
serializer(this->time_constant_);
serializer(this->water_density_);
serializer(this->water_viscosity_);
}
private:
double time_constant_{};
double water_density_{};
double water_viscosity_{};
};
Aquifetp() = default;
Aquifetp(const TableManager& tables, const Deck& deck);
explicit Aquifetp(const std::vector<Aquifetp::AQUFETP_data>& data);
void loadFromRestart(const RestartIO::RstAquifer& rst,
const TableManager& tables);
static Aquifetp serializeObject();
const std::vector<Aquifetp::AQUFETP_data>& data() const;
std::size_t size() const;
std::vector<Aquifetp::AQUFETP_data>::const_iterator begin() const;
std::vector<Aquifetp::AQUFETP_data>::const_iterator end() const;
bool operator==(const Aquifetp& other) const;
bool hasAquifer(const int aquID) const;
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer.vector(m_aqufetp);
}
private:
std::vector<Aquifetp::AQUFETP_data> m_aqufetp;
};
}
#endif

View File

@@ -1,71 +0,0 @@
/*
Copyright 2021 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_MAPAXES_HPP
#define OPM_MAPAXES_HPP
#include <array>
#include <optional>
#include <string>
#include <vector>
namespace Opm {
/*
This class will internalize the information needed to transform (X,Y)
coordinates between grid input coordinates and world coordinates based on the
MAPAXES keyword.
*/
class Deck;
namespace EclIO {
class EclFile;
}
class MapAxes {
public:
MapAxes();
MapAxes(double X1, double Y1, double X2, double Y2, double X3, double Y3);
MapAxes(const std::string& mapunits, double X1, double Y1, double X2, double Y2, double X3, double Y3);
explicit MapAxes(EclIO::EclFile& egridfile);
explicit MapAxes(const Deck& deck);
void transform(double& x, double& y) const;
void inv_transform(double& x, double& y) const;
const std::optional<std::string>& mapunits() const;
const std::vector<float>& input() const;
bool operator==(const MapAxes& other) const;
private:
MapAxes(double length_factor, double X1, double Y1, double X2, double Y2, double X3, double Y3);
void init(double length_factor, double X1, double Y1, double X2, double Y2, double X3, double Y3);
std::array<double, 2> origin;
std::array<double, 2> unit_x;
std::array<double, 2> unit_y;
std::vector<float> m_input;
double inv_norm = 1.0;
std::optional<std::string> map_units;
};
}
#endif

View File

@@ -1,177 +0,0 @@
/*
Copyright 2021 NORCE.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_PARSER_MICPPARA_HPP
#define OPM_PARSER_MICPPARA_HPP
namespace Opm {
class Deck;
class MICPpara {
public:
MICPpara();
explicit MICPpara(const Deck& deck);
static MICPpara serializeObject()
{
MICPpara mp;
mp.m_density_biofilm = 100;
mp.m_fitting_factor = 77;
mp.m_maximum_oxygen_concentration = -1;
return mp;
}
double getDensityBiofilm() const {
return m_density_biofilm;
}
double getDensityCalcite() const {
return m_density_calcite;
}
double getDetachmentRate() const {
return m_detachment_rate;
}
double getCriticalPorosity() const {
return m_critical_porosity;
}
double getFittingFactor() const {
return m_fitting_factor;
}
double getHalfVelocityOxygen() const {
return m_half_velocity_oxygen;
}
double getHalfVelocityUrea() const {
return m_half_velocity_urea;
}
double getMaximumGrowthRate() const {
return m_maximum_growth_rate;
}
double getMaximumOxygenConcentration() const {
return m_maximum_oxygen_concentration;
}
double getMaximumUreaConcentration() const {
return m_maximum_urea_concentration;
}
double getMaximumUreaUtilization() const {
return m_maximum_urea_utilization;
}
double getMicrobialAttachmentRate() const {
return m_microbial_attachment_rate;
}
double getMicrobialDeathRate() const {
return m_microbial_death_rate;
}
double getMinimumPermeability() const {
return m_minimum_permeability;
}
double getOxygenConsumptionFactor() const {
return m_oxygen_consumption_factor;
}
double getToleranceBeforeClogging() const {
return m_tolerance_before_clogging;
}
double getYieldGrowthCoefficient() const {
return m_yield_growth_coefficient;
}
bool operator==(const MICPpara& data) const
{
return this->getDensityBiofilm() == data.getDensityBiofilm() &&
this->getDensityCalcite() == data.getDensityCalcite() &&
this->getDetachmentRate() == data.getDetachmentRate() &&
this->getCriticalPorosity() == data.getCriticalPorosity() &&
this->getFittingFactor() == data.getFittingFactor() &&
this->getHalfVelocityOxygen() == data.getHalfVelocityOxygen() &&
this->getHalfVelocityUrea() == data.getHalfVelocityUrea() &&
this->getMaximumGrowthRate() == data.getMaximumGrowthRate() &&
this->getMaximumOxygenConcentration() == data.getMaximumOxygenConcentration() &&
this->getMaximumUreaConcentration() == data.getMaximumUreaConcentration() &&
this->getMaximumUreaUtilization() == data.getMaximumUreaUtilization() &&
this->getMicrobialAttachmentRate() == data.getMicrobialAttachmentRate() &&
this->getMicrobialDeathRate() == data.getMicrobialDeathRate() &&
this->getMinimumPermeability() == data.getMinimumPermeability() &&
this->getOxygenConsumptionFactor() == data.getOxygenConsumptionFactor() &&
this->getToleranceBeforeClogging() == data.getToleranceBeforeClogging() &&
this->getYieldGrowthCoefficient() == data.getYieldGrowthCoefficient();
}
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(m_density_biofilm);
serializer(m_density_calcite);
serializer(m_detachment_rate);
serializer(m_critical_porosity);
serializer(m_fitting_factor);
serializer(m_half_velocity_oxygen);
serializer(m_half_velocity_urea);
serializer(m_maximum_growth_rate);
serializer(m_maximum_oxygen_concentration);
serializer(m_maximum_urea_concentration);
serializer(m_maximum_urea_utilization);
serializer(m_microbial_attachment_rate);
serializer(m_microbial_death_rate);
serializer(m_minimum_permeability);
serializer(m_oxygen_consumption_factor);
serializer(m_tolerance_before_clogging);
serializer(m_yield_growth_coefficient);
}
private:
double m_density_biofilm;
double m_density_calcite;
double m_detachment_rate;
double m_critical_porosity;
double m_fitting_factor;
double m_half_velocity_oxygen;
double m_half_velocity_urea;
double m_maximum_growth_rate;
double m_maximum_oxygen_concentration;
double m_maximum_urea_concentration;
double m_maximum_urea_utilization;
double m_microbial_attachment_rate;
double m_microbial_death_rate;
double m_minimum_permeability;
double m_oxygen_consumption_factor;
double m_tolerance_before_clogging;
double m_yield_growth_coefficient;
};
}
#endif

View File

@@ -1,36 +0,0 @@
/*
Copyright (C) 2020 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_PARSER_SALTSOLUBILITY_TABLE_HPP
#define OPM_PARSER_SALTSOLUBILITY_TABLE_HPP
#include "SimpleTable.hpp"
namespace Opm {
class DeckItem;
class SaltsolTable : public SimpleTable {
public:
SaltsolTable( const DeckItem& item );
const TableColumn& getSaltsolColumn() const;
};
}
#endif

View File

@@ -1,139 +0,0 @@
/*
Copyright (C) 2020 Equinor
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_TRACER_CONFIG_HPP
#define OPM_TRACER_CONFIG_HPP
#include <opm/input/eclipse/EclipseState/Runspec.hpp>
#include <opm/input/eclipse/EclipseState/Tables/TracerVdTable.hpp>
namespace Opm {
class Deck;
class UnitSystem;
class TracerConfig {
public:
struct TracerEntry {
std::string name;
std::string unit_string;
Phase phase = Phase::OIL;
std::optional<std::vector<double>> free_concentration;
std::optional<std::vector<double>> solution_concentration;
std::optional<TracerVdTable> free_tvdp;
std::optional<TracerVdTable> solution_tvdp;
std::string fname() const {
return this->name + "F";
}
TracerEntry() = default;
TracerEntry(const std::string& name_, const std::string& unit_string_,
Phase phase_, std::vector<double> free_concentration_)
: name(name_)
, unit_string(unit_string_)
, phase(phase_)
, free_concentration(std::move(free_concentration_))
{}
TracerEntry(const std::string& name_, const std::string& unit_string_,
Phase phase_, std::vector<double> free_concentration_, std::vector<double> solution_concentration_)
: name(name_)
, unit_string(unit_string_)
, phase(phase_)
, free_concentration(std::move(free_concentration_))
, solution_concentration(std::move(solution_concentration_))
{}
TracerEntry(const std::string& name_, const std::string& unit_string_,
Phase phase_, TracerVdTable free_tvdp_)
: name(name_)
, unit_string(unit_string_)
, phase(phase_)
, free_tvdp(std::move(free_tvdp_))
{}
TracerEntry(const std::string& name_, const std::string& unit_string_,
Phase phase_, TracerVdTable free_tvdp_, TracerVdTable solution_tvdp_)
: name(name_)
, unit_string(unit_string_)
, phase(phase_)
, free_tvdp(std::move(free_tvdp_))
, solution_tvdp(std::move(solution_tvdp_))
{}
TracerEntry(const std::string& name_, const std::string& unit_string_, Phase phase_)
: name(name_)
, unit_string(unit_string_)
, phase(phase_)
{}
bool operator==(const TracerEntry& data) const {
return this->name == data.name &&
this->unit_string == data.unit_string &&
this->phase == data.phase &&
this->free_concentration == data.free_concentration &&
this->solution_concentration == data.solution_concentration &&
this->free_tvdp == data.free_tvdp &&
this->solution_tvdp == data.solution_tvdp;
}
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(name);
serializer(unit_string);
serializer(phase);
serializer(free_concentration);
serializer(solution_concentration);
serializer(this->free_tvdp);
serializer(this->solution_tvdp);
}
};
TracerConfig() = default;
TracerConfig(const UnitSystem& unit_system, const Deck& deck);
static TracerConfig serializeObject();
size_t size() const;
bool empty() const;
const std::vector<TracerEntry>::const_iterator begin() const;
const std::vector<TracerEntry>::const_iterator end() const;
const TracerEntry& operator[](const std::string& name) const;
const TracerEntry& operator[](std::size_t index) const;
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer.vector(tracers);
}
bool operator==(const TracerConfig& data) const;
std::string get_unit_string(const UnitSystem& unit_system, const std::string & tracer_kw) const;
private:
std::vector<TracerEntry> tracers;
};
}
#endif

View File

@@ -1,51 +0,0 @@
/*
Copyright 2021 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACTION_ENUMS_HPP
#define ACTION_ENUMS_HPP
#include <string>
namespace Opm {
namespace Action {
enum class Logical {
AND,
OR,
END
};
enum class Comparator {
EQUAL,
GREATER,
LESS,
GREATER_EQUAL,
LESS_EQUAL,
INVALID
};
Comparator comparator_from_int(int cmp_int);
std::string comparator_as_string(Comparator cmp);
Logical logic_from_int(int int_logic);
}
}
#endif

View File

@@ -1,47 +0,0 @@
/*
Copyright 2021 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SIMULATOR_UPDATE_HPP
#define SIMULATOR_UPDATE_HPP
#include <unordered_set>
namespace Opm {
/*
This struct is used to communicate back from the Schdule::applyAction() what
needs to be updated in the simulator when execution is returned to the
simulator code.
*/
struct SimulatorUpdate {
// These wells have been affected by the ACTIONX and the simulator needs to
// reapply rates and state from the newly updated Schedule object.
std::unordered_set<std::string> affected_wells;
// If one of the transmissibility multiplier keywords has been invoked as an
// ACTIONX keyword the simulator needs to recalculate the transmissibility.
bool tran_update{false};
};
}
#endif

View File

@@ -1,114 +0,0 @@
/*
Copyright 2020 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACTION_STATE_HPP
#define ACTION_STATE_HPP
#include <ctime>
#include <map>
#include <opm/input/eclipse/Schedule/Action/ActionResult.hpp>
namespace Opm {
namespace RestartIO {
struct RstState;
}
namespace Action {
class ActionX;
class Actions;
class PyAction;
class State {
struct RunState {
RunState() = default;
RunState(std::time_t sim_time)
: run_count(1)
, last_run(sim_time)
{}
void add_run(std::time_t sim_time) {
this->last_run = sim_time;
this->run_count += 1;
}
static RunState serializeObject()
{
RunState rs;
rs.run_count = 100;
rs.last_run = 123456;
return rs;
}
bool operator==(const RunState& other) const {
return this->run_count == other.run_count &&
this->last_run == other.last_run;
}
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(this->run_count);
serializer(this->last_run);
}
std::size_t run_count;
std::time_t last_run;
};
public:
void add_run(const ActionX& action, std::time_t sim_time, Result result);
void add_run(const PyAction& action, bool result);
std::size_t run_count(const ActionX& action) const;
std::time_t run_time(const ActionX& action) const;
std::optional<Result> result(const std::string& action) const;
std::optional<bool> python_result(const std::string& action) const;
void load_rst(const Actions& action_config, const RestartIO::RstState& rst_state);
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer.map(this->run_state);
serializer.map(this->last_result);
serializer.template map<std::map<std::string,bool>, false>(this->m_python_result);
}
static State serializeObject();
bool operator==(const State& other) const;
private:
using action_id = std::pair<std::string, std::size_t>;
static action_id make_id(const ActionX& action);
std::map<action_id, RunState> run_state;
std::map<std::string, Result> last_result;
std::map<std::string, bool> m_python_result;
};
}
}
#endif

View File

@@ -1,144 +0,0 @@
/*
Copyright 2021 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COMPLETED_CELLS
#define COMPLETED_CELLS
#include <optional>
#include <unordered_map>
#include <opm/input/eclipse/EclipseState/Grid/GridDims.hpp>
namespace Opm {
class CompletedCells {
public:
struct Cell {
std::size_t global_index;
std::size_t i, j, k;
struct Props{
std::size_t active_index;
double permx;
double permy;
double permz;
int satnum;
int pvtnum;
double ntg;
bool operator==(const Props& other) const{
return this->active_index == other.active_index &&
this->permx == other.permx &&
this->permy == other.permy &&
this->permz == other.permz &&
this->satnum == other.satnum &&
this->pvtnum == other.pvtnum &&
this->ntg == other.ntg;
}
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(this->permx);
serializer(this->permy);
serializer(this->permz);
serializer(this->satnum);
serializer(this->pvtnum);
serializer(this->ntg);
}
static Props serializeObject(){
Props props;
props.permx = 10.0;
props.permy = 78.0;
props.permz = 45.4;
props.satnum = 3;
props.pvtnum = 5;
props.ntg = 45.1;
return props;
}
};
std::optional<Props> props;
std::size_t active_index() const;
bool is_active() const;
double depth;
std::array<double, 3> dimensions;
bool operator==(const Cell& other) const {
return this->global_index == other.global_index &&
this->i == other.i &&
this->j == other.j &&
this->k == other.k &&
this->depth == other.depth &&
this->dimensions == other.dimensions &&
this->props == other.props;
}
static Cell serializeObject() {
Cell cell(0,1,1,1);
cell.depth = 12345;
return cell;
}
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(this->global_index);
serializer(this->i);
serializer(this->j);
serializer(this->k);
serializer(this->depth);
serializer(this->props);
serializer.template array<std::array<double,3>, false>(this->dimensions);
}
Cell(std::size_t g, std::size_t i_, std::size_t j_, std::size_t k_)
: global_index(g)
, i(i_)
, j(j_)
, k(k_)
{}
Cell() = default;
};
CompletedCells() = default;
CompletedCells(const GridDims& dims);
CompletedCells(std::size_t nx, std::size_t ny, std::size_t nz);
const Cell& get(std::size_t i, std::size_t j, std::size_t k) const;
std::pair<bool, Cell&> try_get(std::size_t i, std::size_t j, std::size_t k);
bool operator==(const CompletedCells& other) const;
static CompletedCells serializeObject();
template<class Serializer>
void serializeOp(Serializer& serializer)
{
this->dims.serializeOp(serializer);
serializer.map(this->cells);
}
private:
GridDims dims;
std::unordered_map<std::size_t, Cell> cells;
};
}
#endif

View File

@@ -1,177 +0,0 @@
/*
Copyright 2019, 2020 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GUIDE_RATE_HPP
#define GUIDE_RATE_HPP
#include <opm/input/eclipse/Schedule/Group/Group.hpp>
#include <opm/input/eclipse/Schedule/Group/GuideRateModel.hpp>
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
#include <cstddef>
#include <ctime>
#include <limits>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
namespace Opm {
class Schedule;
} // namespace Opm
namespace Opm {
class GuideRate
{
public:
// used for potentials and well rates
struct RateVector {
RateVector() = default;
RateVector(const double orat, const double grat, const double wrat)
: oil_rat(orat)
, gas_rat(grat)
, wat_rat(wrat)
{}
double eval(const Well::GuideRateTarget target) const;
double eval(const Group::GuideRateProdTarget target) const;
double eval(const GuideRateModel::Target target) const;
double oil_rat{0.0};
double gas_rat{0.0};
double wat_rat{0.0};
};
struct GuideRateValue {
GuideRateValue() = default;
GuideRateValue(const double t, const double v, const GuideRateModel::Target tg)
: sim_time(t)
, value (v)
, target (tg)
{}
bool operator==(const GuideRateValue& other) const
{
return (this->sim_time == other.sim_time)
&& (this->value == other.value);
}
bool operator!=(const GuideRateValue& other) const
{
return !(*this == other);
}
double sim_time { std::numeric_limits<double>::lowest() };
double value { std::numeric_limits<double>::lowest() };
GuideRateModel::Target target { GuideRateModel::Target::NONE };
};
GuideRate(const Schedule& schedule);
void compute(const std::string& wgname,
const std::size_t report_step,
const double sim_time,
const double oil_pot,
const double gas_pot,
const double wat_pot);
void compute(const std::string& wgname,
const Phase& phase,
const std::size_t report_step,
const double guide_rate);
bool has(const std::string& name) const;
bool hasPotentials(const std::string& name) const;
bool has(const std::string& name, const Phase& phase) const;
double get(const std::string& well, const Well::GuideRateTarget target, const RateVector& rates) const;
double get(const std::string& group, const Group::GuideRateProdTarget target, const RateVector& rates) const;
double get(const std::string& name, const GuideRateModel::Target model_target, const RateVector& rates) const;
double get(const std::string& group, const Phase& phase) const;
double getSI(const std::string& well, const Well::GuideRateTarget target, const RateVector& rates) const;
double getSI(const std::string& group, const Group::GuideRateProdTarget target, const RateVector& rates) const;
double getSI(const std::string& wgname, const GuideRateModel::Target target, const RateVector& rates) const;
double getSI(const std::string& group, const Phase& phase) const;
void init_grvalue(const std::size_t report_step, const std::string& wgname, GuideRateValue value);
void init_grvalue_SI(const std::size_t report_step, const std::string& wgname, GuideRateValue value);
void updateGuideRateExpiration(const double sim_time,
const std::size_t report_step);
private:
struct GRValState
{
GuideRateValue curr{};
GuideRateValue prev{};
};
struct pair_hash
{
template <class T1, class T2>
std::size_t operator()(const std::pair<T1, T2>& pair) const
{
return std::hash<T1>()(pair.first) ^ std::hash<T2>()(pair.second);
}
};
using GRValPtr = std::unique_ptr<GRValState>;
using pair = std::pair<Phase, std::string>;
void well_compute(const std::string& wgname,
const std::size_t report_step,
const double sim_time,
const double oil_pot,
const double gas_pot,
const double wat_pot);
void group_compute(const std::string& wgname,
const std::size_t report_step,
const double sim_time,
const double oil_pot,
const double gas_pot,
const double wat_pot);
double eval_form(const GuideRateModel& model,
const double oil_pot,
const double gas_pot,
const double wat_pot) const;
double eval_group_pot() const;
double eval_group_resvinj() const;
void assign_grvalue(const std::string& wgname,
const GuideRateModel& model,
GuideRateValue&& value);
double get_grvalue_result(const GRValState& gr) const;
const Schedule& schedule;
std::unordered_map<std::string, GRValPtr> values{};
std::unordered_map<pair, double, pair_hash> injection_group_values{};
std::unordered_map<std::string, RateVector> potentials{};
bool guide_rates_expired {false};
};
} // namespace Opm
#endif

View File

@@ -1,92 +0,0 @@
/*
Copyright 2021 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NETWORK_BALANCE_HPP
#define NETWORK_BALANCE_HPP
#include <optional>
#include <cstddef>
namespace Opm {
class DeckKeyword;
struct Tuning;
class UnitSystem;
namespace Network {
class Balance {
public:
enum class CalcMode {
None = 0,
TimeInterval = 1,
TimeStepStart = 2,
NUPCOL = 3
};
Balance() = default;
Balance(bool network_active, const Tuning& tuning);
Balance(const Tuning& tuning, const DeckKeyword& keyword);
CalcMode mode() const;
double interval() const;
double pressure_tolerance() const;
std::size_t pressure_max_iter() const;
double thp_tolerance() const;
std::size_t thp_max_iter() const;
std::optional<double> target_balance_error() const;
std::optional<double> max_balance_error() const;
double min_tstep() const;
static Balance serializeObject();
bool operator==(const Balance& other) const;
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(this->calc_mode);
serializer(this->calc_interval);
serializer(this->ptol);
serializer(this->m_pressure_max_iter);
serializer(this->m_thp_tolerance);
serializer(this->m_thp_max_iter);
serializer(this->target_branch_balance_error);
serializer(this->max_branch_balance_error);
serializer(this->m_min_tstep);
}
private:
CalcMode calc_mode{CalcMode::None};
double calc_interval;
double ptol;
std::size_t m_pressure_max_iter;
double m_thp_tolerance;
std::size_t m_thp_max_iter;
std::optional<double> target_branch_balance_error;
std::optional<double> max_branch_balance_error;
double m_min_tstep;
};
}
}
#endif

Some files were not shown because too many files have changed in this diff Show More