Merge from upstream and corrected conflictes.

This commit is contained in:
Halvor Møll Nilsen 2012-10-30 13:38:55 +01:00
commit c6a609bea6
113 changed files with 8394 additions and 1751 deletions

14
.gitignore vendored
View File

@ -1,6 +1,7 @@
*~ *~
.\#* .\#*
*.o *.o
*.mod
*.lo *.lo
*.la *.la
.libs .libs
@ -29,19 +30,30 @@ ltmain.sh
m4/libtool.m4 m4/libtool.m4
m4/lt*.m4 m4/lt*.m4
missing missing
ar-lib
tutorials/tutorial[1-4] tutorials/tutorial[1-4]
opmcore-config.cmake
# in-tree build with CMake
CMakeCache.txt
CMakeFiles/
cmake_install.cmake
# Ignoring executables # Ignoring executables
*_test *_test
examples/compute_tof
examples/scaneclipsedeck examples/scaneclipsedeck
examples/spu_2p examples/spu_2p
examples/reorder-qfs examples/reorder-qfs
examples/refine_wells examples/refine_wells
examples/sim_2p_incomp_reorder examples/sim_2p_incomp_reorder
examples/sim_2p_comp_reorder
examples/sim_wateroil examples/sim_wateroil
examples/wells_example examples/wells_example
tests/test_agmg
tests/test_cfs_tpfa tests/test_cfs_tpfa
tests/test_jacsys tests/test_jacsys
tests/test_read_grid
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
@ -51,4 +63,6 @@ tests/test_column_extract
tests/test_lapack tests/test_lapack
tests/test_read_vag tests/test_read_vag
tests/test_readpolymer tests/test_readpolymer
tests/test_velocityinterpolation
tests/test_wells
tests/test_writeVtkData tests/test_writeVtkData

View File

@ -12,7 +12,9 @@ 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 = \
$(OPM_BOOST_CPPFLAGS) $(ERT_CPPFLAGS) \
$(OPM_BOOST_CPPFLAGS) \
$(SUPERLU_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
@ -20,14 +22,17 @@ $(OPM_BOOST_CPPFLAGS)
lib_libopmcore_la_LDFLAGS = \ lib_libopmcore_la_LDFLAGS = \
-R $(OPM_BOOST_LIBDIR) \ -R $(OPM_BOOST_LIBDIR) \
$(OPM_BOOST_LDFLAGS) $(OPM_BOOST_LDFLAGS) \
$(ERT_LDFLAGS) \
$(SUPERLU_LDFLAGS)
lib_libopmcore_la_LIBADD = \ lib_libopmcore_la_LIBADD = \
$(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) \
$(LAPACK_LIBS) $(BLAS_LIBS) $(LIBS) $(ERT_LIBS) \
$(LAPACK_LIBS) $(SUPERLU_LIBS) $(BLAS_LIBS) $(LIBS)
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# Library constituents. SOURCES followed by HEADERS. # Library constituents. SOURCES followed by HEADERS.
@ -53,10 +58,12 @@ 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/SatFuncGwseg.cpp \
opm/core/fluid/SatFuncStone2.cpp \ opm/core/fluid/SatFuncStone2.cpp \
opm/core/fluid/SatFuncSimple.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 \
@ -103,6 +110,8 @@ opm/core/simulator/SimulatorReport.cpp \
opm/core/simulator/SimulatorTimer.cpp \ opm/core/simulator/SimulatorTimer.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/TransportModelTracerTof.cpp \
opm/core/transport/reorder/TransportModelTracerTofDiscGal.cpp \
opm/core/transport/reorder/TransportModelTwophase.cpp \ opm/core/transport/reorder/TransportModelTwophase.cpp \
opm/core/transport/reorder/nlsolvers.c \ opm/core/transport/reorder/nlsolvers.c \
opm/core/transport/reorder/reordersequence.cpp \ opm/core/transport/reorder/reordersequence.cpp \
@ -112,6 +121,8 @@ opm/core/transport/spu_implicit.c \
opm/core/transport/transport_source.c \ opm/core/transport/transport_source.c \
opm/core/utility/MonotCubicInterpolator.cpp \ opm/core/utility/MonotCubicInterpolator.cpp \
opm/core/utility/StopWatch.cpp \ opm/core/utility/StopWatch.cpp \
opm/core/utility/VelocityInterpolation.cpp \
opm/core/utility/WachspressCoord.cpp \
opm/core/utility/miscUtilities.cpp \ opm/core/utility/miscUtilities.cpp \
opm/core/utility/miscUtilitiesBlackoil.cpp \ opm/core/utility/miscUtilitiesBlackoil.cpp \
opm/core/utility/parameters/Parameter.cpp \ opm/core/utility/parameters/Parameter.cpp \
@ -150,15 +161,19 @@ 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/SaturationPropsBasic.hpp \ opm/core/fluid/SatFuncGwseg.hpp \
opm/core/fluid/SaturationPropsFromDeck.hpp \
opm/core/fluid/SatFuncStone2.hpp \ opm/core/fluid/SatFuncStone2.hpp \
opm/core/fluid/SatFuncSimple.hpp \ opm/core/fluid/SatFuncSimple.hpp \
opm/core/fluid/SaturationPropsBasic.hpp \
opm/core/fluid/SaturationPropsFromDeck.hpp \
opm/core/fluid/SaturationPropsFromDeck_impl.hpp \
opm/core/fluid/SaturationPropsInterface.hpp \
opm/core/fluid/SimpleFluid2p.hpp \ opm/core/fluid/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 \
@ -221,6 +236,8 @@ opm/core/transport/SimpleFluid2pWrapper.hpp \
opm/core/transport/SinglePointUpwindTwoPhase.hpp \ opm/core/transport/SinglePointUpwindTwoPhase.hpp \
opm/core/transport/reorder/TransportModelCompressibleTwophase.hpp \ opm/core/transport/reorder/TransportModelCompressibleTwophase.hpp \
opm/core/transport/reorder/TransportModelInterface.hpp \ opm/core/transport/reorder/TransportModelInterface.hpp \
opm/core/transport/reorder/TransportModelTracerTof.hpp \
opm/core/transport/reorder/TransportModelTracerTofDiscGal.hpp \
opm/core/transport/reorder/TransportModelTwophase.hpp \ opm/core/transport/reorder/TransportModelTwophase.hpp \
opm/core/transport/reorder/nlsolvers.h \ opm/core/transport/reorder/nlsolvers.h \
opm/core/transport/reorder/reordersequence.h \ opm/core/transport/reorder/reordersequence.h \
@ -230,15 +247,19 @@ 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 \
opm/core/utility/StopWatch.hpp \ opm/core/utility/StopWatch.hpp \
opm/core/utility/UniformTableLinear.hpp \ opm/core/utility/UniformTableLinear.hpp \
opm/core/utility/Units.hpp \ opm/core/utility/Units.hpp \
opm/core/utility/VelocityInterpolation.hpp \
opm/core/utility/WachspressCoord.hpp \
opm/core/utility/buildUniformMonotoneTable.hpp \ opm/core/utility/buildUniformMonotoneTable.hpp \
opm/core/utility/initState.hpp \ opm/core/utility/initState.hpp \
opm/core/utility/initState_impl.hpp \ opm/core/utility/initState_impl.hpp \
@ -279,6 +300,15 @@ opm/core/linalg/LinearSolverUmfpack.hpp
endif endif
if HAVE_ERT
lib_libopmcore_la_SOURCES += \
opm/core/utility/writeECLData.cpp
nobase_include_HEADERS += \
opm/core/utility/writeECLData.hpp
endif
if DUNE_ISTL if DUNE_ISTL
lib_libopmcore_la_SOURCES += \ lib_libopmcore_la_SOURCES += \
opm/core/linalg/LinearSolverIstl.cpp opm/core/linalg/LinearSolverIstl.cpp
@ -302,3 +332,6 @@ opm/core/linalg/LinearSolverAGMG.hpp
lib_libopmcore_la_LDFLAGS += \ lib_libopmcore_la_LDFLAGS += \
$(FCLIBS) $(FCLIBS)
endif endif
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = lib/pkgconfig/opm-core.pc

73
README
View File

@ -46,46 +46,39 @@ sudo apt-get install -y build-essential gfortran pkg-config libtool \
sudo apt-get install -y doxygen ghostscript texlive-latex-recommended pgf sudo apt-get install -y doxygen ghostscript texlive-latex-recommended pgf
# packages necessary for version control # packages necessary for version control
sudo apt-get install -y git-core git-svn subversion sudo apt-get install -y git-core
# libraries necessary for DUNE # basic libraries necessary for both DUNE and OPM
sudo apt-get install -y libboost-all-dev libsuperlu3-dev libsuitesparse-dev sudo apt-get install -y libboost-all-dev libsuperlu3-dev libsuitesparse-dev
# libraries necessary for OPM # for server edition of Ubuntu add-apt-repository depends on
sudo apt-get install -y libxml0-dev sudo apt-get install python-software-properties
# add this repository for necessary backports (required for Ubuntu Precise)
sudo add-apt-repository -y ppa:opm/ppa
sudo apt-get update
# parts of DUNE needed
sudo apt-get install libdune-common-dev libdune-istl-dev libdune-grid-dev
# libraries necessary for OPM
sudo apt-get install -y libxml2-dev
Note: You should compile the OPM modules using the same toolchain that
was used to build DUNE. Otherwise, you can get strange ABI errors.
DEPENDENCIES FOR SUSE BASED DISTRIBUTIONS DEPENDENCIES FOR SUSE BASED DISTRIBUTIONS
----------------------------------------- -----------------------------------------
# libraries # libraries
sudo zypper install blas libblas3 lapack liblapack3 libboost libxml2 umfpack sudo zypper in blas libblas3 lapack liblapack3 libboost libxml2 umfpack
# tools # tools
sudo zypper install gcc automake autoconf git doxygen sudo zypper in gcc automake autoconf git doxygen
# DUNE libraries
RETRIEVING AND BUILDING DUNE PREREQUISITES sudo zypper ar http://download.opensuse.org/repositories/science/openSUSE_12.2/science.repo
------------------------------------------ sudo zypper in dune-common dune-istl
(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 DOWNLOADING
@ -100,20 +93,26 @@ If you want to contribute, fork OPM/opm-core on github.
BUILDING BUILDING
-------- --------
(standalone opm-core:) There are two ways to build the opm-core library:
cd ../opm-core 1. As a stand-alone library.
cd opm-core
autoreconf -i autoreconf -i
./configure ./configure
make make -j -l 0.9
If you want to install the library:
make install
or (if installing to /usr/local or similar)
sudo make install sudo make install
(using opm-core as a dune module:) 2. As a dune module.
- Put the opm-core directory in the same directory
# note: this is done from the parent directory of opm-core as the other dune modules to be built (e.g. dune-commmon,
env CCACHE_DISABLE=1 dune-common/bin/dunecontrol --only=opm-core \ dune-grid). Note that for Ubuntu you can install Dune
--configure-opts="" --make-opts="-j -l 0.8" autogen : configure : make from the ppa as outlined above.
- Run dunecontrol as normal. For more information on
the dune build system, see
http://www.dune-project.org/doc/installation-notes.html
DOCUMENTATION DOCUMENTATION

View File

@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
AC_PREREQ([2.59]) AC_PREREQ([2.59])
AC_INIT([OPM Core Library], [0.1], [atgeirr@sintef.no],dnl AC_INIT([OPM Core Library], [0.1], [atgeirr@sintef.no],
[opmcore], [https://public.ict.sintef.no/opm/hg/opmcore]) [opmcore], [https://public.ict.sintef.no/opm/hg/opmcore])
AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
@ -20,8 +20,11 @@ AC_CONFIG_HEADERS([config.h])
AC_PROG_CC AC_PROG_CC
AM_PROG_CC_C_O AM_PROG_CC_C_O
dnl Initialize libtool; the funny indentation here is to
dnl satisfy libtoolize' check for the presence of this macro
m4_ifdef([LT_INIT], m4_ifdef([LT_INIT],
[LT_INIT[]dnl [
LT_INIT[]dnl
LT_LANG([C++])dnl LT_LANG([C++])dnl
LT_LANG([Fortran 77])dnl LT_LANG([Fortran 77])dnl
LT_LANG([Fortran])dnl LT_LANG([Fortran])dnl
@ -36,6 +39,8 @@ OPM_CORE_CHECKS
OPM_DYNLINK_BOOST_TEST OPM_DYNLINK_BOOST_TEST
ERT
dnl Substitute Autoconf's abs_*dir variables into the Makefiles for the dnl Substitute Autoconf's abs_*dir variables into the Makefiles for the
dnl benefit of external code that uses these variables to derive dnl benefit of external code that uses these variables to derive
dnl locations (e.g., Dune's DUNE_CHECK_MODULES macro). Automakes prior dnl locations (e.g., Dune's DUNE_CHECK_MODULES macro). Automakes prior
@ -51,6 +56,9 @@ AC_CONFIG_FILES([
tests/Makefile tests/Makefile
examples/Makefile examples/Makefile
tutorials/Makefile tutorials/Makefile
opm-core.pc
lib/pkgconfig/opm-core.pc
opmcore-config.cmake
]) ])
AC_OUTPUT AC_OUTPUT

View File

@ -1,24 +1,48 @@
# Build-time flags needed to form example programs # Build-time flags needed to form example programs
AM_CPPFLAGS = \ ERT_INCLUDE_PATH = $(ERT_ROOT)/include
-I$(top_srcdir) \
AM_CPPFLAGS = \
-I$(top_srcdir) \
-I$(ERT_INCLUDE_PATH) \
$(OPM_BOOST_CPPFLAGS) $(OPM_BOOST_CPPFLAGS)
# All targets link to the library # All targets link to the library
LDADD = \ LDADD = \
$(top_builddir)/lib/libopmcore.la $(top_builddir)/lib/libopmcore.la
# Convenience definition for targets that use Boost.Filesystem directly.
# While libopmcore depends on (and references) Boost.Filesystem (through
# the $(BOOST_FILESYSTEM_LIB) macro) this indirect dependency is not
# sufficient to satisfy the requirements of targets that use the indirect
# libraries directly.
#
# Additional details at
# https://fedoraproject.org/wiki/UnderstandingDSOLinkChange
#
LINK_BOOST_FILESYSTEM = \
$(OPM_BOOST_LDFLAGS) \
$(BOOST_FILESYSTEM_LIB) \
$(BOOST_SYSTEM_LIB)
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# Declare products (i.e., the example programs). # Declare products (i.e., the example programs).
# #
# Please keep the list sorted. # Please keep the list sorted.
noinst_PROGRAMS = \ noinst_PROGRAMS = \
refine_wells \ compute_tof \
scaneclipsedeck \ refine_wells \
sim_2p_comp_reorder \ scaneclipsedeck \
sim_2p_incomp_reorder \ sim_2p_comp_reorder \
sim_wateroil \ sim_2p_incomp_reorder \
wells_example sim_wateroil \
wells_example
if HAVE_ERT
noinst_PROGRAMS += import_rewrite
endif
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# Product constituents. Must be specified for every product that's # Product constituents. Must be specified for every product that's
@ -27,10 +51,25 @@ wells_example
# #
# Please maintain sort order from "noinst_PROGRAMS". # Please maintain sort order from "noinst_PROGRAMS".
compute_tof_SOURCES = compute_tof.cpp
compute_tof_LDADD = $(LDADD) $(LINK_BOOST_FILESYSTEM)
refine_wells_SOURCES = refine_wells.cpp refine_wells_SOURCES = refine_wells.cpp
if HAVE_ERT
import_rewrite_SOURCES = import_rewrite.cpp
import_rewrite_LDADD = $(LDADD) $(LINK_BOOST_FILESYSTEM)
endif
sim_2p_comp_reorder_SOURCES = sim_2p_comp_reorder.cpp sim_2p_comp_reorder_SOURCES = sim_2p_comp_reorder.cpp
sim_2p_comp_reorder_LDADD = $(LDADD) $(LINK_BOOST_FILESYSTEM)
sim_2p_incomp_reorder_SOURCES = sim_2p_incomp_reorder.cpp sim_2p_incomp_reorder_SOURCES = sim_2p_incomp_reorder.cpp
sim_2p_incomp_reorder_LDADD = $(LDADD) $(LINK_BOOST_FILESYSTEM)
sim_wateroil_SOURCES = sim_wateroil.cpp sim_wateroil_SOURCES = sim_wateroil.cpp
sim_wateroil_LDADD = $(LDADD) $(LINK_BOOST_FILESYSTEM)
wells_example_SOURCES = wells_example.cpp wells_example_SOURCES = wells_example.cpp
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
@ -40,7 +79,8 @@ if UMFPACK
noinst_PROGRAMS += spu_2p noinst_PROGRAMS += spu_2p
spu_2p_SOURCES = spu_2p.cpp spu_2p_SOURCES = spu_2p.cpp
spu_2p_LDADD = \ spu_2p_LDADD = \
$(LDADD) \ $(LDADD) \
$(LINK_BOOST_FILESYSTEM) \
$(LAPACK_LIBS) $(BLAS_LIBS) $(LIBS) $(LAPACK_LIBS) $(BLAS_LIBS) $(LIBS)
endif endif

255
examples/compute_tof.cpp Normal file
View File

@ -0,0 +1,255 @@
/*
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/utility/StopWatch.hpp>
#include <opm/core/utility/miscUtilities.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp>
#include <opm/core/fluid/IncompPropertiesBasic.hpp>
#include <opm/core/fluid/IncompPropertiesFromDeck.hpp>
#include <opm/core/linalg/LinearSolverFactory.hpp>
#include <opm/core/simulator/TwophaseState.hpp>
#include <opm/core/simulator/WellState.hpp>
#include <opm/core/pressure/IncompTpfa.hpp>
#include <opm/core/transport/reorder/TransportModelTracerTof.hpp>
#include <opm/core/transport/reorder/TransportModelTracerTofDiscGal.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 incompressible tof computations ===============\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<IncompPropertiesInterface> props;
boost::scoped_ptr<Opm::WellsManager> wells;
TwophaseState 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 IncompPropertiesFromDeck(*deck, *grid->c_grid()));
// Wells init.
wells.reset(new Opm::WellsManager(*deck, *grid->c_grid(), props->permeability()));
// 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);
}
} 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 IncompPropertiesBasic(param, grid->c_grid()->dimensions, grid->c_grid()->number_of_cells));
// Wells init.
wells.reset(new Opm::WellsManager());
// Gravity.
gravity[2] = param.getDefault("gravity", 0.0);
// Init state variables (saturation and pressure).
initStateBasic(*grid->c_grid(), *props, param, gravity[2], state);
}
// Warn if gravity but no density difference.
bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0);
if (use_gravity) {
if (props->density()[0] == props->density()[1]) {
std::cout << "**** Warning: nonzero gravity, but zero density difference." << std::endl;
}
}
const double *grav = use_gravity ? &gravity[0] : 0;
// Initialising src
std::vector<double> porevol;
computePorevolume(*grid->c_grid(), props->porosity(), porevol);
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 {
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);
// Pressure solver.
Opm::IncompTpfa psolver(*grid->c_grid(), *props, 0, linsolver,
0.0, 0.0, 0,
grav, wells->c_wells(), src, bcs.c_bcs());
// Choice of tof solver.
bool use_dg = param.getDefault("use_dg", false);
int dg_degree = -1;
if (use_dg) {
dg_degree = param.getDefault("dg_degree", 0);
}
// 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");
}
// Init wells.
Opm::WellState well_state;
well_state.init(wells->c_wells(), state);
// Main solvers.
Opm::time::StopWatch pressure_timer;
double ptime = 0.0;
Opm::time::StopWatch transport_timer;
double ttime = 0.0;
Opm::time::StopWatch total_timer;
total_timer.start();
std::cout << "\n\n================ Starting main solvers ===============" << std::endl;
// Solve pressure.
pressure_timer.start();
psolver.solve(1.0, state, well_state);
pressure_timer.stop();
double pt = pressure_timer.secsSinceStart();
std::cout << "Pressure solver took: " << pt << " seconds." << std::endl;
ptime += pt;
// Process transport sources (to include bdy terms and well flows).
std::vector<double> transport_src;
Opm::computeTransportSource(*grid->c_grid(), src, state.faceflux(), 1.0,
wells->c_wells(), well_state.perfRates(), transport_src);
// Solve time-of-flight.
std::vector<double> tof;
if (use_dg) {
bool use_cvi = param.getDefault("use_cvi", false);
Opm::TransportModelTracerTofDiscGal tofsolver(*grid->c_grid(), use_cvi);
transport_timer.start();
tofsolver.solveTof(&state.faceflux()[0], &porevol[0], &transport_src[0], dg_degree, tof);
transport_timer.stop();
} else {
Opm::TransportModelTracerTof tofsolver(*grid->c_grid());
transport_timer.start();
tofsolver.solveTof(&state.faceflux()[0], &porevol[0], &transport_src[0], tof);
transport_timer.stop();
}
double tt = transport_timer.secsSinceStart();
std::cout << "Transport solver took: " << tt << " seconds." << std::endl;
ttime += tt;
total_timer.stop();
// Output.
if (output) {
std::string tof_filename = output_dir + "/tof.txt";
std::ofstream tof_stream(tof_filename.c_str());
std::copy(tof.begin(), tof.end(), std::ostream_iterator<double>(tof_stream, "\n"));
}
std::cout << "\n\n================ End of simulation ===============\n"
<< "Total time taken: " << total_timer.secsSinceStart()
<< "\n Pressure time: " << ptime
<< "\n Transport time: " << ttime << std::endl;
}

252
examples/import_rewrite.cpp Normal file
View File

@ -0,0 +1,252 @@
#if HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H
#include <opm/core/eclipse/EclipseGridParser.hpp>
#include <boost/filesystem/convenience.hpp>
#ifdef HAVE_ERT
#include <opm/core/utility/writeECLData.hpp>
#include <util.h>
#include <ecl_util.h>
#include <ecl_kw.h>
#include <ecl_endian_flip.h>
#include <fortio.h>
#endif
/*
Small utility to read through an ECLIPSE input deck and replace
occurences of (large) numerical fields like COORD and ZCORN with
IMPORT statements of a binary versions of the relevant keywords. The
program will follow INCLUDE statements.
Usage: import_rewrite eclipse_case.data
*/
/*
The numerical keywords in the ECLIPSE datafile like e.g. PORO and
COORD are not annoted with type information; however when read and
written in binary form they are of type float. If the updated
datafile should be used with ECLIPSE these float values must be
exported as float; this is achieved by setting the outputFloatType
variable to ECL_FLOAT_TYPE.
In OPM all numerical fields are treated as double, hence if the OPM
EclipseParser meets an import of a float keyword it will be
converted to double. If the output from this little utility will
only be used from OPM the output can be saved as double directly by
setting the outputFloatType to ECL_DOUBLE_TYPE.
*/
const ecl_type_enum outputFloatType = ECL_DOUBLE_TYPE;
/*
Only keywords which have more >= minImportSize elements are
converted to binary form. This is to avoid convertion short keywords
like MAPAXES and TABDIMS.
*/
const int minImportSize = 10;
using namespace Opm;
static void skipKeyword( std::ifstream& is) {
std::string keyword;
EclipseGridParser::readKeyword( is , keyword );
while (true) {
std::ios::pos_type pos = is.tellg();
if (EclipseGridParser::readKeyword( is , keyword )) {
is.seekg( pos ); // Repos to start of keyword for next read.
break;
} else
is >> ignoreLine;
if (!is.good()) {
is.clear();
is.seekg( 0 , std::ios::end );
break;
}
}
}
static void copyKeyword( std::ifstream& is , std::ofstream& os) {
std::ios::pos_type start_pos = is.tellg();
skipKeyword( is );
{
std::ios::pos_type end_pos = is.tellg();
long length = end_pos - start_pos;
{
char * buffer = new char[length];
{
is.seekg( start_pos );
is.read( buffer , length );
}
os.write( buffer , length );
delete[] buffer;
}
}
}
static ecl_kw_type * loadFromcstdio( const std::string& filename , std::ios::pos_type& offset , ecl_type_enum ecl_type) {
ecl_kw_type * ecl_kw;
FILE * cstream = util_fopen( filename.c_str() , "r");
fseek( cstream , offset , SEEK_SET);
ecl_kw = ecl_kw_fscanf_alloc_current_grdecl( cstream , ecl_type );
offset = ftell( cstream );
fclose( cstream );
return ecl_kw;
}
static bool convertKeyword( const std::string& inputFile , const std::string& outputPath , std::ifstream& is , FieldType fieldType , std::ofstream& os ) {
bool convert = true;
ecl_type_enum ecl_type;
if (fieldType == Integer)
ecl_type = ECL_INT_TYPE;
else
ecl_type = outputFloatType;
{
std::ios::pos_type inputPos = is.tellg();
ecl_kw_type * ecl_kw = loadFromcstdio( inputFile , inputPos , ecl_type );
if (ecl_kw_get_size( ecl_kw ) >= minImportSize) {
{
std::string outputFile = outputPath + "/" + ecl_kw_get_header( ecl_kw );
fortio_type * fortio = fortio_open_writer( outputFile.c_str() , false , ECL_ENDIAN_FLIP );
ecl_kw_fwrite( ecl_kw , fortio );
fortio_fclose( fortio );
os << "IMPORT" << std::endl << " '" << outputFile << "' /" << std::endl << std::endl;
}
is.seekg( inputPos );
} else {
copyKeyword( is , os );
convert = false;
}
ecl_kw_free( ecl_kw );
}
return convert;
}
static bool parseFile(const std::string& inputFile, std::string& outputFile, const std::string& indent = "") {
bool updateFile = false;
std::cout << indent << "Parsing " << inputFile << "\n";
{
std::ifstream is(inputFile.c_str());
if (is) {
std::ofstream os;
std::string keyword;
std::string path;
{
boost::filesystem::path inputPath(inputFile);
path = inputPath.parent_path().string();
}
{
std::string basename;
std::string extension;
outputFile = inputFile;
size_t ext_pos = inputFile.rfind(".");
if (ext_pos == std::string::npos) {
basename = outputFile.substr();
extension = "";
} else {
basename = outputFile.substr(0,ext_pos);
extension = outputFile.substr(ext_pos);
}
outputFile = basename + "_import" + extension;
}
os.open( outputFile.c_str() );
while(is.good()) {
is >> ignoreWhitespace;
{
std::ios::pos_type start_pos = is.tellg();
if (EclipseGridParser::readKeyword( is , keyword )) {
FieldType fieldType = EclipseGridParser::classifyKeyword( keyword );
switch (fieldType) {
case(Integer):
case(FloatingPoint):
{
is.seekg( start_pos );
if (convertKeyword( inputFile , path , is , fieldType , os )) {
std::cout << indent + " " << "Writing binary file: " << path << "/" << keyword << std::endl;
updateFile = true;
}
break;
}
case(Include):
{
std::string includeFile = readString(is);
if (!path.empty()) {
includeFile = path + '/' + includeFile;
}
{
std::string __outputFile;
bool updateInclude = parseFile( includeFile , __outputFile , indent + " ");
if (updateInclude) {
is.seekg( start_pos );
skipKeyword( is );
os << "INCLUDE" << std::endl << " '" << __outputFile << "' /" << std::endl << std::endl;
}
updateFile |= updateInclude;
}
break;
}
default:
{
is.seekg( start_pos );
copyKeyword( is , os);
break;
}
}
} else
is >> ignoreLine; // Not at a valid keyword
}
}
os.close();
is.close();
if (updateFile)
std::cout << indent << "Written updated include file: " << outputFile << std::endl;
else
remove( outputFile.c_str() );
} else
std::cerr << indent << "** WARNING: Failed to open include file: " << inputFile << " for reading **" << std::endl;
}
return updateFile;
}
int
main(int argc, char** argv)
{
if (argc != 2)
THROW("Need the name of ECLIPSE file on command line");
{
std::string outputFile;
parseFile(argv[1] , outputFile);
}
}

View File

@ -32,7 +32,6 @@
#include <opm/core/simulator/SimulatorReport.hpp> #include <opm/core/simulator/SimulatorReport.hpp>
#include <opm/core/simulator/SimulatorTimer.hpp> #include <opm/core/simulator/SimulatorTimer.hpp>
#include <opm/core/utility/miscUtilities.hpp> #include <opm/core/utility/miscUtilities.hpp>
#include <opm/core/utility/miscUtilities.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp> #include <opm/core/utility/parameters/ParameterGroup.hpp>
#include <opm/core/fluid/BlackoilPropertiesBasic.hpp> #include <opm/core/fluid/BlackoilPropertiesBasic.hpp>
@ -94,7 +93,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
props.reset(new BlackoilPropertiesFromDeck(*deck, *grid->c_grid())); props.reset(new BlackoilPropertiesFromDeck(*deck, *grid->c_grid(), param));
// 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.

View File

@ -175,7 +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
props.reset(new Opm::BlackoilPropertiesFromDeck(deck, *grid->c_grid())); props.reset(new BlackoilPropertiesFromDeck(deck, *grid->c_grid(), param));
// 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);
@ -408,7 +408,7 @@ main(int argc, char** argv)
state.saturation(), state.surfacevol()); 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) {
reorder_model.solveGravity(columns, &state.pressure()[0], &initial_porevol[0], reorder_model.solveGravity(columns,
stepsize, state.saturation(), state.surfacevol()); stepsize, state.saturation(), state.surfacevol());
} }
} }

View File

@ -94,14 +94,19 @@
#include <numeric> #include <numeric>
#ifdef HAVE_ERT
#include <opm/core/utility/writeECLData.hpp>
#endif
static void outputState(const UnstructuredGrid& grid, static void outputState(const UnstructuredGrid& grid,
const Opm::TwophaseState& state, const Opm::TwophaseState& state,
const int step, const Opm::SimulatorTimer& simtimer,
const std::string& output_dir) const std::string& output_dir)
{ {
// Write data in VTK format. // Write data in VTK format.
int step = simtimer.currentStepNum();
std::ostringstream vtkfilename; std::ostringstream vtkfilename;
vtkfilename << output_dir << "/output-" << std::setw(3) << std::setfill('0') << step << ".vtu"; vtkfilename << output_dir << "/output-" << std::setw(3) << std::setfill('0') << step << ".vtu";
std::ofstream vtkfile(vtkfilename.str().c_str()); std::ofstream vtkfile(vtkfilename.str().c_str());
@ -115,6 +120,9 @@ static void outputState(const UnstructuredGrid& grid,
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);
#ifdef HAVE_ERT
Opm::writeECLData(grid , dm , simtimer , output_dir , "OPM" );
#endif
// 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) {
@ -530,7 +538,7 @@ main(int argc, char** argv)
// Report timestep and (optionally) write state to disk. // Report timestep and (optionally) write state to disk.
simtimer.report(std::cout); simtimer.report(std::cout);
if (output && (simtimer.currentStepNum() % output_interval == 0)) { if (output && (simtimer.currentStepNum() % output_interval == 0)) {
outputState(*grid->c_grid(), state, simtimer.currentStepNum(), output_dir); outputState(*grid->c_grid(), state, simtimer , output_dir);
} }
// Solve pressure. // Solve pressure.
@ -699,7 +707,7 @@ main(int argc, char** argv)
<< "\n Transport time: " << ttime << std::endl; << "\n Transport time: " << ttime << std::endl;
if (output) { if (output) {
outputState(*grid->c_grid(), state, simtimer.currentStepNum(), output_dir); outputState(*grid->c_grid(), state, simtimer, output_dir);
outputWaterCut(watercut, output_dir); outputWaterCut(watercut, output_dir);
if (wells->c_wells()) { if (wells->c_wells()) {
outputWellReport(wellreport, output_dir); outputWellReport(wellreport, output_dir);

View File

@ -0,0 +1,11 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: @PACKAGE_NAME@
Description: @PACKAGE_STRING@
Version: @PACKAGE_VERSION@
URL: @PACKAGE_URL@
Libs: -L${libdir} -l@PACKAGE@
Cflags: -I${includedir}

View File

@ -98,6 +98,11 @@ if test "x$BLAS_LIBS" != x; then
fi fi
fi fi
# don't probe if explicitly defined; by bailing out here if the
# argument is set, we guard against typo, incompatible libs. etc.
# being inadvertedly overrided by another (random) implementation
if test "x$with_blas" == x; then
# BLAS linked to by default? (happens on some supercomputers) # BLAS linked to by default? (happens on some supercomputers)
if test $ax_blas_ok = no; then if test $ax_blas_ok = no; then
save_LIBS="$LIBS"; LIBS="$LIBS" save_LIBS="$LIBS"; LIBS="$LIBS"
@ -186,6 +191,9 @@ if test $ax_blas_ok = no; then
AC_CHECK_LIB(blas, $sgemm, [ax_blas_ok=yes; BLAS_LIBS="-lblas"]) AC_CHECK_LIB(blas, $sgemm, [ax_blas_ok=yes; BLAS_LIBS="-lblas"])
fi fi
# end of guard against automatic overriding explicit definitions
fi
AC_SUBST(BLAS_LIBS) AC_SUBST(BLAS_LIBS)
LIBS="$ax_blas_save_LIBS" LIBS="$ax_blas_save_LIBS"

View File

@ -101,6 +101,11 @@ if test "x$LAPACK_LIBS" != x; then
fi fi
fi fi
# don't probe if explicitly defined; by bailing out here if the
# argument is set, we guard against typo, incompatible libs. etc.
# being inadvertedly overrided by another (random) implementation
if test "x$with_lapack" = x; then
# LAPACK linked to by default? (is sometimes included in BLAS lib) # LAPACK linked to by default? (is sometimes included in BLAS lib)
if test $ax_lapack_ok = no; then if test $ax_lapack_ok = no; then
save_LIBS="$LIBS"; LIBS="$LIBS $BLAS_LIBS $FLIBS" save_LIBS="$LIBS"; LIBS="$LIBS $BLAS_LIBS $FLIBS"
@ -118,6 +123,9 @@ for lapack in lapack lapack_rs6k; do
fi fi
done done
# end of guard against automatic overriding explicit definitions
fi
AC_SUBST(LAPACK_LIBS) AC_SUBST(LAPACK_LIBS)
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:

66
m4/cxx0x_compiler.m4 Normal file
View File

@ -0,0 +1,66 @@
# whether compiler accepts -std=c++11 or -std=c++0x
# can be disabled by --disable-gxx0xcheck
AC_DEFUN([GXX0X],[
save_CXX="$CXX"
# put this check first, so we get disable C++11 if C++0x is
AC_ARG_ENABLE(gxx0xcheck,
AC_HELP_STRING([--disable-gxx0xcheck],
[try flag -std=c++0x to enable C++0x features [[default=yes]]]),
[gxx0xcheck=$enableval],
[gxx0xcheck=yes])
AC_ARG_ENABLE(gxx11check,
AC_HELP_STRING([--disable-gxx11check],
[try flag -std=c++11 to enable C++11 features [[default=yes]]]),
[gxx11check=$enableval],
[gxx11check=yes])
# try flag -std=c++11
AC_CACHE_CHECK([whether $CXX accepts -std=c++11], dune_cv_gplusplus_accepts_cplusplus11, [
AC_REQUIRE([AC_PROG_CXX])
if test "x$GXX" = xyes && test "x$gxx11check" = xyes && test "x$gxx0xcheck" = xyes ; then
AC_LANG_PUSH([C++])
CXX="$CXX -std=c++11"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
#include <iostream>
#include <array>
],)],
[dune_cv_gplusplus_accepts_cplusplus11=yes],
[dune_cv_gplusplus_accepts_cplusplus11=no])
AC_LANG_POP([C++])
else
dune_cv_gplusplus_accepts_cplusplus11=disabled
fi
])
if test "x$dune_cv_gplusplus_accepts_cplusplus11" = "xyes" ; then
CXX="$save_CXX -std=c++11"
CXXCPP="$CXXCPP -std=c++11"
else
CXX="$save_CXX"
# try flag -std=c++0x instead
AC_CACHE_CHECK([whether $CXX accepts -std=c++0x], dune_cv_gplusplus_accepts_cplusplus0x, [
AC_REQUIRE([AC_PROG_CXX])
if test "x$GXX" = xyes && test "x$gxx0xcheck" = xyes; then
AC_LANG_PUSH([C++])
CXX="$CXX -std=c++0x"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
#include <iostream>
#include <array>
],)],
[dune_cv_gplusplus_accepts_cplusplus0x=yes],
[dune_cv_gplusplus_accepts_cplusplus0x=no])
AC_LANG_POP([C++])
else
dune_cv_gplusplus_accepts_cplusplus0x=disabled
fi
])
if test "x$dune_cv_gplusplus_accepts_cplusplus0x" = "xyes" ; then
CXX="$save_CXX -std=c++0x"
CXXCPP="$CXXCPP -std=c++0x"
else
CXX="$save_CXX"
fi
fi
])

17
m4/cxx0x_nullptr.m4 Normal file
View File

@ -0,0 +1,17 @@
AC_DEFUN([NULLPTR_CHECK],[
AC_CACHE_CHECK([whether nullptr is supported], dune_cv_nullptr_support, [
AC_REQUIRE([AC_PROG_CXX])
AC_REQUIRE([GXX0X])
AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[
char* ch = nullptr;
if(ch!=nullptr) { ; }
])],
[dune_cv_nullptr_support=yes],
[dune_cv_nullptr_support=no])
AC_LANG_POP
])
if test "x$dune_cv_nullptr_support" = xyes; then
AC_DEFINE(HAVE_NULLPTR, 1, [Define to 1 if nullptr is supported])
fi
])

14
m4/cxx0x_static_assert.m4 Normal file
View File

@ -0,0 +1,14 @@
AC_DEFUN([STATIC_ASSERT_CHECK],[
AC_CACHE_CHECK([whether static_assert is supported], dune_cv_static_assert_support, [
AC_REQUIRE([AC_PROG_CXX])
AC_REQUIRE([GXX0X])
AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[static_assert(true,"MSG")])],
[dune_cv_static_assert_support=yes],
[dune_cv_static_assert_support=no])
AC_LANG_POP
])
if test "x$dune_cv_static_assert_support" = xyes; then
AC_DEFINE(HAVE_STATIC_ASSERT, 1, [Define to 1 if static_assert is supported])
fi
])

67
m4/ert.m4 Normal file
View File

