Merge branch 'master' into ert

Conflicts:
	Makefile.am
	configure.ac
	examples/Makefile.am
	opm/core/GridManager.cpp
	opm/core/eclipse/EclipseGridParser.cpp
	opm/core/grid/cpgpreprocess/preprocess.h
	tests/Makefile.am
This commit is contained in:
Atgeirr Flø Rasmussen 2012-09-05 13:36:19 +02:00
commit 00bbfa06a8
119 changed files with 7557 additions and 2902 deletions

12
.gitignore vendored
View File

@ -1,4 +1,5 @@
*~ *~
.\#*
*.o *.o
*.lo *.lo
*.la *.la
@ -28,15 +29,26 @@ ltmain.sh
m4/libtool.m4 m4/libtool.m4
m4/lt*.m4 m4/lt*.m4
missing missing
tutorials/tutorial[1-4]
# Ignoring executables # Ignoring executables
*_test *_test
examples/scaneclipsedeck examples/scaneclipsedeck
examples/spu_2p examples/spu_2p
examples/reorder-qfs examples/reorder-qfs
examples/refine_wells
examples/sim_2p_incomp_reorder
examples/sim_wateroil
examples/wells_example
tests/test_cfs_tpfa tests/test_cfs_tpfa
tests/test_jacsys tests/test_jacsys
tests/test_readvector tests/test_readvector
tests/test_sf2p tests/test_sf2p
tests/bo_fluid_p_and_z_deps tests/bo_fluid_p_and_z_deps
tests/bo_fluid_pressuredeps tests/bo_fluid_pressuredeps
tests/test_cartgrid
tests/test_column_extract
tests/test_lapack
tests/test_read_vag
tests/test_readpolymer
tests/test_writeVtkData

View File

@ -2,8 +2,10 @@ cmake_minimum_required (VERSION 2.6)
project (opm-core) project (opm-core)
enable_language(Fortran) 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_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON) set(Boost_USE_MULTITHREADED ON)
@ -16,94 +18,50 @@ include_directories(${PROJECT_SOURCE_DIR} ${Boost_INCLUDE_DIRS})
# The opmcore library # The opmcore library
add_library(opmcore FILE(GLOB_RECURSE C_FILES_CORE "opm/core/*.c")
opm/core/eclipse/EclipseGridInspector.cpp FILE(GLOB_RECURSE CPP_FILES_CORE "opm/core/*.cpp")
opm/core/eclipse/EclipseGridParser.cpp FILE(GLOB_RECURSE REMOVE_FILES "processgrid.c")
opm/core/fluid/blackoil/BlackoilPvtProperties.cpp FILE(GLOB_RECURSE REMOVE_FILESMX "mx*.c")
opm/core/fluid/blackoil/SinglePvtDead.cpp FILE(GLOB_RECURSE REMOVE_FILESAGMG "*AGMG.cpp" "*test*")
opm/core/fluid/blackoil/SinglePvtLiveGas.cpp list(REMOVE_ITEM C_FILES_CORE ${REMOVE_FILES} ${REMOVE_FILESMX} )
opm/core/fluid/blackoil/SinglePvtLiveOil.cpp list(REMOVE_ITEM CPP_FILES_CORE ${REMOVE_FILES} ${REMOVE_FILESAGMG})
opm/core/fluid/blackoil/SinglePvtInterface.cpp add_library(opmcore ${C_FILES_CORE} ${CPP_FILES_CORE} )
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
)
target_link_libraries(opmcore target_link_libraries(opmcore
${UMFPACK_LIBRARIES} ${LAPACK_LINKER_FLAGS} ${LAPACK_LIBRARIES} ${Boost_LIBRARIES} ${UMFPACK_LIBRARIES} ${LAPACK_LINKER_FLAGS} ${LAPACK_LIBRARIES} ${Boost_LIBRARIES}
-lcholmod -lcamd -lccolamd -lmetis -ldunecommon -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(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 target_link_libraries(spu_2p
opmcore 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)

56
FindUmfPack.cmake Normal file
View File

@ -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)

View File

@ -6,34 +6,39 @@ SUBDIRS = . tests examples tutorials
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# Declare products (i.e., the library) # Declare products (i.e., the library)
lib_LTLIBRARIES = libopmcore.la lib_LTLIBRARIES = lib/libopmcore.la
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# Build-time flags needed to build libopmcore.la # Build-time flags needed to build libopmcore.la
AM_CPPFLAGS = \ AM_CPPFLAGS = \
$(ERT_CPPFLAGS) \ $(ERT_CPPFLAGS) \
$(BOOST_CPPFLAGS) $(OPM_BOOST_CPPFLAGS)
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# Link-time flags needed both to successfully link the library and to # Link-time flags needed both to successfully link the library and to
# (transitively) convey inter-library dependency information. # (transitively) convey inter-library dependency information.
libopmcore_la_LDFLAGS = \ lib_libopmcore_la_LDFLAGS = \
$(ERT_LDFLAGS) \ $(ERT_LDFLAGS) \
$(BOOST_LDFLAGS) \ $(OPM_BOOST_LDFLAGS) \
$(BOOST_FILESYSTEM_LIB) \ $(BOOST_FILESYSTEM_LIB) \
$(BOOST_SYSTEM_LIB) \ $(BOOST_SYSTEM_LIB) \
$(BOOST_DATE_TIME_LIB) \ $(BOOST_DATE_TIME_LIB) \
$(BOOST_UNIT_TEST_FRAMEWORK_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. # Library constituents. SOURCES followed by HEADERS.
# #
# Please try to keep the list sorted. # 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/GridManager.cpp \
opm/core/eclipse/EclipseGridInspector.cpp \ opm/core/eclipse/EclipseGridInspector.cpp \
opm/core/eclipse/EclipseGridParser.cpp \ opm/core/eclipse/EclipseGridParser.cpp \
@ -48,8 +53,11 @@ opm/core/fluid/RockCompressibility.cpp \
opm/core/fluid/RockFromDeck.cpp \ opm/core/fluid/RockFromDeck.cpp \
opm/core/fluid/SaturationPropsBasic.cpp \ opm/core/fluid/SaturationPropsBasic.cpp \
opm/core/fluid/SaturationPropsFromDeck.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/BlackoilPvtProperties.cpp \
opm/core/fluid/blackoil/SinglePvtDead.cpp \ opm/core/fluid/blackoil/SinglePvtDead.cpp \
opm/core/fluid/blackoil/SinglePvtDeadSpline.cpp \
opm/core/fluid/blackoil/SinglePvtInterface.cpp \ opm/core/fluid/blackoil/SinglePvtInterface.cpp \
opm/core/fluid/blackoil/SinglePvtLiveGas.cpp \ opm/core/fluid/blackoil/SinglePvtLiveGas.cpp \
opm/core/fluid/blackoil/SinglePvtLiveOil.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/ifs_tpfa.c \
opm/core/pressure/tpfa/trans_tpfa.c \ opm/core/pressure/tpfa/trans_tpfa.c \
opm/core/pressure/well.c \ opm/core/pressure/well.c \
opm/core/simulator/SimulatorCompressibleTwophase.cpp \
opm/core/simulator/SimulatorIncompTwophase.cpp \
opm/core/simulator/SimulatorReport.cpp \ opm/core/simulator/SimulatorReport.cpp \
opm/core/simulator/SimulatorTimer.cpp \ opm/core/simulator/SimulatorTimer.cpp \
opm/core/simulator/SimulatorTwophase.cpp \
opm/core/transport/reorder/TransportModelCompressibleTwophase.cpp \ opm/core/transport/reorder/TransportModelCompressibleTwophase.cpp \
opm/core/transport/reorder/TransportModelInterface.cpp \ opm/core/transport/reorder/TransportModelInterface.cpp \
opm/core/transport/reorder/TransportModelTwophase.cpp \ opm/core/transport/reorder/TransportModelTwophase.cpp \
@ -142,13 +151,18 @@ opm/core/fluid/PvtPropertiesIncompFromDeck.hpp \
opm/core/fluid/RockBasic.hpp \ opm/core/fluid/RockBasic.hpp \
opm/core/fluid/RockCompressibility.hpp \ opm/core/fluid/RockCompressibility.hpp \
opm/core/fluid/RockFromDeck.hpp \ opm/core/fluid/RockFromDeck.hpp \
opm/core/fluid/SatFuncStone2.hpp \
opm/core/fluid/SatFuncSimple.hpp \
opm/core/fluid/SaturationPropsBasic.hpp \ opm/core/fluid/SaturationPropsBasic.hpp \
opm/core/fluid/SaturationPropsFromDeck.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/SimpleFluid2p.hpp \
opm/core/fluid/blackoil/BlackoilPhases.hpp \ opm/core/fluid/blackoil/BlackoilPhases.hpp \
opm/core/fluid/blackoil/BlackoilPvtProperties.hpp \ opm/core/fluid/blackoil/BlackoilPvtProperties.hpp \
opm/core/fluid/blackoil/SinglePvtConstCompr.hpp \ opm/core/fluid/blackoil/SinglePvtConstCompr.hpp \
opm/core/fluid/blackoil/SinglePvtDead.hpp \ opm/core/fluid/blackoil/SinglePvtDead.hpp \
opm/core/fluid/blackoil/SinglePvtDeadSpline.hpp \
opm/core/fluid/blackoil/SinglePvtInterface.hpp \ opm/core/fluid/blackoil/SinglePvtInterface.hpp \
opm/core/fluid/blackoil/SinglePvtLiveGas.hpp \ opm/core/fluid/blackoil/SinglePvtLiveGas.hpp \
opm/core/fluid/blackoil/SinglePvtLiveOil.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/ifs_tpfa.h \
opm/core/pressure/tpfa/trans_tpfa.h \ opm/core/pressure/tpfa/trans_tpfa.h \
opm/core/simulator/BlackoilState.hpp \ opm/core/simulator/BlackoilState.hpp \
opm/core/simulator/SimulatorCompressibleTwophase.hpp \
opm/core/simulator/SimulatorReport.hpp \ opm/core/simulator/SimulatorReport.hpp \
opm/core/simulator/SimulatorIncompTwophase.hpp \
opm/core/simulator/SimulatorTimer.hpp \ opm/core/simulator/SimulatorTimer.hpp \
opm/core/simulator/SimulatorTwophase.hpp \
opm/core/simulator/TwophaseState.hpp \ opm/core/simulator/TwophaseState.hpp \
opm/core/simulator/WellState.hpp \ opm/core/simulator/WellState.hpp \
opm/core/transport/CSRMatrixBlockAssembler.hpp \ opm/core/transport/CSRMatrixBlockAssembler.hpp \
@ -219,9 +234,11 @@ opm/core/transport/spu_implicit.h \
opm/core/transport/transport_source.h \ opm/core/transport/transport_source.h \
opm/core/utility/Average.hpp \ opm/core/utility/Average.hpp \
opm/core/utility/ColumnExtract.hpp \ opm/core/utility/ColumnExtract.hpp \
opm/core/utility/DataMap.hpp \
opm/core/utility/ErrorMacros.hpp \ opm/core/utility/ErrorMacros.hpp \
opm/core/utility/Factory.hpp \ opm/core/utility/Factory.hpp \
opm/core/utility/MonotCubicInterpolator.hpp \ opm/core/utility/MonotCubicInterpolator.hpp \
opm/core/utility/NonuniformTableLinear.hpp \
opm/core/utility/RootFinders.hpp \ opm/core/utility/RootFinders.hpp \
opm/core/utility/SparseTable.hpp \ opm/core/utility/SparseTable.hpp \
opm/core/utility/SparseVector.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/tinystr.h \
opm/core/utility/parameters/tinyxml/tinyxml.h \ opm/core/utility/parameters/tinyxml/tinyxml.h \
opm/core/utility/writeVtkData.hpp \ opm/core/utility/writeVtkData.hpp \
opm/core/utility/DataMap.hpp \
opm/core/vag_format/vag.hpp \ opm/core/vag_format/vag.hpp \
opm/core/well.h \ opm/core/well.h \
opm/core/wells/InjectionSpecification.hpp \ opm/core/wells/InjectionSpecification.hpp \
@ -259,7 +275,7 @@ opm/core/wells/WellsManager.hpp
# Optional library constituents. # Optional library constituents.
if UMFPACK if UMFPACK
libopmcore_la_SOURCES += \ lib_libopmcore_la_SOURCES += \
opm/core/linalg/call_umfpack.c \ opm/core/linalg/call_umfpack.c \
opm/core/linalg/LinearSolverUmfpack.cpp opm/core/linalg/LinearSolverUmfpack.cpp
@ -269,7 +285,7 @@ opm/core/linalg/LinearSolverUmfpack.hpp
endif endif
if HAVE_ERT if HAVE_ERT
libopmcore_la_SOURCES += \ lib_libopmcore_la_SOURCES += \
opm/core/utility/writeECLData.cpp opm/core/utility/writeECLData.cpp
nobase_include_HEADERS += \ nobase_include_HEADERS += \
@ -278,7 +294,7 @@ endif
if DUNE_ISTL if DUNE_ISTL
libopmcore_la_SOURCES += \ lib_libopmcore_la_SOURCES += \
opm/core/linalg/LinearSolverIstl.cpp opm/core/linalg/LinearSolverIstl.cpp
nobase_include_HEADERS += \ nobase_include_HEADERS += \
@ -287,14 +303,16 @@ endif
if BUILD_AGMG if BUILD_AGMG
libopmcore_la_SOURCES += \ nodist_lib_libopmcore_la_SOURCES += \
$(AGMG_SRCDIR)/dagmg.f90 \ $(AGMG_SRCDIR)/dagmg.f90 \
$(AGMG_SRCDIR)/dagmg_mumps.f90 \ $(AGMG_SRCDIR)/dagmg_mumps.f90
lib_libopmcore_la_SOURCES += \
opm/core/linalg/LinearSolverAGMG.cpp opm/core/linalg/LinearSolverAGMG.cpp
nobase_include_HEADERS += \ nobase_include_HEADERS += \
opm/core/linalg/LinearSolverAGMG.hpp opm/core/linalg/LinearSolverAGMG.hpp
libopmcore_la_LDFLAGS += \ lib_libopmcore_la_LDFLAGS += \
$(FCLIBS) $(FCLIBS)
endif endif

125
README Normal file
View File

@ -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.

View File

@ -8,6 +8,10 @@ AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) 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_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([opm/core/grid.h]) AC_CONFIG_SRCDIR([opm/core/grid.h])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
@ -28,60 +32,22 @@ m4_ifdef([LT_INIT],
AC_PROG_FC[]dnl AC_PROG_FC[]dnl
])[]dnl ])[]dnl
# Checks for libraries. OPM_CORE_CHECKS
# 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_DYNLINK_BOOST_TEST 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 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([ AC_CONFIG_FILES([
Makefile Makefile
tests/Makefile tests/Makefile

7
dune.module Normal file
View File

@ -0,0 +1,7 @@
#dune module information file#
##############################
#Name of the module
Module: opm-core
Version: 0.1
Maintainer: atgeirr@sintef.no

View File

@ -3,12 +3,12 @@ ERT_INCLUDE_PATH = $(ERT_ROOT)/include
AM_CPPFLAGS = \ AM_CPPFLAGS = \
-I$(top_srcdir) \ -I$(top_srcdir) \
$(BOOST_CPPFLAGS) \ $(OPM_BOOST_CPPFLAGS) \
-I$(ERT_INCLUDE_PATH) -I$(ERT_INCLUDE_PATH)
# All targets link to the library # All targets link to the library
LDADD = \ LDADD = \
$(top_builddir)/libopmcore.la \ $(top_builddir)/lib/libopmcore.la \
$(BOOST_FILESYSTEM_LIB) \ $(BOOST_FILESYSTEM_LIB) \
$(BOOST_SYSTEM_LIB) $(BOOST_SYSTEM_LIB)
@ -20,6 +20,7 @@ $(BOOST_SYSTEM_LIB)
noinst_PROGRAMS = \ noinst_PROGRAMS = \
refine_wells \ refine_wells \
scaneclipsedeck \ scaneclipsedeck \
sim_2p_comp_reorder \
sim_2p_incomp_reorder \ sim_2p_incomp_reorder \
sim_wateroil \ sim_wateroil \
wells_example wells_example
@ -32,6 +33,7 @@ wells_example
# Please maintain sort order from "noinst_PROGRAMS". # Please maintain sort order from "noinst_PROGRAMS".
refine_wells_SOURCES = refine_wells.cpp 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_2p_incomp_reorder_SOURCES = sim_2p_incomp_reorder.cpp
sim_wateroil_SOURCES = sim_wateroil.cpp sim_wateroil_SOURCES = sim_wateroil.cpp
wells_example_SOURCES = wells_example.cpp wells_example_SOURCES = wells_example.cpp

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H
#include <opm/core/pressure/FlowBCManager.hpp>
#include <opm/core/grid.h>
#include <opm/core/GridManager.hpp>
#include <opm/core/newwells.h>
#include <opm/core/wells/WellsManager.hpp>
#include <opm/core/utility/ErrorMacros.hpp>
#include <opm/core/utility/initState.hpp>
#include <opm/core/simulator/SimulatorReport.hpp>
#include <opm/core/simulator/SimulatorTimer.hpp>
#include <opm/core/utility/miscUtilities.hpp>
#include <opm/core/utility/miscUtilities.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp>
#include <opm/core/fluid/BlackoilPropertiesBasic.hpp>
#include <opm/core/fluid/BlackoilPropertiesFromDeck.hpp>
#include <opm/core/fluid/RockCompressibility.hpp>
#include <opm/core/linalg/LinearSolverFactory.hpp>
#include <opm/core/simulator/BlackoilState.hpp>
#include <opm/core/simulator/WellState.hpp>
#include <opm/core/simulator/SimulatorCompressibleTwophase.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/filesystem.hpp>
#include <algorithm>
#include <iostream>
#include <vector>
#include <numeric>
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<EclipseGridParser> deck;
boost::scoped_ptr<GridManager> grid;
boost::scoped_ptr<BlackoilPropertiesInterface> props;
boost::scoped_ptr<RockCompressibility> 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<std::string>("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<double> 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<double> 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<double>("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<int>("pside");
double pside_pressure = param.get<double>("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);
}
}

View File

@ -43,9 +43,10 @@
#include <opm/core/simulator/TwophaseState.hpp> #include <opm/core/simulator/TwophaseState.hpp>
#include <opm/core/simulator/WellState.hpp> #include <opm/core/simulator/WellState.hpp>
#include <opm/core/simulator/SimulatorTwophase.hpp> #include <opm/core/simulator/SimulatorIncompTwophase.hpp>
#include <boost/scoped_ptr.hpp> #include <boost/scoped_ptr.hpp>
#include <boost/filesystem.hpp>
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
@ -53,6 +54,18 @@
#include <numeric> #include <numeric>
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 ----------------- // ----------------- Main program -----------------
@ -81,9 +94,7 @@ main(int argc, char** argv)
// Grid init // Grid init
grid.reset(new GridManager(*deck)); grid.reset(new GridManager(*deck));
// Rock and fluid init // Rock and fluid init
const int* gc = grid->c_grid()->global_cell; props.reset(new IncompPropertiesFromDeck(*deck, *grid->c_grid()));
std::vector<int> global_cell(gc, gc + grid->c_grid()->number_of_cells);
props.reset(new IncompPropertiesFromDeck(*deck, global_cell));
// check_well_controls = param.getDefault("check_well_controls", false); // check_well_controls = param.getDefault("check_well_controls", false);
// max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10);
// Rock compressibility. // Rock compressibility.
@ -157,17 +168,28 @@ main(int argc, char** argv)
// Linear solver. // Linear solver.
LinearSolverFactory linsolver(param); 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. // Write parameters used for later reference.
// if (output) { bool output = param.getDefault("output", true);
// param.writeParam(output_dir + "/spu_2p.param"); 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" std::cout << "\n\n================ Starting main simulation loop ===============\n"
@ -177,17 +199,19 @@ main(int argc, char** argv)
SimulatorReport rep; SimulatorReport rep;
if (!use_deck) { if (!use_deck) {
// Simple simulation without a deck. // Simple simulation without a deck.
SimulatorTwophase simulator(param, WellsManager wells; // no wells.
SimulatorIncompTwophase simulator(param,
*grid->c_grid(), *grid->c_grid(),
*props, *props,
rock_comp->isActive() ? rock_comp.get() : 0, rock_comp->isActive() ? rock_comp.get() : 0,
0, // wells wells,
src, src,
bcs.c_bcs(), bcs.c_bcs(),
linsolver, linsolver,
grav); grav);
SimulatorTimer simtimer; SimulatorTimer simtimer;
simtimer.init(param); simtimer.init(param);
warnIfUnusedParams(param);
WellState well_state; WellState well_state;
well_state.init(0, state); well_state.init(0, state);
rep = simulator.run(simtimer, state, well_state); rep = simulator.run(simtimer, state, well_state);
@ -221,7 +245,7 @@ main(int argc, char** argv)
<< "\n (number of steps: " << "\n (number of steps: "
<< simtimer.numSteps() - step << ")\n\n" << std::flush; << 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()); WellsManager wells(*deck, *grid->c_grid(), props->permeability());
// @@@ HACK: we should really make a new well state and // @@@ HACK: we should really make a new well state and
// properly transfer old well state to it every epoch, // properly transfer old well state to it every epoch,
@ -231,17 +255,22 @@ main(int argc, char** argv)
} }
// Create and run simulator. // Create and run simulator.
SimulatorTwophase simulator(param, SimulatorIncompTwophase simulator(param,
*grid->c_grid(), *grid->c_grid(),
*props, *props,
rock_comp->isActive() ? rock_comp.get() : 0, rock_comp->isActive() ? rock_comp.get() : 0,
wells.c_wells(), wells,
src, src,
bcs.c_bcs(), bcs.c_bcs(),
linsolver, linsolver,
grav); grav);
if (epoch == 0) {
warnIfUnusedParams(param);
}
SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state); SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state);
if (output) {
epoch_rep.reportParam(epoch_os);
}
// Update total timing report and remember step number. // Update total timing report and remember step number.
rep += epoch_rep; rep += epoch_rep;
step = simtimer.currentStepNum(); step = simtimer.currentStepNum();
@ -250,4 +279,11 @@ main(int argc, char** argv)
std::cout << "\n\n================ End of simulation ===============\n\n"; std::cout << "\n\n================ End of simulation ===============\n\n";
rep.report(std::cout); 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);
}
} }

View File

