diff --git a/.gitignore b/.gitignore index 74fa1ca2..d124eee7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *~ +.\#* *.o *.lo *.la @@ -28,15 +29,26 @@ ltmain.sh m4/libtool.m4 m4/lt*.m4 missing +tutorials/tutorial[1-4] # Ignoring executables *_test examples/scaneclipsedeck examples/spu_2p examples/reorder-qfs +examples/refine_wells +examples/sim_2p_incomp_reorder +examples/sim_wateroil +examples/wells_example tests/test_cfs_tpfa tests/test_jacsys tests/test_readvector tests/test_sf2p tests/bo_fluid_p_and_z_deps tests/bo_fluid_pressuredeps +tests/test_cartgrid +tests/test_column_extract +tests/test_lapack +tests/test_read_vag +tests/test_readpolymer +tests/test_writeVtkData diff --git a/CMakeLists.txt b/CMakeLists.txt index a6e3b690..eec84d9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,10 @@ cmake_minimum_required (VERSION 2.6) project (opm-core) enable_language(Fortran) - - +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) +SET(CMAKE_BUILD_TYPE "debug") +SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG -pg -Wall -fopenmp -ggdb") +SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -pg -Wall -fopenmp -ggdb") set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_MULTITHREADED ON) @@ -16,94 +18,50 @@ include_directories(${PROJECT_SOURCE_DIR} ${Boost_INCLUDE_DIRS}) # The opmcore library -add_library(opmcore -opm/core/eclipse/EclipseGridInspector.cpp -opm/core/eclipse/EclipseGridParser.cpp -opm/core/fluid/blackoil/BlackoilPvtProperties.cpp -opm/core/fluid/blackoil/SinglePvtDead.cpp -opm/core/fluid/blackoil/SinglePvtLiveGas.cpp -opm/core/fluid/blackoil/SinglePvtLiveOil.cpp -opm/core/fluid/blackoil/SinglePvtInterface.cpp -opm/core/fluid/BlackoilPropertiesBasic.cpp -opm/core/fluid/BlackoilPropertiesFromDeck.cpp -opm/core/fluid/IncompPropertiesBasic.cpp -opm/core/fluid/IncompPropertiesFromDeck.cpp -opm/core/fluid/PvtPropertiesBasic.cpp -opm/core/fluid/PvtPropertiesIncompFromDeck.cpp -opm/core/fluid/RockBasic.cpp -opm/core/fluid/RockFromDeck.cpp -opm/core/fluid/SaturationPropsBasic.cpp -opm/core/fluid/SaturationPropsFromDeck.cpp -opm/core/utility/MonotCubicInterpolator.cpp -opm/core/utility/parameters/Parameter.cpp -opm/core/utility/parameters/ParameterGroup.cpp -opm/core/utility/parameters/ParameterTools.cpp -opm/core/utility/parameters/ParameterXML.cpp -opm/core/utility/parameters/tinyxml/tinystr.cpp -opm/core/utility/parameters/tinyxml/tinyxml.cpp -opm/core/utility/parameters/tinyxml/tinyxmlerror.cpp -opm/core/utility/parameters/tinyxml/tinyxmlparser.cpp -opm/core/utility/cart_grid.c -opm/core/utility/cpgpreprocess/geometry.c -opm/core/utility/cpgpreprocess/preprocess.c -opm/core/utility/cpgpreprocess/readvector.cpp -opm/core/utility/cpgpreprocess/cgridinterface.c -opm/core/utility/cpgpreprocess/sparsetable.c -opm/core/utility/cpgpreprocess/facetopology.c -opm/core/utility/cpgpreprocess/uniquepoints.c -opm/core/utility/StopWatch.cpp -opm/core/utility/newwells.c -opm/core/utility/writeVtkData.cpp -opm/core/GridManager.cpp -opm/core/linalg/sparse_sys.c -opm/core/pressure/cfsh.c -opm/core/pressure/flow_bc.c -opm/core/pressure/well.c -opm/core/pressure/fsh_common_impl.c -opm/core/pressure/fsh.c -opm/core/pressure/tpfa/ifs_tpfa.c -opm/core/pressure/tpfa/compr_source.c -opm/core/pressure/tpfa/cfs_tpfa.c -opm/core/pressure/tpfa/compr_bc.c -opm/core/pressure/tpfa/compr_quant.c -opm/core/pressure/tpfa/compr_quant_general.c -opm/core/pressure/tpfa/cfs_tpfa_residual.c -opm/core/pressure/tpfa/trans_tpfa.c -opm/core/pressure/msmfem/coarse_conn.c -opm/core/pressure/msmfem/partition.c -opm/core/pressure/msmfem/hash_set.c -opm/core/pressure/msmfem/ifsh_ms.c -opm/core/pressure/msmfem/dfs.c -opm/core/pressure/msmfem/coarse_sys.c -opm/core/pressure/ifsh.c -opm/core/pressure/IncompTpfa.cpp -opm/core/pressure/mimetic/mimetic.c -opm/core/pressure/mimetic/hybsys_global.c -opm/core/pressure/mimetic/hybsys.c -opm/core/transport/spu_explicit.c -opm/core/transport/spu_implicit.c -opm/core/transport/transport_source.c -opm/core/transport/reorder/TransportModelInterface.cpp -opm/core/transport/reorder/TransportModelTwophase.cpp -opm/core/transport/reorder/reordersequence.cpp -opm/core/transport/reorder/nlsolvers.c -opm/core/transport/reorder/tarjan.c -opm/core/linalg/call_umfpack.c -opm/core/pressure/IncompTpfa.cpp -opm/core/linalg/LinearSolverInterface.cpp -opm/core/linalg/LinearSolverIstl.cpp -opm/core/linalg/LinearSolverUmfpack.cpp -) +FILE(GLOB_RECURSE C_FILES_CORE "opm/core/*.c") +FILE(GLOB_RECURSE CPP_FILES_CORE "opm/core/*.cpp") +FILE(GLOB_RECURSE REMOVE_FILES "processgrid.c") +FILE(GLOB_RECURSE REMOVE_FILESMX "mx*.c") +FILE(GLOB_RECURSE REMOVE_FILESAGMG "*AGMG.cpp" "*test*") +list(REMOVE_ITEM C_FILES_CORE ${REMOVE_FILES} ${REMOVE_FILESMX} ) +list(REMOVE_ITEM CPP_FILES_CORE ${REMOVE_FILES} ${REMOVE_FILESAGMG}) +add_library(opmcore ${C_FILES_CORE} ${CPP_FILES_CORE} ) + target_link_libraries(opmcore ${UMFPACK_LIBRARIES} ${LAPACK_LINKER_FLAGS} ${LAPACK_LIBRARIES} ${Boost_LIBRARIES} -lcholmod -lcamd -lccolamd -lmetis -ldunecommon ) +FILE(GLOB CPP_EXAMPLES "examples/*.cpp") +FILE(GLOB CPP_tests "tests/*.cpp") add_executable(spu_2p examples/spu_2p.cpp) +add_executable(sim_2p_incomp_reorder examples/sim_2p_incomp_reorder.cpp) +add_executable(sim_wateroil examples/sim_wateroil.cpp) + +add_executable(pvt_test tests/pvt_test.cpp) +add_executable(relperm_test tests/relperm_test.cpp) target_link_libraries(spu_2p opmcore ) +target_link_libraries(sim_2p_incomp_reorder + opmcore +) -set_target_properties(opmcore spu_2p PROPERTIES COMPILE_FLAGS -m64 LINKER_LANGUAGE CXX LINK_FLAGS -m64) +target_link_libraries(sim_wateroil + opmcore +) + +target_link_libraries(pvt_test + opmcore +) +target_link_libraries(relperm_test + opmcore +) + +#set_target_properties(opmcore spu_2p PROPERTIES COMPILE_FLAGS -m64 LINKER_LANGUAGE CXX LINK_FLAGS -m64) +#set_target_properties(opmcore sim_2p_incomp_reorder PROPERTIES COMPILE_FLAGS -m64 LINKER_LANGUAGE CXX LINK_FLAGS -m64) +#set_target_properties(opmcore sim_wateroil PROPERTIES COMPILE_FLAGS -m64 LINKER_LANGUAGE CXX LINK_FLAGS -m64) +#set_target_properties(opmcore pvt_test PROPERTIES COMPILE_FLAGS -m64 LINKER_LANGUAGE CXX LINK_FLAGS -m64) +set_target_properties(opmcore relperm_test PROPERTIES COMPILE_FLAGS -m64 LINKER_LANGUAGE CXX LINK_FLAGS -m64) diff --git a/FindUmfPack.cmake b/FindUmfPack.cmake new file mode 100644 index 00000000..5fe5db16 --- /dev/null +++ b/FindUmfPack.cmake @@ -0,0 +1,56 @@ +if (UMFPACK_INCLUDES AND UMFPACK_LIBRARIES) + set(UMFPACK_FIND_QUIETLY TRUE) +endif (UMFPACK_INCLUDES AND UMFPACK_LIBRARIES) + +find_package(BLAS) + +if(BLAS_FOUND) + + find_path(UMFPACK_INCLUDES + NAMES + umfpack.h + PATHS + $ENV{UMFPACKDIR} + ${INCLUDE_INSTALL_DIR} + PATH_SUFFIXES + suitesparse + ) + + find_library(UMFPACK_LIBRARIES umfpack PATHS $ENV{UMFPACKDIR} ${LIB_INSTALL_DIR}) + + if(UMFPACK_LIBRARIES) + + get_filename_component(UMFPACK_LIBDIR ${UMFPACK_LIBRARIES} PATH) + + find_library(AMD_LIBRARY amd PATHS ${UMFPACK_LIBDIR} $ENV{UMFPACKDIR} ${LIB_INSTALL_DIR}) + if (AMD_LIBRARY) + set(UMFPACK_LIBRARIES ${UMFPACK_LIBRARIES} ${AMD_LIBRARY}) + #else (AMD_LIBRARY) + # set(UMFPACK_LIBRARIES FALSE) + endif (AMD_LIBRARY) + + endif(UMFPACK_LIBRARIES) + + if(UMFPACK_LIBRARIES) + + find_library(COLAMD_LIBRARY colamd PATHS ${UMFPACK_LIBDIR} $ENV{UMFPACKDIR} ${LIB_INSTALL_DIR}) + if (COLAMD_LIBRARY) + set(UMFPACK_LIBRARIES ${UMFPACK_LIBRARIES} ${COLAMD_LIBRARY}) + #else (COLAMD_LIBRARY) + # set(UMFPACK_LIBRARIES FALSE) + endif (COLAMD_LIBRARY) + + endif(UMFPACK_LIBRARIES) + + if(UMFPACK_LIBRARIES) + set(UMFPACK_LIBRARIES ${UMFPACK_LIBRARIES} ${BLAS_LIBRARIES}) + endif(UMFPACK_LIBRARIES) + +endif(BLAS_FOUND) + + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(UMFPACK DEFAULT_MSG + UMFPACK_INCLUDES UMFPACK_LIBRARIES) + +mark_as_advanced(UMFPACK_INCLUDES UMFPACK_LIBRARIES AMD_LIBRARY COLAMD_LIBRARY) \ No newline at end of file diff --git a/Makefile.am b/Makefile.am index 115cd789..434f183f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,34 +6,39 @@ SUBDIRS = . tests examples tutorials # ---------------------------------------------------------------------- # Declare products (i.e., the library) -lib_LTLIBRARIES = libopmcore.la +lib_LTLIBRARIES = lib/libopmcore.la # ---------------------------------------------------------------------- # Build-time flags needed to build libopmcore.la -AM_CPPFLAGS = \ -$(ERT_CPPFLAGS) \ -$(BOOST_CPPFLAGS) +AM_CPPFLAGS = \ +$(ERT_CPPFLAGS) \ +$(OPM_BOOST_CPPFLAGS) # ---------------------------------------------------------------------- # Link-time flags needed both to successfully link the library and to # (transitively) convey inter-library dependency information. -libopmcore_la_LDFLAGS = \ -$(ERT_LDFLAGS) \ -$(BOOST_LDFLAGS) \ -$(BOOST_FILESYSTEM_LIB) \ -$(BOOST_SYSTEM_LIB) \ -$(BOOST_DATE_TIME_LIB) \ +lib_libopmcore_la_LDFLAGS = \ +$(ERT_LDFLAGS) \ +$(OPM_BOOST_LDFLAGS) \ +$(BOOST_FILESYSTEM_LIB) \ +$(BOOST_SYSTEM_LIB) \ +$(BOOST_DATE_TIME_LIB) \ $(BOOST_UNIT_TEST_FRAMEWORK_LIB) \ -$(ERT_LIBS) $(LAPACK_LIBS) $(BLAS_LIBS) $(LIBS) +$(ERT_LIBS) \ +$(LAPACK_LIBS) $(BLAS_LIBS) $(LIBS) # ---------------------------------------------------------------------- # Library constituents. SOURCES followed by HEADERS. # # Please try to keep the list sorted. -libopmcore_la_SOURCES = \ +# List of sources that should be built but not distributed. See AGMG +# support below for additional details. +nodist_lib_libopmcore_la_SOURCES = + +lib_libopmcore_la_SOURCES = \ opm/core/GridManager.cpp \ opm/core/eclipse/EclipseGridInspector.cpp \ opm/core/eclipse/EclipseGridParser.cpp \ @@ -48,8 +53,11 @@ opm/core/fluid/RockCompressibility.cpp \ opm/core/fluid/RockFromDeck.cpp \ opm/core/fluid/SaturationPropsBasic.cpp \ opm/core/fluid/SaturationPropsFromDeck.cpp \ +opm/core/fluid/SatFuncStone2.cpp \ +opm/core/fluid/SatFuncSimple.cpp \ opm/core/fluid/blackoil/BlackoilPvtProperties.cpp \ opm/core/fluid/blackoil/SinglePvtDead.cpp \ +opm/core/fluid/blackoil/SinglePvtDeadSpline.cpp \ opm/core/fluid/blackoil/SinglePvtInterface.cpp \ opm/core/fluid/blackoil/SinglePvtLiveGas.cpp \ opm/core/fluid/blackoil/SinglePvtLiveOil.cpp \ @@ -90,9 +98,10 @@ opm/core/pressure/tpfa/compr_source.c \ opm/core/pressure/tpfa/ifs_tpfa.c \ opm/core/pressure/tpfa/trans_tpfa.c \ opm/core/pressure/well.c \ +opm/core/simulator/SimulatorCompressibleTwophase.cpp \ +opm/core/simulator/SimulatorIncompTwophase.cpp \ opm/core/simulator/SimulatorReport.cpp \ opm/core/simulator/SimulatorTimer.cpp \ -opm/core/simulator/SimulatorTwophase.cpp \ opm/core/transport/reorder/TransportModelCompressibleTwophase.cpp \ opm/core/transport/reorder/TransportModelInterface.cpp \ opm/core/transport/reorder/TransportModelTwophase.cpp \ @@ -142,13 +151,18 @@ opm/core/fluid/PvtPropertiesIncompFromDeck.hpp \ opm/core/fluid/RockBasic.hpp \ opm/core/fluid/RockCompressibility.hpp \ opm/core/fluid/RockFromDeck.hpp \ +opm/core/fluid/SatFuncStone2.hpp \ +opm/core/fluid/SatFuncSimple.hpp \ opm/core/fluid/SaturationPropsBasic.hpp \ opm/core/fluid/SaturationPropsFromDeck.hpp \ +opm/core/fluid/SaturationPropsFromDeck_impl.hpp \ +opm/core/fluid/SaturationPropsInterface.hpp \ opm/core/fluid/SimpleFluid2p.hpp \ opm/core/fluid/blackoil/BlackoilPhases.hpp \ opm/core/fluid/blackoil/BlackoilPvtProperties.hpp \ opm/core/fluid/blackoil/SinglePvtConstCompr.hpp \ opm/core/fluid/blackoil/SinglePvtDead.hpp \ +opm/core/fluid/blackoil/SinglePvtDeadSpline.hpp \ opm/core/fluid/blackoil/SinglePvtInterface.hpp \ opm/core/fluid/blackoil/SinglePvtLiveGas.hpp \ opm/core/fluid/blackoil/SinglePvtLiveOil.hpp \ @@ -193,9 +207,10 @@ opm/core/pressure/tpfa/compr_source.h \ opm/core/pressure/tpfa/ifs_tpfa.h \ opm/core/pressure/tpfa/trans_tpfa.h \ opm/core/simulator/BlackoilState.hpp \ +opm/core/simulator/SimulatorCompressibleTwophase.hpp \ opm/core/simulator/SimulatorReport.hpp \ +opm/core/simulator/SimulatorIncompTwophase.hpp \ opm/core/simulator/SimulatorTimer.hpp \ -opm/core/simulator/SimulatorTwophase.hpp \ opm/core/simulator/TwophaseState.hpp \ opm/core/simulator/WellState.hpp \ opm/core/transport/CSRMatrixBlockAssembler.hpp \ @@ -219,9 +234,11 @@ opm/core/transport/spu_implicit.h \ opm/core/transport/transport_source.h \ opm/core/utility/Average.hpp \ opm/core/utility/ColumnExtract.hpp \ +opm/core/utility/DataMap.hpp \ opm/core/utility/ErrorMacros.hpp \ opm/core/utility/Factory.hpp \ opm/core/utility/MonotCubicInterpolator.hpp \ +opm/core/utility/NonuniformTableLinear.hpp \ opm/core/utility/RootFinders.hpp \ opm/core/utility/SparseTable.hpp \ opm/core/utility/SparseVector.hpp \ @@ -246,7 +263,6 @@ opm/core/utility/parameters/ParameterXML.hpp \ opm/core/utility/parameters/tinyxml/tinystr.h \ opm/core/utility/parameters/tinyxml/tinyxml.h \ opm/core/utility/writeVtkData.hpp \ -opm/core/utility/DataMap.hpp \ opm/core/vag_format/vag.hpp \ opm/core/well.h \ opm/core/wells/InjectionSpecification.hpp \ @@ -259,7 +275,7 @@ opm/core/wells/WellsManager.hpp # Optional library constituents. if UMFPACK -libopmcore_la_SOURCES += \ +lib_libopmcore_la_SOURCES += \ opm/core/linalg/call_umfpack.c \ opm/core/linalg/LinearSolverUmfpack.cpp @@ -269,16 +285,16 @@ opm/core/linalg/LinearSolverUmfpack.hpp endif if HAVE_ERT -libopmcore_la_SOURCES += \ +lib_libopmcore_la_SOURCES += \ opm/core/utility/writeECLData.cpp -nobase_include_HEADERS += \ +nobase_include_HEADERS += \ opm/core/utility/writeECLData.hpp endif if DUNE_ISTL -libopmcore_la_SOURCES += \ +lib_libopmcore_la_SOURCES += \ opm/core/linalg/LinearSolverIstl.cpp nobase_include_HEADERS += \ @@ -287,14 +303,16 @@ endif if BUILD_AGMG -libopmcore_la_SOURCES += \ +nodist_lib_libopmcore_la_SOURCES += \ $(AGMG_SRCDIR)/dagmg.f90 \ -$(AGMG_SRCDIR)/dagmg_mumps.f90 \ +$(AGMG_SRCDIR)/dagmg_mumps.f90 + +lib_libopmcore_la_SOURCES += \ opm/core/linalg/LinearSolverAGMG.cpp nobase_include_HEADERS += \ opm/core/linalg/LinearSolverAGMG.hpp -libopmcore_la_LDFLAGS += \ +lib_libopmcore_la_LDFLAGS += \ $(FCLIBS) endif diff --git a/README b/README new file mode 100644 index 00000000..8c7c522f --- /dev/null +++ b/README @@ -0,0 +1,125 @@ +Open Porous Media Core Library +============================== + +These are release notes for opm-core. + + +CONTENT +------- + +opm-core is the core library within OPM and contains the following + +* Eclipse deck input and preprosessing +* Fluid properties (basic PVT models and rock properties) +* Grid handling (cornerpoint grids, unstructured grid interface) +* Linear Algebra (interface to different linear solvers) +* Pressure solvers (various discretization schemes, flow models) +* Simulators (some basic examples of simulators based on sequential splitting schemes) +* Transport solvers (various discretization schemes, flow models) +* Utilities (input and output processing, unit conversion) +* Wells (basic well handling) + + +LICENSE +------- + +The library is distributed under the GNU General Public License, +version 3 or later (GPLv3+). + + +PLATFORMS +--------- + +The opm-core module is designed to run on Linux platforms. It is also +regularly run on Mac OS X. No efforts have been made to ensure that +the code will compile and run on windows platforms. + + +DEPENDENCIES FOR DEBIAN BASED DISTRIBUTIONS (Debian Squeeze/Ubuntu Precise) +--------------------------------------------------------------------------- + +# packages necessary for building +sudo apt-get install -y build-essential gfortran pkg-config libtool \ + automake autoconf + +# packages necessary for documentation +sudo apt-get install -y doxygen ghostscript texlive-latex-recommended pgf + +# packages necessary for version control +sudo apt-get install -y git-core git-svn subversion + +# libraries necessary for DUNE +sudo apt-get install -y libboost-all-dev libsuperlu3-dev libsuitesparse-dev + +# libraries necessary for OPM +sudo apt-get install -y libxml0-dev + + +DEPENDENCIES FOR SUSE BASED DISTRIBUTIONS +----------------------------------------- + +# libraries +sudo zypper install blas libblas3 lapack liblapack3 libboost libxml2 umfpack + +# tools +sudo zypper install gcc automake autoconf git doxygen + + +RETRIEVING AND BUILDING DUNE PREREQUISITES +------------------------------------------ + +(only necessary if you want to use opm-core as a dune module) + +# trust DUNE certificate (sic) +echo p | svn list https://svn.dune-project.org/svn/dune-common + +# checkout DUNE libraries +for module in common istl geometry grid localfunctions; do + git svn clone -s \ + https://svn.dune-project.org/svn/dune-$module/branches/release-2.2/ \ + dune-$module +done + +# building DUNE libraries +for module in common istl geometry grid localfunctions; do + env CCACHE_DISABLE=1 dune-common/bin/dunecontrol --only=dune-$module \ + --configure-opts="--enable-fieldvector-size-is-method" \ + --make-opts="-j -l 0.8" autogen : configure : make +done + + +DOWNLOADING +----------- + +For a read-only download: +git clone git://github.com/OPM/opm-core.git + +If you want to contribute, fork OPM/opm-core on github. + + +BUILDING +-------- + +(standalone opm-core:) + + cd ../opm-core + autoreconf -i + ./configure + make + sudo make install + +(using opm-core as a dune module:) + + # note: this is done from the parent directory of opm-core + env CCACHE_DISABLE=1 dune-common/bin/dunecontrol --only=opm-core \ + --configure-opts="" --make-opts="-j -l 0.8" autogen : configure : make + + + +DOCUMENTATION +------------- + +Efforts have been made to document the code with Doxygen. +In order to build the documentation, enter the command +$ doxygen +in the topmost directory. diff --git a/configure.ac b/configure.ac index 140c6ff5..e603100a 100644 --- a/configure.ac +++ b/configure.ac @@ -8,6 +8,10 @@ AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) +# Needed for automake since version 1.12 because extra-portability +# warnings were then added to -Wall. Ifdef makes it backwards compatible. +m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) + AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([opm/core/grid.h]) AC_CONFIG_HEADERS([config.h]) @@ -28,60 +32,22 @@ m4_ifdef([LT_INIT], AC_PROG_FC[]dnl ])[]dnl -# Checks for libraries. - -# Bring in numerics support (standard library component) -AC_SEARCH_LIBS([sqrt], [m]) - -OPM_LAPACK - -AX_BOOST_BASE([1.37]) -AX_BOOST_SYSTEM -AX_BOOST_DATE_TIME -AX_BOOST_FILESYSTEM -AX_BOOST_UNIT_TEST_FRAMEWORK - -AX_DUNE_ISTL -OPM_AGMG +OPM_CORE_CHECKS OPM_DYNLINK_BOOST_TEST -# Checks for header files. -AC_CHECK_HEADERS([float.h limits.h stddef.h stdlib.h string.h]) - -AC_CHECK_HEADERS([suitesparse/umfpack.h], - [umfpack_header=yes], - [umfpack_header=no]) - -# Checks for typedefs, structures, and compiler characteristics. -AC_HEADER_STDBOOL -AC_TYPE_SIZE_T -AC_CHECK_TYPES([ptrdiff_t]) - -# Checks for library functions. -AC_CHECK_FUNCS([floor memset memmove strchr strtol sqrt pow]) -AC_FUNC_STRTOD - -# Search for UMFPACK direct sparse solver. -AC_SEARCH_LIBS([amd_free], [amd]) -AC_SEARCH_LIBS([camd_free], [camd]) -AC_SEARCH_LIBS([colamd_set_defaults], [colamd]) -AC_SEARCH_LIBS([ccolamd_set_defaults], [ccolamd]) -AC_SEARCH_LIBS([cholmod_l_start], [cholmod]) -AC_SEARCH_LIBS([umfpack_dl_solve], [umfpack],dnl - [umfpack_lib=yes], [umfpack_lib=no]) - -AM_CONDITIONAL([UMFPACK], - [test "x$umfpack_header" != "xno" -a "x$umfpack_lib" != "xno"]) - -m4_ifdef([AM_COND_IF], -[AM_COND_IF([UMFPACK], [], - [AC_MSG_NOTICE([Found no working installation of UMFPACK. - UMFPACK support is disabled.])]) -]) - ERT +dnl Substitute Autoconf's abs_*dir variables into the Makefiles for the +dnl benefit of external code that uses these variables to derive +dnl locations (e.g., Dune's DUNE_CHECK_MODULES macro). Automakes prior +dnl to version 1.10 do not automatically substitute these variables into +dnl output files. +AC_SUBST([abs_srcdir]) +AC_SUBST([abs_builddir]) +AC_SUBST([abs_top_srcdir]) +AC_SUBST([abs_top_builddir]) + AC_CONFIG_FILES([ Makefile tests/Makefile diff --git a/dune.module b/dune.module new file mode 100644 index 00000000..7deeedde --- /dev/null +++ b/dune.module @@ -0,0 +1,7 @@ +#dune module information file# +############################## + +#Name of the module +Module: opm-core +Version: 0.1 +Maintainer: atgeirr@sintef.no diff --git a/examples/Makefile.am b/examples/Makefile.am index 5fafc37d..b9657dd0 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1,15 +1,15 @@ # Build-time flags needed to form example programs ERT_INCLUDE_PATH = $(ERT_ROOT)/include -AM_CPPFLAGS = \ --I$(top_srcdir) \ -$(BOOST_CPPFLAGS) \ +AM_CPPFLAGS = \ +-I$(top_srcdir) \ +$(OPM_BOOST_CPPFLAGS) \ -I$(ERT_INCLUDE_PATH) # All targets link to the library -LDADD = \ -$(top_builddir)/libopmcore.la \ -$(BOOST_FILESYSTEM_LIB) \ +LDADD = \ +$(top_builddir)/lib/libopmcore.la \ +$(BOOST_FILESYSTEM_LIB) \ $(BOOST_SYSTEM_LIB) # ---------------------------------------------------------------------- @@ -17,11 +17,12 @@ $(BOOST_SYSTEM_LIB) # # Please keep the list sorted. -noinst_PROGRAMS = \ -refine_wells \ -scaneclipsedeck \ -sim_2p_incomp_reorder \ -sim_wateroil \ +noinst_PROGRAMS = \ +refine_wells \ +scaneclipsedeck \ +sim_2p_comp_reorder \ +sim_2p_incomp_reorder \ +sim_wateroil \ wells_example # ---------------------------------------------------------------------- @@ -32,6 +33,7 @@ wells_example # Please maintain sort order from "noinst_PROGRAMS". refine_wells_SOURCES = refine_wells.cpp +sim_2p_comp_reorder_SOURCES = sim_2p_comp_reorder.cpp sim_2p_incomp_reorder_SOURCES = sim_2p_incomp_reorder.cpp sim_wateroil_SOURCES = sim_wateroil.cpp wells_example_SOURCES = wells_example.cpp @@ -43,7 +45,7 @@ if UMFPACK noinst_PROGRAMS += spu_2p spu_2p_SOURCES = spu_2p.cpp -spu_2p_LDADD = \ -$(LDADD) \ +spu_2p_LDADD = \ +$(LDADD) \ $(LAPACK_LIBS) $(BLAS_LIBS) $(LIBS) endif diff --git a/examples/sim_2p_comp_reorder.cpp b/examples/sim_2p_comp_reorder.cpp new file mode 100644 index 00000000..cdc4a5a7 --- /dev/null +++ b/examples/sim_2p_comp_reorder.cpp @@ -0,0 +1,285 @@ +/* + Copyright 2012 SINTEF ICT, Applied Mathematics. + + 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 . +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + + +namespace +{ + void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param) + { + if (param.anyUnused()) { + std::cout << "-------------------- Unused parameters: --------------------\n"; + param.displayUsage(); + std::cout << "----------------------------------------------------------------" << std::endl; + } + } +} // anon namespace + + + +// ----------------- Main program ----------------- +int +main(int argc, char** argv) +{ + using namespace Opm; + + std::cout << "\n================ Test program for weakly compressible two-phase flow ===============\n\n"; + parameter::ParameterGroup param(argc, argv, false); + std::cout << "--------------- Reading parameters ---------------" << std::endl; + + // If we have a "deck_filename", grid and props will be read from that. + bool use_deck = param.has("deck_filename"); + boost::scoped_ptr deck; + boost::scoped_ptr grid; + boost::scoped_ptr props; + boost::scoped_ptr rock_comp; + BlackoilState state; + // bool check_well_controls = false; + // int max_well_control_iterations = 0; + double gravity[3] = { 0.0 }; + if (use_deck) { + std::string deck_filename = param.get("deck_filename"); + deck.reset(new EclipseGridParser(deck_filename)); + // Grid init + grid.reset(new GridManager(*deck)); + // Rock and fluid init + props.reset(new BlackoilPropertiesFromDeck(*deck, *grid->c_grid(), param)); + // check_well_controls = param.getDefault("check_well_controls", false); + // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); + // Rock compressibility. + rock_comp.reset(new RockCompressibility(*deck)); + // Gravity. + gravity[2] = deck->hasField("NOGRAV") ? 0.0 : unit::gravity; + // Init state variables (saturation and pressure). + if (param.has("init_saturation")) { + initStateBasic(*grid->c_grid(), *props, param, gravity[2], state); + } else { + initStateFromDeck(*grid->c_grid(), *props, *deck, gravity[2], state); + } + initBlackoilSurfvol(*grid->c_grid(), *props, state); + } else { + // Grid init. + const int nx = param.getDefault("nx", 100); + const int ny = param.getDefault("ny", 100); + const int nz = param.getDefault("nz", 1); + const double dx = param.getDefault("dx", 1.0); + const double dy = param.getDefault("dy", 1.0); + const double dz = param.getDefault("dz", 1.0); + grid.reset(new GridManager(nx, ny, nz, dx, dy, dz)); + // Rock and fluid init. + props.reset(new BlackoilPropertiesBasic(param, grid->c_grid()->dimensions, grid->c_grid()->number_of_cells)); + // Rock compressibility. + rock_comp.reset(new RockCompressibility(param)); + // Gravity. + gravity[2] = param.getDefault("gravity", 0.0); + // Init state variables (saturation and pressure). + initStateBasic(*grid->c_grid(), *props, param, gravity[2], state); + initBlackoilSurfvol(*grid->c_grid(), *props, state); + } + + bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); + const double *grav = use_gravity ? &gravity[0] : 0; + + // Initialising src + int num_cells = grid->c_grid()->number_of_cells; + std::vector src(num_cells, 0.0); + if (use_deck) { + // Do nothing, wells will be the driving force, not source terms. + } else { + // Compute pore volumes, in order to enable specifying injection rate + // terms of total pore volume. + std::vector porevol; + if (rock_comp->isActive()) { + computePorevolume(*grid->c_grid(), props->porosity(), *rock_comp, state.pressure(), porevol); + } else { + computePorevolume(*grid->c_grid(), props->porosity(), porevol); + } + const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); + const double default_injection = use_gravity ? 0.0 : 0.1; + const double flow_per_sec = param.getDefault("injected_porevolumes_per_day", default_injection) + *tot_porevol_init/unit::day; + src[0] = flow_per_sec; + src[num_cells - 1] = -flow_per_sec; + } + + // Boundary conditions. + FlowBCManager bcs; + if (param.getDefault("use_pside", false)) { + int pside = param.get("pside"); + double pside_pressure = param.get("pside_pressure"); + bcs.pressureSide(*grid->c_grid(), FlowBCManager::Side(pside), pside_pressure); + } + + // Linear solver. + LinearSolverFactory linsolver(param); + + // Write parameters used for later reference. + bool output = param.getDefault("output", true); + std::ofstream epoch_os; + std::string output_dir; + if (output) { + output_dir = + param.getDefault("output_dir", std::string("output")); + boost::filesystem::path fpath(output_dir); + try { + create_directories(fpath); + } + catch (...) { + THROW("Creating directories failed: " << fpath); + } + std::string filename = output_dir + "/epoch_timing.param"; + epoch_os.open(filename.c_str(), std::fstream::trunc | std::fstream::out); + // open file to clean it. The file is appended to in SimulatorTwophase + filename = output_dir + "/step_timing.param"; + std::fstream step_os(filename.c_str(), std::fstream::trunc | std::fstream::out); + step_os.close(); + param.writeParam(output_dir + "/simulation.param"); + } + + + std::cout << "\n\n================ Starting main simulation loop ===============\n" + << " (number of epochs: " + << (use_deck ? deck->numberOfEpochs() : 1) << ")\n\n" << std::flush; + + SimulatorReport rep; + if (!use_deck) { + // Simple simulation without a deck. + WellsManager wells; // no wells. + SimulatorCompressibleTwophase simulator(param, + *grid->c_grid(), + *props, + rock_comp->isActive() ? rock_comp.get() : 0, + wells, + src, + bcs.c_bcs(), + linsolver, + grav); + SimulatorTimer simtimer; + simtimer.init(param); + warnIfUnusedParams(param); + WellState well_state; + well_state.init(0, state); + rep = simulator.run(simtimer, state, well_state); + } else { + // With a deck, we may have more epochs etc. + WellState well_state; + int step = 0; + SimulatorTimer simtimer; + // Use timer for last epoch to obtain total time. + deck->setCurrentEpoch(deck->numberOfEpochs() - 1); + simtimer.init(*deck); + const double total_time = simtimer.totalTime(); + for (int epoch = 0; epoch < deck->numberOfEpochs(); ++epoch) { + // Set epoch index. + deck->setCurrentEpoch(epoch); + + // Update the timer. + if (deck->hasField("TSTEP")) { + simtimer.init(*deck); + } else { + if (epoch != 0) { + THROW("No TSTEP in deck for epoch " << epoch); + } + simtimer.init(param); + } + simtimer.setCurrentStepNum(step); + simtimer.setTotalTime(total_time); + + // Report on start of epoch. + std::cout << "\n\n-------------- Starting epoch " << epoch << " --------------" + << "\n (number of steps: " + << simtimer.numSteps() - step << ")\n\n" << std::flush; + + // Create new wells, well_state + WellsManager wells(*deck, *grid->c_grid(), props->permeability()); + // @@@ HACK: we should really make a new well state and + // properly transfer old well state to it every epoch, + // since number of wells may change etc. + if (epoch == 0) { + well_state.init(wells.c_wells(), state); + } + + // Create and run simulator. + SimulatorCompressibleTwophase simulator(param, + *grid->c_grid(), + *props, + rock_comp->isActive() ? rock_comp.get() : 0, + wells, + src, + bcs.c_bcs(), + linsolver, + grav); + if (epoch == 0) { + warnIfUnusedParams(param); + } + SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state); + if (output) { + epoch_rep.reportParam(epoch_os); + } + // Update total timing report and remember step number. + rep += epoch_rep; + step = simtimer.currentStepNum(); + } + } + + std::cout << "\n\n================ End of simulation ===============\n\n"; + rep.report(std::cout); + + if (output) { + std::string filename = output_dir + "/walltime.param"; + std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out); + rep.reportParam(tot_os); + } + +} diff --git a/examples/sim_2p_incomp_reorder.cpp b/examples/sim_2p_incomp_reorder.cpp index bc688b3f..0a7c3a71 100644 --- a/examples/sim_2p_incomp_reorder.cpp +++ b/examples/sim_2p_incomp_reorder.cpp @@ -43,9 +43,10 @@ #include #include -#include +#include #include +#include #include #include @@ -53,6 +54,18 @@ #include +namespace +{ + void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param) + { + if (param.anyUnused()) { + std::cout << "-------------------- Unused parameters: --------------------\n"; + param.displayUsage(); + std::cout << "----------------------------------------------------------------" << std::endl; + } + } +} // anon namespace + // ----------------- Main program ----------------- @@ -81,9 +94,7 @@ main(int argc, char** argv) // Grid init grid.reset(new GridManager(*deck)); // Rock and fluid init - const int* gc = grid->c_grid()->global_cell; - std::vector global_cell(gc, gc + grid->c_grid()->number_of_cells); - props.reset(new IncompPropertiesFromDeck(*deck, global_cell)); + props.reset(new IncompPropertiesFromDeck(*deck, *grid->c_grid())); // check_well_controls = param.getDefault("check_well_controls", false); // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); // Rock compressibility. @@ -157,17 +168,28 @@ main(int argc, char** argv) // Linear solver. LinearSolverFactory linsolver(param); - // Warn if any parameters are unused. - // if (param.anyUnused()) { - // std::cout << "-------------------- Unused parameters: --------------------\n"; - // param.displayUsage(); - // std::cout << "----------------------------------------------------------------" << std::endl; - // } - // Write parameters used for later reference. - // if (output) { - // param.writeParam(output_dir + "/spu_2p.param"); - // } + bool output = param.getDefault("output", true); + std::ofstream epoch_os; + std::string output_dir; + if (output) { + output_dir = + param.getDefault("output_dir", std::string("output")); + boost::filesystem::path fpath(output_dir); + try { + create_directories(fpath); + } + catch (...) { + THROW("Creating directories failed: " << fpath); + } + std::string filename = output_dir + "/epoch_timing.param"; + epoch_os.open(filename.c_str(), std::fstream::trunc | std::fstream::out); + // open file to clean it. The file is appended to in SimulatorTwophase + filename = output_dir + "/step_timing.param"; + std::fstream step_os(filename.c_str(), std::fstream::trunc | std::fstream::out); + step_os.close(); + param.writeParam(output_dir + "/simulation.param"); + } std::cout << "\n\n================ Starting main simulation loop ===============\n" @@ -177,17 +199,19 @@ main(int argc, char** argv) SimulatorReport rep; if (!use_deck) { // Simple simulation without a deck. - SimulatorTwophase simulator(param, - *grid->c_grid(), - *props, - rock_comp->isActive() ? rock_comp.get() : 0, - 0, // wells - src, - bcs.c_bcs(), - linsolver, - grav); + WellsManager wells; // no wells. + SimulatorIncompTwophase simulator(param, + *grid->c_grid(), + *props, + rock_comp->isActive() ? rock_comp.get() : 0, + wells, + src, + bcs.c_bcs(), + linsolver, + grav); SimulatorTimer simtimer; simtimer.init(param); + warnIfUnusedParams(param); WellState well_state; well_state.init(0, state); rep = simulator.run(simtimer, state, well_state); @@ -221,7 +245,7 @@ main(int argc, char** argv) << "\n (number of steps: " << simtimer.numSteps() - step << ")\n\n" << std::flush; - // Create new wells, well_satate + // Create new wells, well_state WellsManager wells(*deck, *grid->c_grid(), props->permeability()); // @@@ HACK: we should really make a new well state and // properly transfer old well state to it every epoch, @@ -231,17 +255,22 @@ main(int argc, char** argv) } // Create and run simulator. - SimulatorTwophase simulator(param, - *grid->c_grid(), - *props, - rock_comp->isActive() ? rock_comp.get() : 0, - wells.c_wells(), - src, - bcs.c_bcs(), - linsolver, - grav); + SimulatorIncompTwophase simulator(param, + *grid->c_grid(), + *props, + rock_comp->isActive() ? rock_comp.get() : 0, + wells, + src, + bcs.c_bcs(), + linsolver, + grav); + if (epoch == 0) { + warnIfUnusedParams(param); + } SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state); - + if (output) { + epoch_rep.reportParam(epoch_os); + } // Update total timing report and remember step number. rep += epoch_rep; step = simtimer.currentStepNum(); @@ -250,4 +279,11 @@ main(int argc, char** argv) std::cout << "\n\n================ End of simulation ===============\n\n"; rep.report(std::cout); + + if (output) { + std::string filename = output_dir + "/walltime.param"; + std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out); + rep.reportParam(tot_os); + } + } diff --git a/examples/sim_wateroil.cpp b/examples/sim_wateroil.cpp index 1ffdd2b0..73374f00 100644 --- a/examples/sim_wateroil.cpp +++ b/examples/sim_wateroil.cpp @@ -68,8 +68,6 @@ #include #include -#define TRANSPORT_SOLVER_FIXED 1 - template static void outputState(const UnstructuredGrid& grid, @@ -143,7 +141,6 @@ main(int argc, char** argv) std::cout << "--------------- Reading parameters ---------------" << std::endl; // Reading various control parameters. - const bool use_reorder = param.getDefault("use_reorder", true); const bool output = param.getDefault("output", true); std::string output_dir; int output_interval = 1; @@ -178,9 +175,7 @@ main(int argc, char** argv) // Grid init grid.reset(new Opm::GridManager(deck)); // Rock and fluid init - const int* gc = grid->c_grid()->global_cell; - std::vector global_cell(gc, gc + grid->c_grid()->number_of_cells); - props.reset(new Opm::BlackoilPropertiesFromDeck(deck, global_cell)); + props.reset(new BlackoilPropertiesFromDeck(deck, *grid->c_grid(), param)); // Wells init. wells.reset(new Opm::WellsManager(deck, *grid->c_grid(), props->permeability())); check_well_controls = param.getDefault("check_well_controls", false); @@ -233,28 +228,8 @@ main(int argc, char** argv) } } bool use_segregation_split = false; - bool use_column_solver = false; - bool use_gauss_seidel_gravity = false; - if (use_gravity && use_reorder) { + if (use_gravity) { use_segregation_split = param.getDefault("use_segregation_split", use_segregation_split); - if (use_segregation_split) { - use_column_solver = param.getDefault("use_column_solver", use_column_solver); - if (use_column_solver) { - use_gauss_seidel_gravity = param.getDefault("use_gauss_seidel_gravity", use_gauss_seidel_gravity); - } - } - } - - // Check that rock compressibility is not used with solvers that do not handle it. - // int nl_pressure_maxiter = 0; - // double nl_pressure_tolerance = 0.0; - if (rock_comp->isActive()) { - THROW("No rock compressibility in comp. pressure solver yet."); - if (!use_reorder) { - THROW("Cannot run implicit (non-reordering) transport solver with rock compressibility yet."); - } - // nl_pressure_maxiter = param.getDefault("nl_pressure_maxiter", 10); - // nl_pressure_tolerance = param.getDefault("nl_pressure_tolerance", 1.0); // in Pascal } // Source-related variables init. @@ -266,11 +241,11 @@ main(int argc, char** argv) // Extra rock init. std::vector porevol; if (rock_comp->isActive()) { - THROW("CompressibleTpfa solver does not handle this."); computePorevolume(*grid->c_grid(), props->porosity(), *rock_comp, state.pressure(), porevol); } else { computePorevolume(*grid->c_grid(), props->porosity(), porevol); } + std::vector initial_porevol = porevol; double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); @@ -297,21 +272,20 @@ main(int argc, char** argv) const double nl_press_change_tol = param.getDefault("nl_press_change_tol", 10.0); const int nl_press_maxiter = param.getDefault("nl_press_maxiter", 20); const double *grav = use_gravity ? &gravity[0] : 0; - Opm::CompressibleTpfa psolver(*grid->c_grid(), *props, linsolver, + Opm::CompressibleTpfa psolver(*grid->c_grid(), *props, rock_comp.get(), linsolver, nl_press_res_tol, nl_press_change_tol, nl_press_maxiter, grav, wells->c_wells()); // Reordering solver. -#if TRANSPORT_SOLVER_FIXED const double nl_tolerance = param.getDefault("nl_tolerance", 1e-9); const int nl_maxiter = param.getDefault("nl_maxiter", 30); Opm::TransportModelCompressibleTwophase reorder_model(*grid->c_grid(), *props, nl_tolerance, nl_maxiter); - if (use_gauss_seidel_gravity) { + if (use_segregation_split) { reorder_model.initGravity(grav); } -#endif // TRANSPORT_SOLVER_FIXED + // Column-based gravity segregation solver. std::vector > columns; - if (use_column_solver) { + if (use_segregation_split) { Opm::extractColumn(*grid->c_grid(), columns); } @@ -414,24 +388,30 @@ main(int argc, char** argv) Opm::computeTransportSource(*grid->c_grid(), src, state.faceflux(), 1.0, wells->c_wells(), well_state.perfRates(), reorder_src); + // Compute new porevolumes after pressure solve, if necessary. + if (rock_comp->isActive()) { + initial_porevol = porevol; + computePorevolume(*grid->c_grid(), props->porosity(), *rock_comp, state.pressure(), porevol); + } // Solve transport. transport_timer.start(); -#if TRANSPORT_SOLVER_FIXED double stepsize = simtimer.currentStepLength(); if (num_transport_substeps != 1) { stepsize /= double(num_transport_substeps); std::cout << "Making " << num_transport_substeps << " transport substeps." << std::endl; } for (int tr_substep = 0; tr_substep < num_transport_substeps; ++tr_substep) { - reorder_model.solve(&state.faceflux()[0], &state.pressure()[0], &state.surfacevol()[0], - &porevol[0], &reorder_src[0], stepsize, state.saturation()); + // Note that for now we do not handle rock compressibility, + // although the transport solver should be able to. + reorder_model.solve(&state.faceflux()[0], &state.pressure()[0], + &porevol[0], &initial_porevol[0], &reorder_src[0], stepsize, + state.saturation(), state.surfacevol()); // Opm::computeInjectedProduced(*props, state.saturation(), reorder_src, stepsize, injected, produced); if (use_segregation_split) { - THROW("Segregation not implemented yet."); - // reorder_model.solveGravity(columns, &porevol[0], stepsize, state.saturation()); + reorder_model.solveGravity(columns, &state.pressure()[0], &initial_porevol[0], + stepsize, state.saturation(), state.surfacevol()); } } -#endif // TRANSPORT_SOLVER_FIXED transport_timer.stop(); double tt = transport_timer.secsSinceStart(); std::cout << "Transport solver took: " << tt << " seconds." << std::endl; diff --git a/examples/spu_2p.cpp b/examples/spu_2p.cpp index ca05b424..755ef202 100644 --- a/examples/spu_2p.cpp +++ b/examples/spu_2p.cpp @@ -317,9 +317,7 @@ main(int argc, char** argv) // Grid init grid.reset(new Opm::GridManager(deck)); // Rock and fluid init - const int* gc = grid->c_grid()->global_cell; - std::vector global_cell(gc, gc + grid->c_grid()->number_of_cells); - props.reset(new Opm::IncompPropertiesFromDeck(deck, global_cell)); + props.reset(new Opm::IncompPropertiesFromDeck(deck, *grid->c_grid())); // Wells init. wells.reset(new Opm::WellsManager(deck, *grid->c_grid(), props->permeability())); check_well_controls = param.getDefault("check_well_controls", false); @@ -503,7 +501,7 @@ main(int argc, char** argv) // Write parameters used for later reference. if (output) { - param.writeParam(output_dir + "/spu_2p.param"); + param.writeParam(output_dir + "/simulation.param"); } // Main simulation loop. diff --git a/examples/wells_example.cpp b/examples/wells_example.cpp index 9f7be7ec..1c523420 100644 --- a/examples/wells_example.cpp +++ b/examples/wells_example.cpp @@ -38,10 +38,8 @@ int main(int argc, char** argv) // Finally handle the wells WellsManager wells(parser, *grid.c_grid(), NULL); - std::vector global_cells(grid.c_grid()->global_cell, grid.c_grid()->global_cell + grid.c_grid()->number_of_cells); - double gravity[3] = {0.0, 0.0, parameters.getDefault("gravity", 0.0)}; - IncompPropertiesFromDeck incomp_properties(parser, global_cells); + IncompPropertiesFromDeck incomp_properties(parser, *grid.c_grid()); RockCompressibility rock_comp(parser); diff --git a/generate_doc_figures.py b/generate_doc_figures.py index ae42709d..8d4931ee 100755 --- a/generate_doc_figures.py +++ b/generate_doc_figures.py @@ -1,6 +1,24 @@ + +# This script generate the illustration pictures for the documentation. +# +# To run this script, you have to install paraview, see: +# +# http://www.paraview.org/paraview/resources/software.php +# +# Eventually, set up the paths (figure_path, tutorial_data_path, tutorial_path) according to your own installation. +# (The default values should be ok.) +# +# Make sure that pvpython is in your path of executables. +# +# Run the following command at the root of the directory where +# opm-core is installed (which also corresponds to the directory where +# generate_doc_figures is located): +# +# pvpython generate_doc_figures.py +# + from subprocess import call from paraview.simple import * -# from paraview import servermanager from os import remove, mkdir, curdir from os.path import join, isdir @@ -13,7 +31,6 @@ collected_garbage_file = [] if not isdir(figure_path): mkdir(figure_path) -# connection = servermanager.Connect() # tutorial 1 call(join(tutorial_path, "tutorial1")) diff --git a/m4/agmg.m4 b/m4/agmg.m4 index 9e09157b..0a16e9b3 100644 --- a/m4/agmg.m4 +++ b/m4/agmg.m4 @@ -11,10 +11,14 @@ AC_DEFUN([OPM_AGMG],dnl [AS_IF([test -f "$with_agmg/dagmg.f90"],dnl [AC_SUBST([AGMG_SRCDIR], [$with_agmg])[]dnl AC_DEFINE([HAVE_AGMG], [1],dnl - [Define to `1' if Notay's AGMG solver is included])[]dnl + [Define to 1 if Notay's AGMG solver is included.])[]dnl build_agmg="yes"],dnl - [build_agmg="no"])],dnl - [build_agmg="no"])[]dnl + [AC_DEFINE([HAVE_AGMG], [0],dnl + [Define to 0 if Notay's AGMG solver is unavailable.])[]dnl + build_agmg="no"])],dnl + [AC_DEFINE([HAVE_AGMG], [0],dnl + [Define to 0 if Notay's AGMG solver is unwanted.])[]dnl + build_agmg="no"])[]dnl AS_IF([test x"$build_agmg" = x"yes"],dnl [AC_PROG_FC_C_O[]dnl diff --git a/m4/ax_boost_date_time.m4 b/m4/ax_boost_date_time.m4 index 959ab8ad..4e9d57ba 100644 --- a/m4/ax_boost_date_time.m4 +++ b/m4/ax_boost_date_time.m4 @@ -55,11 +55,11 @@ AC_DEFUN([AX_BOOST_DATE_TIME], if test "x$want_boost" = "xyes"; then AC_REQUIRE([AC_PROG_CC]) CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + CPPFLAGS="$CPPFLAGS $OPM_BOOST_CPPFLAGS" export CPPFLAGS LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + LDFLAGS="$LDFLAGS $OPM_BOOST_LDFLAGS" export LDFLAGS AC_CACHE_CHECK(whether the Boost::Date_Time library is available, @@ -74,7 +74,7 @@ AC_DEFUN([AX_BOOST_DATE_TIME], ]) if test "x$ax_cv_boost_date_time" = "xyes"; then AC_DEFINE(HAVE_BOOST_DATE_TIME,,[define if the Boost::Date_Time library is available]) - BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + BOOSTLIBDIR=`echo $OPM_BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` ax_lib="-lboost_date_time" if test "x$ax_boost_user_date_time_lib" = "x"; then diff --git a/m4/ax_boost_filesystem.m4 b/m4/ax_boost_filesystem.m4 index b230a191..2bb74e5a 100644 --- a/m4/ax_boost_filesystem.m4 +++ b/m4/ax_boost_filesystem.m4 @@ -56,11 +56,11 @@ AC_DEFUN([AX_BOOST_FILESYSTEM], if test "x$want_boost" = "xyes"; then AC_REQUIRE([AC_PROG_CC]) CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + CPPFLAGS="$CPPFLAGS $OPM_BOOST_CPPFLAGS" export CPPFLAGS LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + LDFLAGS="$LDFLAGS $OPM_BOOST_LDFLAGS" export LDFLAGS LIBS_SAVED=$LIBS @@ -79,7 +79,7 @@ AC_DEFUN([AX_BOOST_FILESYSTEM], ]) if test "x$ax_cv_boost_filesystem" = "xyes"; then AC_DEFINE(HAVE_BOOST_FILESYSTEM,,[define if the Boost::Filesystem library is available]) - BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + BOOSTLIBDIR=`echo $OPM_BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` ax_lib="-lboost_filesystem" if test "x$ax_boost_user_filesystem_lib" = "x"; then for libextension in `ls $BOOSTLIBDIR/libboost_filesystem*.so* $BOOSTLIBDIR/libboost_filesystem*.dylib* $BOOSTLIBDIR/libboost_filesystem*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_filesystem.*\)\.so.*$;\1;' -e 's;^lib\(boost_filesystem.*\)\.a*$;\1;' -e 's;^lib\(boost_filesystem.*\)\.dylib$;\1;'` ; do diff --git a/m4/ax_boost_system.m4 b/m4/ax_boost_system.m4 index b97f3739..2d47ad45 100644 --- a/m4/ax_boost_system.m4 +++ b/m4/ax_boost_system.m4 @@ -57,11 +57,11 @@ AC_DEFUN([AX_BOOST_SYSTEM], AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_CANONICAL_BUILD]) CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + CPPFLAGS="$CPPFLAGS $OPM_BOOST_CPPFLAGS" export CPPFLAGS LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + LDFLAGS="$LDFLAGS $OPM_BOOST_LDFLAGS" export LDFLAGS AC_CACHE_CHECK(whether the Boost::System library is available, @@ -76,10 +76,10 @@ AC_DEFUN([AX_BOOST_SYSTEM], AC_LANG_POP([C++]) ]) if test "x$ax_cv_boost_system" = "xyes"; then - AC_SUBST(BOOST_CPPFLAGS) + AC_SUBST(OPM_BOOST_CPPFLAGS) AC_DEFINE(HAVE_BOOST_SYSTEM,,[define if the Boost::System library is available]) - BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + BOOSTLIBDIR=`echo $OPM_BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` ax_lib="-lboost_system" LDFLAGS_SAVE=$LDFLAGS diff --git a/m4/ax_boost_unit_test_framework.m4 b/m4/ax_boost_unit_test_framework.m4 index fa34c974..0cee8baa 100644 --- a/m4/ax_boost_unit_test_framework.m4 +++ b/m4/ax_boost_unit_test_framework.m4 @@ -54,11 +54,11 @@ AC_DEFUN([AX_BOOST_UNIT_TEST_FRAMEWORK], if test "x$want_boost" = "xyes"; then AC_REQUIRE([AC_PROG_CC]) CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + CPPFLAGS="$CPPFLAGS $OPM_BOOST_CPPFLAGS" export CPPFLAGS LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + LDFLAGS="$LDFLAGS $OPM_BOOST_LDFLAGS" export LDFLAGS AC_CACHE_CHECK(whether the Boost::Unit_Test_Framework library is available, @@ -72,7 +72,7 @@ AC_DEFUN([AX_BOOST_UNIT_TEST_FRAMEWORK], ]) if test "x$ax_cv_boost_unit_test_framework" = "xyes"; then AC_DEFINE(HAVE_BOOST_UNIT_TEST_FRAMEWORK,,[define if the Boost::Unit_Test_Framework library is available]) - BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + BOOSTLIBDIR=`echo $OPM_BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` ax_lib="-lboost_unit_test_framework" if test "x$ax_boost_user_unit_test_framework_lib" = "x"; then diff --git a/m4/ax_dune_istl.m4 b/m4/ax_dune_istl.m4 index 2f0fd55b..40fa9072 100644 --- a/m4/ax_dune_istl.m4 +++ b/m4/ax_dune_istl.m4 @@ -31,7 +31,9 @@ AC_DEFUN([AX_DUNE_ISTL], "x$ax_cv_dune_common_available" = "xyes"],dnl [AC_DEFINE([HAVE_DUNE_ISTL], [1],dnl [Define to 1 if `dune-istl' is available]) - ])[]dnl + ],dnl + [AC_DEFINE([HAVE_DUNE_ISTL], [0],dnl + [Define to 0 if `dune-istl' is unavailable.])])[]dnl AM_CONDITIONAL([DUNE_ISTL], [test "x$ax_cv_dune_istl_available" = "xyes" -a \ diff --git a/m4/ax_boost_base.m4 b/m4/opm_boost_base.m4 similarity index 87% rename from m4/ax_boost_base.m4 rename to m4/opm_boost_base.m4 index 54a2a1be..430f9abb 100644 --- a/m4/ax_boost_base.m4 +++ b/m4/opm_boost_base.m4 @@ -4,7 +4,7 @@ # # SYNOPSIS # -# AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# OPM_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # # DESCRIPTION # @@ -17,11 +17,11 @@ # # This macro calls: # -# AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS) +# AC_SUBST(OPM_BOOST_CPPFLAGS) / AC_SUBST(OPM_BOOST_LDFLAGS) # # And sets: # -# HAVE_BOOST +# OPM_HAVE_BOOST # # LICENSE # @@ -35,7 +35,7 @@ #serial 20 -AC_DEFUN([AX_BOOST_BASE], +AC_DEFUN([OPM_BOOST_BASE], [ AC_ARG_WITH([boost], [AS_HELP_STRING([--with-boost@<:@=ARG@:>@], @@ -99,10 +99,10 @@ if test "x$want_boost" = "xyes"; then dnl this location ist chosen if boost libraries are installed with the --layout=system option dnl or if you install boost with RPM if test "$ac_boost_path" != ""; then - BOOST_CPPFLAGS="-I$ac_boost_path/include" + OPM_BOOST_CPPFLAGS="-I$ac_boost_path/include" for ac_boost_path_tmp in $libsubdirs; do if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then - BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" + OPM_BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" break fi done @@ -112,8 +112,8 @@ if test "x$want_boost" = "xyes"; then for libsubdir in $libsubdirs ; do if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi done - BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" - BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" + OPM_BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" + OPM_BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" break; fi done @@ -122,15 +122,15 @@ if test "x$want_boost" = "xyes"; then dnl overwrite ld flags if we have required special directory with dnl --with-boost-libdir parameter if test "$ac_boost_lib_path" != ""; then - BOOST_LDFLAGS="-L$ac_boost_lib_path" + OPM_BOOST_LDFLAGS="-L$ac_boost_lib_path" fi CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + CPPFLAGS="$CPPFLAGS $OPM_BOOST_CPPFLAGS" export CPPFLAGS LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + LDFLAGS="$LDFLAGS $OPM_BOOST_LDFLAGS" export LDFLAGS AC_REQUIRE([AC_PROG_CXX]) @@ -166,7 +166,7 @@ if test "x$want_boost" = "xyes"; then _version=$_version_tmp fi VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` - BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" + OPM_BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" done fi else @@ -185,12 +185,12 @@ if test "x$want_boost" = "xyes"; then done VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` - BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" + OPM_BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" if test "$ac_boost_lib_path" = ""; then for libsubdir in $libsubdirs ; do if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi done - BOOST_LDFLAGS="-L$best_path/$libsubdir" + OPM_BOOST_LDFLAGS="-L$best_path/$libsubdir" fi fi @@ -205,16 +205,16 @@ if test "x$want_boost" = "xyes"; then V_CHECK=`expr $stage_version_shorten \>\= $_version` if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT) - BOOST_CPPFLAGS="-I$BOOST_ROOT" - BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" + OPM_BOOST_CPPFLAGS="-I$BOOST_ROOT" + OPM_BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" fi fi fi fi - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + CPPFLAGS="$CPPFLAGS $OPM_BOOST_CPPFLAGS" export CPPFLAGS - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + LDFLAGS="$LDFLAGS $OPM_BOOST_LDFLAGS" export LDFLAGS AC_LANG_PUSH(C++) @@ -243,10 +243,12 @@ if test "x$want_boost" = "xyes"; then fi # execute ACTION-IF-NOT-FOUND (if present): ifelse([$3], , :, [$3]) + AC_DEFINE([OPM_HAVE_BOOST], [0],dnl + [Define to `0' if the Boost library is not available]) else - AC_SUBST(BOOST_CPPFLAGS) - AC_SUBST(BOOST_LDFLAGS) - AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available]) + AC_SUBST([OPM_BOOST_CPPFLAGS]) + AC_SUBST([OPM_BOOST_LDFLAGS]) + AC_DEFINE([OPM_HAVE_BOOST], [1], [define if the Boost library is available]) # execute ACTION-IF-FOUND (if present): ifelse([$2], , :, [$2]) fi diff --git a/m4/opm_core.m4 b/m4/opm_core.m4 new file mode 100644 index 00000000..efb75c13 --- /dev/null +++ b/m4/opm_core.m4 @@ -0,0 +1,66 @@ +dnl -*- autoconf -*- + # Macros needed to find OPM-core and dependent libraries. They are called by +# the macros in ${top_src_dir}/dependencies.m4, which is generated by +# "dunecontrol autogen" +AC_DEFUN([OPM_CORE_CHECKS], +[ + +# Checks for libraries. + +# Bring in numerics support (standard library component) +AC_SEARCH_LIBS([sqrt], [m]) + +OPM_LAPACK + +OPM_BOOST_BASE([1.37]) +AX_BOOST_SYSTEM +AX_BOOST_DATE_TIME +AX_BOOST_FILESYSTEM +AX_BOOST_UNIT_TEST_FRAMEWORK + +AX_DUNE_ISTL +OPM_AGMG + +# Checks for header files. +AC_CHECK_HEADERS([float.h limits.h stddef.h stdlib.h string.h]) + +AC_CHECK_HEADERS([suitesparse/umfpack.h], + [umfpack_header=yes], + [umfpack_header=no]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_TYPE_SIZE_T +AC_CHECK_TYPES([ptrdiff_t]) + +# Checks for library functions. +AC_CHECK_FUNCS([floor memset memmove strchr strtol sqrt pow]) +AC_FUNC_STRTOD + +# Search for UMFPACK direct sparse solver. +AC_SEARCH_LIBS([amd_free], [amd]) +AC_SEARCH_LIBS([camd_free], [camd]) +AC_SEARCH_LIBS([colamd_set_defaults], [colamd]) +AC_SEARCH_LIBS([ccolamd_set_defaults], [ccolamd]) +AC_SEARCH_LIBS([cholmod_l_start], [cholmod]) +AC_SEARCH_LIBS([umfpack_dl_solve], [umfpack],dnl + [umfpack_lib=yes], [umfpack_lib=no]) + +AM_CONDITIONAL([UMFPACK], + [test "x$umfpack_header" != "xno" -a "x$umfpack_lib" != "xno"]) + +m4_ifdef([AM_COND_IF], +[AM_COND_IF([UMFPACK], [], + [AC_MSG_NOTICE([Found no working installation of UMFPACK. + UMFPACK support is disabled.])]) +]) + +]) + +# Additional checks needed to find eWoms +# This macro should be invoked by every module which depends on dumux, but +# not by dumux itself +AC_DEFUN([OPM_CORE_CHECK_MODULE], +[ + OPM_CORE_CHECK_MODULES([opm-core],[opm/core/grid.h],[create_grid_empty()]) +]) diff --git a/m4/opm_core_check_modules.m4 b/m4/opm_core_check_modules.m4 new file mode 100644 index 00000000..1f7cf878 --- /dev/null +++ b/m4/opm_core_check_modules.m4 @@ -0,0 +1,281 @@ +dnl -*- autoconf -*- + +# OPM_CORE_CHECK_MODULES(NAME, HEADER, SYMBOL) +# +# THIS MACRO IS JUST A COPY OF DUNE_CHECK_MODULES WITH THE REQUIREMENT +# THAT ALL HEADERS MUST RESIDE IN $MODULE_ROOT/dune REMOVED. REMOVE +# THIS MACRO AS SOON AS DUNE DOES NOT ENFORCE THIS ANYMORE. +# +# Generic check for dune modules. This macro should not be used directly, but +# in the modules m4/{module}.m4 in the {MODULE}_CHECK_MODULE macro. The +# {MODULE}_CHECK_MODULE macro knows the parameters to call this +# DUNE_CHECK_MODULES macro with, and it does not take any parameters itself, +# so it may be used with AC_REQUIRE. +# +# NAME Name of the module, lowercase with dashes (like "dune-common"). The +# value must be known when autoconf runs, so shell variables in the +# value are not permissible. +# +# HEADER Header to check for. The check will really be for , +# so the header must reside within a directory called "dune". +# +# SYMBOL Symbol to check for in the module's library. If this argument is +# empty or missing, it is assumed that the module does not provide a +# library. The value must be known when autoconf runs, so shell +# variables in the value are not permissible. This value is actually +# handed to AC_TRY_LINK unchanged as the FUNCTION-BODY argument, so it +# may contain more complex stuff than a simple symbol. +# +# The name of the library is assumed to be the same as the module name, +# with any occurance of "-" removed. The path of the library is +# obtained from pkgconfig for installed modules, or assumed to be the +# directory "lib" within the modules root for non-installed modules. +# +# In the following, {module} is {NAME} with any "-" replaced by "_" and +# {MODULE} is the uppercase version of {module}. +# +# configure options: +# --with-{NAME} +# +# configure/shell variables: +# {MODULE}_ROOT, {MODULE}_LIBDIR +# HAVE_{MODULE} (1 or 0) +# with_{module} ("yes" or "no") +# DUNE_CPPFLAGS, DUNE_LDFLAGS, DUNE_LIBS (adds the modules values here, +# substitution done by DUNE_CHECK_ALL) +# ALL_PKG_CPPFLAGS, ALL_PKG_LDFLAGS, ALL_PKG_LIBS (adds the modules values +# here, substitution done by DUNE_CHECK_ALL) +# {MODULE}_VERSION +# {MODULE}_VERSION_MAJOR +# {MODULE}_VERSION_MINOR +# {MODULE}_VERSION_REVISION +# +# configure substitutions/makefile variables: +# {MODULE}_CPPFLAGS, {MODULE}_LDFLAGS, {MODULE}_LIBS +# {MODULE}_ROOT +# {MODULE}_LIBDIR (only if modules provides a library) +# +# preprocessor defines: +# HAVE_{MODULE} (1 or undefined) +# {MODULE}_VERSION +# {MODULE}_VERSION_MAJOR +# {MODULE}_VERSION_MINOR +# {MODULE}_VERSION_REVISION +# +# automake conditionals: +# HAVE_{MODULE} +AC_DEFUN([OPM_CORE_CHECK_MODULES],[ + AC_REQUIRE([AC_PROG_CXX]) + AC_REQUIRE([AC_PROG_CXXCPP]) + AC_REQUIRE([PKG_PROG_PKG_CONFIG]) + AC_REQUIRE([DUNE_DISABLE_LIBCHECK]) + AC_REQUIRE([LT_OUTPUT]) + + # ____DUNE_CHECK_MODULES_____ ($1) + + m4_pushdef([_dune_name], [$1]) + m4_pushdef([_dune_module], [m4_translit(_dune_name, [-], [_])]) + m4_pushdef([_dune_header], [$2]) + m4_pushdef([_dune_ldpath], [lib]) + m4_pushdef([_dune_lib], [m4_translit(_dune_name, [-], [])]) + m4_pushdef([_dune_symbol], [$3]) + m4_pushdef([_DUNE_MODULE], [m4_toupper(_dune_module)]) + + # switch tests to c++ + AC_LANG_PUSH([C++]) + + # the usual option... + AC_ARG_WITH(_dune_name, + AS_HELP_STRING([--with-_dune_name=PATH],[_dune_module directory])) + + # backup of flags + ac_save_CPPFLAGS="$CPPFLAGS" + ac_save_LIBS="$LIBS" + ac_save_LDFLAGS="$LDFLAGS" + CPPFLAGS="" + LIBS="" + + ## + ## Where is the module $1? + ## + + AC_MSG_CHECKING([for $1 installation or source tree]) + + # is a directory set? + AS_IF([test -z "$with_[]_dune_module"],[ + # + # search module $1 via pkg-config + # + with_[]_dune_module="global installation" + AS_IF([test -z "$PKG_CONFIG"],[ + AC_MSG_RESULT([failed]) + AC_MSG_NOTICE([could not search for module _dune_name]) + AC_MSG_ERROR([pkg-config is required for using installed modules]) + ]) + AS_IF(AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]),[ + _dune_cm_CPPFLAGS="`$PKG_CONFIG --cflags _dune_name`" 2>/dev/null + _DUNE_MODULE[]_ROOT="`$PKG_CONFIG --variable=prefix _dune_name`" 2>/dev/null + _DUNE_MODULE[]_VERSION="`$PKG_CONFIG --modversion _dune_name`" 2>/dev/null + _dune_cm_LDFLAGS="" + ifelse(_dune_symbol,, + [_DUNE_MODULE[]_LIBDIR="" + _dune_cm_LIBS=""], + [_DUNE_MODULE[]_LIBDIR=`$PKG_CONFIG --variable=libdir _dune_name 2>/dev/null` + _dune_cm_LIBS="-L$_DUNE_MODULE[]_LIBDIR -l[]_dune_lib"]) + HAVE_[]_DUNE_MODULE=1 + AC_MSG_RESULT([global installation in $_DUNE_MODULE[]_ROOT]) + ],[ + HAVE_[]_DUNE_MODULE=0 + AC_MSG_RESULT([not found]) + ]) + ],[ + # + # path for module $1 is specified via command line + # + AS_IF([test -d "$with_[]_dune_module"],[ + # expand tilde / other stuff + _DUNE_MODULE[]_ROOT=`cd $with_[]_dune_module && pwd` + + # expand search path (otherwise empty CPPFLAGS) + AS_IF([test -d "$_DUNE_MODULE[]_ROOT/include/dune"],[ + # Dune was installed into directory given by with-dunecommon + _dune_cm_CPPFLAGS="-I$_DUNE_MODULE[]_ROOT/include" + _DUNE_MODULE[]_BUILDDIR=_DUNE_MODULE[]_ROOT + _DUNE_MODULE[]_VERSION="`PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$_DUNE_MODULE[]_ROOT/lib/pkgconfig $PKG_CONFIG --modversion _dune_name`" 2>/dev/null + ],[ + _DUNE_MODULE[]_SRCDIR=$_DUNE_MODULE[]_ROOT + # extract src and build path from Makefile, if found + AS_IF([test -f $_DUNE_MODULE[]_ROOT/Makefile],[ + _DUNE_MODULE[]_SRCDIR="`sed -ne '/^abs_top_srcdir = /{s/^abs_top_srcdir = //; p;}' $_DUNE_MODULE[]_ROOT/Makefile`" + ]) + _dune_cm_CPPFLAGS="-I$_DUNE_MODULE[]_SRCDIR" + _DUNE_MODULE[]_VERSION="`grep Version $_DUNE_MODULE[]_SRCDIR/dune.module | sed -e 's/^Version: *//'`" 2>/dev/null + ]) + _dune_cm_LDFLAGS="" + ifelse(_dune_symbol,, + [_DUNE_MODULE[]_LIBDIR="" + _dune_cm_LIBS=""], + [_DUNE_MODULE[]_LIBDIR="$_DUNE_MODULE[]_ROOT/lib" + _dune_cm_LIBS="-L$_DUNE_MODULE[]_LIBDIR -l[]_dune_lib"]) + # set expanded module path + with_[]_dune_module="$_DUNE_MODULE[]_ROOT" + HAVE_[]_DUNE_MODULE=1 + AC_MSG_RESULT([found in $_DUNE_MODULE[]_ROOT]) + ],[ + HAVE_[]_DUNE_MODULE=0 + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([_dune_name-directory $with_[]_dune_module does not exist]) + ]) + ]) + + CPPFLAGS="$ac_save_CPPFLAGS $DUNE_CPPFLAGS $_dune_cm_CPPFLAGS" + ## + ## check for an arbitrary header + ## + AS_IF([test "$HAVE_[]_DUNE_MODULE" != "1"],[ + AC_CHECK_HEADER([[]_dune_header], + [HAVE_[]_DUNE_MODULE=1], [HAVE_[]_DUNE_MODULE=0]) + ]) + + AS_IF([test "$HAVE_[]_DUNE_MODULE" != "1"],[ + AC_MSG_WARN([$_DUNE_MODULE[]_ROOT does not seem to contain a valid _dune_name (dune/[]_dune_header not found)]) + ]) + + ## + ## check for lib (if lib name was provided) + ## + ifelse(_dune_symbol,, + AC_MSG_NOTICE([_dune_name does not provide libs]), + + AS_IF([test "x$enable_dunelibcheck" = "xno"],[ + AC_MSG_WARN([library check for _dune_name is disabled. DANGEROUS!]) + ],[ + AS_IF([test "x$HAVE_[]_DUNE_MODULE" = "x1"],[ + + # save current LDFLAGS + ac_save_CXX="$CXX" + HAVE_[]_DUNE_MODULE=0 + + # define LTCXXLINK like it will be defined in the Makefile + CXX="./libtool --tag=CXX --mode=link $ac_save_CXX" + + # use module LDFLAGS + LDFLAGS="$ac_save_LDFLAGS $DUNE_LDFLAGS $_dune_cm_LDFLAGS" + LIBS="$_dune_cm_LIBS $DUNE_LIBS $LIBS" + + AC_MSG_CHECKING([for lib[]_dune_lib]) + + AC_TRY_LINK(dnl + [#]include<_dune_header>, + _dune_symbol, + [ + AC_MSG_RESULT([yes]) + HAVE_[]_DUNE_MODULE=1 + ],[ + AC_MSG_RESULT([no]) + HAVE_[]_DUNE_MODULE=0 + AS_IF([test -n "$_DUNE_MODULE[]_ROOT"],[ + AC_MSG_WARN([$with_[]_dune_module does not seem to contain a valid _dune_name (failed to link with lib[]_dune_lib[].la)]) + ]) + ] + ) + ]) + + # reset variables + CXX="$ac_save_CXX" + ]) + ) + + # did we succeed? + AS_IF([test "x$HAVE_[]_DUNE_MODULE" = "x1"],[ + # add the module's own flags and libs to the modules and the global + # variables + DUNE_ADD_MODULE_DEPS(m4_defn([_dune_name]), m4_defn([_dune_name]), + [$_dune_cm_CPPFLAGS], [$_dune_cm_LDFLAGS], [$_dune_cm_LIBS]) + + # set variables for our modules + AC_SUBST(_DUNE_MODULE[]_CPPFLAGS, "$_DUNE_MODULE[]_CPPFLAGS") + AC_SUBST(_DUNE_MODULE[]_LDFLAGS, "$_DUNE_MODULE[]_LDFLAGS") + AC_SUBST(_DUNE_MODULE[]_LIBS, "$_DUNE_MODULE[]_LIBS") + AC_SUBST(_DUNE_MODULE[]_ROOT, "$_DUNE_MODULE[]_ROOT") + ifelse(m4_defn([_dune_symbol]),, + [], + [AC_SUBST(_DUNE_MODULE[]_LIBDIR) + ]) + AC_DEFINE(HAVE_[]_DUNE_MODULE, 1, [Define to 1 if] _dune_name [was found]) + + DUNE_PARSE_MODULE_VERSION(_dune_name, $_DUNE_MODULE[]_VERSION) + + # set DUNE_* variables + # This should actually be unneccesary, but I'm keeping it in here for now + # for backward compatibility + DUNE_LDFLAGS="$DUNE_LDFLAGS $_DUNE_MODULE[]_LDFLAGS" + DUNE_LIBS="$_DUNE_MODULE[]_LIBS $DUNE_LIBS" + + with_[]_dune_module="yes" + ],[ + with_[]_dune_module="no" + ]) + + AM_CONDITIONAL(HAVE_[]_DUNE_MODULE, test x$HAVE_[]_DUNE_MODULE = x1) + + # reset previous flags + CPPFLAGS="$ac_save_CPPFLAGS" + LDFLAGS="$ac_save_LDFLAGS" + LIBS="$ac_save_LIBS" + + # add this module to DUNE_SUMMARY + DUNE_MODULE_ADD_SUMMARY_ENTRY(_dune_name) + + # remove local variables + m4_popdef([_dune_name]) + m4_popdef([_dune_module]) + m4_popdef([_dune_header]) + m4_popdef([_dune_ldpath]) + m4_popdef([_dune_lib]) + m4_popdef([_dune_symbol]) + m4_popdef([_DUNE_MODULE]) + + # restore previous language settings (leave C++) + AC_LANG_POP([C++]) +]) diff --git a/m4/opm_dynlink_boost_test.m4 b/m4/opm_dynlink_boost_test.m4 index 89c82908..7005b552 100644 --- a/m4/opm_dynlink_boost_test.m4 +++ b/m4/opm_dynlink_boost_test.m4 @@ -38,14 +38,14 @@ dnl ------------------------------------------------------------------- # system uses dynamic linking of Boost.Test . AC_DEFUN([OPM_DYNLINK_BOOST_TEST], [ -AC_REQUIRE([AX_BOOST_BASE]) +AC_REQUIRE([OPM_BOOST_BASE]) AC_REQUIRE([AX_BOOST_UNIT_TEST_FRAMEWORK]) -_opm_LIBS_SAVE="$LIBS" -_opm_CPPFLAGS_SAVE="$CPPFLAGS" +_opm_LIBS_SAVE="${LIBS}" +_opm_CPPFLAGS_SAVE="${CPPFLAGS}" -LIBS="${BOOST_LDFLAGS} ${BOOST_UNIT_TEST_FRAMEWORK_LIB} ${LIBS}" -CPPFLAGS="${BOOST_CPPFLAGS} ${CPPFLAGS}" +LIBS="${OPM_BOOST_LDFLAGS} ${BOOST_UNIT_TEST_FRAMEWORK_LIB} ${LIBS}" +CPPFLAGS="${OPM_BOOST_CPPFLAGS} ${CPPFLAGS}" AC_LANG_PUSH([C++]) @@ -66,12 +66,12 @@ AC_CACHE_CHECK([if the Boost.Test library can be linked dynamically],dnl AC_LANG_POP([C++]) -LIBS="$_opm_LIBS_SAVE" -CPPFLAGS="$_opm_CPPFLAGS_SAVE" +LIBS="${_opm_LIBS_SAVE}" +CPPFLAGS="${_opm_CPPFLAGS_SAVE}" -AS_IF([test x"$opm_cv_boost_link_static" = x"yes" -o \ - x"$opm_cv_boost_link_dynamic" = x"yes"], -[AS_IF([test x"$opm_cv_boost_link_dynamic" = x"yes"], +AS_IF([test x"${opm_cv_boost_link_static}" = x"yes" -o \ + x"${opm_cv_boost_link_dynamic}" = x"yes"], +[AS_IF([test x"${opm_cv_boost_link_dynamic}" = x"yes"], [AC_DEFINE([HAVE_DYNAMIC_BOOST_TEST], [1], [Define to `1' if Boost.Test should use BOOST_TEST_DYN_LINK])] [:])[]dnl diff --git a/opm/core/GridManager.cpp b/opm/core/GridManager.cpp index f84692ee..d286e504 100644 --- a/opm/core/GridManager.cpp +++ b/opm/core/GridManager.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include @@ -33,14 +35,26 @@ namespace Opm /// Construct a 3d corner-point grid from a deck. GridManager::GridManager(const Opm::EclipseGridParser& deck) { - // Collect in input struct for preprocessing. - struct grdecl grdecl = deck.get_grdecl(); - - // Process grid. - ug_ = create_grid_cornerpoint(&grdecl, 0.0); - if (!ug_) { - THROW("Failed to construct grid."); - } + // We accept two different ways to specify the grid. + // 1. Corner point format. + // Requires ZCORN, COORDS, DIMENS or SPECGRID, optionally + // ACTNUM, optionally MAPAXES. + // For this format, we will verify that DXV, DYV, DZV, + // DEPTHZ and TOPS are not present. + // 2. Tensor grid format. + // Requires DXV, DYV, DZV, optionally DEPTHZ or TOPS. + // For this format, we will verify that ZCORN, COORDS + // and ACTNUM are not present. + // Note that for TOPS, we only allow a uniform vector of values. + + if (deck.hasField("ZCORN") && deck.hasField("COORD")) { + initFromDeckCornerpoint(deck); + } else if (deck.hasField("DXV") && deck.hasField("DYV") && deck.hasField("DZV")) { + initFromDeckTensorgrid(deck); + } else { + THROW("Could not initialize grid from deck. " + "Need either ZCORN + COORD or DXV + DYV + DZV keywords."); + } } @@ -102,5 +116,94 @@ namespace Opm + // Construct corner-point grid from deck. + void GridManager::initFromDeckCornerpoint(const Opm::EclipseGridParser& deck) + { + // Extract data from deck. + // Collect in input struct for preprocessing. + struct grdecl grdecl = deck.get_grdecl(); + + // Process grid. + ug_ = create_grid_cornerpoint(&grdecl, 0.0); + if (!ug_) { + THROW("Failed to construct grid."); + } + } + + + namespace + { + std::vector coordsFromDeltas(const std::vector& deltas) + { + std::vector coords(deltas.size() + 1); + coords[0] = 0.0; + std::partial_sum(deltas.begin(), deltas.end(), coords.begin() + 1); + return coords; + } + } // anonymous namespace + + + // Construct tensor grid from deck. + void GridManager::initFromDeckTensorgrid(const Opm::EclipseGridParser& deck) + { + // Extract logical cartesian size. + std::vector dims; + if (deck.hasField("DIMENS")) { + dims = deck.getIntegerValue("DIMENS"); + } else if (deck.hasField("SPECGRID")) { + dims = deck.getSPECGRID().dimensions; + } else { + THROW("Deck must have either DIMENS or SPECGRID."); + } + + // Extract coordinates (or offsets from top, in case of z). + const std::vector& dxv = deck.getFloatingPointValue("DXV"); + const std::vector& dyv = deck.getFloatingPointValue("DYV"); + const std::vector& dzv = deck.getFloatingPointValue("DZV"); + std::vector x = coordsFromDeltas(dxv); + std::vector y = coordsFromDeltas(dyv); + std::vector z = coordsFromDeltas(dzv); + + // Check that number of cells given are consistent with DIMENS/SPECGRID. + if (dims[0] != int(dxv.size())) { + THROW("Number of DXV data points do not match DIMENS or SPECGRID."); + } + if (dims[1] != int(dyv.size())) { + THROW("Number of DYV data points do not match DIMENS or SPECGRID."); + } + if (dims[2] != int(dzv.size())) { + THROW("Number of DZV data points do not match DIMENS or SPECGRID."); + } + + // Extract top corner depths, if available. + const double* top_depths = 0; + std::vector top_depths_vec; + if (deck.hasField("DEPTHZ")) { + const std::vector& depthz = deck.getFloatingPointValue("DEPTHZ"); + if (depthz.size() != x.size()*y.size()) { + THROW("Incorrect size of DEPTHZ: " << depthz.size()); + } + top_depths = &depthz[0]; + } else if (deck.hasField("TOPS")) { + // We only support constant values for TOPS. + // It is not 100% clear how we best can deal with + // varying TOPS (stair-stepping grid, or not). + const std::vector& tops = deck.getFloatingPointValue("TOPS"); + if (std::count(tops.begin(), tops.end(), tops[0]) != int(tops.size())) { + THROW("We do not support nonuniform TOPS, please use ZCORN/COORDS instead."); + } + top_depths_vec.resize(x.size()*y.size(), tops[0]); + top_depths = &top_depths_vec[0]; + } + + // Construct grid. + ug_ = create_grid_tensor3d(dxv.size(), dyv.size(), dzv.size(), + &x[0], &y[0], &z[0], top_depths); + if (!ug_) { + THROW("Failed to construct grid."); + } + } + + } // namespace Opm diff --git a/opm/core/GridManager.hpp b/opm/core/GridManager.hpp index 97dd84fd..26e335f2 100644 --- a/opm/core/GridManager.hpp +++ b/opm/core/GridManager.hpp @@ -33,39 +33,46 @@ namespace Opm /// encapsulates creation and destruction of the grid. /// The following grid types can be constructed: /// - 3d corner-point grids (from deck input) + /// - 3d tensor grids (from deck input) /// - 2d cartesian grids /// - 3d cartesian grids /// The resulting UnstructuredGrid is available through the c_grid() method. class GridManager { public: - /// Construct a 3d corner-point grid from a deck. - GridManager(const Opm::EclipseGridParser& deck); + /// Construct a 3d corner-point grid or tensor grid from a deck. + GridManager(const Opm::EclipseGridParser& deck); - /// Construct a 2d cartesian grid with cells of unit size. - GridManager(int nx, int ny); + /// Construct a 2d cartesian grid with cells of unit size. + GridManager(int nx, int ny); - /// Construct a 3d cartesian grid with cells of unit size. - GridManager(int nx, int ny, int nz); + /// Construct a 3d cartesian grid with cells of unit size. + GridManager(int nx, int ny, int nz); - /// Construct a 3d cartesian grid with cells of size [dx, dy, dz]. - GridManager(int nx, int ny, int nz, - double dx, double dy, double dz); + /// Construct a 3d cartesian grid with cells of size [dx, dy, dz]. + GridManager(int nx, int ny, int nz, + double dx, double dy, double dz); - /// Destructor. - ~GridManager(); + /// Destructor. + ~GridManager(); - /// Access the managed UnstructuredGrid. - /// The method is named similarly to c_str() in std::string, - /// to make it clear that we are returning a C-compatible struct. - const UnstructuredGrid* c_grid() const; + /// Access the managed UnstructuredGrid. + /// The method is named similarly to c_str() in std::string, + /// to make it clear that we are returning a C-compatible struct. + const UnstructuredGrid* c_grid() const; private: - // Disable copying and assignment. - GridManager(const GridManager& other); - GridManager& operator=(const GridManager& other); - // The managed UnstructuredGrid. - UnstructuredGrid* ug_; + // Disable copying and assignment. + GridManager(const GridManager& other); + GridManager& operator=(const GridManager& other); + + // Construct corner-point grid from deck. + void initFromDeckCornerpoint(const Opm::EclipseGridParser& deck); + // Construct tensor grid from deck. + void initFromDeckTensorgrid(const Opm::EclipseGridParser& deck); + + // The managed UnstructuredGrid. + UnstructuredGrid* ug_; }; } // namespace Opm diff --git a/opm/core/eclipse/EclipseGridParser.cpp b/opm/core/eclipse/EclipseGridParser.cpp index d582a996..9b0352b2 100644 --- a/opm/core/eclipse/EclipseGridParser.cpp +++ b/opm/core/eclipse/EclipseGridParser.cpp @@ -96,7 +96,7 @@ namespace EclipseKeywords string("MULTPV"), string("PRESSURE"), string("SGAS"), string("SWAT"), string("SOIL"), string("RS"), string("DXV"), string("DYV"), string("DZV"), - string("DEPTHZ"), string("MAPAXES") + string("DEPTHZ"), string("TOPS"), string("MAPAXES") }; const int num_floating_fields = sizeof(floating_fields) / sizeof(floating_fields[0]); @@ -201,21 +201,7 @@ namespace { return us; } - inline std::string readKeyword(std::istream& is) - { - std::string keyword_candidate; - while (!is.eof()) { - is >> keyword_candidate; - if(keyword_candidate.find("--") == 0) { - is >> ignoreLine; // This line is a comment - } else { - return upcase(keyword_candidate); - } - } - return "CONTINUE"; // Last line in included file is a comment - } - - inline bool readKeywordNew(std::istream& is, std::string& keyword) + inline bool readKeyword(std::istream& is, std::string& keyword) { char buf[9]; int i, j; @@ -387,7 +373,7 @@ void EclipseGridParser::readImpl(istream& is) std::string keyword; while (is.good()) { is >> ignoreWhitespace; - bool ok = readKeywordNew(is, keyword); + bool ok = readKeyword(is, keyword); if (ok) { //#ifdef VERBOSE cout << "Keyword found: " << keyword << endl; @@ -549,9 +535,9 @@ void EclipseGridParser::convertToSI() // Find the right unit. double unit = 1e100; bool do_convert = true; - if (key == "COORD" || key == "ZCORN" || - key == "DXV" || key == "DYV" || key == "DZV" || - key == "DEPTHZ") { + if (key == "COORD" || key == "ZCORN" || + key == "DXV" || key == "DYV" || key == "DZV" || + key == "DEPTHZ" || key == "TOPS") { unit = units_.length; } else if (key == "PERMX" || key == "PERMY" || key == "PERMZ" || key == "PERMXX" || key == "PERMYY" || key == "PERMZZ" || diff --git a/opm/core/eclipse/EclipseGridParser.hpp b/opm/core/eclipse/EclipseGridParser.hpp index 31195e77..98942375 100644 --- a/opm/core/eclipse/EclipseGridParser.hpp +++ b/opm/core/eclipse/EclipseGridParser.hpp @@ -136,47 +136,47 @@ public: { return dynamic_cast(*getSpecialValue(#keyword)); } // Support for special fields. - SPECIAL_FIELD(SPECGRID); - SPECIAL_FIELD(FAULTS); - SPECIAL_FIELD(MULTFLT); - SPECIAL_FIELD(TITLE); - SPECIAL_FIELD(START); - SPECIAL_FIELD(DATES); - SPECIAL_FIELD(DENSITY); - SPECIAL_FIELD(PVDG); - SPECIAL_FIELD(PVDO); - SPECIAL_FIELD(PVTG); - SPECIAL_FIELD(PVTO); - SPECIAL_FIELD(PVTW); - SPECIAL_FIELD(SGOF); - SPECIAL_FIELD(SWOF); - SPECIAL_FIELD(ROCK); - SPECIAL_FIELD(ROCKTAB); - SPECIAL_FIELD(WELSPECS); - SPECIAL_FIELD(COMPDAT); - SPECIAL_FIELD(WCONINJE); - SPECIAL_FIELD(WCONPROD); - SPECIAL_FIELD(WELTARG); - SPECIAL_FIELD(WELOPEN); - SPECIAL_FIELD(EQUIL); - SPECIAL_FIELD(PVCDO); - SPECIAL_FIELD(TSTEP); - SPECIAL_FIELD(PLYVISC); - SPECIAL_FIELD(PLYROCK); - SPECIAL_FIELD(PLYADS); - SPECIAL_FIELD(PLYMAX); - SPECIAL_FIELD(TLMIXPAR); - SPECIAL_FIELD(WPOLYMER); - SPECIAL_FIELD(GRUPTREE); - SPECIAL_FIELD(GCONINJE); - SPECIAL_FIELD(GCONPROD); - SPECIAL_FIELD(WGRUPCON); + SPECIAL_FIELD(SPECGRID) + SPECIAL_FIELD(FAULTS) + SPECIAL_FIELD(MULTFLT) + SPECIAL_FIELD(TITLE) + SPECIAL_FIELD(START) + SPECIAL_FIELD(DATES) + SPECIAL_FIELD(DENSITY) + SPECIAL_FIELD(PVDG) + SPECIAL_FIELD(PVDO) + SPECIAL_FIELD(PVTG) + SPECIAL_FIELD(PVTO) + SPECIAL_FIELD(PVTW) + SPECIAL_FIELD(SGOF) + SPECIAL_FIELD(SWOF) + SPECIAL_FIELD(ROCK) + SPECIAL_FIELD(ROCKTAB) + SPECIAL_FIELD(WELSPECS) + SPECIAL_FIELD(COMPDAT) + SPECIAL_FIELD(WCONINJE) + SPECIAL_FIELD(WCONPROD) + SPECIAL_FIELD(WELTARG) + SPECIAL_FIELD(WELOPEN) + SPECIAL_FIELD(EQUIL) + SPECIAL_FIELD(PVCDO) + SPECIAL_FIELD(TSTEP) + SPECIAL_FIELD(PLYVISC) + SPECIAL_FIELD(PLYROCK) + SPECIAL_FIELD(PLYADS) + SPECIAL_FIELD(PLYMAX) + SPECIAL_FIELD(TLMIXPAR) + SPECIAL_FIELD(WPOLYMER) + SPECIAL_FIELD(GRUPTREE) + SPECIAL_FIELD(GCONINJE) + SPECIAL_FIELD(GCONPROD) + SPECIAL_FIELD(WGRUPCON) // The following fields only have a dummy implementation // that allows us to ignore them. - SPECIAL_FIELD(SWFN); - SPECIAL_FIELD(SOF2); - SPECIAL_FIELD(TUNING); + SPECIAL_FIELD(SWFN) + SPECIAL_FIELD(SOF2) + SPECIAL_FIELD(TUNING) #undef SPECIAL_FIELD diff --git a/opm/core/eclipse/SpecialEclipseFields.hpp b/opm/core/eclipse/SpecialEclipseFields.hpp index 7e2fce9a..a8a1ba11 100644 --- a/opm/core/eclipse/SpecialEclipseFields.hpp +++ b/opm/core/eclipse/SpecialEclipseFields.hpp @@ -780,9 +780,11 @@ struct WelspecsLine int fluids_in_place_reg_numb_; // Fluids in place region number WelspecsLine() : - datum_depth_BHP_(-1.0), drain_rad_(0.0), spec_inflow_("STD"), - shut_in_("SHUT"), crossflow_("YES"), pressure_table_number_(0), - density_calc_type_("SEG"), fluids_in_place_reg_numb_(0) + name_(), group_(), I_(-1), J_(-1), + datum_depth_BHP_(-1.0), pref_phase_(), drain_rad_(0.0), + spec_inflow_("STD"), shut_in_("SHUT"), crossflow_("YES"), + pressure_table_number_(0), density_calc_type_("SEG"), + fluids_in_place_reg_numb_(0) {} }; @@ -1360,8 +1362,8 @@ struct WgrupconLine bool available_for_group_control_; double guide_rate_; std::string phase_; - WgrupconLine() : - available_for_group_control_(true) + WgrupconLine() + : well_(), available_for_group_control_(true), guide_rate_(-1.0), phase_() { } }; @@ -1580,6 +1582,7 @@ struct WeltargLine double new_value_; // New value of this quantity WeltargLine() + : well_(), control_change_(), new_value_(-1.0) { } }; @@ -1739,6 +1742,13 @@ struct EquilLine // initial fluids in place calculation. EquilLine() { + datum_depth_ = datum_depth_pressure_ = 0.0; + water_oil_contact_depth_ = oil_water_cap_pressure_ = 0.0; + gas_oil_contact_depth_ = gas_oil_cap_pressure_ = 0.0; + + live_oil_table_index_ = 0; + wet_gas_table_index_ = 0; + N_ = 0; } }; @@ -2128,7 +2138,8 @@ struct WpolymerLine WpolymerLine() { - well_ = polymer_group_ = salt_group_ = ""; + well_ = polymer_group_ = salt_group_ = ""; + polymer_concentration_ = salt_concentration_ = 0.0; } }; diff --git a/opm/core/fluid/BlackoilPropertiesBasic.cpp b/opm/core/fluid/BlackoilPropertiesBasic.cpp index be8211c1..2496d98e 100644 --- a/opm/core/fluid/BlackoilPropertiesBasic.cpp +++ b/opm/core/fluid/BlackoilPropertiesBasic.cpp @@ -26,20 +26,20 @@ namespace Opm { BlackoilPropertiesBasic::BlackoilPropertiesBasic(const parameter::ParameterGroup& param, - const int dim, - const int num_cells) + const int dim, + const int num_cells) { - double poro = param.getDefault("porosity", 1.0); - using namespace Opm::unit; - using namespace Opm::prefix; - double perm = param.getDefault("permeability", 100.0)*milli*darcy; + double poro = param.getDefault("porosity", 1.0); + using namespace Opm::unit; + using namespace Opm::prefix; + double perm = param.getDefault("permeability", 100.0)*milli*darcy; rock_.init(dim, num_cells, poro, perm); - pvt_.init(param); + pvt_.init(param); satprops_.init(param); - if (pvt_.numPhases() != satprops_.numPhases()) { - THROW("BlackoilPropertiesBasic::BlackoilPropertiesBasic() - Inconsistent number of phases in pvt data (" - << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); - } + if (pvt_.numPhases() != satprops_.numPhases()) { + THROW("BlackoilPropertiesBasic::BlackoilPropertiesBasic() - Inconsistent number of phases in pvt data (" + << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); + } } BlackoilPropertiesBasic::~BlackoilPropertiesBasic() @@ -90,11 +90,11 @@ namespace Opm /// \param[out] dmudp If non-null: array of nP viscosity derivative values, /// array must be valid before calling. void BlackoilPropertiesBasic::viscosity(const int n, - const double* p, - const double* z, - const int* /*cells*/, - double* mu, - double* dmudp) const + const double* p, + const double* z, + const int* /*cells*/, + double* mu, + double* dmudp) const { if (dmudp) { THROW("BlackoilPropertiesBasic::viscosity() -- derivatives of viscosity not yet implemented."); @@ -114,16 +114,16 @@ namespace Opm /// array must be valid before calling. The matrices are output /// in Fortran order. void BlackoilPropertiesBasic::matrix(const int n, - const double* /*p*/, - const double* /*z*/, - const int* /*cells*/, - double* A, - double* dAdp) const + const double* /*p*/, + const double* /*z*/, + const int* /*cells*/, + double* A, + double* dAdp) const { - const int np = numPhases(); - ASSERT(np <= 2); - double B[2]; // Must be enough since component classes do not handle more than 2. - pvt_.B(1, 0, 0, B); + const int np = numPhases(); + ASSERT(np <= 2); + double B[2]; // Must be enough since component classes do not handle more than 2. + pvt_.B(1, 0, 0, B); // Compute A matrix // #pragma omp parallel for for (int i = 0; i < n; ++i) { @@ -152,8 +152,8 @@ namespace Opm /// of a call to the method matrix(). /// \param[out] rho Array of nP density values, array must be valid before calling. void BlackoilPropertiesBasic::density(const int n, - const double* A, - double* rho) const + const double* A, + double* rho) const { const int np = numPhases(); const double* sdens = pvt_.surfaceDensities(); @@ -186,10 +186,10 @@ namespace Opm /// m_{ij} = \frac{dkr_i}{ds^j}, /// and is output in Fortran order (m_00 m_10 m_20 m01 ...) void BlackoilPropertiesBasic::relperm(const int n, - const double* s, - const int* /*cells*/, - double* kr, - double* dkrds) const + const double* s, + const int* /*cells*/, + double* kr, + double* dkrds) const { satprops_.relperm(n, s, kr, dkrds); } @@ -205,10 +205,10 @@ namespace Opm /// m_{ij} = \frac{dpc_i}{ds^j}, /// and is output in Fortran order (m_00 m_10 m_20 m01 ...) void BlackoilPropertiesBasic::capPress(const int n, - const double* s, - const int* /*cells*/, - double* pc, - double* dpcds) const + const double* s, + const int* /*cells*/, + double* pc, + double* dpcds) const { satprops_.capPress(n, s, pc, dpcds); } @@ -226,7 +226,7 @@ namespace Opm double* smin, double* smax) const { - satprops_.satRange(n, smin, smax); + satprops_.satRange(n, smin, smax); } diff --git a/opm/core/fluid/BlackoilPropertiesBasic.hpp b/opm/core/fluid/BlackoilPropertiesBasic.hpp index d879036e..44a76013 100644 --- a/opm/core/fluid/BlackoilPropertiesBasic.hpp +++ b/opm/core/fluid/BlackoilPropertiesBasic.hpp @@ -35,16 +35,16 @@ namespace Opm { public: /// Construct from parameters. - /// The following parameters are accepted (defaults): - /// num_phases (2) Must be 1 or 2. - /// relperm_func ("Linear") Must be "Constant", "Linear" or "Quadratic". - /// rho1 [rho2, rho3] (1.0e3) Density in kg/m^3 - /// mu1 [mu2, mu3] (1.0) Viscosity in cP - /// porosity (1.0) Porosity - /// permeability (100.0) Permeability in mD + /// The following parameters are accepted (defaults): + /// num_phases (2) Must be 1 or 2. + /// relperm_func ("Linear") Must be "Constant", "Linear" or "Quadratic". + /// rho1 [rho2, rho3] (1.0e3) Density in kg/m^3 + /// mu1 [mu2, mu3] (1.0) Viscosity in cP + /// porosity (1.0) Porosity + /// permeability (100.0) Permeability in mD BlackoilPropertiesBasic(const parameter::ParameterGroup& param, - const int dim, - const int num_cells); + const int dim, + const int num_cells); /// Destructor. virtual ~BlackoilPropertiesBasic(); @@ -151,7 +151,7 @@ namespace Opm double* dpcds) const; - /// Obtain the range of allowable saturation values. +/// Obtain the range of allowable saturation values. /// In cell cells[i], saturation of phase p is allowed to be /// in the interval [smin[i*P + p], smax[i*P + p]]. /// \param[in] n Number of data points. diff --git a/opm/core/fluid/BlackoilPropertiesFromDeck.cpp b/opm/core/fluid/BlackoilPropertiesFromDeck.cpp index 0eb23cb0..e2457f39 100644 --- a/opm/core/fluid/BlackoilPropertiesFromDeck.cpp +++ b/opm/core/fluid/BlackoilPropertiesFromDeck.cpp @@ -18,20 +18,69 @@ */ #include +#include namespace Opm { BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck(const EclipseGridParser& deck, - const std::vector& global_cell) + const UnstructuredGrid& grid) { - rock_.init(deck, global_cell); - pvt_.init(deck); - satprops_.init(deck, global_cell); - if (pvt_.numPhases() != satprops_.numPhases()) { - THROW("BlackoilPropertiesBasic::BlackoilPropertiesBasic() - Inconsistent number of phases in pvt data (" - << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); - } + rock_.init(deck, grid); + pvt_.init(deck, 200); + SaturationPropsFromDeck* ptr + = new SaturationPropsFromDeck(); + satprops_.reset(ptr); + ptr->init(deck, grid, 200); + + if (pvt_.numPhases() != satprops_->numPhases()) { + THROW("BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck() - Inconsistent number of phases in pvt data (" + << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_->numPhases() << ")."); + } + } + + BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck(const EclipseGridParser& deck, + const UnstructuredGrid& grid, + const parameter::ParameterGroup& param) + { + rock_.init(deck, grid); + const int pvt_samples = param.getDefault("pvt_tab_size", 200); + pvt_.init(deck, pvt_samples); + + // Unfortunate lack of pointer smartness here... + const int sat_samples = param.getDefault("sat_tab_size", 200); + std::string threephase_model = param.getDefault("threephase_model", "simple"); + bool use_stone2 = (threephase_model == "stone2"); + if (sat_samples > 1) { + if (use_stone2) { + SaturationPropsFromDeck* ptr + = new SaturationPropsFromDeck(); + satprops_.reset(ptr); + ptr->init(deck, grid, sat_samples); + } else { + SaturationPropsFromDeck* ptr + = new SaturationPropsFromDeck(); + satprops_.reset(ptr); + ptr->init(deck, grid, sat_samples); + } + } else { + if (use_stone2) { + SaturationPropsFromDeck* ptr + = new SaturationPropsFromDeck(); + satprops_.reset(ptr); + ptr->init(deck, grid, sat_samples); + } else { + SaturationPropsFromDeck* ptr + = new SaturationPropsFromDeck(); + satprops_.reset(ptr); + ptr->init(deck, grid, sat_samples); + } + } + + if (pvt_.numPhases() != satprops_->numPhases()) { + THROW("BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck() - Inconsistent number of phases in pvt data (" + << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_->numPhases() << ")."); + } } BlackoilPropertiesFromDeck::~BlackoilPropertiesFromDeck() @@ -235,7 +284,7 @@ namespace Opm double* kr, double* dkrds) const { - satprops_.relperm(n, s, cells, kr, dkrds); + satprops_->relperm(n, s, cells, kr, dkrds); } @@ -254,7 +303,7 @@ namespace Opm double* pc, double* dpcds) const { - satprops_.capPress(n, s, cells, pc, dpcds); + satprops_->capPress(n, s, cells, pc, dpcds); } @@ -270,7 +319,7 @@ namespace Opm double* smin, double* smax) const { - satprops_.satRange(n, cells, smin, smax); + satprops_->satRange(n, cells, smin, smax); } diff --git a/opm/core/fluid/BlackoilPropertiesFromDeck.hpp b/opm/core/fluid/BlackoilPropertiesFromDeck.hpp index 9c20b568..eb8b947b 100644 --- a/opm/core/fluid/BlackoilPropertiesFromDeck.hpp +++ b/opm/core/fluid/BlackoilPropertiesFromDeck.hpp @@ -26,6 +26,10 @@ #include #include #include +#include +#include + +struct UnstructuredGrid; namespace Opm { @@ -35,12 +39,28 @@ namespace Opm class BlackoilPropertiesFromDeck : public BlackoilPropertiesInterface { public: - /// Construct from deck and cell mapping. - /// \param deck eclipse input parser - /// \param global_cell mapping from cell indices (typically from a processed grid) + /// Initialize from deck and grid. + /// \param[in] deck Deck input parser + /// \param[in] grid Grid to which property object applies, needed for the + /// mapping from cell indices (typically from a processed grid) /// to logical cartesian indices consistent with the deck. BlackoilPropertiesFromDeck(const EclipseGridParser& deck, - const std::vector& global_cell); + const UnstructuredGrid& grid); + + /// Initialize from deck, grid and parameters. + /// \param[in] deck Deck input parser + /// \param[in] grid Grid to which property object applies, needed for the + /// mapping from cell indices (typically from a processed grid) + /// to logical cartesian indices consistent with the deck. + /// \param[in] param Parameters. Accepted parameters include: + /// pvt_tab_size (200) number of uniform sample points for dead-oil pvt tables. + /// sat_tab_size (200) number of uniform sample points for saturation tables. + /// threephase_model("simple") three-phase relperm model (accepts "simple" and "stone2"). + /// For both size parameters, a 0 or negative value indicates that no spline fitting is to + /// be done, and the input fluid data used directly for linear interpolation. + BlackoilPropertiesFromDeck(const EclipseGridParser& deck, + const UnstructuredGrid& grid, + const parameter::ParameterGroup& param); /// Destructor. virtual ~BlackoilPropertiesFromDeck(); @@ -147,9 +167,9 @@ namespace Opm double* dpcds) const; - /// Obtain the range of allowable saturation values. - /// In cell cells[i], saturation of phase p is allowed to be - /// in the interval [smin[i*P + p], smax[i*P + p]]. + /// Obtain the range of allowable saturation values. + /// In cell cells[i], saturation of phase p is allowed to be + /// in the interval [smin[i*P + p], smax[i*P + p]]. /// \param[in] n Number of data points. /// \param[in] cells Array of n cell indices. /// \param[out] smin Array of nP minimum s values, array must be valid before calling. @@ -162,7 +182,7 @@ namespace Opm private: RockFromDeck rock_; BlackoilPvtProperties pvt_; - SaturationPropsFromDeck satprops_; + boost::scoped_ptr satprops_; mutable std::vector B_; mutable std::vector dB_; mutable std::vector R_; diff --git a/opm/core/fluid/BlackoilPropertiesInterface.hpp b/opm/core/fluid/BlackoilPropertiesInterface.hpp index dd8a857f..501c8a40 100644 --- a/opm/core/fluid/BlackoilPropertiesInterface.hpp +++ b/opm/core/fluid/BlackoilPropertiesInterface.hpp @@ -138,9 +138,9 @@ namespace Opm double* dpcds) const = 0; - /// Obtain the range of allowable saturation values. - /// In cell cells[i], saturation of phase p is allowed to be - /// in the interval [smin[i*P + p], smax[i*P + p]]. + /// Obtain the range of allowable saturation values. + /// In cell cells[i], saturation of phase p is allowed to be + /// in the interval [smin[i*P + p], smax[i*P + p]]. /// \param[in] n Number of data points. /// \param[in] cells Array of n cell indices. /// \param[out] smin Array of nP minimum s values, array must be valid before calling. diff --git a/opm/core/fluid/IncompPropertiesBasic.cpp b/opm/core/fluid/IncompPropertiesBasic.cpp index 0ce3c88a..ab68017c 100644 --- a/opm/core/fluid/IncompPropertiesBasic.cpp +++ b/opm/core/fluid/IncompPropertiesBasic.cpp @@ -28,22 +28,22 @@ namespace Opm { IncompPropertiesBasic::IncompPropertiesBasic(const parameter::ParameterGroup& param, - const int dim, - const int num_cells) + const int dim, + const int num_cells) { - double poro = param.getDefault("porosity", 1.0); - using namespace Opm::unit; - using namespace Opm::prefix; - double perm = param.getDefault("permeability", 100.0)*milli*darcy; + double poro = param.getDefault("porosity", 1.0); + using namespace Opm::unit; + using namespace Opm::prefix; + double perm = param.getDefault("permeability", 100.0)*milli*darcy; rock_.init(dim, num_cells, poro, perm); - pvt_.init(param); + pvt_.init(param); satprops_.init(param); - if (pvt_.numPhases() != satprops_.numPhases()) { - THROW("IncompPropertiesBasic::IncompPropertiesBasic() - Inconsistent number of phases in pvt data (" - << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); - } - viscosity_.resize(pvt_.numPhases()); - pvt_.mu(1, 0, 0, &viscosity_[0]); + if (pvt_.numPhases() != satprops_.numPhases()) { + THROW("IncompPropertiesBasic::IncompPropertiesBasic() - Inconsistent number of phases in pvt data (" + << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); + } + viscosity_.resize(pvt_.numPhases()); + pvt_.mu(1, 0, 0, &viscosity_[0]); } IncompPropertiesBasic::IncompPropertiesBasic(const int num_phases, @@ -56,14 +56,14 @@ namespace Opm const int num_cells) { rock_.init(dim, num_cells, por, perm); - pvt_.init(num_phases, rho, mu); + pvt_.init(num_phases, rho, mu); satprops_.init(num_phases, relpermfunc); - if (pvt_.numPhases() != satprops_.numPhases()) { - THROW("IncompPropertiesBasic::IncompPropertiesBasic() - Inconsistent number of phases in pvt data (" - << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); - } - viscosity_.resize(pvt_.numPhases()); - pvt_.mu(1, 0, 0, &viscosity_[0]); + if (pvt_.numPhases() != satprops_.numPhases()) { + THROW("IncompPropertiesBasic::IncompPropertiesBasic() - Inconsistent number of phases in pvt data (" + << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); + } + viscosity_.resize(pvt_.numPhases()); + pvt_.mu(1, 0, 0, &viscosity_[0]); } IncompPropertiesBasic::~IncompPropertiesBasic() @@ -109,7 +109,7 @@ namespace Opm /// \return Array of P viscosity values. const double* IncompPropertiesBasic::viscosity() const { - return &viscosity_[0]; + return &viscosity_[0]; } /// \return Array of P density values. @@ -117,7 +117,7 @@ namespace Opm { // No difference between reservoir and surface densities // modelled by this class. - return pvt_.surfaceDensities(); + return pvt_.surfaceDensities(); } /// \return Array of P density values. @@ -125,7 +125,7 @@ namespace Opm { // No difference between reservoir and surface densities // modelled by this class. - return pvt_.surfaceDensities(); + return pvt_.surfaceDensities(); } /// \param[in] n Number of data points. @@ -138,10 +138,10 @@ namespace Opm /// m_{ij} = \frac{dkr_i}{ds^j}, /// and is output in Fortran order (m_00 m_10 m_20 m_01 ...) void IncompPropertiesBasic::relperm(const int n, - const double* s, - const int* /*cells*/, - double* kr, - double* dkrds) const + const double* s, + const int* /*cells*/, + double* kr, + double* dkrds) const { satprops_.relperm(n, s, kr, dkrds); } @@ -157,10 +157,10 @@ namespace Opm /// m_{ij} = \frac{dpc_i}{ds^j}, /// and is output in Fortran order (m_00 m_10 m_20 m_01 ...) void IncompPropertiesBasic::capPress(const int n, - const double* s, - const int* /*cells*/, - double* pc, - double* dpcds) const + const double* s, + const int* /*cells*/, + double* pc, + double* dpcds) const { satprops_.capPress(n, s, pc, dpcds); } @@ -174,11 +174,11 @@ namespace Opm /// \param[out] smin Array of nP minimum s values, array must be valid before calling. /// \param[out] smax Array of nP maximum s values, array must be valid before calling. void IncompPropertiesBasic::satRange(const int n, - const int* /*cells*/, - double* smin, - double* smax) const + const int* /*cells*/, + double* smin, + double* smax) const { - satprops_.satRange(n, smin, smax); + satprops_.satRange(n, smin, smax); } } // namespace Opm diff --git a/opm/core/fluid/IncompPropertiesBasic.hpp b/opm/core/fluid/IncompPropertiesBasic.hpp index b90b0876..72c39b1b 100644 --- a/opm/core/fluid/IncompPropertiesBasic.hpp +++ b/opm/core/fluid/IncompPropertiesBasic.hpp @@ -42,29 +42,29 @@ namespace Opm { public: /// Construct from parameters. - /// The following parameters are accepted (defaults): - /// num_phases (2) Must be 1 or 2. - /// relperm_func ("Linear") Must be "Constant", "Linear" or "Quadratic". - /// rho1 [rho2, rho3] (1.0e3) Density in kg/m^3 - /// mu1 [mu2, mu3] (1.0) Viscosity in cP - /// porosity (1.0) Porosity - /// permeability (100.0) Permeability in mD + /// The following parameters are accepted (defaults): + /// num_phases (2) Must be 1 or 2. + /// relperm_func ("Linear") Must be "Constant", "Linear" or "Quadratic". + /// rho1 [rho2, rho3] (1.0e3) Density in kg/m^3 + /// mu1 [mu2, mu3] (1.0) Viscosity in cP + /// porosity (1.0) Porosity + /// permeability (100.0) Permeability in mD IncompPropertiesBasic(const parameter::ParameterGroup& param, - const int dim, - const int num_cells); + const int dim, + const int num_cells); /// Construct from arguments a basic two phase fluid. IncompPropertiesBasic(const int num_phases, const SaturationPropsBasic::RelPermFunc& relpermfunc, const std::vector& rho, - const std::vector& mu, + const std::vector& mu, const double porosity, const double permeability, const int dim, - const int num_cells); + const int num_cells); - /// Destructor. + /// Destructor. virtual ~IncompPropertiesBasic(); // ---- Rock interface ---- @@ -132,9 +132,9 @@ namespace Opm double* dpcds) const; - /// Obtain the range of allowable saturation values. - /// In cell cells[i], saturation of phase p is allowed to be - /// in the interval [smin[i*P + p], smax[i*P + p]]. + /// Obtain the range of allowable saturation values. + /// In cell cells[i], saturation of phase p is allowed to be + /// in the interval [smin[i*P + p], smax[i*P + p]]. /// \param[in] n Number of data points. /// \param[in] cells Array of n cell indices. /// \param[out] smin Array of nP minimum s values, array must be valid before calling. @@ -145,9 +145,9 @@ namespace Opm double* smax) const; private: RockBasic rock_; - PvtPropertiesBasic pvt_; + PvtPropertiesBasic pvt_; SaturationPropsBasic satprops_; - std::vector viscosity_; + std::vector viscosity_; }; diff --git a/opm/core/fluid/IncompPropertiesFromDeck.cpp b/opm/core/fluid/IncompPropertiesFromDeck.cpp index e61e13c8..9aa69098 100644 --- a/opm/core/fluid/IncompPropertiesFromDeck.cpp +++ b/opm/core/fluid/IncompPropertiesFromDeck.cpp @@ -27,15 +27,15 @@ namespace Opm { IncompPropertiesFromDeck::IncompPropertiesFromDeck(const EclipseGridParser& deck, - const std::vector& global_cell) + const UnstructuredGrid& grid) { - rock_.init(deck, global_cell); - pvt_.init(deck); - satprops_.init(deck, global_cell); - if (pvt_.numPhases() != satprops_.numPhases()) { - THROW("IncompPropertiesFromDeck::IncompPropertiesFromDeck() - Inconsistent number of phases in pvt data (" - << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); - } + rock_.init(deck, grid); + pvt_.init(deck); + satprops_.init(deck, grid, 200); + if (pvt_.numPhases() != satprops_.numPhases()) { + THROW("IncompPropertiesFromDeck::IncompPropertiesFromDeck() - Inconsistent number of phases in pvt data (" + << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); + } } IncompPropertiesFromDeck::~IncompPropertiesFromDeck() @@ -81,19 +81,19 @@ namespace Opm /// \return Array of P viscosity values. const double* IncompPropertiesFromDeck::viscosity() const { - return pvt_.viscosity(); + return pvt_.viscosity(); } /// \return Array of P density values. const double* IncompPropertiesFromDeck::density() const { - return pvt_.reservoirDensities(); + return pvt_.reservoirDensities(); } /// \return Array of P density values. const double* IncompPropertiesFromDeck::surfaceDensity() const { - return pvt_.surfaceDensities(); + return pvt_.surfaceDensities(); } /// \param[in] n Number of data points. @@ -106,10 +106,10 @@ namespace Opm /// m_{ij} = \frac{dkr_i}{ds^j}, /// and is output in Fortran order (m_00 m_10 m_20 m_01 ...) void IncompPropertiesFromDeck::relperm(const int n, - const double* s, - const int* cells, - double* kr, - double* dkrds) const + const double* s, + const int* cells, + double* kr, + double* dkrds) const { satprops_.relperm(n, s, cells, kr, dkrds); } @@ -125,10 +125,10 @@ namespace Opm /// m_{ij} = \frac{dpc_i}{ds^j}, /// and is output in Fortran order (m_00 m_10 m_20 m_01 ...) void IncompPropertiesFromDeck::capPress(const int n, - const double* s, - const int* cells, - double* pc, - double* dpcds) const + const double* s, + const int* cells, + double* pc, + double* dpcds) const { satprops_.capPress(n, s, cells, pc, dpcds); } @@ -142,11 +142,11 @@ namespace Opm /// \param[out] smin Array of nP minimum s values, array must be valid before calling. /// \param[out] smax Array of nP maximum s values, array must be valid before calling. void IncompPropertiesFromDeck::satRange(const int n, - const int* cells, - double* smin, - double* smax) const + const int* cells, + double* smin, + double* smax) const { - satprops_.satRange(n, cells, smin, smax); + satprops_.satRange(n, cells, smin, smax); } } // namespace Opm diff --git a/opm/core/fluid/IncompPropertiesFromDeck.hpp b/opm/core/fluid/IncompPropertiesFromDeck.hpp index d17dd1b7..6680eaa4 100644 --- a/opm/core/fluid/IncompPropertiesFromDeck.hpp +++ b/opm/core/fluid/IncompPropertiesFromDeck.hpp @@ -26,6 +26,8 @@ #include #include +struct UnstructuredGrid; + namespace Opm { @@ -43,14 +45,15 @@ namespace Opm class IncompPropertiesFromDeck : public IncompPropertiesInterface { public: - /// Construct from deck and cell mapping. - /// \param deck eclipse input parser - /// \param global_cell mapping from cell indices (typically from a processed grid) + /// Initialize from deck and grid. + /// \param deck Deck input parser + /// \param grid Grid to which property object applies, needed for the + /// mapping from cell indices (typically from a processed grid) /// to logical cartesian indices consistent with the deck. IncompPropertiesFromDeck(const EclipseGridParser& deck, - const std::vector& global_cell); + const UnstructuredGrid& grid); - /// Destructor. + /// Destructor. virtual ~IncompPropertiesFromDeck(); // ---- Rock interface ---- @@ -118,9 +121,9 @@ namespace Opm double* dpcds) const; - /// Obtain the range of allowable saturation values. - /// In cell cells[i], saturation of phase p is allowed to be - /// in the interval [smin[i*P + p], smax[i*P + p]]. + /// Obtain the range of allowable saturation values. + /// In cell cells[i], saturation of phase p is allowed to be + /// in the interval [smin[i*P + p], smax[i*P + p]]. /// \param[in] n Number of data points. /// \param[in] cells Array of n cell indices. /// \param[out] smin Array of nP minimum s values, array must be valid before calling. @@ -131,8 +134,8 @@ namespace Opm double* smax) const; private: RockFromDeck rock_; - PvtPropertiesIncompFromDeck pvt_; - SaturationPropsFromDeck satprops_; + PvtPropertiesIncompFromDeck pvt_; + SaturationPropsFromDeck satprops_; }; diff --git a/opm/core/fluid/IncompPropertiesInterface.hpp b/opm/core/fluid/IncompPropertiesInterface.hpp index a5025e8a..4f8bfb51 100644 --- a/opm/core/fluid/IncompPropertiesInterface.hpp +++ b/opm/core/fluid/IncompPropertiesInterface.hpp @@ -109,9 +109,9 @@ namespace Opm double* pc, double* dpcds) const = 0; - /// Obtain the range of allowable saturation values. - /// In cell cells[i], saturation of phase p is allowed to be - /// in the interval [smin[i*P + p], smax[i*P + p]]. + /// Obtain the range of allowable saturation values. + /// In cell cells[i], saturation of phase p is allowed to be + /// in the interval [smin[i*P + p], smax[i*P + p]]. /// \param[in] n Number of data points. /// \param[in] cells Array of n cell indices. /// \param[out] smin Array of nP minimum s values, array must be valid before calling. diff --git a/opm/core/fluid/PvtPropertiesBasic.cpp b/opm/core/fluid/PvtPropertiesBasic.cpp index 3d7d7bb2..5220502a 100644 --- a/opm/core/fluid/PvtPropertiesBasic.cpp +++ b/opm/core/fluid/PvtPropertiesBasic.cpp @@ -34,41 +34,41 @@ namespace Opm void PvtPropertiesBasic::init(const parameter::ParameterGroup& param) { - int num_phases = param.getDefault("num_phases", 2); - if (num_phases > 3 || num_phases < 1) { - THROW("PvtPropertiesBasic::init() illegal num_phases: " << num_phases); - } - density_.resize(num_phases); - viscosity_.resize(num_phases); - // We currently do not allow the user to set B. - formation_volume_factor_.clear(); - formation_volume_factor_.resize(num_phases, 1.0); + int num_phases = param.getDefault("num_phases", 2); + if (num_phases > 3 || num_phases < 1) { + THROW("PvtPropertiesBasic::init() illegal num_phases: " << num_phases); + } + density_.resize(num_phases); + viscosity_.resize(num_phases); + // We currently do not allow the user to set B. + formation_volume_factor_.clear(); + formation_volume_factor_.resize(num_phases, 1.0); - // Setting mu and rho from parameters - using namespace Opm::prefix; - using namespace Opm::unit; - const double kgpm3 = kilogram/cubic(meter); - const double cP = centi*Poise; - std::string rname[3] = { "rho1", "rho2", "rho3" }; - double rdefault[3] = { 1.0e3, 1.0e3, 1.0e3 }; - std::string vname[3] = { "mu1", "mu2", "mu3" }; - double vdefault[3] = { 1.0, 1.0, 1.0 }; - for (int phase = 0; phase < num_phases; ++phase) { - density_[phase] = kgpm3*param.getDefault(rname[phase], rdefault[phase]); - viscosity_[phase] = cP*param.getDefault(vname[phase], vdefault[phase]); - } + // Setting mu and rho from parameters + using namespace Opm::prefix; + using namespace Opm::unit; + const double kgpm3 = kilogram/cubic(meter); + const double cP = centi*Poise; + std::string rname[3] = { "rho1", "rho2", "rho3" }; + double rdefault[3] = { 1.0e3, 1.0e3, 1.0e3 }; + std::string vname[3] = { "mu1", "mu2", "mu3" }; + double vdefault[3] = { 1.0, 1.0, 1.0 }; + for (int phase = 0; phase < num_phases; ++phase) { + density_[phase] = kgpm3*param.getDefault(rname[phase], rdefault[phase]); + viscosity_[phase] = cP*param.getDefault(vname[phase], vdefault[phase]); + } } void PvtPropertiesBasic::init(const int num_phases, const std::vector& rho, const std::vector& visc) { - if (num_phases > 3 || num_phases < 1) { - THROW("PvtPropertiesBasic::init() illegal num_phases: " << num_phases); - } - // We currently do not allow the user to set B. - formation_volume_factor_.clear(); - formation_volume_factor_.resize(num_phases, 1.0); + if (num_phases > 3 || num_phases < 1) { + THROW("PvtPropertiesBasic::init() illegal num_phases: " << num_phases); + } + // We currently do not allow the user to set B. + formation_volume_factor_.clear(); + formation_volume_factor_.resize(num_phases, 1.0); density_ = rho; viscosity_ = visc; } @@ -87,69 +87,69 @@ namespace Opm void PvtPropertiesBasic::mu(const int n, - const double* /*p*/, - const double* /*z*/, - double* output_mu) const + const double* /*p*/, + const double* /*z*/, + double* output_mu) const { - const int np = numPhases(); + const int np = numPhases(); for (int phase = 0; phase < np; ++phase) { // #pragma omp parallel for for (int i = 0; i < n; ++i) { output_mu[np*i + phase] = viscosity_[phase]; } - } + } } void PvtPropertiesBasic::B(const int n, - const double* /*p*/, - const double* /*z*/, - double* output_B) const + const double* /*p*/, + const double* /*z*/, + double* output_B) const { - const int np = numPhases(); + const int np = numPhases(); for (int phase = 0; phase < np; ++phase) { // #pragma omp parallel for for (int i = 0; i < n; ++i) { output_B[np*i + phase] = formation_volume_factor_[phase]; } - } + } } void PvtPropertiesBasic::dBdp(const int n, - const double* /*p*/, - const double* /*z*/, - double* output_B, - double* output_dBdp) const + const double* /*p*/, + const double* /*z*/, + double* output_B, + double* output_dBdp) const { - const int np = numPhases(); + const int np = numPhases(); for (int phase = 0; phase < np; ++phase) { // #pragma omp parallel for for (int i = 0; i < n; ++i) { output_B[np*i + phase] = formation_volume_factor_[phase]; output_dBdp[np*i + phase] = 0.0; } - } + } } void PvtPropertiesBasic::R(const int n, - const double* /*p*/, - const double* /*z*/, - double* output_R) const + const double* /*p*/, + const double* /*z*/, + double* output_R) const { - const int np = numPhases(); - std::fill(output_R, output_R + n*np, 0.0); + const int np = numPhases(); + std::fill(output_R, output_R + n*np, 0.0); } void PvtPropertiesBasic::dRdp(const int n, - const double* /*p*/, - const double* /*z*/, - double* output_R, - double* output_dRdp) const + const double* /*p*/, + const double* /*z*/, + double* output_R, + double* output_dRdp) const { - const int np = numPhases(); - std::fill(output_R, output_R + n*np, 0.0); - std::fill(output_dRdp, output_dRdp + n*np, 0.0); + const int np = numPhases(); + std::fill(output_R, output_R + n*np, 0.0); + std::fill(output_dRdp, output_dRdp + n*np, 0.0); } } // namespace Opm diff --git a/opm/core/fluid/PvtPropertiesBasic.hpp b/opm/core/fluid/PvtPropertiesBasic.hpp index d1a3e9f3..3e30e807 100644 --- a/opm/core/fluid/PvtPropertiesBasic.hpp +++ b/opm/core/fluid/PvtPropertiesBasic.hpp @@ -38,11 +38,11 @@ namespace Opm PvtPropertiesBasic(); /// Initialize from parameters. - /// The following parameters are accepted (defaults): - /// num_phases (2) Must be 1, 2 or 3. - /// rho1 [rho2, rho3] (1.0e3) Density in kg/m^3 - /// mu1 [mu2, mu3] (1.0) Viscosity in cP - void init(const parameter::ParameterGroup& param); + /// The following parameters are accepted (defaults): + /// num_phases (2) Must be 1, 2 or 3. + /// rho1 [rho2, rho3] (1.0e3) Density in kg/m^3 + /// mu1 [mu2, mu3] (1.0) Viscosity in cP + void init(const parameter::ParameterGroup& param); /// Initialize from arguments. /// Basic multi phase fluid pvt properties. @@ -55,7 +55,7 @@ namespace Opm /// Densities of stock components at surface conditions. /// \return Array of size numPhases(). - const double* surfaceDensities() const; + const double* surfaceDensities() const; /// Viscosity as a function of p and z. void mu(const int n, @@ -90,9 +90,9 @@ namespace Opm double* output_dRdp) const; private: - std::vector density_; - std::vector viscosity_; - std::vector formation_volume_factor_; + std::vector density_; + std::vector viscosity_; + std::vector formation_volume_factor_; }; } diff --git a/opm/core/fluid/PvtPropertiesIncompFromDeck.cpp b/opm/core/fluid/PvtPropertiesIncompFromDeck.cpp index 62cc1b23..34a4ba00 100644 --- a/opm/core/fluid/PvtPropertiesIncompFromDeck.cpp +++ b/opm/core/fluid/PvtPropertiesIncompFromDeck.cpp @@ -38,54 +38,54 @@ namespace Opm { typedef std::vector > > table_t; // If we need multiple regions, this class and the SinglePvt* classes must change. - int region_number = 0; + int region_number = 0; PhaseUsage phase_usage = phaseUsageFromDeck(deck); - if (phase_usage.phase_used[PhaseUsage::Vapour] || - !phase_usage.phase_used[PhaseUsage::Aqua] || - !phase_usage.phase_used[PhaseUsage::Liquid]) { - THROW("PvtPropertiesIncompFromDeck::init() -- must have gas and oil phases (only) in deck input.\n"); - } + if (phase_usage.phase_used[PhaseUsage::Vapour] || + !phase_usage.phase_used[PhaseUsage::Aqua] || + !phase_usage.phase_used[PhaseUsage::Liquid]) { + THROW("PvtPropertiesIncompFromDeck::init() -- must have gas and oil phases (only) in deck input.\n"); + } - // Surface densities. Accounting for different orders in eclipse and our code. - if (deck.hasField("DENSITY")) { - const std::vector& d = deck.getDENSITY().densities_[region_number]; - enum { ECL_oil = 0, ECL_water = 1, ECL_gas = 2 }; - surface_density_[phase_usage.phase_pos[PhaseUsage::Aqua]] = d[ECL_water]; - surface_density_[phase_usage.phase_pos[PhaseUsage::Liquid]] = d[ECL_oil]; - } else { - THROW("Input is missing DENSITY\n"); - } + // Surface densities. Accounting for different orders in eclipse and our code. + if (deck.hasField("DENSITY")) { + const std::vector& d = deck.getDENSITY().densities_[region_number]; + enum { ECL_oil = 0, ECL_water = 1, ECL_gas = 2 }; + surface_density_[phase_usage.phase_pos[PhaseUsage::Aqua]] = d[ECL_water]; + surface_density_[phase_usage.phase_pos[PhaseUsage::Liquid]] = d[ECL_oil]; + } else { + THROW("Input is missing DENSITY\n"); + } // Make reservoir densities the same as surface densities initially. // We will modify them with formation volume factors if found. reservoir_density_ = surface_density_; // Water viscosity. - if (deck.hasField("PVTW")) { - const std::vector& pvtw = deck.getPVTW().pvtw_[region_number]; - if (pvtw[2] != 0.0 || pvtw[4] != 0.0) { - MESSAGE("Compressibility effects in PVTW are ignored."); - } + if (deck.hasField("PVTW")) { + const std::vector& pvtw = deck.getPVTW().pvtw_[region_number]; + if (pvtw[2] != 0.0 || pvtw[4] != 0.0) { + MESSAGE("Compressibility effects in PVTW are ignored."); + } reservoir_density_[phase_usage.phase_pos[PhaseUsage::Aqua]] /= pvtw[1]; - viscosity_[phase_usage.phase_pos[PhaseUsage::Aqua]] = pvtw[3]; - } else { - // Eclipse 100 default. - // viscosity_[phase_usage.phase_pos[PhaseUsage::Aqua]] = 0.5*Opm::prefix::centi*Opm::unit::Poise; - THROW("Input is missing PVTW\n"); - } + viscosity_[phase_usage.phase_pos[PhaseUsage::Aqua]] = pvtw[3]; + } else { + // Eclipse 100 default. + // viscosity_[phase_usage.phase_pos[PhaseUsage::Aqua]] = 0.5*Opm::prefix::centi*Opm::unit::Poise; + THROW("Input is missing PVTW\n"); + } // Oil viscosity. - if (deck.hasField("PVCDO")) { - const std::vector& pvcdo = deck.getPVCDO().pvcdo_[region_number]; - if (pvcdo[2] != 0.0 || pvcdo[4] != 0.0) { - MESSAGE("Compressibility effects in PVCDO are ignored."); - } + if (deck.hasField("PVCDO")) { + const std::vector& pvcdo = deck.getPVCDO().pvcdo_[region_number]; + if (pvcdo[2] != 0.0 || pvcdo[4] != 0.0) { + MESSAGE("Compressibility effects in PVCDO are ignored."); + } reservoir_density_[phase_usage.phase_pos[PhaseUsage::Liquid]] /= pvcdo[1]; - viscosity_[phase_usage.phase_pos[PhaseUsage::Liquid]] = pvcdo[3]; - } else { - THROW("Input is missing PVCDO\n"); - } + viscosity_[phase_usage.phase_pos[PhaseUsage::Liquid]] = pvcdo[3]; + } else { + THROW("Input is missing PVCDO\n"); + } } const double* PvtPropertiesIncompFromDeck::surfaceDensities() const diff --git a/opm/core/fluid/PvtPropertiesIncompFromDeck.hpp b/opm/core/fluid/PvtPropertiesIncompFromDeck.hpp index acbabaa4..01bbeed1 100644 --- a/opm/core/fluid/PvtPropertiesIncompFromDeck.hpp +++ b/opm/core/fluid/PvtPropertiesIncompFromDeck.hpp @@ -39,14 +39,14 @@ namespace Opm PvtPropertiesIncompFromDeck(); /// Initialize from deck. - void init(const EclipseGridParser& deck); + void init(const EclipseGridParser& deck); /// Number of active phases. int numPhases() const; /// Densities of stock components at surface conditions. /// \return Array of size numPhases(). - const double* surfaceDensities() const; + const double* surfaceDensities() const; /// Densities of stock components at reservoir conditions. /// Note: a reasonable question to ask is why there can be @@ -58,15 +58,15 @@ namespace Opm /// reporting and using data given in terms of surface values, /// we need to handle this difference. /// \return Array of size numPhases(). - const double* reservoirDensities() const; + const double* reservoirDensities() const; /// Viscosities. const double* viscosity() const; private: - std::tr1::array surface_density_; - std::tr1::array reservoir_density_; - std::tr1::array viscosity_; + std::tr1::array surface_density_; + std::tr1::array reservoir_density_; + std::tr1::array viscosity_; }; } diff --git a/opm/core/fluid/RockBasic.cpp b/opm/core/fluid/RockBasic.cpp index a1423b76..b66cccce 100644 --- a/opm/core/fluid/RockBasic.cpp +++ b/opm/core/fluid/RockBasic.cpp @@ -25,29 +25,29 @@ namespace Opm /// Default constructor. RockBasic::RockBasic() - : dimensions_(-1) + : dimensions_(-1) { } /// Initialize with homogenous porosity and permeability. void RockBasic::init(const int dimensions, - const int num_cells, - const double poro, - const double perm) + const int num_cells, + const double poro, + const double perm) { - dimensions_ = dimensions; - porosity_.clear(); - porosity_.resize(num_cells, poro); - permeability_.clear(); - const int dsq = dimensions*dimensions; - permeability_.resize(num_cells*dsq, 0.0); + dimensions_ = dimensions; + porosity_.clear(); + porosity_.resize(num_cells, poro); + permeability_.clear(); + const int dsq = dimensions*dimensions; + permeability_.resize(num_cells*dsq, 0.0); // #pragma omp parallel for - for (int i = 0; i < num_cells; ++i) { - for (int d = 0; d < dimensions; ++d) { - permeability_[dsq*i + dimensions*d + d] = perm; - } - } + for (int i = 0; i < num_cells; ++i) { + for (int d = 0; d < dimensions; ++d) { + permeability_[dsq*i + dimensions*d + d] = perm; + } + } } diff --git a/opm/core/fluid/RockBasic.hpp b/opm/core/fluid/RockBasic.hpp index 96563780..2adc3ffd 100644 --- a/opm/core/fluid/RockBasic.hpp +++ b/opm/core/fluid/RockBasic.hpp @@ -35,9 +35,9 @@ namespace Opm /// Initialize with homogenous porosity and permeability. void init(const int dimensions, - const int num_cells, - const double poro, - const double perm); + const int num_cells, + const double poro, + const double perm); /// \return D, the number of spatial dimensions. int numDimensions() const @@ -66,7 +66,7 @@ namespace Opm } private: - int dimensions_; + int dimensions_; std::vector porosity_; std::vector permeability_; }; diff --git a/opm/core/fluid/RockCompressibility.cpp b/opm/core/fluid/RockCompressibility.cpp index 17d86beb..af65d852 100644 --- a/opm/core/fluid/RockCompressibility.cpp +++ b/opm/core/fluid/RockCompressibility.cpp @@ -69,7 +69,8 @@ namespace Opm const double cpnorm = rock_comp_*(pressure - pref_); return (1.0 + cpnorm + 0.5*cpnorm*cpnorm); } else { - return Opm::linearInterpolation(p_, poromult_, pressure); + // return Opm::linearInterpolation(p_, poromult_, pressure); + return Opm::linearInterpolationExtrap(p_, poromult_, pressure); } } @@ -78,8 +79,11 @@ namespace Opm if (p_.empty()) { return rock_comp_; } else { - const double poromult = Opm::linearInterpolation(p_, poromult_, pressure); - const double dporomultdp = Opm::linearInterpolationDerivative(p_, poromult_, pressure); + //const double poromult = Opm::linearInterpolation(p_, poromult_, pressure); + //const double dporomultdp = Opm::linearInterpolationDerivative(p_, poromult_, pressure); + const double poromult = Opm::linearInterpolationExtrap(p_, poromult_, pressure); + const double dporomultdp = Opm::linearInterpolationDerivativeExtrap(p_, poromult_, pressure); + return dporomultdp/poromult; } } diff --git a/opm/core/fluid/RockFromDeck.cpp b/opm/core/fluid/RockFromDeck.cpp index 94777904..ec7db9a6 100644 --- a/opm/core/fluid/RockFromDeck.cpp +++ b/opm/core/fluid/RockFromDeck.cpp @@ -19,7 +19,7 @@ #include - +#include #include namespace Opm @@ -36,8 +36,6 @@ namespace Opm PermeabilityKind fillTensor(const EclipseGridParser& parser, std::vector*>& tensor, std::tr1::array& kmap); - - int numGlobalCells(const EclipseGridParser& parser); } // anonymous namespace @@ -53,28 +51,29 @@ namespace Opm /// Initialize from deck and cell mapping. /// \param deck Deck input parser - /// \param global_cell mapping from cell indices (typically from a processed grid) + /// \param grid grid to which property object applies, needed for the + /// mapping from cell indices (typically from a processed grid) /// to logical cartesian indices consistent with the deck. void RockFromDeck::init(const EclipseGridParser& deck, - const std::vector& global_cell) + const UnstructuredGrid& grid) { - assignPorosity(deck, global_cell); - permfield_valid_.assign(global_cell.size(), false); + assignPorosity(deck, grid); + permfield_valid_.assign(grid.number_of_cells, false); const double perm_threshold = 0.0; // Maybe turn into parameter? - assignPermeability(deck, global_cell, perm_threshold); + assignPermeability(deck, grid, perm_threshold); } void RockFromDeck::assignPorosity(const EclipseGridParser& parser, - const std::vector& global_cell) + const UnstructuredGrid& grid) { - porosity_.assign(global_cell.size(), 1.0); - + porosity_.assign(grid.number_of_cells, 1.0); + const int* gc = grid.global_cell; if (parser.hasField("PORO")) { const std::vector& poro = parser.getFloatingPointValue("PORO"); - for (int c = 0; c < int(porosity_.size()); ++c) { - porosity_[c] = poro[global_cell[c]]; + const int deck_pos = (gc == NULL) ? c : gc[c]; + porosity_[c] = poro[deck_pos]; } } } @@ -82,14 +81,16 @@ namespace Opm void RockFromDeck::assignPermeability(const EclipseGridParser& parser, - const std::vector& global_cell, + const UnstructuredGrid& grid, double perm_threshold) { const int dim = 3; - const int num_global_cells = numGlobalCells(parser); + const int num_global_cells = grid.cartdims[0]*grid.cartdims[1]*grid.cartdims[2]; + const int nc = grid.number_of_cells; + ASSERT (num_global_cells > 0); - permeability_.assign(dim * dim * global_cell.size(), 0.0); + permeability_.assign(dim * dim * nc, 0.0); std::vector*> tensor; tensor.reserve(10); @@ -111,13 +112,13 @@ namespace Opm // chosen) default value... // if (tensor.size() > 1) { - const int nc = global_cell.size(); - int off = 0; + const int* gc = grid.global_cell; + int off = 0; for (int c = 0; c < nc; ++c, off += dim*dim) { // SharedPermTensor K(dim, dim, &permeability_[off]); int kix = 0; - const int glob = global_cell[c]; + const int glob = (gc == NULL) ? c : gc[c]; for (int i = 0; i < dim; ++i) { for (int j = 0; j < dim; ++j, ++kix) { @@ -331,26 +332,6 @@ namespace Opm return kind; } - int numGlobalCells(const EclipseGridParser& parser) - { - int ngc = -1; - - if (parser.hasField("DIMENS")) { - const std::vector& - dims = parser.getIntegerValue("DIMENS"); - - ngc = dims[0] * dims[1] * dims[2]; - } - else if (parser.hasField("SPECGRID")) { - const SPECGRID& sgr = parser.getSPECGRID(); - - ngc = sgr.dimensions[ 0 ]; - ngc *= sgr.dimensions[ 1 ]; - ngc *= sgr.dimensions[ 2 ]; - } - - return ngc; - } } // anonymous namespace } // namespace Opm diff --git a/opm/core/fluid/RockFromDeck.hpp b/opm/core/fluid/RockFromDeck.hpp index abd9c14c..63e71a6e 100644 --- a/opm/core/fluid/RockFromDeck.hpp +++ b/opm/core/fluid/RockFromDeck.hpp @@ -24,6 +24,7 @@ #include #include +struct UnstructuredGrid; namespace Opm { @@ -34,12 +35,13 @@ namespace Opm /// Default constructor. RockFromDeck(); - /// Initialize from deck and cell mapping. + /// Initialize from deck and grid. /// \param deck Deck input parser - /// \param global_cell mapping from cell indices (typically from a processed grid) + /// \param grid Grid to which property object applies, needed for the + /// mapping from cell indices (typically from a processed grid) /// to logical cartesian indices consistent with the deck. void init(const EclipseGridParser& deck, - const std::vector& global_cell); + const UnstructuredGrid& grid); /// \return D, the number of spatial dimensions. Always 3 for deck input. int numDimensions() const @@ -69,9 +71,9 @@ namespace Opm private: void assignPorosity(const EclipseGridParser& parser, - const std::vector& global_cell); + const UnstructuredGrid& grid); void assignPermeability(const EclipseGridParser& parser, - const std::vector& global_cell, + const UnstructuredGrid& grid, const double perm_threshold); std::vector porosity_; diff --git a/opm/core/fluid/SatFuncSimple.cpp b/opm/core/fluid/SatFuncSimple.cpp new file mode 100644 index 00000000..7e226a4a --- /dev/null +++ b/opm/core/fluid/SatFuncSimple.cpp @@ -0,0 +1,425 @@ +/* + Copyright 2012 SINTEF ICT, Applied Mathematics. + + 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 . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Opm +{ + + + + + void SatFuncSimpleUniform::init(const EclipseGridParser& deck, + const int table_num, + const PhaseUsage phase_usg, + const int samples) + { + phase_usage = phase_usg; + double swco = 0.0; + double swmax = 1.0; + if (phase_usage.phase_used[Aqua]) { + const SWOF::table_t& swof_table = deck.getSWOF().swof_; + const std::vector& sw = swof_table[table_num][0]; + const std::vector& krw = swof_table[table_num][1]; + const std::vector& krow = swof_table[table_num][2]; + const std::vector& pcow = swof_table[table_num][3]; + buildUniformMonotoneTable(sw, krw, samples, krw_); + buildUniformMonotoneTable(sw, krow, samples, krow_); + buildUniformMonotoneTable(sw, pcow, samples, pcow_); + krocw_ = krow[0]; // At connate water -> ecl. SWOF + swco = sw[0]; + smin_[phase_usage.phase_pos[Aqua]] = sw[0]; + swmax = sw.back(); + smax_[phase_usage.phase_pos[Aqua]] = sw.back(); + } + if (phase_usage.phase_used[Vapour]) { + const SGOF::table_t& sgof_table = deck.getSGOF().sgof_; + const std::vector& sg = sgof_table[table_num][0]; + const std::vector& krg = sgof_table[table_num][1]; + const std::vector& krog = sgof_table[table_num][2]; + const std::vector& pcog = sgof_table[table_num][3]; + buildUniformMonotoneTable(sg, krg, samples, krg_); + buildUniformMonotoneTable(sg, krog, samples, krog_); + buildUniformMonotoneTable(sg, pcog, samples, pcog_); + smin_[phase_usage.phase_pos[Vapour]] = sg[0]; + if (std::fabs(sg.back() + swco - 1.0) > 1e-3) { + THROW("Gas maximum saturation in SGOF table = " << sg.back() << + ", should equal (1.0 - connate water sat) = " << (1.0 - swco)); + } + smax_[phase_usage.phase_pos[Vapour]] = sg.back(); + } + // These only consider water min/max sats. Consider gas sats? + smin_[phase_usage.phase_pos[Liquid]] = 1.0 - swmax; + smax_[phase_usage.phase_pos[Liquid]] = 1.0 - swco; + } + + + void SatFuncSimpleUniform::evalKr(const double* s, double* kr) const + { + if (phase_usage.num_phases == 3) { + // A simplified relative permeability model. + double sw = s[Aqua]; + double sg = s[Vapour]; + double krw = krw_(sw); + double krg = krg_(sg); + double krow = krow_(sw + sg); // = 1 - so + // double krog = krog_(sg); // = 1 - so - sw + // double krocw = krocw_; + kr[Aqua] = krw; + kr[Vapour] = krg; + kr[Liquid] = krow; + if (kr[Liquid] < 0.0) { + kr[Liquid] = 0.0; + } + return; + } + // We have a two-phase situation. We know that oil is active. + if (phase_usage.phase_used[Aqua]) { + int wpos = phase_usage.phase_pos[Aqua]; + int opos = phase_usage.phase_pos[Liquid]; + double sw = s[wpos]; + double krw = krw_(sw); + double krow = krow_(sw); + kr[wpos] = krw; + kr[opos] = krow; + } else { + ASSERT(phase_usage.phase_used[Vapour]); + int gpos = phase_usage.phase_pos[Vapour]; + int opos = phase_usage.phase_pos[Liquid]; + double sg = s[gpos]; + double krg = krg_(sg); + double krog = krog_(sg); + kr[gpos] = krg; + kr[opos] = krog; + } + } + + + void SatFuncSimpleUniform::evalKrDeriv(const double* s, double* kr, double* dkrds) const + { + const int np = phase_usage.num_phases; + std::fill(dkrds, dkrds + np*np, 0.0); + + if (np == 3) { + // A simplified relative permeability model. + double sw = s[Aqua]; + double sg = s[Vapour]; + double krw = krw_(sw); + double dkrww = krw_.derivative(sw); + double krg = krg_(sg); + double dkrgg = krg_.derivative(sg); + double krow = krow_(sw + sg); + double dkrow = krow_.derivative(sw + sg); + // double krog = krog_(sg); + // double dkrog = krog_.derivative(sg); + // double krocw = krocw_; + kr[Aqua] = krw; + kr[Vapour] = krg; + kr[Liquid] = krow; + //krocw*((krow/krocw + krw)*(krog/krocw + krg) - krw - krg); + if (kr[Liquid] < 0.0) { + kr[Liquid] = 0.0; + } + dkrds[Aqua + Aqua*np] = dkrww; + dkrds[Vapour + Vapour*np] = dkrgg; + //dkrds[Liquid + Aqua*np] = dkrow; + dkrds[Liquid + Liquid*np] = -dkrow; + //krocw*((dkrow/krocw + dkrww)*(krog/krocw + krg) - dkrww); + dkrds[Liquid + Vapour*np] = 0.0; + //krocw*((krow/krocw + krw)*(dkrog/krocw + dkrgg) - dkrgg) + //+ krocw*((dkrow/krocw + krw)*(krog/krocw + krg) - dkrgg); + return; + } + // We have a two-phase situation. We know that oil is active. + if (phase_usage.phase_used[Aqua]) { + int wpos = phase_usage.phase_pos[Aqua]; + int opos = phase_usage.phase_pos[Liquid]; + double sw = s[wpos]; + double krw = krw_(sw); + double dkrww = krw_.derivative(sw); + double krow = krow_(sw); + double dkrow = krow_.derivative(sw); + kr[wpos] = krw; + kr[opos] = krow; + dkrds[wpos + wpos*np] = dkrww; + dkrds[opos + wpos*np] = dkrow; // Row opos, column wpos, fortran order. + } else { + ASSERT(phase_usage.phase_used[Vapour]); + int gpos = phase_usage.phase_pos[Vapour]; + int opos = phase_usage.phase_pos[Liquid]; + double sg = s[gpos]; + double krg = krg_(sg); + double dkrgg = krg_.derivative(sg); + double krog = krog_(sg); + double dkrog = krog_.derivative(sg); + kr[gpos] = krg; + kr[opos] = krog; + dkrds[gpos + gpos*np] = dkrgg; + dkrds[opos + gpos*np] = dkrog; + } + + } + + + void SatFuncSimpleUniform::evalPc(const double* s, double* pc) const + { + pc[phase_usage.phase_pos[Liquid]] = 0.0; + if (phase_usage.phase_used[Aqua]) { + int pos = phase_usage.phase_pos[Aqua]; + pc[pos] = pcow_(s[pos]); + } + if (phase_usage.phase_used[Vapour]) { + int pos = phase_usage.phase_pos[Vapour]; + pc[pos] = pcog_(s[pos]); + } + } + + void SatFuncSimpleUniform::evalPcDeriv(const double* s, double* pc, double* dpcds) const + { + // The problem of determining three-phase capillary pressures + // is very hard experimentally, usually one extends two-phase + // data (as for relative permeability). + // In our approach the derivative matrix is quite sparse, only + // the diagonal elements corresponding to non-oil phases are + // (potentially) nonzero. + const int np = phase_usage.num_phases; + std::fill(dpcds, dpcds + np*np, 0.0); + pc[phase_usage.phase_pos[Liquid]] = 0.0; + if (phase_usage.phase_used[Aqua]) { + int pos = phase_usage.phase_pos[Aqua]; + pc[pos] = pcow_(s[pos]); + dpcds[np*pos + pos] = pcow_.derivative(s[pos]); + } + if (phase_usage.phase_used[Vapour]) { + int pos = phase_usage.phase_pos[Vapour]; + pc[pos] = pcog_(s[pos]); + dpcds[np*pos + pos] = pcog_.derivative(s[pos]); + } + } + + + + + + // ====== Methods for SatFuncSimpleNonuniform ====== + + + + + + + void SatFuncSimpleNonuniform::init(const EclipseGridParser& deck, + const int table_num, + const PhaseUsage phase_usg, + const int /*samples*/) + { + phase_usage = phase_usg; + double swco = 0.0; + double swmax = 1.0; + if (phase_usage.phase_used[Aqua]) { + const SWOF::table_t& swof_table = deck.getSWOF().swof_; + const std::vector& sw = swof_table[table_num][0]; + const std::vector& krw = swof_table[table_num][1]; + const std::vector& krow = swof_table[table_num][2]; + const std::vector& pcow = swof_table[table_num][3]; + krw_ = NonuniformTableLinear(sw, krw); + krow_ = NonuniformTableLinear(sw, krow); + pcow_ = NonuniformTableLinear(sw, pcow); + krocw_ = krow[0]; // At connate water -> ecl. SWOF + swco = sw[0]; + smin_[phase_usage.phase_pos[Aqua]] = sw[0]; + swmax = sw.back(); + smax_[phase_usage.phase_pos[Aqua]] = sw.back(); + } + if (phase_usage.phase_used[Vapour]) { + const SGOF::table_t& sgof_table = deck.getSGOF().sgof_; + const std::vector& sg = sgof_table[table_num][0]; + const std::vector& krg = sgof_table[table_num][1]; + const std::vector& krog = sgof_table[table_num][2]; + const std::vector& pcog = sgof_table[table_num][3]; + krg_ = NonuniformTableLinear(sg, krg); + krog_ = NonuniformTableLinear(sg, krog); + pcog_ = NonuniformTableLinear(sg, pcog); + smin_[phase_usage.phase_pos[Vapour]] = sg[0]; + if (std::fabs(sg.back() + swco - 1.0) > 1e-3) { + THROW("Gas maximum saturation in SGOF table = " << sg.back() << + ", should equal (1.0 - connate water sat) = " << (1.0 - swco)); + } + smax_[phase_usage.phase_pos[Vapour]] = sg.back(); + } + // These only consider water min/max sats. Consider gas sats? + smin_[phase_usage.phase_pos[Liquid]] = 1.0 - swmax; + smax_[phase_usage.phase_pos[Liquid]] = 1.0 - swco; + } + + + void SatFuncSimpleNonuniform::evalKr(const double* s, double* kr) const + { + if (phase_usage.num_phases == 3) { + // A simplified relative permeability model. + double sw = s[Aqua]; + double sg = s[Vapour]; + double krw = krw_(sw); + double krg = krg_(sg); + double krow = krow_(sw + sg); // = 1 - so + // double krog = krog_(sg); // = 1 - so - sw + // double krocw = krocw_; + kr[Aqua] = krw; + kr[Vapour] = krg; + kr[Liquid] = krow; + if (kr[Liquid] < 0.0) { + kr[Liquid] = 0.0; + } + return; + } + // We have a two-phase situation. We know that oil is active. + if (phase_usage.phase_used[Aqua]) { + int wpos = phase_usage.phase_pos[Aqua]; + int opos = phase_usage.phase_pos[Liquid]; + double sw = s[wpos]; + double krw = krw_(sw); + double krow = krow_(sw); + kr[wpos] = krw; + kr[opos] = krow; + } else { + ASSERT(phase_usage.phase_used[Vapour]); + int gpos = phase_usage.phase_pos[Vapour]; + int opos = phase_usage.phase_pos[Liquid]; + double sg = s[gpos]; + double krg = krg_(sg); + double krog = krog_(sg); + kr[gpos] = krg; + kr[opos] = krog; + } + } + + + void SatFuncSimpleNonuniform::evalKrDeriv(const double* s, double* kr, double* dkrds) const + { + const int np = phase_usage.num_phases; + std::fill(dkrds, dkrds + np*np, 0.0); + + if (np == 3) { + // A simplified relative permeability model. + double sw = s[Aqua]; + double sg = s[Vapour]; + double krw = krw_(sw); + double dkrww = krw_.derivative(sw); + double krg = krg_(sg); + double dkrgg = krg_.derivative(sg); + double krow = krow_(sw + sg); + double dkrow = krow_.derivative(sw + sg); + // double krog = krog_(sg); + // double dkrog = krog_.derivative(sg); + // double krocw = krocw_; + kr[Aqua] = krw; + kr[Vapour] = krg; + kr[Liquid] = krow; + //krocw*((krow/krocw + krw)*(krog/krocw + krg) - krw - krg); + if (kr[Liquid] < 0.0) { + kr[Liquid] = 0.0; + } + dkrds[Aqua + Aqua*np] = dkrww; + dkrds[Vapour + Vapour*np] = dkrgg; + //dkrds[Liquid + Aqua*np] = dkrow; + dkrds[Liquid + Liquid*np] = -dkrow; + //krocw*((dkrow/krocw + dkrww)*(krog/krocw + krg) - dkrww); + dkrds[Liquid + Vapour*np] = 0.0; + //krocw*((krow/krocw + krw)*(dkrog/krocw + dkrgg) - dkrgg) + //+ krocw*((dkrow/krocw + krw)*(krog/krocw + krg) - dkrgg); + return; + } + // We have a two-phase situation. We know that oil is active. + if (phase_usage.phase_used[Aqua]) { + int wpos = phase_usage.phase_pos[Aqua]; + int opos = phase_usage.phase_pos[Liquid]; + double sw = s[wpos]; + double krw = krw_(sw); + double dkrww = krw_.derivative(sw); + double krow = krow_(sw); + double dkrow = krow_.derivative(sw); + kr[wpos] = krw; + kr[opos] = krow; + dkrds[wpos + wpos*np] = dkrww; + dkrds[opos + wpos*np] = dkrow; // Row opos, column wpos, fortran order. + } else { + ASSERT(phase_usage.phase_used[Vapour]); + int gpos = phase_usage.phase_pos[Vapour]; + int opos = phase_usage.phase_pos[Liquid]; + double sg = s[gpos]; + double krg = krg_(sg); + double dkrgg = krg_.derivative(sg); + double krog = krog_(sg); + double dkrog = krog_.derivative(sg); + kr[gpos] = krg; + kr[opos] = krog; + dkrds[gpos + gpos*np] = dkrgg; + dkrds[opos + gpos*np] = dkrog; + } + + } + + + void SatFuncSimpleNonuniform::evalPc(const double* s, double* pc) const + { + pc[phase_usage.phase_pos[Liquid]] = 0.0; + if (phase_usage.phase_used[Aqua]) { + int pos = phase_usage.phase_pos[Aqua]; + pc[pos] = pcow_(s[pos]); + } + if (phase_usage.phase_used[Vapour]) { + int pos = phase_usage.phase_pos[Vapour]; + pc[pos] = pcog_(s[pos]); + } + } + + void SatFuncSimpleNonuniform::evalPcDeriv(const double* s, double* pc, double* dpcds) const + { + // The problem of determining three-phase capillary pressures + // is very hard experimentally, usually one extends two-phase + // data (as for relative permeability). + // In our approach the derivative matrix is quite sparse, only + // the diagonal elements corresponding to non-oil phases are + // (potentially) nonzero. + const int np = phase_usage.num_phases; + std::fill(dpcds, dpcds + np*np, 0.0); + pc[phase_usage.phase_pos[Liquid]] = 0.0; + if (phase_usage.phase_used[Aqua]) { + int pos = phase_usage.phase_pos[Aqua]; + pc[pos] = pcow_(s[pos]); + dpcds[np*pos + pos] = pcow_.derivative(s[pos]); + } + if (phase_usage.phase_used[Vapour]) { + int pos = phase_usage.phase_pos[Vapour]; + pc[pos] = pcog_(s[pos]); + dpcds[np*pos + pos] = pcog_.derivative(s[pos]); + } + } + + + + +} // namespace Opm diff --git a/opm/core/fluid/SatFuncSimple.hpp b/opm/core/fluid/SatFuncSimple.hpp new file mode 100644 index 00000000..338a359e --- /dev/null +++ b/opm/core/fluid/SatFuncSimple.hpp @@ -0,0 +1,80 @@ +/* + Copyright 2012 SINTEF ICT, Applied Mathematics. + + 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 . +*/ +#ifndef SATFUNCSIMPLE_HPP +#define SATFUNCSIMPLE_HPP + +#include +#include +#include +#include +#include + +namespace Opm +{ + class SatFuncSimpleUniform : public BlackoilPhases + { + public: + void init(const EclipseGridParser& deck, + const int table_num, + const PhaseUsage phase_usg, + const int samples); + void evalKr(const double* s, double* kr) const; + void evalKrDeriv(const double* s, double* kr, double* dkrds) const; + void evalPc(const double* s, double* pc) const; + void evalPcDeriv(const double* s, double* pc, double* dpcds) const; + double smin_[PhaseUsage::MaxNumPhases]; + double smax_[PhaseUsage::MaxNumPhases]; + private: + PhaseUsage phase_usage; // A copy of the outer class' phase_usage_. + UniformTableLinear krw_; + UniformTableLinear krow_; + UniformTableLinear pcow_; + UniformTableLinear krg_; + UniformTableLinear krog_; + UniformTableLinear pcog_; + double krocw_; // = krow_(s_wc) + }; + + + class SatFuncSimpleNonuniform : public BlackoilPhases + { + public: + void init(const EclipseGridParser& deck, + const int table_num, + const PhaseUsage phase_usg, + const int samples); + void evalKr(const double* s, double* kr) const; + void evalKrDeriv(const double* s, double* kr, double* dkrds) const; + void evalPc(const double* s, double* pc) const; + void evalPcDeriv(const double* s, double* pc, double* dpcds) const; + double smin_[PhaseUsage::MaxNumPhases]; + double smax_[PhaseUsage::MaxNumPhases]; + private: + PhaseUsage phase_usage; // A copy of the outer class' phase_usage_. + NonuniformTableLinear krw_; + NonuniformTableLinear krow_; + NonuniformTableLinear pcow_; + NonuniformTableLinear krg_; + NonuniformTableLinear krog_; + NonuniformTableLinear pcog_; + double krocw_; // = krow_(s_wc) + }; + +} // namespace Opm +#endif // SATFUNCSIMPLE_HPP diff --git a/opm/core/fluid/SatFuncStone2.cpp b/opm/core/fluid/SatFuncStone2.cpp new file mode 100644 index 00000000..062f8534 --- /dev/null +++ b/opm/core/fluid/SatFuncStone2.cpp @@ -0,0 +1,417 @@ +/* + Copyright 2012 SINTEF ICT, Applied Mathematics. + + 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 . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Opm +{ + + + + + void SatFuncStone2Uniform::init(const EclipseGridParser& deck, + const int table_num, + const PhaseUsage phase_usg, + const int samples) + { + phase_usage = phase_usg; + double swco = 0.0; + double swmax = 1.0; + if (phase_usage.phase_used[Aqua]) { + const SWOF::table_t& swof_table = deck.getSWOF().swof_; + const std::vector& sw = swof_table[table_num][0]; + const std::vector& krw = swof_table[table_num][1]; + const std::vector& krow = swof_table[table_num][2]; + const std::vector& pcow = swof_table[table_num][3]; + buildUniformMonotoneTable(sw, krw, samples, krw_); + buildUniformMonotoneTable(sw, krow, samples, krow_); + buildUniformMonotoneTable(sw, pcow, samples, pcow_); + krocw_ = krow[0]; // At connate water -> ecl. SWOF + swco = sw[0]; + smin_[phase_usage.phase_pos[Aqua]] = sw[0]; + swmax = sw.back(); + smax_[phase_usage.phase_pos[Aqua]] = sw.back(); + } + if (phase_usage.phase_used[Vapour]) { + const SGOF::table_t& sgof_table = deck.getSGOF().sgof_; + const std::vector& sg = sgof_table[table_num][0]; + const std::vector& krg = sgof_table[table_num][1]; + const std::vector& krog = sgof_table[table_num][2]; + const std::vector& pcog = sgof_table[table_num][3]; + buildUniformMonotoneTable(sg, krg, samples, krg_); + buildUniformMonotoneTable(sg, krog, samples, krog_); + buildUniformMonotoneTable(sg, pcog, samples, pcog_); + smin_[phase_usage.phase_pos[Vapour]] = sg[0]; + if (std::fabs(sg.back() + swco - 1.0) > 1e-3) { + THROW("Gas maximum saturation in SGOF table = " << sg.back() << + ", should equal (1.0 - connate water sat) = " << (1.0 - swco)); + } + smax_[phase_usage.phase_pos[Vapour]] = sg.back(); + } + // These only consider water min/max sats. Consider gas sats? + smin_[phase_usage.phase_pos[Liquid]] = 1.0 - swmax; + smax_[phase_usage.phase_pos[Liquid]] = 1.0 - swco; + } + + + void SatFuncStone2Uniform::evalKr(const double* s, double* kr) const + { + if (phase_usage.num_phases == 3) { + // Stone-II relative permeability model. + double sw = s[Aqua]; + double sg = s[Vapour]; + double krw = krw_(sw); + double krg = krg_(sg); + double krow = krow_(sw + sg); // = 1 - so + double krog = krog_(sg); // = 1 - so - sw + double krocw = krocw_; + kr[Aqua] = krw; + kr[Vapour] = krg; + kr[Liquid] = krocw*((krow/krocw + krw)*(krog/krocw + krg) - krw - krg); + if (kr[Liquid] < 0.0) { + kr[Liquid] = 0.0; + } + return; + } + // We have a two-phase situation. We know that oil is active. + if (phase_usage.phase_used[Aqua]) { + int wpos = phase_usage.phase_pos[Aqua]; + int opos = phase_usage.phase_pos[Liquid]; + double sw = s[wpos]; + double krw = krw_(sw); + double krow = krow_(sw); + kr[wpos] = krw; + kr[opos] = krow; + } else { + ASSERT(phase_usage.phase_used[Vapour]); + int gpos = phase_usage.phase_pos[Vapour]; + int opos = phase_usage.phase_pos[Liquid]; + double sg = s[gpos]; + double krg = krg_(sg); + double krog = krog_(sg); + kr[gpos] = krg; + kr[opos] = krog; + } + } + + + void SatFuncStone2Uniform::evalKrDeriv(const double* s, double* kr, double* dkrds) const + { + const int np = phase_usage.num_phases; + std::fill(dkrds, dkrds + np*np, 0.0); + + if (np == 3) { + // Stone-II relative permeability model. + double sw = s[Aqua]; + double sg = s[Vapour]; + double krw = krw_(sw); + double dkrww = krw_.derivative(sw); + double krg = krg_(sg); + double dkrgg = krg_.derivative(sg); + double krow = krow_(sw + sg); + double dkrow = krow_.derivative(sw + sg); + double krog = krog_(sg); + double dkrog = krog_.derivative(sg); + double krocw = krocw_; + kr[Aqua] = krw; + kr[Vapour] = krg; + kr[Liquid] = krocw*((krow/krocw + krw)*(krog/krocw + krg) - krw - krg); + if (kr[Liquid] < 0.0) { + kr[Liquid] = 0.0; + } + dkrds[Aqua + Aqua*np] = dkrww; + dkrds[Vapour + Vapour*np] = dkrgg; + dkrds[Liquid + Aqua*np] = krocw*((dkrow/krocw + dkrww)*(krog/krocw + krg) - dkrww); + dkrds[Liquid + Vapour*np] = krocw*((krow/krocw + krw)*(dkrog/krocw + dkrgg) - dkrgg) + + krocw*((dkrow/krocw + krw)*(krog/krocw + krg) - dkrgg); + return; + } + // We have a two-phase situation. We know that oil is active. + if (phase_usage.phase_used[Aqua]) { + int wpos = phase_usage.phase_pos[Aqua]; + int opos = phase_usage.phase_pos[Liquid]; + double sw = s[wpos]; + double krw = krw_(sw); + double dkrww = krw_.derivative(sw); + double krow = krow_(sw); + double dkrow = krow_.derivative(sw); + kr[wpos] = krw; + kr[opos] = krow; + dkrds[wpos + wpos*np] = dkrww; + dkrds[opos + wpos*np] = dkrow; // Row opos, column wpos, fortran order. + } else { + ASSERT(phase_usage.phase_used[Vapour]); + int gpos = phase_usage.phase_pos[Vapour]; + int opos = phase_usage.phase_pos[Liquid]; + double sg = s[gpos]; + double krg = krg_(sg); + double dkrgg = krg_.derivative(sg); + double krog = krog_(sg); + double dkrog = krog_.derivative(sg); + kr[gpos] = krg; + kr[opos] = krog; + dkrds[gpos + gpos*np] = dkrgg; + dkrds[opos + gpos*np] = dkrog; + } + + } + + + void SatFuncStone2Uniform::evalPc(const double* s, double* pc) const + { + pc[phase_usage.phase_pos[Liquid]] = 0.0; + if (phase_usage.phase_used[Aqua]) { + int pos = phase_usage.phase_pos[Aqua]; + pc[pos] = pcow_(s[pos]); + } + if (phase_usage.phase_used[Vapour]) { + int pos = phase_usage.phase_pos[Vapour]; + pc[pos] = pcog_(s[pos]); + } + } + + void SatFuncStone2Uniform::evalPcDeriv(const double* s, double* pc, double* dpcds) const + { + // The problem of determining three-phase capillary pressures + // is very hard experimentally, usually one extends two-phase + // data (as for relative permeability). + // In our approach the derivative matrix is quite sparse, only + // the diagonal elements corresponding to non-oil phases are + // (potentially) nonzero. + const int np = phase_usage.num_phases; + std::fill(dpcds, dpcds + np*np, 0.0); + pc[phase_usage.phase_pos[Liquid]] = 0.0; + if (phase_usage.phase_used[Aqua]) { + int pos = phase_usage.phase_pos[Aqua]; + pc[pos] = pcow_(s[pos]); + dpcds[np*pos + pos] = pcow_.derivative(s[pos]); + } + if (phase_usage.phase_used[Vapour]) { + int pos = phase_usage.phase_pos[Vapour]; + pc[pos] = pcog_(s[pos]); + dpcds[np*pos + pos] = pcog_.derivative(s[pos]); + } + } + + + + + + + // ====== Methods for SatFuncSimpleNonuniform ====== + + + + + void SatFuncStone2Nonuniform::init(const EclipseGridParser& deck, + const int table_num, + const PhaseUsage phase_usg, + const int /*samples*/) + { + phase_usage = phase_usg; + double swco = 0.0; + double swmax = 1.0; + if (phase_usage.phase_used[Aqua]) { + const SWOF::table_t& swof_table = deck.getSWOF().swof_; + const std::vector& sw = swof_table[table_num][0]; + const std::vector& krw = swof_table[table_num][1]; + const std::vector& krow = swof_table[table_num][2]; + const std::vector& pcow = swof_table[table_num][3]; + krw_ = NonuniformTableLinear(sw, krw); + krow_ = NonuniformTableLinear(sw, krow); + pcow_ = NonuniformTableLinear(sw, pcow); + krocw_ = krow[0]; // At connate water -> ecl. SWOF + swco = sw[0]; + smin_[phase_usage.phase_pos[Aqua]] = sw[0]; + swmax = sw.back(); + smax_[phase_usage.phase_pos[Aqua]] = sw.back(); + } + if (phase_usage.phase_used[Vapour]) { + const SGOF::table_t& sgof_table = deck.getSGOF().sgof_; + const std::vector& sg = sgof_table[table_num][0]; + const std::vector& krg = sgof_table[table_num][1]; + const std::vector& krog = sgof_table[table_num][2]; + const std::vector& pcog = sgof_table[table_num][3]; + krg_ = NonuniformTableLinear(sg, krg); + krog_ = NonuniformTableLinear(sg, krog); + pcog_ = NonuniformTableLinear(sg, pcog); + smin_[phase_usage.phase_pos[Vapour]] = sg[0]; + if (std::fabs(sg.back() + swco - 1.0) > 1e-3) { + THROW("Gas maximum saturation in SGOF table = " << sg.back() << + ", should equal (1.0 - connate water sat) = " << (1.0 - swco)); + } + smax_[phase_usage.phase_pos[Vapour]] = sg.back(); + } + // These only consider water min/max sats. Consider gas sats? + smin_[phase_usage.phase_pos[Liquid]] = 1.0 - swmax; + smax_[phase_usage.phase_pos[Liquid]] = 1.0 - swco; + } + + + void SatFuncStone2Nonuniform::evalKr(const double* s, double* kr) const + { + if (phase_usage.num_phases == 3) { + // Stone-II relative permeability model. + double sw = s[Aqua]; + double sg = s[Vapour]; + double krw = krw_(sw); + double krg = krg_(sg); + double krow = krow_(sw + sg); // = 1 - so + double krog = krog_(sg); // = 1 - so - sw + double krocw = krocw_; + kr[Aqua] = krw; + kr[Vapour] = krg; + kr[Liquid] = krocw*((krow/krocw + krw)*(krog/krocw + krg) - krw - krg); + if (kr[Liquid] < 0.0) { + kr[Liquid] = 0.0; + } + return; + } + // We have a two-phase situation. We know that oil is active. + if (phase_usage.phase_used[Aqua]) { + int wpos = phase_usage.phase_pos[Aqua]; + int opos = phase_usage.phase_pos[Liquid]; + double sw = s[wpos]; + double krw = krw_(sw); + double krow = krow_(sw); + kr[wpos] = krw; + kr[opos] = krow; + } else { + ASSERT(phase_usage.phase_used[Vapour]); + int gpos = phase_usage.phase_pos[Vapour]; + int opos = phase_usage.phase_pos[Liquid]; + double sg = s[gpos]; + double krg = krg_(sg); + double krog = krog_(sg); + kr[gpos] = krg; + kr[opos] = krog; + } + } + + + void SatFuncStone2Nonuniform::evalKrDeriv(const double* s, double* kr, double* dkrds) const + { + const int np = phase_usage.num_phases; + std::fill(dkrds, dkrds + np*np, 0.0); + + if (np == 3) { + // Stone-II relative permeability model. + double sw = s[Aqua]; + double sg = s[Vapour]; + double krw = krw_(sw); + double dkrww = krw_.derivative(sw); + double krg = krg_(sg); + double dkrgg = krg_.derivative(sg); + double krow = krow_(sw + sg); + double dkrow = krow_.derivative(sw + sg); + double krog = krog_(sg); + double dkrog = krog_.derivative(sg); + double krocw = krocw_; + kr[Aqua] = krw; + kr[Vapour] = krg; + kr[Liquid] = krocw*((krow/krocw + krw)*(krog/krocw + krg) - krw - krg); + if (kr[Liquid] < 0.0) { + kr[Liquid] = 0.0; + } + dkrds[Aqua + Aqua*np] = dkrww; + dkrds[Vapour + Vapour*np] = dkrgg; + dkrds[Liquid + Aqua*np] = krocw*((dkrow/krocw + dkrww)*(krog/krocw + krg) - dkrww); + dkrds[Liquid + Vapour*np] = krocw*((krow/krocw + krw)*(dkrog/krocw + dkrgg) - dkrgg) + + krocw*((dkrow/krocw + krw)*(krog/krocw + krg) - dkrgg); + return; + } + // We have a two-phase situation. We know that oil is active. + if (phase_usage.phase_used[Aqua]) { + int wpos = phase_usage.phase_pos[Aqua]; + int opos = phase_usage.phase_pos[Liquid]; + double sw = s[wpos]; + double krw = krw_(sw); + double dkrww = krw_.derivative(sw); + double krow = krow_(sw); + double dkrow = krow_.derivative(sw); + kr[wpos] = krw; + kr[opos] = krow; + dkrds[wpos + wpos*np] = dkrww; + dkrds[opos + wpos*np] = dkrow; // Row opos, column wpos, fortran order. + } else { + ASSERT(phase_usage.phase_used[Vapour]); + int gpos = phase_usage.phase_pos[Vapour]; + int opos = phase_usage.phase_pos[Liquid]; + double sg = s[gpos]; + double krg = krg_(sg); + double dkrgg = krg_.derivative(sg); + double krog = krog_(sg); + double dkrog = krog_.derivative(sg); + kr[gpos] = krg; + kr[opos] = krog; + dkrds[gpos + gpos*np] = dkrgg; + dkrds[opos + gpos*np] = dkrog; + } + + } + + + void SatFuncStone2Nonuniform::evalPc(const double* s, double* pc) const + { + pc[phase_usage.phase_pos[Liquid]] = 0.0; + if (phase_usage.phase_used[Aqua]) { + int pos = phase_usage.phase_pos[Aqua]; + pc[pos] = pcow_(s[pos]); + } + if (phase_usage.phase_used[Vapour]) { + int pos = phase_usage.phase_pos[Vapour]; + pc[pos] = pcog_(s[pos]); + } + } + + void SatFuncStone2Nonuniform::evalPcDeriv(const double* s, double* pc, double* dpcds) const + { + // The problem of determining three-phase capillary pressures + // is very hard experimentally, usually one extends two-phase + // data (as for relative permeability). + // In our approach the derivative matrix is quite sparse, only + // the diagonal elements corresponding to non-oil phases are + // (potentially) nonzero. + const int np = phase_usage.num_phases; + std::fill(dpcds, dpcds + np*np, 0.0); + pc[phase_usage.phase_pos[Liquid]] = 0.0; + if (phase_usage.phase_used[Aqua]) { + int pos = phase_usage.phase_pos[Aqua]; + pc[pos] = pcow_(s[pos]); + dpcds[np*pos + pos] = pcow_.derivative(s[pos]); + } + if (phase_usage.phase_used[Vapour]) { + int pos = phase_usage.phase_pos[Vapour]; + pc[pos] = pcog_(s[pos]); + dpcds[np*pos + pos] = pcog_.derivative(s[pos]); + } + } + + + + + +} // namespace Opm diff --git a/opm/core/fluid/SatFuncStone2.hpp b/opm/core/fluid/SatFuncStone2.hpp new file mode 100644 index 00000000..532bf7ce --- /dev/null +++ b/opm/core/fluid/SatFuncStone2.hpp @@ -0,0 +1,80 @@ +/* + Copyright 2012 SINTEF ICT, Applied Mathematics. + + 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 . +*/ +#ifndef SATFUNCSTONE2_HPP +#define SATFUNCSTONE2_HPP + +#include +#include +#include +#include +#include + +namespace Opm +{ + class SatFuncStone2Uniform : public BlackoilPhases + { + public: + void init(const EclipseGridParser& deck, + const int table_num, + const PhaseUsage phase_usg, + const int samples); + void evalKr(const double* s, double* kr) const; + void evalKrDeriv(const double* s, double* kr, double* dkrds) const; + void evalPc(const double* s, double* pc) const; + void evalPcDeriv(const double* s, double* pc, double* dpcds) const; + double smin_[PhaseUsage::MaxNumPhases]; + double smax_[PhaseUsage::MaxNumPhases]; + private: + PhaseUsage phase_usage; // A copy of the outer class' phase_usage_. + UniformTableLinear krw_; + UniformTableLinear krow_; + UniformTableLinear pcow_; + UniformTableLinear krg_; + UniformTableLinear krog_; + UniformTableLinear pcog_; + double krocw_; // = krow_(s_wc) + }; + + + class SatFuncStone2Nonuniform : public BlackoilPhases + { + public: + void init(const EclipseGridParser& deck, + const int table_num, + const PhaseUsage phase_usg, + const int samples); + void evalKr(const double* s, double* kr) const; + void evalKrDeriv(const double* s, double* kr, double* dkrds) const; + void evalPc(const double* s, double* pc) const; + void evalPcDeriv(const double* s, double* pc, double* dpcds) const; + double smin_[PhaseUsage::MaxNumPhases]; + double smax_[PhaseUsage::MaxNumPhases]; + private: + PhaseUsage phase_usage; // A copy of the outer class' phase_usage_. + NonuniformTableLinear krw_; + NonuniformTableLinear krow_; + NonuniformTableLinear pcow_; + NonuniformTableLinear krg_; + NonuniformTableLinear krog_; + NonuniformTableLinear pcog_; + double krocw_; // = krow_(s_wc) + }; + +} // namespace Opm +#endif // SATFUNCSTONE2_HPP diff --git a/opm/core/fluid/SaturationPropsBasic.cpp b/opm/core/fluid/SaturationPropsBasic.cpp index 4f20bd1b..10ddf23d 100644 --- a/opm/core/fluid/SaturationPropsBasic.cpp +++ b/opm/core/fluid/SaturationPropsBasic.cpp @@ -29,64 +29,64 @@ namespace Opm namespace { - struct KrFunConstant - { - double kr(double) - { - return 1.0; - } - double dkrds(double) - { - return 0.0; - } - }; + struct KrFunConstant + { + double kr(double) + { + return 1.0; + } + double dkrds(double) + { + return 0.0; + } + }; - struct KrFunLinear - { - double kr(double s) - { - return s; - } - double dkrds(double) - { - return 1.0; - } - }; + struct KrFunLinear + { + double kr(double s) + { + return s; + } + double dkrds(double) + { + return 1.0; + } + }; - struct KrFunQuadratic - { - double kr(double s) - { - return s*s; - } - double dkrds(double s) - { - return 2.0*s; - } - }; + struct KrFunQuadratic + { + double kr(double s) + { + return s*s; + } + double dkrds(double s) + { + return 2.0*s; + } + }; - template - static inline void evalAllKrDeriv(const int n, const int np, - const double* s, double* kr, double* dkrds, Fun fun) - { - if (dkrds == 0) { + template + static inline void evalAllKrDeriv(const int n, const int np, + const double* s, double* kr, double* dkrds, Fun fun) + { + if (dkrds == 0) { // #pragma omp parallel for - for (int i = 0; i < n*np; ++i) { - kr[i] = fun.kr(s[i]); - } - return; - } + for (int i = 0; i < n*np; ++i) { + kr[i] = fun.kr(s[i]); + } + return; + } // #pragma omp parallel for - for (int i = 0; i < n; ++i) { - std::fill(dkrds + i*np*np, dkrds + (i+1)*np*np, 0.0); - for (int phase = 0; phase < np; ++phase) { - kr[i*np + phase] = fun.kr(s[i*np + phase]); - // Only diagonal elements in derivative. - dkrds[i*np*np + phase*np + phase] = fun.dkrds(s[i*np + phase]); - } - } - } + for (int i = 0; i < n; ++i) { + std::fill(dkrds + i*np*np, dkrds + (i+1)*np*np, 0.0); + for (int phase = 0; phase < np; ++phase) { + kr[i*np + phase] = fun.kr(s[i*np + phase]); + // Only diagonal elements in derivative. + dkrds[i*np*np + phase*np + phase] = fun.dkrds(s[i*np + phase]); + } + } + } } // anon namespace @@ -99,6 +99,7 @@ namespace Opm /// Default constructor. SaturationPropsBasic::SaturationPropsBasic() + : num_phases_(0), relperm_func_(Constant) { } @@ -108,24 +109,25 @@ namespace Opm /// Initialize from parameters. void SaturationPropsBasic::init(const parameter::ParameterGroup& param) { - int num_phases = param.getDefault("num_phases", 2); - if (num_phases > 2 || num_phases < 1) { - THROW("SaturationPropsBasic::init() illegal num_phases: " << num_phases); - } + int num_phases = param.getDefault("num_phases", 2); + if (num_phases > 2 || num_phases < 1) { + THROW("SaturationPropsBasic::init() illegal num_phases: " << num_phases); + } num_phases_ = num_phases; - std::string rpf = param.getDefault("relperm_func", std::string("Unset")); - if (rpf == "Constant") { - relperm_func_ = Constant; - if(num_phases!=1){ - THROW("Constant relperm with more than one phase???"); - } - } else if (rpf == "Linear") { - relperm_func_ = Linear; - } else if (rpf == "Quadratic") { - relperm_func_ = Quadratic; - } else { - THROW("SaturationPropsBasic::init() illegal relperm_func: " << rpf); - } + //std::string rpf = param.getDefault("relperm_func", std::string("Unset")); + std::string rpf = param.getDefault("relperm_func", std::string("Linear")); + if (rpf == "Constant") { + relperm_func_ = Constant; + if(num_phases!=1){ + THROW("Constant relperm with more than one phase???"); + } + } else if (rpf == "Linear") { + relperm_func_ = Linear; + } else if (rpf == "Quadratic") { + relperm_func_ = Quadratic; + } else { + THROW("SaturationPropsBasic::init() illegal relperm_func: " << rpf); + } } @@ -134,7 +136,7 @@ namespace Opm /// \return P, the number of phases. int SaturationPropsBasic::numPhases() const { - return num_phases_; + return num_phases_; } @@ -150,29 +152,29 @@ namespace Opm /// m_{ij} = \frac{dkr_i}{ds^j}, /// and is output in Fortran order (m_00 m_10 m_20 m01 ...) void SaturationPropsBasic::relperm(const int n, - const double* s, - double* kr, - double* dkrds) const + const double* s, + double* kr, + double* dkrds) const { - switch (relperm_func_) { - case Constant: - { - evalAllKrDeriv(n, num_phases_, s, kr, dkrds, KrFunConstant()); - break; - } - case Linear: - { - evalAllKrDeriv(n, num_phases_, s, kr, dkrds, KrFunLinear()); - break; - } - case Quadratic: - { - evalAllKrDeriv(n, num_phases_, s, kr, dkrds, KrFunQuadratic()); - break; - } - default: - THROW("SaturationPropsBasic::relperm() unhandled relperm func type: " << relperm_func_); - } + switch (relperm_func_) { + case Constant: + { + evalAllKrDeriv(n, num_phases_, s, kr, dkrds, KrFunConstant()); + break; + } + case Linear: + { + evalAllKrDeriv(n, num_phases_, s, kr, dkrds, KrFunLinear()); + break; + } + case Quadratic: + { + evalAllKrDeriv(n, num_phases_, s, kr, dkrds, KrFunQuadratic()); + break; + } + default: + THROW("SaturationPropsBasic::relperm() unhandled relperm func type: " << relperm_func_); + } } @@ -188,13 +190,13 @@ namespace Opm /// m_{ij} = \frac{dpc_i}{ds^j}, /// and is output in Fortran order (m_00 m_10 m_20 m01 ...) void SaturationPropsBasic::capPress(const int n, - const double* /*s*/, - double* pc, - double* dpcds) const + const double* /*s*/, + double* pc, + double* dpcds) const { - std::fill(pc, pc + num_phases_*n, 0.0); + std::fill(pc, pc + num_phases_*n, 0.0); if (dpcds) { - std::fill(dpcds, dpcds + num_phases_*num_phases_*n, 0.0); + std::fill(dpcds, dpcds + num_phases_*num_phases_*n, 0.0); } } @@ -205,11 +207,11 @@ namespace Opm /// \param[out] smin Array of nP minimum s values, array must be valid before calling. /// \param[out] smax Array of nP maximum s values, array must be valid before calling. void SaturationPropsBasic::satRange(const int n, - double* smin, - double* smax) const + double* smin, + double* smax) const { - std::fill(smin, smin + num_phases_*n, 0.0); - std::fill(smax, smax + num_phases_*n, 1.0); + std::fill(smin, smin + num_phases_*n, 0.0); + std::fill(smax, smax + num_phases_*n, 1.0); } diff --git a/opm/core/fluid/SaturationPropsBasic.hpp b/opm/core/fluid/SaturationPropsBasic.hpp index 789e0a35..2f90672d 100644 --- a/opm/core/fluid/SaturationPropsBasic.hpp +++ b/opm/core/fluid/SaturationPropsBasic.hpp @@ -40,16 +40,16 @@ namespace Opm SaturationPropsBasic(); /// Initialize from parameters. - /// The following parameters are accepted (defaults): - /// num_phases (2) Must be 1 or 2. - /// relperm_func ("Linear") Must be "Constant", "Linear" or "Quadratic". + /// The following parameters are accepted (defaults): + /// num_phases (2) Must be 1 or 2. + /// relperm_func ("Linear") Must be "Constant", "Linear" or "Quadratic". void init(const parameter::ParameterGroup& param); - enum RelPermFunc { Constant, Linear, Quadratic }; + enum RelPermFunc { Constant, Linear, Quadratic }; /// Initialize from arguments a basic Saturation property. void init(const int num_phases, - const RelPermFunc& relperm_func) + const RelPermFunc& relperm_func) { num_phases_ = num_phases; relperm_func_ = relperm_func; @@ -86,18 +86,18 @@ namespace Opm double* pc, double* dpcds) const; - /// Obtain the range of allowable saturation values. + /// Obtain the range of allowable saturation values. /// \param[in] n Number of data points. /// \param[out] smin Array of nP minimum s values, array must be valid before calling. /// \param[out] smax Array of nP maximum s values, array must be valid before calling. - void satRange(const int n, - double* smin, - double* smax) const; + void satRange(const int n, + double* smin, + double* smax) const; private: - int num_phases_; - RelPermFunc relperm_func_; + int num_phases_; + RelPermFunc relperm_func_; }; diff --git a/opm/core/fluid/SaturationPropsFromDeck.cpp b/opm/core/fluid/SaturationPropsFromDeck.cpp index 89f81278..83c942c5 100644 --- a/opm/core/fluid/SaturationPropsFromDeck.cpp +++ b/opm/core/fluid/SaturationPropsFromDeck.cpp @@ -18,7 +18,7 @@ */ #include -#include +#include #include #include #include @@ -26,368 +26,8 @@ namespace Opm { - /// Default constructor. - SaturationPropsFromDeck::SaturationPropsFromDeck() - { - } - - /// Initialize from deck. - void SaturationPropsFromDeck::init(const EclipseGridParser& deck, - const std::vector& global_cell) - { - phase_usage_ = phaseUsageFromDeck(deck); - - // Extract input data. - // Oil phase should be active. - if (!phase_usage_.phase_used[Liquid]) { - THROW("SaturationPropsFromDeck::init() -- oil phase must be active."); - } - - // Obtain SATNUM, if it exists, and create cell_to_func_. - // Otherwise, let the cell_to_func_ mapping be just empty. - int satfuncs_expected = 1; - if (deck.hasField("SATNUM")) { - const std::vector& satnum = deck.getIntegerValue("SATNUM"); - satfuncs_expected = *std::max_element(satnum.begin(), satnum.end()); - int num_cells = global_cell.size(); - cell_to_func_.resize(num_cells); - for (int cell = 0; cell < num_cells; ++cell) { - cell_to_func_[cell] = satnum[global_cell[cell]] - 1; - } - } - - // Find number of tables, check for consistency. - enum { Uninitialized = -1 }; - int num_tables = Uninitialized; - if (phase_usage_.phase_used[Aqua]) { - const SWOF::table_t& swof_table = deck.getSWOF().swof_; - num_tables = swof_table.size(); - if (num_tables < satfuncs_expected) { - THROW("Found " << num_tables << " SWOF tables, SATNUM specifies at least " << satfuncs_expected); - } - } - if (phase_usage_.phase_used[Vapour]) { - const SGOF::table_t& sgof_table = deck.getSGOF().sgof_; - int num_sgof_tables = sgof_table.size(); - if (num_sgof_tables < satfuncs_expected) { - THROW("Found " << num_tables << " SGOF tables, SATNUM specifies at least " << satfuncs_expected); - } - if (num_tables == Uninitialized) { - num_tables = num_sgof_tables; - } else if (num_tables != num_sgof_tables) { - THROW("Inconsistent number of tables in SWOF and SGOF."); - } - } - - // Initialize tables. - satfuncset_.resize(num_tables); - for (int table = 0; table < num_tables; ++table) { - satfuncset_[table].init(deck, table, phase_usage_); - } - } - - - - - /// \return P, the number of phases. - int SaturationPropsFromDeck::numPhases() const - { - return phase_usage_.num_phases; - } - - - - - /// Relative permeability. - /// \param[in] n Number of data points. - /// \param[in] s Array of nP saturation values. - /// \param[in] cells Array of n cell indices to be associated with the s values. - /// \param[out] kr Array of nP relperm values, array must be valid before calling. - /// \param[out] dkrds If non-null: array of nP^2 relperm derivative values, - /// array must be valid before calling. - /// The P^2 derivative matrix is - /// m_{ij} = \frac{dkr_i}{ds^j}, - /// and is output in Fortran order (m_00 m_10 m_20 m01 ...) - void SaturationPropsFromDeck::relperm(const int n, - const double* s, - const int* cells, - double* kr, - double* dkrds) const - { - ASSERT (cells != 0); - - const int np = phase_usage_.num_phases; - if (dkrds) { -// #pragma omp parallel for - for (int i = 0; i < n; ++i) { - funcForCell(cells[i]).evalKrDeriv(s + np*i, kr + np*i, dkrds + np*np*i); - } - } else { -// #pragma omp parallel for - for (int i = 0; i < n; ++i) { - funcForCell(cells[i]).evalKr(s + np*i, kr + np*i); - } - } - } - - - - - /// Capillary pressure. - /// \param[in] n Number of data points. - /// \param[in] s Array of nP saturation values. - /// \param[in] cells Array of n cell indices to be associated with the s values. - /// \param[out] pc Array of nP capillary pressure values, array must be valid before calling. - /// \param[out] dpcds If non-null: array of nP^2 derivative values, - /// array must be valid before calling. - /// The P^2 derivative matrix is - /// m_{ij} = \frac{dpc_i}{ds^j}, - /// and is output in Fortran order (m_00 m_10 m_20 m01 ...) - void SaturationPropsFromDeck::capPress(const int n, - const double* s, - const int* cells, - double* pc, - double* dpcds) const - { - ASSERT (cells != 0); - - const int np = phase_usage_.num_phases; - if (dpcds) { -// #pragma omp parallel for - for (int i = 0; i < n; ++i) { - funcForCell(cells[i]).evalPcDeriv(s + np*i, pc + np*i, dpcds + np*np*i); - } - } else { -// #pragma omp parallel for - for (int i = 0; i < n; ++i) { - funcForCell(cells[i]).evalPc(s + np*i, pc + np*i); - } - } - } - - - - - /// Obtain the range of allowable saturation values. - /// \param[in] n Number of data points. - /// \param[in] cells Array of n cell indices. - /// \param[out] smin Array of nP minimum s values, array must be valid before calling. - /// \param[out] smax Array of nP maximum s values, array must be valid before calling. - void SaturationPropsFromDeck::satRange(const int n, - const int* cells, - double* smin, - double* smax) const - { - ASSERT (cells != 0); - - const int np = phase_usage_.num_phases; - for (int i = 0; i < n; ++i) { - for (int p = 0; p < np; ++p) { - smin[np*i + p] = funcForCell(cells[i]).smin_[p]; - smax[np*i + p] = funcForCell(cells[i]).smax_[p]; - } - } - } - - - // Map the cell number to the correct function set. - const SaturationPropsFromDeck::SatFuncSet& - SaturationPropsFromDeck::funcForCell(const int cell) const - { - return cell_to_func_.empty() ? satfuncset_[0] : satfuncset_[cell_to_func_[cell]]; - } - - - - // ----------- Methods of SatFuncSet below ----------- - - - void SaturationPropsFromDeck::SatFuncSet::init(const EclipseGridParser& deck, - const int table_num, - const PhaseUsage phase_usg) - { - phase_usage = phase_usg; - const int samples = 200; - double swco = 0.0; - double swmax = 1.0; - if (phase_usage.phase_used[Aqua]) { - const SWOF::table_t& swof_table = deck.getSWOF().swof_; - const std::vector& sw = swof_table[table_num][0]; - const std::vector& krw = swof_table[table_num][1]; - const std::vector& krow = swof_table[table_num][2]; - const std::vector& pcow = swof_table[table_num][3]; - buildUniformMonotoneTable(sw, krw, samples, krw_); - buildUniformMonotoneTable(sw, krow, samples, krow_); - buildUniformMonotoneTable(sw, pcow, samples, pcow_); - krocw_ = krow[0]; // At connate water -> ecl. SWOF - swco = sw[0]; - smin_[phase_usage.phase_pos[Aqua]] = sw[0]; - swmax = sw.back(); - smax_[phase_usage.phase_pos[Aqua]] = sw.back(); - } - if (phase_usage.phase_used[Vapour]) { - const SGOF::table_t& sgof_table = deck.getSGOF().sgof_; - const std::vector& sg = sgof_table[table_num][0]; - const std::vector& krg = sgof_table[table_num][1]; - const std::vector& krog = sgof_table[table_num][2]; - const std::vector& pcog = sgof_table[table_num][3]; - buildUniformMonotoneTable(sg, krg, samples, krg_); - buildUniformMonotoneTable(sg, krog, samples, krog_); - buildUniformMonotoneTable(sg, pcog, samples, pcog_); - smin_[phase_usage.phase_pos[Vapour]] = sg[0]; - if (std::fabs(sg.back() + swco - 1.0) > 1e-3) { - THROW("Gas maximum saturation in SGOF table = " << sg.back() << - ", should equal (1.0 - connate water sat) = " << (1.0 - swco)); - } - smax_[phase_usage.phase_pos[Vapour]] = sg.back(); - } - // These only consider water min/max sats. Consider gas sats? - smin_[phase_usage.phase_pos[Liquid]] = 1.0 - swmax; - smax_[phase_usage.phase_pos[Liquid]] = 1.0 - swco; - } - - - void SaturationPropsFromDeck::SatFuncSet::evalKr(const double* s, double* kr) const - { - if (phase_usage.num_phases == 3) { - // Stone-II relative permeability model. - double sw = s[Aqua]; - double sg = s[Vapour]; - double krw = krw_(sw); - double krg = krg_(sg); - double krow = krow_(sw + sg); // = 1 - so - double krog = krog_(sg); // = 1 - so - sw - double krocw = krocw_; - kr[Aqua] = krw; - kr[Vapour] = krg; - kr[Liquid] = krocw*((krow/krocw + krw)*(krog/krocw + krg) - krw - krg); - if (kr[Liquid] < 0.0) { - kr[Liquid] = 0.0; - } - return; - } - // We have a two-phase situation. We know that oil is active. - if (phase_usage.phase_used[Aqua]) { - int wpos = phase_usage.phase_pos[Aqua]; - int opos = phase_usage.phase_pos[Liquid]; - double sw = s[wpos]; - double krw = krw_(sw); - double krow = krow_(sw); - kr[wpos] = krw; - kr[opos] = krow; - } else { - ASSERT(phase_usage.phase_used[Vapour]); - int gpos = phase_usage.phase_pos[Vapour]; - int opos = phase_usage.phase_pos[Liquid]; - double sg = s[gpos]; - double krg = krg_(sg); - double krog = krog_(sg); - kr[gpos] = krg; - kr[opos] = krog; - } - } - - - void SaturationPropsFromDeck::SatFuncSet::evalKrDeriv(const double* s, double* kr, double* dkrds) const - { - const int np = phase_usage.num_phases; - std::fill(dkrds, dkrds + np*np, 0.0); - - if (np == 3) { - // Stone-II relative permeability model. - double sw = s[Aqua]; - double sg = s[Vapour]; - double krw = krw_(sw); - double dkrww = krw_.derivative(sw); - double krg = krg_(sg); - double dkrgg = krg_.derivative(sg); - double krow = krow_(sw + sg); - double dkrow = krow_.derivative(sw + sg); - double krog = krog_(sg); - double dkrog = krog_.derivative(sg); - double krocw = krocw_; - kr[Aqua] = krw; - kr[Vapour] = krg; - kr[Liquid] = krocw*((krow/krocw + krw)*(krog/krocw + krg) - krw - krg); - if (kr[Liquid] < 0.0) { - kr[Liquid] = 0.0; - } - dkrds[Aqua + Aqua*np] = dkrww; - dkrds[Vapour + Vapour*np] = dkrgg; - dkrds[Liquid + Aqua*np] = krocw*((dkrow/krocw + dkrww)*(krog/krocw + krg) - dkrww); - dkrds[Liquid + Vapour*np] = krocw*((krow/krocw + krw)*(dkrog/krocw + dkrgg) - dkrgg) - + krocw*((dkrow/krocw + krw)*(krog/krocw + krg) - dkrgg); - return; - } - // We have a two-phase situation. We know that oil is active. - if (phase_usage.phase_used[Aqua]) { - int wpos = phase_usage.phase_pos[Aqua]; - int opos = phase_usage.phase_pos[Liquid]; - double sw = s[wpos]; - double krw = krw_(sw); - double dkrww = krw_.derivative(sw); - double krow = krow_(sw); - double dkrow = krow_.derivative(sw); - kr[wpos] = krw; - kr[opos] = krow; - dkrds[wpos + wpos*np] = dkrww; - dkrds[opos + wpos*np] = dkrow; // Row opos, column wpos, fortran order. - } else { - ASSERT(phase_usage.phase_used[Vapour]); - int gpos = phase_usage.phase_pos[Vapour]; - int opos = phase_usage.phase_pos[Liquid]; - double sg = s[gpos]; - double krg = krg_(sg); - double dkrgg = krg_.derivative(sg); - double krog = krog_(sg); - double dkrog = krog_.derivative(sg); - kr[gpos] = krg; - kr[opos] = krog; - dkrds[gpos + gpos*np] = dkrgg; - dkrds[opos + gpos*np] = dkrog; - } - - } - - - void SaturationPropsFromDeck::SatFuncSet::evalPc(const double* s, double* pc) const - { - pc[phase_usage.phase_pos[Liquid]] = 0.0; - if (phase_usage.phase_used[Aqua]) { - int pos = phase_usage.phase_pos[Aqua]; - pc[pos] = pcow_(s[pos]); - } - if (phase_usage.phase_used[Vapour]) { - int pos = phase_usage.phase_pos[Vapour]; - pc[pos] = pcog_(s[pos]); - } - } - - void SaturationPropsFromDeck::SatFuncSet::evalPcDeriv(const double* s, double* pc, double* dpcds) const - { - // The problem of determining three-phase capillary pressures - // is very hard experimentally, usually one extends two-phase - // data (as for relative permeability). - // In our approach the derivative matrix is quite sparse, only - // the diagonal elements corresponding to non-oil phases are - // (potentially) nonzero. - const int np = phase_usage.num_phases; - std::fill(dpcds, dpcds + np*np, 0.0); - pc[phase_usage.phase_pos[Liquid]] = 0.0; - if (phase_usage.phase_used[Aqua]) { - int pos = phase_usage.phase_pos[Aqua]; - pc[pos] = pcow_(s[pos]); - dpcds[np*pos + pos] = pcow_.derivative(s[pos]); - } - if (phase_usage.phase_used[Vapour]) { - int pos = phase_usage.phase_pos[Vapour]; - pc[pos] = pcog_(s[pos]); - dpcds[np*pos + pos] = pcog_.derivative(s[pos]); - } - } - - - + // This file should be removed in the future. + // Holding off until refactoring of SaturationPropsFromDeck class is done. } // namespace Opm diff --git a/opm/core/fluid/SaturationPropsFromDeck.hpp b/opm/core/fluid/SaturationPropsFromDeck.hpp index 7c8be8d4..0a0599ec 100644 --- a/opm/core/fluid/SaturationPropsFromDeck.hpp +++ b/opm/core/fluid/SaturationPropsFromDeck.hpp @@ -20,24 +20,44 @@ #ifndef OPM_SATURATIONPROPSFROMDECK_HEADER_INCLUDED #define OPM_SATURATIONPROPSFROMDECK_HEADER_INCLUDED +#include +#include #include -#include #include +#include +#include #include +struct UnstructuredGrid; + namespace Opm { - class SaturationPropsFromDeck : public BlackoilPhases + + + /// Interface to saturation functions from deck. + /// Possible values for template argument (for now): + /// SatFuncSetStone2Nonuniform, + /// SatFuncSetStone2Uniform. + /// SatFuncSetSimpleNonuniform, + /// SatFuncSetSimpleUniform. + template + class SaturationPropsFromDeck : public SaturationPropsInterface { public: /// Default constructor. SaturationPropsFromDeck(); - /// Initialize from deck. - /// global_cell maps from grid cells to their original logical Cartesian indices. + /// Initialize from deck and grid. + /// \param[in] deck Deck input parser + /// \param[in] grid Grid to which property object applies, needed for the + /// mapping from cell indices (typically from a processed grid) + /// to logical cartesian indices consistent with the deck. + /// \param[in] samples Number of uniform sample points for saturation tables. + /// NOTE: samples will only be used with the SatFuncSetUniform template argument. void init(const EclipseGridParser& deck, - const std::vector& global_cell); + const UnstructuredGrid& grid, + const int samples); /// \return P, the number of phases. int numPhases() const; @@ -72,41 +92,23 @@ namespace Opm double* pc, double* dpcds) const; - /// Obtain the range of allowable saturation values. + /// Obtain the range of allowable saturation values. /// \param[in] n Number of data points. /// \param[out] smin Array of nP minimum s values, array must be valid before calling. /// \param[out] smax Array of nP maximum s values, array must be valid before calling. - void satRange(const int n, + void satRange(const int n, const int* cells, - double* smin, - double* smax) const; + double* smin, + double* smax) const; private: PhaseUsage phase_usage_; - class SatFuncSet - { - public: - void init(const EclipseGridParser& deck, const int table_num, PhaseUsage phase_usg); - void evalKr(const double* s, double* kr) const; - void evalKrDeriv(const double* s, double* kr, double* dkrds) const; - void evalPc(const double* s, double* pc) const; - void evalPcDeriv(const double* s, double* pc, double* dpcds) const; - double smin_[PhaseUsage::MaxNumPhases]; - double smax_[PhaseUsage::MaxNumPhases]; - private: - PhaseUsage phase_usage; // A copy of the outer class' phase_usage_. - UniformTableLinear krw_; - UniformTableLinear krow_; - UniformTableLinear pcow_; - UniformTableLinear krg_; - UniformTableLinear krog_; - UniformTableLinear pcog_; - double krocw_; // = krow_(s_wc) - }; std::vector satfuncset_; std::vector cell_to_func_; // = SATNUM - 1 - const SatFuncSet& funcForCell(const int cell) const; + typedef SatFuncSet Funcs; + + const Funcs& funcForCell(const int cell) const; }; @@ -114,6 +116,7 @@ namespace Opm } // namespace Opm +#include #endif // OPM_SATURATIONPROPSFROMDECK_HEADER_INCLUDED diff --git a/opm/core/fluid/SaturationPropsFromDeck_impl.hpp b/opm/core/fluid/SaturationPropsFromDeck_impl.hpp new file mode 100644 index 00000000..f7a80b45 --- /dev/null +++ b/opm/core/fluid/SaturationPropsFromDeck_impl.hpp @@ -0,0 +1,221 @@ +/* + Copyright 2012 SINTEF ICT, Applied Mathematics. + + 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 . +*/ + +#ifndef OPM_SATURATIONPROPSFROMDECK_IMPL_HEADER_INCLUDED +#define OPM_SATURATIONPROPSFROMDECK_IMPL_HEADER_INCLUDED + + +#include +#include +#include +#include + +namespace Opm +{ + + + // ----------- Methods of SaturationPropsFromDeck --------- + + + /// Default constructor. + template + SaturationPropsFromDeck::SaturationPropsFromDeck() + { + } + + /// Initialize from deck. + template + void SaturationPropsFromDeck::init(const EclipseGridParser& deck, + const UnstructuredGrid& grid, + const int samples) + { + phase_usage_ = phaseUsageFromDeck(deck); + + // Extract input data. + // Oil phase should be active. + if (!phase_usage_.phase_used[Liquid]) { + THROW("SaturationPropsFromDeck::init() -- oil phase must be active."); + } + + // Obtain SATNUM, if it exists, and create cell_to_func_. + // Otherwise, let the cell_to_func_ mapping be just empty. + int satfuncs_expected = 1; + if (deck.hasField("SATNUM")) { + const std::vector& satnum = deck.getIntegerValue("SATNUM"); + satfuncs_expected = *std::max_element(satnum.begin(), satnum.end()); + const int num_cells = grid.number_of_cells; + cell_to_func_.resize(num_cells); + const int* gc = grid.global_cell; + for (int cell = 0; cell < num_cells; ++cell) { + const int deck_pos = (gc == NULL) ? cell : gc[cell]; + cell_to_func_[cell] = satnum[deck_pos] - 1; + } + } + + // Find number of tables, check for consistency. + enum { Uninitialized = -1 }; + int num_tables = Uninitialized; + if (phase_usage_.phase_used[Aqua]) { + const SWOF::table_t& swof_table = deck.getSWOF().swof_; + num_tables = swof_table.size(); + if (num_tables < satfuncs_expected) { + THROW("Found " << num_tables << " SWOF tables, SATNUM specifies at least " << satfuncs_expected); + } + } + if (phase_usage_.phase_used[Vapour]) { + const SGOF::table_t& sgof_table = deck.getSGOF().sgof_; + int num_sgof_tables = sgof_table.size(); + if (num_sgof_tables < satfuncs_expected) { + THROW("Found " << num_tables << " SGOF tables, SATNUM specifies at least " << satfuncs_expected); + } + if (num_tables == Uninitialized) { + num_tables = num_sgof_tables; + } else if (num_tables != num_sgof_tables) { + THROW("Inconsistent number of tables in SWOF and SGOF."); + } + } + + // Initialize tables. + satfuncset_.resize(num_tables); + for (int table = 0; table < num_tables; ++table) { + satfuncset_[table].init(deck, table, phase_usage_, samples); + } + } + + + + + /// \return P, the number of phases. + template + int SaturationPropsFromDeck::numPhases() const + { + return phase_usage_.num_phases; + } + + + + + /// Relative permeability. + /// \param[in] n Number of data points. + /// \param[in] s Array of nP saturation values. + /// \param[in] cells Array of n cell indices to be associated with the s values. + /// \param[out] kr Array of nP relperm values, array must be valid before calling. + /// \param[out] dkrds If non-null: array of nP^2 relperm derivative values, + /// array must be valid before calling. + /// The P^2 derivative matrix is + /// m_{ij} = \frac{dkr_i}{ds^j}, + /// and is output in Fortran order (m_00 m_10 m_20 m01 ...) + template + void SaturationPropsFromDeck::relperm(const int n, + const double* s, + const int* cells, + double* kr, + double* dkrds) const + { + ASSERT (cells != 0); + + const int np = phase_usage_.num_phases; + if (dkrds) { +// #pragma omp parallel for + for (int i = 0; i < n; ++i) { + funcForCell(cells[i]).evalKrDeriv(s + np*i, kr + np*i, dkrds + np*np*i); + } + } else { +// #pragma omp parallel for + for (int i = 0; i < n; ++i) { + funcForCell(cells[i]).evalKr(s + np*i, kr + np*i); + } + } + } + + + + + /// Capillary pressure. + /// \param[in] n Number of data points. + /// \param[in] s Array of nP saturation values. + /// \param[in] cells Array of n cell indices to be associated with the s values. + /// \param[out] pc Array of nP capillary pressure values, array must be valid before calling. + /// \param[out] dpcds If non-null: array of nP^2 derivative values, + /// array must be valid before calling. + /// The P^2 derivative matrix is + /// m_{ij} = \frac{dpc_i}{ds^j}, + /// and is output in Fortran order (m_00 m_10 m_20 m01 ...) + template + void SaturationPropsFromDeck::capPress(const int n, + const double* s, + const int* cells, + double* pc, + double* dpcds) const + { + ASSERT (cells != 0); + + const int np = phase_usage_.num_phases; + if (dpcds) { +// #pragma omp parallel for + for (int i = 0; i < n; ++i) { + funcForCell(cells[i]).evalPcDeriv(s + np*i, pc + np*i, dpcds + np*np*i); + } + } else { +// #pragma omp parallel for + for (int i = 0; i < n; ++i) { + funcForCell(cells[i]).evalPc(s + np*i, pc + np*i); + } + } + } + + + + + /// Obtain the range of allowable saturation values. + /// \param[in] n Number of data points. + /// \param[in] cells Array of n cell indices. + /// \param[out] smin Array of nP minimum s values, array must be valid before calling. + /// \param[out] smax Array of nP maximum s values, array must be valid before calling. + template + void SaturationPropsFromDeck::satRange(const int n, + const int* cells, + double* smin, + double* smax) const + { + ASSERT (cells != 0); + + const int np = phase_usage_.num_phases; + for (int i = 0; i < n; ++i) { + for (int p = 0; p < np; ++p) { + smin[np*i + p] = funcForCell(cells[i]).smin_[p]; + smax[np*i + p] = funcForCell(cells[i]).smax_[p]; + } + } + } + + + // Map the cell number to the correct function set. + template + const typename SaturationPropsFromDeck::Funcs& + SaturationPropsFromDeck::funcForCell(const int cell) const + { + return cell_to_func_.empty() ? satfuncset_[0] : satfuncset_[cell_to_func_[cell]]; + } + + + +} // namespace Opm + +#endif // OPM_SATURATIONPROPSFROMDECK_IMPL_HEADER_INCLUDED diff --git a/opm/core/fluid/SaturationPropsInterface.hpp b/opm/core/fluid/SaturationPropsInterface.hpp new file mode 100644 index 00000000..30f277cc --- /dev/null +++ b/opm/core/fluid/SaturationPropsInterface.hpp @@ -0,0 +1,85 @@ +/* + Copyright 2012 SINTEF ICT, Applied Mathematics. + + 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 . +*/ + +#ifndef OPM_SATURATIONPROPSINTERFACE_HEADER_INCLUDED +#define OPM_SATURATIONPROPSINTERFACE_HEADER_INCLUDED + +#include + + +namespace Opm +{ + + class SaturationPropsInterface : public BlackoilPhases + { + public: + /// Virtual destructor. + virtual ~SaturationPropsInterface() {}; + + /// \return P, the number of phases. + virtual int numPhases() const = 0; + + /// Relative permeability. + /// \param[in] n Number of data points. + /// \param[in] s Array of nP saturation values. + /// \param[out] kr Array of nP relperm values, array must be valid before calling. + /// \param[out] dkrds If non-null: array of nP^2 relperm derivative values, + /// array must be valid before calling. + /// The P^2 derivative matrix is + /// m_{ij} = \frac{dkr_i}{ds^j}, + /// and is output in Fortran order (m_00 m_10 m_20 m01 ...) + virtual void relperm(const int n, + const double* s, + const int* cells, + double* kr, + double* dkrds) const = 0; + + /// Capillary pressure. + /// \param[in] n Number of data points. + /// \param[in] s Array of nP saturation values. + /// \param[out] pc Array of nP capillary pressure values, array must be valid before calling. + /// \param[out] dpcds If non-null: array of nP^2 derivative values, + /// array must be valid before calling. + /// The P^2 derivative matrix is + /// m_{ij} = \frac{dpc_i}{ds^j}, + /// and is output in Fortran order (m_00 m_10 m_20 m01 ...) + virtual void capPress(const int n, + const double* s, + const int* cells, + double* pc, + double* dpcds) const = 0; + + /// Obtain the range of allowable saturation values. + /// \param[in] n Number of data points. + /// \param[out] smin Array of nP minimum s values, array must be valid before calling. + /// \param[out] smax Array of nP maximum s values, array must be valid before calling. + virtual void satRange(const int n, + const int* cells, + double* smin, + double* smax) const = 0; + + }; + + + +} // namespace Opm + + + +#endif // OPM_SATURATIONPROPSINTERFACE_HEADER_INCLUDED diff --git a/opm/core/fluid/blackoil/BlackoilPvtProperties.cpp b/opm/core/fluid/blackoil/BlackoilPvtProperties.cpp index 9fdaf445..4a0004ee 100644 --- a/opm/core/fluid/blackoil/BlackoilPvtProperties.cpp +++ b/opm/core/fluid/blackoil/BlackoilPvtProperties.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -39,18 +40,18 @@ namespace Opm } - void BlackoilPvtProperties::init(const EclipseGridParser& deck) + void BlackoilPvtProperties::init(const EclipseGridParser& deck, const int samples) { typedef std::vector > > table_t; // If we need multiple regions, this class and the SinglePvt* classes must change. - region_number_ = 0; + region_number_ = 0; phase_usage_ = phaseUsageFromDeck(deck); - // Surface densities. Accounting for different orders in eclipse and our code. - if (deck.hasField("DENSITY")) { - const std::vector& d = deck.getDENSITY().densities_[region_number_]; - enum { ECL_oil = 0, ECL_water = 1, ECL_gas = 2 }; + // Surface densities. Accounting for different orders in eclipse and our code. + if (deck.hasField("DENSITY")) { + const std::vector& d = deck.getDENSITY().densities_[region_number_]; + enum { ECL_oil = 0, ECL_water = 1, ECL_gas = 2 }; if (phase_usage_.phase_used[Aqua]) { densities_[phase_usage_.phase_pos[Aqua]] = d[ECL_water]; } @@ -60,9 +61,9 @@ namespace Opm if (phase_usage_.phase_used[Liquid]) { densities_[phase_usage_.phase_pos[Liquid]] = d[ECL_oil]; } - } else { - THROW("Input is missing DENSITY\n"); - } + } else { + THROW("Input is missing DENSITY\n"); + } // Set the properties. props_.resize(phase_usage_.num_phases); @@ -78,7 +79,11 @@ namespace Opm // Oil PVT if (phase_usage_.phase_used[Liquid]) { if (deck.hasField("PVDO")) { - props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDead(deck.getPVDO().pvdo_)); + if (samples > 0) { + props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDeadSpline(deck.getPVDO().pvdo_, samples)); + } else { + props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDead(deck.getPVDO().pvdo_)); + } } else if (deck.hasField("PVTO")) { props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtLiveOil(deck.getPVTO().pvto_)); } else if (deck.hasField("PVCDO")) { @@ -87,10 +92,14 @@ namespace Opm THROW("Input is missing PVDO or PVTO\n"); } } - // Gas PVT + // Gas PVT if (phase_usage_.phase_used[Vapour]) { if (deck.hasField("PVDG")) { - props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDead(deck.getPVDG().pvdg_)); + if (samples > 0) { + props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDeadSpline(deck.getPVDG().pvdg_, samples)); + } else { + props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDead(deck.getPVDG().pvdg_)); + } } else if (deck.hasField("PVTG")) { props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtLiveGas(deck.getPVTG().pvtg_)); } else { diff --git a/opm/core/fluid/blackoil/BlackoilPvtProperties.hpp b/opm/core/fluid/blackoil/BlackoilPvtProperties.hpp index 627f266b..8633fd9e 100644 --- a/opm/core/fluid/blackoil/BlackoilPvtProperties.hpp +++ b/opm/core/fluid/blackoil/BlackoilPvtProperties.hpp @@ -47,7 +47,13 @@ namespace Opm BlackoilPvtProperties(); /// Initialize from deck. - void init(const EclipseGridParser& deck); + /// \param deck An input deck. + /// \param samples If greater than zero, indicates the number of + /// uniform samples to be taken from monotone spline + /// curves interpolating the fluid data. + /// Otherwise, interpolate linearly in the original + /// data without fitting a spline. + void init(const EclipseGridParser& deck, const int samples); /// Number of active phases. int numPhases() const; @@ -64,7 +70,7 @@ namespace Opm /// Densities of stock components at surface conditions. /// \return Array of size numPhases(). - const double* surfaceDensities() const; + const double* surfaceDensities() const; /// Viscosity as a function of p and z. void mu(const int n, @@ -105,11 +111,11 @@ namespace Opm PhaseUsage phase_usage_; - int region_number_; + int region_number_; std::vector > props_; - double densities_[MaxNumPhases]; + double densities_[MaxNumPhases]; mutable std::vector data1_; mutable std::vector data2_; }; diff --git a/opm/core/fluid/blackoil/SinglePvtConstCompr.hpp b/opm/core/fluid/blackoil/SinglePvtConstCompr.hpp index 1bc5638f..9226d0d8 100644 --- a/opm/core/fluid/blackoil/SinglePvtConstCompr.hpp +++ b/opm/core/fluid/blackoil/SinglePvtConstCompr.hpp @@ -40,12 +40,12 @@ namespace Opm public: typedef std::vector > table_t; - SinglePvtConstCompr(const table_t& pvtw) + SinglePvtConstCompr(const table_t& pvtw) { - const int region_number = 0; - if (pvtw.size() != 1) { - THROW("More than one PVD-region"); - } + const int region_number = 0; + if (pvtw.size() != 1) { + THROW("More than one PVD-region"); + } ref_press_ = pvtw[region_number][0]; ref_B_ = pvtw[region_number][1]; comp_ = pvtw[region_number][2]; @@ -53,7 +53,7 @@ namespace Opm visc_comp_ = pvtw[region_number][4]; } - SinglePvtConstCompr(double visc) + SinglePvtConstCompr(double visc) : ref_press_(0.0), ref_B_(1.0), comp_(0.0), @@ -62,7 +62,7 @@ namespace Opm { } - virtual ~SinglePvtConstCompr() + virtual ~SinglePvtConstCompr() { } @@ -106,13 +106,16 @@ namespace Opm double* output_B, double* output_dBdp) const { - B(n, p, 0, output_B); if (comp_) { // #pragma omp parallel for for (int i = 0; i < n; ++i) { - output_dBdp[i] = -comp_*output_B[i]; + double x = comp_*(p[i] - ref_press_); + double d = (1.0 + x + 0.5*x*x); + output_B[i] = ref_B_/d; + output_dBdp[i] = (-ref_B_/(d*d))*(1 + x) * comp_; } } else { + std::fill(output_B, output_B + n, ref_B_); std::fill(output_dBdp, output_dBdp + n, 0.0); } } diff --git a/opm/core/fluid/blackoil/SinglePvtDead.cpp b/opm/core/fluid/blackoil/SinglePvtDead.cpp index 3d1a2740..34aeaf6e 100644 --- a/opm/core/fluid/blackoil/SinglePvtDead.cpp +++ b/opm/core/fluid/blackoil/SinglePvtDead.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010, 2011, 2012 SINTEF ICT, Applied Mathematics. + Copyright 2012 SINTEF ICT, Applied Mathematics. This file is part of the Open Porous Media project (OPM). @@ -17,8 +17,8 @@ along with OPM. If not, see . */ + #include -#include #include // Extra includes for debug dumping of tables. @@ -32,28 +32,26 @@ namespace Opm //------------------------------------------------------------------------ // Member functions //------------------------------------------------------------------------- - /// Constructor SinglePvtDead::SinglePvtDead(const table_t& pvd_table) { - const int region_number = 0; - if (pvd_table.size() != 1) { - THROW("More than one PVT-region"); - } + const int region_number = 0; + if (pvd_table.size() != 1) { + THROW("More than one PVT-region"); + } - // Copy data - const int sz = pvd_table[region_number][0].size(); + // Copy data + const int sz = pvd_table[region_number][0].size(); std::vector press(sz); std::vector B_inv(sz); std::vector visc(sz); - for (int i = 0; i < sz; ++i) { + for (int i = 0; i < sz; ++i) { press[i] = pvd_table[region_number][0][i]; B_inv[i] = 1.0 / pvd_table[region_number][1][i]; visc[i] = pvd_table[region_number][2][i]; - } - int samples = 1025; - buildUniformMonotoneTable(press, B_inv, samples, one_over_B_); - buildUniformMonotoneTable(press, visc, samples, viscosity_); + } + one_over_B_ = NonuniformTableLinear(press, B_inv); + viscosity_ = NonuniformTableLinear(press, visc); // Dumping the created tables. // static int count = 0; diff --git a/opm/core/fluid/blackoil/SinglePvtDead.hpp b/opm/core/fluid/blackoil/SinglePvtDead.hpp index 137e2e30..db86b39d 100644 --- a/opm/core/fluid/blackoil/SinglePvtDead.hpp +++ b/opm/core/fluid/blackoil/SinglePvtDead.hpp @@ -1,5 +1,5 @@ /* - Copyright 2010, 2011, 2012 SINTEF ICT, Applied Mathematics. + Copyright 2012 SINTEF ICT, Applied Mathematics. This file is part of the Open Porous Media project (OPM). @@ -22,7 +22,7 @@ #include -#include +#include #include namespace Opm @@ -36,10 +36,9 @@ namespace Opm class SinglePvtDead : public SinglePvtInterface { public: - typedef std::vector > > table_t; - - SinglePvtDead(const table_t& pvd_table); - virtual ~SinglePvtDead(); + typedef std::vector > > table_t; + SinglePvtDead(const table_t& pvd_table); + virtual ~SinglePvtDead(); /// Viscosity as a function of p and z. virtual void mu(const int n, @@ -73,12 +72,12 @@ namespace Opm double* output_R, double* output_dRdp) const; private: - // PVT properties of dry gas or dead oil - UniformTableLinear one_over_B_; - UniformTableLinear viscosity_; + // PVT properties of dry gas or dead oil + NonuniformTableLinear one_over_B_; + NonuniformTableLinear viscosity_; }; } -#endif // OPM_SINGLEPVTDEAD_HEADER_INCLUDED +#endif // OPM_SINGLEPVTDEAD_HEADER_INCLUDED diff --git a/opm/core/fluid/blackoil/SinglePvtDeadSpline.cpp b/opm/core/fluid/blackoil/SinglePvtDeadSpline.cpp new file mode 100644 index 00000000..132ffa51 --- /dev/null +++ b/opm/core/fluid/blackoil/SinglePvtDeadSpline.cpp @@ -0,0 +1,127 @@ +/* + Copyright 2010, 2011, 2012 SINTEF ICT, Applied Mathematics. + + 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 . +*/ + +#include +#include +#include + +// Extra includes for debug dumping of tables. +// #include +// #include +// #include + +namespace Opm +{ + + //------------------------------------------------------------------------ + // Member functions + //------------------------------------------------------------------------- + + /// Constructor + SinglePvtDeadSpline::SinglePvtDeadSpline(const table_t& pvd_table, const int samples) + { + const int region_number = 0; + if (pvd_table.size() != 1) { + THROW("More than one PVT-region"); + } + + // Copy data + const int sz = pvd_table[region_number][0].size(); + std::vector press(sz); + std::vector B_inv(sz); + std::vector visc(sz); + for (int i = 0; i < sz; ++i) { + press[i] = pvd_table[region_number][0][i]; + B_inv[i] = 1.0 / pvd_table[region_number][1][i]; + visc[i] = pvd_table[region_number][2][i]; + } + buildUniformMonotoneTable(press, B_inv, samples, one_over_B_); + buildUniformMonotoneTable(press, visc, samples, viscosity_); + + // Dumping the created tables. +// static int count = 0; +// std::ofstream os((std::string("dump-") + boost::lexical_cast(count++)).c_str()); +// os.precision(15); +// os << "1/B\n\n" << one_over_B_ +// << "\n\nvisc\n\n" << viscosity_ << std::endl; + } + + // Destructor + SinglePvtDeadSpline::~SinglePvtDeadSpline() + { + } + + + + void SinglePvtDeadSpline::mu(const int n, + const double* p, + const double* /*z*/, + double* output_mu) const + { +// #pragma omp parallel for + for (int i = 0; i < n; ++i) { + output_mu[i] = viscosity_(p[i]); + } + } + + void SinglePvtDeadSpline::B(const int n, + const double* p, + const double* /*z*/, + double* output_B) const + { +// #pragma omp parallel for + for (int i = 0; i < n; ++i) { + output_B[i] = 1.0/one_over_B_(p[i]); + } + } + + void SinglePvtDeadSpline::dBdp(const int n, + const double* p, + const double* /*z*/, + double* output_B, + double* output_dBdp) const + { + B(n, p, 0, output_B); +// #pragma omp parallel for + for (int i = 0; i < n; ++i) { + double Bg = output_B[i]; + output_dBdp[i] = -Bg*Bg*one_over_B_.derivative(p[i]); + } + } + + + void SinglePvtDeadSpline::R(const int n, + const double* /*p*/, + const double* /*z*/, + double* output_R) const + { + std::fill(output_R, output_R + n, 0.0); + } + + void SinglePvtDeadSpline::dRdp(const int n, + const double* /*p*/, + const double* /*z*/, + double* output_R, + double* output_dRdp) const + { + std::fill(output_R, output_R + n, 0.0); + std::fill(output_dRdp, output_dRdp + n, 0.0); + } + +} diff --git a/opm/core/fluid/blackoil/SinglePvtDeadSpline.hpp b/opm/core/fluid/blackoil/SinglePvtDeadSpline.hpp new file mode 100644 index 00000000..18d7ecfe --- /dev/null +++ b/opm/core/fluid/blackoil/SinglePvtDeadSpline.hpp @@ -0,0 +1,84 @@ +/* + Copyright 2010, 2011, 2012 SINTEF ICT, Applied Mathematics. + + 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 . +*/ + +#ifndef OPM_SINGLEPVTDEADSPLINE_HEADER_INCLUDED +#define OPM_SINGLEPVTDEADSPLINE_HEADER_INCLUDED + + +#include +#include +#include + +namespace Opm +{ + + /// Class for immiscible dead oil and dry gas. + /// For all the virtual methods, the following apply: p and z + /// are expected to be of size n and n*num_phases, respectively. + /// Output arrays shall be of size n, and must be valid before + /// calling the method. + class SinglePvtDeadSpline : public SinglePvtInterface + { + public: + typedef std::vector > > table_t; + + SinglePvtDeadSpline(const table_t& pvd_table, const int samples); + virtual ~SinglePvtDeadSpline(); + + /// Viscosity as a function of p and z. + virtual void mu(const int n, + const double* p, + const double* z, + double* output_mu) const; + + /// Formation volume factor as a function of p and z. + virtual void B(const int n, + const double* p, + const double* z, + double* output_B) const; + + /// Formation volume factor and p-derivative as functions of p and z. + virtual void dBdp(const int n, + const double* p, + const double* z, + double* output_B, + double* output_dBdp) const; + + /// Solution factor as a function of p and z. + virtual void R(const int n, + const double* p, + const double* z, + double* output_R) const; + + /// Solution factor and p-derivative as functions of p and z. + virtual void dRdp(const int n, + const double* p, + const double* z, + double* output_R, + double* output_dRdp) const; + private: + // PVT properties of dry gas or dead oil + UniformTableLinear one_over_B_; + UniformTableLinear viscosity_; + }; + +} + +#endif // OPM_SINGLEPVTDEADSPLINE_HEADER_INCLUDED + diff --git a/opm/core/fluid/blackoil/SinglePvtInterface.hpp b/opm/core/fluid/blackoil/SinglePvtInterface.hpp index d9369d33..7bcbd44b 100644 --- a/opm/core/fluid/blackoil/SinglePvtInterface.hpp +++ b/opm/core/fluid/blackoil/SinglePvtInterface.hpp @@ -32,7 +32,7 @@ namespace Opm public: SinglePvtInterface(); - virtual ~SinglePvtInterface(); + virtual ~SinglePvtInterface(); /// \param[in] num_phases The number of active phases. /// \param[in] phase_pos Array of BlackpoilPhases::MaxNumPhases diff --git a/opm/core/fluid/blackoil/SinglePvtLiveGas.cpp b/opm/core/fluid/blackoil/SinglePvtLiveGas.cpp index 2526d34c..a6785467 100644 --- a/opm/core/fluid/blackoil/SinglePvtLiveGas.cpp +++ b/opm/core/fluid/blackoil/SinglePvtLiveGas.cpp @@ -1,13 +1,13 @@ //=========================================================================== -// -// File: MiscibilityLiveGas.cpp -// -// Created: Wed Feb 10 09:21:53 2010 -// +// +// File: MiscibilityLiveGas.cpp +// +// Created: Wed Feb 10 09:21:53 2010 +// // Author: Bjørn Spjelkavik -// +// // Revision: $Id$ -// +// //=========================================================================== /* Copyright 2010 SINTEF ICT, Applied Mathematics. @@ -47,37 +47,37 @@ namespace Opm /// Constructor SinglePvtLiveGas::SinglePvtLiveGas(const table_t& pvtg) { - // GAS, PVTG - const int region_number = 0; - if (pvtg.size() != 1) { - THROW("More than one PVD-region"); - } - saturated_gas_table_.resize(4); - const int sz = pvtg[region_number].size(); - for (int k=0; k<4; ++k) { - saturated_gas_table_[k].resize(sz); - } + // GAS, PVTG + const int region_number = 0; + if (pvtg.size() != 1) { + THROW("More than one PVD-region"); + } + saturated_gas_table_.resize(4); + const int sz = pvtg[region_number].size(); + for (int k=0; k<4; ++k) { + saturated_gas_table_[k].resize(sz); + } - for (int i=0; i saturated_gas_table_[0][ltp]) { - return linearInterpolationExtrap(undersat_gas_tables_[ltp][0], - undersat_gas_tables_[ltp][item], - maxR); - } + // Extrapolate from last table section + int ltp = saturated_gas_table_[0].size() - 1; + if (is+1 == ltp && press > saturated_gas_table_[0][ltp]) { + return linearInterpolationExtrap(undersat_gas_tables_[ltp][0], + undersat_gas_tables_[ltp][item], + maxR); + } - // Interpolate between table sections - double w = (press - saturated_gas_table_[0][is]) / - (saturated_gas_table_[0][is+1] - - saturated_gas_table_[0][is]); - if (undersat_gas_tables_[is][0].size() < 2) { - double val = saturated_gas_table_[item][is] + - w*(saturated_gas_table_[item][is+1] - - saturated_gas_table_[item][is]); - return val; - } - double val1 = - linearInterpolationExtrap(undersat_gas_tables_[is][0], - undersat_gas_tables_[is][item], - maxR); - double val2 = - linearInterpolationExtrap(undersat_gas_tables_[is+1][0], - undersat_gas_tables_[is+1][item], - maxR); - double val = val1 + w*(val2 - val1); - return val; - } - } + // Interpolate between table sections + double w = (press - saturated_gas_table_[0][is]) / + (saturated_gas_table_[0][is+1] - + saturated_gas_table_[0][is]); + if (undersat_gas_tables_[is][0].size() < 2) { + double val = saturated_gas_table_[item][is] + + w*(saturated_gas_table_[item][is+1] - + saturated_gas_table_[item][is]); + return val; + } + double val1 = + linearInterpolationExtrap(undersat_gas_tables_[is][0], + undersat_gas_tables_[is][item], + maxR); + double val2 = + linearInterpolationExtrap(undersat_gas_tables_[is+1][0], + undersat_gas_tables_[is+1][item], + maxR); + double val = val1 + w*(val2 - val1); + return val; + } + } } diff --git a/opm/core/fluid/blackoil/SinglePvtLiveGas.hpp b/opm/core/fluid/blackoil/SinglePvtLiveGas.hpp index 3eff52f4..c7761551 100644 --- a/opm/core/fluid/blackoil/SinglePvtLiveGas.hpp +++ b/opm/core/fluid/blackoil/SinglePvtLiveGas.hpp @@ -33,10 +33,10 @@ namespace Opm class SinglePvtLiveGas : public SinglePvtInterface { public: - typedef std::vector > > table_t; + typedef std::vector > > table_t; - SinglePvtLiveGas(const table_t& pvto); - virtual ~SinglePvtLiveGas(); + SinglePvtLiveGas(const table_t& pvto); + virtual ~SinglePvtLiveGas(); /// Viscosity as a function of p and z. virtual void mu(const int n, @@ -76,14 +76,14 @@ namespace Opm double evalR(double press, const double* surfvol) const; void evalRDeriv(double press, const double* surfvol, double& R, double& dRdp) const; - // item: 1=>B 2=>mu; - double miscible_gas(const double press, + // item: 1=>B 2=>mu; + double miscible_gas(const double press, const double* surfvol, const int item, - const bool deriv = false) const; - // PVT properties of wet gas (with vaporised oil) - std::vector > saturated_gas_table_; - std::vector > > undersat_gas_tables_; + const bool deriv = false) const; + // PVT properties of wet gas (with vaporised oil) + std::vector > saturated_gas_table_; + std::vector > > undersat_gas_tables_; }; diff --git a/opm/core/fluid/blackoil/SinglePvtLiveOil.cpp b/opm/core/fluid/blackoil/SinglePvtLiveOil.cpp index 2a393839..990ae114 100644 --- a/opm/core/fluid/blackoil/SinglePvtLiveOil.cpp +++ b/opm/core/fluid/blackoil/SinglePvtLiveOil.cpp @@ -38,122 +38,122 @@ namespace Opm /// Constructor SinglePvtLiveOil::SinglePvtLiveOil(const table_t& pvto) { - // OIL, PVTO - const int region_number = 0; - if (pvto.size() != 1) { - THROW("More than one PVD-region"); - } - saturated_oil_table_.resize(4); - const int sz = pvto[region_number].size(); - for (int k=0; k<4; ++k) { - saturated_oil_table_[k].resize(sz); - } - for (int i=0; i 1) { - iPrev = i; - continue; - } - - bool flagPrev = (iPrev >= 0); - bool flagNext = true; - if (iNext < i) { - iPrev = iNext; - flagPrev = true; - iNext = i+1; - while (undersat_oil_tables_[iNext][0].size() < 2) { - ++iNext; - } - } - double slopePrevBinv = 0.0; - double slopePrevVisc = 0.0; - double slopeNextBinv = 0.0; - double slopeNextVisc = 0.0; - while (flagPrev || flagNext) { - double pressure0 = undersat_oil_tables_[i][0].back(); - double pressure = 1.0e47; - if (flagPrev) { - std::vector::iterator itPrev = upper_bound(undersat_oil_tables_[iPrev][0].begin(), - undersat_oil_tables_[iPrev][0].end(),pressure0+1.); - if (itPrev == undersat_oil_tables_[iPrev][0].end()) { - --itPrev; // Extrapolation ... - } else if (itPrev == undersat_oil_tables_[iPrev][0].begin()) { - ++itPrev; - } - if (itPrev == undersat_oil_tables_[iPrev][0].end()-1) { - flagPrev = false; // Last data set for "prev" ... - } - double dPPrev = *itPrev - *(itPrev-1); - pressure = *itPrev; - int index = int(itPrev - undersat_oil_tables_[iPrev][0].begin()); - slopePrevBinv = (undersat_oil_tables_[iPrev][1][index] - undersat_oil_tables_[iPrev][1][index-1])/dPPrev; - slopePrevVisc = (undersat_oil_tables_[iPrev][2][index] - undersat_oil_tables_[iPrev][2][index-1])/dPPrev; - } - if (flagNext) { - std::vector::iterator itNext = upper_bound(undersat_oil_tables_[iNext][0].begin(), - undersat_oil_tables_[iNext][0].end(),pressure0+1.); - if (itNext == undersat_oil_tables_[iNext][0].end()) { - --itNext; // Extrapolation ... - } else if (itNext == undersat_oil_tables_[iNext][0].begin()) { - ++itNext; - } - if (itNext == undersat_oil_tables_[iNext][0].end()-1) { - flagNext = false; // Last data set for "next" ... - } - double dPNext = *itNext - *(itNext-1); - if (flagPrev) { - pressure = std::min(pressure,*itNext); - } else { - pressure = *itNext; - } - int index = int(itNext - undersat_oil_tables_[iNext][0].begin()); - slopeNextBinv = (undersat_oil_tables_[iNext][1][index] - undersat_oil_tables_[iNext][1][index-1])/dPNext; - slopeNextVisc = (undersat_oil_tables_[iNext][2][index] - undersat_oil_tables_[iNext][2][index-1])/dPNext; - } - double dP = pressure - pressure0; - if (iPrev >= 0) { - double w = (saturated_oil_table_[3][i] - saturated_oil_table_[3][iPrev]) / - (saturated_oil_table_[3][iNext] - saturated_oil_table_[3][iPrev]); - undersat_oil_tables_[i][0].push_back(pressure0+dP); - undersat_oil_tables_[i][1].push_back(undersat_oil_tables_[i][1].back() + - dP*(slopePrevBinv+w*(slopeNextBinv-slopePrevBinv))); - undersat_oil_tables_[i][2].push_back(undersat_oil_tables_[i][2].back() + - dP*(slopePrevVisc+w*(slopeNextVisc-slopePrevVisc))); - } else { - undersat_oil_tables_[i][0].push_back(pressure0+dP); - undersat_oil_tables_[i][1].push_back(undersat_oil_tables_[i][1].back()+dP*slopeNextBinv); - undersat_oil_tables_[i][2].push_back(undersat_oil_tables_[i][2].back()+dP*slopeNextVisc); - } - } - } + // OIL, PVTO + const int region_number = 0; + if (pvto.size() != 1) { + THROW("More than one PVD-region"); + } + saturated_oil_table_.resize(4); + const int sz = pvto[region_number].size(); + for (int k=0; k<4; ++k) { + saturated_oil_table_[k].resize(sz); + } + for (int i=0; i 1) { + iPrev = i; + continue; + } + + bool flagPrev = (iPrev >= 0); + bool flagNext = true; + if (iNext < i) { + iPrev = iNext; + flagPrev = true; + iNext = i+1; + while (undersat_oil_tables_[iNext][0].size() < 2) { + ++iNext; + } + } + double slopePrevBinv = 0.0; + double slopePrevVisc = 0.0; + double slopeNextBinv = 0.0; + double slopeNextVisc = 0.0; + while (flagPrev || flagNext) { + double pressure0 = undersat_oil_tables_[i][0].back(); + double pressure = 1.0e47; + if (flagPrev) { + std::vector::iterator itPrev = upper_bound(undersat_oil_tables_[iPrev][0].begin(), + undersat_oil_tables_[iPrev][0].end(),pressure0+1.); + if (itPrev == undersat_oil_tables_[iPrev][0].end()) { + --itPrev; // Extrapolation ... + } else if (itPrev == undersat_oil_tables_[iPrev][0].begin()) { + ++itPrev; + } + if (itPrev == undersat_oil_tables_[iPrev][0].end()-1) { + flagPrev = false; // Last data set for "prev" ... + } + double dPPrev = *itPrev - *(itPrev-1); + pressure = *itPrev; + int index = int(itPrev - undersat_oil_tables_[iPrev][0].begin()); + slopePrevBinv = (undersat_oil_tables_[iPrev][1][index] - undersat_oil_tables_[iPrev][1][index-1])/dPPrev; + slopePrevVisc = (undersat_oil_tables_[iPrev][2][index] - undersat_oil_tables_[iPrev][2][index-1])/dPPrev; + } + if (flagNext) { + std::vector::iterator itNext = upper_bound(undersat_oil_tables_[iNext][0].begin(), + undersat_oil_tables_[iNext][0].end(),pressure0+1.); + if (itNext == undersat_oil_tables_[iNext][0].end()) { + --itNext; // Extrapolation ... + } else if (itNext == undersat_oil_tables_[iNext][0].begin()) { + ++itNext; + } + if (itNext == undersat_oil_tables_[iNext][0].end()-1) { + flagNext = false; // Last data set for "next" ... + } + double dPNext = *itNext - *(itNext-1); + if (flagPrev) { + pressure = std::min(pressure,*itNext); + } else { + pressure = *itNext; + } + int index = int(itNext - undersat_oil_tables_[iNext][0].begin()); + slopeNextBinv = (undersat_oil_tables_[iNext][1][index] - undersat_oil_tables_[iNext][1][index-1])/dPNext; + slopeNextVisc = (undersat_oil_tables_[iNext][2][index] - undersat_oil_tables_[iNext][2][index-1])/dPNext; + } + double dP = pressure - pressure0; + if (iPrev >= 0) { + double w = (saturated_oil_table_[3][i] - saturated_oil_table_[3][iPrev]) / + (saturated_oil_table_[3][iNext] - saturated_oil_table_[3][iPrev]); + undersat_oil_tables_[i][0].push_back(pressure0+dP); + undersat_oil_tables_[i][1].push_back(undersat_oil_tables_[i][1].back() + + dP*(slopePrevBinv+w*(slopeNextBinv-slopePrevBinv))); + undersat_oil_tables_[i][2].push_back(undersat_oil_tables_[i][2].back() + + dP*(slopePrevVisc+w*(slopeNextVisc-slopePrevVisc))); + } else { + undersat_oil_tables_[i][0].push_back(pressure0+dP); + undersat_oil_tables_[i][1].push_back(undersat_oil_tables_[i][1].back()+dP*slopeNextBinv); + undersat_oil_tables_[i][2].push_back(undersat_oil_tables_[i][2].back()+dP*slopeNextVisc); + } + } + } } /// Destructor. @@ -238,30 +238,30 @@ namespace Opm double SinglePvtLiveOil::evalB(double press, const double* surfvol) const { // if (surfvol[phase_pos_[Liquid]] == 0.0) return 1.0; // To handle no-oil case. - return 1.0/miscible_oil(press, surfvol, 1, false); + return 1.0/miscible_oil(press, surfvol, 1, false); } void SinglePvtLiveOil::evalBDeriv(const double press, const double* surfvol, double& Bval, double& dBdpval) const { - Bval = evalB(press, surfvol); - dBdpval = -Bval*Bval*miscible_oil(press, surfvol, 1, true); + Bval = evalB(press, surfvol); + dBdpval = -Bval*Bval*miscible_oil(press, surfvol, 1, true); } double SinglePvtLiveOil::evalR(double press, const double* surfvol) const { if (surfvol[phase_pos_[Vapour]] == 0.0) { return 0.0; - } - double Rval = linearInterpolationExtrap(saturated_oil_table_[0], - saturated_oil_table_[3], press); - double maxR = surfvol[phase_pos_[Vapour]]/surfvol[phase_pos_[Liquid]]; - if (Rval < maxR ) { // Saturated case - return Rval; - } else { - return maxR; // Undersaturated case - } + } + double Rval = linearInterpolationExtrap(saturated_oil_table_[0], + saturated_oil_table_[3], press); + double maxR = surfvol[phase_pos_[Vapour]]/surfvol[phase_pos_[Liquid]]; + if (Rval < maxR ) { // Saturated case + return Rval; + } else { + return maxR; // Undersaturated case + } } void SinglePvtLiveOil::evalRDeriv(const double press, const double* surfvol, @@ -272,19 +272,19 @@ namespace Opm dRdpval = 0.0; return; } - Rval = linearInterpolationExtrap(saturated_oil_table_[0], + Rval = linearInterpolationExtrap(saturated_oil_table_[0], saturated_oil_table_[3], press); - double maxR = surfvol[phase_pos_[Vapour]]/surfvol[phase_pos_[Liquid]]; - if (Rval < maxR ) { + double maxR = surfvol[phase_pos_[Vapour]]/surfvol[phase_pos_[Liquid]]; + if (Rval < maxR ) { // Saturated case - dRdpval = linearInterpolDerivative(saturated_oil_table_[0], - saturated_oil_table_[3], - press); - } else { + dRdpval = linearInterpolDerivative(saturated_oil_table_[0], + saturated_oil_table_[3], + press); + } else { // Undersaturated case Rval = maxR; - dRdpval = 0.0; - } + dRdpval = 0.0; + } } @@ -293,57 +293,57 @@ namespace Opm const int item, const bool deriv) const { - int section; - double Rval = linearInterpolationExtrap(saturated_oil_table_[0], + int section; + double Rval = linearInterpolationExtrap(saturated_oil_table_[0], saturated_oil_table_[3], press, section); - double maxR = (surfvol[phase_pos_[Liquid]] == 0.0) ? 0.0 : surfvol[phase_pos_[Vapour]]/surfvol[phase_pos_[Liquid]]; - if (deriv) { - if (Rval < maxR ) { // Saturated case - return linearInterpolDerivative(saturated_oil_table_[0], - saturated_oil_table_[item], - press); - } else { // Undersaturated case - int is = tableIndex(saturated_oil_table_[3], maxR); - double w = (maxR - saturated_oil_table_[3][is]) / - (saturated_oil_table_[3][is+1] - saturated_oil_table_[3][is]); - ASSERT(undersat_oil_tables_[is][0].size() >= 2); - ASSERT(undersat_oil_tables_[is+1][0].size() >= 2); - double val1 = - linearInterpolDerivative(undersat_oil_tables_[is][0], - undersat_oil_tables_[is][item], - press); - double val2 = - linearInterpolDerivative(undersat_oil_tables_[is+1][0], - undersat_oil_tables_[is+1][item], - press); - double val = val1 + w*(val2 - val1); - return val; - } - } else { - if (Rval < maxR ) { // Saturated case - return linearInterpolationExtrap(saturated_oil_table_[0], - saturated_oil_table_[item], - press); - } else { // Undersaturated case - // Interpolate between table sections + double maxR = (surfvol[phase_pos_[Liquid]] == 0.0) ? 0.0 : surfvol[phase_pos_[Vapour]]/surfvol[phase_pos_[Liquid]]; + if (deriv) { + if (Rval < maxR ) { // Saturated case + return linearInterpolDerivative(saturated_oil_table_[0], + saturated_oil_table_[item], + press); + } else { // Undersaturated case int is = tableIndex(saturated_oil_table_[3], maxR); - double w = (maxR - saturated_oil_table_[3][is]) / - (saturated_oil_table_[3][is+1] - saturated_oil_table_[3][is]); + double w = (maxR - saturated_oil_table_[3][is]) / + (saturated_oil_table_[3][is+1] - saturated_oil_table_[3][is]); ASSERT(undersat_oil_tables_[is][0].size() >= 2); ASSERT(undersat_oil_tables_[is+1][0].size() >= 2); - double val1 = - linearInterpolationExtrap(undersat_oil_tables_[is][0], - undersat_oil_tables_[is][item], - press); - double val2 = - linearInterpolationExtrap(undersat_oil_tables_[is+1][0], - undersat_oil_tables_[is+1][item], - press); - double val = val1 + w*(val2 - val1); - return val; - } - } + double val1 = + linearInterpolDerivative(undersat_oil_tables_[is][0], + undersat_oil_tables_[is][item], + press); + double val2 = + linearInterpolDerivative(undersat_oil_tables_[is+1][0], + undersat_oil_tables_[is+1][item], + press); + double val = val1 + w*(val2 - val1); + return val; + } + } else { + if (Rval < maxR ) { // Saturated case + return linearInterpolationExtrap(saturated_oil_table_[0], + saturated_oil_table_[item], + press); + } else { // Undersaturated case + // Interpolate between table sections + int is = tableIndex(saturated_oil_table_[3], maxR); + double w = (maxR - saturated_oil_table_[3][is]) / + (saturated_oil_table_[3][is+1] - saturated_oil_table_[3][is]); + ASSERT(undersat_oil_tables_[is][0].size() >= 2); + ASSERT(undersat_oil_tables_[is+1][0].size() >= 2); + double val1 = + linearInterpolationExtrap(undersat_oil_tables_[is][0], + undersat_oil_tables_[is][item], + press); + double val2 = + linearInterpolationExtrap(undersat_oil_tables_[is+1][0], + undersat_oil_tables_[is+1][item], + press); + double val = val1 + w*(val2 - val1); + return val; + } + } } } // namespace Opm diff --git a/opm/core/fluid/blackoil/SinglePvtLiveOil.hpp b/opm/core/fluid/blackoil/SinglePvtLiveOil.hpp index f8be6353..cef9595e 100644 --- a/opm/core/fluid/blackoil/SinglePvtLiveOil.hpp +++ b/opm/core/fluid/blackoil/SinglePvtLiveOil.hpp @@ -34,10 +34,10 @@ namespace Opm class SinglePvtLiveOil : public SinglePvtInterface { public: - typedef std::vector > > table_t; + typedef std::vector > > table_t; - SinglePvtLiveOil(const table_t& pvto); - virtual ~SinglePvtLiveOil(); + SinglePvtLiveOil(const table_t& pvto); + virtual ~SinglePvtLiveOil(); /// Viscosity as a function of p and z. virtual void mu(const int n, @@ -77,15 +77,15 @@ namespace Opm double evalR(double press, const double* surfvol) const; void evalRDeriv(double press, const double* surfvol, double& R, double& dRdp) const; - // item: 1=>1/B 2=>mu; - double miscible_oil(const double press, + // item: 1=>1/B 2=>mu; + double miscible_oil(const double press, const double* surfvol, const int item, - const bool deriv = false) const; + const bool deriv = false) const; - // PVT properties of live oil (with dissolved gas) - std::vector > saturated_oil_table_; - std::vector > > undersat_oil_tables_; + // PVT properties of live oil (with dissolved gas) + std::vector > saturated_oil_table_; + std::vector > > undersat_oil_tables_; }; } diff --git a/opm/core/grid.c b/opm/core/grid.c index 60093e83..9fa4cd67 100644 --- a/opm/core/grid.c +++ b/opm/core/grid.c @@ -104,6 +104,8 @@ allocate_grid(size_t ndims , nel = ncellfaces; G->cell_faces = malloc(nel * sizeof *G->cell_faces); + G->cell_facetag = malloc(nel * sizeof *G->cell_facetag); + nel = ncells + 1; G->cell_facepos = malloc(nel * sizeof *G->cell_facepos); @@ -121,6 +123,7 @@ allocate_grid(size_t ndims , (G->face_normals == NULL) || (G->face_areas == NULL) || (G->cell_faces == NULL) || + (G->cell_facetag == NULL) || (G->cell_facepos == NULL) || (G->cell_centroids == NULL) || (G->cell_volumes == NULL) ) diff --git a/opm/core/grid/cart_grid.c b/opm/core/grid/cart_grid.c index 2847e1fd..989bbfe6 100644 --- a/opm/core/grid/cart_grid.c +++ b/opm/core/grid/cart_grid.c @@ -118,7 +118,7 @@ create_grid_cart2d(int nx, int ny) /* --------------------------------------------------------------------- */ struct UnstructuredGrid * -create_grid_tensor2d(int nx, int ny, double x[], double y[]) +create_grid_tensor2d(int nx, int ny, const double *x, const double *y) { struct UnstructuredGrid *G; @@ -136,9 +136,13 @@ create_grid_tensor2d(int nx, int ny, double x[], double y[]) /* --------------------------------------------------------------------- */ struct UnstructuredGrid * -create_grid_tensor3d(int nx, int ny , int nz , - double x[], double y[], double z[], - const double depthz[]) +create_grid_tensor3d(int nx , + int ny , + int nz , + const double *x , + const double *y , + const double *z , + const double *depthz) { struct UnstructuredGrid *G; @@ -259,6 +263,11 @@ fill_cart_topology_3d(struct UnstructuredGrid *G) } } + for (k = 0; k < nx * ny * nz; ++k) { + for (i = 0; i < 6; ++i) { + G->cell_facetag[k*6 + i] = i; + } + } fnodes = G->face_nodes; fnodepos = G->face_nodepos; @@ -561,6 +570,12 @@ fill_cart_topology_2d(struct UnstructuredGrid *G) } } + for (j = 0; j < nx * ny; ++j) { + G->cell_facetag[j*4 + 0] = 0; + G->cell_facetag[j*4 + 1] = 2; + G->cell_facetag[j*4 + 2] = 1; + G->cell_facetag[j*4 + 3] = 3; + } fnodes = G->face_nodes; diff --git a/opm/core/grid/cart_grid.h b/opm/core/grid/cart_grid.h index b11898a2..2ab9c673 100644 --- a/opm/core/grid/cart_grid.h +++ b/opm/core/grid/cart_grid.h @@ -30,25 +30,140 @@ #ifndef OPM_CART_GRID_H_HEADER #define OPM_CART_GRID_H_HEADER +/** + * \file + * Routines to construct fully formed grid structures from a simple Cartesian + * (i.e., tensor product) description. + * + * The cells are lexicographically ordered with the @c i index cycling the most + * rapidly, followed by the @c j index and then, in three space dimensions, the + * @c k (`layer') index as the least rapidly cycling index. + */ + #ifdef __cplusplus extern "C" { #endif struct UnstructuredGrid; -struct UnstructuredGrid *create_grid_cart2d(int nx, int ny); -struct UnstructuredGrid *create_grid_cart3d(int nx, int ny, int nz); -struct UnstructuredGrid *create_grid_hexa3d(int nx, int ny, int nz, - double dx, double dy, double dz); - -struct UnstructuredGrid *create_grid_tensor2d(int nx, int ny, - double x[], double y[]); +/** + * Form geometrically Cartesian grid in two space dimensions with unit-sized + * cells. + * + * @param[in] nx Number of cells in @c x direction. + * @param[in] ny Number of cells in @c y direction. + * + * @return Fully formed grid structure containing valid geometric primitives. + * Must be destroyed using function destroy_grid(). + */ struct UnstructuredGrid * -create_grid_tensor3d(int nx, int ny, int nz, - double x[], double y[], double z[], - const double depthz[]); +create_grid_cart2d(int nx, int ny); +/** + * Form geometrically Cartesian grid in three space dimensions with unit-sized + * cells. + * + * @param[in] nx Number of cells in @c x direction. + * @param[in] ny Number of cells in @c y direction. + * @param[in] nz Number of cells in @c z direction. + * + * @return Fully formed grid structure containing valid geometric primitives. + * Must be destroyed using function destroy_grid(). + */ +struct UnstructuredGrid * +create_grid_cart3d(int nx, int ny, int nz); + + +/** + * Form geometrically Cartesian grid in three space dimensions with equally + * sized cells. + * + * Each cell has physical size (volume) \f$\mathit{dx}\times \mathit{dy}\times + * \mathit{dz}\f$. + * + * @param[in] nx Number of cells in @c x direction. + * @param[in] ny Number of cells in @c y direction. + * @param[in] nz Number of cells in @c z direction. + * + * @param[in] dx Length, in meters, of each cell's @c x extent. + * @param[in] dy Length, in meters, of each cell's @c y extent. + * @param[in] dz Length, in meters, of each cell's @c z extent. + * + * @return Fully formed grid structure containing valid geometric primitives. + * Must be destroyed using function destroy_grid(). + */ +struct UnstructuredGrid * +create_grid_hexa3d(int nx, int ny, int nz, + double dx, double dy, double dz); + + +/** + * Form tensor product (Cartesian) grid in two space dimensions. + * + * The size (volume) of cell \f$(i,j)\f$ is + * \f[ + * v_{ij} = (x_{i+1} - x_i)\cdot (y_{j+1} - y_j) + * \f] + * Similar relations hold for the cell and interface centroids as well as the + * interface areas and normal vectors. In other words, cell \f$(i,j)\f$ is the + * convex hull bounded by the tensor product of nodes \f$x_i\f$, \f$x_{i+1}\f$, + * \f$y_j\f$, and \f$y_{j+1}\f$. + * + * @param[in] nx Number of cells in @c x direction. + * @param[in] ny Number of cells in @c y direction. + * + * @param[in] x Position along @c x axis of each grid line with constant @c x + * coordinate. Array of size nx + 1. + * @param[in] y Position along @c y axis of each grid line with constant @c y + * coordinate. Array of size ny + 1. + * + * @return Fully formed grid structure containing valid geometric primitives. + * Must be destroyed using function destroy_grid(). + */ +struct UnstructuredGrid * +create_grid_tensor2d(int nx, int ny, + const double *x , const double *y ); + + +/** + * Form tensor product (i.e., topologically Cartesian) grid in three space + * dimensions--possibly with a variable top-layer topography. + * + * If @c depthz is @c NULL, then geometric information such as volumes and + * centroids is calculated from analytic expressions. Otherwise, these values + * are computed using function compute_geometry(). + * + * @param[in] nx Number of cells in @c x direction. + * @param[in] ny Number of cells in @c y direction. + * @param[in] nz Number of cells in @c z direction. + * + * @param[in] x Position along @c x axis of each grid line with constant @c x + * coordinate. Array of size nx + 1. + * @param[in] y Position along @c y axis of each grid line with constant @c y + * coordinate. Array of size ny + 1. + * @param[in] z Distance (depth) from top-layer measured along the @c z axis of + * each grid line with constant @c z coordinate. Array of size + * nz + 1. + * + * @param[in] depthz + * Top-layer topography specification. If @c NULL, interpreted as + * horizontal top-layer at z=0. Otherwise, must be + * an array of size (nx + 1) * (ny + 1), ordered + * lexicographically, that defines the depth of each top-layer + * pillar vertex. + * + * @return Fully formed grid structure containing valid geometric primitives. + * Must be destroyed using function destroy_grid(). + */ +struct UnstructuredGrid * +create_grid_tensor3d(int nx , + int ny , + int nz , + const double *x , + const double *y , + const double *z , + const double *depthz); #ifdef __cplusplus } #endif diff --git a/opm/core/grid/cornerpoint_grid.h b/opm/core/grid/cornerpoint_grid.h index fef14735..9e9c94f9 100644 --- a/opm/core/grid/cornerpoint_grid.h +++ b/opm/core/grid/cornerpoint_grid.h @@ -34,6 +34,12 @@ #ifndef OPM_CORNERPOINT_GRID_HEADER_INCLUDED #define OPM_CORNERPOINT_GRID_HEADER_INCLUDED +/** + * \file + * Routines to form a complete UnstructuredGrid from a corner-point + * specification. + */ + #include #include @@ -41,9 +47,53 @@ extern "C" { #endif + /** + * Construct grid representation from corner-point specification of a + * particular geological model. + * + * Pinched cells will be removed irrespective of any explicit "active" map + * in the geological model input specification. Geometric primitives such + * as cell barycenters (i.e., centroids), volumes and interface areas are + * computed internally using function compute_geometry(). The caller does + * not need to compute this information separately. + * + * @param[in] in Corner-point specification. If "actnum" is NULL, then the + * specification is interpreted as if all cells are initially + * active. + * + * @param[in] tol Absolute tolerance of node-coincidence. + * + * @return Fully formed grid data structure that manages the grid defined by + * the input corner-point specification. Must be destroyed using function + * destroy_grid(). + */ struct UnstructuredGrid * create_grid_cornerpoint(const struct grdecl *in, double tol); + + /** + * Compute derived geometric primitives in a grid. + * + * This function computes values for each of the following quantities + * - Quantities pertaining to interfaces (connections, faces) + * -# Barycenters (centroids), g->dimensions scalars per face + * stored sequentially in g->face_centroids. + * -# Areas, one scalar per face stored sequentially in + * g->face_areas. + * -# Normals, g->dimensions scalars per face stored + * sequentially in g->face_normals. The Euclidian norm of + * each normal is equal to the corresponding face's area. + * + * - Quantities pertaining to cells (volumes) + * -# Barycenters (centroids), g->dimensions scalars per cell + * stored sequentially in g->cell_centroids. + * -# Volumes, one scalar per cell stored sequentially in + * g->cell_volumes. + * + * These fields must be allocated prior to calling compute_geometry(). + * + * @param[in,out] g Grid structure. + */ void compute_geometry(struct UnstructuredGrid *g); #ifdef __cplusplus diff --git a/opm/core/grid/cpgpreprocess/preprocess.c b/opm/core/grid/cpgpreprocess/preprocess.c index fe2b4a86..84e9e8fe 100644 --- a/opm/core/grid/cpgpreprocess/preprocess.c +++ b/opm/core/grid/cpgpreprocess/preprocess.c @@ -587,8 +587,8 @@ get_zcorn_sign(int nx, int ny, int nz, const int *actnum, z1 = sign*zcorn[i+2*nx*(j+2*ny*(k))]; z2 = sign*zcorn[i+2*nx*(j+2*ny*(k+1))]; - c1 = i/2 + nx*(j/2 + ny*k/2); - c2 = i/2 + nx*(j/2 + ny*(k+1)/2); + c1 = i/2 + nx*(j/2 + ny*(k/2)); + c2 = i/2 + nx*(j/2 + ny*((k+1)/2)); if (((actnum == NULL) || (actnum[c1] && actnum[c2])) @@ -872,7 +872,7 @@ void process_grdecl(const struct grdecl *in, /* -----------------------------------------------------------------*/ /* (re)allocate space for and compute coordinates of nodes that - * arise from intesecting cells (faults) */ + * arise from intersecting cells (faults) */ compute_intersection_coordinates(intersections, out); free (intersections); diff --git a/opm/core/grid/cpgpreprocess/preprocess.h b/opm/core/grid/cpgpreprocess/preprocess.h index 4ee9be52..d3614c1d 100644 --- a/opm/core/grid/cpgpreprocess/preprocess.h +++ b/opm/core/grid/cpgpreprocess/preprocess.h @@ -35,48 +35,112 @@ #ifndef OPENRS_PREPROCESS_HEADER #define OPENRS_PREPROCESS_HEADER +/** + * \file + * Low-level corner-point processing routines and supporting data structures. + * + * User code should typically employ higher-level routines such as + * create_grid_cornerpoint() in order to construct fully formed UnstructuredGrid + * data structures from a corner-point specification. Incidentally, the routines + * provided by this module are used to implement function + * create_grid_cornerpoint(). + */ #ifdef __cplusplus extern "C" { #endif - /* Input structure holding raw cornerpoint spec. */ - struct grdecl{ - int dims[3]; - const double *coord; - const double *zcorn; - const int *actnum; - const double *mapaxes; /* 6 Element rotation vector - can be NULL. */ + /** + * Raw corner-point specification of a particular geological model. + */ + struct grdecl { + int dims[3]; /**< Cartesian box dimensions. */ + const double *coord; /**< Pillar end-points. */ + const double *zcorn; /**< Explicit "active" map. May be NULL.*/ + const int *actnum; /**< Corner-point depths. */ + const double *mapaxes; /**< 6 Element rotation vector - can be NULL. */ }; - /* Constant: I J K */ - enum face_tag { LEFT, BACK, TOP }; - - /* Output structure holding grid topology */ - struct processed_grid{ - int m; /** Upper bound on "number_of_faces" */ - int n; /** Upper bound on "number_of_nodes" */ - int dimensions[3]; /* Cartesian dimension */ - int number_of_faces; - int *face_nodes; /* Nodes numbers of each face sequentially. */ - int *face_ptr; /* Start position for each face in face_nodes. */ - int *face_neighbors; /* Global cell numbers. 2 ints per face sequentially */ - enum face_tag *face_tag; - - int number_of_nodes; - int number_of_nodes_on_pillars; /** Total number of unique cell vertices that lie on pillars. */ - double *node_coordinates; /* 3 doubles per node, sequentially */ - - int number_of_cells; /* number of active cells */ - int *local_cell_index; /* Global to local map */ + /** + * Connection taxonomy. + */ + enum face_tag { + LEFT, /**< Connection topologically parallel to J-K plane. */ + BACK, /**< Connection topologically parallel to I-K plane. */ + TOP /**< Connection topologically parallel to I-J plane. */ }; - void process_grdecl (const struct grdecl *g, - double tol, - struct processed_grid *out); + /** + * Result structure representing minimal derived topology and geometry of + * a geological model in corner-point format. + */ + struct processed_grid { + int m; /**< Upper bound on "number_of_faces". For internal use in + function process_grid()'s memory management. */ + int n; /**< Upper bound on "number_of_nodes". For internal use in + function process_grid()'s memory management. */ + + int dimensions[3]; /**< Cartesian box dimensions. */ + + int number_of_faces; /**< Total number of unique grid faces + (i.e., connections). */ + int *face_nodes; /**< Node (vertex) numbers of each face, + stored sequentially. */ + int *face_ptr; /**< Start position for each face's + `face_nodes'. */ + int *face_neighbors; /**< Global cell numbers. Two elements per + face, stored sequentially. */ + enum face_tag *face_tag; /**< Classification of grid's individual + connections (faces). */ + + int number_of_nodes; /**< Number of unique grid vertices. */ + int number_of_nodes_on_pillars; /**< Total number of unique cell + vertices that lie on pillars. */ + double *node_coordinates; /**< Vertex coordinates. Three doubles + (\f$x\f$, \f$y\f$, \f$z\f$) per vertex, + stored sequentially. */ + + int number_of_cells; /**< Number of active grid cells. */ + int *local_cell_index; /**< Deceptively named local-to-global cell + index mapping. */ + }; + + + /** + * Construct a prototypical grid representation from a corner-point + * specification. + * + * Pinched cells will be removed irrespective of any explicit "active" map + * in the geological model input specification. On input, the result + * structure "out" must point to a valid management structure. In other + * words, the result structure must point to a region of memory that is + * typically backed by automatic or allocated (dynamic) storage duration. + * + * @param[in] g Corner-point specification. If "actnum" is NULL, then + * the specification is interpreted as if all cells are + * initially active. + * @param[in] tol Absolute tolerance of node-coincidence. + * @param[in,out] out Minimal grid representation featuring face-to-cell + * neighbourship definition, vertex geometry, face's + * constituent vertices, and local-to-global cell + * mapping. + */ + void process_grdecl(const struct grdecl *g , + double tol, + struct processed_grid *out); + + /** + * Release memory resources acquired in previous grid processing using + * function process_grdecl(). + * + * Note: This function releases the resources associated to the individual + * fields of the processed_grid, but does not free() the structure itself. + * + * @param[in,out] g Prototypical grid representation obtained in an earlier + * call to function process_grdecl(). + */ void free_processed_grid(struct processed_grid *g); - #ifdef __cplusplus } #endif diff --git a/opm/core/linalg/LinearSolverAGMG.cpp b/opm/core/linalg/LinearSolverAGMG.cpp index ae76bd4e..086c9316 100644 --- a/opm/core/linalg/LinearSolverAGMG.cpp +++ b/opm/core/linalg/LinearSolverAGMG.cpp @@ -65,13 +65,20 @@ namespace Opm rtol_ (rtol) , is_spd_(is_spd) { -#if !HAVE_AGMG - THROW("AGMG support is not enabled in this library"); -#endif // HAVE_AGMG + } + + LinearSolverAGMG::LinearSolverAGMG(const parameter::ParameterGroup& param) + : max_it_(100) , + rtol_ (1.0e-6), + is_spd_(false) + { + max_it_ = param.getDefault("max_it", max_it_); + rtol_ = param.getDefault("rtol" , rtol_ ); + is_spd_ = param.getDefault("is_spd", is_spd_); } LinearSolverAGMG::~LinearSolverAGMG() {} - + LinearSolverInterface::LinearSolverReport LinearSolverAGMG::solve(const int size , const int nonzeros, diff --git a/opm/core/linalg/LinearSolverAGMG.hpp b/opm/core/linalg/LinearSolverAGMG.hpp index 813d57b8..9038ba75 100644 --- a/opm/core/linalg/LinearSolverAGMG.hpp +++ b/opm/core/linalg/LinearSolverAGMG.hpp @@ -43,6 +43,7 @@ */ #include +#include namespace Opm { @@ -62,20 +63,29 @@ namespace Opm const double rtol = 1.0e-6, const bool is_spd = false); - /// Destructor. + /** + * Constructor. + * \param[in] param ParameterGroup object containing the fields + * max_it,rtol,is_spd as used in the constructor. + */ + LinearSolverAGMG(const parameter::ParameterGroup& param); + + /** + * Destructor. + */ virtual ~LinearSolverAGMG(); using LinearSolverInterface::solve; /// Solve a linear system, with a matrix given in compressed /// sparse row format. - /// \param[in] size Number of rows (and colums). - /// \param[in] nonzeros Number of (structural) non-zeros. - /// \param[in] ia Row pointers. - /// \param[in] ja Column indices. - /// \param[in] sa (structurally) non-zero elements. - /// \param[in] rhs System right-hand side. - /// \param[inout] solution System solution. + /// \param[in] size Number of rows (and columns). + /// \param[in] nonzeros Number of (structural) non-zeros. + /// \param[in] ia Row pointers. + /// \param[in] ja Column indices. + /// \param[in] sa (structurally) non-zero elements. + /// \param[in] rhs System right-hand side. + /// \param[in,out] solution System solution. /// \return Solver meta-data concerning most recent system solve. virtual LinearSolverInterface::LinearSolverReport solve(const int size, const int nonzeros, diff --git a/opm/core/linalg/LinearSolverFactory.cpp b/opm/core/linalg/LinearSolverFactory.cpp index 9b6d8909..4e2f3679 100644 --- a/opm/core/linalg/LinearSolverFactory.cpp +++ b/opm/core/linalg/LinearSolverFactory.cpp @@ -31,6 +31,11 @@ #include #endif +#if HAVE_AGMG +#include +#endif + + #include #include #include @@ -70,6 +75,12 @@ namespace Opm #endif } + else if (ls == "agmg") { +#if HAVE_AGMG + solver_.reset(new LinearSolverAGMG(param)); +#endif + } + else { THROW("Linear solver " << ls << " is unknown."); } diff --git a/opm/core/linalg/LinearSolverIstl.cpp b/opm/core/linalg/LinearSolverIstl.cpp index 791fcb17..3c4435e8 100644 --- a/opm/core/linalg/LinearSolverIstl.cpp +++ b/opm/core/linalg/LinearSolverIstl.cpp @@ -24,10 +24,7 @@ #include -// Work around the fact that istl headers expect -// HAVE_BOOST to be 1, and not just defined. -#undef HAVE_BOOST -#define HAVE_BOOST 1 +#include // TODO: clean up includes. #include diff --git a/opm/core/pressure/CompressibleTpfa.cpp b/opm/core/pressure/CompressibleTpfa.cpp index 7d936d6e..954e07fa 100644 --- a/opm/core/pressure/CompressibleTpfa.cpp +++ b/opm/core/pressure/CompressibleTpfa.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -58,6 +59,7 @@ namespace Opm /// to change. CompressibleTpfa::CompressibleTpfa(const UnstructuredGrid& grid, const BlackoilPropertiesInterface& props, + const RockCompressibility* rock_comp_props, const LinearSolverInterface& linsolver, const double residual_tol, const double change_tol, @@ -66,6 +68,7 @@ namespace Opm const struct Wells* wells) : grid_(grid), props_(props), + rock_comp_props_(rock_comp_props), linsolver_(linsolver), residual_tol_(residual_tol), change_tol_(change_tol), @@ -74,8 +77,8 @@ namespace Opm wells_(wells), htrans_(grid.cell_facepos[ grid.number_of_cells ]), trans_ (grid.number_of_faces), - porevol_(grid.number_of_cells), - allcells_(grid.number_of_cells) + allcells_(grid.number_of_cells), + singular_(false) { if (wells_ && (wells_->number_of_phases != props.numPhases())) { THROW("Inconsistent number of phases specified (wells vs. props): " @@ -86,7 +89,12 @@ namespace Opm UnstructuredGrid* gg = const_cast(&grid_); tpfa_htrans_compute(gg, props.permeability(), &htrans_[0]); tpfa_trans_compute(gg, &htrans_[0], &trans_[0]); - computePorevolume(grid_, props.porosity(), porevol_); + // If we have rock compressibility, pore volumes are updated + // in the compute*() methods, otherwise they are constant and + // hence may be computed here. + if (rock_comp_props_ == NULL || !rock_comp_props_->isActive()) { + computePorevolume(grid_, props.porosity(), porevol_); + } for (int c = 0; c < grid.number_of_cells; ++c) { allcells_[c] = c; } @@ -182,6 +190,21 @@ namespace Opm + /// @brief After solve(), was the resulting pressure singular. + /// Returns true if the pressure is singular in the following + /// sense: if everything is incompressible and there are no + /// pressure conditions, the absolute values of the pressure + /// solution are arbitrary. (But the differences in pressure + /// are significant.) + bool CompressibleTpfa::singularPressure() const + { + return singular_; + } + + + + + /// Compute well potentials. void CompressibleTpfa::computeWellPotentials(const BlackoilState& state) { @@ -230,6 +253,9 @@ namespace Opm const WellState& /*well_state*/) { computeWellPotentials(state); + if (rock_comp_props_ && rock_comp_props_->isActive()) { + computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), initial_porevol_); + } } @@ -252,6 +278,8 @@ namespace Opm // std::vector face_gravcap_; // std::vector wellperf_A_; // std::vector wellperf_phasemob_; + // std::vector porevol_; // Only modified if rock_comp_props_ is non-null. + // std::vector rock_comp_; // Empty unless rock_comp_props_ is non-null. computeCellDynamicData(dt, state, well_state); computeFaceDynamicData(dt, state, well_state); computeWellDynamicData(dt, state, well_state); @@ -273,6 +301,8 @@ namespace Opm // std::vector cell_viscosity_; // std::vector cell_phasemob_; // std::vector cell_voldisc_; + // std::vector porevol_; // Only modified if rock_comp_props_ is non-null. + // std::vector rock_comp_; // Empty unless rock_comp_props_ is non-null. const int nc = grid_.number_of_cells; const int np = props_.numPhases(); const double* cell_p = &state.pressure()[0]; @@ -296,6 +326,14 @@ namespace Opm // TODO: Check this! cell_voldisc_.clear(); cell_voldisc_.resize(nc, 0.0); + + if (rock_comp_props_ && rock_comp_props_->isActive()) { + computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol_); + rock_comp_.resize(nc); + for (int cell = 0; cell < nc; ++cell) { + rock_comp_[cell] = rock_comp_props_->rockComp(state.pressure()[cell]); + } + } } @@ -341,7 +379,7 @@ namespace Opm const double depth_diff = face_depth - grid_.cell_centroids[c[j]*dim + dim - 1]; props_.density(1, &cell_A_[np*np*c[j]], &gravcontrib[j][0]); for (int p = 0; p < np; ++p) { - gravcontrib[j][p] *= depth_diff; + gravcontrib[j][p] *= depth_diff*grav; } } else { std::fill(gravcontrib[j].begin(), gravcontrib[j].end(), 0.0); @@ -465,9 +503,20 @@ namespace Opm cq.Af = &face_A_[0]; cq.phasemobf = &face_phasemob_[0]; cq.voldiscr = &cell_voldisc_[0]; - cfs_tpfa_res_assemble(gg, dt, &forces, z, &cq, &trans_[0], - &face_gravcap_[0], cell_press, well_bhp, - &porevol_[0], h_); + int was_adjusted = 0; + if (! (rock_comp_props_ && rock_comp_props_->isActive())) { + was_adjusted = + cfs_tpfa_res_assemble(gg, dt, &forces, z, &cq, &trans_[0], + &face_gravcap_[0], cell_press, well_bhp, + &porevol_[0], h_); + } else { + was_adjusted = + cfs_tpfa_res_comprock_assemble(gg, dt, &forces, z, &cq, &trans_[0], + &face_gravcap_[0], cell_press, well_bhp, + &porevol_[0], &initial_porevol_[0], + &rock_comp_[0], h_); + } + singular_ = (was_adjusted == 1); } diff --git a/opm/core/pressure/CompressibleTpfa.hpp b/opm/core/pressure/CompressibleTpfa.hpp index 8233ee17..0b54178e 100644 --- a/opm/core/pressure/CompressibleTpfa.hpp +++ b/opm/core/pressure/CompressibleTpfa.hpp @@ -33,6 +33,7 @@ namespace Opm class BlackoilState; class BlackoilPropertiesInterface; + class RockCompressibility; class LinearSolverInterface; class WellState; @@ -44,23 +45,25 @@ namespace Opm { public: /// Construct solver. - /// \param[in] grid A 2d or 3d grid. - /// \param[in] props Rock and fluid properties. - /// \param[in] linsolver Linear solver to use. - /// \param[in] residual_tol Solution accepted if inf-norm of residual is smaller. - /// \param[in] change_tol Solution accepted if inf-norm of change in pressure is smaller. - /// \param[in] maxiter Maximum acceptable number of iterations. - /// \param[in] gravity Gravity vector. If non-null, the array should - /// have D elements. - /// \param[in] wells The wells argument. Will be used in solution, - /// is ignored if NULL. - /// Note: this class observes the well object, and - /// makes the assumption that the well topology - /// and completions does not change during the - /// run. However, controls (only) are allowed - /// to change. - CompressibleTpfa(const UnstructuredGrid& grid, + /// \param[in] grid A 2d or 3d grid. + /// \param[in] props Rock and fluid properties. + /// \param[in] rock_comp_props Rock compressibility properties. May be null. + /// \param[in] linsolver Linear solver to use. + /// \param[in] residual_tol Solution accepted if inf-norm of residual is smaller. + /// \param[in] change_tol Solution accepted if inf-norm of change in pressure is smaller. + /// \param[in] maxiter Maximum acceptable number of iterations. + /// \param[in] gravity Gravity vector. If non-null, the array should + /// have D elements. + /// \param[in] wells The wells argument. Will be used in solution, + /// is ignored if NULL. + /// Note: this class observes the well object, and + /// makes the assumption that the well topology + /// and completions does not change during the + /// run. However, controls (only) are allowed + /// to change. + CompressibleTpfa(const UnstructuredGrid& grid, const BlackoilPropertiesInterface& props, + const RockCompressibility* rock_comp_props, const LinearSolverInterface& linsolver, const double residual_tol, const double change_tol, @@ -68,8 +71,8 @@ namespace Opm const double* gravity, const Wells* wells); - /// Destructor. - ~CompressibleTpfa(); + /// Destructor. + ~CompressibleTpfa(); /// Solve the pressure equation by Newton-Raphson scheme. /// May throw an exception if the number of iterations @@ -78,17 +81,24 @@ namespace Opm BlackoilState& state, WellState& well_state); + /// @brief After solve(), was the resulting pressure singular. + /// Returns true if the pressure is singular in the following + /// sense: if everything is incompressible and there are no + /// pressure conditions, the absolute values of the pressure + /// solution are arbitrary. (But the differences in pressure + /// are significant.) + bool singularPressure() const; + private: - void computePerSolveDynamicData(const double dt, - const BlackoilState& state, - const WellState& well_state); - void computeWellPotentials(const BlackoilState& state); + virtual void computePerSolveDynamicData(const double dt, + const BlackoilState& state, + const WellState& well_state); void computePerIterationDynamicData(const double dt, const BlackoilState& state, const WellState& well_state); - void computeCellDynamicData(const double dt, - const BlackoilState& state, - const WellState& well_state); + virtual void computeCellDynamicData(const double dt, + const BlackoilState& state, + const WellState& well_state); void computeFaceDynamicData(const double dt, const BlackoilState& state, const WellState& well_state); @@ -101,28 +111,31 @@ namespace Opm void solveIncrement(); double residualNorm() const; double incrementNorm() const; - void computeResults(BlackoilState& state, + void computeResults(BlackoilState& state, WellState& well_state) const; + protected: + void computeWellPotentials(const BlackoilState& state); // ------ Data that will remain unmodified after construction. ------ - const UnstructuredGrid& grid_; + const UnstructuredGrid& grid_; const BlackoilPropertiesInterface& props_; + const RockCompressibility* rock_comp_props_; const LinearSolverInterface& linsolver_; const double residual_tol_; const double change_tol_; const int maxiter_; const double* gravity_; // May be NULL const Wells* wells_; // May be NULL, outside may modify controls (only) between calls to solve(). - std::vector htrans_; - std::vector trans_ ; - std::vector porevol_; + std::vector htrans_; + std::vector trans_ ; std::vector allcells_; // ------ Internal data for the cfs_tpfa_res solver. ------ - struct cfs_tpfa_res_data* h_; + struct cfs_tpfa_res_data* h_; // ------ Data that will be modified for every solve. ------ std::vector wellperf_gpot_; + std::vector initial_porevol_; // ------ Data that will be modified for every solver iteration. ------ std::vector cell_A_; @@ -135,13 +148,15 @@ namespace Opm std::vector face_gravcap_; std::vector wellperf_A_; std::vector wellperf_phasemob_; + std::vector porevol_; // Only modified if rock_comp_props_ is non-null. + std::vector rock_comp_; // Empty unless rock_comp_props_ is non-null. // The update to be applied to the pressures (cell and bhp). std::vector pressure_increment_; - - - - - + // True if the matrix assembled would be singular but for the + // adjustment made in the cfs_*_assemble() calls. This happens + // if everything is incompressible and there are no pressure + // conditions. + bool singular_; }; } // namespace Opm diff --git a/opm/core/pressure/TPFAPressureSolver.hpp b/opm/core/pressure/TPFAPressureSolver.hpp index 45a37012..33f7e33a 100644 --- a/opm/core/pressure/TPFAPressureSolver.hpp +++ b/opm/core/pressure/TPFAPressureSolver.hpp @@ -27,8 +27,9 @@ #include #include // for updating gpress #include +#include #include - +#include /// @brief diff --git a/opm/core/pressure/mimetic/hybsys.h b/opm/core/pressure/mimetic/hybsys.h index 2fd77a96..12ace45e 100644 --- a/opm/core/pressure/mimetic/hybsys.h +++ b/opm/core/pressure/mimetic/hybsys.h @@ -358,28 +358,193 @@ hybsys_schur_comp_gen(int nc, const int *pconn, const double *Binv, const double *C2, const double *P, struct hybsys *sys); +/** + * Compute elemental contributions to global, symmetric system of + * simultaneous linear equations from cell<->well connections. + * + * Specifically, for a well @c w intersecting a cell @c c, this function + * computes the elemental contributions + * \f[ + * (F_1)_{wc} = C_{wc}^\mathsf{T} B_{wc}^{-1} D_{wc} = \mathit{WI}_{wc} + * \f] + * and + * \f[ + * L_{wc} = C_{wc}^\mathsf{T} B_{wc}^{-1} C_{wc} = \mathit{WI}_{wc} + * \f] + * and incorporates the contributions into the global system quantities + * as appropriate. + * + * This function modifies sys->L and wsys->F1. + * + * @param[in] nc Total number of grid cells. + * @param[in] cwpos Indirection array that defines each cell's + * connecting wells. Values typically computed + * using function derive_cell_wells(). + * @param[in] WI Peaceman well connection indices. Array of + * size cwpos[nc]. Must incorporate + * effects of multiple phases (i.e., total mobility) + * if applicable. + * @param[in,out] sys Hybrid system management structure allocated + * using hybsys_allocate_symm() and initialised + * using hybsys_init() and/or filled using function + * hybsys_schur_comp_symm(). + * @param[in,out] wsys Hybrid well-system management structure obtained + * from function hybsys_well_allocate_symm(). + */ void hybsys_well_schur_comp_symm(int nc, const int *cwpos, double *WI, struct hybsys *sys, struct hybsys_well *wsys); +/** + * Compute final (symmetric) Schur complement contributions to + * global system of simultaneous linear equations. + * + * This function forms the coefficient matrix + * \f[ + * S_c = D^\mathsf{T}B_c^{-1}D - F_c^\mathsf{T}L_c^{-1}F_c + * \f] + * and similar right-hand side \f$r_c\f$ elemental contributions. + * These values must be subsequently assembled into the global system + * using function hybsys_global_assemble_cell() after imposing any + * applicable boundary conditions. + * + * This function overwrites the fields @c S and @c r of the hybrid system + * structure. + * + * @param[in] c Cell for which to compute local contributions. + * @param[in] nconn Number of connections (faces) of cell @c c. + * @param[in] p1 Start address (into @c gpress) of the gravity + * contributions of cell @c c. + * @param[in] p2 Start address (into @c Binv) of the inverse + * inner product of cell @c c. + * @param[in] gpress Gravity contributions of all cells. Must + * include effects of multiple phases if applicable. + * @param[in] src Explicit source terms for all cells. + * @param[in] Binv Inverse inner products for all cells. Must + * include effects of multiple phases if applicable. + * @param[in,out] sys Hybrid system management structure allocated + * using hybsys_allocate_symm() and initialised + * using hybsys_init() and/or filled using function + * hybsys_schur_comp_symm() and + * hybsys_well_schur_comp_symm() if applicable. + */ void hybsys_cellcontrib_symm(int c, int nconn, int p1, int p2, const double *gpress, const double *src, const double *Binv, struct hybsys *sys); + +/** + * Compute final (non-symmetric) Schur complement contributions to + * global system of simultaneous linear equations. + * + * This function forms the coefficient matrix + * \f[ + * S_c = D^\mathsf{T}B_c^{-1}D - (F_1)_c^\mathsf{T}L_c^{-1}(F_2)_c + * \f] + * and similar right-hand side \f$r_c\f$ elemental contributions. + * These values must be subsequently assembled into the global system + * using function hybsys_global_assemble_cell() after imposing any + * applicable boundary conditions. + * + * This function overwrites the fields @c S and @c r of the hybrid system + * structure. + * + * @param[in] c Cell for which to compute local contributions. + * @param[in] nconn Number of connections (faces) of cell @c c. + * @param[in] p1 Start address (into @c gpress) of the gravity + * contributions of cell @c c. + * @param[in] p2 Start address (into @c Binv) of the inverse + * inner product of cell @c c. + * @param[in] gpress Gravity contributions of all cells. Must + * include effects of multiple phases if applicable. + * @param[in] src Explicit source terms for all cells. + * @param[in] Binv Inverse inner products for all cells. Must + * include effects of multiple phases if applicable. + * @param[in,out] sys Hybrid system management structure allocated + * using hybsys_allocate_symm() and initialised + * using hybsys_init() and/or filled using functions + * hybsys_schur_comp_unsymm() or hybsys_schur_comp_gen(). + */ void hybsys_cellcontrib_unsymm(int c, int nconn, int p1, int p2, const double *gpress, const double *src, const double *Binv, struct hybsys *sys); + +/** + * Form elemental direct contributions to global system of simultaneous linear + * equations from cell<->well interactions. + * + * Plays a role similar to function hybsys_cellcontrib_symm(), but for wells. + * + * @param[in] c Cell for which to compute cell<->well Schur complement + * @param[in] ngconn Number of inter-cell connections (faces) of cell @c c. + * @param[in] p1 Start index (into sys->F1) of cell @c c. + * @param[in] cwpos Indirection array that defines each cell's connecting + * wells. Must coincide with equally named parameter of + * function hybsys_well_schur_comp_symm(). + * @param[in] WI Peaceman well connection indices. Array of + * size pwconn[nc]. Must coincide with + * equally named parameter of contribution function + * hybsys_well_schur_comp_symm(). + * @param[in] wdp Well connection gravity pressure adjustments. + * One scalar for each well connection in an array of size + * pwconn[nc]. + * @param[in,out] sys Hybrid system management structure filled using + * functions hybsys_schur_comp_unsymm() or + * hybsys_schur_comp_gen(). + * @param[in,out] wsys Hybrid well-system management structure filled using + * function hybsys_well_schur_comp_symm(). + */ void hybsys_well_cellcontrib_symm(int c, int ngconn, int p1, const int *cwpos, const double *WI, const double *wdp, struct hybsys *sys, struct hybsys_well *wsys); + +/** + * Recover cell pressures and outward fluxes (with respect to cells--i.e., the + * ``half-face fluxes'') through back substitution after solving a symmetric + * (i.e., incompressible) Schur complement system of simultaneous linear + * equations. + * + * Specifically, given the solution \f$\pi\f$ to the global system of + * simultaneous linear equations, \f$A\pi=b\f$, that arises as a result of the + * Schur complement analysis, this function recovers the cell pressures \f$p\f$ + * and outward fluxes \f$v\f$ defined by + * \f[ + * \begin{aligned} + * Lp &= g - C_2^\mathsf{T}B^{-1}G + F_2\pi \\ + * Bv &= G + C_1p - D\pi + * \end{aligned}. + * \f] + * + * @param[in] nc Total number of grid cells. + * @param[in] pconn Cell-to-face start pointers. + * @param[in] conn Cell-to-face mapping. + * @param[in] gpress Gravity contributions of all cells. Must coincide with + * equally named parameter in calls to cell contribution + * functions such as hybsys_cellcontrib_symm(). + * @param[in] Binv Inverse inner products for all cells. Must coincide + * with equally named parameter in calls to contribution + * functions such as hybsys_cellcontrib_symm(). + * @param[in] sys Hybrid system management structure coinciding with + * equally named parameter in contribution functions such + * as hybsys_cellcontrib_symm() or + * hybsys_cellcontrib_unsymm(). + * @param[in] pi Solution (interface/contact pressure) obtained from + * solving the global system \f$A\pi = b\f$. + * @param[out] press Cell pressures, \f$p\f$. Array of size @c nc. + * @param[out] flux Outward interface fluxes, \f$v\f$. Array of size + * pconn[nc]. + * @param[in,out] work Scratch array for temporary results. Array of size at + * least \f$\max_c \{ \mathit{pconn}_{c + 1} + * - \mathit{pconn}_c \} \f$. + */ void hybsys_compute_press_flux(int nc, const int *pconn, const int *conn, const double *gpress, @@ -387,6 +552,52 @@ hybsys_compute_press_flux(int nc, const int *pconn, const int *conn, const double *pi, double *press, double *flux, double *work); + +/** + * Recover well pressures (i.e., bottom-hole pressure values) and well + * connection (perforation) fluxes. + * + * Specifically, this function performs the same role (i.e., back-substitution) + * for wells as function hybsys_compute_press_flux() does for grid cells and + * grid contacts (interfaces). + * + * @param[in] nc Total number of grid cells. + * @param[in] pgconn Cell-to-face start pointers. + * @param[in] nf Total number of grid faces. + * @param[in] nw Total number of wells. + * @param[in] pwconn Cell-to-well start pointers. If nw > 0, + * then this parameter must coincide with the @c cwpos + * array used in call to hybsys_well_schur_comp_symm(). + * @param[in] wconn Cell-to-well mapping. + * @param[in] Binv Inverse inner products for all cells. Must coincide + * with equally named parameter in calls to contribution + * functions such as hybsys_well_cellcontrib_symm(). + * @param[in] WI Peaceman well connection indices. Array of + * size pwconn[nc]. Must coincide with + * equally named parameter of contribution function + * hybsys_well_cellcontrib_symm(). + * @param[in] wdp Well connection gravity pressure adjustments. + * @param[in] sys Hybrid system management structure coinciding with + * equally named parameter in contribution functions such + * as hybsys_cellcontrib_symm() and + * hybsys_well_cellcontrib_symm(). + * @param[in] wsys Hybrid well-system management structure. Must coincide + * with equally named paramter of contribution function + * hybsys_well_cellcontrib_symm(). + * @param[in] pi Solution (interface/contact pressure and well BHPs) + * obtained from solving the global system \f$A\pi = b\f$. + * @param[in] cpress Cell pressures, \f$p\f$, obtained from a previous call + * to function hybsys_compute_press_flux(). + * @param[in] cflux Outward fluxes, \f$v\f$, obtained from a previous call + * to function hybsys_compute_press_flux(). + * @param[out] wpress Well (i.e., bottom-hole) pressures. Array of size + * @c nw. + * @param[out] wflux Well connection (perforation) fluxes. Array of size + * pwconn[nw]. + * @param[in,out] work Scratch array for storing intermediate results. Array + * of size at least \f$\max_w \{ \mathit{pwconn}_{w + 1} + * - \mathit{pwconn}_w\}\f$. + */ void hybsys_compute_press_flux_well(int nc, const int *pgconn, int nf, int nw, const int *pwconn, const int *wconn, diff --git a/opm/core/pressure/tpfa/cfs_tpfa_residual.c b/opm/core/pressure/tpfa/cfs_tpfa_residual.c index 7a52db1e..eb8dc8f3 100644 --- a/opm/core/pressure/tpfa/cfs_tpfa_residual.c +++ b/opm/core/pressure/tpfa/cfs_tpfa_residual.c @@ -1156,7 +1156,7 @@ cfs_tpfa_res_construct(struct UnstructuredGrid *G , /* ---------------------------------------------------------------------- */ -void +int cfs_tpfa_res_assemble(struct UnstructuredGrid *G , double dt , struct cfs_tpfa_res_forces *forces , @@ -1170,7 +1170,7 @@ cfs_tpfa_res_assemble(struct UnstructuredGrid *G , struct cfs_tpfa_res_data *h ) /* ---------------------------------------------------------------------- */ { - int res_is_neumann, well_is_neumann, c, np2; + int res_is_neumann, well_is_neumann, c, np2, singular; csrmatrix_zero( h->J); vector_zero (h->J->m, h->F); @@ -1207,9 +1207,76 @@ cfs_tpfa_res_assemble(struct UnstructuredGrid *G , assemble_sources(dt, forces->src, h); } - if (res_is_neumann && well_is_neumann && h->pimpl->is_incomp) { - h->J->sa[0] *= 2; + singular = res_is_neumann && well_is_neumann && h->pimpl->is_incomp; + if (singular) { + h->J->sa[0] *= 2.0; } + + return singular; +} + + +/* ---------------------------------------------------------------------- */ +int +cfs_tpfa_res_comprock_assemble( + struct UnstructuredGrid *G , + double dt , + struct cfs_tpfa_res_forces *forces , + const double *zc , + struct compr_quantities_gen *cq , + const double *trans , + const double *gravcap_f, + const double *cpress , + const double *wpress , + const double *porevol , + const double *porevol0 , + const double *rock_comp, + struct cfs_tpfa_res_data *h ) +/* ---------------------------------------------------------------------- */ +{ + /* We want to add this term to the usual residual: + * + * (porevol(pressure)-porevol(initial_pressure))/dt. + * + * Its derivative (for the diagonal term of the Jacobian) is: + * + * porevol(pressure)*rock_comp(pressure)/dt + */ + + int c, rock_is_incomp, singular; + size_t j; + double dpv; + + /* Assemble usual system (without rock compressibility). */ + singular = cfs_tpfa_res_assemble(G, dt, forces, zc, cq, trans, gravcap_f, + cpress, wpress, porevol0, h); + + /* If we made a singularity-removing adjustment in the + regular assembly, we undo it here. */ + if (singular) { + h->J->sa[0] /= 2.0; + } + + /* Add new terms to residual and Jacobian. */ + rock_is_incomp = 1; + for (c = 0; c < G->number_of_cells; c++) { + j = csrmatrix_elm_index(c, c, h->J); + + dpv = (porevol[c] - porevol0[c]); + if (dpv != 0.0 || rock_comp[c] != 0.0) { + rock_is_incomp = 0; + } + + h->J->sa[j] += porevol[c] * rock_comp[c]; + h->F[c] += dpv; + } + + /* Re-do the singularity-removing adjustment if necessary */ + if (rock_is_incomp && singular) { + h->J->sa[0] *= 2.0; + } + + return rock_is_incomp && singular; } diff --git a/opm/core/pressure/tpfa/cfs_tpfa_residual.h b/opm/core/pressure/tpfa/cfs_tpfa_residual.h index 4fde3ba3..5eddeed6 100644 --- a/opm/core/pressure/tpfa/cfs_tpfa_residual.h +++ b/opm/core/pressure/tpfa/cfs_tpfa_residual.h @@ -59,7 +59,11 @@ cfs_tpfa_res_construct(struct UnstructuredGrid *G , void cfs_tpfa_res_destroy(struct cfs_tpfa_res_data *h); -void +/* Return value is 1 if the assembled matrix was adjusted to remove a + singularity. This happens if all fluids are incompressible and + there are no pressure conditions on wells or boundaries. + Otherwise return 0. */ +int cfs_tpfa_res_assemble(struct UnstructuredGrid *G, double dt, struct cfs_tpfa_res_forces *forces, @@ -72,6 +76,27 @@ cfs_tpfa_res_assemble(struct UnstructuredGrid *G, const double *porevol, struct cfs_tpfa_res_data *h); +/* Return value is 1 if the assembled matrix was adjusted to remove a + singularity. This happens if all fluids are incompressible, the + rock is incompressible, and there are no pressure conditions on + wells or boundaries. + Otherwise return 0. */ +int +cfs_tpfa_res_comprock_assemble( + struct UnstructuredGrid *G, + double dt, + struct cfs_tpfa_res_forces *forces, + const double *zc, + struct compr_quantities_gen *cq, + const double *trans, + const double *gravcap_f, + const double *cpress, + const double *wpress, + const double *porevol, + const double *porevol0, + const double *rock_comp, + struct cfs_tpfa_res_data *h); + void cfs_tpfa_res_flux(struct UnstructuredGrid *G , struct cfs_tpfa_res_forces *forces , diff --git a/opm/core/pressure/tpfa/ifs_tpfa.c b/opm/core/pressure/tpfa/ifs_tpfa.c index 94e3d638..0e69bc49 100644 --- a/opm/core/pressure/tpfa/ifs_tpfa.c +++ b/opm/core/pressure/tpfa/ifs_tpfa.c @@ -771,7 +771,7 @@ ifs_tpfa_assemble_comprock_increment(struct UnstructuredGrid *G , assemble_incompressible(G, F, trans, gpress, h, &system_singular, &ok); /* We want to solve a Newton step for the residual - * (porevol(pressure)-porevol(initial_pressure))/dt + residual_for_imcompressible + * (porevol(pressure)-porevol(initial_pressure))/dt + residual_for_incompressible * */ diff --git a/opm/core/pressure/tpfa/ifs_tpfa.h b/opm/core/pressure/tpfa/ifs_tpfa.h index f7ab4230..d7853bef 100644 --- a/opm/core/pressure/tpfa/ifs_tpfa.h +++ b/opm/core/pressure/tpfa/ifs_tpfa.h @@ -20,6 +20,16 @@ #ifndef OPM_IFS_TPFA_HEADER_INCLUDED #define OPM_IFS_TPFA_HEADER_INCLUDED +/** + * \file + * Interfaces and data structures to assemble a system of simultaneous linear + * equations discretising a flow problem that is either incompressible or + * features rock compressibility using the two-point flux approximation method. + * + * Includes support for reconstructing the Darcy flux field as well as well + * connection fluxes. + */ + #include #ifdef __cplusplus @@ -31,37 +41,66 @@ struct CSRMatrix; struct FlowBoundaryConditions; struct Wells; +/** + * Main data structure presenting a view of an assembled system of simultaneous + * linear equations which may be solved using external software. + */ struct ifs_tpfa_data { - struct CSRMatrix *A; - double *b; - double *x; + struct CSRMatrix *A; /**< Coefficient matrix */ + double *b; /**< Right-hand side */ + double *x; /**< Solution */ - struct ifs_tpfa_impl *pimpl; + struct ifs_tpfa_impl *pimpl; /**< Internal management structure */ }; +/** + * Solution variables. + */ struct ifs_tpfa_solution { - double *cell_press; - double *face_flux ; + double *cell_press; /**< Cell pressures */ + double *face_flux ; /**< Interface fluxes */ - double *well_press; /* BHP */ - double *well_flux ; /* Perforation (total) fluxes */ + double *well_press; /**< Bottom-hole pressures for each well */ + double *well_flux ; /**< Well connection total fluxes */ }; +/** + * Driving forces pertaining to a particular model setup. + */ struct ifs_tpfa_forces { - const double *src; - const struct FlowBoundaryConditions *bc ; + const double *src; /**< Explicit source terms */ + const struct FlowBoundaryConditions *bc ; /**< Boundary conditions */ - const struct Wells *W ; - const double *totmob; - const double *wdp ; + const struct Wells *W ; /**< Well topology */ + const double *totmob; /**< Total mobility in each cell */ + const double *wdp ; /**< Gravity adjustment at each perforation */ }; +/** + * Allocate TPFA management structure capable of assembling a system of + * simultaneous linear equations corresponding to a particular grid and well + * configuration. + * + * @param[in] G Grid. + * @param[in] W Well topology. + * @return Fully formed TPFA management structure if successful, @c NULL in case + * of allocation failure. + */ struct ifs_tpfa_data * ifs_tpfa_construct(struct UnstructuredGrid *G, struct Wells *W); +/** + * + * @param[in] G + * @param[in] F + * @param[in] trans + * @param[in] gpress + * @param[in,out] h + * @return + */ int ifs_tpfa_assemble(struct UnstructuredGrid *G , const struct ifs_tpfa_forces *F , diff --git a/opm/core/pressure/tpfa/trans_tpfa.h b/opm/core/pressure/tpfa/trans_tpfa.h index ab3fba76..5da1490f 100644 --- a/opm/core/pressure/tpfa/trans_tpfa.h +++ b/opm/core/pressure/tpfa/trans_tpfa.h @@ -20,23 +20,88 @@ #ifndef OPM_TRANS_TPFA_HEADER_INCLUDED #define OPM_TRANS_TPFA_HEADER_INCLUDED +/** + * \file + * Routines to assist in the calculation of two-point transmissibilities. + */ + #include #ifdef __cplusplus extern "C" { #endif +/** + * Calculate static, one-sided transmissibilities for use in the two-point flux + * approximation method. + * + * The one-sided transmissibilities are defined by the formula + * \f[ + * t_i = \frac{\vec{n}_f \mathsf{K}_c \vec{c}_c}{\lVert \vec{c}_c \rVert^2} + * \f] + * in which @c i is the half-face index corresponding to the cell-face index + * pair (c,f) and \f$\vec{c}_{cf} = \Bar{x}_f - \Bar{x}_c\f$ is the + * centroid difference vector. + * + * @param[in] G Grid. + * @param[in] perm Permeability. One symmetric, positive definite tensor + * per grid cell. + * @param[out] htrans One-sided transmissibilities. Array of size at least + * G->cell_facepos[ G->number_of_cells ]. + */ void -tpfa_htrans_compute(struct UnstructuredGrid *G, const double *perm, double *htrans); +tpfa_htrans_compute(struct UnstructuredGrid *G , + const double *perm , + double *htrans); +/** + * Compute two-point transmissibilities from one-sided transmissibilities. + * + * The two-point transmissibilities are given by the simple, near-harmonic + * average (save a factor of two) + * \f[ + * \mathsf{T}_f = \big(\frac{1}{t_1} + \frac{1}{t_2}\big)^{-1} + * = \frac{t_1t_2}{t_1 + t_2} + * \f] + * in which \f$t_1\f$ and \f$t_2\f$ are the one-sided transmissibilities that + * connect the neighbouring cells of face @c f. + * + * @param[in] G Grid. + * @param[in] htrans One-sided transmissibilities as defined by function + * tpfa_htrans_compute(). + * @param[out] trans Interface, two-point transmissibilities. Array of size + * at least G->number_of_faces. + */ void -tpfa_trans_compute(struct UnstructuredGrid *G, const double *htrans, double *trans); +tpfa_trans_compute(struct UnstructuredGrid *G , + const double *htrans, + double *trans ); +/** + * Calculate effective two-point transmissibilities from one-sided, total + * mobility weighted, transmissibilities. + * + * Specifically, compute the following product + * \f[ + * \mathsf{T}_f = \big(\frac{1}{\lambda_1t_1} + \frac{1}{\lambda_2t_2}\big)^{-1} + * = \lambda_1\lambda_2 \frac{t_1t_2}{t_1 + t_2} + * \f] + * in which \f$t_1\f$ and \f$t_2\f$ are the one-sided, static transmissibility + * values connecting the cells of face @c f and \f$\lambda_1\f$ and + * \f$\lambda_2\f$ denote the total mobilities of the respective cells. + * + * @param[in] G Grid. + * @param[in] totmob Total mobilities. One positive scalar value for each cell. + * @param[in] htrans One-sided transmissibilities as defined by function + * tpfa_htrans_compute(). + * @param[out] trans Effective, two-point transmissibilities. Array of size at + * least G->number_of_faces. + */ void -tpfa_eff_trans_compute(struct UnstructuredGrid *G, - const double *totmob, - const double *htrans, - double *trans); +tpfa_eff_trans_compute(struct UnstructuredGrid *G , + const double *totmob, + const double *htrans, + double *trans ); #ifdef __cplusplus } diff --git a/opm/core/simulator/SimulatorTwophase.cpp b/opm/core/simulator/SimulatorCompressibleTwophase.cpp similarity index 53% rename from opm/core/simulator/SimulatorTwophase.cpp rename to opm/core/simulator/SimulatorCompressibleTwophase.cpp index 9c1f9023..c5a77eb0 100644 --- a/opm/core/simulator/SimulatorTwophase.cpp +++ b/opm/core/simulator/SimulatorCompressibleTwophase.cpp @@ -21,11 +21,11 @@ #include "config.h" #endif // HAVE_CONFIG_H -#include +#include #include #include -#include +#include #include #include @@ -36,16 +36,19 @@ #include #include #include +#include -#include +#include + +#include #include #include -#include +#include #include -#include +#include -#include +#include #include #include @@ -56,21 +59,21 @@ namespace Opm { - class SimulatorTwophase::Impl + class SimulatorCompressibleTwophase::Impl { public: Impl(const parameter::ParameterGroup& param, const UnstructuredGrid& grid, - const IncompPropertiesInterface& props, + const BlackoilPropertiesInterface& props, const RockCompressibility* rock_comp, - const Wells* wells, + WellsManager& wells_manager, const std::vector& src, const FlowBoundaryConditions* bcs, LinearSolverInterface& linsolver, const double* gravity); SimulatorReport run(SimulatorTimer& timer, - TwophaseState& state, + BlackoilState& state, WellState& well_state); private: @@ -78,23 +81,27 @@ namespace Opm // Parameters for output. bool output_; + bool output_vtk_; std::string output_dir_; int output_interval_; + // Parameters for well control + bool check_well_controls_; + int max_well_control_iterations_; // Parameters for transport solver. int num_transport_substeps_; bool use_segregation_split_; // Observed objects. const UnstructuredGrid& grid_; - const IncompPropertiesInterface& props_; + const BlackoilPropertiesInterface& props_; const RockCompressibility* rock_comp_; + WellsManager& wells_manager_; const Wells* wells_; const std::vector& src_; const FlowBoundaryConditions* bcs_; - const LinearSolverInterface& linsolver_; const double* gravity_; // Solvers - IncompTpfa psolver_; - TransportModelTwophase tsolver_; + CompressibleTpfa psolver_; + TransportModelCompressibleTwophase tsolver_; // Needed by column-based gravity segregation solver. std::vector< std::vector > columns_; // Misc. data @@ -104,40 +111,48 @@ namespace Opm - SimulatorTwophase::SimulatorTwophase(const parameter::ParameterGroup& param, - const UnstructuredGrid& grid, - const IncompPropertiesInterface& props, - const RockCompressibility* rock_comp, - const Wells* wells, - const std::vector& src, - const FlowBoundaryConditions* bcs, - LinearSolverInterface& linsolver, - const double* gravity) + SimulatorCompressibleTwophase::SimulatorCompressibleTwophase(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const BlackoilPropertiesInterface& props, + const RockCompressibility* rock_comp, + WellsManager& wells_manager, + const std::vector& src, + const FlowBoundaryConditions* bcs, + LinearSolverInterface& linsolver, + const double* gravity) { - pimpl_.reset(new Impl(param, grid, props, rock_comp, wells, src, bcs, linsolver, gravity)); + pimpl_.reset(new Impl(param, grid, props, rock_comp, wells_manager, src, bcs, linsolver, gravity)); } - SimulatorReport SimulatorTwophase::run(SimulatorTimer& timer, - TwophaseState& state, - WellState& well_state) + SimulatorReport SimulatorCompressibleTwophase::run(SimulatorTimer& timer, + BlackoilState& state, + WellState& well_state) { return pimpl_->run(timer, state, well_state); } - static void outputState(const UnstructuredGrid& grid, - const Opm::TwophaseState& state, - const int step, - const std::string& output_dir) + static void outputStateVtk(const UnstructuredGrid& grid, + const Opm::BlackoilState& state, + const int step, + const std::string& output_dir) { // Write data in VTK format. std::ostringstream vtkfilename; - vtkfilename << output_dir << "/output-" << std::setw(3) << std::setfill('0') << step << ".vtu"; + vtkfilename << output_dir << "/vtk_files"; + boost::filesystem::path fpath(vtkfilename.str()); + try { + create_directories(fpath); + } + catch (...) { + THROW("Creating directories failed: " << fpath); + } + vtkfilename << "/output-" << std::setw(3) << std::setfill('0') << step << ".vtu"; std::ofstream vtkfile(vtkfilename.str().c_str()); if (!vtkfile) { THROW("Failed to open " << vtkfilename.str()); @@ -149,11 +164,33 @@ namespace Opm Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); dm["velocity"] = &cell_velocity; Opm::writeVtkData(grid, dm, vtkfile); + } + + + static void outputStateMatlab(const UnstructuredGrid& grid, + const Opm::BlackoilState& state, + const int step, + const std::string& output_dir) + { + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + std::vector cell_velocity; + Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); + dm["velocity"] = &cell_velocity; // Write data (not grid) in Matlab format for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { std::ostringstream fname; - fname << output_dir << "/" << it->first << "-" << std::setw(3) << std::setfill('0') << step << ".dat"; + fname << output_dir << "/" << it->first; + boost::filesystem::path fpath = fname.str(); + try { + create_directories(fpath); + } + catch (...) { + THROW("Creating directories failed: " << fpath); + } + fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; std::ofstream file(fname.str().c_str()); if (!file) { THROW("Failed to open " << fname.str()); @@ -191,30 +228,28 @@ namespace Opm - - - SimulatorTwophase::Impl::Impl(const parameter::ParameterGroup& param, - const UnstructuredGrid& grid, - const IncompPropertiesInterface& props, - const RockCompressibility* rock_comp, - const Wells* wells, - const std::vector& src, - const FlowBoundaryConditions* bcs, - LinearSolverInterface& linsolver, - const double* gravity) + // \TODO: make CompressibleTpfa take src and bcs. + SimulatorCompressibleTwophase::Impl::Impl(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const BlackoilPropertiesInterface& props, + const RockCompressibility* rock_comp, + WellsManager& wells_manager, + const std::vector& src, + const FlowBoundaryConditions* bcs, + LinearSolverInterface& linsolver, + const double* gravity) : grid_(grid), props_(props), rock_comp_(rock_comp), - wells_(wells), + wells_manager_(wells_manager), + wells_(wells_manager.c_wells()), src_(src), bcs_(bcs), - linsolver_(linsolver), - gravity_(gravity), psolver_(grid, props, rock_comp, linsolver, param.getDefault("nl_pressure_residual_tolerance", 0.0), param.getDefault("nl_pressure_change_tolerance", 1.0), param.getDefault("nl_pressure_maxiter", 10), - gravity, wells, src, bcs), + gravity, wells_manager.c_wells() /*, src, bcs*/), tsolver_(grid, props, param.getDefault("nl_tolerance", 1e-9), param.getDefault("nl_maxiter", 30)) @@ -222,6 +257,7 @@ namespace Opm // For output. output_ = param.getDefault("output", true); if (output_) { + output_vtk_ = param.getDefault("output_vtk", true); output_dir_ = param.getDefault("output_dir", std::string("output")); // Ensure that output dir exists boost::filesystem::path fpath(output_dir_); @@ -234,6 +270,10 @@ namespace Opm output_interval_ = param.getDefault("output_interval", 1); } + // Well control related init. + check_well_controls_ = param.getDefault("check_well_controls", false); + max_well_control_iterations_ = param.getDefault("max_well_control_iterations", 10); + // Transport related init. num_transport_substeps_ = param.getDefault("num_transport_substeps", 1); use_segregation_split_ = param.getDefault("use_segregation_split", false); @@ -253,9 +293,9 @@ namespace Opm - SimulatorReport SimulatorTwophase::Impl::run(SimulatorTimer& timer, - TwophaseState& state, - WellState& well_state) + SimulatorReport SimulatorCompressibleTwophase::Impl::run(SimulatorTimer& timer, + BlackoilState& state, + WellState& well_state) { std::vector transport_src; @@ -267,13 +307,14 @@ namespace Opm computePorevolume(grid_, props_.porosity(), porevol); } const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); - + std::vector initial_porevol = porevol; // Main simulation loop. Opm::time::StopWatch pressure_timer; double ptime = 0.0; Opm::time::StopWatch transport_timer; double ttime = 0.0; + Opm::time::StopWatch step_timer; Opm::time::StopWatch total_timer; total_timer.start(); double init_satvol[2] = { 0.0 }; @@ -292,27 +333,102 @@ namespace Opm std::vector well_resflows_phase; if (wells_) { well_resflows_phase.resize((wells_->number_of_phases)*(wells_->number_of_wells), 0.0); - wellreport.push(props_, *wells_, state.saturation(), 0.0, well_state.bhp(), well_state.perfRates()); + wellreport.push(props_, *wells_, + state.pressure(), state.surfacevol(), state.saturation(), + 0.0, well_state.bhp(), well_state.perfRates()); + } + std::fstream tstep_os; + if (output_) { + std::string filename = output_dir_ + "/step_timing.param"; + tstep_os.open(filename.c_str(), std::fstream::out | std::fstream::app); } for (; !timer.done(); ++timer) { // Report timestep and (optionally) write state to disk. + step_timer.start(); timer.report(std::cout); if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { - outputState(grid_, state, timer.currentStepNum(), output_dir_); + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); } - // Solve pressure. + SimulatorReport sreport; + + // Solve pressure equation. + if (check_well_controls_) { + computeFractionalFlow(props_, allcells_, + state.pressure(), state.surfacevol(), state.saturation(), + fractional_flows); + wells_manager_.applyExplicitReinjectionControls(well_resflows_phase, well_resflows_phase); + } + bool well_control_passed = !check_well_controls_; + int well_control_iteration = 0; do { + // Run solver. pressure_timer.start(); + std::vector initial_pressure = state.pressure(); psolver_.solve(timer.currentStepLength(), state, well_state); + + // Renormalize pressure if both fluids and rock are + // incompressible, and there are no pressure + // conditions (bcs or wells). It is deemed sufficient + // for now to renormalize using geometric volume + // instead of pore volume. + if (psolver_.singularPressure()) { + // Compute average pressures of previous and last + // step, and total volume. + double av_prev_press = 0.0; + double av_press = 0.0; + double tot_vol = 0.0; + const int num_cells = grid_.number_of_cells; + for (int cell = 0; cell < num_cells; ++cell) { + av_prev_press += initial_pressure[cell]*grid_.cell_volumes[cell]; + av_press += state.pressure()[cell]*grid_.cell_volumes[cell]; + tot_vol += grid_.cell_volumes[cell]; + } + // Renormalization constant + const double ren_const = (av_prev_press - av_press)/tot_vol; + for (int cell = 0; cell < num_cells; ++cell) { + state.pressure()[cell] += ren_const; + } + const int num_wells = (wells_ == NULL) ? 0 : wells_->number_of_wells; + for (int well = 0; well < num_wells; ++well) { + well_state.bhp()[well] += ren_const; + } + } + + // Stop timer and report. pressure_timer.stop(); double pt = pressure_timer.secsSinceStart(); std::cout << "Pressure solver took: " << pt << " seconds." << std::endl; ptime += pt; - } while (false); + sreport.pressure_time = pt; + + // Optionally, check if well controls are satisfied. + if (check_well_controls_) { + Opm::computePhaseFlowRatesPerWell(*wells_, + fractional_flows, + well_state.perfRates(), + well_resflows_phase); + std::cout << "Checking well conditions." << std::endl; + // For testing we set surface := reservoir + well_control_passed = wells_manager_.conditionsMet(well_state.bhp(), well_resflows_phase, well_resflows_phase); + ++well_control_iteration; + if (!well_control_passed && well_control_iteration > max_well_control_iterations_) { + THROW("Could not satisfy well conditions in " << max_well_control_iterations_ << " tries."); + } + if (!well_control_passed) { + std::cout << "Well controls not passed, solving again." << std::endl; + } else { + std::cout << "Well conditions met." << std::endl; + } + } + } while (!well_control_passed); // Update pore volumes if rock is compressible. if (rock_comp_ && rock_comp_->isActive()) { + initial_porevol = porevol; computePorevolume(grid_, props_.porosity(), *rock_comp_, state.pressure(), porevol); } @@ -328,18 +444,22 @@ namespace Opm std::cout << "Making " << num_transport_substeps_ << " transport substeps." << std::endl; } for (int tr_substep = 0; tr_substep < num_transport_substeps_; ++tr_substep) { - tsolver_.solve(&state.faceflux()[0], &porevol[0], &transport_src[0], - stepsize, state.saturation()); - Opm::computeInjectedProduced(props_, state.saturation(), transport_src, stepsize, injected, produced); - if (use_segregation_split_) { - tsolver_.solveGravity(columns_, &porevol[0], stepsize, state.saturation()); + tsolver_.solve(&state.faceflux()[0], &state.pressure()[0], + &porevol[0], &initial_porevol[0], &transport_src[0], stepsize, + state.saturation(), state.surfacevol()); + Opm::computeInjectedProduced(props_, + state.pressure(), state.surfacevol(), state.saturation(), + transport_src, stepsize, injected, produced); + if (gravity_ != 0 && use_segregation_split_) { + tsolver_.solveGravity(columns_, &state.pressure()[0], &initial_porevol[0], + stepsize, state.saturation(), state.surfacevol()); } } transport_timer.stop(); double tt = transport_timer.secsSinceStart(); + sreport.transport_time = tt; std::cout << "Transport solver took: " << tt << " seconds." << std::endl; ttime += tt; - // Report volume balances. Opm::computeSaturatedVol(porevol, state.saturation(), satvol); tot_injected[0] += injected[0]; @@ -377,18 +497,27 @@ namespace Opm produced[0]/(produced[0] + produced[1]), tot_produced[0]/tot_porevol_init); if (wells_) { - wellreport.push(props_, *wells_, state.saturation(), + wellreport.push(props_, *wells_, + state.pressure(), state.surfacevol(), state.saturation(), timer.currentTime() + timer.currentStepLength(), well_state.bhp(), well_state.perfRates()); } + sreport.total_time = step_timer.secsSinceStart(); + if (output_) { + sreport.reportParam(tstep_os); + } } if (output_) { - outputState(grid_, state, timer.currentStepNum(), output_dir_); + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); outputWaterCut(watercut, output_dir_); if (wells_) { outputWellReport(wellreport, output_dir_); } + tstep_os.close(); } total_timer.stop(); diff --git a/opm/core/simulator/SimulatorCompressibleTwophase.hpp b/opm/core/simulator/SimulatorCompressibleTwophase.hpp new file mode 100644 index 00000000..d38d74b2 --- /dev/null +++ b/opm/core/simulator/SimulatorCompressibleTwophase.hpp @@ -0,0 +1,99 @@ +/* + Copyright 2012 SINTEF ICT, Applied Mathematics. + + 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 . +*/ + +#ifndef OPM_SIMULATORCOMPRESSIBLETWOPHASE_HEADER_INCLUDED +#define OPM_SIMULATORCOMPRESSIBLETWOPHASE_HEADER_INCLUDED + +#include +#include + +struct UnstructuredGrid; +struct Wells; +struct FlowBoundaryConditions; + +namespace Opm +{ + namespace parameter { class ParameterGroup; } + class BlackoilPropertiesInterface; + class RockCompressibility; + class WellsManager; + class LinearSolverInterface; + class SimulatorTimer; + class BlackoilState; + class WellState; + struct SimulatorReport; + + /// Class collecting all necessary components for a two-phase simulation. + class SimulatorCompressibleTwophase + { + public: + /// Initialise from parameters and objects to observe. + /// \param[in] param parameters, this class accepts the following: + /// parameter (default) effect + /// ----------------------------------------------------------- + /// output (true) write output to files? + /// output_dir ("output") output directoty + /// output_interval (1) output every nth step + /// nl_pressure_residual_tolerance (0.0) pressure solver residual tolerance (in Pascal) + /// nl_pressure_change_tolerance (1.0) pressure solver change tolerance (in Pascal) + /// nl_pressure_maxiter (10) max nonlinear iterations in pressure + /// nl_maxiter (30) max nonlinear iterations in transport + /// nl_tolerance (1e-9) transport solver absolute residual tolerance + /// num_transport_substeps (1) number of transport steps per pressure step + /// use_segregation_split (false) solve for gravity segregation (if false, + /// segregation is ignored). + /// + /// \param[in] grid grid data structure + /// \param[in] props fluid and rock properties + /// \param[in] rock_comp if non-null, rock compressibility properties + /// \param[in] well_manager well manager, may manage no (null) wells + /// \param[in] src source terms + /// \param[in] bcs boundary conditions, treat as all noflow if null + /// \param[in] linsolver linear solver + /// \param[in] gravity if non-null, gravity vector + SimulatorCompressibleTwophase(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const BlackoilPropertiesInterface& props, + const RockCompressibility* rock_comp, + WellsManager& wells_manager, + const std::vector& src, + const FlowBoundaryConditions* bcs, + LinearSolverInterface& linsolver, + const double* gravity); + + /// Run the simulation. + /// This will run succesive timesteps until timer.done() is true. It will + /// modify the reservoir and well states. + /// \param[in,out] timer governs the requested reporting timesteps + /// \param[in,out] state state of reservoir: pressure, fluxes + /// \param[in,out] well_state state of wells: bhp, perforation rates + /// \return simulation report, with timing data + SimulatorReport run(SimulatorTimer& timer, + BlackoilState& state, + WellState& well_state); + + private: + class Impl; + // Using shared_ptr instead of scoped_ptr since scoped_ptr requires complete type for Impl. + boost::shared_ptr pimpl_; + }; + +} // namespace Opm + +#endif // OPM_SIMULATORCOMPRESSIBLETWOPHASE_HEADER_INCLUDED diff --git a/opm/core/simulator/SimulatorIncompTwophase.cpp b/opm/core/simulator/SimulatorIncompTwophase.cpp new file mode 100644 index 00000000..1f7e1119 --- /dev/null +++ b/opm/core/simulator/SimulatorIncompTwophase.cpp @@ -0,0 +1,586 @@ +/* + Copyright 2012 SINTEF ICT, Applied Mathematics. + + 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 . +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + + +namespace Opm +{ + + class SimulatorIncompTwophase::Impl + { + public: + Impl(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const IncompPropertiesInterface& props, + const RockCompressibility* rock_comp, + WellsManager& wells_manager, + const std::vector& src, + const FlowBoundaryConditions* bcs, + LinearSolverInterface& linsolver, + const double* gravity); + + SimulatorReport run(SimulatorTimer& timer, + TwophaseState& state, + WellState& well_state); + + private: + // Data. + // Parameters for output. + bool output_; + bool output_vtk_; + std::string output_dir_; + int output_interval_; + // Parameters for well control + bool check_well_controls_; + int max_well_control_iterations_; + // Parameters for transport solver. + int num_transport_substeps_; + bool use_segregation_split_; + // Observed objects. + const UnstructuredGrid& grid_; + const IncompPropertiesInterface& props_; + const RockCompressibility* rock_comp_; + WellsManager& wells_manager_; + const Wells* wells_; + const std::vector& src_; + const FlowBoundaryConditions* bcs_; + // Solvers + IncompTpfa psolver_; + TransportModelTwophase tsolver_; + // Needed by column-based gravity segregation solver. + std::vector< std::vector > columns_; + // Misc. data + std::vector allcells_; + }; + + + + + SimulatorIncompTwophase::SimulatorIncompTwophase(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const IncompPropertiesInterface& props, + const RockCompressibility* rock_comp, + WellsManager& wells_manager, + const std::vector& src, + const FlowBoundaryConditions* bcs, + LinearSolverInterface& linsolver, + const double* gravity) + { + pimpl_.reset(new Impl(param, grid, props, rock_comp, wells_manager, src, bcs, linsolver, gravity)); + } + + + + + + SimulatorReport SimulatorIncompTwophase::run(SimulatorTimer& timer, + TwophaseState& state, + WellState& well_state) + { + return pimpl_->run(timer, state, well_state); + } + + static void reportVolumes(std::ostream &os, double satvol[2], double tot_porevol_init, + double tot_injected[2], double tot_produced[2], + double injected[2], double produced[2], + double init_satvol[2]) + { + std::cout.precision(5); + const int width = 18; + os << "\nVolume balance report (all numbers relative to total pore volume).\n"; + os << " Saturated volumes: " + << std::setw(width) << satvol[0]/tot_porevol_init + << std::setw(width) << satvol[1]/tot_porevol_init << std::endl; + os << " Injected volumes: " + << std::setw(width) << injected[0]/tot_porevol_init + << std::setw(width) << injected[1]/tot_porevol_init << std::endl; + os << " Produced volumes: " + << std::setw(width) << produced[0]/tot_porevol_init + << std::setw(width) << produced[1]/tot_porevol_init << std::endl; + os << " Total inj volumes: " + << std::setw(width) << tot_injected[0]/tot_porevol_init + << std::setw(width) << tot_injected[1]/tot_porevol_init << std::endl; + os << " Total prod volumes: " + << std::setw(width) << tot_produced[0]/tot_porevol_init + << std::setw(width) << tot_produced[1]/tot_porevol_init << std::endl; + os << " In-place + prod - inj: " + << std::setw(width) << (satvol[0] + tot_produced[0] - tot_injected[0])/tot_porevol_init + << std::setw(width) << (satvol[1] + tot_produced[1] - tot_injected[1])/tot_porevol_init << std::endl; + os << " Init - now - pr + inj: " + << std::setw(width) << (init_satvol[0] - satvol[0] - tot_produced[0] + tot_injected[0])/tot_porevol_init + << std::setw(width) << (init_satvol[1] - satvol[1] - tot_produced[1] + tot_injected[1])/tot_porevol_init + << std::endl; + os.precision(8); + } + + static void outputStateVtk(const UnstructuredGrid& grid, + const Opm::TwophaseState& state, + const int step, + const std::string& output_dir) + { + // Write data in VTK format. + std::ostringstream vtkfilename; + vtkfilename << output_dir << "/vtk_files"; + boost::filesystem::path fpath(vtkfilename.str()); + try { + create_directories(fpath); + } + catch (...) { + THROW("Creating directories failed: " << fpath); + } + vtkfilename << "/output-" << std::setw(3) << std::setfill('0') << step << ".vtu"; + std::ofstream vtkfile(vtkfilename.str().c_str()); + if (!vtkfile) { + THROW("Failed to open " << vtkfilename.str()); + } + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + std::vector cell_velocity; + Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); + dm["velocity"] = &cell_velocity; + Opm::writeVtkData(grid, dm, vtkfile); + } + + static void outputVectorMatlab(const std::string& name, + const std::vector& vec, + const int step, + const std::string& output_dir) + { + std::ostringstream fname; + fname << output_dir << "/" << name; + boost::filesystem::path fpath = fname.str(); + try { + create_directories(fpath); + } + catch (...) { + THROW("Creating directories failed: " << fpath); + } + fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; + std::ofstream file(fname.str().c_str()); + if (!file) { + THROW("Failed to open " << fname.str()); + } + std::copy(vec.begin(), vec.end(), std::ostream_iterator(file, "\n")); + } + + static void outputStateMatlab(const UnstructuredGrid& grid, + const Opm::TwophaseState& state, + const int step, + const std::string& output_dir) + { + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + std::vector cell_velocity; + Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); + dm["velocity"] = &cell_velocity; + + // Write data (not grid) in Matlab format + for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { + std::ostringstream fname; + fname << output_dir << "/" << it->first; + boost::filesystem::path fpath = fname.str(); + try { + create_directories(fpath); + } + catch (...) { + THROW("Creating directories failed: " << fpath); + } + fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; + std::ofstream file(fname.str().c_str()); + if (!file) { + THROW("Failed to open " << fname.str()); + } + const std::vector& d = *(it->second); + std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); + } + } + + + static void outputWaterCut(const Opm::Watercut& watercut, + const std::string& output_dir) + { + // Write water cut curve. + std::string fname = output_dir + "/watercut.txt"; + std::ofstream os(fname.c_str()); + if (!os) { + THROW("Failed to open " << fname); + } + watercut.write(os); + } + + + static void outputWellReport(const Opm::WellReport& wellreport, + const std::string& output_dir) + { + // Write well report. + std::string fname = output_dir + "/wellreport.txt"; + std::ofstream os(fname.c_str()); + if (!os) { + THROW("Failed to open " << fname); + } + wellreport.write(os); + } + + + static bool allNeumannBCs(const FlowBoundaryConditions* bcs) + { + if (bcs == NULL) { + return true; + } else { + return std::find(bcs->type, bcs->type + bcs->nbc, BC_PRESSURE) + == bcs->type + bcs->nbc; + } + } + + + static bool allRateWells(const Wells* wells) + { + if (wells == NULL) { + return true; + } + const int nw = wells->number_of_wells; + for (int w = 0; w < nw; ++w) { + const WellControls* wc = wells->ctrls[w]; + if (wc->current >= 0) { + if (wc->type[wc->current] == BHP) { + return false; + } + } + } + return true; + } + + + + + + SimulatorIncompTwophase::Impl::Impl(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const IncompPropertiesInterface& props, + const RockCompressibility* rock_comp, + WellsManager& wells_manager, + const std::vector& src, + const FlowBoundaryConditions* bcs, + LinearSolverInterface& linsolver, + const double* gravity) + : grid_(grid), + props_(props), + rock_comp_(rock_comp), + wells_manager_(wells_manager), + wells_(wells_manager.c_wells()), + src_(src), + bcs_(bcs), + psolver_(grid, props, rock_comp, linsolver, + param.getDefault("nl_pressure_residual_tolerance", 0.0), + param.getDefault("nl_pressure_change_tolerance", 1.0), + param.getDefault("nl_pressure_maxiter", 10), + gravity, wells_manager.c_wells(), src, bcs), + tsolver_(grid, props, + param.getDefault("nl_tolerance", 1e-9), + param.getDefault("nl_maxiter", 30)) + { + // For output. + output_ = param.getDefault("output", true); + if (output_) { + output_vtk_ = param.getDefault("output_vtk", true); + output_dir_ = param.getDefault("output_dir", std::string("output")); + // Ensure that output dir exists + boost::filesystem::path fpath(output_dir_); + try { + create_directories(fpath); + } + catch (...) { + THROW("Creating directories failed: " << fpath); + } + output_interval_ = param.getDefault("output_interval", 1); + } + + // Well control related init. + check_well_controls_ = param.getDefault("check_well_controls", false); + max_well_control_iterations_ = param.getDefault("max_well_control_iterations", 10); + + // Transport related init. + num_transport_substeps_ = param.getDefault("num_transport_substeps", 1); + use_segregation_split_ = param.getDefault("use_segregation_split", false); + if (gravity != 0 && use_segregation_split_){ + tsolver_.initGravity(gravity); + extractColumn(grid_, columns_); + } + + // Misc init. + const int num_cells = grid.number_of_cells; + allcells_.resize(num_cells); + for (int cell = 0; cell < num_cells; ++cell) { + allcells_[cell] = cell; + } + } + + + + + SimulatorReport SimulatorIncompTwophase::Impl::run(SimulatorTimer& timer, + TwophaseState& state, + WellState& well_state) + { + std::vector transport_src; + + // Initialisation. + std::vector porevol; + if (rock_comp_ && rock_comp_->isActive()) { + computePorevolume(grid_, props_.porosity(), *rock_comp_, state.pressure(), porevol); + } else { + computePorevolume(grid_, props_.porosity(), porevol); + } + const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); + std::vector initial_porevol = porevol; + + // Main simulation loop. + Opm::time::StopWatch pressure_timer; + double ptime = 0.0; + Opm::time::StopWatch transport_timer; + double ttime = 0.0; + Opm::time::StopWatch step_timer; + Opm::time::StopWatch total_timer; + total_timer.start(); + double init_satvol[2] = { 0.0 }; + double satvol[2] = { 0.0 }; + double injected[2] = { 0.0 }; + double produced[2] = { 0.0 }; + double tot_injected[2] = { 0.0 }; + double tot_produced[2] = { 0.0 }; + Opm::computeSaturatedVol(porevol, state.saturation(), init_satvol); + std::cout << "\nInitial saturations are " << init_satvol[0]/tot_porevol_init + << " " << init_satvol[1]/tot_porevol_init << std::endl; + Opm::Watercut watercut; + watercut.push(0.0, 0.0, 0.0); + Opm::WellReport wellreport; + std::vector fractional_flows; + std::vector well_resflows_phase; + if (wells_) { + well_resflows_phase.resize((wells_->number_of_phases)*(wells_->number_of_wells), 0.0); + wellreport.push(props_, *wells_, state.saturation(), 0.0, well_state.bhp(), well_state.perfRates()); + } + std::fstream tstep_os; + if (output_) { + std::string filename = output_dir_ + "/step_timing.param"; + tstep_os.open(filename.c_str(), std::fstream::out | std::fstream::app); + } + for (; !timer.done(); ++timer) { + // Report timestep and (optionally) write state to disk. + step_timer.start(); + timer.report(std::cout); + if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); + outputVectorMatlab(std::string("reorder_it"), + tsolver_.getReorderIterations(), + timer.currentStepNum(), output_dir_); + } + + SimulatorReport sreport; + + // Solve pressure equation. + if (check_well_controls_) { + computeFractionalFlow(props_, allcells_, state.saturation(), fractional_flows); + wells_manager_.applyExplicitReinjectionControls(well_resflows_phase, well_resflows_phase); + } + bool well_control_passed = !check_well_controls_; + int well_control_iteration = 0; + do { + // Run solver. + pressure_timer.start(); + std::vector initial_pressure = state.pressure(); + psolver_.solve(timer.currentStepLength(), state, well_state); + + // Renormalize pressure if rock is incompressible, and + // there are no pressure conditions (bcs or wells). + // It is deemed sufficient for now to renormalize + // using geometric volume instead of pore volume. + if ((rock_comp_ == NULL || !rock_comp_->isActive()) + && allNeumannBCs(bcs_) && allRateWells(wells_)) { + // Compute average pressures of previous and last + // step, and total volume. + double av_prev_press = 0.0; + double av_press = 0.0; + double tot_vol = 0.0; + const int num_cells = grid_.number_of_cells; + for (int cell = 0; cell < num_cells; ++cell) { + av_prev_press += initial_pressure[cell]*grid_.cell_volumes[cell]; + av_press += state.pressure()[cell]*grid_.cell_volumes[cell]; + tot_vol += grid_.cell_volumes[cell]; + } + // Renormalization constant + const double ren_const = (av_prev_press - av_press)/tot_vol; + for (int cell = 0; cell < num_cells; ++cell) { + state.pressure()[cell] += ren_const; + } + const int num_wells = (wells_ == NULL) ? 0 : wells_->number_of_wells; + for (int well = 0; well < num_wells; ++well) { + well_state.bhp()[well] += ren_const; + } + } + + // Stop timer and report. + pressure_timer.stop(); + double pt = pressure_timer.secsSinceStart(); + std::cout << "Pressure solver took: " << pt << " seconds." << std::endl; + ptime += pt; + sreport.pressure_time = pt; + + // Optionally, check if well controls are satisfied. + if (check_well_controls_) { + Opm::computePhaseFlowRatesPerWell(*wells_, + fractional_flows, + well_state.perfRates(), + well_resflows_phase); + std::cout << "Checking well conditions." << std::endl; + // For testing we set surface := reservoir + well_control_passed = wells_manager_.conditionsMet(well_state.bhp(), well_resflows_phase, well_resflows_phase); + ++well_control_iteration; + if (!well_control_passed && well_control_iteration > max_well_control_iterations_) { + THROW("Could not satisfy well conditions in " << max_well_control_iterations_ << " tries."); + } + if (!well_control_passed) { + std::cout << "Well controls not passed, solving again." << std::endl; + } else { + std::cout << "Well conditions met." << std::endl; + } + } + } while (!well_control_passed); + + // Update pore volumes if rock is compressible. + if (rock_comp_ && rock_comp_->isActive()) { + initial_porevol = porevol; + computePorevolume(grid_, props_.porosity(), *rock_comp_, state.pressure(), porevol); + } + + // Process transport sources (to include bdy terms and well flows). + Opm::computeTransportSource(grid_, src_, state.faceflux(), 1.0, + wells_, well_state.perfRates(), transport_src); + + // Solve transport. + transport_timer.start(); + double stepsize = timer.currentStepLength(); + if (num_transport_substeps_ != 1) { + stepsize /= double(num_transport_substeps_); + std::cout << "Making " << num_transport_substeps_ << " transport substeps." << std::endl; + } + for (int tr_substep = 0; tr_substep < num_transport_substeps_; ++tr_substep) { + tsolver_.solve(&state.faceflux()[0], &initial_porevol[0], &transport_src[0], + stepsize, state.saturation()); + Opm::computeInjectedProduced(props_, state.saturation(), transport_src, stepsize, injected, produced); + if (use_segregation_split_) { + tsolver_.solveGravity(columns_, &initial_porevol[0], stepsize, state.saturation()); + } + watercut.push(timer.currentTime() + timer.currentStepLength(), + produced[0]/(produced[0] + produced[1]), + tot_produced[0]/tot_porevol_init); + if (wells_) { + wellreport.push(props_, *wells_, state.saturation(), + timer.currentTime() + timer.currentStepLength(), + well_state.bhp(), well_state.perfRates()); + } + } + transport_timer.stop(); + double tt = transport_timer.secsSinceStart(); + sreport.transport_time = tt; + std::cout << "Transport solver took: " << tt << " seconds." << std::endl; + ttime += tt; + // Report volume balances. + Opm::computeSaturatedVol(porevol, state.saturation(), satvol); + tot_injected[0] += injected[0]; + tot_injected[1] += injected[1]; + tot_produced[0] += produced[0]; + tot_produced[1] += produced[1]; + reportVolumes(std::cout,satvol, tot_porevol_init, + tot_injected, tot_produced, + injected, produced, + init_satvol); + sreport.total_time = step_timer.secsSinceStart(); + if (output_) { + sreport.reportParam(tstep_os); + } + } + + if (output_) { + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); + outputVectorMatlab(std::string("reorder_it"), + tsolver_.getReorderIterations(), + timer.currentStepNum(), output_dir_); + outputWaterCut(watercut, output_dir_); + if (wells_) { + outputWellReport(wellreport, output_dir_); + } + tstep_os.close(); + } + + total_timer.stop(); + + SimulatorReport report; + report.pressure_time = ptime; + report.transport_time = ttime; + report.total_time = total_timer.secsSinceStart(); + return report; + } + + +} // namespace Opm diff --git a/opm/core/simulator/SimulatorTwophase.hpp b/opm/core/simulator/SimulatorIncompTwophase.hpp similarity index 70% rename from opm/core/simulator/SimulatorTwophase.hpp rename to opm/core/simulator/SimulatorIncompTwophase.hpp index 5baa161e..10c46ff0 100644 --- a/opm/core/simulator/SimulatorTwophase.hpp +++ b/opm/core/simulator/SimulatorIncompTwophase.hpp @@ -17,8 +17,8 @@ along with OPM. If not, see . */ -#ifndef OPM_SIMULATORTWOPHASE_HEADER_INCLUDED -#define OPM_SIMULATORTWOPHASE_HEADER_INCLUDED +#ifndef OPM_SIMULATORINCOMPTWOPHASE_HEADER_INCLUDED +#define OPM_SIMULATORINCOMPTWOPHASE_HEADER_INCLUDED #include #include @@ -32,14 +32,15 @@ namespace Opm namespace parameter { class ParameterGroup; } class IncompPropertiesInterface; class RockCompressibility; + class WellsManager; class LinearSolverInterface; class SimulatorTimer; class TwophaseState; class WellState; - class SimulatorReport; + struct SimulatorReport; /// Class collecting all necessary components for a two-phase simulation. - class SimulatorTwophase + class SimulatorIncompTwophase { public: /// Initialise from parameters and objects to observe. @@ -58,23 +59,23 @@ namespace Opm /// use_segregation_split (false) solve for gravity segregation (if false, /// segregation is ignored). /// - /// \param[in] grid grid data structure - /// \param[in] props fluid and rock properties - /// \param[in] rock_comp if non-null, rock compressibility properties - /// \param[in] wells if non-null, wells data structure - /// \param[in] src source terms - /// \param[in] bcs boundary conditions, treat as all noflow if null - /// \param[in] linsolver linear solver - /// \param[in] gravity if non-null, gravity vector - SimulatorTwophase(const parameter::ParameterGroup& param, - const UnstructuredGrid& grid, - const IncompPropertiesInterface& props, - const RockCompressibility* rock_comp, - const Wells* wells, - const std::vector& src, - const FlowBoundaryConditions* bcs, - LinearSolverInterface& linsolver, - const double* gravity); + /// \param[in] grid grid data structure + /// \param[in] props fluid and rock properties + /// \param[in] rock_comp if non-null, rock compressibility properties + /// \param[in] well_manager well manager, may manage no (null) wells + /// \param[in] src source terms + /// \param[in] bcs boundary conditions, treat as all noflow if null + /// \param[in] linsolver linear solver + /// \param[in] gravity if non-null, gravity vector + SimulatorIncompTwophase(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const IncompPropertiesInterface& props, + const RockCompressibility* rock_comp, + WellsManager& wells_manager, + const std::vector& src, + const FlowBoundaryConditions* bcs, + LinearSolverInterface& linsolver, + const double* gravity); /// Run the simulation. /// This will run succesive timesteps until timer.done() is true. It will @@ -95,4 +96,4 @@ namespace Opm } // namespace Opm -#endif // OPM_SIMULATORTWOPHASE_HEADER_INCLUDED +#endif // OPM_SIMULATORINCOMPTWOPHASE_HEADER_INCLUDED diff --git a/opm/core/simulator/SimulatorReport.cpp b/opm/core/simulator/SimulatorReport.cpp index 1fa26459..76326260 100644 --- a/opm/core/simulator/SimulatorReport.cpp +++ b/opm/core/simulator/SimulatorReport.cpp @@ -42,6 +42,12 @@ namespace Opm << "\n Pressure time: " << pressure_time << "\n Transport time: " << transport_time << std::endl; } + void SimulatorReport::reportParam(std::ostream& os) + { + os << "/timing/total_time=" << total_time + << "\n/timing/pressure/total_time=" << pressure_time + << "\n/timing/transport/total_time=" << transport_time << std::endl; + } } // namespace Opm diff --git a/opm/core/simulator/SimulatorReport.hpp b/opm/core/simulator/SimulatorReport.hpp index a763156c..8e7cb2e8 100644 --- a/opm/core/simulator/SimulatorReport.hpp +++ b/opm/core/simulator/SimulatorReport.hpp @@ -38,6 +38,7 @@ namespace Opm void operator+=(const SimulatorReport& sr); /// Print a report to the given stream. void report(std::ostream& os); + void reportParam(std::ostream& os); }; } // namespace Opm diff --git a/opm/core/transport/ImplicitAssembly.hpp b/opm/core/transport/ImplicitAssembly.hpp index 88f7ac11..20958071 100644 --- a/opm/core/transport/ImplicitAssembly.hpp +++ b/opm/core/transport/ImplicitAssembly.hpp @@ -47,7 +47,9 @@ namespace Opm { public: ImplicitAssembly(Model& model) - : model_(model) + : model_(model), + nconn_(-1) , + asm_buffer_() {} template . */ +/** + * \file + * Numerical model and support classes needed to model transport of two + * incompressible fluid phases. Intended for the ImplicitTransport system. + */ + #ifndef OPM_SINGLEPOINTUPWINDTWOPHASE_HPP_HEADER #define OPM_SINGLEPOINTUPWINDTWOPHASE_HPP_HEADER @@ -45,8 +51,43 @@ namespace Opm { namespace spu_2p { + /** + * Internal class to manage the direct and derived quantities needed to + * formulate the fluid transport system. + * + * Note: This class elides off-diagonal elements of the phase mobility + * Jacobian, \f$(\partial_{s_\beta} \lambda_\alpha)_{\alpha\beta}\f$. + * These elements are assumed to be strictly equal to zero. In other + * words, the relative permeability of phase \f$\alpha\f$ is assumed to + * depend only on the saturation of phase \f$\alpha\f$. This convention + * allows storing only the two diagonals of the mobility Jacobian per + * grid cell. + * + * The static gravity term is the scalar value + * \f[ + * \Delta G_i = \mathsf{T}_f\, \vec{g}\cdot(\Bar{x}_f - \Bar{x}_c) + * \f] + * in which @c i is the half face index corresponding to the cell-face + * pair (f,c) and \f$\mathsf{T}_f\f$ is the absolute + * (bacground) two-point transmissibility of face @c f. + * + * The fluid transport problem is formulated in terms of saturation + * changes, \f$\Delta s\f$, per cell. These changes are the primary + * degrees of freedom in this model. + * + * Capillary pressures are defined by the fluid model, but usually + * correspond to \f$p_w - p_n\f$ (e.g., \f$p_\mathit{oil} - + * p_\mathit{water}\f$). + */ class ModelParameterStorage { public: + /** + * Constructor. + * + * @param[in] nc Total number of grid cells. + * @param[in] totconn Total number of connections, accumulated per + * cell (``half faces''). + */ ModelParameterStorage(int nc, int totconn) : drho_(0.0), mob_(0), dmob_(0), porevol_(0), dg_(0), ds_(0), pc_(0), dpc_(0), trans_(0), @@ -74,44 +115,160 @@ namespace Opm { trans_ = dpc_ + (1 * nc ); } + /** + * Modifiable density difference. + * @return Reference to modifiable internal representation of fluid + * phase density difference. + */ double& drho () { return drho_ ; } + /** + * Read-only density difference. + * @return Read-only value of current fluid phase difference value. + */ double drho () const { return drho_ ; } + /** + * Phase mobility in cell. + * @param[in] c Cell. + * @return Read-write reference to two consecutive phase mobilities + * in cell @c c. + */ double* mob (int c) { return mob_ + (2*c + 0); } + /** + * Phase mobility in cell. + * @param[in] c Cell. + * @return Read-only reference to two consecutive phase mobilities + * in cell @c c. + */ const double* mob (int c) const { return mob_ + (2*c + 0); } + /** + * Diagonal elements of phase mobility derivative (Jacobian). + * + * @param[in] c Cell. + * @return Read-write reference to diagonal elements of phase + * mobility Jacobian in cell @c c. + */ double* dmob (int c) { return dmob_ + (2*c + 0); } + /** + * Diagonal elements of phase mobility derivative (Jacobian). + * + * @param[in] c Cell. + * @return Read-only reference to two consecutive diagonal elements + * of phase mobility Jacobian in cell @c c. + */ const double* dmob (int c) const { return dmob_ + (2*c + 0); } + /** + * Retrieve pore volumes for all cells. + * @return Modifiable vector of pore volumes for all cells. + */ double* porevol() { return porevol_ ; } + /** + * Pore volume of single cell. + * @param[in] c Cell. + * @return Pore volume of cell @c c. + */ double porevol(int c) const { return porevol_[c] ; } + /** + * Static gravity term associated to single half face. + * + * @param[in] i Half face index corresponding to particular + * cell-face pair. + * @return Read-write reference to static gravity term of single + * half face. + */ double& dg(int i) { return dg_[i] ; } + /** + * Static gravity term associated to single half face. + * @param[in] i Half face index corresponding to particular + * cell-face pair. + * @return Read-only reference to static gravity term of single half + * face. + */ double dg(int i) const { return dg_[i] ; } + /** + * Saturation change in particular cell. + * + * @param[in] c + * @return Read-write reference to saturation change (scalar) in + * cell @c c. + */ double& ds(int c) { return ds_[c] ; } + /** + * Saturation change in particular cell. + * + * @param[in] c + * @return Read-only reference to saturation change (scalar) in cell + * @c c. + */ double ds(int c) const { return ds_[c] ; } + /** + * Capillary pressure in particular cell. + * + * @param[in] c Cell. + * @return Read-write reference to capillary pressure in cell @c c. + */ double& pc(int c) { return pc_[c] ; } + /** + * Capillary pressure in particular cell. + * + * @param[in] c Cell + * @return Read-only reference to capillary pressure in cell @c c. + */ double pc(int c) const { return pc_[c] ; } + /** + * Derivative of capillary pressure with respect to saturation. + * + * @param[in] c Cell + * @return Read-write reference to capillary pressure derivative + * with respect to primary saturation in cell @c c. + */ double& dpc(int c) { return dpc_[c] ; } + /** + * Derivative of capillary pressure with respect to saturation. + * + * @param[in] c Cell + * @return Read-only reference to capillary pressure derivative with + * respect to primary saturation in cell @c c. + */ double dpc(int c) const { return dpc_[c] ; } + /** + * Background (absolute) face transmissibility of particular face. + * + * @param[in] f Face + * @return Read-write reference to background face transmissibility + * of face @c f. + */ double& trans(int f) { return trans_[f] ; } + /** + * Background (absolute) face transmissibility of particular face. + * + * @param[in] f Face + * @return Read-only reference to bacground face transmissibility of + * face @c f. + */ double trans(int f) const { return trans_[f] ; } private: - double drho_ ; - double *mob_ ; - double *dmob_ ; - double *porevol_; - double *dg_ ; - double *ds_ ; - double *pc_ ; - double *dpc_ ; - double *trans_ ; + double drho_ ; /**< Fluid phase density difference */ + double *mob_ ; /**< Fluid phase mobility in all cells */ + double *dmob_ ; /**< Derivative of phase mobility in all cells */ + double *porevol_; /**< Pore volume in all cells */ + double *dg_ ; /**< Static gravity term on all half faces */ + double *ds_ ; /**< Saturation change in all cells */ + double *pc_ ; /**< Capillary pressure in all cells */ + double *dpc_ ; /**< Derivative of cap. pressure in all cells */ + double *trans_ ; /**< Absolute transmissibility on all faces */ + /** + * Data storage from which individual quantities are managed. + */ std::vector data_; }; } @@ -149,42 +306,42 @@ namespace Opm { std::copy(porevol.begin(), porevol.end(), store_.porevol()); } - void makefhfQPeriodic( const std::vector& p_faces,const std::vector& hf_faces, - const std::vector& nb_faces) + + void + makefhfQPeriodic(const std::vector& p_faces , + const std::vector& hf_faces, + const std::vector& nb_faces) { + assert (! p_faces.empty()); + assert (p_faces.size() == hf_faces.size()); + assert (hf_faces.size() == nb_faces.size()); + std::vector nbhf(hf_faces.size()); - for(unsigned int i=0; i::size_type i = 0; i < p_faces.size(); ++i) { + const int nbf = nb_faces[i]; + + assert (2*std::vector::size_type(nbf) + 1 < f2hf_.size()); + assert ((f2hf_[2*nbf + 0] < 0) ^ (f2hf_[2*nbf + 1] < 0)); + + const int p = (f2hf_[2*nbf + 0] < 0) ? 1 : 0; // "Self" + nbhf[ i ] = f2hf_[2*nbf + p]; } - for(unsigned int i=0; i::size_type i = 0; i < p_faces.size(); ++i) { + const int f = p_faces [i]; + const int hf = hf_faces[i]; - if(f2hf_[2*f] == hf){ - assert(f2hf_[2*f+1]==-1); - }else{ - assert(f2hf_[2*f]==-1); - f2hf_[2*f]=nbhf[i]; - changed=true; - } - if(!changed){ - if(f2hf_[2*f+1]== hf){ - assert(f2hf_[2*f]==-1); - }else{ - assert(f2hf_[2*f+1]==-1); - f2hf_[2*f+1]=nbhf[i]; - changed=true; - } - } - assert(changed); + assert (0 <= f); + assert (0 <= hf); + assert (2*std::vector::size_type(f) + 1 < f2hf_.size()); + + assert ((f2hf_[2*f + 0] < 0 ) ^ (f2hf_[2*f + 1] < 0 )); + assert ((f2hf_[2*f + 0] == hf) ^ (f2hf_[2*f + 1] == hf)); + + const int p = (f2hf_[2*f + 0] == hf) ? 1 : 0; // "Other" + + f2hf_[2*f + p] = nbhf[ i ]; } } diff --git a/opm/core/transport/reorder/TransportModelCompressibleTwophase.cpp b/opm/core/transport/reorder/TransportModelCompressibleTwophase.cpp index c04aac0a..01882053 100644 --- a/opm/core/transport/reorder/TransportModelCompressibleTwophase.cpp +++ b/opm/core/transport/reorder/TransportModelCompressibleTwophase.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -38,263 +39,277 @@ namespace Opm typedef RegulaFalsi RootFinder; - TransportModelCompressibleTwophase::TransportModelCompressibleTwophase(const UnstructuredGrid& grid, - const Opm::BlackoilPropertiesInterface& props, - const double tol, - const int maxit) - : grid_(grid), - props_(props), - tol_(tol), - maxit_(maxit), - darcyflux_(0), - source_(0), - dt_(0.0), - saturation_(grid.number_of_cells, -1.0), - fractionalflow_(grid.number_of_cells, -1.0), - mob_(2*grid.number_of_cells, -1.0), + TransportModelCompressibleTwophase::TransportModelCompressibleTwophase( + const UnstructuredGrid& grid, + const Opm::BlackoilPropertiesInterface& props, + const double tol, + const int maxit) + : grid_(grid), + props_(props), + tol_(tol), + maxit_(maxit), + darcyflux_(0), + source_(0), + dt_(0.0), + saturation_(grid.number_of_cells, -1.0), + fractionalflow_(grid.number_of_cells, -1.0), + gravity_(0), + mob_(2*grid.number_of_cells, -1.0), ia_upw_(grid.number_of_cells + 1, -1), - ja_upw_(grid.number_of_faces, -1), - ia_downw_(grid.number_of_cells + 1, -1), - ja_downw_(grid.number_of_faces, -1) + ja_upw_(grid.number_of_faces, -1), + ia_downw_(grid.number_of_cells + 1, -1), + ja_downw_(grid.number_of_faces, -1) { - if (props.numPhases() != 2) { - THROW("Property object must have 2 phases"); - } + if (props.numPhases() != 2) { + THROW("Property object must have 2 phases"); + } int np = props.numPhases(); - int num_cells = props.numCells(); - visc_.resize(np*num_cells); + int num_cells = props.numCells(); + visc_.resize(np*num_cells); A_.resize(np*np*num_cells); - smin_.resize(np*num_cells); - smax_.resize(np*num_cells); - allcells_.resize(num_cells); - for (int i = 0; i < num_cells; ++i) { - allcells_[i] = i; - } - props.satRange(props.numCells(), &allcells_[0], &smin_[0], &smax_[0]); + smin_.resize(np*num_cells); + smax_.resize(np*num_cells); + allcells_.resize(num_cells); + for (int i = 0; i < num_cells; ++i) { + allcells_[i] = i; + } + props.satRange(props.numCells(), &allcells_[0], &smin_[0], &smax_[0]); } void TransportModelCompressibleTwophase::solve(const double* darcyflux, const double* pressure, - const double* surfacevol0, + const double* porevolume0, const double* porevolume, const double* source, const double dt, - std::vector& saturation) + std::vector& saturation, + std::vector& surfacevol) { - darcyflux_ = darcyflux; - surfacevol0_ = surfacevol0; + darcyflux_ = darcyflux; + surfacevol0_ = &surfacevol[0]; + porevolume0_ = porevolume0; porevolume_ = porevolume; - source_ = source; - dt_ = dt; + source_ = source; + dt_ = dt; toWaterSat(saturation, saturation_); props_.viscosity(props_.numCells(), pressure, NULL, &allcells_[0], &visc_[0], NULL); props_.matrix(props_.numCells(), pressure, NULL, &allcells_[0], &A_[0], NULL); - std::vector seq(grid_.number_of_cells); - std::vector comp(grid_.number_of_cells + 1); - int ncomp; - compute_sequence_graph(&grid_, darcyflux_, - &seq[0], &comp[0], &ncomp, - &ia_upw_[0], &ja_upw_[0]); - const int nf = grid_.number_of_faces; - std::vector neg_darcyflux(nf); - std::transform(darcyflux, darcyflux + nf, neg_darcyflux.begin(), std::negate()); - compute_sequence_graph(&grid_, &neg_darcyflux[0], - &seq[0], &comp[0], &ncomp, - &ia_downw_[0], &ja_downw_[0]); - reorderAndTransport(grid_, darcyflux); + // Check immiscibility requirement (only done for first cell). + if (A_[1] != 0.0 || A_[2] != 0.0) { + THROW("TransportModelCompressibleTwophase requires a property object without miscibility."); + } + + std::vector seq(grid_.number_of_cells); + std::vector comp(grid_.number_of_cells + 1); + int ncomp; + compute_sequence_graph(&grid_, darcyflux_, + &seq[0], &comp[0], &ncomp, + &ia_upw_[0], &ja_upw_[0]); + const int nf = grid_.number_of_faces; + std::vector neg_darcyflux(nf); + std::transform(darcyflux, darcyflux + nf, neg_darcyflux.begin(), std::negate()); + compute_sequence_graph(&grid_, &neg_darcyflux[0], + &seq[0], &comp[0], &ncomp, + &ia_downw_[0], &ja_downw_[0]); + reorderAndTransport(grid_, darcyflux); toBothSat(saturation_, saturation); + + // Compute surface volume as a postprocessing step from saturation and A_ + computeSurfacevol(grid_.number_of_cells, props_.numPhases(), &A_[0], &saturation[0], &surfacevol[0]); } // Residual function r(s) for a single-cell implicit Euler transport // // [[ incompressible was: r(s) = s - s0 + dt/pv*( influx + outflux*f(s) ) ]] // - // r(s) = s - B*z0 + dt/pv*( influx + outflux*f(s)) + // r(s) = s - B*z0 + s*(poro - poro0)/poro0 + dt/pv*( influx + outflux*f(s) ) // // @@@ What about the source term - // + // // where influx is water influx, outflux is total outflux. - // We need the formula influx = B_i sum_{j->i} b_j v_{ij} + q_w. - // outflux = B_i sum_{i->j} b_i v_{ij} - q = sum_{i->j} v_{ij} - q (as before) + // We need the formula influx = B_i sum_{j->i} b_j v_{ij} - B_i q_w. + // outflux = B_i sum_{i->j} b_i v_{ij} - B_i q = sum_{i->j} v_{ij} - B_i q // Influxes are negative, outfluxes positive. struct TransportModelCompressibleTwophase::Residual { - int cell; - double s0; - double influx; // sum_j min(v_ij, 0)*f(s_j) + q_w // TODO: fix comment. - double outflux; // sum_j max(v_ij, 0) - q + int cell; + double B_cell; + double z0; + double influx; // B_i sum_j b_j min(v_ij, 0)*f(s_j) - B_i q_w + double outflux; // sum_j max(v_ij, 0) - B_i q // @@@ TODO: figure out change to rock-comp. terms with fluid compr. - // double comp_term; // q - sum_j v_ij - double dtpv; // dt/pv(i) - const TransportModelCompressibleTwophase& tm; - explicit Residual(const TransportModelCompressibleTwophase& tmodel, int cell_index) - : tm(tmodel) - { - cell = cell_index; - s0 = tm.saturation_[cell]; + double comp_term; // Now: used to be: q - sum_j v_ij + double dtpv; // dt/pv(i) + const TransportModelCompressibleTwophase& tm; + explicit Residual(const TransportModelCompressibleTwophase& tmodel, int cell_index) + : tm(tmodel) + { + cell = cell_index; const int np = tm.props_.numPhases(); - const double B_cell = 1.0/tm.A_[np*np*cell + 0]; + z0 = tm.surfacevol0_[np*cell + 0]; // I.e. water surface volume + B_cell = 1.0/tm.A_[np*np*cell + 0]; double src_flux = -tm.source_[cell]; bool src_is_inflow = src_flux < 0.0; - influx = src_is_inflow ? src_flux : 0.0; - outflux = !src_is_inflow ? src_flux : 0.0; - // comp_term = tm.source_[cell]; // Note: this assumes that all source flux is water. - dtpv = tm.dt_/tm.porevolume_[cell]; - for (int i = tm.grid_.cell_facepos[cell]; i < tm.grid_.cell_facepos[cell+1]; ++i) { - const int f = tm.grid_.cell_faces[i]; - double flux; - int other; - // Compute cell flux - if (cell == tm.grid_.face_cells[2*f]) { - flux = tm.darcyflux_[f]; - other = tm.grid_.face_cells[2*f+1]; - } else { - flux =-tm.darcyflux_[f]; - other = tm.grid_.face_cells[2*f]; - } - // Add flux to influx or outflux, if interior. - if (other != -1) { - if (flux < 0.0) { + influx = src_is_inflow ? B_cell*src_flux : 0.0; + outflux = !src_is_inflow ? B_cell*src_flux : 0.0; + comp_term = (tm.porevolume_[cell] - tm.porevolume0_[cell])/tm.porevolume0_[cell]; + dtpv = tm.dt_/tm.porevolume0_[cell]; + for (int i = tm.grid_.cell_facepos[cell]; i < tm.grid_.cell_facepos[cell+1]; ++i) { + const int f = tm.grid_.cell_faces[i]; + double flux; + int other; + // Compute cell flux + if (cell == tm.grid_.face_cells[2*f]) { + flux = tm.darcyflux_[f]; + other = tm.grid_.face_cells[2*f+1]; + } else { + flux =-tm.darcyflux_[f]; + other = tm.grid_.face_cells[2*f]; + } + // Add flux to influx or outflux, if interior. + if (other != -1) { + if (flux < 0.0) { const double b_face = tm.A_[np*np*other + 0]; - influx += B_cell*b_face*flux*tm.fractionalflow_[other]; - } else { - outflux += flux; - } - // comp_term -= flux; - } - } - } - double operator()(double s) const - { - // return s - s0 + dtpv*(outflux*tm.fracFlow(s, cell) + influx + s*comp_term); - return s - s0 + dtpv*(outflux*tm.fracFlow(s, cell) + influx); - } + influx += B_cell*b_face*flux*tm.fractionalflow_[other]; + } else { + outflux += flux; // Because B_cell*b_face = 1 for outflow faces + } + } + } + } + double operator()(double s) const + { + // return s - s0 + dtpv*(outflux*tm.fracFlow(s, cell) + influx + s*comp_term); + return s - B_cell*z0 + dtpv*(outflux*tm.fracFlow(s, cell) + influx) + s*comp_term; + } }; void TransportModelCompressibleTwophase::solveSingleCell(const int cell) { - Residual res(*this, cell); - int iters_used; - saturation_[cell] = RootFinder::solve(res, saturation_[cell], 0.0, 1.0, maxit_, tol_, iters_used); - fractionalflow_[cell] = fracFlow(saturation_[cell], cell); + Residual res(*this, cell); + int iters_used; + saturation_[cell] = RootFinder::solve(res, saturation_[cell], 0.0, 1.0, maxit_, tol_, iters_used); + fractionalflow_[cell] = fracFlow(saturation_[cell], cell); } void TransportModelCompressibleTwophase::solveMultiCell(const int num_cells, const int* cells) { - // Experiment: when a cell changes more than the tolerance, - // mark all downwind cells as needing updates. After - // computing a single update in each cell, use marks - // to guide further updating. Clear mark in cell when - // its solution gets updated. - // Verdict: this is a good one! Approx. halved total time. - std::vector needs_update(num_cells, 1); - // This one also needs the mapping from all cells to - // the strongly connected subset to filter out connections - std::vector pos(grid_.number_of_cells, -1); - for (int i = 0; i < num_cells; ++i) { - const int cell = cells[i]; - pos[cell] = i; - } + // Experiment: when a cell changes more than the tolerance, + // mark all downwind cells as needing updates. After + // computing a single update in each cell, use marks + // to guide further updating. Clear mark in cell when + // its solution gets updated. + // Verdict: this is a good one! Approx. halved total time. + std::vector needs_update(num_cells, 1); + // This one also needs the mapping from all cells to + // the strongly connected subset to filter out connections + std::vector pos(grid_.number_of_cells, -1); + for (int i = 0; i < num_cells; ++i) { + const int cell = cells[i]; + pos[cell] = i; + } - // Note: partially copied from below. - const double tol = 1e-9; - const int max_iters = 300; - // Must store s0 before we start. - std::vector s0(num_cells); - // Must set initial fractional flows before we start. - // Also, we compute the # of upstream neighbours. - // std::vector num_upstream(num_cells); - for (int i = 0; i < num_cells; ++i) { - const int cell = cells[i]; - fractionalflow_[cell] = fracFlow(saturation_[cell], cell); - s0[i] = saturation_[cell]; - // num_upstream[i] = ia_upw_[cell + 1] - ia_upw_[cell]; - } - // Solve once in each cell. - // std::vector fully_marked_stack; - // fully_marked_stack.reserve(num_cells); - int num_iters = 0; - int update_count = 0; // Change name/meaning to cells_updated? - do { - update_count = 0; // Must reset count for every iteration. - for (int i = 0; i < num_cells; ++i) { - // while (!fully_marked_stack.empty()) { - // // std::cout << "# fully marked cells = " << fully_marked_stack.size() << std::endl; - // const int fully_marked_ci = fully_marked_stack.back(); - // fully_marked_stack.pop_back(); - // ++update_count; - // const int cell = cells[fully_marked_ci]; - // const double old_s = saturation_[cell]; - // saturation_[cell] = s0[fully_marked_ci]; - // solveSingleCell(cell); - // const double s_change = std::fabs(saturation_[cell] - old_s); - // if (s_change > tol) { - // // Mark downwind cells. - // for (int j = ia_downw_[cell]; j < ia_downw_[cell+1]; ++j) { - // const int downwind_cell = ja_downw_[j]; - // int ci = pos[downwind_cell]; - // ++needs_update[ci]; - // if (needs_update[ci] == num_upstream[ci]) { - // fully_marked_stack.push_back(ci); - // } - // } - // } - // // Unmark this cell. - // needs_update[fully_marked_ci] = 0; - // } - if (!needs_update[i]) { - continue; - } - ++update_count; - const int cell = cells[i]; - const double old_s = saturation_[cell]; - saturation_[cell] = s0[i]; - solveSingleCell(cell); - const double s_change = std::fabs(saturation_[cell] - old_s); - if (s_change > tol) { - // Mark downwind cells. - for (int j = ia_downw_[cell]; j < ia_downw_[cell+1]; ++j) { - const int downwind_cell = ja_downw_[j]; - int ci = pos[downwind_cell]; + // Note: partially copied from below. + const double tol = 1e-9; + const int max_iters = 300; + // Must store s0 before we start. + std::vector s0(num_cells); + // Must set initial fractional flows before we start. + // Also, we compute the # of upstream neighbours. + // std::vector num_upstream(num_cells); + for (int i = 0; i < num_cells; ++i) { + const int cell = cells[i]; + fractionalflow_[cell] = fracFlow(saturation_[cell], cell); + s0[i] = saturation_[cell]; + // num_upstream[i] = ia_upw_[cell + 1] - ia_upw_[cell]; + } + // Solve once in each cell. + // std::vector fully_marked_stack; + // fully_marked_stack.reserve(num_cells); + int num_iters = 0; + int update_count = 0; // Change name/meaning to cells_updated? + do { + update_count = 0; // Must reset count for every iteration. + for (int i = 0; i < num_cells; ++i) { + // while (!fully_marked_stack.empty()) { + // // std::cout << "# fully marked cells = " << fully_marked_stack.size() << std::endl; + // const int fully_marked_ci = fully_marked_stack.back(); + // fully_marked_stack.pop_back(); + // ++update_count; + // const int cell = cells[fully_marked_ci]; + // const double old_s = saturation_[cell]; + // saturation_[cell] = s0[fully_marked_ci]; + // solveSingleCell(cell); + // const double s_change = std::fabs(saturation_[cell] - old_s); + // if (s_change > tol) { + // // Mark downwind cells. + // for (int j = ia_downw_[cell]; j < ia_downw_[cell+1]; ++j) { + // const int downwind_cell = ja_downw_[j]; + // int ci = pos[downwind_cell]; + // ++needs_update[ci]; + // if (needs_update[ci] == num_upstream[ci]) { + // fully_marked_stack.push_back(ci); + // } + // } + // } + // // Unmark this cell. + // needs_update[fully_marked_ci] = 0; + // } + if (!needs_update[i]) { + continue; + } + ++update_count; + const int cell = cells[i]; + const double old_s = saturation_[cell]; + // solveSingleCell() requires saturation_[cell] + // to be s0. + saturation_[cell] = s0[i]; + solveSingleCell(cell); + const double s_change = std::fabs(saturation_[cell] - old_s); + if (s_change > tol) { + // Mark downwind cells. + for (int j = ia_downw_[cell]; j < ia_downw_[cell+1]; ++j) { + const int downwind_cell = ja_downw_[j]; + int ci = pos[downwind_cell]; if (ci != -1) { needs_update[ci] = 1; } - // ++needs_update[ci]; - // if (needs_update[ci] == num_upstream[ci]) { - // fully_marked_stack.push_back(ci); - // } - } - } - // Unmark this cell. - needs_update[i] = 0; - } - // std::cout << "Iter = " << num_iters << " update_count = " << update_count - // << " # marked cells = " - // << std::accumulate(needs_update.begin(), needs_update.end(), 0) << std::endl; - } while (update_count > 0 && ++num_iters < max_iters); + // ++needs_update[ci]; + // if (needs_update[ci] == num_upstream[ci]) { + // fully_marked_stack.push_back(ci); + // } + } + } + // Unmark this cell. + needs_update[i] = 0; + } + // std::cout << "Iter = " << num_iters << " update_count = " << update_count + // << " # marked cells = " + // << std::accumulate(needs_update.begin(), needs_update.end(), 0) << std::endl; + } while (update_count > 0 && ++num_iters < max_iters); - // Done with iterations, check if we succeeded. - if (update_count > 0) { - THROW("In solveMultiCell(), we did not converge after " - << num_iters << " iterations. Remaining update count = " << update_count); - } - std::cout << "Solved " << num_cells << " cell multicell problem in " - << num_iters << " iterations." << std::endl; + // Done with iterations, check if we succeeded. + if (update_count > 0) { + THROW("In solveMultiCell(), we did not converge after " + << num_iters << " iterations. Remaining update count = " << update_count); + } + std::cout << "Solved " << num_cells << " cell multicell problem in " + << num_iters << " iterations." << std::endl; } double TransportModelCompressibleTwophase::fracFlow(double s, int cell) const { - double sat[2] = { s, 1.0 - s }; - double mob[2]; - props_.relperm(1, sat, &cell, mob, 0); - mob[0] /= visc_[2*cell + 0]; - mob[1] /= visc_[2*cell + 1]; - return mob[0]/(mob[0] + mob[1]); + double sat[2] = { s, 1.0 - s }; + double mob[2]; + props_.relperm(1, sat, &cell, mob, 0); + mob[0] /= visc_[2*cell + 0]; + mob[1] /= visc_[2*cell + 1]; + return mob[0]/(mob[0] + mob[1]); } @@ -303,23 +318,24 @@ namespace Opm // Residual function r(s) for a single-cell implicit Euler gravity segregation // - // r(s) = s - s0 + dt/pv*sum_{j adj i}( gravmod_ij * gf_ij ). + // [[ incompressible was: r(s) = s - s0 + dt/pv*sum_{j adj i}( gravmod_ij * gf_ij ) ]] // + // r(s) = s - B*z0 + dt/pv*( influx + outflux*f(s) ) struct TransportModelCompressibleTwophase::GravityResidual { - int cell; + int cell; int nbcell[2]; - double s0; - double dtpv; // dt/pv(i) + double s0; + double dtpv; // dt/pv(i) double gf[2]; - const TransportModelCompressibleTwophase& tm; - explicit GravityResidual(const TransportModelCompressibleTwophase& tmodel, + const TransportModelCompressibleTwophase& tm; + explicit GravityResidual(const TransportModelCompressibleTwophase& tmodel, const std::vector& cells, const int pos, const double* gravflux) // Always oriented towards next in column. Size = colsize - 1. - : tm(tmodel) - { - cell = cells[pos]; + : tm(tmodel) + { + cell = cells[pos]; nbcell[0] = -1; gf[0] = 0.0; if (pos > 0) { @@ -332,70 +348,94 @@ namespace Opm nbcell[1] = cells[pos + 1]; gf[1] = gravflux[pos]; } - s0 = tm.saturation_[cell]; - dtpv = tm.dt_/tm.porevolume_[cell]; - - } - double operator()(double s) const - { - double res = s - s0; + s0 = tm.saturation_[cell]; + dtpv = tm.dt_/tm.porevolume0_[cell]; + + } + double operator()(double s) const + { + double res = s - s0; double mobcell[2]; tm.mobility(s, cell, mobcell); for (int nb = 0; nb < 2; ++nb) { - if (nbcell[nb] != -1) { + if (nbcell[nb] != -1) { double m[2]; if (gf[nb] < 0.0) { m[0] = mobcell[0]; m[1] = tm.mob_[2*nbcell[nb] + 1]; - } else { + } else { m[0] = tm.mob_[2*nbcell[nb]]; m[1] = mobcell[1]; - } - if (m[0] + m[1] > 0.0) { + } + if (m[0] + m[1] > 0.0) { res += -dtpv*gf[nb]*m[0]*m[1]/(m[0] + m[1]); } - } + } } return res; - } + } }; void TransportModelCompressibleTwophase::mobility(double s, int cell, double* mob) const { - double sat[2] = { s, 1.0 - s }; - props_.relperm(1, sat, &cell, mob, 0); - mob[0] /= visc_[0]; - mob[1] /= visc_[1]; + double sat[2] = { s, 1.0 - s }; + props_.relperm(1, sat, &cell, mob, 0); + mob[0] /= visc_[0]; + mob[1] /= visc_[1]; } void TransportModelCompressibleTwophase::initGravity(const double* grav) { - // Set up gravflux_ = T_ij g (rho_w - rho_o) (z_i - z_j) + // Set up transmissibilities. std::vector htrans(grid_.cell_facepos[grid_.number_of_cells]); const int nf = grid_.number_of_faces; - const int dim = grid_.dimensions; + trans_.resize(nf); gravflux_.resize(nf); tpfa_htrans_compute(const_cast(&grid_), props_.permeability(), &htrans[0]); - tpfa_trans_compute(const_cast(&grid_), &htrans[0], &gravflux_[0]); + tpfa_trans_compute(const_cast(&grid_), &htrans[0], &trans_[0]); - const double delta_rho = 0.0;// props_.density()[0] - props_.density()[1]; - THROW("TransportModelCompressibleTwophase gravity solver not done yet."); // See line above... + // Remember gravity vector. + gravity_ = grav; + } + + + + + void TransportModelCompressibleTwophase::initGravityDynamic() + { + // Set up gravflux_ = T_ij g [ (b_w,i rho_w,S - b_o,i rho_o,S) (z_i - z_f) + // + (b_w,j rho_w,S - b_o,j rho_o,S) (z_f - z_j) ] + // But b_w,i * rho_w,S = rho_w,i, which we conmpute with a call to props_.density(). + // We assume that we already have stored T_ij in trans_. + // We also assume that the A_ matrices are updated from an earlier call to solve(). + const int nc = grid_.number_of_cells; + const int nf = grid_.number_of_faces; + const int np = props_.numPhases(); + ASSERT(np == 2); + const int dim = grid_.dimensions; + density_.resize(nc*np); + props_.density(grid_.number_of_cells, &A_[0], &density_[0]); + std::fill(gravflux_.begin(), gravflux_.end(), 0.0); for (int f = 0; f < nf; ++f) { const int* c = &grid_.face_cells[2*f]; - double gdz = 0.0; + const double signs[2] = { 1.0, -1.0 }; if (c[0] != -1 && c[1] != -1) { - for (int d = 0; d < dim; ++d) { - gdz += grav[d]*(grid_.cell_centroids[dim*c[0] + d] - grid_.cell_centroids[dim*c[1] + d]); + for (int ci = 0; ci < 2; ++ci) { + double gdz = 0.0; + for (int d = 0; d < dim; ++d) { + gdz += gravity_[d]*(grid_.cell_centroids[dim*c[ci] + d] - grid_.face_centroids[dim*f + d]); + } + gravflux_[f] += signs[ci]*trans_[f]*gdz*(density_[2*c[ci]] - density_[2*c[ci] + 1]); } } - gravflux_[f] *= delta_rho*gdz; } } + void TransportModelCompressibleTwophase::solveSingleCellGravity(const std::vector& cells, const int pos, const double* gravflux) @@ -407,7 +447,7 @@ namespace Opm saturation_[cell] = RootFinder::solve(res, smin_[2*cell], smax_[2*cell], maxit_, tol_, iters_used); } saturation_[cell] = std::min(std::max(saturation_[cell], smin_[2*cell]), smax_[2*cell]); - mobility(saturation_[cell], cell, &mob_[2*cell]); + mobility(saturation_[cell], cell, &mob_[2*cell]); } @@ -418,17 +458,17 @@ namespace Opm const int nc = cells.size(); std::vector col_gravflux(nc - 1); for (int ci = 0; ci < nc - 1; ++ci) { - const int cell = cells[ci]; - const int next_cell = cells[ci + 1]; - for (int j = grid_.cell_facepos[cell]; j < grid_.cell_facepos[cell+1]; ++j) { - const int face = grid_.cell_faces[j]; - const int c1 = grid_.face_cells[2*face + 0]; + const int cell = cells[ci]; + const int next_cell = cells[ci + 1]; + for (int j = grid_.cell_facepos[cell]; j < grid_.cell_facepos[cell+1]; ++j) { + const int face = grid_.cell_faces[j]; + const int c1 = grid_.face_cells[2*face + 0]; const int c2 = grid_.face_cells[2*face + 1]; - if (c1 == next_cell || c2 == next_cell) { + if (c1 == next_cell || c2 == next_cell) { const double gf = gravflux_[face]; col_gravflux[ci] = (c1 == cell) ? gf : -gf; - } - } + } + } } // Store initial saturation s0 @@ -438,7 +478,7 @@ namespace Opm } // Solve single cell problems, repeating if necessary. - double max_s_change = 0.0; + double max_s_change = 0.0; int num_iters = 0; do { max_s_change = 0.0; @@ -454,12 +494,12 @@ namespace Opm std::fabs(saturation_[cells[ci2]] - old_s[1]))); } // std::cout << "Iter = " << num_iters << " max_s_change = " << max_s_change << std::endl; - } while (max_s_change > tol_ && ++num_iters < maxit_); + } while (max_s_change > tol_ && ++num_iters < maxit_); - if (max_s_change > tol_) { - THROW("In solveGravityColumn(), we did not converge after " - << num_iters << " iterations. Delta s = " << max_s_change); - } + if (max_s_change > tol_) { + THROW("In solveGravityColumn(), we did not converge after " + << num_iters << " iterations. Delta s = " << max_s_change); + } return num_iters + 1; } @@ -467,10 +507,14 @@ namespace Opm void TransportModelCompressibleTwophase::solveGravity(const std::vector >& columns, const double* pressure, - const double* porevolume, + const double* porevolume0, const double dt, - std::vector& saturation) + std::vector& saturation, + std::vector& surfacevol) { + // Assume that solve() has already been called, so that A_ is current. + initGravityDynamic(); + // Initialize mobilities. const int nc = grid_.number_of_cells; std::vector cells(nc); @@ -489,7 +533,7 @@ namespace Opm } // Set up other variables. - porevolume_ = porevolume; + porevolume0_ = porevolume0; dt_ = dt; toWaterSat(saturation, saturation_); @@ -502,6 +546,9 @@ namespace Opm std::cout << "Gauss-Seidel column solver average iterations: " << double(num_iters)/double(columns.size()) << std::endl; toBothSat(saturation_, saturation); + + // Compute surface volume as a postprocessing step from saturation and A_ + computeSurfacevol(grid_.number_of_cells, props_.numPhases(), &A_[0], &saturation[0], &surfacevol[0]); } } // namespace Opm diff --git a/opm/core/transport/reorder/TransportModelCompressibleTwophase.hpp b/opm/core/transport/reorder/TransportModelCompressibleTwophase.hpp index d41deef6..4ee93e0a 100644 --- a/opm/core/transport/reorder/TransportModelCompressibleTwophase.hpp +++ b/opm/core/transport/reorder/TransportModelCompressibleTwophase.hpp @@ -30,37 +30,42 @@ namespace Opm class BlackoilPropertiesInterface; + /// Implements a reordering transport solver for compressible, + /// non-miscible two-phase flow. class TransportModelCompressibleTwophase : public TransportModelInterface { public: - /// Construct solver. - /// \param[in] grid A 2d or 3d grid. - /// \param[in] props Rock and fluid properties. - /// \param[in] tol Tolerance used in the solver. - /// \param[in] maxit Maximum number of non-linear iterations used. - TransportModelCompressibleTwophase(const UnstructuredGrid& grid, + /// Construct solver. + /// \param[in] grid A 2d or 3d grid. + /// \param[in] props Rock and fluid properties. + /// \param[in] tol Tolerance used in the solver. + /// \param[in] maxit Maximum number of non-linear iterations used. + TransportModelCompressibleTwophase(const UnstructuredGrid& grid, const Opm::BlackoilPropertiesInterface& props, const double tol, const int maxit); - /// Solve for saturation at next timestep. - /// \param[in] darcyflux Array of signed face fluxes. - /// \param[in] pressure Array of cell pressures - /// \param[in] surfacevol0 Array of surface volumes at start of timestep - /// \param[in] porevolume Array of pore volumes. - /// \param[in] source Transport source term. - /// \param[in] dt Time step. - /// \param[in, out] saturation Phase saturations. - void solve(const double* darcyflux, + /// Solve for saturation at next timestep. + /// \param[in] darcyflux Array of signed face fluxes. + /// \param[in] pressure Array of cell pressures + /// \param[in] surfacevol0 Array of surface volumes at start of timestep + /// \param[in] porevolume0 Array of pore volumes at start of timestep. + /// \param[in] porevolume Array of pore volumes at end of timestep. + /// \param[in] source Transport source term. + /// \param[in] dt Time step. + /// \param[in, out] saturation Phase saturations. + /// \param[in, out] surfacevol Surface volume densities for each phase. + void solve(const double* darcyflux, const double* pressure, - const double* surfacevol0, + const double* porevolume0, const double* porevolume, - const double* source, - const double dt, - std::vector& saturation); + const double* source, + const double dt, + std::vector& saturation, + std::vector& surfacevol); /// Initialise quantities needed by gravity solver. - /// \param[in] grav gravity vector + /// \param[in] grav Gravity vector void initGravity(const double* grav); /// Solve for gravity segregation. @@ -68,52 +73,62 @@ namespace Opm /// It assumes that the input columns contain cells in a single /// vertical stack, that do not interact with other columns (for /// gravity segregation. - /// \TODO: Implement this. + /// \param[in] columns Vector of cell-columns. + /// \param[in] porevolume0 Array of pore volumes at start of timestep. + /// \param[in] dt Time step. + /// \param[in, out] saturation Phase saturations. + /// \param[in, out] surfacevol Surface volume densities for each phase. void solveGravity(const std::vector >& columns, const double* pressure, - const double* porevolume, + const double* porevolume0, const double dt, - std::vector& saturation); + std::vector& saturation, + std::vector& surfacevol); private: - virtual void solveSingleCell(const int cell); - virtual void solveMultiCell(const int num_cells, const int* cells); + virtual void solveSingleCell(const int cell); + virtual void solveMultiCell(const int num_cells, const int* cells); void solveSingleCellGravity(const std::vector& cells, const int pos, const double* gravflux); int solveGravityColumn(const std::vector& cells); + void initGravityDynamic(); private: - const UnstructuredGrid& grid_; - const BlackoilPropertiesInterface& props_; + const UnstructuredGrid& grid_; + const BlackoilPropertiesInterface& props_; std::vector allcells_; std::vector visc_; std::vector A_; - std::vector smin_; - std::vector smax_; - double tol_; - double maxit_; + std::vector smin_; + std::vector smax_; + double tol_; + double maxit_; - const double* darcyflux_; // one flux per grid face - const double* surfacevol0_; // one per phase per cell - const double* porevolume_; // one volume per cell - const double* source_; // one source per cell - double dt_; + const double* darcyflux_; // one flux per grid face + const double* surfacevol0_; // one per phase per cell + const double* porevolume0_; // one volume per cell + const double* porevolume_; // one volume per cell + const double* source_; // one source per cell + double dt_; std::vector saturation_; // P (= num. phases) per cell - std::vector fractionalflow_; // = m[0]/(m[0] + m[1]) per cell + std::vector fractionalflow_; // = m[0]/(m[0] + m[1]) per cell // For gravity segregation. + const double* gravity_; + std::vector trans_; + std::vector density_; std::vector gravflux_; std::vector mob_; std::vector s0_; - // Storing the upwind and downwind graphs for experiments. - std::vector ia_upw_; - std::vector ja_upw_; - std::vector ia_downw_; - std::vector ja_downw_; + // Storing the upwind and downwind graphs for experiments. + std::vector ia_upw_; + std::vector ja_upw_; + std::vector ia_downw_; + std::vector ja_downw_; - struct Residual; - double fracFlow(double s, int cell) const; + struct Residual; + double fracFlow(double s, int cell) const; struct GravityResidual; void mobility(double s, int cell, double* mob) const; diff --git a/opm/core/transport/reorder/TransportModelTwophase.cpp b/opm/core/transport/reorder/TransportModelTwophase.cpp index ff6d2c05..2740d065 100644 --- a/opm/core/transport/reorder/TransportModelTwophase.cpp +++ b/opm/core/transport/reorder/TransportModelTwophase.cpp @@ -41,71 +41,79 @@ namespace Opm TransportModelTwophase::TransportModelTwophase(const UnstructuredGrid& grid, - const Opm::IncompPropertiesInterface& props, - const double tol, - const int maxit) - : grid_(grid), - props_(props), - tol_(tol), - maxit_(maxit), - darcyflux_(0), - source_(0), - dt_(0.0), - saturation_(grid.number_of_cells, -1.0), - fractionalflow_(grid.number_of_cells, -1.0), - mob_(2*grid.number_of_cells, -1.0) + const Opm::IncompPropertiesInterface& props, + const double tol, + const int maxit) + : grid_(grid), + props_(props), + tol_(tol), + maxit_(maxit), + darcyflux_(0), + source_(0), + dt_(0.0), + saturation_(grid.number_of_cells, -1.0), + fractionalflow_(grid.number_of_cells, -1.0), + reorder_iterations_(grid.number_of_cells, 0), + mob_(2*grid.number_of_cells, -1.0) #ifdef EXPERIMENT_GAUSS_SEIDEL - , ia_upw_(grid.number_of_cells + 1, -1), - ja_upw_(grid.number_of_faces, -1), - ia_downw_(grid.number_of_cells + 1, -1), - ja_downw_(grid.number_of_faces, -1) + , ia_upw_(grid.number_of_cells + 1, -1), + ja_upw_(grid.number_of_faces, -1), + ia_downw_(grid.number_of_cells + 1, -1), + ja_downw_(grid.number_of_faces, -1) #endif { - if (props.numPhases() != 2) { - THROW("Property object must have 2 phases"); - } - visc_ = props.viscosity(); - int num_cells = props.numCells(); - smin_.resize(props.numPhases()*num_cells); - smax_.resize(props.numPhases()*num_cells); - std::vector cells(num_cells); - for (int i = 0; i < num_cells; ++i) { - cells[i] = i; - } - props.satRange(props.numCells(), &cells[0], &smin_[0], &smax_[0]); + if (props.numPhases() != 2) { + THROW("Property object must have 2 phases"); + } + visc_ = props.viscosity(); + int num_cells = props.numCells(); + smin_.resize(props.numPhases()*num_cells); + smax_.resize(props.numPhases()*num_cells); + std::vector cells(num_cells); + for (int i = 0; i < num_cells; ++i) { + cells[i] = i; + } + props.satRange(props.numCells(), &cells[0], &smin_[0], &smax_[0]); } void TransportModelTwophase::solve(const double* darcyflux, const double* porevolume, - const double* source, - const double dt, - std::vector& saturation) + const double* source, + const double dt, + std::vector& saturation) { - darcyflux_ = darcyflux; + darcyflux_ = darcyflux; porevolume_ = porevolume; - source_ = source; - dt_ = dt; + source_ = source; + dt_ = dt; toWaterSat(saturation, saturation_); #ifdef EXPERIMENT_GAUSS_SEIDEL - std::vector seq(grid_.number_of_cells); - std::vector comp(grid_.number_of_cells + 1); - int ncomp; - compute_sequence_graph(&grid_, darcyflux_, - &seq[0], &comp[0], &ncomp, - &ia_upw_[0], &ja_upw_[0]); - const int nf = grid_.number_of_faces; - std::vector neg_darcyflux(nf); - std::transform(darcyflux, darcyflux + nf, neg_darcyflux.begin(), std::negate()); - compute_sequence_graph(&grid_, &neg_darcyflux[0], - &seq[0], &comp[0], &ncomp, - &ia_downw_[0], &ja_downw_[0]); + std::vector seq(grid_.number_of_cells); + std::vector comp(grid_.number_of_cells + 1); + int ncomp; + compute_sequence_graph(&grid_, darcyflux_, + &seq[0], &comp[0], &ncomp, + &ia_upw_[0], &ja_upw_[0]); + const int nf = grid_.number_of_faces; + std::vector neg_darcyflux(nf); + std::transform(darcyflux, darcyflux + nf, neg_darcyflux.begin(), std::negate()); + compute_sequence_graph(&grid_, &neg_darcyflux[0], + &seq[0], &comp[0], &ncomp, + &ia_downw_[0], &ja_downw_[0]); #endif - - reorderAndTransport(grid_, darcyflux); + std::fill(reorder_iterations_.begin(),reorder_iterations_.end(),0); + reorderAndTransport(grid_, darcyflux); toBothSat(saturation_, saturation); } + + const std::vector& TransportModelTwophase::getReorderIterations() const + { + return reorder_iterations_; + } + + // Residual function r(s) for a single-cell implicit Euler transport // // r(s) = s - s0 + dt/pv*( influx + outflux*f(s) ) @@ -114,330 +122,332 @@ namespace Opm // Influxes are negative, outfluxes positive. struct TransportModelTwophase::Residual { - int cell; - double s0; - double influx; // sum_j min(v_ij, 0)*f(s_j) + q_w - double outflux; // sum_j max(v_ij, 0) - q + int cell; + double s0; + double influx; // sum_j min(v_ij, 0)*f(s_j) + q_w + double outflux; // sum_j max(v_ij, 0) - q double comp_term; // q - sum_j v_ij - double dtpv; // dt/pv(i) - const TransportModelTwophase& tm; - explicit Residual(const TransportModelTwophase& tmodel, int cell_index) - : tm(tmodel) - { - cell = cell_index; - s0 = tm.saturation_[cell]; + double dtpv; // dt/pv(i) + const TransportModelTwophase& tm; + explicit Residual(const TransportModelTwophase& tmodel, int cell_index) + : tm(tmodel) + { + cell = cell_index; + s0 = tm.saturation_[cell]; double src_flux = -tm.source_[cell]; bool src_is_inflow = src_flux < 0.0; - influx = src_is_inflow ? src_flux : 0.0; - outflux = !src_is_inflow ? src_flux : 0.0; + influx = src_is_inflow ? src_flux : 0.0; + outflux = !src_is_inflow ? src_flux : 0.0; comp_term = tm.source_[cell]; // Note: this assumes that all source flux is water. - dtpv = tm.dt_/tm.porevolume_[cell]; + dtpv = tm.dt_/tm.porevolume_[cell]; - for (int i = tm.grid_.cell_facepos[cell]; i < tm.grid_.cell_facepos[cell+1]; ++i) { - int f = tm.grid_.cell_faces[i]; - double flux; - int other; - // Compute cell flux - if (cell == tm.grid_.face_cells[2*f]) { - flux = tm.darcyflux_[f]; - other = tm.grid_.face_cells[2*f+1]; - } else { - flux =-tm.darcyflux_[f]; - other = tm.grid_.face_cells[2*f]; - } - // Add flux to influx or outflux, if interior. - if (other != -1) { - if (flux < 0.0) { - influx += flux*tm.fractionalflow_[other]; - } else { - outflux += flux; - } + for (int i = tm.grid_.cell_facepos[cell]; i < tm.grid_.cell_facepos[cell+1]; ++i) { + int f = tm.grid_.cell_faces[i]; + double flux; + int other; + // Compute cell flux + if (cell == tm.grid_.face_cells[2*f]) { + flux = tm.darcyflux_[f]; + other = tm.grid_.face_cells[2*f+1]; + } else { + flux =-tm.darcyflux_[f]; + other = tm.grid_.face_cells[2*f]; + } + // Add flux to influx or outflux, if interior. + if (other != -1) { + if (flux < 0.0) { + influx += flux*tm.fractionalflow_[other]; + } else { + outflux += flux; + } comp_term -= flux; - } - } - } - double operator()(double s) const - { - return s - s0 + dtpv*(outflux*tm.fracFlow(s, cell) + influx + s*comp_term); - } + } + } + } + double operator()(double s) const + { + return s - s0 + dtpv*(outflux*tm.fracFlow(s, cell) + influx + s*comp_term); + } }; void TransportModelTwophase::solveSingleCell(const int cell) { - Residual res(*this, cell); - // const double r0 = res(saturation_[cell]); - // if (std::fabs(r0) < tol_) { - // return; - // } - int iters_used; - // saturation_[cell] = modifiedRegulaFalsi(res, smin_[2*cell], smax_[2*cell], maxit_, tol_, iters_used); - saturation_[cell] = RootFinder::solve(res, saturation_[cell], 0.0, 1.0, maxit_, tol_, iters_used); - fractionalflow_[cell] = fracFlow(saturation_[cell], cell); + Residual res(*this, cell); + // const double r0 = res(saturation_[cell]); + // if (std::fabs(r0) < tol_) { + // return; + // } + int iters_used = 0; + // saturation_[cell] = modifiedRegulaFalsi(res, smin_[2*cell], smax_[2*cell], maxit_, tol_, iters_used); + saturation_[cell] = RootFinder::solve(res, saturation_[cell], 0.0, 1.0, maxit_, tol_, iters_used); + // add if it is iteration on an out loop + reorder_iterations_[cell] = reorder_iterations_[cell] + iters_used; + fractionalflow_[cell] = fracFlow(saturation_[cell], cell); } // namespace { - // class TofComputer - // { - // public: - // TofComputer(const int num_cells, - // const int* ia, - // const int* ja, - // const int startcell, - // std::vector& tof) - // : ia_(ia), - // ja_(ja) - // { - // tof.clear(); - // tof.resize(num_cells, num_cells); - // tof[startcell] = 0; - // tof_ = &tof[0]; - // visitTof(startcell); - // } + // class TofComputer + // { + // public: + // TofComputer(const int num_cells, + // const int* ia, + // const int* ja, + // const int startcell, + // std::vector& tof) + // : ia_(ia), + // ja_(ja) + // { + // tof.clear(); + // tof.resize(num_cells, num_cells); + // tof[startcell] = 0; + // tof_ = &tof[0]; + // visitTof(startcell); + // } - // private: - // const int* ia_; - // const int* ja_; - // int* tof_; + // private: + // const int* ia_; + // const int* ja_; + // int* tof_; - // void visitTof(const int cell) - // { - // for (int j = ia_[cell]; j < ia_[cell+1]; ++j) { - // const int nb_cell = ja_[j]; - // if (tof_[nb_cell] > tof_[cell] + 1) { - // tof_[nb_cell] = tof_[cell] + 1; - // visitTof(nb_cell); - // } - // } - // } + // void visitTof(const int cell) + // { + // for (int j = ia_[cell]; j < ia_[cell+1]; ++j) { + // const int nb_cell = ja_[j]; + // if (tof_[nb_cell] > tof_[cell] + 1) { + // tof_[nb_cell] = tof_[cell] + 1; + // visitTof(nb_cell); + // } + // } + // } - // }; + // }; // } // anon namespace void TransportModelTwophase::solveMultiCell(const int num_cells, const int* cells) { - // std::ofstream os("dump"); - // std::copy(cells, cells + num_cells, std::ostream_iterator(os, "\n")); + // std::ofstream os("dump"); + // std::copy(cells, cells + num_cells, std::ostream_iterator(os, "\n")); - // Experiment: try a breath-first search to build a more suitable ordering. - // Verdict: failed to improve #iterations. - // { - // std::vector pos(grid_.number_of_cells, -1); - // for (int i = 0; i < num_cells; ++i) { - // const int cell = cells[i]; - // pos[cell] = i; - // } - // std::vector done_pos(num_cells, 0); - // std::vector upstream_pos; - // std::vector new_pos; - // upstream_pos.push_back(0); - // done_pos[0] = 1; - // int current = 0; - // while (int(new_pos.size()) < num_cells) { - // const int i = upstream_pos[current++]; - // new_pos.push_back(i); - // const int cell = cells[i]; - // for (int j = ia_[cell]; j < ia_[cell+1]; ++j) { - // const int opos = pos[ja_[j]]; - // if (!done_pos[opos]) { - // upstream_pos.push_back(opos); - // done_pos[opos] = 1; - // } - // } - // } - // std::reverse(new_pos.begin(), new_pos.end()); - // std::copy(new_pos.begin(), new_pos.end(), const_cast(cells)); - // } + // Experiment: try a breath-first search to build a more suitable ordering. + // Verdict: failed to improve #iterations. + // { + // std::vector pos(grid_.number_of_cells, -1); + // for (int i = 0; i < num_cells; ++i) { + // const int cell = cells[i]; + // pos[cell] = i; + // } + // std::vector done_pos(num_cells, 0); + // std::vector upstream_pos; + // std::vector new_pos; + // upstream_pos.push_back(0); + // done_pos[0] = 1; + // int current = 0; + // while (int(new_pos.size()) < num_cells) { + // const int i = upstream_pos[current++]; + // new_pos.push_back(i); + // const int cell = cells[i]; + // for (int j = ia_[cell]; j < ia_[cell+1]; ++j) { + // const int opos = pos[ja_[j]]; + // if (!done_pos[opos]) { + // upstream_pos.push_back(opos); + // done_pos[opos] = 1; + // } + // } + // } + // std::reverse(new_pos.begin(), new_pos.end()); + // std::copy(new_pos.begin(), new_pos.end(), const_cast(cells)); + // } - // Experiment: try a random ordering. - // Verdict: amazingly, reduced #iterations by approx. 25%! - // int* c = const_cast(cells); - // std::random_shuffle(c, c + num_cells); + // Experiment: try a random ordering. + // Verdict: amazingly, reduced #iterations by approx. 25%! + // int* c = const_cast(cells); + // std::random_shuffle(c, c + num_cells); - // Experiment: compute topological tof from first cell. - // Verdict: maybe useful, not tried to exploit it yet. - // std::vector tof; - // TofComputer comp(grid_.number_of_cells, &ia_[0], &ja_[0], cells[0], tof); - // std::ofstream tofdump("tofdump"); - // std::copy(tof.begin(), tof.end(), std::ostream_iterator(tofdump, "\n")); + // Experiment: compute topological tof from first cell. + // Verdict: maybe useful, not tried to exploit it yet. + // std::vector tof; + // TofComputer comp(grid_.number_of_cells, &ia_[0], &ja_[0], cells[0], tof); + // std::ofstream tofdump("tofdump"); + // std::copy(tof.begin(), tof.end(), std::ostream_iterator(tofdump, "\n")); - // Experiment: implement a metric measuring badness of ordering - // as average distance in (cyclic) ordering from - // upstream neighbours. - // Verdict: does not seem to predict #iterations very well, if at all. - // std::vector pos(grid_.number_of_cells, -1); - // for (int i = 0; i < num_cells; ++i) { - // const int cell = cells[i]; - // pos[cell] = i; - // } - // double diffsum = 0; - // for (int i = 0; i < num_cells; ++i) { - // const int cell = cells[i]; - // int num_upstream = 0; - // int loc_diffsum = 0; - // for (int j = ia_[cell]; j < ia_[cell+1]; ++j) { - // const int opos = pos[ja_[j]]; - // if (opos == -1) { - // std::cout << "Hmmm" << std::endl; - // continue; - // } - // ++num_upstream; - // const int diff = (i - opos + num_cells) % num_cells; - // loc_diffsum += diff; - // } - // diffsum += double(loc_diffsum)/double(num_upstream); - // } - // std::cout << "Average distance from upstream neighbours: " << diffsum/double(num_cells) - // << std::endl; + // Experiment: implement a metric measuring badness of ordering + // as average distance in (cyclic) ordering from + // upstream neighbours. + // Verdict: does not seem to predict #iterations very well, if at all. + // std::vector pos(grid_.number_of_cells, -1); + // for (int i = 0; i < num_cells; ++i) { + // const int cell = cells[i]; + // pos[cell] = i; + // } + // double diffsum = 0; + // for (int i = 0; i < num_cells; ++i) { + // const int cell = cells[i]; + // int num_upstream = 0; + // int loc_diffsum = 0; + // for (int j = ia_[cell]; j < ia_[cell+1]; ++j) { + // const int opos = pos[ja_[j]]; + // if (opos == -1) { + // std::cout << "Hmmm" << std::endl; + // continue; + // } + // ++num_upstream; + // const int diff = (i - opos + num_cells) % num_cells; + // loc_diffsum += diff; + // } + // diffsum += double(loc_diffsum)/double(num_upstream); + // } + // std::cout << "Average distance from upstream neighbours: " << diffsum/double(num_cells) + // << std::endl; #ifdef EXPERIMENT_GAUSS_SEIDEL - // Experiment: when a cell changes more than the tolerance, - // mark all downwind cells as needing updates. After - // computing a single update in each cell, use marks - // to guide further updating. Clear mark in cell when - // its solution gets updated. - // Verdict: this is a good one! Approx. halved total time. - std::vector needs_update(num_cells, 1); - // This one also needs the mapping from all cells to - // the strongly connected subset to filter out connections - std::vector pos(grid_.number_of_cells, -1); - for (int i = 0; i < num_cells; ++i) { - const int cell = cells[i]; - pos[cell] = i; - } + // Experiment: when a cell changes more than the tolerance, + // mark all downwind cells as needing updates. After + // computing a single update in each cell, use marks + // to guide further updating. Clear mark in cell when + // its solution gets updated. + // Verdict: this is a good one! Approx. halved total time. + std::vector needs_update(num_cells, 1); + // This one also needs the mapping from all cells to + // the strongly connected subset to filter out connections + std::vector pos(grid_.number_of_cells, -1); + for (int i = 0; i < num_cells; ++i) { + const int cell = cells[i]; + pos[cell] = i; + } - // Note: partially copied from below. - const double tol = 1e-9; - const int max_iters = 300; - // Must store s0 before we start. - std::vector s0(num_cells); - // Must set initial fractional flows before we start. - // Also, we compute the # of upstream neighbours. - // std::vector num_upstream(num_cells); - for (int i = 0; i < num_cells; ++i) { - const int cell = cells[i]; - fractionalflow_[cell] = fracFlow(saturation_[cell], cell); - s0[i] = saturation_[cell]; - // num_upstream[i] = ia_upw_[cell + 1] - ia_upw_[cell]; - } - // Solve once in each cell. - // std::vector fully_marked_stack; - // fully_marked_stack.reserve(num_cells); - int num_iters = 0; - int update_count = 0; // Change name/meaning to cells_updated? - do { - update_count = 0; // Must reset count for every iteration. - for (int i = 0; i < num_cells; ++i) { - // while (!fully_marked_stack.empty()) { - // // std::cout << "# fully marked cells = " << fully_marked_stack.size() << std::endl; - // const int fully_marked_ci = fully_marked_stack.back(); - // fully_marked_stack.pop_back(); - // ++update_count; - // const int cell = cells[fully_marked_ci]; - // const double old_s = saturation_[cell]; - // saturation_[cell] = s0[fully_marked_ci]; - // solveSingleCell(cell); - // const double s_change = std::fabs(saturation_[cell] - old_s); - // if (s_change > tol) { - // // Mark downwind cells. - // for (int j = ia_downw_[cell]; j < ia_downw_[cell+1]; ++j) { - // const int downwind_cell = ja_downw_[j]; - // int ci = pos[downwind_cell]; - // ++needs_update[ci]; - // if (needs_update[ci] == num_upstream[ci]) { - // fully_marked_stack.push_back(ci); - // } - // } - // } - // // Unmark this cell. - // needs_update[fully_marked_ci] = 0; - // } - if (!needs_update[i]) { - continue; - } - ++update_count; - const int cell = cells[i]; - const double old_s = saturation_[cell]; - saturation_[cell] = s0[i]; - solveSingleCell(cell); - const double s_change = std::fabs(saturation_[cell] - old_s); - if (s_change > tol) { - // Mark downwind cells. - for (int j = ia_downw_[cell]; j < ia_downw_[cell+1]; ++j) { - const int downwind_cell = ja_downw_[j]; - int ci = pos[downwind_cell]; + // Note: partially copied from below. + const double tol = 1e-9; + const int max_iters = 300; + // Must store s0 before we start. + std::vector s0(num_cells); + // Must set initial fractional flows before we start. + // Also, we compute the # of upstream neighbours. + // std::vector num_upstream(num_cells); + for (int i = 0; i < num_cells; ++i) { + const int cell = cells[i]; + fractionalflow_[cell] = fracFlow(saturation_[cell], cell); + s0[i] = saturation_[cell]; + // num_upstream[i] = ia_upw_[cell + 1] - ia_upw_[cell]; + } + // Solve once in each cell. + // std::vector fully_marked_stack; + // fully_marked_stack.reserve(num_cells); + int num_iters = 0; + int update_count = 0; // Change name/meaning to cells_updated? + do { + update_count = 0; // Must reset count for every iteration. + for (int i = 0; i < num_cells; ++i) { + // while (!fully_marked_stack.empty()) { + // // std::cout << "# fully marked cells = " << fully_marked_stack.size() << std::endl; + // const int fully_marked_ci = fully_marked_stack.back(); + // fully_marked_stack.pop_back(); + // ++update_count; + // const int cell = cells[fully_marked_ci]; + // const double old_s = saturation_[cell]; + // saturation_[cell] = s0[fully_marked_ci]; + // solveSingleCell(cell); + // const double s_change = std::fabs(saturation_[cell] - old_s); + // if (s_change > tol) { + // // Mark downwind cells. + // for (int j = ia_downw_[cell]; j < ia_downw_[cell+1]; ++j) { + // const int downwind_cell = ja_downw_[j]; + // int ci = pos[downwind_cell]; + // ++needs_update[ci]; + // if (needs_update[ci] == num_upstream[ci]) { + // fully_marked_stack.push_back(ci); + // } + // } + // } + // // Unmark this cell. + // needs_update[fully_marked_ci] = 0; + // } + if (!needs_update[i]) { + continue; + } + ++update_count; + const int cell = cells[i]; + const double old_s = saturation_[cell]; + saturation_[cell] = s0[i]; + solveSingleCell(cell); + const double s_change = std::fabs(saturation_[cell] - old_s); + if (s_change > tol) { + // Mark downwind cells. + for (int j = ia_downw_[cell]; j < ia_downw_[cell+1]; ++j) { + const int downwind_cell = ja_downw_[j]; + int ci = pos[downwind_cell]; if (ci != -1) { needs_update[ci] = 1; } - // ++needs_update[ci]; - // if (needs_update[ci] == num_upstream[ci]) { - // fully_marked_stack.push_back(ci); - // } - } - } - // Unmark this cell. - needs_update[i] = 0; - } - // std::cout << "Iter = " << num_iters << " update_count = " << update_count - // << " # marked cells = " - // << std::accumulate(needs_update.begin(), needs_update.end(), 0) << std::endl; - } while (update_count > 0 && ++num_iters < max_iters); + // ++needs_update[ci]; + // if (needs_update[ci] == num_upstream[ci]) { + // fully_marked_stack.push_back(ci); + // } + } + } + // Unmark this cell. + needs_update[i] = 0; + } + // std::cout << "Iter = " << num_iters << " update_count = " << update_count + // << " # marked cells = " + // << std::accumulate(needs_update.begin(), needs_update.end(), 0) << std::endl; + } while (update_count > 0 && ++num_iters < max_iters); - // Done with iterations, check if we succeeded. - if (update_count > 0) { - THROW("In solveMultiCell(), we did not converge after " - << num_iters << " iterations. Remaining update count = " << update_count); - } - std::cout << "Solved " << num_cells << " cell multicell problem in " - << num_iters << " iterations." << std::endl; + // Done with iterations, check if we succeeded. + if (update_count > 0) { + THROW("In solveMultiCell(), we did not converge after " + << num_iters << " iterations. Remaining update count = " << update_count); + } + std::cout << "Solved " << num_cells << " cell multicell problem in " + << num_iters << " iterations." << std::endl; #else - double max_s_change = 0.0; - const double tol = 1e-9; - const int max_iters = 300; - int num_iters = 0; - // Must store s0 before we start. - std::vector s0(num_cells); - // Must set initial fractional flows before we start. - for (int i = 0; i < num_cells; ++i) { - const int cell = cells[i]; - fractionalflow_[cell] = fracFlow(saturation_[cell], cell); - s0[i] = saturation_[cell]; - } - do { - max_s_change = 0.0; - for (int i = 0; i < num_cells; ++i) { - const int cell = cells[i]; - const double old_s = saturation_[cell]; - saturation_[cell] = s0[i]; - solveSingleCell(cell); - double s_change = std::fabs(saturation_[cell] - old_s); - // std::cout << "cell = " << cell << " delta s = " << s_change << std::endl; - if (max_s_change < s_change) { - max_s_change = s_change; - } - } - // std::cout << "Iter = " << num_iters << " max_s_change = " << max_s_change - // << " in cell " << max_change_cell << std::endl; - } while (max_s_change > tol && ++num_iters < max_iters); - if (max_s_change > tol) { - THROW("In solveMultiCell(), we did not converge after " - << num_iters << " iterations. Delta s = " << max_s_change); - } - std::cout << "Solved " << num_cells << " cell multicell problem in " - << num_iters << " iterations." << std::endl; + double max_s_change = 0.0; + const double tol = 1e-9; + const int max_iters = 300; + int num_iters = 0; + // Must store s0 before we start. + std::vector s0(num_cells); + // Must set initial fractional flows before we start. + for (int i = 0; i < num_cells; ++i) { + const int cell = cells[i]; + fractionalflow_[cell] = fracFlow(saturation_[cell], cell); + s0[i] = saturation_[cell]; + } + do { + max_s_change = 0.0; + for (int i = 0; i < num_cells; ++i) { + const int cell = cells[i]; + const double old_s = saturation_[cell]; + saturation_[cell] = s0[i]; + solveSingleCell(cell); + double s_change = std::fabs(saturation_[cell] - old_s); + // std::cout << "cell = " << cell << " delta s = " << s_change << std::endl; + if (max_s_change < s_change) { + max_s_change = s_change; + } + } + // std::cout << "Iter = " << num_iters << " max_s_change = " << max_s_change + // << " in cell " << max_change_cell << std::endl; + } while (max_s_change > tol && ++num_iters < max_iters); + if (max_s_change > tol) { + THROW("In solveMultiCell(), we did not converge after " + << num_iters << " iterations. Delta s = " << max_s_change); + } + std::cout << "Solved " << num_cells << " cell multicell problem in " + << num_iters << " iterations." << std::endl; #endif // EXPERIMENT_GAUSS_SEIDEL } double TransportModelTwophase::fracFlow(double s, int cell) const { - double sat[2] = { s, 1.0 - s }; - double mob[2]; - props_.relperm(1, sat, &cell, mob, 0); - mob[0] /= visc_[0]; - mob[1] /= visc_[1]; - return mob[0]/(mob[0] + mob[1]); + double sat[2] = { s, 1.0 - s }; + double mob[2]; + props_.relperm(1, sat, &cell, mob, 0); + mob[0] /= visc_[0]; + mob[1] /= visc_[1]; + return mob[0]/(mob[0] + mob[1]); } @@ -450,19 +460,19 @@ namespace Opm // struct TransportModelTwophase::GravityResidual { - int cell; + int cell; int nbcell[2]; - double s0; - double dtpv; // dt/pv(i) + double s0; + double dtpv; // dt/pv(i) double gf[2]; - const TransportModelTwophase& tm; - explicit GravityResidual(const TransportModelTwophase& tmodel, + const TransportModelTwophase& tm; + explicit GravityResidual(const TransportModelTwophase& tmodel, const std::vector& cells, const int pos, const double* gravflux) // Always oriented towards next in column. Size = colsize - 1. - : tm(tmodel) - { - cell = cells[pos]; + : tm(tmodel) + { + cell = cells[pos]; nbcell[0] = -1; gf[0] = 0.0; if (pos > 0) { @@ -475,40 +485,40 @@ namespace Opm nbcell[1] = cells[pos + 1]; gf[1] = gravflux[pos]; } - s0 = tm.saturation_[cell]; - dtpv = tm.dt_/tm.porevolume_[cell]; - - } - double operator()(double s) const - { - double res = s - s0; + s0 = tm.saturation_[cell]; + dtpv = tm.dt_/tm.porevolume_[cell]; + + } + double operator()(double s) const + { + double res = s - s0; double mobcell[2]; tm.mobility(s, cell, mobcell); for (int nb = 0; nb < 2; ++nb) { - if (nbcell[nb] != -1) { + if (nbcell[nb] != -1) { double m[2]; if (gf[nb] < 0.0) { m[0] = mobcell[0]; m[1] = tm.mob_[2*nbcell[nb] + 1]; - } else { + } else { m[0] = tm.mob_[2*nbcell[nb]]; m[1] = mobcell[1]; - } - if (m[0] + m[1] > 0.0) { + } + if (m[0] + m[1] > 0.0) { res += -dtpv*gf[nb]*m[0]*m[1]/(m[0] + m[1]); } - } + } } return res; - } + } }; void TransportModelTwophase::mobility(double s, int cell, double* mob) const { - double sat[2] = { s, 1.0 - s }; - props_.relperm(1, sat, &cell, mob, 0); - mob[0] /= visc_[0]; - mob[1] /= visc_[1]; + double sat[2] = { s, 1.0 - s }; + props_.relperm(1, sat, &cell, mob, 0); + mob[0] /= visc_[0]; + mob[1] /= visc_[1]; } @@ -544,11 +554,12 @@ namespace Opm const int cell = cells[pos]; GravityResidual res(*this, cells, pos, gravflux); if (std::fabs(res(saturation_[cell])) > tol_) { - int iters_used; + int iters_used = 0; saturation_[cell] = RootFinder::solve(res, smin_[2*cell], smax_[2*cell], maxit_, tol_, iters_used); + reorder_iterations_[cell] = reorder_iterations_[cell] + iters_used; } saturation_[cell] = std::min(std::max(saturation_[cell], smin_[2*cell]), smax_[2*cell]); - mobility(saturation_[cell], cell, &mob_[2*cell]); + mobility(saturation_[cell], cell, &mob_[2*cell]); } @@ -559,17 +570,17 @@ namespace Opm const int nc = cells.size(); std::vector col_gravflux(nc - 1); for (int ci = 0; ci < nc - 1; ++ci) { - const int cell = cells[ci]; - const int next_cell = cells[ci + 1]; - for (int j = grid_.cell_facepos[cell]; j < grid_.cell_facepos[cell+1]; ++j) { - const int face = grid_.cell_faces[j]; - const int c1 = grid_.face_cells[2*face + 0]; + const int cell = cells[ci]; + const int next_cell = cells[ci + 1]; + for (int j = grid_.cell_facepos[cell]; j < grid_.cell_facepos[cell+1]; ++j) { + const int face = grid_.cell_faces[j]; + const int c1 = grid_.face_cells[2*face + 0]; const int c2 = grid_.face_cells[2*face + 1]; - if (c1 == next_cell || c2 == next_cell) { + if (c1 == next_cell || c2 == next_cell) { const double gf = gravflux_[face]; col_gravflux[ci] = (c1 == cell) ? gf : -gf; - } - } + } + } } // Store initial saturation s0 @@ -579,7 +590,7 @@ namespace Opm } // Solve single cell problems, repeating if necessary. - double max_s_change = 0.0; + double max_s_change = 0.0; int num_iters = 0; do { max_s_change = 0.0; @@ -595,12 +606,12 @@ namespace Opm std::fabs(saturation_[cells[ci2]] - old_s[1]))); } // std::cout << "Iter = " << num_iters << " max_s_change = " << max_s_change << std::endl; - } while (max_s_change > tol_ && ++num_iters < maxit_); + } while (max_s_change > tol_ && ++num_iters < maxit_); - if (max_s_change > tol_) { - THROW("In solveGravityColumn(), we did not converge after " - << num_iters << " iterations. Delta s = " << max_s_change); - } + if (max_s_change > tol_) { + THROW("In solveGravityColumn(), we did not converge after " + << num_iters << " iterations. Delta s = " << max_s_change); + } return num_iters + 1; } diff --git a/opm/core/transport/reorder/TransportModelTwophase.hpp b/opm/core/transport/reorder/TransportModelTwophase.hpp index ca302c18..97386c8a 100644 --- a/opm/core/transport/reorder/TransportModelTwophase.hpp +++ b/opm/core/transport/reorder/TransportModelTwophase.hpp @@ -23,7 +23,7 @@ #include #include #include - +#include struct UnstructuredGrid; namespace Opm @@ -35,27 +35,27 @@ namespace Opm class TransportModelTwophase : public TransportModelInterface { public: - /// Construct solver. - /// \param[in] grid A 2d or 3d grid. - /// \param[in] props Rock and fluid properties. - /// \param[in] tol Tolerance used in the solver. - /// \param[in] maxit Maximum number of non-linear iterations used. - TransportModelTwophase(const UnstructuredGrid& grid, - const Opm::IncompPropertiesInterface& props, - const double tol, - const int maxit); + /// Construct solver. + /// \param[in] grid A 2d or 3d grid. + /// \param[in] props Rock and fluid properties. + /// \param[in] tol Tolerance used in the solver. + /// \param[in] maxit Maximum number of non-linear iterations used. + TransportModelTwophase(const UnstructuredGrid& grid, + const Opm::IncompPropertiesInterface& props, + const double tol, + const int maxit); - /// Solve for saturation at next timestep. - /// \param[in] darcyflux Array of signed face fluxes. - /// \param[in] porevolume Array of pore volumes. - /// \param[in] source Transport source term. - /// \param[in] dt Time step. - /// \param[in, out] saturation Phase saturations. - void solve(const double* darcyflux, + /// Solve for saturation at next timestep. + /// \param[in] darcyflux Array of signed face fluxes. + /// \param[in] porevolume Array of pore volumes. + /// \param[in] source Transport source term. + /// \param[in] dt Time step. + /// \param[in, out] saturation Phase saturations. + void solve(const double* darcyflux, const double* porevolume, - const double* source, - const double dt, - std::vector& saturation); + const double* source, + const double dt, + std::vector& saturation); /// Initialise quantities needed by gravity solver. /// \param[in] grav gravity vector @@ -66,52 +66,57 @@ namespace Opm /// It assumes that the input columns contain cells in a single /// vertical stack, that do not interact with other columns (for /// gravity segregation. - /// \param[in] columns Vector of cell-columns. - /// \param[in] porevolume Array of pore volumes. - /// \param[in] dt Time step. - /// \param[in, out] saturation Phase saturations. + /// \param[in] columns Vector of cell-columns. + /// \param[in] porevolume Array of pore volumes. + /// \param[in] dt Time step. + /// \param[in, out] saturation Phase saturations. void solveGravity(const std::vector >& columns, const double* porevolume, const double dt, std::vector& saturation); + //// Return the number of iterations used by the reordering solver. + //// \return vector of iteration per cell + const std::vector& getReorderIterations() const; + private: - virtual void solveSingleCell(const int cell); - virtual void solveMultiCell(const int num_cells, const int* cells); + virtual void solveSingleCell(const int cell); + virtual void solveMultiCell(const int num_cells, const int* cells); void solveSingleCellGravity(const std::vector& cells, const int pos, const double* gravflux); int solveGravityColumn(const std::vector& cells); - private: - const UnstructuredGrid& grid_; - const IncompPropertiesInterface& props_; - const double* visc_; - std::vector smin_; - std::vector smax_; - double tol_; - double maxit_; + const UnstructuredGrid& grid_; + const IncompPropertiesInterface& props_; + const double* visc_; + std::vector smin_; + std::vector smax_; + double tol_; + double maxit_; - const double* darcyflux_; // one flux per grid face - const double* porevolume_; // one volume per cell - const double* source_; // one source per cell - double dt_; + const double* darcyflux_; // one flux per grid face + const double* porevolume_; // one volume per cell + const double* source_; // one source per cell + double dt_; std::vector saturation_; // one per cell, only water saturation! - std::vector fractionalflow_; // = m[0]/(m[0] + m[1]) per cell + std::vector fractionalflow_; // = m[0]/(m[0] + m[1]) per cell + std::vector reorder_iterations_; + //std::vector reorder_fval_; // For gravity segregation. std::vector gravflux_; std::vector mob_; std::vector s0_; - // Storing the upwind and downwind graphs for experiments. - std::vector ia_upw_; - std::vector ja_upw_; - std::vector ia_downw_; - std::vector ja_downw_; + // Storing the upwind and downwind graphs for experiments. + std::vector ia_upw_; + std::vector ja_upw_; + std::vector ia_downw_; + std::vector ja_downw_; - struct Residual; - double fracFlow(double s, int cell) const; + struct Residual; + double fracFlow(double s, int cell) const; struct GravityResidual; void mobility(double s, int cell, double* mob) const; diff --git a/opm/core/utility/Factory.hpp b/opm/core/utility/Factory.hpp index eb73d208..124b5b44 100644 --- a/opm/core/utility/Factory.hpp +++ b/opm/core/utility/Factory.hpp @@ -86,7 +86,7 @@ namespace Opm template static void addCreator(const std::string& type) { - instance().doAddCreator(type); + instance().template doAddCreator(type); } private: diff --git a/opm/core/utility/MonotCubicInterpolator.hpp b/opm/core/utility/MonotCubicInterpolator.hpp index 7e1c6710..fb635ac7 100644 --- a/opm/core/utility/MonotCubicInterpolator.hpp +++ b/opm/core/utility/MonotCubicInterpolator.hpp @@ -73,7 +73,7 @@ class MonotCubicInterpolator { if (!read(datafilename)) { throw("Unable to constuct MonotCubicInterpolator from file.") ; } ; - } ; + } /** @@ -94,7 +94,7 @@ class MonotCubicInterpolator { if (!read(std::string(datafilename))) { throw("Unable to constuct MonotCubicInterpolator from file.") ; } ; - } ; + } /** @@ -110,7 +110,7 @@ class MonotCubicInterpolator { if (!read(std::string(datafilename),xColumn,fColumn)) { throw("Unable to constuct MonotCubicInterpolator from file.") ; } ; - } ; + } /** @param datafilename data file @@ -125,7 +125,7 @@ class MonotCubicInterpolator { if (!read(datafilename,xColumn,fColumn)) { throw("Unable to constuct MonotCubicInterpolator from file.") ; } ; - } ; + } /** @param x vector of x values @@ -144,7 +144,7 @@ class MonotCubicInterpolator { This object must be treated with care until populated. */ - MonotCubicInterpolator() {;} ; + MonotCubicInterpolator() { } @@ -163,7 +163,7 @@ class MonotCubicInterpolator { */ bool read(const std::string & datafilename) { return read(datafilename,1,2) ; - } ; + } /** @param datafilename data file @@ -188,7 +188,7 @@ class MonotCubicInterpolator { @return f(x) for a given x */ - double operator () (double x) const { return evaluate(x) ; } ; + double operator () (double x) const { return evaluate(x) ; } /** @param x x value @@ -485,7 +485,7 @@ class MonotCubicInterpolator { */ void shrinkFlatAreas() { shrinkFlatAreas(1e-14); - }; + } diff --git a/opm/core/utility/NonuniformTableLinear.hpp b/opm/core/utility/NonuniformTableLinear.hpp new file mode 100644 index 00000000..71334460 --- /dev/null +++ b/opm/core/utility/NonuniformTableLinear.hpp @@ -0,0 +1,244 @@ +/* + Copyright 2009, 2010, 2011, 2012 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010, 2011, 2012 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 . +*/ + +#ifndef OPM_NONUNIFORMTABLELINEAR_HEADER_INCLUDED +#define OPM_NONUNIFORMTABLELINEAR_HEADER_INCLUDED + +#include +#include +#include +#include + +#include +#include + +namespace Opm +{ + + + /// @brief Exception used for domain errors. + struct OutsideDomainException : public std::exception {}; + + + /// @brief This class uses linear interpolation to compute the value + /// (and its derivative) of a function f sampled at possibly + /// nonuniform points. + /// @tparam T the range type of the function (should be an algebraic ring type) + template + class NonuniformTableLinear + { + public: + /// @brief Default constructor. + NonuniformTableLinear(); + + /// @brief Construct from vectors of x and y values. + /// @param x_values vector of domain values + /// @param y_values vector of corresponding range values. + NonuniformTableLinear(const std::vector& x_values, + const std::vector& y_values); + + /// @brief Get the domain. + /// @return the domain as a pair of doubles. + std::pair domain(); + + /// @brief Rescale the domain. + /// @param new_domain the new domain as a pair of doubles. + void rescaleDomain(std::pair new_domain); + + /// @brief Evaluate the value at x. + /// @param x a domain value + /// @return f(x) + double operator()(const double x) const; + + /// @brief Evaluate the derivative at x. + /// @param x a domain value + /// @return f'(x) + double derivative(const double x) const; + + /// @brief Evaluate the inverse at y. Requires T to be a double. + /// @param y a range value + /// @return f^{-1}(y) + double inverse(const double y) const; + + /// @brief Equality operator. + /// @param other another NonuniformTableLinear. + /// @return true if they are represented exactly alike. + bool operator==(const NonuniformTableLinear& other) const; + + /// @brief Policies for how to behave when trying to evaluate outside the domain. + enum RangePolicy {Throw = 0, ClosestValue = 1, Extrapolate = 2}; + + /// @brief Sets the behavioural policy for evaluation to the left of the domain. + /// @param rp the policy + void setLeftPolicy(RangePolicy rp); + + /// @brief Sets the behavioural policy for evaluation to the right of the domain. + /// @param rp the policy + void setRightPolicy(RangePolicy rp); + + protected: + std::vector x_values_; + std::vector y_values_; + mutable std::vector x_values_reversed_; + mutable std::vector y_values_reversed_; + RangePolicy left_; + RangePolicy right_; + }; + + + // A utility function + /// @brief Detect if a sequence is nondecreasing. + /// @tparam FI a forward iterator whose value type has operator< defined. + /// @param beg start of sequence + /// @param end one-beyond-end of sequence + /// @return false if there exists two consecutive values (v1, v2) in the sequence + /// for which v2 < v1, else returns true. + template + bool isNondecreasing(const FI beg, const FI end) + { + if (beg == end) return true; + FI it = beg; + ++it; + FI prev = beg; + for (; it != end; ++it, ++prev) { + if (*it < *prev) { + return false; + } + } + return true; + } + + + + // Member implementations. + + template + inline + NonuniformTableLinear + ::NonuniformTableLinear() + : left_(ClosestValue), right_(ClosestValue) + { + } + + template + inline + NonuniformTableLinear + ::NonuniformTableLinear(const std::vector& x_values, + const std::vector& y_values) + : x_values_(x_values), y_values_(y_values), + left_(ClosestValue), right_(ClosestValue) + { + ASSERT(isNondecreasing(x_values.begin(), x_values.end())); + } + + template + inline std::pair + NonuniformTableLinear + ::domain() + { + return std::make_pair(x_values_[0], x_values_.back()); + } + + template + inline void + NonuniformTableLinear + ::rescaleDomain(std::pair new_domain) + { + const double a = x_values_[0]; + const double b = x_values_.back(); + const double c = new_domain.first; + const double d = new_domain.second; + // x in [a, b] -> x in [c, d] + for (int i = 0; i < int(x_values_.size()); ++i) { + x_values_[i] = (x_values_[i] - a)*(d - c)/(b - a) + c; + } + } + + template + inline double + NonuniformTableLinear + ::operator()(const double x) const + { + return Opm::linearInterpolation(x_values_, y_values_, x); + } + + template + inline double + NonuniformTableLinear + ::derivative(const double x) const + { + return Opm::linearInterpolationDerivative(x_values_, y_values_, x); + } + + template + inline double + NonuniformTableLinear + ::inverse(const double y) const + { + if (y_values_.front() < y_values_.back()) { + return Opm::linearInterpolation(y_values_, x_values_, y); + } else { + if (y_values_reversed_.empty()) { + y_values_reversed_ = y_values_; + std::reverse(y_values_reversed_.begin(), y_values_reversed_.end()); + ASSERT(isNondecreasing(y_values_reversed_.begin(), y_values_reversed_.end())); + x_values_reversed_ = x_values_; + std::reverse(x_values_reversed_.begin(), x_values_reversed_.end()); + } + return Opm::linearInterpolation(y_values_reversed_, x_values_reversed_, y); + } + } + + template + inline bool + NonuniformTableLinear + ::operator==(const NonuniformTableLinear& other) const + { + return x_values_ == other.x_values_ + && y_values_ == other.y_values_ + && left_ == other.left_ + && right_ == other.right_; + } + + template + inline void + NonuniformTableLinear + ::setLeftPolicy(RangePolicy rp) + { + if (rp != ClosestValue) { + THROW("Only ClosestValue RangePolicy implemented."); + } + left_ = rp; + } + + template + inline void + NonuniformTableLinear + ::setRightPolicy(RangePolicy rp) + { + if (rp != ClosestValue) { + THROW("Only ClosestValue RangePolicy implemented."); + } + right_ = rp; + } + +} // namespace Opm + +#endif // OPM_NONUNIFORMTABLELINEAR_HEADER_INCLUDED diff --git a/opm/core/utility/StopWatch.hpp b/opm/core/utility/StopWatch.hpp index ee996d40..ec631670 100644 --- a/opm/core/utility/StopWatch.hpp +++ b/opm/core/utility/StopWatch.hpp @@ -57,11 +57,11 @@ namespace Opm /// restarted by a call to start(). void stop(); - /// \ret the number of running seconds that have passed + /// \return the number of running seconds that have passed /// since last call to start(), secsSinceLast() or /// secsSinceStart() double secsSinceLast(); - /// \ret the number of running seconds that have passed + /// \return the number of running seconds that have passed /// since last call to start(). double secsSinceStart(); diff --git a/opm/core/utility/UniformTableLinear.hpp b/opm/core/utility/UniformTableLinear.hpp index 11c2d4ca..2a0fc55f 100644 --- a/opm/core/utility/UniformTableLinear.hpp +++ b/opm/core/utility/UniformTableLinear.hpp @@ -188,19 +188,25 @@ namespace Opm { UniformTableLinear ::derivative(const double xparam) const { - // Implements ClosestValue policy. - double x = std::min(xparam, xmax_); - x = std::max(x, xmin_); - - // Lookup is easy since we are uniform in x. - double pos = (x - xmin_)/xdelta_; - double posi = std::floor(pos); - int left = int(posi); - if (left == int(y_values_.size()) - 1) { - // We are at xmax_ - --left; + // Implements derivative consistent + // with ClosestValue policy for function + double value; + if (xparam > xmax_ || xparam < xmin_) { + value = 0.0; + } else { + double x = std::min(xparam, xmax_); + x = std::max(x, xmin_); + // Lookup is easy since we are uniform in x. + double pos = (x - xmin_)/xdelta_; + double posi = std::floor(pos); + int left = int(posi); + if (left == int(y_values_.size()) - 1) { + // We are at xmax_ + --left; + } + value = (y_values_[left + 1] - y_values_[left])/xdelta_; } - return (y_values_[left + 1] - y_values_[left])/xdelta_; + return value; } diff --git a/opm/core/utility/Units.hpp b/opm/core/utility/Units.hpp index 2e8e37b2..b1b0f1ee 100644 --- a/opm/core/utility/Units.hpp +++ b/opm/core/utility/Units.hpp @@ -35,18 +35,24 @@ #ifndef OPENRS_UNITS_HEADER #define OPENRS_UNITS_HEADER +/** + * \file + * Constants and routines to assist in handling units of measurement. These are + * geared towards handling common units in reservoir descriptions. + */ + namespace Opm { namespace prefix /// Conversion prefix for units. { - const double micro = 1.0e-6; - const double milli = 1.0e-3; - const double centi = 1.0e-2; - const double deci = 1.0e-1; - const double kilo = 1.0e3; - const double mega = 1.0e6; - const double giga = 1.0e9; + const double micro = 1.0e-6; /**< Unit prefix [\f$\mu\f$] */ + const double milli = 1.0e-3; /**< Unit prefix [m] */ + const double centi = 1.0e-2; /**< Non-standard unit prefix [c] */ + const double deci = 1.0e-1; /**< Non-standard unit prefix [d] */ + const double kilo = 1.0e3; /**< Unit prefix [k] */ + const double mega = 1.0e6; /**< Unit prefix [M] */ + const double giga = 1.0e9; /**< Unit prefix [G] */ } // namespace prefix namespace unit @@ -137,15 +143,6 @@ namespace Opm const double Poise = prefix::deci*Pas; /// @} - /// \name Permeability - /// @{ - /// - /// A porous medium with a permeability of 1 darcy permits a - /// flow (flux) of \f$1\,cm^3/s\f$ of a fluid with viscosity - /// \f$1\,cP\f$ (\f$1\,mPa\cdot s\f$) under a pressure - /// gradient of \f$1\,atm/cm\f$ acting across an area of - /// \f$1\,cm^2\f$. - /// namespace perm_details { const double p_grad = atm / (prefix::centi*meter); const double area = square(prefix::centi*meter); @@ -156,47 +153,63 @@ namespace Opm // == 1e-7 [m^2] / 101325 // == 9.869232667160130e-13 [m^2] } + /// \name Permeability + /// @{ + /// + /// A porous medium with a permeability of 1 darcy permits a flow (flux) + /// of \f$1\,\mathit{cm}^3/s\f$ of a fluid with viscosity + /// \f$1\,\mathit{cP}\f$ (\f$1\,mPa\cdot s\f$) under a pressure gradient + /// of \f$1\,\mathit{atm}/\mathit{cm}\f$ acting across an area of + /// \f$1\,\mathit{cm}^2\f$. + /// const double darcy = perm_details::darcy; /// @} - // Unit conversion support. - // - // Note: Under the penalty of treason will you be - // - // using namespace Opm::unit::convert; - // - // I mean it! - // + /** + * Unit conversion routines. + */ namespace convert { - // Convert from external units of measurements to equivalent - // internal units of measurements. Note: The internal units - // of measurements are *ALWAYS*, and exclusively, SI. - // - // Example: Convert a double kx, containing a permeability - // value in units of milli-darcy (mD) to the equivalent - // value in SI units (m^2). - // - // using namespace Opm::unit; - // using namespace Opm::prefix; - // convert::from(kx, milli*darcy); - // + /** + * Convert from external units of measurements to equivalent + * internal units of measurements. Note: The internal units of + * measurements are *ALWAYS*, and exclusively, SI. + * + * Example: Convert a double @c kx, containing a permeability value + * in units of milli-darcy (mD) to the equivalent value in SI units + * (i.e., \f$m^2\f$). + * \code + * using namespace Opm::unit; + * using namespace Opm::prefix; + * convert::from(kx, milli*darcy); + * \endcode + * + * @param[in] q Physical quantity. + * @param[in] unit Physical unit of measurement. + * @return Value of @c q in equivalent SI units of measurements. + */ inline double from(const double q, const double unit) { return q * unit; } - // Convert from internal units of measurements to equivalent - // external units of measurements. Note: The internal units - // of measurements are *ALWAYS*, and exclusively, SI. - // - // Example: Convert a std::vector p, containing - // pressure values in the SI unit Pascal (i.e., unit::Pascal) - // to the equivalent values in Psi (unit::psia). - // - // using namespace Opm::unit; - // std::transform(p.begin(), p.end(), p.begin(), - // boost::bind(convert::to, _1, psia)); - // + /** + * Convert from internal units of measurements to equivalent + * external units of measurements. Note: The internal units of + * measurements are *ALWAYS*, and exclusively, SI. + * + * Example: Convert a std::vector p, containing + * pressure values in the SI unit Pascal (i.e., unit::Pascal) to the + * equivalent values in Psi (unit::psia). + * \code + * using namespace Opm::unit; + * std::transform(p.begin(), p.end(), p.begin(), + * boost::bind(convert::to, _1, psia)); + * \endcode + * + * @param[in] q Physical quantity, measured in SI units. + * @param[in] unit Physical unit of measurement. + * @return Value of @c q in unit unit. + */ inline double to(const double q, const double unit) { return q / unit; diff --git a/opm/core/utility/have_boost_redef.hpp b/opm/core/utility/have_boost_redef.hpp new file mode 100644 index 00000000..bba49681 --- /dev/null +++ b/opm/core/utility/have_boost_redef.hpp @@ -0,0 +1,44 @@ +/*=========================================================================== +// +// File: have_boost_redef.hpp +// +// Created: 2012-07-12 11:02:22+0200 +// +//==========================================================================*/ + + +/* + Copyright 2012 SINTEF ICT, Applied Mathematics. + Copyright 2012 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 . +*/ + +// Note: This file should be #include<>d after in software +// that uses the ISTL. This is more or less a hack to work around +// Autoconfs that do not correctly process dune-istl's `ENABLE_BOOST' +// feature. We will happily remove this file if a better solution +// presents. + +#ifndef OPM_HAVE_BOOST_REDEF_HPP_HEADER +#define OPM_HAVE_BOOST_REDEF_HPP_HEADER + +#if defined(HAVE_BOOST) +#undef HAVE_BOOST +#define HAVE_BOOST OPM_HAVE_BOOST +#endif + +#endif /* OPM_HAVE_BOOST_REDEF_HPP_HEADER */ diff --git a/opm/core/utility/initState_impl.hpp b/opm/core/utility/initState_impl.hpp index c50db8a9..7d01ade0 100644 --- a/opm/core/utility/initState_impl.hpp +++ b/opm/core/utility/initState_impl.hpp @@ -512,11 +512,11 @@ namespace Opm State& state) { const int num_phases = props.numPhases(); - if (num_phases != 2) { - THROW("initStateFromDeck(): currently handling only two-phase scenarios."); - } state.init(grid, num_phases); if (deck.hasField("EQUIL")) { + if (num_phases != 2) { + THROW("initStateFromDeck(): EQUIL-based init currently handling only two-phase scenarios."); + } // Set saturations depending on oil-water contact. const EQUIL& equil= deck.getEQUIL(); if (equil.equil.size() != 1) { @@ -535,11 +535,27 @@ namespace Opm const std::vector& sw_deck = deck.getFloatingPointValue("SWAT"); const std::vector& p_deck = deck.getFloatingPointValue("PRESSURE"); const int num_cells = grid.number_of_cells; - for (int c = 0; c < num_cells; ++c) { - int c_deck = (grid.global_cell == NULL) ? c : grid.global_cell[c]; - s[2*c] = sw_deck[c_deck]; - s[2*c + 1] = 1.0 - s[2*c]; - p[c] = p_deck[c_deck]; + if (num_phases == 2) { + for (int c = 0; c < num_cells; ++c) { + int c_deck = (grid.global_cell == NULL) ? c : grid.global_cell[c]; + s[2*c] = sw_deck[c_deck]; + s[2*c + 1] = 1.0 - s[2*c]; + p[c] = p_deck[c_deck]; + } + } else if (num_phases == 3) { + if (!deck.hasField("SGAS")) { + THROW("initStateFromDeck(): missing SGAS keyword in 3-phase init (only SWAT and PRESSURE found)."); + } + const std::vector& sg_deck = deck.getFloatingPointValue("SGAS"); + for (int c = 0; c < num_cells; ++c) { + int c_deck = (grid.global_cell == NULL) ? c : grid.global_cell[c]; + s[3*c] = sw_deck[c_deck]; + s[3*c + 1] = 1.0 - (sw_deck[c_deck] + sg_deck[c_deck]); + s[3*c + 2] = sg_deck[c_deck]; + p[c] = p_deck[c_deck]; + } + } else { + THROW("initStateFromDeck(): init with SWAT etc. only available with 2 or 3 phases."); } } else { THROW("initStateFromDeck(): we must either have EQUIL, or both SWAT and PRESSURE."); diff --git a/opm/core/utility/linearInterpolation.hpp b/opm/core/utility/linearInterpolation.hpp index 0ed8b57b..743f396c 100644 --- a/opm/core/utility/linearInterpolation.hpp +++ b/opm/core/utility/linearInterpolation.hpp @@ -85,6 +85,40 @@ namespace Opm } } + template + T linearInterpolationDerivativeExtrap(const std::vector& xv, + const std::vector& yv, + double x) + { + std::vector::const_iterator lb = std::lower_bound(xv.begin(), xv.end(), x); + int lb_ix = lb - xv.begin(); + int nend = int(xv.size()); + if (lb_ix == 0) { + return (yv[1]-yv[0])/(xv[1]-xv[0]); + } else if (lb_ix == int(xv.size())) { + return (yv[nend-1]-yv[nend-2])/(xv[nend-1]-xv[nend-2]); + } else { + return (yv[lb_ix] - yv[lb_ix - 1])/(xv[lb_ix] - xv[lb_ix - 1]); + } + } + + template + T linearInterpolationExtrap(const std::vector& xv, + const std::vector& yv, + double x) + { + std::vector::const_iterator lb = std::lower_bound(xv.begin(), xv.end(), x); + int lb_ix = lb - xv.begin(); + if (lb_ix == 0) { + lb_ix=1; + } else if (lb_ix == int(xv.size())){ + lb_ix = int(xv.size())-1; + } + double w = (x - xv[lb_ix - 1])/(xv[lb_ix] - xv[lb_ix - 1]); + return (1.0 - w)*yv[lb_ix - 1] + w*yv[lb_ix]; + + } + } // namespace Opm diff --git a/opm/core/utility/miscUtilities.cpp b/opm/core/utility/miscUtilities.cpp index 16c59b17..aeafbfca 100644 --- a/opm/core/utility/miscUtilities.cpp +++ b/opm/core/utility/miscUtilities.cpp @@ -40,14 +40,14 @@ namespace Opm /// @param[out] porevol the pore volume by cell. void computePorevolume(const UnstructuredGrid& grid, const double* porosity, - std::vector& porevol) + std::vector& porevol) { - int num_cells = grid.number_of_cells; - porevol.resize(num_cells); - std::transform(porosity, porosity + num_cells, - grid.cell_volumes, - porevol.begin(), - std::multiplies()); + int num_cells = grid.number_of_cells; + porevol.resize(num_cells); + std::transform(porosity, porosity + num_cells, + grid.cell_volumes, + porevol.begin(), + std::multiplies()); } @@ -70,6 +70,25 @@ namespace Opm } } + /// @brief Computes porosity of all cells in a grid, with rock compressibility effects. + /// @param[in] grid a grid + /// @param[in] porosity_standard array of grid.number_of_cells porosity values (at standard conditions) + /// @param[in] rock_comp rock compressibility properties + /// @param[in] pressure pressure by cell + /// @param[out] porosity porosity (at reservoir condition) + void computePorosity(const UnstructuredGrid& grid, + const double* porosity_standard, + const RockCompressibility& rock_comp, + const std::vector& pressure, + std::vector& porosity) + { + int num_cells = grid.number_of_cells; + porosity.resize(num_cells); + for (int i = 0; i < num_cells; ++i) { + porosity[i] = porosity_standard[i]*rock_comp.poroMult(pressure[i]); + } + } + /// @brief Computes total saturated volumes over all grid cells. /// @param[in] pv the pore volume by cell. @@ -79,20 +98,20 @@ namespace Opm /// For each phase p, we compute /// sat_vol_p = sum_i s_p_i pv_i void computeSaturatedVol(const std::vector& pv, - const std::vector& s, - double* sat_vol) + const std::vector& s, + double* sat_vol) { - const int num_cells = pv.size(); - const int np = s.size()/pv.size(); - if (int(s.size()) != num_cells*np) { - THROW("Sizes of s and pv vectors do not match."); - } - std::fill(sat_vol, sat_vol + np, 0.0); - for (int c = 0; c < num_cells; ++c) { - for (int p = 0; p < np; ++p) { - sat_vol[p] += pv[c]*s[np*c + p]; - } - } + const int num_cells = pv.size(); + const int np = s.size()/pv.size(); + if (int(s.size()) != num_cells*np) { + THROW("Sizes of s and pv vectors do not match."); + } + std::fill(sat_vol, sat_vol + np, 0.0); + for (int c = 0; c < num_cells; ++c) { + for (int p = 0; p < np; ++p) { + sat_vol[p] += pv[c]*s[np*c + p]; + } + } } @@ -104,28 +123,28 @@ namespace Opm /// For each phase p, we compute /// aver_sat_p = (sum_i s_p_i pv_i) / (sum_i pv_i). void computeAverageSat(const std::vector& pv, - const std::vector& s, - double* aver_sat) + const std::vector& s, + double* aver_sat) { - const int num_cells = pv.size(); - const int np = s.size()/pv.size(); - if (int(s.size()) != num_cells*np) { - THROW("Sizes of s and pv vectors do not match."); - } - double tot_pv = 0.0; - // Note that we abuse the output array to accumulate the - // saturated pore volumes. - std::fill(aver_sat, aver_sat + np, 0.0); - for (int c = 0; c < num_cells; ++c) { - tot_pv += pv[c]; - for (int p = 0; p < np; ++p) { - aver_sat[p] += pv[c]*s[np*c + p]; - } - } - // Must divide by pore volumes to get saturations. - for (int p = 0; p < np; ++p) { - aver_sat[p] /= tot_pv; - } + const int num_cells = pv.size(); + const int np = s.size()/pv.size(); + if (int(s.size()) != num_cells*np) { + THROW("Sizes of s and pv vectors do not match."); + } + double tot_pv = 0.0; + // Note that we abuse the output array to accumulate the + // saturated pore volumes. + std::fill(aver_sat, aver_sat + np, 0.0); + for (int c = 0; c < num_cells; ++c) { + tot_pv += pv[c]; + for (int p = 0; p < np; ++p) { + aver_sat[p] += pv[c]*s[np*c + p]; + } + } + // Must divide by pore volumes to get saturations. + for (int p = 0; p < np; ++p) { + aver_sat[p] /= tot_pv; + } } @@ -142,38 +161,38 @@ namespace Opm /// where P = s.size()/src.size(). /// @param[out] produced must also point to a valid array with P elements. void computeInjectedProduced(const IncompPropertiesInterface& props, - const std::vector& s, - const std::vector& src, - const double dt, - double* injected, - double* produced) + const std::vector& s, + const std::vector& src, + const double dt, + double* injected, + double* produced) { - const int num_cells = src.size(); - const int np = s.size()/src.size(); - if (int(s.size()) != num_cells*np) { - THROW("Sizes of s and src vectors do not match."); - } - std::fill(injected, injected + np, 0.0); - std::fill(produced, produced + np, 0.0); - const double* visc = props.viscosity(); - std::vector mob(np); - for (int c = 0; c < num_cells; ++c) { - if (src[c] > 0.0) { - injected[0] += src[c]*dt; - } else if (src[c] < 0.0) { - const double flux = -src[c]*dt; - const double* sat = &s[np*c]; - props.relperm(1, sat, &c, &mob[0], 0); - double totmob = 0.0; - for (int p = 0; p < np; ++p) { - mob[p] /= visc[p]; - totmob += mob[p]; - } - for (int p = 0; p < np; ++p) { - produced[p] += (mob[p]/totmob)*flux; - } - } - } + const int num_cells = src.size(); + const int np = s.size()/src.size(); + if (int(s.size()) != num_cells*np) { + THROW("Sizes of s and src vectors do not match."); + } + std::fill(injected, injected + np, 0.0); + std::fill(produced, produced + np, 0.0); + const double* visc = props.viscosity(); + std::vector mob(np); + for (int c = 0; c < num_cells; ++c) { + if (src[c] > 0.0) { + injected[0] += src[c]*dt; + } else if (src[c] < 0.0) { + const double flux = -src[c]*dt; + const double* sat = &s[np*c]; + props.relperm(1, sat, &c, &mob[0], 0); + double totmob = 0.0; + for (int p = 0; p < np; ++p) { + mob[p] /= visc[p]; + totmob += mob[p]; + } + for (int p = 0; p < np; ++p) { + produced[p] += (mob[p]/totmob)*flux; + } + } + } } @@ -184,9 +203,9 @@ namespace Opm /// @param[in] s saturation values (for all phases) /// @param[out] totmob total mobilities. void computeTotalMobility(const Opm::IncompPropertiesInterface& props, - const std::vector& cells, - const std::vector& s, - std::vector& totmob) + const std::vector& cells, + const std::vector& s, + std::vector& totmob) { std::vector pmobc; @@ -212,10 +231,10 @@ namespace Opm /// @param[out] totmob total mobility /// @param[out] omega fractional-flow weighted fluid densities. void computeTotalMobilityOmega(const Opm::IncompPropertiesInterface& props, - const std::vector& cells, - const std::vector& s, - std::vector& totmob, - std::vector& omega) + const std::vector& cells, + const std::vector& s, + std::vector& totmob, + std::vector& omega) { std::vector pmobc; @@ -312,33 +331,33 @@ namespace Opm /// (+) positive inflow of first phase (water) /// (-) negative total outflow of both phases void computeTransportSource(const UnstructuredGrid& grid, - const std::vector& src, - const std::vector& faceflux, - const double inflow_frac, + const std::vector& src, + const std::vector& faceflux, + const double inflow_frac, const Wells* wells, const std::vector& well_perfrates, - std::vector& transport_src) + std::vector& transport_src) { - int nc = grid.number_of_cells; - transport_src.resize(nc); + int nc = grid.number_of_cells; + transport_src.resize(nc); // Source term and boundary contributions. - for (int c = 0; c < nc; ++c) { - transport_src[c] = 0.0; - transport_src[c] += src[c] > 0.0 ? inflow_frac*src[c] : src[c]; - for (int hf = grid.cell_facepos[c]; hf < grid.cell_facepos[c + 1]; ++hf) { - int f = grid.cell_faces[hf]; - const int* f2c = &grid.face_cells[2*f]; - double bdy_influx = 0.0; - if (f2c[0] == c && f2c[1] == -1) { - bdy_influx = -faceflux[f]; - } else if (f2c[0] == -1 && f2c[1] == c) { - bdy_influx = faceflux[f]; - } - if (bdy_influx != 0.0) { - transport_src[c] += bdy_influx > 0.0 ? inflow_frac*bdy_influx : bdy_influx; - } - } - } + for (int c = 0; c < nc; ++c) { + transport_src[c] = 0.0; + transport_src[c] += src[c] > 0.0 ? inflow_frac*src[c] : src[c]; + for (int hf = grid.cell_facepos[c]; hf < grid.cell_facepos[c + 1]; ++hf) { + int f = grid.cell_faces[hf]; + const int* f2c = &grid.face_cells[2*f]; + double bdy_influx = 0.0; + if (f2c[0] == c && f2c[1] == -1) { + bdy_influx = -faceflux[f]; + } else if (f2c[0] == -1 && f2c[1] == c) { + bdy_influx = faceflux[f]; + } + if (bdy_influx != 0.0) { + transport_src[c] += bdy_influx > 0.0 ? inflow_frac*bdy_influx : bdy_influx; + } + } + } // Well contributions. if (wells) { @@ -373,52 +392,52 @@ namespace Opm /// @param[in] face_flux signed per-face fluxes /// @param[out] cell_velocity the estimated velocities. void estimateCellVelocity(const UnstructuredGrid& grid, - const std::vector& face_flux, - std::vector& cell_velocity) + const std::vector& face_flux, + std::vector& cell_velocity) { - const int dim = grid.dimensions; - cell_velocity.clear(); - cell_velocity.resize(grid.number_of_cells*dim, 0.0); - for (int face = 0; face < grid.number_of_faces; ++face) { - int c[2] = { grid.face_cells[2*face], grid.face_cells[2*face + 1] }; - const double* fc = &grid.face_centroids[face*dim]; - double flux = face_flux[face]; - for (int i = 0; i < 2; ++i) { - if (c[i] >= 0) { - const double* cc = &grid.cell_centroids[c[i]*dim]; - for (int d = 0; d < dim; ++d) { - double v_contrib = fc[d] - cc[d]; - v_contrib *= flux/grid.cell_volumes[c[i]]; - cell_velocity[c[i]*dim + d] += (i == 0) ? v_contrib : -v_contrib; - } - } - } - } + const int dim = grid.dimensions; + cell_velocity.clear(); + cell_velocity.resize(grid.number_of_cells*dim, 0.0); + for (int face = 0; face < grid.number_of_faces; ++face) { + int c[2] = { grid.face_cells[2*face], grid.face_cells[2*face + 1] }; + const double* fc = &grid.face_centroids[face*dim]; + double flux = face_flux[face]; + for (int i = 0; i < 2; ++i) { + if (c[i] >= 0) { + const double* cc = &grid.cell_centroids[c[i]*dim]; + for (int d = 0; d < dim; ++d) { + double v_contrib = fc[d] - cc[d]; + v_contrib *= flux/grid.cell_volumes[c[i]]; + cell_velocity[c[i]*dim + d] += (i == 0) ? v_contrib : -v_contrib; + } + } + } + } } /// Extract a vector of water saturations from a vector of /// interleaved water and oil saturations. void toWaterSat(const std::vector& sboth, - std::vector& sw) + std::vector& sw) { - int num = sboth.size()/2; - sw.resize(num); - for (int i = 0; i < num; ++i) { - sw[i] = sboth[2*i]; - } + int num = sboth.size()/2; + sw.resize(num); + for (int i = 0; i < num; ++i) { + sw[i] = sboth[2*i]; + } } /// Make a a vector of interleaved water and oil saturations from /// a vector of water saturations. void toBothSat(const std::vector& sw, - std::vector& sboth) + std::vector& sboth) { - int num = sw.size(); - sboth.resize(2*num); - for (int i = 0; i < num; ++i) { - sboth[2*i] = sw[i]; - sboth[2*i + 1] = 1.0 - sw[i]; - } + int num = sw.size(); + sboth.resize(2*num); + for (int i = 0; i < num; ++i) { + sboth[2*i] = sw[i]; + sboth[2*i + 1] = 1.0 - sw[i]; + } } @@ -431,30 +450,30 @@ namespace Opm if (np != 2) { THROW("wellsToSrc() requires a 2 phase case."); } - src.resize(num_cells); - for (int w = 0; w < wells.number_of_wells; ++w) { + src.resize(num_cells); + for (int w = 0; w < wells.number_of_wells; ++w) { const int cur = wells.ctrls[w]->current; - if (wells.ctrls[w]->num != 1) { - MESSAGE("In wellsToSrc(): well has more than one control, all but current control will be ignored."); - } - if (wells.ctrls[w]->type[cur] != RESERVOIR_RATE) { - THROW("In wellsToSrc(): well is something other than RESERVOIR_RATE."); - } - if (wells.well_connpos[w+1] - wells.well_connpos[w] != 1) { - THROW("In wellsToSrc(): well has multiple perforations."); - } + if (wells.ctrls[w]->num != 1) { + MESSAGE("In wellsToSrc(): well has more than one control, all but current control will be ignored."); + } + if (wells.ctrls[w]->type[cur] != RESERVOIR_RATE) { + THROW("In wellsToSrc(): well is something other than RESERVOIR_RATE."); + } + if (wells.well_connpos[w+1] - wells.well_connpos[w] != 1) { + THROW("In wellsToSrc(): well has multiple perforations."); + } for (int p = 0; p < np; ++p) { if (wells.ctrls[w]->distr[np*cur + p] != 1.0) { THROW("In wellsToSrc(): well not controlled on total rate."); } } - double flow = wells.ctrls[w]->target[cur]; + double flow = wells.ctrls[w]->target[cur]; if (wells.type[w] == INJECTOR) { flow *= wells.comp_frac[np*w + 0]; // Obtaining water rate for inflow source. } - const double cell = wells.well_cells[wells.well_connpos[w]]; - src[cell] = flow; - } + const double cell = wells.well_cells[wells.well_connpos[w]]; + src[cell] = flow; + } } @@ -574,8 +593,10 @@ namespace Opm { int nw = well_bhp.size(); ASSERT(nw == wells.number_of_wells); - if (props.numPhases() != 2) { - THROW("WellReport for now assumes two phase flow."); + int np = props.numPhases(); + const int max_np = 3; + if (np > max_np) { + THROW("WellReport for now assumes #phases <= " << max_np); } const double* visc = props.viscosity(); std::vector data_now; @@ -586,7 +607,8 @@ namespace Opm double well_rate_total = 0.0; double well_rate_water = 0.0; for (int perf = wells.well_connpos[w]; perf < wells.well_connpos[w + 1]; ++perf) { - const double perf_rate = well_perfrates[perf]*(unit::day/unit::second); + const double perf_rate = unit::convert::to(well_perfrates[perf], + unit::cubic(unit::meter)/unit::day); well_rate_total += perf_rate; if (perf_rate > 0.0) { // Injection. @@ -594,11 +616,14 @@ namespace Opm } else { // Production. const int cell = wells.well_cells[perf]; - double mob[2]; + double mob[max_np]; props.relperm(1, &saturation[2*cell], &cell, mob, 0); - mob[0] /= visc[0]; - mob[1] /= visc[1]; - const double fracflow = mob[0]/(mob[0] + mob[1]); + double tmob = 0; + for(int i = 0; i < np; ++i) { + mob[i] /= visc[i]; + tmob += mob[i]; + } + const double fracflow = mob[0]/tmob; well_rate_water += perf_rate*fracflow; } } @@ -627,8 +652,10 @@ namespace Opm // TODO: refactor, since this is almost identical to the other push(). int nw = well_bhp.size(); ASSERT(nw == wells.number_of_wells); - if (props.numPhases() != 2) { - THROW("WellReport for now assumes two phase flow."); + int np = props.numPhases(); + const int max_np = 3; + if (np > max_np) { + THROW("WellReport for now assumes #phases <= " << max_np); } std::vector data_now; data_now.reserve(1 + 3*nw); @@ -638,7 +665,8 @@ namespace Opm double well_rate_total = 0.0; double well_rate_water = 0.0; for (int perf = wells.well_connpos[w]; perf < wells.well_connpos[w + 1]; ++perf) { - const double perf_rate = well_perfrates[perf]*(unit::day/unit::second); + const double perf_rate = unit::convert::to(well_perfrates[perf], + unit::cubic(unit::meter)/unit::day); well_rate_total += perf_rate; if (perf_rate > 0.0) { // Injection. @@ -646,13 +674,16 @@ namespace Opm } else { // Production. const int cell = wells.well_cells[perf]; - double mob[2]; - props.relperm(1, &s[2*cell], &cell, mob, 0); - double visc[2]; - props.viscosity(1, &p[cell], &z[2*cell], &cell, visc, 0); - mob[0] /= visc[0]; - mob[1] /= visc[1]; - const double fracflow = mob[0]/(mob[0] + mob[1]); + double mob[max_np]; + props.relperm(1, &s[np*cell], &cell, mob, 0); + double visc[max_np]; + props.viscosity(1, &p[cell], &z[np*cell], &cell, visc, 0); + double tmob = 0; + for(int i = 0; i < np; ++i) { + mob[i] /= visc[i]; + tmob += mob[i]; + } + const double fracflow = mob[0]/(tmob); well_rate_water += perf_rate*fracflow; } } diff --git a/opm/core/utility/miscUtilities.hpp b/opm/core/utility/miscUtilities.hpp index 37b252bc..2def319a 100644 --- a/opm/core/utility/miscUtilities.hpp +++ b/opm/core/utility/miscUtilities.hpp @@ -44,7 +44,7 @@ namespace Opm /// @brief Computes pore volume of all cells in a grid, with rock compressibility effects. /// @param[in] grid a grid - /// @param[in] porosity array of grid.number_of_cells porosity values + /// @param[in] porosity array of grid.number_of_cells porosity values (at reference pressure) /// @param[in] rock_comp rock compressibility properties /// @param[in] pressure pressure by cell /// @param[out] porevol the pore volume by cell. @@ -54,6 +54,17 @@ namespace Opm const std::vector& pressure, std::vector& porevol); + /// @brief Computes porosity of all cells in a grid, with rock compressibility effects. + /// @param[in] grid a grid + /// @param[in] porosity_standard array of grid.number_of_cells porosity values (at reference presure) + /// @param[in] rock_comp rock compressibility properties + /// @param[in] pressure pressure by cell + /// @param[out] porosity porosity (at reservoir condition) + void computePorosity(const UnstructuredGrid& grid, + const double* porosity_standard, + const RockCompressibility& rock_comp, + const std::vector& pressure, + std::vector& porosity); /// @brief Computes total saturated volumes over all grid cells. /// @param[in] pv the pore volume by cell. diff --git a/opm/core/utility/miscUtilitiesBlackoil.cpp b/opm/core/utility/miscUtilitiesBlackoil.cpp index af088e05..6f4f7556 100644 --- a/opm/core/utility/miscUtilitiesBlackoil.cpp +++ b/opm/core/utility/miscUtilitiesBlackoil.cpp @@ -48,39 +48,39 @@ namespace Opm void computeInjectedProduced(const BlackoilPropertiesInterface& props, const std::vector& press, const std::vector& z, - const std::vector& s, - const std::vector& src, - const double dt, - double* injected, - double* produced) + const std::vector& s, + const std::vector& src, + const double dt, + double* injected, + double* produced) { - const int num_cells = src.size(); - const int np = s.size()/src.size(); - if (int(s.size()) != num_cells*np) { - THROW("Sizes of s and src vectors do not match."); - } - std::fill(injected, injected + np, 0.0); - std::fill(produced, produced + np, 0.0); + const int num_cells = src.size(); + const int np = s.size()/src.size(); + if (int(s.size()) != num_cells*np) { + THROW("Sizes of s and src vectors do not match."); + } + std::fill(injected, injected + np, 0.0); + std::fill(produced, produced + np, 0.0); std::vector visc(np); - std::vector mob(np); - for (int c = 0; c < num_cells; ++c) { - if (src[c] > 0.0) { - injected[0] += src[c]*dt; - } else if (src[c] < 0.0) { - const double flux = -src[c]*dt; - const double* sat = &s[np*c]; - props.relperm(1, sat, &c, &mob[0], 0); + std::vector mob(np); + for (int c = 0; c < num_cells; ++c) { + if (src[c] > 0.0) { + injected[0] += src[c]*dt; + } else if (src[c] < 0.0) { + const double flux = -src[c]*dt; + const double* sat = &s[np*c]; + props.relperm(1, sat, &c, &mob[0], 0); props.viscosity(1, &press[c], &z[np*c], &c, &visc[0], 0); - double totmob = 0.0; - for (int p = 0; p < np; ++p) { - mob[p] /= visc[p]; - totmob += mob[p]; - } - for (int p = 0; p < np; ++p) { - produced[p] += (mob[p]/totmob)*flux; - } - } - } + double totmob = 0.0; + for (int p = 0; p < np; ++p) { + mob[p] /= visc[p]; + totmob += mob[p]; + } + for (int p = 0; p < np; ++p) { + produced[p] += (mob[p]/totmob)*flux; + } + } + } } @@ -93,11 +93,11 @@ namespace Opm /// @param[in] s saturation values (for all phases) /// @param[out] totmob total mobilities. void computeTotalMobility(const Opm::BlackoilPropertiesInterface& props, - const std::vector& cells, + const std::vector& cells, const std::vector& press, const std::vector& z, - const std::vector& s, - std::vector& totmob) + const std::vector& s, + std::vector& totmob) { std::vector pmobc; @@ -126,12 +126,12 @@ namespace Opm /// @param[out] totmob total mobility /// @param[out] omega fractional-flow weighted fluid densities. void computeTotalMobilityOmega(const Opm::BlackoilPropertiesInterface& props, - const std::vector& cells, + const std::vector& cells, const std::vector& p, const std::vector& z, - const std::vector& s, - std::vector& totmob, - std::vector& omega) + const std::vector& s, + std::vector& totmob, + std::vector& omega) { std::vector pmobc; @@ -185,10 +185,10 @@ namespace Opm props.relperm(nc, &s[0], &cells[0], &pmobc[0], dpmobc); - std::transform(pmobc.begin(), pmobc.end(), - mu.begin(), - pmobc.begin(), - std::divides()); + std::transform(pmobc.begin(), pmobc.end(), + mu.begin(), + pmobc.begin(), + std::divides()); } /// Computes the fractional flow for each cell in the cells argument @@ -220,5 +220,35 @@ namespace Opm } } + /// Computes the surface volume densities from saturations by the formula + /// z = A s + /// for a number of data points, where z is the surface volume density, + /// s is the saturation (both as column vectors) and A is the + /// phase-to-component relation matrix. + /// @param[in] n number of data points + /// @param[in] np number of phases, must be 2 or 3 + /// @param[in] A array containing n square matrices of size num_phases^2, + /// in Fortran ordering, typically the output of a call + /// to the matrix() method of a BlackoilProperties* class. + /// @param[in] saturation concatenated saturation values (for all P phases) + /// @param[out] surfacevol concatenated surface-volume values (for all P phases) + void computeSurfacevol(const int n, + const int np, + const double* A, + const double* saturation, + double* surfacevol) + { + // Note: since this is a simple matrix-vector product, it can + // be done by a BLAS call, but then we have to reorder the A + // matrix data. + std::fill(surfacevol, surfacevol + n*np, 0.0); + for (int i = 0; i < n; ++i) { + for (int col = 0; col < np; ++col) { + for (int row = 0; row < np; ++row) { + surfacevol[i*np + row] += A[i*np*np + row + col*np] * saturation[i*np + col]; + } + } + } + } } // namespace Opm diff --git a/opm/core/utility/miscUtilitiesBlackoil.hpp b/opm/core/utility/miscUtilitiesBlackoil.hpp index 929d42ec..565acc6e 100644 --- a/opm/core/utility/miscUtilitiesBlackoil.hpp +++ b/opm/core/utility/miscUtilitiesBlackoil.hpp @@ -46,11 +46,11 @@ namespace Opm void computeInjectedProduced(const BlackoilPropertiesInterface& props, const std::vector& p, const std::vector& z, - const std::vector& s, - const std::vector& src, - const double dt, - double* injected, - double* produced); + const std::vector& s, + const std::vector& src, + const double dt, + double* injected, + double* produced); /// @brief Computes total mobility for a set of saturation values. /// @param[in] props rock and fluid properties @@ -60,11 +60,11 @@ namespace Opm /// @param[in] s saturation values (for all phases) /// @param[out] totmob total mobilities. void computeTotalMobility(const Opm::BlackoilPropertiesInterface& props, - const std::vector& cells, + const std::vector& cells, const std::vector& p, const std::vector& z, - const std::vector& s, - std::vector& totmob); + const std::vector& s, + std::vector& totmob); /// @brief Computes total mobility and omega for a set of saturation values. /// @param[in] props rock and fluid properties @@ -75,12 +75,12 @@ namespace Opm /// @param[out] totmob total mobility /// @param[out] omega fractional-flow weighted fluid densities. void computeTotalMobilityOmega(const Opm::BlackoilPropertiesInterface& props, - const std::vector& cells, + const std::vector& cells, const std::vector& p, const std::vector& z, - const std::vector& s, - std::vector& totmob, - std::vector& omega); + const std::vector& s, + std::vector& totmob, + std::vector& omega); /// @brief Computes phase mobilities for a set of saturation values. @@ -96,7 +96,7 @@ namespace Opm const std::vector& z, const std::vector& s, std::vector& pmobc); - + /// Computes the fractional flow for each cell in the cells argument /// @param[in] props rock and fluid properties @@ -112,6 +112,25 @@ namespace Opm const std::vector& s, std::vector& fractional_flows); + + /// Computes the surface volume densities from saturations by the formula + /// z = A s + /// for a number of data points, where z is the surface volume density, + /// s is the saturation (both as column vectors) and A is the + /// phase-to-component relation matrix. + /// @param[in] n number of data points + /// @param[in] np number of phases, must be 2 or 3 + /// @param[in] A array containing n square matrices of size num_phases, + /// in Fortran ordering, typically the output of a call + /// to the matrix() method of a BlackoilProperties* class. + /// @param[in] saturation concatenated saturation values (for all P phases) + /// @param[out] surfacevol concatenated surface-volume values (for all P phases) + void computeSurfacevol(const int n, + const int np, + const double* A, + const double* saturation, + double* surfacevol); + } // namespace Opm #endif // OPM_MISCUTILITIESBLACKOIL_HEADER_INCLUDED diff --git a/tests/Makefile.am b/tests/Makefile.am index 29003343..bb6a1c16 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,27 +1,29 @@ AM_CPPFLAGS = \ -I$(top_srcdir) \ -$(BOOST_CPPFLAGS) \ +$(OPM_BOOST_CPPFLAGS) \ $(ERT_CPPFLAGS) -AM_LDFLAGS = $(BOOST_LDFLAGS) $(ERT_LDFLAGS) +AM_LDFLAGS = $(OPM_BOOST_LDFLAGS) $(ERT_LDFLAGS) -LDADD = $(top_builddir)/libopmcore.la +LDADD = $(top_builddir)/lib/libopmcore.la -noinst_PROGRAMS = \ -bo_resprop_test \ -monotcubicinterpolator_test \ -param_test \ -sparsetable_test \ -sparsevector_test \ -test_cartgrid \ -test_column_extract \ -test_lapack \ -test_read_vag \ -test_readpolymer \ -test_sf2p \ -test_writeVtkData \ +noinst_PROGRAMS = \ +bo_resprop_test \ +monotcubicinterpolator_test \ +param_test \ +pvt_test \ +relperm_test \ +sparsetable_test \ +sparsevector_test \ +test_cartgrid \ +test_column_extract \ +test_lapack \ +test_read_vag \ +test_readpolymer \ +test_sf2p \ +test_writeVtkData \ unit_test @@ -32,6 +34,10 @@ monotcubicinterpolator_test_SOURCES = monotcubicinterpolator_test.cpp param_test_SOURCES = param_test.cpp param_test_LDADD = $(LDADD) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) +pvt_test_SOURCES = pvt_test.cpp + +relperm_test_SOURCES = relperm_test.cpp + sparsetable_test_SOURCES = sparsetable_test.cpp sparsetable_test_LDADD = $(LDADD) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) diff --git a/tests/bo_resprop_test.cpp b/tests/bo_resprop_test.cpp index 316bd9f3..02357e75 100644 --- a/tests/bo_resprop_test.cpp +++ b/tests/bo_resprop_test.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -39,14 +40,10 @@ int main(int argc, char** argv) // Parser. std::string ecl_file = param.get("filename"); Opm::EclipseGridParser deck(ecl_file); - Opm::EclipseGridInspector insp(deck); - std::tr1::array gs = insp.gridSize(); - int num_cells = gs[0]*gs[1]*gs[2]; - std::vector global_cell(num_cells); - for (int i = 0; i < num_cells; ++i) { - global_cell[i] = i; - } - Opm::BlackoilPropertiesFromDeck props(deck, global_cell); + UnstructuredGrid grid; + grid.number_of_cells = 1; + grid.global_cell = NULL; + Opm::BlackoilPropertiesFromDeck props(deck, grid, param); const int n = 1; double p[n] = { 150e5 }; diff --git a/tests/pvt_test.cpp b/tests/pvt_test.cpp new file mode 100644 index 00000000..a175885a --- /dev/null +++ b/tests/pvt_test.cpp @@ -0,0 +1,146 @@ +/* + Copyright 2012 SINTEF ICT, Applied Mathematics. + + 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 . +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + using namespace std; + // Parameters. + Opm::parameter::ParameterGroup param(argc, argv); + + // Parser. + std::string ecl_file = param.get("deck_filename"); + std::string input_file = param.get("input_filename"); + std::string matrix_output = param.get("matrix_output"); + std::string rs_output = param.get("rs_output"); + std::string b_output = param.get("b_output"); + std::string mu_output = param.get("mu_output"); + Opm::EclipseGridParser deck(ecl_file); + UnstructuredGrid grid; + grid.number_of_cells = 1; + grid.global_cell = NULL; + grid.dimensions = 3; + grid.cartdims[0] = 1; + grid.cartdims[1] = 1; + grid.cartdims[2] = 1; + Opm::BlackoilPropertiesFromDeck props(deck, grid, param); + Opm::BlackoilPvtProperties pvt; + int samples = param.getDefault("dead_tab_size", 1025); + pvt.init(deck, samples); + + + const int n = 1; + std::fstream inos(input_file.c_str()); + if(!inos.good()){ + std::cout << "Could not open :" << input_file << std::endl; + exit(3); + } + std::fstream aos(matrix_output.c_str(), std::fstream::out | std::fstream::trunc); + aos << setiosflags(ios::scientific) << setprecision(12); + if(!(aos.good())){ + std::cout << "Could not open"<< matrix_output << std::endl; + exit(3); + } + std::fstream bos(b_output.c_str(), std::fstream::out | std::fstream::trunc); + bos << setiosflags(ios::scientific) << setprecision(12); + if(!(bos.good())){ + std::cout << "Could not open"<< b_output << std::endl; + exit(3); + } + std::fstream rsos(rs_output.c_str(), std::fstream::out | std::fstream::trunc); + rsos << setiosflags(ios::scientific) << setprecision(12); + if(!(rsos.good())){ + std::cout << "Could not open"<< rs_output << std::endl; + exit(3); + } + std::fstream muos(mu_output.c_str(), std::fstream::out | std::fstream::trunc); + if(!(muos.good())){ + std::cout << "Could not open"<< rs_output << std::endl; + exit(3); + } + + + + const int np = props.numPhases(); + const int max_np = 3; + if (np > max_np) { + THROW("Max #phases is 3."); + } + while((inos.good()) && (!inos.eof())){ + double p[n]; + double z[max_np*n]; + int cells[n] = { 0 }; + inos >> p[0]; + for(int i=0; i < np; ++i){ + inos >> z[i]; + } + + if(inos.good()){ + double A[max_np*max_np*n]; + double dA[max_np*max_np*n]; + props.matrix(n, p, z, cells, A, dA); + std::copy(A, A + np*np*n, std::ostream_iterator(aos, " ")); + std::copy(dA, dA + np*np*n, std::ostream_iterator(aos, " ")); + aos << std::endl; + double mu[max_np]; + //double dmu[max_np];//not implemented + props.viscosity(n, p, z, cells, mu, 0); + std::copy(mu, mu + np*n, std::ostream_iterator(muos, " ")); + //std::copy(dmu, dmu + np*n, std::ostream_iterator(muos, " ")); + aos << std::endl; + + double b[max_np]; + double dbdp[max_np]; + pvt.dBdp(n, p, z, b, dbdp); + std::copy(b, b + np*n, std::ostream_iterator(bos, " ")); + std::copy(dbdp, dbdp + np*n, std::ostream_iterator(bos, " ")); + bos << std::endl; + + double rs[max_np]; + double drs[max_np]; + //pvt.R(n, p, z, rs); + pvt.dRdp(n, p, z, rs,drs); + std::copy(rs, rs + np*n, std::ostream_iterator(rsos, " ")); + std::copy(drs, drs + np*n, std::ostream_iterator(rsos, " ")); + rsos << std::endl; + } + } + if (param.anyUnused()) { + std::cout << "-------------------- Unused parameters: --------------------\n"; + param.displayUsage(); + std::cout << "----------------------------------------------------------------" << std::endl; + } +} diff --git a/tests/relperm_test.cpp b/tests/relperm_test.cpp new file mode 100644 index 00000000..4c777071 --- /dev/null +++ b/tests/relperm_test.cpp @@ -0,0 +1,97 @@ +/* + Copyright 2012 SINTEF ICT, Applied Mathematics. + + 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 . +*/ + + +#if HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + // Parameters. + Opm::parameter::ParameterGroup param(argc, argv); + + // Parser. + std::string ecl_file = param.get("deck_filename"); + std::string input_file = param.get("input_filename"); + std::string relperm_output = param.get("relperm_output"); + //std::string relperm_output = param.get("relperm_outout"); + Opm::EclipseGridParser deck(ecl_file); + UnstructuredGrid grid; + grid.number_of_cells = 1; + grid.global_cell = NULL; + grid.dimensions = 3; + grid.cartdims[0] = 1; + grid.cartdims[1] = 1; + grid.cartdims[2] = 1; + Opm::BlackoilPropertiesFromDeck props(deck, grid, param); + + std::fstream inos(input_file.c_str());//, std::fstream::in); + if(!inos.good()){ + std::cout << "Could not open :" << input_file << std::endl; + exit(3); + } + std::fstream kros(relperm_output.c_str(), std::fstream::out | std::fstream::trunc); + if(!kros.good()){ + std::cout << "Could not open :" << input_file << std::endl; + exit(3); + } + const int np = props.numPhases(); + const int max_np = 3; + if (np > max_np) { + THROW("Max #phases is 3."); + } + while((inos.good()) && (!inos.eof())){ + double s[max_np]; + for(int i=0; i < np; ++i){ + inos >> s[i]; + } + if(inos.good()){ + double kr[max_np]; + double dkr[max_np*max_np]; + int cell[1]; + cell[0]=1; + props.relperm(1,s, cell, kr, dkr); + std::copy(s, s + np, std::ostream_iterator(kros, " ")); + kros << " "; + std::copy(kr, kr + np, std::ostream_iterator(kros, " ")); + kros << " "; + std::copy(dkr, dkr + np*np, std::ostream_iterator(kros, " ")); + kros << "\n"; + } + } + if (param.anyUnused()) { + std::cout << "-------------------- Unused parameters: --------------------\n"; + param.displayUsage(); + std::cout << "----------------------------------------------------------------" << std::endl; + } +} diff --git a/tutorials/Makefile.am b/tutorials/Makefile.am index 5d77386b..a5e42569 100644 --- a/tutorials/Makefile.am +++ b/tutorials/Makefile.am @@ -1,10 +1,10 @@ AM_CPPFLAGS = \ -I$(top_srcdir) \ -$(BOOST_CPPFLAGS) +$(OPM_BOOST_CPPFLAGS) -LDFLAGS = $(BOOST_LDFLAGS) +AM_LDFLAGS = $(OPM_BOOST_LDFLAGS) -LDADD = $(top_builddir)/libopmcore.la +LDADD = $(top_builddir)/lib/libopmcore.la noinst_PROGRAMS = tutorial1 tutorial1_SOURCES = tutorial1.cpp