@ -0,0 +1,67 @@
AC_DEFUN([_ERT_SOURCE_TEXT],
[
AC_LANG_PROGRAM(
[[
#include <stddef.h>
#include <ecl_util.h>
]],dnl
[[
int sz;
sz = ecl_util_get_sizeof_ctype(ECL_INT_TYPE);
]])[]dnl
])[]dnl
# ----------------------------------------------------------------------
AC_DEFUN([ERT],
[
AC_ARG_WITH([ert],
[AS_HELP_STRING([--with-ert=<root>], [Use ERT libraries])],
[], [with_ert=no])
use_ert=no
AS_IF([test x"${with_ert}" != x"no"],
[
_ert_LDFLAGS_SAVE="${LDFLAGS}"
_ert_LIBS_SAVE="${LIBS}"
_ert_CPPFLAGS_SAVE="${CPPFLAGS}"
_ert_CFLAGS_SAVE="${CFLAGS}"
ERT_CPPFLAGS=
ERT_LDFLAGS=
ERT_LIBS="-lecl -lgeometry -lert_util -lpthread -lz -lgomp"
AS_IF([test x"${with_ert}" != x"yes"],
[ERT_LDFLAGS="-L${with_ert}/lib"
ERT_CPPFLAGS="-I${with_ert}/include"], [:])[]dnl
CFLAGS="-std=gnu99"
CPPFLAGS="${ERT_CPPFLAGS} ${CPPFLAGS}"
LDFLAGS="${ERT_LDFLAGS} ${LDFLAGS}"
LIBS="${ERT_LIBS} ${LIBS}"
AC_LINK_IFELSE([_ERT_SOURCE_TEXT],
[use_ert=yes], [use_ert=no])
LIBS="${_ert_LIBS_SAVE}"
CPPFLAGS="${_ert_CPPFLAGS_SAVE}"
LDFLAGS="${_ert_LDFLAGS_SAVE}"
CFLAGS="${_ert_CFLAGS_SAVE}"
AS_IF([test x"${use_ert}" = x"yes"],
[AC_SUBST([ERT_CPPFLAGS])
AC_SUBST([ERT_LDFLAGS])
AC_SUBST([ERT_LIBS])
AC_DEFINE([HAVE_ERT], [1],
[Are the ERT libraries available for reading and writing ECLIPSE files.])],dnl
[:])[]dnl
], [:])[]dnl
AM_CONDITIONAL([HAVE_ERT], [test x"${use_ert}" != x"no"])
# AC_MSG_ERROR(
# [**** ERT_CPPFLAGS = ${ERT_CPPFLAGS} ****
# **** ERT_LDFLAGS = ${ERT_LDFLAGS} ****
# **** ERT_LIBS = ${ERT_LIBS} ****
# ])
])

View File

@ -5,6 +5,11 @@ dnl -*- autoconf -*-
AC_DEFUN([OPM_CORE_CHECKS], AC_DEFUN([OPM_CORE_CHECKS],
[ [
# Language features
GXX0X
STATIC_ASSERT_CHECK
NULLPTR_CHECK
# Checks for libraries. # Checks for libraries.
# Bring in numerics support (standard library component) # Bring in numerics support (standard library component)
@ -17,8 +22,8 @@ AX_BOOST_SYSTEM
AX_BOOST_DATE_TIME AX_BOOST_DATE_TIME
AX_BOOST_FILESYSTEM AX_BOOST_FILESYSTEM
AX_BOOST_UNIT_TEST_FRAMEWORK AX_BOOST_UNIT_TEST_FRAMEWORK
AX_DUNE_ISTL AX_DUNE_ISTL
OPM_PATH_SUPERLU
OPM_AGMG OPM_AGMG
# Checks for header files. # Checks for header files.

View File

@ -1,4 +1,7 @@
AC_DEFUN([OPM_LAPACK], AC_DEFUN([OPM_LAPACK],
[AC_REQUIRE([AC_F77_WRAPPERS])dnl [AC_REQUIRE([AC_F77_WRAPPERS])dnl
AC_REQUIRE([AX_LAPACK])dnl AC_REQUIRE([AX_LAPACK])dnl
if test x"$ax_lapack_ok" != xyes; then
AC_MSG_ERROR([BLAS/LAPACK required, but not found.])
fi
])[]dnl ])[]dnl

334
m4/opm_superlu.m4 Normal file
View File

@ -0,0 +1,334 @@
## -*- autoconf -*-
# $Id: superlu.m4 5908 2010-02-23 16:46:05Z joe $
# searches for SuperLU headers and libs
# _slu_lib_path(SUPERLU_ROOT, HEADER)
#
# Try to find the subpath unter SUPERLU_ROOT containing HEADER. Try
# SUPERLU_ROOT/"include/superlu", SUPERLU_ROOT/"include", and
# SUPERLU_ROOT/"SRC", in that order. Set the subpath for the library to
# "lib". If HEADER was found in SUPERLU_ROOT/"SRC", check whether
# SUPERLU_ROOT/"lib" is a directory, and set the subpath for the library to
# the empty string "" if it isn't.
#
# Shell variables:
# my_include_path
# The subpath HEADER was found in: "include/superlu", "include", or
# "SRC". Contents is only meaningful for my_slu_found=yes.
# my_lib_path
# The subpath for the library: "lib" or "". Contents is only meaningful
# for my_slu_found=yes.
# my_slu_found
# Whether HEADER was found at all. Either "yes" or "no".
AC_DEFUN([_slu_lib_path],
[
my_include_path=include/superlu
my_lib_path=lib
my_slu_found=yes
if test ! -f "$1/$my_include_path/$2" ; then
#Try to find headers under superlu
my_include_path=include
if test ! -f "$1/$my_include_path/$2" ; then
my_include_path=SRC
if test ! -f "$1/$my_include_path/$2"; then
my_slu_found=no
else
if ! test -d "$1/$my_lib_path"; then
my_lib_path=""
fi
fi
fi
fi
]
)
# _slu_search_versions(SUPERLU_ROOT)
#
# Search for either "slu_ddefs.h" or "dsp_defs.h" using _slu_lib_path().
#
# Shell variables:
# my_slu_header
# The name of the header that was found: first of "slu_ddefs.h" or
# "dsp_defs.h". Contents is only meaningful for my_slu_found=yes.
# my_include_path
# The subpath the header was found in: "include/superlu", "include", or
# "SRC". Contents is only meaningful for my_slu_found=yes.
# my_lib_path
# The subpath for the library: "lib" or "". Contents is only meaningful
# for my_slu_found=yes.
# my_slu_found
# Whether any of the headers. Either "yes" or "no".
AC_DEFUN([_slu_search_versions],
[
my_slu_header=slu_ddefs.h
_slu_lib_path($1, $my_slu_header)
if test "$my_slu_found" != "yes"; then
my_slu_header="dsp_defs.h"
_slu_lib_path($1, $my_slu_header)
fi
]
)
# _slu_search_default()
#
# Search for SuperLU in the default locations "/usr" and "/usr/local".
#
# Shell variables:
# with_superlu
# Root of the SuperLU installation: first of "/usr" and "/usr/local".
# Contents is only meaningful for my_slu_found=yes.
# For other output variables see documentation of _slu_search_versions().
AC_DEFUN([_slu_search_default],
[
with_superlu=/usr
_slu_search_versions($with_superlu)
if test "$my_slu_found" = "no"; then
with_superlu=/usr/local
_slu_search_versions($with_superlu)
fi
]
)
# OPM_PATH_SUPERLU()
#
# REQUIRES: AC_PROG_CC, AX_BLAS
#
# Shell variables:
# with_superlu
# "no", "yes (version 4.3 or newer)", "yes (version 4.2 or older, post 2005)" or "yes (pre 2005)"
# direct_SUPERLU_CPPFLAGS
# direct_SUPERLU_LIBS
# CPPFLAGS and LIBS necessary to link against SuperLU. This variable
# contains no indirect references and is suitable for use inside
# configure. Guaranteed empty if SuperLU was not found.
# SUPERLU_CPPFLAGS
# SUPERLU_LIBS
# CPPFLAGS and LIBS necessary to link against SuperLU. This variable may
# contain indirect references and is suitable for use inside makefiles.
# Guaranteed empty if SuperLU was not found.
# HAVE_SUPERLU
# "0" or "1" depending on whether SuperLU was found.
#
# Substitutions:
# SUPERLU_LIBS
# SUPERLU_CPPFLAGS
# Substitutes the values of the corresponding shell variables.
# ALL_PKG_LIBS
# ALL_PKG_CPPFLAGS
# Adds references to SuperLU's substitutions.
#
# Defines:
# HAVE_SUPERLU
# ENABLE_SUPERLU or undefined. Whether SuperLU was found. The correct
# way to check this is "#if HAVE_SUPERLU": This way SuperLU features will
# be disabled unless $(SUPERLU_CPPFLAGS) was given when compiling.
# SUPERLU_POST_2005_VERSION
# 1 or undefined. A post-2005 version of SuperLU uses the header
# "slu_ddefs.h" while earlier versions use "dsp_defs.h".
# SUPERLU_MIN_VERSION_4_3
# 1 or undefined. SuperLU version 4.3 or newer uses the symbol
# "SLU_DOUBLE" while earlier versions use "DOUBLE".
# HAVE_MEM_USAGE_T_EXPANSIONS
# 1 or undefined. Whether "mem_usage_t.expansions" was found in
# "slu_ddefs.h" or "dsp_defs.h" as apropriate.
#
# Conditionals:
# SUPERLU
AC_DEFUN([OPM_PATH_SUPERLU],[
AC_REQUIRE([AC_PROG_CC])
# we need this for FLIBS
AC_REQUIRE([AC_F77_LIBRARY_LDFLAGS])
AC_REQUIRE([AX_BLAS])
#
# User hints ...
#
my_lib_path=""
my_include_path=""
AC_ARG_WITH([superlu],
[AC_HELP_STRING([--with-superlu],[user defined path to SuperLU library])],
[dnl
if test x"$withval" != xno ; then
# get absolute path
with_superlu=`eval cd $withval > /dev/null 2>&1 && pwd`
if test x"$withval" = xyes; then
# Search in default locations
_slu_search_default
else
# Search for the headers in the specified location
_slu_search_versions(["$with_superlu"])
fi
fi
], [dnl
# Search in default locations
_slu_search_default
])
AC_ARG_WITH([superlu-lib],
[AC_HELP_STRING([--with-superlu-lib],
[The name of the static SuperLU library to link to. By default
the static library with the name superlu.a is tried, but
only if shared linking has failed first. Giving this
options forces static linking for SuperLU.])],
[
if test x"$withval" = xno ; then
with_superlu_lib=
fi
])
AC_ARG_WITH([superlu-blaslib],
[AC_HELP_STRING([--with-superlu-blaslib],
[The name of the static blas library to link to. By default
we try to link to the systems blas library (see
--with-blas). Giving this options forces static linking
for SuperLU.])],
[
if test "$withval" = no ; then
with_superlu_blaslib=
fi
])
# store old values
ac_save_LDFLAGS="$LDFLAGS"
ac_save_CPPFLAGS="$CPPFLAGS"
ac_save_LIBS="$LIBS"
# do nothing if --without-superlu is used
if test x"$with_superlu" != x"no" ; then
# defaultpath
SUPERLU_LIB_PATH="$with_superlu/$my_lib_path"
SUPERLU_INCLUDE_PATH="$with_superlu/$my_include_path"
# set variables so that tests can use them
direct_SUPERLU_CPPFLAGS="-I$SUPERLU_INCLUDE_PATH -DENABLE_SUPERLU"
SUPERLU_CPPFLAGS="-I$SUPERLU_INCLUDE_PATH -DENABLE_SUPERLU"
CPPFLAGS="$CPPFLAGS $direct_SUPERLU_CPPFLAGS"
# check for central header
AC_CHECK_HEADER([$my_slu_header],
[HAVE_SUPERLU="1"],
[
HAVE_SUPERLU="0"
AC_MSG_WARN([$my_slu_header not found in $SUPERLU_INCLUDE_PATH with $CPPFLAGS])
])
# if header is found check for the libs
if test x$HAVE_SUPERLU = x1 ; then
HAVE_SUPERLU=0
# if neither --with-superlu-lib nor --with-superlu-blaslib was
# given, try to link dynamically or with properly names static libs
if test x"$with_superlu_lib$with_superlu_blaslib" = x; then
LDFLAGS="$ac_save_LDFLAGS -L$SUPERLU_LIB_PATH"
LIBS="$ac_save_LIBS"
AC_CHECK_LIB([superlu], [dgssvx],
[
direct_SUPERLU_LIBS="-L$SUPERLU_LIB_PATH -lsuperlu $BLAS_LIBS $FLIBS"
SUPERLU_LIBS="-L$SUPERLU_LIB_PATH -lsuperlu \${BLAS_LIBS} \${FLIBS}"
HAVE_SUPERLU="1"
], [], [$BLAS_LIBS $FLIBS])
fi
if test $HAVE_SUPERLU = 0 && test x"$with_superlu_lib" = x; then
# set the default
with_superlu_lib=superlu.a
fi
if test $HAVE_SUPERLU = 0 &&
test x"$with_superlu_blaslib" = x; then
# try system blas
LDFLAGS="$ac_save_LDFLAGS"
LIBS="$SUPERLU_LIB_PATH/$with_superlu_lib $BLAS_LIBS $FLIBS $ac_save_LIBS"
AC_CHECK_FUNC([dgssvx],
[
direct_SUPERLU_LIBS="$SUPERLU_LIB_PATH/$with_superlu_lib $BLAS_LIBS $FLIBS"
SUPERLU_LIBS="$SUPERLU_LIB_PATH/$with_superlu_lib \${BLAS_LIBS} \${FLIBS}"
HAVE_SUPERLU="1"
])
fi
# No default for with_superlu_blaslib
if test $HAVE_SUPERLU = 0 &&
test x"$with_superlu_blaslib" != x; then
# try internal blas
LDFLAGS="$ac_save_LDFLAGS"
LIBS="$SUPERLU_LIB_PATH/$with_superlu_lib $SUPERLU_LIB_PATH/$with_superlu_blaslib $FLIBS $ac_save_LIBS"
AC_CHECK_FUNC([dgssvx],
[
direct_SUPERLU_LIBS="$SUPERLU_LIB_PATH/$with_superlu_lib $SUPERLU_LIB_PATH/$with_superlu_blaslib $FLIBS"
SUPERLU_LIBS="$SUPERLU_LIB_PATH/$with_superlu_lib $SUPERLU_LIB_PATH/$with_superlu_blaslib \${FLIBS}"
HAVE_SUPERLU="1"
])
fi
# test whether SuperLU version is at least 4.3
if test $HAVE_SUPERLU = "1" ; then
AC_CHECK_DECL([SLU_DOUBLE], [SUPERLU_MIN_VERSION_4_3="1"], [], [#include <$my_slu_header>])
fi
fi
else # $with_superlu = no
HAVE_SUPERLU=0
fi
# Inform the user whether SuperLU was sucessfully found
AC_MSG_CHECKING([SuperLU])
if test x$HAVE_SUPERLU = x1 ; then
if test "$my_slu_header" = "slu_ddefs.h"; then
if test x$SUPERLU_MIN_VERSION_4_3 = x1 ; then
with_superlu="yes (version 4.3 or newer)"
else
with_superlu="yes (version 4.2 or older, post 2005)"
fi
else
with_superlu="yes (pre 2005)"
fi
else
with_superlu="no"
fi
AC_MSG_RESULT([$with_superlu])
# check for optional member
if test $HAVE_SUPERLU = 1 ; then
if test "$my_slu_header" = "slu_ddefs.h"; then
AC_CHECK_MEMBERS([mem_usage_t.expansions],[],[],[#include "slu_ddefs.h"])
else
AC_CHECK_MEMBERS([mem_usage_t.expansions],[],[],[#include "dsp_defs.h"])
fi
fi
# substitute variables
if test x$HAVE_SUPERLU = x0 ; then
SUPERLU_LIBS=
SUPERLU_CPPFLAGS=
fi
AC_SUBST([SUPERLU_LIBS])
AC_SUBST([SUPERLU_CPPFLAGS])
#DUNE_ADD_ALL_PKG([SUPERLU], [\$(SUPERLU_CPPFLAGS)], [], [\$(SUPERLU_LIBS)])
# tell automake
AM_CONDITIONAL(SUPERLU, test x$HAVE_SUPERLU = x1)
# tell the preprocessor
if test x$HAVE_SUPERLU = x1 ; then
AC_DEFINE([HAVE_SUPERLU], [ENABLE_SUPERLU], [Define to ENABLE_SUPERLU if SUPERLU is found])
if test "$my_slu_header" = "slu_ddefs.h"; then
AC_DEFINE([SUPERLU_POST_2005_VERSION], 1, [define to 1 if there is a header slu_ddefs.h in SuperLU])
if test x$SUPERLU_MIN_VERSION_4_3 = x1 ; then
AC_DEFINE([SUPERLU_MIN_VERSION_4_3], 1, [define to 1 if there SLU_DOUBLE imported by header slu_ddefs.h from SuperLU])
fi
fi
fi
# summary
#DUNE_ADD_SUMMARY_ENTRY([SuperLU],[$with_superlu])
# restore variables
LDFLAGS="$ac_save_LDFLAGS"
CPPFLAGS="$ac_save_CPPFLAGS"
LIBS="$ac_save_LIBS"
])

18
opm-core.pc.in Normal file
View File

@ -0,0 +1,18 @@
# This is the configuration for local builds. Use this by putting the
# compilation output path (the directory in which you ran ./configure)
# into the environment variable PKG_CONFIG_PATH. This will enable you
# to use pkg-config in your code while making changes to opm-core.
# This is NOT the file that is installed in the system directories when
# you do `make install`. That is the one in lib/pkgconfig. However, if
# you make changes here, you should consider that one as well.
libdir=@abs_top_builddir@/lib/.libs
includedir=@abs_top_srcdir@
Name: @PACKAGE_NAME@
Description: @PACKAGE_STRING@
Version: @PACKAGE_VERSION@
URL: @PACKAGE_URL@
Libs: -L${libdir} -l@PACKAGE@
Cflags: -I${includedir}

View File

@ -37,7 +37,8 @@ namespace Opm
{ {
// We accept two different ways to specify the grid. // We accept two different ways to specify the grid.
// 1. Corner point format. // 1. Corner point format.
// Requires ZCORN, COORDS, DIMENS or SPECGRID, optionally ACTNUM. // Requires ZCORN, COORDS, DIMENS or SPECGRID, optionally
// ACTNUM, optionally MAPAXES.
// For this format, we will verify that DXV, DYV, DZV, // For this format, we will verify that DXV, DYV, DZV,
// DEPTHZ and TOPS are not present. // DEPTHZ and TOPS are not present.
// 2. Tensor grid format. // 2. Tensor grid format.
@ -119,29 +120,8 @@ namespace Opm
void GridManager::initFromDeckCornerpoint(const Opm::EclipseGridParser& deck) void GridManager::initFromDeckCornerpoint(const Opm::EclipseGridParser& deck)
{ {
// Extract data from deck. // Extract data from deck.
const std::vector<double>& zcorn = deck.getFloatingPointValue("ZCORN");
const std::vector<double>& coord = deck.getFloatingPointValue("COORD");
const int* actnum = 0;
if (deck.hasField("ACTNUM")) {
actnum = &(deck.getIntegerValue("ACTNUM")[0]);
}
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.");
}
// Collect in input struct for preprocessing. // Collect in input struct for preprocessing.
struct grdecl grdecl; struct grdecl grdecl = deck.get_grdecl();
grdecl.zcorn = &zcorn[0];
grdecl.coord = &coord[0];
grdecl.actnum = actnum;
grdecl.dims[0] = dims[0];
grdecl.dims[1] = dims[1];
grdecl.dims[2] = dims[2];
// Process grid. // Process grid.
ug_ = create_grid_cornerpoint(&grdecl, 0.0); ug_ = create_grid_cornerpoint(&grdecl, 0.0);

View File

@ -47,8 +47,19 @@
#include <opm/core/eclipse/EclipseGridParserHelpers.hpp> #include <opm/core/eclipse/EclipseGridParserHelpers.hpp>
#include <opm/core/eclipse/SpecialEclipseFields.hpp> #include <opm/core/eclipse/SpecialEclipseFields.hpp>
#include <opm/core/utility/ErrorMacros.hpp> #include <opm/core/utility/ErrorMacros.hpp>
#include <boost/filesystem.hpp>
#include <opm/core/utility/Units.hpp> #include <opm/core/utility/Units.hpp>
#include <opm/core/grid/cpgpreprocess/preprocess.h>
#include <boost/filesystem.hpp>
#ifdef HAVE_ERT
#include <fortio.h>
#include <ecl_grid.h>
#include <ecl_kw_magic.h>
#include <ecl_kw.h>
#include <ecl_util.h>
#include <ecl_init_file.h>
#include <ecl_file.h>
#endif
using namespace std; using namespace std;
@ -86,7 +97,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("TOPS") 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]);
@ -114,7 +125,7 @@ namespace EclipseKeywords
const int num_special_fields = sizeof(special_fields) / sizeof(special_fields[0]); const int num_special_fields = sizeof(special_fields) / sizeof(special_fields[0]);
string ignore_with_data[] = string ignore_with_data[] =
{ string("MAPUNITS"), string("MAPAXES"), string("GRIDUNIT"), { string("MAPUNITS"), string("GRIDUNIT"),
string("NTG"), string("REGDIMS"), string("WELLDIMS"), string("NTG"), string("REGDIMS"), string("WELLDIMS"),
string("NSTACK"), string("SATNUM"), string("NSTACK"), string("SATNUM"),
string("RPTRST"), string("ROIP"), string("RWIP"), string("RPTRST"), string("ROIP"), string("RWIP"),
@ -141,44 +152,14 @@ namespace EclipseKeywords
string include_keywords[] = { string("INCLUDE") }; string include_keywords[] = { string("INCLUDE") };
const int num_include_keywords = sizeof(include_keywords) / sizeof(include_keywords[0]); const int num_include_keywords = sizeof(include_keywords) / sizeof(include_keywords[0]);
string import_keywords[] = { string("IMPORT") };
const int num_import_keywords = sizeof(import_keywords) / sizeof(import_keywords[0]);
} // namespace EclipseKeywords } // namespace EclipseKeywords
namespace { namespace {
enum FieldType {
Integer,
FloatingPoint,
Timestepping,
SpecialField,
IgnoreWithData,
IgnoreNoData,
Include,
Unknown
};
inline FieldType classifyKeyword(const string& keyword)
{
using namespace EclipseKeywords;
if (count(integer_fields, integer_fields + num_integer_fields, keyword)) {
return Integer;
} else if (count(floating_fields, floating_fields + num_floating_fields, keyword)) {
return FloatingPoint;
} else if (keyword == "TSTEP" || keyword == "DATES") {
return Timestepping;
} else if (count(special_fields, special_fields + num_special_fields, keyword)) {
return SpecialField;
} else if (count(ignore_with_data, ignore_with_data + num_ignore_with_data, keyword)) {
return IgnoreWithData;
} else if (count(ignore_no_data, ignore_no_data + num_ignore_no_data, keyword)) {
return IgnoreNoData;
} else if (count(include_keywords, include_keywords + num_include_keywords, keyword)) {
return Include;
} else {
return Unknown;
}
}
inline std::string upcase(const std::string& s) inline std::string upcase(const std::string& s)
{ {
std::string us(s); std::string us(s);
@ -191,71 +172,7 @@ namespace {
return us; return us;
} }
inline bool readKeyword(std::istream& is, std::string& keyword)
{
char buf[9];
int i, j;
char c;
/* Clear buf */
for (i=0; i<9; ++i) {
buf[i] = '\0';
}
/* Read first character and check if it is uppercase*/
//buf[0] = fgetc(fp);
is.get(buf[0]);
if ( !isupper( buf[0] ) ) {
is.unget();
return false; /* NOT VALID CHARACTER */
}
/* Scan as much as possible possible keyword, 8 characters long */
i = 1;
is.get(c);
while ( (is.good()) &&
(c != EOF ) &&
(!isblank(c) ) &&
(isupper(c) || isdigit(c)) &&
(c != '\n' ) &&
(c != '/' ) &&
(i < 8 )) {
buf[i++] = c;
is.get(c);
}
/* Skip rest of line */
if (c != '\n'){
is.get(c);
while ( (is.good()) &&
(c != EOF ) &&
(c != '\n' )) {
is.get(c);
}
}
if(c == '\n') {
is.unget();
}
/* Find first non-uppercase or non-digit character */
for (i=0; i<8; ++i) {
if ( !(isupper(buf[i]) || isdigit(buf[i])) ) {
break;
}
}
/* Check if remaining characters are blank */
for (j = i; j<8; ++j) {
if(!isspace(buf[j]) && buf[j] != '\0') {
return false; /* CHARACTER AFTER SPACE OR INVALID CHARACTER */
}
buf[j] = '\0';
}
keyword = std::string(buf);
std::string::size_type end = keyword.find_last_of('\0');
if(end != keyword.npos)
keyword = keyword.substr(0, end+1);
return true;
}
} // anon namespace } // anon namespace
@ -298,6 +215,98 @@ EclipseGridParser::EclipseGridParser(const string& filename, bool convert_to_SI)
} }
FieldType EclipseGridParser::classifyKeyword(const string& keyword)
{
using namespace EclipseKeywords;
if (count(integer_fields, integer_fields + num_integer_fields, keyword)) {
return Integer;
} else if (count(floating_fields, floating_fields + num_floating_fields, keyword)) {
return FloatingPoint;
} else if (keyword == "TSTEP" || keyword == "DATES") {
return Timestepping;
} else if (count(special_fields, special_fields + num_special_fields, keyword)) {
return SpecialField;
} else if (count(ignore_with_data, ignore_with_data + num_ignore_with_data, keyword)) {
return IgnoreWithData;
} else if (count(ignore_no_data, ignore_no_data + num_ignore_no_data, keyword)) {
return IgnoreNoData;
} else if (count(include_keywords, include_keywords + num_include_keywords, keyword)) {
return Include;
} else if (count(import_keywords, import_keywords + num_import_keywords, keyword)) {
return Import;
} else {
return Unknown;
}
}
bool EclipseGridParser::readKeyword(std::istream& is, std::string& keyword)
{
char buf[9];
int i, j;
char c;
/* Clear buf */
for (i=0; i<9; ++i) {
buf[i] = '\0';
}
/* Read first character and check if it is uppercase*/
//buf[0] = fgetc(fp);
is.get(buf[0]);
if ( !isupper( buf[0] ) ) {
is.unget();
return false; /* NOT VALID CHARACTER */
}
/* Scan as much as possible possible keyword, 8 characters long */
i = 1;
is.get(c);
while ( (is.good()) &&
(c != EOF ) &&
(!isblank(c) ) &&
(isupper(c) || isdigit(c)) &&
(c != '\n' ) &&
(c != '/' ) &&
(i < 8 )) {
buf[i++] = c;
is.get(c);
}
/* Skip rest of line */
if (c != '\n'){
is.get(c);
while ( (is.good()) &&
(c != EOF ) &&
(c != '\n' )) {
is.get(c);
}
}
if(c == '\n') {
is.unget();
}
/* Find first non-uppercase or non-digit character */
for (i=0; i<8; ++i) {
if ( !(isupper(buf[i]) || isdigit(buf[i])) ) {
break;
}
}
/* Check if remaining characters are blank */
for (j = i; j<8; ++j) {
if(!isspace(buf[j]) && buf[j] != '\0') {
return false; /* CHARACTER AFTER SPACE OR INVALID CHARACTER */
}
buf[j] = '\0';
}
keyword = std::string(buf);
std::string::size_type end = keyword.find_last_of('\0');
if(end != keyword.npos)
keyword = keyword.substr(0, end+1);
return true;
}
/// Read the given stream, overwriting any previous data. /// Read the given stream, overwriting any previous data.
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void EclipseGridParser::read(istream& is, bool convert_to_SI) void EclipseGridParser::read(istream& is, bool convert_to_SI)
@ -488,6 +497,14 @@ void EclipseGridParser::readImpl(istream& is)
// is >> ignoreSlashLine; // is >> ignoreSlashLine;
break; break;
} }
case Import: {
string import_filename = readString(is);
if (!directory_.empty()) {
import_filename = directory_ + '/' + import_filename;
}
getNumericErtFields(import_filename);
break;
}
case Unknown: case Unknown:
default: default:
ignored_fields_.insert(keyword); ignored_fields_.insert(keyword);
@ -542,6 +559,9 @@ void EclipseGridParser::convertToSI()
do_convert = false; // Dimensionless keywords... do_convert = false; // Dimensionless keywords...
} else if (key == "PRESSURE") { } else if (key == "PRESSURE") {
unit = units_.pressure; unit = units_.pressure;
} else if (key == "MAPAXES") {
MESSAGE("Not applying units to MAPAXES yet!");
unit = 1.0;
} else { } else {
THROW("Units for field " << key << " not specified. Cannon convert to SI."); THROW("Units for field " << key << " not specified. Cannon convert to SI.");
} }
@ -800,5 +820,311 @@ void EclipseGridParser::computeUnits()
THROW("Unknown unit family " << unit_family); THROW("Unknown unit family " << unit_family);
} }
} }
struct grdecl EclipseGridParser::get_grdecl() const {
struct grdecl grdecl;
// Extract data from deck.
const std::vector<double>& zcorn = getFloatingPointValue("ZCORN");
const std::vector<double>& coord = getFloatingPointValue("COORD");
const int* actnum = NULL;
if (hasField("ACTNUM")) {
actnum = &(getIntegerValue("ACTNUM")[0]);
}
std::vector<int> dims;
if (hasField("DIMENS")) {
dims = getIntegerValue("DIMENS");
} else if (hasField("SPECGRID")) {
dims = getSPECGRID().dimensions;
} else {
THROW("Deck must have either DIMENS or SPECGRID.");
}
// Collect in input struct for preprocessing.
grdecl.zcorn = &zcorn[0];
grdecl.coord = &coord[0];
grdecl.actnum = actnum;
grdecl.dims[0] = dims[0];
grdecl.dims[1] = dims[1];
grdecl.dims[2] = dims[2];
if (hasField("MAPAXES")) {
const std::vector<double> &mapaxes = getFloatingPointValue("MAPAXES");
grdecl.mapaxes = &mapaxes[0];
} else
grdecl.mapaxes = NULL;
return grdecl;
}
#ifdef HAVE_ERT
/*
This function will create a ecl_kw instance filled with the data
from input argument @keyword. The ecl_kw will get it's own copy of
the data.
If the input type ecl_type == ECL_INT_TYPE the function will use the
getIntegerValue() method to get the keyword data, if ecl_type ==
ECL_DOUBLE_TYPE || ECL_FLOAT_TYPE the getFloatingPointValue()
function is invoked. If ecl_type == ECL_FLOAT_TYPE the data will be
converted to when inserting into the ecl_kw.
When the ecl_kw instance is no longer needed it should be discarded
with a call to ecl_kw_free( ).
If you are asking for a non-existent field the function will return NULL
*/
ecl_kw_type * EclipseGridParser::newEclKW(const std::string &keyword , ecl_type_enum ecl_type) const {
ecl_kw_type * ecl_kw = NULL;
if (hasField(keyword)) {
if (ecl_type == ECL_INT_TYPE) {
std::vector<int> data = getIntegerValue( keyword );
ecl_kw = ecl_kw_alloc( keyword.c_str() , data.size() , ecl_type );
ecl_kw_set_memcpy_data( ecl_kw , &data[0]);
} else {
std::vector<double> data = getFloatingPointValue( keyword );
if (ecl_type == ECL_DOUBLE_TYPE) {
ecl_kw = ecl_kw_alloc( keyword.c_str() , data.size() , ecl_type );
ecl_kw_set_memcpy_data( ecl_kw , &data[0]);
} else if (ecl_type == ECL_FLOAT_TYPE) {
ecl_kw = ecl_kw_alloc( keyword.c_str() , data.size() , ecl_type );
for (std::vector<double>::size_type i=0; i < data.size(); i++)
ecl_kw_iset_float( ecl_kw , i , data[i] );
}
}
}
return ecl_kw;
}
/**
This function will extract the COORD, ZCORN, ACTNUM and optionaly
MAPAXES keywords from the eclipse deck and create an ecl_grid
instance.
When you are finished working with the ecl_grid instance it should
be disposed with ecl_grid_free( ).
*/
ecl_grid_type * EclipseGridParser::newGrid( ) {
struct grdecl grdecl = get_grdecl();
ecl_kw_type * coord_kw = newEclKW( COORD_KW , ECL_FLOAT_TYPE );
ecl_kw_type * zcorn_kw = newEclKW( ZCORN_KW , ECL_FLOAT_TYPE );
ecl_kw_type * actnum_kw = newEclKW( ACTNUM_KW , ECL_INT_TYPE );
ecl_kw_type * mapaxes_kw = NULL;
ecl_grid_type * grid ;
if (grdecl.mapaxes != NULL)
mapaxes_kw = newEclKW( MAPAXES_KW , ECL_FLOAT_TYPE );
grid = ecl_grid_alloc_GRDECL_kw( grdecl.dims[0] , grdecl.dims[1] , grdecl.dims[2] , zcorn_kw , coord_kw , actnum_kw , mapaxes_kw );
ecl_kw_free( coord_kw );
ecl_kw_free( zcorn_kw );
ecl_kw_free( actnum_kw );
if (mapaxes_kw != NULL)
ecl_kw_free( mapaxes_kw );
return grid;
}
/**
This function will save an EGRID file based on the COORD, ZCORN,
ACTNUM and optionally MAPAXES keywords included in the deck.
This function creates the EGRID file without going through a
ecl_grid instance; this is obviously somewhat faster and less
memory demanding. Alternatively you can create a ecl_grid instance
and then subsequently store that grid as an EGRID file:
{
ecl_grid_type * grid = newGRID( );
ecl_grid_fwrite_EGRID( grid , filename );
ecl_grid_free( grid );
}
*/
void EclipseGridParser::saveEGRID( const std::string & filename) {
bool endian_flip = true;//ECL_ENDIAN_FLIP;
bool fmt_file = ecl_util_fmt_file( filename.c_str() );
struct grdecl grdecl = get_grdecl();
fortio_type * fortio = fortio_open_writer( filename.c_str() , fmt_file , endian_flip );
{
float * mapaxes = NULL;
if (grdecl.mapaxes != NULL) {
mapaxes = new float[6];
for (int i=0; i < 6; i++)
mapaxes[i]= grdecl.mapaxes[i];
}
ecl_grid_fwrite_EGRID_header( grdecl.dims , mapaxes , fortio );
if (grdecl.mapaxes != NULL)
delete[] mapaxes;
}
{
ecl_kw_type * coord_kw = newEclKW( COORD_KW , ECL_FLOAT_TYPE );
ecl_kw_type * zcorn_kw = newEclKW( ZCORN_KW , ECL_FLOAT_TYPE );
ecl_kw_type * actnum_kw = newEclKW( ACTNUM_KW , ECL_INT_TYPE );
ecl_kw_type * endgrid_kw = ecl_kw_alloc( ENDGRID_KW , 0 , ECL_INT_TYPE );
ecl_kw_fwrite( coord_kw , fortio );
ecl_kw_fwrite( zcorn_kw , fortio );
ecl_kw_fwrite( actnum_kw , fortio );
ecl_kw_fwrite( endgrid_kw , fortio );
ecl_kw_free( coord_kw );
ecl_kw_free( zcorn_kw );
ecl_kw_free( actnum_kw );
ecl_kw_free( endgrid_kw );
}
fortio_fclose( fortio );
}
/**
Will query the deck for keyword @kw; and save it to the @fortio
instance if the keyword can be found.
*/
void EclipseGridParser::save_kw( fortio_type * fortio , const std::string & kw , ecl_type_enum ecl_type) {
ecl_kw_type * ecl_kw = newEclKW( kw , ecl_type );
if (ecl_kw != NULL) {
ecl_kw_fwrite( ecl_kw , fortio );
ecl_kw_free( ecl_kw );
}
}
/**
Will save an ECLIPSE INIT file to @filename. Observe that the main
focus of this function is to store grid properties like PERMX and
PORO, various tabular properties like e.g. relperm tables and
thermodynamic properties are ignored.
*/
void EclipseGridParser::saveINIT( const std::string & filename , const ecl_grid_type * ecl_grid) {
int phases = ECL_OIL_PHASE + ECL_WATER_PHASE;
bool fmt_file = ecl_util_fmt_file( filename.c_str() );
bool endian_flip = true;//ECL_ENDIAN_FLIP;
fortio_type * fortio = fortio_open_writer( filename.c_str() , fmt_file , endian_flip );
{
ecl_kw_type * poro_kw = newEclKW( PORO_KW , ECL_FLOAT_TYPE );
time_t start_date;
{
tm td_tm = to_tm( start_date_ );
start_date = mktime( &td_tm );
}
ecl_init_file_fwrite_header( fortio , ecl_grid , poro_kw , phases , start_date );
ecl_kw_free( poro_kw );
}
/* This collection of keywords is somewhat arbitrary and random. */
save_kw( fortio , "PERMX" , ECL_FLOAT_TYPE);
save_kw( fortio , "PERMY" , ECL_FLOAT_TYPE);
save_kw( fortio , "PERMZ" , ECL_FLOAT_TYPE);
save_kw( fortio , "FIPNUM" , ECL_INT_TYPE);
save_kw( fortio , "SATNUM" , ECL_INT_TYPE);
save_kw( fortio , "EQLNUM" , ECL_INT_TYPE);
fortio_fclose( fortio );
}
/**
This is the main function used to save the state of the ECLIPSE
deck in ECLIPSE format. The function will save an INIT file and an
EGRID file.
The input arguments are the output directory to store files in, and
the basename to use for the files; the function will build up a
ECLIPSE standard filename internally.
*/
void EclipseGridParser::saveEGRID_INIT( const std::string& output_dir , const std::string& basename, bool fmt_file) {
ecl_grid_type * ecl_grid = newGrid();
char * egrid_file = ecl_util_alloc_filename( output_dir.c_str() , basename.c_str() , ECL_EGRID_FILE , fmt_file , 0);
char * init_file = ecl_util_alloc_filename( output_dir.c_str() , basename.c_str() , ECL_INIT_FILE , fmt_file , 0);
ecl_grid_fwrite_EGRID( ecl_grid , egrid_file );
saveINIT( init_file , ecl_grid );
free( init_file );
free( egrid_file );
ecl_grid_free( ecl_grid );
}
#endif
// Read an imported fortio data file using Ert.
// Data stored in 'integer_field_map_' and 'floating_field_map_'.
void EclipseGridParser::getNumericErtFields(const string& filename)
{
#ifdef HAVE_ERT
// Read file
ecl_file_type * ecl_file = ecl_file_open(filename.c_str());
if (ecl_file == NULL) {
THROW("Could not open IMPORTed file " << filename);
}
const int num_kw = ecl_file_get_size(ecl_file);
std::vector<double> double_vec;
std::vector<int> int_vec;
for (int i=0; i<num_kw; ++i) {
ecl_kw_type * ecl_kw = ecl_file_iget_kw(ecl_file, i);
const char* keyword = ecl_kw_get_header(ecl_kw);
FieldType field_type = classifyKeyword(keyword);
if (field_type == Unknown) {
ignored_fields_.insert(keyword);
cout << "*** Warning: keyword " << keyword << " is unknown." << endl;
continue;
} else {
#ifdef VERBOSE
cout << "Imported keyword found: " << keyword << endl;
#endif
}
ecl_type_enum ecl_type = ecl_kw_get_type(ecl_kw);
int data_size = ecl_kw_get_size(ecl_kw);
switch(ecl_type) {
case ECL_FLOAT_TYPE : {
double_vec.resize(data_size);
ecl_kw_get_data_as_double(ecl_kw, &double_vec[0]);
floating_field_map_[keyword] = double_vec;
break;
}
case ECL_DOUBLE_TYPE : {
double_vec.resize(data_size);
ecl_kw_get_memcpy_double_data(ecl_kw, &double_vec[0]);
floating_field_map_[keyword] = double_vec;
break;
}
case ECL_INT_TYPE : {
int_vec.resize(data_size);
ecl_kw_get_memcpy_int_data(ecl_kw, &int_vec[0]);
integer_field_map_[keyword] = int_vec;
break;
}
default: {
std::cout << "Ignored non-numeric type in file: " << filename << " Keyword="
<< keyword << " Size=" << ecl_kw_get_size(ecl_kw)
<< " Type=" << ecl_util_get_type_name(ecl_kw_get_type(ecl_kw))
<< std::endl;
break;
}
}
}
ecl_file_close(ecl_file);
#else
static_cast<void>(filename); // Suppress "unused variable" warning.
THROW("Cannot use IMPORT keyword without ert library support. Reconfigure opm-core with --with-ert and recompile.");
#endif // HAVE_ERT
}
} // namespace Opm } // namespace Opm

View File

@ -45,6 +45,13 @@ along with OpenRS. If not, see <http://www.gnu.org/licenses/>.
#include <opm/core/eclipse/EclipseUnits.hpp> #include <opm/core/eclipse/EclipseUnits.hpp>
#include <opm/core/utility/Factory.hpp> #include <opm/core/utility/Factory.hpp>
#include <opm/core/grid/cornerpoint_grid.h>
#ifdef HAVE_ERT
#include <ecl_kw.h>
#include <ecl_grid.h>
#endif
namespace Opm namespace Opm
{ {
@ -64,9 +71,23 @@ namespace Opm
*/ */
class EclipseGridParser enum FieldType {
{ Integer,
public: FloatingPoint,
Timestepping,
SpecialField,
IgnoreWithData,
IgnoreNoData,
Include,
Import,
Unknown
};
class EclipseGridParser
{
public:
/// Default constructor. /// Default constructor.
EclipseGridParser(); EclipseGridParser();
/// Constructor taking an eclipse filename. Unless the second /// Constructor taking an eclipse filename. Unless the second
@ -74,6 +95,11 @@ public:
/// converted to SI units. /// converted to SI units.
explicit EclipseGridParser(const std::string& filename, bool convert_to_SI = true); explicit EclipseGridParser(const std::string& filename, bool convert_to_SI = true);
static FieldType classifyKeyword(const std::string& keyword);
static bool readKeyword(std::istream& is, std::string& keyword);
/// Read the given stream, overwriting any previous data. Unless /// Read the given stream, overwriting any previous data. Unless
/// the second argument 'convert_to_SI' is false, all fields will /// the second argument 'convert_to_SI' is false, all fields will
/// be converted to SI units. /// be converted to SI units.
@ -191,11 +217,28 @@ public:
/// The units specified by the eclipse file read. /// The units specified by the eclipse file read.
const EclipseUnits& units() const; const EclipseUnits& units() const;
struct grdecl get_grdecl() const;
#ifdef HAVE_ERT
void saveEGRID_INIT( const std::string& output_dir , const std::string& basename, bool fmt_file = false);
void saveEGRID( const std::string & filename );
void saveINIT( const std::string & filename , const ecl_grid_type * ecl_grid);
ecl_grid_type * newGrid( );
#endif
private: private:
#ifdef HAVE_ERT
ecl_kw_type * newEclKW(const std::string &keyword , ecl_type_enum ecl_type) const;
void save_kw( fortio_type * fortio , const std::string & kw , ecl_type_enum ecl_type);
#endif
SpecialFieldPtr createSpecialField(std::istream& is, const std::string& fieldname); SpecialFieldPtr createSpecialField(std::istream& is, const std::string& fieldname);
SpecialFieldPtr cloneSpecialField(const std::string& fieldname, SpecialFieldPtr cloneSpecialField(const std::string& fieldname,
const std::tr1::shared_ptr<SpecialBase> original); const std::tr1::shared_ptr<SpecialBase> original);
void readImpl(std::istream& is); void readImpl(std::istream& is);
void getNumericErtFields(const std::string& filename);
std::string directory_; std::string directory_;
@ -216,6 +259,7 @@ private:
}; };
} // namespace Opm } // namespace Opm
#endif // SINTEF_ECLIPSEGRIDPARSER_HEADER #endif // SINTEF_ECLIPSEGRIDPARSER_HEADER

View File

@ -398,10 +398,18 @@ namespace
yv.push_back(table[k][i]); yv.push_back(table[k][i]);
} }
} }
// Interpolate if (xv.empty()) {
for (int i=0; i<int(indx.size()); ++i) { // Nothing specified, the entire column is defaulted.
table[k][indx[i]] = linearInterpolationExtrap(xv, yv, x[i]); // We insert zeros.
} for (int i=0; i<int(indx.size()); ++i) {
table[k][indx[i]] = 0.0;
}
} else {
// Interpolate
for (int i=0; i<int(indx.size()); ++i) {
table[k][indx[i]] = linearInterpolationExtrap(xv, yv, x[i]);
}
}
} }
} }
} }

