From 4ea3e7ed537100c9a007e3224fc3826dcafd0b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Tue, 14 Jan 2014 20:37:28 +0100 Subject: [PATCH 01/81] Add basic equilibration facility This commit adds a simple facility for calculating initial phase pressures assuming stationary conditions, a known reference pressure in the oil zone as well as the depth and capillary pressures at the water-oil and gas-oil contacts. Function 'Opm::equil::phasePressures()' uses a simple ODE/IVP-based approach, solved using the traditional RK4 method with constant step sizes, to derive the required pressure values. Specifically, we solve the ODE dp/dz = rho(z,p) * g with 'z' represening depth, 'p' being a phase pressure and 'rho' the associate phase density. Finally, 'g' is the acceleration of gravity. We assume that we can calculate phase densities, e.g., from table look-up. This assumption holds in the case of an ECLIPSE input deck. Using RK4 with constant step sizes is a limitation of this implementation. This, basically, assumes that the phase densities varies only smoothly with depth and pressure (at reservoir conditions). --- CMakeLists_files.cmake | 3 + opm/core/simulator/initStateEquil.hpp | 173 +++++++ opm/core/simulator/initStateEquil_impl.hpp | 513 +++++++++++++++++++++ tests/test_equil.cpp | 89 ++++ 4 files changed, 778 insertions(+) create mode 100644 opm/core/simulator/initStateEquil.hpp create mode 100644 opm/core/simulator/initStateEquil_impl.hpp create mode 100644 tests/test_equil.cpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index a4cd51ce..b9cf606c 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -169,6 +169,7 @@ list (APPEND TEST_SOURCE_FILES tests/test_blackoilstate.cpp tests/test_wellsmanager.cpp tests/test_wellcontrols.cpp + tests/test_equil.cpp ) # originally generated with the command: @@ -322,6 +323,8 @@ list (APPEND PUBLIC_HEADER_FILES opm/core/simulator/WellState.hpp opm/core/simulator/initState.hpp opm/core/simulator/initState_impl.hpp + opm/core/simulator/initStateEquil.hpp + opm/core/simulator/initStateEquil_impl.hpp opm/core/tof/DGBasis.hpp opm/core/tof/TofReorder.hpp opm/core/tof/TofDiscGalReorder.hpp diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp new file mode 100644 index 00000000..61f7debc --- /dev/null +++ b/opm/core/simulator/initStateEquil.hpp @@ -0,0 +1,173 @@ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#ifndef OPM_INITSTATEEQUIL_HEADER_INCLUDED +#define OPM_INITSTATEEQUIL_HEADER_INCLUDED + +#include +#include +#include +#include + +#include +#include +#include + +struct UnstructuredGrid; + +namespace Opm +{ + namespace equil { + template + class DensityCalculator; + + template <> + class DensityCalculator< BlackoilPropertiesInterface > { + public: + DensityCalculator(const BlackoilPropertiesInterface& props, + const int c) + : props_(props) + , c_(1, c) + { + } + + std::vector + operator()(const double p, + const std::vector& z) const + { + const int np = props_.numPhases(); + std::vector A(np * np, 0); + + assert (z.size() == std::vector::size_type(np)); + + double* dAdp = 0; + props_.matrix(1, &p, &z[0], &c_[0], &A[0], dAdp); + + std::vector rho(np, 0.0); + props_.density(1, &A[0], &rho[0]); + + return rho; + } + + private: + const BlackoilPropertiesInterface& props_; + const std::vector c_; + }; + + namespace miscibility { + struct NoMixing { + double + operator()(const double /* depth */, + const double /* press */) const + { + return 0.0; + } + }; + + class RsVD { + public: + RsVD(const std::vector& depth, + const std::vector& rs) + : depth_(depth) + , rs_(rs) + { + } + + double + operator()(const double depth, + const double /* press */) const + { + return linearInterpolation(depth_, rs_, depth); + } + + private: + std::vector depth_; + std::vector rs_; + }; + } // namespace miscibility + + struct EquilRecord { + struct { + double depth; + double press; + } main, woc, goc; + }; + + template + class EquilReg { + public: + EquilReg(const EquilRecord& rec, + const DensCalc& density, + const RS& rs, + const RV& rv, + const PhaseUsage& pu) + : rec_ (rec) + , density_(density) + , rs_ (rs) + , rv_ (rv) + , pu_ (pu) + { + } + + typedef DensCalc CalcDensity; + typedef RS CalcDissolution; + typedef RV CalcEvaporation; + + double datum() const { return this->rec_.main.depth; } + double pressure() const { return this->rec_.main.press; } + + double zwoc() const { return this->rec_.woc .depth; } + double pcow_woc() const { return this->rec_.woc .press; } + + double zgoc() const { return this->rec_.goc .depth; } + double pcgo_goc() const { return this->rec_.goc .press; } + + const CalcDensity& + densityCalculator() const { return this->density_; } + + const CalcDissolution& + dissolutionCalculator() const { return this->rs_; } + + const CalcEvaporation& + evaporationCalculator() const { return this->rv_; } + + const PhaseUsage& + phaseUsage() const { return this->pu_; } + + private: + EquilRecord rec_; + DensCalc density_; + RS rs_; + RV rv_; + PhaseUsage pu_; + }; + + template + std::vector< std::vector > + phasePressures(const UnstructuredGrid& G, + const Region& reg, + const double grav = unit::gravity); + } // namespace equil +} // namespace Opm + +#include + +#endif // OPM_INITSTATEEQUIL_HEADER_INCLUDED diff --git a/opm/core/simulator/initStateEquil_impl.hpp b/opm/core/simulator/initStateEquil_impl.hpp new file mode 100644 index 00000000..cb19d020 --- /dev/null +++ b/opm/core/simulator/initStateEquil_impl.hpp @@ -0,0 +1,513 @@ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#ifndef OPM_INITSTATEEQUIL_IMPL_HEADER_INCLUDED +#define OPM_INITSTATEEQUIL_IMPL_HEADER_INCLUDED + +#include +#include + +#include +#include +#include +#include + +namespace Opm +{ + namespace Details { + template + class RK4IVP : public std::binary_function { + public: + RK4IVP(const RHS& f , + const std::array& span, + const double y0 , + const int N ) + : N_(N) + , span_(span) + { + const double h = stepsize(); + const double h2 = h / 2; + const double h6 = h / 6; + + y_.reserve(N + 1); + f_.reserve(N + 1); + + y_.push_back(y0); + f_.push_back(f(span_[0], y0)); + + for (int i = 0; i < N; ++i) { + const double x = span_[0] + i*h; + const double y = y_.back(); + + const double k1 = f_[i]; + const double k2 = f(x + h2, y + h2*k1); + const double k3 = f(x + h2, y + h2*k2); + const double k4 = f(x + h , y + h*k3); + + y_.push_back(y + h6*(k1 + 2*(k2 + k3) + k4)); + f_.push_back(f(x + h, y_.back())); + } + + assert (y_.size() == std::vector::size_type(N + 1)); + } + + double + operator()(const double x) const + { + // Dense output (O(h**3)) according to Shampine + // (Hermite interpolation) + const double h = stepsize(); + const int i = (x - span_[0]) / h; + const double t = (x - (span_[0] + i*h)) / h; + + const double y0 = y_[i], y1 = y_[i + 1]; + const double f0 = f_[i], f1 = f_[i + 1]; + + double u = (1 - 2*t) * (y1 - y0); + u += h * ((t - 1)*f0 + t*f1); + u *= t * (t - 1); + u += (1 - t)*y0 + t*y1; + + return u; + } + + private: + int N_; + std::array span_; + std::vector y_; + std::vector f_; + + double + stepsize() const { return (span_[1] - span_[0]) / N_; } + }; + + namespace PhasePressODE { + template + class Water { + public: + Water(const Density& rho, + const int np, + const int ix, + const double norm_grav) + : rho_(rho) + , svol_(np, 0) + , ix_(ix) + , g_(norm_grav) + { + svol_[ix_] = 1.0; + } + + double + operator()(const double /* depth */, + const double press) const + { + return this->density(press) * g_; + } + + private: + const Density& rho_; + std::vector svol_; + const int ix_; + const double g_; + + double + density(const double press) const + { + const std::vector& rho = rho_(press, svol_); + + return rho[ix_]; + } + }; + + template + class Oil { + public: + Oil(const Density& rho, + const RS& rs, + const int np, + const int oix, + const int gix, + const double norm_grav) + : rho_(rho) + , rs_(rs) + , svol_(np, 0) + , oix_(oix) + , gix_(gix) + , g_(norm_grav) + { + svol_[oix_] = 1.0; + } + + double + operator()(const double depth, + const double press) const + { + return this->density(depth, press) * g_; + } + + private: + const Density& rho_; + const RS& rs_; + mutable std::vector svol_; + const int oix_; + const int gix_; + const double g_; + + double + density(const double depth, + const double press) const + { + if (gix_ >= 0) { + svol_[gix_] = rs_(depth, press); + } + + const std::vector& rho = rho_(press, svol_); + return rho[oix_]; + } + }; + + template + class Gas { + public: + Gas(const Density& rho, + const RV& rv, + const int np, + const int gix, + const int oix, + const double norm_grav) + : rho_(rho) + , rv_(rv) + , svol_(np, 0) + , gix_(gix) + , oix_(oix) + , g_(norm_grav) + { + svol_[gix_] = 1.0; + } + + double + operator()(const double depth, + const double press) const + { + return this->density(depth, press) * g_; + } + + private: + const Density& rho_; + const RV& rv_; + mutable std::vector svol_; + const int gix_; + const int oix_; + const double g_; + + double + density(const double depth, + const double press) const + { + if (oix_ >= 0) { + svol_[oix_] = rv_(depth, press); + } + + const std::vector& rho = rho_(press, svol_); + return rho[gix_]; + } + }; + } // namespace PhasePressODE + + namespace PhaseUsed { + inline bool + water(const PhaseUsage& pu) + { + return bool(pu.phase_used[ Opm::BlackoilPhases::Aqua ]); + } + + inline bool + oil(const PhaseUsage& pu) + { + return bool(pu.phase_used[ Opm::BlackoilPhases::Liquid ]); + } + + inline bool + gas(const PhaseUsage& pu) + { + return bool(pu.phase_used[ Opm::BlackoilPhases::Vapour ]); + } + } // namespace PhaseUsed + + namespace PhaseIndex { + inline int + water(const PhaseUsage& pu) + { + int i = -1; + if (PhaseUsed::water(pu)) { + i = pu.phase_pos[ Opm::BlackoilPhases::Aqua ]; + } + + return i; + } + + inline int + oil(const PhaseUsage& pu) + { + int i = -1; + if (PhaseUsed::oil(pu)) { + i = pu.phase_pos[ Opm::BlackoilPhases::Liquid ]; + } + + return i; + } + + inline int + gas(const PhaseUsage& pu) + { + int i = -1; + if (PhaseUsed::gas(pu)) { + i = pu.phase_pos[ Opm::BlackoilPhases::Vapour ]; + } + + return i; + } + } // namespace PhaseIndex + + namespace PhasePressure { + template + void + assign(const UnstructuredGrid& G , + const std::array& f , + const double split, + std::vector& p ) + { + const int nd = G.dimensions, nc = G.number_of_cells; + const double* depth = & G.cell_centroids[0*nd + (nd - 1)]; + + enum { up = 0, down = 1 }; + + for (int c = 0; c < nc; ++c, depth += nd) { + const double z = *depth; + p[c] = (z < split) ? f[up](z) : f[down](z); + } + } + + template + void + water(const UnstructuredGrid& G , + const Region& reg , + const std::array& span , + const double grav , + const double po_woc, + std::vector& press ) + { + using PhasePressODE::Water; + typedef Water ODE; + + const PhaseUsage& pu = reg.phaseUsage(); + + const int wix = PhaseIndex::water(pu); + ODE drho(reg.densityCalculator(), pu.num_phases, wix, grav); + + const double z0 = reg.zwoc(); + const double p0 = po_woc - reg.pcow_woc(); // Pcow = Po - Pw + + std::array up = {{ z0, span[0] }}; + std::array down = {{ z0, span[1] }}; + + typedef Details::RK4IVP WPress; + std::array wpress = { + { + WPress(drho, up , p0, 100) + , + WPress(drho, down, p0, 100) + } + }; + + assign(G, wpress, z0, press); + } + + template + void + oil(const UnstructuredGrid& G , + const Region& reg , + const std::array& span , + const double grav , + std::vector& press , + double& po_woc, + double& po_goc) + { + using PhasePressODE::Oil; + typedef Oil ODE; + + const PhaseUsage& pu = reg.phaseUsage(); + + const int oix = PhaseIndex::oil(pu); + const int gix = PhaseIndex::gas(pu); + ODE drho(reg.densityCalculator(), + reg.dissolutionCalculator(), + pu.num_phases, oix, gix, grav); + + const double z0 = reg.datum(); + const double p0 = reg.pressure(); + + std::array up = {{ z0, span[0] }}; + std::array down = {{ z0, span[1] }}; + + typedef Details::RK4IVP OPress; + std::array opress = { + { + OPress(drho, up , p0, 100) + , + OPress(drho, down, p0, 100) + } + }; + + assign(G, opress, z0, press); + + po_woc = -std::numeric_limits::max(); + po_goc = -std::numeric_limits::max(); + + const double woc = reg.zwoc(); + // Compute Oil pressure at WOC + if (z0 > woc) { po_woc = opress[0](woc); } // WOC above datum + else if (z0 < woc) { po_woc = opress[1](woc); } // WOC below datum + else { po_woc = p0; } // WOC *at* datum + + const double goc = reg.zgoc(); + // Compute Oil pressure at GOC + if (z0 > goc) { po_goc = opress[0](goc); } // GOC above datum + else if (z0 < goc) { po_goc = opress[1](goc); } // GOC below datum + else { po_goc = p0; } // GOC *at* datum + } + + template + void + gas(const UnstructuredGrid& G , + const Region& reg , + const std::array& span , + const double grav , + const double po_goc, + std::vector& press ) + { + using PhasePressODE::Gas; + typedef Gas ODE; + + const PhaseUsage& pu = reg.phaseUsage(); + + const int gix = PhaseIndex::gas(pu); + const int oix = PhaseIndex::oil(pu); + + ODE drho(reg.densityCalculator(), + reg.evaporationCalculator(), + pu.num_phases, gix, oix, grav); + + const double z0 = reg.zgoc(); + const double p0 = po_goc + reg.pcgo_goc(); // Pcog = Pg - Po + + std::array up = {{ z0, span[0] }}; + std::array down = {{ z0, span[1] }}; + + typedef Details::RK4IVP GPress; + std::array gpress = { + { + GPress(drho, up , p0, 100) + , + GPress(drho, down, p0, 100) + } + }; + + assign(G, gpress, z0, press); + } + } // namespace PhasePressure + + template + void + equilibrateOWG(const UnstructuredGrid& G, + const Region& reg, + const double grav, + const std::array& span, + std::vector< std::vector >& press) + { + const PhaseUsage& pu = reg.phaseUsage(); + + double po_woc = -1, po_goc = -1; + if (PhaseUsed::oil(pu)) { + const int oix = PhaseIndex::oil(pu); + PhasePressure::oil(G, reg, span, grav, + press[ oix ], po_woc, po_goc); + } + + if (PhaseUsed::water(pu)) { + const int wix = PhaseIndex::water(pu); + PhasePressure::water(G, reg, span, grav, + po_woc, press[ wix ]); + } + + if (PhaseUsed::gas(pu)) { + const int gix = PhaseIndex::gas(pu); + PhasePressure::gas(G, reg, span, grav, + po_goc, press[ gix ]); + } + } + } // namespace Details + + namespace equil { + template + std::vector< std::vector > + phasePressures(const UnstructuredGrid& G, + const Region& reg, + const double grav) + { + std::array span = + {{ std::numeric_limits::max() , + -std::numeric_limits::max() }}; // Symm. about 0. + { + // This code is only supported in three space dimensions + assert (G.dimensions == 3); + + const int nd = G.dimensions; + const double* depth = & G.node_coordinates[0*nd + (nd - 1)]; + + for (int n = 0; n < G.number_of_nodes; ++n, depth += nd) { + const double z = *depth; + + if (z < span[0]) { span[0] = z; } + if (z > span[1]) { span[1] = z; } + } + } + + const int np = reg.phaseUsage().num_phases; + + typedef std::vector pval; + std::vector press(np, pval(G.number_of_cells, 0.0)); + + const double z0 = reg.datum(); + const double zwoc = reg.zwoc (); + const double zgoc = reg.zgoc (); + + if (! ((zgoc > z0) || (z0 > zwoc))) { + // Datum depth in oil zone (zgoc <= z0 <= zwoc) + Details::equilibrateOWG(G, reg, grav, span, press); + } + + return press; + } + } // namespace equil +} // namespace Opm + +#endif // OPM_INITSTATEEQUIL_IMPL_HEADER_INCLUDED diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp new file mode 100644 index 00000000..4d7cbe83 --- /dev/null +++ b/tests/test_equil.cpp @@ -0,0 +1,89 @@ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. +*/ + +#include "config.h" + +/* --- Boost.Test boilerplate --- */ +#if HAVE_DYNAMIC_BOOST_TEST +#define BOOST_TEST_DYN_LINK +#endif + +#define NVERBOSE // Suppress own messages when throw()ing + +#define BOOST_TEST_MODULE UnitsTest +#include +#include + +/* --- our own headers --- */ + +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +BOOST_AUTO_TEST_SUITE () + +BOOST_AUTO_TEST_CASE (PhasePressure) +{ + typedef std::vector PVal; + typedef std::vector PPress; + + std::shared_ptr + G(create_grid_cart3d(10, 1, 10), destroy_grid); + + Opm::parameter::ParameterGroup param; + { + using Opm::unit::kilogram; + using Opm::unit::meter; + using Opm::unit::cubic; + + std::stringstream dens; dens << 700*kilogram/cubic(meter); + param.insertParameter("rho2", dens.str()); + } + + typedef Opm::BlackoilPropertiesBasic Props; + Props props(param, G->dimensions, G->number_of_cells); + + typedef Opm::equil::DensityCalculator RhoCalc; + RhoCalc calc(props, 0); + + Opm::equil::EquilRecord record = + { + { 0 , 1e5 } , // Datum depth, pressure + { 5 , 0 } , // Zwoc , Pcow_woc + { 0 , 0 } // Zgoc , Pcgo_goc + }; + + Opm::equil::EquilReg + region(record, calc, + Opm::equil::miscibility::NoMixing(), + Opm::equil::miscibility::NoMixing(), + props.phaseUsage()); + + const double grav = 10; + const PPress ppress = Opm::equil::phasePressures(*G, region, grav); + + const int first = 0, last = G->number_of_cells - 1; + const double reltol = 1.0e-8; + BOOST_CHECK_CLOSE(ppress[0][first] , 90e3 , reltol); + BOOST_CHECK_CLOSE(ppress[0][last ] , 180e3 , reltol); + BOOST_CHECK_CLOSE(ppress[1][first] , 103.5e3 , reltol); + BOOST_CHECK_CLOSE(ppress[1][last ] , 166.5e3 , reltol); +} + +BOOST_AUTO_TEST_SUITE_END() From e84e6ee4bbde6679c250142ebca6baf076c72c32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 17 Jan 2014 17:43:27 +0100 Subject: [PATCH 02/81] Compute phase pressures in subset of cells This commit adds support for assigning the initial phase pressure distribution to a subset of the total grid cells. This is needed in order to fully support equilibration regions. The existing region support (template parameter 'Region' in function 'phasePressures()') was only used/needed to define PVT property (specifically, the fluid phase density) calculator pertaining to a particular equilibration region. --- opm/core/simulator/initStateEquil.hpp | 3 +- opm/core/simulator/initStateEquil_impl.hpp | 107 ++++++++++++++++----- tests/test_equil.cpp | 5 +- 3 files changed, 87 insertions(+), 28 deletions(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 61f7debc..b81183cf 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -160,10 +160,11 @@ namespace Opm PhaseUsage pu_; }; - template + template std::vector< std::vector > phasePressures(const UnstructuredGrid& G, const Region& reg, + const CellRange& cells, const double grav = unit::gravity); } // namespace equil } // namespace Opm diff --git a/opm/core/simulator/initStateEquil_impl.hpp b/opm/core/simulator/initStateEquil_impl.hpp index cb19d020..befbf275 100644 --- a/opm/core/simulator/initStateEquil_impl.hpp +++ b/opm/core/simulator/initStateEquil_impl.hpp @@ -286,31 +286,40 @@ namespace Opm } // namespace PhaseIndex namespace PhasePressure { - template + template void assign(const UnstructuredGrid& G , const std::array& f , const double split, + const CellRange& cells, std::vector& p ) { - const int nd = G.dimensions, nc = G.number_of_cells; - const double* depth = & G.cell_centroids[0*nd + (nd - 1)]; + const int nd = G.dimensions; enum { up = 0, down = 1 }; - for (int c = 0; c < nc; ++c, depth += nd) { - const double z = *depth; + std::vector::size_type c = 0; + for (typename CellRange::const_iterator + ci = cells.begin(), ce = cells.end(); + ci != ce; ++ci, ++c) + { + assert (c < p.size()); + + const double z = G.cell_centroids[(*ci)*nd + (nd - 1)]; p[c] = (z < split) ? f[up](z) : f[down](z); } } - template + template void water(const UnstructuredGrid& G , const Region& reg , const std::array& span , const double grav , const double po_woc, + const CellRange& cells , std::vector& press ) { using PhasePressODE::Water; @@ -336,15 +345,17 @@ namespace Opm } }; - assign(G, wpress, z0, press); + assign(G, wpress, z0, cells, press); } - template + template void oil(const UnstructuredGrid& G , const Region& reg , const std::array& span , const double grav , + const CellRange& cells , std::vector& press , double& po_woc, double& po_goc) @@ -376,7 +387,7 @@ namespace Opm } }; - assign(G, opress, z0, press); + assign(G, opress, z0, cells, press); po_woc = -std::numeric_limits::max(); po_goc = -std::numeric_limits::max(); @@ -394,13 +405,15 @@ namespace Opm else { po_goc = p0; } // GOC *at* datum } - template + template void gas(const UnstructuredGrid& G , const Region& reg , const std::array& span , const double grav , const double po_goc, + const CellRange& cells , std::vector& press ) { using PhasePressODE::Gas; @@ -431,16 +444,18 @@ namespace Opm } }; - assign(G, gpress, z0, press); + assign(G, gpress, z0, cells, press); } } // namespace PhasePressure - template + template void equilibrateOWG(const UnstructuredGrid& G, const Region& reg, const double grav, const std::array& span, + const CellRange& cells, std::vector< std::vector >& press) { const PhaseUsage& pu = reg.phaseUsage(); @@ -448,53 +463,93 @@ namespace Opm double po_woc = -1, po_goc = -1; if (PhaseUsed::oil(pu)) { const int oix = PhaseIndex::oil(pu); - PhasePressure::oil(G, reg, span, grav, + PhasePressure::oil(G, reg, span, grav, cells, press[ oix ], po_woc, po_goc); } if (PhaseUsed::water(pu)) { const int wix = PhaseIndex::water(pu); - PhasePressure::water(G, reg, span, grav, - po_woc, press[ wix ]); + PhasePressure::water(G, reg, span, grav, po_woc, + cells, press[ wix ]); } if (PhaseUsed::gas(pu)) { const int gix = PhaseIndex::gas(pu); - PhasePressure::gas(G, reg, span, grav, - po_goc, press[ gix ]); + PhasePressure::gas(G, reg, span, grav, po_goc, + cells, press[ gix ]); } } } // namespace Details namespace equil { - template + template std::vector< std::vector > phasePressures(const UnstructuredGrid& G, const Region& reg, + const CellRange& cells, const double grav) { std::array span = {{ std::numeric_limits::max() , -std::numeric_limits::max() }}; // Symm. about 0. + + int ncell = 0; { // This code is only supported in three space dimensions assert (G.dimensions == 3); - const int nd = G.dimensions; - const double* depth = & G.node_coordinates[0*nd + (nd - 1)]; + const int nd = G.dimensions; - for (int n = 0; n < G.number_of_nodes; ++n, depth += nd) { - const double z = *depth; + // Define short-name aliases to reduce visual clutter. + const double* const nc = & G.node_coordinates[0]; - if (z < span[0]) { span[0] = z; } - if (z > span[1]) { span[1] = z; } + const int* const cfp = & G.cell_facepos[0]; + const int* const cf = & G.cell_faces[0]; + + const int* const fnp = & G.face_nodepos[0]; + const int* const fn = & G.face_nodes[0]; + + // Define vertical span as + // + // [minimum(node depth(cells)), maximum(node depth(cells))] + // + // Note: We use a sledgehammer approach--looping all + // the nodes of all the faces of all the 'cells'--to + // compute those bounds. This necessarily entails + // visiting some nodes (and faces) multiple times. + // + // Note: The implementation of 'RK4IVP<>' implicitly + // imposes the requirement that cell centroids are all + // within this vertical span. That requirement is not + // checked. + for (typename CellRange::const_iterator + ci = cells.begin(), ce = cells.end(); + ci != ce; ++ci, ++ncell) + { + for (const int + *fi = & cf[ cfp[*ci + 0] ], + *fe = & cf[ cfp[*ci + 1] ]; + fi != fe; ++fi) + { + for (const int + *i = & fn[ fnp[*fi + 0] ], + *e = & fn[ fnp[*fi + 1] ]; + i != e; ++i) + { + const double z = nc[(*i)*nd + (nd - 1)]; + + if (z < span[0]) { span[0] = z; } + if (z > span[1]) { span[1] = z; } + } + } } } const int np = reg.phaseUsage().num_phases; typedef std::vector pval; - std::vector press(np, pval(G.number_of_cells, 0.0)); + std::vector press(np, pval(ncell, 0.0)); const double z0 = reg.datum(); const double zwoc = reg.zwoc (); @@ -502,7 +557,7 @@ namespace Opm if (! ((zgoc > z0) || (z0 > zwoc))) { // Datum depth in oil zone (zgoc <= z0 <= zwoc) - Details::equilibrateOWG(G, reg, grav, span, press); + Details::equilibrateOWG(G, reg, grav, span, cells, press); } return press; diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index 4d7cbe83..5a97c74c 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -75,8 +75,11 @@ BOOST_AUTO_TEST_CASE (PhasePressure) Opm::equil::miscibility::NoMixing(), props.phaseUsage()); + std::vector cells(G->number_of_cells); + std::iota(cells.begin(), cells.end(), 0); + const double grav = 10; - const PPress ppress = Opm::equil::phasePressures(*G, region, grav); + const PPress ppress = Opm::equil::phasePressures(*G, region, cells, grav); const int first = 0, last = G->number_of_cells - 1; const double reltol = 1.0e-8; From 88b9db9a45d15e945cf82960958253b611af2dfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 17 Jan 2014 19:41:22 +0100 Subject: [PATCH 03/81] Test cell subset phase pressure assignment. --- tests/test_equil.cpp | 112 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index 5a97c74c..36d1d452 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -89,4 +89,116 @@ BOOST_AUTO_TEST_CASE (PhasePressure) BOOST_CHECK_CLOSE(ppress[1][last ] , 166.5e3 , reltol); } +BOOST_AUTO_TEST_CASE (CellSubset) +{ + typedef std::vector PVal; + typedef std::vector PPress; + + std::shared_ptr + G(create_grid_cart3d(10, 1, 10), destroy_grid); + + Opm::parameter::ParameterGroup param; + { + using Opm::unit::kilogram; + using Opm::unit::meter; + using Opm::unit::cubic; + + std::stringstream dens; dens << 700*kilogram/cubic(meter); + param.insertParameter("rho2", dens.str()); + } + + typedef Opm::BlackoilPropertiesBasic Props; + Props props(param, G->dimensions, G->number_of_cells); + + typedef Opm::equil::DensityCalculator RhoCalc; + RhoCalc calc(props, 0); + + Opm::equil::EquilRecord record[] = + { + { + { 0 , 1e5 } , // Datum depth, pressure + { 2.5 , -0.075e5 } , // Zwoc , Pcow_woc + { 0 , 0 } // Zgoc , Pcgo_goc + } + , + { + { 5 , 1.35e5 } , // Datum depth, pressure + { 7.5 , -0.225e5 } , // Zwoc , Pcow_woc + { 5 , 0 } // Zgoc , Pcgo_goc + } + }; + + Opm::equil::EquilReg region[] = + { + Opm::equil::EquilReg(record[0], calc, + Opm::equil::miscibility::NoMixing(), + Opm::equil::miscibility::NoMixing(), + props.phaseUsage()) + , + Opm::equil::EquilReg(record[0], calc, + Opm::equil::miscibility::NoMixing(), + Opm::equil::miscibility::NoMixing(), + props.phaseUsage()) + , + Opm::equil::EquilReg(record[1], calc, + Opm::equil::miscibility::NoMixing(), + Opm::equil::miscibility::NoMixing(), + props.phaseUsage()) + , + Opm::equil::EquilReg(record[1], calc, + Opm::equil::miscibility::NoMixing(), + Opm::equil::miscibility::NoMixing(), + props.phaseUsage()) + }; + + const int cdim[] = { 2, 1, 2 }; + int ncoarse = cdim[0]; + for (std::size_t d = 1; d < 3; ++d) { ncoarse *= cdim[d]; } + + std::vector< std::vector > cells(ncoarse); + for (int c = 0; c < G->number_of_cells; ++c) { + int ci = c; + const int i = ci % G->cartdims[0]; ci /= G->cartdims[0]; + const int j = ci % G->cartdims[1]; + const int k = ci / G->cartdims[1]; + + const int ic = (i / (G->cartdims[0] / cdim[0])); + const int jc = (j / (G->cartdims[1] / cdim[1])); + const int kc = (k / (G->cartdims[2] / cdim[2])); + const int ix = ic + cdim[0]*(jc + cdim[1]*kc); + + assert ((0 <= ix) && (ix < ncoarse)); + cells[ix].push_back(c); + } + + PPress ppress(2, PVal(G->number_of_cells, 0)); + for (std::vector< std::vector >::const_iterator + r = cells.begin(), e = cells.end(); + r != e; ++r) + { + const int rno = int(r - cells.begin()); + const double grav = 10; + const PPress p = + Opm::equil::phasePressures(*G, region[rno], *r, grav); + + PVal::size_type i = 0; + for (std::vector::const_iterator + c = r->begin(), ce = r->end(); + c != ce; ++c, ++i) + { + assert (i < p[0].size()); + + ppress[0][*c] = p[0][i]; + ppress[1][*c] = p[1][i]; + } + } + + const int first = 0, last = G->number_of_cells - 1; + const double reltol = 1.0e-8; + BOOST_CHECK_CLOSE(ppress[0][first] , 105e3 , reltol); + BOOST_CHECK_CLOSE(ppress[0][last ] , 195e3 , reltol); + BOOST_CHECK_CLOSE(ppress[1][first] , 103.5e3 , reltol); + BOOST_CHECK_CLOSE(ppress[1][last ] , 166.5e3 , reltol); +} + BOOST_AUTO_TEST_SUITE_END() From aa9e28a5b83dd0c493b89d2c824be06ddfec7822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 17 Jan 2014 20:07:51 +0100 Subject: [PATCH 04/81] Add reverse look-up mapping for region vectors Class RegionMapping<> provides an easy way of extracting the cells that belong to any identified region (e.g., as defined by EQLNUM) of the deck. --- opm/core/simulator/initStateEquil.hpp | 91 +++++++++++++++++++++ tests/test_equil.cpp | 110 ++++++++++++++++++++++++++ 2 files changed, 201 insertions(+) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index b81183cf..5df47a0c 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -27,6 +27,7 @@ #include #include +#include #include struct UnstructuredGrid; @@ -102,6 +103,96 @@ namespace Opm }; } // namespace miscibility + template < class Region = std::vector > + class RegionMapping { + public: + explicit + RegionMapping(const Region& reg) + : reg_(reg) + { + rev_.init(reg_); + } + + typedef typename Region::value_type RegionId; + typedef typename Region::size_type CellId; + typedef typename std::vector::const_iterator CellIter; + + class CellRange { + public: + CellRange(const CellIter b, + const CellIter e) + : b_(b), e_(e) + {} + + typedef CellIter iterator; + typedef CellIter const_iterator; + + iterator begin() const { return b_; } + iterator end() const { return e_; } + + private: + iterator b_; + iterator e_; + }; + + RegionId + numRegions() const { return RegionId(rev_.p.size()) - 1; } + + RegionId + region(const CellId c) const { return reg_[c]; } + + CellRange + cells(const RegionId r) const { + const RegionId i = r - rev_.low; + return CellRange(rev_.c.begin() + rev_.p[i + 0], + rev_.c.begin() + rev_.p[i + 1]); + } + + private: + Region reg_; + + struct { + typedef typename std::vector::size_type Pos; + std::vector p; + std::vector c; + RegionId low; + + void + init(const Region& reg) + { + typedef typename Region::const_iterator CI; + const std::pair + m = std::minmax_element(reg.begin(), reg.end()); + + low = *m.first; + + const typename Region::size_type + n = *m.second - low + 1; + + p.resize(n + 1); std::fill(p.begin(), p.end(), Pos(0)); + for (CellId i = 0, nc = reg.size(); i < nc; ++i) { + p[ reg[i] - low + 1 ] += 1; + } + + for (typename std::vector::size_type + i = 1, sz = p.size(); i < sz; ++i) { + p[0] += p[i]; + p[i] = p[0] - p[i]; + } + + assert (p[0] == + static_cast(reg.size())); + + c.resize(reg.size()); + for (CellId i = 0, nc = reg.size(); i < nc; ++i) { + c[ p[ reg[i] - low + 1 ] ++ ] = i; + } + + p[0] = 0; + } + } rev_; + }; + struct EquilRecord { struct { double depth; diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index 36d1d452..80ae2652 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -25,6 +25,8 @@ #include #include +#include + #include #include @@ -201,4 +203,112 @@ BOOST_AUTO_TEST_CASE (CellSubset) BOOST_CHECK_CLOSE(ppress[1][last ] , 166.5e3 , reltol); } +BOOST_AUTO_TEST_CASE (RegMapping) +{ + typedef std::vector PVal; + typedef std::vector PPress; + + std::shared_ptr + G(create_grid_cart3d(10, 1, 10), destroy_grid); + + Opm::parameter::ParameterGroup param; + { + using Opm::unit::kilogram; + using Opm::unit::meter; + using Opm::unit::cubic; + + std::stringstream dens; dens << 700*kilogram/cubic(meter); + param.insertParameter("rho2", dens.str()); + } + + typedef Opm::BlackoilPropertiesBasic Props; + Props props(param, G->dimensions, G->number_of_cells); + + typedef Opm::equil::DensityCalculator RhoCalc; + RhoCalc calc(props, 0); + + Opm::equil::EquilRecord record[] = + { + { + { 0 , 1e5 } , // Datum depth, pressure + { 2.5 , -0.075e5 } , // Zwoc , Pcow_woc + { 0 , 0 } // Zgoc , Pcgo_goc + } + , + { + { 5 , 1.35e5 } , // Datum depth, pressure + { 7.5 , -0.225e5 } , // Zwoc , Pcow_woc + { 5 , 0 } // Zgoc , Pcgo_goc + } + }; + + Opm::equil::EquilReg region[] = + { + Opm::equil::EquilReg(record[0], calc, + Opm::equil::miscibility::NoMixing(), + Opm::equil::miscibility::NoMixing(), + props.phaseUsage()) + , + Opm::equil::EquilReg(record[0], calc, + Opm::equil::miscibility::NoMixing(), + Opm::equil::miscibility::NoMixing(), + props.phaseUsage()) + , + Opm::equil::EquilReg(record[1], calc, + Opm::equil::miscibility::NoMixing(), + Opm::equil::miscibility::NoMixing(), + props.phaseUsage()) + , + Opm::equil::EquilReg(record[1], calc, + Opm::equil::miscibility::NoMixing(), + Opm::equil::miscibility::NoMixing(), + props.phaseUsage()) + }; + + std::vector eqlnum(G->number_of_cells); + { + std::vector cells(G->number_of_cells); + std::iota(cells.begin(), cells.end(), 0); + + const int cdim[] = { 2, 1, 2 }; + int ncoarse = cdim[0]; + for (std::size_t d = 1; d < 3; ++d) { ncoarse *= cdim[d]; } + + partition_unif_idx(G->dimensions, G->number_of_cells, + G->cartdims, cdim, + &cells[0], &eqlnum[0]); + } + Opm::equil::RegionMapping<> eqlmap(eqlnum); + + PPress ppress(2, PVal(G->number_of_cells, 0)); + for (int r = 0, e = eqlmap.numRegions(); r != e; ++r) + { + const Opm::equil::RegionMapping<>::CellRange& + rng = eqlmap.cells(r); + + const int rno = r; + const double grav = 10; + const PPress p = + Opm::equil::phasePressures(*G, region[rno], rng, grav); + + PVal::size_type i = 0; + for (Opm::equil::RegionMapping<>::CellRange::const_iterator + c = rng.begin(), ce = rng.end(); + c != ce; ++c, ++i) + { + assert (i < p[0].size()); + + ppress[0][*c] = p[0][i]; + ppress[1][*c] = p[1][i]; + } + } + + const int first = 0, last = G->number_of_cells - 1; + const double reltol = 1.0e-8; + BOOST_CHECK_CLOSE(ppress[0][first] , 105e3 , reltol); + BOOST_CHECK_CLOSE(ppress[0][last ] , 195e3 , reltol); + BOOST_CHECK_CLOSE(ppress[1][first] , 103.5e3 , reltol); + BOOST_CHECK_CLOSE(ppress[1][last ] , 166.5e3 , reltol); +} + BOOST_AUTO_TEST_SUITE_END() From 20ec2fb3d7542fbec77c237dbb0d8f43991eedda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Mon, 20 Jan 2014 01:25:33 +0100 Subject: [PATCH 05/81] Document public interface of phasePressures(). --- opm/core/simulator/initStateEquil.hpp | 356 ++++++++++++++++++++++++-- 1 file changed, 336 insertions(+), 20 deletions(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 5df47a0c..43f77aac 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -30,17 +30,45 @@ #include #include +/** + * \file + * Facilities for an ECLIPSE-style equilibration-based + * initialisation scheme (keyword 'EQUIL'). + */ struct UnstructuredGrid; namespace Opm { + /** + * Types and routines that collectively implement a basic + * ECLIPSE-style equilibration-based initialisation scheme. + * + * This namespace is intentionally nested to avoid name clashes + * with other parts of OPM. + */ namespace equil { template class DensityCalculator; + /** + * Facility for calculating phase densities based on the + * BlackoilPropertiesInterface. + * + * Implements the crucial operator()(p,svol) + * function that is expected by class EquilReg. + */ template <> class DensityCalculator< BlackoilPropertiesInterface > { public: + /** + * Constructor. + * + * \param[in] props Implementation of the + * BlackoilPropertiesInterface. + * + * \param[in] c Single cell used as a representative cell + * in a PVT region. + */ DensityCalculator(const BlackoilPropertiesInterface& props, const int c) : props_(props) @@ -48,6 +76,16 @@ namespace Opm { } + /** + * Compute phase densities of all phases at phase point + * given by (pressure, surface volume) tuple. + * + * \param[in] p Fluid pressure. + * + * \param[in] z Surface volumes of all phases. + * + * \return Phase densities at phase point. + */ std::vector operator()(const double p, const std::vector& z) const @@ -71,8 +109,28 @@ namespace Opm const std::vector c_; }; + /** + * Types and routines relating to phase mixing in + * equilibration calculations. + */ namespace miscibility { + /** + * Type that implements "no phase mixing" policy. + */ struct NoMixing { + /** + * Function call. + * + * \param[in] depth Depth at which to calculate RS + * value. + * + * \param[in] press Pressure at which to calculate RS + * value. + * + * \return Dissolved gas-oil ratio (RS) at depth @c + * depth and pressure @c press. In "no mixing + * policy", this is identically zero. + */ double operator()(const double /* depth */, const double /* press */) const @@ -81,8 +139,19 @@ namespace Opm } }; + /** + * Type that implements "dissolved gas-oil ratio" + * tabulated as a function of depth policy. Data + * typically taken from keyword 'RSVD'. + */ class RsVD { public: + /** + * Constructor. + * + * \param[in] depth Depth nodes. + * \param[in] rs Dissolved gas-oil ratio at @c depth. + */ RsVD(const std::vector& depth, const std::vector& rs) : depth_(depth) @@ -90,6 +159,18 @@ namespace Opm { } + /** + * Function call. + * + * \param[in] depth Depth at which to calculate RS + * value. + * + * \param[in] press Pressure at which to calculate RS + * value. + * + * \return Dissolved gas-oil ratio (RS) at depth @c + * depth and pressure @c press. + */ double operator()(const double depth, const double /* press */) const @@ -98,14 +179,31 @@ namespace Opm } private: - std::vector depth_; - std::vector rs_; + std::vector depth_; /**< Depth nodes */ + std::vector rs_; /**< Dissolved gas-oil ratio */ }; } // namespace miscibility + /** + * Forward and reverse mappings between cells and + * regions/partitions (e.g., the ECLIPSE-style 'SATNUM', + * 'PVTNUM', or 'EQUILNUM' arrays). + * + * \tparam Region Type of a forward region mapping. Expected + * to provide indexed access through + * operator[]() as well as inner types + * 'value_type', 'size_type', and + * 'const_iterator'. + */ template < class Region = std::vector > class RegionMapping { public: + /** + * Constructor. + * + * \param[in] reg Forward region mapping, restricted to + * active cells only. + */ explicit RegionMapping(const Region& reg) : reg_(reg) @@ -113,34 +211,82 @@ namespace Opm rev_.init(reg_); } - typedef typename Region::value_type RegionId; - typedef typename Region::size_type CellId; + /** + * Type of forward (cell-to-region) mapping result. + * Expected to be an integer. + */ + typedef typename Region::value_type RegionId; + + /** + * Type of reverse (region-to-cell) mapping (element) + * result. + */ + typedef typename Region::size_type CellId; + + /** + * Type of reverse region-to-cell range bounds and + * iterators. + */ typedef typename std::vector::const_iterator CellIter; + /** + * Range of cells. Result from reverse (region-to-cell) + * mapping. + */ class CellRange { public: + /** + * Constructor. + * + * \param[in] b Beginning of range. + * \param[in] e One past end of range. + */ CellRange(const CellIter b, const CellIter e) : b_(b), e_(e) {} - typedef CellIter iterator; + /** + * Read-only iterator on cell ranges. + */ typedef CellIter const_iterator; - iterator begin() const { return b_; } - iterator end() const { return e_; } + /** + * Beginning of cell range. + */ + const_iterator begin() const { return b_; } + + /** + * One past end of cell range. + */ + const_iterator end() const { return e_; } private: - iterator b_; - iterator e_; + const_iterator b_; + const_iterator e_; }; + /** + * Number of declared regions in cell-to-region mapping. + */ RegionId numRegions() const { return RegionId(rev_.p.size()) - 1; } + /** + * Compute region number of given active cell. + * + * \param[in] c Active cell + * \return Region to which @c c belongs. + */ RegionId region(const CellId c) const { return reg_[c]; } + /** + * Extract active cells in particular region. + * + * \param[in] r Region number + * \returns Range of active cells in region @c r. + */ CellRange cells(const RegionId r) const { const RegionId i = r - rev_.low; @@ -149,14 +295,24 @@ namespace Opm } private: + /** + * Copy of forward region mapping (cell-to-region). + */ Region reg_; + /** + * Reverse mapping (region-to-cell). + */ struct { typedef typename std::vector::size_type Pos; - std::vector p; - std::vector c; - RegionId low; + std::vector p; /**< Region start pointers */ + std::vector c; /**< Region cells */ + RegionId low; /**< Smallest region number */ + /** + * Compute reverse mapping. Standard linear insertion + * sort algorithm. + */ void init(const Region& reg) { @@ -190,9 +346,32 @@ namespace Opm p[0] = 0; } - } rev_; + } rev_; /**< Reverse mapping instance */ }; + /** + * Equilibration record. + * + * Layout and contents inspired by first six items of + * ECLIPSE's 'EQUIL' records. This is the minimum amount of + * input data needed to define phase pressures in an + * equilibration region. + * + * Data consists of three pairs of depth and pressure values: + * 1. main + * - @c depth Main datum depth. + * - @c press Pressure at datum depth. + * + * 2. woc + * - @c depth Depth of water-oil contact + * - @c press water-oil capillary pressure at water-oil contact. + * Capillary pressure defined as "P_oil - P_water". + * + * 3. goc + * - @c depth Depth of gas-oil contact + * - @c press Gas-oil capillary pressure at gas-oil contact. + * Capillary pressure defined as "P_gas - P_oil". + */ struct EquilRecord { struct { double depth; @@ -200,11 +379,63 @@ namespace Opm } main, woc, goc; }; + /** + * Aggregate information base of an equilibration region. + * + * Provides inquiry methods for retrieving depths of contacs + * and pressure values as well as a means of calculating fluid + * densities, dissolved gas-oil ratio and vapourised oil-gas + * ratios. + * + * \tparam DensCalc Type that provides access to a phase + * density calculation facility. Must implement an operator() + * declared as + * + * std::vector + * operator()(const double press, + * const std::vector& svol ) + * + * that calculates the phase densities of all phases in @c + * svol at fluid pressure @c press. + * + * \tparam RS Type that provides access to a calculator for + * (initial) dissolved gas-oil ratios as a function of depth + * and (oil) pressure. Must implement an operator() declared + * as + * + * double + * operator()(const double depth, + * const double press) + * + * that calculates the dissolved gas-oil ratio at depth @c + * depth and (oil) pressure @c press. + * + * \tparam RV Type that provides access to a calculator for + * (initial) vapourised oil-gas ratios as a function of depth + * and (gas) pressure. Must implement an operator() declared + * as + * + * double + * operator()(const double depth, + * const double press) + * + * that calculates the vapourised oil-gas ratio at depth @c + * depth and (gas) pressure @c press. + */ template class EquilReg { public: + /** + * Constructor. + * + * \param[in] rec Equilibration data of current region. + * \param[in] density Density calculator of current region. + * \param[in] rs Calculator of dissolved gas-oil ratio. + * \param[in] rv Calculator of vapourised oil-gas ratio. + * \param[in] pu Summary of current active phases. + */ EquilReg(const EquilRecord& rec, const DensCalc& density, const RS& rs, @@ -218,39 +449,124 @@ namespace Opm { } + /** + * Type of density calculator. + */ typedef DensCalc CalcDensity; - typedef RS CalcDissolution; - typedef RV CalcEvaporation; + /** + * Type of dissolved gas-oil ratio calculator. + */ + typedef RS CalcDissolution; + + /** + * Type of vapourised oil-gas ratio calculator. + */ + typedef RV CalcEvaporation; + + /** + * Datum depth in current region + */ double datum() const { return this->rec_.main.depth; } + + /** + * Pressure at datum depth in current region. + */ double pressure() const { return this->rec_.main.press; } + /** + * Depth of water-oil contact. + */ double zwoc() const { return this->rec_.woc .depth; } + + /** + * water-oil capillary pressure at water-oil contact. + * + * \return P_o - P_w at WOC. + */ double pcow_woc() const { return this->rec_.woc .press; } + /** + * Depth of gas-oil contact. + */ double zgoc() const { return this->rec_.goc .depth; } + + /** + * Gas-oil capillary pressure at gas-oil contact. + * + * \return P_g - P_o at GOC. + */ double pcgo_goc() const { return this->rec_.goc .press; } + /** + * Retrieve phase density calculator of current region. + */ const CalcDensity& densityCalculator() const { return this->density_; } + /** + * Retrieve dissolved gas-oil ratio calculator of current + * region. + */ const CalcDissolution& dissolutionCalculator() const { return this->rs_; } + /** + * Retrieve vapourised oil-gas ratio calculator of current + * region. + */ const CalcEvaporation& evaporationCalculator() const { return this->rv_; } + /** + * Retrieve active fluid phase summary. + */ const PhaseUsage& phaseUsage() const { return this->pu_; } private: - EquilRecord rec_; - DensCalc density_; - RS rs_; - RV rv_; - PhaseUsage pu_; + EquilRecord rec_; /**< Equilibration data */ + DensCalc density_; /**< Density calculator */ + RS rs_; /**< RS calculator */ + RV rv_; /**< RV calculator */ + PhaseUsage pu_; /**< Active phase summary */ }; + /** + * Compute initial phase pressures by means of equilibration. + * + * This function uses the information contained in an + * equilibration record (i.e., depths and pressurs) as well as + * a density calculator and related data to vertically + * integrate the phase pressure ODE + * \f[ + * \frac{\mathrm{d}p_{\alpha}}{\mathrm{d}z} = + * \rho_{\alpha}(z,p_{\alpha})\cdot g + * \f] + * in which \f$\rho_{\alpha}$ denotes the fluid density of + * fluid phase \f$\alpha\f$, \f$p_{\alpha}\f$ is the + * corresponding phase pressure, \f$z\f$ is the depth and + * \f$g\f$ is the acceleration due to gravity (assumed + * directed downwords, in the positive \f$z\f$ direction). + * + * \tparam Region Type of an equilibration region information + * base. Typically an instance of the EquilReg + * class template. + * + * \tparam CellRange Type of cell range that demarcates the + * cells pertaining to the current + * equilibration region. + * + * \param[in] G Grid. + * \param[in] reg Current equilibration region. + * \param[in] cells Range that spans the cells of the current + * equilibration region. + * \param[in] grav Acceleration of gravity. + * + * \return Phase pressures, one vector for each active phase, + * of pressure values in each cell in the current + * equilibration region. + */ template std::vector< std::vector > phasePressures(const UnstructuredGrid& G, From f19bc37f0048550311d9e1aec79bf27e0ce9f21c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Mon, 20 Jan 2014 10:33:42 +0100 Subject: [PATCH 06/81] Document requirements of CellRange. --- opm/core/simulator/initStateEquil.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 43f77aac..4d7a6f0f 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -555,7 +555,10 @@ namespace Opm * * \tparam CellRange Type of cell range that demarcates the * cells pertaining to the current - * equilibration region. + * equilibration region. Must implement + * methods begin() and end() to bound the range + * as well as provide an inner type, + * const_iterator, to traverse the range. * * \param[in] G Grid. * \param[in] reg Current equilibration region. From 965918a8a353e97106e5f767d3cc686c29f40a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Tue, 21 Jan 2014 13:49:06 +0100 Subject: [PATCH 07/81] Include for std::iota() Header was missing in earlier revision. --- tests/test_equil.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index 80ae2652..27b46b55 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include From 4772f43aeff5018edccd75049587d7493847cfdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Tue, 21 Jan 2014 17:49:02 +0100 Subject: [PATCH 08/81] Install crude handling of data point outside vertical span The initial implementation of RK4IVP<>::operator() failed to take into account the possibility that we might need to evaluate the function outside the vertical span for which it was initially defined. This situation occurs, for instance, in the not uncommon cases of the GOC being above or the WOC being below the model. This commit installs a crude Hermitian extrapolation procedure to handle these cases. Refinements are likely. --- opm/core/simulator/initStateEquil_impl.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/opm/core/simulator/initStateEquil_impl.hpp b/opm/core/simulator/initStateEquil_impl.hpp index befbf275..ffc566be 100644 --- a/opm/core/simulator/initStateEquil_impl.hpp +++ b/opm/core/simulator/initStateEquil_impl.hpp @@ -73,9 +73,13 @@ namespace Opm // Dense output (O(h**3)) according to Shampine // (Hermite interpolation) const double h = stepsize(); - const int i = (x - span_[0]) / h; + int i = (x - span_[0]) / h; const double t = (x - (span_[0] + i*h)) / h; + // Crude handling of evaluation point outside "span_"; + if (i < 0) { i = 0; } + if (N_ <= i) { i = N_ - 1; } + const double y0 = y_[i], y1 = y_[i + 1]; const double f0 = f_[i], f1 = f_[i + 1]; From ae580d8aeccc50d91b45385705a322e01584e15a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Thu, 23 Jan 2014 10:16:49 +0100 Subject: [PATCH 09/81] Add a layer of glue to extract data from deck This is a work in progress. --- opm/core/simulator/initStateEquil.hpp | 126 ++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 4d7a6f0f..f9b5a65a 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -20,6 +20,7 @@ #ifndef OPM_INITSTATEEQUIL_HEADER_INCLUDED #define OPM_INITSTATEEQUIL_HEADER_INCLUDED +#include #include #include #include @@ -576,6 +577,131 @@ namespace Opm const Region& reg, const CellRange& cells, const double grav = unit::gravity); + + namespace DeckDependent { + inline + std::vector + getEquil(const EclipseGridParser& deck) + { + if (deck.hasField("EQUIL")) { + const EQUIL& eql = deck.getEQUIL(); + + typedef std::vector::size_type sz_t; + const sz_t nrec = eql.equil.size(); + + std::vector ret; + ret.reserve(nrec); + for (sz_t r = 0; r < nrec; ++r) { + const EquilLine& rec = eql.equil[r]; + + EquilRecord record = + { + { rec.datum_depth_ , + rec.datum_depth_pressure_ } + , + { rec.water_oil_contact_depth_ , + rec.oil_water_cap_pressure_ } + , + { rec.gas_oil_contact_depth_ , + rec.gas_oil_cap_pressure_ } + }; + + ret.push_back(record); + } + + return ret; + } + else { + OPM_THROW(std::domain_error, + "Deck does not provide equilibration data."); + } + } + + inline + std::vector + equilnum(const EclipseGridParser& deck, + const UnstructuredGrid& G ) + { + std::vector eqlnum; + if (deck.hasField("EQLNUM")) { + eqlnum = deck.getIntegerValue("EQLNUM"); + } + else { + // No explicit equilibration region. + // All cells in region zero. + eqlnum.assign(G.number_of_cells, 0); + } + + return eqlnum; + } + + template + class PhasePressureComputer; + + template <> + class PhasePressureComputer { + public: + PhasePressureComputer(const BlackoilPropertiesInterface& props, + const EclipseGridParser& deck , + const UnstructuredGrid& G ) + : pp_(props.numPhases(), + std::vector(G.number_of_cells)) + { + const std::vector rec = getEquil(deck); + const RegionMapping<> eqlmap(equilnum(deck, G)); + + calcII(eqlmap, rec, props, G); + } + + typedef std::vector PVal; + typedef std::vector PPress; + + const PPress& press() const { return pp_; } + + private: + typedef DensityCalculator RhoCalc; + typedef EquilReg EqReg; + + PPress pp_; + + template + void + calcII(const RMap& reg , + const std::vector< EquilRecord >& rec , + const Opm::BlackoilPropertiesInterface& props, + const UnstructuredGrid& G ) + { + typedef miscibility::NoMixing NoMix; + + for (typename RMap::RegionId + r = 0, nr = reg.numRegions(); + r < nr; ++r) + { + const typename RMap::CellRange cells = reg.cells(r); + + const int repcell = *cells.begin(); + const RhoCalc calc(props, repcell); + + const EqReg eqreg(rec[r], calc, NoMix(), NoMix(), + props.phaseUsage()); + + const PPress& res = phasePressures(G, eqreg, cells); + + for (int p = 0, np = props.numPhases(); p < np; ++p) { + PVal& d = pp_[p]; + PVal::const_iterator s = res[p].begin(); + for (typename RMap::CellRange::const_iterator + c = cells.begin(), + e = cells.end(); + c != e; ++c, ++s) + { + d[*c] = *s; + } + } + } + } + }; + } // namespace DeckDependent } // namespace equil } // namespace Opm From 5a37091d4ee8872aa645881f629d8b6baa6b57fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Mon, 3 Feb 2014 11:32:46 +0100 Subject: [PATCH 10/81] Removed RK4IVP's inheritance from binary_function. Three reasons: - class is a unary functor, - the typedefs obtained were not used, - binary_function is deprecated in C++11. --- opm/core/simulator/initStateEquil_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/core/simulator/initStateEquil_impl.hpp b/opm/core/simulator/initStateEquil_impl.hpp index ffc566be..381a3f51 100644 --- a/opm/core/simulator/initStateEquil_impl.hpp +++ b/opm/core/simulator/initStateEquil_impl.hpp @@ -32,7 +32,7 @@ namespace Opm { namespace Details { template - class RK4IVP : public std::binary_function { + class RK4IVP { public: RK4IVP(const RHS& f , const std::array& span, From 562b00effb2bff73e0069556ca1a8d9f35cde102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Mon, 3 Feb 2014 15:36:20 +0100 Subject: [PATCH 11/81] Add (unfinished) test case. --- CMakeLists_files.cmake | 1 + tests/deadfluids.DATA | 9 +++++++++ tests/test_equil.cpp | 20 ++++++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 tests/deadfluids.DATA diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 5f4004e7..09a67d9a 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -182,6 +182,7 @@ list (APPEND TEST_DATA_FILES tests/testBlackoilState1.DATA tests/testBlackoilState2.DATA tests/wells_manager_data.data + tests/deadfluids.DATA ) # originally generated with the command: diff --git a/tests/deadfluids.DATA b/tests/deadfluids.DATA new file mode 100644 index 00000000..37b3c6ee --- /dev/null +++ b/tests/deadfluids.DATA @@ -0,0 +1,9 @@ +WATER +OIL +GAS + +PVDO +100 1.0 1.0 +200 0.9 1.0 +/ + diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index 27b46b55..7060c501 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -92,6 +93,9 @@ BOOST_AUTO_TEST_CASE (PhasePressure) BOOST_CHECK_CLOSE(ppress[1][last ] , 166.5e3 , reltol); } + + + BOOST_AUTO_TEST_CASE (CellSubset) { typedef std::vector PVal; @@ -204,6 +208,9 @@ BOOST_AUTO_TEST_CASE (CellSubset) BOOST_CHECK_CLOSE(ppress[1][last ] , 166.5e3 , reltol); } + + + BOOST_AUTO_TEST_CASE (RegMapping) { typedef std::vector PVal; @@ -312,4 +319,17 @@ BOOST_AUTO_TEST_CASE (RegMapping) BOOST_CHECK_CLOSE(ppress[1][last ] , 166.5e3 , reltol); } + + +BOOST_AUTO_TEST_CASE (DeckAllDead) +{ + std::shared_ptr + grid(create_grid_cart3d(10, 1, 10), destroy_grid); + Opm::EclipseGridParser deck("deadfluids.DATA"); + Opm::BlackoilPropertiesFromDeck props(deck, *grid, false); + Opm::equil::DeckDependent::PhasePressureComputer comp(props, deck, *grid); +} + + + BOOST_AUTO_TEST_SUITE_END() From f7a298969729f456b778a9985431f3c0b4f7c8d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Tue, 4 Feb 2014 09:38:47 +0100 Subject: [PATCH 12/81] Created simple data for init testing. --- tests/deadfluids.DATA | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/deadfluids.DATA b/tests/deadfluids.DATA index 37b3c6ee..190d5d97 100644 --- a/tests/deadfluids.DATA +++ b/tests/deadfluids.DATA @@ -7,3 +7,25 @@ PVDO 200 0.9 1.0 / +PVDG +100 0.05 0.1 +200 0.02 0.2 +/ + +SWOF +0 0 1 0 +1 1 0 0 +/ + +SGOF +0 0 1 0 +1 1 0 0 +/ + +DENSITY +700 1000 10 +/ + +EQUIL +5 150 2 0 8 0 +/ From c409bd287476bd4a4c880ea880494a4bfba805c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Tue, 4 Feb 2014 10:56:09 +0100 Subject: [PATCH 13/81] Throw exception if datum not in oil zone. We are not capable of handling this, and must abort. --- opm/core/simulator/initStateEquil_impl.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/opm/core/simulator/initStateEquil_impl.hpp b/opm/core/simulator/initStateEquil_impl.hpp index 381a3f51..a713e3b3 100644 --- a/opm/core/simulator/initStateEquil_impl.hpp +++ b/opm/core/simulator/initStateEquil_impl.hpp @@ -562,6 +562,8 @@ namespace Opm if (! ((zgoc > z0) || (z0 > zwoc))) { // Datum depth in oil zone (zgoc <= z0 <= zwoc) Details::equilibrateOWG(G, reg, grav, span, cells, press); + } else { + OPM_THROW(std::runtime_error, "Cannot initialise: the datum depth must be in the oil zone."); } return press; From b1ffc25476e2866d9b2d1a957ccf169592b5cb95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Tue, 4 Feb 2014 10:57:23 +0100 Subject: [PATCH 14/81] Fix contact depths in test deck. --- tests/deadfluids.DATA | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/deadfluids.DATA b/tests/deadfluids.DATA index 190d5d97..8c368ff8 100644 --- a/tests/deadfluids.DATA +++ b/tests/deadfluids.DATA @@ -27,5 +27,5 @@ DENSITY / EQUIL -5 150 2 0 8 0 +5 150 8 0 2 0 / From 4396130765a078a877096c18f9f281d86df44920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Wed, 5 Feb 2014 11:26:29 +0100 Subject: [PATCH 15/81] Still working on test_equil.cpp. --- tests/test_equil.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index 7060c501..844cf6ad 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -324,10 +324,19 @@ BOOST_AUTO_TEST_CASE (RegMapping) BOOST_AUTO_TEST_CASE (DeckAllDead) { std::shared_ptr - grid(create_grid_cart3d(10, 1, 10), destroy_grid); + grid(create_grid_cart3d(1, 1, 10), destroy_grid); Opm::EclipseGridParser deck("deadfluids.DATA"); Opm::BlackoilPropertiesFromDeck props(deck, *grid, false); Opm::equil::DeckDependent::PhasePressureComputer comp(props, deck, *grid); + const auto& pressures = comp.press(); + BOOST_REQUIRE(pressures.size() == 3); + BOOST_REQUIRE(int(pressures[0].size()) == grid->number_of_cells); + for (auto pp : pressures) { + for (auto p : pp){ + std::cout << p << ' '; + } + std::cout << std::endl; + } } From 0d7ec9cce0b99f7eae2d091bdf9efc34c5aeb232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Wed, 19 Feb 2014 13:38:21 +0100 Subject: [PATCH 16/81] Add (defaulted) gravity argument in some places. This is done to facilitate testing, using gravity = 10 m/s^2 for example. --- opm/core/simulator/initStateEquil.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index f9b5a65a..cde37352 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -643,14 +643,15 @@ namespace Opm public: PhasePressureComputer(const BlackoilPropertiesInterface& props, const EclipseGridParser& deck , - const UnstructuredGrid& G ) + const UnstructuredGrid& G , + const double grav = unit::gravity) : pp_(props.numPhases(), std::vector(G.number_of_cells)) { const std::vector rec = getEquil(deck); const RegionMapping<> eqlmap(equilnum(deck, G)); - calcII(eqlmap, rec, props, G); + calcII(eqlmap, rec, props, G, grav); } typedef std::vector PVal; @@ -669,7 +670,8 @@ namespace Opm calcII(const RMap& reg , const std::vector< EquilRecord >& rec , const Opm::BlackoilPropertiesInterface& props, - const UnstructuredGrid& G ) + const UnstructuredGrid& G , + const double grav) { typedef miscibility::NoMixing NoMix; @@ -685,7 +687,7 @@ namespace Opm const EqReg eqreg(rec[r], calc, NoMix(), NoMix(), props.phaseUsage()); - const PPress& res = phasePressures(G, eqreg, cells); + const PPress& res = phasePressures(G, eqreg, cells, grav); for (int p = 0, np = props.numPhases(); p < np; ++p) { PVal& d = pp_[p]; From f6f4a1a0da7856e74ee25bc66f15d60a3dfcc2c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Wed, 19 Feb 2014 13:40:02 +0100 Subject: [PATCH 17/81] Modify test data. --- tests/deadfluids.DATA | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/deadfluids.DATA b/tests/deadfluids.DATA index 8c368ff8..ffaebc7f 100644 --- a/tests/deadfluids.DATA +++ b/tests/deadfluids.DATA @@ -4,7 +4,7 @@ GAS PVDO 100 1.0 1.0 -200 0.9 1.0 +200 0.5 1.0 / PVDG @@ -27,5 +27,5 @@ DENSITY / EQUIL -5 150 8 0 2 0 +5 150 5 0 2 0 / From e81aa92c0361d2afb27af94af816de0d8f36388a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Wed, 19 Feb 2014 13:41:20 +0100 Subject: [PATCH 18/81] Complete pressure test for dead-oil deck. --- tests/test_equil.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index 844cf6ad..ff48cbc1 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -327,16 +327,20 @@ BOOST_AUTO_TEST_CASE (DeckAllDead) grid(create_grid_cart3d(1, 1, 10), destroy_grid); Opm::EclipseGridParser deck("deadfluids.DATA"); Opm::BlackoilPropertiesFromDeck props(deck, *grid, false); - Opm::equil::DeckDependent::PhasePressureComputer comp(props, deck, *grid); + Opm::equil::DeckDependent::PhasePressureComputer comp(props, deck, *grid, 10.0); const auto& pressures = comp.press(); BOOST_REQUIRE(pressures.size() == 3); BOOST_REQUIRE(int(pressures[0].size()) == grid->number_of_cells); - for (auto pp : pressures) { - for (auto p : pp){ - std::cout << p << ' '; - } - std::cout << std::endl; - } + + const int first = 0, last = grid->number_of_cells - 1; + // The relative tolerance is too loose to be very useful, + // but the answer we are checking is the result of an ODE + // solver, and it is unclear if we should check it against + // the true answer or something else. + const double reltol = 1.0e-3; + BOOST_CHECK_CLOSE(pressures[0][first] , 14955e3 , reltol); + BOOST_CHECK_CLOSE(pressures[0][last ] , 15045e3 , reltol); + BOOST_CHECK_CLOSE(pressures[1][last] , 1.50473e7 , reltol); } From 71245ded79840156d0da8f29d52499829b7ee59b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Wed, 19 Feb 2014 13:42:07 +0100 Subject: [PATCH 19/81] Add saturation init facilities. This adds the function phaseSaturations() and some helpers: satFromPc() and satFromSumOfPcs(). --- opm/core/simulator/initStateEquil.hpp | 223 ++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index cde37352..6613d550 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -533,6 +534,143 @@ namespace Opm PhaseUsage pu_; /**< Active phase summary */ }; + + + /// Functor for inverting capillary pressure function. + /// Function represented is + /// f(s) = pc(s) - target_pc + struct PcEq + { + PcEq(const BlackoilPropertiesInterface& props, + const int phase, + const int cell, + const double target_pc) + : props_(props), + phase_(phase), + cell_(cell), + target_pc_(target_pc) + { + std::fill(s_, s_ + BlackoilPhases::MaxNumPhases, 0.0); + std::fill(pc_, pc_ + BlackoilPhases::MaxNumPhases, 0.0); + } + double operator()(double s) const + { + s_[phase_] = s; + props_.capPress(1, s_, &cell_, pc_, 0); + return pc_[phase_] - target_pc_; + } + private: + const BlackoilPropertiesInterface& props_; + const int phase_; + const int cell_; + const int target_pc_; + mutable double s_[BlackoilPhases::MaxNumPhases]; + mutable double pc_[BlackoilPhases::MaxNumPhases]; + }; + + + + /// Compute saturation of some phase corresponding to a given + /// capillary pressure. + inline double satFromPc(const BlackoilPropertiesInterface& props, + const int phase, + const int cell, + const double target_pc, + const bool increasing = false) + { + // Find minimum and maximum saturations. + double sminarr[BlackoilPhases::MaxNumPhases]; + double smaxarr[BlackoilPhases::MaxNumPhases]; + props.satRange(1, &cell, sminarr, smaxarr); + const double s0 = increasing ? smaxarr[phase] : sminarr[phase]; + const double s1 = increasing ? sminarr[phase] : smaxarr[phase]; + + // Create the equation f(s) = pc(s) - target_pc + const PcEq f(props, phase, cell, target_pc); + const double f0 = f(s0); + const double f1 = f(s1); + if (f0 <= 0.0) { + return s0; + } else if (f1 > 0.0) { + return s1; + } else { + const int max_iter = 30; + const double tol = 1e-6; + int iter_used = -1; + typedef RegulaFalsi ScalarSolver; + const double sol = ScalarSolver::solve(f, std::min(s0, s1), std::max(s0, s1), max_iter, tol, iter_used); + return sol; + } + } + + + /// Functor for inverting a sum of capillary pressure functions. + /// Function represented is + /// f(s) = pc1(s) + pc2(1 - s) - target_pc + struct PcEqSum + { + PcEqSum(const BlackoilPropertiesInterface& props, + const int phase1, + const int phase2, + const int cell, + const double target_pc) + : props_(props), + phase1_(phase1), + phase2_(phase2), + cell_(cell), + target_pc_(target_pc) + { + std::fill(s_, s_ + BlackoilPhases::MaxNumPhases, 0.0); + std::fill(pc_, pc_ + BlackoilPhases::MaxNumPhases, 0.0); + } + double operator()(double s) const + { + s_[phase1_] = s; + s_[phase2_] = 1.0 - s; + props_.capPress(1, s_, &cell_, pc_, 0); + return pc_[phase1_] + pc_[phase2_] - target_pc_; + } + private: + const BlackoilPropertiesInterface& props_; + const int phase1_; + const int phase2_; + const int cell_; + const int target_pc_; + mutable double s_[BlackoilPhases::MaxNumPhases]; + mutable double pc_[BlackoilPhases::MaxNumPhases]; + }; + + + + + /// Compute saturation of some phase corresponding to a given + /// capillary pressure, where the capillary pressure function + /// is given as a sum of two other functions. + inline double satFromSumOfPcs(const BlackoilPropertiesInterface& props, + const int phase1, + const int phase2, + const int cell, + const double target_pc) + { + // Find minimum and maximum saturations. + double sminarr[BlackoilPhases::MaxNumPhases]; + double smaxarr[BlackoilPhases::MaxNumPhases]; + props.satRange(1, &cell, sminarr, smaxarr); + const double smin = sminarr[phase1]; + const double smax = smaxarr[phase1]; + + // Create the equation f(s) = pc1(s) + pc2(1-s) - target_pc + const PcEqSum f(props, phase1, phase2, cell, target_pc); + const int max_iter = 30; + const double tol = 1e-6; + int iter_used = -1; + typedef RegulaFalsi ScalarSolver; + const double sol = ScalarSolver::solve(f, smin, smax, max_iter, tol, iter_used); + return sol; + } + + + /** * Compute initial phase pressures by means of equilibration. * @@ -578,6 +716,91 @@ namespace Opm const CellRange& cells, const double grav = unit::gravity); + + + /** + * Compute initial phase saturations by means of equilibration. + * + * \tparam Region Type of an equilibration region information + * base. Typically an instance of the EquilReg + * class template. + * + * \tparam CellRange Type of cell range that demarcates the + * cells pertaining to the current + * equilibration region. Must implement + * methods begin() and end() to bound the range + * as well as provide an inner type, + * const_iterator, to traverse the range. + * + * \param[in] reg Current equilibration region. + * \param[in] cells Range that spans the cells of the current + * equilibration region. + * \param[in] props Property object, needed for capillary functions. + * \param[in] phase_pressures Phase pressures, one vector for each active phase, + * of pressure values in each cell in the current + * equilibration region. + * \return Phase saturations, one vector for each phase, each containing + * one saturation value per cell in the region. + */ + template + std::vector< std::vector > + phaseSaturations(const Region& reg, + const CellRange& cells, + const BlackoilPropertiesInterface& props, + const std::vector< std::vector >& phase_pressures) + { + const double z0 = reg.datum(); + const double zwoc = reg.zwoc (); + const double zgoc = reg.zgoc (); + if ((zgoc > z0) || (z0 > zwoc)) { + OPM_THROW(std::runtime_error, "Cannot initialise: the datum depth must be in the oil zone."); + } + if (!reg.phaseUsage().phase_used[BlackoilPhases::Liquid]) { + OPM_THROW(std::runtime_error, "Cannot initialise: not handling water-gas cases."); + } + + std::vector< std::vector > phase_saturations = phase_pressures; // Just to get the right size. + + const int oilpos = reg.phaseUsage().phase_pos[BlackoilPhases::Liquid]; + std::vector::size_type local_index = 0; + for (typename CellRange::const_iterator ci = cells.begin(); ci != cells.end(); ++ci, ++local_index) { + const int cell = *ci; + // Find saturations from pressure differences by + // inverting capillary pressure functions. + double sw = 0.0; + if (reg.phaseUsage().phase_used[BlackoilPhases::Aqua]) { + const int waterpos = reg.phaseUsage().phase_pos[BlackoilPhases::Aqua]; + const double pcov = phase_pressures[oilpos][local_index] - phase_pressures[waterpos][local_index]; + sw = satFromPc(props, waterpos, cell, pcov); + phase_saturations[waterpos][local_index] = sw; + } + double sg = 0.0; + if (reg.phaseUsage().phase_used[BlackoilPhases::Vapour]) { + const int gaspos = reg.phaseUsage().phase_pos[BlackoilPhases::Vapour]; + // Note that pcog is defined to be (pg - po), not (po - pg). + const double pcog = phase_pressures[gaspos][local_index] - phase_pressures[oilpos][local_index]; + const double increasing = true; // pcog(sg) expected to be increasing function + sg = satFromPc(props, gaspos, cell, pcog, increasing); + phase_saturations[gaspos][local_index] = sg; + } + if (sg > 0.0 && sw > 0.0) { + // Overlapping gas-oil and oil-water transition + // zones. Must recalculate using gas-water + // capillary pressure. + const int waterpos = reg.phaseUsage().phase_pos[BlackoilPhases::Aqua]; + const int gaspos = reg.phaseUsage().phase_pos[BlackoilPhases::Vapour]; + const double pcgw = phase_pressures[gaspos][local_index] - phase_pressures[waterpos][local_index]; + sw = satFromSumOfPcs(props, waterpos, gaspos, cell, pcgw); + sg = 1.0 - sw; + phase_saturations[waterpos][local_index] = sw; + phase_saturations[gaspos][local_index] = sg; + } + phase_saturations[oilpos][local_index] = 1.0 - sw - sg; + } + } + + + namespace DeckDependent { inline std::vector From c4b3bcae4197cb1f3aad669f82f41ea6d31cc3aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 20 Feb 2014 15:24:27 +0100 Subject: [PATCH 20/81] Fix bugs in saturation initialisation and helpers. --- opm/core/simulator/initStateEquil.hpp | 38 ++++++++++++++++++--------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 6613d550..723776c1 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -661,12 +661,20 @@ namespace Opm // Create the equation f(s) = pc1(s) + pc2(1-s) - target_pc const PcEqSum f(props, phase1, phase2, cell, target_pc); - const int max_iter = 30; - const double tol = 1e-6; - int iter_used = -1; - typedef RegulaFalsi ScalarSolver; - const double sol = ScalarSolver::solve(f, smin, smax, max_iter, tol, iter_used); - return sol; + const double f0 = f(smin); + const double f1 = f(smax); + if (f0 <= 0.0) { + return smin; + } else if (f1 > 0.0) { + return smax; + } else { + const int max_iter = 30; + const double tol = 1e-6; + int iter_used = -1; + typedef RegulaFalsi ScalarSolver; + const double sol = ScalarSolver::solve(f, smin, smax, max_iter, tol, iter_used); + return sol; + } } @@ -760,35 +768,38 @@ namespace Opm } std::vector< std::vector > phase_saturations = phase_pressures; // Just to get the right size. + double smin[BlackoilPhases::MaxNumPhases] = { 0.0 }; + double smax[BlackoilPhases::MaxNumPhases] = { 0.0 }; + const bool water = reg.phaseUsage().phase_used[BlackoilPhases::Aqua]; + const bool gas = reg.phaseUsage().phase_used[BlackoilPhases::Vapour]; const int oilpos = reg.phaseUsage().phase_pos[BlackoilPhases::Liquid]; + const int waterpos = reg.phaseUsage().phase_pos[BlackoilPhases::Aqua]; + const int gaspos = reg.phaseUsage().phase_pos[BlackoilPhases::Vapour]; std::vector::size_type local_index = 0; for (typename CellRange::const_iterator ci = cells.begin(); ci != cells.end(); ++ci, ++local_index) { const int cell = *ci; + props.satRange(1, &cell, smin, smax); // Find saturations from pressure differences by // inverting capillary pressure functions. double sw = 0.0; - if (reg.phaseUsage().phase_used[BlackoilPhases::Aqua]) { - const int waterpos = reg.phaseUsage().phase_pos[BlackoilPhases::Aqua]; + if (water) { const double pcov = phase_pressures[oilpos][local_index] - phase_pressures[waterpos][local_index]; sw = satFromPc(props, waterpos, cell, pcov); phase_saturations[waterpos][local_index] = sw; } double sg = 0.0; - if (reg.phaseUsage().phase_used[BlackoilPhases::Vapour]) { - const int gaspos = reg.phaseUsage().phase_pos[BlackoilPhases::Vapour]; + if (gas) { // Note that pcog is defined to be (pg - po), not (po - pg). const double pcog = phase_pressures[gaspos][local_index] - phase_pressures[oilpos][local_index]; const double increasing = true; // pcog(sg) expected to be increasing function sg = satFromPc(props, gaspos, cell, pcog, increasing); phase_saturations[gaspos][local_index] = sg; } - if (sg > 0.0 && sw > 0.0) { + if (gas && water && sg > smin[gaspos] && sw > smin[waterpos]) { // Overlapping gas-oil and oil-water transition // zones. Must recalculate using gas-water // capillary pressure. - const int waterpos = reg.phaseUsage().phase_pos[BlackoilPhases::Aqua]; - const int gaspos = reg.phaseUsage().phase_pos[BlackoilPhases::Vapour]; const double pcgw = phase_pressures[gaspos][local_index] - phase_pressures[waterpos][local_index]; sw = satFromSumOfPcs(props, waterpos, gaspos, cell, pcgw); sg = 1.0 - sw; @@ -797,6 +808,7 @@ namespace Opm } phase_saturations[oilpos][local_index] = 1.0 - sw - sg; } + return phase_saturations; } From 4408ea669b953e2bd9e4166fef3858fea6ca3539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 20 Feb 2014 15:39:15 +0100 Subject: [PATCH 21/81] Add another test deck for initialisation. This deck includes capillary functions. --- CMakeLists_files.cmake | 1 + tests/capillary.DATA | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 tests/capillary.DATA diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 6820f7fa..d15bfa51 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -182,6 +182,7 @@ list (APPEND TEST_DATA_FILES tests/testBlackoilState1.DATA tests/testBlackoilState2.DATA tests/wells_manager_data.data + tests/capillary.DATA tests/deadfluids.DATA ) diff --git a/tests/capillary.DATA b/tests/capillary.DATA new file mode 100644 index 00000000..d8d163fc --- /dev/null +++ b/tests/capillary.DATA @@ -0,0 +1,31 @@ +WATER +OIL +GAS + +PVDO +100 1.0 1.0 +200 0.9 1.0 +/ + +PVDG +100 0.010 0.1 +200 0.005 0.2 +/ + +SWOF +0.2 0 1 0.4 +1 1 0 0.1 +/ + +SGOF +0 0 1 0.2 +0.8 1 0 0.5 +/ + +DENSITY +700 1000 1 +/ + +EQUIL +50 150 50 0.25 20 0.35 +/ From 5e1ad40afda114ec34668222d971d66ddcdeed5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Fri, 21 Feb 2014 08:32:15 +0100 Subject: [PATCH 22/81] Add test case for capillary inversion. --- tests/test_equil.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index ff48cbc1..712ee31c 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -345,4 +346,58 @@ BOOST_AUTO_TEST_CASE (DeckAllDead) +BOOST_AUTO_TEST_CASE (CapillaryInversion) +{ + // Test setup. + Opm::GridManager gm(1, 1, 40, 1.0, 1.0, 2.5); + const UnstructuredGrid& grid = *(gm.c_grid()); + Opm::EclipseGridParser deck("capillary.DATA"); + Opm::BlackoilPropertiesFromDeck props(deck, grid, false); + + // Test the capillary inversion for oil-water. + const int cell = 0; + const double reltol = 1.0e-7; + { + const int phase = 0; + const bool increasing = false; + const std::vector pc = { 10.0e5, 0.5e5, 0.4e5, 0.3e5, 0.2e5, 0.1e5, 0.099e5, 0.0e5, -10.0e5 }; + const std::vector s = { 0.2, 0.2, 0.2, 0.466666666666, 0.733333333333, 1.0, 1.0, 1.0, 1.0 }; + BOOST_REQUIRE(pc.size() == s.size()); + for (size_t i = 0; i < pc.size(); ++i) { + const double s_computed = Opm::equil::satFromPc(props, phase, cell, pc[i], increasing); + BOOST_CHECK_CLOSE(s_computed, s[i], reltol); + } + } + + // Test the capillary inversion for gas-oil. + { + const int phase = 2; + const bool increasing = true; + const std::vector pc = { 10.0e5, 0.6e5, 0.5e5, 0.4e5, 0.3e5, 0.2e5, 0.1e5, 0.0e5, -10.0e5 }; + const std::vector s = { 0.8, 0.8, 0.8, 0.533333333333, 0.266666666666, 0.0, 0.0, 0.0, 0.0 }; + BOOST_REQUIRE(pc.size() == s.size()); + for (size_t i = 0; i < pc.size(); ++i) { + const double s_computed = Opm::equil::satFromPc(props, phase, cell, pc[i], increasing); + BOOST_CHECK_CLOSE(s_computed, s[i], reltol); + } + } + + // Test the capillary inversion for gas-water. + { + const int water = 0; + const int gas = 2; + const std::vector pc = { 0.9e5, 0.8e5, 0.6e5, 0.4e5, 0.3e5 }; + const std::vector s = { 0.2, 0.333333333333, 0.6, 0.866666666666, 1.0 }; + BOOST_REQUIRE(pc.size() == s.size()); + for (size_t i = 0; i < pc.size(); ++i) { + const double s_computed = Opm::equil::satFromSumOfPcs(props, water, gas, cell, pc[i]); + BOOST_CHECK_CLOSE(s_computed, s[i], reltol); + } + } +} + + + + + BOOST_AUTO_TEST_SUITE_END() From e6d21e31d384d6cc9db7b3a6a5ee92e1e5a2a86b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Fri, 21 Feb 2014 08:52:25 +0100 Subject: [PATCH 23/81] Add saturation computation to and rename computer class. Opm::equil::DeckDependent::PhasePressureComputer -> Opm::equil::DeckDependent::PhasePressureSaturationComputer --- opm/core/simulator/initStateEquil.hpp | 81 +++++++++++++++++++++++---- tests/test_equil.cpp | 4 +- 2 files changed, 70 insertions(+), 15 deletions(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 723776c1..e3460bc9 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -871,42 +871,47 @@ namespace Opm } template - class PhasePressureComputer; + class PhasePressureSaturationComputer; template <> - class PhasePressureComputer { + class PhasePressureSaturationComputer { public: - PhasePressureComputer(const BlackoilPropertiesInterface& props, - const EclipseGridParser& deck , - const UnstructuredGrid& G , - const double grav = unit::gravity) + PhasePressureSaturationComputer(const BlackoilPropertiesInterface& props, + const EclipseGridParser& deck , + const UnstructuredGrid& G , + const double grav = unit::gravity) : pp_(props.numPhases(), + std::vector(G.number_of_cells)), + sat_(props.numPhases(), std::vector(G.number_of_cells)) { const std::vector rec = getEquil(deck); const RegionMapping<> eqlmap(equilnum(deck, G)); - calcII(eqlmap, rec, props, G, grav); + calcPressII(eqlmap, rec, props, G, grav); + calcSat(eqlmap, rec, props, G, grav); } typedef std::vector PVal; typedef std::vector PPress; const PPress& press() const { return pp_; } + const PPress& saturation() const { return sat_; } private: typedef DensityCalculator RhoCalc; typedef EquilReg EqReg; PPress pp_; + PPress sat_; template void - calcII(const RMap& reg , - const std::vector< EquilRecord >& rec , - const Opm::BlackoilPropertiesInterface& props, - const UnstructuredGrid& G , - const double grav) + calcPressII(const RMap& reg , + const std::vector< EquilRecord >& rec , + const Opm::BlackoilPropertiesInterface& props, + const UnstructuredGrid& G , + const double grav) { typedef miscibility::NoMixing NoMix; @@ -937,6 +942,58 @@ namespace Opm } } } + + template + void + calcSat(const RMap& reg , + const std::vector< EquilRecord >& rec , + const Opm::BlackoilPropertiesInterface& props, + const UnstructuredGrid& G , + const double grav) + { + typedef miscibility::NoMixing NoMix; + + for (typename RMap::RegionId + r = 0, nr = reg.numRegions(); + r < nr; ++r) + { + const typename RMap::CellRange cells = reg.cells(r); + + const int repcell = *cells.begin(); + const RhoCalc calc(props, repcell); + + const EqReg eqreg(rec[r], calc, NoMix(), NoMix(), + props.phaseUsage()); + + const PPress press = phasePressures(G, eqreg, cells, grav); + const PPress sat = phaseSaturations(eqreg, cells, props, press); + + for (int p = 0, np = props.numPhases(); p < np; ++p) { + PVal& d = pp_[p]; + PVal::const_iterator s = press[p].begin(); + for (typename RMap::CellRange::const_iterator + c = cells.begin(), + e = cells.end(); + c != e; ++c, ++s) + { + d[*c] = *s; + } + } + for (int p = 0, np = props.numPhases(); p < np; ++p) { + PVal& d = sat_[p]; + PVal::const_iterator s = sat[p].begin(); + for (typename RMap::CellRange::const_iterator + c = cells.begin(), + e = cells.end(); + c != e; ++c, ++s) + { + d[*c] = *s; + } + } + } + } + + }; } // namespace DeckDependent } // namespace equil diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index 712ee31c..83148218 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -328,7 +328,7 @@ BOOST_AUTO_TEST_CASE (DeckAllDead) grid(create_grid_cart3d(1, 1, 10), destroy_grid); Opm::EclipseGridParser deck("deadfluids.DATA"); Opm::BlackoilPropertiesFromDeck props(deck, *grid, false); - Opm::equil::DeckDependent::PhasePressureComputer comp(props, deck, *grid, 10.0); + Opm::equil::DeckDependent::PhasePressureSaturationComputer comp(props, deck, *grid, 10.0); const auto& pressures = comp.press(); BOOST_REQUIRE(pressures.size() == 3); BOOST_REQUIRE(int(pressures[0].size()) == grid->number_of_cells); @@ -398,6 +398,4 @@ BOOST_AUTO_TEST_CASE (CapillaryInversion) - - BOOST_AUTO_TEST_SUITE_END() From c7af2aa8ca645d69465735b632ac4d82d3df013e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Fri, 21 Feb 2014 08:55:15 +0100 Subject: [PATCH 24/81] Add test case with capillary transition region. --- tests/test_equil.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index 83148218..d62aa5f1 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -398,4 +398,42 @@ BOOST_AUTO_TEST_CASE (CapillaryInversion) +BOOST_AUTO_TEST_CASE (DeckWithCapillary) +{ + Opm::GridManager gm(1, 1, 20, 1.0, 1.0, 5.0); + const UnstructuredGrid& grid = *(gm.c_grid()); + Opm::EclipseGridParser deck("capillary.DATA"); + Opm::BlackoilPropertiesFromDeck props(deck, grid, false); + + Opm::equil::DeckDependent::PhasePressureSaturationComputer comp(props, deck, grid, 10.0); + const auto& pressures = comp.press(); + BOOST_REQUIRE(pressures.size() == 3); + BOOST_REQUIRE(int(pressures[0].size()) == grid.number_of_cells); + + const int first = 0, last = grid.number_of_cells - 1; + // The relative tolerance is too loose to be very useful, + // but the answer we are checking is the result of an ODE + // solver, and it is unclear if we should check it against + // the true answer or something else. + const double reltol = 1.0e-6; + BOOST_CHECK_CLOSE(pressures[0][first] , 1.45e7 , reltol); + BOOST_CHECK_CLOSE(pressures[0][last ] , 1.545e7 , reltol); + BOOST_CHECK_CLOSE(pressures[1][last] , 1.5351621345e7 , reltol); + + const auto& sats = comp.saturation(); + const std::vector s[3]{ + { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.425893333333, 0.774026666666, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 0, 0, 0, 0.00736, 0.792746666666, 0.8, 0.8, 0.8, 0.8, 0.574106666666, 0.225973333333, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.8, 0.8, 0.8, 0.79264, 0.007253333333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + }; + for (int phase = 0; phase < 3; ++phase) { + BOOST_REQUIRE(sats[phase].size() == s[phase].size()); + for (size_t i = 0; i < s[phase].size(); ++i) { + BOOST_CHECK_CLOSE(sats[phase][i], s[phase][i], reltol); + } + } + +} + + BOOST_AUTO_TEST_SUITE_END() From f9195b824396aef0e3eba41a7ceccb9ae420136a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Fri, 21 Feb 2014 14:47:14 +0100 Subject: [PATCH 25/81] Fix bug in saturation initialisation. We shall only use gas-water capillary to initialise when we would get unphysical saturations otherwise. --- opm/core/simulator/initStateEquil.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index e3460bc9..8e2b561f 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -796,9 +796,10 @@ namespace Opm sg = satFromPc(props, gaspos, cell, pcog, increasing); phase_saturations[gaspos][local_index] = sg; } - if (gas && water && sg > smin[gaspos] && sw > smin[waterpos]) { + if (gas && water && (sg + sw > 1.0)) { // Overlapping gas-oil and oil-water transition - // zones. Must recalculate using gas-water + // zones can lead to unphysical saturations when + // treated as above. Must recalculate using gas-water // capillary pressure. const double pcgw = phase_pressures[gaspos][local_index] - phase_pressures[waterpos][local_index]; sw = satFromSumOfPcs(props, waterpos, gaspos, cell, pcgw); From 2b8deb20fd84458a8fc9f551bb369b4438e4f77a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Fri, 21 Feb 2014 14:50:45 +0100 Subject: [PATCH 26/81] Add test case with overlapping transitions. Capillary pressure functions and contact depths have been modified to ensure a large overlap. --- CMakeLists_files.cmake | 1 + tests/capillary_overlap.DATA | 31 +++++++++++++++++++++++++ tests/test_equil.cpp | 44 ++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 tests/capillary_overlap.DATA diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index d15bfa51..2968d25f 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -183,6 +183,7 @@ list (APPEND TEST_DATA_FILES tests/testBlackoilState2.DATA tests/wells_manager_data.data tests/capillary.DATA + tests/capillary_overlap.DATA tests/deadfluids.DATA ) diff --git a/tests/capillary_overlap.DATA b/tests/capillary_overlap.DATA new file mode 100644 index 00000000..185784ee --- /dev/null +++ b/tests/capillary_overlap.DATA @@ -0,0 +1,31 @@ +WATER +OIL +GAS + +PVDO +100 1.0 1.0 +200 0.9 1.0 +/ + +PVDG +100 0.010 0.1 +200 0.005 0.2 +/ + +SWOF +0.2 0 1 0.9 +1 1 0 0.1 +/ + +SGOF +0 0 1 0.2 +0.8 1 0 0.5 +/ + +DENSITY +700 1000 1 +/ + +EQUIL +50 150 50 0.25 45 0.35 +/ diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index d62aa5f1..c4db1786 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -432,7 +432,51 @@ BOOST_AUTO_TEST_CASE (DeckWithCapillary) BOOST_CHECK_CLOSE(sats[phase][i], s[phase][i], reltol); } } +} + + +BOOST_AUTO_TEST_CASE (DeckWithCapillaryOverlap) +{ + Opm::GridManager gm(1, 1, 20, 1.0, 1.0, 5.0); + const UnstructuredGrid& grid = *(gm.c_grid()); + Opm::EclipseGridParser deck("capillary_overlap.DATA"); + Opm::BlackoilPropertiesFromDeck props(deck, grid, false); + + Opm::equil::DeckDependent::PhasePressureSaturationComputer comp(props, deck, grid, 10.0); + const auto& pressures = comp.press(); + BOOST_REQUIRE(pressures.size() == 3); + BOOST_REQUIRE(int(pressures[0].size()) == grid.number_of_cells); + + const int first = 0, last = grid.number_of_cells - 1; + // The relative tolerance is too loose to be very useful, + // but the answer we are checking is the result of an ODE + // solver, and it is unclear if we should check it against + // the true answer or something else. + const double reltol = 1.0e-6; + BOOST_CHECK_CLOSE(pressures[0][first] , 1.45e7 , reltol); + BOOST_CHECK_CLOSE(pressures[0][last ] , 1.545e7 , reltol); + BOOST_CHECK_CLOSE(pressures[1][last] , 1.5351621345e7 , reltol); + + const auto& sats = comp.saturation(); + // std::cout << "Saturations:\n"; + // for (const auto& sat : sats) { + // for (const double s : sat) { + // std::cout << s << ' '; + // } + // std::cout << std::endl; + // } + const std::vector s[3]{ + { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.223141818182, 0.532269090909, 0.78471, 0.91526, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.207743333333, 0.08474, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.776858181818, 0.467730909091, 0.0075466666666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + }; + for (int phase = 0; phase < 3; ++phase) { + BOOST_REQUIRE(sats[phase].size() == s[phase].size()); + for (size_t i = 0; i < s[phase].size(); ++i) { + BOOST_CHECK_CLOSE(sats[phase][i], s[phase][i], reltol); + } + } } From 2caf790c29092eef6d9cbfa0ff403b6496b3e29c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Mon, 24 Feb 2014 13:47:03 +0100 Subject: [PATCH 27/81] Added class RsSatAtContact (not tested). --- opm/core/simulator/initStateEquil.hpp | 74 +++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 8e2b561f..4ed2b619 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -184,8 +184,82 @@ namespace Opm std::vector depth_; /**< Depth nodes */ std::vector rs_; /**< Dissolved gas-oil ratio */ }; + + + /** + * Class that implements "dissolved gas-oil ratio" (Rs) + * as function of depth and pressure as follows: + * + * 1. The Rs at the gas-oil contact is equal to the + * saturated Rs value, Rs_sat_contact. + * + * 2. The Rs elsewhere is equal to Rs_sat_contact, but + * constrained to the saturated value as given by the + * local pressure. + * + * This should yield Rs-values that are constant below the + * contact, and decreasing above the contact. + */ + class RsSatAtContact { + public: + /** + * Constructor. + * + * \param[in] props property object + * \param[in] cell any cell in the pvt region + * \param[in] p_contact oil pressure at the contact + */ + RsSatAtContact(const BlackoilPropertiesInterface& props, const int cell, const double p_contact) + : props_(props), cell_(cell) + { + auto pu = props_.phaseUsage(); + std::fill(z_, z_ + BlackoilPhases::MaxNumPhases, 0.0); + z_[pu.phase_pos[BlackoilPhases::Vapour]] = 1e100; + z_[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; + rs_sat_contact_ = satRs(p_contact); + } + + /** + * Function call. + * + * \param[in] depth Depth at which to calculate RS + * value. + * + * \param[in] press Pressure at which to calculate RS + * value. + * + * \return Dissolved gas-oil ratio (RS) at depth @c + * depth and pressure @c press. + */ + double + operator()(const double /* depth */, + const double press) const + { + return std::max(satRs(press), rs_sat_contact_); + } + + private: + const BlackoilPropertiesInterface& props_; + const int cell_; + double z_[BlackoilPhases::MaxNumPhases]; + double rs_sat_contact_; + mutable double A_[BlackoilPhases::MaxNumPhases * BlackoilPhases::MaxNumPhases]; + + double satRs(const double press) const + { + props_.matrix(1, &press, z_, &cell_, A_, 0); + // Rs/Bo is in the gas row and oil column of A_. + // 1/Bo is in the oil row and column. + // Recall also that it is stored in column-major order. + const int opos = props_.phaseUsage().phase_pos[BlackoilPhases::Liquid]; + const int gpos = props_.phaseUsage().phase_pos[BlackoilPhases::Vapour]; + const int np = props_.numPhases(); + return A_[np*opos + gpos] / A_[np*opos + opos]; + } + }; } // namespace miscibility + /** * Forward and reverse mappings between cells and * regions/partitions (e.g., the ECLIPSE-style 'SATNUM', From 34595ae2258e541bc59183374de087ac2486ecfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Mon, 24 Feb 2014 15:19:04 +0100 Subject: [PATCH 28/81] Move RegionMapping class to its own header, add test. Class now resides in opm/core/utility/RegionMapping.hpp. --- CMakeLists_files.cmake | 2 + opm/core/simulator/initStateEquil.hpp | 165 +--------------------- opm/core/utility/RegionMapping.hpp | 195 ++++++++++++++++++++++++++ tests/test_equil.cpp | 6 +- tests/test_regionmapping.cpp | 64 +++++++++ 5 files changed, 265 insertions(+), 167 deletions(-) create mode 100644 opm/core/utility/RegionMapping.hpp create mode 100644 tests/test_regionmapping.cpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 2968d25f..010cb7f4 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -170,6 +170,7 @@ list (APPEND TEST_SOURCE_FILES tests/test_wellsmanager.cpp tests/test_wellcontrols.cpp tests/test_equil.cpp + tests/test_regionmapping.cpp ) # originally generated with the command: @@ -364,6 +365,7 @@ list (APPEND PUBLIC_HEADER_FILES opm/core/utility/NonuniformTableLinear.hpp opm/core/utility/NullStream.hpp opm/core/utility/PolynomialUtils.hpp + opm/core/utility/RegionMapping.hpp opm/core/utility/RootFinders.hpp opm/core/utility/SparseTable.hpp opm/core/utility/SparseVector.hpp diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 4ed2b619..fd1c6721 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -260,170 +261,6 @@ namespace Opm } // namespace miscibility - /** - * Forward and reverse mappings between cells and - * regions/partitions (e.g., the ECLIPSE-style 'SATNUM', - * 'PVTNUM', or 'EQUILNUM' arrays). - * - * \tparam Region Type of a forward region mapping. Expected - * to provide indexed access through - * operator[]() as well as inner types - * 'value_type', 'size_type', and - * 'const_iterator'. - */ - template < class Region = std::vector > - class RegionMapping { - public: - /** - * Constructor. - * - * \param[in] reg Forward region mapping, restricted to - * active cells only. - */ - explicit - RegionMapping(const Region& reg) - : reg_(reg) - { - rev_.init(reg_); - } - - /** - * Type of forward (cell-to-region) mapping result. - * Expected to be an integer. - */ - typedef typename Region::value_type RegionId; - - /** - * Type of reverse (region-to-cell) mapping (element) - * result. - */ - typedef typename Region::size_type CellId; - - /** - * Type of reverse region-to-cell range bounds and - * iterators. - */ - typedef typename std::vector::const_iterator CellIter; - - /** - * Range of cells. Result from reverse (region-to-cell) - * mapping. - */ - class CellRange { - public: - /** - * Constructor. - * - * \param[in] b Beginning of range. - * \param[in] e One past end of range. - */ - CellRange(const CellIter b, - const CellIter e) - : b_(b), e_(e) - {} - - /** - * Read-only iterator on cell ranges. - */ - typedef CellIter const_iterator; - - /** - * Beginning of cell range. - */ - const_iterator begin() const { return b_; } - - /** - * One past end of cell range. - */ - const_iterator end() const { return e_; } - - private: - const_iterator b_; - const_iterator e_; - }; - - /** - * Number of declared regions in cell-to-region mapping. - */ - RegionId - numRegions() const { return RegionId(rev_.p.size()) - 1; } - - /** - * Compute region number of given active cell. - * - * \param[in] c Active cell - * \return Region to which @c c belongs. - */ - RegionId - region(const CellId c) const { return reg_[c]; } - - /** - * Extract active cells in particular region. - * - * \param[in] r Region number - * \returns Range of active cells in region @c r. - */ - CellRange - cells(const RegionId r) const { - const RegionId i = r - rev_.low; - return CellRange(rev_.c.begin() + rev_.p[i + 0], - rev_.c.begin() + rev_.p[i + 1]); - } - - private: - /** - * Copy of forward region mapping (cell-to-region). - */ - Region reg_; - - /** - * Reverse mapping (region-to-cell). - */ - struct { - typedef typename std::vector::size_type Pos; - std::vector p; /**< Region start pointers */ - std::vector c; /**< Region cells */ - RegionId low; /**< Smallest region number */ - - /** - * Compute reverse mapping. Standard linear insertion - * sort algorithm. - */ - void - init(const Region& reg) - { - typedef typename Region::const_iterator CI; - const std::pair - m = std::minmax_element(reg.begin(), reg.end()); - - low = *m.first; - - const typename Region::size_type - n = *m.second - low + 1; - - p.resize(n + 1); std::fill(p.begin(), p.end(), Pos(0)); - for (CellId i = 0, nc = reg.size(); i < nc; ++i) { - p[ reg[i] - low + 1 ] += 1; - } - - for (typename std::vector::size_type - i = 1, sz = p.size(); i < sz; ++i) { - p[0] += p[i]; - p[i] = p[0] - p[i]; - } - - assert (p[0] == - static_cast(reg.size())); - - c.resize(reg.size()); - for (CellId i = 0, nc = reg.size(); i < nc; ++i) { - c[ p[ reg[i] - low + 1 ] ++ ] = i; - } - - p[0] = 0; - } - } rev_; /**< Reverse mapping instance */ - }; /** * Equilibration record. diff --git a/opm/core/utility/RegionMapping.hpp b/opm/core/utility/RegionMapping.hpp new file mode 100644 index 00000000..9845919f --- /dev/null +++ b/opm/core/utility/RegionMapping.hpp @@ -0,0 +1,195 @@ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#ifndef OPM_REGIONMAPPING_HEADER_INCLUDED +#define OPM_REGIONMAPPING_HEADER_INCLUDED + +#include + +namespace Opm +{ + + /** + * Forward and reverse mappings between cells and + * regions/partitions (e.g., the ECLIPSE-style 'SATNUM', + * 'PVTNUM', or 'EQUILNUM' arrays). + * + * \tparam Region Type of a forward region mapping. Expected + * to provide indexed access through + * operator[]() as well as inner types + * 'value_type', 'size_type', and + * 'const_iterator'. + */ + template < class Region = std::vector > + class RegionMapping { + public: + /** + * Constructor. + * + * \param[in] reg Forward region mapping, restricted to + * active cells only. + */ + explicit + RegionMapping(const Region& reg) + : reg_(reg) + { + rev_.init(reg_); + } + + /** + * Type of forward (cell-to-region) mapping result. + * Expected to be an integer. + */ + typedef typename Region::value_type RegionId; + + /** + * Type of reverse (region-to-cell) mapping (element) + * result. + */ + typedef typename Region::size_type CellId; + + /** + * Type of reverse region-to-cell range bounds and + * iterators. + */ + typedef typename std::vector::const_iterator CellIter; + + /** + * Range of cells. Result from reverse (region-to-cell) + * mapping. + */ + class CellRange { + public: + /** + * Constructor. + * + * \param[in] b Beginning of range. + * \param[in] e One past end of range. + */ + CellRange(const CellIter b, + const CellIter e) + : b_(b), e_(e) + {} + + /** + * Read-only iterator on cell ranges. + */ + typedef CellIter const_iterator; + + /** + * Beginning of cell range. + */ + const_iterator begin() const { return b_; } + + /** + * One past end of cell range. + */ + const_iterator end() const { return e_; } + + private: + const_iterator b_; + const_iterator e_; + }; + + /** + * Number of declared regions in cell-to-region mapping. + */ + RegionId + numRegions() const { return RegionId(rev_.p.size()) - 1; } + + /** + * Compute region number of given active cell. + * + * \param[in] c Active cell + * \return Region to which @c c belongs. + */ + RegionId + region(const CellId c) const { return reg_[c]; } + + /** + * Extract active cells in particular region. + * + * \param[in] r Region number + * \returns Range of active cells in region @c r. + */ + CellRange + cells(const RegionId r) const { + const RegionId i = r - rev_.low; + return CellRange(rev_.c.begin() + rev_.p[i + 0], + rev_.c.begin() + rev_.p[i + 1]); + } + + private: + /** + * Copy of forward region mapping (cell-to-region). + */ + Region reg_; + + /** + * Reverse mapping (region-to-cell). + */ + struct { + typedef typename std::vector::size_type Pos; + std::vector p; /**< Region start pointers */ + std::vector c; /**< Region cells */ + RegionId low; /**< Smallest region number */ + + /** + * Compute reverse mapping. Standard linear insertion + * sort algorithm. + */ + void + init(const Region& reg) + { + typedef typename Region::const_iterator CI; + const std::pair + m = std::minmax_element(reg.begin(), reg.end()); + + low = *m.first; + + const typename Region::size_type + n = *m.second - low + 1; + + p.resize(n + 1); std::fill(p.begin(), p.end(), Pos(0)); + for (CellId i = 0, nc = reg.size(); i < nc; ++i) { + p[ reg[i] - low + 1 ] += 1; + } + + for (typename std::vector::size_type + i = 1, sz = p.size(); i < sz; ++i) { + p[0] += p[i]; + p[i] = p[0] - p[i]; + } + + assert (p[0] == + static_cast(reg.size())); + + c.resize(reg.size()); + for (CellId i = 0, nc = reg.size(); i < nc; ++i) { + c[ p[ reg[i] - low + 1 ] ++ ] = i; + } + + p[0] = 0; + } + } rev_; /**< Reverse mapping instance */ + }; + +} // namespace Opm + +#endif // OPM_REGIONMAPPING_HEADER_INCLUDED diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index c4db1786..4519f5f5 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -287,12 +287,12 @@ BOOST_AUTO_TEST_CASE (RegMapping) G->cartdims, cdim, &cells[0], &eqlnum[0]); } - Opm::equil::RegionMapping<> eqlmap(eqlnum); + Opm::RegionMapping<> eqlmap(eqlnum); PPress ppress(2, PVal(G->number_of_cells, 0)); for (int r = 0, e = eqlmap.numRegions(); r != e; ++r) { - const Opm::equil::RegionMapping<>::CellRange& + const Opm::RegionMapping<>::CellRange& rng = eqlmap.cells(r); const int rno = r; @@ -301,7 +301,7 @@ BOOST_AUTO_TEST_CASE (RegMapping) Opm::equil::phasePressures(*G, region[rno], rng, grav); PVal::size_type i = 0; - for (Opm::equil::RegionMapping<>::CellRange::const_iterator + for (Opm::RegionMapping<>::CellRange::const_iterator c = rng.begin(), ce = rng.end(); c != ce; ++c, ++i) { diff --git a/tests/test_regionmapping.cpp b/tests/test_regionmapping.cpp new file mode 100644 index 00000000..ca2ad4ea --- /dev/null +++ b/tests/test_regionmapping.cpp @@ -0,0 +1,64 @@ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#include "config.h" + +/* --- Boost.Test boilerplate --- */ +#if HAVE_DYNAMIC_BOOST_TEST +#define BOOST_TEST_DYN_LINK +#endif + +#define NVERBOSE // Suppress own messages when throw()ing + +#define BOOST_TEST_MODULE UnitsTest +#include +#include + +/* --- our own headers --- */ + +#include + + +BOOST_AUTO_TEST_SUITE () + + +BOOST_AUTO_TEST_CASE (RegionMapping) +{ + // 0 1 2 3 4 5 6 7 8 + std::vector regions = { 2, 5, 2, 4, 2, 7, 6, 3, 6 }; + Opm::RegionMapping<> rm(regions); + for (size_t i = 0; i < regions.size(); ++i) { + BOOST_CHECK_EQUAL(rm.region(i), regions[i]); + } + std::vector region_ids = { 2, 3, 4, 5, 6, 7 }; + std::vector< std::vector > region_cells = { { 0, 2, 4 }, { 7 }, { 3 }, { 1 }, { 6, 8 }, { 5 } }; + BOOST_REQUIRE_EQUAL(rm.numRegions(), region_ids.size()); + for (size_t i = 0; i < region_ids.size(); ++i) { + auto cells = rm.cells(region_ids[i]); + BOOST_REQUIRE_EQUAL(std::distance(cells.begin(), cells.end()), region_cells[i].size()); + size_t count = 0; + for (auto iter = cells.begin(); iter != cells.end(); ++iter, ++count) { + BOOST_CHECK_EQUAL(*iter, region_cells[i][count]); + } + } +} + + + +BOOST_AUTO_TEST_SUITE_END() From 14276f917560b58053c184423a9baf21bf9fbbbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Mon, 24 Feb 2014 15:55:14 +0100 Subject: [PATCH 29/81] Capitalize nested namespace names. equil -> Equil miscibility -> Miscibility --- opm/core/simulator/initStateEquil.hpp | 16 ++-- opm/core/simulator/initStateEquil_impl.hpp | 4 +- tests/test_equil.cpp | 88 +++++++++++----------- 3 files changed, 54 insertions(+), 54 deletions(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index fd1c6721..1f6f11c5 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -49,7 +49,7 @@ namespace Opm * This namespace is intentionally nested to avoid name clashes * with other parts of OPM. */ - namespace equil { + namespace Equil { template class DensityCalculator; @@ -116,7 +116,7 @@ namespace Opm * Types and routines relating to phase mixing in * equilibration calculations. */ - namespace miscibility { + namespace Miscibility { /** * Type that implements "no phase mixing" policy. */ @@ -258,7 +258,7 @@ namespace Opm return A_[np*opos + gpos] / A_[np*opos + opos]; } }; - } // namespace miscibility + } // namespace Miscibility @@ -336,8 +336,8 @@ namespace Opm * depth and (gas) pressure @c press. */ template + class RS = Miscibility::NoMixing, + class RV = Miscibility::NoMixing> class EquilReg { public: /** @@ -825,7 +825,7 @@ namespace Opm const UnstructuredGrid& G , const double grav) { - typedef miscibility::NoMixing NoMix; + typedef Miscibility::NoMixing NoMix; for (typename RMap::RegionId r = 0, nr = reg.numRegions(); @@ -863,7 +863,7 @@ namespace Opm const UnstructuredGrid& G , const double grav) { - typedef miscibility::NoMixing NoMix; + typedef Miscibility::NoMixing NoMix; for (typename RMap::RegionId r = 0, nr = reg.numRegions(); @@ -908,7 +908,7 @@ namespace Opm }; } // namespace DeckDependent - } // namespace equil + } // namespace Equil } // namespace Opm #include diff --git a/opm/core/simulator/initStateEquil_impl.hpp b/opm/core/simulator/initStateEquil_impl.hpp index a713e3b3..29ab6ac1 100644 --- a/opm/core/simulator/initStateEquil_impl.hpp +++ b/opm/core/simulator/initStateEquil_impl.hpp @@ -485,7 +485,7 @@ namespace Opm } } // namespace Details - namespace equil { + namespace Equil { template std::vector< std::vector > @@ -568,7 +568,7 @@ namespace Opm return press; } - } // namespace equil + } // namespace Equil } // namespace Opm #endif // OPM_INITSTATEEQUIL_IMPL_HEADER_INCLUDED diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index 4519f5f5..88bec072 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -64,27 +64,27 @@ BOOST_AUTO_TEST_CASE (PhasePressure) typedef Opm::BlackoilPropertiesBasic Props; Props props(param, G->dimensions, G->number_of_cells); - typedef Opm::equil::DensityCalculator RhoCalc; + typedef Opm::Equil::DensityCalculator RhoCalc; RhoCalc calc(props, 0); - Opm::equil::EquilRecord record = + Opm::Equil::EquilRecord record = { { 0 , 1e5 } , // Datum depth, pressure { 5 , 0 } , // Zwoc , Pcow_woc { 0 , 0 } // Zgoc , Pcgo_goc }; - Opm::equil::EquilReg + Opm::Equil::EquilReg region(record, calc, - Opm::equil::miscibility::NoMixing(), - Opm::equil::miscibility::NoMixing(), + Opm::Equil::Miscibility::NoMixing(), + Opm::Equil::Miscibility::NoMixing(), props.phaseUsage()); std::vector cells(G->number_of_cells); std::iota(cells.begin(), cells.end(), 0); const double grav = 10; - const PPress ppress = Opm::equil::phasePressures(*G, region, cells, grav); + const PPress ppress = Opm::Equil::phasePressures(*G, region, cells, grav); const int first = 0, last = G->number_of_cells - 1; const double reltol = 1.0e-8; @@ -118,10 +118,10 @@ BOOST_AUTO_TEST_CASE (CellSubset) typedef Opm::BlackoilPropertiesBasic Props; Props props(param, G->dimensions, G->number_of_cells); - typedef Opm::equil::DensityCalculator RhoCalc; + typedef Opm::Equil::DensityCalculator RhoCalc; RhoCalc calc(props, 0); - Opm::equil::EquilRecord record[] = + Opm::Equil::EquilRecord record[] = { { { 0 , 1e5 } , // Datum depth, pressure @@ -136,26 +136,26 @@ BOOST_AUTO_TEST_CASE (CellSubset) } }; - Opm::equil::EquilReg region[] = + Opm::Equil::EquilReg region[] = { - Opm::equil::EquilReg(record[0], calc, - Opm::equil::miscibility::NoMixing(), - Opm::equil::miscibility::NoMixing(), + Opm::Equil::EquilReg(record[0], calc, + Opm::Equil::Miscibility::NoMixing(), + Opm::Equil::Miscibility::NoMixing(), props.phaseUsage()) , - Opm::equil::EquilReg(record[0], calc, - Opm::equil::miscibility::NoMixing(), - Opm::equil::miscibility::NoMixing(), + Opm::Equil::EquilReg(record[0], calc, + Opm::Equil::Miscibility::NoMixing(), + Opm::Equil::Miscibility::NoMixing(), props.phaseUsage()) , - Opm::equil::EquilReg(record[1], calc, - Opm::equil::miscibility::NoMixing(), - Opm::equil::miscibility::NoMixing(), + Opm::Equil::EquilReg(record[1], calc, + Opm::Equil::Miscibility::NoMixing(), + Opm::Equil::Miscibility::NoMixing(), props.phaseUsage()) , - Opm::equil::EquilReg(record[1], calc, - Opm::equil::miscibility::NoMixing(), - Opm::equil::miscibility::NoMixing(), + Opm::Equil::EquilReg(record[1], calc, + Opm::Equil::Miscibility::NoMixing(), + Opm::Equil::Miscibility::NoMixing(), props.phaseUsage()) }; @@ -187,7 +187,7 @@ BOOST_AUTO_TEST_CASE (CellSubset) const int rno = int(r - cells.begin()); const double grav = 10; const PPress p = - Opm::equil::phasePressures(*G, region[rno], *r, grav); + Opm::Equil::phasePressures(*G, region[rno], *r, grav); PVal::size_type i = 0; for (std::vector::const_iterator @@ -233,10 +233,10 @@ BOOST_AUTO_TEST_CASE (RegMapping) typedef Opm::BlackoilPropertiesBasic Props; Props props(param, G->dimensions, G->number_of_cells); - typedef Opm::equil::DensityCalculator RhoCalc; + typedef Opm::Equil::DensityCalculator RhoCalc; RhoCalc calc(props, 0); - Opm::equil::EquilRecord record[] = + Opm::Equil::EquilRecord record[] = { { { 0 , 1e5 } , // Datum depth, pressure @@ -251,26 +251,26 @@ BOOST_AUTO_TEST_CASE (RegMapping) } }; - Opm::equil::EquilReg region[] = + Opm::Equil::EquilReg region[] = { - Opm::equil::EquilReg(record[0], calc, - Opm::equil::miscibility::NoMixing(), - Opm::equil::miscibility::NoMixing(), + Opm::Equil::EquilReg(record[0], calc, + Opm::Equil::Miscibility::NoMixing(), + Opm::Equil::Miscibility::NoMixing(), props.phaseUsage()) , - Opm::equil::EquilReg(record[0], calc, - Opm::equil::miscibility::NoMixing(), - Opm::equil::miscibility::NoMixing(), + Opm::Equil::EquilReg(record[0], calc, + Opm::Equil::Miscibility::NoMixing(), + Opm::Equil::Miscibility::NoMixing(), props.phaseUsage()) , - Opm::equil::EquilReg(record[1], calc, - Opm::equil::miscibility::NoMixing(), - Opm::equil::miscibility::NoMixing(), + Opm::Equil::EquilReg(record[1], calc, + Opm::Equil::Miscibility::NoMixing(), + Opm::Equil::Miscibility::NoMixing(), props.phaseUsage()) , - Opm::equil::EquilReg(record[1], calc, - Opm::equil::miscibility::NoMixing(), - Opm::equil::miscibility::NoMixing(), + Opm::Equil::EquilReg(record[1], calc, + Opm::Equil::Miscibility::NoMixing(), + Opm::Equil::Miscibility::NoMixing(), props.phaseUsage()) }; @@ -298,7 +298,7 @@ BOOST_AUTO_TEST_CASE (RegMapping) const int rno = r; const double grav = 10; const PPress p = - Opm::equil::phasePressures(*G, region[rno], rng, grav); + Opm::Equil::phasePressures(*G, region[rno], rng, grav); PVal::size_type i = 0; for (Opm::RegionMapping<>::CellRange::const_iterator @@ -328,7 +328,7 @@ BOOST_AUTO_TEST_CASE (DeckAllDead) grid(create_grid_cart3d(1, 1, 10), destroy_grid); Opm::EclipseGridParser deck("deadfluids.DATA"); Opm::BlackoilPropertiesFromDeck props(deck, *grid, false); - Opm::equil::DeckDependent::PhasePressureSaturationComputer comp(props, deck, *grid, 10.0); + Opm::Equil::DeckDependent::PhasePressureSaturationComputer comp(props, deck, *grid, 10.0); const auto& pressures = comp.press(); BOOST_REQUIRE(pressures.size() == 3); BOOST_REQUIRE(int(pressures[0].size()) == grid->number_of_cells); @@ -364,7 +364,7 @@ BOOST_AUTO_TEST_CASE (CapillaryInversion) const std::vector s = { 0.2, 0.2, 0.2, 0.466666666666, 0.733333333333, 1.0, 1.0, 1.0, 1.0 }; BOOST_REQUIRE(pc.size() == s.size()); for (size_t i = 0; i < pc.size(); ++i) { - const double s_computed = Opm::equil::satFromPc(props, phase, cell, pc[i], increasing); + const double s_computed = Opm::Equil::satFromPc(props, phase, cell, pc[i], increasing); BOOST_CHECK_CLOSE(s_computed, s[i], reltol); } } @@ -377,7 +377,7 @@ BOOST_AUTO_TEST_CASE (CapillaryInversion) const std::vector s = { 0.8, 0.8, 0.8, 0.533333333333, 0.266666666666, 0.0, 0.0, 0.0, 0.0 }; BOOST_REQUIRE(pc.size() == s.size()); for (size_t i = 0; i < pc.size(); ++i) { - const double s_computed = Opm::equil::satFromPc(props, phase, cell, pc[i], increasing); + const double s_computed = Opm::Equil::satFromPc(props, phase, cell, pc[i], increasing); BOOST_CHECK_CLOSE(s_computed, s[i], reltol); } } @@ -390,7 +390,7 @@ BOOST_AUTO_TEST_CASE (CapillaryInversion) const std::vector s = { 0.2, 0.333333333333, 0.6, 0.866666666666, 1.0 }; BOOST_REQUIRE(pc.size() == s.size()); for (size_t i = 0; i < pc.size(); ++i) { - const double s_computed = Opm::equil::satFromSumOfPcs(props, water, gas, cell, pc[i]); + const double s_computed = Opm::Equil::satFromSumOfPcs(props, water, gas, cell, pc[i]); BOOST_CHECK_CLOSE(s_computed, s[i], reltol); } } @@ -405,7 +405,7 @@ BOOST_AUTO_TEST_CASE (DeckWithCapillary) Opm::EclipseGridParser deck("capillary.DATA"); Opm::BlackoilPropertiesFromDeck props(deck, grid, false); - Opm::equil::DeckDependent::PhasePressureSaturationComputer comp(props, deck, grid, 10.0); + Opm::Equil::DeckDependent::PhasePressureSaturationComputer comp(props, deck, grid, 10.0); const auto& pressures = comp.press(); BOOST_REQUIRE(pressures.size() == 3); BOOST_REQUIRE(int(pressures[0].size()) == grid.number_of_cells); @@ -443,7 +443,7 @@ BOOST_AUTO_TEST_CASE (DeckWithCapillaryOverlap) Opm::EclipseGridParser deck("capillary_overlap.DATA"); Opm::BlackoilPropertiesFromDeck props(deck, grid, false); - Opm::equil::DeckDependent::PhasePressureSaturationComputer comp(props, deck, grid, 10.0); + Opm::Equil::DeckDependent::PhasePressureSaturationComputer comp(props, deck, grid, 10.0); const auto& pressures = comp.press(); BOOST_REQUIRE(pressures.size() == 3); BOOST_REQUIRE(int(pressures[0].size()) == grid.number_of_cells); From 4a8c0dd955c43fc91f4f057504300762f4251aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Mon, 24 Feb 2014 16:09:04 +0100 Subject: [PATCH 30/81] Moved equilibration utilities to separate file. --- CMakeLists_files.cmake | 1 + opm/core/simulator/EquilibrationHelpers.hpp | 631 ++++++++++++++++++++ opm/core/simulator/initStateEquil.hpp | 540 +---------------- 3 files changed, 633 insertions(+), 539 deletions(-) create mode 100644 opm/core/simulator/EquilibrationHelpers.hpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 010cb7f4..db22a529 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -318,6 +318,7 @@ list (APPEND PUBLIC_HEADER_FILES opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp opm/core/props/satfunc/SaturationPropsInterface.hpp opm/core/simulator/BlackoilState.hpp + opm/core/simulator/EquilibrationHelpers.hpp opm/core/simulator/SimulatorCompressibleTwophase.hpp opm/core/simulator/SimulatorIncompTwophase.hpp opm/core/simulator/SimulatorOutput.hpp diff --git a/opm/core/simulator/EquilibrationHelpers.hpp b/opm/core/simulator/EquilibrationHelpers.hpp new file mode 100644 index 00000000..442702d6 --- /dev/null +++ b/opm/core/simulator/EquilibrationHelpers.hpp @@ -0,0 +1,631 @@ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#ifndef OPM_EQUILIBRATIONHELPERS_HEADER_INCLUDED +#define OPM_EQUILIBRATIONHELPERS_HEADER_INCLUDED + +#include +#include +#include +#include +#include + + +/* +---- synopsis of EquilibrationHelpers.hpp ---- + +namespace Opm +{ + namespace Equil { + + template + class DensityCalculator; + + template <> + class DensityCalculator< BlackoilPropertiesInterface >; + + namespace Miscibility { + struct NoMixing; + class RsVD; + class RsSatAtContact; + } + + struct EquilRecord; + + template + class EquilReg; + + + struct PcEq; + + inline double satFromPc(const BlackoilPropertiesInterface& props, + const int phase, + const int cell, + const double target_pc, + const bool increasing = false); + struct PcEqSum + inline double satFromSumOfPcs(const BlackoilPropertiesInterface& props, + const int phase1, + const int phase2, + const int cell, + const double target_pc); + } // namespace Equil +} // namespace Opm + +---- end of synopsis of EquilibrationHelpers.hpp ---- +*/ + + +namespace Opm +{ + /** + * Types and routines that collectively implement a basic + * ECLIPSE-style equilibration-based initialisation scheme. + * + * This namespace is intentionally nested to avoid name clashes + * with other parts of OPM. + */ + namespace Equil { + + + template + class DensityCalculator; + + /** + * Facility for calculating phase densities based on the + * BlackoilPropertiesInterface. + * + * Implements the crucial operator()(p,svol) + * function that is expected by class EquilReg. + */ + template <> + class DensityCalculator< BlackoilPropertiesInterface > { + public: + /** + * Constructor. + * + * \param[in] props Implementation of the + * BlackoilPropertiesInterface. + * + * \param[in] c Single cell used as a representative cell + * in a PVT region. + */ + DensityCalculator(const BlackoilPropertiesInterface& props, + const int c) + : props_(props) + , c_(1, c) + { + } + + /** + * Compute phase densities of all phases at phase point + * given by (pressure, surface volume) tuple. + * + * \param[in] p Fluid pressure. + * + * \param[in] z Surface volumes of all phases. + * + * \return Phase densities at phase point. + */ + std::vector + operator()(const double p, + const std::vector& z) const + { + const int np = props_.numPhases(); + std::vector A(np * np, 0); + + assert (z.size() == std::vector::size_type(np)); + + double* dAdp = 0; + props_.matrix(1, &p, &z[0], &c_[0], &A[0], dAdp); + + std::vector rho(np, 0.0); + props_.density(1, &A[0], &rho[0]); + + return rho; + } + + private: + const BlackoilPropertiesInterface& props_; + const std::vector c_; + }; + + /** + * Types and routines relating to phase mixing in + * equilibration calculations. + */ + namespace Miscibility { + /** + * Type that implements "no phase mixing" policy. + */ + struct NoMixing { + /** + * Function call. + * + * \param[in] depth Depth at which to calculate RS + * value. + * + * \param[in] press Pressure at which to calculate RS + * value. + * + * \return Dissolved gas-oil ratio (RS) at depth @c + * depth and pressure @c press. In "no mixing + * policy", this is identically zero. + */ + double + operator()(const double /* depth */, + const double /* press */) const + { + return 0.0; + } + }; + + /** + * Type that implements "dissolved gas-oil ratio" + * tabulated as a function of depth policy. Data + * typically taken from keyword 'RSVD'. + */ + class RsVD { + public: + /** + * Constructor. + * + * \param[in] depth Depth nodes. + * \param[in] rs Dissolved gas-oil ratio at @c depth. + */ + RsVD(const std::vector& depth, + const std::vector& rs) + : depth_(depth) + , rs_(rs) + { + } + + /** + * Function call. + * + * \param[in] depth Depth at which to calculate RS + * value. + * + * \param[in] press Pressure at which to calculate RS + * value. + * + * \return Dissolved gas-oil ratio (RS) at depth @c + * depth and pressure @c press. + */ + double + operator()(const double depth, + const double /* press */) const + { + return linearInterpolation(depth_, rs_, depth); + } + + private: + std::vector depth_; /**< Depth nodes */ + std::vector rs_; /**< Dissolved gas-oil ratio */ + }; + + + /** + * Class that implements "dissolved gas-oil ratio" (Rs) + * as function of depth and pressure as follows: + * + * 1. The Rs at the gas-oil contact is equal to the + * saturated Rs value, Rs_sat_contact. + * + * 2. The Rs elsewhere is equal to Rs_sat_contact, but + * constrained to the saturated value as given by the + * local pressure. + * + * This should yield Rs-values that are constant below the + * contact, and decreasing above the contact. + */ + class RsSatAtContact { + public: + /** + * Constructor. + * + * \param[in] props property object + * \param[in] cell any cell in the pvt region + * \param[in] p_contact oil pressure at the contact + */ + RsSatAtContact(const BlackoilPropertiesInterface& props, const int cell, const double p_contact) + : props_(props), cell_(cell) + { + auto pu = props_.phaseUsage(); + std::fill(z_, z_ + BlackoilPhases::MaxNumPhases, 0.0); + z_[pu.phase_pos[BlackoilPhases::Vapour]] = 1e100; + z_[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; + rs_sat_contact_ = satRs(p_contact); + } + + /** + * Function call. + * + * \param[in] depth Depth at which to calculate RS + * value. + * + * \param[in] press Pressure at which to calculate RS + * value. + * + * \return Dissolved gas-oil ratio (RS) at depth @c + * depth and pressure @c press. + */ + double + operator()(const double /* depth */, + const double press) const + { + return std::max(satRs(press), rs_sat_contact_); + } + + private: + const BlackoilPropertiesInterface& props_; + const int cell_; + double z_[BlackoilPhases::MaxNumPhases]; + double rs_sat_contact_; + mutable double A_[BlackoilPhases::MaxNumPhases * BlackoilPhases::MaxNumPhases]; + + double satRs(const double press) const + { + props_.matrix(1, &press, z_, &cell_, A_, 0); + // Rs/Bo is in the gas row and oil column of A_. + // 1/Bo is in the oil row and column. + // Recall also that it is stored in column-major order. + const int opos = props_.phaseUsage().phase_pos[BlackoilPhases::Liquid]; + const int gpos = props_.phaseUsage().phase_pos[BlackoilPhases::Vapour]; + const int np = props_.numPhases(); + return A_[np*opos + gpos] / A_[np*opos + opos]; + } + }; + } // namespace Miscibility + + + + /** + * Equilibration record. + * + * Layout and contents inspired by first six items of + * ECLIPSE's 'EQUIL' records. This is the minimum amount of + * input data needed to define phase pressures in an + * equilibration region. + * + * Data consists of three pairs of depth and pressure values: + * 1. main + * - @c depth Main datum depth. + * - @c press Pressure at datum depth. + * + * 2. woc + * - @c depth Depth of water-oil contact + * - @c press water-oil capillary pressure at water-oil contact. + * Capillary pressure defined as "P_oil - P_water". + * + * 3. goc + * - @c depth Depth of gas-oil contact + * - @c press Gas-oil capillary pressure at gas-oil contact. + * Capillary pressure defined as "P_gas - P_oil". + */ + struct EquilRecord { + struct { + double depth; + double press; + } main, woc, goc; + }; + + /** + * Aggregate information base of an equilibration region. + * + * Provides inquiry methods for retrieving depths of contacs + * and pressure values as well as a means of calculating fluid + * densities, dissolved gas-oil ratio and vapourised oil-gas + * ratios. + * + * \tparam DensCalc Type that provides access to a phase + * density calculation facility. Must implement an operator() + * declared as + * + * std::vector + * operator()(const double press, + * const std::vector& svol ) + * + * that calculates the phase densities of all phases in @c + * svol at fluid pressure @c press. + * + * \tparam RS Type that provides access to a calculator for + * (initial) dissolved gas-oil ratios as a function of depth + * and (oil) pressure. Must implement an operator() declared + * as + * + * double + * operator()(const double depth, + * const double press) + * + * that calculates the dissolved gas-oil ratio at depth @c + * depth and (oil) pressure @c press. + * + * \tparam RV Type that provides access to a calculator for + * (initial) vapourised oil-gas ratios as a function of depth + * and (gas) pressure. Must implement an operator() declared + * as + * + * double + * operator()(const double depth, + * const double press) + * + * that calculates the vapourised oil-gas ratio at depth @c + * depth and (gas) pressure @c press. + */ + template + class EquilReg { + public: + /** + * Constructor. + * + * \param[in] rec Equilibration data of current region. + * \param[in] density Density calculator of current region. + * \param[in] rs Calculator of dissolved gas-oil ratio. + * \param[in] rv Calculator of vapourised oil-gas ratio. + * \param[in] pu Summary of current active phases. + */ + EquilReg(const EquilRecord& rec, + const DensCalc& density, + const RS& rs, + const RV& rv, + const PhaseUsage& pu) + : rec_ (rec) + , density_(density) + , rs_ (rs) + , rv_ (rv) + , pu_ (pu) + { + } + + /** + * Type of density calculator. + */ + typedef DensCalc CalcDensity; + + /** + * Type of dissolved gas-oil ratio calculator. + */ + typedef RS CalcDissolution; + + /** + * Type of vapourised oil-gas ratio calculator. + */ + typedef RV CalcEvaporation; + + /** + * Datum depth in current region + */ + double datum() const { return this->rec_.main.depth; } + + /** + * Pressure at datum depth in current region. + */ + double pressure() const { return this->rec_.main.press; } + + /** + * Depth of water-oil contact. + */ + double zwoc() const { return this->rec_.woc .depth; } + + /** + * water-oil capillary pressure at water-oil contact. + * + * \return P_o - P_w at WOC. + */ + double pcow_woc() const { return this->rec_.woc .press; } + + /** + * Depth of gas-oil contact. + */ + double zgoc() const { return this->rec_.goc .depth; } + + /** + * Gas-oil capillary pressure at gas-oil contact. + * + * \return P_g - P_o at GOC. + */ + double pcgo_goc() const { return this->rec_.goc .press; } + + /** + * Retrieve phase density calculator of current region. + */ + const CalcDensity& + densityCalculator() const { return this->density_; } + + /** + * Retrieve dissolved gas-oil ratio calculator of current + * region. + */ + const CalcDissolution& + dissolutionCalculator() const { return this->rs_; } + + /** + * Retrieve vapourised oil-gas ratio calculator of current + * region. + */ + const CalcEvaporation& + evaporationCalculator() const { return this->rv_; } + + /** + * Retrieve active fluid phase summary. + */ + const PhaseUsage& + phaseUsage() const { return this->pu_; } + + private: + EquilRecord rec_; /**< Equilibration data */ + DensCalc density_; /**< Density calculator */ + RS rs_; /**< RS calculator */ + RV rv_; /**< RV calculator */ + PhaseUsage pu_; /**< Active phase summary */ + }; + + + + /// Functor for inverting capillary pressure function. + /// Function represented is + /// f(s) = pc(s) - target_pc + struct PcEq + { + PcEq(const BlackoilPropertiesInterface& props, + const int phase, + const int cell, + const double target_pc) + : props_(props), + phase_(phase), + cell_(cell), + target_pc_(target_pc) + { + std::fill(s_, s_ + BlackoilPhases::MaxNumPhases, 0.0); + std::fill(pc_, pc_ + BlackoilPhases::MaxNumPhases, 0.0); + } + double operator()(double s) const + { + s_[phase_] = s; + props_.capPress(1, s_, &cell_, pc_, 0); + return pc_[phase_] - target_pc_; + } + private: + const BlackoilPropertiesInterface& props_; + const int phase_; + const int cell_; + const int target_pc_; + mutable double s_[BlackoilPhases::MaxNumPhases]; + mutable double pc_[BlackoilPhases::MaxNumPhases]; + }; + + + + /// Compute saturation of some phase corresponding to a given + /// capillary pressure. + inline double satFromPc(const BlackoilPropertiesInterface& props, + const int phase, + const int cell, + const double target_pc, + const bool increasing = false) + { + // Find minimum and maximum saturations. + double sminarr[BlackoilPhases::MaxNumPhases]; + double smaxarr[BlackoilPhases::MaxNumPhases]; + props.satRange(1, &cell, sminarr, smaxarr); + const double s0 = increasing ? smaxarr[phase] : sminarr[phase]; + const double s1 = increasing ? sminarr[phase] : smaxarr[phase]; + + // Create the equation f(s) = pc(s) - target_pc + const PcEq f(props, phase, cell, target_pc); + const double f0 = f(s0); + const double f1 = f(s1); + if (f0 <= 0.0) { + return s0; + } else if (f1 > 0.0) { + return s1; + } else { + const int max_iter = 30; + const double tol = 1e-6; + int iter_used = -1; + typedef RegulaFalsi ScalarSolver; + const double sol = ScalarSolver::solve(f, std::min(s0, s1), std::max(s0, s1), max_iter, tol, iter_used); + return sol; + } + } + + + /// Functor for inverting a sum of capillary pressure functions. + /// Function represented is + /// f(s) = pc1(s) + pc2(1 - s) - target_pc + struct PcEqSum + { + PcEqSum(const BlackoilPropertiesInterface& props, + const int phase1, + const int phase2, + const int cell, + const double target_pc) + : props_(props), + phase1_(phase1), + phase2_(phase2), + cell_(cell), + target_pc_(target_pc) + { + std::fill(s_, s_ + BlackoilPhases::MaxNumPhases, 0.0); + std::fill(pc_, pc_ + BlackoilPhases::MaxNumPhases, 0.0); + } + double operator()(double s) const + { + s_[phase1_] = s; + s_[phase2_] = 1.0 - s; + props_.capPress(1, s_, &cell_, pc_, 0); + return pc_[phase1_] + pc_[phase2_] - target_pc_; + } + private: + const BlackoilPropertiesInterface& props_; + const int phase1_; + const int phase2_; + const int cell_; + const int target_pc_; + mutable double s_[BlackoilPhases::MaxNumPhases]; + mutable double pc_[BlackoilPhases::MaxNumPhases]; + }; + + + + + /// Compute saturation of some phase corresponding to a given + /// capillary pressure, where the capillary pressure function + /// is given as a sum of two other functions. + inline double satFromSumOfPcs(const BlackoilPropertiesInterface& props, + const int phase1, + const int phase2, + const int cell, + const double target_pc) + { + // Find minimum and maximum saturations. + double sminarr[BlackoilPhases::MaxNumPhases]; + double smaxarr[BlackoilPhases::MaxNumPhases]; + props.satRange(1, &cell, sminarr, smaxarr); + const double smin = sminarr[phase1]; + const double smax = smaxarr[phase1]; + + // Create the equation f(s) = pc1(s) + pc2(1-s) - target_pc + const PcEqSum f(props, phase1, phase2, cell, target_pc); + const double f0 = f(smin); + const double f1 = f(smax); + if (f0 <= 0.0) { + return smin; + } else if (f1 > 0.0) { + return smax; + } else { + const int max_iter = 30; + const double tol = 1e-6; + int iter_used = -1; + typedef RegulaFalsi ScalarSolver; + const double sol = ScalarSolver::solve(f, smin, smax, max_iter, tol, iter_used); + return sol; + } + } + + } // namespace Equil +} // namespace Opm + + +#endif // OPM_EQUILIBRATIONHELPERS_HEADER_INCLUDED diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 1f6f11c5..8e049017 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -20,6 +20,7 @@ #ifndef OPM_INITSTATEEQUIL_HEADER_INCLUDED #define OPM_INITSTATEEQUIL_HEADER_INCLUDED +#include #include #include #include @@ -50,545 +51,6 @@ namespace Opm * with other parts of OPM. */ namespace Equil { - template - class DensityCalculator; - - /** - * Facility for calculating phase densities based on the - * BlackoilPropertiesInterface. - * - * Implements the crucial operator()(p,svol) - * function that is expected by class EquilReg. - */ - template <> - class DensityCalculator< BlackoilPropertiesInterface > { - public: - /** - * Constructor. - * - * \param[in] props Implementation of the - * BlackoilPropertiesInterface. - * - * \param[in] c Single cell used as a representative cell - * in a PVT region. - */ - DensityCalculator(const BlackoilPropertiesInterface& props, - const int c) - : props_(props) - , c_(1, c) - { - } - - /** - * Compute phase densities of all phases at phase point - * given by (pressure, surface volume) tuple. - * - * \param[in] p Fluid pressure. - * - * \param[in] z Surface volumes of all phases. - * - * \return Phase densities at phase point. - */ - std::vector - operator()(const double p, - const std::vector& z) const - { - const int np = props_.numPhases(); - std::vector A(np * np, 0); - - assert (z.size() == std::vector::size_type(np)); - - double* dAdp = 0; - props_.matrix(1, &p, &z[0], &c_[0], &A[0], dAdp); - - std::vector rho(np, 0.0); - props_.density(1, &A[0], &rho[0]); - - return rho; - } - - private: - const BlackoilPropertiesInterface& props_; - const std::vector c_; - }; - - /** - * Types and routines relating to phase mixing in - * equilibration calculations. - */ - namespace Miscibility { - /** - * Type that implements "no phase mixing" policy. - */ - struct NoMixing { - /** - * Function call. - * - * \param[in] depth Depth at which to calculate RS - * value. - * - * \param[in] press Pressure at which to calculate RS - * value. - * - * \return Dissolved gas-oil ratio (RS) at depth @c - * depth and pressure @c press. In "no mixing - * policy", this is identically zero. - */ - double - operator()(const double /* depth */, - const double /* press */) const - { - return 0.0; - } - }; - - /** - * Type that implements "dissolved gas-oil ratio" - * tabulated as a function of depth policy. Data - * typically taken from keyword 'RSVD'. - */ - class RsVD { - public: - /** - * Constructor. - * - * \param[in] depth Depth nodes. - * \param[in] rs Dissolved gas-oil ratio at @c depth. - */ - RsVD(const std::vector& depth, - const std::vector& rs) - : depth_(depth) - , rs_(rs) - { - } - - /** - * Function call. - * - * \param[in] depth Depth at which to calculate RS - * value. - * - * \param[in] press Pressure at which to calculate RS - * value. - * - * \return Dissolved gas-oil ratio (RS) at depth @c - * depth and pressure @c press. - */ - double - operator()(const double depth, - const double /* press */) const - { - return linearInterpolation(depth_, rs_, depth); - } - - private: - std::vector depth_; /**< Depth nodes */ - std::vector rs_; /**< Dissolved gas-oil ratio */ - }; - - - /** - * Class that implements "dissolved gas-oil ratio" (Rs) - * as function of depth and pressure as follows: - * - * 1. The Rs at the gas-oil contact is equal to the - * saturated Rs value, Rs_sat_contact. - * - * 2. The Rs elsewhere is equal to Rs_sat_contact, but - * constrained to the saturated value as given by the - * local pressure. - * - * This should yield Rs-values that are constant below the - * contact, and decreasing above the contact. - */ - class RsSatAtContact { - public: - /** - * Constructor. - * - * \param[in] props property object - * \param[in] cell any cell in the pvt region - * \param[in] p_contact oil pressure at the contact - */ - RsSatAtContact(const BlackoilPropertiesInterface& props, const int cell, const double p_contact) - : props_(props), cell_(cell) - { - auto pu = props_.phaseUsage(); - std::fill(z_, z_ + BlackoilPhases::MaxNumPhases, 0.0); - z_[pu.phase_pos[BlackoilPhases::Vapour]] = 1e100; - z_[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; - rs_sat_contact_ = satRs(p_contact); - } - - /** - * Function call. - * - * \param[in] depth Depth at which to calculate RS - * value. - * - * \param[in] press Pressure at which to calculate RS - * value. - * - * \return Dissolved gas-oil ratio (RS) at depth @c - * depth and pressure @c press. - */ - double - operator()(const double /* depth */, - const double press) const - { - return std::max(satRs(press), rs_sat_contact_); - } - - private: - const BlackoilPropertiesInterface& props_; - const int cell_; - double z_[BlackoilPhases::MaxNumPhases]; - double rs_sat_contact_; - mutable double A_[BlackoilPhases::MaxNumPhases * BlackoilPhases::MaxNumPhases]; - - double satRs(const double press) const - { - props_.matrix(1, &press, z_, &cell_, A_, 0); - // Rs/Bo is in the gas row and oil column of A_. - // 1/Bo is in the oil row and column. - // Recall also that it is stored in column-major order. - const int opos = props_.phaseUsage().phase_pos[BlackoilPhases::Liquid]; - const int gpos = props_.phaseUsage().phase_pos[BlackoilPhases::Vapour]; - const int np = props_.numPhases(); - return A_[np*opos + gpos] / A_[np*opos + opos]; - } - }; - } // namespace Miscibility - - - - /** - * Equilibration record. - * - * Layout and contents inspired by first six items of - * ECLIPSE's 'EQUIL' records. This is the minimum amount of - * input data needed to define phase pressures in an - * equilibration region. - * - * Data consists of three pairs of depth and pressure values: - * 1. main - * - @c depth Main datum depth. - * - @c press Pressure at datum depth. - * - * 2. woc - * - @c depth Depth of water-oil contact - * - @c press water-oil capillary pressure at water-oil contact. - * Capillary pressure defined as "P_oil - P_water". - * - * 3. goc - * - @c depth Depth of gas-oil contact - * - @c press Gas-oil capillary pressure at gas-oil contact. - * Capillary pressure defined as "P_gas - P_oil". - */ - struct EquilRecord { - struct { - double depth; - double press; - } main, woc, goc; - }; - - /** - * Aggregate information base of an equilibration region. - * - * Provides inquiry methods for retrieving depths of contacs - * and pressure values as well as a means of calculating fluid - * densities, dissolved gas-oil ratio and vapourised oil-gas - * ratios. - * - * \tparam DensCalc Type that provides access to a phase - * density calculation facility. Must implement an operator() - * declared as - * - * std::vector - * operator()(const double press, - * const std::vector& svol ) - * - * that calculates the phase densities of all phases in @c - * svol at fluid pressure @c press. - * - * \tparam RS Type that provides access to a calculator for - * (initial) dissolved gas-oil ratios as a function of depth - * and (oil) pressure. Must implement an operator() declared - * as - * - * double - * operator()(const double depth, - * const double press) - * - * that calculates the dissolved gas-oil ratio at depth @c - * depth and (oil) pressure @c press. - * - * \tparam RV Type that provides access to a calculator for - * (initial) vapourised oil-gas ratios as a function of depth - * and (gas) pressure. Must implement an operator() declared - * as - * - * double - * operator()(const double depth, - * const double press) - * - * that calculates the vapourised oil-gas ratio at depth @c - * depth and (gas) pressure @c press. - */ - template - class EquilReg { - public: - /** - * Constructor. - * - * \param[in] rec Equilibration data of current region. - * \param[in] density Density calculator of current region. - * \param[in] rs Calculator of dissolved gas-oil ratio. - * \param[in] rv Calculator of vapourised oil-gas ratio. - * \param[in] pu Summary of current active phases. - */ - EquilReg(const EquilRecord& rec, - const DensCalc& density, - const RS& rs, - const RV& rv, - const PhaseUsage& pu) - : rec_ (rec) - , density_(density) - , rs_ (rs) - , rv_ (rv) - , pu_ (pu) - { - } - - /** - * Type of density calculator. - */ - typedef DensCalc CalcDensity; - - /** - * Type of dissolved gas-oil ratio calculator. - */ - typedef RS CalcDissolution; - - /** - * Type of vapourised oil-gas ratio calculator. - */ - typedef RV CalcEvaporation; - - /** - * Datum depth in current region - */ - double datum() const { return this->rec_.main.depth; } - - /** - * Pressure at datum depth in current region. - */ - double pressure() const { return this->rec_.main.press; } - - /** - * Depth of water-oil contact. - */ - double zwoc() const { return this->rec_.woc .depth; } - - /** - * water-oil capillary pressure at water-oil contact. - * - * \return P_o - P_w at WOC. - */ - double pcow_woc() const { return this->rec_.woc .press; } - - /** - * Depth of gas-oil contact. - */ - double zgoc() const { return this->rec_.goc .depth; } - - /** - * Gas-oil capillary pressure at gas-oil contact. - * - * \return P_g - P_o at GOC. - */ - double pcgo_goc() const { return this->rec_.goc .press; } - - /** - * Retrieve phase density calculator of current region. - */ - const CalcDensity& - densityCalculator() const { return this->density_; } - - /** - * Retrieve dissolved gas-oil ratio calculator of current - * region. - */ - const CalcDissolution& - dissolutionCalculator() const { return this->rs_; } - - /** - * Retrieve vapourised oil-gas ratio calculator of current - * region. - */ - const CalcEvaporation& - evaporationCalculator() const { return this->rv_; } - - /** - * Retrieve active fluid phase summary. - */ - const PhaseUsage& - phaseUsage() const { return this->pu_; } - - private: - EquilRecord rec_; /**< Equilibration data */ - DensCalc density_; /**< Density calculator */ - RS rs_; /**< RS calculator */ - RV rv_; /**< RV calculator */ - PhaseUsage pu_; /**< Active phase summary */ - }; - - - - /// Functor for inverting capillary pressure function. - /// Function represented is - /// f(s) = pc(s) - target_pc - struct PcEq - { - PcEq(const BlackoilPropertiesInterface& props, - const int phase, - const int cell, - const double target_pc) - : props_(props), - phase_(phase), - cell_(cell), - target_pc_(target_pc) - { - std::fill(s_, s_ + BlackoilPhases::MaxNumPhases, 0.0); - std::fill(pc_, pc_ + BlackoilPhases::MaxNumPhases, 0.0); - } - double operator()(double s) const - { - s_[phase_] = s; - props_.capPress(1, s_, &cell_, pc_, 0); - return pc_[phase_] - target_pc_; - } - private: - const BlackoilPropertiesInterface& props_; - const int phase_; - const int cell_; - const int target_pc_; - mutable double s_[BlackoilPhases::MaxNumPhases]; - mutable double pc_[BlackoilPhases::MaxNumPhases]; - }; - - - - /// Compute saturation of some phase corresponding to a given - /// capillary pressure. - inline double satFromPc(const BlackoilPropertiesInterface& props, - const int phase, - const int cell, - const double target_pc, - const bool increasing = false) - { - // Find minimum and maximum saturations. - double sminarr[BlackoilPhases::MaxNumPhases]; - double smaxarr[BlackoilPhases::MaxNumPhases]; - props.satRange(1, &cell, sminarr, smaxarr); - const double s0 = increasing ? smaxarr[phase] : sminarr[phase]; - const double s1 = increasing ? sminarr[phase] : smaxarr[phase]; - - // Create the equation f(s) = pc(s) - target_pc - const PcEq f(props, phase, cell, target_pc); - const double f0 = f(s0); - const double f1 = f(s1); - if (f0 <= 0.0) { - return s0; - } else if (f1 > 0.0) { - return s1; - } else { - const int max_iter = 30; - const double tol = 1e-6; - int iter_used = -1; - typedef RegulaFalsi ScalarSolver; - const double sol = ScalarSolver::solve(f, std::min(s0, s1), std::max(s0, s1), max_iter, tol, iter_used); - return sol; - } - } - - - /// Functor for inverting a sum of capillary pressure functions. - /// Function represented is - /// f(s) = pc1(s) + pc2(1 - s) - target_pc - struct PcEqSum - { - PcEqSum(const BlackoilPropertiesInterface& props, - const int phase1, - const int phase2, - const int cell, - const double target_pc) - : props_(props), - phase1_(phase1), - phase2_(phase2), - cell_(cell), - target_pc_(target_pc) - { - std::fill(s_, s_ + BlackoilPhases::MaxNumPhases, 0.0); - std::fill(pc_, pc_ + BlackoilPhases::MaxNumPhases, 0.0); - } - double operator()(double s) const - { - s_[phase1_] = s; - s_[phase2_] = 1.0 - s; - props_.capPress(1, s_, &cell_, pc_, 0); - return pc_[phase1_] + pc_[phase2_] - target_pc_; - } - private: - const BlackoilPropertiesInterface& props_; - const int phase1_; - const int phase2_; - const int cell_; - const int target_pc_; - mutable double s_[BlackoilPhases::MaxNumPhases]; - mutable double pc_[BlackoilPhases::MaxNumPhases]; - }; - - - - - /// Compute saturation of some phase corresponding to a given - /// capillary pressure, where the capillary pressure function - /// is given as a sum of two other functions. - inline double satFromSumOfPcs(const BlackoilPropertiesInterface& props, - const int phase1, - const int phase2, - const int cell, - const double target_pc) - { - // Find minimum and maximum saturations. - double sminarr[BlackoilPhases::MaxNumPhases]; - double smaxarr[BlackoilPhases::MaxNumPhases]; - props.satRange(1, &cell, sminarr, smaxarr); - const double smin = sminarr[phase1]; - const double smax = smaxarr[phase1]; - - // Create the equation f(s) = pc1(s) + pc2(1-s) - target_pc - const PcEqSum f(props, phase1, phase2, cell, target_pc); - const double f0 = f(smin); - const double f1 = f(smax); - if (f0 <= 0.0) { - return smin; - } else if (f1 > 0.0) { - return smax; - } else { - const int max_iter = 30; - const double tol = 1e-6; - int iter_used = -1; - typedef RegulaFalsi ScalarSolver; - const double sol = ScalarSolver::solve(f, smin, smax, max_iter, tol, iter_used); - return sol; - } - } - - /** * Compute initial phase pressures by means of equilibration. From c80ffbefbb188a266a8c9a9f0b4e7c9d1aa2bd71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Mon, 24 Feb 2014 16:11:50 +0100 Subject: [PATCH 31/81] Prune includes. --- opm/core/simulator/initStateEquil.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 8e049017..9be77685 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -24,9 +24,7 @@ #include #include #include -#include #include -#include #include #include From 75ca41805e27e9fc564fcad553a018b6657d1f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Wed, 26 Feb 2014 14:16:51 +0100 Subject: [PATCH 32/81] Made NoMixing a class. For uniformity with its sibling classes. --- opm/core/simulator/EquilibrationHelpers.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/opm/core/simulator/EquilibrationHelpers.hpp b/opm/core/simulator/EquilibrationHelpers.hpp index 442702d6..316b435b 100644 --- a/opm/core/simulator/EquilibrationHelpers.hpp +++ b/opm/core/simulator/EquilibrationHelpers.hpp @@ -41,7 +41,7 @@ namespace Opm class DensityCalculator< BlackoilPropertiesInterface >; namespace Miscibility { - struct NoMixing; + class NoMixing; class RsVD; class RsSatAtContact; } @@ -156,7 +156,8 @@ namespace Opm /** * Type that implements "no phase mixing" policy. */ - struct NoMixing { + class NoMixing { + public: /** * Function call. * From 8ef54b7c6436882971561bb587ab3ff8a5a29c33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Wed, 26 Feb 2014 14:47:24 +0100 Subject: [PATCH 33/81] Made phase mixing functors a class hierarchy. In summary: - added RsFunction (base class), - made NoMixing, RsVD, RsSatAtContact inherit RsFunction, - RS and RV are no longer template arguments for EquilReg class, - EquilReg constructor now takes two shared_ptr, - use of constructor updated, mostly using make_shared. --- opm/core/simulator/EquilibrationHelpers.hpp | 84 +++++++++++---------- opm/core/simulator/initStateEquil.hpp | 6 +- tests/test_equil.cpp | 36 ++++----- 3 files changed, 65 insertions(+), 61 deletions(-) diff --git a/opm/core/simulator/EquilibrationHelpers.hpp b/opm/core/simulator/EquilibrationHelpers.hpp index 316b435b..0588f346 100644 --- a/opm/core/simulator/EquilibrationHelpers.hpp +++ b/opm/core/simulator/EquilibrationHelpers.hpp @@ -26,6 +26,8 @@ #include #include +#include + /* ---- synopsis of EquilibrationHelpers.hpp ---- @@ -41,6 +43,7 @@ namespace Opm class DensityCalculator< BlackoilPropertiesInterface >; namespace Miscibility { + class RsFunction; class NoMixing; class RsVD; class RsSatAtContact; @@ -48,9 +51,7 @@ namespace Opm struct EquilRecord; - template + template class EquilReg; @@ -148,15 +149,40 @@ namespace Opm const std::vector c_; }; + /** * Types and routines relating to phase mixing in * equilibration calculations. */ namespace Miscibility { + + /** + * Base class for phase mixing functions. + */ + class RsFunction + { + public: + /** + * Function call operator. + * + * \param[in] depth Depth at which to calculate RS + * value. + * + * \param[in] press Pressure at which to calculate RS + * value. + * + * \return Dissolved gas-oil ratio (RS) at depth @c + * depth and pressure @c press. + */ + virtual double operator()(const double depth, + const double press) const = 0; + }; + + /** * Type that implements "no phase mixing" policy. */ - class NoMixing { + class NoMixing : public RsFunction { public: /** * Function call. @@ -179,12 +205,13 @@ namespace Opm } }; + /** * Type that implements "dissolved gas-oil ratio" * tabulated as a function of depth policy. Data * typically taken from keyword 'RSVD'. */ - class RsVD { + class RsVD : public RsFunction { public: /** * Constructor. @@ -238,7 +265,7 @@ namespace Opm * This should yield Rs-values that are constant below the * contact, and decreasing above the contact. */ - class RsSatAtContact { + class RsSatAtContact : public RsFunction { public: /** * Constructor. @@ -295,6 +322,7 @@ namespace Opm return A_[np*opos + gpos] / A_[np*opos + opos]; } }; + } // namespace Miscibility @@ -347,34 +375,8 @@ namespace Opm * * that calculates the phase densities of all phases in @c * svol at fluid pressure @c press. - * - * \tparam RS Type that provides access to a calculator for - * (initial) dissolved gas-oil ratios as a function of depth - * and (oil) pressure. Must implement an operator() declared - * as - * - * double - * operator()(const double depth, - * const double press) - * - * that calculates the dissolved gas-oil ratio at depth @c - * depth and (oil) pressure @c press. - * - * \tparam RV Type that provides access to a calculator for - * (initial) vapourised oil-gas ratios as a function of depth - * and (gas) pressure. Must implement an operator() declared - * as - * - * double - * operator()(const double depth, - * const double press) - * - * that calculates the vapourised oil-gas ratio at depth @c - * depth and (gas) pressure @c press. */ - template + template class EquilReg { public: /** @@ -388,8 +390,8 @@ namespace Opm */ EquilReg(const EquilRecord& rec, const DensCalc& density, - const RS& rs, - const RV& rv, + std::shared_ptr rs, + std::shared_ptr rv, const PhaseUsage& pu) : rec_ (rec) , density_(density) @@ -407,12 +409,12 @@ namespace Opm /** * Type of dissolved gas-oil ratio calculator. */ - typedef RS CalcDissolution; + typedef Miscibility::RsFunction CalcDissolution; /** * Type of vapourised oil-gas ratio calculator. */ - typedef RV CalcEvaporation; + typedef Miscibility::RsFunction CalcEvaporation; /** * Datum depth in current region @@ -459,14 +461,14 @@ namespace Opm * region. */ const CalcDissolution& - dissolutionCalculator() const { return this->rs_; } + dissolutionCalculator() const { return *this->rs_; } /** * Retrieve vapourised oil-gas ratio calculator of current * region. */ const CalcEvaporation& - evaporationCalculator() const { return this->rv_; } + evaporationCalculator() const { return *this->rv_; } /** * Retrieve active fluid phase summary. @@ -477,8 +479,8 @@ namespace Opm private: EquilRecord rec_; /**< Equilibration data */ DensCalc density_; /**< Density calculator */ - RS rs_; /**< RS calculator */ - RV rv_; /**< RV calculator */ + std::shared_ptr rs_; /**< RS calculator */ + std::shared_ptr rv_; /**< RV calculator */ PhaseUsage pu_; /**< Active phase summary */ }; diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 9be77685..aa9f7862 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -296,7 +296,8 @@ namespace Opm const int repcell = *cells.begin(); const RhoCalc calc(props, repcell); - const EqReg eqreg(rec[r], calc, NoMix(), NoMix(), + const EqReg eqreg(rec[r], calc, + std::make_shared(), std::make_shared(), props.phaseUsage()); const PPress& res = phasePressures(G, eqreg, cells, grav); @@ -334,7 +335,8 @@ namespace Opm const int repcell = *cells.begin(); const RhoCalc calc(props, repcell); - const EqReg eqreg(rec[r], calc, NoMix(), NoMix(), + const EqReg eqreg(rec[r], calc, + std::make_shared(), std::make_shared(), props.phaseUsage()); const PPress press = phasePressures(G, eqreg, cells, grav); diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index 88bec072..5a5e9019 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -76,8 +76,8 @@ BOOST_AUTO_TEST_CASE (PhasePressure) Opm::Equil::EquilReg region(record, calc, - Opm::Equil::Miscibility::NoMixing(), - Opm::Equil::Miscibility::NoMixing(), + std::make_shared(), + std::make_shared(), props.phaseUsage()); std::vector cells(G->number_of_cells); @@ -139,23 +139,23 @@ BOOST_AUTO_TEST_CASE (CellSubset) Opm::Equil::EquilReg region[] = { Opm::Equil::EquilReg(record[0], calc, - Opm::Equil::Miscibility::NoMixing(), - Opm::Equil::Miscibility::NoMixing(), + std::make_shared(), + std::make_shared(), props.phaseUsage()) , Opm::Equil::EquilReg(record[0], calc, - Opm::Equil::Miscibility::NoMixing(), - Opm::Equil::Miscibility::NoMixing(), + std::make_shared(), + std::make_shared(), props.phaseUsage()) , Opm::Equil::EquilReg(record[1], calc, - Opm::Equil::Miscibility::NoMixing(), - Opm::Equil::Miscibility::NoMixing(), + std::make_shared(), + std::make_shared(), props.phaseUsage()) , Opm::Equil::EquilReg(record[1], calc, - Opm::Equil::Miscibility::NoMixing(), - Opm::Equil::Miscibility::NoMixing(), + std::make_shared(), + std::make_shared(), props.phaseUsage()) }; @@ -254,23 +254,23 @@ BOOST_AUTO_TEST_CASE (RegMapping) Opm::Equil::EquilReg region[] = { Opm::Equil::EquilReg(record[0], calc, - Opm::Equil::Miscibility::NoMixing(), - Opm::Equil::Miscibility::NoMixing(), + std::make_shared(), + std::make_shared(), props.phaseUsage()) , Opm::Equil::EquilReg(record[0], calc, - Opm::Equil::Miscibility::NoMixing(), - Opm::Equil::Miscibility::NoMixing(), + std::make_shared(), + std::make_shared(), props.phaseUsage()) , Opm::Equil::EquilReg(record[1], calc, - Opm::Equil::Miscibility::NoMixing(), - Opm::Equil::Miscibility::NoMixing(), + std::make_shared(), + std::make_shared(), props.phaseUsage()) , Opm::Equil::EquilReg(record[1], calc, - Opm::Equil::Miscibility::NoMixing(), - Opm::Equil::Miscibility::NoMixing(), + std::make_shared(), + std::make_shared(), props.phaseUsage()) }; From db06c2eb80ee67c6e504cf498a35052e80217ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Wed, 26 Feb 2014 14:49:06 +0100 Subject: [PATCH 34/81] Removed redundant calcPressII() method. Pressure is also calculated in the calcPressSat() method. --- opm/core/simulator/initStateEquil.hpp | 53 +++------------------------ 1 file changed, 6 insertions(+), 47 deletions(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index aa9f7862..9ba62524 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -259,9 +259,7 @@ namespace Opm { const std::vector rec = getEquil(deck); const RegionMapping<> eqlmap(equilnum(deck, G)); - - calcPressII(eqlmap, rec, props, G, grav); - calcSat(eqlmap, rec, props, G, grav); + calcPressSat(eqlmap, rec, props, G, grav); } typedef std::vector PVal; @@ -279,50 +277,11 @@ namespace Opm template void - calcPressII(const RMap& reg , - const std::vector< EquilRecord >& rec , - const Opm::BlackoilPropertiesInterface& props, - const UnstructuredGrid& G , - const double grav) - { - typedef Miscibility::NoMixing NoMix; - - for (typename RMap::RegionId - r = 0, nr = reg.numRegions(); - r < nr; ++r) - { - const typename RMap::CellRange cells = reg.cells(r); - - const int repcell = *cells.begin(); - const RhoCalc calc(props, repcell); - - const EqReg eqreg(rec[r], calc, - std::make_shared(), std::make_shared(), - props.phaseUsage()); - - const PPress& res = phasePressures(G, eqreg, cells, grav); - - for (int p = 0, np = props.numPhases(); p < np; ++p) { - PVal& d = pp_[p]; - PVal::const_iterator s = res[p].begin(); - for (typename RMap::CellRange::const_iterator - c = cells.begin(), - e = cells.end(); - c != e; ++c, ++s) - { - d[*c] = *s; - } - } - } - } - - template - void - calcSat(const RMap& reg , - const std::vector< EquilRecord >& rec , - const Opm::BlackoilPropertiesInterface& props, - const UnstructuredGrid& G , - const double grav) + calcPressSat(const RMap& reg , + const std::vector< EquilRecord >& rec , + const Opm::BlackoilPropertiesInterface& props, + const UnstructuredGrid& G , + const double grav) { typedef Miscibility::NoMixing NoMix; From 5a00d854678e39eb4d9d20243041ec48133b951b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 27 Feb 2014 08:31:03 +0100 Subject: [PATCH 35/81] Enable live oil in initialisation. --- opm/core/simulator/initStateEquil.hpp | 29 ++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 9ba62524..d2ebae79 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -257,8 +257,33 @@ namespace Opm sat_(props.numPhases(), std::vector(G.number_of_cells)) { + // Get the equilibration records. const std::vector rec = getEquil(deck); + + // Create (inverse) region mapping. const RegionMapping<> eqlmap(equilnum(deck, G)); + + // Create Rs functions. + rs_func_.reserve(rec.size()); + if (deck.hasField("DISGAS")) { + if (deck.hasField("RSVD")) { + // Rs has been specified as a function of depth. + OPM_THROW(std::runtime_error, "Cannot initialise: RSVD field not read by EclipseGridParser class."); + } else { + // Default initialisation: constant Rs below contact, saturated above. + for (size_t i = 0; i < rec.size(); ++i) { + const int cell = *(eqlmap.cells(i + 1).begin()); + const double p_contact = rec[i].goc.press; + rs_func_.push_back(std::make_shared(props, cell, p_contact)); + } + } + } else { + for (size_t i = 0; i < rec.size(); ++i) { + rs_func_.push_back(std::make_shared()); + } + } + + // Compute phase pressures and saturations. calcPressSat(eqlmap, rec, props, G, grav); } @@ -272,6 +297,8 @@ namespace Opm typedef DensityCalculator RhoCalc; typedef EquilReg EqReg; + std::vector< std::shared_ptr > rs_func_; + PPress pp_; PPress sat_; @@ -295,7 +322,7 @@ namespace Opm const RhoCalc calc(props, repcell); const EqReg eqreg(rec[r], calc, - std::make_shared(), std::make_shared(), + rs_func_[r], std::make_shared(), props.phaseUsage()); const PPress press = phasePressures(G, eqreg, cells, grav); From 7cc0802f3e5fe90e33d30e171650c8f2dcf4a91d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 27 Feb 2014 08:31:51 +0100 Subject: [PATCH 36/81] Add test for live oil initialisation. The test is not finished or verified yet. --- CMakeLists_files.cmake | 1 + tests/equil_liveoil.DATA | 46 +++++++++++++++++++++++++++++++++++++++ tests/test_equil.cpp | 47 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 tests/equil_liveoil.DATA diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index db22a529..8135a404 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -186,6 +186,7 @@ list (APPEND TEST_DATA_FILES tests/capillary.DATA tests/capillary_overlap.DATA tests/deadfluids.DATA + tests/equil_liveoil.DATA ) # originally generated with the command: diff --git a/tests/equil_liveoil.DATA b/tests/equil_liveoil.DATA new file mode 100644 index 00000000..5b276691 --- /dev/null +++ b/tests/equil_liveoil.DATA @@ -0,0 +1,46 @@ +WATER +OIL +GAS +DISGAS + + + +PVTO +-- Rs Pbub Bo Vo + 0 1. 1.0000 1.20 / + 100 40. 1.0120 1.17 / + 200 80. 1.0255 1.14 / + 300 120. 1.0380 1.11 / + 400 160. 1.0510 1.08 / + 500 200. 1.0630 1.06 / + 600 240. 1.0750 1.03 / + 700 280. 1.0870 1.00 / + 800 320. 1.0985 .98 / + 900 360. 1.1100 .95 / + 1000 400. 1.1200 .94 + 500. 1.1189 .94 / + / + + +PVDG +100 0.010 0.1 +200 0.005 0.2 +/ + +SWOF +0.2 0 1 0.9 +1 1 0 0.1 +/ + +SGOF +0 0 1 0.2 +0.8 1 0 0.5 +/ + +DENSITY +700 1000 1 +/ + +EQUIL +50 150 50 0.25 45 0.35 +/ diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index 5a5e9019..3e186677 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -480,4 +480,51 @@ BOOST_AUTO_TEST_CASE (DeckWithCapillaryOverlap) } + +BOOST_AUTO_TEST_CASE (DeckWithLiveOil) +{ + Opm::GridManager gm(1, 1, 20, 1.0, 1.0, 5.0); + const UnstructuredGrid& grid = *(gm.c_grid()); + Opm::EclipseGridParser deck("equil_liveoil.DATA"); + Opm::BlackoilPropertiesFromDeck props(deck, grid, false); + + Opm::Equil::DeckDependent::PhasePressureSaturationComputer comp(props, deck, grid, 10.0); + const auto& pressures = comp.press(); + BOOST_REQUIRE(pressures.size() == 3); + BOOST_REQUIRE(int(pressures[0].size()) == grid.number_of_cells); + + const int first = 0, last = grid.number_of_cells - 1; + // The relative tolerance is too loose to be very useful, + // but the answer we are checking is the result of an ODE + // solver, and it is unclear if we should check it against + // the true answer or something else. + const double reltol = 1.0e-6; + BOOST_CHECK_CLOSE(pressures[0][first] , 1.45e7 , reltol); + BOOST_CHECK_CLOSE(pressures[0][last ] , 1.545e7 , reltol); + BOOST_CHECK_CLOSE(pressures[1][last] , 1.5489764605846416e7 , reltol); + + const auto& sats = comp.saturation(); + // std::cout << "Saturations:\n"; + // for (const auto& sat : sats) { + // for (const double s : sat) { + // std::cout << s << ' '; + // } + // std::cout << std::endl; + // } + const std::vector s[3]{ + { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.223141818182, 0.532269090909, 0.78471, 0.91526, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.207743333333, 0.08474, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.776858181818, 0.467730909091, 0.0075466666666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + }; + for (int phase = 0; phase < 3; ++phase) { + BOOST_REQUIRE(sats[phase].size() == s[phase].size()); + for (size_t i = 0; i < s[phase].size(); ++i) { + std::cout << sats[phase][i] << '\n'; + //BOOST_CHECK_CLOSE(sats[phase][i], s[phase][i], reltol); + } + std::cout << std::endl; + } +} + + BOOST_AUTO_TEST_SUITE_END() From 283dc96776dbb3d37641f15ea462c4884686e084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 27 Feb 2014 08:58:38 +0100 Subject: [PATCH 37/81] Added size() method to CellRange inner class. --- opm/core/utility/RegionMapping.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/opm/core/utility/RegionMapping.hpp b/opm/core/utility/RegionMapping.hpp index 9845919f..151ad07b 100644 --- a/opm/core/utility/RegionMapping.hpp +++ b/opm/core/utility/RegionMapping.hpp @@ -92,6 +92,11 @@ namespace Opm */ typedef CellIter const_iterator; + /** + * Size type for this range. + */ + typedef typename std::vector::size_type size_type; + /** * Beginning of cell range. */ @@ -102,6 +107,11 @@ namespace Opm */ const_iterator end() const { return e_; } + /** + * Number of elements in the range. + */ + size_type size() const { return e_ - b_; } + private: const_iterator b_; const_iterator e_; From 036ab95da209a0a8ca18970df6473ddcf222b59d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 27 Feb 2014 09:08:39 +0100 Subject: [PATCH 38/81] Rename PhasePressureSaturationComputer -> InitialStateComputer. Also add (unused so far) rs_ field to class. --- opm/core/simulator/initStateEquil.hpp | 51 +++++++++++++++++---------- tests/test_equil.cpp | 8 ++--- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index d2ebae79..ee8bf7ac 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -243,19 +243,20 @@ namespace Opm } template - class PhasePressureSaturationComputer; + class InitialStateComputer; template <> - class PhasePressureSaturationComputer { + class InitialStateComputer { public: - PhasePressureSaturationComputer(const BlackoilPropertiesInterface& props, - const EclipseGridParser& deck , - const UnstructuredGrid& G , - const double grav = unit::gravity) + InitialStateComputer(const BlackoilPropertiesInterface& props, + const EclipseGridParser& deck , + const UnstructuredGrid& G , + const double grav = unit::gravity) : pp_(props.numPhases(), std::vector(G.number_of_cells)), sat_(props.numPhases(), - std::vector(G.number_of_cells)) + std::vector(G.number_of_cells)), + rs_(G.number_of_cells) { // Get the equilibration records. const std::vector rec = getEquil(deck); @@ -287,11 +288,11 @@ namespace Opm calcPressSat(eqlmap, rec, props, G, grav); } - typedef std::vector PVal; - typedef std::vector PPress; + typedef std::vector Vec; + typedef std::vector PVec; // One per phase. - const PPress& press() const { return pp_; } - const PPress& saturation() const { return sat_; } + const PVec& press() const { return pp_; } + const PVec& saturation() const { return sat_; } private: typedef DensityCalculator RhoCalc; @@ -299,8 +300,9 @@ namespace Opm std::vector< std::shared_ptr > rs_func_; - PPress pp_; - PPress sat_; + PVec pp_; + PVec sat_; + Vec rs_; template void @@ -325,12 +327,13 @@ namespace Opm rs_func_[r], std::make_shared(), props.phaseUsage()); - const PPress press = phasePressures(G, eqreg, cells, grav); - const PPress sat = phaseSaturations(eqreg, cells, props, press); + const PVec press = phasePressures(G, eqreg, cells, grav); + const PVec sat = phaseSaturations(eqreg, cells, props, press); + const Vec rs(cells.size());// = gasOilRatio(); for (int p = 0, np = props.numPhases(); p < np; ++p) { - PVal& d = pp_[p]; - PVal::const_iterator s = press[p].begin(); + Vec& d = pp_[p]; + Vec::const_iterator s = press[p].begin(); for (typename RMap::CellRange::const_iterator c = cells.begin(), e = cells.end(); @@ -340,8 +343,8 @@ namespace Opm } } for (int p = 0, np = props.numPhases(); p < np; ++p) { - PVal& d = sat_[p]; - PVal::const_iterator s = sat[p].begin(); + Vec& d = sat_[p]; + Vec::const_iterator s = sat[p].begin(); for (typename RMap::CellRange::const_iterator c = cells.begin(), e = cells.end(); @@ -350,6 +353,16 @@ namespace Opm d[*c] = *s; } } + Vec::const_iterator s = rs.begin(); + Vec& d = rs_; + for (typename RMap::CellRange::const_iterator + c = cells.begin(), + e = cells.end(); + c != e; ++c, ++s) + { + d[*c] = *s; + } + } } diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index 3e186677..38bdff80 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -328,7 +328,7 @@ BOOST_AUTO_TEST_CASE (DeckAllDead) grid(create_grid_cart3d(1, 1, 10), destroy_grid); Opm::EclipseGridParser deck("deadfluids.DATA"); Opm::BlackoilPropertiesFromDeck props(deck, *grid, false); - Opm::Equil::DeckDependent::PhasePressureSaturationComputer comp(props, deck, *grid, 10.0); + Opm::Equil::DeckDependent::InitialStateComputer comp(props, deck, *grid, 10.0); const auto& pressures = comp.press(); BOOST_REQUIRE(pressures.size() == 3); BOOST_REQUIRE(int(pressures[0].size()) == grid->number_of_cells); @@ -405,7 +405,7 @@ BOOST_AUTO_TEST_CASE (DeckWithCapillary) Opm::EclipseGridParser deck("capillary.DATA"); Opm::BlackoilPropertiesFromDeck props(deck, grid, false); - Opm::Equil::DeckDependent::PhasePressureSaturationComputer comp(props, deck, grid, 10.0); + Opm::Equil::DeckDependent::InitialStateComputer comp(props, deck, grid, 10.0); const auto& pressures = comp.press(); BOOST_REQUIRE(pressures.size() == 3); BOOST_REQUIRE(int(pressures[0].size()) == grid.number_of_cells); @@ -443,7 +443,7 @@ BOOST_AUTO_TEST_CASE (DeckWithCapillaryOverlap) Opm::EclipseGridParser deck("capillary_overlap.DATA"); Opm::BlackoilPropertiesFromDeck props(deck, grid, false); - Opm::Equil::DeckDependent::PhasePressureSaturationComputer comp(props, deck, grid, 10.0); + Opm::Equil::DeckDependent::InitialStateComputer comp(props, deck, grid, 10.0); const auto& pressures = comp.press(); BOOST_REQUIRE(pressures.size() == 3); BOOST_REQUIRE(int(pressures[0].size()) == grid.number_of_cells); @@ -488,7 +488,7 @@ BOOST_AUTO_TEST_CASE (DeckWithLiveOil) Opm::EclipseGridParser deck("equil_liveoil.DATA"); Opm::BlackoilPropertiesFromDeck props(deck, grid, false); - Opm::Equil::DeckDependent::PhasePressureSaturationComputer comp(props, deck, grid, 10.0); + Opm::Equil::DeckDependent::InitialStateComputer comp(props, deck, grid, 10.0); const auto& pressures = comp.press(); BOOST_REQUIRE(pressures.size() == 3); BOOST_REQUIRE(int(pressures[0].size()) == grid.number_of_cells); From c9c34f63a36c5c2f1f576ad761a740ee64f93d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 27 Feb 2014 09:31:48 +0100 Subject: [PATCH 39/81] Refactor copying of region to global data. --- opm/core/simulator/initStateEquil.hpp | 48 ++++++++++----------------- 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index ee8bf7ac..6ddb80ef 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -331,41 +331,27 @@ namespace Opm const PVec sat = phaseSaturations(eqreg, cells, props, press); const Vec rs(cells.size());// = gasOilRatio(); - for (int p = 0, np = props.numPhases(); p < np; ++p) { - Vec& d = pp_[p]; - Vec::const_iterator s = press[p].begin(); - for (typename RMap::CellRange::const_iterator - c = cells.begin(), - e = cells.end(); - c != e; ++c, ++s) - { - d[*c] = *s; - } + const int np = props.numPhases(); + for (int p = 0; p < np; ++p) { + copyFromRegion(press[p], cells, pp_[p]); + copyFromRegion(sat[p], cells, sat_[p]); } - for (int p = 0, np = props.numPhases(); p < np; ++p) { - Vec& d = sat_[p]; - Vec::const_iterator s = sat[p].begin(); - for (typename RMap::CellRange::const_iterator - c = cells.begin(), - e = cells.end(); - c != e; ++c, ++s) - { - d[*c] = *s; - } - } - Vec::const_iterator s = rs.begin(); - Vec& d = rs_; - for (typename RMap::CellRange::const_iterator - c = cells.begin(), - e = cells.end(); - c != e; ++c, ++s) - { - d[*c] = *s; - } - + copyFromRegion(rs, cells, rs_); } } + template + void copyFromRegion(const Vec& source, + const CellRangeType& cells, + Vec& destination) + { + auto s = source.begin(); + auto c = cells.begin(); + const auto e = cells.end(); + for (; c != e; ++c, ++s) { + destination[*c] = *s; + } + } }; } // namespace DeckDependent From 818d79adc789dd12c85b43025bac0cf6c2be2ab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 27 Feb 2014 09:37:48 +0100 Subject: [PATCH 40/81] Moved implementation of phaseSaturations() to _impl file. --- opm/core/simulator/initStateEquil.hpp | 58 +----------------- opm/core/simulator/initStateEquil_impl.hpp | 70 ++++++++++++++++++++++ 2 files changed, 71 insertions(+), 57 deletions(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 6ddb80ef..296e6774 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -126,63 +126,7 @@ namespace Opm phaseSaturations(const Region& reg, const CellRange& cells, const BlackoilPropertiesInterface& props, - const std::vector< std::vector >& phase_pressures) - { - const double z0 = reg.datum(); - const double zwoc = reg.zwoc (); - const double zgoc = reg.zgoc (); - if ((zgoc > z0) || (z0 > zwoc)) { - OPM_THROW(std::runtime_error, "Cannot initialise: the datum depth must be in the oil zone."); - } - if (!reg.phaseUsage().phase_used[BlackoilPhases::Liquid]) { - OPM_THROW(std::runtime_error, "Cannot initialise: not handling water-gas cases."); - } - - std::vector< std::vector > phase_saturations = phase_pressures; // Just to get the right size. - double smin[BlackoilPhases::MaxNumPhases] = { 0.0 }; - double smax[BlackoilPhases::MaxNumPhases] = { 0.0 }; - - const bool water = reg.phaseUsage().phase_used[BlackoilPhases::Aqua]; - const bool gas = reg.phaseUsage().phase_used[BlackoilPhases::Vapour]; - const int oilpos = reg.phaseUsage().phase_pos[BlackoilPhases::Liquid]; - const int waterpos = reg.phaseUsage().phase_pos[BlackoilPhases::Aqua]; - const int gaspos = reg.phaseUsage().phase_pos[BlackoilPhases::Vapour]; - std::vector::size_type local_index = 0; - for (typename CellRange::const_iterator ci = cells.begin(); ci != cells.end(); ++ci, ++local_index) { - const int cell = *ci; - props.satRange(1, &cell, smin, smax); - // Find saturations from pressure differences by - // inverting capillary pressure functions. - double sw = 0.0; - if (water) { - const double pcov = phase_pressures[oilpos][local_index] - phase_pressures[waterpos][local_index]; - sw = satFromPc(props, waterpos, cell, pcov); - phase_saturations[waterpos][local_index] = sw; - } - double sg = 0.0; - if (gas) { - // Note that pcog is defined to be (pg - po), not (po - pg). - const double pcog = phase_pressures[gaspos][local_index] - phase_pressures[oilpos][local_index]; - const double increasing = true; // pcog(sg) expected to be increasing function - sg = satFromPc(props, gaspos, cell, pcog, increasing); - phase_saturations[gaspos][local_index] = sg; - } - if (gas && water && (sg + sw > 1.0)) { - // Overlapping gas-oil and oil-water transition - // zones can lead to unphysical saturations when - // treated as above. Must recalculate using gas-water - // capillary pressure. - const double pcgw = phase_pressures[gaspos][local_index] - phase_pressures[waterpos][local_index]; - sw = satFromSumOfPcs(props, waterpos, gaspos, cell, pcgw); - sg = 1.0 - sw; - phase_saturations[waterpos][local_index] = sw; - phase_saturations[gaspos][local_index] = sg; - } - phase_saturations[oilpos][local_index] = 1.0 - sw - sg; - } - return phase_saturations; - } - + const std::vector< std::vector >& phase_pressures); namespace DeckDependent { diff --git a/opm/core/simulator/initStateEquil_impl.hpp b/opm/core/simulator/initStateEquil_impl.hpp index 29ab6ac1..03c10b4a 100644 --- a/opm/core/simulator/initStateEquil_impl.hpp +++ b/opm/core/simulator/initStateEquil_impl.hpp @@ -485,7 +485,10 @@ namespace Opm } } // namespace Details + namespace Equil { + + template std::vector< std::vector > @@ -568,6 +571,73 @@ namespace Opm return press; } + + + + + template + std::vector< std::vector > + phaseSaturations(const Region& reg, + const CellRange& cells, + const BlackoilPropertiesInterface& props, + const std::vector< std::vector >& phase_pressures) + { + const double z0 = reg.datum(); + const double zwoc = reg.zwoc (); + const double zgoc = reg.zgoc (); + if ((zgoc > z0) || (z0 > zwoc)) { + OPM_THROW(std::runtime_error, "Cannot initialise: the datum depth must be in the oil zone."); + } + if (!reg.phaseUsage().phase_used[BlackoilPhases::Liquid]) { + OPM_THROW(std::runtime_error, "Cannot initialise: not handling water-gas cases."); + } + + std::vector< std::vector > phase_saturations = phase_pressures; // Just to get the right size. + double smin[BlackoilPhases::MaxNumPhases] = { 0.0 }; + double smax[BlackoilPhases::MaxNumPhases] = { 0.0 }; + + const bool water = reg.phaseUsage().phase_used[BlackoilPhases::Aqua]; + const bool gas = reg.phaseUsage().phase_used[BlackoilPhases::Vapour]; + const int oilpos = reg.phaseUsage().phase_pos[BlackoilPhases::Liquid]; + const int waterpos = reg.phaseUsage().phase_pos[BlackoilPhases::Aqua]; + const int gaspos = reg.phaseUsage().phase_pos[BlackoilPhases::Vapour]; + std::vector::size_type local_index = 0; + for (typename CellRange::const_iterator ci = cells.begin(); ci != cells.end(); ++ci, ++local_index) { + const int cell = *ci; + props.satRange(1, &cell, smin, smax); + // Find saturations from pressure differences by + // inverting capillary pressure functions. + double sw = 0.0; + if (water) { + const double pcov = phase_pressures[oilpos][local_index] - phase_pressures[waterpos][local_index]; + sw = satFromPc(props, waterpos, cell, pcov); + phase_saturations[waterpos][local_index] = sw; + } + double sg = 0.0; + if (gas) { + // Note that pcog is defined to be (pg - po), not (po - pg). + const double pcog = phase_pressures[gaspos][local_index] - phase_pressures[oilpos][local_index]; + const double increasing = true; // pcog(sg) expected to be increasing function + sg = satFromPc(props, gaspos, cell, pcog, increasing); + phase_saturations[gaspos][local_index] = sg; + } + if (gas && water && (sg + sw > 1.0)) { + // Overlapping gas-oil and oil-water transition + // zones can lead to unphysical saturations when + // treated as above. Must recalculate using gas-water + // capillary pressure. + const double pcgw = phase_pressures[gaspos][local_index] - phase_pressures[waterpos][local_index]; + sw = satFromSumOfPcs(props, waterpos, gaspos, cell, pcgw); + sg = 1.0 - sw; + phase_saturations[waterpos][local_index] = sw; + phase_saturations[gaspos][local_index] = sg; + } + phase_saturations[oilpos][local_index] = 1.0 - sw - sg; + } + return phase_saturations; + } + + } // namespace Equil } // namespace Opm From e1feaf9345a4f3aac5fe790eba530ccb0dcbe29a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 27 Feb 2014 10:39:18 +0100 Subject: [PATCH 41/81] Added Rv field to InitialStateComputer. It is currently not computed, as for Rs. --- opm/core/simulator/initStateEquil.hpp | 37 ++++++++++++++++++++------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 296e6774..98842fb4 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -129,7 +129,11 @@ namespace Opm const std::vector< std::vector >& phase_pressures); + + + namespace DeckDependent { + inline std::vector getEquil(const EclipseGridParser& deck) @@ -168,6 +172,9 @@ namespace Opm } } + + + inline std::vector equilnum(const EclipseGridParser& deck, @@ -186,6 +193,9 @@ namespace Opm return eqlnum; } + + + template class InitialStateComputer; @@ -200,7 +210,8 @@ namespace Opm std::vector(G.number_of_cells)), sat_(props.numPhases(), std::vector(G.number_of_cells)), - rs_(G.number_of_cells) + rs_(G.number_of_cells), + rv_(G.number_of_cells) { // Get the equilibration records. const std::vector rec = getEquil(deck); @@ -228,8 +239,11 @@ namespace Opm } } - // Compute phase pressures and saturations. - calcPressSat(eqlmap, rec, props, G, grav); + // Compute pressures, saturations, rs and rv factors. + calcPressSatRsRv(eqlmap, rec, props, G, grav); + + // Modify oil pressure in no-oil regions so that the pressures of present phases can + // be recovered from the oil pressure and capillary relations. } typedef std::vector Vec; @@ -237,6 +251,8 @@ namespace Opm const PVec& press() const { return pp_; } const PVec& saturation() const { return sat_; } + const Vec& rs() const { return rs_; } + const Vec& rv() const { return rv_; } private: typedef DensityCalculator RhoCalc; @@ -247,14 +263,15 @@ namespace Opm PVec pp_; PVec sat_; Vec rs_; + Vec rv_; template void - calcPressSat(const RMap& reg , - const std::vector< EquilRecord >& rec , - const Opm::BlackoilPropertiesInterface& props, - const UnstructuredGrid& G , - const double grav) + calcPressSatRsRv(const RMap& reg , + const std::vector< EquilRecord >& rec , + const Opm::BlackoilPropertiesInterface& props, + const UnstructuredGrid& G , + const double grav) { typedef Miscibility::NoMixing NoMix; @@ -273,7 +290,8 @@ namespace Opm const PVec press = phasePressures(G, eqreg, cells, grav); const PVec sat = phaseSaturations(eqreg, cells, props, press); - const Vec rs(cells.size());// = gasOilRatio(); + const Vec rs(cells.size()); + const Vec rv(cells.size()); const int np = props.numPhases(); for (int p = 0; p < np; ++p) { @@ -281,6 +299,7 @@ namespace Opm copyFromRegion(sat[p], cells, sat_[p]); } copyFromRegion(rs, cells, rs_); + copyFromRegion(rv, cells, rv_); } } From 6a61b905240eb5637294d1f1cf31f9edda6cd288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 27 Feb 2014 10:40:14 +0100 Subject: [PATCH 42/81] Add initStateEquil() function. It is not quite complete yet for the following reasons: - it does not compute state.surfacevol(), - the InitialStateComputer class does not compute Rs or Rv, - it has not been verified. --- opm/core/simulator/initStateEquil.hpp | 24 +++++++++ opm/core/simulator/initStateEquil_impl.hpp | 58 ++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 98842fb4..43b34d2c 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -21,6 +21,7 @@ #define OPM_INITSTATEEQUIL_HEADER_INCLUDED #include +#include #include #include #include @@ -41,6 +42,29 @@ struct UnstructuredGrid; namespace Opm { + + /** + * Compute initial state by an equilibration procedure. + * + * The following state fields are modified: + * pressure(), + * saturation(), + * surfacevol(), + * gasoilratio(), + * rv(). + * + * \param[in] grid Grid. + * \param[in] props Property object, pvt and capillary properties are used. + * \param[in] deck Simulation deck, used to obtain EQUIL and related data. + * \param[in] gravity Acceleration of gravity, assumed to be in Z direction. + */ + void initStateEquil(const UnstructuredGrid& grid, + const BlackoilPropertiesInterface& props, + const EclipseGridParser& deck, + const double gravity, + BlackoilState& state); + + /** * Types and routines that collectively implement a basic * ECLIPSE-style equilibration-based initialisation scheme. diff --git a/opm/core/simulator/initStateEquil_impl.hpp b/opm/core/simulator/initStateEquil_impl.hpp index 03c10b4a..2b2f99db 100644 --- a/opm/core/simulator/initStateEquil_impl.hpp +++ b/opm/core/simulator/initStateEquil_impl.hpp @@ -639,6 +639,64 @@ namespace Opm } // namespace Equil + + + namespace + { + /// Convert saturations from a vector of individual phase saturation vectors + /// to an interleaved format where all values for a given cell come before all + /// values for the next cell, all in a single vector. + std::vector convertSats(const std::vector< std::vector >& sat) + { + const int np = sat.size(); + const int nc = sat[0].size(); + std::vector s(np * nc); + for (int c = 0; c < nc; ++c) { + for (int p = 0; p < np; ++p) { + s[np*c + p] = sat[p][c]; + } + } + return s; + } + } + + + /** + * Compute initial state by an equilibration procedure. + * + * The following state fields are modified: + * pressure(), + * saturation(), + * surfacevol(), + * gasoilratio(), + * rv(). + * + * \param[in] grid Grid. + * \param[in] props Property object, pvt and capillary properties are used. + * \param[in] deck Simulation deck, used to obtain EQUIL and related data. + * \param[in] gravity Acceleration of gravity, assumed to be in Z direction. + */ + void initStateEquil(const UnstructuredGrid& grid, + const BlackoilPropertiesInterface& props, + const EclipseGridParser& deck, + const double gravity, + BlackoilState& state) + { + typedef Equil::DeckDependent::InitialStateComputer ISC; + ISC isc(props, deck, grid, gravity); + const auto pu = props.phaseUsage(); + const int ref_phase = pu.phase_used[BlackoilPhases::Liquid] + ? pu.phase_pos[BlackoilPhases::Liquid] + : pu.phase_pos[BlackoilPhases::Aqua]; + state.pressure() = isc.press()[ref_phase]; + state.saturation() = convertSats(isc.saturation()); + state.gasoilratio() = isc.rs(); + state.rv() = isc.rv(); + // TODO: state.surfacevol() must be computed from s, rs, rv. + } + + + } // namespace Opm #endif // OPM_INITSTATEEQUIL_IMPL_HEADER_INCLUDED From 3f5cf72c013701097ed00f73aea2a1b3d19e76ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 27 Feb 2014 13:14:48 +0100 Subject: [PATCH 43/81] Add computeRs() function and use from InitialStateComputer. --- opm/core/simulator/initStateEquil.hpp | 36 +++++++++++++++++++--- opm/core/simulator/initStateEquil_impl.hpp | 35 +++++++++++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 43b34d2c..cb4051e0 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -154,6 +154,28 @@ namespace Opm + /** + * Compute initial Rs values. + * + * \tparam CellRangeType Type of cell range that demarcates the + * cells pertaining to the current + * equilibration region. Must implement + * methods begin() and end() to bound the range + * as well as provide an inner type, + * const_iterator, to traverse the range. + * + * \param[in] grid Grid. + * \param[in] cells Range that spans the cells of the current + * equilibration region. + * \param[in] oil_pressure Oil pressure for each cell in range. + * \param[in] rs_func Rs as function of pressure and depth. + * \return Rs values, one for each cell in the 'cells' range. + */ + template + std::vector computeRs(const UnstructuredGrid& grid, + const CellRangeType& cells, + const std::vector oil_pressure, + const Miscibility::RsFunction& rs_func); namespace DeckDependent { @@ -314,16 +336,20 @@ namespace Opm const PVec press = phasePressures(G, eqreg, cells, grav); const PVec sat = phaseSaturations(eqreg, cells, props, press); - const Vec rs(cells.size()); - const Vec rv(cells.size()); - const int np = props.numPhases(); for (int p = 0; p < np; ++p) { copyFromRegion(press[p], cells, pp_[p]); copyFromRegion(sat[p], cells, sat_[p]); } - copyFromRegion(rs, cells, rs_); - copyFromRegion(rv, cells, rv_); + if (props.phaseUsage().phase_used[BlackoilPhases::Liquid] + && props.phaseUsage().phase_used[BlackoilPhases::Vapour]) { + const int oilpos = props.phaseUsage().phase_pos[BlackoilPhases::Liquid]; + const Vec rs = computeRs(G, cells, press[oilpos], *(rs_func_[r])); + const Vec rv(cells.size(), 0.0); + std::cout << "rs.size() = " << rs.size() << std::endl; + copyFromRegion(rs, cells, rs_); + copyFromRegion(rv, cells, rv_); + } } } diff --git a/opm/core/simulator/initStateEquil_impl.hpp b/opm/core/simulator/initStateEquil_impl.hpp index 2b2f99db..c075f4ec 100644 --- a/opm/core/simulator/initStateEquil_impl.hpp +++ b/opm/core/simulator/initStateEquil_impl.hpp @@ -638,6 +638,41 @@ namespace Opm } + /** + * Compute initial Rs values. + * + * \tparam CellRangeType Type of cell range that demarcates the + * cells pertaining to the current + * equilibration region. Must implement + * methods begin() and end() to bound the range + * as well as provide an inner type, + * const_iterator, to traverse the range. + * + * \param[in] grid Grid. + * \param[in] cells Range that spans the cells of the current + * equilibration region. + * \param[in] oil_pressure Oil pressure for each cell in range. + * \param[in] rs_func Rs as function of pressure and depth. + * \return Rs values, one for each cell in the 'cells' range. + */ + template + std::vector computeRs(const UnstructuredGrid& grid, + const CellRangeType& cells, + const std::vector oil_pressure, + const Miscibility::RsFunction& rs_func) + { + assert(grid.dimensions == 3); + std::vector rs(cells.size()); + int count = 0; + for (auto it = cells.begin(); it != cells.end(); ++it, ++count) { + const double depth = grid.cell_centroids[3*(*it) + 2]; + rs[count] = rs_func(depth, oil_pressure[count]); + } + return rs; + } + + + } // namespace Equil From 1a238de759224443eb299d309d0f9b18e41d61eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 27 Feb 2014 13:27:07 +0100 Subject: [PATCH 44/81] Remove debugging output. --- opm/core/simulator/initStateEquil.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index cb4051e0..1b81b07b 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -346,7 +346,6 @@ namespace Opm const int oilpos = props.phaseUsage().phase_pos[BlackoilPhases::Liquid]; const Vec rs = computeRs(G, cells, press[oilpos], *(rs_func_[r])); const Vec rv(cells.size(), 0.0); - std::cout << "rs.size() = " << rs.size() << std::endl; copyFromRegion(rs, cells, rs_); copyFromRegion(rv, cells, rv_); } From a58faea9abfb3c41451ce5a58e4738c5612b5dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 27 Feb 2014 13:55:15 +0100 Subject: [PATCH 45/81] Added simple program example for initialisation. --- CMakeLists_files.cmake | 1 + examples/compute_initial_state.cpp | 104 +++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 examples/compute_initial_state.cpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 8135a404..1d25e094 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -192,6 +192,7 @@ list (APPEND TEST_DATA_FILES # originally generated with the command: # find tutorials examples -name '*.c*' -printf '\t%p\n' | sort list (APPEND EXAMPLE_SOURCE_FILES + examples/compute_initial_state.cpp examples/compute_tof.cpp examples/compute_tof_from_files.cpp examples/import_rewrite.cpp diff --git a/examples/compute_initial_state.cpp b/examples/compute_initial_state.cpp new file mode 100644 index 00000000..0b11ecf1 --- /dev/null +++ b/examples/compute_initial_state.cpp @@ -0,0 +1,104 @@ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + + +#if HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param) + { + if (param.anyUnused()) { + std::cout << "-------------------- Unused parameters: --------------------\n"; + param.displayUsage(); + std::cout << "----------------------------------------------------------------" << std::endl; + } + } + + void outputData(const std::string& output_dir, + const std::string& name, + const std::vector& data) + { + std::ostringstream fname; + fname << output_dir << "/" << name; + boost::filesystem::path fpath = fname.str(); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + fname << "/" << "initial.txt"; + std::ofstream file(fname.str().c_str()); + if (!file) { + OPM_THROW(std::runtime_error, "Failed to open " << fname.str()); + } + std::copy(data.begin(), data.end(), std::ostream_iterator(file, "\n")); + } + + + +} // anon namespace + + + +// ----------------- Main program ----------------- +int +main(int argc, char** argv) +try +{ + using namespace Opm; + + // Setup. + parameter::ParameterGroup param(argc, argv, false); + std::cout << "--------------- Reading parameters ---------------" << std::endl; + const std::string deck_filename = param.get("deck_filename"); + const double grav = param.getDefault("gravity", unit::gravity); + EclipseGridParser deck(deck_filename); + GridManager gm(deck); + const UnstructuredGrid& grid = *gm.c_grid(); + BlackoilPropertiesFromDeck props(deck, grid, param); + warnIfUnusedParams(param); + + // Initialisation. + BlackoilState state; + initStateEquil(grid, props, deck, grav, state); + + // Output. + const std::string output_dir = param.getDefault("output_dir", "output"); + outputData(output_dir, "pressure", state.pressure()); + outputData(output_dir, "saturation", state.saturation()); + outputData(output_dir, "rs", state.gasoilratio()); + outputData(output_dir, "rv", state.rv()); +} +catch (const std::exception& e) { + std::cerr << "Program threw an exception: " << e.what() << "\n"; + throw; +} From 3e9b82e0834312ce448ad19da466b3659bf23425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 27 Feb 2014 14:48:14 +0100 Subject: [PATCH 46/81] Fix bug in RS initialisation. Also throw if default init is specified and datum != goc depth. --- opm/core/simulator/initStateEquil.hpp | 8 +++++++- tests/equil_liveoil.DATA | 2 +- tests/test_equil.cpp | 6 +++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 1b81b07b..3767563f 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -275,7 +275,13 @@ namespace Opm // Default initialisation: constant Rs below contact, saturated above. for (size_t i = 0; i < rec.size(); ++i) { const int cell = *(eqlmap.cells(i + 1).begin()); - const double p_contact = rec[i].goc.press; + if (rec[i].goc.depth != rec[i].main.depth) { + OPM_THROW(std::runtime_error, + "Cannot initialise: when no explicit RSVD table is given, \n" + "datum depth must be at the gas-oil-contact. " + "In EQUIL region " << (i + 1) << " (counting from 1), this does not hold."); + } + const double p_contact = rec[i].main.press; rs_func_.push_back(std::make_shared(props, cell, p_contact)); } } diff --git a/tests/equil_liveoil.DATA b/tests/equil_liveoil.DATA index 5b276691..955a09b7 100644 --- a/tests/equil_liveoil.DATA +++ b/tests/equil_liveoil.DATA @@ -42,5 +42,5 @@ DENSITY / EQUIL -50 150 50 0.25 45 0.35 +45 150 50 0.25 45 0.35 / diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index 38bdff80..6a175acc 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -499,9 +499,9 @@ BOOST_AUTO_TEST_CASE (DeckWithLiveOil) // solver, and it is unclear if we should check it against // the true answer or something else. const double reltol = 1.0e-6; - BOOST_CHECK_CLOSE(pressures[0][first] , 1.45e7 , reltol); - BOOST_CHECK_CLOSE(pressures[0][last ] , 1.545e7 , reltol); - BOOST_CHECK_CLOSE(pressures[1][last] , 1.5489764605846416e7 , reltol); + BOOST_CHECK_CLOSE(pressures[0][first] , 1.4551328443106037e7 , reltol); + BOOST_CHECK_CLOSE(pressures[0][last ] , 1.5501328443106037e7 , reltol); + BOOST_CHECK_CLOSE(pressures[1][last] , 1.5541598197355453e7 , reltol); const auto& sats = comp.saturation(); // std::cout << "Saturations:\n"; From 75a7c5cee8511dba4682e1bb01eb508443880c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 27 Feb 2014 14:57:38 +0100 Subject: [PATCH 47/81] Bugfix in RsSatAtContact: use min(), not max(). Also modified test to match output. --- opm/core/simulator/EquilibrationHelpers.hpp | 2 +- tests/test_equil.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/opm/core/simulator/EquilibrationHelpers.hpp b/opm/core/simulator/EquilibrationHelpers.hpp index 0588f346..0d4d0819 100644 --- a/opm/core/simulator/EquilibrationHelpers.hpp +++ b/opm/core/simulator/EquilibrationHelpers.hpp @@ -300,7 +300,7 @@ namespace Opm operator()(const double /* depth */, const double press) const { - return std::max(satRs(press), rs_sat_contact_); + return std::min(satRs(press), rs_sat_contact_); } private: diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index 6a175acc..b4df1fae 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -499,9 +499,9 @@ BOOST_AUTO_TEST_CASE (DeckWithLiveOil) // solver, and it is unclear if we should check it against // the true answer or something else. const double reltol = 1.0e-6; - BOOST_CHECK_CLOSE(pressures[0][first] , 1.4551328443106037e7 , reltol); - BOOST_CHECK_CLOSE(pressures[0][last ] , 1.5501328443106037e7 , reltol); - BOOST_CHECK_CLOSE(pressures[1][last] , 1.5541598197355453e7 , reltol); + BOOST_CHECK_CLOSE(pressures[0][first], 1.4551302072306179e7, reltol); + BOOST_CHECK_CLOSE(pressures[0][last], 1.5501302072306179e7, reltol); + BOOST_CHECK_CLOSE(pressures[1][last], 1.5538684664272346e7, reltol); const auto& sats = comp.saturation(); // std::cout << "Saturations:\n"; From 0f852aadc8e9f70d720ef70c2bb5db6585793d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 27 Feb 2014 15:55:08 +0100 Subject: [PATCH 48/81] Add test data file for compute_initial_state.cpp. --- tests/equil_liveoil_grid.DATA | 65 +++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 tests/equil_liveoil_grid.DATA diff --git a/tests/equil_liveoil_grid.DATA b/tests/equil_liveoil_grid.DATA new file mode 100644 index 00000000..92e503c5 --- /dev/null +++ b/tests/equil_liveoil_grid.DATA @@ -0,0 +1,65 @@ +WATER +OIL +GAS +DISGAS + +DIMENS +1 1 20 +/ + +DXV +1.0 +/ + +DYV +1.0 +/ + +DZV +20*5.0 +/ + +TOPS +4*0.0 +/ + + +PVTO +-- Rs Pbub Bo Vo + 0 1. 1.0000 1.20 / + 100 40. 1.0120 1.17 / + 200 80. 1.0255 1.14 / + 300 120. 1.0380 1.11 / + 400 160. 1.0510 1.08 / + 500 200. 1.0630 1.06 / + 600 240. 1.0750 1.03 / + 700 280. 1.0870 1.00 / + 800 320. 1.0985 .98 / + 900 360. 1.1100 .95 / + 1000 400. 1.1200 .94 + 500. 1.1189 .94 / + / + + +PVDG +100 0.010 0.1 +200 0.005 0.2 +/ + +SWOF +0.2 0 1 0.9 +1 1 0 0.1 +/ + +SGOF +0 0 1 0.2 +0.8 1 0 0.5 +/ + +DENSITY +700 1000 1 +/ + +EQUIL +45 150 50 0.25 45 0.35 +/ From 5777547219351fc29f6b561d96f19445a4b4d4da Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Wed, 19 Mar 2014 17:37:12 +0100 Subject: [PATCH 49/81] Explicitly construct the ScalarProduct, and SequentialInformation. This patch refactors the calls to the dune-istl solvers. The SeqScalarProduct, and SequentialInformation is now explicitly constructed and later used to construct the smoothers generically. Aditionally the linear operator (MatrixAdapter) is constructed before calling the various solver dependent solve routines. While this does not change the behaviour of the code it is a preparatory step to support parallel solvers. These parallel solvers only differ in the type of the scalarproduct and linear operator used from the sequential ones. --- opm/core/linalg/LinearSolverIstl.cpp | 98 +++++++++++++++++++--------- opm/core/linalg/LinearSolverIstl.hpp | 13 ++++ 2 files changed, 80 insertions(+), 31 deletions(-) diff --git a/opm/core/linalg/LinearSolverIstl.cpp b/opm/core/linalg/LinearSolverIstl.cpp index c32ad02e..181b9ed2 100644 --- a/opm/core/linalg/LinearSolverIstl.cpp +++ b/opm/core/linalg/LinearSolverIstl.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3) #include @@ -59,25 +60,30 @@ namespace Opm typedef Dune::BlockVector Vector; typedef Dune::MatrixAdapter Operator; + template LinearSolverInterface::LinearSolverReport - solveCG_ILU0(const Mat& A, Vector& x, Vector& b, double tolerance, int maxit, int verbosity); + solveCG_ILU0(O& A, Vector& x, Vector& b, S& sp, const C& comm, double tolerance, int maxit, int verbosity); + template LinearSolverInterface::LinearSolverReport - solveCG_AMG(const Mat& A, Vector& x, Vector& b, double tolerance, int maxit, int verbosity, + solveCG_AMG(O& A, Vector& x, Vector& b, S& sp, const C& comm, double tolerance, int maxit, int verbosity, double prolongateFactor, int smoothsteps); #if defined(HAS_DUNE_FAST_AMG) || DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3) + template LinearSolverInterface::LinearSolverReport - solveKAMG(const Mat& A, Vector& x, Vector& b, double tolerance, int maxit, int verbosity, + solveKAMG(O& A, Vector& x, Vector& b, S& sp, const C& comm, double tolerance, int maxit, int verbosity, double prolongateFactor, int smoothsteps); + template LinearSolverInterface::LinearSolverReport - solveFastAMG(const Mat& A, Vector& x, Vector& b, double tolerance, int maxit, int verbosity, + solveFastAMG(O& A, Vector& x, Vector& b, S& sp, const C& comm, double tolerance, int maxit, int verbosity, double prolongateFactor); #endif + template LinearSolverInterface::LinearSolverReport - solveBiCGStab_ILU0(const Mat& A, Vector& x, Vector& b, double tolerance, int maxit, int verbosity); + solveBiCGStab_ILU0(O& A, Vector& x, Vector& b, S& sp, const C& comm, double tolerance, int maxit, int verbosity); } // anonymous namespace @@ -167,19 +173,29 @@ namespace Opm if (maxit == 0) { maxit = 5000; } + Dune::SeqScalarProduct sp; + Dune::Amg::SequentialInformation comm; + Operator opA(A); + return solveSystem(opA, x, solution, b, sp, comm, maxit); + } +template + LinearSolverInterface::LinearSolverReport + LinearSolverIstl::solveSystem (O& opA, V& x, double* solution, V& b, + S& sp, const C& comm, int maxit) const + { LinearSolverReport res; switch (linsolver_type_) { case CG_ILU0: - res = solveCG_ILU0(A, x, b, linsolver_residual_tolerance_, maxit, linsolver_verbosity_); + res = solveCG_ILU0(opA, x, b, sp, comm, linsolver_residual_tolerance_, maxit, linsolver_verbosity_); break; case CG_AMG: - res = solveCG_AMG(A, x, b, linsolver_residual_tolerance_, maxit, linsolver_verbosity_, + res = solveCG_AMG(opA, x, b, sp, comm, linsolver_residual_tolerance_, maxit, linsolver_verbosity_, linsolver_prolongate_factor_, linsolver_smooth_steps_); break; case KAMG: #if defined(HAS_DUNE_FAST_AMG) || DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3) - res = solveKAMG(A, x, b, linsolver_residual_tolerance_, maxit, linsolver_verbosity_, + res = solveKAMG(opA, x, b, sp, comm, linsolver_residual_tolerance_, maxit, linsolver_verbosity_, linsolver_prolongate_factor_, linsolver_smooth_steps_); #else throw std::runtime_error("KAMG not supported with this version of DUNE"); @@ -187,17 +203,17 @@ namespace Opm break; case FastAMG: #if defined(HAS_DUNE_FAST_AMG) || DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3) - res = solveFastAMG(A, x, b, linsolver_residual_tolerance_, maxit, linsolver_verbosity_, + res = solveFastAMG(opA, x, b, sp, comm, linsolver_residual_tolerance_, maxit, linsolver_verbosity_, linsolver_prolongate_factor_); #else if(linsolver_verbosity_) std::cerr<<"Fast AMG is not available; falling back to CG preconditioned with the normal one"< + struct PreconditionerTraits + { + typedef std::shared_ptr

PointerType; + }; + + template + typename PreconditionerTraits::PointerType + makePreconditioner(O& opA, double relax, const C& comm, int iterations=1) + { + typename Dune::Amg::SmootherTraits

::Arguments args; + typename Dune::Amg::ConstructionTraits

::Arguments cargs; + cargs.setMatrix(opA.getmat()); + args.iterations=iterations; + args.relaxationFactor=relax; + cargs.setArgs(args); + cargs.setComm(comm); + return std::shared_ptr

(Dune::Amg::ConstructionTraits

::construct(cargs)); + } + + template + LinearSolverInterface::LinearSolverReport + solveCG_ILU0(O& opA, Vector& x, Vector& b, S& sp, const C& comm, double tolerance, int maxit, int verbosity) { - Operator opA(A); // Construct preconditioner. - Dune::SeqILU0 precond(A, 1.0); + typedef Dune::SeqILU0 Preconditioner; + auto precond = makePreconditioner(opA, 1.0, comm); // Construct linear solver. - Dune::CGSolver linsolve(opA, precond, tolerance, maxit, verbosity); + Dune::CGSolver linsolve(opA, sp, *precond, tolerance, maxit, verbosity); // Solve system. Dune::InverseOperatorResult result; @@ -266,8 +302,9 @@ namespace Opm criterion.setGamma(1); // V-cycle; this is the default } + template LinearSolverInterface::LinearSolverReport - solveCG_AMG(const Mat& A, Vector& x, Vector& b, double tolerance, int maxit, int verbosity, + solveCG_AMG(O& opA, Vector& x, Vector& b, S& sp, const C& comm, double tolerance, int maxit, int verbosity, double linsolver_prolongate_factor, int linsolver_smooth_steps) { // Solve with AMG solver. @@ -295,13 +332,12 @@ namespace Opm // Construct preconditioner. Criterion criterion; Precond::SmootherArgs smootherArgs; - Operator opA(A); setUpCriterion(criterion, linsolver_prolongate_factor, verbosity, linsolver_smooth_steps); Precond precond(opA, criterion, smootherArgs); // Construct linear solver. - Dune::CGSolver linsolve(opA, precond, tolerance, maxit, verbosity); + Dune::CGSolver linsolve(opA, sp, precond, tolerance, maxit, verbosity); // Solve system. Dune::InverseOperatorResult result; @@ -317,8 +353,9 @@ namespace Opm #if defined(HAS_DUNE_FAST_AMG) || DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3) + template LinearSolverInterface::LinearSolverReport - solveKAMG(const Mat& A, Vector& x, Vector& b, double tolerance, int maxit, int verbosity, + solveKAMG(O& opA, Vector& x, Vector& b, S& sp, const C& comm, double tolerance, int maxit, int verbosity, double linsolver_prolongate_factor, int linsolver_smooth_steps) { // Solve with AMG solver. @@ -344,7 +381,6 @@ namespace Opm typedef Dune::Amg::KAMG Precond; // Construct preconditioner. - Operator opA(A); Precond::SmootherArgs smootherArgs; Criterion criterion; setUpCriterion(criterion, linsolver_prolongate_factor, verbosity, @@ -352,7 +388,7 @@ namespace Opm Precond precond(opA, criterion, smootherArgs); // Construct linear solver. - Dune::GeneralizedPCGSolver linsolve(opA, precond, tolerance, maxit, verbosity); + Dune::GeneralizedPCGSolver linsolve(opA, sp, precond, tolerance, maxit, verbosity); // Solve system. Dune::InverseOperatorResult result; @@ -366,8 +402,9 @@ namespace Opm return res; } + template LinearSolverInterface::LinearSolverReport - solveFastAMG(const Mat& A, Vector& x, Vector& b, double tolerance, int maxit, int verbosity, + solveFastAMG(O& opA, Vector& x, Vector& b, S& sp, const C& comm, double tolerance, int maxit, int verbosity, double linsolver_prolongate_factor) { // Solve with AMG solver. @@ -388,7 +425,6 @@ namespace Opm typedef Dune::Amg::FastAMG Precond; // Construct preconditioner. - Operator opA(A); Criterion criterion; const int smooth_steps = 1; setUpCriterion(criterion, linsolver_prolongate_factor, verbosity, smooth_steps); @@ -400,7 +436,7 @@ namespace Opm Precond precond(opA, criterion, parms); // Construct linear solver. - Dune::GeneralizedPCGSolver linsolve(opA, precond, tolerance, maxit, verbosity); + Dune::GeneralizedPCGSolver linsolve(opA, sp, precond, tolerance, maxit, verbosity); // Solve system. Dune::InverseOperatorResult result; @@ -415,17 +451,17 @@ namespace Opm } #endif - + template LinearSolverInterface::LinearSolverReport - solveBiCGStab_ILU0(const Mat& A, Vector& x, Vector& b, double tolerance, int maxit, int verbosity) + solveBiCGStab_ILU0(O& opA, Vector& x, Vector& b, S& sp, const C& comm, double tolerance, int maxit, int verbosity) { - Operator opA(A); // Construct preconditioner. - Dune::SeqILU0 precond(A, 1.0); + typedef Dune::SeqILU0 Preconditioner; + auto precond = makePreconditioner(opA, 1.0, comm); // Construct linear solver. - Dune::BiCGSTABSolver linsolve(opA, precond, tolerance, maxit, verbosity); + Dune::BiCGSTABSolver linsolve(opA, sp, *precond, tolerance, maxit, verbosity); // Solve system. Dune::InverseOperatorResult result; diff --git a/opm/core/linalg/LinearSolverIstl.hpp b/opm/core/linalg/LinearSolverIstl.hpp index da8e9ab3..df7b681e 100644 --- a/opm/core/linalg/LinearSolverIstl.hpp +++ b/opm/core/linalg/LinearSolverIstl.hpp @@ -86,6 +86,19 @@ namespace Opm virtual double getTolerance() const; private: + /// \brief Solve the linear system using ISTL + /// \param[in] opA The linear operator of the system to solve. + /// \param[in,out] x The vector with the initial guess that will store + /// the computed solution + /// \param[out] solution C array for storing the solution vector. + /// \param[in] b The vector containing the right hand side of the system. + /// \param[in] sp The scalar product to use. + /// \param[in] comm The information about the parallel domain decomposition. + /// \param[in] maxit The maximum number of iterations allowed. + template + LinearSolverReport solveSystem(O& opA, V& x, double* solution, + V& b, S& sp, const C& comm, int maxit) const; + double linsolver_residual_tolerance_; int linsolver_verbosity_; enum LinsolverType { CG_ILU0 = 0, CG_AMG = 1, BiCGStab_ILU0 = 2, FastAMG=3, KAMG=4 }; From 1e205adcf137c368dada39a35ca705a93afea3a0 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 20 Mar 2014 21:50:09 +0100 Subject: [PATCH 50/81] Added support for the parallel solvers of dune-istl. To support this the solveSystem methods of the LinearSolverInterface gets an optional additional template parameter of type boost::any. It can hold any copy constructable object. In our case it is used to pass the information about the parallelization into the solvers of dune-istl without the compiler needing to know their type. Inside of LinearSolverIstl::solveSystem we check whether the type stored inside of boost::any is the new ParallelIstlInformation. If this is the case we extract the information and use the parallel solvers if available, otherwise we solve serial/sequential. The new ParallelIstlInformation is needed as the OwnerOverlapCopyCommunication is not copy constructable. This is indeed a design flaw that should and will fixed upstream, but for the time being we need ParallelIstlInformation to transfer the ParallelIndexSet and RemoteIndices objects. --- CMakeLists.txt | 9 + CMakeLists_files.cmake | 1 + opm/core/linalg/LinearSolverFactory.cpp | 5 +- opm/core/linalg/LinearSolverFactory.hpp | 3 +- opm/core/linalg/LinearSolverInterface.hpp | 4 +- opm/core/linalg/LinearSolverIstl.cpp | 82 +++++-- opm/core/linalg/LinearSolverIstl.hpp | 6 +- opm/core/linalg/LinearSolverUmfpack.cpp | 3 +- opm/core/linalg/LinearSolverUmfpack.hpp | 3 +- opm/core/linalg/ParallelIstlInformation.hpp | 160 ++++++++++++ tests/test_parallel_linearsolver.cpp | 254 ++++++++++++++++++++ 11 files changed, 502 insertions(+), 28 deletions(-) create mode 100644 opm/core/linalg/ParallelIstlInformation.hpp create mode 100644 tests/test_parallel_linearsolver.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b4fd8b2e..1ca303dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,12 @@ macro (sources_hook) ) endif (NOT SuiteSparse_FOUND) + if (NOT MPI_FOUND OR NOT DUNE_ISTL_FOUND) + list (REMOVE_ITEM TEST_SOURCE_FILES + tests/test_parallel_linearsolver.cpp + ) + endif (NOT MPI_FOUND OR NOT DUNE_ISTL_FOUND) + # we are not supposed to include the TinyXML test prog. regardless list (REMOVE_ITEM opm-core_SOURCES ${PROJECT_SOURCE_DIR}/${opm-core_DIR}/core/utility/parameters/tinyxml/xmltest.cpp @@ -118,3 +124,6 @@ endmacro (install_hook) # all setup common to the OPM library modules is done here include (OpmLibMain) + + +message("MPI_FOUND=${MPI_FOUND} DUNE_ISTL_FOUND=${DUNE_ISTL_FOUND}") diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index c16ca66e..a59d89ad 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -163,6 +163,7 @@ list (APPEND TEST_SOURCE_FILES tests/test_column_extract.cpp tests/test_geom2d.cpp tests/test_linearsolver.cpp + tests/test_parallel_linearsolver.cpp tests/test_param.cpp tests/test_blackoilfluid.cpp tests/test_shadow.cpp diff --git a/opm/core/linalg/LinearSolverFactory.cpp b/opm/core/linalg/LinearSolverFactory.cpp index bd517b66..207c4ea5 100644 --- a/opm/core/linalg/LinearSolverFactory.cpp +++ b/opm/core/linalg/LinearSolverFactory.cpp @@ -97,9 +97,10 @@ namespace Opm const int* ja, const double* sa, const double* rhs, - double* solution) const + double* solution, + const boost::any& add) const { - return solver_->solve(size, nonzeros, ia, ja, sa, rhs, solution); + return solver_->solve(size, nonzeros, ia, ja, sa, rhs, solution, add); } void LinearSolverFactory::setTolerance(const double tol) diff --git a/opm/core/linalg/LinearSolverFactory.hpp b/opm/core/linalg/LinearSolverFactory.hpp index 6cc2354b..f27140bc 100644 --- a/opm/core/linalg/LinearSolverFactory.hpp +++ b/opm/core/linalg/LinearSolverFactory.hpp @@ -74,7 +74,8 @@ namespace Opm const int* ja, const double* sa, const double* rhs, - double* solution) const; + double* solution, + const boost::any& add=boost::any()) const; /// Set tolerance for the linear solver. /// \param[in] tol tolerance value diff --git a/opm/core/linalg/LinearSolverInterface.hpp b/opm/core/linalg/LinearSolverInterface.hpp index 559eaaec..ba60e7b7 100644 --- a/opm/core/linalg/LinearSolverInterface.hpp +++ b/opm/core/linalg/LinearSolverInterface.hpp @@ -20,6 +20,7 @@ #ifndef OPM_LINEARSOLVERINTERFACE_HEADER_INCLUDED #define OPM_LINEARSOLVERINTERFACE_HEADER_INCLUDED +#include struct CSRMatrix; @@ -69,7 +70,8 @@ namespace Opm const int* ja, const double* sa, const double* rhs, - double* solution) const = 0; + double* solution, + const boost::any& add=boost::any()) const = 0; /// Set tolerance for the linear solver. /// \param[in] tol tolerance value diff --git a/opm/core/linalg/LinearSolverIstl.cpp b/opm/core/linalg/LinearSolverIstl.cpp index 181b9ed2..82278290 100644 --- a/opm/core/linalg/LinearSolverIstl.cpp +++ b/opm/core/linalg/LinearSolverIstl.cpp @@ -23,6 +23,7 @@ #endif #include +#include // Silence compatibility warning from DUNE headers since we don't use // the deprecated member anyway (in this compilation unit) @@ -35,7 +36,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -134,7 +137,8 @@ namespace Opm const int* ja, const double* sa, const double* rhs, - double* solution) const + double* solution, + const boost::any& comm) const { // Build Istl structures from input. // System matrix @@ -173,10 +177,26 @@ namespace Opm if (maxit == 0) { maxit = 5000; } - Dune::SeqScalarProduct sp; - Dune::Amg::SequentialInformation comm; - Operator opA(A); - return solveSystem(opA, x, solution, b, sp, comm, maxit); +#if HAVE_MPI + if(comm.type()==typeid(ParallelISTLInformation)) + { + typedef Dune::OwnerOverlapCopyCommunication Comm; + const ParallelISTLInformation& info = boost::any_cast(comm); + Comm istlComm(info.communicator()); + info.copyValuesTo(istlComm.indexSet(), istlComm.remoteIndices()); + Dune::OverlappingSchwarzOperator + opA(A, istlComm); + Dune::OverlappingSchwarzScalarProduct sp(istlComm); + return solveSystem(opA, x, solution, b, sp, istlComm, maxit); + } + else +#endif + { + Dune::SeqScalarProduct sp; + Dune::Amg::SequentialInformation comm; + Operator opA(A); + return solveSystem(opA, x, solution, b, sp, comm, maxit); + } } template @@ -237,24 +257,44 @@ template namespace { + template + struct SmootherChooser + { + typedef P Type; + }; + +#if HAVE_MPI template + struct SmootherChooser > + { + typedef Dune::OwnerOverlapCopyCommunication Comm; + typedef Dune::BlockPreconditioner + Type; + }; +#endif + + template struct PreconditionerTraits { - typedef std::shared_ptr

PointerType; + typedef typename SmootherChooser::Type SmootherType; + typedef std::shared_ptr PointerType; }; template - typename PreconditionerTraits::PointerType + typename PreconditionerTraits::PointerType makePreconditioner(O& opA, double relax, const C& comm, int iterations=1) { - typename Dune::Amg::SmootherTraits

::Arguments args; - typename Dune::Amg::ConstructionTraits

::Arguments cargs; + typedef typename SmootherChooser::Type SmootherType; + typedef typename PreconditionerTraits::PointerType PointerType; + typename Dune::Amg::SmootherTraits::Arguments args; + typename Dune::Amg::ConstructionTraits::Arguments cargs; cargs.setMatrix(opA.getmat()); args.iterations=iterations; args.relaxationFactor=relax; cargs.setArgs(args); cargs.setComm(comm); - return std::shared_ptr

(Dune::Amg::ConstructionTraits

::construct(cargs)); + return PointerType(Dune::Amg::ConstructionTraits::construct(cargs)); } template @@ -322,19 +362,20 @@ template #endif #if SMOOTHER_ILU - typedef Dune::SeqILU0 Smoother; + typedef Dune::SeqILU0 SeqSmoother; #else - typedef Dune::SeqSOR Smoother; + typedef Dune::SeqSOR SeqSmoother; #endif + typedef typename SmootherChooser::Type Smoother; typedef Dune::Amg::CoarsenCriterion Criterion; - typedef Dune::Amg::AMG Precond; + typedef Dune::Amg::AMG Precond; // Construct preconditioner. Criterion criterion; - Precond::SmootherArgs smootherArgs; + typename Precond::SmootherArgs smootherArgs; setUpCriterion(criterion, linsolver_prolongate_factor, verbosity, linsolver_smooth_steps); - Precond precond(opA, criterion, smootherArgs); + Precond precond(opA, criterion, smootherArgs, comm); // Construct linear solver. Dune::CGSolver linsolve(opA, sp, precond, tolerance, maxit, verbosity); @@ -359,6 +400,7 @@ template double linsolver_prolongate_factor, int linsolver_smooth_steps) { // Solve with AMG solver. + Dune::MatrixAdapter sOpA(opA.getmat()); #if FIRST_DIAGONAL typedef Dune::Amg::FirstDiagonal CouplingMetric; @@ -385,10 +427,10 @@ template Criterion criterion; setUpCriterion(criterion, linsolver_prolongate_factor, verbosity, linsolver_smooth_steps); - Precond precond(opA, criterion, smootherArgs); + Precond precond(sOpA, criterion, smootherArgs); // Construct linear solver. - Dune::GeneralizedPCGSolver linsolve(opA, sp, precond, tolerance, maxit, verbosity); + Dune::GeneralizedPCGSolver linsolve(sOpA, precond, tolerance, maxit, verbosity); // Solve system. Dune::InverseOperatorResult result; @@ -408,6 +450,8 @@ template double linsolver_prolongate_factor) { // Solve with AMG solver. + typedef Dune::MatrixAdapter Operator; + Operator sOpA(opA.getmat()); #if FIRST_DIAGONAL typedef Dune::Amg::FirstDiagonal CouplingMetric; @@ -433,10 +477,10 @@ template parms.setNoPreSmoothSteps(smooth_steps); parms.setNoPostSmoothSteps(smooth_steps); parms.setProlongationDampingFactor(linsolver_prolongate_factor); - Precond precond(opA, criterion, parms); + Precond precond(sOpA, criterion, parms); // Construct linear solver. - Dune::GeneralizedPCGSolver linsolve(opA, sp, precond, tolerance, maxit, verbosity); + Dune::GeneralizedPCGSolver linsolve(sOpA, precond, tolerance, maxit, verbosity); // Solve system. Dune::InverseOperatorResult result; diff --git a/opm/core/linalg/LinearSolverIstl.hpp b/opm/core/linalg/LinearSolverIstl.hpp index df7b681e..de32783f 100644 --- a/opm/core/linalg/LinearSolverIstl.hpp +++ b/opm/core/linalg/LinearSolverIstl.hpp @@ -24,12 +24,11 @@ #include #include #include - +#include namespace Opm { - /// Concrete class encapsulating some dune-istl linear solvers. class LinearSolverIstl : public LinearSolverInterface { @@ -75,7 +74,8 @@ namespace Opm const int* ja, const double* sa, const double* rhs, - double* solution) const; + double* solution, + const boost::any& comm=boost::any()) const; /// Set tolerance for the residual in dune istl linear solver. /// \param[in] tol tolerance value diff --git a/opm/core/linalg/LinearSolverUmfpack.cpp b/opm/core/linalg/LinearSolverUmfpack.cpp index 8980d4b1..e7067b69 100644 --- a/opm/core/linalg/LinearSolverUmfpack.cpp +++ b/opm/core/linalg/LinearSolverUmfpack.cpp @@ -46,7 +46,8 @@ namespace Opm const int* ja, const double* sa, const double* rhs, - double* solution) const + double* solution, + const boost::any&) const { CSRMatrix A = { (size_t)size, diff --git a/opm/core/linalg/LinearSolverUmfpack.hpp b/opm/core/linalg/LinearSolverUmfpack.hpp index f2562a15..7126bc4a 100644 --- a/opm/core/linalg/LinearSolverUmfpack.hpp +++ b/opm/core/linalg/LinearSolverUmfpack.hpp @@ -55,7 +55,8 @@ namespace Opm const int* ja, const double* sa, const double* rhs, - double* solution) const; + double* solution, + const boost::any& add=boost::any()) const; /// Set tolerance for the linear solver. /// \param[in] tol tolerance value diff --git a/opm/core/linalg/ParallelIstlInformation.hpp b/opm/core/linalg/ParallelIstlInformation.hpp new file mode 100644 index 00000000..38795774 --- /dev/null +++ b/opm/core/linalg/ParallelIstlInformation.hpp @@ -0,0 +1,160 @@ +/* + Copyright 2014 Dr. Markus Blatt - HPC-Simulation-Software & Services + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ +#ifndef OPM_PARALLELISTLINFORMTION_HEADER_INCLUDED +#define OPM_PARALLELISTLINFORMTION_HEADER_INCLUDED + +#if HAVE_MPI && HAVE_DUNE_ISTL + +#include "mpi.h" +#include +#include +#include +#include + +#include + +namespace Opm +{ + +/// \brief Class that encapsulates the parallelization information needed by the +/// ISTL solvers. +class ParallelISTLInformation +{ +public: + /// \brief The type of the parallel index set used. + typedef Dune::OwnerOverlapCopyCommunication::ParallelIndexSet ParallelIndexSet; + /// \brief The type of the remote indices information used. + typedef Dune::OwnerOverlapCopyCommunication::RemoteIndices RemoteIndices; + + /// \brief Constructs an empty parallel information object using MPI_COMM_WORLD + ParallelISTLInformation() + : indexSet_(new ParallelIndexSet), + remoteIndices_(new RemoteIndices(*indexSet_, *indexSet_, MPI_COMM_WORLD)), + communicator_(MPI_COMM_WORLD) + {} + /// \brief Constructs an empty parallel information object using a communicator. + /// \param communicator The communicator to use. + ParallelISTLInformation(MPI_Comm communicator) + : indexSet_(new ParallelIndexSet), + remoteIndices_(new RemoteIndices(*indexSet_, *indexSet_, communicator)), + communicator_(communicator) + {} + /// \brief Constructs a parallel information object from the specified information. + /// \param indexSet The parallel index set to use. + /// \param remoteIndices The remote indices information to use. + /// \param communicator The communicator to use. + ParallelISTLInformation(const std::shared_ptr& indexSet, + const std::shared_ptr& remoteIndices, + MPI_Comm communicator) + : indexSet_(indexSet), remoteIndices_(remoteIndices), communicator_(communicator) + {} + /// \brief Copy constructor. + /// + /// The information will be shared by the the two objects. + ParallelISTLInformation(const ParallelISTLInformation& other) + : indexSet_(other.indexSet_), remoteIndices_(other.remoteIndices_), + communicator_(other.communicator_) + {} + /// \brief Get a pointer to the underlying index set. + std::shared_ptr indexSet() const + { + return indexSet_; + } + /// \brief Get a pointer to the remote indices information. + std::shared_ptr remoteIndices() const + { + return remoteIndices_; + } + /// \brief Get the MPI communicator that we use. + MPI_Comm communicator() const + { + return communicator_; + } + /// \brief Copy the information stored to the specified objects. + /// \param[out] indexSet The object to store the index set in. + /// \param[out] remoteIndices The object to store the remote indices information in. + void copyValuesTo(ParallelIndexSet& indexSet, RemoteIndices& remoteIndices) const + { + indexSet.beginResize(); + IndexSetInserter inserter(indexSet); + std::for_each(indexSet_->begin(), indexSet_->end(), inserter); + indexSet.endResize(); + remoteIndices.rebuild(); + } + /// \brief Communcate the dofs owned by us to the other process. + /// + /// Afterwards all associated dofs will contain the same data. + template + void copyOwnerToAll (const T& source, T& dest) const + { + typedef Dune::Combine,Dune::EnumItem,Dune::OwnerOverlapCopyAttributeSet::AttributeSet> OwnerOverlapSet; + typedef Dune::Combine,Dune::OwnerOverlapCopyAttributeSet::AttributeSet> AllSet; + OwnerOverlapSet sourceFlags; + AllSet destFlags; + Dune::Interface interface(communicator_); + if(!remoteIndices_->isSynced()) + remoteIndices_->rebuild(); + interface.build(*remoteIndices_,sourceFlags,destFlags); + Dune::BufferedCommunicator communicator; + communicator.template build(interface); + communicator.template forward >(source,dest); + communicator.free(); + } +private: + /** \brief gather/scatter callback for communcation */ + template + struct CopyGatherScatter + { + typedef typename Dune::CommPolicy::IndexedType V; + + static V gather(const T& a, std::size_t i) + { + return a[i]; + } + + static void scatter(T& a, V v, std::size_t i) + { + a[i] = v; + } + }; + template + class IndexSetInserter + { + public: + typedef T ParallelIndexSet; + + IndexSetInserter(ParallelIndexSet& indexSet) + : indexSet_(&indexSet) + {} + void operator()(const typename ParallelIndexSet::IndexPair& pair) + { + indexSet_->add(pair.global(), pair.local()); + } + + private: + ParallelIndexSet* indexSet_; + }; + + std::shared_ptr indexSet_; + std::shared_ptr remoteIndices_; + MPI_Comm communicator_; +}; +} // end namespace Opm +#endif +#endif diff --git a/tests/test_parallel_linearsolver.cpp b/tests/test_parallel_linearsolver.cpp new file mode 100644 index 00000000..aa670d27 --- /dev/null +++ b/tests/test_parallel_linearsolver.cpp @@ -0,0 +1,254 @@ +/* + Copyright 2014 Dr. Markus Blatt - HPC-Simulation-Software & Services + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#include + +#if HAVE_DYNAMIC_BOOST_TEST +#define BOOST_TEST_DYN_LINK +#endif +#define NVERBOSE // to suppress our messages when throwing + +#define BOOST_TEST_MODULE OPM-ParallelIterativeSolverTest +#include + +// MPI header +#if HAVE_MPI +#include +#include +#include +#include +#include +#include +#include +#include +#else +#error "This file needs to compiled with MPI support!" +#endif +#include +#include + +#include +#include +#include + +struct MPIFixture { + MPIFixture() + { + int m_argc = boost::unit_test::framework::master_test_suite().argc; + char** m_argv = boost::unit_test::framework::master_test_suite().argv; + MPI_Init(&m_argc, &m_argv); + } + ~MPIFixture() + { + MPI_Finalize(); + } +}; + +BOOST_GLOBAL_FIXTURE(MPIFixture); + +struct MyMatrix +{ + MyMatrix(std::size_t rows, std::size_t nnz) + : data(nnz, 0.0), rowStart(rows+1, -1), + colIndex(nnz, -1) + {} + MyMatrix() + : data(), rowStart(), colIndex() + {} + + std::vector data; + std::vector rowStart; + std::vector colIndex; +}; + +typedef int LocalId; +typedef int GlobalId; +typedef Dune::OwnerOverlapCopyCommunication Communication; +typedef Dune::OwnerOverlapCopyAttributeSet GridAttributes; +typedef GridAttributes::AttributeSet GridFlag; +typedef Dune::ParallelLocalIndex LocalIndex; + +/// \brief Sets up a paralle Laplacian. +/// +/// The process stores the unknowns with indices in the range [start, end). +/// As we use an overlapping domain decomposition, the process owns the indices +/// in the range [istart, iend]. If we would only used the indices in this range then +/// they form a partitioning of the whole index set. +/// \tparam I The type of the parallel index set (for convenience) +/// \param indexset The parallel index set for marking owner and copy region. +/// \param N The global number of unknowns of the system. +/// \param start The first index stored on this process +/// \param end One past the last index stored on this process +/// \param istart The first index that the process owns. +/// \param iend One past the last index the process owns. +template +std::shared_ptr create1DLaplacian(I& indexset, int N, int start, int end, + int istart, int iend) +{ + indexset.beginResize(); + MyMatrix* mm=new MyMatrix(end-start, (end-start)*3); + int nnz=0; + mm->rowStart[0]=0; + assert(start==0||start=iend) + { + // We are in the overlap region of the grid + // therefore we setup the system such that + // right hand side will equal the left hand side + // of the linear system. + mm->colIndex[nnz]=localRow; + mm->data[nnz++]=1.0; + indexset.add(row, LocalIndex(localRow, GridAttributes::copy, true)); + mm->rowStart[localRow+1]=nnz; + continue; + } + + double dval=0; + if(row>0) + { + mm->colIndex[nnz]=localRow-1; + mm->data[nnz++]=-1; + dval+=1; + } + mm->colIndex[nnz]=localRow; + mm->data[nnz++]=2;//dval+(rowcolIndex[nnz]=localRow+1; + mm->data[nnz++]=-1; + dval+=1; + } + mm->rowStart[localRow+1]=nnz; + indexset.add(row, LocalIndex(localRow, GridAttributes::owner, true)); + } + mm->data.resize(nnz); + mm->colIndex.resize(nnz); + indexset.endResize(); + return std::shared_ptr(mm); +} + +template +void createRandomVectors(O& pinfo, int NN, std::vector& x, std::vector& b, + const MyMatrix& mat) +{ + x.resize(NN); + for(auto entry=x.begin(), end =x.end(); entry!=end; ++entry) + *entry=((double) (rand()%100))/10.0; + + pinfo.copyOwnerToAll(x,x); + + b.resize(NN); + + // Construct the right hand side as b=A*x + std::fill(b.begin(), b.end(), 0.0); + for(std::size_t row=0; row0) + start = istart - 1; + else + start = istart; + + if(iend x(end-start), b(end-start); + createRandomVectors(comm, end-start, x, b, *mat); + std::vector exact(x); + std::fill(x.begin(), x.end(), 0.0); + Opm::LinearSolverFactory ls(param); + boost::any anyComm(comm); + ls.solve(N, mat->data.size(), &(mat->rowStart[0]), + &(mat->colIndex[0]), &(mat->data[0]), &(b[0]), + &(x[0]), anyComm); +} + +#ifdef HAVE_DUNE_ISTL +BOOST_AUTO_TEST_CASE(CGAMGTest) +{ + Opm::parameter::ParameterGroup param; + param.insertParameter(std::string("linsolver"), std::string("istl")); + param.insertParameter(std::string("linsolver_type"), std::string("1")); + param.insertParameter(std::string("linsolver_max_iterations"), std::string("200")); + run_test(param); +} + +BOOST_AUTO_TEST_CASE(CGILUTest) +{ + Opm::parameter::ParameterGroup param; + param.insertParameter(std::string("linsolver"), std::string("istl")); + param.insertParameter(std::string("linsolver_type"), std::string("0")); + param.insertParameter(std::string("linsolver_max_iterations"), std::string("200")); + run_test(param); +} + +BOOST_AUTO_TEST_CASE(BiCGILUTest) +{ + Opm::parameter::ParameterGroup param; + param.insertParameter(std::string("linsolver"), std::string("istl")); + param.insertParameter(std::string("linsolver_type"), std::string("2")); + param.insertParameter(std::string("linsolver_max_iterations"), std::string("200")); + run_test(param); +} +#endif + From c1a7a03987bea217b2a835f5a3050d8fa3167680 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Tue, 25 Mar 2014 09:58:07 +0100 Subject: [PATCH 51/81] Moved ISTL right and left hand side construction for ISTL to solveSystem cherry-picked from add-scalarproduct and ported. --- opm/core/linalg/LinearSolverIstl.cpp | 47 ++++++++++++++-------------- opm/core/linalg/LinearSolverIstl.hpp | 10 +++--- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/opm/core/linalg/LinearSolverIstl.cpp b/opm/core/linalg/LinearSolverIstl.cpp index 82278290..5355d05b 100644 --- a/opm/core/linalg/LinearSolverIstl.cpp +++ b/opm/core/linalg/LinearSolverIstl.cpp @@ -154,30 +154,12 @@ namespace Opm A[ri][ja[i]] = sa[i]; } } - // System RHS - Vector b(size); - std::copy(rhs, rhs + size, b.begin()); - // System solution - Vector x(size); - x = 0.0; - - if (linsolver_save_system_) - { - // Save system to files. - writeMatrixToMatlab(A, linsolver_save_filename_ + "-mat"); - std::string rhsfile(linsolver_save_filename_ + "-rhs"); - std::ofstream rhsf(rhsfile.c_str()); - rhsf.precision(15); - rhsf.setf(std::ios::scientific | std::ios::showpos); - std::copy(b.begin(), b.end(), - std::ostream_iterator(rhsf, "\n")); - } int maxit = linsolver_max_iterations_; if (maxit == 0) { maxit = 5000; } -#if HAVE_MPI + #if HAVE_MPI if(comm.type()==typeid(ParallelISTLInformation)) { typedef Dune::OwnerOverlapCopyCommunication Comm; @@ -187,7 +169,7 @@ namespace Opm Dune::OverlappingSchwarzOperator opA(A, istlComm); Dune::OverlappingSchwarzScalarProduct sp(istlComm); - return solveSystem(opA, x, solution, b, sp, istlComm, maxit); + return solveSystem(opA, solution, rhs, sp, istlComm, maxit); } else #endif @@ -195,15 +177,34 @@ namespace Opm Dune::SeqScalarProduct sp; Dune::Amg::SequentialInformation comm; Operator opA(A); - return solveSystem(opA, x, solution, b, sp, comm, maxit); + return solveSystem(opA, solution, rhs, sp, comm, maxit); } } -template + template LinearSolverInterface::LinearSolverReport - LinearSolverIstl::solveSystem (O& opA, V& x, double* solution, V& b, + LinearSolverIstl::solveSystem (O& opA, double* solution, const double* rhs, S& sp, const C& comm, int maxit) const { + // System RHS + Vector b(opA.getmat().N()); + std::copy(rhs, rhs + size, b.begin()); + // System solution + Vector x(opA.getmat().M()); + x = 0.0; + + if (linsolver_save_system_) + { + // Save system to files. + writeMatrixToMatlab(opA.getmat(), linsolver_save_filename_ + "-mat"); + std::string rhsfile(linsolver_save_filename_ + "-rhs"); + std::ofstream rhsf(rhsfile.c_str()); + rhsf.precision(15); + rhsf.setf(std::ios::scientific | std::ios::showpos); + std::copy(b.begin(), b.end(), + std::ostream_iterator(rhsf, "\n")); + } + LinearSolverReport res; switch (linsolver_type_) { case CG_ILU0: diff --git a/opm/core/linalg/LinearSolverIstl.hpp b/opm/core/linalg/LinearSolverIstl.hpp index de32783f..4a948c08 100644 --- a/opm/core/linalg/LinearSolverIstl.hpp +++ b/opm/core/linalg/LinearSolverIstl.hpp @@ -88,16 +88,14 @@ namespace Opm private: /// \brief Solve the linear system using ISTL /// \param[in] opA The linear operator of the system to solve. - /// \param[in,out] x The vector with the initial guess that will store - /// the computed solution /// \param[out] solution C array for storing the solution vector. - /// \param[in] b The vector containing the right hand side of the system. + /// \param[in] rhs C array containing the right hand side. /// \param[in] sp The scalar product to use. /// \param[in] comm The information about the parallel domain decomposition. /// \param[in] maxit The maximum number of iterations allowed. - template - LinearSolverReport solveSystem(O& opA, V& x, double* solution, - V& b, S& sp, const C& comm, int maxit) const; + template + LinearSolverReport solveSystem(O& opA, double* solution, const double *rhs, + S& sp, const C& comm, int maxit) const; double linsolver_residual_tolerance_; int linsolver_verbosity_; From 14e271f0496cba7ff39dca5d2edeadbfa0ff0660 Mon Sep 17 00:00:00 2001 From: osae Date: Wed, 26 Mar 2014 14:08:39 +0100 Subject: [PATCH 52/81] Some adjustments to equil initialisation. - Saturations, phase pressures, and standard initialsation of RS and RV now agree to baseline. - Tables of RS and RV versus vertical depth (kw RSVD RVVD) have been hardcoded for testing (need new parser) and the calculations agree to baseline in the gas and oil zones. In the water zone there is some differences: Our code computes saturated RS and RV using the final phase pressures (these are modified to be consistent with saturations and capillary pressures) while the baseline uses unmodified phase pressures. --- opm/core/simulator/EquilibrationHelpers.hpp | 213 +++++++++++++++++++- opm/core/simulator/initStateEquil.hpp | 91 +++++++-- opm/core/simulator/initStateEquil_impl.hpp | 34 +++- 3 files changed, 306 insertions(+), 32 deletions(-) diff --git a/opm/core/simulator/EquilibrationHelpers.hpp b/opm/core/simulator/EquilibrationHelpers.hpp index 0d4d0819..d89dd8c3 100644 --- a/opm/core/simulator/EquilibrationHelpers.hpp +++ b/opm/core/simulator/EquilibrationHelpers.hpp @@ -175,7 +175,8 @@ namespace Opm * depth and pressure @c press. */ virtual double operator()(const double depth, - const double press) const = 0; + const double press, + const double sat = 0.0) const = 0; }; @@ -199,7 +200,8 @@ namespace Opm */ double operator()(const double /* depth */, - const double /* press */) const + const double /* press */, + const double sat = 0.0) const { return 0.0; } @@ -216,14 +218,24 @@ namespace Opm /** * Constructor. * + * \param[in] props property object + * \param[in] cell any cell in the pvt region * \param[in] depth Depth nodes. * \param[in] rs Dissolved gas-oil ratio at @c depth. */ - RsVD(const std::vector& depth, + RsVD(const BlackoilPropertiesInterface& props, + const int cell, + const std::vector& depth, const std::vector& rs) - : depth_(depth) + : props_(props) + , cell_(cell) + , depth_(depth) , rs_(rs) { + auto pu = props_.phaseUsage(); + std::fill(z_, z_ + BlackoilPhases::MaxNumPhases, 0.0); + z_[pu.phase_pos[BlackoilPhases::Vapour]] = 1e100; + z_[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; } /** @@ -240,14 +252,111 @@ namespace Opm */ double operator()(const double depth, - const double /* press */) const + const double press, + const double sat_gas = 0.0) const { - return linearInterpolation(depth_, rs_, depth); + if (sat_gas > 0.0) { + return satRs(press); + } else { + return std::min(satRs(press), linearInterpolation(depth_, rs_, depth)); + } } private: + const BlackoilPropertiesInterface& props_; + const int cell_; std::vector depth_; /**< Depth nodes */ std::vector rs_; /**< Dissolved gas-oil ratio */ + double z_[BlackoilPhases::MaxNumPhases]; + mutable double A_[BlackoilPhases::MaxNumPhases * BlackoilPhases::MaxNumPhases]; + + double satRs(const double press) const + { + props_.matrix(1, &press, z_, &cell_, A_, 0); + // Rs/Bo is in the gas row and oil column of A_. + // 1/Bo is in the oil row and column. + // Recall also that it is stored in column-major order. + const int opos = props_.phaseUsage().phase_pos[BlackoilPhases::Liquid]; + const int gpos = props_.phaseUsage().phase_pos[BlackoilPhases::Vapour]; + const int np = props_.numPhases(); + return A_[np*opos + gpos] / A_[np*opos + opos]; + } + }; + + + /** + * Type that implements "vaporized oil-gas ratio" + * tabulated as a function of depth policy. Data + * typically taken from keyword 'RVVD'. + */ + class RvVD : public RsFunction { + public: + /** + * Constructor. + * + * \param[in] props property object + * \param[in] cell any cell in the pvt region + * \param[in] depth Depth nodes. + * \param[in] rv Dissolved gas-oil ratio at @c depth. + */ + RvVD(const BlackoilPropertiesInterface& props, + const int cell, + const std::vector& depth, + const std::vector& rv) + : props_(props) + , cell_(cell) + , depth_(depth) + , rv_(rv) + { + auto pu = props_.phaseUsage(); + std::fill(z_, z_ + BlackoilPhases::MaxNumPhases, 0.0); + z_[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; + z_[pu.phase_pos[BlackoilPhases::Liquid]] = 1e100; + } + + /** + * Function call. + * + * \param[in] depth Depth at which to calculate RV + * value. + * + * \param[in] press Pressure at which to calculate RV + * value. + * + * \return Vaporized oil-gas ratio (RV) at depth @c + * depth and pressure @c press. + */ + double + operator()(const double depth, + const double press, + const double sat_oil = 0.0 ) const + { + if (sat_oil > 0.0) { + return satRv(press); + } else { + return std::min(satRv(press), linearInterpolation(depth_, rv_, depth)); + } + } + + private: + const BlackoilPropertiesInterface& props_; + const int cell_; + std::vector depth_; /**< Depth nodes */ + std::vector rv_; /**< Vaporized oil-gas ratio */ + double z_[BlackoilPhases::MaxNumPhases]; + mutable double A_[BlackoilPhases::MaxNumPhases * BlackoilPhases::MaxNumPhases]; + + double satRv(const double press) const + { + props_.matrix(1, &press, z_, &cell_, A_, 0); + // Rv/Bg is in the oil row and gas column of A_. + // 1/Bg is in the gas row and column. + // Recall also that it is stored in column-major order. + const int opos = props_.phaseUsage().phase_pos[BlackoilPhases::Liquid]; + const int gpos = props_.phaseUsage().phase_pos[BlackoilPhases::Vapour]; + const int np = props_.numPhases(); + return A_[np*gpos + opos] / A_[np*gpos + gpos]; + } }; @@ -298,9 +407,14 @@ namespace Opm */ double operator()(const double /* depth */, - const double press) const - { - return std::min(satRs(press), rs_sat_contact_); + const double press, + const double sat_gas = 0.0) const + { + if (sat_gas > 0.0) { + return satRs(press); + } else { + return std::min(satRs(press), rs_sat_contact_); + } } private: @@ -323,6 +437,84 @@ namespace Opm } }; + + /** + * Class that implements "vaporized oil-gas ratio" (Rv) + * as function of depth and pressure as follows: + * + * 1. The Rv at the gas-oil contact is equal to the + * saturated Rv value, Rv_sat_contact. + * + * 2. The Rv elsewhere is equal to Rv_sat_contact, but + * constrained to the saturated value as given by the + * local pressure. + * + * This should yield Rv-values that are constant below the + * contact, and decreasing above the contact. + */ + class RvSatAtContact : public RsFunction { + public: + /** + * Constructor. + * + * \param[in] props property object + * \param[in] cell any cell in the pvt region + * \param[in] p_contact oil pressure at the contact + */ + RvSatAtContact(const BlackoilPropertiesInterface& props, const int cell, const double p_contact) + : props_(props), cell_(cell) + { + auto pu = props_.phaseUsage(); + std::fill(z_, z_ + BlackoilPhases::MaxNumPhases, 0.0); + z_[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; + z_[pu.phase_pos[BlackoilPhases::Liquid]] = 1e100; + rv_sat_contact_ = satRv(p_contact); + } + + /** + * Function call. + * + * \param[in] depth Depth at which to calculate RV + * value. + * + * \param[in] press Pressure at which to calculate RV + * value. + * + * \return Dissolved oil-gas ratio (RV) at depth @c + * depth and pressure @c press. + */ + double + operator()(const double /*depth*/, + const double press, + const double sat_oil = 0.0) const + { + if (sat_oil > 0.0) { + return satRv(press); + } else { + return std::min(satRv(press), rv_sat_contact_); + } + } + + private: + const BlackoilPropertiesInterface& props_; + const int cell_; + double z_[BlackoilPhases::MaxNumPhases]; + double rv_sat_contact_; + mutable double A_[BlackoilPhases::MaxNumPhases * BlackoilPhases::MaxNumPhases]; + + double satRv(const double press) const + { + props_.matrix(1, &press, z_, &cell_, A_, 0); + // Rv/Bg is in the oil row and gas column of A_. + // 1/Bg is in the gas row and column. + // Recall also that it is stored in column-major order. + const int opos = props_.phaseUsage().phase_pos[BlackoilPhases::Liquid]; + const int gpos = props_.phaseUsage().phase_pos[BlackoilPhases::Vapour]; + const int np = props_.numPhases(); + return A_[np*gpos + opos] / A_[np*gpos + gpos]; + } + }; + } // namespace Miscibility @@ -355,6 +547,9 @@ namespace Opm double depth; double press; } main, woc, goc; + int live_oil_table_index; + int wet_gas_table_index; + int N; }; /** diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 3767563f..566f2b00 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -175,8 +175,8 @@ namespace Opm std::vector computeRs(const UnstructuredGrid& grid, const CellRangeType& cells, const std::vector oil_pressure, - const Miscibility::RsFunction& rs_func); - + const Miscibility::RsFunction& rs_func, + const std::vector gas_saturation); namespace DeckDependent { @@ -205,8 +205,17 @@ namespace Opm , { rec.gas_oil_contact_depth_ , rec.gas_oil_cap_pressure_ } + , + rec.live_oil_table_index_ + , + rec.wet_gas_table_index_ + , + rec.N_ }; - + if (record.N != 0) { + OPM_THROW(std::domain_error, + "kw EQUIL, item 9: Only N=0 supported."); + } ret.push_back(record); } @@ -267,14 +276,25 @@ namespace Opm // Create Rs functions. rs_func_.reserve(rec.size()); - if (deck.hasField("DISGAS")) { - if (deck.hasField("RSVD")) { - // Rs has been specified as a function of depth. - OPM_THROW(std::runtime_error, "Cannot initialise: RSVD field not read by EclipseGridParser class."); - } else { - // Default initialisation: constant Rs below contact, saturated above. - for (size_t i = 0; i < rec.size(); ++i) { - const int cell = *(eqlmap.cells(i + 1).begin()); + if (deck.hasField("DISGAS")) { + for (size_t i = 0; i < rec.size(); ++i) { + const int cell = *(eqlmap.cells(i + 1).begin()); + // TODO Check this ... + // The index is here picked as a representative for its equlibrium region, + // but is below used to identify PVT region. + // This assumes that an eq-region has uniform pvt properties, is this always ok? + // For Norne this is trivial, as there is only one active pvt-region (PVTNUM=1 for all cells): + if (rec[i].live_oil_table_index > 0) { + if (deck.hasField("RSVD")) { + // TODO When this kw is actually parsed, also check for proper number of available tables + // For now, just use dummy ... + std::vector depth; depth.push_back(0.0); depth.push_back(100.0); + std::vector rs; rs.push_back(0.0); rs.push_back(100.0); + rs_func_.push_back(std::make_shared(props, cell, depth, rs)); + } else { + OPM_THROW(std::runtime_error, "Cannot initialise: RSVD table " << (rec[i].live_oil_table_index) << " not available."); + } + } else { if (rec[i].goc.depth != rec[i].main.depth) { OPM_THROW(std::runtime_error, "Cannot initialise: when no explicit RSVD table is given, \n" @@ -289,8 +309,40 @@ namespace Opm for (size_t i = 0; i < rec.size(); ++i) { rs_func_.push_back(std::make_shared()); } - } + } + rv_func_.reserve(rec.size()); + if (deck.hasField("VAPOIL")) { + for (size_t i = 0; i < rec.size(); ++i) { + const int cell = *(eqlmap.cells(i + 1).begin()); + // TODO Similar as above: eq-region vs pvt-region ... + if (rec[i].wet_gas_table_index > 0) { + if (deck.hasField("RVVD")) { + // TODO When this kw is actually parsed, also check for proper number of available tables + // For now, just use dummy ... + std::vector depth; depth.push_back(0.0); depth.push_back(100.0); + std::vector rv; rv.push_back(0.0); rv.push_back(0.0001); + rv_func_.push_back(std::make_shared(props, cell, depth, rv)); + } else { + OPM_THROW(std::runtime_error, "Cannot initialise: RVVD table " << (rec[i].wet_gas_table_index) << " not available."); + } + } else { + if (rec[i].goc.depth != rec[i].main.depth) { + OPM_THROW(std::runtime_error, + "Cannot initialise: when no explicit RVVD table is given, \n" + "datum depth must be at the gas-oil-contact. " + "In EQUIL region " << (i + 1) << " (counting from 1), this does not hold."); + } + const double p_contact = rec[i].main.press + rec[i].goc.press; + rv_func_.push_back(std::make_shared(props, cell, p_contact)); + } + } + } else { + for (size_t i = 0; i < rec.size(); ++i) { + rv_func_.push_back(std::make_shared()); + } + } + // Compute pressures, saturations, rs and rv factors. calcPressSatRsRv(eqlmap, rec, props, G, grav); @@ -311,6 +363,7 @@ namespace Opm typedef EquilReg EqReg; std::vector< std::shared_ptr > rs_func_; + std::vector< std::shared_ptr > rv_func_; PVec pp_; PVec sat_; @@ -335,13 +388,14 @@ namespace Opm const int repcell = *cells.begin(); const RhoCalc calc(props, repcell); - const EqReg eqreg(rec[r], calc, - rs_func_[r], std::make_shared(), + rs_func_[r], rv_func_[r], props.phaseUsage()); - - const PVec press = phasePressures(G, eqreg, cells, grav); + + PVec press = phasePressures(G, eqreg, cells, grav); + const PVec sat = phaseSaturations(eqreg, cells, props, press); + const int np = props.numPhases(); for (int p = 0; p < np; ++p) { copyFromRegion(press[p], cells, pp_[p]); @@ -350,8 +404,9 @@ namespace Opm if (props.phaseUsage().phase_used[BlackoilPhases::Liquid] && props.phaseUsage().phase_used[BlackoilPhases::Vapour]) { const int oilpos = props.phaseUsage().phase_pos[BlackoilPhases::Liquid]; - const Vec rs = computeRs(G, cells, press[oilpos], *(rs_func_[r])); - const Vec rv(cells.size(), 0.0); + const int gaspos = props.phaseUsage().phase_pos[BlackoilPhases::Vapour]; + const Vec rs = computeRs(G, cells, press[oilpos], *(rs_func_[r]), sat[gaspos]); + const Vec rv = computeRs(G, cells, press[gaspos], *(rv_func_[r]), sat[oilpos]); copyFromRegion(rs, cells, rs_); copyFromRegion(rv, cells, rv_); } diff --git a/opm/core/simulator/initStateEquil_impl.hpp b/opm/core/simulator/initStateEquil_impl.hpp index c075f4ec..6c991215 100644 --- a/opm/core/simulator/initStateEquil_impl.hpp +++ b/opm/core/simulator/initStateEquil_impl.hpp @@ -580,7 +580,7 @@ namespace Opm phaseSaturations(const Region& reg, const CellRange& cells, const BlackoilPropertiesInterface& props, - const std::vector< std::vector >& phase_pressures) + std::vector< std::vector >& phase_pressures) { const double z0 = reg.datum(); const double zwoc = reg.zwoc (); @@ -621,6 +621,7 @@ namespace Opm sg = satFromPc(props, gaspos, cell, pcog, increasing); phase_saturations[gaspos][local_index] = sg; } + bool overlap = false; if (gas && water && (sg + sw > 1.0)) { // Overlapping gas-oil and oil-water transition // zones can lead to unphysical saturations when @@ -631,8 +632,32 @@ namespace Opm sg = 1.0 - sw; phase_saturations[waterpos][local_index] = sw; phase_saturations[gaspos][local_index] = sg; + overlap = true; } phase_saturations[oilpos][local_index] = 1.0 - sw - sg; + + // Adjust phase pressures for max and min saturation ... + double pc[BlackoilPhases::MaxNumPhases]; + double sat[BlackoilPhases::MaxNumPhases]; + if (sw > smax[waterpos]-1.0e-6) { + sat[waterpos] = smax[waterpos]; + props.capPress(1, sat, &cell, pc, 0); + phase_pressures[oilpos][local_index] = phase_pressures[waterpos][local_index] + pc[waterpos]; + } else if (overlap || sg > smax[gaspos]-1.0e-6) { + sat[gaspos] = smax[gaspos]; + props.capPress(1, sat, &cell, pc, 0); + phase_pressures[oilpos][local_index] = phase_pressures[gaspos][local_index] - pc[gaspos]; + } + if (sg < smin[gaspos]+1.0e-6) { + sat[gaspos] = smin[gaspos]; + props.capPress(1, sat, &cell, pc, 0); + phase_pressures[gaspos][local_index] = phase_pressures[oilpos][local_index] + pc[gaspos]; + } + if (sw < smin[waterpos]+1.0e-6) { + sat[waterpos] = smin[waterpos]; + props.capPress(1, sat, &cell, pc, 0); + phase_pressures[waterpos][local_index] = phase_pressures[oilpos][local_index] - pc[waterpos]; + } } return phase_saturations; } @@ -659,20 +684,19 @@ namespace Opm std::vector computeRs(const UnstructuredGrid& grid, const CellRangeType& cells, const std::vector oil_pressure, - const Miscibility::RsFunction& rs_func) + const Miscibility::RsFunction& rs_func, + const std::vector gas_saturation) { assert(grid.dimensions == 3); std::vector rs(cells.size()); int count = 0; for (auto it = cells.begin(); it != cells.end(); ++it, ++count) { const double depth = grid.cell_centroids[3*(*it) + 2]; - rs[count] = rs_func(depth, oil_pressure[count]); + rs[count] = rs_func(depth, oil_pressure[count], gas_saturation[count]); } return rs; } - - } // namespace Equil From 66a48897710dec95465ddddde2b1168a75c841a5 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 27 Mar 2014 11:45:34 +0100 Subject: [PATCH 53/81] [cmake] Export HAVE_DUNE_ISTL for other modules. This is e.g. needed in the parallel version of autodiff to test whether ISTL is there. --- cmake/Modules/opm-core-prereqs.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/Modules/opm-core-prereqs.cmake b/cmake/Modules/opm-core-prereqs.cmake index 8b3d45de..b7cf3bf3 100644 --- a/cmake/Modules/opm-core-prereqs.cmake +++ b/cmake/Modules/opm-core-prereqs.cmake @@ -5,6 +5,7 @@ set (opm-core_CONFIG_VAR HAVE_ERT HAVE_SUITESPARSE_UMFPACK_H + HAVE_DUNE_ISTL ) # dependencies From aa5ac90fa6e330efb976f75772938789541f22ca Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 27 Mar 2014 11:47:35 +0100 Subject: [PATCH 54/81] Removed superfluous export of HAVE_DUNE_ISTL. With the last patch HAVE_DUNE_ISTL will already be defined in config.h --- CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ca303dc..f310e854 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,9 +47,6 @@ include (CMakeLists_files.cmake) macro (config_hook) # opm_need_version_of ("dune-common") opm_need_version_of ("dune-istl") - list (APPEND ${project}_CONFIG_IMPL_VARS - HAVE_DUNE_ISTL - ) endmacro (config_hook) macro (prereqs_hook) @@ -126,4 +123,3 @@ endmacro (install_hook) include (OpmLibMain) -message("MPI_FOUND=${MPI_FOUND} DUNE_ISTL_FOUND=${DUNE_ISTL_FOUND}") From 58a7589d7de8b9e673479213a3d5d2417516f88d Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 27 Mar 2014 11:49:41 +0100 Subject: [PATCH 55/81] Throw an error when using a sequential solver in parallel --- opm/core/linalg/LinearSolverIstl.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/opm/core/linalg/LinearSolverIstl.cpp b/opm/core/linalg/LinearSolverIstl.cpp index 5355d05b..fdb6c58a 100644 --- a/opm/core/linalg/LinearSolverIstl.cpp +++ b/opm/core/linalg/LinearSolverIstl.cpp @@ -24,6 +24,7 @@ #include #include +#include // Silence compatibility warning from DUNE headers since we don't use // the deprecated member anyway (in this compilation unit) @@ -50,7 +51,7 @@ #include #include - +#include namespace Opm { @@ -224,6 +225,10 @@ namespace Opm break; case FastAMG: #if defined(HAS_DUNE_FAST_AMG) || DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3) + if(std::is_same >::value) + { + OPM_THROW(std::runtime_error, "Trying to use sequential FastAMG solver for a parallel problem!"); + } res = solveFastAMG(opA, x, b, sp, comm, linsolver_residual_tolerance_, maxit, linsolver_verbosity_, linsolver_prolongate_factor_); #else From 3fceb968eaf9f82504371dc74f4ace441dd2d9e2 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Wed, 26 Mar 2014 10:23:11 +0100 Subject: [PATCH 56/81] [bugfix] Use the size of the vector for the copying. The last patch did not compile as there was no size member in scope. Therefore this patch resorts to using the size of the vector. --- opm/core/linalg/LinearSolverIstl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/core/linalg/LinearSolverIstl.cpp b/opm/core/linalg/LinearSolverIstl.cpp index fdb6c58a..d3e48e47 100644 --- a/opm/core/linalg/LinearSolverIstl.cpp +++ b/opm/core/linalg/LinearSolverIstl.cpp @@ -189,7 +189,7 @@ namespace Opm { // System RHS Vector b(opA.getmat().N()); - std::copy(rhs, rhs + size, b.begin()); + std::copy(rhs, rhs+b.size(), b.begin()); // System solution Vector x(opA.getmat().M()); x = 0.0; From 782a5934c9023c8a731ca5831c024797086bfc21 Mon Sep 17 00:00:00 2001 From: osae Date: Fri, 28 Mar 2014 17:14:16 +0100 Subject: [PATCH 57/81] Fixed Bg versus 1/Bg issue. --- opm/core/props/pvt/SinglePvtLiveGas.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/opm/core/props/pvt/SinglePvtLiveGas.cpp b/opm/core/props/pvt/SinglePvtLiveGas.cpp index 43debe7b..02197dcf 100644 --- a/opm/core/props/pvt/SinglePvtLiveGas.cpp +++ b/opm/core/props/pvt/SinglePvtLiveGas.cpp @@ -34,6 +34,7 @@ #include #include +#include namespace Opm { @@ -101,6 +102,14 @@ namespace Opm undersat_gas_tables_[i][1] = undersatTable.getGasFormationFactorColumn(); undersat_gas_tables_[i][2] = pvtgTable.getOuterTable()->getGasViscosityColumn(); } + + // Bg -> 1/Bg + for (int i=0; i Date: Fri, 28 Mar 2014 17:18:50 +0100 Subject: [PATCH 58/81] Default equil region should be one not zero ... Otherwise problems when kw EQLNUM is used. --- opm/core/simulator/initStateEquil.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index 566f2b00..cfcb56ba 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -241,8 +241,8 @@ namespace Opm } else { // No explicit equilibration region. - // All cells in region zero. - eqlnum.assign(G.number_of_cells, 0); + // All cells in region one. + eqlnum.assign(G.number_of_cells, 1); } return eqlnum; @@ -384,7 +384,7 @@ namespace Opm r = 0, nr = reg.numRegions(); r < nr; ++r) { - const typename RMap::CellRange cells = reg.cells(r); + const typename RMap::CellRange cells = reg.cells(r+1); const int repcell = *cells.begin(); const RhoCalc calc(props, repcell); From d6a2696cbd9fd7748c84a157a543bb91b7fe393a Mon Sep 17 00:00:00 2001 From: osae Date: Fri, 28 Mar 2014 17:35:43 +0100 Subject: [PATCH 59/81] New parser included. --- examples/compute_initial_state.cpp | 8 +- opm/core/simulator/initStateEquil.hpp | 247 ++++++++++++++++++++- opm/core/simulator/initStateEquil_impl.hpp | 20 ++ 3 files changed, 266 insertions(+), 9 deletions(-) diff --git a/examples/compute_initial_state.cpp b/examples/compute_initial_state.cpp index 0b11ecf1..3193c9fa 100644 --- a/examples/compute_initial_state.cpp +++ b/examples/compute_initial_state.cpp @@ -29,6 +29,10 @@ #include #include #include + +#include +#include + #include namespace @@ -80,8 +84,10 @@ try parameter::ParameterGroup param(argc, argv, false); std::cout << "--------------- Reading parameters ---------------" << std::endl; const std::string deck_filename = param.get("deck_filename"); + Opm::ParserPtr parser(new Opm::Parser() ); + Opm::DeckConstPtr deck = parser->parseFile(deck_filename); const double grav = param.getDefault("gravity", unit::gravity); - EclipseGridParser deck(deck_filename); + //EclipseGridParser deck(deck_filename); GridManager gm(deck); const UnstructuredGrid& grid = *gm.c_grid(); BlackoilPropertiesFromDeck props(deck, grid, param); diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index cfcb56ba..c4608b80 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include @@ -63,6 +65,13 @@ namespace Opm const EclipseGridParser& deck, const double gravity, BlackoilState& state); + + + void initStateEquil(const UnstructuredGrid& grid, + const BlackoilPropertiesInterface& props, + const Opm::DeckConstPtr newParserDeck, + const double gravity, + BlackoilState& state); /** @@ -227,7 +236,51 @@ namespace Opm } } + inline + std::vector + getEquil(const Opm::DeckConstPtr newParserDeck) + { + if (newParserDeck->hasKeyword("EQUIL")) { + + Opm::EquilWrapper eql(newParserDeck->getKeyword("EQUIL")); + const int nrec = eql.numRegions(); + + std::vector ret; + ret.reserve(nrec); + for (int r = 0; r < nrec; ++r) { + + EquilRecord record = + { + { eql.datumDepth(r) , + eql.datumDepthPressure(r) } + , + { eql.waterOilContactDepth(r) , + eql.waterOilContactCapillaryPressure(r) } + , + { eql.gasOilContactDepth(r) , + eql.gasOilContactCapillaryPressure(r) } + , + eql.liveOilInitProceedure(r) + , + eql.wetGasInitProceedure(r) + , + eql.initializationTargetAccuracy(r) + }; + if (record.N != 0) { + OPM_THROW(std::domain_error, + "kw EQUIL, item 9: Only N=0 supported."); + } + ret.push_back(record); + } + + return ret; + } + else { + OPM_THROW(std::domain_error, + "Deck does not provide equilibration data."); + } + } inline @@ -249,6 +302,23 @@ namespace Opm } + inline + std::vector + equilnum(const Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& G ) + { + std::vector eqlnum; + if (newParserDeck->hasKeyword("EQLNUM")) { + eqlnum = newParserDeck->getKeyword("EQLNUM")->getIntData(); + } + else { + // No explicit equilibration region. + // All cells in region one. + eqlnum.assign(G.number_of_cells, 1); + } + + return eqlnum; + } template @@ -278,12 +348,7 @@ namespace Opm rs_func_.reserve(rec.size()); if (deck.hasField("DISGAS")) { for (size_t i = 0; i < rec.size(); ++i) { - const int cell = *(eqlmap.cells(i + 1).begin()); - // TODO Check this ... - // The index is here picked as a representative for its equlibrium region, - // but is below used to identify PVT region. - // This assumes that an eq-region has uniform pvt properties, is this always ok? - // For Norne this is trivial, as there is only one active pvt-region (PVTNUM=1 for all cells): + const int cell = *(eqlmap.cells(i + 1).begin()); if (rec[i].live_oil_table_index > 0) { if (deck.hasField("RSVD")) { // TODO When this kw is actually parsed, also check for proper number of available tables @@ -314,8 +379,7 @@ namespace Opm rv_func_.reserve(rec.size()); if (deck.hasField("VAPOIL")) { for (size_t i = 0; i < rec.size(); ++i) { - const int cell = *(eqlmap.cells(i + 1).begin()); - // TODO Similar as above: eq-region vs pvt-region ... + const int cell = *(eqlmap.cells(i + 1).begin()); if (rec[i].wet_gas_table_index > 0) { if (deck.hasField("RVVD")) { // TODO When this kw is actually parsed, also check for proper number of available tables @@ -427,6 +491,173 @@ namespace Opm } }; + + + template <> + class InitialStateComputer { + public: + InitialStateComputer(const BlackoilPropertiesInterface& props, + const Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& G , + const double grav = unit::gravity) + : pp_(props.numPhases(), + std::vector(G.number_of_cells)), + sat_(props.numPhases(), + std::vector(G.number_of_cells)), + rs_(G.number_of_cells), + rv_(G.number_of_cells) + { + // Get the equilibration records. + const std::vector rec = getEquil(newParserDeck); + + // Create (inverse) region mapping. + const RegionMapping<> eqlmap(equilnum(newParserDeck, G)); + + // Create Rs functions. + rs_func_.reserve(rec.size()); + if (newParserDeck->hasKeyword("DISGAS")) { + for (size_t i = 0; i < rec.size(); ++i) { + const int cell = *(eqlmap.cells(i + 1).begin()); + if (rec[i].live_oil_table_index > 0) { + if (newParserDeck->hasKeyword("RSVD") && rec[i].live_oil_table_index <= newParserDeck->getKeyword("RSVD")->size()) { + Opm::SimpleTable rsvd(newParserDeck->getKeyword("RSVD"),std::vector{"vd", "rs"},rec[i].live_oil_table_index-1); + std::vector vd(rsvd.getColumn("vd")); + std::vector rs(rsvd.getColumn("rs")); + rs_func_.push_back(std::make_shared(props, cell, vd, rs)); + } else { + OPM_THROW(std::runtime_error, "Cannot initialise: RSVD table " << (rec[i].live_oil_table_index) << " not available."); + } + } else { + if (rec[i].goc.depth != rec[i].main.depth) { + OPM_THROW(std::runtime_error, + "Cannot initialise: when no explicit RSVD table is given, \n" + "datum depth must be at the gas-oil-contact. " + "In EQUIL region " << (i + 1) << " (counting from 1), this does not hold."); + } + const double p_contact = rec[i].main.press; + rs_func_.push_back(std::make_shared(props, cell, p_contact)); + } + } + } else { + for (size_t i = 0; i < rec.size(); ++i) { + rs_func_.push_back(std::make_shared()); + } + } + + rv_func_.reserve(rec.size()); + if (newParserDeck->hasKeyword("VAPOIL")) { + for (size_t i = 0; i < rec.size(); ++i) { + const int cell = *(eqlmap.cells(i + 1).begin()); + if (rec[i].wet_gas_table_index > 0) { + if (newParserDeck->hasKeyword("RVVD") && rec[i].wet_gas_table_index <= newParserDeck->getKeyword("RVVD")->size()) { + Opm::SimpleTable rvvd(newParserDeck->getKeyword("RVVD"),std::vector{"vd", "rv"},rec[i].wet_gas_table_index-1); + std::vector vd(rvvd.getColumn("vd")); + std::vector rv(rvvd.getColumn("rv")); + rv_func_.push_back(std::make_shared(props, cell, vd, rv)); + } else { + OPM_THROW(std::runtime_error, "Cannot initialise: RVVD table " << (rec[i].wet_gas_table_index) << " not available."); + } + } else { + if (rec[i].goc.depth != rec[i].main.depth) { + OPM_THROW(std::runtime_error, + "Cannot initialise: when no explicit RVVD table is given, \n" + "datum depth must be at the gas-oil-contact. " + "In EQUIL region " << (i + 1) << " (counting from 1), this does not hold."); + } + const double p_contact = rec[i].main.press + rec[i].goc.press; + rv_func_.push_back(std::make_shared(props, cell, p_contact)); + } + } + } else { + for (size_t i = 0; i < rec.size(); ++i) { + rv_func_.push_back(std::make_shared()); + } + } + + // Compute pressures, saturations, rs and rv factors. + calcPressSatRsRv(eqlmap, rec, props, G, grav); + + // Modify oil pressure in no-oil regions so that the pressures of present phases can + // be recovered from the oil pressure and capillary relations. + } + + typedef std::vector Vec; + typedef std::vector PVec; // One per phase. + + const PVec& press() const { return pp_; } + const PVec& saturation() const { return sat_; } + const Vec& rs() const { return rs_; } + const Vec& rv() const { return rv_; } + + private: + typedef DensityCalculator RhoCalc; + typedef EquilReg EqReg; + + std::vector< std::shared_ptr > rs_func_; + std::vector< std::shared_ptr > rv_func_; + + PVec pp_; + PVec sat_; + Vec rs_; + Vec rv_; + + template + void + calcPressSatRsRv(const RMap& reg , + const std::vector< EquilRecord >& rec , + const Opm::BlackoilPropertiesInterface& props, + const UnstructuredGrid& G , + const double grav) + { + typedef Miscibility::NoMixing NoMix; + + for (typename RMap::RegionId + r = 0, nr = reg.numRegions(); + r < nr; ++r) + { + const typename RMap::CellRange cells = reg.cells(r+1); + + const int repcell = *cells.begin(); + const RhoCalc calc(props, repcell); + const EqReg eqreg(rec[r], calc, + rs_func_[r], rv_func_[r], + props.phaseUsage()); + + PVec press = phasePressures(G, eqreg, cells, grav); + + const PVec sat = phaseSaturations(eqreg, cells, props, press); + + const int np = props.numPhases(); + for (int p = 0; p < np; ++p) { + copyFromRegion(press[p], cells, pp_[p]); + copyFromRegion(sat[p], cells, sat_[p]); + } + if (props.phaseUsage().phase_used[BlackoilPhases::Liquid] + && props.phaseUsage().phase_used[BlackoilPhases::Vapour]) { + const int oilpos = props.phaseUsage().phase_pos[BlackoilPhases::Liquid]; + const int gaspos = props.phaseUsage().phase_pos[BlackoilPhases::Vapour]; + const Vec rs = computeRs(G, cells, press[oilpos], *(rs_func_[r]), sat[gaspos]); + const Vec rv = computeRs(G, cells, press[gaspos], *(rv_func_[r]), sat[oilpos]); + copyFromRegion(rs, cells, rs_); + copyFromRegion(rv, cells, rv_); + } + } + } + + template + void copyFromRegion(const Vec& source, + const CellRangeType& cells, + Vec& destination) + { + auto s = source.begin(); + auto c = cells.begin(); + const auto e = cells.end(); + for (; c != e; ++c, ++s) { + destination[*c] = *s; + } + } + + }; } // namespace DeckDependent } // namespace Equil } // namespace Opm diff --git a/opm/core/simulator/initStateEquil_impl.hpp b/opm/core/simulator/initStateEquil_impl.hpp index 6c991215..d227d54d 100644 --- a/opm/core/simulator/initStateEquil_impl.hpp +++ b/opm/core/simulator/initStateEquil_impl.hpp @@ -753,6 +753,26 @@ namespace Opm state.rv() = isc.rv(); // TODO: state.surfacevol() must be computed from s, rs, rv. } + + + void initStateEquil(const UnstructuredGrid& grid, + const BlackoilPropertiesInterface& props, + const Opm::DeckConstPtr newParserDeck, + const double gravity, + BlackoilState& state) + { + typedef Equil::DeckDependent::InitialStateComputer ISC; + ISC isc(props, newParserDeck, grid, gravity); + const auto pu = props.phaseUsage(); + const int ref_phase = pu.phase_used[BlackoilPhases::Liquid] + ? pu.phase_pos[BlackoilPhases::Liquid] + : pu.phase_pos[BlackoilPhases::Aqua]; + state.pressure() = isc.press()[ref_phase]; + state.saturation() = convertSats(isc.saturation()); + state.gasoilratio() = isc.rs(); + state.rv() = isc.rv(); + // TODO: state.surfacevol() must be computed from s, rs, rv. + } From d0da7a9437cfe37a3650e9f3eec46b662b1b782d Mon Sep 17 00:00:00 2001 From: osae Date: Mon, 31 Mar 2014 15:32:06 +0200 Subject: [PATCH 60/81] Update tests and provide some eclipse output. --- tests/capillary_overlap.DATA | 99 ++++++++++++++++++++++++++++- tests/equil_liveoil.DATA | 118 +++++++++++++++++++++++++++++++---- tests/test_equil.cpp | 93 ++++++++++++++++++--------- 3 files changed, 268 insertions(+), 42 deletions(-) diff --git a/tests/capillary_overlap.DATA b/tests/capillary_overlap.DATA index 185784ee..b59e4220 100644 --- a/tests/capillary_overlap.DATA +++ b/tests/capillary_overlap.DATA @@ -1,7 +1,77 @@ +NOECHO + +RUNSPEC ====== + WATER OIL GAS +TABDIMS + 1 1 40 20 1 20 / + +DIMENS +1 1 20 +/ + +WELLDIMS + 30 10 2 30 / + +START + 1 'JAN' 1990 / + +NSTACK + 25 / + +EQLDIMS +-- NTEQUL + 1 / + + +FMTOUT +FMTIN + +GRID ====== + +DXV +1.0 +/ + +DYV +1.0 +/ + +DZV +20*5.0 +/ + + +PORO +20*0.2 +/ + + +PERMZ + 20*1.0 +/ + +PERMY +20*100.0 +/ + +PERMX +20*100.0 +/ + +BOX + 1 1 1 1 1 1 / + +TOPS +0.0 +/ + +PROPS ====== + + PVDO 100 1.0 1.0 200 0.9 1.0 @@ -22,10 +92,37 @@ SGOF 0.8 1 0 0.5 / +PVTW +--RefPres Bw Comp Vw Cv + 1. 1.0 4.0E-5 0.96 0.0 / + + +ROCK +--RefPres Comp + 1. 5.0E-5 / + DENSITY 700 1000 1 / +SOLUTION ====== + EQUIL -50 150 50 0.25 45 0.35 +45 150 50 0.25 45 0.35 1* 1* 0 / + +RPTSOL +'PRES' 'PGAS' 'PWAT' 'SOIL' 'SWAT' 'SGAS' 'RESTART=2' / + +SUMMARY ====== +RUNSUM + +SEPARATE + +SCHEDULE ====== + +RPTSCHED +'PRES' 'PGAS' 'PWAT' 'SOIL' 'SWAT' 'SGAS' 'RESTART=3' 'NEWTON=2' / + + +END diff --git a/tests/equil_liveoil.DATA b/tests/equil_liveoil.DATA index 955a09b7..e1b417c4 100644 --- a/tests/equil_liveoil.DATA +++ b/tests/equil_liveoil.DATA @@ -1,27 +1,94 @@ +NOECHO + +RUNSPEC ====== + WATER OIL GAS DISGAS +TABDIMS + 1 1 40 20 1 20 / + +DIMENS +1 1 20 +/ + +WELLDIMS + 30 10 2 30 / + +START + 1 'JAN' 1990 / + +NSTACK + 25 / + +EQLDIMS +-- NTEQUL + 1 / + + +FMTOUT +FMTIN + +GRID ====== + +DXV +1.0 +/ + +DYV +1.0 +/ + +DZV +20*5.0 +/ + + +PORO +20*0.2 +/ + + +PERMZ + 20*1.0 +/ + +PERMY +20*100.0 +/ + +PERMX +20*100.0 +/ + +BOX + 1 1 1 1 1 1 / + +TOPS +0.0 +/ + +PROPS ====== PVTO -- Rs Pbub Bo Vo 0 1. 1.0000 1.20 / - 100 40. 1.0120 1.17 / - 200 80. 1.0255 1.14 / - 300 120. 1.0380 1.11 / - 400 160. 1.0510 1.08 / - 500 200. 1.0630 1.06 / - 600 240. 1.0750 1.03 / - 700 280. 1.0870 1.00 / - 800 320. 1.0985 .98 / - 900 360. 1.1100 .95 / - 1000 400. 1.1200 .94 + 20 40. 1.0120 1.17 / + 40 80. 1.0255 1.14 / + 60 120. 1.0380 1.11 / + 80 160. 1.0510 1.08 / + 100 200. 1.0630 1.06 / + 120 240. 1.0750 1.03 / + 140 280. 1.0870 1.00 / + 160 320. 1.0985 .98 / + 180 360. 1.1100 .95 / + 200 400. 1.1200 .94 500. 1.1189 .94 / / - PVDG 100 0.010 0.1 200 0.005 0.2 @@ -37,10 +104,37 @@ SGOF 0.8 1 0 0.5 / +PVTW +--RefPres Bw Comp Vw Cv + 1. 1.0 4.0E-5 0.96 0.0 / + + +ROCK +--RefPres Comp + 1. 5.0E-5 / + DENSITY 700 1000 1 / +SOLUTION ====== + EQUIL -45 150 50 0.25 45 0.35 +45 150 50 0.25 45 0.35 1* 1* 0 / + +RPTSOL +'PRES' 'PGAS' 'PWAT' 'SOIL' 'SWAT' 'SGAS' 'RS' 'RESTART=2' / + +SUMMARY ====== +RUNSUM + +SEPARATE + +SCHEDULE ====== + +RPTSCHED +'PRES' 'PGAS' 'PWAT' 'SOIL' 'SWAT' 'SGAS' 'RS' 'RESTART=3' 'NEWTON=2' / + + +END diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index b4df1fae..b1e6f6c0 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -27,6 +27,9 @@ #include #include +#include +#include + #include #include @@ -339,9 +342,9 @@ BOOST_AUTO_TEST_CASE (DeckAllDead) // solver, and it is unclear if we should check it against // the true answer or something else. const double reltol = 1.0e-3; - BOOST_CHECK_CLOSE(pressures[0][first] , 14955e3 , reltol); - BOOST_CHECK_CLOSE(pressures[0][last ] , 15045e3 , reltol); - BOOST_CHECK_CLOSE(pressures[1][last] , 1.50473e7 , reltol); + BOOST_CHECK_CLOSE(pressures[0][first] , 1.496329839e7 , reltol); + BOOST_CHECK_CLOSE(pressures[0][last ] , 1.50473245e7 , reltol); + BOOST_CHECK_CLOSE(pressures[1][last] , 1.50473245e7 , reltol); } @@ -416,9 +419,9 @@ BOOST_AUTO_TEST_CASE (DeckWithCapillary) // solver, and it is unclear if we should check it against // the true answer or something else. const double reltol = 1.0e-6; - BOOST_CHECK_CLOSE(pressures[0][first] , 1.45e7 , reltol); + BOOST_CHECK_CLOSE(pressures[0][first] , 1.469769063e7 , reltol); BOOST_CHECK_CLOSE(pressures[0][last ] , 1.545e7 , reltol); - BOOST_CHECK_CLOSE(pressures[1][last] , 1.5351621345e7 , reltol); + BOOST_CHECK_CLOSE(pressures[1][last] , 1.546e7 , reltol); const auto& sats = comp.saturation(); const std::vector s[3]{ @@ -443,7 +446,7 @@ BOOST_AUTO_TEST_CASE (DeckWithCapillaryOverlap) Opm::EclipseGridParser deck("capillary_overlap.DATA"); Opm::BlackoilPropertiesFromDeck props(deck, grid, false); - Opm::Equil::DeckDependent::InitialStateComputer comp(props, deck, grid, 10.0); + Opm::Equil::DeckDependent::InitialStateComputer comp(props, deck, grid, 9.80665); const auto& pressures = comp.press(); BOOST_REQUIRE(pressures.size() == 3); BOOST_REQUIRE(int(pressures[0].size()) == grid.number_of_cells); @@ -454,9 +457,15 @@ BOOST_AUTO_TEST_CASE (DeckWithCapillaryOverlap) // solver, and it is unclear if we should check it against // the true answer or something else. const double reltol = 1.0e-6; - BOOST_CHECK_CLOSE(pressures[0][first] , 1.45e7 , reltol); - BOOST_CHECK_CLOSE(pressures[0][last ] , 1.545e7 , reltol); - BOOST_CHECK_CLOSE(pressures[1][last] , 1.5351621345e7 , reltol); + const double reltol_ecl = 1.0; + BOOST_CHECK_CLOSE(pressures[0][first], 1.48324e+07, reltol_ecl); // eclipse + BOOST_CHECK_CLOSE(pressures[0][last], 1.54801e+07, reltol_ecl); + BOOST_CHECK_CLOSE(pressures[1][first], 1.49224e+07, reltol_ecl); + BOOST_CHECK_CLOSE(pressures[1][last], 1.54901e+07, reltol_ecl); + + BOOST_CHECK_CLOSE(pressures[0][first] , 14832467.14, reltol); // opm + BOOST_CHECK_CLOSE(pressures[0][last ] , 15479883.47, reltol); + BOOST_CHECK_CLOSE(pressures[1][last ] , 15489883.47, reltol); const auto& sats = comp.saturation(); // std::cout << "Saturations:\n"; @@ -466,15 +475,24 @@ BOOST_AUTO_TEST_CASE (DeckWithCapillaryOverlap) // } // std::cout << std::endl; // } - const std::vector s[3]{ - { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.223141818182, 0.532269090909, 0.78471, 0.91526, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.207743333333, 0.08474, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.776858181818, 0.467730909091, 0.0075466666666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + + const std::vector s_ecl[3]{// eclipse + { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.22874042, 0.53397995, 0.78454906, 0.91542006, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.20039, 0.08458, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.77125955, 0.46602005, 0.015063271, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + }; + + const std::vector s_opm[3]{ // opm + { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2289309090909091, 0.53406545454545451, 0.78458, 0.9154, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2002466666666666, 0.0846, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.77106909090909093, 0.46593454545454549, 0.015173333333333336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; for (int phase = 0; phase < 3; ++phase) { - BOOST_REQUIRE(sats[phase].size() == s[phase].size()); - for (size_t i = 0; i < s[phase].size(); ++i) { - BOOST_CHECK_CLOSE(sats[phase][i], s[phase][i], reltol); + BOOST_REQUIRE(sats[phase].size() == s_opm[phase].size()); + for (size_t i = 0; i < s_opm[phase].size(); ++i) { + //std::cout << std::setprecision(10) << sats[phase][i] << '\n'; + BOOST_CHECK_CLOSE(sats[phase][i], s_ecl[phase][i], reltol_ecl); + BOOST_CHECK_CLOSE(sats[phase][i], s_opm[phase][i], reltol); } } } @@ -485,10 +503,12 @@ BOOST_AUTO_TEST_CASE (DeckWithLiveOil) { Opm::GridManager gm(1, 1, 20, 1.0, 1.0, 5.0); const UnstructuredGrid& grid = *(gm.c_grid()); - Opm::EclipseGridParser deck("equil_liveoil.DATA"); + Opm::ParserPtr parser(new Opm::Parser() ); + Opm::DeckConstPtr deck = parser->parseFile("equil_liveoil.DATA"); + //Opm::EclipseGridParser deck("equil_liveoil.DATA"); Opm::BlackoilPropertiesFromDeck props(deck, grid, false); - Opm::Equil::DeckDependent::InitialStateComputer comp(props, deck, grid, 10.0); + Opm::Equil::DeckDependent::InitialStateComputer comp(props, deck, grid, 9.80665); const auto& pressures = comp.press(); BOOST_REQUIRE(pressures.size() == 3); BOOST_REQUIRE(int(pressures[0].size()) == grid.number_of_cells); @@ -499,9 +519,16 @@ BOOST_AUTO_TEST_CASE (DeckWithLiveOil) // solver, and it is unclear if we should check it against // the true answer or something else. const double reltol = 1.0e-6; - BOOST_CHECK_CLOSE(pressures[0][first], 1.4551302072306179e7, reltol); - BOOST_CHECK_CLOSE(pressures[0][last], 1.5501302072306179e7, reltol); - BOOST_CHECK_CLOSE(pressures[1][last], 1.5538684664272346e7, reltol); + const double reltol_ecl = 1.0; + BOOST_CHECK_CLOSE(pressures[0][first], 1.48324e+07, reltol_ecl); // eclipse + BOOST_CHECK_CLOSE(pressures[0][last], 1.54801e+07, reltol_ecl); + BOOST_CHECK_CLOSE(pressures[1][first], 1.49224e+07, reltol_ecl); + BOOST_CHECK_CLOSE(pressures[1][last], 1.54901e+07, reltol_ecl); + + BOOST_CHECK_CLOSE(pressures[0][first], 1.483246714e7, reltol); // opm + BOOST_CHECK_CLOSE(pressures[0][last], 1.547991652e7, reltol); + BOOST_CHECK_CLOSE(pressures[1][first], 1.492246714e7, reltol); + BOOST_CHECK_CLOSE(pressures[1][last], 1.548991652e7, reltol); const auto& sats = comp.saturation(); // std::cout << "Saturations:\n"; @@ -511,16 +538,24 @@ BOOST_AUTO_TEST_CASE (DeckWithLiveOil) // } // std::cout << std::endl; // } - const std::vector s[3]{ - { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.223141818182, 0.532269090909, 0.78471, 0.91526, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.207743333333, 0.08474, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.776858181818, 0.467730909091, 0.0075466666666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + const std::vector s_ecl[3]{ // eclipse + { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.22898, 0.53422, 0.78470, 0.91531, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.20073, 0.08469, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.77102, 0.46578, 0.01458, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; + + const std::vector s_opm[3]{ // opm + { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2291709091, 0.5343054545, 0.78472, 0.91529, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2005866667, 0.08471, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.7708290909, 0.4656945455, 0.01469333333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + }; + for (int phase = 0; phase < 3; ++phase) { - BOOST_REQUIRE(sats[phase].size() == s[phase].size()); - for (size_t i = 0; i < s[phase].size(); ++i) { - std::cout << sats[phase][i] << '\n'; - //BOOST_CHECK_CLOSE(sats[phase][i], s[phase][i], reltol); + BOOST_REQUIRE(sats[phase].size() == s_opm[phase].size()); + for (size_t i = 0; i < s_opm[phase].size(); ++i) { + //std::cout << std::setprecision(10) << sats[phase][i] << '\n'; + BOOST_CHECK_CLOSE(sats[phase][i], s_opm[phase][i], reltol); + BOOST_CHECK_CLOSE(sats[phase][i], s_ecl[phase][i], reltol_ecl); } std::cout << std::endl; } From 7d8996acbb24c8f7dd5b7549b69ab6e543b94c46 Mon Sep 17 00:00:00 2001 From: osae Date: Mon, 31 Mar 2014 16:16:45 +0200 Subject: [PATCH 61/81] Equil regions now internally indexed 0..(NTEQUL-1) --- opm/core/simulator/initStateEquil.hpp | 34 ++++++++++++++++----------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/opm/core/simulator/initStateEquil.hpp b/opm/core/simulator/initStateEquil.hpp index c4608b80..c1f72b71 100644 --- a/opm/core/simulator/initStateEquil.hpp +++ b/opm/core/simulator/initStateEquil.hpp @@ -289,13 +289,16 @@ namespace Opm const UnstructuredGrid& G ) { std::vector eqlnum; - if (deck.hasField("EQLNUM")) { - eqlnum = deck.getIntegerValue("EQLNUM"); + if (deck.hasField("EQLNUM")) { + const std::vector& e = deck.getIntegerValue("EQLNUM"); + eqlnum.reserve(e.size()); + std::transform(e.begin(), e.end(), std::back_inserter(eqlnum), + std::bind2nd(std::minus(), 1)); } else { // No explicit equilibration region. - // All cells in region one. - eqlnum.assign(G.number_of_cells, 1); + // All cells in region zero. + eqlnum.assign(G.number_of_cells, 0); } return eqlnum; @@ -308,13 +311,16 @@ namespace Opm const UnstructuredGrid& G ) { std::vector eqlnum; - if (newParserDeck->hasKeyword("EQLNUM")) { - eqlnum = newParserDeck->getKeyword("EQLNUM")->getIntData(); + if (newParserDeck->hasKeyword("EQLNUM")) { + const std::vector& e = newParserDeck->getKeyword("EQLNUM")->getIntData(); + eqlnum.reserve(e.size()); + std::transform(e.begin(), e.end(), std::back_inserter(eqlnum), + std::bind2nd(std::minus(), 1)); } else { // No explicit equilibration region. - // All cells in region one. - eqlnum.assign(G.number_of_cells, 1); + // All cells in region zero. + eqlnum.assign(G.number_of_cells, 0); } return eqlnum; @@ -348,7 +354,7 @@ namespace Opm rs_func_.reserve(rec.size()); if (deck.hasField("DISGAS")) { for (size_t i = 0; i < rec.size(); ++i) { - const int cell = *(eqlmap.cells(i + 1).begin()); + const int cell = *(eqlmap.cells(i).begin()); if (rec[i].live_oil_table_index > 0) { if (deck.hasField("RSVD")) { // TODO When this kw is actually parsed, also check for proper number of available tables @@ -379,7 +385,7 @@ namespace Opm rv_func_.reserve(rec.size()); if (deck.hasField("VAPOIL")) { for (size_t i = 0; i < rec.size(); ++i) { - const int cell = *(eqlmap.cells(i + 1).begin()); + const int cell = *(eqlmap.cells(i).begin()); if (rec[i].wet_gas_table_index > 0) { if (deck.hasField("RVVD")) { // TODO When this kw is actually parsed, also check for proper number of available tables @@ -448,7 +454,7 @@ namespace Opm r = 0, nr = reg.numRegions(); r < nr; ++r) { - const typename RMap::CellRange cells = reg.cells(r+1); + const typename RMap::CellRange cells = reg.cells(r); const int repcell = *cells.begin(); const RhoCalc calc(props, repcell); @@ -517,7 +523,7 @@ namespace Opm rs_func_.reserve(rec.size()); if (newParserDeck->hasKeyword("DISGAS")) { for (size_t i = 0; i < rec.size(); ++i) { - const int cell = *(eqlmap.cells(i + 1).begin()); + const int cell = *(eqlmap.cells(i).begin()); if (rec[i].live_oil_table_index > 0) { if (newParserDeck->hasKeyword("RSVD") && rec[i].live_oil_table_index <= newParserDeck->getKeyword("RSVD")->size()) { Opm::SimpleTable rsvd(newParserDeck->getKeyword("RSVD"),std::vector{"vd", "rs"},rec[i].live_oil_table_index-1); @@ -547,7 +553,7 @@ namespace Opm rv_func_.reserve(rec.size()); if (newParserDeck->hasKeyword("VAPOIL")) { for (size_t i = 0; i < rec.size(); ++i) { - const int cell = *(eqlmap.cells(i + 1).begin()); + const int cell = *(eqlmap.cells(i).begin()); if (rec[i].wet_gas_table_index > 0) { if (newParserDeck->hasKeyword("RVVD") && rec[i].wet_gas_table_index <= newParserDeck->getKeyword("RVVD")->size()) { Opm::SimpleTable rvvd(newParserDeck->getKeyword("RVVD"),std::vector{"vd", "rv"},rec[i].wet_gas_table_index-1); @@ -615,7 +621,7 @@ namespace Opm r = 0, nr = reg.numRegions(); r < nr; ++r) { - const typename RMap::CellRange cells = reg.cells(r+1); + const typename RMap::CellRange cells = reg.cells(r); const int repcell = *cells.begin(); const RhoCalc calc(props, repcell); From 5371e19d1924effc530d6f208c65d1a31937f8d0 Mon Sep 17 00:00:00 2001 From: Roland Kaufmann Date: Tue, 1 Apr 2014 14:03:09 +0200 Subject: [PATCH 62/81] Use new parser in common output interface Since EclipseWriter can now take a Deck instead of an EclipseGridParser the interface can be changed to take this type instead. --- opm/core/io/OutputWriter.cpp | 6 +++--- opm/core/io/OutputWriter.hpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/opm/core/io/OutputWriter.cpp b/opm/core/io/OutputWriter.cpp index 5645f420..4b66e2c4 100644 --- a/opm/core/io/OutputWriter.cpp +++ b/opm/core/io/OutputWriter.cpp @@ -48,7 +48,7 @@ private: /// Psuedo-constructor, can appear in template template unique_ptr create (const ParameterGroup& params, - std::shared_ptr parser, + std::shared_ptr parser, std::shared_ptr grid) { return unique_ptr (new Format (params, parser, grid)); } @@ -61,7 +61,7 @@ create (const ParameterGroup& params, /// to the list below! typedef map (*)( const ParameterGroup&, - std::shared_ptr , + std::shared_ptr , std::shared_ptr )> map_t; map_t FORMATS = { { "output_ecl", &create }, @@ -71,7 +71,7 @@ map_t FORMATS = { unique_ptr OutputWriter::create (const ParameterGroup& params, - std::shared_ptr parser, + std::shared_ptr parser, std::shared_ptr grid) { // allocate a list which will be filled with writers. this list // is initially empty (no output). diff --git a/opm/core/io/OutputWriter.hpp b/opm/core/io/OutputWriter.hpp index ea19ea01..46bca77a 100644 --- a/opm/core/io/OutputWriter.hpp +++ b/opm/core/io/OutputWriter.hpp @@ -27,7 +27,7 @@ struct UnstructuredGrid; namespace Opm { // forward declaration -class EclipseGridParser; +class Deck; namespace parameter { class ParameterGroup; } class SimulatorState; class SimulatorTimer; @@ -43,7 +43,7 @@ class WellState; * \example * \code{.cpp} * ParameterGroup params (argc, argv, false); - * auto parser = std::make_shared ( + * auto parser = std::make_shared ( * params.get ("deck_filename")); * * std::unique_ptr writer = @@ -108,7 +108,7 @@ public: */ static std::unique_ptr create (const parameter::ParameterGroup& params, - std::shared_ptr parser, + std::shared_ptr parser, std::shared_ptr grid); }; From a4c168e58ee8550cee290a08afc987bae7abeeec Mon Sep 17 00:00:00 2001 From: Roland Kaufmann Date: Tue, 1 Apr 2014 14:32:30 +0200 Subject: [PATCH 63/81] Use new parser for reading time map and generating output --- opm/core/simulator/SimulatorOutput.cpp | 16 +++++++++++----- opm/core/simulator/SimulatorOutput.hpp | 10 +++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/opm/core/simulator/SimulatorOutput.cpp b/opm/core/simulator/SimulatorOutput.cpp index 2fade1fd..a5d7be95 100644 --- a/opm/core/simulator/SimulatorOutput.cpp +++ b/opm/core/simulator/SimulatorOutput.cpp @@ -20,7 +20,8 @@ #include "SimulatorOutput.hpp" // we need complete definitions for these types -#include +#include +#include #include #include @@ -30,7 +31,7 @@ using namespace Opm; SimulatorOutputBase::SimulatorOutputBase ( const parameter::ParameterGroup& params, - std::shared_ptr parser, + std::shared_ptr parser, std::shared_ptr grid, std::shared_ptr timer, std::shared_ptr state, @@ -53,9 +54,14 @@ SimulatorOutputBase::SimulatorOutputBase ( // timesteps, we make a list of accumulated such to compare with // current time. add an extra zero at the beginning so that the // initial state is also written - const std::vector & tstep = parser->getTSTEP ().tstep_; - times_.resize (tstep.size (), 0.); - std::partial_sum (tstep.begin(), tstep.end(), times_.begin()); + TimeMap tmap(parser); + times_.resize (tmap.size () + 1, 0.); + double sum = 0.; + times_[0] = sum; + for (size_t step = 0; step < tmap.numTimesteps(); ++step) { + sum += tmap.getTimePassedUntil (step); + times_[step + 1] = sum; + } // write the static initialization files, even before simulation starts writer_->writeInit (*timer, *state, *wellState); diff --git a/opm/core/simulator/SimulatorOutput.hpp b/opm/core/simulator/SimulatorOutput.hpp index 22dffeeb..ff44c893 100644 --- a/opm/core/simulator/SimulatorOutput.hpp +++ b/opm/core/simulator/SimulatorOutput.hpp @@ -32,7 +32,7 @@ struct UnstructuredGrid; namespace Opm { // forward definitions -class EclipseGridParser; +class Deck; class OutputWriter; namespace parameter { class ParameterGroup; } class SimulatorState; @@ -53,7 +53,7 @@ protected: * need to pick them up from the object members. */ SimulatorOutputBase (const parameter::ParameterGroup& p, - std::shared_ptr parser, + std::shared_ptr parser, std::shared_ptr grid, std::shared_ptr timer, std::shared_ptr state, @@ -110,7 +110,7 @@ private: * ParameterGroup params (argc, argv, false); * * // input file - * auto deck = make_shared ( ... ); + * auto deck = make_shared ( ... ); * const GridManager manager (*parser); * auto grid = share_obj (*manager.c_grid ()); * @@ -139,7 +139,7 @@ private: template struct SimulatorOutput : public SimulatorOutputBase { SimulatorOutput (const parameter::ParameterGroup& params, - std::shared_ptr parser, + std::shared_ptr parser, std::shared_ptr grid, std::shared_ptr timer, std::shared_ptr state, @@ -161,7 +161,7 @@ struct SimulatorOutput : public SimulatorOutputBase { * the arguments passed exceeds the lifetime of this object. */ SimulatorOutput (const parameter::ParameterGroup& params, - EclipseGridParser& parser, + const Deck& parser, const UnstructuredGrid& grid, const SimulatorTimer& timer, const SimulatorState& state, From acf9f96e4616c2550061b0002164c33d8d402b70 Mon Sep 17 00:00:00 2001 From: Roland Kaufmann Date: Tue, 1 Apr 2014 20:27:53 +0200 Subject: [PATCH 64/81] Use TimeMap directly instead of copying times The simulator object will probably have created a time map anyway to control the timestepping so this can be reused directly by the output writer. (If not, it is easy to create one and pass it). --- opm/core/simulator/SimulatorOutput.cpp | 24 ++++++++---------------- opm/core/simulator/SimulatorOutput.hpp | 12 ++++++++++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/opm/core/simulator/SimulatorOutput.cpp b/opm/core/simulator/SimulatorOutput.cpp index a5d7be95..67092c3f 100644 --- a/opm/core/simulator/SimulatorOutput.cpp +++ b/opm/core/simulator/SimulatorOutput.cpp @@ -32,6 +32,7 @@ using namespace Opm; SimulatorOutputBase::SimulatorOutputBase ( const parameter::ParameterGroup& params, std::shared_ptr parser, + std::shared_ptr timeMap, std::shared_ptr grid, std::shared_ptr timer, std::shared_ptr state, @@ -40,6 +41,7 @@ SimulatorOutputBase::SimulatorOutputBase ( // store all parameters passed into the object, making them curried // parameters to the writeOutput function. : timer_ (timer ) + , timeMap_ (timeMap ) , reservoirState_ (state ) , wellState_ (wellState) @@ -50,19 +52,6 @@ SimulatorOutputBase::SimulatorOutputBase ( // always start from the first timestep , next_ (0) { - // make a list of times to dump. since the original list are relative - // timesteps, we make a list of accumulated such to compare with - // current time. add an extra zero at the beginning so that the - // initial state is also written - TimeMap tmap(parser); - times_.resize (tmap.size () + 1, 0.); - double sum = 0.; - times_[0] = sum; - for (size_t step = 0; step < tmap.numTimesteps(); ++step) { - sum += tmap.getTimePassedUntil (step); - times_[step + 1] = sum; - } - // write the static initialization files, even before simulation starts writer_->writeInit (*timer, *state, *wellState); } @@ -82,15 +71,18 @@ SimulatorOutputBase::writeOutput () { // if the simulator signals for timesteps that aren't reporting // times, then ignore them - if (next_ < times_.size () && times_[next_] <= this_time) { + if (next_ < timeMap_->size () + && timeMap_->getTimePassedUntil (next_) <= this_time) { // uh-oh, the simulator has skipped reporting timesteps that // occurred before this timestep (it doesn't honor the TSTEP setting) - while (next_ < times_.size () && times_[next_] < this_time) { + while (next_ < timeMap_->size () + && timeMap_->getTimePassedUntil (next_) < this_time) { ++next_; } // report this timestep if it matches - if (next_ < times_.size () && times_[next_] == this_time) { + if (next_ < timeMap_->size () + && timeMap_->getTimePassedUntil (next_) == this_time) { // make sure the simulator has spilled all necessary internal // state. notice that this calls *our* sync, which is overridden // in the template companion to call the simulator diff --git a/opm/core/simulator/SimulatorOutput.hpp b/opm/core/simulator/SimulatorOutput.hpp index ff44c893..b276ac57 100644 --- a/opm/core/simulator/SimulatorOutput.hpp +++ b/opm/core/simulator/SimulatorOutput.hpp @@ -37,6 +37,7 @@ class OutputWriter; namespace parameter { class ParameterGroup; } class SimulatorState; class SimulatorTimer; +class TimeMap; class WellState; /** @@ -54,6 +55,7 @@ protected: */ SimulatorOutputBase (const parameter::ParameterGroup& p, std::shared_ptr parser, + std::shared_ptr timeMap, std::shared_ptr grid, std::shared_ptr timer, std::shared_ptr state, @@ -75,6 +77,7 @@ protected: /// Just hold a reference to these objects that are owned elsewhere. std::shared_ptr timer_; + std::shared_ptr timeMap_; std::shared_ptr reservoirState_; std::shared_ptr wellState_; @@ -122,11 +125,12 @@ private: * auto wellState = make_shared (); * * // set up simulation + * auto timeMap = make_shared (deck); * auto sim = make_shared (params, *grid, ... ); * * // use this to dump state to disk * auto output = make_shared ( - * params, deck, grid, timer, state, wellState, sim); + * params, deck, timeMap, grid, timer, state, wellState, sim); * * // start simulation * sim.run (timer, state, ... ) @@ -140,13 +144,15 @@ template struct SimulatorOutput : public SimulatorOutputBase { SimulatorOutput (const parameter::ParameterGroup& params, std::shared_ptr parser, + std::shared_ptr timeMap, std::shared_ptr grid, std::shared_ptr timer, std::shared_ptr state, std::shared_ptr wellState, std::shared_ptr sim) // send all other parameters to base class - : SimulatorOutputBase (params, parser, grid, timer, state, wellState) + : SimulatorOutputBase (params, parser, timeMap, + grid, timer, state, wellState) // store reference to simulator in derived class , sim_ (sim) { @@ -162,6 +168,7 @@ struct SimulatorOutput : public SimulatorOutputBase { */ SimulatorOutput (const parameter::ParameterGroup& params, const Deck& parser, + const TimeMap& timeMap, const UnstructuredGrid& grid, const SimulatorTimer& timer, const SimulatorState& state, @@ -170,6 +177,7 @@ struct SimulatorOutput : public SimulatorOutputBase { // send all other parameters to base class : SimulatorOutputBase (params, share_obj (parser), + share_obj (timeMap), share_obj (grid), share_obj (timer), share_obj (state), From a02f315acef9b45b53f27d03efb00d7d37851a7d Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Mon, 31 Mar 2014 17:38:25 +0200 Subject: [PATCH 65/81] EclipseWriter: remove old parser this patch gets rid of the old-parser-taking methods of EclipseWriter. this avoids quite a few consistency issues and also reduces the amount of duplicate work required. --- opm/core/io/eclipse/EclipseWriter.cpp | 545 +++----------------------- opm/core/io/eclipse/EclipseWriter.hpp | 10 - 2 files changed, 62 insertions(+), 493 deletions(-) diff --git a/opm/core/io/eclipse/EclipseWriter.cpp b/opm/core/io/eclipse/EclipseWriter.cpp index 789503e7..241ab953 100644 --- a/opm/core/io/eclipse/EclipseWriter.cpp +++ b/opm/core/io/eclipse/EclipseWriter.cpp @@ -22,7 +22,6 @@ #include "EclipseWriter.hpp" -#include #include #include #include @@ -296,10 +295,6 @@ struct EclipseKeyword : public EclipseHandle { static_cast (name); } - // constructor to keep compatibility with the old eclipse parser - EclipseKeyword (const std::string& name, - const EclipseGridParser& parser); - // GCC 4.4 doesn't generate these constructors for us; provide the // default implementation explicitly here instead EclipseKeyword (EclipseKeyword&& rhs) @@ -389,24 +384,6 @@ template <> ecl_type_enum EclipseKeyword::type () { return ECL_INT_TYPE template <> ecl_type_enum EclipseKeyword::type () { return ECL_FLOAT_TYPE ; } template <> ecl_type_enum EclipseKeyword::type () { return ECL_DOUBLE_TYPE; } -/// keywords in ERT requires single-precision type, but OPM have them -/// stored as double-precision. this template specialization instantiates -/// a copy function that downcast the data to the required type. -template <> -EclipseKeyword ::EclipseKeyword ( - const std::string& name, - const EclipseGridParser& parser) - // allocate handle and put in smart pointer base class - : EclipseHandle ( - ecl_kw_alloc (name.c_str(), - // we can safely use the *size* of the original - dataSize (parser.getValue (name), 0, 1), - type ()), - ecl_kw_free) { - const std::vector & data = parser.getValue (name); - copyData (data, &noConversion, 0, 1); -} - /** * Pointer to memory that holds the name to an Eclipse output file. */ @@ -432,29 +409,6 @@ private: } }; -/// Get dimensions of the grid from the parse of the input file -std::vector parserDim (const EclipseGridParser& parser) { - std::vector dim(/* n = */ 3); - // dimensions explicitly given - if (parser.hasField("SPECGRID")) { - dim = parser.getSPECGRID ().dimensions; - } - // dimensions implicitly given by number of deltas - else if (parser.hasField("DXV")) { - assert(parser.hasField("DYV")); - assert(parser.hasField("DZV")); - dim[0] = parser.getFloatingPointValue("DXV").size(); - dim[1] = parser.getFloatingPointValue("DYV").size(); - dim[2] = parser.getFloatingPointValue("DZV").size(); - } - else { - OPM_THROW(std::runtime_error, - "Only decks featureing either the SPECGRID or the D[XYZ]V keywords " - "are currently supported"); - } - return dim; -} - /// Get dimensions of the grid from the parse of the input file std::vector parserDim (Opm::DeckConstPtr newParserDeck) { std::vector dim(/* n = */ 3); @@ -501,24 +455,6 @@ struct EclipseRestart : public EclipseHandle { outputStepIdx)), ecl_rst_file_close) { } - void writeHeader (const SimulatorTimer& timer, - int outputStepIdx, - const PhaseUsage uses, - const EclipseGridParser parser, - const int num_active_cells) { - const std::vector dim = parserDim (parser); - ecl_rst_file_fwrite_header (*this, - outputStepIdx, - timer.currentPosixTime(), - Opm::unit::convert::to (timer.simulationTimeElapsed (), - Opm::unit::day), - dim[0], - dim[1], - dim[2], - num_active_cells, - phaseMask (uses)); - } - void writeHeader (const SimulatorTimer& timer, int outputStepIdx, const PhaseUsage uses, @@ -565,49 +501,6 @@ private: * Representation of an Eclipse grid. */ struct EclipseGrid : public EclipseHandle { - /// Create a grid based on the keywords available in input file - static EclipseGrid make (const EclipseGridParser& parser, - const UnstructuredGrid& grid) { - if (parser.hasField("DXV")) { - // make sure that the DYV and DZV keywords are present if the - // DXV keyword is used in the deck... - assert(parser.hasField("DYV")); - assert(parser.hasField("DZV")); - - const auto& dxv = parser.getFloatingPointValue("DXV"); - const auto& dyv = parser.getFloatingPointValue("DYV"); - const auto& dzv = parser.getFloatingPointValue("DZV"); - - return EclipseGrid (dxv, dyv, dzv); - } - else if (parser.hasField("ZCORN")) { - struct grdecl g = parser.get_grdecl (); - - auto coordData = parser.getFloatingPointValue(COORD_KW); - auto zcornData = parser.getFloatingPointValue(ZCORN_KW); - - EclipseKeyword coord_kw (COORD_KW, coordData); - EclipseKeyword zcorn_kw (ZCORN_KW, zcornData); - - // get the actually active cells, after processing - std::vector actnum; - getActiveCells_(grid, actnum); - EclipseKeyword actnum_kw (ACTNUM_KW, actnum); - - EclipseKeyword mapaxes_kw (MAPAXES_KW); - if (g.mapaxes) { - auto mapaxesData = parser.getFloatingPointValue(MAPAXES_KW); - mapaxes_kw = std::move (EclipseKeyword (MAPAXES_KW, mapaxesData)); - } - - return EclipseGrid (g.dims, zcorn_kw, coord_kw, actnum_kw, mapaxes_kw); - } - else { - OPM_THROW(std::runtime_error, - "Can't create an ERT grid (no supported keywords found in deck)"); - } - } - /// Create a grid based on the keywords available in input file static EclipseGrid make (Opm::DeckConstPtr newParserDeck, const UnstructuredGrid& grid) @@ -736,18 +629,6 @@ struct EclipseInit : public EclipseHandle { return EclipseInit (initFileName, fmt_file); } - void writeHeader (const EclipseGrid& grid, - const SimulatorTimer& timer, - const EclipseGridParser& parser, - const PhaseUsage uses) { - EclipseKeyword poro (PORO_KW, parser); - ecl_init_file_fwrite_header (*this, - grid, - poro, - phaseMask (uses), - timer.currentPosixTime()); - } - void writeHeader (const UnstructuredGrid& grid, const SimulatorTimer& timer, Opm::DeckConstPtr newParserDeck, @@ -766,15 +647,6 @@ struct EclipseInit : public EclipseHandle { timer.currentPosixTime ()); } - void writeKeyword (const std::string& keywordName, - const EclipseGridParser& parser, - double (* const transf)(const double&)) { - auto data = parser.getValue (keywordName); - convertUnit_(data, transf); - EclipseKeyword kw (keywordName, data); - ecl_kw_fwrite (kw, *this); - } - void writeKeyword (const std::string& keywordName, const std::vector &data) { EclipseKeyword kw (keywordName, data); @@ -814,14 +686,6 @@ template struct EclipseHandle; struct EclipseWellReport; struct EclipseSummary : public EclipseHandle { - EclipseSummary (const std::string& outputDir, - const std::string& baseName, - const SimulatorTimer& timer, - const EclipseGridParser parser) - : EclipseHandle ( - alloc_writer (outputDir, baseName, timer, parser), - ecl_sum_free) { } - EclipseSummary (const std::string& outputDir, const std::string& baseName, const SimulatorTimer& timer, @@ -845,10 +709,6 @@ struct EclipseSummary : public EclipseHandle { ecl_sum_fwrite (*this); } - // add rate variables for each of the well in the input file - void addWells (const EclipseGridParser& parser, - const PhaseUsage& uses); - // add rate variables for each of the well in the input file void addWells (Opm::DeckConstPtr newParserDeck, const PhaseUsage& uses); @@ -879,27 +739,6 @@ private: return std::unique_ptr (tstep); } - /// Helper routine that lets us use local variables to hold - /// intermediate results while filling out the allocations function's - /// argument list. - static ecl_sum_type* alloc_writer (const std::string& outputDir, - const std::string& baseName, - const SimulatorTimer& timer, - const EclipseGridParser& parser) { - boost::filesystem::path casePath (outputDir); - casePath /= boost::to_upper_copy (baseName); - - const std::vector dim = parserDim (parser); - return ecl_sum_alloc_writer (casePath.string ().c_str (), - false, /* formatted */ - true, /* unified */ - ":", /* join string */ - timer.simulationTimeElapsed (), - dim[0], - dim[1], - dim[2]); - } - /// Helper routine that lets us use local variables to hold /// intermediate results while filling out the allocations function's /// argument list. @@ -928,29 +767,6 @@ private: */ struct EclipseWellReport : public EclipseHandle { protected: - EclipseWellReport (const EclipseSummary& summary, /* section to add to */ - const EclipseGridParser& parser, /* well names */ - int whichWell, /* index of well line */ - PhaseUsage uses, /* phases present */ - BlackoilPhases::PhaseIndex phase, /* oil, water or gas */ - WellType type, /* prod. or inj. */ - char aggregation, /* rate or total or BHP */ - std::string unit) - : EclipseHandle ( - ecl_sum_add_var (summary, - varName (phase, - type, - aggregation).c_str (), - wellName (parser, whichWell).c_str (), - /* num = */ 0, - unit.c_str(), - /* defaultValue = */ 0.)) - // save these for when we update the value in a timestep - , index_ (whichWell * uses.num_phases + uses.phase_pos [phase]) - - // producers can be seen as negative injectors - , sign_ (type == INJECTOR ? +1. : -1.) { } - EclipseWellReport (const EclipseSummary& summary, /* section to add to */ Opm::DeckConstPtr newParserDeck, /* well names */ int whichWell, /* index of well line */ @@ -992,12 +808,6 @@ private: /// natural sign of the rate const double sign_; - /// Get the name associated with this well - std::string wellName (const EclipseGridParser& parser, - int whichWell) { - return parser.getWELSPECS().welspecs[whichWell].name_; - } - /// Get the name associated with this well std::string wellName (Opm::DeckConstPtr newParserDeck, int whichWell) @@ -1053,21 +863,6 @@ protected: /// Monitors the rate given by a well. struct EclipseWellRate : public EclipseWellReport { - EclipseWellRate (const EclipseSummary& summary, - const EclipseGridParser& parser, - int whichWell, - PhaseUsage uses, - BlackoilPhases::PhaseIndex phase, - WellType type) - : EclipseWellReport (summary, - parser, - whichWell, - uses, - phase, - type, - 'R', - "SM3/DAY" /* surf. cub. m. per day */ ) { } - EclipseWellRate (const EclipseSummary& summary, Opm::DeckConstPtr newParserDeck, int whichWell, @@ -1092,24 +887,6 @@ struct EclipseWellRate : public EclipseWellReport { /// Monitors the total production in a well. struct EclipseWellTotal : public EclipseWellReport { - EclipseWellTotal (const EclipseSummary& summary, - const EclipseGridParser& parser, - int whichWell, - PhaseUsage uses, - BlackoilPhases::PhaseIndex phase, - WellType type) - : EclipseWellReport (summary, - parser, - whichWell, - uses, - phase, - type, - 'T', - "SM3" /* surface cubic meter */ ) - - // nothing produced when the reporting starts - , total_ (0.) { } - EclipseWellTotal (const EclipseSummary& summary, Opm::DeckConstPtr newParserDeck, int whichWell, @@ -1152,22 +929,6 @@ private: /// Monitors the bottom hole pressure in a well. struct EclipseWellBhp : public EclipseWellReport { - EclipseWellBhp (const EclipseSummary& summary, - const EclipseGridParser& parser, - int whichWell, - PhaseUsage uses, - BlackoilPhases::PhaseIndex phase, - WellType type) - : EclipseWellReport (summary, - parser, - whichWell, - uses, - phase, - type, - 'B', - "Pascal") - { } - EclipseWellBhp (const EclipseSummary& summary, Opm::DeckConstPtr newParserDeck, int whichWell, @@ -1208,69 +969,6 @@ EclipseSummary::writeTimeStep (const SimulatorTimer& timer, /// so we must have an explicit array. static WellType WELL_TYPES[] = { INJECTOR, PRODUCER }; -inline void -EclipseSummary::addWells (const EclipseGridParser& parser, - const PhaseUsage& uses) -{ - // TODO: Only create report variables that are requested with keywords - // (e.g. "WOPR") in the input files, and only for those wells that are - // mentioned in those keywords - const int numWells = parser.getWELSPECS().welspecs.size(); - for (int phaseCounter = 0; - phaseCounter != BlackoilPhases::MaxNumPhases; - ++phaseCounter) { - const BlackoilPhases::PhaseIndex phase = - static_cast (phaseCounter); - // don't bother with reporting for phases that aren't there - if (!uses.phase_used [phaseCounter]) { - continue; - } - for (size_t typeIndex = 0; - typeIndex < sizeof (WELL_TYPES) / sizeof (WELL_TYPES[0]); - ++typeIndex) { - const WellType type = WELL_TYPES[typeIndex]; - for (int whichWell = 0; whichWell != numWells; ++whichWell) { - // W{O,G,W}{I,P}R - add (std::unique_ptr ( - new EclipseWellRate (*this, - parser, - whichWell, - uses, - phase, - type))); - // W{O,G,W}{I,P}T - add (std::unique_ptr ( - new EclipseWellTotal (*this, - parser, - whichWell, - uses, - phase, - type))); - } - } - } - - // Add BHP monitors - for (int whichWell = 0; whichWell != numWells; ++whichWell) { - // In the call below: uses, phase and the well type arguments - // are not used, except to set up an index that stores the - // well indirectly. For details see the implementation of the - // EclipseWellReport constructor, and the method - // EclipseWellReport::bhp(). - BlackoilPhases::PhaseIndex phase = BlackoilPhases::Liquid; - if (!uses.phase_used[BlackoilPhases::Liquid]) { - phase = BlackoilPhases::Vapour; - } - add (std::unique_ptr ( - new EclipseWellBhp (*this, - parser, - whichWell, - uses, - phase, - WELL_TYPES[0]))); - } -} - inline void EclipseSummary::addWells (Opm::DeckConstPtr newParserDeck, const PhaseUsage& uses) { @@ -1346,70 +1044,45 @@ void EclipseWriter::writeInit(const SimulatorTimer &timer, if (!enableOutput_) { return; } - if (newParserDeck_) { - /* Grid files */ - EclipseGrid eclGrid = EclipseGrid::make (newParserDeck_, *grid_); - eclGrid.write (outputDir_, baseName_, /*stepIdx=*/0); + /* Grid files */ + EclipseGrid eclGrid = EclipseGrid::make (newParserDeck_, *grid_); + eclGrid.write (outputDir_, baseName_, /*stepIdx=*/0); - EclipseInit fortio = EclipseInit::make (outputDir_, baseName_, /*stepIdx=*/0); - fortio.writeHeader (*grid_, - timer, - newParserDeck_, - uses_); + EclipseInit fortio = EclipseInit::make (outputDir_, baseName_, /*stepIdx=*/0); + fortio.writeHeader (*grid_, + timer, + newParserDeck_, + uses_); - if (newParserDeck_->hasKeyword("PERM")) { - auto data = getAllSiDoubles_(newParserDeck_->getKeyword("PERM")); - convertUnit_(data, toMilliDarcy); - fortio.writeKeyword ("PERM", data); - } - - if (newParserDeck_->hasKeyword("PERMX")) { - auto data = getAllSiDoubles_(newParserDeck_->getKeyword("PERMX")); - convertUnit_(data, toMilliDarcy); - fortio.writeKeyword ("PERMX", data); - } - if (newParserDeck_->hasKeyword("PERMY")) { - auto data = getAllSiDoubles_(newParserDeck_->getKeyword("PERMY")); - convertUnit_(data, toMilliDarcy); - fortio.writeKeyword ("PERMY", data); - } - if (newParserDeck_->hasKeyword("PERMZ")) { - auto data = getAllSiDoubles_(newParserDeck_->getKeyword("PERMZ")); - convertUnit_(data, toMilliDarcy); - fortio.writeKeyword ("PERMZ", data); - } - - /* Initial solution (pressure and saturation) */ - writeSolution_(timer, reservoirState); - - /* Create summary object (could not do it at construction time, - since it requires knowledge of the start time). */ - summary_.reset(new EclipseSummary(outputDir_, baseName_, timer, newParserDeck_)); - summary_->addWells (newParserDeck_, uses_); + if (newParserDeck_->hasKeyword("PERM")) { + auto data = getAllSiDoubles_(newParserDeck_->getKeyword("PERM")); + convertUnit_(data, toMilliDarcy); + fortio.writeKeyword ("PERM", data); } - else { - /* Grid files */ - EclipseGrid ecl_grid = EclipseGrid::make (*parser_, *grid_); - ecl_grid.write (outputDir_, baseName_, /*stepIdx=*/0); - EclipseInit fortio = EclipseInit::make (outputDir_, baseName_, /*stepIdx=*/0); - fortio.writeHeader (ecl_grid, - timer, - *parser_, - uses_); - - fortio.writeKeyword ("PERMX", *parser_, &toMilliDarcy); - fortio.writeKeyword ("PERMY", *parser_, &toMilliDarcy); - fortio.writeKeyword ("PERMZ", *parser_, &toMilliDarcy); - - /* Initial solution (pressure and saturation) */ - writeSolution_(timer, reservoirState); - - /* Create summary object (could not do it at construction time, - since it requires knowledge of the start time). */ - summary_.reset(new EclipseSummary(outputDir_, baseName_, timer, *parser_)); - summary_->addWells (*parser_, uses_); + if (newParserDeck_->hasKeyword("PERMX")) { + auto data = getAllSiDoubles_(newParserDeck_->getKeyword("PERMX")); + convertUnit_(data, toMilliDarcy); + fortio.writeKeyword ("PERMX", data); } + if (newParserDeck_->hasKeyword("PERMY")) { + auto data = getAllSiDoubles_(newParserDeck_->getKeyword("PERMY")); + convertUnit_(data, toMilliDarcy); + fortio.writeKeyword ("PERMY", data); + } + if (newParserDeck_->hasKeyword("PERMZ")) { + auto data = getAllSiDoubles_(newParserDeck_->getKeyword("PERMZ")); + convertUnit_(data, toMilliDarcy); + fortio.writeKeyword ("PERMZ", data); + } + + /* Initial solution (pressure and saturation) */ + writeSolution_(timer, reservoirState); + + /* Create summary object (could not do it at construction time, + since it requires knowledge of the start time). */ + summary_.reset(new EclipseSummary(outputDir_, baseName_, timer, newParserDeck_)); + summary_->addWells (newParserDeck_, uses_); } void EclipseWriter::activeToGlobalCellData_(std::vector &globalCellsBuf, @@ -1431,69 +1104,32 @@ void EclipseWriter::activeToGlobalCellData_(std::vector &globalCellsBuf, void EclipseWriter::writeSolution_(const SimulatorTimer& timer, const SimulatorState& reservoirState) { - if (newParserDeck_) { - // start writing to files - EclipseRestart rst(outputDir_, baseName_, timer, outputTimeStepIdx_); - rst.writeHeader (timer, outputTimeStepIdx_, uses_, newParserDeck_, reservoirState.pressure().size ()); - EclipseSolution sol (rst); + // start writing to files + EclipseRestart rst(outputDir_, baseName_, timer, outputTimeStepIdx_); + rst.writeHeader (timer, outputTimeStepIdx_, uses_, newParserDeck_, reservoirState.pressure().size ()); + EclipseSolution sol (rst); - // write out the pressure of the reference phase (whatever - // phase that is...). this is not the most performant solution - // thinkable, but this is also not in the most performance - // critical code path! - std::vector tmp = reservoirState.pressure(); - convertUnit_(tmp, toBar); + // write out the pressure of the reference phase (whatever + // phase that is...). this is not the most performant solution + // thinkable, but this is also not in the most performance + // critical code path! + std::vector tmp = reservoirState.pressure(); + convertUnit_(tmp, toBar); - sol.add(EclipseKeyword("PRESSURE", tmp)); + sol.add(EclipseKeyword("PRESSURE", tmp)); - for (int phase = 0; phase != BlackoilPhases::MaxNumPhases; ++phase) { - // Eclipse never writes the oil saturation, so all post-processors - // must calculate this from the other saturations anyway - if (phase == BlackoilPhases::PhaseIndex::Liquid) { - continue; - } - if (uses_.phase_used [phase]) { - tmp = reservoirState.saturation(); - extractFromStripedData_(tmp, - /*offset=*/uses_.phase_pos[phase], - /*stride=*/uses_.num_phases); - sol.add(EclipseKeyword(SAT_NAMES[phase], tmp)); - } + for (int phase = 0; phase != BlackoilPhases::MaxNumPhases; ++phase) { + // Eclipse never writes the oil saturation, so all post-processors + // must calculate this from the other saturations anyway + if (phase == BlackoilPhases::PhaseIndex::Liquid) { + continue; } - } - else { - // start writing to files - EclipseRestart rst (outputDir_, - baseName_, - timer, - outputTimeStepIdx_); - rst.writeHeader (timer, - outputTimeStepIdx_, - uses_, - *parser_, - reservoirState.pressure ().size ()); - EclipseSolution sol (rst); - - // write pressure and saturation fields (same as DataMap holds) - // convert the pressures from Pascals to bar because Eclipse - // seems to write bars - auto data = reservoirState.pressure(); - convertUnit_(data, toBar); - sol.add(EclipseKeyword("PRESSURE", data)); - - for (int phase = 0; phase != BlackoilPhases::MaxNumPhases; ++phase) { - // Eclipse never writes the oil saturation, so all post-processors - // must calculate this from the other saturations anyway - if (phase == BlackoilPhases::PhaseIndex::Liquid) { - continue; - } - if (uses_.phase_used [phase]) { - auto tmp = reservoirState.saturation(); - extractFromStripedData_(tmp, - /*offset=*/uses_.phase_pos[phase], - /*stride=*/uses_.num_phases); - sol.add (EclipseKeyword(SAT_NAMES[phase], tmp)); - } + if (uses_.phase_used [phase]) { + tmp = reservoirState.saturation(); + extractFromStripedData_(tmp, + /*offset=*/uses_.phase_pos[phase], + /*stride=*/uses_.num_phases); + sol.add(EclipseKeyword(SAT_NAMES[phase], tmp)); } } @@ -1569,69 +1205,12 @@ void EclipseWriter::writeTimeStep( #endif // HAVE_ERT -EclipseWriter::EclipseWriter ( - const ParameterGroup& params, - std::shared_ptr parser, - std::shared_ptr grid) - : parser_ (parser) - , newParserDeck_() - , grid_ (grid) - , uses_ (phaseUsageFromDeck (*parser)) -{ - // set the index of the first time step written to 0... - outputTimeStepIdx_ = 0; - - - // get the base name from the name of the deck - using boost::filesystem::path; - path deck (params.get ("deck_filename")); - if (boost::to_upper_copy (path (deck.extension ()).string ()) == ".DATA") { - baseName_ = path (deck.stem ()).string (); - } - else { - baseName_ = path (deck.filename ()).string (); - } - - // make uppercase of everything (or otherwise we'll get uppercase - // of some of the files (.SMSPEC, .UNSMRY) and not others - baseName_ = boost::to_upper_copy (baseName_); - - // retrieve the value of the "output" parameter - enableOutput_ = params.getDefault("output", /*defaultValue=*/true); - - // retrieve the interval at which something should get written to - // disk (once every N timesteps) - outputInterval_ = params.getDefault("output_interval", /*defaultValue=*/1); - - // store in current directory if not explicitly set - outputDir_ = params.getDefault("output_dir", "."); - - // set the index of the first time step written to 0... - outputTimeStepIdx_ = 0; - - if (enableOutput_) { - // make sure that the output directory exists, if not try to create it - if (!boost::filesystem::exists(outputDir_)) { - std::cout << "Trying to create directory \"" << outputDir_ << "\" for the simulation output\n"; - boost::filesystem::create_directories(outputDir_); - } - - if (!boost::filesystem::is_directory(outputDir_)) { - OPM_THROW(std::runtime_error, - "The path specified as output directory '" << outputDir_ - << "' is not a directory"); - } - } -} - -EclipseWriter::EclipseWriter ( - const ParameterGroup& params, - Opm::DeckConstPtr newParserDeck, - std::shared_ptr grid) - : parser_ () - , newParserDeck_(newParserDeck) - , grid_ (grid) - , uses_ (phaseUsageFromDeck (newParserDeck)) +EclipseWriter::EclipseWriter(const ParameterGroup& params, + Opm::DeckConstPtr newParserDeck, + std::shared_ptr grid) + : newParserDeck_(newParserDeck) + , grid_(grid) + , uses_(phaseUsageFromDeck(newParserDeck)) { // get the base name from the name of the deck using boost::filesystem::path; diff --git a/opm/core/io/eclipse/EclipseWriter.hpp b/opm/core/io/eclipse/EclipseWriter.hpp index 4adfbba6..b2808372 100644 --- a/opm/core/io/eclipse/EclipseWriter.hpp +++ b/opm/core/io/eclipse/EclipseWriter.hpp @@ -36,7 +36,6 @@ struct EclipseSummary; namespace Opm { // forward declarations -class EclipseGridParser; class SimulatorState; class SimulatorTimer; class WellState; @@ -55,14 +54,6 @@ namespace parameter { class ParameterGroup; } class EclipseWriter : public OutputWriter { public: - /*! - * \brief Sets the common attributes required to write eclipse - * binary files using ERT. - */ - EclipseWriter(const parameter::ParameterGroup& params, - std::shared_ptr parser, - std::shared_ptr grid); - /*! * \brief Sets the common attributes required to write eclipse * binary files using ERT. @@ -97,7 +88,6 @@ public: const WellState& wellState); private: - std::shared_ptr parser_; Opm::DeckConstPtr newParserDeck_; std::shared_ptr grid_; bool enableOutput_; From 3ce9d6b3b1a39f0b9dcd571c7ba32c94e19e4c8b Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Wed, 2 Apr 2014 16:38:53 +0200 Subject: [PATCH 66/81] output writers: remove the state parameters from writeInit() because as discussed with Atgeirr, this function should no longer write the initial solution, it also does no longer need to know the initial simulator state... --- opm/core/io/OutputWriter.cpp | 6 ++---- opm/core/io/OutputWriter.hpp | 11 ++++------- opm/core/io/eclipse/EclipseWriter.cpp | 13 ++++--------- opm/core/io/eclipse/EclipseWriter.hpp | 18 +++++++++++------- opm/core/simulator/SimulatorOutput.cpp | 2 +- 5 files changed, 22 insertions(+), 28 deletions(-) diff --git a/opm/core/io/OutputWriter.cpp b/opm/core/io/OutputWriter.cpp index 4b66e2c4..da3c688a 100644 --- a/opm/core/io/OutputWriter.cpp +++ b/opm/core/io/OutputWriter.cpp @@ -25,11 +25,9 @@ struct MultiWriter : public OutputWriter { MultiWriter (ptr_t writers) : writers_ (std::move (writers)) { } /// Forward the call to all writers - virtual void writeInit(const SimulatorTimer &timer, - const SimulatorState& reservoirState, - const WellState& wellState) { + virtual void writeInit(const SimulatorTimer &timer) { for (it_t it = writers_->begin (); it != writers_->end (); ++it) { - (*it)->writeInit (timer, reservoirState, wellState); + (*it)->writeInit (timer); } } diff --git a/opm/core/io/OutputWriter.hpp b/opm/core/io/OutputWriter.hpp index 46bca77a..df3759bc 100644 --- a/opm/core/io/OutputWriter.hpp +++ b/opm/core/io/OutputWriter.hpp @@ -67,15 +67,12 @@ public: virtual ~OutputWriter () { } /** - * Write the static eclipse data (grid, PVT curves, etc) as well as the - * initial state to disk. + * Write the static data (grid, PVT curves, etc) to disk. * * This routine should be called before the first timestep (i.e. when * timer.currentStepNum () == 0) */ - virtual void writeInit(const SimulatorTimer &timer, - const SimulatorState& reservoirState, - const WellState& wellState) = 0; + virtual void writeInit(const SimulatorTimer &timer) = 0; /*! * \brief Write a blackoil reservoir state to disk for later inspection with @@ -88,8 +85,8 @@ public: * i.e. timer.currentStepNum () > 0. */ virtual void writeTimeStep(const SimulatorTimer& timer, - const SimulatorState& reservoirState, - const WellState& wellState) = 0; + const SimulatorState& reservoirState, + const WellState& wellState) = 0; /*! * Create a suitable set of output formats based on configuration. diff --git a/opm/core/io/eclipse/EclipseWriter.cpp b/opm/core/io/eclipse/EclipseWriter.cpp index 241ab953..f7f26141 100644 --- a/opm/core/io/eclipse/EclipseWriter.cpp +++ b/opm/core/io/eclipse/EclipseWriter.cpp @@ -1035,9 +1035,7 @@ EclipseSummary::addWells (Opm::DeckConstPtr newParserDeck, namespace Opm { -void EclipseWriter::writeInit(const SimulatorTimer &timer, - const SimulatorState& reservoirState, - const WellState& wellState) +void EclipseWriter::writeInit(const SimulatorTimer &timer) { // if we don't want to write anything, this method becomes a // no-op... @@ -1076,9 +1074,6 @@ void EclipseWriter::writeInit(const SimulatorTimer &timer, fortio.writeKeyword ("PERMZ", data); } - /* Initial solution (pressure and saturation) */ - writeSolution_(timer, reservoirState); - /* Create summary object (could not do it at construction time, since it requires knowledge of the start time). */ summary_.reset(new EclipseSummary(outputDir_, baseName_, timer, newParserDeck_)); @@ -1169,14 +1164,14 @@ void EclipseWriter::writeTimeStep(const SimulatorTimer& timer, // will contain data from the whole simulation, instead of just // the last step. summary_->writeTimeStep(timer, wellState); + + ++outputTimeStepIdx_; } #else namespace Opm { -void EclipseWriter::writeInit(const SimulatorTimer&, - const SimulatorState&, - const WellState&) +void EclipseWriter::writeInit(const SimulatorTimer&) { // if we don't want to write anything, this method becomes a // no-op... diff --git a/opm/core/io/eclipse/EclipseWriter.hpp b/opm/core/io/eclipse/EclipseWriter.hpp index b2808372..304d4dc4 100644 --- a/opm/core/io/eclipse/EclipseWriter.hpp +++ b/opm/core/io/eclipse/EclipseWriter.hpp @@ -69,16 +69,20 @@ public: virtual ~EclipseWriter (); /** - * Write the static eclipse data (grid, PVT curves, etc) as well as the - * initial state to disk. + * Write the static eclipse data (grid, PVT curves, etc) to disk. */ - virtual void writeInit(const SimulatorTimer &timer, - const SimulatorState& reservoirState, - const WellState& wellState); + virtual void writeInit(const SimulatorTimer &timer); /*! - * \brief Write a blackoil reservoir state to disk for later inspection with - * visualization tools like ResInsight + * \brief Write a reservoir state and summary information to disk. + * + * + * The reservoir state can be inspected with visualization tools like + * ResInsight. + * + * The summary information can then be visualized using tools from + * ERT or ECLIPSE. Note that calling this method is only + * meaningful after the first time step has been completed. * * \param[in] reservoirState The thermodynamic state of the reservoir * \param[in] wellState The production/injection data for all wells diff --git a/opm/core/simulator/SimulatorOutput.cpp b/opm/core/simulator/SimulatorOutput.cpp index 67092c3f..250046da 100644 --- a/opm/core/simulator/SimulatorOutput.cpp +++ b/opm/core/simulator/SimulatorOutput.cpp @@ -53,7 +53,7 @@ SimulatorOutputBase::SimulatorOutputBase ( , next_ (0) { // write the static initialization files, even before simulation starts - writer_->writeInit (*timer, *state, *wellState); + writer_->writeInit (*timer); } // default destructor is OK, just need to be defined From c8683b39d0bba5d5c14b1a1fdc17dbd44e67cd8c Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Wed, 2 Apr 2014 16:45:46 +0200 Subject: [PATCH 67/81] EclipseWriter: fold the writeSolution_() method back into writeTimeStep() because this was the only caller... --- opm/core/io/eclipse/EclipseWriter.cpp | 37 ++++++++++----------------- opm/core/io/eclipse/EclipseWriter.hpp | 4 --- 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/opm/core/io/eclipse/EclipseWriter.cpp b/opm/core/io/eclipse/EclipseWriter.cpp index f7f26141..07e03696 100644 --- a/opm/core/io/eclipse/EclipseWriter.cpp +++ b/opm/core/io/eclipse/EclipseWriter.cpp @@ -1096,9 +1096,21 @@ void EclipseWriter::activeToGlobalCellData_(std::vector &globalCellsBuf, } } -void EclipseWriter::writeSolution_(const SimulatorTimer& timer, - const SimulatorState& reservoirState) +void EclipseWriter::writeTimeStep(const SimulatorTimer& timer, + const SimulatorState& reservoirState, + const WellState& wellState) { + // if we don't want to write anything, this method becomes a + // no-op... + if (!enableOutput_) { + return; + } + + // respected the output_interval parameter + if (outputTimeStepIdx_ % outputInterval_ != 0) { + return; + } + // start writing to files EclipseRestart rst(outputDir_, baseName_, timer, outputTimeStepIdx_); rst.writeHeader (timer, outputTimeStepIdx_, uses_, newParserDeck_, reservoirState.pressure().size ()); @@ -1128,27 +1140,6 @@ void EclipseWriter::writeSolution_(const SimulatorTimer& timer, } } - ++outputTimeStepIdx_; -} - -void EclipseWriter::writeTimeStep(const SimulatorTimer& timer, - const SimulatorState& reservoirState, - const WellState& wellState) -{ - // if we don't want to write anything, this method becomes a - // no-op... - if (!enableOutput_) { - return; - } - - // respected the output_interval parameter - if (timer.currentStepNum() % outputInterval_ != 0) { - return; - } - - /* Field variables (pressure, saturation) */ - writeSolution_(timer, reservoirState); - /* Summary variables (well reporting) */ // TODO: instead of writing the header (smspec) every time, it should // only be written when there is a change in the well configuration diff --git a/opm/core/io/eclipse/EclipseWriter.hpp b/opm/core/io/eclipse/EclipseWriter.hpp index 304d4dc4..b5a2742e 100644 --- a/opm/core/io/eclipse/EclipseWriter.hpp +++ b/opm/core/io/eclipse/EclipseWriter.hpp @@ -105,10 +105,6 @@ private: void activeToGlobalCellData_(std::vector &globalCellsBuf, const std::vector &activeCellsBuf, const std::vector &inactiveCellsBuf) const; - - /// Write solution field variables (pressure and saturation) - void writeSolution_(const SimulatorTimer& timer, - const SimulatorState& reservoirState); }; } // namespace Opm From b4b03ffee35c6d64e0f6a1c750dc1207c6154e3b Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Wed, 2 Apr 2014 16:47:59 +0200 Subject: [PATCH 68/81] EclipseWriter: remove unused method activeToGlobalCellData_() --- opm/core/io/eclipse/EclipseWriter.cpp | 16 ---------------- opm/core/io/eclipse/EclipseWriter.hpp | 4 ---- 2 files changed, 20 deletions(-) diff --git a/opm/core/io/eclipse/EclipseWriter.cpp b/opm/core/io/eclipse/EclipseWriter.cpp index 07e03696..88a599db 100644 --- a/opm/core/io/eclipse/EclipseWriter.cpp +++ b/opm/core/io/eclipse/EclipseWriter.cpp @@ -1080,22 +1080,6 @@ void EclipseWriter::writeInit(const SimulatorTimer &timer) summary_->addWells (newParserDeck_, uses_); } -void EclipseWriter::activeToGlobalCellData_(std::vector &globalCellsBuf, - const std::vector &activeCellsBuf, - const std::vector &inactiveCellsBuf) const -{ - globalCellsBuf = inactiveCellsBuf; - - // overwrite the values of active cells - for (int activeCellIdx = 0; - activeCellIdx < grid_->number_of_cells; - ++activeCellIdx) - { - int globalCellIdx = grid_->global_cell[activeCellIdx]; - globalCellsBuf[globalCellIdx] = activeCellsBuf[activeCellIdx]; - } -} - void EclipseWriter::writeTimeStep(const SimulatorTimer& timer, const SimulatorState& reservoirState, const WellState& wellState) diff --git a/opm/core/io/eclipse/EclipseWriter.hpp b/opm/core/io/eclipse/EclipseWriter.hpp index b5a2742e..1f0b8652 100644 --- a/opm/core/io/eclipse/EclipseWriter.hpp +++ b/opm/core/io/eclipse/EclipseWriter.hpp @@ -101,10 +101,6 @@ private: std::string baseName_; PhaseUsage uses_; // active phases in the input deck std::shared_ptr summary_; - - void activeToGlobalCellData_(std::vector &globalCellsBuf, - const std::vector &activeCellsBuf, - const std::vector &inactiveCellsBuf) const; }; } // namespace Opm From 04d8322fa001bc66389a62ce7607ea4313aa7d35 Mon Sep 17 00:00:00 2001 From: osae Date: Thu, 3 Apr 2014 09:07:00 +0200 Subject: [PATCH 69/81] Some additional tests: live gas, RSVD and RVVD --- tests/capillary.DATA | 9 +- tests/deadfluids.DATA | 9 +- tests/equil_livegas.DATA | 131 ++++++++++++++++++++ tests/equil_rsvd_and_rvvd.DATA | 151 +++++++++++++++++++++++ tests/test_equil.cpp | 219 +++++++++++++++++++++++++++++++-- 5 files changed, 507 insertions(+), 12 deletions(-) create mode 100644 tests/equil_livegas.DATA create mode 100644 tests/equil_rsvd_and_rvvd.DATA diff --git a/tests/capillary.DATA b/tests/capillary.DATA index d8d163fc..89c93899 100644 --- a/tests/capillary.DATA +++ b/tests/capillary.DATA @@ -2,6 +2,13 @@ WATER OIL GAS +TABDIMS + 1 1 40 20 1 20 / + +EQLDIMS +-- NTEQUL + 1 / + PVDO 100 1.0 1.0 200 0.9 1.0 @@ -27,5 +34,5 @@ DENSITY / EQUIL -50 150 50 0.25 20 0.35 +50 150 50 0.25 20 0.35 1* 1* 0 / diff --git a/tests/deadfluids.DATA b/tests/deadfluids.DATA index ffaebc7f..89232054 100644 --- a/tests/deadfluids.DATA +++ b/tests/deadfluids.DATA @@ -2,6 +2,13 @@ WATER OIL GAS +TABDIMS + 1 1 40 20 1 20 / + +EQLDIMS +-- NTEQUL + 1 / + PVDO 100 1.0 1.0 200 0.5 1.0 @@ -27,5 +34,5 @@ DENSITY / EQUIL -5 150 5 0 2 0 +5 150 5 0 2 0 1* 1* 0 / diff --git a/tests/equil_livegas.DATA b/tests/equil_livegas.DATA new file mode 100644 index 00000000..a0676f52 --- /dev/null +++ b/tests/equil_livegas.DATA @@ -0,0 +1,131 @@ +NOECHO + +RUNSPEC ====== + +WATER +OIL +GAS +VAPOIL + +TABDIMS + 1 1 40 20 1 20 / + +DIMENS +1 1 20 +/ + +WELLDIMS + 30 10 2 30 / + +START + 1 'JAN' 1990 / + +NSTACK + 25 / + +EQLDIMS +-- NTEQUL + 1 / + + +FMTOUT +FMTIN + +GRID ====== + +DXV +1.0 +/ + +DYV +1.0 +/ + +DZV +20*5.0 +/ + + +PORO +20*0.2 +/ + + +PERMZ + 20*1.0 +/ + +PERMY +20*100.0 +/ + +PERMX +20*100.0 +/ + +BOX + 1 1 1 1 1 1 / + +TOPS +0.0 +/ + +PROPS ====== + +PVDO +100 1.0 1.0 +200 0.9 1.0 +/ + +PVTG +-- Pg Rv Bg Vg + 100 0.0001 0.010 0.1 + 0.0 0.0104 0.1 / + 200 0.0004 0.005 0.2 + 0.0 0.0054 0.2 / +/ + +SWOF +0.2 0 1 0.9 +1 1 0 0.1 +/ + +SGOF +0 0 1 0.2 +0.8 1 0 0.5 +/ + +PVTW +--RefPres Bw Comp Vw Cv + 1. 1.0 4.0E-5 0.96 0.0 / + + +ROCK +--RefPres Comp + 1. 5.0E-5 / + +DENSITY +700 1000 1 +/ + +SOLUTION ====== + +EQUIL +45 150 50 0.25 45 0.35 1* 1* 0 +/ + +RPTSOL +'PRES' 'PGAS' 'PWAT' 'SOIL' 'SWAT' 'SGAS' 'RS' 'RESTART=2' / + +SUMMARY ====== +RUNSUM + +SEPARATE + +SCHEDULE ====== + +RPTSCHED +'PRES' 'PGAS' 'PWAT' 'SOIL' 'SWAT' 'SGAS' 'RS' 'RESTART=3' 'NEWTON=2' / + + +END diff --git a/tests/equil_rsvd_and_rvvd.DATA b/tests/equil_rsvd_and_rvvd.DATA new file mode 100644 index 00000000..3076524e --- /dev/null +++ b/tests/equil_rsvd_and_rvvd.DATA @@ -0,0 +1,151 @@ +NOECHO + +RUNSPEC ====== + +WATER +OIL +GAS +DISGAS +VAPOIL + +TABDIMS + 1 1 40 20 1 20 / + +DIMENS +1 1 20 +/ + +WELLDIMS + 30 10 2 30 / + +START + 1 'JAN' 1990 / + +NSTACK + 25 / + +EQLDIMS +-- NTEQUL + 1 / + + +FMTOUT +FMTIN + +GRID ====== + +DXV +1.0 +/ + +DYV +1.0 +/ + +DZV +20*5.0 +/ + + +PORO +20*0.2 +/ + + +PERMZ + 20*1.0 +/ + +PERMY +20*100.0 +/ + +PERMX +20*100.0 +/ + +BOX + 1 1 1 1 1 1 / + +TOPS +0.0 +/ + +PROPS ====== + +PVTO +-- Rs Pbub Bo Vo + 0 1. 1.0000 1.20 / + 20 40. 1.0120 1.17 / + 40 80. 1.0255 1.14 / + 60 120. 1.0380 1.11 / + 80 160. 1.0510 1.08 / + 100 200. 1.0630 1.06 / + 120 240. 1.0750 1.03 / + 140 280. 1.0870 1.00 / + 160 320. 1.0985 .98 / + 180 360. 1.1100 .95 / + 200 400. 1.1200 .94 + 500. 1.1189 .94 / +/ + +PVTG +-- Pg Rv Bg Vg + 100 0.0001 0.010 0.1 + 0.0 0.0104 0.1 / + 200 0.0004 0.005 0.2 + 0.0 0.0054 0.2 / +/ + +SWOF +0.2 0 1 0.9 +1 1 0 0.1 +/ + +SGOF +0 0 1 0.2 +0.8 1 0 0.5 +/ + +PVTW +--RefPres Bw Comp Vw Cv + 1. 1.0 4.0E-5 0.96 0.0 / + + +ROCK +--RefPres Comp + 1. 5.0E-5 / + +DENSITY +700 1000 1 +/ + +SOLUTION ====== + +EQUIL +45 150 50 0.25 45 0.35 1 1 0 +/ + +RSVD + 0 0.0 + 100 100. / + +RVVD + 0. 0. + 100. 0.0001 / + +RPTSOL +'PRES' 'PGAS' 'PWAT' 'SOIL' 'SWAT' 'SGAS' 'RS' 'RESTART=2' / + +SUMMARY ====== +RUNSUM + +SEPARATE + +SCHEDULE ====== + +RPTSCHED +'PRES' 'PGAS' 'PWAT' 'SOIL' 'SWAT' 'SGAS' 'RS' 'RESTART=3' 'NEWTON=2' / + + +END diff --git a/tests/test_equil.cpp b/tests/test_equil.cpp index b1e6f6c0..d68470b0 100644 --- a/tests/test_equil.cpp +++ b/tests/test_equil.cpp @@ -329,9 +329,10 @@ BOOST_AUTO_TEST_CASE (DeckAllDead) { std::shared_ptr grid(create_grid_cart3d(1, 1, 10), destroy_grid); - Opm::EclipseGridParser deck("deadfluids.DATA"); + Opm::ParserPtr parser(new Opm::Parser() ); + Opm::DeckConstPtr deck = parser->parseFile("deadfluids.DATA"); Opm::BlackoilPropertiesFromDeck props(deck, *grid, false); - Opm::Equil::DeckDependent::InitialStateComputer comp(props, deck, *grid, 10.0); + Opm::Equil::DeckDependent::InitialStateComputer comp(props, deck, *grid, 10.0); const auto& pressures = comp.press(); BOOST_REQUIRE(pressures.size() == 3); BOOST_REQUIRE(int(pressures[0].size()) == grid->number_of_cells); @@ -354,7 +355,8 @@ BOOST_AUTO_TEST_CASE (CapillaryInversion) // Test setup. Opm::GridManager gm(1, 1, 40, 1.0, 1.0, 2.5); const UnstructuredGrid& grid = *(gm.c_grid()); - Opm::EclipseGridParser deck("capillary.DATA"); + Opm::ParserPtr parser(new Opm::Parser() ); + Opm::DeckConstPtr deck = parser->parseFile("capillary.DATA"); Opm::BlackoilPropertiesFromDeck props(deck, grid, false); // Test the capillary inversion for oil-water. @@ -405,10 +407,11 @@ BOOST_AUTO_TEST_CASE (DeckWithCapillary) { Opm::GridManager gm(1, 1, 20, 1.0, 1.0, 5.0); const UnstructuredGrid& grid = *(gm.c_grid()); - Opm::EclipseGridParser deck("capillary.DATA"); + Opm::ParserPtr parser(new Opm::Parser() ); + Opm::DeckConstPtr deck = parser->parseFile("capillary.DATA"); Opm::BlackoilPropertiesFromDeck props(deck, grid, false); - Opm::Equil::DeckDependent::InitialStateComputer comp(props, deck, grid, 10.0); + Opm::Equil::DeckDependent::InitialStateComputer comp(props, deck, grid, 10.0); const auto& pressures = comp.press(); BOOST_REQUIRE(pressures.size() == 3); BOOST_REQUIRE(int(pressures[0].size()) == grid.number_of_cells); @@ -443,10 +446,11 @@ BOOST_AUTO_TEST_CASE (DeckWithCapillaryOverlap) { Opm::GridManager gm(1, 1, 20, 1.0, 1.0, 5.0); const UnstructuredGrid& grid = *(gm.c_grid()); - Opm::EclipseGridParser deck("capillary_overlap.DATA"); + Opm::ParserPtr parser(new Opm::Parser() ); + Opm::DeckConstPtr deck = parser->parseFile("capillary_overlap.DATA"); Opm::BlackoilPropertiesFromDeck props(deck, grid, false); - Opm::Equil::DeckDependent::InitialStateComputer comp(props, deck, grid, 9.80665); + Opm::Equil::DeckDependent::InitialStateComputer comp(props, deck, grid, 9.80665); const auto& pressures = comp.press(); BOOST_REQUIRE(pressures.size() == 3); BOOST_REQUIRE(int(pressures[0].size()) == grid.number_of_cells); @@ -505,7 +509,6 @@ BOOST_AUTO_TEST_CASE (DeckWithLiveOil) const UnstructuredGrid& grid = *(gm.c_grid()); Opm::ParserPtr parser(new Opm::Parser() ); Opm::DeckConstPtr deck = parser->parseFile("equil_liveoil.DATA"); - //Opm::EclipseGridParser deck("equil_liveoil.DATA"); Opm::BlackoilPropertiesFromDeck props(deck, grid, false); Opm::Equil::DeckDependent::InitialStateComputer comp(props, deck, grid, 9.80665); @@ -543,13 +546,11 @@ BOOST_AUTO_TEST_CASE (DeckWithLiveOil) { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.20073, 0.08469, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.77102, 0.46578, 0.01458, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; - const std::vector s_opm[3]{ // opm { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2291709091, 0.5343054545, 0.78472, 0.91529, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2005866667, 0.08471, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.7708290909, 0.4656945455, 0.01469333333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; - for (int phase = 0; phase < 3; ++phase) { BOOST_REQUIRE(sats[phase].size() == s_opm[phase].size()); for (size_t i = 0; i < s_opm[phase].size(); ++i) { @@ -559,7 +560,205 @@ BOOST_AUTO_TEST_CASE (DeckWithLiveOil) } std::cout << std::endl; } + + const auto& rs = comp.rs(); + const std::vector rs_opm {74.612335679539058, 74.649052116644228, 74.685786561426298, 74.722539022717172, // opm + 74.759309509353145, 74.796098030174733, 74.8329045940269, 74.869729209758916, + 74.906571886224327, 75.090675116639048, 75.0, 75.0, + 75.0, 75.0, 75.0, 75.0, + 75.0, 75.0, 75.0, 75.0}; + const std::vector rs_ecl {74.612228, 74.648956, 74.685707, 74.722473, // eclipse + 74.759254, 74.796051, 74.832870, 74.875145, + 74.969231, 75.090706, 75.000000, 75.000000, + 75.000000, 75.000000, 75.000000, 75.000000, + 75.000000, 75.000000, 75.000000, 75.000000}; + for (size_t i = 0; i < rs_opm.size(); ++i) { + //std::cout << std::setprecision(10) << sats[phase][i] << '\n'; + BOOST_CHECK_CLOSE(rs[i], rs_opm[i], reltol); + BOOST_CHECK_CLOSE(rs[i], rs_ecl[i], reltol_ecl); + } } + +BOOST_AUTO_TEST_CASE (DeckWithLiveGas) +{ + Opm::GridManager gm(1, 1, 20, 1.0, 1.0, 5.0); + const UnstructuredGrid& grid = *(gm.c_grid()); + Opm::ParserPtr parser(new Opm::Parser() ); + Opm::DeckConstPtr deck = parser->parseFile("equil_livegas.DATA"); + Opm::BlackoilPropertiesFromDeck props(deck, grid, false); + + Opm::Equil::DeckDependent::InitialStateComputer comp(props, deck, grid, 9.80665); + const auto& pressures = comp.press(); + BOOST_REQUIRE(pressures.size() == 3); + BOOST_REQUIRE(int(pressures[0].size()) == grid.number_of_cells); + + const int first = 0, last = grid.number_of_cells - 1; + // The relative tolerance is too loose to be very useful, + // but the answer we are checking is the result of an ODE + // solver, and it is unclear if we should check it against + // the true answer or something else. + const double reltol = 1.0e-6; + const double reltol_ecl = 1.0; + BOOST_CHECK_CLOSE(pressures[0][first], 1.48215e+07, reltol_ecl); // eclipse + BOOST_CHECK_CLOSE(pressures[0][last], 1.54801e+07, reltol_ecl); + BOOST_CHECK_CLOSE(pressures[1][first], 1.49115e+07, reltol_ecl); + BOOST_CHECK_CLOSE(pressures[1][last], 1.54901e+07, reltol_ecl); + + BOOST_CHECK_CLOSE(pressures[0][first], 1.482150311e7, reltol); // opm + BOOST_CHECK_CLOSE(pressures[0][last], 1.547988347e7, reltol); + BOOST_CHECK_CLOSE(pressures[1][first], 1.491150311e7, reltol); + BOOST_CHECK_CLOSE(pressures[1][last], 1.548988347e7, reltol); + + const auto& sats = comp.saturation(); + // std::cout << "Saturations:\n"; + // for (const auto& sat : sats) { + // for (const double s : sat) { + // std::cout << s << ' '; + // } + // std::cout << std::endl; + // } + const std::vector s_ecl[3]{ // eclipse + { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.24285614, 0.53869015, 0.78454906, 0.91542006, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.18311, 0.08458, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.75714386, 0.46130988, 0.032345835, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + }; + + const std::vector s_opm[3]{ // opm + { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.24310545, 0.5388, 0.78458, 0.91540, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.18288667, 0.0846, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.75689455, 0.4612, 0.03253333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + }; + for (int phase = 0; phase < 3; ++phase) { + BOOST_REQUIRE(sats[phase].size() == s_opm[phase].size()); + for (size_t i = 0; i < s_opm[phase].size(); ++i) { + //std::cout << std::setprecision(10) << sats[phase][i] << '\n'; + BOOST_CHECK_CLOSE(sats[phase][i], s_opm[phase][i], 100.*reltol); + BOOST_CHECK_CLOSE(sats[phase][i], s_ecl[phase][i], reltol_ecl); + } + std::cout << std::endl; + } + + const auto& rv = comp.rv(); + const std::vector rv_opm { // opm + 2.4884509e-4, 2.4910378e-4, 2.4936267e-4, 2.4962174e-4, + 2.4988100e-4, 2.5014044e-4, 2.5040008e-4, 2.5065990e-4, + 2.5091992e-4, 2.5118012e-4, 2.5223082e-4, 2.5105e-4, + 2.5105e-4, 2.5105e-4, 2.5105e-4, 2.5105e-4, + 2.5105e-4, 2.5105e-4, 2.5105e-4, 2.5105e-4}; + + const std::vector rv_ecl { // eclipse + 0.24884584E-03, 0.24910446E-03, 0.24936325E-03, 0.24962222E-03, + 0.24988138E-03, 0.25014076E-03, 0.25040031E-03, 0.25066003E-03, + 0.25091995E-03, 0.25118008E-03, 0.25223137E-03, 0.25104999E-03, + 0.25104999E-03, 0.25104999E-03, 0.25104999E-03, 0.25104999E-03, + 0.25104999E-03, 0.25104999E-03, 0.25104999E-03, 0.25104999E-03}; + + for (size_t i = 0; i < rv_opm.size(); ++i) { + //std::cout << std::setprecision(10) << sats[phase][i] << '\n'; + BOOST_CHECK_CLOSE(rv[i], rv_opm[i], 100.*reltol); + BOOST_CHECK_CLOSE(rv[i], rv_ecl[i], reltol_ecl); + } +} + +BOOST_AUTO_TEST_CASE (DeckWithRSVDAndRVVD) +{ + Opm::GridManager gm(1, 1, 20, 1.0, 1.0, 5.0); + const UnstructuredGrid& grid = *(gm.c_grid()); + Opm::ParserPtr parser(new Opm::Parser() ); + Opm::DeckConstPtr deck = parser->parseFile("equil_rsvd_and_rvvd.DATA"); + Opm::BlackoilPropertiesFromDeck props(deck, grid, false); + + Opm::Equil::DeckDependent::InitialStateComputer comp(props, deck, grid, 9.80665); + const auto& pressures = comp.press(); + BOOST_REQUIRE(pressures.size() == 3); + BOOST_REQUIRE(int(pressures[0].size()) == grid.number_of_cells); + + const int first = 0, last = grid.number_of_cells - 1; + // The relative tolerance is too loose to be very useful, + // but the answer we are checking is the result of an ODE + // solver, and it is unclear if we should check it against + // the true answer or something else. + const double reltol = 1.0e-6; + const double reltol_ecl = 1.0; + BOOST_CHECK_CLOSE(pressures[0][first], 1.48350e+07, reltol_ecl); // eclipse + BOOST_CHECK_CLOSE(pressures[0][last], 1.54794e+07, reltol_ecl); + BOOST_CHECK_CLOSE(pressures[1][first], 1.49250e+07, reltol_ecl); + BOOST_CHECK_CLOSE(pressures[1][last], 1.54894e+07, reltol_ecl); + + BOOST_CHECK_CLOSE(pressures[0][first], 1.483499660e7, reltol); // opm + BOOST_CHECK_CLOSE(pressures[0][last], 1.547924516e7, reltol); + BOOST_CHECK_CLOSE(pressures[1][first], 1.492499660e7, reltol); + BOOST_CHECK_CLOSE(pressures[1][last], 1.548924516e7, reltol); + + const auto& sats = comp.saturation(); + // std::cout << "Saturations:\n"; + // for (const auto& sat : sats) { + // for (const double s : sat) { + // std::cout << s << ' '; + // } + // std::cout << std::endl; + // } + const std::vector s_ecl[3]{ // eclipse + { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.22206347, 0.52871972, 0.78150368, 0.91819441, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.19656529, 0.081805572, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.77793652, 0.47128031, 0.021931054, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + }; + + const std::vector s_opm[3]{ // opm + { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.22232000, 0.52882909, 0.78153000, 0.91817000, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.19636333, 0.08183000, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.77768000, 0.47117091, 0.02210667, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + }; + + for (int phase = 0; phase < 3; ++phase) { + BOOST_REQUIRE(sats[phase].size() == s_opm[phase].size()); + for (size_t i = 0; i < s_opm[phase].size(); ++i) { + //std::cout << std::setprecision(10) << sats[phase][i] << '\n'; + BOOST_CHECK_CLOSE(sats[phase][i], s_opm[phase][i], 100.*reltol); + BOOST_CHECK_CLOSE(sats[phase][i], s_ecl[phase][i], reltol_ecl); + } + std::cout << std::endl; + } + + const auto& rs = comp.rs(); + const std::vector rs_opm { // opm + 74.624983020822540, 74.659590408801634, 74.694380353364522, 74.729353362649505, + 74.764509945812975, 74.799850613032362, 74.835375875509555, 74.87108624547416, + 74.906982236186707, 75.088917653469309, 52.5, 57.5, + 62.5, 67.5, 72.5, 76.45954840804761, + 76.70621044909619, 76.952877357524045, 77.199549133522638, 77.446225777283587}; + + const std::vector rs_ecl { // eclipse + 74.625114, 74.659706, 74.694481, 74.729439, + 74.764580, 74.799904, 74.835419, 74.875252, + 74.968628, 75.088951, 52.500000, 57.500000, + 62.500000, 67.500000, 72.500000, 76.168388, + 76.349953, 76.531532, 76.713142, 76.894775,}; + + const auto& rv = comp.rv(); + const std::vector rv_opm { // opm + 2.50e-6, 7.50e-6, 1.25e-5, 1.75e-5, + 2.25e-5, 2.75e-5, 3.25e-5, 3.75e-5, + 4.25e-5, 2.51158386e-4, 2.52203372e-4, 5.75e-5, + 6.25e-5, 6.75e-5, 7.25e-5, 7.75e-5, + 8.25e-5, 8.75e-5, 9.25e-5, 9.75e-5}; + + const std::vector rv_ecl { // eclipse + 0.24999999E-05, 0.74999998E-05, 0.12500000E-04, 0.17500000E-04, + 0.22500000E-04, 0.27500000E-04, 0.32500000E-04, 0.37500002E-04, + 0.42500000E-04, 0.25115837E-03, 0.25220393E-03, 0.57500001E-04, + 0.62500003E-04, 0.67499997E-04, 0.72499999E-04, 0.77500001E-04, + 0.82500002E-04, 0.87499997E-04, 0.92499999E-04, 0.97500000E-04}; + + for (size_t i = 0; i < rv_opm.size(); ++i) { + //std::cout << std::setprecision(10) << sats[phase][i] << '\n'; + BOOST_CHECK_CLOSE(rs[i], rs_opm[i], 100*reltol); + BOOST_CHECK_CLOSE(rs[i], rs_ecl[i], reltol_ecl); + BOOST_CHECK_CLOSE(rv[i], rv_opm[i], 100.*reltol); + BOOST_CHECK_CLOSE(rv[i], rv_ecl[i], reltol_ecl); + } +} + BOOST_AUTO_TEST_SUITE_END() From aef6205fb753f6efd0acd3cf39a5c157ceafdebb Mon Sep 17 00:00:00 2001 From: Tor Harald Sandve Date: Fri, 4 Apr 2014 12:15:49 +0200 Subject: [PATCH 70/81] Hack to set compi for producer. compi is set to [0 1 0] for all producers --- opm/core/wells/WellsManager.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 389b2ddd..d7ef2c8c 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -761,6 +761,11 @@ namespace Opm OPM_THROW(std::runtime_error, "Control mode type " << mode << " not present in well " << well_names[well_index]); } set_current_control(well_index, cpos, w_); + + // Set well component fraction in producers to 1 for all phases. This HACK make sure volrates are nonzero for all perforations. + double cf[3] = { 0, 1, 0}; + std::copy(cf, cf + phaseUsage.num_phases, w_->comp_frac + well_index*phaseUsage.num_phases); + } well_index++; } From e693fa8cf13c4c558edd0a74be720141a460b849 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Fri, 4 Apr 2014 14:29:34 +0200 Subject: [PATCH 71/81] EclipseWriter: write the summary file after each call to writeTimeStep() this allows to interrupt a simulation and inspect the summary file which was computed up to that point... --- opm/core/io/eclipse/EclipseWriter.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/opm/core/io/eclipse/EclipseWriter.cpp b/opm/core/io/eclipse/EclipseWriter.cpp index 88a599db..a12bc2bf 100644 --- a/opm/core/io/eclipse/EclipseWriter.cpp +++ b/opm/core/io/eclipse/EclipseWriter.cpp @@ -963,6 +963,9 @@ EclipseSummary::writeTimeStep (const SimulatorTimer& timer, const double value = (*v)->update (timer, wellState); ecl_sum_tstep_iset(*tstep, *(*v).get (), value); } + + // write the summary file to disk + ecl_sum_fwrite(*this); } /// Supported well types. Enumeration doesn't let us get all the members, From 4576917061a1bf041922d7cb151f39da080a8860 Mon Sep 17 00:00:00 2001 From: Tor Harald Sandve Date: Fri, 4 Apr 2014 16:06:19 +0200 Subject: [PATCH 72/81] Fix undersaturatd viscosity table Data is now correctly read from ununderdatTable instead of pvtgTable --- opm/core/props/pvt/SinglePvtLiveGas.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/core/props/pvt/SinglePvtLiveGas.cpp b/opm/core/props/pvt/SinglePvtLiveGas.cpp index 43debe7b..283e7030 100644 --- a/opm/core/props/pvt/SinglePvtLiveGas.cpp +++ b/opm/core/props/pvt/SinglePvtLiveGas.cpp @@ -99,7 +99,7 @@ namespace Opm undersat_gas_tables_[i].resize(3); undersat_gas_tables_[i][0] = undersatTable.getOilSolubilityColumn(); undersat_gas_tables_[i][1] = undersatTable.getGasFormationFactorColumn(); - undersat_gas_tables_[i][2] = pvtgTable.getOuterTable()->getGasViscosityColumn(); + undersat_gas_tables_[i][2] = undersatTable.getGasViscosityColumn(); } } From 67fffa09517d7c6fc2c192cfe2271bebb29ab453 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Fri, 4 Apr 2014 21:01:32 +0200 Subject: [PATCH 73/81] Fix matrix size and symmetry for parallel runs. --- tests/test_parallel_linearsolver.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/test_parallel_linearsolver.cpp b/tests/test_parallel_linearsolver.cpp index aa670d27..8fc3833d 100644 --- a/tests/test_parallel_linearsolver.cpp +++ b/tests/test_parallel_linearsolver.cpp @@ -116,10 +116,20 @@ std::shared_ptr create1DLaplacian(I& indexset, int N, int start, int e // therefore we setup the system such that // right hand side will equal the left hand side // of the linear system. + if(localRow>0) + { + mm->colIndex[nnz]=localRow-1; + mm->data[nnz++]=0; + } mm->colIndex[nnz]=localRow; mm->data[nnz++]=1.0; indexset.add(row, LocalIndex(localRow, GridAttributes::copy, true)); - mm->rowStart[localRow+1]=nnz; + if(localRowcolIndex[nnz]=localRow+1; + mm->data[nnz++]=0; + } + mm->rowStart[localRow+1]=nnz; continue; } @@ -218,7 +228,7 @@ void run_test(const Opm::parameter::ParameterGroup& param) std::fill(x.begin(), x.end(), 0.0); Opm::LinearSolverFactory ls(param); boost::any anyComm(comm); - ls.solve(N, mat->data.size(), &(mat->rowStart[0]), + ls.solve(b.size(), mat->data.size(), &(mat->rowStart[0]), &(mat->colIndex[0]), &(mat->data[0]), &(b[0]), &(x[0]), anyComm); } From 4d5a92e3ffd20df9054437c7c1bff86a5b35a41c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Fri, 4 Apr 2014 22:53:34 +0200 Subject: [PATCH 74/81] Add test data files needed for test_equil to the right cmake list. --- CMakeLists_files.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 692c62f7..5108ae68 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -187,7 +187,9 @@ list (APPEND TEST_DATA_FILES tests/capillary.DATA tests/capillary_overlap.DATA tests/deadfluids.DATA + tests/equil_livegas.DATA tests/equil_liveoil.DATA + tests/equil_rsvd_and_rvvd.DATA tests/wetgas.DATA tests/testBlackoilState1.DATA tests/testBlackoilState2.DATA From 498555fcdd3e09d914d87a211ee10aa3432ae223 Mon Sep 17 00:00:00 2001 From: osae Date: Sat, 5 Apr 2014 10:56:30 +0200 Subject: [PATCH 75/81] New parser; proper handling of ENPTVD/ENKRVD. --- .../satfunc/SaturationPropsFromDeck_impl.hpp | 68 ++++++------------- 1 file changed, 22 insertions(+), 46 deletions(-) diff --git a/opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp b/opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp index a6e699fa..7651c83d 100644 --- a/opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp +++ b/opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp @@ -888,8 +888,9 @@ namespace Opm bool hasENPTVD = newParserDeck->hasKeyword("ENPTVD"); bool hasENKRVD = newParserDeck->hasKeyword("ENKRVD"); int itab = 0; - std::vector > > table_dummy; - std::vector > >& table = table_dummy; + std::vector > param_col; + std::vector > depth_col; + std::vector col_names; // Active keyword assigned default values for each cell (in case of possible box-wise assignment) int phase_pos_aqua = phase_usage_.phase_pos[BlackoilPhases::Aqua]; @@ -955,22 +956,14 @@ namespace Opm OPM_THROW(std::runtime_error, " -- unknown keyword: '" << keyword << "'"); } if (!useKeyword && itab > 0) { - Opm::EnptvdTable enptvd(newParserDeck->getKeyword("ENPTVD")); - table.resize(1); // only one region supported so far - for (unsigned i = 0; i < table.size(); ++i) { - table[i].resize(9); - - for (int k = 0; k < enptvd.numRows(); ++k) { - table[i][0] = enptvd.getDepthColumn(); - table[i][1] = enptvd.getSwcoColumn(); - table[i][2] = enptvd.getSwcritColumn(); - table[i][3] = enptvd.getSwmaxColumn(); - table[i][4] = enptvd.getSgcoColumn(); - table[i][5] = enptvd.getSgcritColumn(); - table[i][6] = enptvd.getSgmaxColumn(); - table[i][7] = enptvd.getSowcritColumn(); - table[i][8] = enptvd.getSogcritColumn(); - } + int num_tables = newParserDeck->getKeyword("ENPTVD")->size(); + param_col.resize(num_tables); + depth_col.resize(num_tables); + col_names.resize(9); + for (int table_num=0; table_numgetKeyword("ENPTVD"), col_names, table_num); + depth_col[table_num] = enptvd.getColumn(0); // depth + param_col[table_num] = enptvd.getColumn(itab); // itab=[1-8]: swl swcr swu sgl sgcr sgu sowcr sogcr } } } else if ((keyword[0] == 'K' && (useKeyword || hasENKRVD)) || (keyword[1] == 'K' && useKeyword) ) { @@ -1026,21 +1019,14 @@ namespace Opm OPM_THROW(std::runtime_error, " -- unknown keyword: '" << keyword << "'"); } if (!useKeyword && itab > 0) { - Opm::EnkrvdTable enkrvd(newParserDeck->getKeyword("ENKRVD")); - table.resize(1); // only one region supported so far - for (unsigned i = 0; i < table.size(); ++i) { - table[i].resize(8); - - for (int k = 0; k < enkrvd.numRows(); ++k) { - table[i][0] = enkrvd.getDepthColumn(); - table[i][1] = enkrvd.getKrwmaxColumn(); - table[i][2] = enkrvd.getKrgmaxColumn(); - table[i][3] = enkrvd.getKromaxColumn(); - table[i][4] = enkrvd.getKrwcritColumn(); - table[i][5] = enkrvd.getKrgcritColumn(); - table[i][6] = enkrvd.getKrocritgColumn(); - table[i][7] = enkrvd.getKrocritwColumn(); - } + int num_tables = newParserDeck->getKeyword("ENKRVD")->size(); + param_col.resize(num_tables); + depth_col.resize(num_tables); + col_names.resize(8); + for (int table_num=0; table_numgetKeyword("ENKRVD"), col_names, table_num); + depth_col[table_num] = enkrvd.getColumn(0); // depth + param_col[table_num] = enkrvd.getColumn(itab); // itab=[1-7]: krw krg kro krwr krgr krorw krorg } } } @@ -1057,24 +1043,14 @@ namespace Opm scaleparam[c] = val[deck_pos]; } } else { - // TODO for new parser - /* - std::cout << "--- Scaling parameter '" << keyword << "' assigned via "; - if (keyword[0] == 'S') - newParserDeck.getENPTVD().write(std::cout); - else - newParserDeck.getENKRVD().write(std::cout); - */ const double* cc = grid.cell_centroids; const int dim = grid.dimensions; for (int cell = 0; cell < grid.number_of_cells; ++cell) { int jtab = cell_to_func_.empty() ? 0 : cell_to_func_[cell]; - if (table[itab][jtab][0] != -1.0) { - std::vector& depth = table[0][jtab]; - std::vector& val = table[itab][jtab]; + if (param_col[jtab][0] >= 0.0) { double zc = cc[dim*cell+dim-1]; - if (zc >= depth.front() && zc <= depth.back()) { //don't want extrap outside depth interval - scaleparam[cell] = linearInterpolation(depth, val, zc); + if (zc >= depth_col[jtab].front() && zc <= depth_col[jtab].back()) { //don't want extrap outside depth interval + scaleparam[cell] = linearInterpolation(depth_col[jtab], param_col[jtab], zc); } } } From 3d98f7a77ea38cb93a8aa4273479e112e79287ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Sun, 6 Apr 2014 23:33:43 +0200 Subject: [PATCH 76/81] Use preferred phase of producer well to set comp_fraction. This replaces the previous hack, that set comp_fraction to (0,1,0) always. --- opm/core/wells/WellsManager.cpp | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index d7ef2c8c..add4ada0 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -762,10 +762,29 @@ namespace Opm } set_current_control(well_index, cpos, w_); - // Set well component fraction in producers to 1 for all phases. This HACK make sure volrates are nonzero for all perforations. - double cf[3] = { 0, 1, 0}; - std::copy(cf, cf + phaseUsage.num_phases, w_->comp_frac + well_index*phaseUsage.num_phases); - + // Set well component fraction to match preferred phase for the well. + double cf[3] = { 0.0, 0.0, 0.0 }; + { + switch (well->getPreferredPhase()) { + case Phase::WATER: + if (!phaseUsage.phase_used[BlackoilPhases::Aqua]) { + OPM_THROW(std::runtime_error, "Water phase not used, yet found water-preferring well."); + } + cf[phaseUsage.phase_pos[BlackoilPhases::Aqua]] = 1.0; + break; + case Phase::OIL: + if (!phaseUsage.phase_used[BlackoilPhases::Liquid]) { + OPM_THROW(std::runtime_error, "Oil phase not used, yet found oil-preferring well."); + } + cf[phaseUsage.phase_pos[BlackoilPhases::Liquid]] = 1.0; + case Phase::GAS: + if (!phaseUsage.phase_used[BlackoilPhases::Vapour]) { + OPM_THROW(std::runtime_error, "Gas phase not used, yet found gas-preferring well."); + } + cf[phaseUsage.phase_pos[BlackoilPhases::Vapour]] = 1.0; + } + std::copy(cf, cf + phaseUsage.num_phases, w_->comp_frac + well_index*phaseUsage.num_phases); + } } well_index++; } From 59bdd5f31e4936bdd0d9b2ab3e8f9aa72c1c0cda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Sun, 6 Apr 2014 23:45:08 +0200 Subject: [PATCH 77/81] Changed documentation of Wells::comp_frac to match reality. --- opm/core/wells.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/opm/core/wells.h b/opm/core/wells.h index 05b6ce3e..1d6b7ef2 100644 --- a/opm/core/wells.h +++ b/opm/core/wells.h @@ -65,8 +65,10 @@ struct Wells /** * Component fractions for each well. Array of size * number_of_wells * number_of_phases. - * This is intended to be used for injection wells. For production wells - * the component fractions will vary and cannot be specified a priori. + * For injection wells, this gives the injected component mix. + * For production wells the component fractions of the wellbore + * will vary and cannot be specified a priori, the component mix + * given here should be considered a default or preferred mix. */ double *comp_frac; From a154887a2b8c9bdd29d144c23e6485de923a2114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Mon, 7 Apr 2014 18:37:55 +0200 Subject: [PATCH 78/81] Exclude "test_parallel_linearsolver" from non-MPI builds The "sources_hook" runs after "OpmFiles" in "OpmLibMain". Therefore, we must search the "tests_SOURCES" to exclude particular tests from the build. This fixes a build problem in the non-MPI case. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f310e854..50155d3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,8 +72,8 @@ macro (sources_hook) endif (NOT SuiteSparse_FOUND) if (NOT MPI_FOUND OR NOT DUNE_ISTL_FOUND) - list (REMOVE_ITEM TEST_SOURCE_FILES - tests/test_parallel_linearsolver.cpp + list (REMOVE_ITEM tests_SOURCES + ${PROJECT_SOURCE_DIR}/tests/test_parallel_linearsolver.cpp ) endif (NOT MPI_FOUND OR NOT DUNE_ISTL_FOUND) From 1fca44fb03ef289d29874bbda44bf8b7be51026e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Mon, 7 Apr 2014 19:28:06 +0200 Subject: [PATCH 79/81] Explicitly group conditions For some reason I was unable to produce correct criteria without inserting explicit parentheses. I don't know why that is, but better safe than sorry. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 50155d3d..e1b91625 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,11 +71,11 @@ macro (sources_hook) ) endif (NOT SuiteSparse_FOUND) - if (NOT MPI_FOUND OR NOT DUNE_ISTL_FOUND) + if ((NOT MPI_FOUND) OR (NOT DUNE_ISTL_FOUND)) list (REMOVE_ITEM tests_SOURCES ${PROJECT_SOURCE_DIR}/tests/test_parallel_linearsolver.cpp ) - endif (NOT MPI_FOUND OR NOT DUNE_ISTL_FOUND) + endif ((NOT MPI_FOUND) OR (NOT DUNE_ISTL_FOUND)) # we are not supposed to include the TinyXML test prog. regardless list (REMOVE_ITEM opm-core_SOURCES From e434000f4e5baf667921ede41e9398f74337fe96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Mon, 7 Apr 2014 19:32:18 +0200 Subject: [PATCH 80/81] Search for MPI support The build system depends on knowing if MPI is available. Explicitly search for MPI to honour that requirement. Don't rely on ISTL's transitive searching for the same. --- cmake/Modules/opm-core-prereqs.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmake/Modules/opm-core-prereqs.cmake b/cmake/Modules/opm-core-prereqs.cmake index b7cf3bf3..8249a64b 100644 --- a/cmake/Modules/opm-core-prereqs.cmake +++ b/cmake/Modules/opm-core-prereqs.cmake @@ -6,6 +6,7 @@ set (opm-core_CONFIG_VAR HAVE_ERT HAVE_SUITESPARSE_UMFPACK_H HAVE_DUNE_ISTL + HAVE_MPI ) # dependencies @@ -28,6 +29,8 @@ set (opm-core_DEPS "TinyXML" # Ensembles-based Reservoir Tools (ERT) "ERT" + # Look for MPI support + "MPI" # DUNE dependency "dune-common" "dune-istl" From ce687039b2428fff51ba5cf356d3886d51db3cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Mon, 7 Apr 2014 19:35:10 +0200 Subject: [PATCH 81/81] Don't reference OwnerOverlap* unless MPI is available The class OwnerOverlapCopyCommunication is not defined unless MPI is avilable. Therefore, we cannot reference the type unless we know that MPI is available in the current translation unit. --- opm/core/linalg/LinearSolverIstl.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/opm/core/linalg/LinearSolverIstl.cpp b/opm/core/linalg/LinearSolverIstl.cpp index c6760f46..9dedc2f9 100644 --- a/opm/core/linalg/LinearSolverIstl.cpp +++ b/opm/core/linalg/LinearSolverIstl.cpp @@ -225,10 +225,14 @@ namespace Opm break; case FastAMG: #if defined(HAS_DUNE_FAST_AMG) || DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3) + +#if HAVE_MPI if(std::is_same >::value) { OPM_THROW(std::runtime_error, "Trying to use sequential FastAMG solver for a parallel problem!"); } +#endif // HAVE_MPI + res = solveFastAMG(opA, x, b, sp, comm, linsolver_residual_tolerance_, maxit, linsolver_verbosity_, linsolver_prolongate_factor_); #else