Merge from upstream and corrected conflictes.
This commit is contained in:
commit
c6a609bea6
14
.gitignore
vendored
14
.gitignore
vendored
@ -1,6 +1,7 @@
|
||||
*~
|
||||
.\#*
|
||||
*.o
|
||||
*.mod
|
||||
*.lo
|
||||
*.la
|
||||
.libs
|
||||
@ -29,19 +30,30 @@ ltmain.sh
|
||||
m4/libtool.m4
|
||||
m4/lt*.m4
|
||||
missing
|
||||
ar-lib
|
||||
tutorials/tutorial[1-4]
|
||||
opmcore-config.cmake
|
||||
|
||||
# in-tree build with CMake
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
cmake_install.cmake
|
||||
|
||||
# Ignoring executables
|
||||
*_test
|
||||
examples/compute_tof
|
||||
examples/scaneclipsedeck
|
||||
examples/spu_2p
|
||||
examples/reorder-qfs
|
||||
examples/refine_wells
|
||||
examples/sim_2p_incomp_reorder
|
||||
examples/sim_2p_comp_reorder
|
||||
examples/sim_wateroil
|
||||
examples/wells_example
|
||||
tests/test_agmg
|
||||
tests/test_cfs_tpfa
|
||||
tests/test_jacsys
|
||||
tests/test_read_grid
|
||||
tests/test_readvector
|
||||
tests/test_sf2p
|
||||
tests/bo_fluid_p_and_z_deps
|
||||
@ -51,4 +63,6 @@ tests/test_column_extract
|
||||
tests/test_lapack
|
||||
tests/test_read_vag
|
||||
tests/test_readpolymer
|
||||
tests/test_velocityinterpolation
|
||||
tests/test_wells
|
||||
tests/test_writeVtkData
|
||||
|
43
Makefile.am
43
Makefile.am
@ -12,7 +12,9 @@ lib_LTLIBRARIES = lib/libopmcore.la
|
||||
# Build-time flags needed to build libopmcore.la
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
$(OPM_BOOST_CPPFLAGS)
|
||||
$(ERT_CPPFLAGS) \
|
||||
$(OPM_BOOST_CPPFLAGS) \
|
||||
$(SUPERLU_CPPFLAGS)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Link-time flags needed both to successfully link the library and to
|
||||
@ -20,14 +22,17 @@ $(OPM_BOOST_CPPFLAGS)
|
||||
|
||||
lib_libopmcore_la_LDFLAGS = \
|
||||
-R $(OPM_BOOST_LIBDIR) \
|
||||
$(OPM_BOOST_LDFLAGS)
|
||||
$(OPM_BOOST_LDFLAGS) \
|
||||
$(ERT_LDFLAGS) \
|
||||
$(SUPERLU_LDFLAGS)
|
||||
|
||||
lib_libopmcore_la_LIBADD = \
|
||||
$(BOOST_FILESYSTEM_LIB) \
|
||||
$(BOOST_SYSTEM_LIB) \
|
||||
$(BOOST_DATE_TIME_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.
|
||||
@ -53,10 +58,12 @@ opm/core/fluid/RockCompressibility.cpp \
|
||||
opm/core/fluid/RockFromDeck.cpp \
|
||||
opm/core/fluid/SaturationPropsBasic.cpp \
|
||||
opm/core/fluid/SaturationPropsFromDeck.cpp \
|
||||
opm/core/fluid/SatFuncGwseg.cpp \
|
||||
opm/core/fluid/SatFuncStone2.cpp \
|
||||
opm/core/fluid/SatFuncSimple.cpp \
|
||||
opm/core/fluid/blackoil/BlackoilPvtProperties.cpp \
|
||||
opm/core/fluid/blackoil/SinglePvtDead.cpp \
|
||||
opm/core/fluid/blackoil/SinglePvtDeadSpline.cpp \
|
||||
opm/core/fluid/blackoil/SinglePvtInterface.cpp \
|
||||
opm/core/fluid/blackoil/SinglePvtLiveGas.cpp \
|
||||
opm/core/fluid/blackoil/SinglePvtLiveOil.cpp \
|
||||
@ -103,6 +110,8 @@ opm/core/simulator/SimulatorReport.cpp \
|
||||
opm/core/simulator/SimulatorTimer.cpp \
|
||||
opm/core/transport/reorder/TransportModelCompressibleTwophase.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/nlsolvers.c \
|
||||
opm/core/transport/reorder/reordersequence.cpp \
|
||||
@ -112,6 +121,8 @@ opm/core/transport/spu_implicit.c \
|
||||
opm/core/transport/transport_source.c \
|
||||
opm/core/utility/MonotCubicInterpolator.cpp \
|
||||
opm/core/utility/StopWatch.cpp \
|
||||
opm/core/utility/VelocityInterpolation.cpp \
|
||||
opm/core/utility/WachspressCoord.cpp \
|
||||
opm/core/utility/miscUtilities.cpp \
|
||||
opm/core/utility/miscUtilitiesBlackoil.cpp \
|
||||
opm/core/utility/parameters/Parameter.cpp \
|
||||
@ -150,15 +161,19 @@ opm/core/fluid/PvtPropertiesIncompFromDeck.hpp \
|
||||
opm/core/fluid/RockBasic.hpp \
|
||||
opm/core/fluid/RockCompressibility.hpp \
|
||||
opm/core/fluid/RockFromDeck.hpp \
|
||||
opm/core/fluid/SaturationPropsBasic.hpp \
|
||||
opm/core/fluid/SaturationPropsFromDeck.hpp \
|
||||
opm/core/fluid/SatFuncGwseg.hpp \
|
||||
opm/core/fluid/SatFuncStone2.hpp \
|
||||
opm/core/fluid/SatFuncSimple.hpp \
|
||||
opm/core/fluid/SaturationPropsBasic.hpp \
|
||||
opm/core/fluid/SaturationPropsFromDeck.hpp \
|
||||
opm/core/fluid/SaturationPropsFromDeck_impl.hpp \
|
||||
opm/core/fluid/SaturationPropsInterface.hpp \
|
||||
opm/core/fluid/SimpleFluid2p.hpp \
|
||||
opm/core/fluid/blackoil/BlackoilPhases.hpp \
|
||||
opm/core/fluid/blackoil/BlackoilPvtProperties.hpp \
|
||||
opm/core/fluid/blackoil/SinglePvtConstCompr.hpp \
|
||||
opm/core/fluid/blackoil/SinglePvtDead.hpp \
|
||||
opm/core/fluid/blackoil/SinglePvtDeadSpline.hpp \
|
||||
opm/core/fluid/blackoil/SinglePvtInterface.hpp \
|
||||
opm/core/fluid/blackoil/SinglePvtLiveGas.hpp \
|
||||
opm/core/fluid/blackoil/SinglePvtLiveOil.hpp \
|
||||
@ -221,6 +236,8 @@ opm/core/transport/SimpleFluid2pWrapper.hpp \
|
||||
opm/core/transport/SinglePointUpwindTwoPhase.hpp \
|
||||
opm/core/transport/reorder/TransportModelCompressibleTwophase.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/nlsolvers.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/utility/Average.hpp \
|
||||
opm/core/utility/ColumnExtract.hpp \
|
||||
opm/core/utility/DataMap.hpp \
|
||||
opm/core/utility/ErrorMacros.hpp \
|
||||
opm/core/utility/Factory.hpp \
|
||||
opm/core/utility/MonotCubicInterpolator.hpp \
|
||||
opm/core/utility/NonuniformTableLinear.hpp \
|
||||
opm/core/utility/RootFinders.hpp \
|
||||
opm/core/utility/SparseTable.hpp \
|
||||
opm/core/utility/SparseVector.hpp \
|
||||
opm/core/utility/StopWatch.hpp \
|
||||
opm/core/utility/UniformTableLinear.hpp \
|
||||
opm/core/utility/Units.hpp \
|
||||
opm/core/utility/VelocityInterpolation.hpp \
|
||||
opm/core/utility/WachspressCoord.hpp \
|
||||
opm/core/utility/buildUniformMonotoneTable.hpp \
|
||||
opm/core/utility/initState.hpp \
|
||||
opm/core/utility/initState_impl.hpp \
|
||||
@ -279,6 +300,15 @@ opm/core/linalg/LinearSolverUmfpack.hpp
|
||||
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
|
||||
lib_libopmcore_la_SOURCES += \
|
||||
opm/core/linalg/LinearSolverIstl.cpp
|
||||
@ -302,3 +332,6 @@ opm/core/linalg/LinearSolverAGMG.hpp
|
||||
lib_libopmcore_la_LDFLAGS += \
|
||||
$(FCLIBS)
|
||||
endif
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = lib/pkgconfig/opm-core.pc
|
||||
|
73
README
73
README
@ -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
|
||||
|
||||
# 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
|
||||
|
||||
# libraries necessary for OPM
|
||||
sudo apt-get install -y libxml0-dev
|
||||
# for server edition of Ubuntu add-apt-repository depends on
|
||||
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
|
||||
-----------------------------------------
|
||||
|
||||
# libraries
|
||||
sudo zypper install blas libblas3 lapack liblapack3 libboost libxml2 umfpack
|
||||
sudo zypper in blas libblas3 lapack liblapack3 libboost libxml2 umfpack
|
||||
|
||||
# tools
|
||||
sudo zypper install gcc automake autoconf git doxygen
|
||||
sudo zypper in gcc automake autoconf git doxygen
|
||||
|
||||
|
||||
RETRIEVING AND BUILDING DUNE PREREQUISITES
|
||||
------------------------------------------
|
||||
|
||||
(only necessary if you want to use opm-core as a dune module)
|
||||
|
||||
# trust DUNE certificate (sic)
|
||||
echo p | svn list https://svn.dune-project.org/svn/dune-common
|
||||
|
||||
# checkout DUNE libraries
|
||||
for module in common istl geometry grid localfunctions; do
|
||||
git svn clone -s \
|
||||
https://svn.dune-project.org/svn/dune-$module/branches/release-2.2/ \
|
||||
dune-$module
|
||||
done
|
||||
|
||||
# building DUNE libraries
|
||||
for module in common istl geometry grid localfunctions; do
|
||||
env CCACHE_DISABLE=1 dune-common/bin/dunecontrol --only=dune-$module \
|
||||
--configure-opts="--enable-fieldvector-size-is-method" \
|
||||
--make-opts="-j -l 0.8" autogen : configure : make
|
||||
done
|
||||
# DUNE libraries
|
||||
sudo zypper ar http://download.opensuse.org/repositories/science/openSUSE_12.2/science.repo
|
||||
sudo zypper in dune-common dune-istl
|
||||
|
||||
|
||||
DOWNLOADING
|
||||
@ -100,20 +93,26 @@ If you want to contribute, fork OPM/opm-core on github.
|
||||
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
|
||||
./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
|
||||
|
||||
(using opm-core as a dune module:)
|
||||
|
||||
# note: this is done from the parent directory of opm-core
|
||||
env CCACHE_DISABLE=1 dune-common/bin/dunecontrol --only=opm-core \
|
||||
--configure-opts="" --make-opts="-j -l 0.8" autogen : configure : make
|
||||
|
||||
2. As a dune module.
|
||||
- Put the opm-core directory in the same directory
|
||||
as the other dune modules to be built (e.g. dune-commmon,
|
||||
dune-grid). Note that for Ubuntu you can install Dune
|
||||
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
|
||||
|
12
configure.ac
12
configure.ac
@ -2,7 +2,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
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])
|
||||
AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
|
||||
|
||||
@ -20,8 +20,11 @@ AC_CONFIG_HEADERS([config.h])
|
||||
AC_PROG_CC
|
||||
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],
|
||||
[LT_INIT[]dnl
|
||||
[
|
||||
LT_INIT[]dnl
|
||||
LT_LANG([C++])dnl
|
||||
LT_LANG([Fortran 77])dnl
|
||||
LT_LANG([Fortran])dnl
|
||||
@ -36,6 +39,8 @@ OPM_CORE_CHECKS
|
||||
|
||||
OPM_DYNLINK_BOOST_TEST
|
||||
|
||||
ERT
|
||||
|
||||
dnl Substitute Autoconf's abs_*dir variables into the Makefiles for the
|
||||
dnl benefit of external code that uses these variables to derive
|
||||
dnl locations (e.g., Dune's DUNE_CHECK_MODULES macro). Automakes prior
|
||||
@ -51,6 +56,9 @@ AC_CONFIG_FILES([
|
||||
tests/Makefile
|
||||
examples/Makefile
|
||||
tutorials/Makefile
|
||||
opm-core.pc
|
||||
lib/pkgconfig/opm-core.pc
|
||||
opmcore-config.cmake
|
||||
])
|
||||
|
||||
AC_OUTPUT
|
||||
|
@ -1,18 +1,36 @@
|
||||
# Build-time flags needed to form example programs
|
||||
ERT_INCLUDE_PATH = $(ERT_ROOT)/include
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-I$(top_srcdir) \
|
||||
-I$(ERT_INCLUDE_PATH) \
|
||||
$(OPM_BOOST_CPPFLAGS)
|
||||
|
||||
# All targets link to the library
|
||||
LDADD = \
|
||||
$(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).
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
compute_tof \
|
||||
refine_wells \
|
||||
scaneclipsedeck \
|
||||
sim_2p_comp_reorder \
|
||||
@ -20,6 +38,12 @@ sim_2p_incomp_reorder \
|
||||
sim_wateroil \
|
||||
wells_example
|
||||
|
||||
if HAVE_ERT
|
||||
noinst_PROGRAMS += import_rewrite
|
||||
endif
|
||||
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Product constituents. Must be specified for every product that's
|
||||
# built from more than a single ".c" file and/or that link to anything
|
||||
@ -27,10 +51,25 @@ wells_example
|
||||
#
|
||||
# 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
|
||||
|
||||
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_LDADD = $(LDADD) $(LINK_BOOST_FILESYSTEM)
|
||||
|
||||
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_LDADD = $(LDADD) $(LINK_BOOST_FILESYSTEM)
|
||||
|
||||
wells_example_SOURCES = wells_example.cpp
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
@ -42,5 +81,6 @@ noinst_PROGRAMS += spu_2p
|
||||
spu_2p_SOURCES = spu_2p.cpp
|
||||
spu_2p_LDADD = \
|
||||
$(LDADD) \
|
||||
$(LINK_BOOST_FILESYSTEM) \
|
||||
$(LAPACK_LIBS) $(BLAS_LIBS) $(LIBS)
|
||||
endif
|
||||
|
255
examples/compute_tof.cpp
Normal file
255
examples/compute_tof.cpp
Normal 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
252
examples/import_rewrite.cpp
Normal 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);
|
||||
}
|
||||
}
|
@ -32,7 +32,6 @@
|
||||
#include <opm/core/simulator/SimulatorReport.hpp>
|
||||
#include <opm/core/simulator/SimulatorTimer.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
|
||||
#include <opm/core/fluid/BlackoilPropertiesBasic.hpp>
|
||||
@ -94,7 +93,7 @@ main(int argc, char** argv)
|
||||
// Grid init
|
||||
grid.reset(new GridManager(*deck));
|
||||
// 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);
|
||||
// max_well_control_iterations = param.getDefault("max_well_control_iterations", 10);
|
||||
// Rock compressibility.
|
||||
|
@ -175,7 +175,7 @@ main(int argc, char** argv)
|
||||
// Grid init
|
||||
grid.reset(new Opm::GridManager(deck));
|
||||
// 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.reset(new Opm::WellsManager(deck, *grid->c_grid(), props->permeability()));
|
||||
check_well_controls = param.getDefault("check_well_controls", false);
|
||||
@ -408,7 +408,7 @@ main(int argc, char** argv)
|
||||
state.saturation(), state.surfacevol());
|
||||
// Opm::computeInjectedProduced(*props, state.saturation(), reorder_src, stepsize, injected, produced);
|
||||
if (use_segregation_split) {
|
||||
reorder_model.solveGravity(columns, &state.pressure()[0], &initial_porevol[0],
|
||||
reorder_model.solveGravity(columns,
|
||||
stepsize, state.saturation(), state.surfacevol());
|
||||
}
|
||||
}
|
||||
|
@ -94,14 +94,19 @@
|
||||
#include <numeric>
|
||||
|
||||
|
||||
#ifdef HAVE_ERT
|
||||
#include <opm/core/utility/writeECLData.hpp>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
static void outputState(const UnstructuredGrid& grid,
|
||||
const Opm::TwophaseState& state,
|
||||
const int step,
|
||||
const Opm::SimulatorTimer& simtimer,
|
||||
const std::string& output_dir)
|
||||
{
|
||||
// Write data in VTK format.
|
||||
int step = simtimer.currentStepNum();
|
||||
std::ostringstream vtkfilename;
|
||||
vtkfilename << output_dir << "/output-" << std::setw(3) << std::setfill('0') << step << ".vtu";
|
||||
std::ofstream vtkfile(vtkfilename.str().c_str());
|
||||
@ -115,6 +120,9 @@ static void outputState(const UnstructuredGrid& grid,
|
||||
Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity);
|
||||
dm["velocity"] = &cell_velocity;
|
||||
Opm::writeVtkData(grid, dm, vtkfile);
|
||||
#ifdef HAVE_ERT
|
||||
Opm::writeECLData(grid , dm , simtimer , output_dir , "OPM" );
|
||||
#endif
|
||||
|
||||
// Write data (not grid) in Matlab format
|
||||
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.
|
||||
simtimer.report(std::cout);
|
||||
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.
|
||||
@ -699,7 +707,7 @@ main(int argc, char** argv)
|
||||
<< "\n Transport time: " << ttime << std::endl;
|
||||
|
||||
if (output) {
|
||||
outputState(*grid->c_grid(), state, simtimer.currentStepNum(), output_dir);
|
||||
outputState(*grid->c_grid(), state, simtimer, output_dir);
|
||||
outputWaterCut(watercut, output_dir);
|
||||
if (wells->c_wells()) {
|
||||
outputWellReport(wellreport, output_dir);
|
||||
|
11
lib/pkgconfig/opm-core.pc.in
Normal file
11
lib/pkgconfig/opm-core.pc.in
Normal 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}
|
@ -98,6 +98,11 @@ if test "x$BLAS_LIBS" != x; then
|
||||
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)
|
||||
if test $ax_blas_ok = no; then
|
||||
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"])
|
||||
fi
|
||||
|
||||
# end of guard against automatic overriding explicit definitions
|
||||
fi
|
||||
|
||||
AC_SUBST(BLAS_LIBS)
|
||||
|
||||
LIBS="$ax_blas_save_LIBS"
|
||||
|
@ -101,6 +101,11 @@ if test "x$LAPACK_LIBS" != x; then
|
||||
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)
|
||||
if test $ax_lapack_ok = no; then
|
||||
save_LIBS="$LIBS"; LIBS="$LIBS $BLAS_LIBS $FLIBS"
|
||||
@ -118,6 +123,9 @@ for lapack in lapack lapack_rs6k; do
|
||||
fi
|
||||
done
|
||||
|
||||
# end of guard against automatic overriding explicit definitions
|
||||
fi
|
||||
|
||||
AC_SUBST(LAPACK_LIBS)
|
||||
|
||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||
|
66
m4/cxx0x_compiler.m4
Normal file
66
m4/cxx0x_compiler.m4
Normal 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
17
m4/cxx0x_nullptr.m4
Normal 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
14
m4/cxx0x_static_assert.m4
Normal 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
67
m4/ert.m4
Normal 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} ****
|
||||
# ])
|
||||
])
|
@ -5,6 +5,11 @@ dnl -*- autoconf -*-
|
||||
AC_DEFUN([OPM_CORE_CHECKS],
|
||||
[
|
||||
|
||||
# Language features
|
||||
GXX0X
|
||||
STATIC_ASSERT_CHECK
|
||||
NULLPTR_CHECK
|
||||
|
||||
# Checks for libraries.
|
||||
|
||||
# Bring in numerics support (standard library component)
|
||||
@ -17,8 +22,8 @@ AX_BOOST_SYSTEM
|
||||
AX_BOOST_DATE_TIME
|
||||
AX_BOOST_FILESYSTEM
|
||||
AX_BOOST_UNIT_TEST_FRAMEWORK
|
||||
|
||||
AX_DUNE_ISTL
|
||||
OPM_PATH_SUPERLU
|
||||
OPM_AGMG
|
||||
|
||||
# Checks for header files.
|
||||
|
@ -1,4 +1,7 @@
|
||||
AC_DEFUN([OPM_LAPACK],
|
||||
[AC_REQUIRE([AC_F77_WRAPPERS])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
|
||||
|
334
m4/opm_superlu.m4
Normal file
334
m4/opm_superlu.m4
Normal 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
18
opm-core.pc.in
Normal 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}
|
@ -37,7 +37,8 @@ namespace Opm
|
||||
{
|
||||
// We accept two different ways to specify the grid.
|
||||
// 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,
|
||||
// DEPTHZ and TOPS are not present.
|
||||
// 2. Tensor grid format.
|
||||
@ -119,29 +120,8 @@ namespace Opm
|
||||
void GridManager::initFromDeckCornerpoint(const Opm::EclipseGridParser& 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.
|
||||
struct grdecl 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];
|
||||
struct grdecl grdecl = deck.get_grdecl();
|
||||
|
||||
// Process grid.
|
||||
ug_ = create_grid_cornerpoint(&grdecl, 0.0);
|
||||
|
@ -47,8 +47,19 @@
|
||||
#include <opm/core/eclipse/EclipseGridParserHelpers.hpp>
|
||||
#include <opm/core/eclipse/SpecialEclipseFields.hpp>
|
||||
#include <opm/core/utility/ErrorMacros.hpp>
|
||||
#include <boost/filesystem.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;
|
||||
|
||||
@ -86,7 +97,7 @@ namespace EclipseKeywords
|
||||
string("MULTPV"), string("PRESSURE"), string("SGAS"),
|
||||
string("SWAT"), string("SOIL"), string("RS"),
|
||||
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]);
|
||||
|
||||
@ -114,7 +125,7 @@ namespace EclipseKeywords
|
||||
const int num_special_fields = sizeof(special_fields) / sizeof(special_fields[0]);
|
||||
|
||||
string ignore_with_data[] =
|
||||
{ string("MAPUNITS"), string("MAPAXES"), string("GRIDUNIT"),
|
||||
{ string("MAPUNITS"), string("GRIDUNIT"),
|
||||
string("NTG"), string("REGDIMS"), string("WELLDIMS"),
|
||||
string("NSTACK"), string("SATNUM"),
|
||||
string("RPTRST"), string("ROIP"), string("RWIP"),
|
||||
@ -141,23 +152,70 @@ namespace EclipseKeywords
|
||||
string include_keywords[] = { string("INCLUDE") };
|
||||
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 {
|
||||
|
||||
enum FieldType {
|
||||
Integer,
|
||||
FloatingPoint,
|
||||
Timestepping,
|
||||
SpecialField,
|
||||
IgnoreWithData,
|
||||
IgnoreNoData,
|
||||
Include,
|
||||
Unknown
|
||||
};
|
||||
inline std::string upcase(const std::string& s)
|
||||
{
|
||||
std::string us(s);
|
||||
// Getting the character type facet for toupper().
|
||||
// We use the classic (i.e. C) locale.
|
||||
const std::ctype<char>& ct = std::use_facet< std::ctype<char> >(std::locale::classic());
|
||||
for (int i = 0; i < int(s.size()); ++i) {
|
||||
us[i] = ct.toupper(s[i]);
|
||||
}
|
||||
return us;
|
||||
}
|
||||
|
||||
inline FieldType classifyKeyword(const string& keyword)
|
||||
|
||||
|
||||
} // anon namespace
|
||||
|
||||
|
||||
|
||||
// ---------- Member functions ----------
|
||||
|
||||
/// Default constructor.
|
||||
//---------------------------------------------------------------------------
|
||||
EclipseGridParser::EclipseGridParser()
|
||||
//---------------------------------------------------------------------------
|
||||
: current_reading_mode_(Regular),
|
||||
start_date_(boost::date_time::not_a_date_time),
|
||||
current_time_days_(0.0),
|
||||
current_epoch_(0),
|
||||
special_field_by_epoch_(1)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// Constructor taking an eclipse filename.
|
||||
//---------------------------------------------------------------------------
|
||||
EclipseGridParser::EclipseGridParser(const string& filename, bool convert_to_SI)
|
||||
//---------------------------------------------------------------------------
|
||||
: current_reading_mode_(Regular),
|
||||
start_date_(boost::date_time::not_a_date_time),
|
||||
current_time_days_(0.0),
|
||||
current_epoch_(0),
|
||||
special_field_by_epoch_(1)
|
||||
{
|
||||
// Store directory of filename
|
||||
boost::filesystem::path p(filename);
|
||||
directory_ = p.parent_path().string();
|
||||
ifstream is(filename.c_str());
|
||||
if (!is) {
|
||||
cerr << "Unable to open file " << filename << endl;
|
||||
throw exception();
|
||||
}
|
||||
read(is, convert_to_SI);
|
||||
}
|
||||
|
||||
|
||||
FieldType EclipseGridParser::classifyKeyword(const string& keyword)
|
||||
{
|
||||
using namespace EclipseKeywords;
|
||||
if (count(integer_fields, integer_fields + num_integer_fields, keyword)) {
|
||||
@ -174,25 +232,16 @@ namespace {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string upcase(const std::string& s)
|
||||
{
|
||||
std::string us(s);
|
||||
// Getting the character type facet for toupper().
|
||||
// We use the classic (i.e. C) locale.
|
||||
const std::ctype<char>& ct = std::use_facet< std::ctype<char> >(std::locale::classic());
|
||||
for (int i = 0; i < int(s.size()); ++i) {
|
||||
us[i] = ct.toupper(s[i]);
|
||||
}
|
||||
return us;
|
||||
}
|
||||
|
||||
inline bool readKeyword(std::istream& is, std::string& keyword)
|
||||
{
|
||||
bool EclipseGridParser::readKeyword(std::istream& is, std::string& keyword)
|
||||
{
|
||||
char buf[9];
|
||||
int i, j;
|
||||
char c;
|
||||
@ -255,46 +304,6 @@ namespace {
|
||||
if(end != keyword.npos)
|
||||
keyword = keyword.substr(0, end+1);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // anon namespace
|
||||
|
||||
|
||||
|
||||
// ---------- Member functions ----------
|
||||
|
||||
/// Default constructor.
|
||||
//---------------------------------------------------------------------------
|
||||
EclipseGridParser::EclipseGridParser()
|
||||
//---------------------------------------------------------------------------
|
||||
: current_reading_mode_(Regular),
|
||||
start_date_(boost::date_time::not_a_date_time),
|
||||
current_time_days_(0.0),
|
||||
current_epoch_(0),
|
||||
special_field_by_epoch_(1)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// Constructor taking an eclipse filename.
|
||||
//---------------------------------------------------------------------------
|
||||
EclipseGridParser::EclipseGridParser(const string& filename, bool convert_to_SI)
|
||||
//---------------------------------------------------------------------------
|
||||
: current_reading_mode_(Regular),
|
||||
start_date_(boost::date_time::not_a_date_time),
|
||||
current_time_days_(0.0),
|
||||
current_epoch_(0),
|
||||
special_field_by_epoch_(1)
|
||||
{
|
||||
// Store directory of filename
|
||||
boost::filesystem::path p(filename);
|
||||
directory_ = p.parent_path().string();
|
||||
ifstream is(filename.c_str());
|
||||
if (!is) {
|
||||
cerr << "Unable to open file " << filename << endl;
|
||||
throw exception();
|
||||
}
|
||||
read(is, convert_to_SI);
|
||||
}
|
||||
|
||||
|
||||
@ -488,6 +497,14 @@ void EclipseGridParser::readImpl(istream& is)
|
||||
// is >> ignoreSlashLine;
|
||||
break;
|
||||
}
|
||||
case Import: {
|
||||
string import_filename = readString(is);
|
||||
if (!directory_.empty()) {
|
||||
import_filename = directory_ + '/' + import_filename;
|
||||
}
|
||||
getNumericErtFields(import_filename);
|
||||
break;
|
||||
}
|
||||
case Unknown:
|
||||
default:
|
||||
ignored_fields_.insert(keyword);
|
||||
@ -542,6 +559,9 @@ void EclipseGridParser::convertToSI()
|
||||
do_convert = false; // Dimensionless keywords...
|
||||
} else if (key == "PRESSURE") {
|
||||
unit = units_.pressure;
|
||||
} else if (key == "MAPAXES") {
|
||||
MESSAGE("Not applying units to MAPAXES yet!");
|
||||
unit = 1.0;
|
||||
} else {
|
||||
THROW("Units for field " << key << " not specified. Cannon convert to SI.");
|
||||
}
|
||||
@ -801,4 +821,310 @@ void EclipseGridParser::computeUnits()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
@ -45,6 +45,13 @@ along with OpenRS. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include <opm/core/eclipse/EclipseUnits.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
|
||||
{
|
||||
|
||||
@ -64,9 +71,23 @@ namespace Opm
|
||||
|
||||
*/
|
||||
|
||||
class EclipseGridParser
|
||||
{
|
||||
public:
|
||||
enum FieldType {
|
||||
Integer,
|
||||
FloatingPoint,
|
||||
Timestepping,
|
||||
SpecialField,
|
||||
IgnoreWithData,
|
||||
IgnoreNoData,
|
||||
Include,
|
||||
Import,
|
||||
Unknown
|
||||
};
|
||||
|
||||
|
||||
|
||||
class EclipseGridParser
|
||||
{
|
||||
public:
|
||||
/// Default constructor.
|
||||
EclipseGridParser();
|
||||
/// Constructor taking an eclipse filename. Unless the second
|
||||
@ -74,6 +95,11 @@ public:
|
||||
/// converted to SI units.
|
||||
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
|
||||
/// the second argument 'convert_to_SI' is false, all fields will
|
||||
/// be converted to SI units.
|
||||
@ -191,11 +217,28 @@ public:
|
||||
/// The units specified by the eclipse file read.
|
||||
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:
|
||||
|
||||
#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 cloneSpecialField(const std::string& fieldname,
|
||||
const std::tr1::shared_ptr<SpecialBase> original);
|
||||
void readImpl(std::istream& is);
|
||||
void getNumericErtFields(const std::string& filename);
|
||||
|
||||
|
||||
std::string directory_;
|
||||
@ -216,6 +259,7 @@ private:
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // SINTEF_ECLIPSEGRIDPARSER_HEADER
|
||||
|
@ -398,6 +398,13 @@ namespace
|
||||
yv.push_back(table[k][i]);
|
||||
}
|
||||
}
|
||||
if (xv.empty()) {
|
||||
// Nothing specified, the entire column is defaulted.
|
||||
// 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]);
|
||||
@ -405,6 +412,7 @@ namespace
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reads keywords SGOF and SWOF
|
||||
inline void readSGWOF(std::istream& is, table_t& relperm_table,
|
||||
|
@ -1047,7 +1047,7 @@ struct GCONINJE : public SpecialBase
|
||||
gconinje_line.injector_type_ = readString(is);
|
||||
gconinje_line.control_mode_ = readString(is);
|
||||
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);
|
||||
gconinje_line.surface_flow_max_rate_ = double_data[0];
|
||||
gconinje_line.resv_flow_max_rate_ = double_data[1];
|
||||
|
@ -151,7 +151,7 @@ namespace Opm
|
||||
double* dpcds) const;
|
||||
|
||||
|
||||
/// Obtain the range of allowable saturation values.
|
||||
/// Obtain the range of allowable saturation values.
|
||||
/// In cell cells[i], saturation of phase p is allowed to be
|
||||
/// in the interval [smin[i*P + p], smax[i*P + p]].
|
||||
/// \param[in] n Number of data points.
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include <opm/core/fluid/BlackoilPropertiesFromDeck.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
@ -29,11 +30,15 @@ namespace Opm
|
||||
if (init_rock){
|
||||
rock_.init(deck, grid);
|
||||
}
|
||||
pvt_.init(deck);
|
||||
satprops_.init(deck, grid);
|
||||
if (pvt_.numPhases() != satprops_.numPhases()) {
|
||||
THROW("BlackoilPropertiesBasic::BlackoilPropertiesBasic() - Inconsistent number of phases in pvt data ("
|
||||
<< pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ").");
|
||||
pvt_.init(deck, 200);
|
||||
SaturationPropsFromDeck<SatFuncSimpleUniform>* ptr
|
||||
= new SaturationPropsFromDeck<SatFuncSimpleUniform>();
|
||||
satprops_.reset(ptr);
|
||||
ptr->init(deck, grid, 200);
|
||||
|
||||
if (pvt_.numPhases() != satprops_->numPhases()) {
|
||||
THROW("BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck() - Inconsistent number of phases in pvt data ("
|
||||
<< pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_->numPhases() << ").");
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,13 +50,56 @@ namespace Opm
|
||||
if(init_rock){
|
||||
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()) {
|
||||
THROW("BlackoilPropertiesBasic::BlackoilPropertiesBasic() - Inconsistent number of phases in pvt data ("
|
||||
<< pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ").");
|
||||
const int pvt_samples = param.getDefault("pvt_tab_size", 200);
|
||||
pvt_.init(deck, pvt_samples);
|
||||
|
||||
// Unfortunate lack of pointer smartness here...
|
||||
const int sat_samples = param.getDefault("sat_tab_size", 200);
|
||||
std::string threephase_model = param.getDefault<std::string>("threephase_model", "simple");
|
||||
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* 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* 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* smax) const
|
||||
{
|
||||
satprops_.satRange(n, cells, smin, smax);
|
||||
satprops_->satRange(n, cells, smin, smax);
|
||||
}
|
||||
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <opm/core/fluid/SaturationPropsFromDeck.hpp>
|
||||
#include <opm/core/eclipse/EclipseGridParser.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
struct UnstructuredGrid;
|
||||
|
||||
@ -52,8 +53,11 @@ namespace Opm
|
||||
/// mapping from cell indices (typically from a processed grid)
|
||||
/// to logical cartesian indices consistent with the deck.
|
||||
/// \param[in] param Parameters. Accepted parameters include:
|
||||
/// dead_tab_size (1025) number of uniform sample points for dead-oil pvt tables.
|
||||
/// tab_size_kr (200) number of uniform sample points for saturation tables.
|
||||
/// pvt_tab_size (200) number of uniform sample points for dead-oil pvt tables.
|
||||
/// sat_tab_size (200) number of uniform sample points for saturation tables.
|
||||
/// threephase_model("simple") three-phase relperm model (accepts "simple" and "stone2").
|
||||
/// For both size parameters, a 0 or negative value indicates that no spline fitting is to
|
||||
/// be done, and the input fluid data used directly for linear interpolation.
|
||||
BlackoilPropertiesFromDeck(const EclipseGridParser& deck,
|
||||
const UnstructuredGrid& grid,
|
||||
const parameter::ParameterGroup& param,
|
||||
@ -179,7 +183,7 @@ namespace Opm
|
||||
private:
|
||||
RockFromDeck rock_;
|
||||
BlackoilPvtProperties pvt_;
|
||||
SaturationPropsFromDeck satprops_;
|
||||
boost::scoped_ptr<SaturationPropsInterface> satprops_;
|
||||
mutable std::vector<double> B_;
|
||||
mutable std::vector<double> dB_;
|
||||
mutable std::vector<double> R_;
|
||||
|
@ -31,7 +31,7 @@ namespace Opm
|
||||
{
|
||||
rock_.init(deck, grid);
|
||||
pvt_.init(deck);
|
||||
satprops_.init(deck, grid);
|
||||
satprops_.init(deck, grid, 200);
|
||||
if (pvt_.numPhases() != satprops_.numPhases()) {
|
||||
THROW("IncompPropertiesFromDeck::IncompPropertiesFromDeck() - Inconsistent number of phases in pvt data ("
|
||||
<< pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ").");
|
||||
|
@ -135,7 +135,7 @@ namespace Opm
|
||||
private:
|
||||
RockFromDeck rock_;
|
||||
PvtPropertiesIncompFromDeck pvt_;
|
||||
SaturationPropsFromDeck satprops_;
|
||||
SaturationPropsFromDeck<SatFuncStone2Uniform> satprops_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -62,7 +62,7 @@ namespace Opm
|
||||
/// \return Array of P viscosity values.
|
||||
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.
|
||||
virtual const double* density() const = 0;
|
||||
|
||||
|
440
opm/core/fluid/SatFuncGwseg.cpp
Normal file
440
opm/core/fluid/SatFuncGwseg.cpp
Normal 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
|
80
opm/core/fluid/SatFuncGwseg.hpp
Normal file
80
opm/core/fluid/SatFuncGwseg.hpp
Normal 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
|
@ -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/blackoil/BlackoilPhases.hpp>
|
||||
#include <opm/core/fluid/SaturationPropsFromDeck.hpp>
|
||||
@ -6,21 +25,14 @@
|
||||
#include <opm/core/utility/buildUniformMonotoneTable.hpp>
|
||||
#include <opm/core/utility/ErrorMacros.hpp>
|
||||
#include <iostream>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
void SatFuncSimple::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,
|
||||
void SatFuncSimpleUniform::init(const EclipseGridParser& deck,
|
||||
const int table_num,
|
||||
const PhaseUsage phase_usg,
|
||||
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) {
|
||||
// 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;
|
||||
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;
|
||||
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
|
||||
// is very hard experimentally, usually one extends two-phase
|
||||
|
@ -18,17 +18,21 @@
|
||||
*/
|
||||
#ifndef SATFUNCSIMPLE_HPP
|
||||
#define SATFUNCSIMPLE_HPP
|
||||
|
||||
#include <opm/core/eclipse/EclipseGridParser.hpp>
|
||||
#include <opm/core/utility/UniformTableLinear.hpp>
|
||||
#include <opm/core/utility/NonuniformTableLinear.hpp>
|
||||
#include <opm/core/fluid/blackoil/BlackoilPhases.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
class SatFuncSimple: public BlackoilPhases
|
||||
class SatFuncSimpleUniform : public BlackoilPhases
|
||||
{
|
||||
public:
|
||||
void init(const EclipseGridParser& deck, const int table_num, PhaseUsage phase_usg);
|
||||
void init(const EclipseGridParser& deck, const int table_num, PhaseUsage phase_usg,
|
||||
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;
|
||||
@ -46,5 +50,31 @@ namespace Opm
|
||||
UniformTableLinear<double> pcog_;
|
||||
double krocw_; // = krow_(s_wc)
|
||||
};
|
||||
|
||||
|
||||
class SatFuncSimpleNonuniform : public BlackoilPhases
|
||||
{
|
||||
public:
|
||||
void init(const EclipseGridParser& deck,
|
||||
const int table_num,
|
||||
const PhaseUsage phase_usg,
|
||||
const int samples);
|
||||
void evalKr(const double* s, double* kr) const;
|
||||
void evalKrDeriv(const double* s, double* kr, double* dkrds) const;
|
||||
void evalPc(const double* s, double* pc) const;
|
||||
void evalPcDeriv(const double* s, double* pc, double* dpcds) const;
|
||||
double smin_[PhaseUsage::MaxNumPhases];
|
||||
double smax_[PhaseUsage::MaxNumPhases];
|
||||
private:
|
||||
PhaseUsage phase_usage; // A copy of the outer class' phase_usage_.
|
||||
NonuniformTableLinear<double> krw_;
|
||||
NonuniformTableLinear<double> krow_;
|
||||
NonuniformTableLinear<double> pcow_;
|
||||
NonuniformTableLinear<double> krg_;
|
||||
NonuniformTableLinear<double> krog_;
|
||||
NonuniformTableLinear<double> pcog_;
|
||||
double krocw_; // = krow_(s_wc)
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
#endif // SATFUNCSIMPLE_HPP
|
||||
|
@ -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/blackoil/BlackoilPhases.hpp>
|
||||
#include <opm/core/fluid/SaturationPropsFromDeck.hpp>
|
||||
@ -6,20 +25,14 @@
|
||||
#include <opm/core/utility/buildUniformMonotoneTable.hpp>
|
||||
#include <opm/core/utility/ErrorMacros.hpp>
|
||||
#include <iostream>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
void SatFuncStone2::init(const EclipseGridParser& deck,
|
||||
const int table_num,
|
||||
const PhaseUsage phase_usg)
|
||||
{
|
||||
init(deck, table_num, phase_usg, 200);
|
||||
}
|
||||
|
||||
void SatFuncStone2::init(const EclipseGridParser& deck,
|
||||
void SatFuncStone2Uniform::init(const EclipseGridParser& deck,
|
||||
const int table_num,
|
||||
const PhaseUsage phase_usg,
|
||||
const int samples)
|
||||
@ -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) {
|
||||
// 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;
|
||||
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;
|
||||
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
|
||||
// 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
|
||||
|
@ -18,17 +18,21 @@
|
||||
*/
|
||||
#ifndef SATFUNCSTONE2_HPP
|
||||
#define SATFUNCSTONE2_HPP
|
||||
|
||||
#include <opm/core/eclipse/EclipseGridParser.hpp>
|
||||
#include <opm/core/utility/UniformTableLinear.hpp>
|
||||
#include <opm/core/utility/NonuniformTableLinear.hpp>
|
||||
#include <opm/core/fluid/blackoil/BlackoilPhases.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
class SatFuncStone2: public BlackoilPhases
|
||||
class SatFuncStone2Uniform : public BlackoilPhases
|
||||
{
|
||||
public:
|
||||
void init(const EclipseGridParser& deck, const int table_num, PhaseUsage phase_usg);
|
||||
void init(const EclipseGridParser& deck, const int table_num, PhaseUsage phase_usg,
|
||||
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;
|
||||
@ -46,5 +50,31 @@ namespace Opm
|
||||
UniformTableLinear<double> pcog_;
|
||||
double krocw_; // = krow_(s_wc)
|
||||
};
|
||||
|
||||
|
||||
class SatFuncStone2Nonuniform : public BlackoilPhases
|
||||
{
|
||||
public:
|
||||
void init(const EclipseGridParser& deck,
|
||||
const int table_num,
|
||||
const PhaseUsage phase_usg,
|
||||
const int samples);
|
||||
void evalKr(const double* s, double* kr) const;
|
||||
void evalKrDeriv(const double* s, double* kr, double* dkrds) const;
|
||||
void evalPc(const double* s, double* pc) const;
|
||||
void evalPcDeriv(const double* s, double* pc, double* dpcds) const;
|
||||
double smin_[PhaseUsage::MaxNumPhases];
|
||||
double smax_[PhaseUsage::MaxNumPhases];
|
||||
private:
|
||||
PhaseUsage phase_usage; // A copy of the outer class' phase_usage_.
|
||||
NonuniformTableLinear<double> krw_;
|
||||
NonuniformTableLinear<double> krow_;
|
||||
NonuniformTableLinear<double> pcow_;
|
||||
NonuniformTableLinear<double> krg_;
|
||||
NonuniformTableLinear<double> krog_;
|
||||
NonuniformTableLinear<double> pcog_;
|
||||
double krocw_; // = krow_(s_wc)
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
#endif // SATFUNCSTONE2_HPP
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
#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>
|
||||
@ -27,236 +26,9 @@
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
/// Default constructor.
|
||||
SaturationPropsFromDeck::SaturationPropsFromDeck()
|
||||
{
|
||||
}
|
||||
// This file should be removed in the future.
|
||||
// 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
|
||||
|
||||
|
||||
|
@ -19,12 +19,14 @@
|
||||
|
||||
#ifndef OPM_SATURATIONPROPSFROMDECK_HEADER_INCLUDED
|
||||
#define OPM_SATURATIONPROPSFROMDECK_HEADER_INCLUDED
|
||||
|
||||
#include <opm/core/fluid/SaturationPropsInterface.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
#include <opm/core/eclipse/EclipseGridParser.hpp>
|
||||
#include <opm/core/utility/UniformTableLinear.hpp>
|
||||
#include <opm/core/fluid/blackoil/BlackoilPhases.hpp>
|
||||
#include <opm/core/fluid/SatFuncStone2.hpp>
|
||||
#include <opm/core/fluid/SatFuncSimple.hpp>
|
||||
#include <opm/core/fluid/SatFuncGwseg.hpp>
|
||||
#include <vector>
|
||||
|
||||
struct UnstructuredGrid;
|
||||
@ -32,23 +34,31 @@ struct UnstructuredGrid;
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
class SaturationPropsFromDeck : public BlackoilPhases
|
||||
|
||||
|
||||
/// Interface to saturation functions from deck.
|
||||
/// Possible values for template argument (for now):
|
||||
/// SatFuncSetStone2Nonuniform,
|
||||
/// SatFuncSetStone2Uniform.
|
||||
/// SatFuncSetSimpleNonuniform,
|
||||
/// SatFuncSetSimpleUniform.
|
||||
template <class SatFuncSet>
|
||||
class SaturationPropsFromDeck : public SaturationPropsInterface
|
||||
{
|
||||
public:
|
||||
/// Default constructor.
|
||||
SaturationPropsFromDeck();
|
||||
|
||||
/// Initialize from deck and grid.
|
||||
/// \param deck Deck input parser
|
||||
/// \param grid Grid to which property object applies, needed for the
|
||||
/// \param[in] deck Deck input parser
|
||||
/// \param[in] grid Grid to which property object applies, needed for the
|
||||
/// mapping from cell indices (typically from a processed grid)
|
||||
/// to logical cartesian indices consistent with the deck.
|
||||
void init(const EclipseGridParser& deck,
|
||||
const UnstructuredGrid& grid);
|
||||
|
||||
/// \param[in] samples Number of uniform sample points for saturation tables.
|
||||
/// NOTE: samples will only be used with the SatFuncSetUniform template argument.
|
||||
void init(const EclipseGridParser& deck,
|
||||
const UnstructuredGrid& grid,
|
||||
const parameter::ParameterGroup& param);
|
||||
const int samples);
|
||||
|
||||
/// \return P, the number of phases.
|
||||
int numPhases() const;
|
||||
@ -94,11 +104,12 @@ namespace Opm
|
||||
|
||||
private:
|
||||
PhaseUsage phase_usage_;
|
||||
typedef SatFuncSimple satfunc_t;
|
||||
std::vector<satfunc_t> satfuncset_;
|
||||
std::vector<SatFuncSet> satfuncset_;
|
||||
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
|
||||
|
||||
|
||||
#include <opm/core/fluid/SaturationPropsFromDeck_impl.hpp>
|
||||
|
||||
|
||||
#endif // OPM_SATURATIONPROPSFROMDECK_HEADER_INCLUDED
|
||||
|
221
opm/core/fluid/SaturationPropsFromDeck_impl.hpp
Normal file
221
opm/core/fluid/SaturationPropsFromDeck_impl.hpp
Normal 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
|
85
opm/core/fluid/SaturationPropsInterface.hpp
Normal file
85
opm/core/fluid/SaturationPropsInterface.hpp
Normal 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
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <opm/core/fluid/blackoil/BlackoilPvtProperties.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/SinglePvtLiveGas.hpp>
|
||||
#include <opm/core/fluid/blackoil/SinglePvtConstCompr.hpp>
|
||||
@ -78,7 +79,11 @@ namespace Opm
|
||||
// Oil PVT
|
||||
if (phase_usage_.phase_used[Liquid]) {
|
||||
if (deck.hasField("PVDO")) {
|
||||
props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDead(deck.getPVDO().pvdo_, 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")) {
|
||||
props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtLiveOil(deck.getPVTO().pvto_));
|
||||
} else if (deck.hasField("PVCDO")) {
|
||||
@ -90,7 +95,11 @@ namespace Opm
|
||||
// Gas PVT
|
||||
if (phase_usage_.phase_used[Vapour]) {
|
||||
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")) {
|
||||
props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtLiveGas(deck.getPVTG().pvtg_));
|
||||
} else {
|
||||
|
@ -47,7 +47,13 @@ namespace Opm
|
||||
BlackoilPvtProperties();
|
||||
|
||||
/// 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.
|
||||
int numPhases() const;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2010, 2011, 2012 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
@ -17,8 +17,8 @@
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <opm/core/fluid/blackoil/SinglePvtDead.hpp>
|
||||
#include <opm/core/utility/buildUniformMonotoneTable.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
// Extra includes for debug dumping of tables.
|
||||
@ -33,7 +33,7 @@ namespace Opm
|
||||
// Member functions
|
||||
//-------------------------------------------------------------------------
|
||||
/// Constructor
|
||||
SinglePvtDead::SinglePvtDead(const table_t& pvd_table, const int samples)
|
||||
SinglePvtDead::SinglePvtDead(const table_t& pvd_table)
|
||||
{
|
||||
const int region_number = 0;
|
||||
if (pvd_table.size() != 1) {
|
||||
@ -50,8 +50,8 @@ namespace Opm
|
||||
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_);
|
||||
one_over_B_ = NonuniformTableLinear<double>(press, B_inv);
|
||||
viscosity_ = NonuniformTableLinear<double>(press, visc);
|
||||
|
||||
// Dumping the created tables.
|
||||
// static int count = 0;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2010, 2011, 2012 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
|
||||
|
||||
#include <opm/core/fluid/blackoil/SinglePvtInterface.hpp>
|
||||
#include <opm/core/utility/UniformTableLinear.hpp>
|
||||
#include <opm/core/utility/NonuniformTableLinear.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm
|
||||
@ -37,7 +37,7 @@ namespace Opm
|
||||
{
|
||||
public:
|
||||
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();
|
||||
|
||||
/// Viscosity as a function of p and z.
|
||||
@ -73,11 +73,11 @@ namespace Opm
|
||||
double* output_dRdp) const;
|
||||
private:
|
||||
// PVT properties of dry gas or dead oil
|
||||
UniformTableLinear<double> one_over_B_;
|
||||
UniformTableLinear<double> viscosity_;
|
||||
NonuniformTableLinear<double> one_over_B_;
|
||||
NonuniformTableLinear<double> viscosity_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OPM_SINGLEPVTDEAD_HEADER_INCLUDED
|
||||
|
||||
#endif // OPM_SINGLEPVTDEAD_HEADER_INCLUDED
|
||||
|
127
opm/core/fluid/blackoil/SinglePvtDeadSpline.cpp
Normal file
127
opm/core/fluid/blackoil/SinglePvtDeadSpline.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
84
opm/core/fluid/blackoil/SinglePvtDeadSpline.hpp
Normal file
84
opm/core/fluid/blackoil/SinglePvtDeadSpline.hpp
Normal 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
|
||||
|
@ -56,8 +56,9 @@ extern "C" {
|
||||
struct grdecl {
|
||||
int dims[3]; /**< Cartesian box dimensions. */
|
||||
const double *coord; /**< Pillar end-points. */
|
||||
const double *zcorn; /**< Explicit "active" map. May be NULL.*/
|
||||
const int *actnum; /**< Corner-point depths. */
|
||||
const double *zcorn; /**< Corner-point depths. */
|
||||
const int *actnum; /**< Explicit "active" map. May be NULL.*/
|
||||
const double *mapaxes; /**< 6 Element rotation vector - can be NULL. */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -26,6 +26,10 @@
|
||||
|
||||
#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.
|
||||
#include <dune/common/deprecated.hh>
|
||||
#include <dune/istl/bvector.hh>
|
||||
|
@ -593,3 +593,78 @@ clear_well_controls(int well_index, struct Wells *W)
|
||||
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;
|
||||
}
|
||||
|
@ -235,6 +235,19 @@ void
|
||||
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
|
||||
}
|
||||
#endif
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <numeric>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
@ -122,7 +123,7 @@ namespace Opm
|
||||
WellState& well_state)
|
||||
{
|
||||
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.
|
||||
computePerSolveDynamicData(dt, state, well_state);
|
||||
@ -446,30 +447,54 @@ namespace Opm
|
||||
/// Compute per-iteration dynamic properties for wells.
|
||||
void CompressibleTpfa::computeWellDynamicData(const double /*dt*/,
|
||||
const BlackoilState& /*state*/,
|
||||
const WellState& /*well_state*/)
|
||||
const WellState& well_state)
|
||||
{
|
||||
// These are the variables that get computed by this function:
|
||||
//
|
||||
// std::vector<double> wellperf_A_;
|
||||
// std::vector<double> wellperf_phasemob_;
|
||||
const int np = props_.numPhases();
|
||||
const int nw = wells_->number_of_wells;
|
||||
const int nperf = wells_->well_connpos[nw];
|
||||
const int nw = (wells_ != 0) ? wells_->number_of_wells : 0;
|
||||
const int nperf = (wells_ != 0) ? wells_->well_connpos[nw] : 0;
|
||||
wellperf_A_.resize(nperf*np*np);
|
||||
wellperf_phasemob_.resize(nperf*np);
|
||||
// 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'
|
||||
// mobilities, for both injectors and producers.
|
||||
// mobilities for producers.
|
||||
std::vector<double> mu(np);
|
||||
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) {
|
||||
const int c = wells_->well_cells[j];
|
||||
const double* cA = &cell_A_[np*np*c];
|
||||
double* wpA = &wellperf_A_[np*np*j];
|
||||
double* wpM = &wellperf_phasemob_[np*j];
|
||||
if (producer) {
|
||||
const double* cA = &cell_A_[np*np*c];
|
||||
std::copy(cA, cA + np*np, wpA);
|
||||
const double* cM = &cell_phasemob_[np*c];
|
||||
double* wpM = &wellperf_phasemob_[np*j];
|
||||
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];
|
||||
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
|
||||
CompletionData completion_data;
|
||||
completion_data.gpot = &wellperf_gpot_[0];
|
||||
completion_data.A = &wellperf_A_[0];
|
||||
completion_data.phasemob = &wellperf_phasemob_[0];
|
||||
completion_data.gpot = ! wellperf_gpot_.empty() ? &wellperf_gpot_[0] : 0;
|
||||
completion_data.A = ! wellperf_A_.empty() ? &wellperf_A_[0] : 0;
|
||||
completion_data.phasemob = ! wellperf_phasemob_.empty() ? &wellperf_phasemob_[0] : 0;
|
||||
cfs_tpfa_res_wells wells_tmp;
|
||||
wells_tmp.W = const_cast<Wells*>(wells_);
|
||||
wells_tmp.data = &completion_data;
|
||||
@ -574,9 +599,9 @@ namespace Opm
|
||||
{
|
||||
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
|
||||
CompletionData completion_data;
|
||||
completion_data.gpot = const_cast<double*>(&wellperf_gpot_[0]);
|
||||
completion_data.A = const_cast<double*>(&wellperf_A_[0]);
|
||||
completion_data.phasemob = const_cast<double*>(&wellperf_phasemob_[0]);
|
||||
completion_data.gpot = ! wellperf_gpot_.empty() ? const_cast<double*>(&wellperf_gpot_[0]) : 0;
|
||||
completion_data.A = ! wellperf_A_.empty() ? const_cast<double*>(&wellperf_A_[0]) : 0;
|
||||
completion_data.phasemob = ! wellperf_phasemob_.empty() ? const_cast<double*>(&wellperf_phasemob_[0]) : 0;
|
||||
cfs_tpfa_res_wells wells_tmp;
|
||||
wells_tmp.W = const_cast<Wells*>(wells_);
|
||||
wells_tmp.data = &completion_data;
|
||||
@ -584,6 +609,9 @@ namespace Opm
|
||||
forces.wells = &wells_tmp;
|
||||
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,
|
||||
&forces,
|
||||
props_.numPhases(),
|
||||
@ -592,9 +620,9 @@ namespace Opm
|
||||
&face_phasemob_[0],
|
||||
&face_gravcap_[0],
|
||||
&state.pressure()[0],
|
||||
&well_state.bhp()[0],
|
||||
wpress,
|
||||
&state.faceflux()[0],
|
||||
&well_state.perfRates()[0]);
|
||||
wflux);
|
||||
cfs_tpfa_res_fpress(gg,
|
||||
props_.numPhases(),
|
||||
&htrans_[0],
|
||||
@ -604,6 +632,23 @@ namespace Opm
|
||||
&state.pressure()[0],
|
||||
&state.faceflux()[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
|
||||
|
@ -142,7 +142,7 @@ impl_allocate(struct UnstructuredGrid *G ,
|
||||
nnu = G->number_of_cells;
|
||||
nwperf = 0;
|
||||
|
||||
if (wells != NULL) { assert (wells->W != NULL);
|
||||
if ((wells != NULL) && (wells->W != NULL)) {
|
||||
nnu += 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 wells_present;
|
||||
size_t nnz;
|
||||
|
||||
struct CSRMatrix *A;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -214,7 +216,7 @@ construct_matrix(struct UnstructuredGrid *G ,
|
||||
}
|
||||
}
|
||||
|
||||
if (wells != NULL) {
|
||||
if (wells_present) {
|
||||
/* Well <-> cell connections */
|
||||
struct Wells *W = wells->W;
|
||||
|
||||
@ -252,7 +254,7 @@ construct_matrix(struct UnstructuredGrid *G ,
|
||||
}
|
||||
}
|
||||
|
||||
if (wells != NULL) {
|
||||
if (wells_present) {
|
||||
/* Fill well <-> cell connections */
|
||||
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
|
||||
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
|
||||
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,
|
||||
struct cfs_tpfa_res_wells *wells,
|
||||
struct cfs_tpfa_res_data *h )
|
||||
@ -815,9 +867,10 @@ assemble_completion_to_well(int w, int c, int nc, int np,
|
||||
ctrl = W->ctrls[ w ];
|
||||
|
||||
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 ],
|
||||
@ -830,9 +883,10 @@ assemble_completion_to_well(int w, int c, int nc, int np,
|
||||
break;
|
||||
|
||||
case SURFACE_RATE:
|
||||
assert (0); /* Surface rate currently not supported */
|
||||
welleq_coeff_surfrate(i, np, h, ctrl, &res, &w2c, &w2w);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Assemble completion contributions */
|
||||
wdof = nc + w;
|
||||
@ -854,7 +908,7 @@ assemble_well_contrib(struct cfs_tpfa_res_wells *wells ,
|
||||
struct cfs_tpfa_res_data *h )
|
||||
{
|
||||
int w, i, c, np, np2, nc;
|
||||
int is_neumann;
|
||||
int is_neumann, is_open;
|
||||
double pw, dp;
|
||||
double *WI, *gpot, *pmobp;
|
||||
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++) {
|
||||
pw = wpress[ w ];
|
||||
is_open = W->ctrls[w]->current >= 0;
|
||||
|
||||
for (; i < W->well_connpos[w + 1];
|
||||
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);
|
||||
|
||||
if (is_open) {
|
||||
assemble_completion_to_cell(c, nc + w, np, dt, h);
|
||||
}
|
||||
|
||||
/* Prepare for RESV controls */
|
||||
compute_darcyflux_and_deriv(np, WI[i], dp, pmobp, gpot,
|
||||
h->pimpl->flux_work,
|
||||
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 ];
|
||||
@ -1127,8 +1184,7 @@ cfs_tpfa_res_construct(struct UnstructuredGrid *G ,
|
||||
nf = G->number_of_faces;
|
||||
nwperf = 0;
|
||||
|
||||
if (wells != NULL) {
|
||||
assert (wells->W != NULL);
|
||||
if ((wells != NULL) && (wells->W != NULL)) {
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
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);
|
||||
|
||||
if ((forces != NULL) && (forces->wells != NULL) &&
|
||||
(wpress != NULL) && (wflux != NULL)) {
|
||||
if ((forces != NULL) &&
|
||||
(forces->wells != NULL) &&
|
||||
(forces->wells->W != NULL) && (wpress != NULL) && (wflux != NULL)) {
|
||||
|
||||
compute_wflux(np, forces->wells, pmobc, cpress, wpress, wflux);
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ namespace Opm
|
||||
Impl(const parameter::ParameterGroup& param,
|
||||
const UnstructuredGrid& grid,
|
||||
const BlackoilPropertiesInterface& props,
|
||||
const RockCompressibility* rock_comp,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
WellsManager& wells_manager,
|
||||
const std::vector<double>& src,
|
||||
const FlowBoundaryConditions* bcs,
|
||||
@ -93,7 +93,7 @@ namespace Opm
|
||||
// Observed objects.
|
||||
const UnstructuredGrid& grid_;
|
||||
const BlackoilPropertiesInterface& props_;
|
||||
const RockCompressibility* rock_comp_;
|
||||
const RockCompressibility* rock_comp_props_;
|
||||
WellsManager& wells_manager_;
|
||||
const Wells* wells_;
|
||||
const std::vector<double>& src_;
|
||||
@ -114,14 +114,14 @@ namespace Opm
|
||||
SimulatorCompressibleTwophase::SimulatorCompressibleTwophase(const parameter::ParameterGroup& param,
|
||||
const UnstructuredGrid& grid,
|
||||
const BlackoilPropertiesInterface& props,
|
||||
const RockCompressibility* rock_comp,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
WellsManager& wells_manager,
|
||||
const std::vector<double>& src,
|
||||
const FlowBoundaryConditions* bcs,
|
||||
LinearSolverInterface& linsolver,
|
||||
const double* gravity)
|
||||
{
|
||||
pimpl_.reset(new Impl(param, grid, props, rock_comp, wells_manager, src, bcs, linsolver, gravity));
|
||||
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;
|
||||
dm["saturation"] = &state.saturation();
|
||||
dm["pressure"] = &state.pressure();
|
||||
dm["surfvolume"] = &state.surfacevol();
|
||||
std::vector<double> cell_velocity;
|
||||
Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity);
|
||||
dm["velocity"] = &cell_velocity;
|
||||
@ -195,6 +196,7 @@ namespace Opm
|
||||
if (!file) {
|
||||
THROW("Failed to open " << fname.str());
|
||||
}
|
||||
file.precision(15);
|
||||
const std::vector<double>& d = *(it->second);
|
||||
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,
|
||||
const UnstructuredGrid& grid,
|
||||
const BlackoilPropertiesInterface& props,
|
||||
const RockCompressibility* rock_comp,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
WellsManager& wells_manager,
|
||||
const std::vector<double>& src,
|
||||
const FlowBoundaryConditions* bcs,
|
||||
@ -240,12 +242,13 @@ namespace Opm
|
||||
const double* gravity)
|
||||
: grid_(grid),
|
||||
props_(props),
|
||||
rock_comp_(rock_comp),
|
||||
rock_comp_props_(rock_comp_props),
|
||||
wells_manager_(wells_manager),
|
||||
wells_(wells_manager.c_wells()),
|
||||
src_(src),
|
||||
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_change_tolerance", 1.0),
|
||||
param.getDefault("nl_pressure_maxiter", 10),
|
||||
@ -301,8 +304,8 @@ namespace Opm
|
||||
|
||||
// Initialisation.
|
||||
std::vector<double> porevol;
|
||||
if (rock_comp_ && rock_comp_->isActive()) {
|
||||
computePorevolume(grid_, props_.porosity(), *rock_comp_, state.pressure(), porevol);
|
||||
if (rock_comp_props_ && rock_comp_props_->isActive()) {
|
||||
computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol);
|
||||
} else {
|
||||
computePorevolume(grid_, props_.porosity(), porevol);
|
||||
}
|
||||
@ -317,15 +320,11 @@ namespace Opm
|
||||
Opm::time::StopWatch step_timer;
|
||||
Opm::time::StopWatch total_timer;
|
||||
total_timer.start();
|
||||
double init_satvol[2] = { 0.0 };
|
||||
double satvol[2] = { 0.0 };
|
||||
double injected[2] = { 0.0 };
|
||||
double produced[2] = { 0.0 };
|
||||
double init_surfvol[2] = { 0.0 };
|
||||
double inplace_surfvol[2] = { 0.0 };
|
||||
double tot_injected[2] = { 0.0 };
|
||||
double tot_produced[2] = { 0.0 };
|
||||
Opm::computeSaturatedVol(porevol, state.saturation(), init_satvol);
|
||||
std::cout << "\nInitial saturations are " << init_satvol[0]/tot_porevol_init
|
||||
<< " " << init_satvol[1]/tot_porevol_init << std::endl;
|
||||
Opm::computeSaturatedVol(porevol, state.surfacevol(), init_surfvol);
|
||||
Opm::Watercut watercut;
|
||||
watercut.push(0.0, 0.0, 0.0);
|
||||
Opm::WellReport wellreport;
|
||||
@ -408,8 +407,8 @@ namespace Opm
|
||||
// Optionally, check if well controls are satisfied.
|
||||
if (check_well_controls_) {
|
||||
Opm::computePhaseFlowRatesPerWell(*wells_,
|
||||
fractional_flows,
|
||||
well_state.perfRates(),
|
||||
fractional_flows,
|
||||
well_resflows_phase);
|
||||
std::cout << "Checking well conditions." << std::endl;
|
||||
// For testing we set surface := reservoir
|
||||
@ -427,14 +426,13 @@ namespace Opm
|
||||
} while (!well_control_passed);
|
||||
|
||||
// Update pore volumes if rock is compressible.
|
||||
if (rock_comp_ && rock_comp_->isActive()) {
|
||||
if (rock_comp_props_ && rock_comp_props_->isActive()) {
|
||||
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).
|
||||
Opm::computeTransportSource(grid_, src_, state.faceflux(), 1.0,
|
||||
wells_, well_state.perfRates(), transport_src);
|
||||
// Process transport sources from well flows.
|
||||
Opm::computeTransportSource(props_, wells_, well_state, transport_src);
|
||||
|
||||
// Solve transport.
|
||||
transport_timer.start();
|
||||
@ -443,16 +441,22 @@ namespace Opm
|
||||
stepsize /= double(num_transport_substeps_);
|
||||
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) {
|
||||
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());
|
||||
Opm::computeInjectedProduced(props_,
|
||||
state.pressure(), state.surfacevol(), state.saturation(),
|
||||
transport_src, stepsize, injected, produced);
|
||||
double substep_injected[2] = { 0.0 };
|
||||
double substep_produced[2] = { 0.0 };
|
||||
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_) {
|
||||
tsolver_.solveGravity(columns_, &state.pressure()[0], &initial_porevol[0],
|
||||
stepsize, state.saturation(), state.surfacevol());
|
||||
tsolver_.solveGravity(columns_, stepsize, state.saturation(), state.surfacevol());
|
||||
}
|
||||
}
|
||||
transport_timer.stop();
|
||||
@ -461,35 +465,35 @@ namespace Opm
|
||||
std::cout << "Transport solver took: " << tt << " seconds." << std::endl;
|
||||
ttime += tt;
|
||||
// Report volume balances.
|
||||
Opm::computeSaturatedVol(porevol, state.saturation(), satvol);
|
||||
Opm::computeSaturatedVol(porevol, state.surfacevol(), inplace_surfvol);
|
||||
tot_injected[0] += injected[0];
|
||||
tot_injected[1] += injected[1];
|
||||
tot_produced[0] += produced[0];
|
||||
tot_produced[1] += produced[1];
|
||||
std::cout.precision(5);
|
||||
const int width = 18;
|
||||
std::cout << "\nVolume balance report (all numbers relative to total pore volume).\n";
|
||||
std::cout << " Saturated volumes: "
|
||||
<< std::setw(width) << satvol[0]/tot_porevol_init
|
||||
<< std::setw(width) << satvol[1]/tot_porevol_init << std::endl;
|
||||
std::cout << " Injected volumes: "
|
||||
<< std::setw(width) << injected[0]/tot_porevol_init
|
||||
<< std::setw(width) << injected[1]/tot_porevol_init << std::endl;
|
||||
std::cout << " Produced volumes: "
|
||||
<< std::setw(width) << produced[0]/tot_porevol_init
|
||||
<< std::setw(width) << produced[1]/tot_porevol_init << std::endl;
|
||||
std::cout << " Total inj volumes: "
|
||||
<< std::setw(width) << tot_injected[0]/tot_porevol_init
|
||||
<< std::setw(width) << tot_injected[1]/tot_porevol_init << std::endl;
|
||||
std::cout << " Total prod volumes: "
|
||||
<< std::setw(width) << tot_produced[0]/tot_porevol_init
|
||||
<< std::setw(width) << tot_produced[1]/tot_porevol_init << std::endl;
|
||||
std::cout << " In-place + prod - inj: "
|
||||
<< std::setw(width) << (satvol[0] + tot_produced[0] - tot_injected[0])/tot_porevol_init
|
||||
<< std::setw(width) << (satvol[1] + tot_produced[1] - tot_injected[1])/tot_porevol_init << std::endl;
|
||||
std::cout << " Init - now - pr + inj: "
|
||||
<< std::setw(width) << (init_satvol[0] - satvol[0] - tot_produced[0] + tot_injected[0])/tot_porevol_init
|
||||
<< std::setw(width) << (init_satvol[1] - satvol[1] - tot_produced[1] + tot_injected[1])/tot_porevol_init
|
||||
std::cout << "\nMass balance report.\n";
|
||||
std::cout << " Injected surface volumes: "
|
||||
<< std::setw(width) << injected[0]
|
||||
<< std::setw(width) << injected[1] << std::endl;
|
||||
std::cout << " Produced surface volumes: "
|
||||
<< std::setw(width) << produced[0]
|
||||
<< std::setw(width) << produced[1] << std::endl;
|
||||
std::cout << " Total inj surface volumes: "
|
||||
<< std::setw(width) << tot_injected[0]
|
||||
<< std::setw(width) << tot_injected[1] << std::endl;
|
||||
std::cout << " Total prod surface volumes: "
|
||||
<< std::setw(width) << tot_produced[0]
|
||||
<< std::setw(width) << tot_produced[1] << std::endl;
|
||||
const double balance[2] = { init_surfvol[0] - inplace_surfvol[0] - tot_produced[0] + tot_injected[0],
|
||||
init_surfvol[1] - inplace_surfvol[1] - tot_produced[1] + tot_injected[1] };
|
||||
std::cout << " Initial - inplace + inj - prod: "
|
||||
<< std::setw(width) << balance[0]
|
||||
<< std::setw(width) << balance[1]
|
||||
<< std::endl;
|
||||
std::cout << " Relative mass error: "
|
||||
<< std::setw(width) << balance[0]/(init_surfvol[0] + tot_injected[0])
|
||||
<< std::setw(width) << balance[1]/(init_surfvol[1] + tot_injected[1])
|
||||
<< std::endl;
|
||||
std::cout.precision(8);
|
||||
|
||||
|
@ -61,7 +61,7 @@ namespace Opm
|
||||
///
|
||||
/// \param[in] grid grid data structure
|
||||
/// \param[in] props fluid and rock properties
|
||||
/// \param[in] rock_comp if non-null, rock compressibility properties
|
||||
/// \param[in] rock_comp_props if non-null, rock compressibility properties
|
||||
/// \param[in] well_manager well manager, may manage no (null) wells
|
||||
/// \param[in] src source terms
|
||||
/// \param[in] bcs boundary conditions, treat as all noflow if null
|
||||
@ -70,7 +70,7 @@ namespace Opm
|
||||
SimulatorCompressibleTwophase(const parameter::ParameterGroup& param,
|
||||
const UnstructuredGrid& grid,
|
||||
const BlackoilPropertiesInterface& props,
|
||||
const RockCompressibility* rock_comp,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
WellsManager& wells_manager,
|
||||
const std::vector<double>& src,
|
||||
const FlowBoundaryConditions* bcs,
|
||||
|
@ -64,7 +64,7 @@ namespace Opm
|
||||
Impl(const parameter::ParameterGroup& param,
|
||||
const UnstructuredGrid& grid,
|
||||
const IncompPropertiesInterface& props,
|
||||
const RockCompressibility* rock_comp,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
WellsManager& wells_manager,
|
||||
const std::vector<double>& src,
|
||||
const FlowBoundaryConditions* bcs,
|
||||
@ -91,7 +91,7 @@ namespace Opm
|
||||
// Observed objects.
|
||||
const UnstructuredGrid& grid_;
|
||||
const IncompPropertiesInterface& props_;
|
||||
const RockCompressibility* rock_comp_;
|
||||
const RockCompressibility* rock_comp_props_;
|
||||
WellsManager& wells_manager_;
|
||||
const Wells* wells_;
|
||||
const std::vector<double>& src_;
|
||||
@ -111,14 +111,14 @@ namespace Opm
|
||||
SimulatorIncompTwophase::SimulatorIncompTwophase(const parameter::ParameterGroup& param,
|
||||
const UnstructuredGrid& grid,
|
||||
const IncompPropertiesInterface& props,
|
||||
const RockCompressibility* rock_comp,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
WellsManager& wells_manager,
|
||||
const std::vector<double>& src,
|
||||
const FlowBoundaryConditions* bcs,
|
||||
LinearSolverInterface& linsolver,
|
||||
const double* gravity)
|
||||
{
|
||||
pimpl_.reset(new Impl(param, grid, props, rock_comp, wells_manager, src, bcs, linsolver, gravity));
|
||||
pimpl_.reset(new Impl(param, grid, props, rock_comp_props, wells_manager, src, bcs, linsolver, gravity));
|
||||
}
|
||||
|
||||
|
||||
@ -244,6 +244,7 @@ namespace Opm
|
||||
if (!file) {
|
||||
THROW("Failed to open " << fname.str());
|
||||
}
|
||||
file.precision(15);
|
||||
const std::vector<double>& d = *(it->second);
|
||||
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,
|
||||
const UnstructuredGrid& grid,
|
||||
const IncompPropertiesInterface& props,
|
||||
const RockCompressibility* rock_comp,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
WellsManager& wells_manager,
|
||||
const std::vector<double>& src,
|
||||
const FlowBoundaryConditions* bcs,
|
||||
@ -319,12 +320,12 @@ namespace Opm
|
||||
const double* gravity)
|
||||
: grid_(grid),
|
||||
props_(props),
|
||||
rock_comp_(rock_comp),
|
||||
rock_comp_props_(rock_comp_props),
|
||||
wells_manager_(wells_manager),
|
||||
wells_(wells_manager.c_wells()),
|
||||
src_(src),
|
||||
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_change_tolerance", 1.0),
|
||||
param.getDefault("nl_pressure_maxiter", 10),
|
||||
@ -380,8 +381,8 @@ namespace Opm
|
||||
|
||||
// Initialisation.
|
||||
std::vector<double> porevol;
|
||||
if (rock_comp_ && rock_comp_->isActive()) {
|
||||
computePorevolume(grid_, props_.porosity(), *rock_comp_, state.pressure(), porevol);
|
||||
if (rock_comp_props_ && rock_comp_props_->isActive()) {
|
||||
computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol);
|
||||
} else {
|
||||
computePorevolume(grid_, props_.porosity(), porevol);
|
||||
}
|
||||
@ -398,8 +399,6 @@ namespace Opm
|
||||
total_timer.start();
|
||||
double init_satvol[2] = { 0.0 };
|
||||
double satvol[2] = { 0.0 };
|
||||
double injected[2] = { 0.0 };
|
||||
double produced[2] = { 0.0 };
|
||||
double tot_injected[2] = { 0.0 };
|
||||
double tot_produced[2] = { 0.0 };
|
||||
Opm::computeSaturatedVol(porevol, state.saturation(), init_satvol);
|
||||
@ -452,7 +451,7 @@ namespace Opm
|
||||
// there are no pressure conditions (bcs or wells).
|
||||
// It is deemed sufficient for now to renormalize
|
||||
// using geometric volume instead of pore volume.
|
||||
if ((rock_comp_ == NULL || !rock_comp_->isActive())
|
||||
if ((rock_comp_props_ == NULL || !rock_comp_props_->isActive())
|
||||
&& allNeumannBCs(bcs_) && allRateWells(wells_)) {
|
||||
// Compute average pressures of previous and last
|
||||
// step, and total volume.
|
||||
@ -486,8 +485,8 @@ namespace Opm
|
||||
// Optionally, check if well controls are satisfied.
|
||||
if (check_well_controls_) {
|
||||
Opm::computePhaseFlowRatesPerWell(*wells_,
|
||||
fractional_flows,
|
||||
well_state.perfRates(),
|
||||
fractional_flows,
|
||||
well_resflows_phase);
|
||||
std::cout << "Checking well conditions." << std::endl;
|
||||
// For testing we set surface := reservoir
|
||||
@ -505,9 +504,9 @@ namespace Opm
|
||||
} while (!well_control_passed);
|
||||
|
||||
// Update pore volumes if rock is compressible.
|
||||
if (rock_comp_ && rock_comp_->isActive()) {
|
||||
if (rock_comp_props_ && rock_comp_props_->isActive()) {
|
||||
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).
|
||||
@ -521,10 +520,19 @@ namespace Opm
|
||||
stepsize /= double(num_transport_substeps_);
|
||||
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) {
|
||||
tsolver_.solve(&state.faceflux()[0], &initial_porevol[0], &transport_src[0],
|
||||
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_) {
|
||||
tsolver_.solveGravity(columns_, &initial_porevol[0], stepsize, state.saturation());
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ namespace Opm
|
||||
///
|
||||
/// \param[in] grid grid data structure
|
||||
/// \param[in] props fluid and rock properties
|
||||
/// \param[in] rock_comp if non-null, rock compressibility properties
|
||||
/// \param[in] rock_comp_props if non-null, rock compressibility properties
|
||||
/// \param[in] well_manager well manager, may manage no (null) wells
|
||||
/// \param[in] src source terms
|
||||
/// \param[in] bcs boundary conditions, treat as all noflow if null
|
||||
@ -70,7 +70,7 @@ namespace Opm
|
||||
SimulatorIncompTwophase(const parameter::ParameterGroup& param,
|
||||
const UnstructuredGrid& grid,
|
||||
const IncompPropertiesInterface& props,
|
||||
const RockCompressibility* rock_comp,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
WellsManager& wells_manager,
|
||||
const std::vector<double>& src,
|
||||
const FlowBoundaryConditions* bcs,
|
||||
|
@ -37,12 +37,20 @@ namespace Opm
|
||||
if (wells) {
|
||||
const int nw = wells->number_of_wells;
|
||||
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) {
|
||||
const WellControls* ctrl = wells->ctrls[w];
|
||||
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_; }
|
||||
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:
|
||||
std::vector<double> bhp_;
|
||||
std::vector<double> perfrates_;
|
||||
std::vector<double> perfpress_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
@ -152,8 +152,8 @@ namespace Opm
|
||||
B_cell = 1.0/tm.A_[np*np*cell + 0];
|
||||
double src_flux = -tm.source_[cell];
|
||||
bool src_is_inflow = src_flux < 0.0;
|
||||
influx = src_is_inflow ? B_cell*src_flux : 0.0;
|
||||
outflux = !src_is_inflow ? B_cell*src_flux : 0.0;
|
||||
influx = 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];
|
||||
dtpv = tm.dt_/tm.porevolume0_[cell];
|
||||
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];
|
||||
}
|
||||
s0 = tm.saturation_[cell];
|
||||
dtpv = tm.dt_/tm.porevolume0_[cell];
|
||||
dtpv = tm.dt_/tm.porevolume_[cell];
|
||||
|
||||
}
|
||||
double operator()(double s) const
|
||||
@ -380,8 +380,8 @@ namespace Opm
|
||||
{
|
||||
double sat[2] = { s, 1.0 - s };
|
||||
props_.relperm(1, sat, &cell, mob, 0);
|
||||
mob[0] /= visc_[0];
|
||||
mob[1] /= visc_[1];
|
||||
mob[0] /= visc_[2*cell + 0];
|
||||
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)
|
||||
// + (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 also assume that the A_ matrices are updated from an earlier call to solve().
|
||||
const int nc = grid_.number_of_cells;
|
||||
@ -444,9 +444,8 @@ namespace Opm
|
||||
GravityResidual res(*this, cells, pos, gravflux);
|
||||
if (std::fabs(res(saturation_[cell])) > tol_) {
|
||||
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]);
|
||||
}
|
||||
|
||||
@ -506,8 +505,6 @@ namespace Opm
|
||||
|
||||
|
||||
void TransportModelCompressibleTwophase::solveGravity(const std::vector<std::vector<int> >& columns,
|
||||
const double* pressure,
|
||||
const double* porevolume0,
|
||||
const double dt,
|
||||
std::vector<double>& saturation,
|
||||
std::vector<double>& surfacevol)
|
||||
@ -522,18 +519,22 @@ namespace Opm
|
||||
cells[c] = c;
|
||||
}
|
||||
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) {
|
||||
mob_[2*c + 0] /= visc_[2*c + 0];
|
||||
mob_[2*c + 1] /= visc_[2*c + 1];
|
||||
|
||||
// props_.relperm(cells.size(), &saturation[0], &cells[0], &mob_[0], 0);
|
||||
|
||||
// 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.
|
||||
porevolume0_ = porevolume0;
|
||||
dt_ = dt;
|
||||
toWaterSat(saturation, saturation_);
|
||||
|
||||
|
@ -74,13 +74,10 @@ namespace Opm
|
||||
/// vertical stack, that do not interact with other columns (for
|
||||
/// gravity segregation.
|
||||
/// \param[in] columns Vector of cell-columns.
|
||||
/// \param[in] porevolume0 Array of pore volumes at start of timestep.
|
||||
/// \param[in] dt Time step.
|
||||
/// \param[in, out] saturation Phase saturations.
|
||||
/// \param[in, out] surfacevol Surface volume densities for each phase.
|
||||
void solveGravity(const std::vector<std::vector<int> >& columns,
|
||||
const double* pressure,
|
||||
const double* porevolume0,
|
||||
const double dt,
|
||||
std::vector<double>& saturation,
|
||||
std::vector<double>& surfacevol);
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <opm/core/transport/reorder/TransportModelInterface.hpp>
|
||||
#include <opm/core/transport/reorder/reordersequence.h>
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/utility/StopWatch.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
@ -31,7 +32,11 @@ void Opm::TransportModelInterface::reorderAndTransport(const UnstructuredGrid& g
|
||||
std::vector<int> sequence(grid.number_of_cells);
|
||||
std::vector<int> components(grid.number_of_cells + 1);
|
||||
int ncomponents;
|
||||
time::StopWatch clock;
|
||||
clock.start();
|
||||
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.
|
||||
for (int comp = 0; comp < ncomponents; ++comp) {
|
||||
|
122
opm/core/transport/reorder/TransportModelTracerTof.cpp
Normal file
122
opm/core/transport/reorder/TransportModelTracerTof.cpp
Normal 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
|
74
opm/core/transport/reorder/TransportModelTracerTof.hpp
Normal file
74
opm/core/transport/reorder/TransportModelTracerTof.hpp
Normal 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
|
657
opm/core/transport/reorder/TransportModelTracerTofDiscGal.cpp
Normal file
657
opm/core/transport/reorder/TransportModelTracerTofDiscGal.cpp
Normal 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
|
105
opm/core/transport/reorder/TransportModelTracerTofDiscGal.hpp
Normal file
105
opm/core/transport/reorder/TransportModelTracerTofDiscGal.hpp
Normal 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
|
32
opm/core/utility/DataMap.hpp
Normal file
32
opm/core/utility/DataMap.hpp
Normal 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
|
244
opm/core/utility/NonuniformTableLinear.hpp
Normal file
244
opm/core/utility/NonuniformTableLinear.hpp
Normal 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
|
@ -101,6 +101,7 @@ namespace Opm
|
||||
/// @{
|
||||
const double gallon = 231 * cubic(inch);
|
||||
const double stb = 42 * gallon;
|
||||
const double liter = 1 * cubic(prefix::deci*meter);
|
||||
/// @}
|
||||
|
||||
/// \name Mass
|
||||
|
180
opm/core/utility/VelocityInterpolation.cpp
Normal file
180
opm/core/utility/VelocityInterpolation.cpp
Normal 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
|
122
opm/core/utility/VelocityInterpolation.hpp
Normal file
122
opm/core/utility/VelocityInterpolation.hpp
Normal 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
|
237
opm/core/utility/WachspressCoord.cpp
Normal file
237
opm/core/utility/WachspressCoord.cpp
Normal 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):13–22, 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
|
85
opm/core/utility/WachspressCoord.hpp
Normal file
85
opm/core/utility/WachspressCoord.hpp
Normal 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):13–22, 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
|
@ -29,6 +29,7 @@
|
||||
#include <opm/core/utility/Units.hpp>
|
||||
#include <opm/core/fluid/IncompPropertiesInterface.hpp>
|
||||
#include <opm/core/fluid/BlackoilPropertiesInterface.hpp>
|
||||
#include <opm/core/fluid/blackoil/phaseUsageFromDeck.hpp>
|
||||
#include <cmath>
|
||||
|
||||
namespace Opm
|
||||
@ -512,15 +513,23 @@ namespace Opm
|
||||
State& state)
|
||||
{
|
||||
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);
|
||||
if (deck.hasField("EQUIL")) {
|
||||
if (num_phases != 2) {
|
||||
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.
|
||||
const EQUIL& equil= deck.getEQUIL();
|
||||
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_;
|
||||
initWaterOilContact(grid, props, woc, WaterBelow, state);
|
||||
@ -528,37 +537,64 @@ namespace Opm
|
||||
const double datum_z = equil.equil[0].datum_depth_;
|
||||
const double datum_p = equil.equil[0].datum_depth_pressure_;
|
||||
initHydrostaticPressure(grid, props, woc, gravity, datum_z, datum_p, state);
|
||||
} else if (deck.hasField("SWAT") && deck.hasField("PRESSURE")) {
|
||||
// Set saturations from SWAT, pressure from PRESSURE.
|
||||
} else if (deck.hasField("PRESSURE")) {
|
||||
// Set saturations from SWAT/SGAS, pressure from PRESSURE.
|
||||
std::vector<double>& s = state.saturation();
|
||||
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 int num_cells = grid.number_of_cells;
|
||||
if (num_phases == 2) {
|
||||
if (!pu.phase_used[BlackoilPhases::Aqua]) {
|
||||
// oil-gas: we require SGAS
|
||||
if (!deck.hasField("SGAS")) {
|
||||
THROW("initStateFromDeck(): missing SGAS keyword in 2-phase init");
|
||||
}
|
||||
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] = sw_deck[c_deck];
|
||||
s[2*c + 1] = 1.0 - s[2*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 if (num_phases == 3) {
|
||||
if (!deck.hasField("SGAS")) {
|
||||
THROW("initStateFromDeck(): missing SGAS keyword in 3-phase init (only SWAT and PRESSURE found).");
|
||||
} 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) {
|
||||
const bool has_swat_sgas = deck.hasField("SWAT") && deck.hasField("SGAS");
|
||||
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");
|
||||
for (int c = 0; c < num_cells; ++c) {
|
||||
int c_deck = (grid.global_cell == NULL) ? c : grid.global_cell[c];
|
||||
s[3*c] = sw_deck[c_deck];
|
||||
s[3*c + 1] = 1.0 - (sw_deck[c_deck] + sg_deck[c_deck]);
|
||||
s[3*c + 2] = sg_deck[c_deck];
|
||||
s[3*c + wpos] = sw_deck[c_deck];
|
||||
s[3*c + opos] = 1.0 - (sw_deck[c_deck] + sg_deck[c_deck]);
|
||||
s[3*c + gpos] = sg_deck[c_deck];
|
||||
p[c] = p_deck[c_deck];
|
||||
}
|
||||
} else {
|
||||
THROW("initStateFromDeck(): init with SWAT etc. only available with 2 or 3 phases.");
|
||||
}
|
||||
} else {
|
||||
THROW("initStateFromDeck(): we must either have EQUIL, or both SWAT and PRESSURE.");
|
||||
THROW("initStateFromDeck(): we must either have EQUIL, or PRESSURE and SWAT/SOIL/SGAS.");
|
||||
}
|
||||
|
||||
// Finally, init face pressures.
|
||||
|
@ -374,7 +374,10 @@ namespace Opm
|
||||
if (perf_rate > 0.0) {
|
||||
// perf_rate is a total inflow rate, we want a water rate.
|
||||
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;
|
||||
} else {
|
||||
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 nw = wells.number_of_wells;
|
||||
ASSERT(int(flow_rates_per_well_cell.size()) == wells.well_connpos[nw]);
|
||||
phase_flow_per_well.resize(nw * np);
|
||||
for (int wix = 0; wix < nw; ++wix) {
|
||||
for (int phase = 0; phase < np; ++phase) {
|
||||
|
@ -21,7 +21,10 @@
|
||||
#include <opm/core/utility/miscUtilitiesBlackoil.hpp>
|
||||
#include <opm/core/utility/Units.hpp>
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/newwells.h>
|
||||
#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 <algorithm>
|
||||
#include <functional>
|
||||
@ -31,53 +34,74 @@
|
||||
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 2: assumes that transport has been done with an
|
||||
/// implicit method, i.e. that the current state
|
||||
/// gives the mobilities used for the preceding timestep.
|
||||
/// Note 3: Gives surface volume values, not reservoir volumes
|
||||
/// (as the incompressible version of the function does).
|
||||
/// Also, assumes that transport_src is given in surface volumes
|
||||
/// for injector terms!
|
||||
/// @param[in] props fluid and rock properties.
|
||||
/// @param[in] p pressure (one value per cell)
|
||||
/// @param[in] z surface-volume values (for all P phases)
|
||||
/// @param[in] s saturation values (for all P phases)
|
||||
/// @param[in] src if < 0: total outflow, if > 0: first phase inflow.
|
||||
/// @param[in] state state variables (pressure, sat, surfvol)
|
||||
/// @param[in] transport_src if < 0: total resv outflow, if > 0: first phase surfv inflow
|
||||
/// @param[in] dt timestep used
|
||||
/// @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,
|
||||
const std::vector<double>& press,
|
||||
const std::vector<double>& z,
|
||||
const std::vector<double>& s,
|
||||
const std::vector<double>& src,
|
||||
const BlackoilState& state,
|
||||
const std::vector<double>& transport_src,
|
||||
const double dt,
|
||||
double* injected,
|
||||
double* produced)
|
||||
{
|
||||
const int num_cells = src.size();
|
||||
const int np = s.size()/src.size();
|
||||
if (int(s.size()) != num_cells*np) {
|
||||
THROW("Sizes of s and src vectors do not match.");
|
||||
const int num_cells = transport_src.size();
|
||||
if (props.numCells() != num_cells) {
|
||||
THROW("Size of transport_src vector does not match number of cells in props.");
|
||||
}
|
||||
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(produced, produced + np, 0.0);
|
||||
std::vector<double> visc(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) {
|
||||
if (src[c] > 0.0) {
|
||||
injected[0] += src[c]*dt;
|
||||
} else if (src[c] < 0.0) {
|
||||
const double flux = -src[c]*dt;
|
||||
if (transport_src[c] > 0.0) {
|
||||
// Inflowing transport source is a surface volume flux
|
||||
// for the first phase.
|
||||
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];
|
||||
props.relperm(1, sat, &c, &mob[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;
|
||||
for (int p = 0; p < np; ++p) {
|
||||
mob[p] /= visc[p];
|
||||
totmob += mob[p];
|
||||
}
|
||||
std::fill(prod_surfvol.begin(), prod_surfvol.end(), 0.0);
|
||||
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
|
||||
|
@ -22,36 +22,40 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct UnstructuredGrid;
|
||||
struct Wells;
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
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 2: assumes that transport has been done with an
|
||||
/// implicit method, i.e. that the current state
|
||||
/// gives the mobilities used for the preceding timestep.
|
||||
/// Note 3: Gives surface volume values, not reservoir volumes
|
||||
/// (as the incompressible version of the function does).
|
||||
/// Also, assumes that transport_src is given in surface volumes
|
||||
/// for injector terms!
|
||||
/// @param[in] props fluid and rock properties.
|
||||
/// @param[in] p pressure (one value per cell)
|
||||
/// @param[in] z surface-volume values (for all P phases)
|
||||
/// @param[in] s saturation values (for all P phases)
|
||||
/// @param[in] src if < 0: total outflow, if > 0: first phase inflow.
|
||||
/// @param[in] state state variables (pressure, sat, surfvol)
|
||||
/// @param[in] transport_src if < 0: total resv outflow, if > 0: first phase surfv inflow
|
||||
/// @param[in] dt timestep used
|
||||
/// @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,
|
||||
const std::vector<double>& p,
|
||||
const std::vector<double>& z,
|
||||
const std::vector<double>& s,
|
||||
const std::vector<double>& src,
|
||||
const BlackoilState& state,
|
||||
const std::vector<double>& transport_src,
|
||||
const double dt,
|
||||
double* injected,
|
||||
double* produced);
|
||||
|
||||
|
||||
/// @brief Computes total mobility for a set of saturation values.
|
||||
/// @param[in] props rock and fluid properties
|
||||
/// @param[in] cells cells with which the saturation values are associated
|
||||
@ -66,6 +70,7 @@ namespace Opm
|
||||
const std::vector<double>& s,
|
||||
std::vector<double>& totmob);
|
||||
|
||||
|
||||
/// @brief Computes total mobility and omega for a set of saturation values.
|
||||
/// @param[in] props rock and fluid properties
|
||||
/// @param[in] cells cells with which the saturation values are associated
|
||||
@ -131,6 +136,22 @@ namespace Opm
|
||||
const double* saturation,
|
||||
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
|
||||
|
||||
#endif // OPM_MISCUTILITIESBLACKOIL_HEADER_INCLUDED
|
||||
|
108
opm/core/utility/writeECLData.cpp
Normal file
108
opm/core/utility/writeECLData.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
47
opm/core/utility/writeECLData.hpp
Normal file
47
opm/core/utility/writeECLData.hpp
Normal 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
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include <opm/core/utility/writeVtkData.hpp>
|
||||
#include <opm/core/utility/DataMap.hpp>
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/utility/ErrorMacros.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
@ -26,15 +26,13 @@
|
||||
#include <vector>
|
||||
#include <tr1/array>
|
||||
#include <iosfwd>
|
||||
#include <opm/core/utility/DataMap.hpp>
|
||||
|
||||
struct UnstructuredGrid;
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
/// Intended to map strings (giving the output field names) to data.
|
||||
typedef std::map<std::string, const std::vector<double>*> DataMap;
|
||||
|
||||
/// Vtk output for cartesian grids.
|
||||
void writeVtkData(const std::tr1::array<int, 3>& dims,
|
||||
const std::tr1::array<double, 3>& cell_size,
|
||||
|
@ -64,7 +64,7 @@ namespace OPM
|
||||
}
|
||||
//cout << endl;
|
||||
}
|
||||
if(!(int(pos_struct.value.size())==pos_struct.pos[n])){
|
||||
if(int(pos_struct.value.size()) != pos_struct.pos[n]){
|
||||
cerr << "Failed to read pos structure" << endl;
|
||||
cerr << "pos_struct.value.size()" << pos_struct.value.size() << endl;
|
||||
cerr << "pos_struct.pos[n+1]" << pos_struct.pos[n] << endl;
|
||||
|
@ -572,6 +572,7 @@ namespace Opm
|
||||
break;
|
||||
}
|
||||
const double total_produced = getTotalProductionFlow(well_surfacerates_phase, phase);
|
||||
const double total_reinjected = - total_produced; // Production negative, injection positive
|
||||
const double my_guide_rate = injectionGuideRate(true);
|
||||
for (size_t i = 0; i < children_.size(); ++i) {
|
||||
// Apply for all children.
|
||||
@ -580,11 +581,11 @@ namespace Opm
|
||||
const double children_guide_rate = children_[i]->injectionGuideRate(true);
|
||||
#ifdef DIRTY_WELLCTRL_HACK
|
||||
children_[i]->applyInjGroupControl(InjectionSpecification::RESV,
|
||||
(children_guide_rate / my_guide_rate) * total_produced * injSpec().reinjection_fraction_target_,
|
||||
(children_guide_rate / my_guide_rate) * total_reinjected * injSpec().reinjection_fraction_target_,
|
||||
false);
|
||||
#else
|
||||
children_[i]->applyInjGroupControl(InjectionSpecification::RATE,
|
||||
(children_guide_rate / my_guide_rate) * total_produced * injSpec().reinjection_fraction_target_,
|
||||
(children_guide_rate / my_guide_rate) * total_reinjected * injSpec().reinjection_fraction_target_,
|
||||
false);
|
||||
#endif
|
||||
}
|
||||
@ -600,7 +601,7 @@ namespace Opm
|
||||
if (phaseUsage().phase_used[BlackoilPhases::Vapour]) {
|
||||
total_produced += getTotalProductionFlow(well_reservoirrates_phase, BlackoilPhases::Vapour);
|
||||
}
|
||||
|
||||
const double total_reinjected = - total_produced; // Production negative, injection positive
|
||||
const double my_guide_rate = injectionGuideRate(true);
|
||||
for (size_t i = 0; i < children_.size(); ++i) {
|
||||
// Apply for all children.
|
||||
@ -608,7 +609,7 @@ namespace Opm
|
||||
// as that would check if we're under group control, something we're not.
|
||||
const double children_guide_rate = children_[i]->injectionGuideRate(true);
|
||||
children_[i]->applyInjGroupControl(InjectionSpecification::RESV,
|
||||
(children_guide_rate / my_guide_rate) * total_produced * injSpec().voidage_replacment_fraction_,
|
||||
(children_guide_rate / my_guide_rate) * total_reinjected * injSpec().voidage_replacment_fraction_,
|
||||
false);
|
||||
}
|
||||
|
||||
@ -767,7 +768,8 @@ namespace Opm
|
||||
{
|
||||
const int np = phaseUsage().num_phases;
|
||||
const int index = self_index_*np;
|
||||
return std::make_pair<WellNode*, double>(this, rateByMode(&well_reservoirrates_phase[index],
|
||||
return std::pair<WellNode*, double>(this,
|
||||
rateByMode(&well_reservoirrates_phase[index],
|
||||
&well_surfacerates_phase[index],
|
||||
mode));
|
||||
}
|
||||
@ -781,7 +783,7 @@ namespace Opm
|
||||
&& (injSpec().control_mode_ != InjectionSpecification::GRUP && injSpec().control_mode_ != InjectionSpecification::NONE)) {
|
||||
return;
|
||||
}
|
||||
if (!wells_->type[self_index_] == INJECTOR) {
|
||||
if (wells_->type[self_index_] != INJECTOR) {
|
||||
ASSERT(target == 0.0);
|
||||
return;
|
||||
}
|
||||
@ -858,12 +860,12 @@ namespace Opm
|
||||
std::cout << "Returning" << std::endl;
|
||||
return;
|
||||
}
|
||||
if (!wells_->type[self_index_] == PRODUCER) {
|
||||
if (wells_->type[self_index_] != PRODUCER) {
|
||||
ASSERT(target == 0.0);
|
||||
return;
|
||||
}
|
||||
// We're a producer, so we need to negate the input
|
||||
double ntarget = target;
|
||||
double ntarget = -target;
|
||||
|
||||
double distr[3] = { 0.0, 0.0, 0.0 };
|
||||
const int* phase_pos = phaseUsage().phase_pos;
|
||||
|
@ -230,6 +230,14 @@ namespace Opm
|
||||
|
||||
|
||||
|
||||
/// Construct from existing wells object.
|
||||
WellsManager::WellsManager(struct Wells* W)
|
||||
: w_(clone_wells(W))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Construct wells from deck.
|
||||
WellsManager::WellsManager(const Opm::EclipseGridParser& deck,
|
||||
const UnstructuredGrid& grid,
|
||||
@ -548,7 +556,7 @@ namespace Opm
|
||||
cf[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0;
|
||||
} else if (wci_line.injector_type_[0] == 'G') {
|
||||
if (!pu.phase_used[BlackoilPhases::Vapour]) {
|
||||
THROW("Water phase not used, yet found water-injecting well.");
|
||||
THROW("Gas phase not used, yet found gas-injecting well.");
|
||||
}
|
||||
cf[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0;
|
||||
}
|
||||
@ -721,7 +729,6 @@ namespace Opm
|
||||
|
||||
if (deck.hasField("WELOPEN")) {
|
||||
const WELOPEN& welopen = deck.getWELOPEN();
|
||||
|
||||
for (size_t i = 0; i < welopen.welopen.size(); ++i) {
|
||||
WelopenLine line = welopen.welopen[i];
|
||||
std::string wellname = line.well_;
|
||||
@ -729,22 +736,22 @@ namespace Opm
|
||||
if (it == well_names_to_index.end()) {
|
||||
THROW("Trying to open/shut well with name: \"" << wellname<<"\" but it's not registered under WELSPECS.");
|
||||
}
|
||||
int index = it->second;
|
||||
const int index = it->second;
|
||||
if (line.openshutflag_ == "SHUT") {
|
||||
// We currently don't care if the well is open or not.
|
||||
/// \TODO Should this perhaps be allowed? I.e. should it be if(well_shut) { shutwell(); } else { /* do nothing*/ }?
|
||||
//ASSERT(w_->ctrls[index]->current < 0);
|
||||
int& cur_ctrl = w_->ctrls[index]->current;
|
||||
if (cur_ctrl >= 0) {
|
||||
cur_ctrl = ~cur_ctrl;
|
||||
}
|
||||
ASSERT(w_->ctrls[index]->current < 0);
|
||||
} else if (line.openshutflag_ == "OPEN") {
|
||||
//ASSERT(w_->ctrls[index]->current >= 0);
|
||||
int& cur_ctrl = w_->ctrls[index]->current;
|
||||
if (cur_ctrl < 0) {
|
||||
cur_ctrl = ~cur_ctrl;
|
||||
}
|
||||
ASSERT(w_->ctrls[index]->current >= 0);
|
||||
} else {
|
||||
THROW("Unknown Open/close keyword: \"" << line.openshutflag_<< "\". Allowed values: OPEN, SHUT.");
|
||||
}
|
||||
|
||||
// We revert back to it's original control.
|
||||
// Note that this is OK as ~~ = id.
|
||||
w_->ctrls[index]->current = ~w_->ctrls[index]->current;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,8 +42,12 @@ namespace Opm
|
||||
public:
|
||||
/// Default constructor -- no wells.
|
||||
WellsManager();
|
||||
/// Construct from mrst type output.
|
||||
/// Wellmanger is not properly initialized
|
||||
|
||||
/// Construct from existing wells object.
|
||||
/// WellsManager is not properly initialised in the sense that the logic to
|
||||
/// manage control switching does not exist.
|
||||
///
|
||||
/// @param[in] W Existing wells object.
|
||||
WellsManager(struct Wells* W);
|
||||
|
||||
/// Construct from input deck and grid.
|
||||
|
40
opmcore-config.cmake.in
Normal file
40
opmcore-config.cmake.in
Normal file
@ -0,0 +1,40 @@
|
||||
# - Open Porous Media Initiative Core Library config mode
|
||||
#
|
||||
# Defines the following variables:
|
||||
# @PACKAGE@_FOUND - true
|
||||
# @PACKAGE@_VERSION - version of the opm-core library found, e.g. 0.2
|
||||
# @PACKAGE@_DEFINITIONS - defines to be made on the command line
|
||||
# @PACKAGE@_INCLUDE_DIRS - header directories with which to compile
|
||||
# @PACKAGE@_LIBRARY_DIRS - directories to search for libraries
|
||||
# @PACKAGE@_LIBRARIES - names of the libraries with which to link
|
||||
#
|
||||
# You should put lines like this in your CMakeLists.txt
|
||||
# set (@PACKAGE@_DIR "../@PACKAGE@" CACHE LOCATION "Build tree of @PACKAGE_NAME@")
|
||||
# find_package (@PACKAGE@)
|
||||
|
||||
# <http://www.vtk.org/Wiki/CMake/Tutorials/How_to_create_a_ProjectConfig.cmake_file>
|
||||
|
||||
# propagate this property from one build system to the other
|
||||
set (@PACKAGE@_VERSION @PACKAGE_VERSION@)
|
||||
|
||||
# these definitions may be necessary to make the header files behave the
|
||||
# same way as they did when the library was compiled
|
||||
set (@PACKAGE@_DEFINITIONS "@OPM_BOOST_CPPFLAGS@ @SUPERLU_CPPFLAGS@ @ERT_CPPFLAGS@")
|
||||
|
||||
# include files come from the source tree where the template is stored
|
||||
set (@PACKAGE@_INCLUDE_DIRS "@abs_top_srcdir@")
|
||||
|
||||
# user programs should link with this library (see comment in the header)
|
||||
set (@PACKAGE@_LIBRARIES "@PACKAGE@")
|
||||
|
||||
# convention is to have a variable named like this to add to link directories
|
||||
set (@PACKAGE@_LIBRARY_DIRS "@abs_top_builddir@/lib/.libs")
|
||||
|
||||
# libraries come from the build tree where this file was generated
|
||||
find_library (@PACKAGE@_LOCATION NAMES "@PACKAGE@" PATHS "${@PACKAGE@_LIBRARY_DIRS}")
|
||||
mark_as_advanced (@PACKAGE@_LOCATION)
|
||||
|
||||
# add the library as a target, so that other things in the project including
|
||||
# this file may depend on it and get rebuild if this library changes.
|
||||
add_library (@PACKAGE@ UNKNOWN IMPORTED)
|
||||
set_property (TARGET @PACKAGE@ PROPERTY IMPORTED_LOCATION "${@PACKAGE@_LOCATION}")
|
@ -1,8 +1,9 @@
|
||||
AM_CPPFLAGS = \
|
||||
-I$(top_srcdir) \
|
||||
$(OPM_BOOST_CPPFLAGS)
|
||||
$(OPM_BOOST_CPPFLAGS) $(SUPERLU_CPPFLAGS) \
|
||||
$(ERT_CPPFLAGS)
|
||||
|
||||
AM_LDFLAGS = $(OPM_BOOST_LDFLAGS)
|
||||
AM_LDFLAGS = $(OPM_BOOST_LDFLAGS) $(ERT_LDFLAGS)
|
||||
|
||||
|
||||
LDADD = $(top_builddir)/lib/libopmcore.la
|
||||
@ -23,6 +24,9 @@ test_read_grid \
|
||||
test_read_vag \
|
||||
test_readpolymer \
|
||||
test_sf2p \
|
||||
test_velocityinterpolation \
|
||||
test_wachspresscoord \
|
||||
test_wells \
|
||||
test_writeVtkData \
|
||||
unit_test
|
||||
|
||||
@ -57,8 +61,17 @@ test_read_vag_SOURCES = test_read_vag.cpp
|
||||
|
||||
test_sf2p_SOURCES = test_sf2p.cpp
|
||||
|
||||
test_velocityinterpolation_SOURCES = test_velocityinterpolation.cpp
|
||||
test_velocityinterpolation_LDADD = $(LDADD) $(BOOST_UNIT_TEST_FRAMEWORK_LIB)
|
||||
|
||||
test_wachspresscoord_SOURCES = test_wachspresscoord.cpp
|
||||
test_wachspresscoord_LDADD = $(LDADD) $(BOOST_UNIT_TEST_FRAMEWORK_LIB)
|
||||
|
||||
test_writeVtkData_SOURCES = test_writeVtkData.cpp
|
||||
|
||||
test_wells_SOURCES = test_wells.cpp
|
||||
test_wells_LDADD = $(LDADD) $(BOOST_UNIT_TEST_FRAMEWORK_LIB)
|
||||
|
||||
unit_test_SOURCES = unit_test.cpp
|
||||
|
||||
|
||||
@ -73,3 +86,9 @@ if BUILD_AGMG
|
||||
noinst_PROGRAMS += test_agmg
|
||||
test_agmg_SOURCES = test_agmg.cpp
|
||||
endif
|
||||
|
||||
|
||||
if HAVE_ERT
|
||||
noinst_PROGRAMS += test_ert
|
||||
test_ert_SOURCES = test_ert.cpp
|
||||
endif
|
||||
|
@ -43,7 +43,7 @@ int main(int argc, char** argv)
|
||||
UnstructuredGrid grid;
|
||||
grid.number_of_cells = 1;
|
||||
grid.global_cell = NULL;
|
||||
Opm::BlackoilPropertiesFromDeck props(deck, grid);
|
||||
Opm::BlackoilPropertiesFromDeck props(deck, grid, param);
|
||||
|
||||
const int n = 1;
|
||||
double p[n] = { 150e5 };
|
||||
|
260
tests/test_ert.cpp
Normal file
260
tests/test_ert.cpp
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
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 "config.h"
|
||||
#include <opm/core/eclipse/EclipseGridParser.hpp>
|
||||
#include <opm/core/GridManager.hpp>
|
||||
#include <opm/core/grid/cart_grid.h>
|
||||
#include <opm/core/grid.h>
|
||||
#include <cstdio>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <opm/core/fluid/IncompPropertiesBasic.hpp>
|
||||
#include <opm/core/fluid/IncompPropertiesFromDeck.hpp>
|
||||
|
||||
|
||||
#include <ecl_grid.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#if 0
|
||||
static
|
||||
void cell_nodes(const UnstructuredGrid * c_grid , int cell , std::vector<int>& nodes) {
|
||||
int face_offset = c_grid->cell_facepos[cell];
|
||||
int num_faces = c_grid->cell_facepos[cell + 1] - face_offset;
|
||||
|
||||
nodes.clear();
|
||||
//printf("cell: %d \n",cell);
|
||||
for (int iface = 0; iface < num_faces; iface++) {
|
||||
int face = c_grid->cell_faces[ face_offset + iface];
|
||||
//printf("face[%d] = %d \n",iface , face );
|
||||
|
||||
{
|
||||
int node_offset = c_grid->face_nodepos[ face ];
|
||||
int num_nodes = c_grid->face_nodepos[ face + 1] - node_offset;
|
||||
for (int inode = 0; inode < num_nodes; inode++) {
|
||||
int node = c_grid->face_nodes[ inode + node_offset ];
|
||||
//printf(" node[%d] = %d \n",inode , node);
|
||||
nodes.push_back( node );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/*for (int i =0; i < nodes.size(); i++)
|
||||
std::cout << nodes[i] << " ";
|
||||
std::cout << "\n";
|
||||
*/
|
||||
sort( nodes.begin() , nodes.end());
|
||||
/*for (int i =0; i < nodes.size(); i++)
|
||||
std::cout << nodes[i] << " ";
|
||||
std::cout << "\n";
|
||||
*/
|
||||
unique( nodes.begin() , nodes.end() );
|
||||
/*for (int i =0; i < nodes.size(); i++)
|
||||
std::cout << nodes[i] << " ";
|
||||
std::cout << "\n";
|
||||
*/
|
||||
nodes.resize( 8 );
|
||||
/*for (int i =0; i < nodes.size(); i++)
|
||||
std::cout << nodes[i] << " ";
|
||||
std::cout << "\n";
|
||||
*/
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
static
|
||||
void eclExport(Opm::GridManager& grid) {
|
||||
const UnstructuredGrid * c_grid = grid.c_grid();
|
||||
|
||||
printf("dimensions : %d \n",c_grid->dimensions);
|
||||
printf("number of cells : %d \n",c_grid->number_of_cells);
|
||||
printf("number of nodes : %d \n",c_grid->number_of_nodes);
|
||||
printf("number of faces : %d \n",c_grid->number_of_faces);
|
||||
printf("length(face_nodes) : %d \n",c_grid->face_nodepos[ c_grid->number_of_faces ]);
|
||||
printf("cartdims : %d %d %d \n",
|
||||
c_grid->cartdims[0] ,
|
||||
c_grid->cartdims[1] ,
|
||||
c_grid->cartdims[2]);
|
||||
|
||||
printf("global_cell : %d %d %d %d %d\n",
|
||||
c_grid->global_cell[0] ,
|
||||
c_grid->global_cell[1] ,
|
||||
c_grid->global_cell[2] ,
|
||||
c_grid->global_cell[3] ,
|
||||
c_grid->global_cell[4]);
|
||||
|
||||
{
|
||||
std::vector<int> nodes;
|
||||
cell_nodes( c_grid , 10 , nodes );
|
||||
cell_nodes( c_grid , 15 , nodes );
|
||||
cell_nodes( c_grid , 20 , nodes );
|
||||
cell_nodes( c_grid , 25 , nodes );
|
||||
}
|
||||
|
||||
{
|
||||
ecl_grid_type * ecl_grid;
|
||||
int num_coords = c_grid->number_of_cells;
|
||||
int coords_size = 4;
|
||||
int nx = c_grid->cartdims[0];
|
||||
int ny = c_grid->cartdims[1];
|
||||
int nz = c_grid->cartdims[2];
|
||||
|
||||
int ** coords;
|
||||
float ** corners;
|
||||
// float * mapaxes = NULL;
|
||||
std::vector<int> nodes;
|
||||
|
||||
corners = (float **) malloc( num_coords * sizeof * corners );
|
||||
coords = (int **) malloc( num_coords * sizeof * coords );
|
||||
|
||||
{
|
||||
int c;
|
||||
for (c=0; c < num_coords; c++) {
|
||||
corners[c] = (float *) malloc( 24 * sizeof * corners[c] );
|
||||
coords[c] = (int *) malloc( coords_size * sizeof * coords[c] );
|
||||
}
|
||||
|
||||
for (c=0; c < num_coords; c++) {
|
||||
cell_nodes( c_grid , c , nodes );
|
||||
for (int p=0; p < 8; p++) {
|
||||
int n = nodes[p];
|
||||
for (int d=0; d < c_grid->dimensions; d++)
|
||||
corners[c][3*p + d] = c_grid->node_coordinates[ c_grid->dimensions * n + d ];
|
||||
}
|
||||
|
||||
{
|
||||
int i,j,k;
|
||||
{
|
||||
int g = c_grid->global_cell[ c ];
|
||||
k = g / nx*ny; g -= k * nx*ny;
|
||||
j = g / nx; g -= j * nx;
|
||||
i = g;
|
||||
}
|
||||
|
||||
coords[c][0] = i + 1;
|
||||
coords[c][1] = j + 1;
|
||||
coords[c][2] = k + 1;
|
||||
coords[c][3] = c_grid->global_cell[ c ] + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ecl_grid = ecl_grid_alloc( "/private/joaho/ERT/NR/libenkf/src/Gurbat/EXAMPLE_01_BASE.EGRID" );
|
||||
printf("Grid loaded ... \n");
|
||||
ecl_grid_free( ecl_grid );
|
||||
|
||||
printf("Grid discarded ... \n");
|
||||
|
||||
ecl_grid = ecl_grid_alloc_GRID_data( num_coords , nx , ny , nz , coords_size , coords , corners , NULL );
|
||||
ecl_grid_fwrite_GRID( ecl_grid , "/tmp/test.GRID" );
|
||||
|
||||
{
|
||||
FILE * stream = fopen( "/tmp/test.grdecl" , "w");
|
||||
ecl_grid_fprintf_grdecl( ecl_grid , stream );
|
||||
fclose( stream );
|
||||
}
|
||||
|
||||
ecl_grid_free( ecl_grid );
|
||||
|
||||
{
|
||||
for (int c=0; c < num_coords; c++) {
|
||||
free(corners[c]);
|
||||
free(coords[c]);
|
||||
}
|
||||
}
|
||||
free( corners );
|
||||
free( coords );
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
|
||||
#ifdef HAVE_ERT
|
||||
ecl_grid_type * create_ecl_grid( const struct UnstructuredGrid * g) {
|
||||
int num_coords = g->number_of_cells;
|
||||
int nx = g->cartdims[0];
|
||||
int ny = g->cartdims[1];
|
||||
int nz = g->cartdims[2];
|
||||
int coords_size = 4;
|
||||
int ** coords;
|
||||
float ** corners;
|
||||
float * mapaxes = NULL;
|
||||
|
||||
corners = malloc( num_coords * sizeof * corners );
|
||||
coords = malloc( num_coords * sizeof * coords );
|
||||
|
||||
{
|
||||
for (int c=0; c < num_coords; c++) {
|
||||
corners[c] = malloc( 24 * sizeof * corners[0] );
|
||||
coords[c] = malloc( coords_size * sizeof * coords[0] );
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
for (int k=0; k < nz; k++) {
|
||||
for (int j=0; j < ny; j++) {
|
||||
for (int i=0; i < nx; i++) {
|
||||
int global_index = i + j*nx + k*nx*ny;
|
||||
|
||||
coords[global_index][0] = i;
|
||||
coords[global_index][1] = j;
|
||||
coords[global_index][2] = k;
|
||||
coords[global_index][3] = 1;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
for (int c=0; c < num_coords; c++) {
|
||||
free(corners[c]);
|
||||
free(coords[c]);
|
||||
}
|
||||
}
|
||||
free( corners );
|
||||
free( coords );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
*/
|
||||
|
||||
// struct grdecl : opm/core/grid/cpgpreprocess/preprocess.h
|
||||
// struct processes_grid : opm/core/grid/cpgpreprocess/preprocess.h
|
||||
|
||||
|
||||
|
||||
int main(int /*argc*/ , char **argv)
|
||||
{
|
||||
std::string filename( argv[1] );
|
||||
boost::scoped_ptr<Opm::GridManager> grid;
|
||||
boost::scoped_ptr<Opm::IncompPropertiesInterface> props;
|
||||
Opm::EclipseGridParser eclParser(filename , false);
|
||||
|
||||
//eclParser.saveEGRID_INIT("/tmp" , "OPM" );
|
||||
|
||||
grid.reset(new Opm::GridManager(eclParser));
|
||||
|
||||
props.reset(new Opm::IncompPropertiesFromDeck(eclParser , *grid->c_grid()));
|
||||
}
|
482
tests/test_velocityinterpolation.cpp
Normal file
482
tests/test_velocityinterpolation.cpp
Normal file
@ -0,0 +1,482 @@
|
||||
/*
|
||||
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 <config.h>
|
||||
|
||||
#if HAVE_DYNAMIC_BOOST_TEST
|
||||
#define BOOST_TEST_DYN_LINK
|
||||
#endif
|
||||
#define NVERBOSE // to suppress our messages when throwing
|
||||
|
||||
#define BOOST_TEST_MODULE VelocityInterpolationTest
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <opm/core/utility/VelocityInterpolation.hpp>
|
||||
#include <opm/core/GridManager.hpp>
|
||||
#include <opm/core/grid.h>
|
||||
#include <cmath>
|
||||
|
||||
using namespace Opm;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Compute flux corresponding to a constant velocity vector v.
|
||||
void computeFlux(const UnstructuredGrid& grid, const std::vector<double>& v, std::vector<double>& flux)
|
||||
{
|
||||
const int dim = v.size();
|
||||
ASSERT(dim == grid.dimensions);
|
||||
flux.resize(grid.number_of_faces);
|
||||
for (int face = 0; face < grid.number_of_faces; ++face) {
|
||||
flux[face] = std::inner_product(v.begin(), v.end(), grid.face_normals + face*dim, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute a linear vector function v = v0 + x*v1.
|
||||
void computeLinearVec(const std::vector<double>& v0,
|
||||
const std::vector<double>& v1,
|
||||
const std::vector<double>& x,
|
||||
std::vector<double>& v)
|
||||
{
|
||||
ASSERT(v0.size() == v1.size() && v0.size() == x.size());
|
||||
const int dim = v0.size();
|
||||
v.resize(dim);
|
||||
for (int dd = 0; dd < dim; ++dd) {
|
||||
v[dd] = v0[dd] + x[dd]*v1[dd];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Compute flux corresponding to a velocity vector v = v0 + x*v1.
|
||||
void computeFluxLinear(const UnstructuredGrid& grid,
|
||||
const std::vector<double>& v0,
|
||||
const std::vector<double>& v1,
|
||||
std::vector<double>& flux)
|
||||
{
|
||||
const int dim = v0.size();
|
||||
ASSERT(dim == grid.dimensions);
|
||||
flux.resize(grid.number_of_faces);
|
||||
std::vector<double> x(dim);
|
||||
std::vector<double> v(dim);
|
||||
for (int face = 0; face < grid.number_of_faces; ++face) {
|
||||
const double* fc = grid.face_centroids + face*dim;
|
||||
std::copy(fc, fc + dim, x.begin());
|
||||
computeLinearVec(v0, v1, x, v);
|
||||
flux[face] = std::inner_product(v.begin(), v.end(), grid.face_normals + face*dim, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double vectorDiff2(const std::vector<double>& v1, const std::vector<double>& v2)
|
||||
{
|
||||
ASSERT(v1.size() == v2.size());
|
||||
const int sz = v1.size();
|
||||
double vdiff = 0.0;
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
vdiff += (v1[i] - v2[i])*(v1[i] - v2[i]);
|
||||
}
|
||||
return vdiff;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
template <class VelInterp>
|
||||
void testConstantVelRepro2d()
|
||||
{
|
||||
// Set up 2d 1-cell cartesian case.
|
||||
GridManager g(1, 1);
|
||||
const UnstructuredGrid& grid = *g.c_grid();
|
||||
std::vector<double> v(2);
|
||||
v[0] = 0.12345;
|
||||
v[1] = -0.6789;
|
||||
std::vector<double> flux;
|
||||
computeFlux(grid, v, flux);
|
||||
VelInterp vic(grid);
|
||||
vic.setupFluxes(&flux[0]);
|
||||
|
||||
// Test a few points
|
||||
std::vector<double> x(2);
|
||||
x[0] = 0.23456;
|
||||
x[1] = 0.87654;
|
||||
std::vector<double> v_interp(2);
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
x[0] = 0.5;
|
||||
x[1] = 0.5;
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
x[0] = 1.0;
|
||||
x[1] = 0.5;
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
x[0] = 1.0;
|
||||
x[1] = 1.0;
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Data for a pyramid. Node numbering goes
|
||||
// lexicographic on bottom, then top.
|
||||
// Face numbering goes xmin, xmax, ymin, ymax, bottom.
|
||||
namespace Pyramid
|
||||
{
|
||||
static int face_nodes[] = { 0, 4, 2, 3, 4, 1, 0, 1, 4, 4, 3, 2, 0, 2, 3, 1, };
|
||||
static int face_nodepos[] = { 0, 3, 6, 9, 12, 16 };
|
||||
static int face_cells[] = { 0, -1, 0, -1, 0, -1, 0, -1, 0, -1 };
|
||||
static int cell_faces[] = { 0, 1, 2, 3, 4 };
|
||||
static int cell_facepos[] = { 0, 5 };
|
||||
static double node_coordinates[] = { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0 };
|
||||
static double face_centroids[] = { 0, 1.0/3.0, 1.0/3.0,
|
||||
2.0/3.0, 1.0/3.0, 1.0/3.0,
|
||||
1.0/3.0, 0, 1.0/3.0,
|
||||
1.0/3.0, 2.0/3.0, 1.0/3.0,
|
||||
0.5, 0.5, 0 };
|
||||
static double face_areas[] = { 0.5, std::sqrt(2.0), 0.5, std::sqrt(2.0), 1.0 };
|
||||
static double face_normals[] = { -0.5000, 0, 0,
|
||||
0.5000, 0, 0.5000,
|
||||
0, -0.5000, 0,
|
||||
0, 0.5000, 0.5000,
|
||||
0, 0, -1.0000 };
|
||||
static double cell_centroids[] = { 0.375, 0.375, 0.25 };
|
||||
static double cell_volumes[] = { 1.0/3.0 };
|
||||
|
||||
} // namespace Pyramid
|
||||
|
||||
UnstructuredGrid makePyramid()
|
||||
{
|
||||
// Make a 3d 1-cell grid, where the single cell is a pyramid.
|
||||
UnstructuredGrid grid;
|
||||
grid.dimensions = 3;
|
||||
grid.number_of_cells = 1;
|
||||
grid.number_of_faces = 5;
|
||||
grid.number_of_nodes = 5;
|
||||
grid.face_nodes = Pyramid::face_nodes;
|
||||
grid.face_nodepos = Pyramid::face_nodepos;
|
||||
grid.face_cells = Pyramid::face_cells;
|
||||
grid.cell_faces = Pyramid::cell_faces;
|
||||
grid.cell_facepos = Pyramid::cell_facepos;
|
||||
grid.node_coordinates = Pyramid::node_coordinates;
|
||||
grid.face_centroids = Pyramid::face_centroids;
|
||||
grid.face_areas = Pyramid::face_areas;
|
||||
grid.face_normals = Pyramid::face_normals;
|
||||
grid.cell_centroids = Pyramid::cell_centroids;
|
||||
grid.cell_volumes = Pyramid::cell_volumes;
|
||||
return grid;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
template <class VelInterp>
|
||||
void testConstantVelReproPyramid()
|
||||
{
|
||||
// Set up a 3d 1-cell non-cartesian case (a pyramid).
|
||||
UnstructuredGrid grid = makePyramid();
|
||||
std::vector<double> v(3);
|
||||
v[0] = 0.12345;
|
||||
v[1] = -0.6789;
|
||||
v[2] = 0.3456;
|
||||
std::vector<double> flux;
|
||||
computeFlux(grid, v, flux);
|
||||
VelInterp vic(grid);
|
||||
vic.setupFluxes(&flux[0]);
|
||||
|
||||
// Test a few points
|
||||
std::vector<double> x(3);
|
||||
x[0] = 0.123;
|
||||
x[1] = 0.0123;
|
||||
x[2] = 0.213;
|
||||
std::vector<double> v_interp(3);
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
x[0] = 0.0;
|
||||
x[1] = 0.0;
|
||||
x[2] = 1.0;
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
x[0] = 0.5;
|
||||
x[1] = 0.5;
|
||||
x[2] = 0.0;
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
x[0] = 0.5;
|
||||
x[1] = 0.5;
|
||||
x[2] = 0.1;
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
// Data for an irregular 2d polygon.
|
||||
namespace Irreg2d
|
||||
{
|
||||
static int face_nodes[] = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 0 };
|
||||
static int face_nodepos[] = { 0, 2, 4, 6, 8, 10 };
|
||||
static int face_cells[] = { 0, -1, 0, -1, 0, -1, 0, -1, 0, -1 };
|
||||
static int cell_faces[] = { 0, 1, 2, 3, 4 };
|
||||
static int cell_facepos[] = { 0, 5 };
|
||||
static double node_coordinates[] = { 0, 0, 3, 0, 3, 2, 1, 3, 0, 2 };
|
||||
static double face_centroids[] = { 1.5, 0, 3, 1, 2, 2.5, 0.5, 2.5, 0, 1 };
|
||||
static double face_areas[] = { 3, 2, std::sqrt(5.0), std::sqrt(2.0), 2 };
|
||||
static double face_normals[] = { 0, -3, 2, 0, 1, 2, -1, 1, -2, 0 };
|
||||
static double cell_centroids[] = { 22.0/15.0, 19.0/15.0 };
|
||||
static double cell_volumes[] = { 7.5 };
|
||||
} // namespace Irreg2d
|
||||
|
||||
UnstructuredGrid makeIrreg2d()
|
||||
{
|
||||
// Make a 2d 1-cell grid, where the single cell is a polygon.
|
||||
UnstructuredGrid grid;
|
||||
grid.dimensions = 2;
|
||||
grid.number_of_cells = 1;
|
||||
grid.number_of_faces = 5;
|
||||
grid.number_of_nodes = 5;
|
||||
grid.face_nodes = Irreg2d::face_nodes;
|
||||
grid.face_nodepos = Irreg2d::face_nodepos;
|
||||
grid.face_cells = Irreg2d::face_cells;
|
||||
grid.cell_faces = Irreg2d::cell_faces;
|
||||
grid.cell_facepos = Irreg2d::cell_facepos;
|
||||
grid.node_coordinates = Irreg2d::node_coordinates;
|
||||
grid.face_centroids = Irreg2d::face_centroids;
|
||||
grid.face_areas = Irreg2d::face_areas;
|
||||
grid.face_normals = Irreg2d::face_normals;
|
||||
grid.cell_centroids = Irreg2d::cell_centroids;
|
||||
grid.cell_volumes = Irreg2d::cell_volumes;
|
||||
return grid;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
template <class VelInterp>
|
||||
void testConstantVelReproIrreg2d()
|
||||
{
|
||||
// Set up a 2d 1-cell non-cartesian case (a pyramid).
|
||||
UnstructuredGrid grid = makeIrreg2d();
|
||||
std::vector<double> v(2);
|
||||
v[0] = 0.12345;
|
||||
v[1] = -0.6789;
|
||||
std::vector<double> flux;
|
||||
computeFlux(grid, v, flux);
|
||||
VelInterp vic(grid);
|
||||
vic.setupFluxes(&flux[0]);
|
||||
|
||||
// Test a few points
|
||||
std::vector<double> x(2);
|
||||
x[0] = 1.2345;
|
||||
x[1] = 2.0123;
|
||||
std::vector<double> v_interp(2);
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
x[0] = 0.0;
|
||||
x[1] = 0.0;
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
x[0] = 1.0;
|
||||
x[1] = 3.0;
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
x[0] = 3.0;
|
||||
x[1] = 1.0;
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
// Data for an irregular 3d prism.
|
||||
namespace IrregPrism
|
||||
{
|
||||
static int face_nodes[] = { 0, 4, 2, 1, 3, 5, 0, 1, 5, 4, 2, 4, 5, 3, 2, 3, 0, 1};
|
||||
static int face_nodepos[] = { 0, 3, 6, 10, 14, 18 };
|
||||
static int face_cells[] = { 0, -1, 0, -1, 0, -1, 0, -1, 0, -1 };
|
||||
static int cell_faces[] = { 0, 1, 2, 3, 4 };
|
||||
static int cell_facepos[] = { 0, 5 };
|
||||
static double node_coordinates[] = { 0, 0, 0,
|
||||
2, 0, 0,
|
||||
0, 1, 0,
|
||||
2, 1, 0,
|
||||
0, 0, 1,
|
||||
1, 0, 1 };
|
||||
static double face_centroids[] = { 0, 1.0/3.0, 1.0/3.0,
|
||||
5.0/3.0, 1.0/3.0, 1.0/3.0,
|
||||
7.0/9.0, 0, 4.0/9.0,
|
||||
7.0/9.0, 5.0/9.0, 4.0/9.0,
|
||||
1, 0.5, 0 };
|
||||
static double face_areas[] = { 0.500000000000000,
|
||||
0.707106781186548,
|
||||
1.500000000000000,
|
||||
2.121320343559642,
|
||||
2.000000000000000 };
|
||||
static double face_normals[] = { -0.500000000000000, 0, 0,
|
||||
0.500000000000000, 0.000000000000000, 0.500000000000000,
|
||||
0, -1.500000000000000, 0,
|
||||
0, 1.500000000000000, 1.500000000000000,
|
||||
0, 0, -2.000000000000000 };
|
||||
static double cell_centroids[] = { 0.85, 0.35, 0.3 };
|
||||
static double cell_volumes[] = { 5.0/6.0 };
|
||||
} // namespace IrregPrism
|
||||
|
||||
UnstructuredGrid makeIrregPrism()
|
||||
{
|
||||
// Make a 3d 1-cell grid, where the single cell is a prism.
|
||||
UnstructuredGrid grid;
|
||||
grid.dimensions = 3;
|
||||
grid.number_of_cells = 1;
|
||||
grid.number_of_faces = 5;
|
||||
grid.number_of_nodes = 6;
|
||||
grid.face_nodes = IrregPrism::face_nodes;
|
||||
grid.face_nodepos = IrregPrism::face_nodepos;
|
||||
grid.face_cells = IrregPrism::face_cells;
|
||||
grid.cell_faces = IrregPrism::cell_faces;
|
||||
grid.cell_facepos = IrregPrism::cell_facepos;
|
||||
grid.node_coordinates = IrregPrism::node_coordinates;
|
||||
grid.face_centroids = IrregPrism::face_centroids;
|
||||
grid.face_areas = IrregPrism::face_areas;
|
||||
grid.face_normals = IrregPrism::face_normals;
|
||||
grid.cell_centroids = IrregPrism::cell_centroids;
|
||||
grid.cell_volumes = IrregPrism::cell_volumes;
|
||||
return grid;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
template <class VelInterp>
|
||||
void testConstantVelReproIrregPrism()
|
||||
{
|
||||
// Set up a 3d 1-cell non-cartesian case (a pyramid).
|
||||
UnstructuredGrid grid = makeIrregPrism();
|
||||
std::vector<double> v(3);
|
||||
v[0] = 0.12345;
|
||||
v[1] = -0.6789;
|
||||
v[2] = 0.3456;
|
||||
std::vector<double> flux;
|
||||
computeFlux(grid, v, flux);
|
||||
VelInterp vic(grid);
|
||||
vic.setupFluxes(&flux[0]);
|
||||
|
||||
// Test a few points
|
||||
std::vector<double> x(3);
|
||||
x[0] = 0.123;
|
||||
x[1] = 0.0123;
|
||||
x[2] = 0.213;
|
||||
std::vector<double> v_interp(3);
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
x[0] = 0.0;
|
||||
x[1] = 0.0;
|
||||
x[2] = 1.0;
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
x[0] = 1.0;
|
||||
x[1] = 0.0;
|
||||
x[2] = 1.0;
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
x[0] = 0.5;
|
||||
x[1] = 0.5;
|
||||
x[2] = 0.5;
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
}
|
||||
|
||||
template <class VelInterp>
|
||||
void testLinearVelReproIrregPrism()
|
||||
{
|
||||
// Set up a 3d 1-cell non-cartesian case (a pyramid).
|
||||
UnstructuredGrid grid = makeIrregPrism();
|
||||
std::vector<double> v0(3);
|
||||
// v0[0] = 0.12345;
|
||||
// v0[1] = -0.6789;
|
||||
// v0[2] = 0.423;
|
||||
v0[0] = 0.0;
|
||||
v0[1] = 0.0;
|
||||
v0[2] = 0.0;
|
||||
std::vector<double> v1(3);
|
||||
// v1[0] = -0.1;
|
||||
// v1[1] = 0.454;
|
||||
// v1[2] = 0.21;
|
||||
v1[0] = 0.0;
|
||||
v1[1] = 0.0;
|
||||
v1[2] = 1.0;
|
||||
std::vector<double> flux;
|
||||
computeFluxLinear(grid, v0, v1, flux);
|
||||
VelInterp vic(grid);
|
||||
vic.setupFluxes(&flux[0]);
|
||||
|
||||
// Test a few points
|
||||
std::vector<double> v(3);
|
||||
std::vector<double> x(3);
|
||||
x[0] = 0.123;
|
||||
x[1] = 0.0123;
|
||||
x[2] = 0.213;
|
||||
computeLinearVec(v0, v1, x, v);
|
||||
std::vector<double> v_interp(3);
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
x[0] = 0.0;
|
||||
x[1] = 0.0;
|
||||
x[2] = 1.0;
|
||||
computeLinearVec(v0, v1, x, v);
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
x[0] = 1.0;
|
||||
x[1] = 0.0;
|
||||
x[2] = 1.0;
|
||||
computeLinearVec(v0, v1, x, v);
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
x[0] = 0.5;
|
||||
x[1] = 0.5;
|
||||
x[2] = 0.5;
|
||||
computeLinearVec(v0, v1, x, v);
|
||||
vic.interpolate(0, &x[0], &v_interp[0]);
|
||||
BOOST_CHECK(vectorDiff2(v, v_interp) < 1e-12);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_VelocityInterpolationConstant)
|
||||
{
|
||||
testConstantVelRepro2d<VelocityInterpolationConstant>();
|
||||
testConstantVelReproPyramid<VelocityInterpolationConstant>();
|
||||
testConstantVelReproIrreg2d<VelocityInterpolationConstant>();
|
||||
testConstantVelReproIrregPrism<VelocityInterpolationConstant>();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_VelocityInterpolationECVI)
|
||||
{
|
||||
testConstantVelRepro2d<VelocityInterpolationECVI>();
|
||||
BOOST_CHECK_THROW(testConstantVelReproPyramid<VelocityInterpolationECVI>(), std::exception);
|
||||
testConstantVelReproIrreg2d<VelocityInterpolationECVI>();
|
||||
testConstantVelReproIrregPrism<VelocityInterpolationECVI>();
|
||||
// Though the interpolation has linear precision, the corner velocity
|
||||
// construction does not, so the below test cannot be expected to succeed.
|
||||
// testLinearVelReproIrregPrism<VelocityInterpolationECVI>();
|
||||
}
|
||||
|
||||
|
363
tests/test_wachspresscoord.cpp
Normal file
363
tests/test_wachspresscoord.cpp
Normal file
@ -0,0 +1,363 @@
|
||||
/*
|
||||
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 <config.h>
|
||||
|
||||
#if HAVE_DYNAMIC_BOOST_TEST
|
||||
#define BOOST_TEST_DYN_LINK
|
||||
#endif
|
||||
#define NVERBOSE // to suppress our messages when throwing
|
||||
|
||||
#define BOOST_TEST_MODULE WachspressCoordTest
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <opm/core/utility/WachspressCoord.hpp>
|
||||
#include <opm/core/GridManager.hpp>
|
||||
#include <opm/core/grid.h>
|
||||
#include <cmath>
|
||||
|
||||
using namespace Opm;
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
class Interpolator
|
||||
{
|
||||
public:
|
||||
explicit Interpolator(const UnstructuredGrid& grid)
|
||||
: bcmethod_(grid), grid_(grid)
|
||||
{
|
||||
}
|
||||
template <class Func>
|
||||
double interpolate(const Func& f,
|
||||
const int cell,
|
||||
const std::vector<double>& x) const
|
||||
{
|
||||
const int ncor = bcmethod_.numCorners(cell);
|
||||
bary_coord_.resize(ncor);
|
||||
bcmethod_.cartToBary(cell, &x[0], &bary_coord_[0]);
|
||||
double val = 0.0;
|
||||
for (int cor = 0; cor < ncor; ++cor) {
|
||||
const int vertex = bcmethod_.cornerInfo()[cell][cor].vertex;
|
||||
const double vval = f(grid_.node_coordinates + grid_.dimensions*vertex);
|
||||
val += vval*bary_coord_[cor];
|
||||
}
|
||||
return val;
|
||||
}
|
||||
private:
|
||||
WachspressCoord bcmethod_;
|
||||
const UnstructuredGrid grid_;
|
||||
mutable std::vector<double> bary_coord_;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
struct LinearFunc
|
||||
{
|
||||
double operator()(const double* x) const
|
||||
{
|
||||
return 1.0*x[0] + 2.0*x[1] + 3.0;
|
||||
}
|
||||
};
|
||||
|
||||
static void test2dCart()
|
||||
{
|
||||
// Set up 2d 1-cell cartesian case.
|
||||
GridManager g(1, 1);
|
||||
const UnstructuredGrid& grid = *g.c_grid();
|
||||
Interpolator interp(grid);
|
||||
LinearFunc f;
|
||||
|
||||
// Test a few points
|
||||
std::vector<double> x(2);
|
||||
x[0] = 0.23456;
|
||||
x[1] = 0.87654;
|
||||
double val = interp.interpolate(f, 0, x);
|
||||
BOOST_CHECK(std::fabs(val - f(&x[0])) < 1e-12);
|
||||
x[0] = 0.5;
|
||||
x[1] = 0.5;
|
||||
val = interp.interpolate(f, 0, x);
|
||||
BOOST_CHECK(std::fabs(val - f(&x[0])) < 1e-12);
|
||||
x[0] = 1.0;
|
||||
x[1] = 0.5;
|
||||
val = interp.interpolate(f, 0, x);
|
||||
BOOST_CHECK(std::fabs(val - f(&x[0])) < 1e-12);
|
||||
x[0] = 1.0;
|
||||
x[1] = 1.0;
|
||||
val = interp.interpolate(f, 0, x);
|
||||
BOOST_CHECK(std::fabs(val - f(&x[0])) < 1e-12);
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Data for a pyramid. Node numbering goes
|
||||
// lexicographic on bottom, then top.
|
||||
// Face numbering goes xmin, xmax, ymin, ymax, bottom.
|
||||
namespace Pyramid
|
||||
{
|
||||
static int face_nodes[] = { 0, 4, 2, 3, 4, 1, 0, 1, 4, 4, 3, 2, 0, 2, 3, 1, };
|
||||
static int face_nodepos[] = { 0, 3, 6, 9, 12, 16 };
|
||||
static int face_cells[] = { 0, -1, 0, -1, 0, -1, 0, -1, 0, -1 };
|
||||
static int cell_faces[] = { 0, 1, 2, 3, 4 };
|
||||
static int cell_facepos[] = { 0, 5 };
|
||||
static double node_coordinates[] = { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0 };
|
||||
static double face_centroids[] = { 0, 1.0/3.0, 1.0/3.0,
|
||||
2.0/3.0, 1.0/3.0, 1.0/3.0,
|
||||
1.0/3.0, 0, 1.0/3.0,
|
||||
1.0/3.0, 2.0/3.0, 1.0/3.0,
|
||||
0.5, 0.5, 0 };
|
||||
static double face_areas[] = { 0.5, std::sqrt(2.0), 0.5, std::sqrt(2.0), 1.0 };
|
||||
static double face_normals[] = { -0.5000, 0, 0,
|
||||
0.5000, 0, 0.5000,
|
||||
0, -0.5000, 0,
|
||||
0, 0.5000, 0.5000,
|
||||
0, 0, -1.0000 };
|
||||
static double cell_centroids[] = { 0.375, 0.375, 0.25 };
|
||||
static double cell_volumes[] = { 1.0/3.0 };
|
||||
|
||||
} // namespace Pyramid
|
||||
|
||||
UnstructuredGrid makePyramid()
|
||||
{
|
||||
// Make a 3d 1-cell grid, where the single cell is a pyramid.
|
||||
UnstructuredGrid grid;
|
||||
grid.dimensions = 3;
|
||||
grid.number_of_cells = 1;
|
||||
grid.number_of_faces = 5;
|
||||
grid.number_of_nodes = 5;
|
||||
grid.face_nodes = Pyramid::face_nodes;
|
||||
grid.face_nodepos = Pyramid::face_nodepos;
|
||||
grid.face_cells = Pyramid::face_cells;
|
||||
grid.cell_faces = Pyramid::cell_faces;
|
||||
grid.cell_facepos = Pyramid::cell_facepos;
|
||||
grid.node_coordinates = Pyramid::node_coordinates;
|
||||
grid.face_centroids = Pyramid::face_centroids;
|
||||
grid.face_areas = Pyramid::face_areas;
|
||||
grid.face_normals = Pyramid::face_normals;
|
||||
grid.cell_centroids = Pyramid::cell_centroids;
|
||||
grid.cell_volumes = Pyramid::cell_volumes;
|
||||
return grid;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
static void testPyramid()
|
||||
{
|
||||
// Set up a 3d 1-cell non-cartesian case (a pyramid).
|
||||
UnstructuredGrid grid = makePyramid();
|
||||
Interpolator interp(grid);
|
||||
LinearFunc f;
|
||||
|
||||
// Test a few points
|
||||
std::vector<double> x(3);
|
||||
x[0] = 0.123;
|
||||
x[1] = 0.0123;
|
||||
x[2] = 0.213;
|
||||
double val = interp.interpolate(f, 0, x);
|
||||
BOOST_CHECK(std::fabs(val - f(&x[0])) < 1e-12);
|
||||
x[0] = 0.0;
|
||||
x[1] = 0.0;
|
||||
x[2] = 1.0;
|
||||
val = interp.interpolate(f, 0, x);
|
||||
BOOST_CHECK(std::fabs(val - f(&x[0])) < 1e-12);
|
||||
x[0] = 0.5;
|
||||
x[1] = 0.5;
|
||||
x[2] = 0.0;
|
||||
val = interp.interpolate(f, 0, x);
|
||||
BOOST_CHECK(std::fabs(val - f(&x[0])) < 1e-12);
|
||||
x[0] = 0.5;
|
||||
x[1] = 0.5;
|
||||
x[2] = 0.1;
|
||||
val = interp.interpolate(f, 0, x);
|
||||
BOOST_CHECK(std::fabs(val - f(&x[0])) < 1e-12);
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
// Data for an irregular 2d polygon.
|
||||
namespace Irreg2d
|
||||
{
|
||||
static int face_nodes[] = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 0 };
|
||||
static int face_nodepos[] = { 0, 2, 4, 6, 8, 10 };
|
||||
static int face_cells[] = { 0, -1, 0, -1, 0, -1, 0, -1, 0, -1 };
|
||||
static int cell_faces[] = { 0, 1, 2, 3, 4 };
|
||||
static int cell_facepos[] = { 0, 5 };
|
||||
static double node_coordinates[] = { 0, 0, 3, 0, 3, 2, 1, 3, 0, 2 };
|
||||
static double face_centroids[] = { 1.5, 0, 3, 1, 2, 2.5, 0.5, 2.5, 0, 1 };
|
||||
static double face_areas[] = { 3, 2, std::sqrt(5.0), std::sqrt(2.0), 2 };
|
||||
static double face_normals[] = { 0, -3, 2, 0, 1, 2, -1, 1, -2, 0 };
|
||||
static double cell_centroids[] = { 22.0/15.0, 19.0/15.0 };
|
||||
static double cell_volumes[] = { 7.5 };
|
||||
} // namespace Irreg2d
|
||||
|
||||
UnstructuredGrid makeIrreg2d()
|
||||
{
|
||||
// Make a 2d 1-cell grid, where the single cell is a polygon.
|
||||
UnstructuredGrid grid;
|
||||
grid.dimensions = 2;
|
||||
grid.number_of_cells = 1;
|
||||
grid.number_of_faces = 5;
|
||||
grid.number_of_nodes = 5;
|
||||
grid.face_nodes = Irreg2d::face_nodes;
|
||||
grid.face_nodepos = Irreg2d::face_nodepos;
|
||||
grid.face_cells = Irreg2d::face_cells;
|
||||
grid.cell_faces = Irreg2d::cell_faces;
|
||||
grid.cell_facepos = Irreg2d::cell_facepos;
|
||||
grid.node_coordinates = Irreg2d::node_coordinates;
|
||||
grid.face_centroids = Irreg2d::face_centroids;
|
||||
grid.face_areas = Irreg2d::face_areas;
|
||||
grid.face_normals = Irreg2d::face_normals;
|
||||
grid.cell_centroids = Irreg2d::cell_centroids;
|
||||
grid.cell_volumes = Irreg2d::cell_volumes;
|
||||
return grid;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
static void testIrreg2d()
|
||||
{
|
||||
// Set up a 2d 1-cell where the single cell is a polygon.
|
||||
UnstructuredGrid grid = makeIrreg2d();
|
||||
Interpolator interp(grid);
|
||||
LinearFunc f;
|
||||
|
||||
// Test a few points
|
||||
std::vector<double> x(2);
|
||||
x[0] = 1.2345;
|
||||
x[1] = 2.0123;
|
||||
double val = interp.interpolate(f, 0, x);
|
||||
BOOST_CHECK(std::fabs(val - f(&x[0])) < 1e-12);
|
||||
x[0] = 0.0;
|
||||
x[1] = 0.0;
|
||||
val = interp.interpolate(f, 0, x);
|
||||
BOOST_CHECK(std::fabs(val - f(&x[0])) < 1e-12);
|
||||
x[0] = 1.0;
|
||||
x[1] = 3.0;
|
||||
val = interp.interpolate(f, 0, x);
|
||||
BOOST_CHECK(std::fabs(val - f(&x[0])) < 1e-12);
|
||||
x[0] = 3.0;
|
||||
x[1] = 1.0;
|
||||
val = interp.interpolate(f, 0, x);
|
||||
BOOST_CHECK(std::fabs(val - f(&x[0])) < 1e-12);
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
// Data for an irregular 3d prism.
|
||||
namespace IrregPrism
|
||||
{
|
||||
static int face_nodes[] = { 0, 4, 2, 1, 3, 5, 0, 1, 5, 4, 2, 4, 5, 3, 2, 3, 0, 1};
|
||||
static int face_nodepos[] = { 0, 3, 6, 10, 14, 18 };
|
||||
static int face_cells[] = { 0, -1, 0, -1, 0, -1, 0, -1, 0, -1 };
|
||||
static int cell_faces[] = { 0, 1, 2, 3, 4 };
|
||||
static int cell_facepos[] = { 0, 5 };
|
||||
static double node_coordinates[] = { 0, 0, 0,
|
||||
2, 0, 0,
|
||||
0, 1, 0,
|
||||
2, 1, 0,
|
||||
0, 0, 1,
|
||||
1, 0, 1 };
|
||||
static double face_centroids[] = { 0, 1.0/3.0, 1.0/3.0,
|
||||
5.0/3.0, 1.0/3.0, 1.0/3.0,
|
||||
7.0/9.0, 0, 4.0/9.0,
|
||||
7.0/9.0, 5.0/9.0, 4.0/9.0,
|
||||
1, 0.5, 0 };
|
||||
static double face_areas[] = { 0.500000000000000,
|
||||
0.707106781186548,
|
||||
1.500000000000000,
|
||||
2.121320343559642,
|
||||
2.000000000000000 };
|
||||
static double face_normals[] = { -0.500000000000000, 0, 0,
|
||||
0.500000000000000, 0.000000000000000, 0.500000000000000,
|
||||
0, -1.500000000000000, 0,
|
||||
0, 1.500000000000000, 1.500000000000000,
|
||||
0, 0, -2.000000000000000 };
|
||||
static double cell_centroids[] = { 0.85, 0.35, 0.3 };
|
||||
static double cell_volumes[] = { 5.0/6.0 };
|
||||
} // namespace IrregPrism
|
||||
|
||||
UnstructuredGrid makeIrregPrism()
|
||||
{
|
||||
// Make a 3d 1-cell grid, where the single cell is a prism.
|
||||
UnstructuredGrid grid;
|
||||
grid.dimensions = 3;
|
||||
grid.number_of_cells = 1;
|
||||
grid.number_of_faces = 5;
|
||||
grid.number_of_nodes = 6;
|
||||
grid.face_nodes = IrregPrism::face_nodes;
|
||||
grid.face_nodepos = IrregPrism::face_nodepos;
|
||||
grid.face_cells = IrregPrism::face_cells;
|
||||
grid.cell_faces = IrregPrism::cell_faces;
|
||||
grid.cell_facepos = IrregPrism::cell_facepos;
|
||||
grid.node_coordinates = IrregPrism::node_coordinates;
|
||||
grid.face_centroids = IrregPrism::face_centroids;
|
||||
grid.face_areas = IrregPrism::face_areas;
|
||||
grid.face_normals = IrregPrism::face_normals;
|
||||
grid.cell_centroids = IrregPrism::cell_centroids;
|
||||
grid.cell_volumes = IrregPrism::cell_volumes;
|
||||
return grid;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
static void testIrregPrism()
|
||||
{
|
||||
// Set up a 3d 1-cell non-cartesian case (a prism).
|
||||
UnstructuredGrid grid = makeIrregPrism();
|
||||
Interpolator interp(grid);
|
||||
LinearFunc f;
|
||||
|
||||
// Test a few points
|
||||
std::vector<double> x(3);
|
||||
x[0] = 0.123;
|
||||
x[1] = 0.0123;
|
||||
x[2] = 0.213;
|
||||
double val = interp.interpolate(f, 0, x);
|
||||
BOOST_CHECK(std::fabs(val - f(&x[0])) < 1e-12);
|
||||
x[0] = 0.0;
|
||||
x[1] = 0.0;
|
||||
x[2] = 1.0;
|
||||
val = interp.interpolate(f, 0, x);
|
||||
BOOST_CHECK(std::fabs(val - f(&x[0])) < 1e-12);
|
||||
x[0] = 1.0;
|
||||
x[1] = 0.0;
|
||||
x[2] = 1.0;
|
||||
val = interp.interpolate(f, 0, x);
|
||||
BOOST_CHECK(std::fabs(val - f(&x[0])) < 1e-12);
|
||||
x[0] = 0.5;
|
||||
x[1] = 0.5;
|
||||
x[2] = 0.5;
|
||||
val = interp.interpolate(f, 0, x);
|
||||
BOOST_CHECK(std::fabs(val - f(&x[0])) < 1e-12);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_WachspressCoord)
|
||||
{
|
||||
test2dCart();
|
||||
BOOST_CHECK_THROW(testPyramid(), std::exception);
|
||||
testIrreg2d();
|
||||
testIrregPrism();
|
||||
}
|
205
tests/test_wells.cpp
Normal file
205
tests/test_wells.cpp
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
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 <config.h>
|
||||
|
||||
#if HAVE_DYNAMIC_BOOST_TEST
|
||||
#define BOOST_TEST_DYN_LINK
|
||||
#endif
|
||||
|
||||
#define NVERBOSE // Suppress own messages when throw()ing
|
||||
|
||||
#define BOOST_TEST_MODULE WellsModuleTest
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <opm/core/newwells.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Construction)
|
||||
{
|
||||
const int nphases = 2;
|
||||
const int nwells = 2;
|
||||
const int nperfs = 2;
|
||||
|
||||
boost::shared_ptr<Wells> W(create_wells(nphases, nwells, nperfs),
|
||||
destroy_wells);
|
||||
|
||||
if (W) {
|
||||
int cells[] = { 0, 9 };
|
||||
double WI = 1.0;
|
||||
const double ifrac[] = { 1.0, 0.0 };
|
||||
|
||||
const bool ok0 = add_well(INJECTOR, 0.0, 1, &ifrac[0], &cells[0],
|
||||
&WI, "INJECTOR", W.get());
|
||||
|
||||
const double pfrac[] = { 0.0, 0.0 };
|
||||
const bool ok1 = add_well(PRODUCER, 0.0, 1, &pfrac[0], &cells[1],
|
||||
&WI, "PRODUCER", W.get());
|
||||
|
||||
if (ok0 && ok1) {
|
||||
BOOST_CHECK_EQUAL(W->number_of_phases, nphases);
|
||||
BOOST_CHECK_EQUAL(W->number_of_wells , nwells );
|
||||
|
||||
BOOST_CHECK_EQUAL(W->well_connpos[0], 0);
|
||||
BOOST_CHECK_EQUAL(W->well_connpos[1], 1);
|
||||
BOOST_CHECK_EQUAL(W->well_connpos[W->number_of_wells], nperfs);
|
||||
|
||||
BOOST_CHECK_EQUAL(W->well_cells[W->well_connpos[0]], cells[0]);
|
||||
BOOST_CHECK_EQUAL(W->well_cells[W->well_connpos[1]], cells[1]);
|
||||
|
||||
BOOST_CHECK_EQUAL(W->WI[W->well_connpos[0]], WI);
|
||||
BOOST_CHECK_EQUAL(W->WI[W->well_connpos[1]], WI);
|
||||
|
||||
using std::string;
|
||||
BOOST_CHECK_EQUAL(string(W->name[0]), string("INJECTOR"));
|
||||
BOOST_CHECK_EQUAL(string(W->name[1]), string("PRODUCER"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Controls)
|
||||
{
|
||||
const int nphases = 2;
|
||||
const int nwells = 1;
|
||||
const int nperfs = 2;
|
||||
|
||||
boost::shared_ptr<Wells> W(create_wells(nphases, nwells, nperfs),
|
||||
destroy_wells);
|
||||
|
||||
if (W) {
|
||||
int cells[] = { 0 , 9 };
|
||||
double WI [] = { 1.0, 1.0 };
|
||||
const double ifrac[] = { 1.0, 0.0 };
|
||||
|
||||
const bool ok = add_well(INJECTOR, 0.0, nperfs, &ifrac[0], &cells[0],
|
||||
&WI[0], "INJECTOR", W.get());
|
||||
|
||||
if (ok) {
|
||||
const double distr[] = { 1.0, 0.0 };
|
||||
const bool ok1 = append_well_controls(BHP, 1, &distr[0],
|
||||
0, W.get());
|
||||
const bool ok2 = append_well_controls(SURFACE_RATE, 1,
|
||||
&distr[0], 0, W.get());
|
||||
|
||||
if (ok1 && ok2) {
|
||||
WellControls* ctrls = W->ctrls[0];
|
||||
|
||||
BOOST_CHECK_EQUAL(ctrls->num , 2);
|
||||
BOOST_CHECK_EQUAL(ctrls->current, -1);
|
||||
|
||||
set_current_control(0, 0, W.get());
|
||||
BOOST_CHECK_EQUAL(ctrls->current, 0);
|
||||
|
||||
set_current_control(0, 1, W.get());
|
||||
BOOST_CHECK_EQUAL(ctrls->current, 1);
|
||||
|
||||
BOOST_CHECK_EQUAL(ctrls->type[0], BHP);
|
||||
BOOST_CHECK_EQUAL(ctrls->type[1], SURFACE_RATE);
|
||||
|
||||
BOOST_CHECK_EQUAL(ctrls->target[0], 1.0);
|
||||
BOOST_CHECK_EQUAL(ctrls->target[1], 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Copy)
|
||||
{
|
||||
const int nphases = 2;
|
||||
const int nwells = 2;
|
||||
const int nperfs = 2;
|
||||
|
||||
boost::shared_ptr<Wells> W1(create_wells(nphases, nwells, nperfs),
|
||||
destroy_wells);
|
||||
boost::shared_ptr<Wells> W2;
|
||||
|
||||
if (W1) {
|
||||
int cells[] = { 0, 9 };
|
||||
const double WI = 1.0;
|
||||
const double ifrac[] = { 1.0, 0.0 };
|
||||
|
||||
const bool ok0 = add_well(INJECTOR, 0.0, 1, &ifrac[0], &cells[0],
|
||||
&WI, "INJECTOR", W1.get());
|
||||
|
||||
const double pfrac[] = { 0.0, 0.0 };
|
||||
const bool ok1 = add_well(PRODUCER, 0.0, 1, &pfrac[0], &cells[1],
|
||||
&WI, "PRODUCER", W1.get());
|
||||
|
||||
bool ok = ok0 && ok1;
|
||||
for (int w = 0; ok && (w < W1->number_of_wells); ++w) {
|
||||
const double distr[] = { 1.0, 0.0 };
|
||||
const bool okc1 = append_well_controls(BHP, 1, &distr[0],
|
||||
w, W1.get());
|
||||
const bool okc2 = append_well_controls(SURFACE_RATE, 1,
|
||||
&distr[0], w,
|
||||
W1.get());
|
||||
|
||||
ok = okc1 && okc2;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
W2.reset(clone_wells(W1.get()), destroy_wells);
|
||||
}
|
||||
}
|
||||
|
||||
if (W2) {
|
||||
BOOST_CHECK_EQUAL(W2->number_of_phases, W1->number_of_phases);
|
||||
BOOST_CHECK_EQUAL(W2->number_of_wells , W1->number_of_wells );
|
||||
BOOST_CHECK_EQUAL(W2->well_connpos[0] , W1->well_connpos[0] );
|
||||
|
||||
for (int w = 0; w < W1->number_of_wells; ++w) {
|
||||
using std::string;
|
||||
BOOST_CHECK_EQUAL(string(W2->name[w]), string(W1->name[w]));
|
||||
BOOST_CHECK_EQUAL( W2->type[w] , W1->type[w] );
|
||||
|
||||
BOOST_CHECK_EQUAL(W2->well_connpos[w + 1],
|
||||
W1->well_connpos[w + 1]);
|
||||
|
||||
for (int j = W1->well_connpos[w];
|
||||
j < W1->well_connpos[w + 1]; ++j) {
|
||||
BOOST_CHECK_EQUAL(W2->well_cells[j], W1->well_cells[j]);
|
||||
BOOST_CHECK_EQUAL(W2->WI [j], W1->WI [j]);
|
||||
}
|
||||
|
||||
BOOST_CHECK(W1->ctrls[w] != 0);
|
||||
BOOST_CHECK(W2->ctrls[w] != 0);
|
||||
|
||||
WellControls* c1 = W1->ctrls[w];
|
||||
WellControls* c2 = W2->ctrls[w];
|
||||
|
||||
BOOST_CHECK_EQUAL(c2->num , c1->num );
|
||||
BOOST_CHECK_EQUAL(c2->current, c1->current);
|
||||
|
||||
for (int c = 0; c < c1->num; ++c) {
|
||||
BOOST_CHECK_EQUAL(c2->type [c], c1->type [c]);
|
||||
BOOST_CHECK_EQUAL(c2->target[c], c1->target[c]);
|
||||
|
||||
for (int p = 0; p < W1->number_of_phases; ++p) {
|
||||
BOOST_CHECK_EQUAL(c2->distr[c*W1->number_of_phases + p],
|
||||
c1->distr[c*W1->number_of_phases + p]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user