View File

@ -1047,7 +1047,7 @@ struct GCONINJE : public SpecialBase
gconinje_line.injector_type_ = readString(is); gconinje_line.injector_type_ = readString(is);
gconinje_line.control_mode_ = readString(is); gconinje_line.control_mode_ = readString(is);
std::vector<double> double_data(10, -1.0E20); std::vector<double> double_data(10, -1.0E20);
const int num_to_read = 10; const int num_to_read = 4;
int num_read = readDefaultedVectorData(is, double_data, num_to_read); int num_read = readDefaultedVectorData(is, double_data, num_to_read);
gconinje_line.surface_flow_max_rate_ = double_data[0]; gconinje_line.surface_flow_max_rate_ = double_data[0];
gconinje_line.resv_flow_max_rate_ = double_data[1]; gconinje_line.resv_flow_max_rate_ = double_data[1];

View File

@ -26,20 +26,20 @@ namespace Opm
{ {
BlackoilPropertiesBasic::BlackoilPropertiesBasic(const parameter::ParameterGroup& param, BlackoilPropertiesBasic::BlackoilPropertiesBasic(const parameter::ParameterGroup& param,
const int dim, const int dim,
const int num_cells) const int num_cells)
{ {
double poro = param.getDefault("porosity", 1.0); double poro = param.getDefault("porosity", 1.0);
using namespace Opm::unit; using namespace Opm::unit;
using namespace Opm::prefix; using namespace Opm::prefix;
double perm = param.getDefault("permeability", 100.0)*milli*darcy; double perm = param.getDefault("permeability", 100.0)*milli*darcy;
rock_.init(dim, num_cells, poro, perm); rock_.init(dim, num_cells, poro, perm);
pvt_.init(param); pvt_.init(param);
satprops_.init(param); satprops_.init(param);
if (pvt_.numPhases() != satprops_.numPhases()) { if (pvt_.numPhases() != satprops_.numPhases()) {
THROW("BlackoilPropertiesBasic::BlackoilPropertiesBasic() - Inconsistent number of phases in pvt data (" THROW("BlackoilPropertiesBasic::BlackoilPropertiesBasic() - 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() << ").");
} }
} }
BlackoilPropertiesBasic::~BlackoilPropertiesBasic() BlackoilPropertiesBasic::~BlackoilPropertiesBasic()
@ -90,11 +90,11 @@ namespace Opm
/// \param[out] dmudp If non-null: array of nP viscosity derivative values, /// \param[out] dmudp If non-null: array of nP viscosity derivative values,
/// array must be valid before calling. /// array must be valid before calling.
void BlackoilPropertiesBasic::viscosity(const int n, void BlackoilPropertiesBasic::viscosity(const int n,
const double* p, const double* p,
const double* z, const double* z,
const int* /*cells*/, const int* /*cells*/,
double* mu, double* mu,
double* dmudp) const double* dmudp) const
{ {
if (dmudp) { if (dmudp) {
THROW("BlackoilPropertiesBasic::viscosity() -- derivatives of viscosity not yet implemented."); THROW("BlackoilPropertiesBasic::viscosity() -- derivatives of viscosity not yet implemented.");
@ -114,16 +114,16 @@ namespace Opm
/// array must be valid before calling. The matrices are output /// array must be valid before calling. The matrices are output
/// in Fortran order. /// in Fortran order.
void BlackoilPropertiesBasic::matrix(const int n, void BlackoilPropertiesBasic::matrix(const int n,
const double* /*p*/, const double* /*p*/,
const double* /*z*/, const double* /*z*/,
const int* /*cells*/, const int* /*cells*/,
double* A, double* A,
double* dAdp) const double* dAdp) const
{ {
const int np = numPhases(); const int np = numPhases();
ASSERT(np <= 2); ASSERT(np <= 2);
double B[2]; // Must be enough since component classes do not handle more than 2. double B[2]; // Must be enough since component classes do not handle more than 2.
pvt_.B(1, 0, 0, B); pvt_.B(1, 0, 0, B);
// Compute A matrix // Compute A matrix
// #pragma omp parallel for // #pragma omp parallel for
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
@ -152,8 +152,8 @@ namespace Opm
/// of a call to the method matrix(). /// of a call to the method matrix().
/// \param[out] rho Array of nP density values, array must be valid before calling. /// \param[out] rho Array of nP density values, array must be valid before calling.
void BlackoilPropertiesBasic::density(const int n, void BlackoilPropertiesBasic::density(const int n,
const double* A, const double* A,
double* rho) const double* rho) const
{ {
const int np = numPhases(); const int np = numPhases();
const double* sdens = pvt_.surfaceDensities(); const double* sdens = pvt_.surfaceDensities();
@ -186,10 +186,10 @@ namespace Opm
/// m_{ij} = \frac{dkr_i}{ds^j}, /// m_{ij} = \frac{dkr_i}{ds^j},
/// and is output in Fortran order (m_00 m_10 m_20 m01 ...) /// and is output in Fortran order (m_00 m_10 m_20 m01 ...)
void BlackoilPropertiesBasic::relperm(const int n, void BlackoilPropertiesBasic::relperm(const int n,
const double* s, const double* s,
const int* /*cells*/, const int* /*cells*/,
double* kr, double* kr,
double* dkrds) const double* dkrds) const
{ {
satprops_.relperm(n, s, kr, dkrds); satprops_.relperm(n, s, kr, dkrds);
} }
@ -205,10 +205,10 @@ namespace Opm
/// m_{ij} = \frac{dpc_i}{ds^j}, /// m_{ij} = \frac{dpc_i}{ds^j},
/// and is output in Fortran order (m_00 m_10 m_20 m01 ...) /// and is output in Fortran order (m_00 m_10 m_20 m01 ...)
void BlackoilPropertiesBasic::capPress(const int n, void BlackoilPropertiesBasic::capPress(const int n,
const double* s, const double* s,
const int* /*cells*/, const int* /*cells*/,
double* pc, double* pc,
double* dpcds) const double* dpcds) const
{ {
satprops_.capPress(n, s, pc, dpcds); satprops_.capPress(n, s, pc, dpcds);
} }
@ -226,7 +226,7 @@ namespace Opm
double* smin, double* smin,
double* smax) const double* smax) const
{ {
satprops_.satRange(n, smin, smax); satprops_.satRange(n, smin, smax);
} }

View File

@ -35,16 +35,16 @@ namespace Opm
{ {
public: public:
/// Construct from parameters. /// Construct from parameters.
/// The following parameters are accepted (defaults): /// The following parameters are accepted (defaults):
/// num_phases (2) Must be 1 or 2. /// num_phases (2) Must be 1 or 2.
/// relperm_func ("Linear") Must be "Constant", "Linear" or "Quadratic". /// relperm_func ("Linear") Must be "Constant", "Linear" or "Quadratic".
/// rho1 [rho2, rho3] (1.0e3) Density in kg/m^3 /// rho1 [rho2, rho3] (1.0e3) Density in kg/m^3
/// mu1 [mu2, mu3] (1.0) Viscosity in cP /// mu1 [mu2, mu3] (1.0) Viscosity in cP
/// porosity (1.0) Porosity /// porosity (1.0) Porosity
/// permeability (100.0) Permeability in mD /// permeability (100.0) Permeability in mD
BlackoilPropertiesBasic(const parameter::ParameterGroup& param, BlackoilPropertiesBasic(const parameter::ParameterGroup& param,
const int dim, const int dim,
const int num_cells); const int num_cells);
/// Destructor. /// Destructor.
virtual ~BlackoilPropertiesBasic(); virtual ~BlackoilPropertiesBasic();
@ -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

@ -19,6 +19,7 @@
#include <opm/core/fluid/BlackoilPropertiesFromDeck.hpp> #include <opm/core/fluid/BlackoilPropertiesFromDeck.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp> #include <opm/core/utility/parameters/ParameterGroup.hpp>
namespace Opm namespace Opm
{ {
@ -26,15 +27,19 @@ namespace Opm
const UnstructuredGrid& grid, const UnstructuredGrid& grid,
bool init_rock) bool init_rock)
{ {
if (init_rock){ if (init_rock){
rock_.init(deck, grid); rock_.init(deck, grid);
} }
pvt_.init(deck); pvt_.init(deck, 200);
satprops_.init(deck, grid); SaturationPropsFromDeck<SatFuncSimpleUniform>* ptr
if (pvt_.numPhases() != satprops_.numPhases()) { = new SaturationPropsFromDeck<SatFuncSimpleUniform>();
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, BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck(const EclipseGridParser& deck,
@ -45,13 +50,56 @@ namespace Opm
if(init_rock){ if(init_rock){
rock_.init(deck, grid); rock_.init(deck, grid);
} }
const int samples = param.getDefault("dead_tab_size", 1025);
pvt_.init(deck, samples);
satprops_.init(deck, grid, param);
if (pvt_.numPhases() != satprops_.numPhases()) { const int pvt_samples = param.getDefault("pvt_tab_size", 200);
THROW("BlackoilPropertiesBasic::BlackoilPropertiesBasic() - Inconsistent number of phases in pvt data (" pvt_.init(deck, pvt_samples);
<< pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ").");
// 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");
if (sat_samples > 1) {
if (threephase_model == "stone2") {
SaturationPropsFromDeck<SatFuncStone2Uniform>* ptr
= new SaturationPropsFromDeck<SatFuncStone2Uniform>();
satprops_.reset(ptr);
ptr->init(deck, grid, sat_samples);
} else if (threephase_model == "simple") {
SaturationPropsFromDeck<SatFuncSimpleUniform>* ptr
= new SaturationPropsFromDeck<SatFuncSimpleUniform>();
satprops_.reset(ptr);
ptr->init(deck, grid, sat_samples);
} else if (threephase_model == "gwseg") {
SaturationPropsFromDeck<SatFuncGwsegUniform>* ptr
= new SaturationPropsFromDeck<SatFuncGwsegUniform>();
satprops_.reset(ptr);
ptr->init(deck, grid, sat_samples);
} else {
THROW("Unknown threephase_model: " << threephase_model);
}
} else {
if (threephase_model == "stone2") {
SaturationPropsFromDeck<SatFuncStone2Nonuniform>* ptr
= new SaturationPropsFromDeck<SatFuncStone2Nonuniform>();
satprops_.reset(ptr);
ptr->init(deck, grid, sat_samples);
} else if (threephase_model == "simple") {
SaturationPropsFromDeck<SatFuncSimpleNonuniform>* ptr
= new SaturationPropsFromDeck<SatFuncSimpleNonuniform>();
satprops_.reset(ptr);
ptr->init(deck, grid, sat_samples);
} else if (threephase_model == "gwseg") {
SaturationPropsFromDeck<SatFuncGwsegNonuniform>* ptr
= new SaturationPropsFromDeck<SatFuncGwsegNonuniform>();
satprops_.reset(ptr);
ptr->init(deck, grid, sat_samples);
} else {
THROW("Unknown threephase_model: " << threephase_model);
}
}
if (pvt_.numPhases() != satprops_->numPhases()) {
THROW("BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck() - Inconsistent number of phases in pvt data ("
<< pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_->numPhases() << ").");
} }
} }
@ -256,7 +304,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);
} }
@ -275,7 +323,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);
} }
@ -291,7 +339,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

@ -27,6 +27,7 @@
#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 <opm/core/utility/parameters/ParameterGroup.hpp>
#include <boost/scoped_ptr.hpp>
struct UnstructuredGrid; struct UnstructuredGrid;
@ -40,7 +41,7 @@ namespace Opm
public: public:
/// Initialize from deck and grid. /// Initialize from deck and grid.
/// \param[in] deck Deck input parser /// \param[in] deck Deck input parser
/// \param[in] grid Grid to which property object applies, needed for the /// \param[in] grid Grid to which property object applies, needed for the
/// mapping from cell indices (typically from a processed grid) /// 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,
@ -48,12 +49,15 @@ namespace Opm
/// Initialize from deck, grid and parameters. /// Initialize from deck, grid and parameters.
/// \param[in] deck Deck input parser /// \param[in] deck Deck input parser
/// \param[in] grid Grid to which property object applies, needed for the /// \param[in] grid Grid to which property object applies, needed for the
/// mapping from cell indices (typically from a processed grid) /// 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.
/// \param[in] param Parameters. Accepted parameters include: /// \param[in] param Parameters. Accepted parameters include:
/// dead_tab_size (1025) number of uniform sample points for dead-oil pvt tables. /// pvt_tab_size (200) number of uniform sample points for dead-oil pvt tables.
/// tab_size_kr (200) number of uniform sample points for saturation 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, BlackoilPropertiesFromDeck(const EclipseGridParser& deck,
const UnstructuredGrid& grid, const UnstructuredGrid& grid,
const parameter::ParameterGroup& param, const parameter::ParameterGroup& param,
@ -164,9 +168,9 @@ 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.
/// \param[in] cells Array of n cell indices. /// \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] smin Array of nP minimum s values, array must be valid before calling.
@ -179,7 +183,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

@ -138,9 +138,9 @@ namespace Opm
double* dpcds) const = 0; double* dpcds) const = 0;
/// 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.
/// \param[in] cells Array of n cell indices. /// \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] smin Array of nP minimum s values, array must be valid before calling.

View File

@ -28,22 +28,22 @@ namespace Opm
{ {
IncompPropertiesBasic::IncompPropertiesBasic(const parameter::ParameterGroup& param, IncompPropertiesBasic::IncompPropertiesBasic(const parameter::ParameterGroup& param,
const int dim, const int dim,
const int num_cells) const int num_cells)
{ {
double poro = param.getDefault("porosity", 1.0); double poro = param.getDefault("porosity", 1.0);
using namespace Opm::unit; using namespace Opm::unit;
using namespace Opm::prefix; using namespace Opm::prefix;
double perm = param.getDefault("permeability", 100.0)*milli*darcy; double perm = param.getDefault("permeability", 100.0)*milli*darcy;
rock_.init(dim, num_cells, poro, perm); rock_.init(dim, num_cells, poro, perm);
pvt_.init(param); pvt_.init(param);
satprops_.init(param); satprops_.init(param);
if (pvt_.numPhases() != satprops_.numPhases()) { if (pvt_.numPhases() != satprops_.numPhases()) {
THROW("IncompPropertiesBasic::IncompPropertiesBasic() - Inconsistent number of phases in pvt data (" THROW("IncompPropertiesBasic::IncompPropertiesBasic() - 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() << ").");
} }
viscosity_.resize(pvt_.numPhases()); viscosity_.resize(pvt_.numPhases());
pvt_.mu(1, 0, 0, &viscosity_[0]); pvt_.mu(1, 0, 0, &viscosity_[0]);
} }
IncompPropertiesBasic::IncompPropertiesBasic(const int num_phases, IncompPropertiesBasic::IncompPropertiesBasic(const int num_phases,
@ -56,14 +56,14 @@ namespace Opm
const int num_cells) const int num_cells)
{ {
rock_.init(dim, num_cells, por, perm); rock_.init(dim, num_cells, por, perm);
pvt_.init(num_phases, rho, mu); pvt_.init(num_phases, rho, mu);
satprops_.init(num_phases, relpermfunc); satprops_.init(num_phases, relpermfunc);
if (pvt_.numPhases() != satprops_.numPhases()) { if (pvt_.numPhases() != satprops_.numPhases()) {
THROW("IncompPropertiesBasic::IncompPropertiesBasic() - Inconsistent number of phases in pvt data (" THROW("IncompPropertiesBasic::IncompPropertiesBasic() - 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() << ").");
} }
viscosity_.resize(pvt_.numPhases()); viscosity_.resize(pvt_.numPhases());
pvt_.mu(1, 0, 0, &viscosity_[0]); pvt_.mu(1, 0, 0, &viscosity_[0]);
} }
IncompPropertiesBasic::~IncompPropertiesBasic() IncompPropertiesBasic::~IncompPropertiesBasic()
@ -109,7 +109,7 @@ namespace Opm
/// \return Array of P viscosity values. /// \return Array of P viscosity values.
const double* IncompPropertiesBasic::viscosity() const const double* IncompPropertiesBasic::viscosity() const
{ {
return &viscosity_[0]; return &viscosity_[0];
} }
/// \return Array of P density values. /// \return Array of P density values.
@ -117,7 +117,7 @@ namespace Opm
{ {
// No difference between reservoir and surface densities // No difference between reservoir and surface densities
// modelled by this class. // modelled by this class.
return pvt_.surfaceDensities(); return pvt_.surfaceDensities();
} }
/// \return Array of P density values. /// \return Array of P density values.
@ -125,7 +125,7 @@ namespace Opm
{ {
// No difference between reservoir and surface densities // No difference between reservoir and surface densities
// modelled by this class. // modelled by this class.
return pvt_.surfaceDensities(); return pvt_.surfaceDensities();
} }
/// \param[in] n Number of data points. /// \param[in] n Number of data points.
@ -138,10 +138,10 @@ namespace Opm
/// m_{ij} = \frac{dkr_i}{ds^j}, /// m_{ij} = \frac{dkr_i}{ds^j},
/// and is output in Fortran order (m_00 m_10 m_20 m_01 ...) /// and is output in Fortran order (m_00 m_10 m_20 m_01 ...)
void IncompPropertiesBasic::relperm(const int n, void IncompPropertiesBasic::relperm(const int n,
const double* s, const double* s,
const int* /*cells*/, const int* /*cells*/,
double* kr, double* kr,
double* dkrds) const double* dkrds) const
{ {
satprops_.relperm(n, s, kr, dkrds); satprops_.relperm(n, s, kr, dkrds);
} }
@ -157,10 +157,10 @@ namespace Opm
/// m_{ij} = \frac{dpc_i}{ds^j}, /// m_{ij} = \frac{dpc_i}{ds^j},
/// and is output in Fortran order (m_00 m_10 m_20 m_01 ...) /// and is output in Fortran order (m_00 m_10 m_20 m_01 ...)
void IncompPropertiesBasic::capPress(const int n, void IncompPropertiesBasic::capPress(const int n,
const double* s, const double* s,
const int* /*cells*/, const int* /*cells*/,
double* pc, double* pc,
double* dpcds) const double* dpcds) const
{ {
satprops_.capPress(n, s, pc, dpcds); satprops_.capPress(n, s, pc, dpcds);
} }
@ -174,11 +174,11 @@ namespace Opm
/// \param[out] smin Array of nP minimum s values, array must be valid before calling. /// \param[out] 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. /// \param[out] smax Array of nP maximum s values, array must be valid before calling.
void IncompPropertiesBasic::satRange(const int n, void IncompPropertiesBasic::satRange(const int n,
const int* /*cells*/, const int* /*cells*/,
double* smin, double* smin,
double* smax) const double* smax) const
{ {
satprops_.satRange(n, smin, smax); satprops_.satRange(n, smin, smax);
} }
} // namespace Opm } // namespace Opm

View File

@ -42,29 +42,29 @@ namespace Opm
{ {
public: public:
/// Construct from parameters. /// Construct from parameters.
/// The following parameters are accepted (defaults): /// The following parameters are accepted (defaults):
/// num_phases (2) Must be 1 or 2. /// num_phases (2) Must be 1 or 2.
/// relperm_func ("Linear") Must be "Constant", "Linear" or "Quadratic". /// relperm_func ("Linear") Must be "Constant", "Linear" or "Quadratic".
/// rho1 [rho2, rho3] (1.0e3) Density in kg/m^3 /// rho1 [rho2, rho3] (1.0e3) Density in kg/m^3
/// mu1 [mu2, mu3] (1.0) Viscosity in cP /// mu1 [mu2, mu3] (1.0) Viscosity in cP
/// porosity (1.0) Porosity /// porosity (1.0) Porosity
/// permeability (100.0) Permeability in mD /// permeability (100.0) Permeability in mD
IncompPropertiesBasic(const parameter::ParameterGroup& param, IncompPropertiesBasic(const parameter::ParameterGroup& param,
const int dim, const int dim,
const int num_cells); const int num_cells);
/// Construct from arguments a basic two phase fluid. /// Construct from arguments a basic two phase fluid.
IncompPropertiesBasic(const int num_phases, IncompPropertiesBasic(const int num_phases,
const SaturationPropsBasic::RelPermFunc& relpermfunc, const SaturationPropsBasic::RelPermFunc& relpermfunc,
const std::vector<double>& rho, const std::vector<double>& rho,
const std::vector<double>& mu, const std::vector<double>& mu,
const double porosity, const double porosity,
const double permeability, const double permeability,
const int dim, const int dim,
const int num_cells); const int num_cells);
/// Destructor. /// Destructor.
virtual ~IncompPropertiesBasic(); virtual ~IncompPropertiesBasic();
// ---- Rock interface ---- // ---- Rock interface ----
@ -132,9 +132,9 @@ 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.
/// \param[in] cells Array of n cell indices. /// \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] smin Array of nP minimum s values, array must be valid before calling.
@ -145,9 +145,9 @@ namespace Opm
double* smax) const; double* smax) const;
private: private:
RockBasic rock_; RockBasic rock_;
PvtPropertiesBasic pvt_; PvtPropertiesBasic pvt_;
SaturationPropsBasic satprops_; SaturationPropsBasic satprops_;
std::vector<double> viscosity_; std::vector<double> viscosity_;
}; };

View File

@ -27,15 +27,15 @@ namespace Opm
{ {
IncompPropertiesFromDeck::IncompPropertiesFromDeck(const EclipseGridParser& deck, IncompPropertiesFromDeck::IncompPropertiesFromDeck(const EclipseGridParser& deck,
const UnstructuredGrid& grid) const UnstructuredGrid& grid)
{ {
rock_.init(deck, grid); rock_.init(deck, grid);
pvt_.init(deck); pvt_.init(deck);
satprops_.init(deck, grid); 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() << ").");
} }
} }
IncompPropertiesFromDeck::~IncompPropertiesFromDeck() IncompPropertiesFromDeck::~IncompPropertiesFromDeck()
@ -81,19 +81,19 @@ namespace Opm
/// \return Array of P viscosity values. /// \return Array of P viscosity values.
const double* IncompPropertiesFromDeck::viscosity() const const double* IncompPropertiesFromDeck::viscosity() const
{ {
return pvt_.viscosity(); return pvt_.viscosity();
} }
/// \return Array of P density values. /// \return Array of P density values.
const double* IncompPropertiesFromDeck::density() const const double* IncompPropertiesFromDeck::density() const
{ {
return pvt_.reservoirDensities(); return pvt_.reservoirDensities();
} }
/// \return Array of P density values. /// \return Array of P density values.
const double* IncompPropertiesFromDeck::surfaceDensity() const const double* IncompPropertiesFromDeck::surfaceDensity() const
{ {
return pvt_.surfaceDensities(); return pvt_.surfaceDensities();
} }
/// \param[in] n Number of data points. /// \param[in] n Number of data points.
@ -106,10 +106,10 @@ namespace Opm
/// m_{ij} = \frac{dkr_i}{ds^j}, /// m_{ij} = \frac{dkr_i}{ds^j},
/// and is output in Fortran order (m_00 m_10 m_20 m_01 ...) /// and is output in Fortran order (m_00 m_10 m_20 m_01 ...)
void IncompPropertiesFromDeck::relperm(const int n, void IncompPropertiesFromDeck::relperm(const int n,
const double* s, const double* s,
const int* cells, const int* cells,
double* kr, double* kr,
double* dkrds) const double* dkrds) const
{ {
satprops_.relperm(n, s, cells, kr, dkrds); satprops_.relperm(n, s, cells, kr, dkrds);
} }
@ -125,10 +125,10 @@ namespace Opm
/// m_{ij} = \frac{dpc_i}{ds^j}, /// m_{ij} = \frac{dpc_i}{ds^j},
/// and is output in Fortran order (m_00 m_10 m_20 m_01 ...) /// and is output in Fortran order (m_00 m_10 m_20 m_01 ...)
void IncompPropertiesFromDeck::capPress(const int n, void IncompPropertiesFromDeck::capPress(const int n,
const double* s, const double* s,
const int* cells, const int* cells,
double* pc, double* pc,
double* dpcds) const double* dpcds) const
{ {
satprops_.capPress(n, s, cells, pc, dpcds); satprops_.capPress(n, s, cells, pc, dpcds);
} }
@ -142,11 +142,11 @@ namespace Opm
/// \param[out] smin Array of nP minimum s values, array must be valid before calling. /// \param[out] 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. /// \param[out] smax Array of nP maximum s values, array must be valid before calling.
void IncompPropertiesFromDeck::satRange(const int n, void IncompPropertiesFromDeck::satRange(const int n,
const int* cells, const int* cells,
double* smin, double* smin,
double* smax) const double* smax) const
{ {
satprops_.satRange(n, cells, smin, smax); satprops_.satRange(n, cells, smin, smax);
} }
} // namespace Opm } // namespace Opm

View File

@ -47,13 +47,13 @@ namespace Opm
public: public:
/// Initialize from deck and grid. /// Initialize from deck and grid.
/// \param deck Deck input parser /// \param deck Deck input parser
/// \param grid Grid to which property object applies, needed for the /// \param grid Grid to which property object applies, needed for the
/// mapping from cell indices (typically from a processed grid) /// 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 UnstructuredGrid& grid); const UnstructuredGrid& grid);
/// Destructor. /// Destructor.
virtual ~IncompPropertiesFromDeck(); virtual ~IncompPropertiesFromDeck();
// ---- Rock interface ---- // ---- Rock interface ----
@ -121,9 +121,9 @@ 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.
/// \param[in] cells Array of n cell indices. /// \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] smin Array of nP minimum s values, array must be valid before calling.
@ -134,8 +134,8 @@ namespace Opm
double* smax) const; double* smax) const;
private: private:
RockFromDeck rock_; RockFromDeck rock_;
PvtPropertiesIncompFromDeck pvt_; PvtPropertiesIncompFromDeck pvt_;
SaturationPropsFromDeck satprops_; SaturationPropsFromDeck<SatFuncStone2Uniform> satprops_;
}; };

View File

@ -62,7 +62,7 @@ namespace Opm
/// \return Array of P viscosity values. /// \return Array of P viscosity values.
virtual const double* viscosity() const = 0; virtual const double* viscosity() const = 0;
/// Densities of fluid phases at surface conditions. /// Densities of fluid phases at reservoir conditions.
/// \return Array of P density values. /// \return Array of P density values.
virtual const double* density() const = 0; virtual const double* density() const = 0;
@ -109,9 +109,9 @@ namespace Opm
double* pc, double* pc,
double* dpcds) const = 0; double* dpcds) const = 0;
/// 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.
/// \param[in] cells Array of n cell indices. /// \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] smin Array of nP minimum s values, array must be valid before calling.

View File

@ -34,41 +34,41 @@ namespace Opm
void PvtPropertiesBasic::init(const parameter::ParameterGroup& param) void PvtPropertiesBasic::init(const parameter::ParameterGroup& param)
{ {
int num_phases = param.getDefault("num_phases", 2); int num_phases = param.getDefault("num_phases", 2);
if (num_phases > 3 || num_phases < 1) { if (num_phases > 3 || num_phases < 1) {
THROW("PvtPropertiesBasic::init() illegal num_phases: " << num_phases); THROW("PvtPropertiesBasic::init() illegal num_phases: " << num_phases);
} }
density_.resize(num_phases); density_.resize(num_phases);
viscosity_.resize(num_phases); viscosity_.resize(num_phases);
// We currently do not allow the user to set B. // We currently do not allow the user to set B.
formation_volume_factor_.clear(); formation_volume_factor_.clear();
formation_volume_factor_.resize(num_phases, 1.0); formation_volume_factor_.resize(num_phases, 1.0);
// Setting mu and rho from parameters // Setting mu and rho from parameters
using namespace Opm::prefix; using namespace Opm::prefix;
using namespace Opm::unit; using namespace Opm::unit;
const double kgpm3 = kilogram/cubic(meter); const double kgpm3 = kilogram/cubic(meter);
const double cP = centi*Poise; const double cP = centi*Poise;
std::string rname[3] = { "rho1", "rho2", "rho3" }; std::string rname[3] = { "rho1", "rho2", "rho3" };
double rdefault[3] = { 1.0e3, 1.0e3, 1.0e3 }; double rdefault[3] = { 1.0e3, 1.0e3, 1.0e3 };
std::string vname[3] = { "mu1", "mu2", "mu3" }; std::string vname[3] = { "mu1", "mu2", "mu3" };
double vdefault[3] = { 1.0, 1.0, 1.0 }; double vdefault[3] = { 1.0, 1.0, 1.0 };
for (int phase = 0; phase < num_phases; ++phase) { for (int phase = 0; phase < num_phases; ++phase) {
density_[phase] = kgpm3*param.getDefault(rname[phase], rdefault[phase]); density_[phase] = kgpm3*param.getDefault(rname[phase], rdefault[phase]);
viscosity_[phase] = cP*param.getDefault(vname[phase], vdefault[phase]); viscosity_[phase] = cP*param.getDefault(vname[phase], vdefault[phase]);
} }
} }
void PvtPropertiesBasic::init(const int num_phases, void PvtPropertiesBasic::init(const int num_phases,
const std::vector<double>& rho, const std::vector<double>& rho,
const std::vector<double>& visc) const std::vector<double>& visc)
{ {
if (num_phases > 3 || num_phases < 1) { if (num_phases > 3 || num_phases < 1) {
THROW("PvtPropertiesBasic::init() illegal num_phases: " << num_phases); THROW("PvtPropertiesBasic::init() illegal num_phases: " << num_phases);
} }
// We currently do not allow the user to set B. // We currently do not allow the user to set B.
formation_volume_factor_.clear(); formation_volume_factor_.clear();
formation_volume_factor_.resize(num_phases, 1.0); formation_volume_factor_.resize(num_phases, 1.0);
density_ = rho; density_ = rho;
viscosity_ = visc; viscosity_ = visc;
} }
@ -87,69 +87,69 @@ namespace Opm
void PvtPropertiesBasic::mu(const int n, void PvtPropertiesBasic::mu(const int n,
const double* /*p*/, const double* /*p*/,
const double* /*z*/, const double* /*z*/,
double* output_mu) const double* output_mu) const
{ {
const int np = numPhases(); const int np = numPhases();
for (int phase = 0; phase < np; ++phase) { for (int phase = 0; phase < np; ++phase) {
// #pragma omp parallel for // #pragma omp parallel for
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
output_mu[np*i + phase] = viscosity_[phase]; output_mu[np*i + phase] = viscosity_[phase];
} }
} }
} }
void PvtPropertiesBasic::B(const int n, void PvtPropertiesBasic::B(const int n,
const double* /*p*/, const double* /*p*/,
const double* /*z*/, const double* /*z*/,
double* output_B) const double* output_B) const
{ {
const int np = numPhases(); const int np = numPhases();
for (int phase = 0; phase < np; ++phase) { for (int phase = 0; phase < np; ++phase) {
// #pragma omp parallel for // #pragma omp parallel for
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
output_B[np*i + phase] = formation_volume_factor_[phase]; output_B[np*i + phase] = formation_volume_factor_[phase];
} }
} }
} }
void PvtPropertiesBasic::dBdp(const int n, void PvtPropertiesBasic::dBdp(const int n,
const double* /*p*/, const double* /*p*/,
const double* /*z*/, const double* /*z*/,
double* output_B, double* output_B,
double* output_dBdp) const double* output_dBdp) const
{ {
const int np = numPhases(); const int np = numPhases();
for (int phase = 0; phase < np; ++phase) { for (int phase = 0; phase < np; ++phase) {
// #pragma omp parallel for // #pragma omp parallel for
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
output_B[np*i + phase] = formation_volume_factor_[phase]; output_B[np*i + phase] = formation_volume_factor_[phase];
output_dBdp[np*i + phase] = 0.0; output_dBdp[np*i + phase] = 0.0;
} }
} }
} }
void PvtPropertiesBasic::R(const int n, void PvtPropertiesBasic::R(const int n,
const double* /*p*/, const double* /*p*/,
const double* /*z*/, const double* /*z*/,
double* output_R) const double* output_R) const
{ {
const int np = numPhases(); const int np = numPhases();
std::fill(output_R, output_R + n*np, 0.0); std::fill(output_R, output_R + n*np, 0.0);
} }
void PvtPropertiesBasic::dRdp(const int n, void PvtPropertiesBasic::dRdp(const int n,
const double* /*p*/, const double* /*p*/,
const double* /*z*/, const double* /*z*/,
double* output_R, double* output_R,
double* output_dRdp) const double* output_dRdp) const
{ {
const int np = numPhases(); const int np = numPhases();
std::fill(output_R, output_R + n*np, 0.0); std::fill(output_R, output_R + n*np, 0.0);
std::fill(output_dRdp, output_dRdp + n*np, 0.0); std::fill(output_dRdp, output_dRdp + n*np, 0.0);
} }
} // namespace Opm } // namespace Opm

View File

@ -38,11 +38,11 @@ namespace Opm
PvtPropertiesBasic(); PvtPropertiesBasic();
/// Initialize from parameters. /// Initialize from parameters.
/// The following parameters are accepted (defaults): /// The following parameters are accepted (defaults):
/// num_phases (2) Must be 1, 2 or 3. /// num_phases (2) Must be 1, 2 or 3.
/// rho1 [rho2, rho3] (1.0e3) Density in kg/m^3 /// rho1 [rho2, rho3] (1.0e3) Density in kg/m^3
/// mu1 [mu2, mu3] (1.0) Viscosity in cP /// mu1 [mu2, mu3] (1.0) Viscosity in cP
void init(const parameter::ParameterGroup& param); void init(const parameter::ParameterGroup& param);
/// Initialize from arguments. /// Initialize from arguments.
/// Basic multi phase fluid pvt properties. /// Basic multi phase fluid pvt properties.
@ -55,7 +55,7 @@ namespace Opm
/// Densities of stock components at surface conditions. /// Densities of stock components at surface conditions.
/// \return Array of size numPhases(). /// \return Array of size numPhases().
const double* surfaceDensities() const; const double* surfaceDensities() const;
/// Viscosity as a function of p and z. /// Viscosity as a function of p and z.
void mu(const int n, void mu(const int n,
@ -90,9 +90,9 @@ namespace Opm
double* output_dRdp) const; double* output_dRdp) const;
private: private:
std::vector<double> density_; std::vector<double> density_;
std::vector<double> viscosity_; std::vector<double> viscosity_;
std::vector<double> formation_volume_factor_; std::vector<double> formation_volume_factor_;
}; };
} }

View File