@ -68,8 +68,6 @@
#include <vector> #include <vector>
#include <numeric> #include <numeric>
#define TRANSPORT_SOLVER_FIXED 1
template <class State> template <class State>
static void outputState(const UnstructuredGrid& grid, static void outputState(const UnstructuredGrid& grid,
@ -143,7 +141,6 @@ main(int argc, char** argv)
std::cout << "--------------- Reading parameters ---------------" << std::endl; std::cout << "--------------- Reading parameters ---------------" << std::endl;
// Reading various control parameters. // Reading various control parameters.
const bool use_reorder = param.getDefault("use_reorder", true);
const bool output = param.getDefault("output", true); const bool output = param.getDefault("output", true);
std::string output_dir; std::string output_dir;
int output_interval = 1; int output_interval = 1;
@ -178,9 +175,7 @@ main(int argc, char** argv)
// Grid init // Grid init
grid.reset(new Opm::GridManager(deck)); grid.reset(new Opm::GridManager(deck));
// Rock and fluid init // Rock and fluid init
const int* gc = grid->c_grid()->global_cell; props.reset(new BlackoilPropertiesFromDeck(deck, *grid->c_grid(), param));
std::vector<int> global_cell(gc, gc + grid->c_grid()->number_of_cells);
props.reset(new Opm::BlackoilPropertiesFromDeck(deck, global_cell));
// Wells init. // Wells init.
wells.reset(new Opm::WellsManager(deck, *grid->c_grid(), props->permeability())); wells.reset(new Opm::WellsManager(deck, *grid->c_grid(), props->permeability()));
check_well_controls = param.getDefault("check_well_controls", false); 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_segregation_split = false;
bool use_column_solver = false; if (use_gravity) {
bool use_gauss_seidel_gravity = false;
if (use_gravity && use_reorder) {
use_segregation_split = param.getDefault("use_segregation_split", use_segregation_split); 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. // Source-related variables init.
@ -266,11 +241,11 @@ main(int argc, char** argv)
// Extra rock init. // Extra rock init.
std::vector<double> porevol; std::vector<double> porevol;
if (rock_comp->isActive()) { if (rock_comp->isActive()) {
THROW("CompressibleTpfa solver does not handle this.");
computePorevolume(*grid->c_grid(), props->porosity(), *rock_comp, state.pressure(), porevol); computePorevolume(*grid->c_grid(), props->porosity(), *rock_comp, state.pressure(), porevol);
} else { } else {
computePorevolume(*grid->c_grid(), props->porosity(), porevol); computePorevolume(*grid->c_grid(), props->porosity(), porevol);
} }
std::vector<double> initial_porevol = porevol;
double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); 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 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 int nl_press_maxiter = param.getDefault("nl_press_maxiter", 20);
const double *grav = use_gravity ? &gravity[0] : 0; 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, nl_press_res_tol, nl_press_change_tol, nl_press_maxiter,
grav, wells->c_wells()); grav, wells->c_wells());
// Reordering solver. // Reordering solver.
#if TRANSPORT_SOLVER_FIXED
const double nl_tolerance = param.getDefault("nl_tolerance", 1e-9); const double nl_tolerance = param.getDefault("nl_tolerance", 1e-9);
const int nl_maxiter = param.getDefault("nl_maxiter", 30); const int nl_maxiter = param.getDefault("nl_maxiter", 30);
Opm::TransportModelCompressibleTwophase reorder_model(*grid->c_grid(), *props, nl_tolerance, nl_maxiter); 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); reorder_model.initGravity(grav);
} }
#endif // TRANSPORT_SOLVER_FIXED
// Column-based gravity segregation solver. // Column-based gravity segregation solver.
std::vector<std::vector<int> > columns; std::vector<std::vector<int> > columns;
if (use_column_solver) { if (use_segregation_split) {
Opm::extractColumn(*grid->c_grid(), columns); 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, Opm::computeTransportSource(*grid->c_grid(), src, state.faceflux(), 1.0,
wells->c_wells(), well_state.perfRates(), reorder_src); 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. // Solve transport.
transport_timer.start(); transport_timer.start();
#if TRANSPORT_SOLVER_FIXED
double stepsize = simtimer.currentStepLength(); double stepsize = simtimer.currentStepLength();
if (num_transport_substeps != 1) { if (num_transport_substeps != 1) {
stepsize /= double(num_transport_substeps); stepsize /= double(num_transport_substeps);
std::cout << "Making " << num_transport_substeps << " transport substeps." << std::endl; std::cout << "Making " << num_transport_substeps << " transport substeps." << std::endl;
} }
for (int tr_substep = 0; tr_substep < num_transport_substeps; ++tr_substep) { for (int tr_substep = 0; tr_substep < num_transport_substeps; ++tr_substep) {
reorder_model.solve(&state.faceflux()[0], &state.pressure()[0], &state.surfacevol()[0], // Note that for now we do not handle rock compressibility,
&porevol[0], &reorder_src[0], stepsize, state.saturation()); // 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); // Opm::computeInjectedProduced(*props, state.saturation(), reorder_src, stepsize, injected, produced);
if (use_segregation_split) { if (use_segregation_split) {
THROW("Segregation not implemented yet."); reorder_model.solveGravity(columns, &state.pressure()[0], &initial_porevol[0],
// reorder_model.solveGravity(columns, &porevol[0], stepsize, state.saturation()); stepsize, state.saturation(), state.surfacevol());
} }
} }
#endif // TRANSPORT_SOLVER_FIXED
transport_timer.stop(); transport_timer.stop();
double tt = transport_timer.secsSinceStart(); double tt = transport_timer.secsSinceStart();
std::cout << "Transport solver took: " << tt << " seconds." << std::endl; std::cout << "Transport solver took: " << tt << " seconds." << std::endl;

View File

@ -317,9 +317,7 @@ main(int argc, char** argv)
// Grid init // Grid init
grid.reset(new Opm::GridManager(deck)); grid.reset(new Opm::GridManager(deck));
// Rock and fluid init // Rock and fluid init
const int* gc = grid->c_grid()->global_cell; props.reset(new Opm::IncompPropertiesFromDeck(deck, *grid->c_grid()));
std::vector<int> global_cell(gc, gc + grid->c_grid()->number_of_cells);
props.reset(new Opm::IncompPropertiesFromDeck(deck, global_cell));
// Wells init. // Wells init.
wells.reset(new Opm::WellsManager(deck, *grid->c_grid(), props->permeability())); wells.reset(new Opm::WellsManager(deck, *grid->c_grid(), props->permeability()));
check_well_controls = param.getDefault("check_well_controls", false); check_well_controls = param.getDefault("check_well_controls", false);
@ -503,7 +501,7 @@ main(int argc, char** argv)
// Write parameters used for later reference. // Write parameters used for later reference.
if (output) { if (output) {
param.writeParam(output_dir + "/spu_2p.param"); param.writeParam(output_dir + "/simulation.param");
} }
// Main simulation loop. // Main simulation loop.

View File

@ -38,10 +38,8 @@ int main(int argc, char** argv)
// Finally handle the wells // Finally handle the wells
WellsManager wells(parser, *grid.c_grid(), NULL); WellsManager wells(parser, *grid.c_grid(), NULL);
std::vector<int> 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<double>("gravity", 0.0)}; double gravity[3] = {0.0, 0.0, parameters.getDefault<double>("gravity", 0.0)};
IncompPropertiesFromDeck incomp_properties(parser, global_cells); IncompPropertiesFromDeck incomp_properties(parser, *grid.c_grid());
RockCompressibility rock_comp(parser); RockCompressibility rock_comp(parser);

View File

@ -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 subprocess import call
from paraview.simple import * from paraview.simple import *
# from paraview import servermanager
from os import remove, mkdir, curdir from os import remove, mkdir, curdir
from os.path import join, isdir from os.path import join, isdir
@ -13,7 +31,6 @@ collected_garbage_file = []
if not isdir(figure_path): if not isdir(figure_path):
mkdir(figure_path) mkdir(figure_path)
# connection = servermanager.Connect()
# tutorial 1 # tutorial 1
call(join(tutorial_path, "tutorial1")) call(join(tutorial_path, "tutorial1"))

View File

@ -11,10 +11,14 @@ AC_DEFUN([OPM_AGMG],dnl
[AS_IF([test -f "$with_agmg/dagmg.f90"],dnl [AS_IF([test -f "$with_agmg/dagmg.f90"],dnl
[AC_SUBST([AGMG_SRCDIR], [$with_agmg])[]dnl [AC_SUBST([AGMG_SRCDIR], [$with_agmg])[]dnl
AC_DEFINE([HAVE_AGMG], [1],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="yes"],dnl
[build_agmg="no"])],dnl [AC_DEFINE([HAVE_AGMG], [0],dnl
[build_agmg="no"])[]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 AS_IF([test x"$build_agmg" = x"yes"],dnl
[AC_PROG_FC_C_O[]dnl [AC_PROG_FC_C_O[]dnl

View File

@ -55,11 +55,11 @@ AC_DEFUN([AX_BOOST_DATE_TIME],
if test "x$want_boost" = "xyes"; then if test "x$want_boost" = "xyes"; then
AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_PROG_CC])
CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS_SAVED="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" CPPFLAGS="$CPPFLAGS $OPM_BOOST_CPPFLAGS"
export CPPFLAGS export CPPFLAGS
LDFLAGS_SAVED="$LDFLAGS" LDFLAGS_SAVED="$LDFLAGS"
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" LDFLAGS="$LDFLAGS $OPM_BOOST_LDFLAGS"
export LDFLAGS export LDFLAGS
AC_CACHE_CHECK(whether the Boost::Date_Time library is available, 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 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]) 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" ax_lib="-lboost_date_time"
if test "x$ax_boost_user_date_time_lib" = "x"; then if test "x$ax_boost_user_date_time_lib" = "x"; then

View File

@ -56,11 +56,11 @@ AC_DEFUN([AX_BOOST_FILESYSTEM],
if test "x$want_boost" = "xyes"; then if test "x$want_boost" = "xyes"; then
AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_PROG_CC])
CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS_SAVED="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" CPPFLAGS="$CPPFLAGS $OPM_BOOST_CPPFLAGS"
export CPPFLAGS export CPPFLAGS
LDFLAGS_SAVED="$LDFLAGS" LDFLAGS_SAVED="$LDFLAGS"
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" LDFLAGS="$LDFLAGS $OPM_BOOST_LDFLAGS"
export LDFLAGS export LDFLAGS
LIBS_SAVED=$LIBS LIBS_SAVED=$LIBS
@ -79,7 +79,7 @@ AC_DEFUN([AX_BOOST_FILESYSTEM],
]) ])
if test "x$ax_cv_boost_filesystem" = "xyes"; then if test "x$ax_cv_boost_filesystem" = "xyes"; then
AC_DEFINE(HAVE_BOOST_FILESYSTEM,,[define if the Boost::Filesystem library is available]) 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" ax_lib="-lboost_filesystem"
if test "x$ax_boost_user_filesystem_lib" = "x"; then 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 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

View File

@ -57,11 +57,11 @@ AC_DEFUN([AX_BOOST_SYSTEM],
AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_PROG_CC])
AC_REQUIRE([AC_CANONICAL_BUILD]) AC_REQUIRE([AC_CANONICAL_BUILD])
CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS_SAVED="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" CPPFLAGS="$CPPFLAGS $OPM_BOOST_CPPFLAGS"
export CPPFLAGS export CPPFLAGS
LDFLAGS_SAVED="$LDFLAGS" LDFLAGS_SAVED="$LDFLAGS"
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" LDFLAGS="$LDFLAGS $OPM_BOOST_LDFLAGS"
export LDFLAGS export LDFLAGS
AC_CACHE_CHECK(whether the Boost::System library is available, AC_CACHE_CHECK(whether the Boost::System library is available,
@ -76,10 +76,10 @@ AC_DEFUN([AX_BOOST_SYSTEM],
AC_LANG_POP([C++]) AC_LANG_POP([C++])
]) ])
if test "x$ax_cv_boost_system" = "xyes"; then 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]) 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" ax_lib="-lboost_system"
LDFLAGS_SAVE=$LDFLAGS LDFLAGS_SAVE=$LDFLAGS

View File

@ -54,11 +54,11 @@ AC_DEFUN([AX_BOOST_UNIT_TEST_FRAMEWORK],
if test "x$want_boost" = "xyes"; then if test "x$want_boost" = "xyes"; then
AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_PROG_CC])
CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS_SAVED="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" CPPFLAGS="$CPPFLAGS $OPM_BOOST_CPPFLAGS"
export CPPFLAGS export CPPFLAGS
LDFLAGS_SAVED="$LDFLAGS" LDFLAGS_SAVED="$LDFLAGS"
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" LDFLAGS="$LDFLAGS $OPM_BOOST_LDFLAGS"
export LDFLAGS export LDFLAGS
AC_CACHE_CHECK(whether the Boost::Unit_Test_Framework library is available, 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 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]) 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" ax_lib="-lboost_unit_test_framework"
if test "x$ax_boost_user_unit_test_framework_lib" = "x"; then if test "x$ax_boost_user_unit_test_framework_lib" = "x"; then

View File

@ -31,7 +31,9 @@ AC_DEFUN([AX_DUNE_ISTL],
"x$ax_cv_dune_common_available" = "xyes"],dnl "x$ax_cv_dune_common_available" = "xyes"],dnl
[AC_DEFINE([HAVE_DUNE_ISTL], [1],dnl [AC_DEFINE([HAVE_DUNE_ISTL], [1],dnl
[Define to 1 if `dune-istl' is available]) [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], AM_CONDITIONAL([DUNE_ISTL],
[test "x$ax_cv_dune_istl_available" = "xyes" -a \ [test "x$ax_cv_dune_istl_available" = "xyes" -a \

View File

@ -4,7 +4,7 @@
# #
# SYNOPSIS # 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 # DESCRIPTION
# #
@ -17,11 +17,11 @@
# #
# This macro calls: # This macro calls:
# #
# AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS) # AC_SUBST(OPM_BOOST_CPPFLAGS) / AC_SUBST(OPM_BOOST_LDFLAGS)
# #
# And sets: # And sets:
# #
# HAVE_BOOST # OPM_HAVE_BOOST
# #
# LICENSE # LICENSE
# #
@ -35,7 +35,7 @@
#serial 20 #serial 20
AC_DEFUN([AX_BOOST_BASE], AC_DEFUN([OPM_BOOST_BASE],
[ [
AC_ARG_WITH([boost], AC_ARG_WITH([boost],
[AS_HELP_STRING([--with-boost@<:@=ARG@:>@], [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 this location ist chosen if boost libraries are installed with the --layout=system option
dnl or if you install boost with RPM dnl or if you install boost with RPM
if test "$ac_boost_path" != ""; then 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 for ac_boost_path_tmp in $libsubdirs; do
if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then 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 break
fi fi
done done
@ -112,8 +112,8 @@ if test "x$want_boost" = "xyes"; then
for libsubdir in $libsubdirs ; do for libsubdir in $libsubdirs ; do
if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
done done
BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" OPM_BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir"
BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" OPM_BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include"
break; break;
fi fi
done done
@ -122,15 +122,15 @@ if test "x$want_boost" = "xyes"; then
dnl overwrite ld flags if we have required special directory with dnl overwrite ld flags if we have required special directory with
dnl --with-boost-libdir parameter dnl --with-boost-libdir parameter
if test "$ac_boost_lib_path" != ""; then if test "$ac_boost_lib_path" != ""; then
BOOST_LDFLAGS="-L$ac_boost_lib_path" OPM_BOOST_LDFLAGS="-L$ac_boost_lib_path"
fi fi
CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS_SAVED="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" CPPFLAGS="$CPPFLAGS $OPM_BOOST_CPPFLAGS"
export CPPFLAGS export CPPFLAGS
LDFLAGS_SAVED="$LDFLAGS" LDFLAGS_SAVED="$LDFLAGS"
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" LDFLAGS="$LDFLAGS $OPM_BOOST_LDFLAGS"
export LDFLAGS export LDFLAGS
AC_REQUIRE([AC_PROG_CXX]) AC_REQUIRE([AC_PROG_CXX])
@ -166,7 +166,7 @@ if test "x$want_boost" = "xyes"; then
_version=$_version_tmp _version=$_version_tmp
fi fi
VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` 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 done
fi fi
else else
@ -185,12 +185,12 @@ if test "x$want_boost" = "xyes"; then
done done
VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` 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 if test "$ac_boost_lib_path" = ""; then
for libsubdir in $libsubdirs ; do for libsubdir in $libsubdirs ; do
if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
done done
BOOST_LDFLAGS="-L$best_path/$libsubdir" OPM_BOOST_LDFLAGS="-L$best_path/$libsubdir"
fi fi
fi fi
@ -205,16 +205,16 @@ if test "x$want_boost" = "xyes"; then
V_CHECK=`expr $stage_version_shorten \>\= $_version` V_CHECK=`expr $stage_version_shorten \>\= $_version`
if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then
AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT) AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT)
BOOST_CPPFLAGS="-I$BOOST_ROOT" OPM_BOOST_CPPFLAGS="-I$BOOST_ROOT"
BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" OPM_BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir"
fi fi
fi fi
fi fi
fi fi
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" CPPFLAGS="$CPPFLAGS $OPM_BOOST_CPPFLAGS"
export CPPFLAGS export CPPFLAGS
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" LDFLAGS="$LDFLAGS $OPM_BOOST_LDFLAGS"
export LDFLAGS export LDFLAGS
AC_LANG_PUSH(C++) AC_LANG_PUSH(C++)
@ -243,10 +243,12 @@ if test "x$want_boost" = "xyes"; then
fi fi
# execute ACTION-IF-NOT-FOUND (if present): # execute ACTION-IF-NOT-FOUND (if present):
ifelse([$3], , :, [$3]) ifelse([$3], , :, [$3])
AC_DEFINE([OPM_HAVE_BOOST], [0],dnl
[Define to `0' if the Boost library is not available])
else else
AC_SUBST(BOOST_CPPFLAGS) AC_SUBST([OPM_BOOST_CPPFLAGS])
AC_SUBST(BOOST_LDFLAGS) AC_SUBST([OPM_BOOST_LDFLAGS])
AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available]) AC_DEFINE([OPM_HAVE_BOOST], [1], [define if the Boost library is available])
# execute ACTION-IF-FOUND (if present): # execute ACTION-IF-FOUND (if present):
ifelse([$2], , :, [$2]) ifelse([$2], , :, [$2])
fi fi

66
m4/opm_core.m4 Normal file
View File

@ -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()])
])

View File

@ -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 <dune/{HEADER}>,
# 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++])
])

View File