@ -38,54 +38,54 @@ namespace Opm
{ {
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.
int region_number = 0; int region_number = 0;
PhaseUsage phase_usage = phaseUsageFromDeck(deck); PhaseUsage phase_usage = phaseUsageFromDeck(deck);
if (phase_usage.phase_used[PhaseUsage::Vapour] || if (phase_usage.phase_used[PhaseUsage::Vapour] ||
!phase_usage.phase_used[PhaseUsage::Aqua] || !phase_usage.phase_used[PhaseUsage::Aqua] ||
!phase_usage.phase_used[PhaseUsage::Liquid]) { !phase_usage.phase_used[PhaseUsage::Liquid]) {
THROW("PvtPropertiesIncompFromDeck::init() -- must have gas and oil phases (only) in deck input.\n"); THROW("PvtPropertiesIncompFromDeck::init() -- must have gas and oil phases (only) in deck input.\n");
} }
// Surface densities. Accounting for different orders in eclipse and our code. // Surface densities. Accounting for different orders in eclipse and our code.
if (deck.hasField("DENSITY")) { if (deck.hasField("DENSITY")) {
const std::vector<double>& d = deck.getDENSITY().densities_[region_number]; const std::vector<double>& d = deck.getDENSITY().densities_[region_number];
enum { ECL_oil = 0, ECL_water = 1, ECL_gas = 2 }; enum { ECL_oil = 0, ECL_water = 1, ECL_gas = 2 };
surface_density_[phase_usage.phase_pos[PhaseUsage::Aqua]] = d[ECL_water]; surface_density_[phase_usage.phase_pos[PhaseUsage::Aqua]] = d[ECL_water];
surface_density_[phase_usage.phase_pos[PhaseUsage::Liquid]] = d[ECL_oil]; surface_density_[phase_usage.phase_pos[PhaseUsage::Liquid]] = d[ECL_oil];
} else { } else {
THROW("Input is missing DENSITY\n"); THROW("Input is missing DENSITY\n");
} }
// Make reservoir densities the same as surface densities initially. // Make reservoir densities the same as surface densities initially.
// We will modify them with formation volume factors if found. // We will modify them with formation volume factors if found.
reservoir_density_ = surface_density_; reservoir_density_ = surface_density_;
// Water viscosity. // Water viscosity.
if (deck.hasField("PVTW")) { if (deck.hasField("PVTW")) {
const std::vector<double>& pvtw = deck.getPVTW().pvtw_[region_number]; const std::vector<double>& pvtw = deck.getPVTW().pvtw_[region_number];
if (pvtw[2] != 0.0 || pvtw[4] != 0.0) { if (pvtw[2] != 0.0 || pvtw[4] != 0.0) {
MESSAGE("Compressibility effects in PVTW are ignored."); MESSAGE("Compressibility effects in PVTW are ignored.");
} }
reservoir_density_[phase_usage.phase_pos[PhaseUsage::Aqua]] /= pvtw[1]; reservoir_density_[phase_usage.phase_pos[PhaseUsage::Aqua]] /= pvtw[1];
viscosity_[phase_usage.phase_pos[PhaseUsage::Aqua]] = pvtw[3]; viscosity_[phase_usage.phase_pos[PhaseUsage::Aqua]] = pvtw[3];
} else { } else {
// Eclipse 100 default. // Eclipse 100 default.
// viscosity_[phase_usage.phase_pos[PhaseUsage::Aqua]] = 0.5*Opm::prefix::centi*Opm::unit::Poise; // viscosity_[phase_usage.phase_pos[PhaseUsage::Aqua]] = 0.5*Opm::prefix::centi*Opm::unit::Poise;
THROW("Input is missing PVTW\n"); THROW("Input is missing PVTW\n");
} }
// Oil viscosity. // Oil viscosity.
if (deck.hasField("PVCDO")) { if (deck.hasField("PVCDO")) {
const std::vector<double>& pvcdo = deck.getPVCDO().pvcdo_[region_number]; const std::vector<double>& pvcdo = deck.getPVCDO().pvcdo_[region_number];
if (pvcdo[2] != 0.0 || pvcdo[4] != 0.0) { if (pvcdo[2] != 0.0 || pvcdo[4] != 0.0) {
MESSAGE("Compressibility effects in PVCDO are ignored."); MESSAGE("Compressibility effects in PVCDO are ignored.");
} }
reservoir_density_[phase_usage.phase_pos[PhaseUsage::Liquid]] /= pvcdo[1]; reservoir_density_[phase_usage.phase_pos[PhaseUsage::Liquid]] /= pvcdo[1];
viscosity_[phase_usage.phase_pos[PhaseUsage::Liquid]] = pvcdo[3]; viscosity_[phase_usage.phase_pos[PhaseUsage::Liquid]] = pvcdo[3];
} else { } else {
THROW("Input is missing PVCDO\n"); THROW("Input is missing PVCDO\n");
} }
} }
const double* PvtPropertiesIncompFromDeck::surfaceDensities() const const double* PvtPropertiesIncompFromDeck::surfaceDensities() const

View File

@ -39,14 +39,14 @@ namespace Opm
PvtPropertiesIncompFromDeck(); PvtPropertiesIncompFromDeck();
/// Initialize from deck. /// Initialize from deck.
void init(const EclipseGridParser& deck); void init(const EclipseGridParser& deck);
/// Number of active phases. /// Number of active phases.
int numPhases() const; int numPhases() const;
/// Densities of stock components at surface conditions. /// Densities of stock components at surface conditions.
/// \return Array of size numPhases(). /// \return Array of size numPhases().
const double* surfaceDensities() const; const double* surfaceDensities() const;
/// Densities of stock components at reservoir conditions. /// Densities of stock components at reservoir conditions.
/// Note: a reasonable question to ask is why there can be /// Note: a reasonable question to ask is why there can be
@ -58,15 +58,15 @@ namespace Opm
/// reporting and using data given in terms of surface values, /// reporting and using data given in terms of surface values,
/// we need to handle this difference. /// we need to handle this difference.
/// \return Array of size numPhases(). /// \return Array of size numPhases().
const double* reservoirDensities() const; const double* reservoirDensities() const;
/// Viscosities. /// Viscosities.
const double* viscosity() const; const double* viscosity() const;
private: private:
std::tr1::array<double, 2> surface_density_; std::tr1::array<double, 2> surface_density_;
std::tr1::array<double, 2> reservoir_density_; std::tr1::array<double, 2> reservoir_density_;
std::tr1::array<double, 2> viscosity_; std::tr1::array<double, 2> viscosity_;
}; };
} }

View File

@ -25,29 +25,29 @@ namespace Opm
/// Default constructor. /// Default constructor.
RockBasic::RockBasic() RockBasic::RockBasic()
: dimensions_(-1) : dimensions_(-1)
{ {
} }
/// Initialize with homogenous porosity and permeability. /// Initialize with homogenous porosity and permeability.
void RockBasic::init(const int dimensions, void RockBasic::init(const int dimensions,
const int num_cells, const int num_cells,
const double poro, const double poro,
const double perm) const double perm)
{ {
dimensions_ = dimensions; dimensions_ = dimensions;
porosity_.clear(); porosity_.clear();
porosity_.resize(num_cells, poro); porosity_.resize(num_cells, poro);
permeability_.clear(); permeability_.clear();
const int dsq = dimensions*dimensions; const int dsq = dimensions*dimensions;
permeability_.resize(num_cells*dsq, 0.0); permeability_.resize(num_cells*dsq, 0.0);
// #pragma omp parallel for // #pragma omp parallel for
for (int i = 0; i < num_cells; ++i) { for (int i = 0; i < num_cells; ++i) {
for (int d = 0; d < dimensions; ++d) { for (int d = 0; d < dimensions; ++d) {
permeability_[dsq*i + dimensions*d + d] = perm; permeability_[dsq*i + dimensions*d + d] = perm;
} }
} }
} }

View File

@ -35,9 +35,9 @@ namespace Opm
/// Initialize with homogenous porosity and permeability. /// Initialize with homogenous porosity and permeability.
void init(const int dimensions, void init(const int dimensions,
const int num_cells, const int num_cells,
const double poro, const double poro,
const double perm); const double perm);
/// \return D, the number of spatial dimensions. /// \return D, the number of spatial dimensions.
int numDimensions() const int numDimensions() const
@ -66,7 +66,7 @@ namespace Opm
} }
private: private:
int dimensions_; int dimensions_;
std::vector<double> porosity_; std::vector<double> porosity_;
std::vector<double> permeability_; std::vector<double> permeability_;
}; };

View File

@ -69,8 +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); return Opm::linearInterpolationExtrap(p_, poromult_, pressure);
} }
} }
@ -81,7 +81,7 @@ namespace Opm
} 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 poromult = Opm::linearInterpolationExtrap(p_, poromult_, pressure);
const double dporomultdp = Opm::linearInterpolationDerivativeExtrap(p_, poromult_, pressure); const double dporomultdp = Opm::linearInterpolationDerivativeExtrap(p_, poromult_, pressure);
return dporomultdp/poromult; return dporomultdp/poromult;

View File

@ -51,7 +51,7 @@ 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 grid grid to which property object applies, needed for the /// \param grid grid to which property object applies, needed for the
/// mapping from cell indices (typically from a processed grid) /// 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,

View File

@ -37,7 +37,7 @@ namespace Opm
/// Initialize from deck and grid. /// Initialize from deck and grid.
/// \param deck Deck input parser /// \param deck Deck input parser
/// \param grid Grid to which property object applies, needed for the /// \param grid Grid to which property object applies, needed for the
/// mapping from cell indices (typically from a processed grid) /// 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,

View File

@ -0,0 +1,440 @@
/*
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/SatFuncGwseg.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 SatFuncGwsegUniform::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 SatFuncGwsegUniform::evalKr(const double* s, double* kr) const
{
if (phase_usage.num_phases == 3) {
// Relative permeability model based on segregation of water
// and gas, with oil present in both water and gas zones.
const double swco = smin_[phase_usage.phase_pos[Aqua]];
const double sw = std::max(s[Aqua], swco);
const double sg = s[Vapour];
// xw and xg are the fractions occupied by water and gas zones.
const double eps = 1e-6;
const double xw = (sw - swco) / std::max(sg + sw - swco, eps);
const double xg = 1 - xw;
const double ssw = sg + sw;
const double ssg = sw - swco + sg;
const double krw = krw_(ssw);
const double krg = krg_(ssg);
const double krow = krow_(ssw);
const double krog = krog_(ssg);
kr[Aqua] = xw*krw;
kr[Vapour] = xg*krg;
kr[Liquid] = xw*krow + xg*krog;
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 SatFuncGwsegUniform::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) {
// Relative permeability model based on segregation of water
// and gas, with oil present in both water and gas zones.
const double swco = smin_[phase_usage.phase_pos[Aqua]];
const double sw = std::max(s[Aqua], swco);
const double sg = s[Vapour];
// xw and xg are the fractions occupied by water and gas zones.
const double eps = 1e-6;
const double xw = (sw - swco) / std::max(sg + sw - swco, eps);
const double xg = 1 - xw;
const double ssw = sg + sw;
const double ssg = sw - swco + sg;
const double krw = krw_(ssw);
const double krg = krg_(ssg);
const double krow = krow_(ssw);
const double krog = krog_(ssg);
kr[Aqua] = xw*krw;
kr[Vapour] = xg*krg;
kr[Liquid] = xw*krow + xg*krog;
// Derivatives.
const double dkrww = krw_.derivative(ssw);
const double dkrgg = krg_.derivative(ssg);
const double dkrow = krow_.derivative(ssw);
const double dkrog = krog_.derivative(ssg);
const double d = ssg; // = sw - swco + sg (using 'd' for consistency with mrst docs).
dkrds[Aqua + Aqua*np] = (xg/d)*krw + xw*dkrww;
dkrds[Aqua + Vapour*np] = -(xw/d)*krw + xw*dkrww;
dkrds[Liquid + Aqua*np] = (xg/d)*krow + xw*dkrow - (xg/d)*krog + xg*dkrog;
dkrds[Liquid + Vapour*np] = -(xw/d)*krow + xw*dkrow + (xw/d)*krog + xg*dkrog;
dkrds[Vapour + Aqua*np] = -(xg/d)*krg + xg*dkrgg;
dkrds[Vapour + Vapour*np] = (xw/d)*krg + xg*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 SatFuncGwsegUniform::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 SatFuncGwsegUniform::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 SatFuncGwsegNonuniform ======
void SatFuncGwsegNonuniform::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 SatFuncGwsegNonuniform::evalKr(const double* s, double* kr) const
{
if (phase_usage.num_phases == 3) {
// Relative permeability model based on segregation of water
// and gas, with oil present in both water and gas zones.
const double swco = smin_[phase_usage.phase_pos[Aqua]];
const double sw = std::max(s[Aqua], swco);
const double sg = s[Vapour];
// xw and xg are the fractions occupied by water and gas zones.
const double eps = 1e-6;
const double xw = (sw - swco) / std::max(sg + sw - swco, eps);
const double xg = 1 - xw;
const double ssw = sg + sw;
const double ssg = sw - swco + sg;
const double krw = krw_(ssw);
const double krg = krg_(ssg);
const double krow = krow_(ssw);
const double krog = krog_(ssg);
kr[Aqua] = xw*krw;
kr[Vapour] = xg*krg;
kr[Liquid] = xw*krow + xg*krog;
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 SatFuncGwsegNonuniform::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) {
// Relative permeability model based on segregation of water
// and gas, with oil present in both water and gas zones.
const double swco = smin_[phase_usage.phase_pos[Aqua]];
const double sw = std::max(s[Aqua], swco);
const double sg = s[Vapour];
// xw and xg are the fractions occupied by water and gas zones.
const double eps = 1e-6;
const double xw = (sw - swco) / std::max(sg + sw - swco, eps);
const double xg = 1 - xw;
const double ssw = sg + sw;
const double ssg = sw - swco + sg;
const double krw = krw_(ssw);
const double krg = krg_(ssg);
const double krow = krow_(ssw);
const double krog = krog_(ssg);
kr[Aqua] = xw*krw;
kr[Vapour] = xg*krg;
kr[Liquid] = xw*krow + xg*krog;
// Derivatives.
const double dkrww = krw_.derivative(ssw);
const double dkrgg = krg_.derivative(ssg);
const double dkrow = krow_.derivative(ssw);
const double dkrog = krog_.derivative(ssg);
const double d = ssg; // = sw - swco + sg (using 'd' for consistency with mrst docs).
dkrds[Aqua + Aqua*np] = (xg/d)*krw + xw*dkrww;
dkrds[Aqua + Vapour*np] = -(xw/d)*krw + xw*dkrww;
dkrds[Liquid + Aqua*np] = (xg/d)*krow + xw*dkrow - (xg/d)*krog + xg*dkrog;
dkrds[Liquid + Vapour*np] = -(xw/d)*krow + xw*dkrow + (xw/d)*krog + xg*dkrog;
dkrds[Vapour + Aqua*np] = -(xg/d)*krg + xg*dkrgg;
dkrds[Vapour + Vapour*np] = (xw/d)*krg + xg*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 SatFuncGwsegNonuniform::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 SatFuncGwsegNonuniform::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 SATFUNCGWSEG_HPP
#define SATFUNCGWSEG_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 SatFuncGwsegUniform : 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 SatFuncGwsegNonuniform : 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 // SATFUNCGWSEG_HPP

View File

@ -1,3 +1,22 @@
/*
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/SatFuncSimple.hpp>
#include <opm/core/fluid/blackoil/BlackoilPhases.hpp> #include <opm/core/fluid/blackoil/BlackoilPhases.hpp>
#include <opm/core/fluid/SaturationPropsFromDeck.hpp> #include <opm/core/fluid/SaturationPropsFromDeck.hpp>
@ -6,21 +25,14 @@
#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>
namespace Opm namespace Opm
{ {
void SatFuncSimple::init(const EclipseGridParser& deck, void SatFuncSimpleUniform::init(const EclipseGridParser& deck,
const int table_num,
const PhaseUsage phase_usg)
{
init(deck, table_num, phase_usg, 200);
}
void SatFuncSimple::init(const EclipseGridParser& deck,
const int table_num, const int table_num,
const PhaseUsage phase_usg, const PhaseUsage phase_usg,
const int samples) const int samples)
@ -65,7 +77,7 @@ namespace Opm
} }
void SatFuncSimple::evalKr(const double* s, double* kr) const void SatFuncSimpleUniform::evalKr(const double* s, double* kr) const
{ {
if (phase_usage.num_phases == 3) { if (phase_usage.num_phases == 3) {
// A simplified relative permeability model. // A simplified relative permeability model.
@ -106,7 +118,7 @@ namespace Opm
} }
void SatFuncSimple::evalKrDeriv(const double* s, double* kr, double* dkrds) const void SatFuncSimpleUniform::evalKrDeriv(const double* s, double* kr, double* dkrds) const
{ {
const int np = phase_usage.num_phases; const int np = phase_usage.num_phases;
std::fill(dkrds, dkrds + np*np, 0.0); std::fill(dkrds, dkrds + np*np, 0.0);
@ -172,7 +184,7 @@ namespace Opm
} }
void SatFuncSimple::evalPc(const double* s, double* pc) const void SatFuncSimpleUniform::evalPc(const double* s, double* pc) const
{ {
pc[phase_usage.phase_pos[Liquid]] = 0.0; pc[phase_usage.phase_pos[Liquid]] = 0.0;
if (phase_usage.phase_used[Aqua]) { if (phase_usage.phase_used[Aqua]) {
@ -185,7 +197,206 @@ namespace Opm
} }
} }
void SatFuncSimple::evalPcDeriv(const double* s, double* pc, double* dpcds) const 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 // The problem of determining three-phase capillary pressures
// is very hard experimentally, usually one extends two-phase // is very hard experimentally, usually one extends two-phase

View File

@ -18,17 +18,21 @@
*/ */
#ifndef SATFUNCSIMPLE_HPP #ifndef SATFUNCSIMPLE_HPP
#define SATFUNCSIMPLE_HPP #define SATFUNCSIMPLE_HPP
#include <opm/core/eclipse/EclipseGridParser.hpp> #include <opm/core/eclipse/EclipseGridParser.hpp>
#include <opm/core/utility/UniformTableLinear.hpp> #include <opm/core/utility/UniformTableLinear.hpp>
#include <opm/core/utility/NonuniformTableLinear.hpp>
#include <opm/core/fluid/blackoil/BlackoilPhases.hpp> #include <opm/core/fluid/blackoil/BlackoilPhases.hpp>
#include <vector> #include <vector>
namespace Opm namespace Opm
{ {
class SatFuncSimple: public BlackoilPhases class SatFuncSimpleUniform : public BlackoilPhases
{ {
public: public:
void init(const EclipseGridParser& deck, const int table_num, PhaseUsage phase_usg); void init(const EclipseGridParser& deck,
void init(const EclipseGridParser& deck, const int table_num, PhaseUsage phase_usg, const int table_num,
const PhaseUsage phase_usg,
const int samples); const int samples);
void evalKr(const double* s, double* kr) const; void evalKr(const double* s, double* kr) const;
void evalKrDeriv(const double* s, double* kr, double* dkrds) const; void evalKrDeriv(const double* s, double* kr, double* dkrds) const;
@ -46,5 +50,31 @@ namespace Opm
UniformTableLinear<double> pcog_; UniformTableLinear<double> pcog_;
double krocw_; // = krow_(s_wc) 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 } // namespace Opm
#endif // SATFUNCSIMPLE_HPP #endif // SATFUNCSIMPLE_HPP

View File

@ -1,3 +1,22 @@
/*
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/SatFuncStone2.hpp>
#include <opm/core/fluid/blackoil/BlackoilPhases.hpp> #include <opm/core/fluid/blackoil/BlackoilPhases.hpp>
#include <opm/core/fluid/SaturationPropsFromDeck.hpp> #include <opm/core/fluid/SaturationPropsFromDeck.hpp>
@ -6,23 +25,17 @@
#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>
namespace Opm namespace Opm
{ {
void SatFuncStone2::init(const EclipseGridParser& deck, void SatFuncStone2Uniform::init(const EclipseGridParser& deck,
const int table_num, const int table_num,
const PhaseUsage phase_usg) const PhaseUsage phase_usg,
{ const int samples)
init(deck, table_num, phase_usg, 200);
}
void SatFuncStone2::init(const EclipseGridParser& deck,
const int table_num,
const PhaseUsage phase_usg,
const int samples)
{ {
phase_usage = phase_usg; phase_usage = phase_usg;
double swco = 0.0; double swco = 0.0;
@ -64,7 +77,7 @@ namespace Opm
} }
void SatFuncStone2::evalKr(const double* s, double* kr) const void SatFuncStone2Uniform::evalKr(const double* s, double* kr) const
{ {
if (phase_usage.num_phases == 3) { if (phase_usage.num_phases == 3) {
// Stone-II relative permeability model. // Stone-II relative permeability model.
@ -105,7 +118,7 @@ namespace Opm
} }
void SatFuncStone2::evalKrDeriv(const double* s, double* kr, double* dkrds) const void SatFuncStone2Uniform::evalKrDeriv(const double* s, double* kr, double* dkrds) const
{ {
const int np = phase_usage.num_phases; const int np = phase_usage.num_phases;
std::fill(dkrds, dkrds + np*np, 0.0); std::fill(dkrds, dkrds + np*np, 0.0);
@ -167,7 +180,7 @@ namespace Opm
} }
void SatFuncStone2::evalPc(const double* s, double* pc) const void SatFuncStone2Uniform::evalPc(const double* s, double* pc) const
{ {
pc[phase_usage.phase_pos[Liquid]] = 0.0; pc[phase_usage.phase_pos[Liquid]] = 0.0;
if (phase_usage.phase_used[Aqua]) { if (phase_usage.phase_used[Aqua]) {
@ -180,7 +193,7 @@ namespace Opm
} }
} }
void SatFuncStone2::evalPcDeriv(const double* s, double* pc, double* dpcds) const void SatFuncStone2Uniform::evalPcDeriv(const double* s, double* pc, double* dpcds) const
{ {
// The problem of determining three-phase capillary pressures // The problem of determining three-phase capillary pressures
// is very hard experimentally, usually one extends two-phase // is very hard experimentally, usually one extends two-phase
@ -206,4 +219,199 @@ namespace Opm
// ====== Methods for SatFuncStone2Nonuniform ======
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 } // namespace Opm

View File

@ -18,17 +18,21 @@
*/ */
#ifndef SATFUNCSTONE2_HPP #ifndef SATFUNCSTONE2_HPP
#define SATFUNCSTONE2_HPP #define SATFUNCSTONE2_HPP
#include <opm/core/eclipse/EclipseGridParser.hpp> #include <opm/core/eclipse/EclipseGridParser.hpp>
#include <opm/core/utility/UniformTableLinear.hpp> #include <opm/core/utility/UniformTableLinear.hpp>
#include <opm/core/utility/NonuniformTableLinear.hpp>
#include <opm/core/fluid/blackoil/BlackoilPhases.hpp> #include <opm/core/fluid/blackoil/BlackoilPhases.hpp>
#include <vector> #include <vector>
namespace Opm namespace Opm
{ {
class SatFuncStone2: public BlackoilPhases class SatFuncStone2Uniform : public BlackoilPhases
{ {
public: public:
void init(const EclipseGridParser& deck, const int table_num, PhaseUsage phase_usg); void init(const EclipseGridParser& deck,
void init(const EclipseGridParser& deck, const int table_num, PhaseUsage phase_usg, const int table_num,
const PhaseUsage phase_usg,
const int samples); const int samples);
void evalKr(const double* s, double* kr) const; void evalKr(const double* s, double* kr) const;
void evalKrDeriv(const double* s, double* kr, double* dkrds) const; void evalKrDeriv(const double* s, double* kr, double* dkrds) const;
@ -46,5 +50,31 @@ namespace Opm
UniformTableLinear<double> pcog_; UniformTableLinear<double> pcog_;
double krocw_; // = krow_(s_wc) 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 } // namespace Opm
#endif // SATFUNCSTONE2_HPP #endif // SATFUNCSTONE2_HPP

View File

@ -29,64 +29,64 @@ namespace Opm
namespace { namespace {
struct KrFunConstant struct KrFunConstant
{ {
double kr(double) double kr(double)
{ {
return 1.0; return 1.0;
} }
double dkrds(double) double dkrds(double)
{ {
return 0.0; return 0.0;
} }
}; };
struct KrFunLinear struct KrFunLinear
{ {
double kr(double s) double kr(double s)
{ {
return s; return s;
} }
double dkrds(double) double dkrds(double)
{ {
return 1.0; return 1.0;
} }
}; };
struct KrFunQuadratic struct KrFunQuadratic
{ {
double kr(double s) double kr(double s)
{ {
return s*s; return s*s;
} }
double dkrds(double s) double dkrds(double s)
{ {
return 2.0*s; return 2.0*s;
} }
}; };
template <class Fun> template <class Fun>
static inline void evalAllKrDeriv(const int n, const int np, static inline void evalAllKrDeriv(const int n, const int np,
const double* s, double* kr, double* dkrds, Fun fun) const double* s, double* kr, double* dkrds, Fun fun)
{ {
if (dkrds == 0) { if (dkrds == 0) {
// #pragma omp parallel for // #pragma omp parallel for
for (int i = 0; i < n*np; ++i) { for (int i = 0; i < n*np; ++i) {
kr[i] = fun.kr(s[i]); kr[i] = fun.kr(s[i]);
} }
return; return;
} }
// #pragma omp parallel for // #pragma omp parallel for
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
std::fill(dkrds + i*np*np, dkrds + (i+1)*np*np, 0.0); std::fill(dkrds + i*np*np, dkrds + (i+1)*np*np, 0.0);
for (int phase = 0; phase < np; ++phase) { for (int phase = 0; phase < np; ++phase) {
kr[i*np + phase] = fun.kr(s[i*np + phase]); kr[i*np + phase] = fun.kr(s[i*np + phase]);
// Only diagonal elements in derivative. // Only diagonal elements in derivative.
dkrds[i*np*np + phase*np + phase] = fun.dkrds(s[i*np + phase]); dkrds[i*np*np + phase*np + phase] = fun.dkrds(s[i*np + phase]);
} }
} }
} }
} // anon namespace } // anon namespace
@ -109,25 +109,25 @@ namespace Opm
/// Initialize from parameters. /// Initialize from parameters.
void SaturationPropsBasic::init(const parameter::ParameterGroup& param) void SaturationPropsBasic::init(const parameter::ParameterGroup& param)
{ {
int num_phases = param.getDefault("num_phases", 2); int num_phases = param.getDefault("num_phases", 2);
if (num_phases > 2 || num_phases < 1) { if (num_phases > 2 || num_phases < 1) {
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")); 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){
THROW("Constant relperm with more than one phase???"); THROW("Constant relperm with more than one phase???");
} }
} else if (rpf == "Linear") { } else if (rpf == "Linear") {
relperm_func_ = Linear; relperm_func_ = Linear;
} else if (rpf == "Quadratic") { } else if (rpf == "Quadratic") {
relperm_func_ = Quadratic; relperm_func_ = Quadratic;
} else { } else {
THROW("SaturationPropsBasic::init() illegal relperm_func: " << rpf); THROW("SaturationPropsBasic::init() illegal relperm_func: " << rpf);
} }
} }
@ -136,7 +136,7 @@ namespace Opm
/// \return P, the number of phases. /// \return P, the number of phases.
int SaturationPropsBasic::numPhases() const int SaturationPropsBasic::numPhases() const
{ {
return num_phases_; return num_phases_;
} }
@ -152,29 +152,29 @@ namespace Opm
/// m_{ij} = \frac{dkr_i}{ds^j}, /// m_{ij} = \frac{dkr_i}{ds^j},
/// and is output in Fortran order (m_00 m_10 m_20 m01 ...) /// and is output in Fortran order (m_00 m_10 m_20 m01 ...)
void SaturationPropsBasic::relperm(const int n, void SaturationPropsBasic::relperm(const int n,
const double* s, const double* s,
double* kr, double* kr,
double* dkrds) const double* dkrds) const
{ {
switch (relperm_func_) { switch (relperm_func_) {
case Constant: case Constant:
{ {
evalAllKrDeriv(n, num_phases_, s, kr, dkrds, KrFunConstant()); evalAllKrDeriv(n, num_phases_, s, kr, dkrds, KrFunConstant());
break; break;
} }
case Linear: case Linear:
{ {
evalAllKrDeriv(n, num_phases_, s, kr, dkrds, KrFunLinear()); evalAllKrDeriv(n, num_phases_, s, kr, dkrds, KrFunLinear());
break; break;
} }
case Quadratic: case Quadratic:
{ {
evalAllKrDeriv(n, num_phases_, s, kr, dkrds, KrFunQuadratic()); evalAllKrDeriv(n, num_phases_, s, kr, dkrds, KrFunQuadratic());
break; break;
} }
default: default:
THROW("SaturationPropsBasic::relperm() unhandled relperm func type: " << relperm_func_); THROW("SaturationPropsBasic::relperm() unhandled relperm func type: " << relperm_func_);
} }
} }
@ -190,13 +190,13 @@ namespace Opm
/// m_{ij} = \frac{dpc_i}{ds^j}, /// m_{ij} = \frac{dpc_i}{ds^j},
/// and is output in Fortran order (m_00 m_10 m_20 m01 ...) /// and is output in Fortran order (m_00 m_10 m_20 m01 ...)
void SaturationPropsBasic::capPress(const int n, void SaturationPropsBasic::capPress(const int n,
const double* /*s*/, const double* /*s*/,
double* pc, double* pc,
double* dpcds) const double* dpcds) const
{ {
std::fill(pc, pc + num_phases_*n, 0.0); std::fill(pc, pc + num_phases_*n, 0.0);
if (dpcds) { if (dpcds) {
std::fill(dpcds, dpcds + num_phases_*num_phases_*n, 0.0); std::fill(dpcds, dpcds + num_phases_*num_phases_*n, 0.0);
} }
} }
@ -207,11 +207,11 @@ namespace Opm
/// \param[out] smin Array of nP minimum s values, array must be valid before calling. /// \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. /// \param[out] smax Array of nP maximum s values, array must be valid before calling.
void SaturationPropsBasic::satRange(const int n, void SaturationPropsBasic::satRange(const int n,
double* smin, double* smin,
double* smax) const double* smax) const
{ {
std::fill(smin, smin + num_phases_*n, 0.0); std::fill(smin, smin + num_phases_*n, 0.0);
std::fill(smax, smax + num_phases_*n, 1.0); std::fill(smax, smax + num_phases_*n, 1.0);
} }

View File

@ -40,16 +40,16 @@ namespace Opm
SaturationPropsBasic(); SaturationPropsBasic();
/// Initialize from parameters. /// Initialize from parameters.
/// The following parameters are accepted (defaults): /// The following parameters are accepted (defaults):
/// num_phases (2) Must be 1 or 2. /// num_phases (2) Must be 1 or 2.
/// relperm_func ("Linear") Must be "Constant", "Linear" or "Quadratic". /// relperm_func ("Linear") Must be "Constant", "Linear" or "Quadratic".
void init(const parameter::ParameterGroup& param); void init(const parameter::ParameterGroup& param);
enum RelPermFunc { Constant, Linear, Quadratic }; enum RelPermFunc { Constant, Linear, Quadratic };
/// Initialize from arguments a basic Saturation property. /// Initialize from arguments a basic Saturation property.
void init(const int num_phases, void init(const int num_phases,
const RelPermFunc& relperm_func) const RelPermFunc& relperm_func)
{ {
num_phases_ = num_phases; num_phases_ = num_phases;
relperm_func_ = relperm_func; relperm_func_ = relperm_func;
@ -86,18 +86,18 @@ namespace Opm
double* pc, double* pc,
double* dpcds) const; double* dpcds) const;
/// Obtain the range of allowable saturation values. /// Obtain the range of allowable saturation values.
/// \param[in] n Number of data points. /// \param[in] n Number of data points.
/// \param[out] smin Array of nP minimum s values, array must be valid before calling. /// \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. /// \param[out] smax Array of nP maximum s values, array must be valid before calling.
void satRange(const int n, void satRange(const int n,
double* smin, double* smin,
double* smax) const; double* smax) const;
private: private:
int num_phases_; int num_phases_;
RelPermFunc relperm_func_; RelPermFunc relperm_func_;
}; };

View File

@ -19,7 +19,6 @@
#include <opm/core/fluid/SaturationPropsFromDeck.hpp> #include <opm/core/fluid/SaturationPropsFromDeck.hpp>
#include <opm/core/grid.h> #include <opm/core/grid.h>
#include <opm/core/fluid/blackoil/phaseUsageFromDeck.hpp>
#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>
@ -27,236 +26,9 @@
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 UnstructuredGrid& grid)
{
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_);
}
}
/// Initialize from deck, grid and parameters
void SaturationPropsFromDeck::init(const EclipseGridParser& deck,
const UnstructuredGrid& grid,
const parameter::ParameterGroup& param)
{
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.
const int tab_size = param.getDefault("tab_size_kr", 200);
satfuncset_.resize(num_tables);
for (int table = 0; table < num_tables; ++table) {
satfuncset_[table].init(deck, table, phase_usage_, tab_size);
}
}
/// \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::satfunc_t&
SaturationPropsFromDeck::funcForCell(const int cell) const
{
return cell_to_func_.empty() ? satfuncset_[0] : satfuncset_[cell_to_func_[cell]];
}
} // namespace Opm } // namespace Opm

View File

@ -19,12 +19,14 @@
#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/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/SatFuncStone2.hpp>
#include <opm/core/fluid/SatFuncSimple.hpp> #include <opm/core/fluid/SatFuncSimple.hpp>
#include <opm/core/fluid/SatFuncGwseg.hpp>
#include <vector> #include <vector>
struct UnstructuredGrid; struct UnstructuredGrid;
@ -32,23 +34,31 @@ 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 and grid. /// Initialize from deck and grid.
/// \param deck Deck input parser /// \param[in] deck Deck input parser
/// \param grid Grid to which property object applies, needed for the /// \param[in] grid Grid to which property object applies, needed for the
/// mapping from cell indices (typically from a processed grid) /// 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, /// \param[in] samples Number of uniform sample points for saturation tables.
const UnstructuredGrid& grid); /// NOTE: samples will only be used with the SatFuncSetUniform template argument.
void init(const EclipseGridParser& deck, void init(const EclipseGridParser& deck,
const UnstructuredGrid& grid, const UnstructuredGrid& grid,
const parameter::ParameterGroup& param); const int samples);
/// \return P, the number of phases. /// \return P, the number of phases.
int numPhases() const; int numPhases() const;
@ -83,22 +93,23 @@ namespace Opm
double* pc, double* pc,
double* dpcds) const; double* dpcds) const;
/// Obtain the range of allowable saturation values. /// Obtain the range of allowable saturation values.
/// \param[in] n Number of data points. /// \param[in] n Number of data points.
/// \param[out] smin Array of nP minimum s values, array must be valid before calling. /// \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. /// \param[out] smax Array of nP maximum s values, array must be valid before calling.
void satRange(const int n, void satRange(const int n,
const int* cells, const int* cells,
double* smin, double* smin,
double* smax) const; double* smax) const;
private: private:
PhaseUsage phase_usage_; PhaseUsage phase_usage_;
typedef SatFuncSimple satfunc_t; std::vector<SatFuncSet> satfuncset_;
std::vector<satfunc_t> satfuncset_;
std::vector<int> cell_to_func_; // = SATNUM - 1 std::vector<int> cell_to_func_; // = SATNUM - 1
const satfunc_t& funcForCell(const int cell) const; typedef SatFuncSet Funcs;
const Funcs& funcForCell(const int cell) const;
}; };
@ -106,6 +117,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>
@ -43,14 +44,14 @@ namespace Opm
{ {
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.
region_number_ = 0; region_number_ = 0;
phase_usage_ = phaseUsageFromDeck(deck); phase_usage_ = phaseUsageFromDeck(deck);
// Surface densities. Accounting for different orders in eclipse and our code. // Surface densities. Accounting for different orders in eclipse and our code.
if (deck.hasField("DENSITY")) { if (deck.hasField("DENSITY")) {
const std::vector<double>& d = deck.getDENSITY().densities_[region_number_]; const std::vector<double>& d = deck.getDENSITY().densities_[region_number_];
enum { ECL_oil = 0, ECL_water = 1, ECL_gas = 2 }; enum { ECL_oil = 0, ECL_water = 1, ECL_gas = 2 };
if (phase_usage_.phase_used[Aqua]) { if (phase_usage_.phase_used[Aqua]) {
densities_[phase_usage_.phase_pos[Aqua]] = d[ECL_water]; densities_[phase_usage_.phase_pos[Aqua]] = d[ECL_water];
} }
@ -60,9 +61,9 @@ namespace Opm
if (phase_usage_.phase_used[Liquid]) { if (phase_usage_.phase_used[Liquid]) {
densities_[phase_usage_.phase_pos[Liquid]] = d[ECL_oil]; densities_[phase_usage_.phase_pos[Liquid]] = d[ECL_oil];
} }
} else { } else {
THROW("Input is missing DENSITY\n"); THROW("Input is missing DENSITY\n");
} }
// Set the properties. // Set the properties.
props_.resize(phase_usage_.num_phases); props_.resize(phase_usage_.num_phases);
@ -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")) {
props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDead(deck.getPVDO().pvdo_, samples)); if (samples > 0) {
props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDeadSpline(deck.getPVDO().pvdo_, samples));
} else {
props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDead(deck.getPVDO().pvdo_));
}
} else if (deck.hasField("PVTO")) { } 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")) {
@ -87,10 +92,14 @@ namespace Opm
THROW("Input is missing PVDO or PVTO\n"); THROW("Input is missing PVDO or PVTO\n");
} }
} }
// Gas PVT // Gas PVT
if (phase_usage_.phase_used[Vapour]) { if (phase_usage_.phase_used[Vapour]) {
if (deck.hasField("PVDG")) { if (deck.hasField("PVDG")) {
props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDead(deck.getPVDG().pvdg_, samples)); if (samples > 0) {
props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDeadSpline(deck.getPVDG().pvdg_, samples));
} else {
props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDead(deck.getPVDG().pvdg_));
}
} else if (deck.hasField("PVTG")) { } 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, const int samples = 16); /// \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;
@ -64,7 +70,7 @@ namespace Opm
/// Densities of stock components at surface conditions. /// Densities of stock components at surface conditions.
/// \return Array of size numPhases(). /// \return Array of size numPhases().
const double* surfaceDensities() const; const double* surfaceDensities() const;
/// Viscosity as a function of p and z. /// Viscosity as a function of p and z.
void mu(const int n, void mu(const int n,
@ -105,11 +111,11 @@ namespace Opm
PhaseUsage phase_usage_; PhaseUsage phase_usage_;
int region_number_; int region_number_;
std::vector<std::tr1::shared_ptr<SinglePvtInterface> > props_; std::vector<std::tr1::shared_ptr<SinglePvtInterface> > props_;
double densities_[MaxNumPhases]; double densities_[MaxNumPhases];
mutable std::vector<double> data1_; mutable std::vector<double> data1_;
mutable std::vector<double> data2_; mutable std::vector<double> data2_;
}; };

View File

@ -40,12 +40,12 @@ namespace Opm
public: public:
typedef std::vector<std::vector<double> > table_t; typedef std::vector<std::vector<double> > table_t;
SinglePvtConstCompr(const table_t& pvtw) SinglePvtConstCompr(const table_t& pvtw)
{ {
const int region_number = 0; const int region_number = 0;
if (pvtw.size() != 1) { if (pvtw.size() != 1) {
THROW("More than one PVD-region"); THROW("More than one PVD-region");
} }
ref_press_ = pvtw[region_number][0]; ref_press_ = pvtw[region_number][0];
ref_B_ = pvtw[region_number][1]; ref_B_ = pvtw[region_number][1];
comp_ = pvtw[region_number][2]; comp_ = pvtw[region_number][2];
@ -53,7 +53,7 @@ namespace Opm
visc_comp_ = pvtw[region_number][4]; visc_comp_ = pvtw[region_number][4];
} }
SinglePvtConstCompr(double visc) SinglePvtConstCompr(double visc)
: ref_press_(0.0), : ref_press_(0.0),
ref_B_(1.0), ref_B_(1.0),
comp_(0.0), comp_(0.0),
@ -62,7 +62,7 @@ namespace Opm
{ {
} }
virtual ~SinglePvtConstCompr() virtual ~SinglePvtConstCompr()
{ {
} }

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.
@ -33,25 +33,25 @@ namespace Opm
// Member functions // Member functions
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
/// Constructor /// Constructor
SinglePvtDead::SinglePvtDead(const table_t& pvd_table, const int samples) SinglePvtDead::SinglePvtDead(const table_t& pvd_table)
{ {
const int region_number = 0; const int region_number = 0;
if (pvd_table.size() != 1) { if (pvd_table.size() != 1) {
THROW("More than one PVT-region"); THROW("More than one PVT-region");
} }
// Copy data // Copy data
const int sz = pvd_table[region_number][0].size(); const int sz = pvd_table[region_number][0].size();
std::vector<double> press(sz); std::vector<double> press(sz);
std::vector<double> B_inv(sz); std::vector<double> B_inv(sz);
std::vector<double> visc(sz); std::vector<double> visc(sz);
for (int i = 0; i < sz; ++i) { for (int i = 0; i < sz; ++i) {
press[i] = pvd_table[region_number][0][i]; press[i] = pvd_table[region_number][0][i];
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];
} }
buildUniformMonotoneTable(press, B_inv, samples, one_over_B_); one_over_B_ = NonuniformTableLinear<double>(press, B_inv);
buildUniformMonotoneTable(press, visc, samples, viscosity_); viscosity_ = NonuniformTableLinear<double>(press, visc);
// 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
@ -36,9 +36,9 @@ namespace Opm
class SinglePvtDead : public SinglePvtInterface class SinglePvtDead : public SinglePvtInterface
{ {
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, const int samples = 16); SinglePvtDead(const table_t& pvd_table);
virtual ~SinglePvtDead(); virtual ~SinglePvtDead();
/// Viscosity as a function of p and z. /// Viscosity as a function of p and z.
virtual void mu(const int n, virtual void mu(const int n,
@ -72,12 +72,12 @@ namespace Opm
double* output_R, double* output_R,
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

@ -32,7 +32,7 @@ namespace Opm
public: public:
SinglePvtInterface(); SinglePvtInterface();
virtual ~SinglePvtInterface(); virtual ~SinglePvtInterface();
/// \param[in] num_phases The number of active phases. /// \param[in] num_phases The number of active phases.
/// \param[in] phase_pos Array of BlackpoilPhases::MaxNumPhases /// \param[in] phase_pos Array of BlackpoilPhases::MaxNumPhases

View File

@ -1,13 +1,13 @@
//=========================================================================== //===========================================================================
// //
// File: MiscibilityLiveGas.cpp // File: MiscibilityLiveGas.cpp
// //
// Created: Wed Feb 10 09:21:53 2010 // Created: Wed Feb 10 09:21:53 2010
// //
// Author: Bjørn Spjelkavik <bsp@sintef.no> // Author: Bjørn Spjelkavik <bsp@sintef.no>
// //
// Revision: $Id$ // Revision: $Id$
// //
//=========================================================================== //===========================================================================
/* /*
Copyright 2010 SINTEF ICT, Applied Mathematics. Copyright 2010 SINTEF ICT, Applied Mathematics.
@ -47,37 +47,37 @@ namespace Opm
/// Constructor /// Constructor
SinglePvtLiveGas::SinglePvtLiveGas(const table_t& pvtg) SinglePvtLiveGas::SinglePvtLiveGas(const table_t& pvtg)
{ {
// GAS, PVTG // GAS, PVTG
const int region_number = 0; const int region_number = 0;
if (pvtg.size() != 1) { if (pvtg.size() != 1) {
THROW("More than one PVD-region"); THROW("More than one PVD-region");
} }
saturated_gas_table_.resize(4); saturated_gas_table_.resize(4);
const int sz = pvtg[region_number].size(); const int sz = pvtg[region_number].size();
for (int k=0; k<4; ++k) { for (int k=0; k<4; ++k) {
saturated_gas_table_[k].resize(sz); saturated_gas_table_[k].resize(sz);
} }
for (int i=0; i<sz; ++i) { for (int i=0; i<sz; ++i) {
saturated_gas_table_[0][i] = pvtg[region_number][i][0]; // p saturated_gas_table_[0][i] = pvtg[region_number][i][0]; // p
saturated_gas_table_[1][i] = pvtg[region_number][i][2]; // Bg saturated_gas_table_[1][i] = pvtg[region_number][i][2]; // Bg
saturated_gas_table_[2][i] = pvtg[region_number][i][3]; // mu_g saturated_gas_table_[2][i] = pvtg[region_number][i][3]; // mu_g
saturated_gas_table_[3][i] = pvtg[region_number][i][1]; // Rv saturated_gas_table_[3][i] = pvtg[region_number][i][1]; // Rv
} }
undersat_gas_tables_.resize(sz); undersat_gas_tables_.resize(sz);
for (int i=0; i<sz; ++i) { for (int i=0; i<sz; ++i) {
undersat_gas_tables_[i].resize(3); undersat_gas_tables_[i].resize(3);
int tsize = (pvtg[region_number][i].size() - 1)/3; int tsize = (pvtg[region_number][i].size() - 1)/3;
undersat_gas_tables_[i][0].resize(tsize); undersat_gas_tables_[i][0].resize(tsize);
undersat_gas_tables_[i][1].resize(tsize); undersat_gas_tables_[i][1].resize(tsize);
undersat_gas_tables_[i][2].resize(tsize); undersat_gas_tables_[i][2].resize(tsize);
for (int j=0, k=0; j<tsize; ++j) { for (int j=0, k=0; j<tsize; ++j) {
undersat_gas_tables_[i][0][j] = pvtg[region_number][i][++k]; // Rv undersat_gas_tables_[i][0][j] = pvtg[region_number][i][++k]; // Rv
undersat_gas_tables_[i][1][j] = pvtg[region_number][i][++k]; // Bg undersat_gas_tables_[i][1][j] = pvtg[region_number][i][++k]; // Bg
undersat_gas_tables_[i][2][j] = pvtg[region_number][i][++k]; // mu_g undersat_gas_tables_[i][2][j] = pvtg[region_number][i][++k]; // mu_g
} }
} }
} }
// Destructor // Destructor
@ -184,16 +184,16 @@ namespace Opm
// To handle no-gas case. // To handle no-gas case.
return 0.0; return 0.0;
} }
double satR = linearInterpolationExtrap(saturated_gas_table_[0], double satR = linearInterpolationExtrap(saturated_gas_table_[0],
saturated_gas_table_[3], press); saturated_gas_table_[3], press);
double maxR = surfvol[phase_pos_[Liquid]]/surfvol[phase_pos_[Vapour]]; double maxR = surfvol[phase_pos_[Liquid]]/surfvol[phase_pos_[Vapour]];
if (satR < maxR ) { if (satR < maxR ) {
// Saturated case // Saturated case
return satR; return satR;
} else { } else {
// Undersaturated case // Undersaturated case
return maxR; return maxR;
} }
} }
void SinglePvtLiveGas::evalRDeriv(const double press, const double* surfvol, void SinglePvtLiveGas::evalRDeriv(const double press, const double* surfvol,
@ -205,20 +205,20 @@ namespace Opm
dRdpval = 0.0; dRdpval = 0.0;
return; return;
} }
double satR = linearInterpolationExtrap(saturated_gas_table_[0], double satR = linearInterpolationExtrap(saturated_gas_table_[0],
saturated_gas_table_[3], press); saturated_gas_table_[3], press);
double maxR = surfvol[phase_pos_[Liquid]]/surfvol[phase_pos_[Vapour]]; double maxR = surfvol[phase_pos_[Liquid]]/surfvol[phase_pos_[Vapour]];
if (satR < maxR ) { if (satR < maxR ) {
// Saturated case // Saturated case
Rval = satR; Rval = satR;
dRdpval = linearInterpolDerivative(saturated_gas_table_[0], dRdpval = linearInterpolDerivative(saturated_gas_table_[0],
saturated_gas_table_[3], saturated_gas_table_[3],
press); press);
} else { } else {
// Undersaturated case // Undersaturated case
Rval = maxR; Rval = maxR;
dRdpval = 0.0; dRdpval = 0.0;
} }
} }
double SinglePvtLiveGas::miscible_gas(const double press, double SinglePvtLiveGas::miscible_gas(const double press,
@ -226,81 +226,81 @@ namespace Opm
const int item, const int item,
const bool deriv) const const bool deriv) const
{ {
int section; int section;
double Rval = linearInterpolationExtrap(saturated_gas_table_[0], double Rval = linearInterpolationExtrap(saturated_gas_table_[0],
saturated_gas_table_[3], press, saturated_gas_table_[3], press,
section); section);
double maxR = surfvol[phase_pos_[Liquid]]/surfvol[phase_pos_[Vapour]]; double maxR = surfvol[phase_pos_[Liquid]]/surfvol[phase_pos_[Vapour]];
if (deriv) { if (deriv) {
if (Rval < maxR ) { // Saturated case if (Rval < maxR ) { // Saturated case
return linearInterpolDerivative(saturated_gas_table_[0], return linearInterpolDerivative(saturated_gas_table_[0],
saturated_gas_table_[item], saturated_gas_table_[item],
press); press);
} else { // Undersaturated case } else { // Undersaturated case
int is = section; int is = section;
if (undersat_gas_tables_[is][0].size() < 2) { if (undersat_gas_tables_[is][0].size() < 2) {
double val = (saturated_gas_table_[item][is+1] double val = (saturated_gas_table_[item][is+1]
- saturated_gas_table_[item][is]) / - saturated_gas_table_[item][is]) /
(saturated_gas_table_[0][is+1] - (saturated_gas_table_[0][is+1] -
saturated_gas_table_[0][is]); saturated_gas_table_[0][is]);
return val; return val;
} }
double val1 = double val1 =
linearInterpolationExtrap(undersat_gas_tables_[is][0], linearInterpolationExtrap(undersat_gas_tables_[is][0],
undersat_gas_tables_[is][item], undersat_gas_tables_[is][item],
maxR); maxR);
double val2 = double val2 =
linearInterpolationExtrap(undersat_gas_tables_[is+1][0], linearInterpolationExtrap(undersat_gas_tables_[is+1][0],
undersat_gas_tables_[is+1][item], undersat_gas_tables_[is+1][item],
maxR); maxR);
double val = (val2 - val1)/ double val = (val2 - val1)/
(saturated_gas_table_[0][is+1] - saturated_gas_table_[0][is]); (saturated_gas_table_[0][is+1] - saturated_gas_table_[0][is]);
return val; return val;
} }
} else { } else {
if (Rval < maxR ) { // Saturated case if (Rval < maxR ) { // Saturated case
return linearInterpolationExtrap(saturated_gas_table_[0], return linearInterpolationExtrap(saturated_gas_table_[0],
saturated_gas_table_[item], saturated_gas_table_[item],
press); press);
} else { // Undersaturated case } else { // Undersaturated case
int is = section; int is = section;
// Extrapolate from first table section // Extrapolate from first table section
if (is == 0 && press < saturated_gas_table_[0][0]) { if (is == 0 && press < saturated_gas_table_[0][0]) {
return linearInterpolationExtrap(undersat_gas_tables_[0][0], return linearInterpolationExtrap(undersat_gas_tables_[0][0],
undersat_gas_tables_[0][item], undersat_gas_tables_[0][item],
maxR); maxR);
} }
// Extrapolate from last table section // Extrapolate from last table section
int ltp = saturated_gas_table_[0].size() - 1; int ltp = saturated_gas_table_[0].size() - 1;
if (is+1 == ltp && press > saturated_gas_table_[0][ltp]) { if (is+1 == ltp && press > saturated_gas_table_[0][ltp]) {
return linearInterpolationExtrap(undersat_gas_tables_[ltp][0], return linearInterpolationExtrap(undersat_gas_tables_[ltp][0],
undersat_gas_tables_[ltp][item], undersat_gas_tables_[ltp][item],
maxR); maxR);
} }
// Interpolate between table sections // Interpolate between table sections
double w = (press - saturated_gas_table_[0][is]) / double w = (press - saturated_gas_table_[0][is]) /
(saturated_gas_table_[0][is+1] - (saturated_gas_table_[0][is+1] -
saturated_gas_table_[0][is]); saturated_gas_table_[0][is]);
if (undersat_gas_tables_[is][0].size() < 2) { if (undersat_gas_tables_[is][0].size() < 2) {
double val = saturated_gas_table_[item][is] + double val = saturated_gas_table_[item][is] +
w*(saturated_gas_table_[item][is+1] - w*(saturated_gas_table_[item][is+1] -
saturated_gas_table_[item][is]); saturated_gas_table_[item][is]);
return val; return val;
} }
double val1 = double val1 =
linearInterpolationExtrap(undersat_gas_tables_[is][0], linearInterpolationExtrap(undersat_gas_tables_[is][0],
undersat_gas_tables_[is][item], undersat_gas_tables_[is][item],
maxR); maxR);
double val2 = double val2 =
linearInterpolationExtrap(undersat_gas_tables_[is+1][0], linearInterpolationExtrap(undersat_gas_tables_[is+1][0],
undersat_gas_tables_[is+1][item], undersat_gas_tables_[is+1][item],
maxR); maxR);
double val = val1 + w*(val2 - val1); double val = val1 + w*(val2 - val1);
return val; return val;
} }
} }
} }

View File

@ -33,10 +33,10 @@ namespace Opm
class SinglePvtLiveGas : public SinglePvtInterface class SinglePvtLiveGas : public SinglePvtInterface
{ {
public: public:
typedef std::vector<std::vector<std::vector<double> > > table_t; typedef std::vector<std::vector<std::vector<double> > > table_t;
SinglePvtLiveGas(const table_t& pvto); SinglePvtLiveGas(const table_t& pvto);
virtual ~SinglePvtLiveGas(); virtual ~SinglePvtLiveGas();
/// Viscosity as a function of p and z. /// Viscosity as a function of p and z.
virtual void mu(const int n, virtual void mu(const int n,
@ -76,14 +76,14 @@ namespace Opm
double evalR(double press, const double* surfvol) const; double evalR(double press, const double* surfvol) const;
void evalRDeriv(double press, const double* surfvol, double& R, double& dRdp) const; void evalRDeriv(double press, const double* surfvol, double& R, double& dRdp) const;
// item: 1=>B 2=>mu; // item: 1=>B 2=>mu;
double miscible_gas(const double press, double miscible_gas(const double press,
const double* surfvol, const double* surfvol,
const int item, const int item,
const bool deriv = false) const; const bool deriv = false) const;
// PVT properties of wet gas (with vaporised oil) // PVT properties of wet gas (with vaporised oil)
std::vector<std::vector<double> > saturated_gas_table_; std::vector<std::vector<double> > saturated_gas_table_;
std::vector<std::vector<std::vector<double> > > undersat_gas_tables_; std::vector<std::vector<std::vector<double> > > undersat_gas_tables_;
}; };

View File

@ -38,122 +38,122 @@ namespace Opm
/// Constructor /// Constructor
SinglePvtLiveOil::SinglePvtLiveOil(const table_t& pvto) SinglePvtLiveOil::SinglePvtLiveOil(const table_t& pvto)
{ {
// OIL, PVTO // OIL, PVTO
const int region_number = 0; const int region_number = 0;
if (pvto.size() != 1) { if (pvto.size() != 1) {
THROW("More than one PVD-region"); THROW("More than one PVD-region");
} }
saturated_oil_table_.resize(4); saturated_oil_table_.resize(4);
const int sz = pvto[region_number].size(); const int sz = pvto[region_number].size();
for (int k=0; k<4; ++k) { for (int k=0; k<4; ++k) {
saturated_oil_table_[k].resize(sz); saturated_oil_table_[k].resize(sz);
} }
for (int i=0; i<sz; ++i) { for (int i=0; i<sz; ++i) {
saturated_oil_table_[0][i] = pvto[region_number][i][1]; // p saturated_oil_table_[0][i] = pvto[region_number][i][1]; // p
saturated_oil_table_[1][i] = 1.0/pvto[region_number][i][2]; // 1/Bo saturated_oil_table_[1][i] = 1.0/pvto[region_number][i][2]; // 1/Bo
saturated_oil_table_[2][i] = pvto[region_number][i][3]; // mu_o saturated_oil_table_[2][i] = pvto[region_number][i][3]; // mu_o
saturated_oil_table_[3][i] = pvto[region_number][i][0]; // Rs saturated_oil_table_[3][i] = pvto[region_number][i][0]; // Rs
} }
undersat_oil_tables_.resize(sz); undersat_oil_tables_.resize(sz);
for (int i=0; i<sz; ++i) { for (int i=0; i<sz; ++i) {
undersat_oil_tables_[i].resize(3); undersat_oil_tables_[i].resize(3);
int tsize = (pvto[region_number][i].size() - 1)/3; int tsize = (pvto[region_number][i].size() - 1)/3;
undersat_oil_tables_[i][0].resize(tsize); undersat_oil_tables_[i][0].resize(tsize);
undersat_oil_tables_[i][1].resize(tsize); undersat_oil_tables_[i][1].resize(tsize);
undersat_oil_tables_[i][2].resize(tsize); undersat_oil_tables_[i][2].resize(tsize);
for (int j=0, k=0; j<tsize; ++j) { for (int j=0, k=0; j<tsize; ++j) {
undersat_oil_tables_[i][0][j] = pvto[region_number][i][++k]; // p undersat_oil_tables_[i][0][j] = pvto[region_number][i][++k]; // p
undersat_oil_tables_[i][1][j] = 1.0/pvto[region_number][i][++k]; // 1/Bo undersat_oil_tables_[i][1][j] = 1.0/pvto[region_number][i][++k]; // 1/Bo
undersat_oil_tables_[i][2][j] = pvto[region_number][i][++k]; // mu_o undersat_oil_tables_[i][2][j] = pvto[region_number][i][++k]; // mu_o
} }
} }
// Fill in additional entries in undersaturated tables by interpolating/extrapolating 1/Bo and mu_o ... // Fill in additional entries in undersaturated tables by interpolating/extrapolating 1/Bo and mu_o ...
int iPrev = -1; int iPrev = -1;
int iNext = 1; int iNext = 1;
while (undersat_oil_tables_[iNext][0].size() < 2) { while (undersat_oil_tables_[iNext][0].size() < 2) {
++iNext; ++iNext;
} }
ASSERT(iNext < sz); ASSERT(iNext < sz);
for (int i=0; i<sz; ++i) { for (int i=0; i<sz; ++i) {
if (undersat_oil_tables_[i][0].size() > 1) { if (undersat_oil_tables_[i][0].size() > 1) {
iPrev = i; iPrev = i;
continue; continue;
} }
bool flagPrev = (iPrev >= 0); bool flagPrev = (iPrev >= 0);
bool flagNext = true; bool flagNext = true;
if (iNext < i) { if (iNext < i) {
iPrev = iNext; iPrev = iNext;
flagPrev = true; flagPrev = true;
iNext = i+1; iNext = i+1;
while (undersat_oil_tables_[iNext][0].size() < 2) { while (undersat_oil_tables_[iNext][0].size() < 2) {
++iNext; ++iNext;
} }
} }
double slopePrevBinv = 0.0; double slopePrevBinv = 0.0;
double slopePrevVisc = 0.0; double slopePrevVisc = 0.0;
double slopeNextBinv = 0.0; double slopeNextBinv = 0.0;
double slopeNextVisc = 0.0; double slopeNextVisc = 0.0;
while (flagPrev || flagNext) { while (flagPrev || flagNext) {
double pressure0 = undersat_oil_tables_[i][0].back(); double pressure0 = undersat_oil_tables_[i][0].back();
double pressure = 1.0e47; double pressure = 1.0e47;
if (flagPrev) { if (flagPrev) {
std::vector<double>::iterator itPrev = upper_bound(undersat_oil_tables_[iPrev][0].begin(), std::vector<double>::iterator itPrev = upper_bound(undersat_oil_tables_[iPrev][0].begin(),
undersat_oil_tables_[iPrev][0].end(),pressure0+1.); undersat_oil_tables_[iPrev][0].end(),pressure0+1.);
if (itPrev == undersat_oil_tables_[iPrev][0].end()) { if (itPrev == undersat_oil_tables_[iPrev][0].end()) {
--itPrev; // Extrapolation ... --itPrev; // Extrapolation ...
} else if (itPrev == undersat_oil_tables_[iPrev][0].begin()) { } else if (itPrev == undersat_oil_tables_[iPrev][0].begin()) {
++itPrev; ++itPrev;
} }
if (itPrev == undersat_oil_tables_[iPrev][0].end()-1) { if (itPrev == undersat_oil_tables_[iPrev][0].end()-1) {
flagPrev = false; // Last data set for "prev" ... flagPrev = false; // Last data set for "prev" ...
} }
double dPPrev = *itPrev - *(itPrev-1); double dPPrev = *itPrev - *(itPrev-1);
pressure = *itPrev; pressure = *itPrev;
int index = int(itPrev - undersat_oil_tables_[iPrev][0].begin()); int index = int(itPrev - undersat_oil_tables_[iPrev][0].begin());
slopePrevBinv = (undersat_oil_tables_[iPrev][1][index] - undersat_oil_tables_[iPrev][1][index-1])/dPPrev; slopePrevBinv = (undersat_oil_tables_[iPrev][1][index] - undersat_oil_tables_[iPrev][1][index-1])/dPPrev;
slopePrevVisc = (undersat_oil_tables_[iPrev][2][index] - undersat_oil_tables_[iPrev][2][index-1])/dPPrev; slopePrevVisc = (undersat_oil_tables_[iPrev][2][index] - undersat_oil_tables_[iPrev][2][index-1])/dPPrev;
} }
if (flagNext) { if (flagNext) {
std::vector<double>::iterator itNext = upper_bound(undersat_oil_tables_[iNext][0].begin(), std::vector<double>::iterator itNext = upper_bound(undersat_oil_tables_[iNext][0].begin(),
undersat_oil_tables_[iNext][0].end(),pressure0+1.); undersat_oil_tables_[iNext][0].end(),pressure0+1.);
if (itNext == undersat_oil_tables_[iNext][0].end()) { if (itNext == undersat_oil_tables_[iNext][0].end()) {
--itNext; // Extrapolation ... --itNext; // Extrapolation ...
} else if (itNext == undersat_oil_tables_[iNext][0].begin()) { } else if (itNext == undersat_oil_tables_[iNext][0].begin()) {
++itNext; ++itNext;
} }
if (itNext == undersat_oil_tables_[iNext][0].end()-1) { if (itNext == undersat_oil_tables_[iNext][0].end()-1) {
flagNext = false; // Last data set for "next" ... flagNext = false; // Last data set for "next" ...
} }
double dPNext = *itNext - *(itNext-1); double dPNext = *itNext - *(itNext-1);
if (flagPrev) { if (flagPrev) {
pressure = std::min(pressure,*itNext); pressure = std::min(pressure,*itNext);
} else { } else {
pressure = *itNext; pressure = *itNext;
} }
int index = int(itNext - undersat_oil_tables_[iNext][0].begin()); int index = int(itNext - undersat_oil_tables_[iNext][0].begin());
slopeNextBinv = (undersat_oil_tables_[iNext][1][index] - undersat_oil_tables_[iNext][1][index-1])/dPNext; slopeNextBinv = (undersat_oil_tables_[iNext][1][index] - undersat_oil_tables_[iNext][1][index-1])/dPNext;
slopeNextVisc = (undersat_oil_tables_[iNext][2][index] - undersat_oil_tables_[iNext][2][index-1])/dPNext; slopeNextVisc = (undersat_oil_tables_[iNext][2][index] - undersat_oil_tables_[iNext][2][index-1])/dPNext;
} }
double dP = pressure - pressure0; double dP = pressure - pressure0;
if (iPrev >= 0) { if (iPrev >= 0) {
double w = (saturated_oil_table_[3][i] - saturated_oil_table_[3][iPrev]) / double w = (saturated_oil_table_[3][i] - saturated_oil_table_[3][iPrev]) /
(saturated_oil_table_[3][iNext] - saturated_oil_table_[3][iPrev]); (saturated_oil_table_[3][iNext] - saturated_oil_table_[3][iPrev]);
undersat_oil_tables_[i][0].push_back(pressure0+dP); undersat_oil_tables_[i][0].push_back(pressure0+dP);
undersat_oil_tables_[i][1].push_back(undersat_oil_tables_[i][1].back() + undersat_oil_tables_[i][1].push_back(undersat_oil_tables_[i][1].back() +
dP*(slopePrevBinv+w*(slopeNextBinv-slopePrevBinv))); dP*(slopePrevBinv+w*(slopeNextBinv-slopePrevBinv)));
undersat_oil_tables_[i][2].push_back(undersat_oil_tables_[i][2].back() + undersat_oil_tables_[i][2].push_back(undersat_oil_tables_[i][2].back() +
dP*(slopePrevVisc+w*(slopeNextVisc-slopePrevVisc))); dP*(slopePrevVisc+w*(slopeNextVisc-slopePrevVisc)));
} else { } else {
undersat_oil_tables_[i][0].push_back(pressure0+dP); undersat_oil_tables_[i][0].push_back(pressure0+dP);
undersat_oil_tables_[i][1].push_back(undersat_oil_tables_[i][1].back()+dP*slopeNextBinv); undersat_oil_tables_[i][1].push_back(undersat_oil_tables_[i][1].back()+dP*slopeNextBinv);
undersat_oil_tables_[i][2].push_back(undersat_oil_tables_[i][2].back()+dP*slopeNextVisc); undersat_oil_tables_[i][2].push_back(undersat_oil_tables_[i][2].back()+dP*slopeNextVisc);
} }
} }
} }
} }
/// Destructor. /// Destructor.
@ -238,30 +238,30 @@ namespace Opm
double SinglePvtLiveOil::evalB(double press, const double* surfvol) const double SinglePvtLiveOil::evalB(double press, const double* surfvol) const
{ {
// if (surfvol[phase_pos_[Liquid]] == 0.0) return 1.0; // To handle no-oil case. // if (surfvol[phase_pos_[Liquid]] == 0.0) return 1.0; // To handle no-oil case.
return 1.0/miscible_oil(press, surfvol, 1, false); return 1.0/miscible_oil(press, surfvol, 1, false);
} }
void SinglePvtLiveOil::evalBDeriv(const double press, const double* surfvol, void SinglePvtLiveOil::evalBDeriv(const double press, const double* surfvol,
double& Bval, double& dBdpval) const double& Bval, double& dBdpval) const
{ {
Bval = evalB(press, surfvol); Bval = evalB(press, surfvol);
dBdpval = -Bval*Bval*miscible_oil(press, surfvol, 1, true); dBdpval = -Bval*Bval*miscible_oil(press, surfvol, 1, true);
} }
double SinglePvtLiveOil::evalR(double press, const double* surfvol) const double SinglePvtLiveOil::evalR(double press, const double* surfvol) const
{ {
if (surfvol[phase_pos_[Vapour]] == 0.0) { if (surfvol[phase_pos_[Vapour]] == 0.0) {
return 0.0; return 0.0;
} }
double Rval = linearInterpolationExtrap(saturated_oil_table_[0], double Rval = linearInterpolationExtrap(saturated_oil_table_[0],
saturated_oil_table_[3], press); saturated_oil_table_[3], press);
double maxR = surfvol[phase_pos_[Vapour]]/surfvol[phase_pos_[Liquid]]; double maxR = surfvol[phase_pos_[Vapour]]/surfvol[phase_pos_[Liquid]];
if (Rval < maxR ) { // Saturated case if (Rval < maxR ) { // Saturated case
return Rval; return Rval;
} else { } else {
return maxR; // Undersaturated case return maxR; // Undersaturated case
} }
} }
void SinglePvtLiveOil::evalRDeriv(const double press, const double* surfvol, void SinglePvtLiveOil::evalRDeriv(const double press, const double* surfvol,
@ -272,19 +272,19 @@ namespace Opm
dRdpval = 0.0; dRdpval = 0.0;
return; return;
} }
Rval = linearInterpolationExtrap(saturated_oil_table_[0], Rval = linearInterpolationExtrap(saturated_oil_table_[0],
saturated_oil_table_[3], press); saturated_oil_table_[3], press);
double maxR = surfvol[phase_pos_[Vapour]]/surfvol[phase_pos_[Liquid]]; double maxR = surfvol[phase_pos_[Vapour]]/surfvol[phase_pos_[Liquid]];
if (Rval < maxR ) { if (Rval < maxR ) {
// Saturated case // Saturated case
dRdpval = linearInterpolDerivative(saturated_oil_table_[0], dRdpval = linearInterpolDerivative(saturated_oil_table_[0],
saturated_oil_table_[3], saturated_oil_table_[3],
press); press);
} else { } else {
// Undersaturated case // Undersaturated case
Rval = maxR; Rval = maxR;
dRdpval = 0.0; dRdpval = 0.0;
} }
} }
@ -293,57 +293,57 @@ namespace Opm
const int item, const int item,
const bool deriv) const const bool deriv) const
{ {
int section; int section;
double Rval = linearInterpolationExtrap(saturated_oil_table_[0], double Rval = linearInterpolationExtrap(saturated_oil_table_[0],
saturated_oil_table_[3], saturated_oil_table_[3],
press, section); press, section);
double maxR = (surfvol[phase_pos_[Liquid]] == 0.0) ? 0.0 : surfvol[phase_pos_[Vapour]]/surfvol[phase_pos_[Liquid]]; double maxR = (surfvol[phase_pos_[Liquid]] == 0.0) ? 0.0 : surfvol[phase_pos_[Vapour]]/surfvol[phase_pos_[Liquid]];
if (deriv) { if (deriv) {
if (Rval < maxR ) { // Saturated case if (Rval < maxR ) { // Saturated case
return linearInterpolDerivative(saturated_oil_table_[0], return linearInterpolDerivative(saturated_oil_table_[0],
saturated_oil_table_[item], saturated_oil_table_[item],
press); press);
} else { // Undersaturated case } else { // Undersaturated case
int is = tableIndex(saturated_oil_table_[3], maxR);
double w = (maxR - saturated_oil_table_[3][is]) /
(saturated_oil_table_[3][is+1] - saturated_oil_table_[3][is]);
ASSERT(undersat_oil_tables_[is][0].size() >= 2);
ASSERT(undersat_oil_tables_[is+1][0].size() >= 2);
double val1 =
linearInterpolDerivative(undersat_oil_tables_[is][0],
undersat_oil_tables_[is][item],
press);
double val2 =
linearInterpolDerivative(undersat_oil_tables_[is+1][0],
undersat_oil_tables_[is+1][item],
press);
double val = val1 + w*(val2 - val1);
return val;
}
} else {
if (Rval < maxR ) { // Saturated case
return linearInterpolationExtrap(saturated_oil_table_[0],
saturated_oil_table_[item],
press);
} else { // Undersaturated case
// Interpolate between table sections
int is = tableIndex(saturated_oil_table_[3], maxR); int is = tableIndex(saturated_oil_table_[3], maxR);
double w = (maxR - saturated_oil_table_[3][is]) / double w = (maxR - saturated_oil_table_[3][is]) /
(saturated_oil_table_[3][is+1] - saturated_oil_table_[3][is]); (saturated_oil_table_[3][is+1] - saturated_oil_table_[3][is]);
ASSERT(undersat_oil_tables_[is][0].size() >= 2); ASSERT(undersat_oil_tables_[is][0].size() >= 2);
ASSERT(undersat_oil_tables_[is+1][0].size() >= 2); ASSERT(undersat_oil_tables_[is+1][0].size() >= 2);
double val1 = double val1 =
linearInterpolationExtrap(undersat_oil_tables_[is][0], linearInterpolDerivative(undersat_oil_tables_[is][0],
undersat_oil_tables_[is][item], undersat_oil_tables_[is][item],
press); press);
double val2 = double val2 =
linearInterpolationExtrap(undersat_oil_tables_[is+1][0], linearInterpolDerivative(undersat_oil_tables_[is+1][0],
undersat_oil_tables_[is+1][item], undersat_oil_tables_[is+1][item],
press); press);
double val = val1 + w*(val2 - val1); double val = val1 + w*(val2 - val1);
return val; return val;
} }
} } else {
if (Rval < maxR ) { // Saturated case
return linearInterpolationExtrap(saturated_oil_table_[0],
saturated_oil_table_[item],
press);
} else { // Undersaturated case
// Interpolate between table sections
int is = tableIndex(saturated_oil_table_[3], maxR);
double w = (maxR - saturated_oil_table_[3][is]) /
(saturated_oil_table_[3][is+1] - saturated_oil_table_[3][is]);
ASSERT(undersat_oil_tables_[is][0].size() >= 2);
ASSERT(undersat_oil_tables_[is+1][0].size() >= 2);
double val1 =
linearInterpolationExtrap(undersat_oil_tables_[is][0],
undersat_oil_tables_[is][item],
press);
double val2 =
linearInterpolationExtrap(undersat_oil_tables_[is+1][0],
undersat_oil_tables_[is+1][item],
press);
double val = val1 + w*(val2 - val1);
return val;
}
}
} }
} // namespace Opm } // namespace Opm

View File

@ -34,10 +34,10 @@ namespace Opm
class SinglePvtLiveOil : public SinglePvtInterface class SinglePvtLiveOil : public SinglePvtInterface
{ {
public: public:
typedef std::vector<std::vector<std::vector<double> > > table_t; typedef std::vector<std::vector<std::vector<double> > > table_t;
SinglePvtLiveOil(const table_t& pvto); SinglePvtLiveOil(const table_t& pvto);
virtual ~SinglePvtLiveOil(); virtual ~SinglePvtLiveOil();
/// Viscosity as a function of p and z. /// Viscosity as a function of p and z.
virtual void mu(const int n, virtual void mu(const int n,
@ -77,15 +77,15 @@ namespace Opm
double evalR(double press, const double* surfvol) const; double evalR(double press, const double* surfvol) const;
void evalRDeriv(double press, const double* surfvol, double& R, double& dRdp) const; void evalRDeriv(double press, const double* surfvol, double& R, double& dRdp) const;
// item: 1=>1/B 2=>mu; // item: 1=>1/B 2=>mu;
double miscible_oil(const double press, double miscible_oil(const double press,
const double* surfvol, const double* surfvol,
const int item, const int item,
const bool deriv = false) const; const bool deriv = false) const;
// PVT properties of live oil (with dissolved gas) // PVT properties of live oil (with dissolved gas)
std::vector<std::vector<double> > saturated_oil_table_; std::vector<std::vector<double> > saturated_oil_table_;
std::vector<std::vector<std::vector<double> > > undersat_oil_tables_; std::vector<std::vector<std::vector<double> > > undersat_oil_tables_;
}; };
} }

View File

@ -56,8 +56,9 @@ extern "C" {
struct grdecl { struct grdecl {
int dims[3]; /**< Cartesian box dimensions. */ int dims[3]; /**< Cartesian box dimensions. */
const double *coord; /**< Pillar end-points. */ const double *coord; /**< Pillar end-points. */
const double *zcorn; /**< Explicit "active" map. May be NULL.*/ const double *zcorn; /**< Corner-point depths. */
const int *actnum; /**< Corner-point depths. */ const int *actnum; /**< Explicit "active" map. May be NULL.*/
const double *mapaxes; /**< 6 Element rotation vector - can be NULL. */
}; };
/** /**

View File

@ -26,6 +26,10 @@
#include <opm/core/utility/have_boost_redef.hpp> #include <opm/core/utility/have_boost_redef.hpp>
// Silence compatibility warning from DUNE headers since we don't use
// the deprecated member anyway (in this compilation unit)
#define DUNE_COMMON_FIELDVECTOR_SIZE_IS_METHOD 1
// TODO: clean up includes. // TODO: clean up includes.
#include <dune/common/deprecated.hh> #include <dune/common/deprecated.hh>
#include <dune/istl/bvector.hh> #include <dune/istl/bvector.hh>

View File

@ -593,3 +593,78 @@ clear_well_controls(int well_index, struct Wells *W)
W->ctrls[well_index]->num = 0; W->ctrls[well_index]->num = 0;
} }
} }
/* ---------------------------------------------------------------------- */
struct Wells *
clone_wells(const struct Wells *W)
/* ---------------------------------------------------------------------- */
{
int c, np, nperf, ok, pos, w;
double target;
const int *cells;
const double *WI, *comp_frac, *distr;
enum WellControlType type;
const struct WellControls *ctrls;
struct Wells *ret;
if (W == NULL) {
ret = NULL;
}
else {
np = W->number_of_phases;
ret = create_wells(W->number_of_phases, W->number_of_wells,
W->well_connpos[ W->number_of_wells ]);
if (ret != NULL) {
pos = W->well_connpos[ 0 ];
ok = 1;
for (w = 0; ok && (w < W->number_of_wells); w++) {
nperf = W->well_connpos[w + 1] - pos;
cells = W->well_cells + pos;
WI = W->WI != NULL ? W->WI + pos : NULL;
comp_frac = W->comp_frac != NULL ? W->comp_frac + w*np : NULL;
ok = add_well(W->type[ w ], W->depth_ref[ w ], nperf,
comp_frac, cells, WI, W->name[ w ], ret);
/* Capacity should be sufficient from create_wells() */
assert (ok);
ctrls = W->ctrls[ w ];
if (ok) {
ok = well_controls_reserve(ctrls->num, np, ret->ctrls[ w ]);
for (c = 0; ok && (c < ctrls->num); c++) {
type = ctrls->type [ c ];
target = ctrls->target[ c ];
distr = & ctrls->distr [ c * np ];
ok = append_well_controls(type, target, distr, w, ret);
/* Capacity *should* be sufficient from
* well_controls_reserve() */
assert (ok);
}
}
if (ok) {
set_current_control(w, ctrls->current, ret);
}
pos = W->well_connpos[w + 1];
}
if (! ok) {
destroy_wells(ret);
ret = NULL;
}
}
}
return ret;
}

View File

@ -235,6 +235,19 @@ void
destroy_wells(struct Wells *W); destroy_wells(struct Wells *W);
/**
* Create a deep-copy (i.e., clone) of an existing Wells object, including its
* controls.
*
* @param[in] W Existing Wells object.
* @return Complete clone of the input object. Dispose of resources using
* function destroy_wells() when no longer needed. Returns @c NULL in case of
* allocation failure.
*/
struct Wells *
clone_wells(const struct Wells *W);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -36,6 +36,7 @@
#include <cmath> #include <cmath>
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <numeric>
namespace Opm namespace Opm
{ {
@ -122,7 +123,7 @@ namespace Opm
WellState& well_state) WellState& well_state)
{ {
const int nc = grid_.number_of_cells; const int nc = grid_.number_of_cells;
const int nw = wells_->number_of_wells; const int nw = (wells_ != 0) ? wells_->number_of_wells : 0;
// Set up dynamic data. // Set up dynamic data.
computePerSolveDynamicData(dt, state, well_state); computePerSolveDynamicData(dt, state, well_state);
@ -446,30 +447,54 @@ namespace Opm
/// Compute per-iteration dynamic properties for wells. /// Compute per-iteration dynamic properties for wells.
void CompressibleTpfa::computeWellDynamicData(const double /*dt*/, void CompressibleTpfa::computeWellDynamicData(const double /*dt*/,
const BlackoilState& /*state*/, const BlackoilState& /*state*/,
const WellState& /*well_state*/) const WellState& well_state)
{ {
// These are the variables that get computed by this function: // These are the variables that get computed by this function:
// //
// std::vector<double> wellperf_A_; // std::vector<double> wellperf_A_;
// std::vector<double> wellperf_phasemob_; // std::vector<double> wellperf_phasemob_;
const int np = props_.numPhases(); const int np = props_.numPhases();
const int nw = wells_->number_of_wells; const int nw = (wells_ != 0) ? wells_->number_of_wells : 0;
const int nperf = wells_->well_connpos[nw]; const int nperf = (wells_ != 0) ? wells_->well_connpos[nw] : 0;
wellperf_A_.resize(nperf*np*np); wellperf_A_.resize(nperf*np*np);
wellperf_phasemob_.resize(nperf*np); wellperf_phasemob_.resize(nperf*np);
// The A matrix is set equal to the perforation grid cells' // The A matrix is set equal to the perforation grid cells'
// matrix, for both injectors and producers. // matrix for producers, computed from bhp and injection
// component fractions from
// The mobilities are set equal to the perforation grid cells' // The mobilities are set equal to the perforation grid cells'
// mobilities, for both injectors and producers. // mobilities for producers.
std::vector<double> mu(np);
for (int w = 0; w < nw; ++w) { for (int w = 0; w < nw; ++w) {
bool producer = (wells_->type[w] == PRODUCER);
const double* comp_frac = &wells_->comp_frac[np*w];
for (int j = wells_->well_connpos[w]; j < wells_->well_connpos[w+1]; ++j) { for (int j = wells_->well_connpos[w]; j < wells_->well_connpos[w+1]; ++j) {
const int c = wells_->well_cells[j]; const int c = wells_->well_cells[j];
const double* cA = &cell_A_[np*np*c];
double* wpA = &wellperf_A_[np*np*j]; double* wpA = &wellperf_A_[np*np*j];
std::copy(cA, cA + np*np, wpA);
const double* cM = &cell_phasemob_[np*c];
double* wpM = &wellperf_phasemob_[np*j]; double* wpM = &wellperf_phasemob_[np*j];
std::copy(cM, cM + np, wpM); if (producer) {
const double* cA = &cell_A_[np*np*c];
std::copy(cA, cA + np*np, wpA);
const double* cM = &cell_phasemob_[np*c];
std::copy(cM, cM + np, wpM);
} else {
const double bhp = well_state.bhp()[w];
double perf_p = bhp;
for (int phase = 0; phase < np; ++phase) {
perf_p += wellperf_gpot_[np*j + phase]*comp_frac[phase];
}
// Hack warning: comp_frac is used as a component
// surface-volume variable in calls to matrix() and
// viscosity(), but as a saturation in the call to
// relperm(). This is probably ok as long as injectors
// only inject pure fluids.
props_.matrix(1, &perf_p, comp_frac, &c, wpA, NULL);
props_.viscosity(1, &perf_p, comp_frac, &c, &mu[0], NULL);
ASSERT(std::fabs(std::accumulate(comp_frac, comp_frac + np, 0.0) - 1.0) < 1e-6);
props_.relperm (1, comp_frac, &c, wpM , NULL);
for (int phase = 0; phase < np; ++phase) {
wpM[phase] /= mu[phase];
}
}
} }
} }
} }
@ -487,9 +512,9 @@ namespace Opm
const double* z = &state.surfacevol()[0]; const double* z = &state.surfacevol()[0];
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_); UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
CompletionData completion_data; CompletionData completion_data;
completion_data.gpot = &wellperf_gpot_[0]; completion_data.gpot = ! wellperf_gpot_.empty() ? &wellperf_gpot_[0] : 0;
completion_data.A = &wellperf_A_[0]; completion_data.A = ! wellperf_A_.empty() ? &wellperf_A_[0] : 0;
completion_data.phasemob = &wellperf_phasemob_[0]; completion_data.phasemob = ! wellperf_phasemob_.empty() ? &wellperf_phasemob_[0] : 0;
cfs_tpfa_res_wells wells_tmp; cfs_tpfa_res_wells wells_tmp;
wells_tmp.W = const_cast<Wells*>(wells_); wells_tmp.W = const_cast<Wells*>(wells_);
wells_tmp.data = &completion_data; wells_tmp.data = &completion_data;
@ -574,9 +599,9 @@ namespace Opm
{ {
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_); UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
CompletionData completion_data; CompletionData completion_data;
completion_data.gpot = const_cast<double*>(&wellperf_gpot_[0]); completion_data.gpot = ! wellperf_gpot_.empty() ? const_cast<double*>(&wellperf_gpot_[0]) : 0;
completion_data.A = const_cast<double*>(&wellperf_A_[0]); completion_data.A = ! wellperf_A_.empty() ? const_cast<double*>(&wellperf_A_[0]) : 0;
completion_data.phasemob = const_cast<double*>(&wellperf_phasemob_[0]); completion_data.phasemob = ! wellperf_phasemob_.empty() ? const_cast<double*>(&wellperf_phasemob_[0]) : 0;
cfs_tpfa_res_wells wells_tmp; cfs_tpfa_res_wells wells_tmp;
wells_tmp.W = const_cast<Wells*>(wells_); wells_tmp.W = const_cast<Wells*>(wells_);
wells_tmp.data = &completion_data; wells_tmp.data = &completion_data;
@ -584,6 +609,9 @@ namespace Opm
forces.wells = &wells_tmp; forces.wells = &wells_tmp;
forces.src = NULL; forces.src = NULL;
double* wpress = ! well_state.bhp ().empty() ? & well_state.bhp ()[0] : 0;
double* wflux = ! well_state.perfRates().empty() ? & well_state.perfRates()[0] : 0;
cfs_tpfa_res_flux(gg, cfs_tpfa_res_flux(gg,
&forces, &forces,
props_.numPhases(), props_.numPhases(),
@ -592,9 +620,9 @@ namespace Opm
&face_phasemob_[0], &face_phasemob_[0],
&face_gravcap_[0], &face_gravcap_[0],
&state.pressure()[0], &state.pressure()[0],
&well_state.bhp()[0], wpress,
&state.faceflux()[0], &state.faceflux()[0],
&well_state.perfRates()[0]); wflux);
cfs_tpfa_res_fpress(gg, cfs_tpfa_res_fpress(gg,
props_.numPhases(), props_.numPhases(),
&htrans_[0], &htrans_[0],
@ -604,6 +632,23 @@ namespace Opm
&state.pressure()[0], &state.pressure()[0],
&state.faceflux()[0], &state.faceflux()[0],
&state.facepressure()[0]); &state.facepressure()[0]);
// Compute well perforation pressures (not done by the C code).
if (wells_ != 0) {
const int nw = wells_->number_of_wells;
const int np = props_.numPhases();
for (int w = 0; w < nw; ++w) {
const double* comp_frac = &wells_->comp_frac[np*w];
for (int j = wells_->well_connpos[w]; j < wells_->well_connpos[w+1]; ++j) {
const double bhp = well_state.bhp()[w];
double perf_p = bhp;
for (int phase = 0; phase < np; ++phase) {
perf_p += wellperf_gpot_[np*j + phase]*comp_frac[phase];
}
well_state.perfPress()[j] = perf_p;
}
}
}
} }
} // namespace Opm } // namespace Opm

View File

@ -142,7 +142,7 @@ impl_allocate(struct UnstructuredGrid *G ,
nnu = G->number_of_cells; nnu = G->number_of_cells;
nwperf = 0; nwperf = 0;
if (wells != NULL) { assert (wells->W != NULL); if ((wells != NULL) && (wells->W != NULL)) {
nnu += wells->W->number_of_wells; nnu += wells->W->number_of_wells;
nwperf = wells->W->well_connpos[ wells->W->number_of_wells ]; nwperf = wells->W->well_connpos[ wells->W->number_of_wells ];
} }
@ -185,13 +185,15 @@ construct_matrix(struct UnstructuredGrid *G ,
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
{ {
int f, c1, c2, w, i, nc, nnu; int f, c1, c2, w, i, nc, nnu;
int wells_present;
size_t nnz; size_t nnz;
struct CSRMatrix *A; struct CSRMatrix *A;
nc = nnu = G->number_of_cells; nc = nnu = G->number_of_cells;
if (wells != NULL) {
assert (wells->W != NULL); wells_present = (wells != NULL) && (wells->W != NULL);
if (wells_present) {
nnu += wells->W->number_of_wells; nnu += wells->W->number_of_wells;
} }
@ -214,7 +216,7 @@ construct_matrix(struct UnstructuredGrid *G ,
} }
} }
if (wells != NULL) { if (wells_present) {
/* Well <-> cell connections */ /* Well <-> cell connections */
struct Wells *W = wells->W; struct Wells *W = wells->W;
@ -252,7 +254,7 @@ construct_matrix(struct UnstructuredGrid *G ,
} }
} }
if (wells != NULL) { if (wells_present) {
/* Fill well <-> cell connections */ /* Fill well <-> cell connections */
struct Wells *W = wells->W; struct Wells *W = wells->W;
@ -741,6 +743,29 @@ assemble_completion_to_cell(int c, int wdof, int np, double dt,
} }
/* ---------------------------------------------------------------------- */
static void
welleq_coeff_shut(int np, struct cfs_tpfa_res_data *h,
double *res, double *w2c, double *w2w)
/* ---------------------------------------------------------------------- */
{
int p;
double fwi;
const double *dpflux_w;
/* Sum reservoir phase flux derivatives set by
* compute_darcyflux_and_deriv(). */
dpflux_w = h->pimpl->flux_work + (1 * np);
for (p = 0, fwi = 0.0; p < np; p++) {
fwi += dpflux_w[ p ];
}
*res = 0.0;
*w2c = 0.0;
*w2w = fwi;
}
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
static void static void
welleq_coeff_bhp(int np, double dp, struct cfs_tpfa_res_data *h, welleq_coeff_bhp(int np, double dp, struct cfs_tpfa_res_data *h,
@ -795,7 +820,34 @@ welleq_coeff_resv(int np, struct cfs_tpfa_res_data *h,
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
static void static void
assemble_completion_to_well(int w, int c, int nc, int np, welleq_coeff_surfrate(int i, int np, struct cfs_tpfa_res_data *h,
struct WellControls *ctrl,
double *res, double *w2c, double *w2w)
/* ---------------------------------------------------------------------- */
{
int p;
double f;
const double *pflux, *dpflux_w, *dpflux_c, *distr;
pflux = h->pimpl->compflux_p + (i * (1 * np));
dpflux_w = h->pimpl->compflux_deriv_p + (i * (2 * np));
dpflux_c = dpflux_w + (1 * (1 * np));
distr = ctrl->distr + (ctrl->current * (1 * np));
*res = *w2c = *w2w = 0.0;
for (p = 0; p < np; p++) {
f = distr[ p ];
*res += f * pflux [ p ];
*w2w += f * dpflux_w[ p ];
*w2c += f * dpflux_c[ p ];
}
}
/* ---------------------------------------------------------------------- */
static void
assemble_completion_to_well(int i, int w, int c, int nc, int np,
double pw, double dt, double pw, double dt,
struct cfs_tpfa_res_wells *wells, struct cfs_tpfa_res_wells *wells,
struct cfs_tpfa_res_data *h ) struct cfs_tpfa_res_data *h )
@ -815,23 +867,25 @@ assemble_completion_to_well(int w, int c, int nc, int np,
ctrl = W->ctrls[ w ]; ctrl = W->ctrls[ w ];
if (ctrl->current < 0) { if (ctrl->current < 0) {
assert (0); /* Shut wells currently not supported */ /* Interpreting a negative current control index to mean a shut well */
welleq_coeff_shut(np, h, &res, &w2c, &w2w);
} }
else {
switch (ctrl->type[ ctrl->current ]) {
case BHP :
welleq_coeff_bhp(np, pw - ctrl->target[ ctrl->current ],
h, &res, &w2c, &w2w);
break;
switch (ctrl->type[ ctrl->current ]) { case RESERVOIR_RATE:
case BHP : assert (W->number_of_phases == np);
welleq_coeff_bhp(np, pw - ctrl->target[ ctrl->current ], welleq_coeff_resv(np, h, ctrl, &res, &w2c, &w2w);
h, &res, &w2c, &w2w); break;
break;
case RESERVOIR_RATE: case SURFACE_RATE:
assert (W->number_of_phases == np); welleq_coeff_surfrate(i, np, h, ctrl, &res, &w2c, &w2w);
welleq_coeff_resv(np, h, ctrl, &res, &w2c, &w2w); break;
break; }
case SURFACE_RATE:
assert (0); /* Surface rate currently not supported */
break;
} }
/* Assemble completion contributions */ /* Assemble completion contributions */
@ -854,7 +908,7 @@ assemble_well_contrib(struct cfs_tpfa_res_wells *wells ,
struct cfs_tpfa_res_data *h ) struct cfs_tpfa_res_data *h )
{ {
int w, i, c, np, np2, nc; int w, i, c, np, np2, nc;
int is_neumann; int is_neumann, is_open;
double pw, dp; double pw, dp;
double *WI, *gpot, *pmobp; double *WI, *gpot, *pmobp;
const double *Ac, *dAc; const double *Ac, *dAc;
@ -876,6 +930,7 @@ assemble_well_contrib(struct cfs_tpfa_res_wells *wells ,
for (w = i = 0; w < W->number_of_wells; w++) { for (w = i = 0; w < W->number_of_wells; w++) {
pw = wpress[ w ]; pw = wpress[ w ];
is_open = W->ctrls[w]->current >= 0;
for (; i < W->well_connpos[w + 1]; for (; i < W->well_connpos[w + 1];
i++, gpot += np, pmobp += np) { i++, gpot += np, pmobp += np) {
@ -888,14 +943,16 @@ assemble_well_contrib(struct cfs_tpfa_res_wells *wells ,
init_completion_contrib(i, np, Ac, dAc, h->pimpl); init_completion_contrib(i, np, Ac, dAc, h->pimpl);
assemble_completion_to_cell(c, nc + w, np, dt, h); if (is_open) {
assemble_completion_to_cell(c, nc + w, np, dt, h);
}
/* Prepare for RESV controls */ /* Prepare for RESV controls */
compute_darcyflux_and_deriv(np, WI[i], dp, pmobp, gpot, compute_darcyflux_and_deriv(np, WI[i], dp, pmobp, gpot,
h->pimpl->flux_work, h->pimpl->flux_work,
h->pimpl->flux_work + np); h->pimpl->flux_work + np);
assemble_completion_to_well(w, c, nc, np, pw, dt, wells, h); assemble_completion_to_well(i, w, c, nc, np, pw, dt, wells, h);
} }
ctrl = W->ctrls[ w ]; ctrl = W->ctrls[ w ];
@ -1127,8 +1184,7 @@ cfs_tpfa_res_construct(struct UnstructuredGrid *G ,
nf = G->number_of_faces; nf = G->number_of_faces;
nwperf = 0; nwperf = 0;
if (wells != NULL) { if ((wells != NULL) && (wells->W != NULL)) {
assert (wells->W != NULL);
nwperf = wells->W->well_connpos[ wells->W->number_of_wells ]; nwperf = wells->W->well_connpos[ wells->W->number_of_wells ];
} }
@ -1194,7 +1250,9 @@ cfs_tpfa_res_assemble(struct UnstructuredGrid *G ,
assemble_cell_contrib(G, c, h); assemble_cell_contrib(G, c, h);
} }
if ((forces != NULL) && (forces->wells != NULL)) { if ((forces != NULL) &&
(forces->wells != NULL) &&
(forces->wells->W != NULL)) {
compute_well_compflux_and_deriv(forces->wells, cq->nphases, compute_well_compflux_and_deriv(forces->wells, cq->nphases,
cpress, wpress, h->pimpl); cpress, wpress, h->pimpl);
@ -1297,8 +1355,9 @@ cfs_tpfa_res_flux(struct UnstructuredGrid *G ,
{ {
compute_flux(G, np, trans, pmobf, gravcap_f, cpress, fflux); compute_flux(G, np, trans, pmobf, gravcap_f, cpress, fflux);
if ((forces != NULL) && (forces->wells != NULL) && if ((forces != NULL) &&
(wpress != NULL) && (wflux != NULL)) { (forces->wells != NULL) &&
(forces->wells->W != NULL) && (wpress != NULL) && (wflux != NULL)) {
compute_wflux(np, forces->wells, pmobc, cpress, wpress, wflux); compute_wflux(np, forces->wells, pmobc, cpress, wpress, wflux);
} }

View File

@ -65,7 +65,7 @@ namespace Opm
Impl(const parameter::ParameterGroup& param, Impl(const parameter::ParameterGroup& param,
const UnstructuredGrid& grid, const UnstructuredGrid& grid,
const BlackoilPropertiesInterface& props, const BlackoilPropertiesInterface& props,
const RockCompressibility* rock_comp, const RockCompressibility* rock_comp_props,
WellsManager& wells_manager, WellsManager& wells_manager,
const std::vector<double>& src, const std::vector<double>& src,
const FlowBoundaryConditions* bcs, const FlowBoundaryConditions* bcs,
@ -93,7 +93,7 @@ namespace Opm
// Observed objects. // Observed objects.
const UnstructuredGrid& grid_; const UnstructuredGrid& grid_;
const BlackoilPropertiesInterface& props_; const BlackoilPropertiesInterface& props_;
const RockCompressibility* rock_comp_; const RockCompressibility* rock_comp_props_;
WellsManager& wells_manager_; WellsManager& wells_manager_;
const Wells* wells_; const Wells* wells_;
const std::vector<double>& src_; const std::vector<double>& src_;
@ -114,14 +114,14 @@ namespace Opm
SimulatorCompressibleTwophase::SimulatorCompressibleTwophase(const parameter::ParameterGroup& param, SimulatorCompressibleTwophase::SimulatorCompressibleTwophase(const parameter::ParameterGroup& param,
const UnstructuredGrid& grid, const UnstructuredGrid& grid,
const BlackoilPropertiesInterface& props, const BlackoilPropertiesInterface& props,
const RockCompressibility* rock_comp, const RockCompressibility* rock_comp_props,
WellsManager& wells_manager, 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_manager, src, bcs, linsolver, gravity)); pimpl_.reset(new Impl(param, grid, props, rock_comp_props, wells_manager, src, bcs, linsolver, gravity));
} }
@ -175,6 +175,7 @@ namespace Opm
Opm::DataMap dm; Opm::DataMap dm;
dm["saturation"] = &state.saturation(); dm["saturation"] = &state.saturation();
dm["pressure"] = &state.pressure(); dm["pressure"] = &state.pressure();
dm["surfvolume"] = &state.surfacevol();
std::vector<double> cell_velocity; std::vector<double> cell_velocity;
Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity);
dm["velocity"] = &cell_velocity; dm["velocity"] = &cell_velocity;
@ -195,6 +196,7 @@ namespace Opm
if (!file) { if (!file) {
THROW("Failed to open " << fname.str()); THROW("Failed to open " << fname.str());
} }
file.precision(15);
const std::vector<double>& d = *(it->second); const std::vector<double>& d = *(it->second);
std::copy(d.begin(), d.end(), std::ostream_iterator<double>(file, "\n")); std::copy(d.begin(), d.end(), std::ostream_iterator<double>(file, "\n"));
} }
@ -232,7 +234,7 @@ namespace Opm
SimulatorCompressibleTwophase::Impl::Impl(const parameter::ParameterGroup& param, SimulatorCompressibleTwophase::Impl::Impl(const parameter::ParameterGroup& param,
const UnstructuredGrid& grid, const UnstructuredGrid& grid,
const BlackoilPropertiesInterface& props, const BlackoilPropertiesInterface& props,
const RockCompressibility* rock_comp, const RockCompressibility* rock_comp_props,
WellsManager& wells_manager, WellsManager& wells_manager,
const std::vector<double>& src, const std::vector<double>& src,
const FlowBoundaryConditions* bcs, const FlowBoundaryConditions* bcs,
@ -240,12 +242,13 @@ namespace Opm
const double* gravity) const double* gravity)
: grid_(grid), : grid_(grid),
props_(props), props_(props),
rock_comp_(rock_comp), rock_comp_props_(rock_comp_props),
wells_manager_(wells_manager), wells_manager_(wells_manager),
wells_(wells_manager.c_wells()), wells_(wells_manager.c_wells()),
src_(src), src_(src),
bcs_(bcs), bcs_(bcs),
psolver_(grid, props, rock_comp, linsolver, gravity_(gravity),
psolver_(grid, props, rock_comp_props, 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),
@ -301,8 +304,8 @@ namespace Opm
// Initialisation. // Initialisation.
std::vector<double> porevol; std::vector<double> porevol;
if (rock_comp_ && rock_comp_->isActive()) { if (rock_comp_props_ && rock_comp_props_->isActive()) {
computePorevolume(grid_, props_.porosity(), *rock_comp_, state.pressure(), porevol); computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol);
} else { } else {
computePorevolume(grid_, props_.porosity(), porevol); computePorevolume(grid_, props_.porosity(), porevol);
} }
@ -317,15 +320,11 @@ namespace Opm
Opm::time::StopWatch step_timer; 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_surfvol[2] = { 0.0 };
double satvol[2] = { 0.0 }; double inplace_surfvol[2] = { 0.0 };
double injected[2] = { 0.0 };
double produced[2] = { 0.0 };
double tot_injected[2] = { 0.0 }; double tot_injected[2] = { 0.0 };
double tot_produced[2] = { 0.0 }; double tot_produced[2] = { 0.0 };
Opm::computeSaturatedVol(porevol, state.saturation(), init_satvol); Opm::computeSaturatedVol(porevol, state.surfacevol(), init_surfvol);
std::cout << "\nInitial saturations are " << init_satvol[0]/tot_porevol_init
<< " " << init_satvol[1]/tot_porevol_init << std::endl;
Opm::Watercut watercut; Opm::Watercut watercut;
watercut.push(0.0, 0.0, 0.0); watercut.push(0.0, 0.0, 0.0);
Opm::WellReport wellreport; Opm::WellReport wellreport;
@ -408,8 +407,8 @@ namespace Opm
// Optionally, check if well controls are satisfied. // Optionally, check if well controls are satisfied.
if (check_well_controls_) { if (check_well_controls_) {
Opm::computePhaseFlowRatesPerWell(*wells_, Opm::computePhaseFlowRatesPerWell(*wells_,
fractional_flows,
well_state.perfRates(), well_state.perfRates(),
fractional_flows,
well_resflows_phase); well_resflows_phase);
std::cout << "Checking well conditions." << std::endl; std::cout << "Checking well conditions." << std::endl;
// For testing we set surface := reservoir // For testing we set surface := reservoir
@ -427,14 +426,13 @@ namespace Opm
} while (!well_control_passed); } 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_props_ && rock_comp_props_->isActive()) {
initial_porevol = porevol; initial_porevol = porevol;
computePorevolume(grid_, props_.porosity(), *rock_comp_, state.pressure(), porevol); computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol);
} }
// Process transport sources (to include bdy terms and well flows). // Process transport sources from well flows.
Opm::computeTransportSource(grid_, src_, state.faceflux(), 1.0, Opm::computeTransportSource(props_, wells_, well_state, transport_src);
wells_, well_state.perfRates(), transport_src);
// Solve transport. // Solve transport.
transport_timer.start(); transport_timer.start();
@ -443,16 +441,22 @@ namespace Opm
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;
} }
double injected[2] = { 0.0 };
double produced[2] = { 0.0 };
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], &state.pressure()[0], tsolver_.solve(&state.faceflux()[0], &state.pressure()[0],
&porevol[0], &initial_porevol[0], &transport_src[0], stepsize, &initial_porevol[0], &porevol[0], &transport_src[0], stepsize,
state.saturation(), state.surfacevol()); state.saturation(), state.surfacevol());
Opm::computeInjectedProduced(props_, double substep_injected[2] = { 0.0 };
state.pressure(), state.surfacevol(), state.saturation(), double substep_produced[2] = { 0.0 };
transport_src, stepsize, injected, produced); Opm::computeInjectedProduced(props_, state, transport_src, stepsize,
substep_injected, substep_produced);
injected[0] += substep_injected[0];
injected[1] += substep_injected[1];
produced[0] += substep_produced[0];
produced[1] += substep_produced[1];
if (gravity_ != 0 && use_segregation_split_) { if (gravity_ != 0 && use_segregation_split_) {
tsolver_.solveGravity(columns_, &state.pressure()[0], &initial_porevol[0], tsolver_.solveGravity(columns_, stepsize, state.saturation(), state.surfacevol());
stepsize, state.saturation(), state.surfacevol());
} }
} }
transport_timer.stop(); transport_timer.stop();
@ -461,35 +465,35 @@ namespace Opm
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.surfacevol(), inplace_surfvol);
tot_injected[0] += injected[0]; tot_injected[0] += injected[0];
tot_injected[1] += injected[1]; tot_injected[1] += injected[1];
tot_produced[0] += produced[0]; tot_produced[0] += produced[0];
tot_produced[1] += produced[1]; tot_produced[1] += produced[1];
std::cout.precision(5); std::cout.precision(5);
const int width = 18; const int width = 18;
std::cout << "\nVolume balance report (all numbers relative to total pore volume).\n"; std::cout << "\nMass balance report.\n";
std::cout << " Saturated volumes: " std::cout << " Injected surface volumes: "
<< std::setw(width) << satvol[0]/tot_porevol_init << std::setw(width) << injected[0]
<< std::setw(width) << satvol[1]/tot_porevol_init << std::endl; << std::setw(width) << injected[1] << std::endl;
std::cout << " Injected volumes: " std::cout << " Produced surface volumes: "
<< std::setw(width) << injected[0]/tot_porevol_init << std::setw(width) << produced[0]
<< std::setw(width) << injected[1]/tot_porevol_init << std::endl; << std::setw(width) << produced[1] << std::endl;
std::cout << " Produced volumes: " std::cout << " Total inj surface volumes: "
<< std::setw(width) << produced[0]/tot_porevol_init << std::setw(width) << tot_injected[0]
<< std::setw(width) << produced[1]/tot_porevol_init << std::endl; << std::setw(width) << tot_injected[1] << std::endl;
std::cout << " Total inj volumes: " std::cout << " Total prod surface volumes: "
<< std::setw(width) << tot_injected[0]/tot_porevol_init << std::setw(width) << tot_produced[0]
<< std::setw(width) << tot_injected[1]/tot_porevol_init << std::endl; << std::setw(width) << tot_produced[1] << std::endl;
std::cout << " Total prod volumes: " const double balance[2] = { init_surfvol[0] - inplace_surfvol[0] - tot_produced[0] + tot_injected[0],
<< std::setw(width) << tot_produced[0]/tot_porevol_init init_surfvol[1] - inplace_surfvol[1] - tot_produced[1] + tot_injected[1] };
<< std::setw(width) << tot_produced[1]/tot_porevol_init << std::endl; std::cout << " Initial - inplace + inj - prod: "
std::cout << " In-place + prod - inj: " << std::setw(width) << balance[0]
<< std::setw(width) << (satvol[0] + tot_produced[0] - tot_injected[0])/tot_porevol_init << std::setw(width) << balance[1]
<< std::setw(width) << (satvol[1] + tot_produced[1] - tot_injected[1])/tot_porevol_init << std::endl; << std::endl;
std::cout << " Init - now - pr + inj: " std::cout << " Relative mass error: "
<< std::setw(width) << (init_satvol[0] - satvol[0] - tot_produced[0] + tot_injected[0])/tot_porevol_init << std::setw(width) << balance[0]/(init_surfvol[0] + tot_injected[0])
<< std::setw(width) << (init_satvol[1] - satvol[1] - tot_produced[1] + tot_injected[1])/tot_porevol_init << std::setw(width) << balance[1]/(init_surfvol[1] + tot_injected[1])
<< std::endl; << std::endl;
std::cout.precision(8); std::cout.precision(8);

View File

@ -61,7 +61,7 @@ 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_props if non-null, rock compressibility properties
/// \param[in] well_manager well manager, may manage no (null) wells /// \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
@ -70,7 +70,7 @@ namespace Opm
SimulatorCompressibleTwophase(const parameter::ParameterGroup& param, SimulatorCompressibleTwophase(const parameter::ParameterGroup& param,
const UnstructuredGrid& grid, const UnstructuredGrid& grid,
const BlackoilPropertiesInterface& props, const BlackoilPropertiesInterface& props,
const RockCompressibility* rock_comp, const RockCompressibility* rock_comp_props,
WellsManager& wells_manager, WellsManager& wells_manager,
const std::vector<double>& src, const std::vector<double>& src,
const FlowBoundaryConditions* bcs, const FlowBoundaryConditions* bcs,

View File

@ -64,7 +64,7 @@ namespace Opm
Impl(const parameter::ParameterGroup& param, Impl(const parameter::ParameterGroup& param,
const UnstructuredGrid& grid, const UnstructuredGrid& grid,
const IncompPropertiesInterface& props, const IncompPropertiesInterface& props,
const RockCompressibility* rock_comp, const RockCompressibility* rock_comp_props,
WellsManager& wells_manager, WellsManager& wells_manager,
const std::vector<double>& src, const std::vector<double>& src,
const FlowBoundaryConditions* bcs, const FlowBoundaryConditions* bcs,
@ -91,7 +91,7 @@ namespace Opm
// Observed objects. // Observed objects.
const UnstructuredGrid& grid_; const UnstructuredGrid& grid_;
const IncompPropertiesInterface& props_; const IncompPropertiesInterface& props_;
const RockCompressibility* rock_comp_; const RockCompressibility* rock_comp_props_;
WellsManager& wells_manager_; WellsManager& wells_manager_;
const Wells* wells_; const Wells* wells_;
const std::vector<double>& src_; const std::vector<double>& src_;
@ -111,14 +111,14 @@ namespace Opm
SimulatorIncompTwophase::SimulatorIncompTwophase(const parameter::ParameterGroup& param, SimulatorIncompTwophase::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_props,
WellsManager& wells_manager, 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_manager, src, bcs, linsolver, gravity)); pimpl_.reset(new Impl(param, grid, props, rock_comp_props, wells_manager, src, bcs, linsolver, gravity));
} }
@ -244,6 +244,7 @@ namespace Opm
if (!file) { if (!file) {
THROW("Failed to open " << fname.str()); THROW("Failed to open " << fname.str());
} }
file.precision(15);
const std::vector<double>& d = *(it->second); const std::vector<double>& d = *(it->second);
std::copy(d.begin(), d.end(), std::ostream_iterator<double>(file, "\n")); std::copy(d.begin(), d.end(), std::ostream_iterator<double>(file, "\n"));
} }
@ -311,7 +312,7 @@ namespace Opm
SimulatorIncompTwophase::Impl::Impl(const parameter::ParameterGroup& param, SimulatorIncompTwophase::Impl::Impl(const parameter::ParameterGroup& param,
const UnstructuredGrid& grid, const UnstructuredGrid& grid,
const IncompPropertiesInterface& props, const IncompPropertiesInterface& props,
const RockCompressibility* rock_comp, const RockCompressibility* rock_comp_props,
WellsManager& wells_manager, WellsManager& wells_manager,
const std::vector<double>& src, const std::vector<double>& src,
const FlowBoundaryConditions* bcs, const FlowBoundaryConditions* bcs,
@ -319,12 +320,12 @@ namespace Opm
const double* gravity) const double* gravity)
: grid_(grid), : grid_(grid),
props_(props), props_(props),
rock_comp_(rock_comp), rock_comp_props_(rock_comp_props),
wells_manager_(wells_manager), wells_manager_(wells_manager),
wells_(wells_manager.c_wells()), wells_(wells_manager.c_wells()),
src_(src), src_(src),
bcs_(bcs), bcs_(bcs),
psolver_(grid, props, rock_comp, linsolver, psolver_(grid, props, rock_comp_props, 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),
@ -380,8 +381,8 @@ namespace Opm
// Initialisation. // Initialisation.
std::vector<double> porevol; std::vector<double> porevol;
if (rock_comp_ && rock_comp_->isActive()) { if (rock_comp_props_ && rock_comp_props_->isActive()) {
computePorevolume(grid_, props_.porosity(), *rock_comp_, state.pressure(), porevol); computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol);
} else { } else {
computePorevolume(grid_, props_.porosity(), porevol); computePorevolume(grid_, props_.porosity(), porevol);
} }
@ -398,8 +399,6 @@ namespace Opm
total_timer.start(); total_timer.start();
double init_satvol[2] = { 0.0 }; double init_satvol[2] = { 0.0 };
double 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_injected[2] = { 0.0 };
double tot_produced[2] = { 0.0 }; double tot_produced[2] = { 0.0 };
Opm::computeSaturatedVol(porevol, state.saturation(), init_satvol); Opm::computeSaturatedVol(porevol, state.saturation(), init_satvol);
@ -452,7 +451,7 @@ namespace Opm
// there are no pressure conditions (bcs or wells). // there are no pressure conditions (bcs or wells).
// It is deemed sufficient for now to renormalize // It is deemed sufficient for now to renormalize
// using geometric volume instead of pore volume. // using geometric volume instead of pore volume.
if ((rock_comp_ == NULL || !rock_comp_->isActive()) if ((rock_comp_props_ == NULL || !rock_comp_props_->isActive())
&& allNeumannBCs(bcs_) && allRateWells(wells_)) { && allNeumannBCs(bcs_) && allRateWells(wells_)) {
// Compute average pressures of previous and last // Compute average pressures of previous and last
// step, and total volume. // step, and total volume.
@ -486,8 +485,8 @@ namespace Opm
// Optionally, check if well controls are satisfied. // Optionally, check if well controls are satisfied.
if (check_well_controls_) { if (check_well_controls_) {
Opm::computePhaseFlowRatesPerWell(*wells_, Opm::computePhaseFlowRatesPerWell(*wells_,
fractional_flows,
well_state.perfRates(), well_state.perfRates(),
fractional_flows,
well_resflows_phase); well_resflows_phase);
std::cout << "Checking well conditions." << std::endl; std::cout << "Checking well conditions." << std::endl;
// For testing we set surface := reservoir // For testing we set surface := reservoir
@ -505,9 +504,9 @@ namespace Opm
} while (!well_control_passed); } 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_props_ && rock_comp_props_->isActive()) {
initial_porevol = porevol; initial_porevol = porevol;
computePorevolume(grid_, props_.porosity(), *rock_comp_, state.pressure(), porevol); computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol);
} }
// Process transport sources (to include bdy terms and well flows). // Process transport sources (to include bdy terms and well flows).
@ -521,10 +520,19 @@ namespace Opm
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;
} }
double injected[2] = { 0.0 };
double produced[2] = { 0.0 };
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], &initial_porevol[0], &transport_src[0], tsolver_.solve(&state.faceflux()[0], &initial_porevol[0], &transport_src[0],
stepsize, state.saturation()); stepsize, state.saturation());
Opm::computeInjectedProduced(props_, state.saturation(), transport_src, stepsize, injected, produced); double substep_injected[2] = { 0.0 };
double substep_produced[2] = { 0.0 };
Opm::computeInjectedProduced(props_, state.saturation(), transport_src, stepsize,
substep_injected, substep_produced);
injected[0] += substep_injected[0];
injected[1] += substep_injected[1];
produced[0] += substep_produced[0];
produced[1] += substep_produced[1];
if (use_segregation_split_) { if (use_segregation_split_) {
tsolver_.solveGravity(columns_, &initial_porevol[0], stepsize, state.saturation()); tsolver_.solveGravity(columns_, &initial_porevol[0], stepsize, state.saturation());
} }