@ -38,14 +38,14 @@ dnl -------------------------------------------------------------------
# system uses dynamic linking of Boost.Test . # system uses dynamic linking of Boost.Test .
AC_DEFUN([OPM_DYNLINK_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]) AC_REQUIRE([AX_BOOST_UNIT_TEST_FRAMEWORK])
_opm_LIBS_SAVE="$LIBS" _opm_LIBS_SAVE="${LIBS}"
_opm_CPPFLAGS_SAVE="$CPPFLAGS" _opm_CPPFLAGS_SAVE="${CPPFLAGS}"
LIBS="${BOOST_LDFLAGS} ${BOOST_UNIT_TEST_FRAMEWORK_LIB} ${LIBS}" LIBS="${OPM_BOOST_LDFLAGS} ${BOOST_UNIT_TEST_FRAMEWORK_LIB} ${LIBS}"
CPPFLAGS="${BOOST_CPPFLAGS} ${CPPFLAGS}" CPPFLAGS="${OPM_BOOST_CPPFLAGS} ${CPPFLAGS}"
AC_LANG_PUSH([C++]) 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++]) AC_LANG_POP([C++])
LIBS="$_opm_LIBS_SAVE" LIBS="${_opm_LIBS_SAVE}"
CPPFLAGS="$_opm_CPPFLAGS_SAVE" CPPFLAGS="${_opm_CPPFLAGS_SAVE}"
AS_IF([test x"$opm_cv_boost_link_static" = x"yes" -o \ AS_IF([test x"${opm_cv_boost_link_static}" = x"yes" -o \
x"$opm_cv_boost_link_dynamic" = x"yes"], 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_dynamic}" = x"yes"],
[AC_DEFINE([HAVE_DYNAMIC_BOOST_TEST], [1], [AC_DEFINE([HAVE_DYNAMIC_BOOST_TEST], [1],
[Define to `1' if Boost.Test should use BOOST_TEST_DYN_LINK])] [Define to `1' if Boost.Test should use BOOST_TEST_DYN_LINK])]
[:])[]dnl [:])[]dnl

View File

@ -22,6 +22,8 @@
#include <opm/core/grid.h> #include <opm/core/grid.h>
#include <opm/core/grid/cart_grid.h> #include <opm/core/grid/cart_grid.h>
#include <opm/core/grid/cornerpoint_grid.h> #include <opm/core/grid/cornerpoint_grid.h>
#include <algorithm>
#include <numeric>
@ -33,13 +35,25 @@ namespace Opm
/// Construct a 3d corner-point grid from a deck. /// Construct a 3d corner-point grid from a deck.
GridManager::GridManager(const Opm::EclipseGridParser& deck) GridManager::GridManager(const Opm::EclipseGridParser& deck)
{ {
// Collect in input struct for preprocessing. // We accept two different ways to specify the grid.
struct grdecl grdecl = deck.get_grdecl(); // 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.
// Process grid. if (deck.hasField("ZCORN") && deck.hasField("COORD")) {
ug_ = create_grid_cornerpoint(&grdecl, 0.0); initFromDeckCornerpoint(deck);
if (!ug_) { } else if (deck.hasField("DXV") && deck.hasField("DYV") && deck.hasField("DZV")) {
THROW("Failed to construct grid."); 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<double> coordsFromDeltas(const std::vector<double>& deltas)
{
std::vector<double> 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<int> 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<double>& dxv = deck.getFloatingPointValue("DXV");
const std::vector<double>& dyv = deck.getFloatingPointValue("DYV");
const std::vector<double>& dzv = deck.getFloatingPointValue("DZV");
std::vector<double> x = coordsFromDeltas(dxv);
std::vector<double> y = coordsFromDeltas(dyv);
std::vector<double> 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<double> top_depths_vec;
if (deck.hasField("DEPTHZ")) {
const std::vector<double>& 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<double>& 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 } // namespace Opm

View File

@ -33,13 +33,14 @@ namespace Opm
/// encapsulates creation and destruction of the grid. /// encapsulates creation and destruction of the grid.
/// The following grid types can be constructed: /// The following grid types can be constructed:
/// - 3d corner-point grids (from deck input) /// - 3d corner-point grids (from deck input)
/// - 3d tensor grids (from deck input)
/// - 2d cartesian grids /// - 2d cartesian grids
/// - 3d cartesian grids /// - 3d cartesian grids
/// The resulting UnstructuredGrid is available through the c_grid() method. /// The resulting UnstructuredGrid is available through the c_grid() method.
class GridManager class GridManager
{ {
public: public:
/// Construct a 3d corner-point grid from a deck. /// Construct a 3d corner-point grid or tensor grid from a deck.
GridManager(const Opm::EclipseGridParser& deck); GridManager(const Opm::EclipseGridParser& deck);
/// Construct a 2d cartesian grid with cells of unit size. /// Construct a 2d cartesian grid with cells of unit size.
@ -64,6 +65,12 @@ namespace Opm
// Disable copying and assignment. // Disable copying and assignment.
GridManager(const GridManager& other); GridManager(const GridManager& other);
GridManager& operator=(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. // The managed UnstructuredGrid.
UnstructuredGrid* ug_; UnstructuredGrid* ug_;
}; };

View File

@ -96,7 +96,7 @@ namespace EclipseKeywords
string("MULTPV"), string("PRESSURE"), string("SGAS"), string("MULTPV"), string("PRESSURE"), string("SGAS"),
string("SWAT"), string("SOIL"), string("RS"), string("SWAT"), string("SOIL"), string("RS"),
string("DXV"), string("DYV"), string("DZV"), 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]); const int num_floating_fields = sizeof(floating_fields) / sizeof(floating_fields[0]);
@ -201,21 +201,7 @@ namespace {
return us; return us;
} }
inline std::string readKeyword(std::istream& is) inline bool readKeyword(std::istream& is, std::string& keyword)
{
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)
{ {
char buf[9]; char buf[9];
int i, j; int i, j;
@ -387,7 +373,7 @@ void EclipseGridParser::readImpl(istream& is)
std::string keyword; std::string keyword;
while (is.good()) { while (is.good()) {
is >> ignoreWhitespace; is >> ignoreWhitespace;
bool ok = readKeywordNew(is, keyword); bool ok = readKeyword(is, keyword);
if (ok) { if (ok) {
//#ifdef VERBOSE //#ifdef VERBOSE
cout << "Keyword found: " << keyword << endl; cout << "Keyword found: " << keyword << endl;
@ -551,7 +537,7 @@ void EclipseGridParser::convertToSI()
bool do_convert = true; bool do_convert = true;
if (key == "COORD" || key == "ZCORN" || if (key == "COORD" || key == "ZCORN" ||
key == "DXV" || key == "DYV" || key == "DZV" || key == "DXV" || key == "DYV" || key == "DZV" ||
key == "DEPTHZ") { key == "DEPTHZ" || key == "TOPS") {
unit = units_.length; unit = units_.length;
} else if (key == "PERMX" || key == "PERMY" || key == "PERMZ" || } else if (key == "PERMX" || key == "PERMY" || key == "PERMZ" ||
key == "PERMXX" || key == "PERMYY" || key == "PERMZZ" || key == "PERMXX" || key == "PERMYY" || key == "PERMZZ" ||

View File

@ -136,47 +136,47 @@ public:
{ return dynamic_cast<const keyword&>(*getSpecialValue(#keyword)); } { return dynamic_cast<const keyword&>(*getSpecialValue(#keyword)); }
// Support for special fields. // Support for special fields.
SPECIAL_FIELD(SPECGRID); SPECIAL_FIELD(SPECGRID)
SPECIAL_FIELD(FAULTS); SPECIAL_FIELD(FAULTS)
SPECIAL_FIELD(MULTFLT); SPECIAL_FIELD(MULTFLT)
SPECIAL_FIELD(TITLE); SPECIAL_FIELD(TITLE)
SPECIAL_FIELD(START); SPECIAL_FIELD(START)
SPECIAL_FIELD(DATES); SPECIAL_FIELD(DATES)
SPECIAL_FIELD(DENSITY); SPECIAL_FIELD(DENSITY)
SPECIAL_FIELD(PVDG); SPECIAL_FIELD(PVDG)
SPECIAL_FIELD(PVDO); SPECIAL_FIELD(PVDO)
SPECIAL_FIELD(PVTG); SPECIAL_FIELD(PVTG)
SPECIAL_FIELD(PVTO); SPECIAL_FIELD(PVTO)
SPECIAL_FIELD(PVTW); SPECIAL_FIELD(PVTW)
SPECIAL_FIELD(SGOF); SPECIAL_FIELD(SGOF)
SPECIAL_FIELD(SWOF); SPECIAL_FIELD(SWOF)
SPECIAL_FIELD(ROCK); SPECIAL_FIELD(ROCK)
SPECIAL_FIELD(ROCKTAB); SPECIAL_FIELD(ROCKTAB)
SPECIAL_FIELD(WELSPECS); SPECIAL_FIELD(WELSPECS)
SPECIAL_FIELD(COMPDAT); SPECIAL_FIELD(COMPDAT)
SPECIAL_FIELD(WCONINJE); SPECIAL_FIELD(WCONINJE)
SPECIAL_FIELD(WCONPROD); SPECIAL_FIELD(WCONPROD)
SPECIAL_FIELD(WELTARG); SPECIAL_FIELD(WELTARG)
SPECIAL_FIELD(WELOPEN); SPECIAL_FIELD(WELOPEN)
SPECIAL_FIELD(EQUIL); SPECIAL_FIELD(EQUIL)
SPECIAL_FIELD(PVCDO); SPECIAL_FIELD(PVCDO)
SPECIAL_FIELD(TSTEP); SPECIAL_FIELD(TSTEP)
SPECIAL_FIELD(PLYVISC); SPECIAL_FIELD(PLYVISC)
SPECIAL_FIELD(PLYROCK); SPECIAL_FIELD(PLYROCK)
SPECIAL_FIELD(PLYADS); SPECIAL_FIELD(PLYADS)
SPECIAL_FIELD(PLYMAX); SPECIAL_FIELD(PLYMAX)
SPECIAL_FIELD(TLMIXPAR); SPECIAL_FIELD(TLMIXPAR)
SPECIAL_FIELD(WPOLYMER); SPECIAL_FIELD(WPOLYMER)
SPECIAL_FIELD(GRUPTREE); SPECIAL_FIELD(GRUPTREE)
SPECIAL_FIELD(GCONINJE); SPECIAL_FIELD(GCONINJE)
SPECIAL_FIELD(GCONPROD); SPECIAL_FIELD(GCONPROD)
SPECIAL_FIELD(WGRUPCON); SPECIAL_FIELD(WGRUPCON)
// The following fields only have a dummy implementation // The following fields only have a dummy implementation
// that allows us to ignore them. // that allows us to ignore them.
SPECIAL_FIELD(SWFN); SPECIAL_FIELD(SWFN)
SPECIAL_FIELD(SOF2); SPECIAL_FIELD(SOF2)
SPECIAL_FIELD(TUNING); SPECIAL_FIELD(TUNING)
#undef SPECIAL_FIELD #undef SPECIAL_FIELD

View File

@ -780,9 +780,11 @@ struct WelspecsLine
int fluids_in_place_reg_numb_; // Fluids in place region number int fluids_in_place_reg_numb_; // Fluids in place region number
WelspecsLine() : WelspecsLine() :
datum_depth_BHP_(-1.0), drain_rad_(0.0), spec_inflow_("STD"), name_(), group_(), I_(-1), J_(-1),
shut_in_("SHUT"), crossflow_("YES"), pressure_table_number_(0), datum_depth_BHP_(-1.0), pref_phase_(), drain_rad_(0.0),
density_calc_type_("SEG"), fluids_in_place_reg_numb_(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_; bool available_for_group_control_;
double guide_rate_; double guide_rate_;
std::string phase_; std::string phase_;
WgrupconLine() : WgrupconLine()
available_for_group_control_(true) : 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 double new_value_; // New value of this quantity
WeltargLine() WeltargLine()
: well_(), control_change_(), new_value_(-1.0)
{ {
} }
}; };
@ -1739,6 +1742,13 @@ struct EquilLine
// initial fluids in place calculation. // initial fluids in place calculation.
EquilLine() 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;
} }
}; };
@ -2129,6 +2139,7 @@ struct WpolymerLine
WpolymerLine() WpolymerLine()
{ {
well_ = polymer_group_ = salt_group_ = ""; well_ = polymer_group_ = salt_group_ = "";
polymer_concentration_ = salt_concentration_ = 0.0;
} }
}; };

View File

@ -151,7 +151,7 @@ namespace Opm
double* dpcds) const; 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 cell cells[i], saturation of phase p is allowed to be
/// in the interval [smin[i*P + p], smax[i*P + p]]. /// in the interval [smin[i*P + p], smax[i*P + p]].
/// \param[in] n Number of data points. /// \param[in] n Number of data points.

View File

@ -18,19 +18,68 @@
*/ */
#include <opm/core/fluid/BlackoilPropertiesFromDeck.hpp> #include <opm/core/fluid/BlackoilPropertiesFromDeck.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp>
namespace Opm namespace Opm
{ {
BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck(const EclipseGridParser& deck, BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck(const EclipseGridParser& deck,
const std::vector<int>& global_cell) const UnstructuredGrid& grid)
{ {
rock_.init(deck, global_cell); rock_.init(deck, grid);
pvt_.init(deck); pvt_.init(deck, 200);
satprops_.init(deck, global_cell); SaturationPropsFromDeck<SatFuncStone2Uniform>* ptr
if (pvt_.numPhases() != satprops_.numPhases()) { = new SaturationPropsFromDeck<SatFuncStone2Uniform>();
THROW("BlackoilPropertiesBasic::BlackoilPropertiesBasic() - Inconsistent number of phases in pvt data (" satprops_.reset(ptr);
<< pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); 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<std::string>("threephase_model", "simple");
bool use_stone2 = (threephase_model == "stone2");
if (sat_samples > 1) {
if (use_stone2) {
SaturationPropsFromDeck<SatFuncStone2Uniform>* ptr
= new SaturationPropsFromDeck<SatFuncStone2Uniform>();
satprops_.reset(ptr);
ptr->init(deck, grid, sat_samples);
} else {
SaturationPropsFromDeck<SatFuncSimpleUniform>* ptr
= new SaturationPropsFromDeck<SatFuncSimpleUniform>();
satprops_.reset(ptr);
ptr->init(deck, grid, sat_samples);
}
} else {
if (use_stone2) {
SaturationPropsFromDeck<SatFuncStone2Nonuniform>* ptr
= new SaturationPropsFromDeck<SatFuncStone2Nonuniform>();
satprops_.reset(ptr);
ptr->init(deck, grid, sat_samples);
} else {
SaturationPropsFromDeck<SatFuncSimpleNonuniform>* ptr
= new SaturationPropsFromDeck<SatFuncSimpleNonuniform>();
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() << ").");
} }
} }
@ -235,7 +284,7 @@ namespace Opm
double* kr, double* kr,
double* dkrds) const 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* pc,
double* dpcds) const 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* smin,
double* smax) const double* smax) const
{ {
satprops_.satRange(n, cells, smin, smax); satprops_->satRange(n, cells, smin, smax);
} }

View File

@ -26,6 +26,10 @@
#include <opm/core/fluid/blackoil/BlackoilPvtProperties.hpp> #include <opm/core/fluid/blackoil/BlackoilPvtProperties.hpp>
#include <opm/core/fluid/SaturationPropsFromDeck.hpp> #include <opm/core/fluid/SaturationPropsFromDeck.hpp>
#include <opm/core/eclipse/EclipseGridParser.hpp> #include <opm/core/eclipse/EclipseGridParser.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp>
#include <boost/scoped_ptr.hpp>
struct UnstructuredGrid;
namespace Opm namespace Opm
{ {
@ -35,12 +39,28 @@ namespace Opm
class BlackoilPropertiesFromDeck : public BlackoilPropertiesInterface class BlackoilPropertiesFromDeck : public BlackoilPropertiesInterface
{ {
public: public:
/// Construct from deck and cell mapping. /// Initialize from deck and grid.
/// \param deck eclipse input parser /// \param[in] deck Deck input parser
/// \param global_cell mapping from cell indices (typically from a processed grid) /// \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. /// to logical cartesian indices consistent with the deck.
BlackoilPropertiesFromDeck(const EclipseGridParser& deck, BlackoilPropertiesFromDeck(const EclipseGridParser& deck,
const std::vector<int>& 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. /// Destructor.
virtual ~BlackoilPropertiesFromDeck(); virtual ~BlackoilPropertiesFromDeck();
@ -162,7 +182,7 @@ namespace Opm
private: private:
RockFromDeck rock_; RockFromDeck rock_;
BlackoilPvtProperties pvt_; BlackoilPvtProperties pvt_;
SaturationPropsFromDeck satprops_; boost::scoped_ptr<SaturationPropsInterface> satprops_;
mutable std::vector<double> B_; mutable std::vector<double> B_;
mutable std::vector<double> dB_; mutable std::vector<double> dB_;
mutable std::vector<double> R_; mutable std::vector<double> R_;

View File

@ -27,11 +27,11 @@ namespace Opm
{ {
IncompPropertiesFromDeck::IncompPropertiesFromDeck(const EclipseGridParser& deck, IncompPropertiesFromDeck::IncompPropertiesFromDeck(const EclipseGridParser& deck,
const std::vector<int>& global_cell) const UnstructuredGrid& grid)
{ {
rock_.init(deck, global_cell); rock_.init(deck, grid);
pvt_.init(deck); pvt_.init(deck);
satprops_.init(deck, global_cell); satprops_.init(deck, grid, 200);
if (pvt_.numPhases() != satprops_.numPhases()) { if (pvt_.numPhases() != satprops_.numPhases()) {
THROW("IncompPropertiesFromDeck::IncompPropertiesFromDeck() - Inconsistent number of phases in pvt data (" THROW("IncompPropertiesFromDeck::IncompPropertiesFromDeck() - Inconsistent number of phases in pvt data ("
<< pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ").");

View File

@ -26,6 +26,8 @@
#include <opm/core/fluid/SaturationPropsFromDeck.hpp> #include <opm/core/fluid/SaturationPropsFromDeck.hpp>
#include <opm/core/eclipse/EclipseGridParser.hpp> #include <opm/core/eclipse/EclipseGridParser.hpp>
struct UnstructuredGrid;
namespace Opm namespace Opm
{ {
@ -43,12 +45,13 @@ namespace Opm
class IncompPropertiesFromDeck : public IncompPropertiesInterface class IncompPropertiesFromDeck : public IncompPropertiesInterface
{ {
public: public:
/// Construct from deck and cell mapping. /// Initialize from deck and grid.
/// \param deck eclipse input parser /// \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. /// to logical cartesian indices consistent with the deck.
IncompPropertiesFromDeck(const EclipseGridParser& deck, IncompPropertiesFromDeck(const EclipseGridParser& deck,
const std::vector<int>& global_cell); const UnstructuredGrid& grid);
/// Destructor. /// Destructor.
virtual ~IncompPropertiesFromDeck(); virtual ~IncompPropertiesFromDeck();
@ -132,7 +135,7 @@ namespace Opm
private: private:
RockFromDeck rock_; RockFromDeck rock_;
PvtPropertiesIncompFromDeck pvt_; PvtPropertiesIncompFromDeck pvt_;
SaturationPropsFromDeck satprops_; SaturationPropsFromDeck<SatFuncStone2Uniform> satprops_;
}; };

View File

@ -69,7 +69,8 @@ namespace Opm
const double cpnorm = rock_comp_*(pressure - pref_); const double cpnorm = rock_comp_*(pressure - pref_);
return (1.0 + cpnorm + 0.5*cpnorm*cpnorm); return (1.0 + cpnorm + 0.5*cpnorm*cpnorm);
} else { } 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()) { if (p_.empty()) {
return rock_comp_; return rock_comp_;
} else { } else {
const double poromult = Opm::linearInterpolation(p_, poromult_, pressure); //const double poromult = Opm::linearInterpolation(p_, poromult_, pressure);
const double dporomultdp = Opm::linearInterpolationDerivative(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; return dporomultdp/poromult;
} }
} }

View File

@ -19,7 +19,7 @@
#include <opm/core/fluid/RockFromDeck.hpp> #include <opm/core/fluid/RockFromDeck.hpp>
#include <opm/core/grid.h>
#include <tr1/array> #include <tr1/array>
namespace Opm namespace Opm
@ -36,8 +36,6 @@ namespace Opm
PermeabilityKind fillTensor(const EclipseGridParser& parser, PermeabilityKind fillTensor(const EclipseGridParser& parser,
std::vector<const std::vector<double>*>& tensor, std::vector<const std::vector<double>*>& tensor,
std::tr1::array<int,9>& kmap); std::tr1::array<int,9>& kmap);
int numGlobalCells(const EclipseGridParser& parser);
} // anonymous namespace } // anonymous namespace
@ -53,28 +51,29 @@ namespace Opm
/// Initialize from deck and cell mapping. /// Initialize from deck and cell mapping.
/// \param deck Deck input parser /// \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. /// to logical cartesian indices consistent with the deck.
void RockFromDeck::init(const EclipseGridParser& deck, void RockFromDeck::init(const EclipseGridParser& deck,
const std::vector<int>& global_cell) const UnstructuredGrid& grid)
{ {
assignPorosity(deck, global_cell); assignPorosity(deck, grid);
permfield_valid_.assign(global_cell.size(), false); permfield_valid_.assign(grid.number_of_cells, false);
const double perm_threshold = 0.0; // Maybe turn into parameter? 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, void RockFromDeck::assignPorosity(const EclipseGridParser& parser,
const std::vector<int>& 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")) { if (parser.hasField("PORO")) {
const std::vector<double>& poro = parser.getFloatingPointValue("PORO"); const std::vector<double>& poro = parser.getFloatingPointValue("PORO");
for (int c = 0; c < int(porosity_.size()); ++c) { 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, void RockFromDeck::assignPermeability(const EclipseGridParser& parser,
const std::vector<int>& global_cell, const UnstructuredGrid& grid,
double perm_threshold) double perm_threshold)
{ {
const int dim = 3; 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); ASSERT (num_global_cells > 0);
permeability_.assign(dim * dim * global_cell.size(), 0.0); permeability_.assign(dim * dim * nc, 0.0);
std::vector<const std::vector<double>*> tensor; std::vector<const std::vector<double>*> tensor;
tensor.reserve(10); tensor.reserve(10);
@ -111,13 +112,13 @@ namespace Opm
// chosen) default value... // chosen) default value...
// //
if (tensor.size() > 1) { if (tensor.size() > 1) {
const int nc = global_cell.size(); const int* gc = grid.global_cell;
int off = 0; int off = 0;
for (int c = 0; c < nc; ++c, off += dim*dim) { for (int c = 0; c < nc; ++c, off += dim*dim) {
// SharedPermTensor K(dim, dim, &permeability_[off]); // SharedPermTensor K(dim, dim, &permeability_[off]);
int kix = 0; 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 i = 0; i < dim; ++i) {
for (int j = 0; j < dim; ++j, ++kix) { for (int j = 0; j < dim; ++j, ++kix) {
@ -331,26 +332,6 @@ namespace Opm
return kind; return kind;
} }
int numGlobalCells(const EclipseGridParser& parser)
{
int ngc = -1;
if (parser.hasField("DIMENS")) {
const std::vector<int>&
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 } // anonymous namespace
} // namespace Opm } // namespace Opm

View File

@ -24,6 +24,7 @@
#include <opm/core/eclipse/EclipseGridParser.hpp> #include <opm/core/eclipse/EclipseGridParser.hpp>
#include <vector> #include <vector>
struct UnstructuredGrid;
namespace Opm namespace Opm
{ {
@ -34,12 +35,13 @@ namespace Opm
/// Default constructor. /// Default constructor.
RockFromDeck(); RockFromDeck();
/// Initialize from deck and cell mapping. /// Initialize from deck and grid.
/// \param deck Deck input parser /// \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. /// to logical cartesian indices consistent with the deck.
void init(const EclipseGridParser& deck, void init(const EclipseGridParser& deck,
const std::vector<int>& global_cell); const UnstructuredGrid& grid);
/// \return D, the number of spatial dimensions. Always 3 for deck input. /// \return D, the number of spatial dimensions. Always 3 for deck input.
int numDimensions() const int numDimensions() const
@ -69,9 +71,9 @@ namespace Opm
private: private:
void assignPorosity(const EclipseGridParser& parser, void assignPorosity(const EclipseGridParser& parser,
const std::vector<int>& global_cell); const UnstructuredGrid& grid);
void assignPermeability(const EclipseGridParser& parser, void assignPermeability(const EclipseGridParser& parser,
const std::vector<int>& global_cell, const UnstructuredGrid& grid,
const double perm_threshold); const double perm_threshold);
std::vector<double> porosity_; std::vector<double> porosity_;

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <opm/core/fluid/SatFuncSimple.hpp>
#include <opm/core/fluid/blackoil/BlackoilPhases.hpp>
#include <opm/core/fluid/SaturationPropsFromDeck.hpp>
#include <opm/core/grid.h>
#include <opm/core/fluid/blackoil/phaseUsageFromDeck.hpp>
#include <opm/core/utility/buildUniformMonotoneTable.hpp>
#include <opm/core/utility/ErrorMacros.hpp>
#include <iostream>
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<double>& sw = swof_table[table_num][0];
const std::vector<double>& krw = swof_table[table_num][1];
const std::vector<double>& krow = swof_table[table_num][2];
const std::vector<double>& 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<double>& sg = sgof_table[table_num][0];
const std::vector<double>& krg = sgof_table[table_num][1];
const std::vector<double>& krog = sgof_table[table_num][2];
const std::vector<double>& 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<double>& sw = swof_table[table_num][0];
const std::vector<double>& krw = swof_table[table_num][1];
const std::vector<double>& krow = swof_table[table_num][2];
const std::vector<double>& pcow = swof_table[table_num][3];
krw_ = NonuniformTableLinear<double>(sw, krw);
krow_ = NonuniformTableLinear<double>(sw, krow);
pcow_ = NonuniformTableLinear<double>(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<double>& sg = sgof_table[table_num][0];
const std::vector<double>& krg = sgof_table[table_num][1];
const std::vector<double>& krog = sgof_table[table_num][2];
const std::vector<double>& pcog = sgof_table[table_num][3];
krg_ = NonuniformTableLinear<double>(sg, krg);
krog_ = NonuniformTableLinear<double>(sg, krog);
pcog_ = NonuniformTableLinear<double>(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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef SATFUNCSIMPLE_HPP
#define SATFUNCSIMPLE_HPP
#include <opm/core/eclipse/EclipseGridParser.hpp>
#include <opm/core/utility/UniformTableLinear.hpp>
#include <opm/core/utility/NonuniformTableLinear.hpp>
#include <opm/core/fluid/blackoil/BlackoilPhases.hpp>
#include <vector>
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<double> krw_;
UniformTableLinear<double> krow_;
UniformTableLinear<double> pcow_;
UniformTableLinear<double> krg_;
UniformTableLinear<double> krog_;
UniformTableLinear<double> 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<double> krw_;
NonuniformTableLinear<double> krow_;
NonuniformTableLinear<double> pcow_;
NonuniformTableLinear<double> krg_;
NonuniformTableLinear<double> krog_;
NonuniformTableLinear<double> pcog_;
double krocw_; // = krow_(s_wc)
};
} // namespace Opm
#endif // SATFUNCSIMPLE_HPP

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <opm/core/fluid/SatFuncStone2.hpp>
#include <opm/core/fluid/blackoil/BlackoilPhases.hpp>
#include <opm/core/fluid/SaturationPropsFromDeck.hpp>
#include <opm/core/grid.h>
#include <opm/core/fluid/blackoil/phaseUsageFromDeck.hpp>
#include <opm/core/utility/buildUniformMonotoneTable.hpp>
#include <opm/core/utility/ErrorMacros.hpp>
#include <iostream>
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<double>& sw = swof_table[table_num][0];
const std::vector<double>& krw = swof_table[table_num][1];
const std::vector<double>& krow = swof_table[table_num][2];
const std::vector<double>& 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<double>& sg = sgof_table[table_num][0];
const std::vector<double>& krg = sgof_table[table_num][1];
const std::vector<double>& krog = sgof_table[table_num][2];
const std::vector<double>& 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<double>& sw = swof_table[table_num][0];
const std::vector<double>& krw = swof_table[table_num][1];
const std::vector<double>& krow = swof_table[table_num][2];
const std::vector<double>& pcow = swof_table[table_num][3];
krw_ = NonuniformTableLinear<double>(sw, krw);
krow_ = NonuniformTableLinear<double>(sw, krow);
pcow_ = NonuniformTableLinear<double>(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<double>& sg = sgof_table[table_num][0];
const std::vector<double>& krg = sgof_table[table_num][1];
const std::vector<double>& krog = sgof_table[table_num][2];
const std::vector<double>& pcog = sgof_table[table_num][3];
krg_ = NonuniformTableLinear<double>(sg, krg);
krog_ = NonuniformTableLinear<double>(sg, krog);
pcog_ = NonuniformTableLinear<double>(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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef SATFUNCSTONE2_HPP
#define SATFUNCSTONE2_HPP
#include <opm/core/eclipse/EclipseGridParser.hpp>
#include <opm/core/utility/UniformTableLinear.hpp>
#include <opm/core/utility/NonuniformTableLinear.hpp>
#include <opm/core/fluid/blackoil/BlackoilPhases.hpp>
#include <vector>
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<double> krw_;
UniformTableLinear<double> krow_;
UniformTableLinear<double> pcow_;
UniformTableLinear<double> krg_;
UniformTableLinear<double> krog_;
UniformTableLinear<double> 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<double> krw_;
NonuniformTableLinear<double> krow_;
NonuniformTableLinear<double> pcow_;
NonuniformTableLinear<double> krg_;
NonuniformTableLinear<double> krog_;
NonuniformTableLinear<double> pcog_;
double krocw_; // = krow_(s_wc)
};
} // namespace Opm
#endif // SATFUNCSTONE2_HPP

View File

@ -99,6 +99,7 @@ namespace Opm
/// Default constructor. /// Default constructor.
SaturationPropsBasic::SaturationPropsBasic() SaturationPropsBasic::SaturationPropsBasic()
: num_phases_(0), relperm_func_(Constant)
{ {
} }
@ -113,7 +114,8 @@ namespace Opm
THROW("SaturationPropsBasic::init() illegal num_phases: " << num_phases); THROW("SaturationPropsBasic::init() illegal num_phases: " << num_phases);
} }
num_phases_ = num_phases; num_phases_ = num_phases;
std::string rpf = param.getDefault("relperm_func", std::string("Unset")); //std::string rpf = param.getDefault("relperm_func", std::string("Unset"));
std::string rpf = param.getDefault("relperm_func", std::string("Linear"));
if (rpf == "Constant") { if (rpf == "Constant") {
relperm_func_ = Constant; relperm_func_ = Constant;
if(num_phases!=1){ if(num_phases!=1){

View File

@ -18,7 +18,7 @@
*/ */
#include <opm/core/fluid/SaturationPropsFromDeck.hpp> #include <opm/core/fluid/SaturationPropsFromDeck.hpp>
#include <opm/core/fluid/blackoil/phaseUsageFromDeck.hpp> #include <opm/core/grid.h>
#include <opm/core/utility/buildUniformMonotoneTable.hpp> #include <opm/core/utility/buildUniformMonotoneTable.hpp>
#include <opm/core/utility/ErrorMacros.hpp> #include <opm/core/utility/ErrorMacros.hpp>
#include <iostream> #include <iostream>
@ -26,368 +26,8 @@
namespace Opm namespace Opm
{ {
/// Default constructor. // This file should be removed in the future.
SaturationPropsFromDeck::SaturationPropsFromDeck() // Holding off until refactoring of SaturationPropsFromDeck class is done.
{
}
/// Initialize from deck.
void SaturationPropsFromDeck::init(const EclipseGridParser& deck,
const std::vector<int>& 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<int>& 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<double>& sw = swof_table[table_num][0];
const std::vector<double>& krw = swof_table[table_num][1];
const std::vector<double>& krow = swof_table[table_num][2];
const std::vector<double>& 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<double>& sg = sgof_table[table_num][0];
const std::vector<double>& krg = sgof_table[table_num][1];
const std::vector<double>& krog = sgof_table[table_num][2];
const std::vector<double>& 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]);
}
}
} // namespace Opm } // namespace Opm

View File

@ -20,24 +20,44 @@
#ifndef OPM_SATURATIONPROPSFROMDECK_HEADER_INCLUDED #ifndef OPM_SATURATIONPROPSFROMDECK_HEADER_INCLUDED
#define OPM_SATURATIONPROPSFROMDECK_HEADER_INCLUDED #define OPM_SATURATIONPROPSFROMDECK_HEADER_INCLUDED
#include <opm/core/fluid/SaturationPropsInterface.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp>
#include <opm/core/eclipse/EclipseGridParser.hpp> #include <opm/core/eclipse/EclipseGridParser.hpp>
#include <opm/core/utility/UniformTableLinear.hpp>
#include <opm/core/fluid/blackoil/BlackoilPhases.hpp> #include <opm/core/fluid/blackoil/BlackoilPhases.hpp>
#include <opm/core/fluid/SatFuncStone2.hpp>
#include <opm/core/fluid/SatFuncSimple.hpp>
#include <vector> #include <vector>
struct UnstructuredGrid;
namespace Opm 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 SatFuncSet>
class SaturationPropsFromDeck : public SaturationPropsInterface
{ {
public: public:
/// Default constructor. /// Default constructor.
SaturationPropsFromDeck(); SaturationPropsFromDeck();
/// Initialize from deck. /// Initialize from deck and grid.
/// global_cell maps from grid cells to their original logical Cartesian indices. /// \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, void init(const EclipseGridParser& deck,
const std::vector<int>& global_cell); const UnstructuredGrid& grid,
const int samples);
/// \return P, the number of phases. /// \return P, the number of phases.
int numPhases() const; int numPhases() const;
@ -83,30 +103,12 @@ namespace Opm
private: private:
PhaseUsage phase_usage_; 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<double> krw_;
UniformTableLinear<double> krow_;
UniformTableLinear<double> pcow_;
UniformTableLinear<double> krg_;
UniformTableLinear<double> krog_;
UniformTableLinear<double> pcog_;
double krocw_; // = krow_(s_wc)
};
std::vector<SatFuncSet> satfuncset_; std::vector<SatFuncSet> satfuncset_;
std::vector<int> cell_to_func_; // = SATNUM - 1 std::vector<int> 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 } // namespace Opm
#include <opm/core/fluid/SaturationPropsFromDeck_impl.hpp>
#endif // OPM_SATURATIONPROPSFROMDECK_HEADER_INCLUDED #endif // OPM_SATURATIONPROPSFROMDECK_HEADER_INCLUDED

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_SATURATIONPROPSFROMDECK_IMPL_HEADER_INCLUDED
#define OPM_SATURATIONPROPSFROMDECK_IMPL_HEADER_INCLUDED
#include <opm/core/utility/UniformTableLinear.hpp>
#include <opm/core/utility/NonuniformTableLinear.hpp>
#include <opm/core/fluid/blackoil/phaseUsageFromDeck.hpp>
#include <opm/core/grid.h>
namespace Opm
{
// ----------- Methods of SaturationPropsFromDeck ---------
/// Default constructor.
template <class SatFuncSet>
SaturationPropsFromDeck<SatFuncSet>::SaturationPropsFromDeck()
{
}
/// Initialize from deck.
template <class SatFuncSet>
void SaturationPropsFromDeck<SatFuncSet>::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<int>& 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 <class SatFuncSet>
int SaturationPropsFromDeck<SatFuncSet>::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 <class SatFuncSet>
void SaturationPropsFromDeck<SatFuncSet>::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 <class SatFuncSet>
void SaturationPropsFromDeck<SatFuncSet>::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 <class SatFuncSet>
void SaturationPropsFromDeck<SatFuncSet>::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 <class SatFuncSet>
const typename SaturationPropsFromDeck<SatFuncSet>::Funcs&
SaturationPropsFromDeck<SatFuncSet>::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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_SATURATIONPROPSINTERFACE_HEADER_INCLUDED
#define OPM_SATURATIONPROPSINTERFACE_HEADER_INCLUDED
#include <opm/core/fluid/blackoil/BlackoilPhases.hpp>
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

View File

@ -21,6 +21,7 @@
#include <opm/core/fluid/blackoil/BlackoilPvtProperties.hpp> #include <opm/core/fluid/blackoil/BlackoilPvtProperties.hpp>
#include <opm/core/fluid/blackoil/SinglePvtDead.hpp> #include <opm/core/fluid/blackoil/SinglePvtDead.hpp>
#include <opm/core/fluid/blackoil/SinglePvtDeadSpline.hpp>
#include <opm/core/fluid/blackoil/SinglePvtLiveOil.hpp> #include <opm/core/fluid/blackoil/SinglePvtLiveOil.hpp>
#include <opm/core/fluid/blackoil/SinglePvtLiveGas.hpp> #include <opm/core/fluid/blackoil/SinglePvtLiveGas.hpp>
#include <opm/core/fluid/blackoil/SinglePvtConstCompr.hpp> #include <opm/core/fluid/blackoil/SinglePvtConstCompr.hpp>
@ -39,7 +40,7 @@ namespace Opm
} }
void BlackoilPvtProperties::init(const EclipseGridParser& deck) void BlackoilPvtProperties::init(const EclipseGridParser& deck, const int samples)
{ {
typedef std::vector<std::vector<std::vector<double> > > table_t; typedef std::vector<std::vector<std::vector<double> > > table_t;
// If we need multiple regions, this class and the SinglePvt* classes must change. // If we need multiple regions, this class and the SinglePvt* classes must change.
@ -78,7 +79,11 @@ namespace Opm
// Oil PVT // Oil PVT
if (phase_usage_.phase_used[Liquid]) { if (phase_usage_.phase_used[Liquid]) {
if (deck.hasField("PVDO")) { if (deck.hasField("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_)); props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDead(deck.getPVDO().pvdo_));
}
} else if (deck.hasField("PVTO")) { } else if (deck.hasField("PVTO")) {
props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtLiveOil(deck.getPVTO().pvto_)); props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtLiveOil(deck.getPVTO().pvto_));
} else if (deck.hasField("PVCDO")) { } else if (deck.hasField("PVCDO")) {
@ -90,7 +95,11 @@ namespace Opm
// Gas PVT // Gas PVT
if (phase_usage_.phase_used[Vapour]) { if (phase_usage_.phase_used[Vapour]) {
if (deck.hasField("PVDG")) { if (deck.hasField("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_)); props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDead(deck.getPVDG().pvdg_));
}
} else if (deck.hasField("PVTG")) { } else if (deck.hasField("PVTG")) {
props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtLiveGas(deck.getPVTG().pvtg_)); props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtLiveGas(deck.getPVTG().pvtg_));
} else { } else {

View File

@ -47,7 +47,13 @@ namespace Opm
BlackoilPvtProperties(); BlackoilPvtProperties();
/// Initialize from deck. /// 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. /// Number of active phases.
int numPhases() const; int numPhases() const;

View File

@ -106,13 +106,16 @@ namespace Opm
double* output_B, double* output_B,
double* output_dBdp) const double* output_dBdp) const
{ {
B(n, p, 0, output_B);
if (comp_) { if (comp_) {
// #pragma omp parallel for // #pragma omp parallel for
for (int i = 0; i < n; ++i) { 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 { } else {
std::fill(output_B, output_B + n, ref_B_);
std::fill(output_dBdp, output_dBdp + n, 0.0); std::fill(output_dBdp, output_dBdp + n, 0.0);
} }
} }

View File

@ -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). This file is part of the Open Porous Media project (OPM).
@ -17,8 +17,8 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>. along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <opm/core/fluid/blackoil/SinglePvtDead.hpp> #include <opm/core/fluid/blackoil/SinglePvtDead.hpp>
#include <opm/core/utility/buildUniformMonotoneTable.hpp>
#include <algorithm> #include <algorithm>
// Extra includes for debug dumping of tables. // Extra includes for debug dumping of tables.
@ -32,7 +32,6 @@ namespace Opm
//------------------------------------------------------------------------ //------------------------------------------------------------------------
// Member functions // Member functions
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
/// Constructor /// Constructor
SinglePvtDead::SinglePvtDead(const table_t& pvd_table) SinglePvtDead::SinglePvtDead(const table_t& pvd_table)
{ {
@ -51,9 +50,8 @@ namespace Opm
B_inv[i] = 1.0 / pvd_table[region_number][1][i]; B_inv[i] = 1.0 / pvd_table[region_number][1][i];
visc[i] = pvd_table[region_number][2][i]; visc[i] = pvd_table[region_number][2][i];
} }
int samples = 1025; one_over_B_ = NonuniformTableLinear<double>(press, B_inv);
buildUniformMonotoneTable(press, B_inv, samples, one_over_B_); viscosity_ = NonuniformTableLinear<double>(press, visc);
buildUniformMonotoneTable(press, visc, samples, viscosity_);
// Dumping the created tables. // Dumping the created tables.
// static int count = 0; // static int count = 0;

View File

@ -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). This file is part of the Open Porous Media project (OPM).
@ -22,7 +22,7 @@
#include <opm/core/fluid/blackoil/SinglePvtInterface.hpp> #include <opm/core/fluid/blackoil/SinglePvtInterface.hpp>
#include <opm/core/utility/UniformTableLinear.hpp> #include <opm/core/utility/NonuniformTableLinear.hpp>
#include <vector> #include <vector>
namespace Opm namespace Opm
@ -37,7 +37,6 @@ namespace Opm
{ {
public: public:
typedef std::vector<std::vector<std::vector<double> > > table_t; typedef std::vector<std::vector<std::vector<double> > > table_t;
SinglePvtDead(const table_t& pvd_table); SinglePvtDead(const table_t& pvd_table);
virtual ~SinglePvtDead(); virtual ~SinglePvtDead();
@ -74,11 +73,11 @@ namespace Opm
double* output_dRdp) const; double* output_dRdp) const;
private: private:
// PVT properties of dry gas or dead oil // PVT properties of dry gas or dead oil
UniformTableLinear<double> one_over_B_; NonuniformTableLinear<double> one_over_B_;
UniformTableLinear<double> viscosity_; NonuniformTableLinear<double> viscosity_;
}; };
} }
#endif // OPM_SINGLEPVTDEAD_HEADER_INCLUDED
#endif // OPM_SINGLEPVTDEAD_HEADER_INCLUDED

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <opm/core/fluid/blackoil/SinglePvtDeadSpline.hpp>
#include <opm/core/utility/buildUniformMonotoneTable.hpp>
#include <algorithm>
// Extra includes for debug dumping of tables.
// #include <boost/lexical_cast.hpp>
// #include <string>
// #include <fstream>
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<double> press(sz);
std::vector<double> B_inv(sz);
std::vector<double> 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<std::string>(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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_SINGLEPVTDEADSPLINE_HEADER_INCLUDED
#define OPM_SINGLEPVTDEADSPLINE_HEADER_INCLUDED
#include <opm/core/fluid/blackoil/SinglePvtInterface.hpp>
#include <opm/core/utility/UniformTableLinear.hpp>
#include <vector>
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<std::vector<std::vector<double> > > 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<double> one_over_B_;
UniformTableLinear<double> viscosity_;
};
}
#endif // OPM_SINGLEPVTDEADSPLINE_HEADER_INCLUDED

View File

@ -104,6 +104,8 @@ allocate_grid(size_t ndims ,
nel = ncellfaces; nel = ncellfaces;
G->cell_faces = malloc(nel * sizeof *G->cell_faces); G->cell_faces = malloc(nel * sizeof *G->cell_faces);
G->cell_facetag = malloc(nel * sizeof *G->cell_facetag);
nel = ncells + 1; nel = ncells + 1;
G->cell_facepos = malloc(nel * sizeof *G->cell_facepos); G->cell_facepos = malloc(nel * sizeof *G->cell_facepos);
@ -121,6 +123,7 @@ allocate_grid(size_t ndims ,
(G->face_normals == NULL) || (G->face_normals == NULL) ||
(G->face_areas == NULL) || (G->face_areas == NULL) ||
(G->cell_faces == NULL) || (G->cell_faces == NULL) ||
(G->cell_facetag == NULL) ||
(G->cell_facepos == NULL) || (G->cell_facepos == NULL) ||
(G->cell_centroids == NULL) || (G->cell_centroids == NULL) ||
(G->cell_volumes == NULL) ) (G->cell_volumes == NULL) )

View File

@ -118,7 +118,7 @@ create_grid_cart2d(int nx, int ny)
/* --------------------------------------------------------------------- */ /* --------------------------------------------------------------------- */
struct UnstructuredGrid * 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; struct UnstructuredGrid *G;
@ -136,9 +136,13 @@ create_grid_tensor2d(int nx, int ny, double x[], double y[])
/* --------------------------------------------------------------------- */ /* --------------------------------------------------------------------- */
struct UnstructuredGrid * struct UnstructuredGrid *
create_grid_tensor3d(int nx, int ny , int nz , create_grid_tensor3d(int nx ,
double x[], double y[], double z[], int ny ,
const double depthz[]) int nz ,
const double *x ,
const double *y ,
const double *z ,
const double *depthz)
{ {
struct UnstructuredGrid *G; 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; fnodes = G->face_nodes;
fnodepos = G->face_nodepos; 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; fnodes = G->face_nodes;

View File

@ -30,25 +30,140 @@
#ifndef OPM_CART_GRID_H_HEADER #ifndef OPM_CART_GRID_H_HEADER
#define 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 #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
struct UnstructuredGrid; struct UnstructuredGrid;
struct UnstructuredGrid *create_grid_cart2d(int nx, int ny); /**
struct UnstructuredGrid *create_grid_cart3d(int nx, int ny, int nz); * Form geometrically Cartesian grid in two space dimensions with unit-sized
struct UnstructuredGrid *create_grid_hexa3d(int nx, int ny, int nz, * 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_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); double dx, double dy, double dz);
struct UnstructuredGrid *create_grid_tensor2d(int nx, int ny,
double x[], double y[]); /**
* 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 <CODE>nx + 1</CODE>.
* @param[in] y Position along @c y axis of each grid line with constant @c y
* coordinate. Array of size <CODE>ny + 1</CODE>.
*
* @return Fully formed grid structure containing valid geometric primitives.
* Must be destroyed using function destroy_grid().
*/
struct UnstructuredGrid * struct UnstructuredGrid *
create_grid_tensor3d(int nx, int ny, int nz, create_grid_tensor2d(int nx, int ny,
double x[], double y[], double z[], const double *x , const double *y );
const double depthz[]);
/**
* 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 <CODE>nx + 1</CODE>.
* @param[in] y Position along @c y axis of each grid line with constant @c y
* coordinate. Array of size <CODE>ny + 1</CODE>.
* @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
* <CODE>nz + 1</CODE>.
*
* @param[in] depthz
* Top-layer topography specification. If @c NULL, interpreted as
* horizontal top-layer at <CODE>z=0</CODE>. Otherwise, must be
* an array of size <CODE>(nx + 1) * (ny + 1)</CODE>, 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -34,6 +34,12 @@
#ifndef OPM_CORNERPOINT_GRID_HEADER_INCLUDED #ifndef OPM_CORNERPOINT_GRID_HEADER_INCLUDED
#define OPM_CORNERPOINT_GRID_HEADER_INCLUDED #define OPM_CORNERPOINT_GRID_HEADER_INCLUDED
/**
* \file
* Routines to form a complete UnstructuredGrid from a corner-point
* specification.
*/
#include <opm/core/grid.h> #include <opm/core/grid.h>
#include <opm/core/grid/cpgpreprocess/preprocess.h> #include <opm/core/grid/cpgpreprocess/preprocess.h>
@ -41,9 +47,53 @@
extern "C" { extern "C" {
#endif #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 * struct UnstructuredGrid *
create_grid_cornerpoint(const struct grdecl *in, double tol); 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), <CODE>g->dimensions</CODE> scalars per face
* stored sequentially in <CODE>g->face_centroids</CODE>.
* -# Areas, one scalar per face stored sequentially in
* <CODE>g->face_areas</CODE>.
* -# Normals, <CODE>g->dimensions</CODE> scalars per face stored
* sequentially in <CODE>g->face_normals</CODE>. The Euclidian norm of
* each normal is equal to the corresponding face's area.
*
* - Quantities pertaining to cells (volumes)
* -# Barycenters (centroids), <CODE>g->dimensions</CODE> scalars per cell
* stored sequentially in <CODE>g->cell_centroids</CODE>.
* -# Volumes, one scalar per cell stored sequentially in
* <CODE>g->cell_volumes</CODE>.
*
* These fields must be allocated prior to calling compute_geometry().
*
* @param[in,out] g Grid structure.
*/
void compute_geometry(struct UnstructuredGrid *g); void compute_geometry(struct UnstructuredGrid *g);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -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))]; z1 = sign*zcorn[i+2*nx*(j+2*ny*(k))];
z2 = sign*zcorn[i+2*nx*(j+2*ny*(k+1))]; z2 = sign*zcorn[i+2*nx*(j+2*ny*(k+1))];
c1 = i/2 + nx*(j/2 + ny*k/2); c1 = i/2 + nx*(j/2 + ny*(k/2));
c2 = i/2 + nx*(j/2 + ny*(k+1)/2); c2 = i/2 + nx*(j/2 + ny*((k+1)/2));
if (((actnum == NULL) || if (((actnum == NULL) ||
(actnum[c1] && actnum[c2])) (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 /* (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); compute_intersection_coordinates(intersections, out);
free (intersections); free (intersections);

View File

@ -35,48 +35,112 @@
#ifndef OPENRS_PREPROCESS_HEADER #ifndef OPENRS_PREPROCESS_HEADER
#define 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 #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/* Input structure holding raw cornerpoint spec. */ /**
struct grdecl{ * Raw corner-point specification of a particular geological model.
int dims[3]; */
const double *coord; struct grdecl {
const double *zcorn; int dims[3]; /**< Cartesian box dimensions. */
const int *actnum; const double *coord; /**< Pillar end-points. */
const double *mapaxes; /* 6 Element rotation vector - can be NULL. */ 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 }; * Connection taxonomy.
*/
/* Output structure holding grid topology */ enum face_tag {
struct processed_grid{ LEFT, /**< Connection topologically parallel to J-K plane. */
int m; /** Upper bound on "number_of_faces" */ BACK, /**< Connection topologically parallel to I-K plane. */
int n; /** Upper bound on "number_of_nodes" */ TOP /**< Connection topologically parallel to I-J plane. */
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 */
}; };
void process_grdecl (const struct grdecl *g, /**
* 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, double tol,
struct processed_grid *out); struct processed_grid *out);
void free_processed_grid(struct processed_grid *g);
/**
* 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -65,9 +65,16 @@ namespace Opm
rtol_ (rtol) , rtol_ (rtol) ,
is_spd_(is_spd) 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() {} LinearSolverAGMG::~LinearSolverAGMG() {}

View File

@ -43,6 +43,7 @@
*/ */
#include <opm/core/linalg/LinearSolverInterface.hpp> #include <opm/core/linalg/LinearSolverInterface.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp>
namespace Opm namespace Opm
{ {
@ -62,20 +63,29 @@ namespace Opm
const double rtol = 1.0e-6, const double rtol = 1.0e-6,
const bool is_spd = false); 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(); virtual ~LinearSolverAGMG();
using LinearSolverInterface::solve; using LinearSolverInterface::solve;
/// Solve a linear system, with a matrix given in compressed /// Solve a linear system, with a matrix given in compressed
/// sparse row format. /// sparse row format.
/// \param[in] size Number of rows (and colums). /// \param[in] size Number of rows (and columns).
/// \param[in] nonzeros Number of (structural) non-zeros. /// \param[in] nonzeros Number of (structural) non-zeros.
/// \param[in] ia Row pointers. /// \param[in] ia Row pointers.
/// \param[in] ja Column indices. /// \param[in] ja Column indices.
/// \param[in] sa (structurally) non-zero elements. /// \param[in] sa (structurally) non-zero elements.
/// \param[in] rhs System right-hand side. /// \param[in] rhs System right-hand side.
/// \param[inout] solution System solution. /// \param[in,out] solution System solution.
/// \return Solver meta-data concerning most recent system solve. /// \return Solver meta-data concerning most recent system solve.
virtual LinearSolverInterface::LinearSolverReport virtual LinearSolverInterface::LinearSolverReport
solve(const int size, const int nonzeros, solve(const int size, const int nonzeros,

View File

@ -31,6 +31,11 @@
#include <opm/core/linalg/LinearSolverIstl.hpp> #include <opm/core/linalg/LinearSolverIstl.hpp>
#endif #endif
#if HAVE_AGMG
#include <opm/core/linalg/LinearSolverAGMG.hpp>
#endif
#include <opm/core/utility/parameters/ParameterGroup.hpp> #include <opm/core/utility/parameters/ParameterGroup.hpp>
#include <opm/core/utility/ErrorMacros.hpp> #include <opm/core/utility/ErrorMacros.hpp>
#include <string> #include <string>
@ -70,6 +75,12 @@ namespace Opm
#endif #endif
} }
else if (ls == "agmg") {
#if HAVE_AGMG
solver_.reset(new LinearSolverAGMG(param));
#endif
}
else { else {
THROW("Linear solver " << ls << " is unknown."); THROW("Linear solver " << ls << " is unknown.");
} }

View File

@ -24,10 +24,7 @@
#include <opm/core/linalg/LinearSolverIstl.hpp> #include <opm/core/linalg/LinearSolverIstl.hpp>
// Work around the fact that istl headers expect #include <opm/core/utility/have_boost_redef.hpp>
// HAVE_BOOST to be 1, and not just defined.
#undef HAVE_BOOST
#define HAVE_BOOST 1
// TODO: clean up includes. // TODO: clean up includes.
#include <dune/common/deprecated.hh> #include <dune/common/deprecated.hh>

View File

@ -30,6 +30,7 @@
#include <opm/core/newwells.h> #include <opm/core/newwells.h>
#include <opm/core/simulator/BlackoilState.hpp> #include <opm/core/simulator/BlackoilState.hpp>
#include <opm/core/simulator/WellState.hpp> #include <opm/core/simulator/WellState.hpp>
#include <opm/core/fluid/RockCompressibility.hpp>
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
@ -58,6 +59,7 @@ namespace Opm
/// to change. /// to change.
CompressibleTpfa::CompressibleTpfa(const UnstructuredGrid& grid, CompressibleTpfa::CompressibleTpfa(const UnstructuredGrid& grid,
const BlackoilPropertiesInterface& props, const BlackoilPropertiesInterface& props,
const RockCompressibility* rock_comp_props,
const LinearSolverInterface& linsolver, const LinearSolverInterface& linsolver,
const double residual_tol, const double residual_tol,
const double change_tol, const double change_tol,
@ -66,6 +68,7 @@ namespace Opm
const struct Wells* wells) const struct Wells* wells)
: grid_(grid), : grid_(grid),
props_(props), props_(props),
rock_comp_props_(rock_comp_props),
linsolver_(linsolver), linsolver_(linsolver),
residual_tol_(residual_tol), residual_tol_(residual_tol),
change_tol_(change_tol), change_tol_(change_tol),
@ -74,8 +77,8 @@ namespace Opm
wells_(wells), wells_(wells),
htrans_(grid.cell_facepos[ grid.number_of_cells ]), htrans_(grid.cell_facepos[ grid.number_of_cells ]),
trans_ (grid.number_of_faces), 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())) { if (wells_ && (wells_->number_of_phases != props.numPhases())) {
THROW("Inconsistent number of phases specified (wells vs. props): " THROW("Inconsistent number of phases specified (wells vs. props): "
@ -86,7 +89,12 @@ namespace Opm
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_); UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
tpfa_htrans_compute(gg, props.permeability(), &htrans_[0]); tpfa_htrans_compute(gg, props.permeability(), &htrans_[0]);
tpfa_trans_compute(gg, &htrans_[0], &trans_[0]); tpfa_trans_compute(gg, &htrans_[0], &trans_[0]);
// 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_); computePorevolume(grid_, props.porosity(), porevol_);
}
for (int c = 0; c < grid.number_of_cells; ++c) { for (int c = 0; c < grid.number_of_cells; ++c) {
allcells_[c] = 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. /// Compute well potentials.
void CompressibleTpfa::computeWellPotentials(const BlackoilState& state) void CompressibleTpfa::computeWellPotentials(const BlackoilState& state)
{ {
@ -230,6 +253,9 @@ namespace Opm
const WellState& /*well_state*/) const WellState& /*well_state*/)
{ {
computeWellPotentials(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<double> face_gravcap_; // std::vector<double> face_gravcap_;
// std::vector<double> wellperf_A_; // std::vector<double> wellperf_A_;
// std::vector<double> wellperf_phasemob_; // std::vector<double> wellperf_phasemob_;
// std::vector<double> porevol_; // Only modified if rock_comp_props_ is non-null.
// std::vector<double> rock_comp_; // Empty unless rock_comp_props_ is non-null.
computeCellDynamicData(dt, state, well_state); computeCellDynamicData(dt, state, well_state);
computeFaceDynamicData(dt, state, well_state); computeFaceDynamicData(dt, state, well_state);
computeWellDynamicData(dt, state, well_state); computeWellDynamicData(dt, state, well_state);
@ -273,6 +301,8 @@ namespace Opm
// std::vector<double> cell_viscosity_; // std::vector<double> cell_viscosity_;
// std::vector<double> cell_phasemob_; // std::vector<double> cell_phasemob_;
// std::vector<double> cell_voldisc_; // std::vector<double> cell_voldisc_;
// std::vector<double> porevol_; // Only modified if rock_comp_props_ is non-null.
// std::vector<double> rock_comp_; // Empty unless rock_comp_props_ is non-null.
const int nc = grid_.number_of_cells; const int nc = grid_.number_of_cells;
const int np = props_.numPhases(); const int np = props_.numPhases();
const double* cell_p = &state.pressure()[0]; const double* cell_p = &state.pressure()[0];
@ -296,6 +326,14 @@ namespace Opm
// TODO: Check this! // TODO: Check this!
cell_voldisc_.clear(); cell_voldisc_.clear();
cell_voldisc_.resize(nc, 0.0); 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]; 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]); props_.density(1, &cell_A_[np*np*c[j]], &gravcontrib[j][0]);
for (int p = 0; p < np; ++p) { for (int p = 0; p < np; ++p) {
gravcontrib[j][p] *= depth_diff; gravcontrib[j][p] *= depth_diff*grav;
} }
} else { } else {
std::fill(gravcontrib[j].begin(), gravcontrib[j].end(), 0.0); std::fill(gravcontrib[j].begin(), gravcontrib[j].end(), 0.0);
@ -465,9 +503,20 @@ namespace Opm
cq.Af = &face_A_[0]; cq.Af = &face_A_[0];
cq.phasemobf = &face_phasemob_[0]; cq.phasemobf = &face_phasemob_[0];
cq.voldiscr = &cell_voldisc_[0]; cq.voldiscr = &cell_voldisc_[0];
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], cfs_tpfa_res_assemble(gg, dt, &forces, z, &cq, &trans_[0],
&face_gravcap_[0], cell_press, well_bhp, &face_gravcap_[0], cell_press, well_bhp,
&porevol_[0], h_); &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);
} }

View File

@ -33,6 +33,7 @@ namespace Opm
class BlackoilState; class BlackoilState;
class BlackoilPropertiesInterface; class BlackoilPropertiesInterface;
class RockCompressibility;
class LinearSolverInterface; class LinearSolverInterface;
class WellState; class WellState;
@ -46,6 +47,7 @@ namespace Opm
/// Construct solver. /// Construct solver.
/// \param[in] grid A 2d or 3d grid. /// \param[in] grid A 2d or 3d grid.
/// \param[in] props Rock and fluid properties. /// \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] linsolver Linear solver to use.
/// \param[in] residual_tol Solution accepted if inf-norm of residual is smaller. /// \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] change_tol Solution accepted if inf-norm of change in pressure is smaller.
@ -61,6 +63,7 @@ namespace Opm
/// to change. /// to change.
CompressibleTpfa(const UnstructuredGrid& grid, CompressibleTpfa(const UnstructuredGrid& grid,
const BlackoilPropertiesInterface& props, const BlackoilPropertiesInterface& props,
const RockCompressibility* rock_comp_props,
const LinearSolverInterface& linsolver, const LinearSolverInterface& linsolver,
const double residual_tol, const double residual_tol,
const double change_tol, const double change_tol,
@ -78,15 +81,22 @@ namespace Opm
BlackoilState& state, BlackoilState& state,
WellState& well_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: private:
void computePerSolveDynamicData(const double dt, virtual void computePerSolveDynamicData(const double dt,
const BlackoilState& state, const BlackoilState& state,
const WellState& well_state); const WellState& well_state);
void computeWellPotentials(const BlackoilState& state);
void computePerIterationDynamicData(const double dt, void computePerIterationDynamicData(const double dt,
const BlackoilState& state, const BlackoilState& state,
const WellState& well_state); const WellState& well_state);
void computeCellDynamicData(const double dt, virtual void computeCellDynamicData(const double dt,
const BlackoilState& state, const BlackoilState& state,
const WellState& well_state); const WellState& well_state);
void computeFaceDynamicData(const double dt, void computeFaceDynamicData(const double dt,
@ -103,10 +113,13 @@ namespace Opm
double incrementNorm() const; double incrementNorm() const;
void computeResults(BlackoilState& state, void computeResults(BlackoilState& state,
WellState& well_state) const; WellState& well_state) const;
protected:
void computeWellPotentials(const BlackoilState& state);
// ------ Data that will remain unmodified after construction. ------ // ------ Data that will remain unmodified after construction. ------
const UnstructuredGrid& grid_; const UnstructuredGrid& grid_;
const BlackoilPropertiesInterface& props_; const BlackoilPropertiesInterface& props_;
const RockCompressibility* rock_comp_props_;
const LinearSolverInterface& linsolver_; const LinearSolverInterface& linsolver_;
const double residual_tol_; const double residual_tol_;
const double change_tol_; const double change_tol_;
@ -115,7 +128,6 @@ namespace Opm
const Wells* wells_; // May be NULL, outside may modify controls (only) between calls to solve(). const Wells* wells_; // May be NULL, outside may modify controls (only) between calls to solve().
std::vector<double> htrans_; std::vector<double> htrans_;
std::vector<double> trans_ ; std::vector<double> trans_ ;
std::vector<double> porevol_;
std::vector<int> allcells_; std::vector<int> allcells_;
// ------ Internal data for the cfs_tpfa_res solver. ------ // ------ Internal data for the cfs_tpfa_res solver. ------
@ -123,6 +135,7 @@ namespace Opm
// ------ Data that will be modified for every solve. ------ // ------ Data that will be modified for every solve. ------
std::vector<double> wellperf_gpot_; std::vector<double> wellperf_gpot_;
std::vector<double> initial_porevol_;
// ------ Data that will be modified for every solver iteration. ------ // ------ Data that will be modified for every solver iteration. ------
std::vector<double> cell_A_; std::vector<double> cell_A_;
@ -135,13 +148,15 @@ namespace Opm
std::vector<double> face_gravcap_; std::vector<double> face_gravcap_;
std::vector<double> wellperf_A_; std::vector<double> wellperf_A_;
std::vector<double> wellperf_phasemob_; std::vector<double> wellperf_phasemob_;
std::vector<double> porevol_; // Only modified if rock_comp_props_ is non-null.
std::vector<double> rock_comp_; // Empty unless rock_comp_props_ is non-null.
// The update to be applied to the pressures (cell and bhp). // The update to be applied to the pressures (cell and bhp).
std::vector<double> pressure_increment_; std::vector<double> 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 } // namespace Opm

View File

@ -27,8 +27,9 @@
#include <opm/core/pressure/flow_bc.h> #include <opm/core/pressure/flow_bc.h>
#include <opm/core/pressure/mimetic/mimetic.h> // for updating gpress #include <opm/core/pressure/mimetic/mimetic.h> // for updating gpress
#include <opm/core/GridAdapter.hpp> #include <opm/core/GridAdapter.hpp>
#include <opm/core/utility/ErrorMacros.hpp>
#include <stdexcept> #include <stdexcept>
#include <cassert>
/// @brief /// @brief

View File

@ -358,28 +358,193 @@ hybsys_schur_comp_gen(int nc, const int *pconn,
const double *Binv, const double *C2, const double *Binv, const double *C2,
const double *P, struct hybsys *sys); 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 <CODE>sys->L</CODE> and <CODE>wsys->F1</CODE>.
*
* @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 <CODE>cwpos[nc]</CODE>. 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 void
hybsys_well_schur_comp_symm(int nc, const int *cwpos, hybsys_well_schur_comp_symm(int nc, const int *cwpos,
double *WI, double *WI,
struct hybsys *sys, struct hybsys *sys,
struct hybsys_well *wsys); 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 void
hybsys_cellcontrib_symm(int c, int nconn, int p1, int p2, hybsys_cellcontrib_symm(int c, int nconn, int p1, int p2,
const double *gpress, const double *src, const double *gpress, const double *src,
const double *Binv, struct hybsys *sys); 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 void
hybsys_cellcontrib_unsymm(int c, int nconn, int p1, int p2, hybsys_cellcontrib_unsymm(int c, int nconn, int p1, int p2,
const double *gpress, const double *src, const double *gpress, const double *src,
const double *Binv, struct hybsys *sys); 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 <CODE>sys->F1</CODE>) 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 <CODE>pwconn[nc]</CODE>. 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
* <CODE>pwconn[nc]</CODE>.
* @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 void
hybsys_well_cellcontrib_symm(int c, int ngconn, int p1, hybsys_well_cellcontrib_symm(int c, int ngconn, int p1,
const int *cwpos, const int *cwpos,
const double *WI, const double *wdp, const double *WI, const double *wdp,
struct hybsys *sys, struct hybsys_well *wsys); 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
* <CODE>pconn[nc]</CODE>.
* @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 void
hybsys_compute_press_flux(int nc, const int *pconn, const int *conn, hybsys_compute_press_flux(int nc, const int *pconn, const int *conn,
const double *gpress, 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, const double *pi, double *press, double *flux,
double *work); 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 <CODE>nw > 0</CODE>,
* 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 <CODE>pwconn[nc]</CODE>. 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
* <CODE>pwconn[nw]</CODE>.
* @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 void
hybsys_compute_press_flux_well(int nc, const int *pgconn, int nf, hybsys_compute_press_flux_well(int nc, const int *pgconn, int nf,
int nw, const int *pwconn, const int *wconn, int nw, const int *pwconn, const int *wconn,

View File

@ -1156,7 +1156,7 @@ cfs_tpfa_res_construct(struct UnstructuredGrid *G ,
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
void int
cfs_tpfa_res_assemble(struct UnstructuredGrid *G , cfs_tpfa_res_assemble(struct UnstructuredGrid *G ,
double dt , double dt ,
struct cfs_tpfa_res_forces *forces , struct cfs_tpfa_res_forces *forces ,
@ -1170,7 +1170,7 @@ cfs_tpfa_res_assemble(struct UnstructuredGrid *G ,
struct cfs_tpfa_res_data *h ) 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); csrmatrix_zero( h->J);
vector_zero (h->J->m, h->F); vector_zero (h->J->m, h->F);
@ -1207,9 +1207,76 @@ cfs_tpfa_res_assemble(struct UnstructuredGrid *G ,
assemble_sources(dt, forces->src, h); assemble_sources(dt, forces->src, h);
} }
if (res_is_neumann && well_is_neumann && h->pimpl->is_incomp) { singular = res_is_neumann && well_is_neumann && h->pimpl->is_incomp;
h->J->sa[0] *= 2; 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;
} }

View File

@ -59,7 +59,11 @@ cfs_tpfa_res_construct(struct UnstructuredGrid *G ,
void void
cfs_tpfa_res_destroy(struct cfs_tpfa_res_data *h); 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, cfs_tpfa_res_assemble(struct UnstructuredGrid *G,
double dt, double dt,
struct cfs_tpfa_res_forces *forces, struct cfs_tpfa_res_forces *forces,
@ -72,6 +76,27 @@ cfs_tpfa_res_assemble(struct UnstructuredGrid *G,
const double *porevol, const double *porevol,
struct cfs_tpfa_res_data *h); 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 void
cfs_tpfa_res_flux(struct UnstructuredGrid *G , cfs_tpfa_res_flux(struct UnstructuredGrid *G ,
struct cfs_tpfa_res_forces *forces , struct cfs_tpfa_res_forces *forces ,

View File

@ -771,7 +771,7 @@ ifs_tpfa_assemble_comprock_increment(struct UnstructuredGrid *G ,
assemble_incompressible(G, F, trans, gpress, h, &system_singular, &ok); assemble_incompressible(G, F, trans, gpress, h, &system_singular, &ok);
/* We want to solve a Newton step for the residual /* 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
* *
*/ */

View File

@ -20,6 +20,16 @@
#ifndef OPM_IFS_TPFA_HEADER_INCLUDED #ifndef OPM_IFS_TPFA_HEADER_INCLUDED
#define 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 <opm/core/grid.h> #include <opm/core/grid.h>
#ifdef __cplusplus #ifdef __cplusplus
@ -31,37 +41,66 @@ struct CSRMatrix;
struct FlowBoundaryConditions; struct FlowBoundaryConditions;
struct Wells; 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 ifs_tpfa_data {
struct CSRMatrix *A; struct CSRMatrix *A; /**< Coefficient matrix */
double *b; double *b; /**< Right-hand side */
double *x; double *x; /**< Solution */
struct ifs_tpfa_impl *pimpl; struct ifs_tpfa_impl *pimpl; /**< Internal management structure */
}; };
/**
* Solution variables.
*/
struct ifs_tpfa_solution { struct ifs_tpfa_solution {
double *cell_press; double *cell_press; /**< Cell pressures */
double *face_flux ; double *face_flux ; /**< Interface fluxes */
double *well_press; /* BHP */ double *well_press; /**< Bottom-hole pressures for each well */
double *well_flux ; /* Perforation (total) fluxes */ double *well_flux ; /**< Well connection total fluxes */
}; };
/**
* Driving forces pertaining to a particular model setup.
*/
struct ifs_tpfa_forces { struct ifs_tpfa_forces {
const double *src; const double *src; /**< Explicit source terms */
const struct FlowBoundaryConditions *bc ; const struct FlowBoundaryConditions *bc ; /**< Boundary conditions */
const struct Wells *W ; const struct Wells *W ; /**< Well topology */
const double *totmob; const double *totmob; /**< Total mobility in each cell */
const double *wdp ; 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 * struct ifs_tpfa_data *
ifs_tpfa_construct(struct UnstructuredGrid *G, ifs_tpfa_construct(struct UnstructuredGrid *G,
struct Wells *W); struct Wells *W);
/**
*
* @param[in] G
* @param[in] F
* @param[in] trans
* @param[in] gpress
* @param[in,out] h
* @return
*/
int int
ifs_tpfa_assemble(struct UnstructuredGrid *G , ifs_tpfa_assemble(struct UnstructuredGrid *G ,
const struct ifs_tpfa_forces *F , const struct ifs_tpfa_forces *F ,

View File

@ -20,23 +20,88 @@
#ifndef OPM_TRANS_TPFA_HEADER_INCLUDED #ifndef OPM_TRANS_TPFA_HEADER_INCLUDED
#define OPM_TRANS_TPFA_HEADER_INCLUDED #define OPM_TRANS_TPFA_HEADER_INCLUDED
/**
* \file
* Routines to assist in the calculation of two-point transmissibilities.
*/
#include <opm/core/grid.h> #include <opm/core/grid.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #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 <CODE>(c,f)</CODE> 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
* <CODE>G->cell_facepos[ G->number_of_cells ]</CODE>.
*/
void 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 <CODE>G->number_of_faces</CODE>.
*/
void 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 <CODE>G->number_of_faces</CODE>.
*/
void void
tpfa_eff_trans_compute(struct UnstructuredGrid *G, tpfa_eff_trans_compute(struct UnstructuredGrid *G ,
const double *totmob, const double *totmob,
const double *htrans, const double *htrans,
double *trans); double *trans );
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -21,11 +21,11 @@
#include "config.h" #include "config.h"
#endif // HAVE_CONFIG_H #endif // HAVE_CONFIG_H
#include <opm/core/simulator/SimulatorTwophase.hpp> #include <opm/core/simulator/SimulatorCompressibleTwophase.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp> #include <opm/core/utility/parameters/ParameterGroup.hpp>
#include <opm/core/utility/ErrorMacros.hpp> #include <opm/core/utility/ErrorMacros.hpp>
#include <opm/core/pressure/IncompTpfa.hpp> #include <opm/core/pressure/CompressibleTpfa.hpp>
#include <opm/core/grid.h> #include <opm/core/grid.h>
#include <opm/core/newwells.h> #include <opm/core/newwells.h>
@ -36,16 +36,19 @@
#include <opm/core/utility/StopWatch.hpp> #include <opm/core/utility/StopWatch.hpp>
#include <opm/core/utility/writeVtkData.hpp> #include <opm/core/utility/writeVtkData.hpp>
#include <opm/core/utility/miscUtilities.hpp> #include <opm/core/utility/miscUtilities.hpp>
#include <opm/core/utility/miscUtilitiesBlackoil.hpp>
#include <opm/core/fluid/IncompPropertiesInterface.hpp> #include <opm/core/wells/WellsManager.hpp>
#include <opm/core/fluid/BlackoilPropertiesInterface.hpp>
#include <opm/core/fluid/RockCompressibility.hpp> #include <opm/core/fluid/RockCompressibility.hpp>
#include <opm/core/utility/ColumnExtract.hpp> #include <opm/core/utility/ColumnExtract.hpp>
#include <opm/core/simulator/TwophaseState.hpp> #include <opm/core/simulator/BlackoilState.hpp>
#include <opm/core/simulator/WellState.hpp> #include <opm/core/simulator/WellState.hpp>
#include <opm/core/transport/reorder/TransportModelTwophase.hpp> #include <opm/core/transport/reorder/TransportModelCompressibleTwophase.hpp>
#include <boost/filesystem/convenience.hpp> #include <boost/filesystem.hpp>
#include <boost/scoped_ptr.hpp> #include <boost/scoped_ptr.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
@ -56,21 +59,21 @@
namespace Opm namespace Opm
{ {
class SimulatorTwophase::Impl class SimulatorCompressibleTwophase::Impl
{ {
public: public:
Impl(const parameter::ParameterGroup& param, Impl(const parameter::ParameterGroup& param,
const UnstructuredGrid& grid, const UnstructuredGrid& grid,
const IncompPropertiesInterface& props, const BlackoilPropertiesInterface& props,
const RockCompressibility* rock_comp, const RockCompressibility* rock_comp,
const Wells* wells, WellsManager& wells_manager,
const std::vector<double>& src, const std::vector<double>& src,
const FlowBoundaryConditions* bcs, const FlowBoundaryConditions* bcs,
LinearSolverInterface& linsolver, LinearSolverInterface& linsolver,
const double* gravity); const double* gravity);
SimulatorReport run(SimulatorTimer& timer, SimulatorReport run(SimulatorTimer& timer,
TwophaseState& state, BlackoilState& state,
WellState& well_state); WellState& well_state);
private: private:
@ -78,23 +81,27 @@ namespace Opm
// Parameters for output. // Parameters for output.
bool output_; bool output_;
bool output_vtk_;
std::string output_dir_; std::string output_dir_;
int output_interval_; int output_interval_;
// Parameters for well control
bool check_well_controls_;
int max_well_control_iterations_;
// Parameters for transport solver. // Parameters for transport solver.
int num_transport_substeps_; int num_transport_substeps_;
bool use_segregation_split_; bool use_segregation_split_;
// Observed objects. // Observed objects.
const UnstructuredGrid& grid_; const UnstructuredGrid& grid_;
const IncompPropertiesInterface& props_; const BlackoilPropertiesInterface& props_;
const RockCompressibility* rock_comp_; const RockCompressibility* rock_comp_;
WellsManager& wells_manager_;
const Wells* wells_; const Wells* wells_;
const std::vector<double>& src_; const std::vector<double>& src_;
const FlowBoundaryConditions* bcs_; const FlowBoundaryConditions* bcs_;
const LinearSolverInterface& linsolver_;
const double* gravity_; const double* gravity_;
// Solvers // Solvers
IncompTpfa psolver_; CompressibleTpfa psolver_;
TransportModelTwophase tsolver_; TransportModelCompressibleTwophase tsolver_;
// Needed by column-based gravity segregation solver. // Needed by column-based gravity segregation solver.
std::vector< std::vector<int> > columns_; std::vector< std::vector<int> > columns_;
// Misc. data // Misc. data
@ -104,25 +111,25 @@ namespace Opm
SimulatorTwophase::SimulatorTwophase(const parameter::ParameterGroup& param, SimulatorCompressibleTwophase::SimulatorCompressibleTwophase(const parameter::ParameterGroup& param,
const UnstructuredGrid& grid, const UnstructuredGrid& grid,
const IncompPropertiesInterface& props, const BlackoilPropertiesInterface& props,
const RockCompressibility* rock_comp, const RockCompressibility* rock_comp,
const Wells* wells, WellsManager& wells_manager,
const std::vector<double>& src, const std::vector<double>& src,
const FlowBoundaryConditions* bcs, const FlowBoundaryConditions* bcs,
LinearSolverInterface& linsolver, LinearSolverInterface& linsolver,
const double* gravity) 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, SimulatorReport SimulatorCompressibleTwophase::run(SimulatorTimer& timer,
TwophaseState& state, BlackoilState& state,
WellState& well_state) WellState& well_state)
{ {
return pimpl_->run(timer, state, well_state); return pimpl_->run(timer, state, well_state);
@ -130,14 +137,22 @@ namespace Opm
static void outputState(const UnstructuredGrid& grid, static void outputStateVtk(const UnstructuredGrid& grid,
const Opm::TwophaseState& state, const Opm::BlackoilState& state,
const int step, const int step,
const std::string& output_dir) const std::string& output_dir)
{ {
// Write data in VTK format. // Write data in VTK format.
std::ostringstream vtkfilename; 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()); std::ofstream vtkfile(vtkfilename.str().c_str());
if (!vtkfile) { if (!vtkfile) {
THROW("Failed to open " << vtkfilename.str()); THROW("Failed to open " << vtkfilename.str());
@ -149,11 +164,33 @@ namespace Opm
Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity);
dm["velocity"] = &cell_velocity; dm["velocity"] = &cell_velocity;
Opm::writeVtkData(grid, dm, vtkfile); 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<double> cell_velocity;
Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity);
dm["velocity"] = &cell_velocity;
// Write data (not grid) in Matlab format // Write data (not grid) in Matlab format
for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) {
std::ostringstream fname; 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()); std::ofstream file(fname.str().c_str());
if (!file) { if (!file) {
THROW("Failed to open " << fname.str()); THROW("Failed to open " << fname.str());
@ -191,13 +228,12 @@ namespace Opm
// \TODO: make CompressibleTpfa take src and bcs.
SimulatorCompressibleTwophase::Impl::Impl(const parameter::ParameterGroup& param,
SimulatorTwophase::Impl::Impl(const parameter::ParameterGroup& param,
const UnstructuredGrid& grid, const UnstructuredGrid& grid,
const IncompPropertiesInterface& props, const BlackoilPropertiesInterface& props,
const RockCompressibility* rock_comp, const RockCompressibility* rock_comp,
const Wells* wells, WellsManager& wells_manager,
const std::vector<double>& src, const std::vector<double>& src,
const FlowBoundaryConditions* bcs, const FlowBoundaryConditions* bcs,
LinearSolverInterface& linsolver, LinearSolverInterface& linsolver,
@ -205,16 +241,15 @@ namespace Opm
: grid_(grid), : grid_(grid),
props_(props), props_(props),
rock_comp_(rock_comp), rock_comp_(rock_comp),
wells_(wells), wells_manager_(wells_manager),
wells_(wells_manager.c_wells()),
src_(src), src_(src),
bcs_(bcs), bcs_(bcs),
linsolver_(linsolver),
gravity_(gravity),
psolver_(grid, props, rock_comp, linsolver, psolver_(grid, props, rock_comp, linsolver,
param.getDefault("nl_pressure_residual_tolerance", 0.0), param.getDefault("nl_pressure_residual_tolerance", 0.0),
param.getDefault("nl_pressure_change_tolerance", 1.0), param.getDefault("nl_pressure_change_tolerance", 1.0),
param.getDefault("nl_pressure_maxiter", 10), param.getDefault("nl_pressure_maxiter", 10),
gravity, wells, src, bcs), gravity, wells_manager.c_wells() /*, src, bcs*/),
tsolver_(grid, props, tsolver_(grid, props,
param.getDefault("nl_tolerance", 1e-9), param.getDefault("nl_tolerance", 1e-9),
param.getDefault("nl_maxiter", 30)) param.getDefault("nl_maxiter", 30))
@ -222,6 +257,7 @@ namespace Opm
// For output. // For output.
output_ = param.getDefault("output", true); output_ = param.getDefault("output", true);
if (output_) { if (output_) {
output_vtk_ = param.getDefault("output_vtk", true);
output_dir_ = param.getDefault("output_dir", std::string("output")); output_dir_ = param.getDefault("output_dir", std::string("output"));
// Ensure that output dir exists // Ensure that output dir exists
boost::filesystem::path fpath(output_dir_); boost::filesystem::path fpath(output_dir_);
@ -234,6 +270,10 @@ namespace Opm
output_interval_ = param.getDefault("output_interval", 1); 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. // Transport related init.
num_transport_substeps_ = param.getDefault("num_transport_substeps", 1); num_transport_substeps_ = param.getDefault("num_transport_substeps", 1);
use_segregation_split_ = param.getDefault("use_segregation_split", false); use_segregation_split_ = param.getDefault("use_segregation_split", false);
@ -253,8 +293,8 @@ namespace Opm
SimulatorReport SimulatorTwophase::Impl::run(SimulatorTimer& timer, SimulatorReport SimulatorCompressibleTwophase::Impl::run(SimulatorTimer& timer,
TwophaseState& state, BlackoilState& state,
WellState& well_state) WellState& well_state)
{ {
std::vector<double> transport_src; std::vector<double> transport_src;
@ -267,13 +307,14 @@ namespace Opm
computePorevolume(grid_, props_.porosity(), porevol); computePorevolume(grid_, props_.porosity(), porevol);
} }
const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0);
std::vector<double> initial_porevol = porevol;
// Main simulation loop. // Main simulation loop.
Opm::time::StopWatch pressure_timer; Opm::time::StopWatch pressure_timer;
double ptime = 0.0; double ptime = 0.0;
Opm::time::StopWatch transport_timer; Opm::time::StopWatch transport_timer;
double ttime = 0.0; double ttime = 0.0;
Opm::time::StopWatch step_timer;
Opm::time::StopWatch total_timer; Opm::time::StopWatch total_timer;
total_timer.start(); total_timer.start();
double init_satvol[2] = { 0.0 }; double init_satvol[2] = { 0.0 };
@ -292,27 +333,102 @@ namespace Opm
std::vector<double> well_resflows_phase; std::vector<double> well_resflows_phase;
if (wells_) { if (wells_) {
well_resflows_phase.resize((wells_->number_of_phases)*(wells_->number_of_wells), 0.0); 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) { for (; !timer.done(); ++timer) {
// Report timestep and (optionally) write state to disk. // Report timestep and (optionally) write state to disk.
step_timer.start();
timer.report(std::cout); timer.report(std::cout);
if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { 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 { do {
// Run solver.
pressure_timer.start(); pressure_timer.start();
std::vector<double> initial_pressure = state.pressure();
psolver_.solve(timer.currentStepLength(), state, well_state); 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(); pressure_timer.stop();
double pt = pressure_timer.secsSinceStart(); double pt = pressure_timer.secsSinceStart();
std::cout << "Pressure solver took: " << pt << " seconds." << std::endl; std::cout << "Pressure solver took: " << pt << " seconds." << std::endl;
ptime += pt; 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. // Update pore volumes if rock is compressible.
if (rock_comp_ && rock_comp_->isActive()) { if (rock_comp_ && rock_comp_->isActive()) {
initial_porevol = porevol;
computePorevolume(grid_, props_.porosity(), *rock_comp_, state.pressure(), 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; std::cout << "Making " << num_transport_substeps_ << " transport substeps." << std::endl;
} }
for (int tr_substep = 0; tr_substep < num_transport_substeps_; ++tr_substep) { for (int tr_substep = 0; tr_substep < num_transport_substeps_; ++tr_substep) {
tsolver_.solve(&state.faceflux()[0], &porevol[0], &transport_src[0], tsolver_.solve(&state.faceflux()[0], &state.pressure()[0],
stepsize, state.saturation()); &porevol[0], &initial_porevol[0], &transport_src[0], stepsize,
Opm::computeInjectedProduced(props_, state.saturation(), transport_src, stepsize, injected, produced); state.saturation(), state.surfacevol());
if (use_segregation_split_) { Opm::computeInjectedProduced(props_,
tsolver_.solveGravity(columns_, &porevol[0], stepsize, state.saturation()); 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(); transport_timer.stop();
double tt = transport_timer.secsSinceStart(); double tt = transport_timer.secsSinceStart();
sreport.transport_time = tt;
std::cout << "Transport solver took: " << tt << " seconds." << std::endl; std::cout << "Transport solver took: " << tt << " seconds." << std::endl;
ttime += tt; ttime += tt;
// Report volume balances. // Report volume balances.
Opm::computeSaturatedVol(porevol, state.saturation(), satvol); Opm::computeSaturatedVol(porevol, state.saturation(), satvol);
tot_injected[0] += injected[0]; tot_injected[0] += injected[0];
@ -377,18 +497,27 @@ namespace Opm
produced[0]/(produced[0] + produced[1]), produced[0]/(produced[0] + produced[1]),
tot_produced[0]/tot_porevol_init); tot_produced[0]/tot_porevol_init);
if (wells_) { if (wells_) {
wellreport.push(props_, *wells_, state.saturation(), wellreport.push(props_, *wells_,
state.pressure(), state.surfacevol(), state.saturation(),
timer.currentTime() + timer.currentStepLength(), timer.currentTime() + timer.currentStepLength(),
well_state.bhp(), well_state.perfRates()); well_state.bhp(), well_state.perfRates());
} }
sreport.total_time = step_timer.secsSinceStart();
if (output_) {
sreport.reportParam(tstep_os);
}
} }
if (output_) { 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_); outputWaterCut(watercut, output_dir_);
if (wells_) { if (wells_) {
outputWellReport(wellreport, output_dir_); outputWellReport(wellreport, output_dir_);
} }
tstep_os.close();
} }
total_timer.stop(); total_timer.stop();

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_SIMULATORCOMPRESSIBLETWOPHASE_HEADER_INCLUDED
#define OPM_SIMULATORCOMPRESSIBLETWOPHASE_HEADER_INCLUDED
#include <boost/shared_ptr.hpp>
#include <vector>
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<double>& 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<Impl> pimpl_;
};
} // namespace Opm
#endif // OPM_SIMULATORCOMPRESSIBLETWOPHASE_HEADER_INCLUDED

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H
#include <opm/core/simulator/SimulatorIncompTwophase.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp>
#include <opm/core/utility/ErrorMacros.hpp>
#include <opm/core/pressure/IncompTpfa.hpp>
#include <opm/core/grid.h>
#include <opm/core/newwells.h>
#include <opm/core/pressure/flow_bc.h>
#include <opm/core/simulator/SimulatorReport.hpp>
#include <opm/core/simulator/SimulatorTimer.hpp>
#include <opm/core/utility/StopWatch.hpp>
#include <opm/core/utility/writeVtkData.hpp>
#include <opm/core/utility/miscUtilities.hpp>
#include <opm/core/wells/WellsManager.hpp>
#include <opm/core/fluid/IncompPropertiesInterface.hpp>
#include <opm/core/fluid/RockCompressibility.hpp>
#include <opm/core/utility/ColumnExtract.hpp>
#include <opm/core/simulator/TwophaseState.hpp>
#include <opm/core/simulator/WellState.hpp>
#include <opm/core/transport/reorder/TransportModelTwophase.hpp>
#include <boost/filesystem.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/lexical_cast.hpp>
#include <numeric>
#include <fstream>
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<double>& 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<double>& src_;
const FlowBoundaryConditions* bcs_;
// Solvers
IncompTpfa psolver_;
TransportModelTwophase tsolver_;
// Needed by column-based gravity segregation solver.
std::vector< std::vector<int> > columns_;
// Misc. data
std::vector<int> allcells_;
};
SimulatorIncompTwophase::SimulatorIncompTwophase(const parameter::ParameterGroup& param,
const UnstructuredGrid& grid,
const IncompPropertiesInterface& props,
const RockCompressibility* rock_comp,
WellsManager& wells_manager,
const std::vector<double>& 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<double> 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<int>& 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<double>(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<double> 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<double>& d = *(it->second);
std::copy(d.begin(), d.end(), std::ostream_iterator<double>(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<double>& 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<double> transport_src;
// Initialisation.
std::vector<double> 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<double> 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<double> fractional_flows;
std::vector<double> 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<double> 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

View File

@ -17,8 +17,8 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>. along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef OPM_SIMULATORTWOPHASE_HEADER_INCLUDED #ifndef OPM_SIMULATORINCOMPTWOPHASE_HEADER_INCLUDED
#define OPM_SIMULATORTWOPHASE_HEADER_INCLUDED #define OPM_SIMULATORINCOMPTWOPHASE_HEADER_INCLUDED
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <vector> #include <vector>
@ -32,14 +32,15 @@ namespace Opm
namespace parameter { class ParameterGroup; } namespace parameter { class ParameterGroup; }
class IncompPropertiesInterface; class IncompPropertiesInterface;
class RockCompressibility; class RockCompressibility;
class WellsManager;
class LinearSolverInterface; class LinearSolverInterface;
class SimulatorTimer; class SimulatorTimer;
class TwophaseState; class TwophaseState;
class WellState; class WellState;
class SimulatorReport; struct SimulatorReport;
/// Class collecting all necessary components for a two-phase simulation. /// Class collecting all necessary components for a two-phase simulation.
class SimulatorTwophase class SimulatorIncompTwophase
{ {
public: public:
/// Initialise from parameters and objects to observe. /// Initialise from parameters and objects to observe.
@ -61,16 +62,16 @@ namespace Opm
/// \param[in] grid grid data structure /// \param[in] grid grid data structure
/// \param[in] props fluid and rock properties /// \param[in] props fluid and rock properties
/// \param[in] rock_comp if non-null, rock compressibility properties /// \param[in] rock_comp if non-null, rock compressibility properties
/// \param[in] wells if non-null, wells data structure /// \param[in] well_manager well manager, may manage no (null) wells
/// \param[in] src source terms /// \param[in] src source terms
/// \param[in] bcs boundary conditions, treat as all noflow if null /// \param[in] bcs boundary conditions, treat as all noflow if null
/// \param[in] linsolver linear solver /// \param[in] linsolver linear solver
/// \param[in] gravity if non-null, gravity vector /// \param[in] gravity if non-null, gravity vector
SimulatorTwophase(const parameter::ParameterGroup& param, SimulatorIncompTwophase(const parameter::ParameterGroup& param,
const UnstructuredGrid& grid, const UnstructuredGrid& grid,
const IncompPropertiesInterface& props, const IncompPropertiesInterface& props,
const RockCompressibility* rock_comp, const RockCompressibility* rock_comp,
const Wells* wells, WellsManager& wells_manager,
const std::vector<double>& src, const std::vector<double>& src,
const FlowBoundaryConditions* bcs, const FlowBoundaryConditions* bcs,
LinearSolverInterface& linsolver, LinearSolverInterface& linsolver,
@ -95,4 +96,4 @@ namespace Opm
} // namespace Opm } // namespace Opm
#endif // OPM_SIMULATORTWOPHASE_HEADER_INCLUDED #endif // OPM_SIMULATORINCOMPTWOPHASE_HEADER_INCLUDED

View File

@ -42,6 +42,12 @@ namespace Opm
<< "\n Pressure time: " << pressure_time << "\n Pressure time: " << pressure_time
<< "\n Transport time: " << transport_time << std::endl; << "\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 } // namespace Opm

View File

@ -38,6 +38,7 @@ namespace Opm
void operator+=(const SimulatorReport& sr); void operator+=(const SimulatorReport& sr);
/// Print a report to the given stream. /// Print a report to the given stream.
void report(std::ostream& os); void report(std::ostream& os);
void reportParam(std::ostream& os);
}; };
} // namespace Opm } // namespace Opm

View File

@ -47,7 +47,9 @@ namespace Opm {
public: public:
ImplicitAssembly(Model& model) ImplicitAssembly(Model& model)
: model_(model) : model_(model),
nconn_(-1) ,
asm_buffer_()
{} {}
template <class Grid , template <class Grid ,

View File

@ -33,6 +33,12 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>. along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/ */
/**
* \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 #ifndef OPM_SINGLEPOINTUPWINDTWOPHASE_HPP_HEADER
#define OPM_SINGLEPOINTUPWINDTWOPHASE_HPP_HEADER #define OPM_SINGLEPOINTUPWINDTWOPHASE_HPP_HEADER
@ -45,8 +51,43 @@
namespace Opm { namespace Opm {
namespace spu_2p { 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 <CODE>(f,c)</CODE> 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 { class ModelParameterStorage {
public: 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) ModelParameterStorage(int nc, int totconn)
: drho_(0.0), mob_(0), dmob_(0), : drho_(0.0), mob_(0), dmob_(0),
porevol_(0), dg_(0), ds_(0), pc_(0), dpc_(0), trans_(0), porevol_(0), dg_(0), ds_(0), pc_(0), dpc_(0), trans_(0),
@ -74,44 +115,160 @@ namespace Opm {
trans_ = dpc_ + (1 * nc ); trans_ = dpc_ + (1 * nc );
} }
/**
* Modifiable density difference.
* @return Reference to modifiable internal representation of fluid
* phase density difference.
*/
double& drho () { return drho_ ; } double& drho () { return drho_ ; }
/**
* Read-only density difference.
* @return Read-only value of current fluid phase difference value.
*/
double drho () const { return drho_ ; } 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); } 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); } 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); } 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); } 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_ ; } 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] ; } 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] ; } 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] ; } 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] ; } 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] ; } 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] ; } 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] ; } 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] ; } 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] ; } 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] ; } 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] ; } double trans(int f) const { return trans_[f] ; }
private: private:
double drho_ ; double drho_ ; /**< Fluid phase density difference */
double *mob_ ; double *mob_ ; /**< Fluid phase mobility in all cells */
double *dmob_ ; double *dmob_ ; /**< Derivative of phase mobility in all cells */
double *porevol_; double *porevol_; /**< Pore volume in all cells */
double *dg_ ; double *dg_ ; /**< Static gravity term on all half faces */
double *ds_ ; double *ds_ ; /**< Saturation change in all cells */
double *pc_ ; double *pc_ ; /**< Capillary pressure in all cells */
double *dpc_ ; double *dpc_ ; /**< Derivative of cap. pressure in all cells */
double *trans_ ; double *trans_ ; /**< Absolute transmissibility on all faces */
/**
* Data storage from which individual quantities are managed.
*/
std::vector<double> data_; std::vector<double> data_;
}; };
} }
@ -149,42 +306,42 @@ namespace Opm {
std::copy(porevol.begin(), porevol.end(), store_.porevol()); std::copy(porevol.begin(), porevol.end(), store_.porevol());
} }
void makefhfQPeriodic( const std::vector<int>& p_faces,const std::vector<int>& hf_faces,
void
makefhfQPeriodic(const std::vector<int>& p_faces ,
const std::vector<int>& hf_faces,
const std::vector<int>& nb_faces) const std::vector<int>& nb_faces)
{ {
assert (! p_faces.empty());
assert (p_faces.size() == hf_faces.size());
assert (hf_faces.size() == nb_faces.size());
std::vector<int> nbhf(hf_faces.size()); std::vector<int> nbhf(hf_faces.size());
for(unsigned int i=0; i<p_faces.size(); ++i){
int nbf = nb_faces[i];
if(f2hf_[2*nbf] == -1){
nbhf[i] = f2hf_[2*nbf+1];
}else{
assert(f2hf_[2*nbf+1]==-1);
nbhf[i] = f2hf_[2*nbf];
}
}
for(unsigned int i=0; i<p_faces.size(); ++i){
int f = p_faces[i]; for (std::vector<int>::size_type i = 0; i < p_faces.size(); ++i) {
int hf = hf_faces[i]; const int nbf = nb_faces[i];
bool changed=false;
if(f2hf_[2*f] == hf){ assert (2*std::vector<int>::size_type(nbf) + 1 < f2hf_.size());
assert(f2hf_[2*f+1]==-1); assert ((f2hf_[2*nbf + 0] < 0) ^ (f2hf_[2*nbf + 1] < 0));
}else{
assert(f2hf_[2*f]==-1); const int p = (f2hf_[2*nbf + 0] < 0) ? 1 : 0; // "Self"
f2hf_[2*f]=nbhf[i]; nbhf[ i ] = f2hf_[2*nbf + p];
changed=true;
} }
if(!changed){
if(f2hf_[2*f+1]== hf){ for (std::vector<int>::size_type i = 0; i < p_faces.size(); ++i) {
assert(f2hf_[2*f]==-1); const int f = p_faces [i];
}else{ const int hf = hf_faces[i];
assert(f2hf_[2*f+1]==-1);
f2hf_[2*f+1]=nbhf[i]; assert (0 <= f);
changed=true; assert (0 <= hf);
} assert (2*std::vector<int>::size_type(f) + 1 < f2hf_.size());
}
assert(changed); 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 ];
} }
} }

View File

@ -24,6 +24,7 @@
#include <opm/core/transport/reorder/reordersequence.h> #include <opm/core/transport/reorder/reordersequence.h>
#include <opm/core/utility/RootFinders.hpp> #include <opm/core/utility/RootFinders.hpp>
#include <opm/core/utility/miscUtilities.hpp> #include <opm/core/utility/miscUtilities.hpp>
#include <opm/core/utility/miscUtilitiesBlackoil.hpp>
#include <opm/core/pressure/tpfa/trans_tpfa.h> #include <opm/core/pressure/tpfa/trans_tpfa.h>
#include <fstream> #include <fstream>
@ -38,7 +39,8 @@ namespace Opm
typedef RegulaFalsi<WarnAndContinueOnError> RootFinder; typedef RegulaFalsi<WarnAndContinueOnError> RootFinder;
TransportModelCompressibleTwophase::TransportModelCompressibleTwophase(const UnstructuredGrid& grid, TransportModelCompressibleTwophase::TransportModelCompressibleTwophase(
const UnstructuredGrid& grid,
const Opm::BlackoilPropertiesInterface& props, const Opm::BlackoilPropertiesInterface& props,
const double tol, const double tol,
const int maxit) const int maxit)
@ -51,6 +53,7 @@ namespace Opm
dt_(0.0), dt_(0.0),
saturation_(grid.number_of_cells, -1.0), saturation_(grid.number_of_cells, -1.0),
fractionalflow_(grid.number_of_cells, -1.0), fractionalflow_(grid.number_of_cells, -1.0),
gravity_(0),
mob_(2*grid.number_of_cells, -1.0), mob_(2*grid.number_of_cells, -1.0),
ia_upw_(grid.number_of_cells + 1, -1), ia_upw_(grid.number_of_cells + 1, -1),
ja_upw_(grid.number_of_faces, -1), ja_upw_(grid.number_of_faces, -1),
@ -75,14 +78,16 @@ namespace Opm
void TransportModelCompressibleTwophase::solve(const double* darcyflux, void TransportModelCompressibleTwophase::solve(const double* darcyflux,
const double* pressure, const double* pressure,
const double* surfacevol0, const double* porevolume0,
const double* porevolume, const double* porevolume,
const double* source, const double* source,
const double dt, const double dt,
std::vector<double>& saturation) std::vector<double>& saturation,
std::vector<double>& surfacevol)
{ {
darcyflux_ = darcyflux; darcyflux_ = darcyflux;
surfacevol0_ = surfacevol0; surfacevol0_ = &surfacevol[0];
porevolume0_ = porevolume0;
porevolume_ = porevolume; porevolume_ = porevolume;
source_ = source; source_ = source;
dt_ = dt; dt_ = dt;
@ -91,6 +96,11 @@ namespace Opm
props_.viscosity(props_.numCells(), pressure, NULL, &allcells_[0], &visc_[0], NULL); props_.viscosity(props_.numCells(), pressure, NULL, &allcells_[0], &visc_[0], NULL);
props_.matrix(props_.numCells(), pressure, NULL, &allcells_[0], &A_[0], NULL); props_.matrix(props_.numCells(), pressure, NULL, &allcells_[0], &A_[0], NULL);
// 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<int> seq(grid_.number_of_cells); std::vector<int> seq(grid_.number_of_cells);
std::vector<int> comp(grid_.number_of_cells + 1); std::vector<int> comp(grid_.number_of_cells + 1);
int ncomp; int ncomp;
@ -105,43 +115,47 @@ namespace Opm
&ia_downw_[0], &ja_downw_[0]); &ia_downw_[0], &ja_downw_[0]);
reorderAndTransport(grid_, darcyflux); reorderAndTransport(grid_, darcyflux);
toBothSat(saturation_, saturation); 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 // Residual function r(s) for a single-cell implicit Euler transport
// //
// [[ incompressible was: r(s) = s - s0 + dt/pv*( influx + outflux*f(s) ) ]] // [[ 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 // @@@ What about the source term
// //
// where influx is water influx, outflux is total outflux. // 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. // 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} - q = sum_{i->j} v_{ij} - q (as before) // 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. // Influxes are negative, outfluxes positive.
struct TransportModelCompressibleTwophase::Residual struct TransportModelCompressibleTwophase::Residual
{ {
int cell; int cell;
double s0; double B_cell;
double influx; // sum_j min(v_ij, 0)*f(s_j) + q_w // TODO: fix comment. double z0;
double outflux; // sum_j max(v_ij, 0) - q 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. // @@@ TODO: figure out change to rock-comp. terms with fluid compr.
// double comp_term; // q - sum_j v_ij double comp_term; // Now: used to be: q - sum_j v_ij
double dtpv; // dt/pv(i) double dtpv; // dt/pv(i)
const TransportModelCompressibleTwophase& tm; const TransportModelCompressibleTwophase& tm;
explicit Residual(const TransportModelCompressibleTwophase& tmodel, int cell_index) explicit Residual(const TransportModelCompressibleTwophase& tmodel, int cell_index)
: tm(tmodel) : tm(tmodel)
{ {
cell = cell_index; cell = cell_index;
s0 = tm.saturation_[cell];
const int np = tm.props_.numPhases(); 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]; double src_flux = -tm.source_[cell];
bool src_is_inflow = src_flux < 0.0; bool src_is_inflow = src_flux < 0.0;
influx = src_is_inflow ? src_flux : 0.0; influx = src_is_inflow ? B_cell*src_flux : 0.0;
outflux = !src_is_inflow ? src_flux : 0.0; outflux = !src_is_inflow ? B_cell*src_flux : 0.0;
// comp_term = tm.source_[cell]; // Note: this assumes that all source flux is water. comp_term = (tm.porevolume_[cell] - tm.porevolume0_[cell])/tm.porevolume0_[cell];
dtpv = tm.dt_/tm.porevolume_[cell]; dtpv = tm.dt_/tm.porevolume0_[cell];
for (int i = tm.grid_.cell_facepos[cell]; i < tm.grid_.cell_facepos[cell+1]; ++i) { for (int i = tm.grid_.cell_facepos[cell]; i < tm.grid_.cell_facepos[cell+1]; ++i) {
const int f = tm.grid_.cell_faces[i]; const int f = tm.grid_.cell_faces[i];
double flux; double flux;
@ -160,16 +174,15 @@ namespace Opm
const double b_face = tm.A_[np*np*other + 0]; const double b_face = tm.A_[np*np*other + 0];
influx += B_cell*b_face*flux*tm.fractionalflow_[other]; influx += B_cell*b_face*flux*tm.fractionalflow_[other];
} else { } else {
outflux += flux; outflux += flux; // Because B_cell*b_face = 1 for outflow faces
} }
// comp_term -= flux;
} }
} }
} }
double operator()(double s) const 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 + s*comp_term);
return s - s0 + dtpv*(outflux*tm.fracFlow(s, cell) + influx); return s - B_cell*z0 + dtpv*(outflux*tm.fracFlow(s, cell) + influx) + s*comp_term;
} }
}; };
@ -252,6 +265,8 @@ namespace Opm
++update_count; ++update_count;
const int cell = cells[i]; const int cell = cells[i];
const double old_s = saturation_[cell]; const double old_s = saturation_[cell];
// solveSingleCell() requires saturation_[cell]
// to be s0.
saturation_[cell] = s0[i]; saturation_[cell] = s0[i];
solveSingleCell(cell); solveSingleCell(cell);
const double s_change = std::fabs(saturation_[cell] - old_s); const double s_change = std::fabs(saturation_[cell] - old_s);
@ -303,8 +318,9 @@ namespace Opm
// Residual function r(s) for a single-cell implicit Euler gravity segregation // 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 struct TransportModelCompressibleTwophase::GravityResidual
{ {
int cell; int cell;
@ -333,7 +349,7 @@ namespace Opm
gf[1] = gravflux[pos]; gf[1] = gravflux[pos];
} }
s0 = tm.saturation_[cell]; s0 = tm.saturation_[cell];
dtpv = tm.dt_/tm.porevolume_[cell]; dtpv = tm.dt_/tm.porevolume0_[cell];
} }
double operator()(double s) const double operator()(double s) const
@ -372,30 +388,54 @@ namespace Opm
void TransportModelCompressibleTwophase::initGravity(const double* grav) 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<double> htrans(grid_.cell_facepos[grid_.number_of_cells]); std::vector<double> htrans(grid_.cell_facepos[grid_.number_of_cells]);
const int nf = grid_.number_of_faces; const int nf = grid_.number_of_faces;
const int dim = grid_.dimensions; trans_.resize(nf);
gravflux_.resize(nf); gravflux_.resize(nf);
tpfa_htrans_compute(const_cast<UnstructuredGrid*>(&grid_), props_.permeability(), &htrans[0]); tpfa_htrans_compute(const_cast<UnstructuredGrid*>(&grid_), props_.permeability(), &htrans[0]);
tpfa_trans_compute(const_cast<UnstructuredGrid*>(&grid_), &htrans[0], &gravflux_[0]); tpfa_trans_compute(const_cast<UnstructuredGrid*>(&grid_), &htrans[0], &trans_[0]);
const double delta_rho = 0.0;// props_.density()[0] - props_.density()[1]; // Remember gravity vector.
THROW("TransportModelCompressibleTwophase gravity solver not done yet."); // See line above... 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) { for (int f = 0; f < nf; ++f) {
const int* c = &grid_.face_cells[2*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) { if (c[0] != -1 && c[1] != -1) {
for (int ci = 0; ci < 2; ++ci) {
double gdz = 0.0;
for (int d = 0; d < dim; ++d) { for (int d = 0; d < dim; ++d) {
gdz += grav[d]*(grid_.cell_centroids[dim*c[0] + d] - grid_.cell_centroids[dim*c[1] + 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<int>& cells, void TransportModelCompressibleTwophase::solveSingleCellGravity(const std::vector<int>& cells,
const int pos, const int pos,
const double* gravflux) const double* gravflux)
@ -467,10 +507,14 @@ namespace Opm
void TransportModelCompressibleTwophase::solveGravity(const std::vector<std::vector<int> >& columns, void TransportModelCompressibleTwophase::solveGravity(const std::vector<std::vector<int> >& columns,
const double* pressure, const double* pressure,
const double* porevolume, const double* porevolume0,
const double dt, const double dt,
std::vector<double>& saturation) std::vector<double>& saturation,
std::vector<double>& surfacevol)
{ {
// Assume that solve() has already been called, so that A_ is current.
initGravityDynamic();
// Initialize mobilities. // Initialize mobilities.
const int nc = grid_.number_of_cells; const int nc = grid_.number_of_cells;
std::vector<int> cells(nc); std::vector<int> cells(nc);
@ -489,7 +533,7 @@ namespace Opm
} }
// Set up other variables. // Set up other variables.
porevolume_ = porevolume; porevolume0_ = porevolume0;
dt_ = dt; dt_ = dt;
toWaterSat(saturation, saturation_); toWaterSat(saturation, saturation_);
@ -502,6 +546,9 @@ namespace Opm
std::cout << "Gauss-Seidel column solver average iterations: " std::cout << "Gauss-Seidel column solver average iterations: "
<< double(num_iters)/double(columns.size()) << std::endl; << double(num_iters)/double(columns.size()) << std::endl;
toBothSat(saturation_, saturation); 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 } // namespace Opm

View File

@ -30,6 +30,8 @@ namespace Opm
class BlackoilPropertiesInterface; class BlackoilPropertiesInterface;
/// Implements a reordering transport solver for compressible,
/// non-miscible two-phase flow.
class TransportModelCompressibleTwophase : public TransportModelInterface class TransportModelCompressibleTwophase : public TransportModelInterface
{ {
public: public:
@ -47,20 +49,23 @@ namespace Opm
/// \param[in] darcyflux Array of signed face fluxes. /// \param[in] darcyflux Array of signed face fluxes.
/// \param[in] pressure Array of cell pressures /// \param[in] pressure Array of cell pressures
/// \param[in] surfacevol0 Array of surface volumes at start of timestep /// \param[in] surfacevol0 Array of surface volumes at start of timestep
/// \param[in] porevolume Array of pore volumes. /// \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] source Transport source term.
/// \param[in] dt Time step. /// \param[in] dt Time step.
/// \param[in, out] saturation Phase saturations. /// \param[in, out] saturation Phase saturations.
/// \param[in, out] surfacevol Surface volume densities for each phase.
void solve(const double* darcyflux, void solve(const double* darcyflux,
const double* pressure, const double* pressure,
const double* surfacevol0, const double* porevolume0,
const double* porevolume, const double* porevolume,
const double* source, const double* source,
const double dt, const double dt,
std::vector<double>& saturation); std::vector<double>& saturation,
std::vector<double>& surfacevol);
/// Initialise quantities needed by gravity solver. /// Initialise quantities needed by gravity solver.
/// \param[in] grav gravity vector /// \param[in] grav Gravity vector
void initGravity(const double* grav); void initGravity(const double* grav);
/// Solve for gravity segregation. /// Solve for gravity segregation.
@ -68,12 +73,17 @@ namespace Opm
/// It assumes that the input columns contain cells in a single /// It assumes that the input columns contain cells in a single
/// vertical stack, that do not interact with other columns (for /// vertical stack, that do not interact with other columns (for
/// gravity segregation. /// 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<std::vector<int> >& columns, void solveGravity(const std::vector<std::vector<int> >& columns,
const double* pressure, const double* pressure,
const double* porevolume, const double* porevolume0,
const double dt, const double dt,
std::vector<double>& saturation); std::vector<double>& saturation,
std::vector<double>& surfacevol);
private: private:
virtual void solveSingleCell(const int cell); virtual void solveSingleCell(const int cell);
@ -82,6 +92,7 @@ namespace Opm
const int pos, const int pos,
const double* gravflux); const double* gravflux);
int solveGravityColumn(const std::vector<int>& cells); int solveGravityColumn(const std::vector<int>& cells);
void initGravityDynamic();
private: private:
const UnstructuredGrid& grid_; const UnstructuredGrid& grid_;
@ -96,12 +107,16 @@ namespace Opm
const double* darcyflux_; // one flux per grid face const double* darcyflux_; // one flux per grid face
const double* surfacevol0_; // one per phase per cell const double* surfacevol0_; // one per phase per cell
const double* porevolume0_; // one volume per cell
const double* porevolume_; // one volume per cell const double* porevolume_; // one volume per cell
const double* source_; // one source per cell const double* source_; // one source per cell
double dt_; double dt_;
std::vector<double> saturation_; // P (= num. phases) per cell std::vector<double> saturation_; // P (= num. phases) per cell
std::vector<double> fractionalflow_; // = m[0]/(m[0] + m[1]) per cell std::vector<double> fractionalflow_; // = m[0]/(m[0] + m[1]) per cell
// For gravity segregation. // For gravity segregation.
const double* gravity_;
std::vector<double> trans_;
std::vector<double> density_;
std::vector<double> gravflux_; std::vector<double> gravflux_;
std::vector<double> mob_; std::vector<double> mob_;
std::vector<double> s0_; std::vector<double> s0_;

View File

@ -53,6 +53,7 @@ namespace Opm
dt_(0.0), dt_(0.0),
saturation_(grid.number_of_cells, -1.0), saturation_(grid.number_of_cells, -1.0),
fractionalflow_(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) mob_(2*grid.number_of_cells, -1.0)
#ifdef EXPERIMENT_GAUSS_SEIDEL #ifdef EXPERIMENT_GAUSS_SEIDEL
, ia_upw_(grid.number_of_cells + 1, -1), , ia_upw_(grid.number_of_cells + 1, -1),
@ -101,11 +102,18 @@ namespace Opm
&seq[0], &comp[0], &ncomp, &seq[0], &comp[0], &ncomp,
&ia_downw_[0], &ja_downw_[0]); &ia_downw_[0], &ja_downw_[0]);
#endif #endif
std::fill(reorder_iterations_.begin(),reorder_iterations_.end(),0);
reorderAndTransport(grid_, darcyflux); reorderAndTransport(grid_, darcyflux);
toBothSat(saturation_, saturation); toBothSat(saturation_, saturation);
} }
const std::vector<int>& TransportModelTwophase::getReorderIterations() const
{
return reorder_iterations_;
}
// Residual function r(s) for a single-cell implicit Euler transport // Residual function r(s) for a single-cell implicit Euler transport
// //
// r(s) = s - s0 + dt/pv*( influx + outflux*f(s) ) // r(s) = s - s0 + dt/pv*( influx + outflux*f(s) )
@ -170,9 +178,11 @@ namespace Opm
// if (std::fabs(r0) < tol_) { // if (std::fabs(r0) < tol_) {
// return; // return;
// } // }
int iters_used; int iters_used = 0;
// saturation_[cell] = modifiedRegulaFalsi(res, smin_[2*cell], smax_[2*cell], maxit_, tol_, 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); 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); fractionalflow_[cell] = fracFlow(saturation_[cell], cell);
} }
@ -544,8 +554,9 @@ namespace Opm
const int cell = cells[pos]; const int cell = cells[pos];
GravityResidual res(*this, cells, pos, gravflux); GravityResidual res(*this, cells, pos, gravflux);
if (std::fabs(res(saturation_[cell])) > tol_) { 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); 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]); 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]);

View File

@ -23,7 +23,7 @@
#include <opm/core/transport/reorder/TransportModelInterface.hpp> #include <opm/core/transport/reorder/TransportModelInterface.hpp>
#include <vector> #include <vector>
#include <map> #include <map>
#include <ostream>
struct UnstructuredGrid; struct UnstructuredGrid;
namespace Opm namespace Opm
@ -75,6 +75,10 @@ namespace Opm
const double dt, const double dt,
std::vector<double>& saturation); std::vector<double>& saturation);
//// Return the number of iterations used by the reordering solver.
//// \return vector of iteration per cell
const std::vector<int>& getReorderIterations() const;
private: private:
virtual void solveSingleCell(const int cell); virtual void solveSingleCell(const int cell);
virtual void solveMultiCell(const int num_cells, const int* cells); virtual void solveMultiCell(const int num_cells, const int* cells);
@ -83,7 +87,6 @@ namespace Opm
const int pos, const int pos,
const double* gravflux); const double* gravflux);
int solveGravityColumn(const std::vector<int>& cells); int solveGravityColumn(const std::vector<int>& cells);
private: private:
const UnstructuredGrid& grid_; const UnstructuredGrid& grid_;
const IncompPropertiesInterface& props_; const IncompPropertiesInterface& props_;
@ -99,6 +102,8 @@ namespace Opm
double dt_; double dt_;
std::vector<double> saturation_; // one per cell, only water saturation! std::vector<double> saturation_; // one per cell, only water saturation!
std::vector<double> fractionalflow_; // = m[0]/(m[0] + m[1]) per cell std::vector<double> fractionalflow_; // = m[0]/(m[0] + m[1]) per cell
std::vector<int> reorder_iterations_;
//std::vector<double> reorder_fval_;
// For gravity segregation. // For gravity segregation.
std::vector<double> gravflux_; std::vector<double> gravflux_;
std::vector<double> mob_; std::vector<double> mob_;

View File

@ -86,7 +86,7 @@ namespace Opm
template<class Derived> template<class Derived>
static void addCreator(const std::string& type) static void addCreator(const std::string& type)
{ {
instance().doAddCreator<Derived>(type); instance().template doAddCreator<Derived>(type);
} }
private: private:

View File

@ -73,7 +73,7 @@ class MonotCubicInterpolator {
if (!read(datafilename)) { if (!read(datafilename)) {
throw("Unable to constuct MonotCubicInterpolator from file.") ; throw("Unable to constuct MonotCubicInterpolator from file.") ;
} ; } ;
} ; }
/** /**
@ -94,7 +94,7 @@ class MonotCubicInterpolator {
if (!read(std::string(datafilename))) { if (!read(std::string(datafilename))) {
throw("Unable to constuct MonotCubicInterpolator from file.") ; throw("Unable to constuct MonotCubicInterpolator from file.") ;
} ; } ;
} ; }
/** /**
@ -110,7 +110,7 @@ class MonotCubicInterpolator {
if (!read(std::string(datafilename),xColumn,fColumn)) { if (!read(std::string(datafilename),xColumn,fColumn)) {
throw("Unable to constuct MonotCubicInterpolator from file.") ; throw("Unable to constuct MonotCubicInterpolator from file.") ;
} ; } ;
} ; }
/** /**
@param datafilename data file @param datafilename data file
@ -125,7 +125,7 @@ class MonotCubicInterpolator {
if (!read(datafilename,xColumn,fColumn)) { if (!read(datafilename,xColumn,fColumn)) {
throw("Unable to constuct MonotCubicInterpolator from file.") ; throw("Unable to constuct MonotCubicInterpolator from file.") ;
} ; } ;
} ; }
/** /**
@param x vector of x values @param x vector of x values
@ -144,7 +144,7 @@ class MonotCubicInterpolator {
This object must be treated with care until This object must be treated with care until
populated. populated.
*/ */
MonotCubicInterpolator() {;} ; MonotCubicInterpolator() { }
@ -163,7 +163,7 @@ class MonotCubicInterpolator {
*/ */
bool read(const std::string & datafilename) { bool read(const std::string & datafilename) {
return read(datafilename,1,2) ; return read(datafilename,1,2) ;
} ; }
/** /**
@param datafilename data file @param datafilename data file
@ -188,7 +188,7 @@ class MonotCubicInterpolator {
@return f(x) for a given x @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 @param x x value
@ -485,7 +485,7 @@ class MonotCubicInterpolator {
*/ */
void shrinkFlatAreas() { void shrinkFlatAreas() {
shrinkFlatAreas(1e-14); shrinkFlatAreas(1e-14);
}; }

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_NONUNIFORMTABLELINEAR_HEADER_INCLUDED
#define OPM_NONUNIFORMTABLELINEAR_HEADER_INCLUDED
#include <cmath>
#include <exception>
#include <vector>
#include <utility>
#include <opm/core/utility/ErrorMacros.hpp>
#include <opm/core/utility/linearInterpolation.hpp>
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<typename T>
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<double>& x_values,
const std::vector<T>& y_values);
/// @brief Get the domain.
/// @return the domain as a pair of doubles.
std::pair<double, double> domain();
/// @brief Rescale the domain.
/// @param new_domain the new domain as a pair of doubles.
void rescaleDomain(std::pair<double, double> 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<double> x_values_;
std::vector<T> y_values_;
mutable std::vector<T> x_values_reversed_;
mutable std::vector<T> 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 <typename FI>
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<typename T>
inline
NonuniformTableLinear<T>
::NonuniformTableLinear()
: left_(ClosestValue), right_(ClosestValue)
{
}
template<typename T>
inline
NonuniformTableLinear<T>
::NonuniformTableLinear(const std::vector<double>& x_values,
const std::vector<T>& y_values)
: x_values_(x_values), y_values_(y_values),
left_(ClosestValue), right_(ClosestValue)
{
ASSERT(isNondecreasing(x_values.begin(), x_values.end()));
}
template<typename T>
inline std::pair<double, double>
NonuniformTableLinear<T>
::domain()
{
return std::make_pair(x_values_[0], x_values_.back());
}
template<typename T>
inline void
NonuniformTableLinear<T>
::rescaleDomain(std::pair<double, double> 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<typename T>
inline double
NonuniformTableLinear<T>
::operator()(const double x) const
{
return Opm::linearInterpolation(x_values_, y_values_, x);
}
template<typename T>
inline double
NonuniformTableLinear<T>
::derivative(const double x) const
{
return Opm::linearInterpolationDerivative(x_values_, y_values_, x);
}
template<typename T>
inline double
NonuniformTableLinear<T>
::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<typename T>
inline bool
NonuniformTableLinear<T>
::operator==(const NonuniformTableLinear<T>& other) const
{
return x_values_ == other.x_values_
&& y_values_ == other.y_values_
&& left_ == other.left_
&& right_ == other.right_;
}
template<typename T>
inline void
NonuniformTableLinear<T>
::setLeftPolicy(RangePolicy rp)
{
if (rp != ClosestValue) {
THROW("Only ClosestValue RangePolicy implemented.");
}
left_ = rp;
}
template<typename T>
inline void
NonuniformTableLinear<T>
::setRightPolicy(RangePolicy rp)
{
if (rp != ClosestValue) {
THROW("Only ClosestValue RangePolicy implemented.");
}
right_ = rp;
}
} // namespace Opm
#endif // OPM_NONUNIFORMTABLELINEAR_HEADER_INCLUDED

View File

@ -57,11 +57,11 @@ namespace Opm
/// restarted by a call to start(). /// restarted by a call to start().
void stop(); 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 /// since last call to start(), secsSinceLast() or
/// secsSinceStart() /// secsSinceStart()
double secsSinceLast(); 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(). /// since last call to start().
double secsSinceStart(); double secsSinceStart();

View File

@ -188,10 +188,14 @@ namespace Opm {
UniformTableLinear<T> UniformTableLinear<T>
::derivative(const double xparam) const ::derivative(const double xparam) const
{ {
// Implements ClosestValue policy. // 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_); double x = std::min(xparam, xmax_);
x = std::max(x, xmin_); x = std::max(x, xmin_);
// Lookup is easy since we are uniform in x. // Lookup is easy since we are uniform in x.
double pos = (x - xmin_)/xdelta_; double pos = (x - xmin_)/xdelta_;
double posi = std::floor(pos); double posi = std::floor(pos);
@ -200,7 +204,9 @@ namespace Opm {
// We are at xmax_ // We are at xmax_
--left; --left;
} }
return (y_values_[left + 1] - y_values_[left])/xdelta_; value = (y_values_[left + 1] - y_values_[left])/xdelta_;
}
return value;
} }

View File

@ -35,18 +35,24 @@
#ifndef OPENRS_UNITS_HEADER #ifndef OPENRS_UNITS_HEADER
#define 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 Opm
{ {
namespace prefix namespace prefix
/// Conversion prefix for units. /// Conversion prefix for units.
{ {
const double micro = 1.0e-6; const double micro = 1.0e-6; /**< Unit prefix [\f$\mu\f$] */
const double milli = 1.0e-3; const double milli = 1.0e-3; /**< Unit prefix [m] */
const double centi = 1.0e-2; const double centi = 1.0e-2; /**< Non-standard unit prefix [c] */
const double deci = 1.0e-1; const double deci = 1.0e-1; /**< Non-standard unit prefix [d] */
const double kilo = 1.0e3; const double kilo = 1.0e3; /**< Unit prefix [k] */
const double mega = 1.0e6; const double mega = 1.0e6; /**< Unit prefix [M] */
const double giga = 1.0e9; const double giga = 1.0e9; /**< Unit prefix [G] */
} // namespace prefix } // namespace prefix
namespace unit namespace unit
@ -137,15 +143,6 @@ namespace Opm
const double Poise = prefix::deci*Pas; 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 { namespace perm_details {
const double p_grad = atm / (prefix::centi*meter); const double p_grad = atm / (prefix::centi*meter);
const double area = square(prefix::centi*meter); const double area = square(prefix::centi*meter);
@ -156,47 +153,63 @@ namespace Opm
// == 1e-7 [m^2] / 101325 // == 1e-7 [m^2] / 101325
// == 9.869232667160130e-13 [m^2] // == 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; const double darcy = perm_details::darcy;
/// @} /// @}
// Unit conversion support. /**
// * Unit conversion routines.
// Note: Under the penalty of treason will you be */
//
// using namespace Opm::unit::convert;
//
// I mean it!
//
namespace convert { namespace convert {
// Convert from external units of measurements to equivalent /**
// internal units of measurements. Note: The internal units * Convert from external units of measurements to equivalent
// of measurements are *ALWAYS*, and exclusively, SI. * 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 * Example: Convert a double @c kx, containing a permeability value
// value in SI units (m^2). * in units of milli-darcy (mD) to the equivalent value in SI units
// * (i.e., \f$m^2\f$).
// using namespace Opm::unit; * \code
// using namespace Opm::prefix; * using namespace Opm::unit;
// convert::from(kx, milli*darcy); * 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) inline double from(const double q, const double unit)
{ {
return q * unit; return q * unit;
} }
// Convert from internal units of measurements to equivalent /**
// external units of measurements. Note: The internal units * Convert from internal units of measurements to equivalent
// of measurements are *ALWAYS*, and exclusively, SI. * external units of measurements. Note: The internal units of
// * measurements are *ALWAYS*, and exclusively, SI.
// Example: Convert a std::vector<double> p, containing *
// pressure values in the SI unit Pascal (i.e., unit::Pascal) * Example: Convert a <CODE>std::vector<double> p</CODE>, containing
// to the equivalent values in Psi (unit::psia). * pressure values in the SI unit Pascal (i.e., unit::Pascal) to the
// * equivalent values in Psi (unit::psia).
// using namespace Opm::unit; * \code
// std::transform(p.begin(), p.end(), p.begin(), * using namespace Opm::unit;
// boost::bind(convert::to, _1, psia)); * 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 <CODE>unit</CODE>.
*/
inline double to(const double q, const double unit) inline double to(const double q, const double unit)
{ {
return q / unit; return q / unit;

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
// Note: This file should be #include<>d after <config.h> 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 */

View File

@ -512,11 +512,11 @@ namespace Opm
State& state) State& state)
{ {
const int num_phases = props.numPhases(); const int num_phases = props.numPhases();
if (num_phases != 2) {
THROW("initStateFromDeck(): currently handling only two-phase scenarios.");
}
state.init(grid, num_phases); state.init(grid, num_phases);
if (deck.hasField("EQUIL")) { 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. // Set saturations depending on oil-water contact.
const EQUIL& equil= deck.getEQUIL(); const EQUIL& equil= deck.getEQUIL();
if (equil.equil.size() != 1) { if (equil.equil.size() != 1) {
@ -535,12 +535,28 @@ namespace Opm
const std::vector<double>& sw_deck = deck.getFloatingPointValue("SWAT"); const std::vector<double>& sw_deck = deck.getFloatingPointValue("SWAT");
const std::vector<double>& p_deck = deck.getFloatingPointValue("PRESSURE"); const std::vector<double>& p_deck = deck.getFloatingPointValue("PRESSURE");
const int num_cells = grid.number_of_cells; const int num_cells = grid.number_of_cells;
if (num_phases == 2) {
for (int c = 0; c < num_cells; ++c) { for (int c = 0; c < num_cells; ++c) {
int c_deck = (grid.global_cell == NULL) ? c : grid.global_cell[c]; int c_deck = (grid.global_cell == NULL) ? c : grid.global_cell[c];
s[2*c] = sw_deck[c_deck]; s[2*c] = sw_deck[c_deck];
s[2*c + 1] = 1.0 - s[2*c]; s[2*c + 1] = 1.0 - s[2*c];
p[c] = p_deck[c_deck]; 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<double>& 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 { } else {
THROW("initStateFromDeck(): we must either have EQUIL, or both SWAT and PRESSURE."); THROW("initStateFromDeck(): we must either have EQUIL, or both SWAT and PRESSURE.");
} }

View File

@ -85,6 +85,40 @@ namespace Opm
} }
} }
template <typename T>
T linearInterpolationDerivativeExtrap(const std::vector<double>& xv,
const std::vector<T>& yv,
double x)
{
std::vector<double>::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 <typename T>
T linearInterpolationExtrap(const std::vector<double>& xv,
const std::vector<T>& yv,
double x)
{
std::vector<double>::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 } // namespace Opm

View File

@ -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<double>& pressure,
std::vector<double>& 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. /// @brief Computes total saturated volumes over all grid cells.
/// @param[in] pv the pore volume by cell. /// @param[in] pv the pore volume by cell.
@ -574,8 +593,10 @@ namespace Opm
{ {
int nw = well_bhp.size(); int nw = well_bhp.size();
ASSERT(nw == wells.number_of_wells); ASSERT(nw == wells.number_of_wells);
if (props.numPhases() != 2) { int np = props.numPhases();
THROW("WellReport for now assumes two phase flow."); const int max_np = 3;
if (np > max_np) {
THROW("WellReport for now assumes #phases <= " << max_np);
} }
const double* visc = props.viscosity(); const double* visc = props.viscosity();
std::vector<double> data_now; std::vector<double> data_now;
@ -586,7 +607,8 @@ namespace Opm
double well_rate_total = 0.0; double well_rate_total = 0.0;
double well_rate_water = 0.0; double well_rate_water = 0.0;
for (int perf = wells.well_connpos[w]; perf < wells.well_connpos[w + 1]; ++perf) { 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; well_rate_total += perf_rate;
if (perf_rate > 0.0) { if (perf_rate > 0.0) {
// Injection. // Injection.
@ -594,11 +616,14 @@ namespace Opm
} else { } else {
// Production. // Production.
const int cell = wells.well_cells[perf]; const int cell = wells.well_cells[perf];
double mob[2]; double mob[max_np];
props.relperm(1, &saturation[2*cell], &cell, mob, 0); props.relperm(1, &saturation[2*cell], &cell, mob, 0);
mob[0] /= visc[0]; double tmob = 0;
mob[1] /= visc[1]; for(int i = 0; i < np; ++i) {
const double fracflow = mob[0]/(mob[0] + mob[1]); mob[i] /= visc[i];
tmob += mob[i];
}
const double fracflow = mob[0]/tmob;
well_rate_water += perf_rate*fracflow; well_rate_water += perf_rate*fracflow;
} }
} }
@ -627,8 +652,10 @@ namespace Opm
// TODO: refactor, since this is almost identical to the other push(). // TODO: refactor, since this is almost identical to the other push().
int nw = well_bhp.size(); int nw = well_bhp.size();
ASSERT(nw == wells.number_of_wells); ASSERT(nw == wells.number_of_wells);
if (props.numPhases() != 2) { int np = props.numPhases();
THROW("WellReport for now assumes two phase flow."); const int max_np = 3;
if (np > max_np) {
THROW("WellReport for now assumes #phases <= " << max_np);
} }
std::vector<double> data_now; std::vector<double> data_now;
data_now.reserve(1 + 3*nw); data_now.reserve(1 + 3*nw);
@ -638,7 +665,8 @@ namespace Opm
double well_rate_total = 0.0; double well_rate_total = 0.0;
double well_rate_water = 0.0; double well_rate_water = 0.0;
for (int perf = wells.well_connpos[w]; perf < wells.well_connpos[w + 1]; ++perf) { 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; well_rate_total += perf_rate;
if (perf_rate > 0.0) { if (perf_rate > 0.0) {
// Injection. // Injection.
@ -646,13 +674,16 @@ namespace Opm
} else { } else {
// Production. // Production.
const int cell = wells.well_cells[perf]; const int cell = wells.well_cells[perf];
double mob[2]; double mob[max_np];
props.relperm(1, &s[2*cell], &cell, mob, 0); props.relperm(1, &s[np*cell], &cell, mob, 0);
double visc[2]; double visc[max_np];
props.viscosity(1, &p[cell], &z[2*cell], &cell, visc, 0); props.viscosity(1, &p[cell], &z[np*cell], &cell, visc, 0);
mob[0] /= visc[0]; double tmob = 0;
mob[1] /= visc[1]; for(int i = 0; i < np; ++i) {
const double fracflow = mob[0]/(mob[0] + mob[1]); mob[i] /= visc[i];
tmob += mob[i];
}
const double fracflow = mob[0]/(tmob);
well_rate_water += perf_rate*fracflow; well_rate_water += perf_rate*fracflow;
} }
} }

View File

@ -44,7 +44,7 @@ namespace Opm
/// @brief Computes pore volume of all cells in a grid, with rock compressibility effects. /// @brief Computes pore volume of all cells in a grid, with rock compressibility effects.
/// @param[in] grid a grid /// @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] rock_comp rock compressibility properties
/// @param[in] pressure pressure by cell /// @param[in] pressure pressure by cell
/// @param[out] porevol the pore volume by cell. /// @param[out] porevol the pore volume by cell.
@ -54,6 +54,17 @@ namespace Opm
const std::vector<double>& pressure, const std::vector<double>& pressure,
std::vector<double>& porevol); std::vector<double>& 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<double>& pressure,
std::vector<double>& porosity);
/// @brief Computes total saturated volumes over all grid cells. /// @brief Computes total saturated volumes over all grid cells.
/// @param[in] pv the pore volume by cell. /// @param[in] pv the pore volume by cell.

View File

@ -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 } // namespace Opm

View File

@ -112,6 +112,25 @@ namespace Opm
const std::vector<double>& s, const std::vector<double>& s,
std::vector<double>& fractional_flows); std::vector<double>& 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 } // namespace Opm
#endif // OPM_MISCUTILITIESBLACKOIL_HEADER_INCLUDED #endif // OPM_MISCUTILITIESBLACKOIL_HEADER_INCLUDED

View File

@ -1,18 +1,20 @@
AM_CPPFLAGS = \ AM_CPPFLAGS = \
-I$(top_srcdir) \ -I$(top_srcdir) \
$(BOOST_CPPFLAGS) \ $(OPM_BOOST_CPPFLAGS) \
$(ERT_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 = \ noinst_PROGRAMS = \
bo_resprop_test \ bo_resprop_test \
monotcubicinterpolator_test \ monotcubicinterpolator_test \
param_test \ param_test \
pvt_test \
relperm_test \
sparsetable_test \ sparsetable_test \
sparsevector_test \ sparsevector_test \
test_cartgrid \ test_cartgrid \
@ -32,6 +34,10 @@ monotcubicinterpolator_test_SOURCES = monotcubicinterpolator_test.cpp
param_test_SOURCES = param_test.cpp param_test_SOURCES = param_test.cpp
param_test_LDADD = $(LDADD) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) 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_SOURCES = sparsetable_test.cpp
sparsetable_test_LDADD = $(LDADD) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) sparsetable_test_LDADD = $(LDADD) $(BOOST_UNIT_TEST_FRAMEWORK_LIB)

View File

@ -24,6 +24,7 @@
#include <opm/core/eclipse/EclipseGridParser.hpp> #include <opm/core/eclipse/EclipseGridParser.hpp>
#include <opm/core/eclipse/EclipseGridInspector.hpp> #include <opm/core/eclipse/EclipseGridInspector.hpp>
#include <opm/core/fluid/BlackoilPropertiesFromDeck.hpp> #include <opm/core/fluid/BlackoilPropertiesFromDeck.hpp>
#include <opm/core/grid.h>
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
@ -39,14 +40,10 @@ int main(int argc, char** argv)
// Parser. // Parser.
std::string ecl_file = param.get<std::string>("filename"); std::string ecl_file = param.get<std::string>("filename");
Opm::EclipseGridParser deck(ecl_file); Opm::EclipseGridParser deck(ecl_file);
Opm::EclipseGridInspector insp(deck); UnstructuredGrid grid;
std::tr1::array<int, 3> gs = insp.gridSize(); grid.number_of_cells = 1;
int num_cells = gs[0]*gs[1]*gs[2]; grid.global_cell = NULL;
std::vector<int> global_cell(num_cells); Opm::BlackoilPropertiesFromDeck props(deck, grid, param);
for (int i = 0; i < num_cells; ++i) {
global_cell[i] = i;
}
Opm::BlackoilPropertiesFromDeck props(deck, global_cell);
const int n = 1; const int n = 1;
double p[n] = { 150e5 }; double p[n] = { 150e5 };

146
tests/pvt_test.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H
#include <opm/core/utility/parameters/ParameterGroup.hpp>
#include <opm/core/eclipse/EclipseGridParser.hpp>
#include <opm/core/eclipse/EclipseGridInspector.hpp>
#include <opm/core/fluid/BlackoilPropertiesFromDeck.hpp>
#include <opm/core/grid.h>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <iterator>
#include <string>
#include <vector>
int main(int argc, char** argv)
{
using namespace std;
// Parameters.
Opm::parameter::ParameterGroup param(argc, argv);
// Parser.
std::string ecl_file = param.get<std::string>("deck_filename");
std::string input_file = param.get<std::string>("input_filename");
std::string matrix_output = param.get<std::string>("matrix_output");
std::string rs_output = param.get<std::string>("rs_output");
std::string b_output = param.get<std::string>("b_output");
std::string mu_output = param.get<std::string>("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<double>(aos, " "));
std::copy(dA, dA + np*np*n, std::ostream_iterator<double>(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<double>(muos, " "));
//std::copy(dmu, dmu + np*n, std::ostream_iterator<double>(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<double>(bos, " "));
std::copy(dbdp, dbdp + np*n, std::ostream_iterator<double>(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<double>(rsos, " "));
std::copy(drs, drs + np*n, std::ostream_iterator<double>(rsos, " "));
rsos << std::endl;
}
}
if (param.anyUnused()) {
std::cout << "-------------------- Unused parameters: --------------------\n";
param.displayUsage();
std::cout << "----------------------------------------------------------------" << std::endl;
}
}

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