View File

@ -61,7 +61,7 @@ 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_props if non-null, rock compressibility properties
/// \param[in] well_manager well manager, may manage no (null) wells /// \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
@ -70,7 +70,7 @@ namespace Opm
SimulatorIncompTwophase(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_props,
WellsManager& wells_manager, WellsManager& wells_manager,
const std::vector<double>& src, const std::vector<double>& src,
const FlowBoundaryConditions* bcs, const FlowBoundaryConditions* bcs,

View File

@ -37,12 +37,20 @@ namespace Opm
if (wells) { if (wells) {
const int nw = wells->number_of_wells; const int nw = wells->number_of_wells;
bhp_.resize(nw); bhp_.resize(nw);
// Initialize bhp to be pressure in first perforation cell. // Initialize bhp to be target pressure
// if bhp-controlled well, otherwise set
// to pressure in first perforation cell.
for (int w = 0; w < nw; ++w) { for (int w = 0; w < nw; ++w) {
const int cell = wells->well_cells[wells->well_connpos[w]]; const WellControls* ctrl = wells->ctrls[w];
bhp_[w] = state.pressure()[cell]; if (ctrl->type[ctrl->current] == BHP) {
bhp_[w] = ctrl->target[ctrl->current];
} else {
const int cell = wells->well_cells[wells->well_connpos[w]];
bhp_[w] = state.pressure()[cell];
}
} }
perfrates_.resize(wells->well_connpos[nw]); perfrates_.resize(wells->well_connpos[nw], 0.0);
perfpress_.resize(wells->well_connpos[nw], -1e100);
} }
} }
@ -54,9 +62,14 @@ namespace Opm
std::vector<double>& perfRates() { return perfrates_; } std::vector<double>& perfRates() { return perfrates_; }
const std::vector<double>& perfRates() const { return perfrates_; } const std::vector<double>& perfRates() const { return perfrates_; }
/// One pressure per well connection.
std::vector<double>& perfPress() { return perfpress_; }
const std::vector<double>& perfPress() const { return perfpress_; }
private: private:
std::vector<double> bhp_; std::vector<double> bhp_;
std::vector<double> perfrates_; std::vector<double> perfrates_;
std::vector<double> perfpress_;
}; };
} // namespace Opm } // namespace Opm

View File

@ -152,8 +152,8 @@ namespace Opm
B_cell = 1.0/tm.A_[np*np*cell + 0]; 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 ? B_cell*src_flux : 0.0; influx = src_is_inflow ? B_cell* src_flux : 0.0;
outflux = !src_is_inflow ? B_cell*src_flux : 0.0; outflux = !src_is_inflow ? src_flux : 0.0;
comp_term = (tm.porevolume_[cell] - tm.porevolume0_[cell])/tm.porevolume0_[cell]; comp_term = (tm.porevolume_[cell] - tm.porevolume0_[cell])/tm.porevolume0_[cell];
dtpv = tm.dt_/tm.porevolume0_[cell]; dtpv = tm.dt_/tm.porevolume0_[cell];
for (int i = tm.grid_.cell_facepos[cell]; i < tm.grid_.cell_facepos[cell+1]; ++i) { for (int i = tm.grid_.cell_facepos[cell]; i < tm.grid_.cell_facepos[cell+1]; ++i) {
@ -349,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.porevolume0_[cell]; dtpv = tm.dt_/tm.porevolume_[cell];
} }
double operator()(double s) const double operator()(double s) const
@ -380,8 +380,8 @@ namespace Opm
{ {
double sat[2] = { s, 1.0 - s }; double sat[2] = { s, 1.0 - s };
props_.relperm(1, sat, &cell, mob, 0); props_.relperm(1, sat, &cell, mob, 0);
mob[0] /= visc_[0]; mob[0] /= visc_[2*cell + 0];
mob[1] /= visc_[1]; mob[1] /= visc_[2*cell + 1];
} }
@ -407,7 +407,7 @@ namespace Opm
{ {
// Set up gravflux_ = T_ij g [ (b_w,i rho_w,S - b_o,i rho_o,S) (z_i - z_f) // 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) ] // + (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(). // But b_w,i * rho_w,S = rho_w,i, which we compute with a call to props_.density().
// We assume that we already have stored T_ij in trans_. // 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(). // We also assume that the A_ matrices are updated from an earlier call to solve().
const int nc = grid_.number_of_cells; const int nc = grid_.number_of_cells;
@ -437,16 +437,15 @@ namespace Opm
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)
{ {
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;
saturation_[cell] = RootFinder::solve(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] = 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]);
} }
@ -506,8 +505,6 @@ 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* porevolume0,
const double dt, const double dt,
std::vector<double>& saturation, std::vector<double>& saturation,
std::vector<double>& surfacevol) std::vector<double>& surfacevol)
@ -522,18 +519,22 @@ namespace Opm
cells[c] = c; cells[c] = c;
} }
mob_.resize(2*nc); mob_.resize(2*nc);
std::vector<double> boths;
Opm::toBothSat(saturation, boths);
props_.relperm(cells.size(), &boths[0], &cells[0], &mob_[0], 0);
props_.viscosity(props_.numCells(), pressure, NULL, &allcells_[0], &visc_[0], NULL);
for (int c = 0; c < nc; ++c) { // props_.relperm(cells.size(), &saturation[0], &cells[0], &mob_[0], 0);
mob_[2*c + 0] /= visc_[2*c + 0];
mob_[2*c + 1] /= visc_[2*c + 1]; // props_.viscosity(props_.numCells(), pressure, NULL, &allcells_[0], &visc_[0], NULL);
// for (int c = 0; c < nc; ++c) {
// mob_[2*c + 0] /= visc_[2*c + 0];
// mob_[2*c + 1] /= visc_[2*c + 1];
// }
const int np = props_.numPhases();
for (int cell = 0; cell < nc; ++cell) {
mobility(saturation_[cell], cell, &mob_[np*cell]);
} }
// Set up other variables. // Set up other variables.
porevolume0_ = porevolume0;
dt_ = dt; dt_ = dt;
toWaterSat(saturation, saturation_); toWaterSat(saturation, saturation_);

View File

@ -74,13 +74,10 @@ namespace Opm
/// vertical stack, that do not interact with other columns (for /// vertical stack, that do not interact with other columns (for
/// gravity segregation. /// gravity segregation.
/// \param[in] columns Vector of cell-columns. /// \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] 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. /// \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* porevolume0,
const double dt, const double dt,
std::vector<double>& saturation, std::vector<double>& saturation,
std::vector<double>& surfacevol); std::vector<double>& surfacevol);

View File

@ -20,6 +20,7 @@
#include <opm/core/transport/reorder/TransportModelInterface.hpp> #include <opm/core/transport/reorder/TransportModelInterface.hpp>
#include <opm/core/transport/reorder/reordersequence.h> #include <opm/core/transport/reorder/reordersequence.h>
#include <opm/core/grid.h> #include <opm/core/grid.h>
#include <opm/core/utility/StopWatch.hpp>
#include <vector> #include <vector>
#include <cassert> #include <cassert>
@ -31,7 +32,11 @@ void Opm::TransportModelInterface::reorderAndTransport(const UnstructuredGrid& g
std::vector<int> sequence(grid.number_of_cells); std::vector<int> sequence(grid.number_of_cells);
std::vector<int> components(grid.number_of_cells + 1); std::vector<int> components(grid.number_of_cells + 1);
int ncomponents; int ncomponents;
time::StopWatch clock;
clock.start();
compute_sequence(&grid, darcyflux, &sequence[0], &components[0], &ncomponents); compute_sequence(&grid, darcyflux, &sequence[0], &components[0], &ncomponents);
clock.stop();
std::cout << "Topological sort took: " << clock.secsSinceStart() << " seconds." << std::endl;
// Invoke appropriate solve method for each interdependent component. // Invoke appropriate solve method for each interdependent component.
for (int comp = 0; comp < ncomponents; ++comp) { for (int comp = 0; comp < ncomponents; ++comp) {

View File

@ -0,0 +1,122 @@
/*
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/transport/reorder/TransportModelTracerTof.hpp>
#include <opm/core/grid.h>
#include <opm/core/utility/ErrorMacros.hpp>
#include <algorithm>
#include <numeric>
#include <cmath>
namespace Opm
{
/// Construct solver.
/// \param[in] grid A 2d or 3d grid.
TransportModelTracerTof::TransportModelTracerTof(const UnstructuredGrid& grid)
: grid_(grid)
{
}
/// Solve for time-of-flight.
/// \param[in] darcyflux Array of signed face fluxes.
/// \param[in] porevolume Array of pore volumes.
/// \param[in] source Source term. Sign convention is:
/// (+) inflow flux,
/// (-) outflow flux.
/// \param[out] tof Array of time-of-flight values.
void TransportModelTracerTof::solveTof(const double* darcyflux,
const double* porevolume,
const double* source,
std::vector<double>& tof)
{
darcyflux_ = darcyflux;
porevolume_ = porevolume;
source_ = source;
#ifndef NDEBUG
// Sanity check for sources.
const double cum_src = std::accumulate(source, source + grid_.number_of_cells, 0.0);
if (std::fabs(cum_src) > *std::max_element(source, source + grid_.number_of_cells)*1e-2) {
THROW("Sources do not sum to zero: " << cum_src);
}
#endif
tof.resize(grid_.number_of_cells);
std::fill(tof.begin(), tof.end(), 0.0);
tof_ = &tof[0];
reorderAndTransport(grid_, darcyflux);
}
void TransportModelTracerTof::solveSingleCell(const int cell)
{
// Compute flux terms.
// Sources have zero tof, and therefore do not contribute
// to upwind_term. Sinks on the other hand, must be added
// to the downwind_flux (note sign change resulting from
// different sign conventions: pos. source is injection,
// pos. flux is outflow).
double upwind_term = 0.0;
double downwind_flux = std::max(-source_[cell], 0.0);
for (int i = grid_.cell_facepos[cell]; i < grid_.cell_facepos[cell+1]; ++i) {
int f = grid_.cell_faces[i];
double flux;
int other;
// Compute cell flux
if (cell == grid_.face_cells[2*f]) {
flux = darcyflux_[f];
other = grid_.face_cells[2*f+1];
} else {
flux =-darcyflux_[f];
other = grid_.face_cells[2*f];
}
// Add flux to upwind_term or downwind_flux, if interior.
if (other != -1) {
if (flux < 0.0) {
upwind_term += flux*tof_[other];
} else {
downwind_flux += flux;
}
}
}
// Compute tof.
tof_[cell] = (porevolume_[cell] - upwind_term)/downwind_flux;
}
void TransportModelTracerTof::solveMultiCell(const int num_cells, const int* cells)
{
std::cout << "Pretending to solve multi-cell dependent equation with " << num_cells << " cells." << std::endl;
for (int i = 0; i < num_cells; ++i) {
solveSingleCell(cells[i]);
}
}
} // namespace Opm

View File

@ -0,0 +1,74 @@
/*
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_TRANSPORTMODELTRACERTOF_HEADER_INCLUDED
#define OPM_TRANSPORTMODELTRACERTOF_HEADER_INCLUDED
#include <opm/core/transport/reorder/TransportModelInterface.hpp>
#include <vector>
#include <map>
#include <ostream>
struct UnstructuredGrid;
namespace Opm
{
class IncompPropertiesInterface;
/// Implements a first-order finite volume solver for
/// (single-phase) time-of-flight using reordering.
/// The equation solved is:
/// v \cdot \grad\tau = \phi
/// where v is the fluid velocity, \tau is time-of-flight and
/// \phi is the porosity. This is a boundary value problem, where
/// \tau is specified to be zero on all inflow boundaries.
class TransportModelTracerTof : public TransportModelInterface
{
public:
/// Construct solver.
/// \param[in] grid A 2d or 3d grid.
TransportModelTracerTof(const UnstructuredGrid& grid);
/// Solve for time-of-flight.
/// \param[in] darcyflux Array of signed face fluxes.
/// \param[in] porevolume Array of pore volumes.
/// \param[in] source Source term. Sign convention is:
/// (+) inflow flux,
/// (-) outflow flux.
/// \param[out] tof Array of time-of-flight values.
void solveTof(const double* darcyflux,
const double* porevolume,
const double* source,
std::vector<double>& tof);
private:
virtual void solveSingleCell(const int cell);
virtual void solveMultiCell(const int num_cells, const int* cells);
private:
const UnstructuredGrid& grid_;
const double* darcyflux_; // one flux per grid face
const double* porevolume_; // one volume per cell
const double* source_; // one volumetric source term per cell
double* tof_;
};
} // namespace Opm
#endif // OPM_TRANSPORTMODELTRACERTOF_HEADER_INCLUDED

View File

@ -0,0 +1,657 @@
/*
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/transport/reorder/TransportModelTracerTofDiscGal.hpp>
#include <opm/core/grid.h>
#include <opm/core/utility/ErrorMacros.hpp>
#include <opm/core/utility/VelocityInterpolation.hpp>
#include <opm/core/linalg/blas_lapack.h>
#include <algorithm>
#include <cmath>
#include <numeric>
namespace Opm
{
// --------------- Helpers for TransportModelTracerTofDiscGal ---------------
/// A class providing discontinuous Galerkin basis functions.
struct DGBasis
{
static int numBasisFunc(const int dimensions,
const int degree)
{
switch (dimensions) {
case 1:
return degree + 1;
case 2:
return (degree + 2)*(degree + 1)/2;
case 3:
return (degree + 3)*(degree + 2)*(degree + 1)/6;
default:
THROW("Dimensions must be 1, 2 or 3.");
}
}
/// Evaluate all nonzero basis functions at x,
/// writing to f_x. The array f_x must have
/// size numBasisFunc(grid.dimensions, degree).
///
/// The basis functions are the following
/// Degree 0: 1.
/// Degree 1: x - xc, y - yc, z - zc etc.
/// Further degrees await development.
static void eval(const UnstructuredGrid& grid,
const int cell,
const int degree,
const double* x,
double* f_x)
{
const int dim = grid.dimensions;
const double* cc = grid.cell_centroids + dim*cell;
// Note intentional fallthrough in this switch statement!
switch (degree) {
case 1:
for (int ix = 0; ix < dim; ++ix) {
f_x[1 + ix] = x[ix] - cc[ix];
}
case 0:
f_x[0] = 1;
break;
default:
THROW("Maximum degree is 1 for now.");
}
}
/// Evaluate gradients of all nonzero basis functions at x,
/// writing to grad_f_x. The array grad_f_x must have size
/// numBasisFunc(grid.dimensions, degree) * grid.dimensions.
/// The <grid.dimensions> components of the first basis function
/// gradient come before the components of the second etc.
static void evalGrad(const UnstructuredGrid& grid,
const int /*cell*/,
const int degree,
const double* /*x*/,
double* grad_f_x)
{
const int dim = grid.dimensions;
const int num_basis = numBasisFunc(dim, degree);
std::fill(grad_f_x, grad_f_x + num_basis*dim, 0.0);
if (degree > 1) {
THROW("Maximum degree is 1 for now.");
} else if (degree == 1) {
for (int ix = 0; ix < dim; ++ix) {
grad_f_x[dim*(ix + 1) + ix] = 1.0;
}
}
}
};
static void cross(const double* a, const double* b, double* res)
{
res[0] = a[1]*b[2] - a[2]*b[1];
res[1] = a[2]*b[0] - a[0]*b[2];
res[2] = a[0]*b[1] - a[1]*b[0];
}
static double triangleArea3d(const double* p0,
const double* p1,
const double* p2)
{
double a[3] = { p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2] };
double b[3] = { p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2] };
double cr[3];
cross(a, b, cr);
return 0.5*std::sqrt(cr[0]*cr[0] + cr[1]*cr[1] + cr[2]*cr[2]);
}
/// Calculates the determinant of a 3 x 3 matrix, represented as
/// three three-dimensional arrays.
static double determinantOf(const double* a0,
const double* a1,
const double* a2)
{
return
a0[0] * (a1[1] * a2[2] - a2[1] * a1[2]) -
a0[1] * (a1[0] * a2[2] - a2[0] * a1[2]) +
a0[2] * (a1[0] * a2[1] - a2[0] * a1[1]);
}
/// Computes the volume of a tetrahedron consisting of 4 vertices
/// with 3-dimensional coordinates
static double tetVolume(const double* p0,
const double* p1,
const double* p2,
const double* p3)
{
double a[3] = { p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2] };
double b[3] = { p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2] };
double c[3] = { p3[0] - p0[0], p3[1] - p0[1], p3[2] - p0[2] };
return std::fabs(determinantOf(a, b, c) / 6.0);
}
/// A class providing numerical quadrature for cells.
/// In general: \int_{cell} g(x) dx = \sum_{i=0}^{n-1} w_i g(x_i).
/// Note that this class does multiply weights by cell volume,
/// so weights always sum to cell volume.
/// Degree 1 method:
/// Midpoint (centroid) method.
/// n = 1, w_0 = cell volume, x_0 = cell centroid
/// Degree 2 method:
/// Based on subdivision of each cell face into triangles
/// with the face centroid as a common vertex, and then
/// subdividing the cell into tetrahedra with the cell
/// centroid as a common vertex. Then apply the tetrahedron
/// rule with the following 4 nodes (uniform weights):
/// a = 0.138196601125010515179541316563436
/// x_i has all barycentric coordinates = a, except for
/// the i'th coordinate which is = 1 - 3a.
/// This rule is from http://nines.cs.kuleuven.be/ecf,
/// it is the second degree 2 4-point rule for tets,
/// referenced to Stroud(1971).
/// The tetrahedra are numbered T_{i,j}, and are given by the
/// cell centroid C, the face centroid FC_i, and two nodes
/// of face i: FN_{i,j}, FN_{i,j+1}.
class CellQuadrature
{
public:
CellQuadrature(const UnstructuredGrid& grid,
const int cell,
const int degree)
: grid_(grid), cell_(cell), degree_(degree)
{
if (degree > 2) {
THROW("CellQuadrature exact for polynomial degrees > 1 not implemented.");
}
if (degree == 2) {
// Prepare subdivision.
}
}
int numQuadPts() const
{
if (degree_ < 2) {
return 1;
}
// Degree 2 case.
int sumnodes = 0;
for (int hf = grid_.cell_facepos[cell_]; hf < grid_.cell_facepos[cell_ + 1]; ++hf) {
const int face = grid_.cell_faces[hf];
sumnodes += grid_.face_nodepos[face + 1] - grid_.face_nodepos[face];
}
return 4*sumnodes;
}
void quadPtCoord(const int index, double* coord) const
{
const int dim = grid_.dimensions;
const double* cc = grid_.cell_centroids + dim*cell_;
if (degree_ < 2) {
std::copy(cc, cc + dim, coord);
return;
}
// Degree 2 case.
int tetindex = index / 4;
const int subindex = index % 4;
const double* nc = grid_.node_coordinates;
for (int hf = grid_.cell_facepos[cell_]; hf < grid_.cell_facepos[cell_ + 1]; ++hf) {
const int face = grid_.cell_faces[hf];
const int nfn = grid_.face_nodepos[face + 1] - grid_.face_nodepos[face];
if (nfn <= tetindex) {
// Our tet is not associated with this face.
tetindex -= nfn;
continue;
}
const double* fc = grid_.face_centroids + dim*face;
const int* fnodes = grid_.face_nodes + grid_.face_nodepos[face];
const int node0 = fnodes[tetindex];
const int node1 = fnodes[(tetindex + 1) % nfn];
const double* n0c = nc + dim*node0;
const double* n1c = nc + dim*node1;
const double a = 0.138196601125010515179541316563436;
// Barycentric coordinates of our point in the tet.
double baryc[4] = { a, a, a, a };
baryc[subindex] = 1.0 - 3.0*a;
for (int dd = 0; dd < dim; ++dd) {
coord[dd] = baryc[0]*cc[dd] + baryc[1]*fc[dd] + baryc[2]*n0c[dd] + baryc[3]*n1c[dd];
}
return;
}
THROW("Should never reach this point.");
}
double quadPtWeight(const int index) const
{
if (degree_ < 2) {
return grid_.cell_volumes[cell_];
}
// Degree 2 case.
const int dim = grid_.dimensions;
const double* cc = grid_.cell_centroids + dim*cell_;
int tetindex = index / 4;
const double* nc = grid_.node_coordinates;
for (int hf = grid_.cell_facepos[cell_]; hf < grid_.cell_facepos[cell_ + 1]; ++hf) {
const int face = grid_.cell_faces[hf];
const int nfn = grid_.face_nodepos[face + 1] - grid_.face_nodepos[face];
if (nfn <= tetindex) {
// Our tet is not associated with this face.
tetindex -= nfn;
continue;
}
const double* fc = grid_.face_centroids + dim*face;
const int* fnodes = grid_.face_nodes + grid_.face_nodepos[face];
const int node0 = fnodes[tetindex];
const int node1 = fnodes[(tetindex + 1) % nfn];
const double* n0c = nc + dim*node0;
const double* n1c = nc + dim*node1;
return 0.25*tetVolume(cc, fc, n0c, n1c);
}
THROW("Should never reach this point.");
}
private:
const UnstructuredGrid& grid_;
const int cell_;
const int degree_;
};
/// A class providing numerical quadrature for faces.
/// In general: \int_{face} g(x) dx = \sum_{i=0}^{n-1} w_i g(x_i).
/// Note that this class does multiply weights by face area,
/// so weights always sum to face area.
/// Degree 1 method:
/// Midpoint (centroid) method.
/// n = 1, w_0 = face area, x_0 = face centroid
/// Degree 2 method:
/// Based on subdivision of the face into triangles,
/// with the centroid as a common vertex, and the triangle
/// edge midpoint rule.
/// Triangle i consists of the centroid C, nodes N_i and N_{i+1}.
/// Its area is A_i.
/// n = 2 * nn (nn = num nodes in face)
/// For i = 0..(nn-1):
/// w_i = 1/3 A_i.
/// w_{nn+i} = 1/3 A_{i-1} + 1/3 A_i
/// x_i = (N_i + N_{i+1})/2
/// x_{nn+i} = (C + N_i)/2
/// All N and A indices are interpreted cyclic, modulus nn.
class FaceQuadrature
{
public:
FaceQuadrature(const UnstructuredGrid& grid,
const int face,
const int degree)
: grid_(grid), face_(face), degree_(degree)
{
if (grid_.dimensions != 3) {
THROW("FaceQuadrature only implemented for 3D case.");
}
if (degree_ > 2) {
THROW("FaceQuadrature exact for polynomial degrees > 2 not implemented.");
}
}
int numQuadPts() const
{
if (degree_ < 2) {
return 1;
}
// Degree 2 case.
return 2 * (grid_.face_nodepos[face_ + 1] - grid_.face_nodepos[face_]);
}
void quadPtCoord(const int index, double* coord) const
{
const int dim = grid_.dimensions;
const double* fc = grid_.face_centroids + dim*face_;
if (degree_ < 2) {
std::copy(fc, fc + dim, coord);
return;
}
// Degree 2 case.
const int nn = grid_.face_nodepos[face_ + 1] - grid_.face_nodepos[face_];
const int* fnodes = grid_.face_nodes + grid_.face_nodepos[face_];
const double* nc = grid_.node_coordinates;
if (index < nn) {
// Boundary edge midpoint.
const int node0 = fnodes[index];
const int node1 = fnodes[(index + 1)%nn];
for (int dd = 0; dd < dim; ++dd) {
coord[dd] = 0.5*(nc[dim*node0 + dd] + nc[dim*node1 + dd]);
}
} else {
// Interiour edge midpoint.
// Recall that index is now in [nn, 2*nn).
const int node = fnodes[index - nn];
for (int dd = 0; dd < dim; ++dd) {
coord[dd] = 0.5*(nc[dim*node + dd] + fc[dd]);
}
}
}
double quadPtWeight(const int index) const
{
if (degree_ < 2) {
return grid_.face_areas[face_];
}
// Degree 2 case.
const int dim = grid_.dimensions;
const double* fc = grid_.face_centroids + dim*face_;
const int nn = grid_.face_nodepos[face_ + 1] - grid_.face_nodepos[face_];
const int* fnodes = grid_.face_nodes + grid_.face_nodepos[face_];
const double* nc = grid_.node_coordinates;
if (index < nn) {
// Boundary edge midpoint.
const int node0 = fnodes[index];
const int node1 = fnodes[(index + 1)%nn];
const double area = triangleArea3d(nc + dim*node1, nc + dim*node0, fc);
return area / 3.0;
} else {
// Interiour edge midpoint.
// Recall that index is now in [nn, 2*nn).
const int node0 = fnodes[(index - 1) % nn];
const int node1 = fnodes[index - nn];
const int node2 = fnodes[(index + 1) % nn];
const double area0 = triangleArea3d(nc + dim*node1, nc + dim*node0, fc);
const double area1 = triangleArea3d(nc + dim*node2, nc + dim*node1, fc);
return (area0 + area1) / 3.0;
}
}
private:
const UnstructuredGrid& grid_;
const int face_;
const int degree_;
};
// --------------- Methods of TransportModelTracerTofDiscGal ---------------
/// Construct solver.
/// \param[in] grid A 2d or 3d grid.
/// \param[in] use_cvi If true, use corner point velocity interpolation.
/// Otherwise, use the basic constant interpolation.
TransportModelTracerTofDiscGal::TransportModelTracerTofDiscGal(const UnstructuredGrid& grid,
const bool use_cvi)
: grid_(grid),
coord_(grid.dimensions),
velocity_(grid.dimensions)
{
if (use_cvi) {
velocity_interpolation_.reset(new VelocityInterpolationECVI(grid));
} else {
velocity_interpolation_.reset(new VelocityInterpolationConstant(grid));
}
}
/// Solve for time-of-flight.
/// \param[in] darcyflux Array of signed face fluxes.
/// \param[in] porevolume Array of pore volumes.
/// \param[in] source Source term. Sign convention is:
/// (+) inflow flux,
/// (-) outflow flux.
/// \param[in] degree Polynomial degree of DG basis functions used.
/// \param[out] tof_coeff Array of time-of-flight solution coefficients.
/// The values are ordered by cell, meaning that
/// the K coefficients corresponding to the first
/// cell comes before the K coefficients corresponding
/// to the second cell etc.
/// K depends on degree and grid dimension.
void TransportModelTracerTofDiscGal::solveTof(const double* darcyflux,
const double* porevolume,
const double* source,
const int degree,
std::vector<double>& tof_coeff)
{
darcyflux_ = darcyflux;
porevolume_ = porevolume;
source_ = source;
#ifndef NDEBUG
// Sanity check for sources.
const double cum_src = std::accumulate(source, source + grid_.number_of_cells, 0.0);
if (std::fabs(cum_src) > *std::max_element(source, source + grid_.number_of_cells)*1e-2) {
THROW("Sources do not sum to zero: " << cum_src);
}
#endif
degree_ = degree;
const int num_basis = DGBasis::numBasisFunc(grid_.dimensions, degree_);
tof_coeff.resize(num_basis*grid_.number_of_cells);
std::fill(tof_coeff.begin(), tof_coeff.end(), 0.0);
tof_coeff_ = &tof_coeff[0];
rhs_.resize(num_basis);
jac_.resize(num_basis*num_basis);
orig_jac_.resize(num_basis*num_basis);
basis_.resize(num_basis);
basis_nb_.resize(num_basis);
grad_basis_.resize(num_basis*grid_.dimensions);
velocity_interpolation_->setupFluxes(darcyflux);
reorderAndTransport(grid_, darcyflux);
}
void TransportModelTracerTofDiscGal::solveSingleCell(const int cell)
{
// Residual:
// For each cell K, basis function b_j (spanning V_h),
// writing the solution u_h|K = \sum_i c_i b_i
// Res = - \int_K \sum_i c_i b_i v(x) \cdot \grad b_j dx
// + \int_{\partial K} F(u_h, u_h^{ext}, v(x) \cdot n) b_j ds
// - \int_K \phi b_j
// This is linear in c_i, so we do not need any nonlinear iterations.
// We assemble the jacobian and the right-hand side. The residual is
// equal to Res = Jac*c - rhs, and we compute rhs directly.
const int dim = grid_.dimensions;
const int num_basis = DGBasis::numBasisFunc(dim, degree_);
std::fill(rhs_.begin(), rhs_.end(), 0.0);
std::fill(jac_.begin(), jac_.end(), 0.0);
// Compute cell residual contribution.
// Note: Assumes that \int_K b_j = 0 for all j > 0
rhs_[0] += porevolume_[cell];
// Compute upstream residual contribution.
for (int hface = grid_.cell_facepos[cell]; hface < grid_.cell_facepos[cell+1]; ++hface) {
const int face = grid_.cell_faces[hface];
double flux = 0.0;
int upstream_cell = -1;
if (cell == grid_.face_cells[2*face]) {
flux = darcyflux_[face];
upstream_cell = grid_.face_cells[2*face+1];
} else {
flux = -darcyflux_[face];
upstream_cell = grid_.face_cells[2*face];
}
if (upstream_cell < 0) {
// This is an outer boundary. Assumed tof = 0 on inflow, so no contribution.
continue;
}
if (flux >= 0.0) {
// This is an outflow boundary.
continue;
}
// Do quadrature over the face to compute
// \int_{\partial K} u_h^{ext} (v(x) \cdot n) b_j ds
// (where u_h^{ext} is the upstream unknown (tof)).
const double normal_velocity = flux / grid_.face_areas[face];
FaceQuadrature quad(grid_, face, degree_);
for (int quad_pt = 0; quad_pt < quad.numQuadPts(); ++quad_pt) {
quad.quadPtCoord(quad_pt, &coord_[0]);
DGBasis::eval(grid_, cell, degree_, &coord_[0], &basis_[0]);
DGBasis::eval(grid_, upstream_cell, degree_, &coord_[0], &basis_nb_[0]);
const double tof_upstream = std::inner_product(basis_nb_.begin(), basis_nb_.end(),
tof_coeff_ + num_basis*upstream_cell, 0.0);
const double w = quad.quadPtWeight(quad_pt);
for (int j = 0; j < num_basis; ++j) {
rhs_[j] -= w * tof_upstream * normal_velocity * basis_[j];
}
}
}
// Compute cell jacobian contribution. We use Fortran ordering
// for jac_, i.e. rows cycling fastest.
{
CellQuadrature quad(grid_, cell, 2*degree_ - 1);
for (int quad_pt = 0; quad_pt < quad.numQuadPts(); ++quad_pt) {
// b_i (v \cdot \grad b_j)
quad.quadPtCoord(quad_pt, &coord_[0]);
DGBasis::eval(grid_, cell, degree_, &coord_[0], &basis_[0]);
DGBasis::evalGrad(grid_, cell, degree_, &coord_[0], &grad_basis_[0]);
velocity_interpolation_->interpolate(cell, &coord_[0], &velocity_[0]);
const double w = quad.quadPtWeight(quad_pt);
for (int j = 0; j < num_basis; ++j) {
for (int i = 0; i < num_basis; ++i) {
for (int dd = 0; dd < dim; ++dd) {
jac_[j*num_basis + i] -= w * basis_[j] * grad_basis_[dim*i + dd] * velocity_[dd];
}
}
}
}
}
// Compute downstream jacobian contribution from faces.
for (int hface = grid_.cell_facepos[cell]; hface < grid_.cell_facepos[cell+1]; ++hface) {
const int face = grid_.cell_faces[hface];
double flux = 0.0;
if (cell == grid_.face_cells[2*face]) {
flux = darcyflux_[face];
} else {
flux = -darcyflux_[face];
}
if (flux <= 0.0) {
// This is an inflow boundary.
continue;
}
// Do quadrature over the face to compute
// \int_{\partial K} b_i (v(x) \cdot n) b_j ds
const double normal_velocity = flux / grid_.face_areas[face];
FaceQuadrature quad(grid_, face, 2*degree_);
for (int quad_pt = 0; quad_pt < quad.numQuadPts(); ++quad_pt) {
// u^ext flux B (B = {b_j})
quad.quadPtCoord(quad_pt, &coord_[0]);
DGBasis::eval(grid_, cell, degree_, &coord_[0], &basis_[0]);
const double w = quad.quadPtWeight(quad_pt);
for (int j = 0; j < num_basis; ++j) {
for (int i = 0; i < num_basis; ++i) {
jac_[j*num_basis + i] += w * basis_[i] * normal_velocity * basis_[j];
}
}
}
}
// Compute downstream jacobian contribution from sink terms.
// Contribution from inflow sources would be
// similar to the contribution from upstream faces, but
// it is zero since we let all external inflow be associated
// with a zero tof.
if (source_[cell] < 0.0) {
// A sink.
const double flux = -source_[cell]; // Sign convention for flux: outflux > 0.
const double flux_density = flux / grid_.cell_volumes[cell];
// Do quadrature over the cell to compute
// \int_{K} b_i flux b_j dx
CellQuadrature quad(grid_, cell, 2*degree_);
for (int quad_pt = 0; quad_pt < quad.numQuadPts(); ++quad_pt) {
quad.quadPtCoord(quad_pt, &coord_[0]);
DGBasis::eval(grid_, cell, degree_, &coord_[0], &basis_[0]);
const double w = quad.quadPtWeight(quad_pt);
for (int j = 0; j < num_basis; ++j) {
for (int i = 0; i < num_basis; ++i) {
jac_[j*num_basis + i] += w * basis_[i] * flux_density * basis_[j];
}
}
}
}
// Solve linear equation.
MAT_SIZE_T n = num_basis;
MAT_SIZE_T nrhs = 1;
MAT_SIZE_T lda = num_basis;
std::vector<MAT_SIZE_T> piv(num_basis);
MAT_SIZE_T ldb = num_basis;
MAT_SIZE_T info = 0;
orig_jac_ = jac_;
dgesv_(&n, &nrhs, &jac_[0], &lda, &piv[0], &rhs_[0], &ldb, &info);
if (info != 0) {
// Print the local matrix and rhs.
std::cerr << "Failed solving single-cell system Ax = b in cell " << cell
<< " with A = \n";
for (int row = 0; row < n; ++row) {
for (int col = 0; col < n; ++col) {
std::cerr << " " << orig_jac_[row + n*col];
}
std::cerr << '\n';
}
std::cerr << "and b = \n";
for (int row = 0; row < n; ++row) {
std::cerr << " " << rhs_[row] << '\n';
}
THROW("Lapack error: " << info << " encountered in cell " << cell);
}
// The solution ends up in rhs_, so we must copy it.
std::copy(rhs_.begin(), rhs_.end(), tof_coeff_ + num_basis*cell);
}
void TransportModelTracerTofDiscGal::solveMultiCell(const int num_cells, const int* cells)
{
std::cout << "Pretending to solve multi-cell dependent equation with " << num_cells << " cells." << std::endl;
for (int i = 0; i < num_cells; ++i) {
solveSingleCell(cells[i]);
}
}
} // namespace Opm

View File

@ -0,0 +1,105 @@
/*
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_TRANSPORTMODELTRACERTOFDISCGAL_HEADER_INCLUDED
#define OPM_TRANSPORTMODELTRACERTOFDISCGAL_HEADER_INCLUDED
#include <opm/core/transport/reorder/TransportModelInterface.hpp>
#include <boost/shared_ptr.hpp>
#include <vector>
#include <map>
#include <ostream>
struct UnstructuredGrid;
namespace Opm
{
class IncompPropertiesInterface;
class VelocityInterpolationInterface;
/// Implements a discontinuous Galerkin solver for
/// (single-phase) time-of-flight using reordering.
/// The equation solved is:
/// v \cdot \grad\tau = \phi
/// where v is the fluid velocity, \tau is time-of-flight and
/// \phi is the porosity. This is a boundary value problem, where
/// \tau is specified to be zero on all inflow boundaries.
/// The user may specify the polynomial degree of the basis function space
/// used, but only degrees 0 and 1 are supported so far.
class TransportModelTracerTofDiscGal : public TransportModelInterface
{
public:
/// Construct solver.
/// \param[in] grid A 2d or 3d grid.
/// \param[in] use_cvi If true, use corner point velocity interpolation.
/// Otherwise, use the basic constant interpolation.
TransportModelTracerTofDiscGal(const UnstructuredGrid& grid,
const bool use_cvi);
/// Solve for time-of-flight.
/// \param[in] darcyflux Array of signed face fluxes.
/// \param[in] porevolume Array of pore volumes.
/// \param[in] source Source term. Sign convention is:
/// (+) inflow flux,
/// (-) outflow flux.
/// \param[in] degree Polynomial degree of DG basis functions used.
/// \param[out] tof_coeff Array of time-of-flight solution coefficients.
/// The values are ordered by cell, meaning that
/// the K coefficients corresponding to the first
/// cell comes before the K coefficients corresponding
/// to the second cell etc.
/// K depends on degree and grid dimension.
void solveTof(const double* darcyflux,
const double* porevolume,
const double* source,
const int degree,
std::vector<double>& tof_coeff);
private:
virtual void solveSingleCell(const int cell);
virtual void solveMultiCell(const int num_cells, const int* cells);
private:
// Disable copying and assignment.
TransportModelTracerTofDiscGal(const TransportModelTracerTofDiscGal&);
TransportModelTracerTofDiscGal& operator=(const TransportModelTracerTofDiscGal&);
const UnstructuredGrid& grid_;
boost::shared_ptr<VelocityInterpolationInterface> velocity_interpolation_;
const double* darcyflux_; // one flux per grid face
const double* porevolume_; // one volume per cell
const double* source_; // one volumetric source term per cell
int degree_;
double* tof_coeff_;
std::vector<double> rhs_; // single-cell right-hand-side
std::vector<double> jac_; // single-cell jacobian
std::vector<double> orig_jac_; // single-cell jacobian (copy)
// Below: storage for quantities needed by solveSingleCell().
std::vector<double> coord_;
std::vector<double> basis_;
std::vector<double> basis_nb_;
std::vector<double> grad_basis_;
std::vector<double> velocity_;
};
} // namespace Opm
#endif // OPM_TRANSPORTMODELTRACERTOFDISCGAL_HEADER_INCLUDED

View File

@ -0,0 +1,32 @@
/*
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_DATAMAP_HEADER_INCLUDED
#define OPM_DATAMAP_HEADER_INCLUDED
#include <string>
#include <map>
namespace Opm
{
/// Intended to map strings (giving the output field names) to data.
typedef std::map<std::string, const std::vector<double>*> DataMap;
}
#endif

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

@ -101,6 +101,7 @@ namespace Opm
/// @{ /// @{
const double gallon = 231 * cubic(inch); const double gallon = 231 * cubic(inch);
const double stb = 42 * gallon; const double stb = 42 * gallon;
const double liter = 1 * cubic(prefix::deci*meter);
/// @} /// @}
/// \name Mass /// \name Mass

View File

@ -0,0 +1,180 @@
/*
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/utility/VelocityInterpolation.hpp>
#include <opm/core/grid.h>
#include <opm/core/linalg/blas_lapack.h>
namespace Opm
{
// -------- Methods of class VelocityInterpolationInterface --------
VelocityInterpolationInterface::~VelocityInterpolationInterface()
{
}
// -------- Methods of class VelocityInterpolationConstant --------
/// Constructor.
/// \param[in] grid A grid.
VelocityInterpolationConstant::VelocityInterpolationConstant(const UnstructuredGrid& grid)
: grid_(grid)
{
}
/// Set up fluxes for interpolation.
/// \param[in] flux One signed flux per face in the grid.
void VelocityInterpolationConstant::setupFluxes(const double* flux)
{
flux_ = flux;
}
/// Interpolate velocity.
/// \param[in] cell Cell in which to interpolate.
/// \param[in] x Coordinates of point at which to interpolate.
/// Must be array of length grid.dimensions.
/// \param[out] v Interpolated velocity.
/// Must be array of length grid.dimensions.
void VelocityInterpolationConstant::interpolate(const int cell,
const double* /*x*/,
double* v) const
{
const int dim = grid_.dimensions;
std::fill(v, v + dim, 0.0);
const double* cc = grid_.cell_centroids + cell*dim;
for (int hface = grid_.cell_facepos[cell]; hface < grid_.cell_facepos[cell+1]; ++hface) {
const int face = grid_.cell_faces[hface];
const double* fc = grid_.face_centroids + face*dim;
double face_flux = 0.0;
if (cell == grid_.face_cells[2*face]) {
face_flux = flux_[face];
} else {
ASSERT(cell == grid_.face_cells[2*face + 1]);
face_flux = -flux_[face];
}
for (int dd = 0; dd < dim; ++dd) {
v[dd] += face_flux * (fc[dd] - cc[dd]) / grid_.cell_volumes[cell];
}
}
}
// -------- Methods of class VelocityInterpolationECVI --------
/// Constructor.
/// \param[in] grid A grid.
VelocityInterpolationECVI::VelocityInterpolationECVI(const UnstructuredGrid& grid)
: bcmethod_(grid), grid_(grid)
{
}
/// Set up fluxes for interpolation.
/// Computes the corner velocities.
/// \param[in] flux One signed flux per face in the grid.
void VelocityInterpolationECVI::setupFluxes(const double* flux)
{
// We must now update the velocity member of the CornerInfo
// for each corner.
const int dim = grid_.dimensions;
std::vector<double> N(dim*dim); // Normals matrix. Fortran ordering!
std::vector<double> orig_N(dim*dim); // Normals matrix. Fortran ordering!
std::vector<double> f(dim); // Flux vector.
std::vector<MAT_SIZE_T> piv(dim); // For LAPACK solve
const SparseTable<WachspressCoord::CornerInfo>& all_ci = bcmethod_.cornerInfo();
const std::vector<int>& adj_faces = bcmethod_.adjacentFaces();
corner_velocity_.resize(dim*all_ci.dataSize());
const int num_cells = grid_.number_of_cells;
for (int cell = 0; cell < num_cells; ++cell) {
const int num_cell_corners = bcmethod_.numCorners(cell);
for (int cell_corner = 0; cell_corner < num_cell_corners; ++cell_corner) {
const int cid = all_ci[cell][cell_corner].corner_id;
for (int adj_ix = 0; adj_ix < dim; ++adj_ix) {
const int face = adj_faces[dim*cid + adj_ix];
const double* fn = grid_.face_normals + dim*face;
for (int dd = 0; dd < dim; ++dd) {
N[adj_ix + dd*dim] = fn[dd]; // Row adj_ix, column dd
}
f[adj_ix] = flux[face];
}
// Now we have built N and f. Solve Nv = f.
// Note that the face orientations do not matter,
// as changing an orientation would negate both a
// row in N and the corresponding element of f.
// Solving linear equation with LAPACK.
MAT_SIZE_T n = dim;
MAT_SIZE_T nrhs = 1;
MAT_SIZE_T lda = n;
MAT_SIZE_T ldb = n;
MAT_SIZE_T info = 0;
orig_N = N;
dgesv_(&n, &nrhs, &N[0], &lda, &piv[0], &f[0], &ldb, &info);
if (info != 0) {
// Print the local matrix and rhs.
std::cerr << "Failed solving single-cell system Nv = f in cell " << cell
<< " with N = \n";
for (int row = 0; row < n; ++row) {
for (int col = 0; col < n; ++col) {
std::cerr << " " << orig_N[row + n*col];
}
std::cerr << '\n';
}
std::cerr << "and f = \n";
for (int row = 0; row < n; ++row) {
std::cerr << " " << f[row] << '\n';
}
THROW("Lapack error: " << info << " encountered in cell " << cell);
}
// The solution ends up in f, so we must copy it.
std::copy(f.begin(), f.end(), corner_velocity_.begin() + dim*cid);
}
}
}
/// Interpolate velocity.
/// \param[in] cell Cell in which to interpolate.
/// \param[in] x Coordinates of point at which to interpolate.
/// Must be array of length grid.dimensions.
/// \param[out] v Interpolated velocity.
/// Must be array of length grid.dimensions.
void VelocityInterpolationECVI::interpolate(const int cell,
const double* x,
double* v) const
{
const int n = bcmethod_.numCorners(cell);
const int dim = grid_.dimensions;
bary_coord_.resize(n);
bcmethod_.cartToBary(cell, x, &bary_coord_[0]);
std::fill(v, v + dim, 0.0);
const SparseTable<WachspressCoord::CornerInfo>& all_ci = bcmethod_.cornerInfo();
for (int i = 0; i < n; ++i) {
const int cid = all_ci[cell][i].corner_id;
for (int dd = 0; dd < dim; ++dd) {
v[dd] += corner_velocity_[dim*cid + dd] * bary_coord_[i];
}
}
}
} // namespace Opm

View File

@ -0,0 +1,122 @@
/*
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_VELOCITYINTERPOLATION_HEADER_INCLUDED
#define OPM_VELOCITYINTERPOLATION_HEADER_INCLUDED
#include <opm/core/utility/WachspressCoord.hpp>
#include <vector>
struct UnstructuredGrid;
namespace Opm
{
/// Abstract interface for velocity interpolation method classes.
class VelocityInterpolationInterface
{
public:
virtual ~VelocityInterpolationInterface();
/// Set up fluxes for interpolation.
/// \param[in] flux One signed flux per face in the grid.
virtual void setupFluxes(const double* flux) = 0;
/// Interpolate velocity.
/// \param[in] cell Cell in which to interpolate.
/// \param[in] x Coordinates of point at which to interpolate.
/// Must be array of length grid.dimensions.
/// \param[out] v Interpolated velocity.
/// Must be array of length grid.dimensions.
virtual void interpolate(const int cell,
const double* x,
double* v) const = 0;
};
/// A constant velocity approximation. Will not actually interpolate
/// unless the fluxes are consistent with a constant velocity.
class VelocityInterpolationConstant : public VelocityInterpolationInterface
{
public:
/// Constructor.
/// \param[in] grid A grid.
explicit VelocityInterpolationConstant(const UnstructuredGrid& grid);
/// Set up fluxes for interpolation.
/// \param[in] flux One signed flux per face in the grid.
virtual void setupFluxes(const double* flux);
/// Interpolate velocity.
/// \param[in] cell Cell in which to interpolate.
/// \param[in] x Coordinates of point at which to interpolate.
/// Must be array of length grid.dimensions.
/// \param[out] v Interpolated velocity.
/// Must be array of length grid.dimensions.
virtual void interpolate(const int cell,
const double* x,
double* v) const;
private:
const UnstructuredGrid& grid_;
const double* flux_;
};
/// Interpolate velocity using the extended CVI scheme:
/// compute a corner velocity for each cell corner that
/// is consistent with fluxes of adjacent faces, then
/// interpolate with generalized barycentric coordinates.
class VelocityInterpolationECVI : public VelocityInterpolationInterface
{
public:
/// Constructor.
/// \param[in] grid A grid.
explicit VelocityInterpolationECVI(const UnstructuredGrid& grid);
/// Set up fluxes for interpolation.
/// \param[in] flux One signed flux per face in the grid.
virtual void setupFluxes(const double* flux);
/// Interpolate velocity.
/// \param[in] cell Cell in which to interpolate.
/// \param[in] x Coordinates of point at which to interpolate.
/// Must be array of length grid.dimensions.
/// \param[out] v Interpolated velocity.
/// Must be array of length grid.dimensions.
virtual void interpolate(const int cell,
const double* x,
double* v) const;
private:
WachspressCoord bcmethod_;
const UnstructuredGrid& grid_;
mutable std::vector<double> bary_coord_;
std::vector<double> corner_velocity_; // size = dim * #corners
};
} // namespace Opm
#endif // OPM_VELOCITYINTERPOLATION_HEADER_INCLUDED

View File

@ -0,0 +1,237 @@
/*
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/utility/WachspressCoord.hpp>
#include <opm/core/grid.h>
#include <cmath>
#include <map>
#include <set>
namespace Opm
{
// -------- Helper methods for class WachspressCoord --------
namespace
{
/// Calculates the determinant of a 2 x 2 matrix, represented as
/// two two-dimensional arrays.
double determinantOf(const double* a0,
const double* a1)
{
return
a0[0] * a1[1] - a0[1] * a1[0];
}
/// Calculates the determinant of a 3 x 3 matrix, represented as
/// three three-dimensional arrays.
double determinantOf(const double* a0,
const double* a1,
const double* a2)
{
return
a0[0] * (a1[1] * a2[2] - a2[1] * a1[2]) -
a0[1] * (a1[0] * a2[2] - a2[0] * a1[2]) +
a0[2] * (a1[0] * a2[1] - a2[0] * a1[1]);
}
/// Calculates the volume of the parallelepiped given by
/// the vectors n[i] for i = 0..(dim-1), each n[i] is of size dim.
double cornerVolume(double** n, const int dim)
{
ASSERT(dim == 2 || dim == 3);
double det = (dim == 2) ? determinantOf(n[0], n[1]) : determinantOf(n[0], n[1], n[2]);
return std::fabs(det);
}
} // anonymous namespace
// -------- Methods of class WachspressCoord --------
// The formula used is a modification of the formula given in:
// M. Meyer, A. Barr, H. Lee, and M. Desbrun.
// Generalized barycentric coordinates on irregular poly-
// gons. Journal of Graphics Tools, 7(1):1322, 2002.
//
// The formula given there is, for a corner i,
// b_i = w_i / sum_{k} w_k
// w_i = V_i / (prod_{j \in adjacent faces} n_j * (x_i - x) )
// ^^^ ^^^ ^^^
// corner "volume" normal corner coordinates
// V_i = |Det({n_j}_{j \in adjacent faces})|
// The corner coordinate x_i above can be replaced with any point on face j
// without changing the value of w_i, and we replace it with c_j, the face
// centroid.
// However, this formula has the problem that the denominator of w_i becomes zero
// close to the boundary. Our solution is to multiply all w_i by
/// prod_{all j} n_j * (c_j - x), resulting in the formula:
// w_i = V_i * (prod_{j \in nonadjacent faces} n_j * (c_j - x) ).
// Another implementation note is that the above formulas assumes that
// the normals have length 1 (are unit normals). It is easy to see that this
// can be relaxed, since each normal occurs once in the formula for w_i, and
// all w_i will be scaled by the same number. In our implementation we therefore
// use the area-scaled normals directly as provided by the UnstructuredGrid.
/// Constructor.
/// \param[in] grid A grid.
WachspressCoord::WachspressCoord(const UnstructuredGrid& grid)
: grid_(grid)
{
enum { Maxdim = 3 };
const int dim = grid.dimensions;
if (dim > Maxdim) {
THROW("Grid has more than " << Maxdim << " dimensions.");
}
// Compute static data for each corner.
const int num_cells = grid.number_of_cells;
int corner_id_count = 0;
for (int cell = 0; cell < num_cells; ++cell) {
std::set<int> cell_vertices;
std::vector<int> cell_faces;
std::multimap<int, int> vertex_adj_faces;
for (int hface = grid.cell_facepos[cell]; hface < grid.cell_facepos[cell + 1]; ++hface) {
const int face = grid.cell_faces[hface];
cell_faces.push_back(face);
const int fn0 = grid.face_nodepos[face];
const int fn1 = grid.face_nodepos[face + 1];
cell_vertices.insert(grid.face_nodes + fn0, grid.face_nodes + fn1);
for (int fn = fn0; fn < fn1; ++fn) {
const int vertex = grid.face_nodes[fn];
vertex_adj_faces.insert(std::make_pair(vertex, face));
}
}
std::sort(cell_faces.begin(), cell_faces.end()); // set_difference requires sorted ranges
std::vector<CornerInfo> cell_corner_info;
std::set<int>::const_iterator it = cell_vertices.begin();
for (; it != cell_vertices.end(); ++it) {
CornerInfo ci;
ci.corner_id = corner_id_count++;;
ci.vertex = *it;
double* fnorm[Maxdim] = { 0 };
typedef std::multimap<int, int>::const_iterator MMIt;
std::pair<MMIt, MMIt> frange = vertex_adj_faces.equal_range(ci.vertex);
int fi = 0;
std::vector<int> vert_adj_faces(dim);
for (MMIt face_it = frange.first; face_it != frange.second; ++face_it, ++fi) {
if (fi >= dim) {
THROW("In cell " << cell << ", vertex " << ci.vertex << " has "
<< " more than " << dim << " adjacent faces.");
}
fnorm[fi] = grid_.face_normals + dim*(face_it->second);
vert_adj_faces[fi] = face_it->second;
}
ASSERT(fi == dim);
adj_faces_.insert(adj_faces_.end(), vert_adj_faces.begin(), vert_adj_faces.end());
const double corner_vol = cornerVolume(fnorm, dim);
ci.volume = corner_vol;
cell_corner_info.push_back(ci);
std::sort(vert_adj_faces.begin(), vert_adj_faces.end());
std::vector<int> vert_nonadj_faces(cell_faces.size() - vert_adj_faces.size());
std::set_difference(cell_faces.begin(), cell_faces.end(),
vert_adj_faces.begin(), vert_adj_faces.end(),
vert_nonadj_faces.begin());
nonadj_faces_.appendRow(vert_nonadj_faces.begin(), vert_nonadj_faces.end());
}
corner_info_.appendRow(cell_corner_info.begin(), cell_corner_info.end());
}
ASSERT(corner_id_count == corner_info_.dataSize());
}
/// Count of vertices adjacent to a call.
/// \param[in] cell A cell index.
/// \return Number of corners of cell.
int WachspressCoord::numCorners(const int cell) const
{
return corner_info_[cell].size();
}
/// The class stores some info for each corner.
/// \return The corner info container.
const SparseTable<WachspressCoord::CornerInfo>& WachspressCoord::cornerInfo() const
{
return corner_info_;
}
/// The class stores some info for each corner.
/// \return The corner info container.
const std::vector<int>& WachspressCoord::adjacentFaces() const
{
return adj_faces_;
}
/// Compute generalized barycentric coordinates for some point x
/// with respect to the vertices of a grid cell.
/// \param[in] cell Cell in which to compute coordinates.
/// \param[in] x Coordinates of point in cartesian coordinates.
/// Must be array of length grid.dimensions.
/// \param[out] xb Coordinates of point in barycentric coordinates.
/// Must be array of length numCorners(cell).
void WachspressCoord::cartToBary(const int cell,
const double* x,
double* xb) const
{
// Note:
// A possible optimization is: compute all n_j * (c_j - x) factors
// once, instead of repeating computation for all corners (for
// which j is a nonadjacent face).
const int n = numCorners(cell);
const int dim = grid_.dimensions;
double totw = 0.0;
for (int i = 0; i < n; ++i) {
const CornerInfo& ci = corner_info_[cell][i];
// Weight (unnormalized) is equal to:
// V_i * (prod_{j \in nonadjacent faces} n_j * (c_j - x) )
// ^^^ ^^^ ^^^
// corner "volume" normal centroid
xb[i] = ci.volume;
const int num_nonadj_faces = nonadj_faces_[ci.corner_id].size();
for (int j = 0; j < num_nonadj_faces; ++j) {
const int face = nonadj_faces_[ci.corner_id][j];
double factor = 0.0;
for (int dd = 0; dd < dim; ++dd) {
factor += grid_.face_normals[dim*face + dd]*(grid_.face_centroids[dim*face + dd] - x[dd]);
}
// Assumes outward-pointing normals, so negate factor if necessary.
if (grid_.face_cells[2*face] != cell) {
ASSERT(grid_.face_cells[2*face + 1] == cell);
factor = -factor;
}
xb[i] *= factor;
}
totw += xb[i];
}
for (int i = 0; i < n; ++i) {
xb[i] /= totw;
}
}
} // namespace Opm

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_WACHSPRESSCOORD_HEADER_INCLUDED
#define OPM_WACHSPRESSCOORD_HEADER_INCLUDED
#include <opm/core/utility/SparseTable.hpp>
#include <vector>
struct UnstructuredGrid;
namespace Opm
{
/// Class capable of computing Wachspress coordinates in 2d and 3d.
/// The formula used is a modification of the formula given in:
/// M. Meyer, A. Barr, H. Lee, and M. Desbrun.
/// Generalized barycentric coordinates on irregular poly-
/// gons. Journal of Graphics Tools, 7(1):1322, 2002.
class WachspressCoord
{
public:
/// Constructor.
/// \param[in] grid A grid.
explicit WachspressCoord(const UnstructuredGrid& grid);
/// Count of vertices adjacent to a call.
/// \param[in] cell A cell index.
/// \return Number of corners of cell.
int numCorners(const int cell) const;
/// Compute generalized barycentric coordinates for some point x
/// with respect to the vertices of a grid cell.
/// \param[in] cell Cell in which to compute coordinates.
/// \param[in] x Coordinates of point in cartesian coordinates.
/// Must be array of length grid.dimensions.
/// \param[out] xb Coordinates of point in barycentric coordinates.
/// Must be array of length numCorners(cell).
void cartToBary(const int cell,
const double* x,
double* xb) const;
// A corner is here defined as a {cell, vertex} pair where the
// vertex is adjacent to the cell.
struct CornerInfo
{
int corner_id; // Unique for each corner.
int vertex; // Shared between corners belonging to different cells.
double volume; // Defined as det(N) where N is the matrix of adjacent face normals.
};
/// The class stores some info for each corner, made accessible for user convenience.
/// \return The corner info container.
const SparseTable<CornerInfo>& cornerInfo() const;
/// The class stores adjacent faces for each corner, made accessible for user convenience.
/// \return The vector of adjacent faces. Size = dim * #corners.
const std::vector<int>& adjacentFaces() const;
private:
const UnstructuredGrid& grid_;
SparseTable<CornerInfo> corner_info_; // Corner info by cell.
std::vector<int> adj_faces_; // Set of adjacent faces, by corner id. Contains dim face indices per corner.
SparseTable<int> nonadj_faces_; // Set of nonadjacent faces, by corner id.
};
} // namespace Opm
#endif // OPM_WACHSPRESSCOORD_HEADER_INCLUDED

View File

@ -29,6 +29,7 @@
#include <opm/core/utility/Units.hpp> #include <opm/core/utility/Units.hpp>
#include <opm/core/fluid/IncompPropertiesInterface.hpp> #include <opm/core/fluid/IncompPropertiesInterface.hpp>
#include <opm/core/fluid/BlackoilPropertiesInterface.hpp> #include <opm/core/fluid/BlackoilPropertiesInterface.hpp>
#include <opm/core/fluid/blackoil/phaseUsageFromDeck.hpp>
#include <cmath> #include <cmath>
namespace Opm namespace Opm
@ -512,15 +513,23 @@ namespace Opm
State& state) State& state)
{ {
const int num_phases = props.numPhases(); const int num_phases = props.numPhases();
const PhaseUsage pu = phaseUsageFromDeck(deck);
if (num_phases != pu.num_phases) {
THROW("initStateFromDeck(): user specified property object with " << num_phases << " phases, "
"found " << pu.num_phases << " phases in deck.");
}
state.init(grid, num_phases); state.init(grid, num_phases);
if (deck.hasField("EQUIL")) { if (deck.hasField("EQUIL")) {
if (num_phases != 2) { if (num_phases != 2) {
THROW("initStateFromDeck(): EQUIL-based init currently handling only two-phase scenarios."); THROW("initStateFromDeck(): EQUIL-based init currently handling only two-phase scenarios.");
} }
if (pu.phase_used[BlackoilPhases::Vapour]) {
THROW("initStateFromDeck(): EQUIL-based init currently handling only oil-water scenario (no gas).");
}
// 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) {
THROW("No region support yet."); THROW("initStateFromDeck(): No region support yet.");
} }
const double woc = equil.equil[0].water_oil_contact_depth_; const double woc = equil.equil[0].water_oil_contact_depth_;
initWaterOilContact(grid, props, woc, WaterBelow, state); initWaterOilContact(grid, props, woc, WaterBelow, state);
@ -528,37 +537,64 @@ namespace Opm
const double datum_z = equil.equil[0].datum_depth_; const double datum_z = equil.equil[0].datum_depth_;
const double datum_p = equil.equil[0].datum_depth_pressure_; const double datum_p = equil.equil[0].datum_depth_pressure_;
initHydrostaticPressure(grid, props, woc, gravity, datum_z, datum_p, state); initHydrostaticPressure(grid, props, woc, gravity, datum_z, datum_p, state);
} else if (deck.hasField("SWAT") && deck.hasField("PRESSURE")) { } else if (deck.hasField("PRESSURE")) {
// Set saturations from SWAT, pressure from PRESSURE. // Set saturations from SWAT/SGAS, pressure from PRESSURE.
std::vector<double>& s = state.saturation(); std::vector<double>& s = state.saturation();
std::vector<double>& p = state.pressure(); std::vector<double>& p = state.pressure();
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) { if (num_phases == 2) {
for (int c = 0; c < num_cells; ++c) { if (!pu.phase_used[BlackoilPhases::Aqua]) {
int c_deck = (grid.global_cell == NULL) ? c : grid.global_cell[c]; // oil-gas: we require SGAS
s[2*c] = sw_deck[c_deck]; if (!deck.hasField("SGAS")) {
s[2*c + 1] = 1.0 - s[2*c]; THROW("initStateFromDeck(): missing SGAS keyword in 2-phase init");
p[c] = p_deck[c_deck]; }
const std::vector<double>& sg_deck = deck.getFloatingPointValue("SGAS");
const int gpos = pu.phase_pos[BlackoilPhases::Vapour];
const int opos = pu.phase_pos[BlackoilPhases::Liquid];
for (int c = 0; c < num_cells; ++c) {
int c_deck = (grid.global_cell == NULL) ? c : grid.global_cell[c];
s[2*c + gpos] = sg_deck[c_deck];
s[2*c + opos] = 1.0 - sg_deck[c_deck];
p[c] = p_deck[c_deck];
}
} else {
// water-oil or water-gas: we require SWAT
if (!deck.hasField("SWAT")) {
THROW("initStateFromDeck(): missing SWAT keyword in 2-phase init");
}
const std::vector<double>& sw_deck = deck.getFloatingPointValue("SWAT");
const int wpos = pu.phase_pos[BlackoilPhases::Aqua];
const int nwpos = (wpos + 1) % 2;
for (int c = 0; c < num_cells; ++c) {
int c_deck = (grid.global_cell == NULL) ? c : grid.global_cell[c];
s[2*c + wpos] = sw_deck[c_deck];
s[2*c + nwpos] = 1.0 - sw_deck[c_deck];
p[c] = p_deck[c_deck];
}
} }
} else if (num_phases == 3) { } else if (num_phases == 3) {
if (!deck.hasField("SGAS")) { const bool has_swat_sgas = deck.hasField("SWAT") && deck.hasField("SGAS");
THROW("initStateFromDeck(): missing SGAS keyword in 3-phase init (only SWAT and PRESSURE found)."); if (!has_swat_sgas) {
THROW("initStateFromDeck(): missing SGAS or SWAT keyword in 3-phase init.");
} }
const int wpos = pu.phase_pos[BlackoilPhases::Aqua];
const int gpos = pu.phase_pos[BlackoilPhases::Vapour];
const int opos = pu.phase_pos[BlackoilPhases::Liquid];
const std::vector<double>& sw_deck = deck.getFloatingPointValue("SWAT");
const std::vector<double>& sg_deck = deck.getFloatingPointValue("SGAS"); const std::vector<double>& sg_deck = deck.getFloatingPointValue("SGAS");
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[3*c] = sw_deck[c_deck]; s[3*c + wpos] = sw_deck[c_deck];
s[3*c + 1] = 1.0 - (sw_deck[c_deck] + sg_deck[c_deck]); s[3*c + opos] = 1.0 - (sw_deck[c_deck] + sg_deck[c_deck]);
s[3*c + 2] = sg_deck[c_deck]; s[3*c + gpos] = sg_deck[c_deck];
p[c] = p_deck[c_deck]; p[c] = p_deck[c_deck];
} }
} else { } else {
THROW("initStateFromDeck(): init with SWAT etc. only available with 2 or 3 phases."); 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 PRESSURE and SWAT/SOIL/SGAS.");
} }
// Finally, init face pressures. // Finally, init face pressures.

View File

@ -374,7 +374,10 @@ namespace Opm
if (perf_rate > 0.0) { if (perf_rate > 0.0) {
// perf_rate is a total inflow rate, we want a water rate. // perf_rate is a total inflow rate, we want a water rate.
if (wells->type[w] != INJECTOR) { if (wells->type[w] != INJECTOR) {
std::cout << "**** Warning: crossflow in well with index " << w << " ignored." << std::endl; std::cout << "**** Warning: crossflow in well "
<< w << " perf " << perf - wells->well_connpos[w]
<< " ignored. Rate was "
<< perf_rate/Opm::unit::day << " m^3/day." << std::endl;
perf_rate = 0.0; perf_rate = 0.0;
} else { } else {
ASSERT(std::fabs(comp_frac[0] + comp_frac[1] - 1.0) < 1e-6); ASSERT(std::fabs(comp_frac[0] + comp_frac[1] - 1.0) < 1e-6);
@ -550,6 +553,7 @@ namespace Opm
{ {
const int np = wells.number_of_phases; const int np = wells.number_of_phases;
const int nw = wells.number_of_wells; const int nw = wells.number_of_wells;
ASSERT(int(flow_rates_per_well_cell.size()) == wells.well_connpos[nw]);
phase_flow_per_well.resize(nw * np); phase_flow_per_well.resize(nw * np);
for (int wix = 0; wix < nw; ++wix) { for (int wix = 0; wix < nw; ++wix) {
for (int phase = 0; phase < np; ++phase) { for (int phase = 0; phase < np; ++phase) {

View File

@ -21,7 +21,10 @@
#include <opm/core/utility/miscUtilitiesBlackoil.hpp> #include <opm/core/utility/miscUtilitiesBlackoil.hpp>
#include <opm/core/utility/Units.hpp> #include <opm/core/utility/Units.hpp>
#include <opm/core/grid.h> #include <opm/core/grid.h>
#include <opm/core/newwells.h>
#include <opm/core/fluid/BlackoilPropertiesInterface.hpp> #include <opm/core/fluid/BlackoilPropertiesInterface.hpp>
#include <opm/core/simulator/BlackoilState.hpp>
#include <opm/core/simulator/WellState.hpp>
#include <opm/core/utility/ErrorMacros.hpp> #include <opm/core/utility/ErrorMacros.hpp>
#include <algorithm> #include <algorithm>
#include <functional> #include <functional>
@ -31,53 +34,74 @@
namespace Opm namespace Opm
{ {
/// @brief Computes injected and produced volumes of all phases. /// @brief Computes injected and produced surface volumes of all phases.
/// Note 1: assumes that only the first phase is injected. /// Note 1: assumes that only the first phase is injected.
/// Note 2: assumes that transport has been done with an /// Note 2: assumes that transport has been done with an
/// implicit method, i.e. that the current state /// implicit method, i.e. that the current state
/// gives the mobilities used for the preceding timestep. /// gives the mobilities used for the preceding timestep.
/// @param[in] props fluid and rock properties. /// Note 3: Gives surface volume values, not reservoir volumes
/// @param[in] p pressure (one value per cell) /// (as the incompressible version of the function does).
/// @param[in] z surface-volume values (for all P phases) /// Also, assumes that transport_src is given in surface volumes
/// @param[in] s saturation values (for all P phases) /// for injector terms!
/// @param[in] src if < 0: total outflow, if > 0: first phase inflow. /// @param[in] props fluid and rock properties.
/// @param[in] dt timestep used /// @param[in] state state variables (pressure, sat, surfvol)
/// @param[out] injected must point to a valid array with P elements, /// @param[in] transport_src if < 0: total resv outflow, if > 0: first phase surfv inflow
/// where P = s.size()/src.size(). /// @param[in] dt timestep used
/// @param[out] produced must also point to a valid array with P elements. /// @param[out] injected must point to a valid array with P elements,
/// where P = s.size()/src.size().
/// @param[out] produced must also point to a valid array with P elements.
void computeInjectedProduced(const BlackoilPropertiesInterface& props, void computeInjectedProduced(const BlackoilPropertiesInterface& props,
const std::vector<double>& press, const BlackoilState& state,
const std::vector<double>& z, const std::vector<double>& transport_src,
const std::vector<double>& s,
const std::vector<double>& src,
const double dt, const double dt,
double* injected, double* injected,
double* produced) double* produced)
{ {
const int num_cells = src.size(); const int num_cells = transport_src.size();
const int np = s.size()/src.size(); if (props.numCells() != num_cells) {
if (int(s.size()) != num_cells*np) { THROW("Size of transport_src vector does not match number of cells in props.");
THROW("Sizes of s and src vectors do not match.");
} }
const int np = props.numPhases();
if (int(state.saturation().size()) != num_cells*np) {
THROW("Sizes of state vectors do not match number of cells.");
}
const std::vector<double>& press = state.pressure();
const std::vector<double>& s = state.saturation();
const std::vector<double>& z = state.surfacevol();
std::fill(injected, injected + np, 0.0); std::fill(injected, injected + np, 0.0);
std::fill(produced, produced + np, 0.0); std::fill(produced, produced + np, 0.0);
std::vector<double> visc(np); std::vector<double> visc(np);
std::vector<double> mob(np); std::vector<double> mob(np);
std::vector<double> A(np*np);
std::vector<double> prod_resv_phase(np);
std::vector<double> prod_surfvol(np);
for (int c = 0; c < num_cells; ++c) { for (int c = 0; c < num_cells; ++c) {
if (src[c] > 0.0) { if (transport_src[c] > 0.0) {
injected[0] += src[c]*dt; // Inflowing transport source is a surface volume flux
} else if (src[c] < 0.0) { // for the first phase.
const double flux = -src[c]*dt; injected[0] += transport_src[c]*dt;
} else if (transport_src[c] < 0.0) {
// Outflowing transport source is a total reservoir
// volume flux.
const double flux = -transport_src[c]*dt;
const double* sat = &s[np*c]; const double* sat = &s[np*c];
props.relperm(1, sat, &c, &mob[0], 0); props.relperm(1, sat, &c, &mob[0], 0);
props.viscosity(1, &press[c], &z[np*c], &c, &visc[0], 0); props.viscosity(1, &press[c], &z[np*c], &c, &visc[0], 0);
props.matrix(1, &press[c], &z[np*c], &c, &A[0], 0);
double totmob = 0.0; double totmob = 0.0;
for (int p = 0; p < np; ++p) { for (int p = 0; p < np; ++p) {
mob[p] /= visc[p]; mob[p] /= visc[p];
totmob += mob[p]; totmob += mob[p];
} }
std::fill(prod_surfvol.begin(), prod_surfvol.end(), 0.0);
for (int p = 0; p < np; ++p) { for (int p = 0; p < np; ++p) {
produced[p] += (mob[p]/totmob)*flux; prod_resv_phase[p] = (mob[p]/totmob)*flux;
for (int q = 0; q < np; ++q) {
prod_surfvol[q] += prod_resv_phase[p]*A[q + np*p];
}
}
for (int p = 0; p < np; ++p) {
produced[p] += prod_surfvol[p];
} }
} }
} }
@ -251,4 +275,58 @@ namespace Opm
} }
} }
/// Compute two-phase transport source terms from well terms.
/// Note: Unlike the incompressible version of this function,
/// this version computes surface volume injection rates,
/// production rates are still total reservoir volumes.
/// \param[in] props Fluid and rock properties.
/// \param[in] wells Wells data structure.
/// \param[in] well_state Well pressures and fluxes.
/// \param[out] transport_src The transport source terms. They are to be interpreted depending on sign:
/// (+) positive inflow of first (water) phase (surface volume),
/// (-) negative total outflow of both phases (reservoir volume).
void computeTransportSource(const BlackoilPropertiesInterface& props,
const Wells* wells,
const WellState& well_state,
std::vector<double>& transport_src)
{
int nc = props.numCells();
transport_src.clear();
transport_src.resize(nc, 0.0);
// Well contributions.
if (wells) {
const int nw = wells->number_of_wells;
const int np = wells->number_of_phases;
if (np != 2) {
THROW("computeTransportSource() requires a 2 phase case.");
}
std::vector<double> A(np*np);
for (int w = 0; w < nw; ++w) {
const double* comp_frac = wells->comp_frac + np*w;
for (int perf = wells->well_connpos[w]; perf < wells->well_connpos[w + 1]; ++perf) {
const int perf_cell = wells->well_cells[perf];
double perf_rate = well_state.perfRates()[perf];
if (perf_rate > 0.0) {
// perf_rate is a total inflow reservoir rate, we want a surface water rate.
if (wells->type[w] != INJECTOR) {
std::cout << "**** Warning: crossflow in well "
<< w << " perf " << perf - wells->well_connpos[w]
<< " ignored. Reservoir rate was "
<< perf_rate/Opm::unit::day << " m^3/day." << std::endl;
perf_rate = 0.0;
} else {
ASSERT(std::fabs(comp_frac[0] + comp_frac[1] - 1.0) < 1e-6);
perf_rate *= comp_frac[0]; // Water reservoir volume rate.
props.matrix(1, &well_state.perfPress()[perf], comp_frac, &perf_cell, &A[0], 0);
perf_rate *= A[0]; // Water surface volume rate.
}
}
transport_src[perf_cell] += perf_rate;
}
}
}
}
} // namespace Opm } // namespace Opm

View File

@ -22,36 +22,40 @@
#include <vector> #include <vector>
struct UnstructuredGrid; struct Wells;
namespace Opm namespace Opm
{ {
class BlackoilPropertiesInterface; class BlackoilPropertiesInterface;
class BlackoilState;
class WellState;
/// @brief Computes injected and produced volumes of all phases.
/// @brief Computes injected and produced surface volumes of all phases.
/// Note 1: assumes that only the first phase is injected. /// Note 1: assumes that only the first phase is injected.
/// Note 2: assumes that transport has been done with an /// Note 2: assumes that transport has been done with an
/// implicit method, i.e. that the current state /// implicit method, i.e. that the current state
/// gives the mobilities used for the preceding timestep. /// gives the mobilities used for the preceding timestep.
/// @param[in] props fluid and rock properties. /// Note 3: Gives surface volume values, not reservoir volumes
/// @param[in] p pressure (one value per cell) /// (as the incompressible version of the function does).
/// @param[in] z surface-volume values (for all P phases) /// Also, assumes that transport_src is given in surface volumes
/// @param[in] s saturation values (for all P phases) /// for injector terms!
/// @param[in] src if < 0: total outflow, if > 0: first phase inflow. /// @param[in] props fluid and rock properties.
/// @param[in] dt timestep used /// @param[in] state state variables (pressure, sat, surfvol)
/// @param[out] injected must point to a valid array with P elements, /// @param[in] transport_src if < 0: total resv outflow, if > 0: first phase surfv inflow
/// where P = s.size()/src.size(). /// @param[in] dt timestep used
/// @param[out] produced must also point to a valid array with P elements. /// @param[out] injected must point to a valid array with P elements,
/// where P = s.size()/src.size().
/// @param[out] produced must also point to a valid array with P elements.
void computeInjectedProduced(const BlackoilPropertiesInterface& props, void computeInjectedProduced(const BlackoilPropertiesInterface& props,
const std::vector<double>& p, const BlackoilState& state,
const std::vector<double>& z, const std::vector<double>& transport_src,
const std::vector<double>& s,
const std::vector<double>& src,
const double dt, const double dt,
double* injected, double* injected,
double* produced); double* produced);
/// @brief Computes total mobility for a set of saturation values. /// @brief Computes total mobility for a set of saturation values.
/// @param[in] props rock and fluid properties /// @param[in] props rock and fluid properties
/// @param[in] cells cells with which the saturation values are associated /// @param[in] cells cells with which the saturation values are associated
@ -66,6 +70,7 @@ namespace Opm
const std::vector<double>& s, const std::vector<double>& s,
std::vector<double>& totmob); std::vector<double>& totmob);
/// @brief Computes total mobility and omega for a set of saturation values. /// @brief Computes total mobility and omega for a set of saturation values.
/// @param[in] props rock and fluid properties /// @param[in] props rock and fluid properties
/// @param[in] cells cells with which the saturation values are associated /// @param[in] cells cells with which the saturation values are associated
@ -131,6 +136,22 @@ namespace Opm
const double* saturation, const double* saturation,
double* surfacevol); double* surfacevol);
/// Compute two-phase transport source terms from well terms.
/// Note: Unlike the incompressible version of this function,
/// this version computes surface volume injection rates,
/// production rates are still total reservoir volumes.
/// \param[in] props Fluid and rock properties.
/// \param[in] wells Wells data structure.
/// \param[in] well_state Well pressures and fluxes.
/// \param[out] transport_src The transport source terms. They are to be interpreted depending on sign:
/// (+) positive inflow of first (water) phase (surface volume),
/// (-) negative total outflow of both phases (reservoir volume).
void computeTransportSource(const BlackoilPropertiesInterface& props,
const Wells* wells,
const WellState& well_state,
std::vector<double>& transport_src);
} // namespace Opm } // namespace Opm
#endif // OPM_MISCUTILITIESBLACKOIL_HEADER_INCLUDED #endif // OPM_MISCUTILITIESBLACKOIL_HEADER_INCLUDED

View File

@ -0,0 +1,108 @@
/*
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/>.
*/
#include <opm/core/grid.h>
#include <opm/core/simulator/SimulatorTimer.hpp>
#include <opm/core/utility/writeECLData.hpp>
#include <opm/core/utility/Units.hpp>
#include <vector>
#include <ecl_grid.h>
#include <ecl_util.h>
#include <ecl_rst_file.h>
namespace Opm
{
static ecl_kw_type * ecl_kw_wrapper( const UnstructuredGrid& grid,
const std::string& kw_name ,
const std::vector<double> * data ,
int offset ,
int stride ) {
ecl_kw_type * ecl_kw = ecl_kw_alloc( kw_name.c_str() , data->size() / stride , ECL_FLOAT_TYPE );
if (grid.global_cell == NULL) {
for (int i=0; i < grid.number_of_cells; i++)
ecl_kw_iset_float( ecl_kw , i , (*data)[i*stride + offset]);
} else {
for (int i=0; i < grid.number_of_cells; i++)
ecl_kw_iset_float( ecl_kw , grid.global_cell[i] , (*data)[i*stride + offset]);
}
return ecl_kw;
}
void writeECLData(const UnstructuredGrid& grid,
const DataMap& data,
const SimulatorTimer& simtimer,
const std::string& output_dir,
const std::string& base_name) {
int step = simtimer.currentStepNum();
ecl_file_enum file_type = ECL_UNIFIED_RESTART_FILE;
bool fmt_file = true;
char * filename = ecl_util_alloc_filename(output_dir.c_str() , base_name.c_str() , file_type , fmt_file , step );
int phases = ECL_OIL_PHASE + ECL_WATER_PHASE;
double days = Opm::unit::convert::to(simtimer.currentTime(), Opm::unit::day);
time_t date = 0;
int nx = grid.cartdims[0];
int ny = grid.cartdims[1];
int nz = grid.cartdims[2];
int nactive = grid.number_of_cells;
ecl_rst_file_type * rst_file;
if (step > 0 && file_type == ECL_UNIFIED_RESTART_FILE)
rst_file = ecl_rst_file_open_append( filename );
else
rst_file = ecl_rst_file_open_write( filename );
ecl_rst_file_fwrite_header( rst_file , step , date , days , nx , ny , nz , nactive , phases );
ecl_rst_file_start_solution( rst_file );
{
DataMap::const_iterator i = data.find("pressure");
if (i != data.end()) {
ecl_kw_type * pressure_kw = ecl_kw_wrapper( grid , "PRESSURE" , i->second , 0 , 1);
ecl_rst_file_add_kw( rst_file , pressure_kw );
ecl_kw_free( pressure_kw );
}
}
{
DataMap::const_iterator i = data.find("saturation");
if (i != data.end()) {
ecl_kw_type * swat_kw = ecl_kw_wrapper( grid , "SWAT" , i->second , 0 , 2);
ecl_rst_file_add_kw( rst_file , swat_kw );
ecl_kw_free( swat_kw );
}
}
ecl_rst_file_end_solution( rst_file );
ecl_rst_file_close( rst_file );
free(filename);
}
}

View File

@ -0,0 +1,47 @@
/*
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/>.
*/
#ifndef OPM_WRITEECLDATA_HEADER_INCLUDED
#define OPM_WRITEECLDATA_HEADER_INCLUDED
#include <string>
#include <map>
#include <vector>
#include <tr1/array>
#include <iosfwd>
#include <opm/core/utility/DataMap.hpp>
#include <opm/core/simulator/SimulatorTimer.hpp>
struct UnstructuredGrid;
namespace Opm
{
// ECLIPSE output for general grids.
void writeECLData(const UnstructuredGrid& grid,
const DataMap& data,
const SimulatorTimer& simtimer,
const std::string& output_dir,
const std::string& base_name);
}
#endif

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