From f6acccd39659795adc1b571072721aaa8a8689ef Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 6 Dec 2013 22:13:29 +0800 Subject: [PATCH 01/83] incompressible two phase fully implicit simulator. --- opm/polymer/fullyimplicit/AutoDiff.hpp | 302 +++++++++++ opm/polymer/fullyimplicit/AutoDiffBlock.hpp | 506 +++++++++++++++++ opm/polymer/fullyimplicit/AutoDiffHelpers.hpp | 513 ++++++++++++++++++ .../FullyImplicitTwoPhaseSolver.cpp | 449 +++++++++++++++ .../FullyImplicitTwoPhaseSolver.hpp | 91 ++++ opm/polymer/fullyimplicit/IncompPropsAd.cpp | 5 + opm/polymer/fullyimplicit/IncompPropsAd.hpp | 59 ++ .../fullyimplicit/IncompPropsAdBasic.cpp | 172 ++++++ .../fullyimplicit/IncompPropsAdBasic.hpp | 54 ++ .../fullyimplicit/IncompPropsAdFromDeck.cpp | 136 +++++ .../fullyimplicit/IncompPropsAdFromDeck.hpp | 57 ++ .../fullyimplicit/IncompPropsAdInterface.cpp | 6 + .../fullyimplicit/IncompPropsAdInterface.hpp | 89 +++ opm/polymer/fullyimplicit/PolymerPropsAd.cpp | 216 ++++++++ opm/polymer/fullyimplicit/PolymerPropsAd.hpp | 89 +++ .../SimulatorFullyImplicitTwophase.cpp | 261 +++++++++ .../SimulatorFullyImplicitTwophase.hpp | 85 +++ 17 files changed, 3090 insertions(+) create mode 100644 opm/polymer/fullyimplicit/AutoDiff.hpp create mode 100644 opm/polymer/fullyimplicit/AutoDiffBlock.hpp create mode 100644 opm/polymer/fullyimplicit/AutoDiffHelpers.hpp create mode 100644 opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp create mode 100644 opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.hpp create mode 100644 opm/polymer/fullyimplicit/IncompPropsAd.cpp create mode 100644 opm/polymer/fullyimplicit/IncompPropsAd.hpp create mode 100644 opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp create mode 100644 opm/polymer/fullyimplicit/IncompPropsAdBasic.hpp create mode 100644 opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp create mode 100644 opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp create mode 100644 opm/polymer/fullyimplicit/IncompPropsAdInterface.cpp create mode 100644 opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp create mode 100644 opm/polymer/fullyimplicit/PolymerPropsAd.cpp create mode 100644 opm/polymer/fullyimplicit/PolymerPropsAd.hpp create mode 100644 opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.cpp create mode 100644 opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.hpp diff --git a/opm/polymer/fullyimplicit/AutoDiff.hpp b/opm/polymer/fullyimplicit/AutoDiff.hpp new file mode 100644 index 000000000..a542abe89 --- /dev/null +++ b/opm/polymer/fullyimplicit/AutoDiff.hpp @@ -0,0 +1,302 @@ +/* + Copyright 2013 SINTEF ICT, Applied Mathematics. + Copyright 2013 Statoil ASA. + + This file is part of the Open Porous Media Project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#ifndef OPM_AUTODIFF_HPP_HEADER +#define OPM_AUTODIFF_HPP_HEADER + +#include + +namespace Opm +{ + + /// A simple class for forward-mode automatic differentiation. + /// + /// The class represents a single value and a single derivative. + /// Only basic arithmetic operators and a few functions are + /// implemented for it, it is mostly intended for simple + /// experimentation. + template + class AutoDiff + { + public: + /// Create an AutoDiff object representing a constant, that + /// is, its derivative is zero. + static AutoDiff + constant(const Scalar x) + { + return function(x, Scalar(0)); + } + + /// Create an AutoDiff object representing a primary variable, + /// that is, its derivative is one. + static AutoDiff + variable(const Scalar x) + { + return function(x, Scalar(1)); + } + + /// Create an AutoDiff object representing a function value + /// and its derivative. + static AutoDiff + function(const Scalar x, const Scalar dx) + { + return AutoDiff(x, dx); + } + + void + operator +=(const Scalar& rhs) + { + val_ += rhs; + } + + void + operator +=(const AutoDiff& rhs) + { + val_ += rhs.val_; + der_ += rhs.der_; + } + + void + operator -=(const Scalar& rhs) + { + val_ -= rhs; + } + + void + operator -=(const AutoDiff& rhs) + { + val_ -= rhs.val_; + der_ -= rhs.der_; + } + + void + operator *=(const Scalar& rhs) + { + val_ *= rhs; + der_ *= rhs; + } + + void + operator *=(const AutoDiff& rhs) + { + der_ = der_*rhs.val_ + val_*rhs.der_; + val_ *= rhs.val_; + } + + void + operator /=(const Scalar& rhs) + { + val_ /= rhs; + der_ /= rhs; + } + + void + operator /=(const AutoDiff& rhs) + { + der_ = (der_*rhs.val_ - val_*rhs.der_) / (rhs.val_ * rhs.val_); + val_ /= rhs.val_; + } + + template + Ostream& + print(Ostream& os) const + { + os << "(x,dx) = (" << val_ << ',' << der_ << ")"; + + return os; + } + + const Scalar val() const { return val_; } + const Scalar der() const { return der_; } + + private: + AutoDiff(const Scalar x, const Scalar dx) + : val_(x), der_(dx) + {} + + Scalar val_; + Scalar der_; + }; + + + template + Ostream& + operator<<(Ostream& os, const AutoDiff& fw) + { + return fw.print(os); + } + + template + AutoDiff + operator +(const AutoDiff& lhs, + const AutoDiff& rhs) + { + AutoDiff ret = lhs; + ret += rhs; + + return ret; + } + + template + AutoDiff + operator +(const T lhs, + const AutoDiff& rhs) + { + AutoDiff ret = rhs; + ret += Scalar(lhs); + + return ret; + } + + template + AutoDiff + operator +(const AutoDiff& lhs, + const T rhs) + { + AutoDiff ret = lhs; + ret += Scalar(rhs); + + return ret; + } + + template + AutoDiff + operator -(const AutoDiff& lhs, + const AutoDiff& rhs) + { + AutoDiff ret = lhs; + ret -= rhs; + + return ret; + } + + template + AutoDiff + operator -(const T lhs, + const AutoDiff& rhs) + { + return AutoDiff::function(Scalar(lhs) - rhs.val(), -rhs.der()); + } + + template + AutoDiff + operator -(const AutoDiff& lhs, + const T rhs) + { + AutoDiff ret = lhs; + ret -= Scalar(rhs); + + return ret; + } + + template + AutoDiff + operator *(const AutoDiff& lhs, + const AutoDiff& rhs) + { + AutoDiff ret = lhs; + ret *= rhs; + + return ret; + } + + template + AutoDiff + operator *(const T lhs, + const AutoDiff& rhs) + { + AutoDiff ret = rhs; + ret *= Scalar(lhs); + + return ret; + } + + template + AutoDiff + operator *(const AutoDiff& lhs, + const T rhs) + { + AutoDiff ret = lhs; + ret *= Scalar(rhs); + + return ret; + } + + template + AutoDiff + operator /(const AutoDiff& lhs, + const AutoDiff& rhs) + { + AutoDiff ret = lhs; + ret /= rhs; + + return ret; + } + + template + AutoDiff + operator /(const T lhs, + const AutoDiff& rhs) + { + Scalar a = Scalar(lhs) / rhs.val(); + Scalar b = -Scalar(lhs) / (rhs.val() * rhs.val()); + + return AutoDiff::function(a, b); + } + + template + AutoDiff + operator /(const AutoDiff& lhs, + const T rhs) + { + Scalar a = lhs.val() / Scalar(rhs); + Scalar b = lhs.der() / Scalar(rhs); + + return AutoDiff::function(a, b); + } + + template + AutoDiff + cos(const AutoDiff& x) + { + Scalar a = std::cos(x.val()); + Scalar b = -std::sin(x.val()) * x.der(); + + return AutoDiff::function(a, b); + } + + template + AutoDiff + sqrt(const AutoDiff& x) + { + Scalar a = std::sqrt(x.val()); + Scalar b = (Scalar(1.0) / (Scalar(2.0) * a)) * x.der(); + + return AutoDiff::function(a, b); + } + +} // namespace Opm + +namespace std { + using Opm::cos; + using Opm::sqrt; +} + +#endif /* OPM_AUTODIFF_HPP_HEADER */ diff --git a/opm/polymer/fullyimplicit/AutoDiffBlock.hpp b/opm/polymer/fullyimplicit/AutoDiffBlock.hpp new file mode 100644 index 000000000..927927826 --- /dev/null +++ b/opm/polymer/fullyimplicit/AutoDiffBlock.hpp @@ -0,0 +1,506 @@ +/* + Copyright 2013 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_AUTODIFFBLOCK_HEADER_INCLUDED +#define OPM_AUTODIFFBLOCK_HEADER_INCLUDED + +#include +#include +#include +#include + +namespace Opm +{ + + /// A class for forward-mode automatic differentiation with vector + /// values and sparse jacobian matrices. + /// + /// The class contains a (column) vector of values and multiple + /// sparse matrices representing its partial derivatives. Each + /// such matrix has a number of rows equal to the number of rows + /// in the value vector, and a number of columns equal to the + /// number of discrete variables we want to compute the + /// derivatives with respect to. The reason to have multiple such + /// jacobians instead of just one is to allow simpler grouping of + /// variables, making it easier to implement various + /// preconditioning schemes. Only basic arithmetic operators are + /// implemented for this class, reflecting our needs so far. + /// + /// The class is built on the Eigen library, using an Eigen array + /// type to contain the values and Eigen sparse matrices for the + /// jacobians. The overloaded operators are intended to behave in + /// a similar way to Eigen arrays, meaning for example that the * + /// operator is elementwise multiplication. The only exception is + /// multiplication with a sparse matrix from the left, which is + /// treated as an Eigen matrix operation. + /// + /// There are no public constructors, instead we use the Named + /// Constructor pattern. In general, one needs to know which + /// variables one wants to compute the derivatives with respect to + /// before constructing an AutoDiffBlock. Some of the constructors + /// require you to pass a block pattern. This should be a vector + /// containing the number of columns you want for each jacobian + /// matrix. + /// + /// For example: you want the derivatives with respect to three + /// different variables p, r and s. Assuming that there are 10 + /// elements in p, and 20 in each of r and s, the block pattern is + /// { 10, 20, 20 }. When creating the variables p, r and s in your + /// program you have two options: + /// - Use the variable() constructor three times, passing the + /// index (0 for p, 1 for r and 2 for s), initial value of + /// each variable and the block pattern. + /// - Use the variables() constructor passing only the initial + /// values of each variable. The block pattern will be + /// inferred from the size of the initial value vectors. + /// This is usually the simplest option if you have multiple + /// variables. Note that this constructor returns a vector + /// of all three variables, so you need to use index access + /// (operator[]) to get the individual variables (that is p, + /// r and s). + /// + /// After this, the r variable for example will have a size() of + /// 20 and three jacobian matrices. The first is a 20 by 10 zero + /// matrix, the second is a 20 by 20 identity matrix, and the + /// third is a 20 by 20 zero matrix. + template + class AutoDiffBlock + { + public: + /// Underlying type for values. + typedef Eigen::Array V; + /// Underlying type for jacobians. + typedef Eigen::SparseMatrix M; + + /// Construct an empty AutoDiffBlock. + static AutoDiffBlock null() + { + V val; + std::vector jac; + return AutoDiffBlock(val, jac); + } + + /// Create an AutoDiffBlock representing a constant. + /// \param[in] val values + static AutoDiffBlock constant(const V& val) + { + return AutoDiffBlock(val); + } + + /// Create an AutoDiffBlock representing a constant. + /// This variant requires specifying the block sizes used + /// for the Jacobians even though the Jacobian matrices + /// themselves will be zero. + /// \param[in] val values + /// \param[in] blocksizes block pattern + static AutoDiffBlock constant(const V& val, const std::vector& blocksizes) + { + std::vector jac; + const int num_elem = val.size(); + const int num_blocks = blocksizes.size(); + // For constants, all jacobian blocks are zero. + jac.resize(num_blocks); + for (int i = 0; i < num_blocks; ++i) { + jac[i] = M(num_elem, blocksizes[i]); + } + return AutoDiffBlock(val, jac); + } + + /// Create an AutoDiffBlock representing a single variable block. + /// \param[in] index index of the variable you are constructing + /// \param[in] val values + /// \param[in] blocksizes block pattern + /// The resulting object will have size() equal to block_pattern[index]. + /// Its jacobians will all be zero, except for derivative()[index], which + /// will be an identity matrix. + static AutoDiffBlock variable(const int index, const V& val, const std::vector& blocksizes) + { + std::vector jac; + const int num_elem = val.size(); + const int num_blocks = blocksizes.size(); + // First, set all jacobian blocks to zero... + jac.resize(num_blocks); + for (int i = 0; i < num_blocks; ++i) { + jac[i] = M(num_elem, blocksizes[i]); + } + // ... then set the one corrresponding to this variable to identity. + assert(blocksizes[index] == num_elem); + jac[index].reserve(Eigen::VectorXi::Constant(val.size(), 1)); + for (typename M::Index row = 0; row < val.size(); ++row) { + jac[index].insert(row, row) = Scalar(1.0); + } + return AutoDiffBlock(val, jac); + } + + /// Create an AutoDiffBlock by directly specifying values and jacobians. + /// \param[in] val values + /// \param[in] jac vector of jacobians + static AutoDiffBlock function(const V& val, const std::vector& jac) + { + return AutoDiffBlock(val, jac); + } + + /// Construct a set of primary variables, each initialized to + /// a given vector. + static std::vector variables(const std::vector& initial_values) + { + const int num_vars = initial_values.size(); + std::vector bpat; + for (int v = 0; v < num_vars; ++v) { + bpat.push_back(initial_values[v].size()); + } + std::vector vars; + for (int v = 0; v < num_vars; ++v) { + vars.emplace_back(variable(v, initial_values[v], bpat)); + } + return vars; + } + + /// Elementwise operator += + AutoDiffBlock& operator+=(const AutoDiffBlock& rhs) + { + if (jac_.empty()) { + jac_ = rhs.jac_; + } else if (!rhs.jac_.empty()) { + assert (numBlocks() == rhs.numBlocks()); + assert (value().size() == rhs.value().size()); + + const int num_blocks = numBlocks(); + for (int block = 0; block < num_blocks; ++block) { + assert(jac_[block].rows() == rhs.jac_[block].rows()); + assert(jac_[block].cols() == rhs.jac_[block].cols()); + jac_[block] += rhs.jac_[block]; + } + } + + val_ += rhs.val_; + + return *this; + } + + /// Elementwise operator + + AutoDiffBlock operator+(const AutoDiffBlock& rhs) const + { + if (jac_.empty() && rhs.jac_.empty()) { + return constant(val_ + rhs.val_); + } + if (jac_.empty()) { + return val_ + rhs; + } + if (rhs.jac_.empty()) { + return *this + rhs.val_; + } + std::vector jac = jac_; + assert(numBlocks() == rhs.numBlocks()); + int num_blocks = numBlocks(); + for (int block = 0; block < num_blocks; ++block) { + assert(jac[block].rows() == rhs.jac_[block].rows()); + assert(jac[block].cols() == rhs.jac_[block].cols()); + jac[block] += rhs.jac_[block]; + } + return function(val_ + rhs.val_, jac); + } + + /// Elementwise operator - + AutoDiffBlock operator-(const AutoDiffBlock& rhs) const + { + if (jac_.empty() && rhs.jac_.empty()) { + return constant(val_ - rhs.val_); + } + if (jac_.empty()) { + return val_ - rhs; + } + if (rhs.jac_.empty()) { + return *this - rhs.val_; + } + std::vector jac = jac_; + assert(numBlocks() == rhs.numBlocks()); + int num_blocks = numBlocks(); + for (int block = 0; block < num_blocks; ++block) { + assert(jac[block].rows() == rhs.jac_[block].rows()); + assert(jac[block].cols() == rhs.jac_[block].cols()); + jac[block] -= rhs.jac_[block]; + } + return function(val_ - rhs.val_, jac); + } + + /// Elementwise operator * + AutoDiffBlock operator*(const AutoDiffBlock& rhs) const + { + if (jac_.empty() && rhs.jac_.empty()) { + return constant(val_ * rhs.val_); + } + if (jac_.empty()) { + return val_ * rhs; + } + if (rhs.jac_.empty()) { + return *this * rhs.val_; + } + int num_blocks = numBlocks(); + std::vector jac(num_blocks); + assert(numBlocks() == rhs.numBlocks()); + typedef Eigen::DiagonalMatrix D; + D D1 = val_.matrix().asDiagonal(); + D D2 = rhs.val_.matrix().asDiagonal(); + for (int block = 0; block < num_blocks; ++block) { + assert(jac_[block].rows() == rhs.jac_[block].rows()); + assert(jac_[block].cols() == rhs.jac_[block].cols()); + jac[block] = D2*jac_[block] + D1*rhs.jac_[block]; + } + return function(val_ * rhs.val_, jac); + } + + /// Elementwise operator / + AutoDiffBlock operator/(const AutoDiffBlock& rhs) const + { + if (jac_.empty() && rhs.jac_.empty()) { + return constant(val_ / rhs.val_); + } + if (jac_.empty()) { + return val_ / rhs; + } + if (rhs.jac_.empty()) { + return *this / rhs.val_; + } + int num_blocks = numBlocks(); + std::vector jac(num_blocks); + assert(numBlocks() == rhs.numBlocks()); + typedef Eigen::DiagonalMatrix D; + D D1 = val_.matrix().asDiagonal(); + D D2 = rhs.val_.matrix().asDiagonal(); + D D3 = (1.0/(rhs.val_*rhs.val_)).matrix().asDiagonal(); + for (int block = 0; block < num_blocks; ++block) { + assert(jac_[block].rows() == rhs.jac_[block].rows()); + assert(jac_[block].cols() == rhs.jac_[block].cols()); + jac[block] = D3 * (D2*jac_[block] - D1*rhs.jac_[block]); + } + return function(val_ / rhs.val_, jac); + } + + /// I/O. + template + Ostream& + print(Ostream& os) const + { + int num_blocks = jac_.size(); + os << "Value =\n" << val_ << "\n\nJacobian =\n"; + for (int i = 0; i < num_blocks; ++i) { + os << "Sub Jacobian #" << i << '\n' << jac_[i] << "\n"; + } + return os; + } + + /// Number of elements + int size() const + { + return val_.size(); + } + + /// Number of Jacobian blocks. + int numBlocks() const + { + return jac_.size(); + } + + /// Sizes (number of columns) of Jacobian blocks. + std::vector blockPattern() const + { + const int nb = numBlocks(); + std::vector bp(nb); + for (int block = 0; block < nb; ++block) { + bp[block] = jac_[block].cols(); + } + return bp; + } + + /// Function value. + const V& value() const + { + return val_; + } + + /// Function derivatives. + const std::vector& derivative() const + { + return jac_; + } + + private: + AutoDiffBlock(const V& val) + : val_(val) + { + } + + AutoDiffBlock(const V& val, + const std::vector& jac) + : val_(val), jac_(jac) + { +#ifndef NDEBUG + const int num_elem = val_.size(); + const int num_blocks = jac_.size(); + for (int block = 0; block < num_blocks; ++block) { + assert(num_elem == jac_[block].rows()); + } +#endif + } + + V val_; + std::vector jac_; + }; + + + // --------- Free functions and operators for AutoDiffBlock --------- + + /// Stream output. + template + Ostream& + operator<<(Ostream& os, const AutoDiffBlock& fw) + { + return fw.print(os); + } + + + /// Multiply with sparse matrix from the left. + template + AutoDiffBlock operator*(const typename AutoDiffBlock::M& lhs, + const AutoDiffBlock& rhs) + { + int num_blocks = rhs.numBlocks(); + std::vector::M> jac(num_blocks); + assert(lhs.cols() == rhs.value().rows()); + for (int block = 0; block < num_blocks; ++block) { + jac[block] = lhs*rhs.derivative()[block]; + } + typename AutoDiffBlock::V val = lhs*rhs.value().matrix(); + return AutoDiffBlock::function(val, jac); + } + + + /// Elementwise multiplication with constant on the left. + template + AutoDiffBlock operator*(const typename AutoDiffBlock::V& lhs, + const AutoDiffBlock& rhs) + { + return AutoDiffBlock::constant(lhs, rhs.blockPattern()) * rhs; + } + + + /// Elementwise multiplication with constant on the right. + template + AutoDiffBlock operator*(const AutoDiffBlock& lhs, + const typename AutoDiffBlock::V& rhs) + { + return rhs * lhs; // Commutative operation. + } + + + /// Elementwise addition with constant on the left. + template + AutoDiffBlock operator+(const typename AutoDiffBlock::V& lhs, + const AutoDiffBlock& rhs) + { + return AutoDiffBlock::constant(lhs, rhs.blockPattern()) + rhs; + } + + + /// Elementwise addition with constant on the right. + template + AutoDiffBlock operator+(const AutoDiffBlock& lhs, + const typename AutoDiffBlock::V& rhs) + { + return rhs + lhs; // Commutative operation. + } + + + /// Elementwise subtraction with constant on the left. + template + AutoDiffBlock operator-(const typename AutoDiffBlock::V& lhs, + const AutoDiffBlock& rhs) + { + return AutoDiffBlock::constant(lhs, rhs.blockPattern()) - rhs; + } + + + /// Elementwise subtraction with constant on the right. + template + AutoDiffBlock operator-(const AutoDiffBlock& lhs, + const typename AutoDiffBlock::V& rhs) + { + return lhs - AutoDiffBlock::constant(rhs, lhs.blockPattern()); + } + + + /// Elementwise division with constant on the left. + template + AutoDiffBlock operator/(const typename AutoDiffBlock::V& lhs, + const AutoDiffBlock& rhs) + { + return AutoDiffBlock::constant(lhs, rhs.blockPattern()) / rhs; + } + + + /// Elementwise division with constant on the right. + template + AutoDiffBlock operator/(const AutoDiffBlock& lhs, + const typename AutoDiffBlock::V& rhs) + { + return lhs / AutoDiffBlock::constant(rhs, lhs.blockPattern()); + } + + + /** + * @brief Operator for multiplication with a scalar on the right-hand side + * + * @param lhs The left-hand side AD forward block + * @param rhs The scalar to multiply with + * @return The product + */ + template + AutoDiffBlock operator*(const AutoDiffBlock& lhs, + const Scalar& rhs) + { + std::vector< typename AutoDiffBlock::M > jac; + jac.reserve( lhs.numBlocks() ); + for (int block=0; block::function( lhs.value() * rhs, jac ); + } + + + /** + * @brief Operator for multiplication with a scalar on the left-hand side + * + * @param lhs The scalar to multiply with + * @param rhs The right-hand side AD forward block + * @return The product + */ + template + AutoDiffBlock operator*(const Scalar& lhs, + const AutoDiffBlock& rhs) + { + return rhs * lhs; // Commutative operation. + } + + +} // namespace Opm + + + +#endif // OPM_AUTODIFFBLOCK_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/AutoDiffHelpers.hpp b/opm/polymer/fullyimplicit/AutoDiffHelpers.hpp new file mode 100644 index 000000000..9d26a6d36 --- /dev/null +++ b/opm/polymer/fullyimplicit/AutoDiffHelpers.hpp @@ -0,0 +1,513 @@ +/* + Copyright 2013 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_AUTODIFFHELPERS_HEADER_INCLUDED +#define OPM_AUTODIFFHELPERS_HEADER_INCLUDED + +#include +#include +#include + +#include +#include + +namespace Opm +{ + +// -------------------- class HelperOps -------------------- + +/// Contains vectors and sparse matrices that represent subsets or +/// operations on (AD or regular) vectors of data. +struct HelperOps +{ + typedef AutoDiffBlock::M M; + typedef AutoDiffBlock::V V; + + /// A list of internal faces. + typedef Eigen::Array IFaces; + IFaces internal_faces; + + /// Extract for each internal face the difference of its adjacent cells' values (first - second). + M ngrad; + /// Extract for each face the difference of its adjacent cells' values (second - first). + M grad; + /// Extract for each face the average of its adjacent cells' values. + M caver; + /// Extract for each cell the sum of its adjacent interior faces' (signed) values. + M div; + /// Extract for each face the difference of its adjacent cells' values (first - second). + /// For boundary faces, one of the entries per row (corresponding to the outside) is zero. + M fullngrad; + /// Extract for each cell the sum of all its adjacent faces' (signed) values. + M fulldiv; + + /// Constructs all helper vectors and matrices. + HelperOps(const UnstructuredGrid& grid) + { + const int nc = grid.number_of_cells; + const int nf = grid.number_of_faces; + // Define some neighbourhood-derived helper arrays. + typedef Eigen::Array OneColBool; + typedef Eigen::Array TwoColInt; + typedef Eigen::Array TwoColBool; + TwoColInt nb = Eigen::Map(grid.face_cells, nf, 2); + // std::cout << "nb = \n" << nb << std::endl; + TwoColBool nbib = nb >= 0; + OneColBool ifaces = nbib.rowwise().all(); + const int num_internal = ifaces.cast().sum(); + // std::cout << num_internal << " internal faces." << std::endl; + TwoColInt nbi(num_internal, 2); + internal_faces.resize(num_internal); + int fi = 0; + for (int f = 0; f < nf; ++f) { + if (ifaces[f]) { + internal_faces[fi] = f; + nbi.row(fi) = nb.row(f); + ++fi; + } + } + // std::cout << "nbi = \n" << nbi << std::endl; + // Create matrices. + ngrad.resize(num_internal, nc); + caver.resize(num_internal, nc); + typedef Eigen::Triplet Tri; + std::vector ngrad_tri; + std::vector caver_tri; + ngrad_tri.reserve(2*num_internal); + caver_tri.reserve(2*num_internal); + for (int i = 0; i < num_internal; ++i) { + ngrad_tri.emplace_back(i, nbi(i,0), 1.0); + ngrad_tri.emplace_back(i, nbi(i,1), -1.0); + caver_tri.emplace_back(i, nbi(i,0), 0.5); + caver_tri.emplace_back(i, nbi(i,1), 0.5); + } + ngrad.setFromTriplets(ngrad_tri.begin(), ngrad_tri.end()); + caver.setFromTriplets(caver_tri.begin(), caver_tri.end()); + grad = -ngrad; + div = ngrad.transpose(); + std::vector fullngrad_tri; + fullngrad_tri.reserve(2*nf); + for (int i = 0; i < nf; ++i) { + if (nb(i,0) >= 0) { + fullngrad_tri.emplace_back(i, nb(i,0), 1.0); + } + if (nb(i,1) >= 0) { + fullngrad_tri.emplace_back(i, nb(i,1), -1.0); + } + } + fullngrad.resize(nf, nc); + fullngrad.setFromTriplets(fullngrad_tri.begin(), fullngrad_tri.end()); + fulldiv = fullngrad.transpose(); + } +}; + +// -------------------- upwinding helper class -------------------- + + + /// Upwind selection in absence of counter-current flow (i.e., + /// without effects of gravity and/or capillary pressure). + template + class UpwindSelector { + public: + typedef AutoDiffBlock ADB; + + UpwindSelector(const UnstructuredGrid& g, + const HelperOps& h, + const typename ADB::V& ifaceflux) + { + typedef HelperOps::IFaces::Index IFIndex; + const IFIndex nif = h.internal_faces.size(); + assert(nif == ifaceflux.size()); + + // Define selector structure. + typedef typename Eigen::Triplet Triplet; + std::vector s; s.reserve(nif); + for (IFIndex iface = 0; iface < nif; ++iface) { + const int f = h.internal_faces[iface]; + const int c1 = g.face_cells[2*f + 0]; + const int c2 = g.face_cells[2*f + 1]; + + assert ((c1 >= 0) && (c2 >= 0)); + + // Select upwind cell. + const int c = (ifaceflux[iface] >= 0) ? c1 : c2; + + s.push_back(Triplet(iface, c, Scalar(1))); + } + + // Assemble explicit selector operator. + select_.resize(nif, g.number_of_cells); + select_.setFromTriplets(s.begin(), s.end()); + } + + /// Apply selector to multiple per-cell quantities. + std::vector + select(const std::vector& xc) const + { + // Absence of counter-current flow means that the same + // selector applies to all quantities, 'x', defined per + // cell. + std::vector xf; xf.reserve(xc.size()); + for (typename std::vector::const_iterator + b = xc.begin(), e = xc.end(); b != e; ++b) + { + xf.push_back(select_ * (*b)); + } + + return xf; + } + + /// Apply selector to single per-cell ADB quantity. + ADB select(const ADB& xc) const + { + return select_*xc; + } + + /// Apply selector to single per-cell constant quantity. + typename ADB::V select(const typename ADB::V& xc) const + { + return (select_*xc.matrix()).array(); + } + + private: + typename ADB::M select_; + }; + + + +namespace { + + template + Eigen::SparseMatrix + constructSubsetSparseMatrix(const int full_size, const IntVec& indices) + { + typedef Eigen::Triplet Tri; + const int subset_size = indices.size(); + std::vector triplets(subset_size); + for (int i = 0; i < subset_size; ++i) { + triplets[i] = Tri(i, indices[i], 1); + } + Eigen::SparseMatrix sub(subset_size, full_size); + sub.setFromTriplets(triplets.begin(), triplets.end()); + return sub; + } + + template + Eigen::SparseMatrix + constructSupersetSparseMatrix(const int full_size, const IntVec& indices) + { + return constructSubsetSparseMatrix(full_size, indices).transpose(); + } + +} // anon namespace + + +/// Returns x(indices). +template +AutoDiffBlock +subset(const AutoDiffBlock& x, + const IntVec& indices) +{ + return constructSubsetSparseMatrix(x.value().size(), indices) * x; +} + + + +/// Returns x(indices). +template +Eigen::Array +subset(const Eigen::Array& x, + const IntVec& indices) +{ + return (constructSubsetSparseMatrix(x.size(), indices) * x.matrix()).array(); +} + + + +/// Returns v where v(indices) == x, v(!indices) == 0 and v.size() == n. +template +AutoDiffBlock +superset(const AutoDiffBlock& x, + const IntVec& indices, + const int n) +{ + return constructSupersetSparseMatrix(n, indices) * x; +} + + + +/// Returns v where v(indices) == x, v(!indices) == 0 and v.size() == n. +template +Eigen::Array +superset(const Eigen::Array& x, + const IntVec& indices, + const int n) +{ + return constructSupersetSparseMatrix(n, indices) * x.matrix(); +} + + + +/// Construct square sparse matrix with the +/// elements of d on the diagonal. +/// Need to mark this as inline since it is defined in a header and not a template. +inline +AutoDiffBlock::M +spdiag(const AutoDiffBlock::V& d) +{ + typedef AutoDiffBlock::M M; + + const int n = d.size(); + M mat(n, n); + mat.reserve(Eigen::ArrayXi::Ones(n, 1)); + for (M::Index i = 0; i < n; ++i) { + mat.insert(i, i) = d[i]; + } + + return mat; +} + + + + + /// Selection. Choose first of two elements if selection basis element is nonnegative. + template + class Selector { + public: + typedef AutoDiffBlock ADB; + + Selector(const typename ADB::V& selection_basis) + { + // Define selector structure. + const int n = selection_basis.size(); + // Over-reserving so we do not have to count. + left_elems_.reserve(n); + right_elems_.reserve(n); + for (int i = 0; i < n; ++i) { + if (selection_basis[i] < 0.0) { + right_elems_.push_back(i); + } else { + left_elems_.push_back(i); + } + } + } + + /// Apply selector to ADB quantities. + ADB select(const ADB& x1, const ADB& x2) const + { + if (right_elems_.empty()) { + return x1; + } else if (left_elems_.empty()) { + return x2; + } else { + return superset(subset(x1, left_elems_), left_elems_, x1.size()) + + superset(subset(x2, right_elems_), right_elems_, x2.size()); + } + } + + /// Apply selector to ADB quantities. + typename ADB::V select(const typename ADB::V& x1, const typename ADB::V& x2) const + { + if (right_elems_.empty()) { + return x1; + } else if (left_elems_.empty()) { + return x2; + } else { + return superset(subset(x1, left_elems_), left_elems_, x1.size()) + + superset(subset(x2, right_elems_), right_elems_, x2.size()); + } + } + + private: + std::vector left_elems_; + std::vector right_elems_; + }; + + + + +/// Returns the input expression, but with all Jacobians collapsed to one. +inline +AutoDiffBlock +collapseJacs(const AutoDiffBlock& x) +{ + typedef AutoDiffBlock ADB; + const int nb = x.numBlocks(); + typedef Eigen::Triplet Tri; + int nnz = 0; + for (int block = 0; block < nb; ++block) { + nnz += x.derivative()[block].nonZeros(); + } + std::vector t; + t.reserve(nnz); + int block_col_start = 0; + for (int block = 0; block < nb; ++block) { + const ADB::M& jac = x.derivative()[block]; + for (ADB::M::Index k = 0; k < jac.outerSize(); ++k) { + for (ADB::M::InnerIterator i(jac, k); i ; ++i) { + t.push_back(Tri(i.row(), + i.col() + block_col_start, + i.value())); + } + } + block_col_start += jac.cols(); + } + // Build final jacobian. + std::vector jacs(1); + jacs[0].resize(x.size(), block_col_start); + jacs[0].setFromTriplets(t.begin(), t.end()); + return ADB::function(x.value(), jacs); +} + + + + +/// Returns the vertical concatenation [ x; y ] of the inputs. +inline +AutoDiffBlock +vertcat(const AutoDiffBlock& x, + const AutoDiffBlock& y) +{ + const int nx = x.size(); + const int ny = y.size(); + const int n = nx + ny; + std::vector xind(nx); + for (int i = 0; i < nx; ++i) { + xind[i] = i; + } + std::vector yind(ny); + for (int i = 0; i < ny; ++i) { + yind[i] = nx + i; + } + return superset(x, xind, n) + superset(y, yind, n); +} + + + + + +class Span +{ +public: + explicit Span(const int num) + : num_(num), + stride_(1), + start_(0) + { + } + Span(const int num, const int stride, const int start) + : num_(num), + stride_(stride), + start_(start) + { + } + int operator[](const int i) const + { + assert(i >= 0 && i < num_); + return start_ + i*stride_; + } + int size() const + { + return num_; + } + + + class SpanIterator + { + public: + SpanIterator(const Span* span, const int index) + : span_(span), + index_(index) + { + } + SpanIterator operator++() + { + ++index_; + return *this; + } + SpanIterator operator++(int) + { + SpanIterator before_increment(*this); + ++index_; + return before_increment; + } + bool operator<(const SpanIterator& rhs) const + { + assert(span_ == rhs.span_); + return index_ < rhs.index_; + } + bool operator==(const SpanIterator& rhs) const + { + assert(span_ == rhs.span_); + return index_ == rhs.index_; + } + bool operator!=(const SpanIterator& rhs) const + { + assert(span_ == rhs.span_); + return index_ != rhs.index_; + } + int operator*() + { + return (*span_)[index_]; + } + private: + const Span* span_; + int index_; + }; + + typedef SpanIterator iterator; + typedef SpanIterator const_iterator; + + SpanIterator begin() const + { + return SpanIterator(this, 0); + } + + SpanIterator end() const + { + return SpanIterator(this, num_); + } + + bool operator==(const Span& rhs) + { + return num_ == rhs.num_ && start_ == rhs.start_ && stride_ == rhs.stride_; + } + +private: + const int num_; + const int stride_; + const int start_; +}; + + + +/// Return a vector of (-1.0, 0.0 or 1.0), depending on sign per element. +inline Eigen::ArrayXd sign (const Eigen::ArrayXd& x) +{ + const int n = x.size(); + Eigen::ArrayXd retval(n); + for (int i = 0; i < n; ++i) { + retval[i] = x[i] < 0.0 ? -1.0 : (x[i] > 0.0 ? 1.0 : 0.0); + } + return retval; +} + +} // namespace Opm + +#endif // OPM_AUTODIFFHELPERS_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp new file mode 100644 index 000000000..d70ab1909 --- /dev/null +++ b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp @@ -0,0 +1,449 @@ +/**/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +namespace Opm { + +namespace { + + std::vector + buildAllCells(const int nc) + { + std::vector all_cells(nc); + for (int c = 0; c < nc; ++c) { all_cells[c] = c; } + + return all_cells; + } + struct Chop01 { + double operator()(double x) const { return std::max(std::min(x, 1.0), 0.0); } + }; + +}//anonymous namespace + + + + + +typedef AutoDiffBlock ADB; +typedef ADB::V V; +typedef ADB::M M; +typedef Eigen::Array DataBlock; + + + + + + FullyImplicitTwoPhaseSolver:: + FullyImplicitTwoPhaseSolver(const UnstructuredGrid& grid, + const IncompPropsAdInterface& fluid, + const LinearSolverInterface& linsolver) + : grid_ (grid) + , fluid_(fluid) + , linsolver_(linsolver) + , cells_ (buildAllCells(grid.number_of_cells)) + , ops_(grid) + , residual_(std::vector(fluid.numPhases(), ADB::null())) + { + } + + + + + + void + FullyImplicitTwoPhaseSolver:: + step(const double dt, + TwophaseState& x, + const std::vector& src) + { + + V pvol(grid_.number_of_cells); + // Pore volume + const typename V::Index nc = grid_.number_of_cells; + V rho = V::Constant(pvol.size(), 1, *fluid_.porosity()); + std::transform(grid_.cell_volumes, grid_.cell_volumes + nc, + rho.data(), pvol.data(), + std::multiplies()); + + const V pvdt = pvol / dt; + + const SolutionState old_state = constantState(x); + const double atol = 1.0e-12; + const double rtol = 5.0e-8; + const int maxit = 15; + + assemble(pvdt, old_state, x, src); + + const double r0 = residualNorm(); + int it = 0; + std::cout << "\nIteration Residual\n" + << std::setw(9) << it << std::setprecision(9) + << std::setw(18) << r0 << std::endl; + bool resTooLarge = r0 > atol; + while (resTooLarge && (it < maxit)) { + const V dx = solveJacobianSystem(); + updateState(dx, x); + + assemble(pvdt, old_state, x, src); + + const double r = residualNorm(); + + resTooLarge = (r > atol) && (r > rtol*r0); + + it += 1; + std::cout << std::setw(9) << it << std::setprecision(9) + << std::setw(18) << r << std::endl; + } + + if (resTooLarge) { + std::cerr << "Failed to compute converged solution in " << it << " iterations. Ignoring!\n"; + // OPM_THROW(std::runtime_error, "Failed to compute converged solution in " << it << " iterations."); + } + } + + + + + + FullyImplicitTwoPhaseSolver::SolutionState::SolutionState(const int np) + : pressure ( ADB::null()) + , saturation (np, ADB::null()) + { + } + + + + + + FullyImplicitTwoPhaseSolver::SolutionState + FullyImplicitTwoPhaseSolver::constantState(const TwophaseState& x) + { + const int nc = grid_.number_of_cells; + const int np = x.numPhases(); + + SolutionState state(np); + + // Pressure. + assert (not x.pressure().empty()); + const V p = Eigen::Map(& x.pressure()[0], nc); + state.pressure = ADB::constant(p); + + // Saturation. + assert (not x.saturation().empty()); + const DataBlock s_all = Eigen::Map(& x.saturation()[0], nc, np); + for (int phase = 0; phase < np; ++phase) { + state.saturation[phase] = ADB::constant(s_all.col(phase)); + // state.saturation[1] = ADB::constant(s_all.col(1)); + } + return state; + } + + + + + + FullyImplicitTwoPhaseSolver::SolutionState + FullyImplicitTwoPhaseSolver::variableState(const TwophaseState& x) + { + const int nc = grid_.number_of_cells; + const int np = x.numPhases(); + + std::vector vars0; + vars0.reserve(np); + + // Initial pressure. + assert (not x.pressure().empty()); + const V p = Eigen::Map(& x.pressure()[0], nc); + vars0.push_back(p); + + // Initial saturation. + assert (not x.saturation().empty()); + const DataBlock s_all = Eigen::Map(& x.saturation()[0], nc, np); + const V sw = s_all.col(0); + vars0.push_back(sw); + + + std::vector vars = ADB::variables(vars0); + + SolutionState state(np); + + // Pressure. + int nextvar = 0; + state.pressure = vars[ nextvar++ ]; + + // Saturation. + const std::vector& bpat = vars[0].blockPattern(); + { + ADB so = ADB::constant(V::Ones(nc, 1), bpat); + ADB& sw = vars[ nextvar++ ]; + state.saturation[0] = sw; + so = so - sw; + state.saturation[1] = so; + } + + assert(nextvar == int(vars.size())); + + return state; + } + + + + + + void + FullyImplicitTwoPhaseSolver:: + assemble(const V& pvdt, + const SolutionState& old_state, + const TwophaseState& x , + const std::vector& src) + { + // Create the primary variables. + const SolutionState state = variableState(x); + + // -------- Mass balance equations -------- + const V trans = subset(transmissibility(), ops_.internal_faces); + const std::vector kr = computeRelPerm(state); + for (int phase = 0; phase < fluid_.numPhases(); ++phase) { + const ADB mflux = computeMassFlux(phase, trans, kr, state); + ADB source = accumSource(phase, kr, src); + residual_[phase] = + pvdt*(state.saturation[phase] - old_state.saturation[phase]) + + ops_.div*mflux - source; + } + + } + + + + + + + ADB + FullyImplicitTwoPhaseSolver::accumSource(const int phase, + const std::vector& kr, + const std::vector& src) const + { + //extract the source to out and in source. + std::vector outsrc; + std::vector insrc; + std::vector::const_iterator it; + for (it = src.begin(); it != src.end(); ++it) { + if (*it < 0) { + outsrc.push_back(*it); + insrc.push_back(0.0); + } else if (*it > 0) { + insrc.push_back(*it); + outsrc.push_back(0.0); + } else { + outsrc.emplace_back(0); + insrc.emplace_back(0); + } + } + const V source = Eigen::Map(& src[0], grid_.number_of_cells); + const V outSrc = Eigen::Map(& outsrc[0], grid_.number_of_cells); + const V inSrc = Eigen::Map(& insrc[0], grid_.number_of_cells); + + // compute the out-fracflow. + ADB f_out = computeFracFlow(phase, kr); + // compute the in-fracflow. + V f_in; + if (phase == 1) { + f_in = V::Zero(grid_.number_of_cells); + } else if (phase == 0) { + f_in = V::Ones(grid_.number_of_cells); + } + return f_out * outSrc + f_in * inSrc; + } + + + + + + ADB + FullyImplicitTwoPhaseSolver::computeFracFlow(int phase, + const std::vector& kr) const + { + const double* mus = fluid_.viscosity(); + ADB mob_phase = kr[phase] / V::Constant(kr[phase].size(), 1, mus[phase]); + ADB mob_wat = kr[0] / V::Constant(kr[0].size(), 1, mus[0]); + ADB mob_oil= kr[1] / V::Constant(kr[1].size(), 1, mus[1]); + ADB total_mob = mob_wat + mob_oil; + ADB f = mob_phase / total_mob; + + return f; + } + + + + + + V + FullyImplicitTwoPhaseSolver::solveJacobianSystem() const + { + const int np = fluid_.numPhases(); + if (np != 2) { + OPM_THROW(std::logic_error, "Only two-phase ok in FullyImplicitTwoPhaseSolver."); + } + ADB mass_res = collapseJacs(vertcat(residual_[0], residual_[1])); + const Eigen::SparseMatrix matr = mass_res.derivative()[0]; + V dx(V::Zero(mass_res.size())); + Opm::LinearSolverInterface::LinearSolverReport rep + = linsolver_.solve(matr.rows(), matr.nonZeros(), + matr.outerIndexPtr(), matr.innerIndexPtr(), matr.valuePtr(), + mass_res.value().data(), dx.data()); + if (!rep.converged) { + OPM_THROW(std::runtime_error, + "FullyImplicitBlackoilSolver::solveJacobianSystem(): " + "Linear solver convergence failure."); + } + return dx; + } + + + + + + void FullyImplicitTwoPhaseSolver::updateState(const V& dx, + TwophaseState& state) const + { + const int np = fluid_.numPhases(); + const int nc = grid_.number_of_cells; + const V null; + assert(null.size() == 0); + const V zero = V::Zero(nc); + const V one = V::Constant(nc, 1.0); + + // Extract parts of dx corresponding to each part. + const V dp = subset(dx, Span(nc)); + int varstart = nc; + const V dsw = subset(dx, Span(nc, 1, varstart)); + varstart += dsw.size(); + assert(varstart == dx.size()); + + // Pressure update. + const V p_old = Eigen::Map(&state.pressure()[0], nc); + const V p = p_old - dp; + std::copy(&p[0], &p[0] + nc, state.pressure().begin()); + + + // Saturation updates. + const double dsmax = 0.3; + const DataBlock s_old = Eigen::Map(& state.saturation()[0], nc, np); + V so = one; + const V sw_old = s_old.col(0); + const V dsw_limited = sign(dsw) * dsw.abs().min(dsmax); + const V sw = (sw_old - dsw_limited).unaryExpr(Chop01()); + so -= sw; + for (int c = 0; c < nc; ++c) { + state.saturation()[c*np] = sw[c]; + } + for (int c = 0; c < nc; ++c) { + state.saturation()[c*np + 1] = so[c]; + } + + } + + + + + + std::vector + FullyImplicitTwoPhaseSolver::computeRelPerm(const SolutionState& state) const + { + + const ADB sw = state.saturation[0]; + const ADB so = state.saturation[1]; + + return fluid_.relperm(sw, so, cells_); + } + + + + + + + + + + + ADB + FullyImplicitTwoPhaseSolver::computeMassFlux(const int phase , + const V& trans, + const std::vector& kr , + const SolutionState& state ) const + { +// const ADB tr_mult = transMult(state.pressure); + const double* mus = fluid_.viscosity(); + ADB mob = kr[phase] / V::Constant(kr[phase].size(), 1, mus[phase]); + + const ADB dp = ops_.ngrad * state.pressure; + const ADB head = trans * dp; + + UpwindSelector upwind(grid_, ops_, head.value()); + + return upwind.select(mob) * head; + } + + + + + + double + FullyImplicitTwoPhaseSolver::residualNorm() const + { + double r = 0; + for (std::vector::const_iterator + b = residual_.begin(), + e = residual_.end(); + b != e; ++b) + { + r = std::max(r, (*b).value().matrix().norm()); + } + + return r; + } + + + + + + V + FullyImplicitTwoPhaseSolver::transmissibility() const + { + const V::Index nc = grid_.number_of_cells; + V htrans(grid_.cell_facepos[nc]); + V trans(grid_.cell_facepos[nc]); + UnstructuredGrid* ug = const_cast(& grid_); + tpfa_htrans_compute(ug, fluid_.permeability(), htrans.data()); + tpfa_trans_compute (ug, htrans.data() , trans.data()); + + return trans; + } + + + + + + +}//namespace Opm diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.hpp new file mode 100644 index 000000000..d579cff6b --- /dev/null +++ b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.hpp @@ -0,0 +1,91 @@ +/**/ + +#ifndef OPM_FULLYIMPLICITTWOPHASESOLVER_HEADER_INCLUDED +#define OPM_FULLYIMPLICITTWOPHASESOLVER_HEADER_INCLUDED + +#include +#include +#include +#include + + +struct UnstructuredGrid; +namespace Opm { +// struct HelperOps; + class LinearSolverInterface; + class TwophaseState; + + + class FullyImplicitTwoPhaseSolver + { + public: + FullyImplicitTwoPhaseSolver(const UnstructuredGrid& grid, + const IncompPropsAdInterface& fluid, + const LinearSolverInterface& linsolver); + + void step(const double dt, + TwophaseState& state, + const std::vector& src); + private: + typedef AutoDiffBlock ADB; + typedef ADB::V V; + typedef ADB::M M; + typedef Eigen::Array DataBlock; + struct SolutionState { + SolutionState(const int np); + ADB pressure; + std::vector saturation; + }; + const UnstructuredGrid& grid_; + const IncompPropsAdInterface& fluid_; + const LinearSolverInterface& linsolver_; + const std::vector cells_; + HelperOps ops_; + std::vector residual_; + + + SolutionState + constantState(const TwophaseState& x); + SolutionState + variableState(const TwophaseState& x); + void + assemble(const V& pvdt, + const SolutionState& old_state, + const TwophaseState& x, + const std::vector& src); + V solveJacobianSystem() const; + void updateState(const V& dx, + TwophaseState& x) const; + std::vector + computeRelPerm(const SolutionState& state) const; + V + transmissibility() const; + ADB + computeFracFlow(int phase, + const std::vector& kr) const; + ADB + accumSource(const int phase, + const std::vector& kr, + const std::vector& src) const; + ADB + computeMassFlux(const int phase, + const V& trans, + const std::vector& kr, + const SolutionState& state) const; + double + residualNorm() const; + + ADB + rockPorosity(const ADB& p) const; + ADB + rockPermeability(const ADB& p) const; + const double + fluidDensity(const int phase) const; + ADB + transMult(const ADB& p) const; + }; +} // namespace Opm +#endif// OPM_FULLYIMPLICITTWOPHASESOLVER_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/IncompPropsAd.cpp b/opm/polymer/fullyimplicit/IncompPropsAd.cpp new file mode 100644 index 000000000..d98fb7c15 --- /dev/null +++ b/opm/polymer/fullyimplicit/IncompPropsAd.cpp @@ -0,0 +1,5 @@ +/* +Author : Liu Ming +Date : 2013-11-28 in Oslo +Email : miliu@statoil.com +*/ diff --git a/opm/polymer/fullyimplicit/IncompPropsAd.hpp b/opm/polymer/fullyimplicit/IncompPropsAd.hpp new file mode 100644 index 000000000..21bfa798e --- /dev/null +++ b/opm/polymer/fullyimplicit/IncompPropsAd.hpp @@ -0,0 +1,59 @@ +/* +Author : Liu Ming +Data : 2013-11-28 in Oslo +Email : miliu@statoil.com + +Properties for incompressible immiscible two-phase flow +*/ + +#ifndef OPM_INCOMPROPSAD_HEADER_INCLUDED +#define OPM_INCOMPROPSAD_HEADER_INCLUDED +#include +#include +#include +#include +#include + + +namespace Opm +{ + +// class BlackoilPhases; + + class IncompropsAd : public IncompPropertiesBasic + { + public: + IncomPropsAd(const parameter::ParameterGroup& param, + const int dim, + const int num_cells); + IncompPropsAd(const int num_phases, + const SaturationPropsBasic::RelPermFunc& relpermfunc, + const std::vector& rho, + const std::vector& mu, + const double porosity, + const double permeability, + const int dim, + const int num_cells); + + ~IncompPropsAd(); + + typedef AutoDiffBlock ADB; + typedef ADB::V V; + V relperm(const V& sw, + const V& so, + const std::vector& cells); + ADB relperm(const ADB& sw, + const ADB& so, + const std::vector& cells); + + V capPress(const V& sw, + const V& so, + const std::vector& cells); + ADB capPress(const ADB& sw, + const ADB& so, + const std::vector& cells); + + } +} + +#endif// OPM_INCOMPROPSAD_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp b/opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp new file mode 100644 index 000000000..a15cbcc30 --- /dev/null +++ b/opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp @@ -0,0 +1,172 @@ +/**/ + +#include +#include +#include +#include +#include +#include + +namespace Opm +{ + IncompPropsAdBasic::IncompPropsAdBasic(const parameter::ParameterGroup& param, + const int dim, + const int num_cells) + { + double poro = param.getDefault("porosity", 1.0); + using namespace Opm::unit; + using namespace Opm::prefix; + double perm = param.getDefault("permeability", 100) * milli * darcy; + rock_.init(dim, num_cells, poro, perm); + pvt_.init(param); + satprops_.init(param); + if (pvt_.numPhases() != satprops_.numPhases()) { + OPM_THROW(std::runtime_error, "IncompPropsAdBasic::IncompPropsAdBasic() - Inconsistent number of phases in pvt data (" + << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); + } + viscosity_.resize(pvt_.numPhases()); + pvt_.mu(1, 0, 0, &viscosity_[0]); + } + + + IncompPropsAdBasic::IncompPropsAdBasic(const int num_phases, + const SaturationPropsBasic::RelPermFunc& relpermfunc, + const std::vector& rho, + const std::vector& mu, + const double por, //porosity + const double perm, + const int dim, + const int num_cells) + { + rock_.init(dim, num_cells, por, perm); + pvt_.init(num_phases, rho, mu); + satprops_.init(num_phases, relpermfunc); + if (pvt_.numPhases() != satprops_.numPhases()) { + OPM_THROW(std::runtime_error, "IncompPropsAdBasic::IncompPropsAdBasic() - Inconsistent number of phases in pvt data (" + << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); + } + viscosity_.resize(pvt_.numPhases()); + pvt_.mu(1, 0, 0, &viscosity_[0]); + } + IncompPropsAdBasic::~IncompPropsAdBasic() + { + } + + /// \return D, the number of spatial dimensions. + int IncompPropsAdBasic::numDimensions() const + { + return rock_.numDimensions(); + } + + /// \return N, the number of cells. + int IncompPropsAdBasic::numCells() const + { + return rock_.numCells(); + } + + /// \return Array of N porosity values. + const double* IncompPropsAdBasic::porosity() const + { + return rock_.porosity(); + } + + /// \return Array of ND^2 permeability values. + /// The D^2 permeability values for a cell are organized as a matrix, + /// which is symmetric (so ordering does not matter). + const double* IncompPropsAdBasic::permeability() const + { + return rock_.permeability(); + } + + + // ---- Fluid interface ---- + + /// \return P, the number of phases (also the number of components). + int IncompPropsAdBasic::numPhases() const + { + return pvt_.numPhases(); + } + + /// \return Array of P viscosity values. + const double* IncompPropsAdBasic::viscosity() const + { + return &viscosity_[0]; + } + /// \return Array of P density values. + const double* IncompPropsAdBasic::density() const + { + return pvt_.surfaceDensities(); + } + + const double* IncompPropsAdBasic::surfaceDensity() const + { + return pvt_.surfaceDensities(); + } + typedef IncompPropsAdBasic::ADB ADB; + typedef IncompPropsAdBasic::V V; + typedef Eigen::Array Block; + typedef std::vector Cells; + + std::vector + IncompPropsAdBasic::relperm(const V& sw, + const V& so, + const Cells& cells) const + { + const int n = cells.size(); + const int np = numPhases(); + Block s_all(n, np); + assert(sw.size() == n && so.size() == n); + s_all.col(0) = sw; + s_all.col(1) = so; + Block kr(n, np); +// satprops_.relperm(n, s_all.data(), cells.data(), kr.data(), 0); + satprops_.relperm(n, s_all.data(), kr.data(), 0); + + std::vector relperms; + relperms.reserve(2); + for (int phase = 0; phase < 2; ++phase) { + relperms.emplace_back(kr.col(phase)); + } + return relperms; + } + + std::vector + IncompPropsAdBasic::relperm(const ADB& sw, + const ADB& so, + const Cells& cells) const + { + const int n = cells.size(); + const int np = numPhases(); + Block s_all(n, np); + assert(sw.size() == n && so.size() == n); + s_all.col(0) = sw.value(); + s_all.col(1) = so.value(); + Block kr(n, np); + Block dkr(n, np*np); +// satprops_.relperm(n, s_all.data(), cells.data(), kr.data(), dkr.data()); + satprops_.relperm(n, s_all.data(), kr.data(), dkr.data()); + const int num_blocks = so.numBlocks(); + std::vector relperms; + relperms.reserve(2); + typedef const ADB* ADBPtr; + ADBPtr s[2] = { &sw, &so }; + for (int phase1 = 0; phase1 < 2; ++phase1) { + const int phase1_pos = phase1; + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = ADB::M(n, s[phase1]->derivative()[block].cols()); + } + for (int phase2 = 0; phase2 < 2; ++phase2) { + const int phase2_pos = phase2; + // Assemble dkr1/ds2. + const int column = phase1_pos + np*phase2_pos; // Recall: Fortran ordering from props_.relperm() + ADB::M dkr1_ds2_diag = spdiag(dkr.col(column)); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] += dkr1_ds2_diag * s[phase2]->derivative()[block]; + } + } + relperms.emplace_back(ADB::function(kr.col(phase1_pos), jacs)); + } + return relperms; + } +} diff --git a/opm/polymer/fullyimplicit/IncompPropsAdBasic.hpp b/opm/polymer/fullyimplicit/IncompPropsAdBasic.hpp new file mode 100644 index 000000000..7c5faa932 --- /dev/null +++ b/opm/polymer/fullyimplicit/IncompPropsAdBasic.hpp @@ -0,0 +1,54 @@ +/**/ + +#ifndef OPM_INCOMPPROPSADBASIC_HEADER_INCLUDED +#define OPM_INCOMPPROPSADBASIC_HEADER_INCLUDED + +#include +#include +#include +#include +#include +namespace Opm +{ + class IncompPropsAdBasic : public IncompPropsAdInterface + { + public: + IncompPropsAdBasic(const parameter::ParameterGroup& param, + const int dim, + const int num_cells); + IncompPropsAdBasic(const int num_phases, + const SaturationPropsBasic::RelPermFunc& relpermfunc, + const std::vector& rho, + const std::vector& mu, + const double porosity, + const double permeability, + const int dim, + const int num_cells); + + ~IncompPropsAdBasic(); + int numDimensions() const; + int numCells() const; + const double* porosity() const; + const double* permeability() const; + + int numPhases() const; + const double* viscosity() const; + const double* density() const; + const double* surfaceDensity() const; + + typedef AutoDiffBlock ADB; + typedef ADB::V V; + std::vector relperm(const V& sw, + const V& so, + const std::vector& cells) const; + std::vector relperm(const ADB& sw, + const ADB& so, + const std::vector& cells) const; + private: + RockBasic rock_; + PvtPropertiesBasic pvt_; + SaturationPropsBasic satprops_; + std::vector viscosity_; + }; +} +#endif // OPM_INCOMPPROPSADBASIC_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp new file mode 100644 index 000000000..0cd44beb8 --- /dev/null +++ b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp @@ -0,0 +1,136 @@ +/**/ +#include +#include +#include +#include +#include + +namespace Opm +{ + IncompPropsAdFromDeck::IncompPropsAdFromDeck(const EclipseGridParser& deck, + const UnstructuredGrid& grid) + { + rock_.init(deck, grid); + pvt_.init(deck); + satprops_.init(deck, grid, 200); + if (pvt_.numPhases() != satprops_.numPhases()) { + OPM_THROW(std::runtime_error, "BlackoilPropsAdFromDeck::BlackoilPropsAdFromDeck() - " + "Inconsistent number of phases in pvt data (" << pvt_.numPhases() + << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); + } + } + IncompPropsAdFromDeck::~IncompPropsAdFromDeck() + { + } + // rock interface + int IncompPropsAdFromDeck::numDimensions() const + { + return rock_.numDimensions(); + } + int IncompPropsAdFromDeck::numCells() const + { + return rock_.numCells(); + } + + const double* IncompPropsAdFromDeck::porosity() const + { + return rock_.porosity(); + } + const double* IncompPropsAdFromDeck::permeability() const + { + return rock_.permeability(); + } + + // fluid interface + int IncompPropsAdFromDeck::numPhases() const + { + return pvt_.numPhases(); + } + + + const double* IncompPropsAdFromDeck::viscosity() const + { + return pvt_.viscosity(); + } + + + const double* IncompPropsAdFromDeck::density() const + { + return pvt_.reservoirDensities(); + } + + + const double* IncompPropsAdFromDeck::surfaceDensity() const + { + return pvt_.surfaceDensities(); + } + + + typedef IncompPropsAdFromDeck::ADB ADB; + typedef IncompPropsAdFromDeck::V V; + typedef Eigen::Array Block; + + + std::vector + IncompPropsAdFromDeck::relperm(const V& sw, + const V& so, + const Cells& cells) const + { + const int n = cells.size(); + const int np = numPhases(); + Block s_all(n, np); + assert(sw.size() == n && so.size() == n); + s_all.col(0) = sw; + s_all.col(1) = so; + Block kr(n, np); + satprops_.relperm(n, s_all.data(), cells.data(), kr.data(), 0); + std::vector relperms; + relperms.reserve(np); + for (int phase = 0; phase < np; ++phase) { + relperms.emplace_back(kr.col(phase)); + } + return relperms; + } + + + + std::vector + IncompPropsAdFromDeck::relperm(const ADB& sw, + const ADB& so, + const Cells& cells) const + { + const int n = cells.size(); + const int np = numPhases(); + Block s_all(n, np); + assert(sw.size() == n && so.size() == n); + s_all.col(0) = sw.value(); + s_all.col(1) = so.value(); + Block kr(n, np); + Block dkr(n, np*np); + satprops_.relperm(n, s_all.data(), cells.data(), kr.data(), dkr.data()); + const int num_blocks = so.numBlocks(); + std::vector relperms; + relperms.reserve(np); + typedef const ADB* ADBPtr; + ADBPtr s[2] = { &sw, &so }; + for (int phase1 = 0; phase1 < np; ++phase1) { + const int phase1_pos = phase1; + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = ADB::M(n, s[phase1]->derivative()[block].cols()); + } + for (int phase2 = 0; phase2 < np; ++phase2) { + const int phase2_pos = phase2; + // Assemble dkr1/ds2. + const int column = phase1_pos + np*phase2_pos; // Recall: Fortran ordering from props_.relperm() + ADB::M dkr1_ds2_diag = spdiag(dkr.col(column)); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] += dkr1_ds2_diag * s[phase2]->derivative()[block]; + } + } + relperms.emplace_back(ADB::function(kr.col(phase1_pos), jacs)); + } + return relperms; + } +} //namespace Opm + diff --git a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp new file mode 100644 index 000000000..3cd08793b --- /dev/null +++ b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp @@ -0,0 +1,57 @@ +/**/ +#ifndef OPM_INCOMPPROPSADFROMDECK_HEADER_INCLUDED +#define OPM_INCOMPPROPSADFROMDECK_HEADER_INCLUDED +#include +#include +#include +#include +#include +#include + +#include +#include + +struct UnstructuredGrid; +namespace Opm +{ + class IncompPropsAdFromDeck : public IncompPropsAdInterface + { + public: + IncompPropsAdFromDeck(const EclipseGridParser& deck, + const UnstructuredGrid& grid); + ~IncompPropsAdFromDeck(); + + //--Rock interface-- + int numDimensions() const; + int numCells() const; + const double* porosity() const; + const double* permeability() const; + + // --Fluid interface-- + int numPhases() const; + const double* viscosity() const; + const double* density() const; + const double* surfaceDensity() const; + + typedef AutoDiffBlock ADB; + typedef ADB::V V; + typedef std::vector Cells; + + + std::vector relperm(const V& sw, + const V& so, + const Cells& cells) const; + + std::vector relperm(const ADB& sw, + const ADB& so, + const Cells& cells) const; + + private: + RockFromDeck rock_; + PvtPropertiesIncompFromDeck pvt_; + SaturationPropsFromDeck satprops_; + }; + +} //namespace Opm + +#endif// OPM_INCOMPPROPSADFROMDECK_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/IncompPropsAdInterface.cpp b/opm/polymer/fullyimplicit/IncompPropsAdInterface.cpp new file mode 100644 index 000000000..ff3ba971b --- /dev/null +++ b/opm/polymer/fullyimplicit/IncompPropsAdInterface.cpp @@ -0,0 +1,6 @@ + +#include + +Opm::IncompPropsAdInterface::~IncompPropsAdInterface() +{ +} diff --git a/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp b/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp new file mode 100644 index 000000000..8de4c8309 --- /dev/null +++ b/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp @@ -0,0 +1,89 @@ +/* + +*/ + +#ifndef OPM_INCOMPPROPSADINTERFACE_HEADER_INCLUDED +#define OPM_INCOMPPROPSADINTERFACE_HEADER_INCLUDED +#include + +namespace Opm +{ + class IncompPropsAdInterface + { + public: + virtual ~IncompPropsAdInterface(); + + //////////////////////////// + // Rock interface // + //////////////////////////// + + /// \return D, the number of spatial dimensions. + virtual int numDimensions() const = 0; + + /// \return N, the number of cells. + virtual int numCells() const = 0; + + /// \return Array of N porosity values. + virtual const double* porosity() const = 0; + + /// \return Array of ND^2 permeability values. + /// The D^2 permeability values for a cell are organized as a matrix, + /// which is symmetric (so ordering does not matter). + virtual const double* permeability() const = 0; + + + //////////////////////////// + // Fluid interface // + //////////////////////////// + + typedef AutoDiffBlock ADB; + typedef ADB::V V; + typedef ADB::M M; + typedef std::vector Cells; + + /// \return Number of active phases (also the number of components). + virtual int numPhases() const = 0; + + // ------ Density ------ + + /// Densities of stock components at surface conditions. + /// \return Array of 2 density values. + virtual const double* surfaceDensity() const = 0; + + + // ------ Viscosity ------ + + /// Viscosity of stock components at surface conditions. + /// \return Array of 2 viscosity values. + virtual const double* viscosity() const = 0; + + + + // ------ Relative permeability ------ + + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 2 elements, each an array of n relperm values, + /// containing krw, kro. Use PhaseIndex for indexing into the result. + virtual + std::vector relperm(const V& sw, + const V& so, + const Cells& cells) const = 0; + + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 2 elements, each an array of n relperm values, + /// containing krw, kro. Use PhaseIndex for indexing into the result. + virtual + std::vector relperm(const ADB& sw, + const ADB& so, + const Cells& cells) const = 0; + + }; +} // namespace Opm + +#endif// OPM_INCOMPPROPSADINTERFACE_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp new file mode 100644 index 000000000..333709a7c --- /dev/null +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp @@ -0,0 +1,216 @@ + +#include +#include +#include +#include +#include + +namespace Opm { + + + typedef PolymerPropsAd::ADB ADB; + typedef PolymerPropsAd::V V; + +/* + PolymerPropsAd::PolymerPropsAd() + { + } + + + PolymerPropsAd::PolymerPropsAd(const int num_cells, + const double c_max, + const double mix_param, + const double std::vector& c_vals_visc, + const double std::vector& visc_mult_vals) + : num_cells_ (num_cells) + , c_max_ (c_max) + , mix_param_(mix_param) + , c_vals_visc_ (c_vals_visc) + , visc_mult_vals_ (visc_mult_vals) + { + } + + + double PolymerProsAd::num_cells() const + { + return num__cells_; + } + + + + + double PolymerPropsAd::cMax() const + { + return c_max_; + } + + + + double PolymerPropsAd::mixParam() const + { + return mix_param_; + } + + + + V PolymerPropsAd::muM(const V& c, + const double* visc) const + { + const int nc = num_cells(); + assert(nc == c.size()); + std::vector mu(nc); + + for (int i = 0; i < nc; ++i) { + mu[i] = Opm::linearInterpolation(c_vals_visc_, visc_mult_vals_, c(i)); + } + + const V muM = Eigen::Map(&mu[0], nc); + + const double mu_w = visc[0]; + + return muM * mu_w; + } + + + + ADB PolymerPropsAd::muM(const ADB& c, + const double* visc) const + { + const int nc = num_cells(); + assert(nc == c.size()); + + V mu_m = muM(c.value()); + + std::vector dmu_dc(nc); + + for (int i = 0; i < nc; ++i) { + dmu_dc[i] = Opm::linearInterpolationDerivative(c_vals_visc_, visc_mult_vals_, c.value()(i)); + } + + const V dmu = Eigen::Map(&dmu_dc[0], nc); + + ADB::M dmu_diag = spdiag(dmu); + const int num_blocks = c.numBlocks(); + std::vector jacs(num_blocks); + + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = dmu_diag * c.derivative()[block]; + } + + const double mu_w = visc[0] + + return ADB::function(mu_m, jacs) * mu_w; + } + + + + V PolymerPropsAd::ToddLongstaff(const double mix_param, + const V& muM, + const V& mu) const + { + const int nc = num_cells(); + assert(nc == muM.size()); + + std::vector mueff(nc); + const double omega = mix_param; + + for (int i = 0; i < nc; ++i) { + mueff[i] = std::pow(muM(i),omega) * std::pow(mu(i), 1. - omega); + } + + const V muEff = Eigen::Map(&mueff[0], nc); + + return muEff; + } + + + ADB PolymerPropsAd::ToddLongstaff(const double mix_param, + const ADB& muM, + const ADB& mu) const + { + const int nc = num_cells(); + + } + + + V PolymerPropsAd::muPolyEff(const double mix_param, + const V& muM, + const V& muPoly) const + { + + return ToddLongstaff(mix_param, muM, muPoly); + } + + V PolymerPropsAd::muWatEff(const double mix_param, + const std::vecotr& c_max, + const V& c, + const V& muM, + const V& muWat, + const V& muPolyEff) const + { + const int nc = num_cells(); + assert(nc == c.size()); + V muWate = ToddLongstaff(mix_param, muM, muWat); + +// V cmax = V::Constant(nc, 1, c_max); + const V cmax = Eigen::Map(&c_max[0], nc); + const V one = V::Ones(nc, 1); + V inv_muWatEff = (one - c / cmax) / muWate + c / cmax / muPolyEff; + + V muWatEff = one / inv_muWatEff; + + return muWatEff; + + } +*/ + V PolymerPropsAd::effectiveInvWaterVisc(const V& c, + const double* visc) const + { + const int nc = c.size(); + V inv_mu_w_eff(n); + for (int i = 0; i < nc; ++i) { + double im; + polymer_props_.effectiveInvVisc(c(i), visc, im); + inv_mu_w_eff(i) = im; + } + + return inv_mu_w_eff; + } + + + + + + + ADB PolymerPropsAd::effectiveInvWaterVisc(const ADB& c, + const double* visc) + { + const int n = c.size(); + V inv_mu_w_eff(n); + V dinv_mu_w_eff(n); + for (int i = 0; i < n; ++i) { + double im, dim; + polymer_props_.effectiveInvViscWithDer(c.value()(i), visc, im, dim); + inv_mu_w_eff(i) = im; + dinv_mu_w_eff(i) = dim; + } + ADB::M dim_diag = spdiag(dinv_mu_w_eff); + const int num_blocks = c.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = dim_diag * c.derivative()[block]; + } + return ADB::function(inv_mu_w_eff, jacs); + } + + + + + + V PolymerPropsAd::polymerWaterVelocityRatio(const V& c) const + { + const int nc + + } + +} // namespace Opm diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp new file mode 100644 index 000000000..ad3f00471 --- /dev/null +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp @@ -0,0 +1,89 @@ +#ifndef OPM_POLYMERPROPSAD_HEADED_INLCUDED +#define OPM_POLYMERPROPSAD_HEADED_INLCUDED + + +#include +#include +#include +#include +#include +namespace Opm { + + + class PolymerPropsAd + { + public: +/* PolymerPropsAd(const int num_cells; + const double mix_param, + const std::vector& c_max, + const std::vector& c_vals_visc, + const std::vector& visc_mult_vals); + + + double num_cells() const; + double cMax() const; + double mixParam() const; + + typedef AutoDiffBlock ADB; + typedef ADB::V V; + + + V muM(const V& c, + const double* visc) const; + ADB muM(const ADB& c + const double* visc) const; + + V ToddLongstaff(const double mix_param, + const V& muM, + const V& mu) const; + + ADB ToddLongstaff(const double mix_param, + const ADB& muM, + const ADB& mu) const; + + V muPolyEff(const double mix_param, + const V& muM, + const V& muPoly) const; + + ADB muPolyEff(const double mix_param, + const ADB& muM, + const ADB& muPoly) const; + + V muWatEff(const double mix_param, + const std::vector& c_max, + const V& c, + const V& muM, + const V& muWat, + const V& muPolyEff) const; + + ADB muWatEff(const double mix_param, + const std::vector& c_max, + const ADB& c, + const ADB& muM, + const ADB& muWat, + const ADB& muPolyEff) const; +*/ + typedef AutoDiffBlock ADB; + typedef ADB::V V; + PolymerPropsAd(const PolymerPropperties& polyprops); + ~PolymerPropsAd(); + private: + const PolymerProperties polyprops_; + }; + + +} + + + + + + + + + + + + + +#endif// OPM_POLYMERPROPSAD_HEADED_INLCUDED diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.cpp new file mode 100644 index 000000000..09a5c7852 --- /dev/null +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.cpp @@ -0,0 +1,261 @@ +/* + Copyright 2013 SINTEF ICT, Applied Mathematics. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +namespace Opm +{ + + class SimulatorFullyImplicitTwophase::Impl + { + public: + Impl(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const IncompPropsAdInterface& props, + LinearSolverInterface& linsolver, + std::vector& src); + + SimulatorReport run(SimulatorTimer& timer, + TwophaseState& state, + std::vector& src); + + private: + + // Parameters for output. + bool output_; + bool output_vtk_; + std::string output_dir_; + int output_interval_; + // Parameters for well control + // Observed objects. + const UnstructuredGrid& grid_; + const IncompPropsAdInterface& props_; + // Solvers + FullyImplicitTwoPhaseSolver solver_; + // Misc. data + std::vector allcells_; + }; + + + + + SimulatorFullyImplicitTwophase::SimulatorFullyImplicitTwophase(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const IncompPropsAdInterface& props, + LinearSolverInterface& linsolver, + std::vector& src) + { + pimpl_.reset(new Impl(param, grid, props, linsolver, src)); + } + + + + + + SimulatorReport SimulatorFullyImplicitTwophase::run(SimulatorTimer& timer, + TwophaseState& state, + std::vector& src) + { + return pimpl_->run(timer, state, src); + } + + + + static void outputStateVtk(const UnstructuredGrid& grid, + const Opm::TwophaseState& state, + const int step, + const std::string& output_dir) + { + // Write data in VTK format. + std::ostringstream vtkfilename; + vtkfilename << output_dir << "/vtk_files"; + boost::filesystem::path fpath(vtkfilename.str()); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + vtkfilename << "/output-" << std::setw(3) << std::setfill('0') << step << ".vtu"; + std::ofstream vtkfile(vtkfilename.str().c_str()); + if (!vtkfile) { + OPM_THROW(std::runtime_error, "Failed to open " << vtkfilename.str()); + } + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + std::vector cell_velocity; + Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); + dm["velocity"] = &cell_velocity; + Opm::writeVtkData(grid, dm, vtkfile); + } +/* + static void outputWaterCut(const Opm::Watercut& watercut, + const std::string& output_dir) + { + // Write water cut curve. + std::string fname = output_dir + "/watercut.txt"; + std::ofstream os(fname.c_str()); + if (!os) { + OPM_THROW(std::runtime_error, "Failed to open " << fname); + } + watercut.write(os); + } + + */ + + + + + SimulatorFullyImplicitTwophase::Impl::Impl(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const IncompPropsAdInterface& props, + LinearSolverInterface& linsolver, + std::vector& src) + : grid_(grid), + props_(props), + solver_(grid_, props_, linsolver) + { + // For output. + output_ = param.getDefault("output", true); + if (output_) { + output_vtk_ = param.getDefault("output_vtk", true); + output_dir_ = param.getDefault("output_dir", std::string("output")); + // Ensure that output dir exists + boost::filesystem::path fpath(output_dir_); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + output_interval_ = param.getDefault("output_interval", 1); + } + + // Misc init. + const int num_cells = grid.number_of_cells; + allcells_.resize(num_cells); + for (int cell = 0; cell < num_cells; ++cell) { + allcells_[cell] = cell; + } + } + + + + + SimulatorReport SimulatorFullyImplicitTwophase::Impl::run(SimulatorTimer& timer, + TwophaseState& state, + std::vector& src) + { + // Initialisation. + std::vector porevol; + computePorevolume(grid_, props_.porosity(), porevol); + // const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); + std::vector initial_porevol = porevol; + + // Main simulation loop. + Opm::time::StopWatch solver_timer; + double stime = 0.0; + Opm::time::StopWatch step_timer; + Opm::time::StopWatch total_timer; + total_timer.start(); + // These must be changed for three-phase. + std::vector fractional_flows; + std::fstream tstep_os; + if (output_) { + std::string filename = output_dir_ + "/step_timing.param"; + tstep_os.open(filename.c_str(), std::fstream::out | std::fstream::app); + } + for (; !timer.done(); ++timer) { + // Report timestep and (optionally) write state to disk. + step_timer.start(); + timer.report(std::cout); + if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + } + + SimulatorReport sreport; + + // Run solver. + solver_timer.start(); + std::vector initial_pressure = state.pressure(); + solver_.step(timer.currentStepLength(), state, src); + + // Stop timer and report. + solver_timer.stop(); + const double st = solver_timer.secsSinceStart(); + std::cout << "Fully implicit solver took: " << st << " seconds." << std::endl; + stime += st; + sreport.pressure_time = st; + + sreport.total_time = step_timer.secsSinceStart(); + if (output_) { + sreport.reportParam(tstep_os); + } + } + + if (output_) { + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + // outputWaterCut(watercut, output_dir_); + tstep_os.close(); + } + + total_timer.stop(); + + SimulatorReport report; + report.pressure_time = stime; + report.transport_time = 0.0; + report.total_time = total_timer.secsSinceStart(); + return report; + } + + +} // namespace Opm diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.hpp new file mode 100644 index 000000000..0dac9641c --- /dev/null +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.hpp @@ -0,0 +1,85 @@ +/* + Copyright 2013 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_SIMULATORFULLYIMPLICITTWOPHASE_HEADER_INCLUDED +#define OPM_SIMULATORFULLYIMPLICITTWOPHASE_HEADER_INCLUDED + +#include +#include + +struct UnstructuredGrid; + +namespace Opm +{ + namespace parameter { class ParameterGroup; } + class IncompPropsAdInterface; + class LinearSolverInterface; + class SimulatorTimer; + class TwophaseState; + struct SimulatorReport; + + /// Class collecting all necessary components for a two-phase simulation. + class SimulatorFullyImplicitTwophase + { + public: + /// Initialise from parameters and objects to observe. + /// \param[in] param parameters, this class accepts the following: + /// parameter (default) effect + /// ----------------------------------------------------------- + /// output (true) write output to files? + /// output_dir ("output") output directoty + /// output_interval (1) output every nth step + /// nl_pressure_residual_tolerance (0.0) pressure solver residual tolerance (in Pascal) + /// nl_pressure_change_tolerance (1.0) pressure solver change tolerance (in Pascal) + /// nl_pressure_maxiter (10) max nonlinear iterations in pressure + /// nl_maxiter (30) max nonlinear iterations in transport + /// nl_tolerance (1e-9) transport solver absolute residual tolerance + /// num_transport_substeps (1) number of transport steps per pressure step + /// use_segregation_split (false) solve for gravity segregation (if false, + /// segregation is ignored). + /// + /// \param[in] grid grid data structure + /// \param[in] props fluid and rock properties + /// \param[in] linsolver linear solver + SimulatorFullyImplicitTwophase(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const IncompPropsAdInterface& props, + LinearSolverInterface& linsolver, + std::vector& src); + + /// Run the simulation. + /// This will run succesive timesteps until timer.done() is true. It will + /// modify the reservoir and well states. + /// \param[in,out] timer governs the requested reporting timesteps + /// \param[in,out] state state of reservoir: pressure, fluxes + /// \param[in,out] well_state state of wells: bhp, perforation rates + /// \return simulation report, with timing data + SimulatorReport run(SimulatorTimer& timer, + TwophaseState& state, + std::vector& src); + + private: + class Impl; + // Using shared_ptr instead of scoped_ptr since scoped_ptr requires complete type for Impl. + boost::shared_ptr pimpl_; + }; + +} // namespace Opm + +#endif // OPM_SIMULATORFULLYIMPLICITBLACKOIL_HEADER_INCLUDED From eded8f735cf8a9a5a58cf6a037494a51c758614c Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 6 Dec 2013 22:25:22 +0800 Subject: [PATCH 02/83] chang include header from opm/autodiff to opm/polymer/fullyimplicit --- opm/polymer/fullyimplicit/AutoDiffHelpers.hpp | 2 +- opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp | 8 ++++---- opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.hpp | 6 +++--- opm/polymer/fullyimplicit/IncompPropsAd.hpp | 2 +- opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp | 6 +++--- opm/polymer/fullyimplicit/IncompPropsAdBasic.hpp | 4 ++-- opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp | 4 ++-- opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp | 4 ++-- opm/polymer/fullyimplicit/IncompPropsAdInterface.cpp | 2 +- opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp | 2 +- .../fullyimplicit/SimulatorFullyImplicitTwophase.cpp | 6 +++--- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/opm/polymer/fullyimplicit/AutoDiffHelpers.hpp b/opm/polymer/fullyimplicit/AutoDiffHelpers.hpp index 9d26a6d36..ba154a08b 100644 --- a/opm/polymer/fullyimplicit/AutoDiffHelpers.hpp +++ b/opm/polymer/fullyimplicit/AutoDiffHelpers.hpp @@ -20,7 +20,7 @@ #ifndef OPM_AUTODIFFHELPERS_HEADER_INCLUDED #define OPM_AUTODIFFHELPERS_HEADER_INCLUDED -#include +#include #include #include diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp index d70ab1909..60e7a11be 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp @@ -1,11 +1,11 @@ /**/ -#include +#include #include -#include -#include -#include +#include +#include +#include #include #include diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.hpp index d579cff6b..add966c60 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.hpp @@ -3,9 +3,9 @@ #ifndef OPM_FULLYIMPLICITTWOPHASESOLVER_HEADER_INCLUDED #define OPM_FULLYIMPLICITTWOPHASESOLVER_HEADER_INCLUDED -#include -#include -#include +#include +#include +#include #include diff --git a/opm/polymer/fullyimplicit/IncompPropsAd.hpp b/opm/polymer/fullyimplicit/IncompPropsAd.hpp index 21bfa798e..b6f5f379b 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAd.hpp +++ b/opm/polymer/fullyimplicit/IncompPropsAd.hpp @@ -10,7 +10,7 @@ Properties for incompressible immiscible two-phase flow #define OPM_INCOMPROPSAD_HEADER_INCLUDED #include #include -#include +#include #include #include diff --git a/opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp b/opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp index a15cbcc30..a12eff445 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp +++ b/opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp @@ -1,9 +1,9 @@ /**/ -#include +#include #include -#include -#include +#include +#include #include #include diff --git a/opm/polymer/fullyimplicit/IncompPropsAdBasic.hpp b/opm/polymer/fullyimplicit/IncompPropsAdBasic.hpp index 7c5faa932..3fc901298 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAdBasic.hpp +++ b/opm/polymer/fullyimplicit/IncompPropsAdBasic.hpp @@ -3,11 +3,11 @@ #ifndef OPM_INCOMPPROPSADBASIC_HEADER_INCLUDED #define OPM_INCOMPPROPSADBASIC_HEADER_INCLUDED -#include +#include #include #include #include -#include +#include namespace Opm { class IncompPropsAdBasic : public IncompPropsAdInterface diff --git a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp index 0cd44beb8..4781c85b8 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp +++ b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp @@ -1,6 +1,6 @@ /**/ -#include -#include +#include +#include #include #include #include diff --git a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp index 3cd08793b..8058611d0 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp +++ b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp @@ -1,8 +1,8 @@ /**/ #ifndef OPM_INCOMPPROPSADFROMDECK_HEADER_INCLUDED #define OPM_INCOMPPROPSADFROMDECK_HEADER_INCLUDED -#include -#include +#include +#include #include #include #include diff --git a/opm/polymer/fullyimplicit/IncompPropsAdInterface.cpp b/opm/polymer/fullyimplicit/IncompPropsAdInterface.cpp index ff3ba971b..af33535ea 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAdInterface.cpp +++ b/opm/polymer/fullyimplicit/IncompPropsAdInterface.cpp @@ -1,5 +1,5 @@ -#include +#include Opm::IncompPropsAdInterface::~IncompPropsAdInterface() { diff --git a/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp b/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp index 8de4c8309..b74004c93 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp +++ b/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp @@ -4,7 +4,7 @@ #ifndef OPM_INCOMPPROPSADINTERFACE_HEADER_INCLUDED #define OPM_INCOMPPROPSADINTERFACE_HEADER_INCLUDED -#include +#include namespace Opm { diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.cpp index 09a5c7852..377a42265 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.cpp @@ -17,12 +17,12 @@ along with OPM. If not, see . */ -#include +#include #include #include -#include -#include +#include +#include #include #include From 080116c66ba780382846c9714b20a491e179f8e1 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 6 Dec 2013 23:35:13 +0800 Subject: [PATCH 03/83] Add fully implicit solver for incomp two phase with polymer and the polymer properties based on AD. --- .../FullyImplicitTwophasePolymerSolver.cpp | 485 ++++++++++++++++++ .../FullyImplicitTwophasePolymerSolver.hpp | 98 ++++ opm/polymer/fullyimplicit/PolymerPropsAd.cpp | 77 ++- opm/polymer/fullyimplicit/PolymerPropsAd.hpp | 9 +- 4 files changed, 655 insertions(+), 14 deletions(-) create mode 100644 opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp create mode 100644 opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp new file mode 100644 index 000000000..a2534ba1c --- /dev/null +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -0,0 +1,485 @@ +/**/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +namespace Opm { + +namespace { + + std::vector + buildAllCells(const int nc) + { + std::vector all_cells(nc); + for (int c = 0; c < nc; ++c) { all_cells[c] = c; } + + return all_cells; + } + struct Chop01 { + double operator()(double x) const { return std::max(std::min(x, 1.0), 0.0); } + }; + +}//anonymous namespace + + + + + +typedef AutoDiffBlock ADB; +typedef ADB::V V; +typedef ADB::M M; +typedef Eigen::Array DataBlock; + + + + + + FullyImplicitTwoPhaseSolver:: + FullyImplicitTwoPhaseSolver(const UnstructuredGrid& grid, + const IncompPropsAdInterface& fluid, + const PolymerProperties& polymer_props, + const PolymerPropsAd& polymer_props_ad, + const LinearSolverInterface& linsolver) + : grid_ (grid) + , fluid_(fluid) + , polymer_props_ (polymer_props) + , polymer_props_ad_ (polymer_props_ad_) + , linsolver_(linsolver) + , cells_ (buildAllCells(grid.number_of_cells)) + , ops_(grid) + , residual_(std::vector(fluid.numPhases() + 1, ADB::null())) + { + } + + + + + + void + FullyImplicitTwoPhaseSolver:: + step(const double dt, + PolymerState& x, + const std::vector& src) + { + + V pvol(grid_.number_of_cells); + // Pore volume + const typename V::Index nc = grid_.number_of_cells; + V rho = V::Constant(pvol.size(), 1, *fluid_.porosity()); + std::transform(grid_.cell_volumes, grid_.cell_volumes + nc, + rho.data(), pvol.data(), + std::multiplies()); + + const V pvdt = pvol / dt; + + const SolutionState old_state = constantState(x); + const double atol = 1.0e-12; + const double rtol = 5.0e-8; + const int maxit = 15; + + assemble(pvdt, old_state, x, src); + + const double r0 = residualNorm(); + int it = 0; + std::cout << "\nIteration Residual\n" + << std::setw(9) << it << std::setprecision(9) + << std::setw(18) << r0 << std::endl; + bool resTooLarge = r0 > atol; + while (resTooLarge && (it < maxit)) { + const V dx = solveJacobianSystem(); + updateState(dx, x); + + assemble(pvdt, old_state, x, src); + + const double r = residualNorm(); + + resTooLarge = (r > atol) && (r > rtol*r0); + + it += 1; + std::cout << std::setw(9) << it << std::setprecision(9) + << std::setw(18) << r << std::endl; + } + + if (resTooLarge) { + std::cerr << "Failed to compute converged solution in " << it << " iterations. Ignoring!\n"; + // OPM_THROW(std::runtime_error, "Failed to compute converged solution in " << it << " iterations."); + } + } + + + + + + FullyImplicitTwoPhaseSolver::SolutionState::SolutionState(const int np) + : pressure ( ADB::null()) + , saturation (np, ADB::null()) + , concentration ( ADB::null()) + { + } + + + + + + FullyImplicitTwoPhaseSolver::SolutionState + FullyImplicitTwoPhaseSolver::constantState(const PolymerState& x) + { + const int nc = grid_.number_of_cells; + const int np = x.numPhases(); + + SolutionState state(np); + + // Pressure. + assert (not x.pressure().empty()); + const V p = Eigen::Map(& x.pressure()[0], nc); + state.pressure = ADB::constant(p); + + // Saturation. + assert (not x.saturation().empty()); + const DataBlock s_all = Eigen::Map(& x.saturation()[0], nc, np); + for (int phase = 0; phase < np; ++phase) { + state.saturation[phase] = ADB::constant(s_all.col(phase)); + // state.saturation[1] = ADB::constant(s_all.col(1)); + } + + // Concentration + + assert(not x.concentration().empty()); + const V c = Eigen::Map(&x.concentration()[0], nc); + + state.concentration = ADB::constant(c); + return state; + } + + + + + + FullyImplicitTwoPhaseSolver::SolutionState + FullyImplicitTwoPhaseSolver::variableState(const PolymerState& x) + { + const int nc = grid_.number_of_cells; + const int np = x.numPhases(); + + std::vector vars0; + vars0.reserve(np); + + // Initial pressure. + assert (not x.pressure().empty()); + const V p = Eigen::Map(& x.pressure()[0], nc); + vars0.push_back(p); + + // Initial saturation. + assert (not x.saturation().empty()); + const DataBlock s_all = Eigen::Map(& x.saturation()[0], nc, np); + const V sw = s_all.col(0); + vars0.push_back(sw); + + // Initial saturation. + assert (not x.concentration().empty()); + const V c = Eigen::Map(&x.concentration()[0], nc); + vars0.push_back(c); + + std::vector vars = ADB::variables(vars0); + + SolutionState state(np); + + // Pressure. + int nextvar = 0; + state.pressure = vars[ nextvar++ ]; + + // Saturation. + const std::vector& bpat = vars[0].blockPattern(); + { + ADB so = ADB::constant(V::Ones(nc, 1), bpat); + ADB& sw = vars[ nextvar++ ]; + state.saturation[0] = sw; + so = so - sw; + state.saturation[1] = so; + } + + // Concentration. + state.concentration = vars[nextvar++]; + + assert(nextvar == int(vars.size())); + + return state; + } + + + + + + void + FullyImplicitTwoPhaseSolver:: + assemble(const V& pvdt, + const SolutionState& old_state, + const PolymerState& x , + const std::vector& src) + { + // Create the primary variables. + const SolutionState state = variableState(x); + + // -------- Mass balance equations for water and oil -------- + const V trans = subset(transmissibility(), ops_.internal_faces); + const std::vector kr = computeRelPerm(state); + for (int phase = 0; phase < fluid_.numPhases(); ++phase) { + const ADB mflux = computeMassFlux(phase, trans, kr, state); + ADB source = accumSource(phase, kr, src); + residual_[phase] = + pvdt*(state.saturation[phase] - old_state.saturation[phase]) + + ops_.div*mflux - source; + } + // Mass balance equation for polymer + ADB polysrc = accumPolymerSource(kr, src); + ADB mc = computeMc(); + ADB mflux = computeMassFlux(0, trans, kr, state); + residual_[2] = pvdt * (state.saturation[0] * state.concentration - old_state.saturation[0] * old_state.concentration) + ops_.div * state.concentration * mc * mflux - polysrc; + + } + + + + + + + ADB + FullyImplicitTwoPhaseSolver::accumSource(const int phase, + const std::vector& kr, + const std::vector& src) const + { + //extract the source to out and in source. + std::vector outsrc; + std::vector insrc; + std::vector::const_iterator it; + for (it = src.begin(); it != src.end(); ++it) { + if (*it < 0) { + outsrc.push_back(*it); + insrc.push_back(0.0); + } else if (*it > 0) { + insrc.push_back(*it); + outsrc.push_back(0.0); + } else { + outsrc.emplace_back(0); + insrc.emplace_back(0); + } + } + const V source = Eigen::Map(& src[0], grid_.number_of_cells); + const V outSrc = Eigen::Map(& outsrc[0], grid_.number_of_cells); + const V inSrc = Eigen::Map(& insrc[0], grid_.number_of_cells); + + // compute the out-fracflow. + ADB f_out = computeFracFlow(phase, kr); + // compute the in-fracflow. + V f_in; + if (phase == 1) { + f_in = V::Zero(grid_.number_of_cells); + } else if (phase == 0) { + f_in = V::Ones(grid_.number_of_cells); + } + return f_out * outSrc + f_in * inSrc; + } + + + + + + ADB + FullyImplicitTwoPhaseSolver::computeFracFlow(int phase, + const std::vector& kr) const + { + const double* mus = fluid_.viscosity(); + ADB mob_phase = kr[phase] / V::Constant(kr[phase].size(), 1, mus[phase]); + ADB mob_wat = kr[0] / V::Constant(kr[0].size(), 1, mus[0]); + ADB mob_oil= kr[1] / V::Constant(kr[1].size(), 1, mus[1]); + ADB total_mob = mob_wat + mob_oil; + ADB f = mob_phase / total_mob; + + return f; + } + + + + + + V + FullyImplicitTwoPhaseSolver::solveJacobianSystem() const + { + const int np = fluid_.numPhases(); + if (np != 2) { + OPM_THROW(std::logic_error, "Only two-phase ok in FullyImplicitTwoPhaseSolver."); + } + ADB mass_phase_res = vertcat(residual_[0], residual_[1]); + ADB mass_res = collapseJacs(vertcat(mass_phase_res, residual_[2])); + + const Eigen::SparseMatrix matr = mass_res.derivative()[0]; + V dx(V::Zero(mass_res.size())); + Opm::LinearSolverInterface::LinearSolverReport rep + = linsolver_.solve(matr.rows(), matr.nonZeros(), + matr.outerIndexPtr(), matr.innerIndexPtr(), matr.valuePtr(), + mass_res.value().data(), dx.data()); + if (!rep.converged) { + OPM_THROW(std::runtime_error, + "FullyImplicitBlackoilSolver::solveJacobianSystem(): " + "Linear solver convergence failure."); + } + return dx; + } + + + + + + void FullyImplicitTwoPhaseSolver::updateState(const V& dx, + PolymerState& state) const + { + const int np = fluid_.numPhases(); + const int nc = grid_.number_of_cells; + const V null; + assert(null.size() == 0); + const V zero = V::Zero(nc); + const V one = V::Constant(nc, 1.0); + + // Extract parts of dx corresponding to each part. + const V dp = subset(dx, Span(nc)); + int varstart = nc; + const V dsw = subset(dx, Span(nc, 1, varstart)); + varstart += dsw.size(); + const V dc = subset(dx, Span(nc ,1 varstart)); + varstart += dc.size(); + + assert(varstart == dx.size()); + + // Pressure update. + const V p_old = Eigen::Map(&state.pressure()[0], nc); + const V p = p_old - dp; + std::copy(&p[0], &p[0] + nc, state.pressure().begin()); + + + // Saturation updates. + const double dsmax = 0.3; + const DataBlock s_old = Eigen::Map(& state.saturation()[0], nc, np); + V so = one; + const V sw_old = s_old.col(0); + const V dsw_limited = sign(dsw) * dsw.abs().min(dsmax); + const V sw = (sw_old - dsw_limited).unaryExpr(Chop01()); + so -= sw; + for (int c = 0; c < nc; ++c) { + state.saturation()[c*np] = sw[c]; + } + for (int c = 0; c < nc; ++c) { + state.saturation()[c*np + 1] = so[c]; + } + + // Concentration updates. + const V c_old = Eigen::Map(&state.concentration()[0], nc); + const V c = c_old - dc; + std::copy(&c[0], &c[0] + nc, state.concentration().begin()); + + } + + + + + + std::vector + FullyImplicitTwoPhaseSolver::computeRelPerm(const SolutionState& state) const + { + + const ADB sw = state.saturation[0]; + const ADB so = state.saturation[1]; + + return fluid_.relperm(sw, so, cells_); + } + + + + + + + + + + + ADB + FullyImplicitTwoPhaseSolver::computeMassFlux(const int phase , + const V& trans, + const std::vector& kr , + const SolutionState& state ) const + { +// const ADB tr_mult = transMult(state.pressure); + const double* mus = fluid_.viscosity(); + ADB mob = kr[phase] / V::Constant(kr[phase].size(), 1, mus[phase]); + + const ADB dp = ops_.ngrad * state.pressure; + const ADB head = trans * dp; + + UpwindSelector upwind(grid_, ops_, head.value()); + + return upwind.select(mob) * head; + } + + + + + + double + FullyImplicitTwoPhaseSolver::residualNorm() const + { + double r = 0; + for (std::vector::const_iterator + b = residual_.begin(), + e = residual_.end(); + b != e; ++b) + { + r = std::max(r, (*b).value().matrix().norm()); + } + + return r; + } + + + + + + V + FullyImplicitTwoPhaseSolver::transmissibility() const + { + const V::Index nc = grid_.number_of_cells; + V htrans(grid_.cell_facepos[nc]); + V trans(grid_.cell_facepos[nc]); + UnstructuredGrid* ug = const_cast(& grid_); + tpfa_htrans_compute(ug, fluid_.permeability(), htrans.data()); + tpfa_trans_compute (ug, htrans.data() , trans.data()); + + return trans; + } + + + + + + +}//namespace Opm diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp new file mode 100644 index 000000000..8f5df13db --- /dev/null +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp @@ -0,0 +1,98 @@ +/**/ + +#ifndef OPM_FULLYIMPLICITTWOPHASEPOLYMERSOLVER_HEADER_INCLUDED +#define OPM_FULLYIMPLICITTWOPHASEPOLYMERSOLVER_HEADER_INCLUDED + +#include +#include +#include +#include +#include +#include + + +struct UnstructuredGrid; +namespace Opm { + class LinearSolverInterface; + class PolymerState; + + + class FullyImplicitTwoPhaseSolver + { + public: + FullyImplicitTwoPhaseSolver(const UnstructuredGrid& grid, + const IncompPropsAdInterface& fluid, + const PolymerProperties& polymer_props, + const PolymerPropsAd& polymer_props_ad, + const LinearSolverInterface& linsolver); + + void step(const double dt, + PolymerState& state, + const std::vector& src); + private: + typedef AutoDiffBlock ADB; + typedef ADB::V V; + typedef ADB::M M; + typedef Eigen::Array DataBlock; + struct SolutionState { + SolutionState(const int np); + ADB pressure; + std::vector saturation; + ADB concentration; +// ADB cmax; + }; + const UnstructuredGrid& grid_; + const IncompPropsAdInterface& fluid_; + const PolymerProperties& polymer_props_; + const PolymerPropsAd& polymer_props_ad_; + const LinearSolverInterface& linsolver_; + const std::vector cells_; + HelperOps ops_; + std::vector residual_; + + + SolutionState + constantState(const PolymerState& x); + SolutionState + variableState(const PolymerState& x); + void + assemble(const V& pvdt, + const SolutionState& old_state, + const PolymerState& x, + const std::vector& src); + V solveJacobianSystem() const; + void updateState(const V& dx, + PolymerState& x) const; + std::vector + computeRelPerm(const SolutionState& state) const; + V + transmissibility() const; + ADB + computeFracFlow(int phase, + const std::vector& kr) const; + ADB + accumSource(const int phase, + const std::vector& kr, + const std::vector& src) const; + ADB + computeMassFlux(const int phase, + const V& trans, + const std::vector& kr, + const SolutionState& state) const; + double + residualNorm() const; + + ADB + rockPorosity(const ADB& p) const; + ADB + rockPermeability(const ADB& p) const; + const double + fluidDensity(const int phase) const; + ADB + transMult(const ADB& p) const; + }; +} // namespace Opm +#endif// OPM_FULLYIMPLICITTWOPHASESOLVER_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp index 333709a7c..15adf0149 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp @@ -163,13 +163,28 @@ namespace Opm { } */ + PolymerPropsAd::PolymerPropsAd(const PolymerProperties& polymer_props) + : polymer_props_ (polymer_props) + { + } + + + + + PolymerPropsAd::~PolymerPropsAd() + { + } + + + + V PolymerPropsAd::effectiveInvWaterVisc(const V& c, const double* visc) const { const int nc = c.size(); V inv_mu_w_eff(n); for (int i = 0; i < nc; ++i) { - double im; + double im = 0; polymer_props_.effectiveInvVisc(c(i), visc, im); inv_mu_w_eff(i) = im; } @@ -185,21 +200,21 @@ namespace Opm { ADB PolymerPropsAd::effectiveInvWaterVisc(const ADB& c, const double* visc) { - const int n = c.size(); + const int nc = c.size(); V inv_mu_w_eff(n); V dinv_mu_w_eff(n); - for (int i = 0; i < n; ++i) { - double im, dim; + for (int i = 0; i < nc; ++i) { + double im = 0, dim = 0; polymer_props_.effectiveInvViscWithDer(c.value()(i), visc, im, dim); inv_mu_w_eff(i) = im; dinv_mu_w_eff(i) = dim; } - ADB::M dim_diag = spdiag(dinv_mu_w_eff); - const int num_blocks = c.numBlocks(); - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = dim_diag * c.derivative()[block]; - } + ADB::M dim_diag = spdiag(dinv_mu_w_eff); + const int num_blocks = c.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = dim_diag * c.derivative()[block]; + } return ADB::function(inv_mu_w_eff, jacs); } @@ -209,8 +224,46 @@ namespace Opm { V PolymerPropsAd::polymerWaterVelocityRatio(const V& c) const { - const int nc + const int nc = c.size(); + V mc(n); + + for (int i = 0; i < nc; ++i) { + double m = 0; + polymer_props_.computeMc(c(i), m); + mc(i) = m; + } + return mc; } -} // namespace Opm + + + + + ADB PolymerPropsAd::polymerWaterVelocityRatio(const ADB& c) const + { + + const int nc = c.size(); + V mc(n); + V dmc(n); + + for (int i = 0; i < nc; ++i) { + double m = 0; + double dm = 0; + polymer_props_.computeMcWithDer(c(i), m, dm); + + mc(i) = m; + dmc(i) = dm; + } + + ADB::M dmc_diag = spdiag(dmc); + const int num_blocks = c.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = dmc_diag * c.derivative()[block]; + } + + return ADB::function(mc, jacs); + } + +}// namespace Opm diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp index ad3f00471..2d77b228c 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp @@ -65,10 +65,15 @@ namespace Opm { */ typedef AutoDiffBlock ADB; typedef ADB::V V; - PolymerPropsAd(const PolymerPropperties& polyprops); + PolymerPropsAd(const PolymerPropperties& polymer_props); ~PolymerPropsAd(); + V PolymerPropsAd::effectiveInvWaterVisc(const V& c,const double* visc) const; + ADB PolymerPropsAd::effectiveInvWaterVisc(const ADB& c,const double* visc) const; + + V PolymerPropsAd::polymerWaterVelocityRatio(const V& c) const; + ADB PolymerPropsAd::polymerWaterVelocityRatio(const ADB& c) const; private: - const PolymerProperties polyprops_; + const PolymerProperties polymer_props_; }; From e226b5d95e2ac5379125c833c63044c86563cd90 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Mon, 9 Dec 2013 20:33:05 +0800 Subject: [PATCH 04/83] Simulator for fullyimplicit two phase flow with polymer --- .../FullyImplicitTwophasePolymerSolver.cpp | 105 +++++++++++------- .../FullyImplicitTwophasePolymerSolver.hpp | 26 +++-- .../fullyimplicit/IncompPropsAdBasic.cpp | 2 +- opm/polymer/fullyimplicit/PolymerPropsAd.cpp | 17 ++- opm/polymer/fullyimplicit/PolymerPropsAd.hpp | 26 +++-- 5 files changed, 111 insertions(+), 65 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index a2534ba1c..179fd8253 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -56,15 +57,13 @@ typedef Eigen::Array& src) + const std::vector& src, + const std::vector& polymer_inflow) + // const bool if_polymer_actived) { V pvol(grid_.number_of_cells); @@ -99,7 +100,7 @@ typedef Eigen::Array(&x.concentration()[0], nc); vars0.push_back(c); @@ -214,7 +215,7 @@ typedef Eigen::Array& bpat = vars[0].blockPattern(); { ADB so = ADB::constant(V::Ones(nc, 1), bpat); - ADB& sw = vars[ nextvar++ ]; + ADB sw = vars[ nextvar++ ]; state.saturation[0] = sw; so = so - sw; state.saturation[1] = so; @@ -233,11 +234,13 @@ typedef Eigen::Array& src) + const std::vector& src, + const std::vector& polymer_inflow) + // const bool if_polymer_actived) { // Create the primary variables. const SolutionState state = variableState(x); @@ -252,21 +255,42 @@ typedef Eigen::Array(&polymer_inflow[0], nc); + ADB mc = computeMc(state); + ADB mflux = computeMassFlux(0, trans, kr, state); + residual_[2] = pvdt * (state.saturation[0] * state.concentration + - old_state.saturation[0] * old_state.concentration) + + ops_.div * state.concentration * mc * mflux - src_polymer; + } else { + residual_[2] = ADB::constant(V::Zero(nc)); + } } - + double + FullyImplicitTwophasePolymerSolver:: + PolymerInjectedAmount(const std::vector& polymer_inflow) const + { + double amount = 0; + for (int i = 0; i < int(polymer_inflow.size()); ++i) { + amount += polymer_inflow[i]; + } + return amount; + } ADB - FullyImplicitTwoPhaseSolver::accumSource(const int phase, + FullyImplicitTwophasePolymerSolver::accumSource(const int phase, const std::vector& kr, const std::vector& src) const { @@ -307,7 +331,7 @@ typedef Eigen::Array& kr) const { const double* mus = fluid_.viscosity(); @@ -325,14 +349,14 @@ typedef Eigen::Array matr = mass_res.derivative()[0]; V dx(V::Zero(mass_res.size())); @@ -352,7 +376,7 @@ typedef Eigen::Array - FullyImplicitTwoPhaseSolver::computeRelPerm(const SolutionState& state) const + FullyImplicitTwophasePolymerSolver::computeRelPerm(const SolutionState& state) const { const ADB sw = state.saturation[0]; @@ -424,7 +449,7 @@ typedef Eigen::Array& kr , const SolutionState& state ) const @@ -446,7 +471,7 @@ typedef Eigen::Array::const_iterator @@ -465,7 +490,7 @@ typedef Eigen::Array& src); + const std::vector& src, + const std::vector& polymer_inflow + ); +// const bool if_polymer_actived); private: typedef AutoDiffBlock ADB; typedef ADB::V V; @@ -46,7 +48,6 @@ namespace Opm { }; const UnstructuredGrid& grid_; const IncompPropsAdInterface& fluid_; - const PolymerProperties& polymer_props_; const PolymerPropsAd& polymer_props_ad_; const LinearSolverInterface& linsolver_; const std::vector cells_; @@ -62,7 +63,9 @@ namespace Opm { assemble(const V& pvdt, const SolutionState& old_state, const PolymerState& x, - const std::vector& src); + const std::vector& src, + const std::vector& polymer_inflow); +// const bool if_polymer_actived); V solveJacobianSystem() const; void updateState(const V& dx, PolymerState& x) const; @@ -84,7 +87,10 @@ namespace Opm { const SolutionState& state) const; double residualNorm() const; + + ADB + computeMc(const SolutionState& state) const; ADB rockPorosity(const ADB& p) const; ADB @@ -93,6 +99,8 @@ namespace Opm { fluidDensity(const int phase) const; ADB transMult(const ADB& p) const; + double + PolymerInjectedAmount(const std::vector& polymer_inflow) const; }; } // namespace Opm #endif// OPM_FULLYIMPLICITTWOPHASESOLVER_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp b/opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp index a12eff445..33691fb82 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp +++ b/opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include #include diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp index 15adf0149..d6404618e 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp @@ -182,7 +182,7 @@ namespace Opm { const double* visc) const { const int nc = c.size(); - V inv_mu_w_eff(n); + V inv_mu_w_eff(nc); for (int i = 0; i < nc; ++i) { double im = 0; polymer_props_.effectiveInvVisc(c(i), visc, im); @@ -198,11 +198,11 @@ namespace Opm { ADB PolymerPropsAd::effectiveInvWaterVisc(const ADB& c, - const double* visc) + const double* visc) const { const int nc = c.size(); - V inv_mu_w_eff(n); - V dinv_mu_w_eff(n); + V inv_mu_w_eff(nc); + V dinv_mu_w_eff(nc); for (int i = 0; i < nc; ++i) { double im = 0, dim = 0; polymer_props_.effectiveInvViscWithDer(c.value()(i), visc, im, dim); @@ -221,11 +221,10 @@ namespace Opm { - V PolymerPropsAd::polymerWaterVelocityRatio(const V& c) const { const int nc = c.size(); - V mc(n); + V mc(nc); for (int i = 0; i < nc; ++i) { double m = 0; @@ -244,13 +243,13 @@ namespace Opm { { const int nc = c.size(); - V mc(n); - V dmc(n); + V mc(nc); + V dmc(nc); for (int i = 0; i < nc; ++i) { double m = 0; double dm = 0; - polymer_props_.computeMcWithDer(c(i), m, dm); + polymer_props_.computeMcWithDer(c.value()(i), m, dm); mc(i) = m; dmc(i) = dm; diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp index 2d77b228c..f03ccd34c 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include namespace Opm { @@ -65,15 +65,25 @@ namespace Opm { */ typedef AutoDiffBlock ADB; typedef ADB::V V; - PolymerPropsAd(const PolymerPropperties& polymer_props); - ~PolymerPropsAd(); - V PolymerPropsAd::effectiveInvWaterVisc(const V& c,const double* visc) const; - ADB PolymerPropsAd::effectiveInvWaterVisc(const ADB& c,const double* visc) const; - V PolymerPropsAd::polymerWaterVelocityRatio(const V& c) const; - ADB PolymerPropsAd::polymerWaterVelocityRatio(const ADB& c) const; + PolymerPropsAd(const PolymerProperties& polymer_props); + + ~PolymerPropsAd(); + + V + effectiveInvWaterVisc(const V& c,const double* visc) const; + + ADB + effectiveInvWaterVisc(const ADB& c,const double* visc) const; + + V + polymerWaterVelocityRatio(const V& c) const; + + ADB + polymerWaterVelocityRatio(const ADB& c) const; + private: - const PolymerProperties polymer_props_; + const PolymerProperties& polymer_props_; }; From fb12565ddf1cf31e7541dabd2f88c6b49aae3eed Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Mon, 9 Dec 2013 20:34:23 +0800 Subject: [PATCH 05/83] make PolymerState return numPhases. --- opm/polymer/PolymerState.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/opm/polymer/PolymerState.hpp b/opm/polymer/PolymerState.hpp index 6b9d7fd21..7b3016cf9 100644 --- a/opm/polymer/PolymerState.hpp +++ b/opm/polymer/PolymerState.hpp @@ -50,6 +50,10 @@ namespace Opm state2p_.setFirstSat(cells, props, static_cast(es)); } + inline int numPhases() const + { + return state2p_.numPhases(); + } std::vector& pressure () { return state2p_.pressure(); } std::vector& facepressure() { return state2p_.facepressure(); } std::vector& faceflux () { return state2p_.faceflux(); } @@ -66,7 +70,7 @@ namespace Opm TwophaseState& twophaseState() { return state2p_; } const TwophaseState& twophaseState() const { return state2p_; } - + private: TwophaseState state2p_; std::vector concentration_; From 6fc24236dff0d41620deb23c6fc2d350ffd44e4f Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Mon, 9 Dec 2013 22:57:44 +0800 Subject: [PATCH 06/83] fix FullyImplicitTwophasePolymersolver constructor problem. add some input commits for debugging. --- examples/.sim_poly2p_fincomp_ad.cpp.swp | Bin 0 -> 20480 bytes examples/sim_2p_fim.cpp | 262 ++++++++++++++++++ examples/sim_2p_fincomp_ad.cpp | 91 ++++++ examples/sim_poly2p_fincomp_ad.cpp | 146 ++++++++++ .../FullyImplicitTwophasePolymerSolver.cpp | 80 ++++-- .../FullyImplicitTwophasePolymerSolver.hpp | 8 +- opm/polymer/fullyimplicit/PolymerPropsAd.hpp | 1 - 7 files changed, 561 insertions(+), 27 deletions(-) create mode 100644 examples/.sim_poly2p_fincomp_ad.cpp.swp create mode 100644 examples/sim_2p_fim.cpp create mode 100644 examples/sim_2p_fincomp_ad.cpp create mode 100644 examples/sim_poly2p_fincomp_ad.cpp diff --git a/examples/.sim_poly2p_fincomp_ad.cpp.swp b/examples/.sim_poly2p_fincomp_ad.cpp.swp new file mode 100644 index 0000000000000000000000000000000000000000..00e8add3fc22b334f8bb9566a5eba808f776ff56 GIT binary patch literal 20480 zcmeHPYm6jS6)wORFC`KJ{tz#{WT~0yt?r(6VdD6u0YpQ5{XnDNxsR@@?&_IE zK#8eNzNzke&pqedb02ljxo3B|ee1$ecAfQJ1D{I_q9T5&%l?CHnBR>+eU-*m!$z|&Uhhc*??_U)b@xpCOH z9nW(6{T&6=1hoZf3shTRkoETLzu4G2+i4TQuEtgD(s!P!rmahB3)B{fE<^=X><|LUGu(w)(kdP+sQZxu{mj^+*r*k_y!1i>kxnW(KN`?iQ*X^!z zH;a>bFCHuh{2&7Zk@A5&5Oc_AN-?OW&*_?sx;~jW4*7~BtCcAhlenKA@`h&U({Ci1 znA$F)K(Qnfj(l8u-3d~A-A~;nWAv~?jEX32GF9t%X_MiPD4om;M$UZKcAYev7o%6u zktdnY({A^Wm&Qpy^W*3SzUr*|@gO;95^F#5_~w)x=92DK$}v4cep4vT!#)bNTrm#p_nnGkZsZ-f>(zv>>OS3fG7?bww zzL6R+HK?XDS|g?iY9Zi#Q?dMPN2$SowFuoVK##&9}=cgHHX z%%^>2)4W=?WpgyBc_|uo*owc-Qx?U{U3H=r&isgBp@iI7_5&<6gQkK~w6y3dC6*{4 zv~6|vji_8$#+5Acbxx9LRAO54OBl$pf&*+Hn`SPLG9S;y*iX4!gr#P*peN9!6&v)h zt=Ojft4_+X8+NSrv=B5c#HdbHwO4yoxpiB-f~0bT6q$)EazdW=9hb`)(@%KM-`s|P z-i)%ECMCUTq-rqYY`I*#%22$L_?`vAWuDk^Prl(Kr5i_S#!lf+GH|m&!o4G)-7Q+6 z;WV|xE*d#2Jh5DxewwPsM0ixz2y#tkM%b<+?99=f*q!m+qh0#W^5A=Sf;$F7YN2P< z7Ii_dU0t|YEEGMaww*#xsoCgyG#k+trroUGb3{X8RYAB}Bs}Z}em~{ZyF~{%p$XnF z?iw17RhU$P?9KprqaiQO16Y#=wp3pfJuha9q{60AqH8EdcN=1sm_kmR^_9S7UK7qdDS50;@pCFs=GGD zP7A@yaU8Hx&9DPl2NQG_w&fX5x?R`~M#0x3Nj&IFtD8=;g0lwxt~Xf^hYxMEighq& zoHnv#%b;-!9c)D=eUl8ZKPA)Cg3pw(3)!{7z~gKldAKbX2Z5F!x40a|*4Us~tNs4L zsz!rM>~gJPAJSI24?DIzOapS%ag#XRk@ghU@Xk~r_PkRXE8oeft>c_MlAM?0{sYcbr@@n9p_+yep^CowYaw64q*Sxibc|8Dwg!H4RR+e7WpyWIDToF`tcZ91MjY$zhbR6 z4D$aUfo~kcZzlhLM#Uf=hJXKQU=_Fo_!E5l$AB%M3tR&H8h-t^fiu8Ez`ei#_z-Y4 z@G|`SzXIO?9t0i$Qs5-e1}+1hgOC41;4a{Uz#iaf`1qd%2Ebw9UBC;_?+?K5fbRfz z0p#OZSnJsUwkFl+QxGB)Y3QH;Ri@IdNi*Izy{w78z+8AF@ZSsf90i?~ zyQXeq=$c2mfVVoqfTyNAND_+9h2WBZpAo$Cdy@jcoQTaFV4jM2_2NWE0(=Z&dyb)J zA_68NBBX6P$7W`H9mUXb)uC|F$`C!IfFF4kh}1~mqG%C4uW6ECR#LoA&Iioe1I*Rc z?hmkZJb0X_aPj*AW3i#8UH_L_;)oWr;{B zCK*N1b!1UC*U`$XvXw_SAf2`~h2S)-_%T!{nbXe-1bT{0n~leZVcii}2^q0QUpr;~xgD2Ix-ztH5dC zW56NcYT(cC<(~z<349SqflGm}BSygi8#oCt;2hwmxWoSva3637-~;CaqCaoYLqM%o zZGqYXwFPPmydn#1{{qyt6JCU+RH@fHQ>u!;K?+tUO0iacvLOnY)zP*}P zWL9;^0ew#QI%iGog2DZs=yZNeB@>K%RCW#J3%u%7D)Jj&qhu=5vV3HEZ4;`9|Dw#x zgU`QBQWX*E%%}0hs#5J(a#g9eoWny^RZ~QZ8cTJe26-mbNTSt9WjWbur1I?a$VF9Z zLenKrx%+P)?UTBCHTHAt|fSx|CxzzOCog(a?XZi9OdsX{B5&>l&oY_cBl4J2mcbFN~bU@ea90f zgy2-XM0HME!ULQ5+Py)9SJ*JQmR6q#K*$W*6T`45?f0ojW9W8|^I#EeZ$eXK92FR0 s(5hR=7=nxns8v?%71|+I(w+_x!}O;;CvjE8MANFM2 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + + +namespace +{ + void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param) + { + if (param.anyUnused()) { + std::cout << "-------------------- Unused parameters: --------------------\n"; + param.displayUsage(); + std::cout << "----------------------------------------------------------------" << std::endl; + } + } +} // anon namespace + + + +// ----------------- Main program ----------------- +int +main(int argc, char** argv) +try +{ + using namespace Opm; + + std::cout << "\n================ Test program for incompressible two-phase flow ===============\n\n"; + parameter::ParameterGroup param(argc, argv, false); + std::cout << "--------------- Reading parameters ---------------" << std::endl; + + + // If we have a "deck_filename", grid and props will be read from that. + bool use_deck = param.has("deck_filename"); + boost::scoped_ptr deck; + boost::scoped_ptr grid; + boost::scoped_ptr props; + TwophaseState state; + double gravity[3] = { 0.0 }; + if (use_deck) { + std::string deck_filename = param.get("deck_filename"); + deck.reset(new EclipseGridParser(deck_filename)); + // Grid init + grid.reset(new GridManager(*deck)); + // Rock and fluid init + props.reset(new IncompPropsAdFromDeck(*deck, *grid->c_grid())); + // Gravity. + gravity[2] = deck->hasField("NOGRAV") ? 0.0 : unit::gravity; + // Init state variables (saturation and pressure). + int num_cells = grid->c_grid()->number_of_cells; + if (param.has("init_saturation")) { + //initStateBasic(*grid->c_grid(), *props, param, gravity[2], state); + const double init_saturation = param.get("init_saturation"); + for (int c = 0; c < num_cells; ++c) { + state.saturation()[2*c] = init_saturation; + state.saturation()[2*c+1] = 1. - init_saturation; + } + } else { + if (deck->hasField("PRESSURE")) { + // Set saturations from SWAT/SGAS, pressure from PRESSURE. + std::vector& s = state.saturation(); + std::vector& p = state.pressure(); + const std::vector& p_deck = deck->getFloatingPointValue("PRESSURE"); + // water-oil or water-gas: we require SWAT + if (!deck->hasField("SWAT")) { + OPM_THROW(std::runtime_error, "initStateFromDeck(): missing SWAT keyword in 2-phase init"); + } + const std::vector& sw_deck = deck->getFloatingPointValue("SWAT"); + for (int c = 0; c < num_cells; ++c) { + int c_deck = (grid->c_grid()->global_cell == NULL) ? c : grid->c_grid()->global_cell[c]; + s[2*c] = sw_deck[c_deck]; + s[2*c + 1] = 1.0 - sw_deck[c_deck]; + p[c] = p_deck[c_deck]; + } + } + } + } else { + // Grid init. + const int nx = param.getDefault("nx", 100); + const int ny = param.getDefault("ny", 100); + const int nz = param.getDefault("nz", 1); + const double dx = param.getDefault("dx", 1.0); + const double dy = param.getDefault("dy", 1.0); + const double dz = param.getDefault("dz", 1.0); + grid.reset(new GridManager(nx, ny, nz, dx, dy, dz)); + // Rock and fluid init. + props.reset(new IncompPropsAdBasic(param, grid->c_grid()->dimensions, grid->c_grid()->number_of_cells)); + // Rock compressibility. + // Gravity. + gravity[2] = param.getDefault("gravity", 0.0); + int num_cells = grid->c_grid()->number_of_cells; + } + + // Warn if gravity but no density difference. + bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); + const double *grav = use_gravity ? &gravity[0] : 0; + + // Initialising src + std::vector src(num_cells, 0.0); + if (use_deck) { + // Do nothing, wells will be the driving force, not source terms. + if (deck->hasField("SRC")) { + const std::vector& src_deck = deck->getFloatingPointValue("SRC"); + for (int c = 0; c < num_cells; ++c) { + int c_deck = (grid->c_grid()->global_cell == NULL) ? c : grid->c_grid()->global_cell[c]; + src[c] = src_deck[c_deck]; + } + } + } else { + // Compute pore volumes, in order to enable specifying injection rate + // terms of total pore volume. + std::vector porevol; + computePorevolume(*grid->c_grid(), props->porosity(), porevol); + const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); + const double default_injection = use_gravity ? 0.0 : 0.1; + const double flow_per_sec = param.getDefault("injected_porevolumes_per_day", default_injection) + *tot_porevol_init/unit::day; + src[0] = flow_per_sec; + src[num_cells - 1] = -flow_per_sec; + } + + in: num_cells = grid->c_grid()->number_of_cells; + + // Linear solver. + LinearSolverFactory linsolver(param); + + // Write parameters used for later reference. + bool output = param.getDefault("output", true); + std::ofstream epoch_os; + std::string output_dir; + if (output) { + output_dir = + param.getDefault("output_dir", std::string("output")); + boost::filesystem::path fpath(output_dir); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + std::string filename = output_dir + "/epoch_timing.param"; + epoch_os.open(filename.c_str(), std::fstream::trunc | std::fstream::out); + // open file to clean it. The file is appended to in SimulatorTwophase + filename = output_dir + "/step_timing.param"; + std::fstream step_os(filename.c_str(), std::fstream::trunc | std::fstream::out); + step_os.close(); + param.writeParam(output_dir + "/simulation.param"); + } + + + std::cout << "\n\n================ Starting main simulation loop ===============\n" + << " (number of epochs: " + << (use_deck ? deck->numberOfEpochs() : 1) << ")\n\n" << std::flush; + + SimulatorReport rep; + if (!use_deck) { + // Simple simulation without a deck. + SimulatorFullyImplicitTwophase simulator(param, + *grid->c_grid(), + *props, + linsolver, + src); + SimulatorTimer simtimer; + simtimer.init(param); + warnIfUnusedParams(param); + rep = simulator.run(simtimer, state, src); + } else { + // With a deck, we may have more epochs etc. + int step = 0; + SimulatorTimer simtimer; + // Use timer for last epoch to obtain total time. + deck->setCurrentEpoch(deck->numberOfEpochs() - 1); + simtimer.init(*deck); + const double total_time = simtimer.totalTime(); + for (int epoch = 0; epoch < deck->numberOfEpochs(); ++epoch) { + // Set epoch index. + deck->setCurrentEpoch(epoch); + + // Update the timer. + if (deck->hasField("TSTEP")) { + simtimer.init(*deck); + } else { + if (epoch != 0) { + OPM_THROW(std::runtime_error, "No TSTEP in deck for epoch " << epoch); + } + simtimer.init(param); + } + simtimer.setCurrentStepNum(step); + simtimer.setTotalTime(total_time); + + // Report on start of epoch. + std::cout << "\n\n-------------- Starting epoch " << epoch << " --------------" + << "\n (number of steps: " + << simtimer.numSteps() - step << ")\n\n" << std::flush; + + + // Create and run simulator. + SimulatorFullyImplicitTwophase simulator(param, + *grid->c_grid(), + *props, + linsolver, + src); + if (epoch == 0) { + warnIfUnusedParams(param); + } + SimulatorReport epoch_rep = simulator.run(simtimer, state, src); + if (output) { + epoch_rep.reportParam(epoch_os); + } + // Update total timing report and remember step number. + rep += epoch_rep; + step = simtimer.currentStepNum(); + } + } + + std::cout << "\n\n================ End of simulation ===============\n\n"; + rep.report(std::cout); + + if (output) { + std::string filename = output_dir + "/walltime.param"; + std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out); + rep.reportParam(tot_os); + } + +} +catch (const std::exception &e) { + std::cerr << "Program threw an exception: " << e.what() << "\n"; + throw; +} + diff --git a/examples/sim_2p_fincomp_ad.cpp b/examples/sim_2p_fincomp_ad.cpp new file mode 100644 index 000000000..544ccb8c2 --- /dev/null +++ b/examples/sim_2p_fincomp_ad.cpp @@ -0,0 +1,91 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + +#include +#include +#include + +int main (int argc, char** argv) +try +{ + int nx = 20; + int ny = 20; + int nz = 1; + double dx = 10.0; + double dy = 10.0; + double dz = 10.0; + using namespace Opm; + parameter::ParameterGroup param(argc, argv, false); + GridManager grid_manager(nx, ny, nz, dx, dy, dz); + const UnstructuredGrid& grid = *grid_manager.c_grid(); + int num_cells = grid.number_of_cells; + int num_phases = 2; + using namespace Opm::unit; + using namespace Opm::prefix; + std::vector density(num_phases, 1000.0); + std::vector viscosity(num_phases, 1.0*centi*Poise); + double porosity = 0.5; + double permeability = 10.0*milli*darcy; + SaturationPropsBasic::RelPermFunc rel_perm_func = SaturationPropsBasic::Linear; + IncompPropsAdBasic props(num_phases, rel_perm_func, density, viscosity, + porosity, permeability, grid.dimensions, num_cells); + std::vector omega; + std::vector src(num_cells, 0.0); + src[0] = 1.; + src[num_cells-1] = -1.; + + FlowBCManager bcs; + LinearSolverUmfpack linsolver; + FullyImplicitTwoPhaseSolver solver(grid, props, linsolver); + std::vector porevol; + Opm::computePorevolume(grid, props.porosity(), porevol); + const double dt = param.getDefault("dt", 0.1) * day; + const int num_time_steps = param.getDefault("nsteps", 20); + std::vector allcells(num_cells); + for (int cell = 0; cell < num_cells; ++cell) { + allcells[cell] = cell; + } + TwophaseState state; + state.init(grid, 2); + + //initial sat + for (int c = 0; c < num_cells; ++c) { + state.saturation()[2*c] = 0.2; + state.saturation()[2*c+1] = 0.8; + } + std::vector p(num_cells, 200*Opm::unit::barsa); + state.pressure() = p; +// state.setFirstSat(allcells, props, TwophaseState::MinSat); + std::ostringstream vtkfilename; + + for (int i = 0; i < num_time_steps; ++i) { + solver.step(dt, state, src); + vtkfilename.str(""); + vtkfilename << "sim_2p_fincomp-" << std::setw(3) << std::setfill('0') << i << ".vtu"; + std::ofstream vtkfile(vtkfilename.str().c_str()); + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + Opm::writeVtkData(grid, dm, vtkfile); + } +} +catch (const std::exception &e) { + std::cerr << "Program threw an exception: " << e.what() << "\n"; + throw; +} diff --git a/examples/sim_poly2p_fincomp_ad.cpp b/examples/sim_poly2p_fincomp_ad.cpp new file mode 100644 index 000000000..80e5f3e6a --- /dev/null +++ b/examples/sim_poly2p_fincomp_ad.cpp @@ -0,0 +1,146 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +int main (int argc, char** argv) +try +{ + using namespace Opm; + parameter::ParameterGroup param(argc, argv, false); + bool use_poly_deck = param.has("deck_filename"); + if (!use_poly_deck) { + OPM_THROW(std::runtime_error, "Polymer Properties must be read from deck_filename\n"); + } + std::string deck_filename = param.get("deck_filename"); + EclipseGridParser deck = EclipseGridParser(deck_filename); + int nx = param.getDefault("nx", 20); + int ny = param.getDefault("ny", 20); + int nz = 1; + double dx = 2.0; + double dy = 2.0; + double dz = 0.5; + GridManager grid_manager(nx, ny, nz, dx, dy, dz); + const UnstructuredGrid& grid = *grid_manager.c_grid(); + int num_cells = grid.number_of_cells; + int num_phases = 2; + using namespace Opm::unit; + using namespace Opm::prefix; + std::vector density(num_phases, 1000.0); + std::vector viscosity(num_phases, 1.0*centi*Poise); + viscosity[0] = 0.5 * centi * Poise; + viscosity[0] = 5 * centi * Poise; + double porosity = 0.35; + double permeability = 10.0*milli*darcy; + SaturationPropsBasic::RelPermFunc rel_perm_func = SaturationPropsBasic::Linear; + IncompPropsAdBasic props(num_phases, rel_perm_func, density, viscosity, + porosity, permeability, grid.dimensions, num_cells); + + // Init polymer properties. + // Setting defaults to provide a simple example case. + PolymerProperties polymer_props(deck); + #if 0 + if (use_poly_deck) { + } else { + double c_max = param.getDefault("c_max_limit", 5.0); + double mix_param = param.getDefault("mix_param", 1.0); + double rock_density = param.getDefault("rock_density", 1000.0); + double dead_pore_vol = param.getDefault("dead_pore_vol", 0.15); + double res_factor = param.getDefault("res_factor", 1.) ; // res_factor = 1 gives no change in permeability + double c_max_ads = param.getDefault("c_max_ads", 1.); + int ads_index = param.getDefault("ads_index", Opm::PolymerProperties::NoDesorption); + std::vector c_vals_visc(2, -1e100); + c_vals_visc[0] = 0.0; + c_vals_visc[1] = 7.0; + std::vector visc_mult_vals(2, -1e100); + visc_mult_vals[0] = 1.0; + // poly_props.visc_mult_vals[1] = param.getDefault("c_max_viscmult", 30.0); + visc_mult_vals[1] = 20.0; + std::vector c_vals_ads(3, -1e100); + c_vals_ads[0] = 0.0; + c_vals_ads[1] = 2.0; + c_vals_ads[2] = 8.0; + std::vector ads_vals(3, -1e100); + ads_vals[0] = 0.0; + ads_vals[1] = 0.0015; + ads_vals[2] = 0.0025; + PolymerProperties polymer_props; + polymer_props.set(c_max, mix_param, rock_density, dead_pore_vol, res_factor, c_max_ads, + static_cast(ads_index), + c_vals_visc, visc_mult_vals, c_vals_ads, ads_vals); + } + #endif + PolymerPropsAd polymer_props_ad(polymer_props); + std::vector omega; + std::vector src(num_cells, 0.0); + std::vector src_polymer(num_cells); + src[0] = 10. / day; + src[num_cells-1] = -1. / day; + + PolymerInflowBasic polymer_inflow(param.getDefault("poly_start_days", 30.0)*Opm::unit::day, + param.getDefault("poly_end_days", 80.0)*Opm::unit::day, + param.getDefault("poly_amount", polymer_props.cMax())); + FlowBCManager bcs; + LinearSolverUmfpack linsolver; + FullyImplicitTwophasePolymerSolver solver(grid, props,polymer_props_ad, linsolver); + std::vector porevol; + Opm::computePorevolume(grid, props.porosity(), porevol); + const double dt = param.getDefault("dt", 10.) * day; + const int num_time_steps = param.getDefault("nsteps", 10); + std::vector allcells(num_cells); + for (int cell = 0; cell < num_cells; ++cell) { + allcells[cell] = cell; + } + PolymerState state; + state.init(grid, 2); + //initial sat + for (int c = 0; c < num_cells; ++c) { + state.saturation()[2*c] = 0.2; + state.saturation()[2*c+1] = 0.8; + } + std::vector p(num_cells, 200*Opm::unit::barsa); + state.pressure() = p; + + std::vector c(num_cells, 0.0); + state.concentration() = c; + std::ostringstream vtkfilename; + double currentime = 0; + for (int i = 0; i < num_time_steps; ++i) { + currentime += dt; + polymer_inflow.getInflowValues(currentime, currentime+dt, src_polymer); + solver.step(dt, state, src, src_polymer); + vtkfilename.str(""); + vtkfilename << "sim_poly2p_fincomp_ad_" << std::setw(3) << std::setfill('0') << i << ".vtu"; + std::ofstream vtkfile(vtkfilename.str().c_str()); + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + Opm::writeVtkData(grid, dm, vtkfile); + } +} +catch (const std::exception &e) { + std::cerr << "Program threw an exception: " << e.what() << "\n"; + throw; +} diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index 179fd8253..8e0b90f48 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -64,11 +64,11 @@ typedef Eigen::Array(fluid.numPhases() + 1, ADB::null())) + , residual_(std::vector(3, ADB::null())) { } @@ -82,7 +82,6 @@ typedef Eigen::Array& src, const std::vector& polymer_inflow) - // const bool if_polymer_actived) { V pvol(grid_.number_of_cells); @@ -100,6 +99,10 @@ typedef Eigen::Array(& x.saturation()[0], nc, np); for (int phase = 0; phase < np; ++phase) { state.saturation[phase] = ADB::constant(s_all.col(phase)); - // state.saturation[1] = ADB::constant(s_all.col(1)); } // Concentration - assert(not x.concentration().empty()); const V c = Eigen::Map(&x.concentration()[0], nc); @@ -256,29 +257,28 @@ typedef Eigen::Array(&polymer_inflow[0], nc); + const ADB src_polymer = polymerSource(kr ,src, polymer_inflow, state); ADB mc = computeMc(state); ADB mflux = computeMassFlux(0, trans, kr, state); residual_[2] = pvdt * (state.saturation[0] * state.concentration - old_state.saturation[0] * old_state.concentration) + ops_.div * state.concentration * mc * mflux - src_polymer; - } else { - residual_[2] = ADB::constant(V::Zero(nc)); - } + // } else { + // residual_[2] = ADB::constant(V::Zero(nc)); +// } + for (int i = 0; i < 3; ++i) + std::cout<<"residual_["<& polymer_inflow) const + polymerInjectedAmount(const std::vector& polymer_inflow) const { double amount = 0; for (int i = 0; i < int(polymer_inflow.size()); ++i) { @@ -292,7 +292,8 @@ typedef Eigen::Array& kr, - const std::vector& src) const + const std::vector& src + ) const { //extract the source to out and in source. std::vector outsrc; @@ -313,7 +314,6 @@ typedef Eigen::Array(& src[0], grid_.number_of_cells); const V outSrc = Eigen::Map(& outsrc[0], grid_.number_of_cells); const V inSrc = Eigen::Map(& insrc[0], grid_.number_of_cells); - // compute the out-fracflow. ADB f_out = computeFracFlow(phase, kr); // compute the in-fracflow. @@ -323,11 +323,44 @@ typedef Eigen::Array& kr, + const std::vector& src, + const std::vector& polymer_inflow_c, + const SolutionState& state) const + { + //extract the source to out and in source. + std::vector outsrc; + std::vector insrc; + std::vector::const_iterator it; + for (it = src.begin(); it != src.end(); ++it) { + if (*it < 0) { + outsrc.push_back(*it); + insrc.push_back(0.0); + } else if (*it > 0) { + insrc.push_back(*it); + outsrc.push_back(0.0); + } else { + outsrc.emplace_back(0); + insrc.emplace_back(0); + } + } + const V source = Eigen::Map(& src[0], grid_.number_of_cells); + const V outSrc = Eigen::Map(& outsrc[0], grid_.number_of_cells); + const V inSrc = Eigen::Map(& insrc[0], grid_.number_of_cells); + const V polyin = Eigen::Map(& polymer_inflow_c[0], grid_.number_of_cells); + // compute the out-fracflow. + ADB f_out = computeFracFlow(0, kr); + // compute the in-fracflow. + V f_in = V::Ones(grid_.number_of_cells); + return f_out * outSrc * state.concentration + f_in * inSrc * polyin ; + } ADB @@ -355,8 +388,8 @@ typedef Eigen::Array matr = mass_res.derivative()[0]; V dx(V::Zero(mass_res.size())); @@ -391,7 +424,6 @@ typedef Eigen::Array& kr, + const std::vector& src, + const std::vector& polymer_inflow_c, + const SolutionState& state) const; ADB computeMc(const SolutionState& state) const; @@ -100,7 +104,7 @@ namespace Opm { ADB transMult(const ADB& p) const; double - PolymerInjectedAmount(const std::vector& polymer_inflow) const; + polymerInjectedAmount(const std::vector& polymer_inflow) const; }; } // namespace Opm #endif// OPM_FULLYIMPLICITTWOPHASESOLVER_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp index f03ccd34c..69936843d 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp @@ -65,7 +65,6 @@ namespace Opm { */ typedef AutoDiffBlock ADB; typedef ADB::V V; - PolymerPropsAd(const PolymerProperties& polymer_props); ~PolymerPropsAd(); From a358da7afaf388fea0c480906ca8a0fbb93fad7c Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 10 Dec 2013 20:36:30 +0800 Subject: [PATCH 07/83] fix the polymer source bug, warnning: water initial saturation should not bt zero when running this simulator. --- examples/.sim_poly2p_fincomp_ad.cpp.swp | Bin 20480 -> 0 bytes examples/sim_2p_fincomp_ad.cpp | 28 +++--- examples/sim_poly2p_fincomp_ad.cpp | 21 ++-- .../FullyImplicitTwophasePolymerSolver.cpp | 91 +++++++++--------- .../FullyImplicitTwophasePolymerSolver.hpp | 12 ++- 5 files changed, 79 insertions(+), 73 deletions(-) delete mode 100644 examples/.sim_poly2p_fincomp_ad.cpp.swp diff --git a/examples/.sim_poly2p_fincomp_ad.cpp.swp b/examples/.sim_poly2p_fincomp_ad.cpp.swp deleted file mode 100644 index 00e8add3fc22b334f8bb9566a5eba808f776ff56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 zcmeHPYm6jS6)wORFC`KJ{tz#{WT~0yt?r(6VdD6u0YpQ5{XnDNxsR@@?&_IE zK#8eNzNzke&pqedb02ljxo3B|ee1$ecAfQJ1D{I_q9T5&%l?CHnBR>+eU-*m!$z|&Uhhc*??_U)b@xpCOH z9nW(6{T&6=1hoZf3shTRkoETLzu4G2+i4TQuEtgD(s!P!rmahB3)B{fE<^=X><|LUGu(w)(kdP+sQZxu{mj^+*r*k_y!1i>kxnW(KN`?iQ*X^!z zH;a>bFCHuh{2&7Zk@A5&5Oc_AN-?OW&*_?sx;~jW4*7~BtCcAhlenKA@`h&U({Ci1 znA$F)K(Qnfj(l8u-3d~A-A~;nWAv~?jEX32GF9t%X_MiPD4om;M$UZKcAYev7o%6u zktdnY({A^Wm&Qpy^W*3SzUr*|@gO;95^F#5_~w)x=92DK$}v4cep4vT!#)bNTrm#p_nnGkZsZ-f>(zv>>OS3fG7?bww zzL6R+HK?XDS|g?iY9Zi#Q?dMPN2$SowFuoVK##&9}=cgHHX z%%^>2)4W=?WpgyBc_|uo*owc-Qx?U{U3H=r&isgBp@iI7_5&<6gQkK~w6y3dC6*{4 zv~6|vji_8$#+5Acbxx9LRAO54OBl$pf&*+Hn`SPLG9S;y*iX4!gr#P*peN9!6&v)h zt=Ojft4_+X8+NSrv=B5c#HdbHwO4yoxpiB-f~0bT6q$)EazdW=9hb`)(@%KM-`s|P z-i)%ECMCUTq-rqYY`I*#%22$L_?`vAWuDk^Prl(Kr5i_S#!lf+GH|m&!o4G)-7Q+6 z;WV|xE*d#2Jh5DxewwPsM0ixz2y#tkM%b<+?99=f*q!m+qh0#W^5A=Sf;$F7YN2P< z7Ii_dU0t|YEEGMaww*#xsoCgyG#k+trroUGb3{X8RYAB}Bs}Z}em~{ZyF~{%p$XnF z?iw17RhU$P?9KprqaiQO16Y#=wp3pfJuha9q{60AqH8EdcN=1sm_kmR^_9S7UK7qdDS50;@pCFs=GGD zP7A@yaU8Hx&9DPl2NQG_w&fX5x?R`~M#0x3Nj&IFtD8=;g0lwxt~Xf^hYxMEighq& zoHnv#%b;-!9c)D=eUl8ZKPA)Cg3pw(3)!{7z~gKldAKbX2Z5F!x40a|*4Us~tNs4L zsz!rM>~gJPAJSI24?DIzOapS%ag#XRk@ghU@Xk~r_PkRXE8oeft>c_MlAM?0{sYcbr@@n9p_+yep^CowYaw64q*Sxibc|8Dwg!H4RR+e7WpyWIDToF`tcZ91MjY$zhbR6 z4D$aUfo~kcZzlhLM#Uf=hJXKQU=_Fo_!E5l$AB%M3tR&H8h-t^fiu8Ez`ei#_z-Y4 z@G|`SzXIO?9t0i$Qs5-e1}+1hgOC41;4a{Uz#iaf`1qd%2Ebw9UBC;_?+?K5fbRfz z0p#OZSnJsUwkFl+QxGB)Y3QH;Ri@IdNi*Izy{w78z+8AF@ZSsf90i?~ zyQXeq=$c2mfVVoqfTyNAND_+9h2WBZpAo$Cdy@jcoQTaFV4jM2_2NWE0(=Z&dyb)J zA_68NBBX6P$7W`H9mUXb)uC|F$`C!IfFF4kh}1~mqG%C4uW6ECR#LoA&Iioe1I*Rc z?hmkZJb0X_aPj*AW3i#8UH_L_;)oWr;{B zCK*N1b!1UC*U`$XvXw_SAf2`~h2S)-_%T!{nbXe-1bT{0n~leZVcii}2^q0QUpr;~xgD2Ix-ztH5dC zW56NcYT(cC<(~z<349SqflGm}BSygi8#oCt;2hwmxWoSva3637-~;CaqCaoYLqM%o zZGqYXwFPPmydn#1{{qyt6JCU+RH@fHQ>u!;K?+tUO0iacvLOnY)zP*}P zWL9;^0ew#QI%iGog2DZs=yZNeB@>K%RCW#J3%u%7D)Jj&qhu=5vV3HEZ4;`9|Dw#x zgU`QBQWX*E%%}0hs#5J(a#g9eoWny^RZ~QZ8cTJe26-mbNTSt9WjWbur1I?a$VF9Z zLenKrx%+P)?UTBCHTHAt|fSx|CxzzOCog(a?XZi9OdsX{B5&>l&oY_cBl4J2mcbFN~bU@ea90f zgy2-XM0HME!ULQ5+Py)9SJ*JQmR6q#K*$W*6T`45?f0ojW9W8|^I#EeZ$eXK92FR0 s(5hR=7=nxns8v?%71|+I(w+_x!}O;;CvjE8MANFM2 density(num_phases, 1000.0); std::vector viscosity(num_phases, 1.0*centi*Poise); - double porosity = 0.5; + viscosity[0] = 0.5 * centi * Poise; + viscosity[1] = 5 * centi * Poise; + double porosity = 0.35; double permeability = 10.0*milli*darcy; SaturationPropsBasic::RelPermFunc rel_perm_func = SaturationPropsBasic::Linear; IncompPropsAdBasic props(num_phases, rel_perm_func, density, viscosity, porosity, permeability, grid.dimensions, num_cells); std::vector omega; std::vector src(num_cells, 0.0); - src[0] = 1.; - src[num_cells-1] = -1.; + src[0] = 10. / day; + src[num_cells-1] = -10. / day; FlowBCManager bcs; LinearSolverUmfpack linsolver; FullyImplicitTwoPhaseSolver solver(grid, props, linsolver); std::vector porevol; Opm::computePorevolume(grid, props.porosity(), porevol); - const double dt = param.getDefault("dt", 0.1) * day; - const int num_time_steps = param.getDefault("nsteps", 20); + const double dt = param.getDefault("dt", 10) * day; + const int num_time_steps = param.getDefault("nsteps", 10); std::vector allcells(num_cells); for (int cell = 0; cell < num_cells; ++cell) { allcells[cell] = cell; @@ -66,10 +68,10 @@ try //initial sat for (int c = 0; c < num_cells; ++c) { - state.saturation()[2*c] = 0.2; - state.saturation()[2*c+1] = 0.8; + state.saturation()[2*c] = 0; + state.saturation()[2*c+1] = 1; } - std::vector p(num_cells, 200*Opm::unit::barsa); + std::vector p(num_cells, 100*Opm::unit::barsa); state.pressure() = p; // state.setFirstSat(allcells, props, TwophaseState::MinSat); std::ostringstream vtkfilename; diff --git a/examples/sim_poly2p_fincomp_ad.cpp b/examples/sim_poly2p_fincomp_ad.cpp index 80e5f3e6a..f1262e1f9 100644 --- a/examples/sim_poly2p_fincomp_ad.cpp +++ b/examples/sim_poly2p_fincomp_ad.cpp @@ -36,12 +36,12 @@ try } std::string deck_filename = param.get("deck_filename"); EclipseGridParser deck = EclipseGridParser(deck_filename); - int nx = param.getDefault("nx", 20); - int ny = param.getDefault("ny", 20); + int nx = param.getDefault("nx", 30); + int ny = param.getDefault("ny", 30); int nz = 1; - double dx = 2.0; - double dy = 2.0; - double dz = 0.5; + double dx = 10.0; + double dy = 1.0; + double dz = 1.0; GridManager grid_manager(nx, ny, nz, dx, dy, dz); const UnstructuredGrid& grid = *grid_manager.c_grid(); int num_cells = grid.number_of_cells; @@ -51,7 +51,7 @@ try std::vector density(num_phases, 1000.0); std::vector viscosity(num_phases, 1.0*centi*Poise); viscosity[0] = 0.5 * centi * Poise; - viscosity[0] = 5 * centi * Poise; + viscosity[1] = 5 * centi * Poise; double porosity = 0.35; double permeability = 10.0*milli*darcy; SaturationPropsBasic::RelPermFunc rel_perm_func = SaturationPropsBasic::Linear; @@ -97,10 +97,10 @@ try std::vector src(num_cells, 0.0); std::vector src_polymer(num_cells); src[0] = 10. / day; - src[num_cells-1] = -1. / day; + src[num_cells-1] = -10. / day; - PolymerInflowBasic polymer_inflow(param.getDefault("poly_start_days", 30.0)*Opm::unit::day, - param.getDefault("poly_end_days", 80.0)*Opm::unit::day, + PolymerInflowBasic polymer_inflow(param.getDefault("poly_start_days", 300.0)*Opm::unit::day, + param.getDefault("poly_end_days", 800.0)*Opm::unit::day, param.getDefault("poly_amount", polymer_props.cMax())); FlowBCManager bcs; LinearSolverUmfpack linsolver; @@ -120,7 +120,7 @@ try state.saturation()[2*c] = 0.2; state.saturation()[2*c+1] = 0.8; } - std::vector p(num_cells, 200*Opm::unit::barsa); + std::vector p(num_cells, 100*Opm::unit::barsa); state.pressure() = p; std::vector c(num_cells, 0.0); @@ -137,6 +137,7 @@ try Opm::DataMap dm; dm["saturation"] = &state.saturation(); dm["pressure"] = &state.pressure(); + dm["concentration"] = &state.concentration(); Opm::writeVtkData(grid, dm, vtkfile); } } diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index 8e0b90f48..f6ba27bcb 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -97,13 +97,8 @@ typedef Eigen::Array& src, const std::vector& polymer_inflow) - // const bool if_polymer_actived) { // Create the primary variables. const SolutionState state = variableState(x); @@ -256,44 +250,33 @@ typedef Eigen::Array& polymer_inflow) const - { - double amount = 0; - for (int i = 0; i < int(polymer_inflow.size()); ++i) { - amount += polymer_inflow[i]; - } - return amount; - } - - ADB FullyImplicitTwophasePolymerSolver::accumSource(const int phase, const std::vector& kr, - const std::vector& src - ) const + const std::vector& src) const { //extract the source to out and in source. std::vector outsrc; @@ -329,8 +312,7 @@ typedef Eigen::Array& kr, + polymerSource(const std::vector& kr, const std::vector& src, const std::vector& polymer_inflow_c, const SolutionState& state) const @@ -359,13 +341,15 @@ typedef Eigen::Array& kr) const + FullyImplicitTwophasePolymerSolver::computeFracFlow(int phase, + const std::vector& kr) const { const double* mus = fluid_.viscosity(); ADB mob_phase = kr[phase] / V::Constant(kr[phase].size(), 1, mus[phase]); @@ -498,8 +482,25 @@ typedef Eigen::Array& kr, + const SolutionState& state) const + { + const double* mus = fluid_.viscosity(); + ADB water_mob = kr[0] / V::Constant(kr[0].size(), 1, mus[0]); + ADB poly_mob = state.concentration * mc * water_mob; + + const ADB dp = ops_.ngrad * state.pressure; + const ADB head = trans * dp; + + UpwindSelector upwind(grid_, ops_, head.value()); + + return upwind.select(poly_mob) * head; + } double diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp index 65ad86e3b..6205f9bde 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp @@ -30,7 +30,6 @@ namespace Opm { const std::vector& src, const std::vector& polymer_inflow ); -// const bool if_polymer_actived); private: typedef AutoDiffBlock ADB; typedef ADB::V V; @@ -44,7 +43,6 @@ namespace Opm { ADB pressure; std::vector saturation; ADB concentration; -// ADB cmax; }; const UnstructuredGrid& grid_; const IncompPropsAdInterface& fluid_; @@ -65,7 +63,6 @@ namespace Opm { const PolymerState& x, const std::vector& src, const std::vector& polymer_inflow); -// const bool if_polymer_actived); V solveJacobianSystem() const; void updateState(const V& dx, PolymerState& x) const; @@ -85,6 +82,11 @@ namespace Opm { const V& trans, const std::vector& kr, const SolutionState& state) const; + ADB + computePolymerMassFlux(const V& trans, + const ADB& mc, + const std::vector& kr, + const SolutionState& state) const; double residualNorm() const; ADB @@ -103,8 +105,8 @@ namespace Opm { fluidDensity(const int phase) const; ADB transMult(const ADB& p) const; - double - polymerInjectedAmount(const std::vector& polymer_inflow) const; }; + + } // namespace Opm #endif// OPM_FULLYIMPLICITTWOPHASESOLVER_HEADER_INCLUDED From 11ea882ed8067301d0c52d717b9103add7e4e355 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Wed, 11 Dec 2013 18:27:05 +0800 Subject: [PATCH 08/83] remove some output commits. get values from command line for exapmes. --- examples/sim_2p_fincomp_ad.cpp | 33 +++++++++++------- examples/sim_poly2p_fincomp_ad.cpp | 20 ++++++++--- .../FullyImplicitTwoPhaseSolver.cpp | 3 +- .../FullyImplicitTwophasePolymerSolver.cpp | 34 +++++++++---------- 4 files changed, 54 insertions(+), 36 deletions(-) diff --git a/examples/sim_2p_fincomp_ad.cpp b/examples/sim_2p_fincomp_ad.cpp index 1b7f1679f..17f75425b 100644 --- a/examples/sim_2p_fincomp_ad.cpp +++ b/examples/sim_2p_fincomp_ad.cpp @@ -24,14 +24,14 @@ int main (int argc, char** argv) try { - int nx = 30; - int ny = 30; - int nz = 1; - double dx = 2.0; - double dy = 2.0; - double dz = 0.5; using namespace Opm; parameter::ParameterGroup param(argc, argv, false); + int nx = param.getDefault("nx", 30); + int ny = param.getDefault("ny", 1); + int nz = 1; + double dx = 10./nx; + double dy = 1.0; + double dz = 1.0; GridManager grid_manager(nx, ny, nz, dx, dy, dz); const UnstructuredGrid& grid = *grid_manager.c_grid(); int num_cells = grid.number_of_cells; @@ -49,15 +49,15 @@ try porosity, permeability, grid.dimensions, num_cells); std::vector omega; std::vector src(num_cells, 0.0); - src[0] = 10. / day; - src[num_cells-1] = -10. / day; + src[0] = 1. / day; + src[num_cells-1] = -1. / day; FlowBCManager bcs; LinearSolverUmfpack linsolver; FullyImplicitTwoPhaseSolver solver(grid, props, linsolver); std::vector porevol; Opm::computePorevolume(grid, props.porosity(), porevol); - const double dt = param.getDefault("dt", 10) * day; + const double dt = param.getDefault("dt", 10.) * day; const int num_time_steps = param.getDefault("nsteps", 10); std::vector allcells(num_cells); for (int cell = 0; cell < num_cells; ++cell) { @@ -68,18 +68,25 @@ try //initial sat for (int c = 0; c < num_cells; ++c) { - state.saturation()[2*c] = 0; - state.saturation()[2*c+1] = 1; + state.saturation()[2*c] = 0.2; + state.saturation()[2*c+1] = 0.8; } std::vector p(num_cells, 100*Opm::unit::barsa); state.pressure() = p; -// state.setFirstSat(allcells, props, TwophaseState::MinSat); std::ostringstream vtkfilename; + // Write the initial state. + vtkfilename.str(""); + vtkfilename << "sim_2p_fincomp_" << std::setw(3) << std::setfill('0') << 0 << ".vtu"; + std::ofstream vtkfile(vtkfilename.str().c_str()); + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + Opm::writeVtkData(grid, dm, vtkfile); for (int i = 0; i < num_time_steps; ++i) { solver.step(dt, state, src); vtkfilename.str(""); - vtkfilename << "sim_2p_fincomp-" << std::setw(3) << std::setfill('0') << i << ".vtu"; + vtkfilename << "sim_2p_fincomp_" << std::setw(3) << std::setfill('0') << i + 1 << ".vtu"; std::ofstream vtkfile(vtkfilename.str().c_str()); Opm::DataMap dm; dm["saturation"] = &state.saturation(); diff --git a/examples/sim_poly2p_fincomp_ad.cpp b/examples/sim_poly2p_fincomp_ad.cpp index f1262e1f9..68efdb751 100644 --- a/examples/sim_poly2p_fincomp_ad.cpp +++ b/examples/sim_poly2p_fincomp_ad.cpp @@ -39,7 +39,7 @@ try int nx = param.getDefault("nx", 30); int ny = param.getDefault("ny", 30); int nz = 1; - double dx = 10.0; + double dx = 10./nx; double dy = 1.0; double dz = 1.0; GridManager grid_manager(nx, ny, nz, dx, dy, dz); @@ -60,7 +60,7 @@ try // Init polymer properties. // Setting defaults to provide a simple example case. - PolymerProperties polymer_props(deck); + PolymerProperties polymer_props(deck); #if 0 if (use_poly_deck) { } else { @@ -96,8 +96,8 @@ try std::vector omega; std::vector src(num_cells, 0.0); std::vector src_polymer(num_cells); - src[0] = 10. / day; - src[num_cells-1] = -10. / day; + src[0] = param.getDefault("insrc", 1.) / day; + src[num_cells-1] = -param.getDefault("insrc", 1.) / day; PolymerInflowBasic polymer_inflow(param.getDefault("poly_start_days", 300.0)*Opm::unit::day, param.getDefault("poly_end_days", 800.0)*Opm::unit::day, @@ -127,12 +127,22 @@ try state.concentration() = c; std::ostringstream vtkfilename; double currentime = 0; + + // Write the initial state. + vtkfilename.str(""); + vtkfilename << "sim_poly2p_fincomp_ad_" << std::setw(3) << std::setfill('0') << 0<< ".vtu"; + std::ofstream vtkfile(vtkfilename.str().c_str()); + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + dm["concentration"] = &state.concentration(); + Opm::writeVtkData(grid, dm, vtkfile); for (int i = 0; i < num_time_steps; ++i) { currentime += dt; polymer_inflow.getInflowValues(currentime, currentime+dt, src_polymer); solver.step(dt, state, src, src_polymer); vtkfilename.str(""); - vtkfilename << "sim_poly2p_fincomp_ad_" << std::setw(3) << std::setfill('0') << i << ".vtu"; + vtkfilename << "sim_poly2p_fincomp_ad_" << std::setw(3) << std::setfill('0') << i + 1<< ".vtu"; std::ofstream vtkfile(vtkfilename.str().c_str()); Opm::DataMap dm; dm["saturation"] = &state.saturation(); diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp index 60e7a11be..cf03d56f3 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp @@ -396,7 +396,8 @@ typedef Eigen::Array(&x.concentration()[0], nc); - state.concentration = ADB::constant(c); + return state; } @@ -246,25 +246,16 @@ typedef Eigen::Array(& state.saturation()[0], nc, np); @@ -472,8 +462,18 @@ typedef Eigen::Array Date: Wed, 11 Dec 2013 22:52:11 +0800 Subject: [PATCH 09/83] add adsorption and effective relperm functions. --- .../fullyimplicit/.AutoDiffBlock.hpp.swp | Bin 0 -> 16384 bytes .../FullyImplicitTwophasePolymerSolver.cpp | 2 - opm/polymer/fullyimplicit/PolymerPropsAd.cpp | 93 +++++++++++++++++- opm/polymer/fullyimplicit/PolymerPropsAd.hpp | 11 +++ 4 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 opm/polymer/fullyimplicit/.AutoDiffBlock.hpp.swp diff --git a/opm/polymer/fullyimplicit/.AutoDiffBlock.hpp.swp b/opm/polymer/fullyimplicit/.AutoDiffBlock.hpp.swp new file mode 100644 index 0000000000000000000000000000000000000000..30f7c63bd2ed2b77f4ba3c45af2c8c8c455a27b6 GIT binary patch literal 16384 zcmeHN%a0sK884g=oIn62e?UI46zwkKnYE1|a;#Ox`>;oNcUHTzaV&&P%}n)9+1*{8 z>h2v+EC(eVB3`*5aX}#E1X4~sM8pjVPC<#nf!7%+aOJ=W4E(<8>h9Tb;vgZ@OUkE-e(u0Gq_rq8!OD{y^Mh&SJPZSTSppA*ln2odT$((xge-1q9*RK+s9 zZyhdt(F^QMjlJ11NyIpvm~a*;>wcHtgFFmpI!Z!4(pjgQXJ$hO!Nt&wZnh^$@_{`y z6&eN_2JU6xQE~3eHX1#~(Ymw@P!TVW454VW454VW454VW454VW45)|CWI) zc}RQ(qdVb8xZ^)RwD9>ae?8~#_ZQ0l>C1=y{tp()@A~qYzyGlBpzP-#zC7^dFE5lo z;O{@~KmW2&?r%3A4Fe4W4Fe4W4Fe4W4Fe4W4Fe4W4Fe4W4Fe4W@4Z7R|L=Z6h+hM5062>+rUZS zw>W%w71#zI0e*?Yk{5xefWLybKLWo2n8R-X|LsM%I-L%6gG^bugrcbqLz>7VMUqnef?8T869_M){N$gwEXjCG>2h1}FYctcmQ13o%#8iw zmzF6rbf_}drH;q?NRM+FR(+?mlucwtI;Er~xOpwc1SFwMHnB?84O)~MnwgwprN%5h zPZdG3x z4`*&PDZfgh(u_=!X%mwUoLY{%C=WA@(giU(4wSV}JFq75LS9fH*nQWr&a~qu%rRi6 zN4m)Ekwjt2O_Ur>Dq7k!z?=^GT^34YhN+H=sT>~H;WQn9E}s927tHWH6NyYyyq`lR z_T%(wEez#Y@6HZb=rIKrRWBxKY1P?V(xNl&OJPK3-S>34g{hopp|(BZ>t6L2v@eV$ z(~*LimqUSOmG+4>hgL@QRAp1+XD~fq)w)e*R$Fw|f$p^F>O{pfvPx!5%`y65x1}RM zca~clj|x?!;tZw}b2avsv#b2Wv4=VI9#nYbG4fz-l{RC%rlc$c)sr^YYn!@h>fWh> zV$QZvmn49fG6)xY)K=}{=iS(+65@CuV`cRb#&ra%n(Ax&Yah%`+3{(GZaaqZ^qpkKk17t*$>8XxMCT$dMUp8CNo`dOp`snX95>o;mp9 zF-F+L;ezSJohV^ zlDhzMU(%`}tTu#5w={YQMX3Q2hw^fI0r>Klor!~ zsB66H(povg_#-8q+07@g(g$dz8hvSPNCDuK%#pI8kz?>tY6)6kI6BT05{b(yL*)Xw zu1z$XC5W7C2hQAm6mZd)9lIcPo+KnA7u7H=l`7o-C}isfFT-4iNFT8iRkRQF&`LXN zVLrX}7WdtVODW|ixeUY+hEobWyhv*-9x+J;A>5dyASAhL3Fk2sNL$ZzVikrnM#MFa z$aFM8!~#!!j{ZZL!sM_@$R?4>@bws7l@^u1U_#_g?oellaH;0?hN+cw(aYrZ>{jzq z88U*#jhs^|91jGcHPS5{qsX7G!a=!pp%r|U5Ws*&?t|@afrTLt*)lqA# zQ$%_=dNnTguOeO^ivkq^2arfSLrE3s6R)L0T_IJ0hcMk^r zjo#MQ#Vh^wFI>O8+1=ROyWZPbzjCm#xq-d{M3Ue?s3}%DpiG7ae&g~lgtv)$0mTM^ zHO1QZ!X(R*^PSFgI&B}udE3~-4w8Y2Q}>Au z(^h|vy0qKf8}!x>u5|Zk_h4_gzrTrb?PENJgU-&H}Db6TFhQy{w+0-XP?s<#%9{VQ9&^3OUJRUpiYmq`!tn1Ucl4DOO z(mSsM(Lb7SAcQc;kKEFof82*{1OS)r(+P$&oYg}OSew*qQ>^1g$miHmR2*=RaLW#6 z1OB4}JQ}BB;S^|W@4y#Ffl}Fi0)}@DqH0RpkRl1|;~TvCTLQb-*rD+{49M!kNrtuS znRB$?+Zk+bQEz?FqHcnh>7nDc#E6`i!ZJE5g7^R5#s2&O?BRL;Up`;|G4}bd19yNe z;4{D};6dOm)O`hb3D^cM0b9T(a27ZNtO6Z?<(>eVt6`vFpkbh4pkbh4pkbh4pkbh4 zpkbh4;D64*DV(I@i5MO|eDGljl)b|>7mjeS_awUG{`0ru5gpk{N-Gpj(xLmMi)skDk--UB7oHrhOL~vn$Byr@t_9XYUEV$)+mRHB2D>xxzDMh;{ zS-Ij_W~T6n5XTC1fe(*)c)re>lS=OB2jW-g89%xKP6n_AALFT{uR_mY!;S}5JZ>&^ zTk+EqK6* jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = dads_diag * c.derivative()[block]; + } + + return ADB::function(ads, jacs); + } + + + V + PolymerPropsAd::effectiveRelPerm(const V& c, + const V& cmax_cells, + const V& krw) const + { + const int nc = c.size(); + + V one = V::Ones(nc); + V ads = adsorption(c); + double max_ads = polymer_props_.cMaxAds(); + double res_factor = polymer_props_.resFactor(); + double factor = (res_factor -1.) / max_ads; + V rk = one + factor * ads; + V krw_eff = krw / rk; + + return eff_relperm; + } + + + ADB + PolymerPropsAd::effectiveRelPerm(const ADB& c, + const ADB& cmax_cells, + const ADB& krw, + const ADB& sw) const + { + const int nc = c.value().size(); + + V one = V::Ones(nc); + + ADB ads = adsorption(c); + V krw_eff = effectiveRelPerm(c.value(), cmax_cells.value(), krw.value()); + + double max_ads = polymer_props_.cMaxAds(); + double res_factor = polymer_props_.resFactor(); + double factor = (res_factor - 1.) / max_ads; + ADB rk = one + ads * factor; + ADB::M dkrw_ds = krw.derivative() / rk.derivative(); + ADB::M dkrw_dc = -krw.value() / (rk.value * rk.value()) * ads.derivative() * factor; + + const int num_blocks = c.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jac[block] = dkrw_ds * sw.derivative()[block] + dkrw_dc * c.derivative()[block]; + } + + return ADB::function(krw_eff, jacs); + } + }// namespace Opm diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp index 69936843d..b964a241d 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp @@ -81,6 +81,17 @@ namespace Opm { ADB polymerWaterVelocityRatio(const ADB& c) const; + V + adsorption(const V& c) const; + + ADB + adsorption(const ADB& c) const; + + V + effectiveRelPerm(const V& c, const V& cmax_cells, const V& relperm) const; + + ADB + effectiveRelPerm(const ADB& c, const ADB& cmax_cells, const ADB& krw, const ADB& sw) const; private: const PolymerProperties& polymer_props_; }; From 0558184439efbc6aeee1cbe6f808651fde11167c Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 12 Dec 2013 20:29:40 +0800 Subject: [PATCH 10/83] modify the function for the polymer term. --- .../.IncompPropsAdFromDeck.cpp.swp | Bin 0 -> 16384 bytes .../FullyImplicitTwophasePolymerSolver.cpp | 93 +++++++++--------- opm/polymer/fullyimplicit/PolymerPropsAd.cpp | 10 ++ opm/polymer/fullyimplicit/PolymerPropsAd.hpp | 1 + 4 files changed, 55 insertions(+), 49 deletions(-) create mode 100644 opm/polymer/fullyimplicit/.IncompPropsAdFromDeck.cpp.swp diff --git a/opm/polymer/fullyimplicit/.IncompPropsAdFromDeck.cpp.swp b/opm/polymer/fullyimplicit/.IncompPropsAdFromDeck.cpp.swp new file mode 100644 index 0000000000000000000000000000000000000000..5a92cb6fba3072c52aea393cf7b815ab985ff255 GIT binary patch literal 16384 zcmeI3TWB3c7{|x^wY9WZ@lqV4hI7*NoFppR%>`4_+G0aPX;ZMo!)A9*+-7gi?n#KD zim2^VQ4qlgUqn!R@CMcw`ywd5ct-^BMT_92555$Hir{}{cF)c^Jts{SM05`PvbULU zzWL8(cjw#84ovTwq<2^DB>1f(Syt-4!}f5Kr+aPR89GJPIb5GhcDy z_?#u01V#a)K%u}Ia>vl%fH=Oj=N7tQ{r=*T*)s|l1&jhl0i%FXz$jo8FbWt2{__f? z@rC4Z)bhftrVH8jg0}5N+41^pyV!O;J2V@kfKk9GU=%P47zK<1MggOMQNSo*6fg=H z1^$H!*e)Tx@aqP#0D$;^{{H{_HH4f1Pl6dR2sVM6z*=xMII)(H0L+2=K?Pg_K3+q} zA+Qm=eKjGkf|o!5=70?z02{$N@XJ+%`~cnoZ-8UqC^!OkgB{>@a5MP+NLVg2ZgU`Wd;8XBCcm^;~0}p{2unAlTe!ZNKpTSSyNAMna z4p?9-xCh(@PG3gI2jD1p0?dG|U=uiXDar)L!HZxYxC2}ZPF@24!7JbxI0}w{r@>Po z1S8-|@X5u5d<0$wj{^q`g0k+4=A{9~$3}=bx2o2+5x_PrQP^Qbx0^q__%4u?NXa+f!X>YRFk3SJTTv$$*V&<}X z<_L21QI)Mo!kHg49wZgc62=!;DU7vRwAFcx=E(~KMI{Rz;7}4(aJ)^*QqnEjX)Hcs zsi8nwaw+VmNzL~CifgBKsoYN;=KD$WXr4EB-R8b#P!>UPNf-*_z83%0#r&FCI?(iSmiPE`50L3j!z1|{{e3|5eDyuK7@9>V&w6li>Hphvr_ZzB ziu4_NEgC!PviT&t-J4^fWsP%gFO9fSW7cQ=G&?!56xxC3bUof1El%2pB0ffCJF9le z4R;w{({y@O=`irbtUugsgvASKt&%i&9iwN0g^8DX#hkM&$DPchOJFq0PqCDy{ns z&lQgmOtw+Ci4qSWwTifxGkeza|5_bevukb?lWsmQtKuU5SLKYGS#veTbl#;LXd`b| zt9KJg=Qk33@KA7&IA&I$DbrHB-HyOAmOgJ$4QrvJF+)h!KEiQ{$h~bgadLwkiDJq_^XsL%HeQ{^fd!95@>Cx^yc@dQM z2>&2ck_wy1HOn}S>griFL47Ia61@mHwP@B!VT;l|Q|I4@@!dME?~mhLVKDecy(H8FP`3l=hEi=So2H2-xmqI&`V1$KC|wp zbKGCk|FN0!!`X*9t^7&V4oc?#HVHZ#ZaM=H3r)dZkaQYjo^3 zmeuieNtxU3_+FeaVQI?7r|Z0z%9@g8f;$yUV%!Yqo;biqvR>%;4VTdoFM#*^4d4vyoCYT| zo8Jk$=4TWz3K#{90!9I&fKk9GU=%P47zO?j1rF0{we`JDyml4~3HhQ<77S8rW35T@ z_tfTkc*}x278ornUu9@newR@%M0G@{OkaxcY`t(PFHdYm-h;CumNY{*kBVT=V9ia2 zYOZI`(LTh$u9|&ZXvm8Zba+A#{fHbM10BRTEwq`I-gx%y>eRUu= zA?rKY<_T68V&PIr1gb>*0W^ZJz1G5pdJ7i|SSk&iP$51UBit+kS*lLrco?lm_c8}T zGK=nrc*^aNMqJc2oTGL8GdOLFsi}2=+*z@Og>giy0<20)udC3nLi7}3zi_K4J$G%W zRmi4@Dy0@n8EOrt47KZbb{p&MJ1Q2d5nPpVLDjH>of}q2jP*KgDTHT_fNTW BYpVbN literal 0 HcmV?d00001 diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index a101c5512..a68110425 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -226,6 +226,19 @@ typedef Eigen::Array c.value()(i)) ? cmax(i) : c.value()(i); + } + + return ADB::constant(cmax); + } @@ -243,9 +256,12 @@ typedef Eigen::Array kr = computeRelPerm(state); - for (int phase = 0; phase < fluid_.numPhases(); ++phase) { - const ADB mflux = computeMassFlux(phase, trans, kr, state); - ADB source = accumSource(phase, kr, src); + + const ADB cmax = computeCmax(state.concentration); + const ADB krw_eff = polymer_props_.effectiveRelPerm(c, cmax, kr[0], state.saturation[0]); + + const std::vector mflux = computeMassFlux(trans, kr, state); + const std::vector source = accumSource(phase, kr, src); residual_[phase] = pvdt*(state.saturation[phase] - old_state.saturation[phase]) + ops_.div*mflux - source; @@ -255,17 +271,19 @@ typedef Eigen::Array& kr, - const std::vector& src) const + std::vector + FullyImplicitTwophasePolymerSolver::accumSource(const std::vector& kr, + const std::vector& src, + const std::vector& polymer_inflow_c, + const SolutionState& state) const { //extract the source to out and in source. std::vector outsrc; @@ -286,6 +304,7 @@ typedef Eigen::Array(& src[0], grid_.number_of_cells); const V outSrc = Eigen::Map(& outsrc[0], grid_.number_of_cells); const V inSrc = Eigen::Map(& insrc[0], grid_.number_of_cells); + const V polyin = Eigen::Map(& polymer_inflow_c[0], grid_.number_of_cells); // compute the out-fracflow. ADB f_out = computeFracFlow(phase, kr); // compute the in-fracflow. @@ -336,7 +355,7 @@ typedef Eigen::Array FullyImplicitTwophasePolymerSolver::computeFracFlow(int phase, const std::vector& kr) const { @@ -452,54 +471,30 @@ typedef Eigen::Array& kr , - const SolutionState& state ) const + std::vector + FullyImplicitTwophasePolymerSolver::computeMassFlux(const V& trans, + const ADB& mc, + const std::vector& kr , + const SolutionState& state ) const { -// const ADB tr_mult = transMult(state.pressure); const double* mus = fluid_.viscosity(); - ADB mob = ADB::null(); - if (phase == 0) { - ADB inv_wat_eff_vis = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mus); -// for (int i = 0; i < grid_.number_of_cells; ++i) { -// std::cout << state.concentration.value()(i) << " " << 1./inv_wat_eff_vis.value()(i) << std::endl; -// std::cout << "water effective vis\n"<<1./inv_wat_eff_v1./inv_wat_eff_vis.value()is.value()< mflux(2, ADB::null()); + ADB inv_wat_eff_vis = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mus); + ADB wat_mob = kr[0] * inv_wat_eff_vis; + ADB oil_mob = kr[1] / V::Constant(kr[1].size(), 1, mus[1]); + ADB poly_mob = mc * kr[0] * inv_wat_eff_vis; + + const ADB dp = ops_.ngrad * state.pressure; const ADB head = trans * dp; - UpwindSelector upwind(grid_, ops_, head.value()); - return upwind.select(mob) * head; + mflux.push_back(upwind.select(wat_mob)*head); + mflux.push_back(upwind.select(oil_mob)*head); + mflux.push_back(upwind.select(poly_mob)*head); + return mflux; } - ADB - FullyImplicitTwophasePolymerSolver::computePolymerMassFlux(const V& trans, - const ADB& mc, - const std::vector& kr, - const SolutionState& state) const - - { - const double* mus = fluid_.viscosity(); - ADB inv_wat_eff_vis = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mus); - ADB poly_mob = mc * kr[0] * inv_wat_eff_vis; - - const ADB dp = ops_.ngrad * state.pressure; - const ADB head = trans * dp; - - UpwindSelector upwind(grid_, ops_, head.value()); - - return upwind.select(poly_mob) * head; - - } - double FullyImplicitTwophasePolymerSolver::residualNorm() const diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp index f9ce55952..d1b984ac4 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp @@ -163,6 +163,16 @@ namespace Opm { } */ + double + PolymerPropsAd::rockDensity() const + { + return polymer_props_.rockDensity(); + } + + + + + PolymerPropsAd::PolymerPropsAd(const PolymerProperties& polymer_props) : polymer_props_ (polymer_props) { diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp index b964a241d..5c899ab40 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp @@ -63,6 +63,7 @@ namespace Opm { const ADB& muWat, const ADB& muPolyEff) const; */ + double rockDensity() const; typedef AutoDiffBlock ADB; typedef ADB::V V; PolymerPropsAd(const PolymerProperties& polymer_props); From 25160d019a26d3038d9ee317ca1efb0f722684b9 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 12 Dec 2013 21:24:47 +0800 Subject: [PATCH 11/83] add adsorption term for polymer equation. rewrite some function for simplify. --- .../.IncompPropsAdFromDeck.cpp.swp | Bin 16384 -> 0 bytes .../FullyImplicitTwophasePolymerSolver.cpp | 223 ++++++++---------- .../FullyImplicitTwophasePolymerSolver.hpp | 32 ++- 3 files changed, 121 insertions(+), 134 deletions(-) delete mode 100644 opm/polymer/fullyimplicit/.IncompPropsAdFromDeck.cpp.swp diff --git a/opm/polymer/fullyimplicit/.IncompPropsAdFromDeck.cpp.swp b/opm/polymer/fullyimplicit/.IncompPropsAdFromDeck.cpp.swp deleted file mode 100644 index 5a92cb6fba3072c52aea393cf7b815ab985ff255..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI3TWB3c7{|x^wY9WZ@lqV4hI7*NoFppR%>`4_+G0aPX;ZMo!)A9*+-7gi?n#KD zim2^VQ4qlgUqn!R@CMcw`ywd5ct-^BMT_92555$Hir{}{cF)c^Jts{SM05`PvbULU zzWL8(cjw#84ovTwq<2^DB>1f(Syt-4!}f5Kr+aPR89GJPIb5GhcDy z_?#u01V#a)K%u}Ia>vl%fH=Oj=N7tQ{r=*T*)s|l1&jhl0i%FXz$jo8FbWt2{__f? z@rC4Z)bhftrVH8jg0}5N+41^pyV!O;J2V@kfKk9GU=%P47zK<1MggOMQNSo*6fg=H z1^$H!*e)Tx@aqP#0D$;^{{H{_HH4f1Pl6dR2sVM6z*=xMII)(H0L+2=K?Pg_K3+q} zA+Qm=eKjGkf|o!5=70?z02{$N@XJ+%`~cnoZ-8UqC^!OkgB{>@a5MP+NLVg2ZgU`Wd;8XBCcm^;~0}p{2unAlTe!ZNKpTSSyNAMna z4p?9-xCh(@PG3gI2jD1p0?dG|U=uiXDar)L!HZxYxC2}ZPF@24!7JbxI0}w{r@>Po z1S8-|@X5u5d<0$wj{^q`g0k+4=A{9~$3}=bx2o2+5x_PrQP^Qbx0^q__%4u?NXa+f!X>YRFk3SJTTv$$*V&<}X z<_L21QI)Mo!kHg49wZgc62=!;DU7vRwAFcx=E(~KMI{Rz;7}4(aJ)^*QqnEjX)Hcs zsi8nwaw+VmNzL~CifgBKsoYN;=KD$WXr4EB-R8b#P!>UPNf-*_z83%0#r&FCI?(iSmiPE`50L3j!z1|{{e3|5eDyuK7@9>V&w6li>Hphvr_ZzB ziu4_NEgC!PviT&t-J4^fWsP%gFO9fSW7cQ=G&?!56xxC3bUof1El%2pB0ffCJF9le z4R;w{({y@O=`irbtUugsgvASKt&%i&9iwN0g^8DX#hkM&$DPchOJFq0PqCDy{ns z&lQgmOtw+Ci4qSWwTifxGkeza|5_bevukb?lWsmQtKuU5SLKYGS#veTbl#;LXd`b| zt9KJg=Qk33@KA7&IA&I$DbrHB-HyOAmOgJ$4QrvJF+)h!KEiQ{$h~bgadLwkiDJq_^XsL%HeQ{^fd!95@>Cx^yc@dQM z2>&2ck_wy1HOn}S>griFL47Ia61@mHwP@B!VT;l|Q|I4@@!dME?~mhLVKDecy(H8FP`3l=hEi=So2H2-xmqI&`V1$KC|wp zbKGCk|FN0!!`X*9t^7&V4oc?#HVHZ#ZaM=H3r)dZkaQYjo^3 zmeuieNtxU3_+FeaVQI?7r|Z0z%9@g8f;$yUV%!Yqo;biqvR>%;4VTdoFM#*^4d4vyoCYT| zo8Jk$=4TWz3K#{90!9I&fKk9GU=%P47zO?j1rF0{we`JDyml4~3HhQ<77S8rW35T@ z_tfTkc*}x278ornUu9@newR@%M0G@{OkaxcY`t(PFHdYm-h;CumNY{*kBVT=V9ia2 zYOZI`(LTh$u9|&ZXvm8Zba+A#{fHbM10BRTEwq`I-gx%y>eRUu= zA?rKY<_T68V&PIr1gb>*0W^ZJz1G5pdJ7i|SSk&iP$51UBit+kS*lLrco?lm_c8}T zGK=nrc*^aNMqJc2oTGL8GdOLFsi}2=+*z@Og>giy0<20)udC3nLi7}3zi_K4J$G%W zRmi4@Dy0@n8EOrt47KZbb{p&MJ1Q2d5nPpVLDjH>of}q2jP*KgDTHT_fNTW BYpVbN diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index a68110425..ecfd5f557 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -258,115 +258,117 @@ typedef Eigen::Array kr = computeRelPerm(state); const ADB cmax = computeCmax(state.concentration); - const ADB krw_eff = polymer_props_.effectiveRelPerm(c, cmax, kr[0], state.saturation[0]); + const ADB ads = adsorption(state.concentration, cmax); + const ADB krw_eff = polymer_props_ad_.effectiveRelPerm(c, cmax, kr[0], state.saturation[0]); - const std::vector mflux = computeMassFlux(trans, kr, state); - const std::vector source = accumSource(phase, kr, src); - residual_[phase] = - pvdt*(state.saturation[phase] - old_state.saturation[phase]) - + ops_.div*mflux - source; - } + const ADB mc = computeMc(state); + const std::vector mflux = computeMassFlux(trans, mc, kr[0], krw_eff, state); + const std::vector source = accumSource(kr[1], krw_eff, state.concentration, src, polymer_inflow); + const double rho_r = polymer_props_ad_.rockDensity(); + const V phi = V::Constant(pvdt.size(), 1, *fluid_.porosity()); + residual_[0] = pvdt*(state.saturation[0] - old_state.saturation[0]) + + ops_.div*mflux[0] - source[0]; + residual_[1] = pvdt*(state.saturation[1] - old_state.saturation[1]) + + ops_.div*mflux[1] - source[1]; // Mass balance equation for polymer - const ADB src_polymer = polymerSource(kr, src, polymer_inflow, state); - ADB mc = computeMc(state); - ADB poly_mflux = computePolymerMassFlux(trans, mc, kr, state); residual_[2] = pvdt * (state.saturation[0] * state.concentration - old_state.saturation[0] * old_state.concentration) - + - + ops_.div * poly_mflux - src_polymer; + + pvdt * rho_r * (1. - phi) / phi * ads + + ops_.div * mflux[3] - srouce[3]; } - - std::vector - FullyImplicitTwophasePolymerSolver::accumSource(const std::vector& kr, - const std::vector& src, - const std::vector& polymer_inflow_c, - const SolutionState& state) const - { - //extract the source to out and in source. - std::vector outsrc; - std::vector insrc; - std::vector::const_iterator it; - for (it = src.begin(); it != src.end(); ++it) { - if (*it < 0) { - outsrc.push_back(*it); - insrc.push_back(0.0); - } else if (*it > 0) { - insrc.push_back(*it); - outsrc.push_back(0.0); - } else { - outsrc.emplace_back(0); - insrc.emplace_back(0); - } - } - const V source = Eigen::Map(& src[0], grid_.number_of_cells); - const V outSrc = Eigen::Map(& outsrc[0], grid_.number_of_cells); - const V inSrc = Eigen::Map(& insrc[0], grid_.number_of_cells); - const V polyin = Eigen::Map(& polymer_inflow_c[0], grid_.number_of_cells); - // compute the out-fracflow. - ADB f_out = computeFracFlow(phase, kr); - // compute the in-fracflow. - V f_in; - if (phase == 1) { - f_in = V::Zero(grid_.number_of_cells); - } else if (phase == 0) { - f_in = V::Ones(grid_.number_of_cells); - } - return f_out * outSrc + f_in * inSrc ; - } - - - ADB - FullyImplicitTwophasePolymerSolver:: - polymerSource(const std::vector& kr, - const std::vector& src, - const std::vector& polymer_inflow_c, - const SolutionState& state) const - { - //extract the source to out and in source. - std::vector outsrc; - std::vector insrc; - std::vector::const_iterator it; - for (it = src.begin(); it != src.end(); ++it) { - if (*it < 0) { - outsrc.push_back(*it); - insrc.push_back(0.0); - } else if (*it > 0) { - insrc.push_back(*it); - outsrc.push_back(0.0); - } else { - outsrc.emplace_back(0); - insrc.emplace_back(0); - } - } - const V source = Eigen::Map(& src[0], grid_.number_of_cells); - const V outSrc = Eigen::Map(& outsrc[0], grid_.number_of_cells); - const V inSrc = Eigen::Map(& insrc[0], grid_.number_of_cells); - const V polyin = Eigen::Map(& polymer_inflow_c[0], grid_.number_of_cells); - // compute the out-fracflow. - ADB f_out = computeFracFlow(0, kr); - // compute the in-fracflow. - V f_in = V::Ones(grid_.number_of_cells); - -// ADB polymer_insrc = ADB::function(f_in * inSrc * polyin, state.concentration.derivative()); - return f_out * outSrc * state.concentration + f_in * inSrc * polyin; - } - - - std::vector - FullyImplicitTwophasePolymerSolver::computeFracFlow(int phase, - const std::vector& kr) const + FullyImplicitTwophasePolymerSolver::computeMassFlux(const V& trans, + const ADB& mc, + const ADB& kro, + const ADB& krw_eff, + const SolutionState& state ) const { const double* mus = fluid_.viscosity(); - ADB mob_phase = kr[phase] / V::Constant(kr[phase].size(), 1, mus[phase]); - ADB mob_wat = kr[0] / V::Constant(kr[0].size(), 1, mus[0]); - ADB mob_oil= kr[1] / V::Constant(kr[1].size(), 1, mus[1]); - ADB total_mob = mob_wat + mob_oil; - ADB f = mob_phase / total_mob; + std::vector mflux; + ADB inv_wat_eff_vis = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mus); + ADB wat_mob = krw_eff * inv_wat_eff_vis; + ADB oil_mob = kr[1] / V::Constant(kr[1].size(), 1, mus[1]); + ADB poly_mob = mc * krw_eff * inv_wat_eff_vis; - return f; + + const ADB dp = ops_.ngrad * state.pressure; + const ADB head = trans * dp; + UpwindSelector upwind(grid_, ops_, head.value()); + + mflux.push_back(upwind.select(wat_mob)*head); + mflux.push_back(upwind.select(oil_mob)*head); + mflux.push_back(upwind.select(poly_mob)*head); + + + return mflux; + } + + + std::vector + FullyImplicitTwophasePolymerSolver::accumSource(const ADB& kro, + const ADB& krw_eff, + const ADB& c, + const std::vector& src, + const std::vector& polymer_inflow_c) const + { + //extract the source to out and in source. + std::vector outsrc; + std::vector insrc; + std::vector::const_iterator it; + for (it = src.begin(); it != src.end(); ++it) { + if (*it < 0) { + outsrc.push_back(*it); + insrc.push_back(0.0); + } else if (*it > 0) { + insrc.push_back(*it); + outsrc.push_back(0.0); + } else { + outsrc.emplace_back(0); + insrc.emplace_back(0); + } + } + const V source = Eigen::Map(& src[0], grid_.number_of_cells); + const V outSrc = Eigen::Map(& outsrc[0], grid_.number_of_cells); + const V inSrc = Eigen::Map(& insrc[0], grid_.number_of_cells); + const V polyin = Eigen::Map(& polymer_inflow_c[0], grid_.number_of_cells); + // compute the out-fracflow. + const std::vector f = computeFracFlow(kro, krw_eff, c); + // compute the in-fracflow. + V zero = V::Zero(grid_.number_of_cells); + V one = V::Ones(grid_.number_of_cells); + return f_out * outSrc + f_in * inSrc ; + + std::vector source; + //water source + source.push_back(f[0] * outSrc + one * inSrc); + //oil source + source.push_back(f[1] * outSrc + zero * inSrc); + //polymer source + source.push_back(f[0] * outSrc * c + one * inSrc * polyin) + } + + + + + std::vector + FullyImplicitTwophasePolymerSolver::computeFracFlow(const ADB& kro, + const ADB& krw_eff, + const ADB& c) const + { + const double* mus = fluid_.viscosity(); + ADB inv_wat_eff_vis = polymer_props_ad_.effectiveInvWaterVisc(c, mus); + ADB wat_mob = kr[0] * inv_wat_eff_vis; + ADB oil_mob = kr[1] / V::Constant(kr[1].size(), 1, mus[1]); + ADB total_mob = wat_mob + oil_mob; + + std::vector fracflow; + + fracflow.push_back(wat_mob / total_mob); + fracflow.push_back(oil_mob / total_mob); + + return fracflow; } @@ -471,29 +473,6 @@ typedef Eigen::Array - FullyImplicitTwophasePolymerSolver::computeMassFlux(const V& trans, - const ADB& mc, - const std::vector& kr , - const SolutionState& state ) const - { - const double* mus = fluid_.viscosity(); - std::vector mflux(2, ADB::null()); - ADB inv_wat_eff_vis = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mus); - ADB wat_mob = kr[0] * inv_wat_eff_vis; - ADB oil_mob = kr[1] / V::Constant(kr[1].size(), 1, mus[1]); - ADB poly_mob = mc * kr[0] * inv_wat_eff_vis; - - - const ADB dp = ops_.ngrad * state.pressure; - const ADB head = trans * dp; - UpwindSelector upwind(grid_, ops_, head.value()); - - mflux.push_back(upwind.select(wat_mob)*head); - mflux.push_back(upwind.select(oil_mob)*head); - mflux.push_back(upwind.select(poly_mob)*head); - return mflux; - } double @@ -528,7 +507,7 @@ typedef Eigen::Array& kr) const; - ADB - accumSource(const int phase, - const std::vector& kr, - const std::vector& src) const; - ADB - computeMassFlux(const int phase, - const V& trans, - const std::vector& kr, - const SolutionState& state) const; + + std::vector + computeMassFlux(const V& trans, + const ADB& mc, + const ADB& kro, + const ADB& krw_eff, + const SolutionState& state ) const; + + std::vector + accumSource(const ADB& kro, + const ADB& krw_eff, + const ADB& c, + const std::vector& src, + const std::vector& polymer_inflow_c) const; + + + std::vector + computeFracFlow(const ADB& kro, + const ADB& krw_eff, + const ADB& c) const; ADB computePolymerMassFlux(const V& trans, const ADB& mc, From 2eaf24decf334a7aa3422f38c47d7bcc3e5f161a Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 12 Dec 2013 21:58:25 +0800 Subject: [PATCH 12/83] fix errors for debugging. --- .../fullyimplicit/.AutoDiffBlock.hpp.swp | Bin 16384 -> 0 bytes .../FullyImplicitTwophasePolymerSolver.cpp | 24 +++++++++--------- .../FullyImplicitTwophasePolymerSolver.hpp | 7 ++--- opm/polymer/fullyimplicit/PolymerPropsAd.cpp | 12 ++++----- opm/polymer/fullyimplicit/PolymerPropsAd.hpp | 4 +-- 5 files changed, 22 insertions(+), 25 deletions(-) delete mode 100644 opm/polymer/fullyimplicit/.AutoDiffBlock.hpp.swp diff --git a/opm/polymer/fullyimplicit/.AutoDiffBlock.hpp.swp b/opm/polymer/fullyimplicit/.AutoDiffBlock.hpp.swp deleted file mode 100644 index 30f7c63bd2ed2b77f4ba3c45af2c8c8c455a27b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeHN%a0sK884g=oIn62e?UI46zwkKnYE1|a;#Ox`>;oNcUHTzaV&&P%}n)9+1*{8 z>h2v+EC(eVB3`*5aX}#E1X4~sM8pjVPC<#nf!7%+aOJ=W4E(<8>h9Tb;vgZ@OUkE-e(u0Gq_rq8!OD{y^Mh&SJPZSTSppA*ln2odT$((xge-1q9*RK+s9 zZyhdt(F^QMjlJ11NyIpvm~a*;>wcHtgFFmpI!Z!4(pjgQXJ$hO!Nt&wZnh^$@_{`y z6&eN_2JU6xQE~3eHX1#~(Ymw@P!TVW454VW454VW454VW454VW45)|CWI) zc}RQ(qdVb8xZ^)RwD9>ae?8~#_ZQ0l>C1=y{tp()@A~qYzyGlBpzP-#zC7^dFE5lo z;O{@~KmW2&?r%3A4Fe4W4Fe4W4Fe4W4Fe4W4Fe4W4Fe4W4Fe4W@4Z7R|L=Z6h+hM5062>+rUZS zw>W%w71#zI0e*?Yk{5xefWLybKLWo2n8R-X|LsM%I-L%6gG^bugrcbqLz>7VMUqnef?8T869_M){N$gwEXjCG>2h1}FYctcmQ13o%#8iw zmzF6rbf_}drH;q?NRM+FR(+?mlucwtI;Er~xOpwc1SFwMHnB?84O)~MnwgwprN%5h zPZdG3x z4`*&PDZfgh(u_=!X%mwUoLY{%C=WA@(giU(4wSV}JFq75LS9fH*nQWr&a~qu%rRi6 zN4m)Ekwjt2O_Ur>Dq7k!z?=^GT^34YhN+H=sT>~H;WQn9E}s927tHWH6NyYyyq`lR z_T%(wEez#Y@6HZb=rIKrRWBxKY1P?V(xNl&OJPK3-S>34g{hopp|(BZ>t6L2v@eV$ z(~*LimqUSOmG+4>hgL@QRAp1+XD~fq)w)e*R$Fw|f$p^F>O{pfvPx!5%`y65x1}RM zca~clj|x?!;tZw}b2avsv#b2Wv4=VI9#nYbG4fz-l{RC%rlc$c)sr^YYn!@h>fWh> zV$QZvmn49fG6)xY)K=}{=iS(+65@CuV`cRb#&ra%n(Ax&Yah%`+3{(GZaaqZ^qpkKk17t*$>8XxMCT$dMUp8CNo`dOp`snX95>o;mp9 zF-F+L;ezSJohV^ zlDhzMU(%`}tTu#5w={YQMX3Q2hw^fI0r>Klor!~ zsB66H(povg_#-8q+07@g(g$dz8hvSPNCDuK%#pI8kz?>tY6)6kI6BT05{b(yL*)Xw zu1z$XC5W7C2hQAm6mZd)9lIcPo+KnA7u7H=l`7o-C}isfFT-4iNFT8iRkRQF&`LXN zVLrX}7WdtVODW|ixeUY+hEobWyhv*-9x+J;A>5dyASAhL3Fk2sNL$ZzVikrnM#MFa z$aFM8!~#!!j{ZZL!sM_@$R?4>@bws7l@^u1U_#_g?oellaH;0?hN+cw(aYrZ>{jzq z88U*#jhs^|91jGcHPS5{qsX7G!a=!pp%r|U5Ws*&?t|@afrTLt*)lqA# zQ$%_=dNnTguOeO^ivkq^2arfSLrE3s6R)L0T_IJ0hcMk^r zjo#MQ#Vh^wFI>O8+1=ROyWZPbzjCm#xq-d{M3Ue?s3}%DpiG7ae&g~lgtv)$0mTM^ zHO1QZ!X(R*^PSFgI&B}udE3~-4w8Y2Q}>Au z(^h|vy0qKf8}!x>u5|Zk_h4_gzrTrb?PENJgU-&H}Db6TFhQy{w+0-XP?s<#%9{VQ9&^3OUJRUpiYmq`!tn1Ucl4DOO z(mSsM(Lb7SAcQc;kKEFof82*{1OS)r(+P$&oYg}OSew*qQ>^1g$miHmR2*=RaLW#6 z1OB4}JQ}BB;S^|W@4y#Ffl}Fi0)}@DqH0RpkRl1|;~TvCTLQb-*rD+{49M!kNrtuS znRB$?+Zk+bQEz?FqHcnh>7nDc#E6`i!ZJE5g7^R5#s2&O?BRL;Up`;|G4}bd19yNe z;4{D};6dOm)O`hb3D^cM0b9T(a27ZNtO6Z?<(>eVt6`vFpkbh4pkbh4pkbh4pkbh4 zpkbh4;D64*DV(I@i5MO|eDGljl)b|>7mjeS_awUG{`0ru5gpk{N-Gpj(xLmMi)skDk--UB7oHrhOL~vn$Byr@t_9XYUEV$)+mRHB2D>xxzDMh;{ zS-Ij_W~T6n5XTC1fe(*)c)re>lS=OB2jW-g89%xKP6n_AALFT{uR_mY!;S}5JZ>&^ zTk+EqK6* kr = computeRelPerm(state); const ADB cmax = computeCmax(state.concentration); - const ADB ads = adsorption(state.concentration, cmax); - const ADB krw_eff = polymer_props_ad_.effectiveRelPerm(c, cmax, kr[0], state.saturation[0]); + const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); + const ADB krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, cmax, kr[0], state.saturation[0]); const ADB mc = computeMc(state); const std::vector mflux = computeMassFlux(trans, mc, kr[0], krw_eff, state); @@ -274,7 +274,7 @@ typedef Eigen::Array mflux; ADB inv_wat_eff_vis = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mus); ADB wat_mob = krw_eff * inv_wat_eff_vis; - ADB oil_mob = kr[1] / V::Constant(kr[1].size(), 1, mus[1]); + ADB oil_mob = kro / V::Constant(kro.size(), 1, mus[1]); ADB poly_mob = mc * krw_eff * inv_wat_eff_vis; @@ -325,11 +325,10 @@ typedef Eigen::Array(& src[0], grid_.number_of_cells); const V outSrc = Eigen::Map(& outsrc[0], grid_.number_of_cells); const V inSrc = Eigen::Map(& insrc[0], grid_.number_of_cells); const V polyin = Eigen::Map(& polymer_inflow_c[0], grid_.number_of_cells); @@ -338,7 +337,6 @@ typedef Eigen::Array source; //water source @@ -346,7 +344,9 @@ typedef Eigen::Array fracflow; diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp index 833b7d824..1dffd7a17 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp @@ -90,11 +90,6 @@ namespace Opm { computeFracFlow(const ADB& kro, const ADB& krw_eff, const ADB& c) const; - ADB - computePolymerMassFlux(const V& trans, - const ADB& mc, - const std::vector& kr, - const SolutionState& state) const; double residualNorm() const; ADB @@ -103,6 +98,8 @@ namespace Opm { const std::vector& polymer_inflow_c, const SolutionState& state) const; + ADB + computeCmax(const ADB& c) const; ADB computeMc(const SolutionState& state) const; ADB diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp index d1b984ac4..b322309b6 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp @@ -326,14 +326,14 @@ namespace Opm { const int nc = c.size(); V one = V::Ones(nc); - V ads = adsorption(c); + V ads = adsorption(c, cmax_cells); double max_ads = polymer_props_.cMaxAds(); double res_factor = polymer_props_.resFactor(); double factor = (res_factor -1.) / max_ads; V rk = one + factor * ads; V krw_eff = krw / rk; - return eff_relperm; + return krw_eff; } @@ -347,20 +347,20 @@ namespace Opm { V one = V::Ones(nc); - ADB ads = adsorption(c); + ADB ads = adsorption(c, cmax_cells); V krw_eff = effectiveRelPerm(c.value(), cmax_cells.value(), krw.value()); double max_ads = polymer_props_.cMaxAds(); double res_factor = polymer_props_.resFactor(); double factor = (res_factor - 1.) / max_ads; ADB rk = one + ads * factor; - ADB::M dkrw_ds = krw.derivative() / rk.derivative(); - ADB::M dkrw_dc = -krw.value() / (rk.value * rk.value()) * ads.derivative() * factor; + ADB dkrw_ds = krw / rk.value(); + ADB dkrw_dc = -krw.value() / (rk.value() * rk.value()) * ads * factor; const int num_blocks = c.numBlocks(); std::vector jacs(num_blocks); for (int block = 0; block < num_blocks; ++block) { - jac[block] = dkrw_ds * sw.derivative()[block] + dkrw_dc * c.derivative()[block]; + jacs[block] = dkrw_ds.derivative()[block] * sw.derivative()[block] + dkrw_dc.derivative()[block] * c.derivative()[block]; } return ADB::function(krw_eff, jacs); diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp index 5c899ab40..75050e32e 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp @@ -83,10 +83,10 @@ namespace Opm { polymerWaterVelocityRatio(const ADB& c) const; V - adsorption(const V& c) const; + adsorption(const V& c, const V& cmax_cells) const; ADB - adsorption(const ADB& c) const; + adsorption(const ADB& c, const ADB& cmax_cells) const; V effectiveRelPerm(const V& c, const V& cmax_cells, const V& relperm) const; From c2cdc7ec1745aa905f68820d47c358db9f0b527c Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 12 Dec 2013 22:46:29 +0800 Subject: [PATCH 13/83] add dead poro volume functionality. --- .../FullyImplicitTwophasePolymerSolver.cpp | 24 ++++++++++++------- opm/polymer/fullyimplicit/PolymerPropsAd.cpp | 6 +++++ opm/polymer/fullyimplicit/PolymerPropsAd.hpp | 1 + 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index 4f80fc3b5..90468d347 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -260,21 +260,27 @@ typedef Eigen::Array mflux = computeMassFlux(trans, mc, kr[0], krw_eff, state); + // const std::vector mflux = computeMassFlux(trans, mc, kr[0], krw_eff, state); + const std::vector mflux = computeMassFlux(trans, mc, kr[1], krw_eff, state); const std::vector source = accumSource(kr[1], krw_eff, state.concentration, src, polymer_inflow); const double rho_r = polymer_props_ad_.rockDensity(); const V phi = V::Constant(pvdt.size(), 1, *fluid_.porosity()); - residual_[0] = pvdt*(state.saturation[0] - old_state.saturation[0]) - + ops_.div*mflux[0] - source[0]; - residual_[1] = pvdt*(state.saturation[1] - old_state.saturation[1]) - + ops_.div*mflux[1] - source[1]; + + const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); + residual_[0] = pvdt * (state.saturation[0] - old_state.saturation[0]) + + ops_.div * mflux[0] - source[0]; + residual_[1] = pvdt * (state.saturation[1] - old_state.saturation[1]) + + ops_.div * mflux[1] - source[1]; // Mass balance equation for polymer - residual_[2] = pvdt * (state.saturation[0] * state.concentration - - old_state.saturation[0] * old_state.concentration) + residual_[2] = pvdt * (state.saturation[0] * state.concentration + - old_state.saturation[0] * old_state.concentration) * (1. - dead_pore_vol) + pvdt * rho_r * (1. - phi) / phi * ads - + ops_.div * mflux[3] - source[3]; + + ops_.div * mflux[2] - source[2]; } diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp index b322309b6..37ce0cfb0 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp @@ -170,6 +170,12 @@ namespace Opm { } + double + PolymerPropsAd::deadPoreVol() const + { + return polymer_props_.deadPoreVol(); + } + diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp index 75050e32e..d0d72caaa 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp @@ -64,6 +64,7 @@ namespace Opm { const ADB& muPolyEff) const; */ double rockDensity() const; + double deadPoreVol() const; typedef AutoDiffBlock ADB; typedef ADB::V V; PolymerPropsAd(const PolymerProperties& polymer_props); From 970fe665d894071dff36a504f5d1374062c6f64a Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 17 Dec 2013 17:36:43 +0800 Subject: [PATCH 14/83] add the bhp well controls. --- examples/sim_2p_fincomp_ad.cpp | 35 ++- .../.FullyImplicitTwoPhaseSolver.cpp.swp | Bin 0 -> 36864 bytes .../FullyImplicitTwoPhaseSolver.cpp | 296 +++++++++++++++--- .../FullyImplicitTwoPhaseSolver.hpp | 57 +++- 4 files changed, 318 insertions(+), 70 deletions(-) create mode 100644 opm/polymer/fullyimplicit/.FullyImplicitTwoPhaseSolver.cpp.swp diff --git a/examples/sim_2p_fincomp_ad.cpp b/examples/sim_2p_fincomp_ad.cpp index 17f75425b..91b026fad 100644 --- a/examples/sim_2p_fincomp_ad.cpp +++ b/examples/sim_2p_fincomp_ad.cpp @@ -6,6 +6,9 @@ #include #include #include + +#include +#include #include #include #include @@ -24,8 +27,17 @@ int main (int argc, char** argv) try { + using namespace Opm; parameter::ParameterGroup param(argc, argv, false); + + bool use_deck = param.has("deck_filename"); + if (!use_deck) { + OPM_THROW(std::runtime_error, "FullyImplicitTwoPhaseSolver cannot run without deckfile."); + } + double gravity[3] = { 0.0 }; + std::string deck_filename = param.get("deck_filename"); + EclipseGridParser deck = EclipseGridParser(deck_filename); int nx = param.getDefault("nx", 30); int ny = param.getDefault("ny", 1); int nz = 1; @@ -47,14 +59,20 @@ try SaturationPropsBasic::RelPermFunc rel_perm_func = SaturationPropsBasic::Linear; IncompPropsAdBasic props(num_phases, rel_perm_func, density, viscosity, porosity, permeability, grid.dimensions, num_cells); - std::vector omega; +/* std::vector src(num_cells, 0.0); src[0] = 1. / day; src[num_cells-1] = -1. / day; + */ FlowBCManager bcs; LinearSolverUmfpack linsolver; - FullyImplicitTwoPhaseSolver solver(grid, props, linsolver); + TwophaseState state; + state.init(grid, 2); + WellState well_state; + WellsManager wells(deck, grid, props.permeability()); + well_state.init(wells.c_wells(), state); + FullyImplicitTwoPhaseSolver solver(grid, props, *wells.c_wells(), linsolver, gravity); std::vector porevol; Opm::computePorevolume(grid, props.porosity(), porevol); const double dt = param.getDefault("dt", 10.) * day; @@ -63,10 +81,9 @@ try for (int cell = 0; cell < num_cells; ++cell) { allcells[cell] = cell; } - TwophaseState state; - state.init(grid, 2); - - //initial sat + std::vector src; // empty src term. + gravity[2] = param.getDefault("gravity", 0.0); + //initial sat for (int c = 0; c < num_cells; ++c) { state.saturation()[2*c] = 0.2; state.saturation()[2*c+1] = 0.8; @@ -74,8 +91,6 @@ try std::vector p(num_cells, 100*Opm::unit::barsa); state.pressure() = p; std::ostringstream vtkfilename; - - // Write the initial state. vtkfilename.str(""); vtkfilename << "sim_2p_fincomp_" << std::setw(3) << std::setfill('0') << 0 << ".vtu"; std::ofstream vtkfile(vtkfilename.str().c_str()); @@ -84,7 +99,7 @@ try dm["pressure"] = &state.pressure(); Opm::writeVtkData(grid, dm, vtkfile); for (int i = 0; i < num_time_steps; ++i) { - solver.step(dt, state, src); + solver.step(dt, state, src, well_state); vtkfilename.str(""); vtkfilename << "sim_2p_fincomp_" << std::setw(3) << std::setfill('0') << i + 1 << ".vtu"; std::ofstream vtkfile(vtkfilename.str().c_str()); @@ -92,7 +107,7 @@ try dm["saturation"] = &state.saturation(); dm["pressure"] = &state.pressure(); Opm::writeVtkData(grid, dm, vtkfile); - } + } } catch (const std::exception &e) { std::cerr << "Program threw an exception: " << e.what() << "\n"; diff --git a/opm/polymer/fullyimplicit/.FullyImplicitTwoPhaseSolver.cpp.swp b/opm/polymer/fullyimplicit/.FullyImplicitTwoPhaseSolver.cpp.swp new file mode 100644 index 0000000000000000000000000000000000000000..776e79ab91a6a8d8336cc313dcba0b0fa42293bb GIT binary patch literal 36864 zcmeI533Mb!dB>Z>fB_q0ZWAZ%<-PStD~+TzV5~i0PrQWniaoTzdKpGDEopXVrpG-q zlGe*|B#;0m=1Rae;dmGVCMKNb#s*#>#Ch<5B;4k(frK+aILsCD`|9Yb?iopI8(v-> z)9?K?Gu>5Ref3qno;_hfg;I#}?z{=vdrp2u53R{a7PP)NlKJwp*_st2J8nTBX(*+bO?x znP2-C@^Shh-w*1o!!#4k2%yXOW&vMVr1efsNoH;O^k3 z_YQ*3gLi<-!C`PQm;!$a9tj=@&H(>;uON6cxE8zwyZ|hLHQ=G(EbtKUANQnuZ~|Ni zwu1YEn@fdb=op zOWbdA)3KZ8vHUj|`DXJpyN#JBDaErTMMGyho^q%2^pZbhip*sX4-chTN5a!fd@; zs}_-uiV|uUhDQ9-hI~Secm^7)WC$!%BjIA)YL~QZ9nT}7x;vNDs{YN2NDk^V-CDi6 zrC#4AzYCH|cr)EdB^vUx%_Zf963NO7wazjBLA};&tB9vkA^qwSGbGs*~bP)k$W=Hp zgmz2jWBD?y+L4cf^vhB;YIWwrQOcOr-#xW1+2!&(M(E|*6wVdZ&q~znByp|UK2)tW zAT2k;b)ozi4JQset~EVN5v{G0TGcD9S(41{Lq~GCX5%C*NQNV+qSmRkazC_qcmt0% z_R;&yRqz5A;ktFyKdY@WRZC@%7NMk5;=a%iEcYodizFQ15H3<~b5ZCo3jH))xy_8( zDbNDNcJ0y#Ex1WFgZbr2dRm$ZV$+k8PmYqf&}=bWM#2e_q|`L4A_9@C$ez!r*{(4I zI^J9=MI)_AMp(?=eRD{C)QIx5m-?+!A5VF)k}#hcDSFRDV>or>J&>wl-xl`U+k1I!=tovoyehk{R$}&B`P)!r%mWo@FqxXRUD>EO>)v6uCjYt4J!PwTC-B`R-nm z{%m6e%E+tDDE5{uhJ^qk9P7JMST>VPvPDFclSluc^8QnPKid8CZ!!Ji!l;f$=qucn zGpY{NuRPU$N=mFF#yYLp@>nM+H`^uodeZeF+yZU}lI~Mra1A6dkib9!0|^WyFp$7N0s{#QBruS`Kmr2^3?wj+!2h8H za2#aJQi>I^r#Ty-u`FUv-JtgWYf1A(38ZKLKWkWjFMIq;z)`RXJP14x+=`$7%iv4k zdhk-P51bDk2_6K#gJ1tepaG`99&j!Qz;*cXUk+XdO5iDA6W9pO1*e0X@!@|R+z2iM z&jLHa+2H5+@oxg}25$y008a%aupUf+hk^%yGr@hq8Q?zPbZ`o|1z*3!09+1c!DGOg z;NIXCeE-*gH-ZjW2Y!fAyWFW3VQrk$4mur^d@xi#Ja z@%;7m5R8q5+r()uF09ZRpztZo&PGYptVE&q(uZPxR(ADbU<*-Mm2q7?>CN4#mGQbe zp9L9qijA*+OdHQtnL;!a2S<#F9q&a-rX7}>RZV%e9xp=37OZQBIW}ySB&TvPZ3^4n znRe8{{w}smTNOOG6rkNmo{I%mggRz7ER(IcS>>G<87?$iVz$H}=ao&y9ZQ(zkk&#u zsg(&rXe;6PRZt9B4%$utn(@^3tyxp?3F1#K;>E{ZW~|z~%dL&M^qaLGx>OXOMrjr% zM({L&)=&{caZ+0<^ty-;)7o%mUi?W~GJN;k3c85tP+1$rxyn>5J_Zz$sIbq4!EFawc91b z^dTh@aWJitU^HyU-J}vZaR^blWZ3FwP$^d`-NrtZvY<$hgh!K+u$@@9l+F`|wb`)H zj63qA7&Tg*V}+r-^H!3sQ-K*Mx`p8eUi_ur;#&*V&alxD?J}NpC01Ln(J-!8OM0C2 zJk9zpR}oNPIhRk}(WK^zD=KchlDm=ZPWZHySEJC#K-8Ew&{F@Ok`pBG0qUQhnD&;s&G*IA94Ai2@Cir+>p_3cRHjZ$R{ zswhF#mn6)i?Ga*B*v=SA?bG3~QfJkr6Fj0ul5$pQWdTtixboYn0kd3GBf*<3q-bIo zn$c1Rxk~^84PD@C+eR@Udqqmfsod#ItlO%6C^Wg(tqZ-l1Nz0*xYLOnqw{fHT1wBn zZqj$WInv$`oe-teW^pS|?bVLeO~Ovvi@7Iik>KSYSNU_P)ZE#YSy8IE4ChjqR%#M8 z$hXN!)!JnwLj=d3CgZWmOg+Kam}!+eUhi}rPxlYM&mq{&Ox$F!#g88@&WOu=N`+T8 zWv;gsMSA;EW#-Kzx1|NtJZKvh?cx)kN}D6So9UyvsR>lcHoptE5g- zmNb=}U0gDRyQ^7~#cZaFTA3`*c-z-RmT%Ra+9g)OEaU10skv=Moy08R z<^q*1x^8T$EZt4Dg{7RSX(oL-n>seU`Z~&9r^>fazt)lEW)It}B!yi&wo_(35o8&4 zDOYCGrt4vnZmXh{MHpJeiA4NiHoaMmZMu74g{7N84F{>lQf6N5mui*8;PEt%En$^U z%|OqTq9>jewYnWuYAQ1GLw?d3QTzX`=>4nA{(q0zP=1NM{*6H5{`Z0r@WYefHnq09Sym;2z+U?7=1O{>eb%>wnJP`+eXtZ~)vFypcWklfeDK+u3Iy0q26V z!5@K#g731|{uH!)g^rzd@xi8LRO3WT1UINg#}wmPNg5_&g!l88pV*3YN_BGlq` zq|~f^uJ^fd8(qk*g`8ohoXkZXZ+onH5+;1Yx*VoqxoPsa!W@HHcIB5`d!}}m_FuSn z&p{npmvoyQLLEzyL^+LwYqph}Vcf)~Fwc&jrNh<>r$Pz#l64$$s8}G@cnU@co(Q8l zRqGv>9(#m%!u4WhbhGC7kbJFlF&8vD-m*o=$=*aIq|i(T4?6uq5s#QTL_lKv*mq#>&Mn(^l=g1f zzeAT@q~|C~{K>Y8?Em3Qc&r37mI?}ZN#upl1-a$ zvSA*}Zw0mZq>H6FAfvt$+>ZgJ&^$`0X|<(JY@qk4`$e&hR}a~jH1Y-;uLIlauli{V zEY+Gv)IpY9C(E%ZtifdxSGyI?# z1TEGfF*~G&p`l_~;voq;9@fI@#2xl$l$vcOq;;Sz-9$YPc`i|VHDl#s*`jxy+UTY0 zF2w}Cz%;XjF-yHHC$K5I-kMtW=)GyS|5S$QZSPOuo(gHZI0*Im=Gly(UxNtIKU(Oyosl9HV2fvS}(YRSpX>=sA| zu`dp@3d6Rec3x=O+-An(M>owrHAFZdW_I7YQ*>fAtV?_Toah-X*f$qZL?j zr0pI0IbhdVKG7SVHF?ySmZfqYHAh6JmB*0qM;|rA45fsk+FOok2-`y(>!im@2)!E}mXI1Hvsh3aE2|JY zoVB#5Nn6H2sVIvDHQjFtr}pmIeqh^%4X(crlZHIOO=*gCKeyEuw&7K*xy|l7zlqoo5O3zFxR*UzYPkgOQ z;hAC`>(X|XeM`=C{*#y#^zer|S(LqVyPS5x5Udx!Ib)O8WzzQRqrTVo)!v%Pbd#;8 zEsE{g(56XJ7fG*Y#?{{%&)8I^YrVX)!Q)$WYqm|6Rjo1Zk8-x%vt33S zUaM%g_qWz@eUwXjr>m4}RN>ds`u(?&Uz#2(l)22zQ@P!;$x2G>qO3|D+bt*6usm1h%x5QEtk#;W)Q@%MWj%%)DXf=y zm>%?Ui!RY-b3Rqwh4swI89x{?=QYJ@DDnRfVX1$U>{;>uAJH4e;2rGqF9y@#Vc@sy z^TqFfE$DzJf%V|tK>Yo3=Kq!8x!|eb0!oy;Ir5O-VR<1o(m3u zhl4u-@$vsN_z3tg_z<`OTn|17t^*A)2Q~of=l?DCf?t4J!Oy|Zz)!(X!1uvh!JEJ{ zz)^4pI2F7Vo5EATH24MfgiisnFFX#P@#_c81S^Yr)fjoZr6yJP7~#9F5{fz>Ge40d!~wPe9d z9(G&I=TX`#-Ll6F8ZjKQo5rBMDXe0)RHq>6kAY&CKWPG#P*s+QI*HRZmT7rNz0AB* zWy`p)Rc>lC`$$U0Uk7GLw-=YEtnITpl;C6ti=8kfzDbVcrPWEf?a!;v9%8g%1uZ1h z4&zxwL?im~3HW%Ua%Dc06h$XBTTI-HkV$6cA#R6^g0g8Z_~wjfaB^h)R5_cgqNdlM;&NNr#HUA*Vkqz7qd)3lFJ-y8$vEq(p1a#U z^w|o-HTrZNo-+AQDk4=rc(DfY2(#r{ol)FlEpr6cWK5G=>o{lPS{+9aG$tn%$;Xu| z7!1nIeaG6Js8JZ24A%sXz$&)9G^I>u=Oj@JQ-S8MXU6ntn(un^ilNFbbj^lfM0Abb zXn0+?4*|I)VKl{<^TNrlW;I%x>Tu?FMBlA7n|l<)6`U4j%r9daTq=8RbCG!vz$e9Vt?DM z;)m>MS-aGA2X=*d2DPJFjgr~|!4td#M*M8)xK6pzg~&QB%>)5_UiEUT9nn$R1^P{g zm&G6%LQH5m?>!$nI4HVAl;o)*SVzvLhMFa?jCoS*7Rx-EkT=#S!YA~Hl##PE*W=#+Sfd2eY_&`#`v)lgXK=* z{EW^Mu_!_NN*MNBMS2zBY>APJd6U2Uk_xQMVk6~ut;tWvdsa89G-}#Zp8CE)UEa_! zz5{7ev!7&*@3*SR+tE}*aW$G5ViP{GX+_Gh&N9J>OU1L)nxnFjmhi}Q)uoz7OCmKa zrLxr$U6vk#>q9V*D#@jX;Ix(}ZQipVRt{b^FiAyu-dC%)rDxh}If6XCtrXJ$v;Hie zEV*n?$^H_7sH%!Ph~6EiW5uN!uK9r`lEUk`JPbGY zcH4e*`j^(jRBv(8simBa*>d#mSYldegyKvz$BQ!X(U1`JD7T_!Rfn%p0nbI}o5hVm z*ch2hHD+shlL16r&+e06H`mHZbj4hnu`wcI#Fe7Po9R}p!%3%nXy_MxQ;)ynfynSS zU3w7oVRx+xHuJRj9VCnLB%p}!O11xIi>~&Qvj4a5*Z&ZE{u{vc;ML#+_!Dq1@D29- zuLBkE1TYMK4Ur#%e*ss57lO;dA`m;kDd3~*`)>g62k!@8gx2CoJ3&cDZjUEpl+ zBWwXb1RndAHvq96yb@diUJ5P;&jZf_^R*z121TnjD+ zr-SdX=a>Ee(?9|q2TlWDWAA?)OoId9e&9?X@ACU1_#F5w_zd_Ykn;dHfaiff2MzFO za8K|#V-t9Xu?sZ$jKDeIKHyYv3b+&a4R(QFfS-c@0@s1JfTw}IU;u<;t3d!LReS0^5rM`$gt_ZC2%5)^W4Vny~w7a`u+WgJ9jg zERR5{u1xhHG*~SrLFL9#QqHCtx+d7!^NTL#J!Cz*OsL*?D zr~l1{OL>l*u1F;+@9Ze4hj_&G0f|C)ZX}#n_pF~nvH3*cDr#s%MFJ$#gdMq>vab)!ZH#6Zfm#Qu-xd(q8J-j}`yb zkJE;e)l+IwvsqF5EX*db)wh>v`Rcx~VoNIFW@3>&-8n6d(+|C?$i%g%?0A%BmiIal zYvT42%197hl5RC+dnCs%ip`klY2UIVg{sSSANiRlT^ODjItgl84w@i;XJpol#Hc)z z3rm(HVh(aFt8J5s<2SQxAgMB!@`9Uiplqy%ovYnWRZZpo#3eIKeo_uBc|Gz~y-jTC zNwlfu{vst+uRS$2A)bOS^1Zh~yRF4qvuZ;wyseFdLd-|ys<#0;pwjj@J~avJmX!#1 z#vD0qi!oMI*;0P0hExigKy^%t>b1U#>Q!%gQ!(>RiCQ6@vKFOLl9+A9bbo05VICFG zo8^T$`IgCYsdLMD!`5+PvTf4A`4tYGB+%fl>+6}saGg<%YTsae_tqTTQ<7Eg!Y>p& zkAuiXuIw1Nw7{#&?66m^BlqgS3jCeN7H++>EU2g*_0~U87C2Vq6=?l+hIuPmpbF*B zba<&zNzUOW1uFkFn<{-tbI?5?el)Q@ud$3lHN9^PqiPClbkiBoiPP(5cZ{CT-CDz* zN8FXD8_>1^Oi`|xE*5I-iiGVE{j(uR33@%XlazQJn5@cGXWh;B+&Mba*m}%bv7XeY z<_jyKJG`L`t*V=cebm-!R5ii8;*q7TTywaKLwYra-Wo=;dzeu}W}5+@@cd$>rmk?Rqv0Dsw*Z(wElfiqR8lYvTBiCf1C zYg@|dpgzzX5(MvT`Q{9O*>Lu#E_qnSFLJ=N&m4td*(&?jMT4|#gkC|$)n zMPr5*_YfI9(+Zbw^;(0Sdv&PTZE_}S$5JaP=(l2KmQR*I)#Z@hSet7WxFs_s^T8S5+wALa0XKm! zgD(NG3w!~59$W)vz*#`v5AZGa_7Wo??*(Xq7|1*ScY{ZP)4(U#+y53Bf#(G5;e7=md_}3U8dx5 zs(P72g}SGrZW#c~O`ZvYdp&37?10wk``dTzVJ@Y9?GLkL^K^rDber>EW`gK@{ELWr zH|kV<%pxj3*bgb)oUMdA>owu;3q>?N+0_C;^zU*cv;n_ID5?6_D-5+d)?ax^aQ_hH zUhSr%cY7q7(Qdzw!)8v&6`ho`>Z?a0%l8ZEMLl)+ou53jF)tp+neMNf7Ox-OEs&;` zOAd2-=NgyqnEuj-Rpe%sQ(TB6nbQ!u31ykdqyJwA)fDRl$L^CQ=&}dZL}<5PVfYdMM|#&L5B3Nmxfz#aGMfU)v&)J^TMTZnn*)49`foCfvAD+%WuI!>71ZIM>m8 zcuFriIDDm2-cjn|t~o4bgg2gZadVAZ`iS!g+t2r76*-O0!K%MyaUJ?)z z4#x`!l=~=`$B!+N;5L=gn-PT*&4t90zyUpEL1HyBK|QXW7U$_)nWj>86zLH(vxE!J z_uxwyH@4uO=Py~Zv|PYvwN%pRX= zBQj&%loZ(p9haxeHtB6`PEFgCuVfANiAOj&W(%jsN%4;;jz^=9S|*WnQb|O}U^F_h zKA$S1s9b*|gDNW3=C)I%LeiW}htg3=-CCfSv9OA_HccYZ@-EdFVv*vXJ#qBR9s$bi z487#?6=P-^@FgYKAAwD$XeyHqtEGnTp&)gaf40Ku8udmWWr;NRv>hR(p^j@%1wY7A z6{cgXlsRzNm}3afGB!7FfN4?4i4%EYo6tEq|A$lpT|dQZ{&{PMu?OPX8uW+R!aDfanXAgwp^Se;fX?;(7%?M$c!=zY3-A+ zqp95Xr6fYh>3aTG78d-@&LnQ^*Z%^H|Ey&G7n#}F+J7DvYVU?8bzZF1liB{4vNW^3 zp#l(m)$EwavLwIcvTV5E3pgyGfNS&zqHjG$bOt13{zY0g3+csF)SE>u?+KDXtE&IE h#{ZbX48-eGuyfaZ4V&%*)LA7sps!*m&;3i9{{;vV@NNJA literal 0 HcmV?d00001 diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp index cf03d56f3..b5211a366 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp @@ -22,6 +22,14 @@ #include namespace Opm { +typedef AutoDiffBlock ADB; +typedef ADB::V V; +typedef ADB::M M; +typedef Eigen::Array DataBlock; + namespace { std::vector @@ -36,20 +44,37 @@ namespace { double operator()(double x) const { return std::max(std::min(x, 1.0), 0.0); } }; + + V computePerfPress(const UnstructuredGrid& g, + const Wells& wells, + const V& rho, + const double grav) + { + const int nw = wells.number_of_wells; + const int nperf = wells.well_connpos[nw]; + const int dim = g.dimensions; + + V wdp = V::Zero(nperf, 1); + assert(wdp.size() == rho.size()); + + for (int w = 0; w < nw; ++w) { + const double ref_depth = wells.depth_ref[w]; + for (int j = wells.well_connpos[w]; j < wells.well_connpos[nw + 1]; ++j) { + const int cell = wells.well_cells[j]; + const double cell_depth = g.cell_centroids[dim * cell + dim - 1]; + wdp(j) = rho(j) * grav * (cell_depth - ref_depth); + } + } + + return wdp; + } + }//anonymous namespace -typedef AutoDiffBlock ADB; -typedef ADB::V V; -typedef ADB::M M; -typedef Eigen::Array DataBlock; - @@ -57,25 +82,61 @@ typedef Eigen::Array(fluid.numPhases(), ADB::null())) + , wops_(wells) + , mob_ (fluid.numPhases(), ADB::null()) + , residual_ ({ std::vector(fluid.numPhases(), ADB::null()), + ADB::null(), + ADB::null() }) { } + FullyImplicitTwoPhaseSolver:: + WellOps::WellOps(const Wells& wells) + : w2p(wells.well_connpos[ wells.number_of_wells ], + wells.number_of_wells) + , p2w(wells.number_of_wells, + wells.well_connpos[ wells.number_of_wells ]) + { + const int nw = wells.number_of_wells; + const int* const wpos = wells.well_connpos; + + typedef Eigen::Triplet Tri; + + std::vector scatter, gather; + scatter.reserve(wpos[nw]); + gather .reserve(wpos[nw]); + + for (int w = 0, i = 0; w < nw; ++w) { + for (; i < wpos[ w + 1 ]; ++i) { + scatter.push_back(Tri(i, w, 1.0)); + gather .push_back(Tri(w, i, 1.0)); + } + } + + w2p.setFromTriplets(scatter.begin(), scatter.end()); + p2w.setFromTriplets(gather .begin(), gather .end()); + } + void FullyImplicitTwoPhaseSolver:: step(const double dt, TwophaseState& x, - const std::vector& src) + const std::vector& src, + WellState& xw) { V pvol(grid_.number_of_cells); @@ -88,12 +149,12 @@ typedef Eigen::Array atol; while (resTooLarge && (it < maxit)) { const V dx = solveJacobianSystem(); - updateState(dx, x); + updateState(dx, x, xw); - assemble(pvdt, old_state, x, src); + assemble(pvdt, old_state, x, xw, src); const double r = residualNorm(); @@ -129,6 +190,7 @@ typedef Eigen::Array bpat(np ,nc); + bpat.push_back(xw.bhp().size()); SolutionState state(np); // Pressure. assert (not x.pressure().empty()); const V p = Eigen::Map(& x.pressure()[0], nc); - state.pressure = ADB::constant(p); + state.pressure = ADB::constant(p, bpat); // Saturation. assert (not x.saturation().empty()); const DataBlock s_all = Eigen::Map(& x.saturation()[0], nc, np); for (int phase = 0; phase < np; ++phase) { - state.saturation[phase] = ADB::constant(s_all.col(phase)); + state.saturation[phase] = ADB::constant(s_all.col(phase), bpat); // state.saturation[1] = ADB::constant(s_all.col(1)); } + + // BHP + assert (not x.bhp().empty()); + const V bhp = Eigen::Map(& xw.bhp()[0], xw.bhp().size()); + state.bhp = ADB::constant(bhp, bpat); + return state; } @@ -164,7 +235,8 @@ typedef Eigen::Array(& xw.bhp()[0], xw.bhp().size()); + vars0.push_back(bhp); std::vector vars = ADB::variables(vars0); @@ -201,6 +277,8 @@ typedef Eigen::Array& src) { // Create the primary variables. - const SolutionState state = variableState(x); + const SolutionState state = variableState(x, xw); // -------- Mass balance equations -------- const V trans = subset(transmissibility(), ops_.internal_faces); const std::vector kr = computeRelPerm(state); for (int phase = 0; phase < fluid_.numPhases(); ++phase) { const ADB mflux = computeMassFlux(phase, trans, kr, state); - ADB source = accumSource(phase, kr, src); - residual_[phase] = + residual_.mass_balance[phase] = pvdt*(state.saturation[phase] - old_state.saturation[phase]) - + ops_.div*mflux - source; + + ops_.div*mflux; + if (not src.empty()) { + ADB source = accumSource(phase, kr, src); + residual_.mass_balance[phase] = residual_.mass_balance[phase] - source; + } } + // -------- Well equation, and well contributions to the mass balance equations -------- + + // Contribution to mass balance will have to wait. + const int nc = grid_.number_of_cells; + const int np = wells_.number_of_phases; + const int nw = wells_.number_of_wells; + const int nperf = wells_.well_connpos[nw]; + + const std::vector well_cells(wells_.well_cells, wells_.well_cells + nperf); + const V transw = Eigen::Map(wells_.WI, nperf); + + const ADB& bhp = state.bhp; + + const DataBlock well_s = wops_.w2p * Eigen::Map(wells_.comp_frac, nw, np).matrix(); + + // Extract variables for perforation cell pressures + // and corresponding perforation well pressures. + const ADB p_perfcell = subset(state.pressure, well_cells); + // Finally construct well perforation pressures and well flows. + + // Compute well pressure differentials. + // Construct pressure difference vector for wells. + const int dim = grid_.dimensions; + const double* g = gravity(); + if (g) { + // Guard against gravity in anything but last dimension. + for (int dd = 0; dd < dim - 1; ++dd) { + assert(g[dd] == 0.0); + } + } + ADB cell_rho_total = ADB::constant(V::Zero(nc), state.pressure.blockPattern()); + for (int phase = 0; phase < 2; ++phase) { + const ADB cell_rho = fluidDensity(phase, state.pressure); + cell_rho_total += state.saturation[phase] * cell_rho; + } + ADB inj_rho_total = ADB::constant(V::Zero(nperf), state.pressure.blockPattern()); + assert(np == wells_.number_of_phases); + const DataBlock compi = Eigen::Map(wells_.comp_frac, nw, np); + for (int phase = 0; phase < 2; ++phase) { + const ADB cell_rho = fluidDensity(phase, state.pressure); + const V fraction = compi.col(phase); + inj_rho_total += (wops_.w2p * fraction.matrix()).array() * subset(cell_rho, well_cells); + } + const V rho_perf_cell = subset(cell_rho_total, well_cells).value(); + const V rho_perf_well = inj_rho_total.value(); + V prodperfs = V::Constant(nperf, -1.0); + for (int w = 0; w < nw; ++w) { + if (wells_.type[w] == PRODUCER) { + std::fill(prodperfs.data() + wells_.well_connpos[w], + prodperfs.data() + wells_.well_connpos[w+1], 1.0); + } + } + const Selector producer(prodperfs); + const V rho_perf = producer.select(rho_perf_cell, rho_perf_well); + const V well_perf_dp = computePerfPress(grid_, wells_, rho_perf, g ? g[dim-1] : 0.0); + + const ADB p_perfwell = wops_.w2p * bhp + well_perf_dp; + const ADB nkgradp_well = transw * (p_perfcell - p_perfwell); + + const Selector cell_to_well_selector(nkgradp_well.value()); + ADB well_rates_all = ADB::constant(V::Zero(nw*np), state.bhp.blockPattern()); + ADB perf_total_mob = subset(mob_[0], well_cells) + + subset(mob_[1], well_cells); + std::vector well_contribs(np, ADB::null()); + std::vector well_perf_rates(np, ADB::null()); + for (int phase = 0; phase < np; ++phase) { + // const ADB& cell_b = rq_[phase].b; + // const ADB perf_b = subset(cell_b, well_cells); + const ADB& cell_mob = mob_[phase]; + const V well_fraction = compi.col(phase); + // Using total mobilities for all phases for injection. + const ADB perf_mob_injector = (wops_.w2p * well_fraction.matrix()).array() * perf_total_mob; + const ADB perf_mob = producer.select(subset(cell_mob, well_cells), + perf_mob_injector); + const ADB perf_flux = perf_mob * (nkgradp_well); // No gravity term for perforations. + well_contribs[phase] = superset(perf_flux, well_cells, nc); + residual_.mass_balance[phase] += well_contribs[phase]; + } + + + // Handling BHP and SURFACE_RATE wells. + V bhp_targets(nw); + for (int w = 0; w < nw; ++w) { + const WellControls* wc = wells_.ctrls[w]; + if (wc->type[wc->current] == BHP) { + bhp_targets[w] = wc->target[wc->current]; + } else { + OPM_THROW(std::runtime_error, "Can only handle BHP type controls."); + } + } + const ADB bhp_residual = bhp - bhp_targets; + // Choose bhp residual for positive bhp targets. + residual_.well_eq = bhp_residual; } @@ -265,7 +440,7 @@ typedef Eigen::Array(& insrc[0], grid_.number_of_cells); // compute the out-fracflow. - ADB f_out = computeFracFlow(phase, kr); + ADB f_out = computeFracFlow(phase); // compute the in-fracflow. V f_in; if (phase == 1) { @@ -281,15 +456,10 @@ typedef Eigen::Array& kr) const + FullyImplicitTwoPhaseSolver::computeFracFlow(const int phase) const { - const double* mus = fluid_.viscosity(); - ADB mob_phase = kr[phase] / V::Constant(kr[phase].size(), 1, mus[phase]); - ADB mob_wat = kr[0] / V::Constant(kr[0].size(), 1, mus[0]); - ADB mob_oil= kr[1] / V::Constant(kr[1].size(), 1, mus[1]); - ADB total_mob = mob_wat + mob_oil; - ADB f = mob_phase / total_mob; + ADB total_mob = mob_[0] + mob_[1]; + ADB f = mob_[phase] / total_mob; return f; } @@ -305,13 +475,15 @@ typedef Eigen::Array matr = mass_res.derivative()[0]; - V dx(V::Zero(mass_res.size())); + const ADB mass_res = vertcat(residual_.mass_balance[0], residual_.mass_balance[1]); + const ADB total_res = collapseJacs(vertcat(mass_res, residual_.well_eq)); + + const Eigen::SparseMatrix matr = total_res.derivative()[0]; + V dx(V::Zero(total_res.size())); Opm::LinearSolverInterface::LinearSolverReport rep = linsolver_.solve(matr.rows(), matr.nonZeros(), matr.outerIndexPtr(), matr.innerIndexPtr(), matr.valuePtr(), - mass_res.value().data(), dx.data()); + total_res.value().data(), dx.data()); if (!rep.converged) { OPM_THROW(std::runtime_error, "FullyImplicitBlackoilSolver::solveJacobianSystem(): " @@ -324,11 +496,13 @@ typedef Eigen::Array(&state.pressure()[0], nc); const V p = p_old - dp; @@ -362,6 +540,10 @@ typedef Eigen::Array(&well_state.bhp()[0], nw, 1); + const V bhp = bhp_old - dbhp; + std::copy(&p[0], &p[0] + nc, well_state.bhp().begin()); } @@ -389,21 +571,28 @@ typedef Eigen::Array& kr , - const SolutionState& state ) const + const SolutionState& state ) { // const ADB tr_mult = transMult(state.pressure); const double* mus = fluid_.viscosity(); - ADB mob = kr[phase] / V::Constant(kr[phase].size(), 1, mus[phase]); - if (phase ==0) - std::cout << "watetr mob\n" << mob.value() << std::endl; - const ADB dp = ops_.ngrad * state.pressure; + // ADB& mob = mob_[phase]; + mob_[phase] = kr[phase] / V::Constant(kr[phase].size(), 1, mus[phase]); + // ADB mob = kr[phase] / V::Constant(kr[phase].size(), 1, mus[phase]); + V z(grid_.number_of_cells); + for (int c = 0; c < grid_.number_of_cells; ++c) { + z(c) = grid_.cell_centroids[c * 3 + 2]; + } + const double* grav = gravity(); + const ADB rho = fluidDensity(phase, state.pressure); + const ADB rhoavg = ops_.caver * rho; + const ADB dp = ops_.ngrad * state.pressure - grav[2] * (rhoavg * (ops_.ngrad * z.matrix())); const ADB head = trans * dp; UpwindSelector upwind(grid_, ops_, head.value()); - return upwind.select(mob) * head; + return upwind.select(mob_[phase]) * head; } @@ -415,13 +604,14 @@ typedef Eigen::Array::const_iterator - b = residual_.begin(), - e = residual_.end(); + b = residual_.mass_balance.begin(), + e = residual_.mass_balance.end(); b != e; ++b) { r = std::max(r, (*b).value().matrix().norm()); } - + + r = std::max(r, residual_.well_eq.value().matrix().norm()); return r; } @@ -442,7 +632,15 @@ typedef Eigen::Array #include #include - +#include +#include struct UnstructuredGrid; namespace Opm { @@ -21,11 +22,14 @@ namespace Opm { public: FullyImplicitTwoPhaseSolver(const UnstructuredGrid& grid, const IncompPropsAdInterface& fluid, - const LinearSolverInterface& linsolver); + const Wells& wells, + const LinearSolverInterface& linsolver, + const double* gravity); void step(const double dt, TwophaseState& state, - const std::vector& src); + const std::vector& src, + WellState& wstate); private: typedef AutoDiffBlock ADB; typedef ADB::V V; @@ -38,34 +42,60 @@ namespace Opm { SolutionState(const int np); ADB pressure; std::vector saturation; + ADB bhp; }; + /* + struct Source { + Wells& wells; + std::vector src; + } source; + */ + struct WellOps { + WellOps(const Wells& wells); + M w2p; // well->perf + M p2w; // perf->well + }; + const UnstructuredGrid& grid_; const IncompPropsAdInterface& fluid_; + const Wells& wells_; const LinearSolverInterface& linsolver_; + const double* grav_; const std::vector cells_; HelperOps ops_; - std::vector residual_; + const WellOps wops_; + + std::vector mob_; + + struct { + std::vector mass_balance; + ADB well_flux_eq; + ADB well_eq; + } residual_; SolutionState - constantState(const TwophaseState& x); + constantState(const TwophaseState& x, + const WellState& xw); SolutionState - variableState(const TwophaseState& x); + variableState(const TwophaseState& x, + const WellState& xw); void assemble(const V& pvdt, const SolutionState& old_state, const TwophaseState& x, + const WellState& xw, const std::vector& src); V solveJacobianSystem() const; - void updateState(const V& dx, - TwophaseState& x) const; + void updateState(const V& dx, + TwophaseState& x, + WellState& xw)const; std::vector computeRelPerm(const SolutionState& state) const; V transmissibility() const; ADB - computeFracFlow(int phase, - const std::vector& kr) const; + computeFracFlow(const int phase) const; ADB accumSource(const int phase, const std::vector& kr, @@ -74,7 +104,7 @@ namespace Opm { computeMassFlux(const int phase, const V& trans, const std::vector& kr, - const SolutionState& state) const; + const SolutionState& state); double residualNorm() const; @@ -86,6 +116,11 @@ namespace Opm { fluidDensity(const int phase) const; ADB transMult(const ADB& p) const; + + ADB + fluidDensity(const int phase, + const ADB& p) const; + const double* gravity() const { return grav_; } }; } // namespace Opm #endif// OPM_FULLYIMPLICITTWOPHASESOLVER_HEADER_INCLUDED From 6d5b90df548214d4993fe132861d2403e63b4e8c Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 17 Dec 2013 20:28:32 +0800 Subject: [PATCH 15/83] modified the simulator more general. --- .../.FullyImplicitTwoPhaseSolver.cpp.swp | Bin 36864 -> 0 bytes .../SimulatorFullyImplicitTwophase.cpp | 279 +++++++++++++++--- .../SimulatorFullyImplicitTwophase.hpp | 9 +- 3 files changed, 251 insertions(+), 37 deletions(-) delete mode 100644 opm/polymer/fullyimplicit/.FullyImplicitTwoPhaseSolver.cpp.swp diff --git a/opm/polymer/fullyimplicit/.FullyImplicitTwoPhaseSolver.cpp.swp b/opm/polymer/fullyimplicit/.FullyImplicitTwoPhaseSolver.cpp.swp deleted file mode 100644 index 776e79ab91a6a8d8336cc313dcba0b0fa42293bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36864 zcmeI533Mb!dB>Z>fB_q0ZWAZ%<-PStD~+TzV5~i0PrQWniaoTzdKpGDEopXVrpG-q zlGe*|B#;0m=1Rae;dmGVCMKNb#s*#>#Ch<5B;4k(frK+aILsCD`|9Yb?iopI8(v-> z)9?K?Gu>5Ref3qno;_hfg;I#}?z{=vdrp2u53R{a7PP)NlKJwp*_st2J8nTBX(*+bO?x znP2-C@^Shh-w*1o!!#4k2%yXOW&vMVr1efsNoH;O^k3 z_YQ*3gLi<-!C`PQm;!$a9tj=@&H(>;uON6cxE8zwyZ|hLHQ=G(EbtKUANQnuZ~|Ni zwu1YEn@fdb=op zOWbdA)3KZ8vHUj|`DXJpyN#JBDaErTMMGyho^q%2^pZbhip*sX4-chTN5a!fd@; zs}_-uiV|uUhDQ9-hI~Secm^7)WC$!%BjIA)YL~QZ9nT}7x;vNDs{YN2NDk^V-CDi6 zrC#4AzYCH|cr)EdB^vUx%_Zf963NO7wazjBLA};&tB9vkA^qwSGbGs*~bP)k$W=Hp zgmz2jWBD?y+L4cf^vhB;YIWwrQOcOr-#xW1+2!&(M(E|*6wVdZ&q~znByp|UK2)tW zAT2k;b)ozi4JQset~EVN5v{G0TGcD9S(41{Lq~GCX5%C*NQNV+qSmRkazC_qcmt0% z_R;&yRqz5A;ktFyKdY@WRZC@%7NMk5;=a%iEcYodizFQ15H3<~b5ZCo3jH))xy_8( zDbNDNcJ0y#Ex1WFgZbr2dRm$ZV$+k8PmYqf&}=bWM#2e_q|`L4A_9@C$ez!r*{(4I zI^J9=MI)_AMp(?=eRD{C)QIx5m-?+!A5VF)k}#hcDSFRDV>or>J&>wl-xl`U+k1I!=tovoyehk{R$}&B`P)!r%mWo@FqxXRUD>EO>)v6uCjYt4J!PwTC-B`R-nm z{%m6e%E+tDDE5{uhJ^qk9P7JMST>VPvPDFclSluc^8QnPKid8CZ!!Ji!l;f$=qucn zGpY{NuRPU$N=mFF#yYLp@>nM+H`^uodeZeF+yZU}lI~Mra1A6dkib9!0|^WyFp$7N0s{#QBruS`Kmr2^3?wj+!2h8H za2#aJQi>I^r#Ty-u`FUv-JtgWYf1A(38ZKLKWkWjFMIq;z)`RXJP14x+=`$7%iv4k zdhk-P51bDk2_6K#gJ1tepaG`99&j!Qz;*cXUk+XdO5iDA6W9pO1*e0X@!@|R+z2iM z&jLHa+2H5+@oxg}25$y008a%aupUf+hk^%yGr@hq8Q?zPbZ`o|1z*3!09+1c!DGOg z;NIXCeE-*gH-ZjW2Y!fAyWFW3VQrk$4mur^d@xi#Ja z@%;7m5R8q5+r()uF09ZRpztZo&PGYptVE&q(uZPxR(ADbU<*-Mm2q7?>CN4#mGQbe zp9L9qijA*+OdHQtnL;!a2S<#F9q&a-rX7}>RZV%e9xp=37OZQBIW}ySB&TvPZ3^4n znRe8{{w}smTNOOG6rkNmo{I%mggRz7ER(IcS>>G<87?$iVz$H}=ao&y9ZQ(zkk&#u zsg(&rXe;6PRZt9B4%$utn(@^3tyxp?3F1#K;>E{ZW~|z~%dL&M^qaLGx>OXOMrjr% zM({L&)=&{caZ+0<^ty-;)7o%mUi?W~GJN;k3c85tP+1$rxyn>5J_Zz$sIbq4!EFawc91b z^dTh@aWJitU^HyU-J}vZaR^blWZ3FwP$^d`-NrtZvY<$hgh!K+u$@@9l+F`|wb`)H zj63qA7&Tg*V}+r-^H!3sQ-K*Mx`p8eUi_ur;#&*V&alxD?J}NpC01Ln(J-!8OM0C2 zJk9zpR}oNPIhRk}(WK^zD=KchlDm=ZPWZHySEJC#K-8Ew&{F@Ok`pBG0qUQhnD&;s&G*IA94Ai2@Cir+>p_3cRHjZ$R{ zswhF#mn6)i?Ga*B*v=SA?bG3~QfJkr6Fj0ul5$pQWdTtixboYn0kd3GBf*<3q-bIo zn$c1Rxk~^84PD@C+eR@Udqqmfsod#ItlO%6C^Wg(tqZ-l1Nz0*xYLOnqw{fHT1wBn zZqj$WInv$`oe-teW^pS|?bVLeO~Ovvi@7Iik>KSYSNU_P)ZE#YSy8IE4ChjqR%#M8 z$hXN!)!JnwLj=d3CgZWmOg+Kam}!+eUhi}rPxlYM&mq{&Ox$F!#g88@&WOu=N`+T8 zWv;gsMSA;EW#-Kzx1|NtJZKvh?cx)kN}D6So9UyvsR>lcHoptE5g- zmNb=}U0gDRyQ^7~#cZaFTA3`*c-z-RmT%Ra+9g)OEaU10skv=Moy08R z<^q*1x^8T$EZt4Dg{7RSX(oL-n>seU`Z~&9r^>fazt)lEW)It}B!yi&wo_(35o8&4 zDOYCGrt4vnZmXh{MHpJeiA4NiHoaMmZMu74g{7N84F{>lQf6N5mui*8;PEt%En$^U z%|OqTq9>jewYnWuYAQ1GLw?d3QTzX`=>4nA{(q0zP=1NM{*6H5{`Z0r@WYefHnq09Sym;2z+U?7=1O{>eb%>wnJP`+eXtZ~)vFypcWklfeDK+u3Iy0q26V z!5@K#g731|{uH!)g^rzd@xi8LRO3WT1UINg#}wmPNg5_&g!l88pV*3YN_BGlq` zq|~f^uJ^fd8(qk*g`8ohoXkZXZ+onH5+;1Yx*VoqxoPsa!W@HHcIB5`d!}}m_FuSn z&p{npmvoyQLLEzyL^+LwYqph}Vcf)~Fwc&jrNh<>r$Pz#l64$$s8}G@cnU@co(Q8l zRqGv>9(#m%!u4WhbhGC7kbJFlF&8vD-m*o=$=*aIq|i(T4?6uq5s#QTL_lKv*mq#>&Mn(^l=g1f zzeAT@q~|C~{K>Y8?Em3Qc&r37mI?}ZN#upl1-a$ zvSA*}Zw0mZq>H6FAfvt$+>ZgJ&^$`0X|<(JY@qk4`$e&hR}a~jH1Y-;uLIlauli{V zEY+Gv)IpY9C(E%ZtifdxSGyI?# z1TEGfF*~G&p`l_~;voq;9@fI@#2xl$l$vcOq;;Sz-9$YPc`i|VHDl#s*`jxy+UTY0 zF2w}Cz%;XjF-yHHC$K5I-kMtW=)GyS|5S$QZSPOuo(gHZI0*Im=Gly(UxNtIKU(Oyosl9HV2fvS}(YRSpX>=sA| zu`dp@3d6Rec3x=O+-An(M>owrHAFZdW_I7YQ*>fAtV?_Toah-X*f$qZL?j zr0pI0IbhdVKG7SVHF?ySmZfqYHAh6JmB*0qM;|rA45fsk+FOok2-`y(>!im@2)!E}mXI1Hvsh3aE2|JY zoVB#5Nn6H2sVIvDHQjFtr}pmIeqh^%4X(crlZHIOO=*gCKeyEuw&7K*xy|l7zlqoo5O3zFxR*UzYPkgOQ z;hAC`>(X|XeM`=C{*#y#^zer|S(LqVyPS5x5Udx!Ib)O8WzzQRqrTVo)!v%Pbd#;8 zEsE{g(56XJ7fG*Y#?{{%&)8I^YrVX)!Q)$WYqm|6Rjo1Zk8-x%vt33S zUaM%g_qWz@eUwXjr>m4}RN>ds`u(?&Uz#2(l)22zQ@P!;$x2G>qO3|D+bt*6usm1h%x5QEtk#;W)Q@%MWj%%)DXf=y zm>%?Ui!RY-b3Rqwh4swI89x{?=QYJ@DDnRfVX1$U>{;>uAJH4e;2rGqF9y@#Vc@sy z^TqFfE$DzJf%V|tK>Yo3=Kq!8x!|eb0!oy;Ir5O-VR<1o(m3u zhl4u-@$vsN_z3tg_z<`OTn|17t^*A)2Q~of=l?DCf?t4J!Oy|Zz)!(X!1uvh!JEJ{ zz)^4pI2F7Vo5EATH24MfgiisnFFX#P@#_c81S^Yr)fjoZr6yJP7~#9F5{fz>Ge40d!~wPe9d z9(G&I=TX`#-Ll6F8ZjKQo5rBMDXe0)RHq>6kAY&CKWPG#P*s+QI*HRZmT7rNz0AB* zWy`p)Rc>lC`$$U0Uk7GLw-=YEtnITpl;C6ti=8kfzDbVcrPWEf?a!;v9%8g%1uZ1h z4&zxwL?im~3HW%Ua%Dc06h$XBTTI-HkV$6cA#R6^g0g8Z_~wjfaB^h)R5_cgqNdlM;&NNr#HUA*Vkqz7qd)3lFJ-y8$vEq(p1a#U z^w|o-HTrZNo-+AQDk4=rc(DfY2(#r{ol)FlEpr6cWK5G=>o{lPS{+9aG$tn%$;Xu| z7!1nIeaG6Js8JZ24A%sXz$&)9G^I>u=Oj@JQ-S8MXU6ntn(un^ilNFbbj^lfM0Abb zXn0+?4*|I)VKl{<^TNrlW;I%x>Tu?FMBlA7n|l<)6`U4j%r9daTq=8RbCG!vz$e9Vt?DM z;)m>MS-aGA2X=*d2DPJFjgr~|!4td#M*M8)xK6pzg~&QB%>)5_UiEUT9nn$R1^P{g zm&G6%LQH5m?>!$nI4HVAl;o)*SVzvLhMFa?jCoS*7Rx-EkT=#S!YA~Hl##PE*W=#+Sfd2eY_&`#`v)lgXK=* z{EW^Mu_!_NN*MNBMS2zBY>APJd6U2Uk_xQMVk6~ut;tWvdsa89G-}#Zp8CE)UEa_! zz5{7ev!7&*@3*SR+tE}*aW$G5ViP{GX+_Gh&N9J>OU1L)nxnFjmhi}Q)uoz7OCmKa zrLxr$U6vk#>q9V*D#@jX;Ix(}ZQipVRt{b^FiAyu-dC%)rDxh}If6XCtrXJ$v;Hie zEV*n?$^H_7sH%!Ph~6EiW5uN!uK9r`lEUk`JPbGY zcH4e*`j^(jRBv(8simBa*>d#mSYldegyKvz$BQ!X(U1`JD7T_!Rfn%p0nbI}o5hVm z*ch2hHD+shlL16r&+e06H`mHZbj4hnu`wcI#Fe7Po9R}p!%3%nXy_MxQ;)ynfynSS zU3w7oVRx+xHuJRj9VCnLB%p}!O11xIi>~&Qvj4a5*Z&ZE{u{vc;ML#+_!Dq1@D29- zuLBkE1TYMK4Ur#%e*ss57lO;dA`m;kDd3~*`)>g62k!@8gx2CoJ3&cDZjUEpl+ zBWwXb1RndAHvq96yb@diUJ5P;&jZf_^R*z121TnjD+ zr-SdX=a>Ee(?9|q2TlWDWAA?)OoId9e&9?X@ACU1_#F5w_zd_Ykn;dHfaiff2MzFO za8K|#V-t9Xu?sZ$jKDeIKHyYv3b+&a4R(QFfS-c@0@s1JfTw}IU;u<;t3d!LReS0^5rM`$gt_ZC2%5)^W4Vny~w7a`u+WgJ9jg zERR5{u1xhHG*~SrLFL9#QqHCtx+d7!^NTL#J!Cz*OsL*?D zr~l1{OL>l*u1F;+@9Ze4hj_&G0f|C)ZX}#n_pF~nvH3*cDr#s%MFJ$#gdMq>vab)!ZH#6Zfm#Qu-xd(q8J-j}`yb zkJE;e)l+IwvsqF5EX*db)wh>v`Rcx~VoNIFW@3>&-8n6d(+|C?$i%g%?0A%BmiIal zYvT42%197hl5RC+dnCs%ip`klY2UIVg{sSSANiRlT^ODjItgl84w@i;XJpol#Hc)z z3rm(HVh(aFt8J5s<2SQxAgMB!@`9Uiplqy%ovYnWRZZpo#3eIKeo_uBc|Gz~y-jTC zNwlfu{vst+uRS$2A)bOS^1Zh~yRF4qvuZ;wyseFdLd-|ys<#0;pwjj@J~avJmX!#1 z#vD0qi!oMI*;0P0hExigKy^%t>b1U#>Q!%gQ!(>RiCQ6@vKFOLl9+A9bbo05VICFG zo8^T$`IgCYsdLMD!`5+PvTf4A`4tYGB+%fl>+6}saGg<%YTsae_tqTTQ<7Eg!Y>p& zkAuiXuIw1Nw7{#&?66m^BlqgS3jCeN7H++>EU2g*_0~U87C2Vq6=?l+hIuPmpbF*B zba<&zNzUOW1uFkFn<{-tbI?5?el)Q@ud$3lHN9^PqiPClbkiBoiPP(5cZ{CT-CDz* zN8FXD8_>1^Oi`|xE*5I-iiGVE{j(uR33@%XlazQJn5@cGXWh;B+&Mba*m}%bv7XeY z<_jyKJG`L`t*V=cebm-!R5ii8;*q7TTywaKLwYra-Wo=;dzeu}W}5+@@cd$>rmk?Rqv0Dsw*Z(wElfiqR8lYvTBiCf1C zYg@|dpgzzX5(MvT`Q{9O*>Lu#E_qnSFLJ=N&m4td*(&?jMT4|#gkC|$)n zMPr5*_YfI9(+Zbw^;(0Sdv&PTZE_}S$5JaP=(l2KmQR*I)#Z@hSet7WxFs_s^T8S5+wALa0XKm! zgD(NG3w!~59$W)vz*#`v5AZGa_7Wo??*(Xq7|1*ScY{ZP)4(U#+y53Bf#(G5;e7=md_}3U8dx5 zs(P72g}SGrZW#c~O`ZvYdp&37?10wk``dTzVJ@Y9?GLkL^K^rDber>EW`gK@{ELWr zH|kV<%pxj3*bgb)oUMdA>owu;3q>?N+0_C;^zU*cv;n_ID5?6_D-5+d)?ax^aQ_hH zUhSr%cY7q7(Qdzw!)8v&6`ho`>Z?a0%l8ZEMLl)+ou53jF)tp+neMNf7Ox-OEs&;` zOAd2-=NgyqnEuj-Rpe%sQ(TB6nbQ!u31ykdqyJwA)fDRl$L^CQ=&}dZL}<5PVfYdMM|#&L5B3Nmxfz#aGMfU)v&)J^TMTZnn*)49`foCfvAD+%WuI!>71ZIM>m8 zcuFriIDDm2-cjn|t~o4bgg2gZadVAZ`iS!g+t2r76*-O0!K%MyaUJ?)z z4#x`!l=~=`$B!+N;5L=gn-PT*&4t90zyUpEL1HyBK|QXW7U$_)nWj>86zLH(vxE!J z_uxwyH@4uO=Py~Zv|PYvwN%pRX= zBQj&%loZ(p9haxeHtB6`PEFgCuVfANiAOj&W(%jsN%4;;jz^=9S|*WnQb|O}U^F_h zKA$S1s9b*|gDNW3=C)I%LeiW}htg3=-CCfSv9OA_HccYZ@-EdFVv*vXJ#qBR9s$bi z487#?6=P-^@FgYKAAwD$XeyHqtEGnTp&)gaf40Ku8udmWWr;NRv>hR(p^j@%1wY7A z6{cgXlsRzNm}3afGB!7FfN4?4i4%EYo6tEq|A$lpT|dQZ{&{PMu?OPX8uW+R!aDfanXAgwp^Se;fX?;(7%?M$c!=zY3-A+ zqp95Xr6fYh>3aTG78d-@&LnQ^*Z%^H|Ey&G7n#}F+J7DvYVU?8bzZF1liB{4vNW^3 zp#l(m)$EwavLwIcvTV5E3pgyGfNS&zqHjG$bOt13{zY0g3+csF)SE>u?+KDXtE&IE h#{ZbX48-eGuyfaZ4V&%*)LA7sps!*m&;3i9{{;vV@NNJA diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.cpp index 377a42265..be908b0a4 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.cpp @@ -50,18 +50,23 @@ namespace Opm { + + class SimulatorFullyImplicitTwophase::Impl { public: Impl(const parameter::ParameterGroup& param, const UnstructuredGrid& grid, const IncompPropsAdInterface& props, + WellsManager& wells_manager, LinearSolverInterface& linsolver, - std::vector& src); + std::vector& src, + const double* gravity); SimulatorReport run(SimulatorTimer& timer, TwophaseState& state, - std::vector& src); + std::vector& src, + WellState& well_state); private: @@ -71,9 +76,15 @@ namespace Opm std::string output_dir_; int output_interval_; // Parameters for well control + bool check_well_controls_; + int max_well_control_iterations_; // Observed objects. const UnstructuredGrid& grid_; const IncompPropsAdInterface& props_; + WellsManager& wells_manager_; + const Wells* wells_; + const std::vector& src_; + const double* gravity_; // Solvers FullyImplicitTwoPhaseSolver solver_; // Misc. data @@ -86,10 +97,12 @@ namespace Opm SimulatorFullyImplicitTwophase::SimulatorFullyImplicitTwophase(const parameter::ParameterGroup& param, const UnstructuredGrid& grid, const IncompPropsAdInterface& props, + WellsManager& wells_manager, LinearSolverInterface& linsolver, - std::vector& src) + std::vector& src, + const double* gravity) { - pimpl_.reset(new Impl(param, grid, props, linsolver, src)); + pimpl_.reset(new Impl(param, grid, props, wells_manager, linsolver, src, gravity)); } @@ -98,9 +111,10 @@ namespace Opm SimulatorReport SimulatorFullyImplicitTwophase::run(SimulatorTimer& timer, TwophaseState& state, - std::vector& src) + std::vector& src, + WellState& well_state) { - return pimpl_->run(timer, state, src); + return pimpl_->run(timer, state, src, well_state); } @@ -133,6 +147,75 @@ namespace Opm dm["velocity"] = &cell_velocity; Opm::writeVtkData(grid, dm, vtkfile); } + + + static void outputStateMatlab(const UnstructuredGrid& grid, + const Opm::TwophaseState& state, + const int step, + const std::string& output_dir) + { + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + std::vector cell_velocity; + Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); + dm["velocity"] = &cell_velocity; + + // Write data (not grid) in Matlab format + for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { + std::ostringstream fname; + fname << output_dir << "/" << it->first; + boost::filesystem::path fpath = fname.str(); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; + std::ofstream file(fname.str().c_str()); + if (!file) { + OPM_THROW(std::runtime_error, "Failed to open " << fname.str()); + } + file.precision(15); + const std::vector& d = *(it->second); + std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); + } + } + + static void outputWellStateMatlab(const Opm::WellState& well_state, + const int step, + const std::string& output_dir) + { + Opm::DataMap dm; + dm["bhp"] = &well_state.bhp(); + dm["wellrates"] = &well_state.wellRates(); + + // Write data (not grid) in Matlab format + for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { + std::ostringstream fname; + fname << output_dir << "/" << it->first; + boost::filesystem::path fpath = fname.str(); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error,"Creating directories failed: " << fpath); + } + fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; + std::ofstream file(fname.str().c_str()); + if (!file) { + OPM_THROW(std::runtime_error,"Failed to open " << fname.str()); + } + file.precision(15); + const std::vector& d = *(it->second); + std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); + } + } + + + + /* static void outputWaterCut(const Opm::Watercut& watercut, const std::string& output_dir) @@ -145,20 +228,36 @@ namespace Opm } watercut.write(os); } - - */ - + static void outputWellReport(const Opm::WellReport& wellreport, + const std::string& output_dir) + { + // Write well report. + std::string fname = output_dir + "/wellreport.txt"; + std::ofstream os(fname.c_str()); + if (!os) { + OPM_THROW(std::runtime_error, "Failed to open " << fname); + } + wellreport.write(os); + } + */ SimulatorFullyImplicitTwophase::Impl::Impl(const parameter::ParameterGroup& param, const UnstructuredGrid& grid, const IncompPropsAdInterface& props, + WellsManager& wells_manager, LinearSolverInterface& linsolver, - std::vector& src) + std::vector& src, + const double* gravity) : grid_(grid), props_(props), - solver_(grid_, props_, linsolver) + wells_manager_(wells_manager), + wells_ (wells_manager.c_wells()), + src_ (src), + gravity_(gravity), + solver_(grid_, props_, *wells_manager.c_wells(), linsolver, gravity_) + { // For output. output_ = param.getDefault("output", true); @@ -175,6 +274,9 @@ namespace Opm } output_interval_ = param.getDefault("output_interval", 1); } + // Well control related init. + check_well_controls_ = param.getDefault("check_well_controls", false); + max_well_control_iterations_ = param.getDefault("max_well_control_iterations", 10); // Misc init. const int num_cells = grid.number_of_cells; @@ -184,16 +286,16 @@ namespace Opm } } - - - SimulatorReport SimulatorFullyImplicitTwophase::Impl::run(SimulatorTimer& timer, TwophaseState& state, - std::vector& src) + std::vector& src, + WellState& well_state) { + // Initialisation. std::vector porevol; - computePorevolume(grid_, props_.porosity(), porevol); + Opm::computePorevolume(grid_, props_.porosity(), porevol); + // const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); std::vector initial_porevol = porevol; @@ -203,14 +305,27 @@ namespace Opm Opm::time::StopWatch step_timer; Opm::time::StopWatch total_timer; total_timer.start(); +#if 0 // These must be changed for three-phase. + double init_surfvol[2] = { 0.0 }; + double inplace_surfvol[2] = { 0.0 }; + double tot_injected[2] = { 0.0 }; + double tot_produced[2] = { 0.0 }; + Opm::computeSaturatedVol(porevol, state.surfacevol(), init_surfvol); + Opm::Watercut watercut; + watercut.push(0.0, 0.0, 0.0); +#endif std::vector fractional_flows; + std::vector well_resflows_phase; + if (wells_) { + well_resflows_phase.resize((wells_->number_of_phases)*(wells_->number_of_wells), 0.0); + } std::fstream tstep_os; if (output_) { std::string filename = output_dir_ + "/step_timing.param"; tstep_os.open(filename.c_str(), std::fstream::out | std::fstream::app); } - for (; !timer.done(); ++timer) { + while (!timer.done()) { // Report timestep and (optionally) write state to disk. step_timer.start(); timer.report(std::cout); @@ -218,34 +333,125 @@ namespace Opm if (output_vtk_) { outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); + outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); + } - + SimulatorReport sreport; - // Run solver. - solver_timer.start(); - std::vector initial_pressure = state.pressure(); - solver_.step(timer.currentStepLength(), state, src); + bool well_control_passed = !check_well_controls_; + int well_control_iteration = 0; + do { + // Run solver. + solver_timer.start(); + std::vector initial_pressure = state.pressure(); + solver_.step(timer.currentStepLength(), state, src, well_state); - // Stop timer and report. - solver_timer.stop(); - const double st = solver_timer.secsSinceStart(); - std::cout << "Fully implicit solver took: " << st << " seconds." << std::endl; - stime += st; - sreport.pressure_time = st; + // Stop timer and report. + solver_timer.stop(); + const double st = solver_timer.secsSinceStart(); + std::cout << "Fully implicit solver took: " << st << " seconds." << std::endl; + stime += st; + sreport.pressure_time = st; + + // Optionally, check if well controls are satisfied. + if (check_well_controls_) { + Opm::computePhaseFlowRatesPerWell(*wells_, + well_state.perfRates(), + fractional_flows, + well_resflows_phase); + std::cout << "Checking well conditions." << std::endl; + // For testing we set surface := reservoir + well_control_passed = wells_manager_.conditionsMet(well_state.bhp(), well_resflows_phase, well_resflows_phase); + ++well_control_iteration; + if (!well_control_passed && well_control_iteration > max_well_control_iterations_) { + OPM_THROW(std::runtime_error, "Could not satisfy well conditions in " << max_well_control_iterations_ << " tries."); + } + if (!well_control_passed) { + std::cout << "Well controls not passed, solving again." << std::endl; + } else { + std::cout << "Well conditions met." << std::endl; + } + } + } while (!well_control_passed); + + // Update pore volumes if rock is compressible. + initial_porevol = porevol; + + // The reports below are geared towards two phases only. +#if 0 + // Report mass balances. + double injected[2] = { 0.0 }; + double produced[2] = { 0.0 }; + Opm::computeInjectedProduced(props_, state, transport_src, stepsize, + injected, produced); + Opm::computeSaturatedVol(porevol, state.surfacevol(), inplace_surfvol); + tot_injected[0] += injected[0]; + tot_injected[1] += injected[1]; + tot_produced[0] += produced[0]; + tot_produced[1] += produced[1]; + std::cout.precision(5); + const int width = 18; + std::cout << "\nMass balance report.\n"; + std::cout << " Injected surface volumes: " + << std::setw(width) << injected[0] + << std::setw(width) << injected[1] << std::endl; + std::cout << " Produced surface volumes: " + << std::setw(width) << produced[0] + << std::setw(width) << produced[1] << std::endl; + std::cout << " Total inj surface volumes: " + << std::setw(width) << tot_injected[0] + << std::setw(width) << tot_injected[1] << std::endl; + std::cout << " Total prod surface volumes: " + << std::setw(width) << tot_produced[0] + << std::setw(width) << tot_produced[1] << std::endl; + const double balance[2] = { init_surfvol[0] - inplace_surfvol[0] - tot_produced[0] + tot_injected[0], + init_surfvol[1] - inplace_surfvol[1] - tot_produced[1] + tot_injected[1] }; + std::cout << " Initial - inplace + inj - prod: " + << std::setw(width) << balance[0] + << std::setw(width) << balance[1] + << std::endl; + std::cout << " Relative mass error: " + << std::setw(width) << balance[0]/(init_surfvol[0] + tot_injected[0]) + << std::setw(width) << balance[1]/(init_surfvol[1] + tot_injected[1]) + << std::endl; + std::cout.precision(8); + + // Make well reports. + watercut.push(timer.currentTime() + timer.currentStepLength(), + produced[0]/(produced[0] + produced[1]), + tot_produced[0]/tot_porevol_init); + if (wells_) { + wellreport.push(props_, *wells_, + state.pressure(), state.surfacevol(), state.saturation(), + timer.currentTime() + timer.currentStepLength(), + well_state.bhp(), well_state.perfRates()); + } +#endif sreport.total_time = step_timer.secsSinceStart(); if (output_) { sreport.reportParam(tstep_os); - } - } - if (output_) { - if (output_vtk_) { - outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); + outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); +#if 0 + outputWaterCut(watercut, output_dir_); + if (wells_) { + outputWellReport(wellreport, output_dir_); + } +#endif + tstep_os.close(); } - // outputWaterCut(watercut, output_dir_); - tstep_os.close(); + + // advance to next timestep before reporting at this location + ++timer; + + // write an output file for later inspection } total_timer.stop(); @@ -258,4 +464,7 @@ namespace Opm } + + + } // namespace Opm diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.hpp index 0dac9641c..c70a65e77 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.hpp @@ -32,6 +32,8 @@ namespace Opm class LinearSolverInterface; class SimulatorTimer; class TwophaseState; + class WellsManager; + class WellState; struct SimulatorReport; /// Class collecting all necessary components for a two-phase simulation. @@ -60,8 +62,10 @@ namespace Opm SimulatorFullyImplicitTwophase(const parameter::ParameterGroup& param, const UnstructuredGrid& grid, const IncompPropsAdInterface& props, + WellsManager& wells_manager, LinearSolverInterface& linsolver, - std::vector& src); + std::vector& src, + const double* gravity); /// Run the simulation. /// This will run succesive timesteps until timer.done() is true. It will @@ -72,7 +76,8 @@ namespace Opm /// \return simulation report, with timing data SimulatorReport run(SimulatorTimer& timer, TwophaseState& state, - std::vector& src); + std::vector& src, + WellState& well_state); private: class Impl; From 2985dc7ccea887707984a56eef92c10147e02814 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 17 Dec 2013 23:49:09 +0800 Subject: [PATCH 16/83] make the simulator read from deckfile make it much more gerenal. --- examples/sim_poly_fi2p_incomp_ad.cpp | 252 ++++++++++++++++++ .../FullyImplicitTwoPhaseSolver.cpp | 5 +- .../FullyImplicitTwoPhaseSolver.hpp | 19 +- 3 files changed, 265 insertions(+), 11 deletions(-) create mode 100644 examples/sim_poly_fi2p_incomp_ad.cpp diff --git a/examples/sim_poly_fi2p_incomp_ad.cpp b/examples/sim_poly_fi2p_incomp_ad.cpp new file mode 100644 index 000000000..8a7ed54ef --- /dev/null +++ b/examples/sim_poly_fi2p_incomp_ad.cpp @@ -0,0 +1,252 @@ +/* + Copyright 2013 SINTEF ICT, Applied Mathematics. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + + +namespace +{ + void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param) + { + if (param.anyUnused()) { + std::cout << "-------------------- Unused parameters: --------------------\n"; + param.displayUsage(); + std::cout << "----------------------------------------------------------------" << std::endl; + } + } +} // anon namespace + + + +// ----------------- Main program ----------------- +int +main(int argc, char** argv) +try +{ + using namespace Opm; + + std::cout << "\n================ Test program for fully implicit three-phase black-oil flow ===============\n\n"; + parameter::ParameterGroup param(argc, argv, false); + std::cout << "--------------- Reading parameters ---------------" << std::endl; + + // If we have a "deck_filename", grid and props will be read from that. + bool use_deck = param.has("deck_filename"); + if (!use_deck) { + OPM_THROW(std::runtime_error, "This program must be run with an input deck. " + "Specify the deck with deck_filename=deckname.data (for example)."); + } + boost::scoped_ptr deck; + boost::scoped_ptr grid; + boost::scoped_ptr props; + boost::scoped_ptr new_props; +// boost::scoped_ptr polymer_props; + PolymerState state; + // bool check_well_controls = false; + // int max_well_control_iterations = 0; + double gravity[3] = { 0.0 }; + std::string deck_filename = param.get("deck_filename"); + deck.reset(new EclipseGridParser(deck_filename)); + // Grid init + grid.reset(new GridManager(*deck)); + + // use the capitalized part of the deck's filename between the + // last '/' and the last '.' character as base name. + std::string baseName = deck_filename; + auto charPos = baseName.rfind('/'); + if (charPos != std::string::npos) + baseName = baseName.substr(charPos + 1); + charPos = baseName.rfind('.'); + if (charPos != std::string::npos) + baseName = baseName.substr(0, charPos); + baseName = boost::to_upper_copy(baseName); + + // Rock and fluid init + props.reset(new IncompPropertiesFromDeck(*deck, *grid->c_grid())); + new_props.reset(new IncompPropsAdFromDeck(*deck, *grid->c_grid())); + PolymerProperties polymer_props(*deck); + PolymerPropsAd polymer_props_ad(polymer_props); +// polymer_props.reset(new PolymerPropsAd(*deck, *grid->c_grid())); + // check_well_controls = param.getDefault("check_well_controls", false); + // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); + // Rock compressibility. + // Gravity. + gravity[2] = deck->hasField("NOGRAV") ? 0.0 : unit::gravity; + // Init state variables (saturation and pressure). + + if (param.has("init_saturation")) { + initStateBasic(*grid->c_grid(), *props, param, gravity[2], state); + } else { + initStateFromDeck(*grid->c_grid(), *props, *deck, gravity[2], state); + } + + bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); + const double *grav = use_gravity ? &gravity[0] : 0; + + // Linear solver. + LinearSolverFactory linsolver(param); + + // Write parameters used for later reference. + bool output = param.getDefault("output", true); + std::ofstream epoch_os; + std::string output_dir; + if (output) { + output_dir = + param.getDefault("output_dir", std::string("output")); + boost::filesystem::path fpath(output_dir); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + std::string filename = output_dir + "/epoch_timing.param"; + epoch_os.open(filename.c_str(), std::fstream::trunc | std::fstream::out); + // open file to clean it. The file is appended to in SimulatorTwophase + filename = output_dir + "/step_timing.param"; + std::fstream step_os(filename.c_str(), std::fstream::trunc | std::fstream::out); + step_os.close(); + param.writeParam(output_dir + "/simulation.param"); + } + + + std::cout << "\n\n================ Starting main simulation loop ===============\n" + << " (number of epochs: " + << (deck->numberOfEpochs()) << ")\n\n" << std::flush; + + SimulatorReport rep; + // With a deck, we may have more epochs etc. +// WellState well_state; + int step = 0; + SimulatorTimer simtimer; + // Use timer for last epoch to obtain total time. + deck->setCurrentEpoch(deck->numberOfEpochs() - 1); + simtimer.init(*deck); + const double total_time = simtimer.totalTime(); + for (int epoch = 0; epoch < deck->numberOfEpochs(); ++epoch) { + // Set epoch index. + deck->setCurrentEpoch(epoch); + + // Update the timer. + if (deck->hasField("TSTEP")) { + simtimer.init(*deck); + } else { + if (epoch != 0) { + OPM_THROW(std::runtime_error, "No TSTEP in deck for epoch " << epoch); + } + simtimer.init(param); + } + simtimer.setCurrentStepNum(step); + simtimer.setTotalTime(total_time); + + // Report on start of epoch. + std::cout << "\n\n-------------- Starting epoch " << epoch << " --------------" + << "\n (number of steps: " + << simtimer.numSteps() - step << ")\n\n" << std::flush; + + // Create new wells, well_state +// WellsManager wells(*deck, *grid->c_grid(), props->permeability()); + // @@@ HACK: we should really make a new well state and + // properly transfer old well state to it every epoch, + // since number of wells may change etc. + // if (epoch == 0) { + // well_state.init(wells.c_wells(), state); + // } + + // Create and run simulator. + std::vector src(grid->c_grid()->number_of_cells, 0.0); + src[0] = 10. / Opm::unit::day; + src[grid->c_grid()->number_of_cells-1] = -10. / Opm::unit::day; + PolymerInflowBasic polymer_inflow(param.getDefault("poly_start_days", 300.0)*Opm::unit::day, + param.getDefault("poly_end_days", 800.0)*Opm::unit::day, + param.getDefault("poly_amount", polymer_props.cMax())); + SimulatorFullyImplicitTwophasePolymer simulator(param, + *grid->c_grid(), + *new_props, + polymer_props_ad, + linsolver, + polymer_inflow, + src); + if (epoch == 0) { + warnIfUnusedParams(param); + } + SimulatorReport epoch_rep = simulator.run(simtimer, state); + if (output) { + epoch_rep.reportParam(epoch_os); + } + // Update total timing report and remember step number. + rep += epoch_rep; + step = simtimer.currentStepNum(); + } + + std::cout << "\n\n================ End of simulation ===============\n\n"; + rep.report(std::cout); + + if (output) { + std::string filename = output_dir + "/walltime.param"; + std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out); + rep.reportParam(tot_os); + } + +} +catch (const std::exception &e) { + std::cerr << "Program threw an exception: " << e.what() << "\n"; + throw; +} + diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp index b5211a366..d258b4bc3 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp @@ -313,7 +313,7 @@ namespace { residual_.mass_balance[phase] = residual_.mass_balance[phase] - source; } } - +#if 0 // -------- Well equation, and well contributions to the mass balance equations -------- // Contribution to mass balance will have to wait. @@ -407,6 +407,7 @@ namespace { const ADB bhp_residual = bhp - bhp_targets; // Choose bhp residual for positive bhp targets. residual_.well_eq = bhp_residual; +#endif } @@ -587,7 +588,7 @@ namespace { const double* grav = gravity(); const ADB rho = fluidDensity(phase, state.pressure); const ADB rhoavg = ops_.caver * rho; - const ADB dp = ops_.ngrad * state.pressure - grav[2] * (rhoavg * (ops_.ngrad * z.matrix())); + const ADB dp = ops_.ngrad * state.pressure;// - grav[2] * (rhoavg * (ops_.ngrad * z.matrix())); const ADB head = trans * dp; UpwindSelector upwind(grid_, ops_, head.value()); diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.hpp index 491063770..16ad6b37c 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.hpp @@ -22,14 +22,14 @@ namespace Opm { public: FullyImplicitTwoPhaseSolver(const UnstructuredGrid& grid, const IncompPropsAdInterface& fluid, - const Wells& wells, - const LinearSolverInterface& linsolver, - const double* gravity); + // const Wells& wells, + const LinearSolverInterface& linsolver); + // const double* gravity); void step(const double dt, TwophaseState& state, - const std::vector& src, - WellState& wstate); + const std::vector& src); +// WellState& wstate); private: typedef AutoDiffBlock ADB; typedef ADB::V V; @@ -50,20 +50,21 @@ namespace Opm { std::vector src; } source; */ + /* struct WellOps { WellOps(const Wells& wells); M w2p; // well->perf M p2w; // perf->well }; - +*/ const UnstructuredGrid& grid_; const IncompPropsAdInterface& fluid_; - const Wells& wells_; + // const Wells& wells_; const LinearSolverInterface& linsolver_; - const double* grav_; + // const double* grav_; const std::vector cells_; HelperOps ops_; - const WellOps wops_; + // const WellOps wops_; std::vector mob_; From 7a874427af72c35a65ad2dcf86d167cb1947db20 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 17 Dec 2013 23:53:58 +0800 Subject: [PATCH 17/83] remove fullyincomp twophase solver from cmake file list, because they are handled in opm/autodiff. --- .../SimulatorFullyImplicitTwophasePolymer.cpp | 400 ++++++++++++++++++ .../SimulatorFullyImplicitTwophasePolymer.hpp | 88 ++++ 2 files changed, 488 insertions(+) create mode 100644 opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp create mode 100644 opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp new file mode 100644 index 000000000..e9dce9b50 --- /dev/null +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp @@ -0,0 +1,400 @@ +/* + Copyright 2013 SINTEF ICT, Applied Mathematics. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +namespace Opm +{ + + + + class SimulatorFullyImplicitTwophasePolymer::Impl + { + public: + Impl(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const IncompPropsAdInterface& props, + const PolymerPropsAd& polymer_props, + LinearSolverInterface& linsolver, + const PolymerInflowInterface& polymer_inflow, + std::vector& src); + + SimulatorReport run(SimulatorTimer& timer, + PolymerState& state); + + private: + + // Parameters for output. + bool output_; + bool output_vtk_; + std::string output_dir_; + int output_interval_; + // Parameters for well control + // Observed objects. + const UnstructuredGrid& grid_; + const IncompPropsAdInterface& props_; + const PolymerPropsAd& polymer_props_; + const PolymerInflowInterface& polymer_inflow_; + const std::vector& src_; + // Solvers + FullyImplicitTwophasePolymerSolver solver_; + // Misc. data + std::vector allcells_; + }; + + + + + SimulatorFullyImplicitTwophasePolymer::SimulatorFullyImplicitTwophasePolymer(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const IncompPropsAdInterface& props, + const PolymerPropsAd& polymer_props, + LinearSolverInterface& linsolver, + const PolymerInflowInterface& polymer_inflow, + std::vector& src) + { + pimpl_.reset(new Impl(param, grid, props, polymer_props, linsolver, polymer_inflow, src)); + } + + + + + + SimulatorReport SimulatorFullyImplicitTwophasePolymer::run(SimulatorTimer& timer, + PolymerState& state) + { + return pimpl_->run(timer, state); + } + + + + static void outputStateVtk(const UnstructuredGrid& grid, + const Opm::PolymerState& state, + const int step, + const std::string& output_dir) + { + // Write data in VTK format. + std::ostringstream vtkfilename; + vtkfilename << output_dir << "/vtk_files"; + boost::filesystem::path fpath(vtkfilename.str()); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + vtkfilename << "/output-" << std::setw(3) << std::setfill('0') << step << ".vtu"; + std::ofstream vtkfile(vtkfilename.str().c_str()); + if (!vtkfile) { + OPM_THROW(std::runtime_error, "Failed to open " << vtkfilename.str()); + } + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + std::vector cell_velocity; + Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); + dm["velocity"] = &cell_velocity; + Opm::writeVtkData(grid, dm, vtkfile); + } + + + static void outputStateMatlab(const UnstructuredGrid& grid, + const Opm::PolymerState& state, + const int step, + const std::string& output_dir) + { + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + std::vector cell_velocity; + Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); + dm["velocity"] = &cell_velocity; + + // Write data (not grid) in Matlab format + for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { + std::ostringstream fname; + fname << output_dir << "/" << it->first; + boost::filesystem::path fpath = fname.str(); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; + std::ofstream file(fname.str().c_str()); + if (!file) { + OPM_THROW(std::runtime_error, "Failed to open " << fname.str()); + } + file.precision(15); + const std::vector& d = *(it->second); + std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); + } + } + + + +/* + static void outputWaterCut(const Opm::Watercut& watercut, + const std::string& output_dir) + { + // Write water cut curve. + std::string fname = output_dir + "/watercut.txt"; + std::ofstream os(fname.c_str()); + if (!os) { + OPM_THROW(std::runtime_error, "Failed to open " << fname); + } + watercut.write(os); + } + static void outputWellReport(const Opm::WellReport& wellreport, + const std::string& output_dir) + { + // Write well report. + std::string fname = output_dir + "/wellreport.txt"; + std::ofstream os(fname.c_str()); + if (!os) { + OPM_THROW(std::runtime_error, "Failed to open " << fname); + } + wellreport.write(os); + } + */ + + + + SimulatorFullyImplicitTwophasePolymer::Impl::Impl(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const IncompPropsAdInterface& props, + const PolymerPropsAd& polymer_props, + LinearSolverInterface& linsolver, + const PolymerInflowInterface& polymer_inflow, + std::vector& src) + : grid_(grid), + props_(props), + polymer_props_(polymer_props), + polymer_inflow_(polymer_inflow), + src_ (src), + solver_(grid_, props_, polymer_props_, linsolver) + + { + // For output. + output_ = param.getDefault("output", true); + if (output_) { + output_vtk_ = param.getDefault("output_vtk", true); + output_dir_ = param.getDefault("output_dir", std::string("output")); + // Ensure that output dir exists + boost::filesystem::path fpath(output_dir_); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + output_interval_ = param.getDefault("output_interval", 1); + } + // Misc init. + const int num_cells = grid.number_of_cells; + allcells_.resize(num_cells); + for (int cell = 0; cell < num_cells; ++cell) { + allcells_[cell] = cell; + } + } + + SimulatorReport SimulatorFullyImplicitTwophasePolymer::Impl::run(SimulatorTimer& timer, + PolymerState& state) + { + + // Initialisation. + std::vector porevol; + Opm::computePorevolume(grid_, props_.porosity(), porevol); + + // const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); + std::vector polymer_inflow_c(grid_.number_of_cells); + + // Main simulation loop. + Opm::time::StopWatch solver_timer; + double stime = 0.0; + Opm::time::StopWatch step_timer; + Opm::time::StopWatch total_timer; + total_timer.start(); +#if 0 + // These must be changed for three-phase. + double init_surfvol[2] = { 0.0 }; + double inplace_surfvol[2] = { 0.0 }; + double tot_injected[2] = { 0.0 }; + double tot_produced[2] = { 0.0 }; + Opm::computeSaturatedVol(porevol, state.surfacevol(), init_surfvol); + Opm::Watercut watercut; + watercut.push(0.0, 0.0, 0.0); +#endif + std::vector fractional_flows; + std::vector well_resflows_phase; + std::fstream tstep_os; + if (output_) { + std::string filename = output_dir_ + "/step_timing.param"; + tstep_os.open(filename.c_str(), std::fstream::out | std::fstream::app); + } + while (!timer.done()) { + // Report timestep and (optionally) write state to disk. + step_timer.start(); + timer.report(std::cout); + if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); + + } + + SimulatorReport sreport; + + // Run solver. + // Find inflow rate. + const double current_time = timer.currentTime(); + double stepsize = timer.currentStepLength(); + polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c); + solver_timer.start(); + solver_.step(timer.currentStepLength(), state, src_, polymer_inflow_c); + + // Stop timer and report. + solver_timer.stop(); + const double st = solver_timer.secsSinceStart(); + std::cout << "Fully implicit solver took: " << st << " seconds." << std::endl; + + stime += st; + sreport.pressure_time = st; + + + // Update pore volumes if rock is compressible. + + // The reports below are geared towards two phases only. +#if 0 + // Report mass balances. + double injected[2] = { 0.0 }; + double produced[2] = { 0.0 }; + Opm::computeInjectedProduced(props_, state, transport_src, stepsize, + injected, produced); + Opm::computeSaturatedVol(porevol, state.surfacevol(), inplace_surfvol); + tot_injected[0] += injected[0]; + tot_injected[1] += injected[1]; + tot_produced[0] += produced[0]; + tot_produced[1] += produced[1]; + std::cout.precision(5); + const int width = 18; + std::cout << "\nMass balance report.\n"; + std::cout << " Injected surface volumes: " + << std::setw(width) << injected[0] + << std::setw(width) << injected[1] << std::endl; + std::cout << " Produced surface volumes: " + << std::setw(width) << produced[0] + << std::setw(width) << produced[1] << std::endl; + std::cout << " Total inj surface volumes: " + << std::setw(width) << tot_injected[0] + << std::setw(width) << tot_injected[1] << std::endl; + std::cout << " Total prod surface volumes: " + << std::setw(width) << tot_produced[0] + << std::setw(width) << tot_produced[1] << std::endl; + const double balance[2] = { init_surfvol[0] - inplace_surfvol[0] - tot_produced[0] + tot_injected[0], + init_surfvol[1] - inplace_surfvol[1] - tot_produced[1] + tot_injected[1] }; + std::cout << " Initial - inplace + inj - prod: " + << std::setw(width) << balance[0] + << std::setw(width) << balance[1] + << std::endl; + std::cout << " Relative mass error: " + << std::setw(width) << balance[0]/(init_surfvol[0] + tot_injected[0]) + << std::setw(width) << balance[1]/(init_surfvol[1] + tot_injected[1]) + << std::endl; + std::cout.precision(8); + + // Make well reports. + watercut.push(timer.currentTime() + timer.currentStepLength(), + produced[0]/(produced[0] + produced[1]), + tot_produced[0]/tot_porevol_init); + if (wells_) { + wellreport.push(props_, *wells_, + state.pressure(), state.surfacevol(), state.saturation(), + timer.currentTime() + timer.currentStepLength(), + well_state.bhp(), well_state.perfRates()); + } +#endif + sreport.total_time = step_timer.secsSinceStart(); + if (output_) { + sreport.reportParam(tstep_os); + + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); +#if 0 + outputWaterCut(watercut, output_dir_); + if (wells_) { + outputWellReport(wellreport, output_dir_); + } +#endif + tstep_os.close(); + } + + // advance to next timestep before reporting at this location + ++timer; + + // write an output file for later inspection + } + + total_timer.stop(); + + SimulatorReport report; + report.pressure_time = stime; + report.transport_time = 0.0; + report.total_time = total_timer.secsSinceStart(); + return report; + } + + + + + +} // namespace Opm diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp new file mode 100644 index 000000000..f4ec87ca5 --- /dev/null +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp @@ -0,0 +1,88 @@ +/* + Copyright 2013 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_SIMULATORFULLYIMPLICITTWOPHASEPOLYMER_HEADER_INCLUDED +#define OPM_SIMULATORFULLYIMPLICITTWOPHASEPOLYMER_HEADER_INCLUDED + +#include +#include + +struct UnstructuredGrid; + +namespace Opm +{ + namespace parameter { class ParameterGroup; } + class IncompPropsAdInterface; + class LinearSolverInterface; + class SimulatorTimer; + class PolymerState; + class PolymerPropsAd; + class PolymerInflowInterface; + struct SimulatorReport; + + /// Class collecting all necessary components for a two-phase simulation. + class SimulatorFullyImplicitTwophasePolymer + { + public: + /// Initialise from parameters and objects to observe. + /// \param[in] param parameters, this class accepts the following: + /// parameter (default) effect + /// ----------------------------------------------------------- + /// output (true) write output to files? + /// output_dir ("output") output directoty + /// output_interval (1) output every nth step + /// nl_pressure_residual_tolerance (0.0) pressure solver residual tolerance (in Pascal) + /// nl_pressure_change_tolerance (1.0) pressure solver change tolerance (in Pascal) + /// nl_pressure_maxiter (10) max nonlinear iterations in pressure + /// nl_maxiter (30) max nonlinear iterations in transport + /// nl_tolerance (1e-9) transport solver absolute residual tolerance + /// num_transport_substeps (1) number of transport steps per pressure step + /// use_segregation_split (false) solve for gravity segregation (if false, + /// segregation is ignored). + /// + /// \param[in] grid grid data structure + /// \param[in] props fluid and rock properties + /// \param[in] linsolver linear solver + SimulatorFullyImplicitTwophasePolymer(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const IncompPropsAdInterface& props, + const PolymerPropsAd& polymer_props, + LinearSolverInterface& linsolver, + const PolymerInflowInterface& polymer_inflow, + std::vector& src); + + /// Run the simulation. + /// This will run succesive timesteps until timer.done() is true. It will + /// modify the reservoir and well states. + /// \param[in,out] timer governs the requested reporting timesteps + /// \param[in,out] state state of reservoir: pressure, fluxes + /// \param[in,out] well_state state of wells: bhp, perforation rates + /// \return simulation report, with timing data + SimulatorReport run(SimulatorTimer& timer, + PolymerState& state); + + private: + class Impl; + // Using shared_ptr instead of scoped_ptr since scoped_ptr requires complete type for Impl. + boost::shared_ptr pimpl_; + }; + +} // namespace Opm + +#endif // OPM_SIMULATORFULLYIMPLICITTWOPHASEPOLYMER_HEADER_INCLUDED From c37539b3abcf7fdf2c7e3d02adc272b1b25ec570 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 24 Dec 2013 17:31:11 +0800 Subject: [PATCH 18/83] add well controls for polymer, but rate control just for water phase and oil phase. --- examples/sim_fi2p_incomp_ad.cpp | 242 ++++++++++ examples/sim_poly_fi2p_incomp_ad.cpp | 100 ++-- .../FullyImplicitTwophasePolymerSolver.cpp | 431 +++++++++++++++--- .../FullyImplicitTwophasePolymerSolver.hpp | 47 +- .../fullyimplicit/PolymerWellState.hpp | 104 +++++ .../SimulatorFullyImplicitTwophasePolymer.cpp | 114 ++++- .../SimulatorFullyImplicitTwophasePolymer.hpp | 12 +- 7 files changed, 900 insertions(+), 150 deletions(-) create mode 100644 examples/sim_fi2p_incomp_ad.cpp create mode 100644 opm/polymer/fullyimplicit/PolymerWellState.hpp diff --git a/examples/sim_fi2p_incomp_ad.cpp b/examples/sim_fi2p_incomp_ad.cpp new file mode 100644 index 000000000..4cee73252 --- /dev/null +++ b/examples/sim_fi2p_incomp_ad.cpp @@ -0,0 +1,242 @@ +/* + Copyright 2013 SINTEF ICT, Applied Mathematics. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + + +namespace +{ + void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param) + { + if (param.anyUnused()) { + std::cout << "-------------------- Unused parameters: --------------------\n"; + param.displayUsage(); + std::cout << "----------------------------------------------------------------" << std::endl; + } + } +} // anon namespace + + + +// ----------------- Main program ----------------- +int +main(int argc, char** argv) +try +{ + using namespace Opm; + + std::cout << "\n================ Test program for fully implicit three-phase black-oil flow ===============\n\n"; + parameter::ParameterGroup param(argc, argv, false); + std::cout << "--------------- Reading parameters ---------------" << std::endl; + + // If we have a "deck_filename", grid and props will be read from that. + bool use_deck = param.has("deck_filename"); + if (!use_deck) { + OPM_THROW(std::runtime_error, "This program must be run with an input deck. " + "Specify the deck with deck_filename=deckname.data (for example)."); + } + boost::scoped_ptr deck; + boost::scoped_ptr grid; + boost::scoped_ptr props; + boost::scoped_ptr new_props; + TwophaseState state; + // bool check_well_controls = false; + // int max_well_control_iterations = 0; + double gravity[3] = { 0.0 }; + std::string deck_filename = param.get("deck_filename"); + deck.reset(new EclipseGridParser(deck_filename)); + // Grid init + grid.reset(new GridManager(*deck)); + + // use the capitalized part of the deck's filename between the + // last '/' and the last '.' character as base name. + std::string baseName = deck_filename; + auto charPos = baseName.rfind('/'); + if (charPos != std::string::npos) + baseName = baseName.substr(charPos + 1); + charPos = baseName.rfind('.'); + if (charPos != std::string::npos) + baseName = baseName.substr(0, charPos); + baseName = boost::to_upper_copy(baseName); + + // Rock and fluid init + props.reset(new IncompPropertiesFromDeck(*deck, *grid->c_grid())); + new_props.reset(new IncompPropsAdFromDeck(*deck, *grid->c_grid())); + // check_well_controls = param.getDefault("check_well_controls", false); + // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); + // Rock compressibility. + // Gravity. + gravity[2] = deck->hasField("NOGRAV") ? 0.0 : unit::gravity; + // Init state variables (saturation and pressure). + + if (param.has("init_saturation")) { + initStateBasic(*grid->c_grid(), *props, param, gravity[2], state); + } else { + initStateFromDeck(*grid->c_grid(), *props, *deck, gravity[2], state); + } + + bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); + const double *grav = use_gravity ? &gravity[0] : 0; + + // Linear solver. + LinearSolverFactory linsolver(param); + + // Write parameters used for later reference. + bool output = param.getDefault("output", true); + std::ofstream epoch_os; + std::string output_dir; + if (output) { + output_dir = + param.getDefault("output_dir", std::string("output")); + boost::filesystem::path fpath(output_dir); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + std::string filename = output_dir + "/epoch_timing.param"; + epoch_os.open(filename.c_str(), std::fstream::trunc | std::fstream::out); + // open file to clean it. The file is appended to in SimulatorTwophase + filename = output_dir + "/step_timing.param"; + std::fstream step_os(filename.c_str(), std::fstream::trunc | std::fstream::out); + step_os.close(); + param.writeParam(output_dir + "/simulation.param"); + } + + + std::cout << "\n\n================ Starting main simulation loop ===============\n" + << " (number of epochs: " + << (deck->numberOfEpochs()) << ")\n\n" << std::flush; + + SimulatorReport rep; + // With a deck, we may have more epochs etc. + WellState well_state; + int step = 0; + SimulatorTimer simtimer; + // Use timer for last epoch to obtain total time. + deck->setCurrentEpoch(deck->numberOfEpochs() - 1); + simtimer.init(*deck); + const double total_time = simtimer.totalTime(); + for (int epoch = 0; epoch < deck->numberOfEpochs(); ++epoch) { + // Set epoch index. + deck->setCurrentEpoch(epoch); + + // Update the timer. + if (deck->hasField("TSTEP")) { + simtimer.init(*deck); + } else { + if (epoch != 0) { + OPM_THROW(std::runtime_error, "No TSTEP in deck for epoch " << epoch); + } + simtimer.init(param); + } + simtimer.setCurrentStepNum(step); + simtimer.setTotalTime(total_time); + + // Report on start of epoch. + std::cout << "\n\n-------------- Starting epoch " << epoch << " --------------" + << "\n (number of steps: " + << simtimer.numSteps() - step << ")\n\n" << std::flush; + + // Create new wells, well_state + WellsManager wells(*deck, *grid->c_grid(), props->permeability()); + // @@@ HACK: we should really make a new well state and + // properly transfer old well state to it every epoch, + // since number of wells may change etc. + if (epoch == 0) { + well_state.init(wells.c_wells(), state); + } + + // Create and run simulator. + std::vector src(grid->c_grid()->number_of_cells, 0.0); + src[0] = 10. / Opm::unit::day; + src[grid->c_grid()->number_of_cells-1] = -10. / Opm::unit::day; + SimulatorFullyImplicitTwophase simulator(param, + *grid->c_grid(), + *new_props, + wells, + linsolver, + src, + grav); + if (epoch == 0) { + warnIfUnusedParams(param); + } + SimulatorReport epoch_rep = simulator.run(simtimer, state, src, well_state); + if (output) { + epoch_rep.reportParam(epoch_os); + } + // Update total timing report and remember step number. + rep += epoch_rep; + step = simtimer.currentStepNum(); + } + + std::cout << "\n\n================ End of simulation ===============\n\n"; + rep.report(std::cout); + + if (output) { + std::string filename = output_dir + "/walltime.param"; + std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out); + rep.reportParam(tot_os); + } + +} +catch (const std::exception &e) { + std::cerr << "Program threw an exception: " << e.what() << "\n"; + throw; +} + diff --git a/examples/sim_poly_fi2p_incomp_ad.cpp b/examples/sim_poly_fi2p_incomp_ad.cpp index 8a7ed54ef..5fecc687d 100644 --- a/examples/sim_poly_fi2p_incomp_ad.cpp +++ b/examples/sim_poly_fi2p_incomp_ad.cpp @@ -98,8 +98,8 @@ try boost::scoped_ptr new_props; // boost::scoped_ptr polymer_props; PolymerState state; - // bool check_well_controls = false; - // int max_well_control_iterations = 0; +// bool check_well_controls = false; +// int max_well_control_iterations = 0; double gravity[3] = { 0.0 }; std::string deck_filename = param.get("deck_filename"); deck.reset(new EclipseGridParser(deck_filename)); @@ -123,9 +123,8 @@ try PolymerProperties polymer_props(*deck); PolymerPropsAd polymer_props_ad(polymer_props); // polymer_props.reset(new PolymerPropsAd(*deck, *grid->c_grid())); - // check_well_controls = param.getDefault("check_well_controls", false); - // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); - // Rock compressibility. +// check_well_controls = param.getDefault("check_well_controls", false); +// max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); // Gravity. gravity[2] = deck->hasField("NOGRAV") ? 0.0 : unit::gravity; // Init state variables (saturation and pressure). @@ -137,7 +136,7 @@ try } bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); - const double *grav = use_gravity ? &gravity[0] : 0; + const double* grav = use_gravity ? &gravity[0] : 0; // Linear solver. LinearSolverFactory linsolver(param); @@ -171,62 +170,85 @@ try << (deck->numberOfEpochs()) << ")\n\n" << std::flush; SimulatorReport rep; - // With a deck, we may have more epochs etc. -// WellState well_state; - int step = 0; - SimulatorTimer simtimer; - // Use timer for last epoch to obtain total time. - deck->setCurrentEpoch(deck->numberOfEpochs() - 1); - simtimer.init(*deck); - const double total_time = simtimer.totalTime(); - for (int epoch = 0; epoch < deck->numberOfEpochs(); ++epoch) { - // Set epoch index. - deck->setCurrentEpoch(epoch); - - // Update the timer. - if (deck->hasField("TSTEP")) { - simtimer.init(*deck); - } else { - if (epoch != 0) { - OPM_THROW(std::runtime_error, "No TSTEP in deck for epoch " << epoch); + // With a deck, we may have more epochs etc. + WellState well_state; + int step = 0; + SimulatorTimer simtimer; + // Use timer for last epoch to obtain total time. + deck->setCurrentEpoch(deck->numberOfEpochs() - 1); + simtimer.init(*deck); + const double total_time = simtimer.totalTime(); + // Check for WPOLYMER presence in last epoch to decide + // polymer injection control type. + const bool use_wpolymer = deck->hasField("WPOLYMER"); + if (use_wpolymer) { + if (param.has("poly_start_days")) { + OPM_MESSAGE("Warning: Using WPOLYMER to control injection since it was found in deck. " + "You seem to be trying to control it via parameter poly_start_days (etc.) as well."); } - simtimer.init(param); } - simtimer.setCurrentStepNum(step); - simtimer.setTotalTime(total_time); + for (int epoch = 0; epoch < deck->numberOfEpochs(); ++epoch) { + // Set epoch index. + deck->setCurrentEpoch(epoch); - // Report on start of epoch. - std::cout << "\n\n-------------- Starting epoch " << epoch << " --------------" - << "\n (number of steps: " - << simtimer.numSteps() - step << ")\n\n" << std::flush; + // Update the timer. + if (deck->hasField("TSTEP")) { + simtimer.init(*deck); + } else { + if (epoch != 0) { + OPM_THROW(std::runtime_error, "No TSTEP in deck for epoch " << epoch); + } + simtimer.init(param); + } + simtimer.setCurrentStepNum(step); + simtimer.setTotalTime(total_time); - // Create new wells, well_state -// WellsManager wells(*deck, *grid->c_grid(), props->permeability()); + // Report on start of epoch. + std::cout << "\n\n-------------- Starting epoch " << epoch << " --------------" + << "\n (number of steps: " + << simtimer.numSteps() - step << ")\n\n" << std::flush; + + // Create new wells, polymer inflow controls. + WellsManager wells(*deck, *grid->c_grid(), props->permeability()); + boost::scoped_ptr polymer_inflow; + if (use_wpolymer) { + if (wells.c_wells() == 0) { + OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells."); + } + polymer_inflow.reset(new PolymerInflowFromDeck(*deck, *wells.c_wells(), props->numCells())); + } else { + polymer_inflow.reset(new PolymerInflowBasic(param.getDefault("poly_start_days", 300.0)*Opm::unit::day, + param.getDefault("poly_end_days", 800.0)*Opm::unit::day, + param.getDefault("poly_amount", polymer_props.cMax()))); + } // @@@ HACK: we should really make a new well state and // properly transfer old well state to it every epoch, // since number of wells may change etc. - // if (epoch == 0) { - // well_state.init(wells.c_wells(), state); - // } + if (epoch == 0) { + well_state.init(wells.c_wells(), state); + } // Create and run simulator. + #if 0 std::vector src(grid->c_grid()->number_of_cells, 0.0); src[0] = 10. / Opm::unit::day; src[grid->c_grid()->number_of_cells-1] = -10. / Opm::unit::day; PolymerInflowBasic polymer_inflow(param.getDefault("poly_start_days", 300.0)*Opm::unit::day, param.getDefault("poly_end_days", 800.0)*Opm::unit::day, param.getDefault("poly_amount", polymer_props.cMax())); + #endif SimulatorFullyImplicitTwophasePolymer simulator(param, *grid->c_grid(), *new_props, polymer_props_ad, linsolver, - polymer_inflow, - src); + wells, + *polymer_inflow, + grav); if (epoch == 0) { warnIfUnusedParams(param); } - SimulatorReport epoch_rep = simulator.run(simtimer, state); + SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state); if (output) { epoch_rep.reportParam(epoch_os); } diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index 90468d347..87335048e 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -25,23 +25,6 @@ #include namespace Opm { -namespace { - - std::vector - buildAllCells(const int nc) - { - std::vector all_cells(nc); - for (int c = 0; c < nc; ++c) { all_cells[c] = c; } - - return all_cells; - } - struct Chop01 { - double operator()(double x) const { return std::max(std::min(x, 1.0), 0.0); } - }; - -}//anonymous namespace - - @@ -56,19 +39,74 @@ typedef Eigen::Array + buildAllCells(const int nc) + { + std::vector all_cells(nc); + for (int c = 0; c < nc; ++c) { all_cells[c] = c; } + + return all_cells; + } + struct Chop01 { + double operator()(double x) const { return std::max(std::min(x, 1.0), 0.0); } + }; + + + + + + V computePerfPress(const UnstructuredGrid& grid, const Wells& wells, const V& rho, const double grav) + { + const int nw = wells.number_of_wells; + const int nperf = wells.well_connpos[nw]; + const int dim = grid.dimensions; + V wdp = V::Zero(nperf,1); + assert(wdp.size() == rho.size()); + + // Main loop, iterate over all perforations, + // using the following formula: + // wdp(perf) = g*(perf_z - well_ref_z)*rho(perf) + // where the total density rho(perf) is taken to be + // sum_p (rho_p*saturation_p) in the perforation cell. + // [although this is computed on the outside of this function]. + for (int w = 0; w < nw; ++w) { + const double ref_depth = wells.depth_ref[w]; + for (int j = wells.well_connpos[w]; j < wells.well_connpos[w + 1]; ++j) { + const int cell = wells.well_cells[j]; + const double cell_depth = grid.cell_centroids[dim * cell + dim - 1]; + wdp[j] = rho[j]*grav*(cell_depth - ref_depth); + } + } + return wdp; + } + +}//anonymous namespace + + + + + FullyImplicitTwophasePolymerSolver:: FullyImplicitTwophasePolymerSolver(const UnstructuredGrid& grid, - const IncompPropsAdInterface& fluid, - const PolymerPropsAd& polymer_props_ad, - const LinearSolverInterface& linsolver) + const IncompPropsAdInterface& fluid, + const PolymerPropsAd& polymer_props_ad, + const LinearSolverInterface& linsolver, + const Wells& wells, + const double* gravity) : grid_ (grid) , fluid_(fluid) , polymer_props_ad_ (polymer_props_ad) , linsolver_(linsolver) + , wells_(wells) + , gravity_(gravity) , cells_ (buildAllCells(grid.number_of_cells)) , ops_(grid) - , residual_(std::vector(3, ADB::null())) + , wops_(wells) + , mob_(std::vector(fluid.numPhases() + 1, ADB::null())) + , residual_( { std::vector(fluid.numPhases() + 1, ADB::null()), ADB::null(), ADB::null()}) { } @@ -76,11 +114,42 @@ typedef Eigen::Array Tri; + + std::vector scatter, gather; + scatter.reserve(wpos[nw]); + gather .reserve(wpos[nw]); + + for (int w = 0, i = 0; w < nw; ++w) { + for (; i < wpos[ w + 1 ]; ++i) { + scatter.push_back(Tri(i, w, 1.0)); + gather .push_back(Tri(w, i, 1.0)); + } + } + + w2p.setFromTriplets(scatter.begin(), scatter.end()); + p2w.setFromTriplets(gather .begin(), gather .end()); + } + + + + + void FullyImplicitTwophasePolymerSolver:: step(const double dt, - PolymerState& x, - const std::vector& src, + PolymerState& x, + WellState& xw, const std::vector& polymer_inflow) { @@ -94,11 +163,11 @@ typedef Eigen::Array atol; while (resTooLarge && (it < maxit)) { const V dx = solveJacobianSystem(); - updateState(dx, x); + updateState(dx, x, xw); - assemble(pvdt, old_state, x, src, polymer_inflow); + assemble(pvdt, old_state, x, xw, polymer_inflow); const double r = residualNorm(); @@ -135,6 +204,8 @@ typedef Eigen::Array bpat(np + 1, nc); + bpat.push_back(xw.bhp().size() * np); + bpat.push_back(xw.bhp().size()); + SolutionState state(np); @@ -167,6 +251,20 @@ typedef Eigen::Array(&x.concentration()[0], nc); state.concentration = ADB::constant(c); + // Well rates. + assert (not xw.wellRates().empty()); + // Need to reshuffle well rates, from ordered by wells, then phase, + // to ordered by phase, then wells. + const int nw = wells_.number_of_wells; + // The transpose() below switches the ordering. + const DataBlock wrates = Eigen::Map(& xw.wellRates()[0], nw, np).transpose(); + const V qs = Eigen::Map(wrates.data(), nw * np); + state.qs = ADB::constant(qs, bpat); + + // Bottom hole pressure. + assert (not xw.bhp().empty()); + const V bhp = Eigen::Map(& xw.bhp()[0], xw.bhp().size()); + state.bhp = ADB::constant(bhp, bpat); return state; } @@ -175,13 +273,14 @@ typedef Eigen::Array vars0; - vars0.reserve(np); + vars0.reserve(np + 3); // Initial pressure. assert (not x.pressure().empty()); @@ -199,6 +298,21 @@ typedef Eigen::Array(&x.concentration()[0], nc); vars0.push_back(c); + // Initial well rates. + assert (not xw.wellRates().empty()); + // Need to reshuffle well rates, from ordered by wells, then phase, + // to ordered by phase, then wells. + const int nw = wells_.number_of_wells; + // The transpose() below switches the ordering. + const DataBlock wrates = Eigen::Map(& xw.wellRates()[0], nw, np).transpose(); + const V qs = Eigen::Map(wrates.data(), nw*np); + vars0.push_back(qs); + + // Initial well bottom hole pressure. + assert (not xw.bhp().size()); + const V bhp = Eigen::Map(& xw.bhp()[0], xw.bhp().size()); + vars0.push_back(bhp); + std::vector vars = ADB::variables(vars0); SolutionState state(np); @@ -220,6 +334,10 @@ typedef Eigen::Array c.value()(i)) ? cmax(i) : c.value()(i); } - return ADB::constant(cmax); + return ADB::constant(cmax, c.blockPattern()); } @@ -247,11 +365,11 @@ typedef Eigen::Array& src, + const WellState& xw, const std::vector& polymer_inflow) { // Create the primary variables. - const SolutionState state = variableState(x); + const SolutionState state = variableState(x, xw); // -------- Mass balance equations for water and oil -------- const V trans = subset(transmissibility(), ops_.internal_faces); @@ -260,27 +378,147 @@ typedef Eigen::Array mflux = computeMassFlux(trans, mc, kr[0], krw_eff, state); const std::vector mflux = computeMassFlux(trans, mc, kr[1], krw_eff, state); - const std::vector source = accumSource(kr[1], krw_eff, state.concentration, src, polymer_inflow); + //const std::vector source = accumSource(kr[1], krw_eff, state.concentration, src, polymer_inflow); + // const std::vector source = polymerSource(); const double rho_r = polymer_props_ad_.rockDensity(); const V phi = V::Constant(pvdt.size(), 1, *fluid_.porosity()); const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); - residual_[0] = pvdt * (state.saturation[0] - old_state.saturation[0]) - + ops_.div * mflux[0] - source[0]; - residual_[1] = pvdt * (state.saturation[1] - old_state.saturation[1]) - + ops_.div * mflux[1] - source[1]; + residual_.mass_balance[0] = pvdt * (state.saturation[0] - old_state.saturation[0]) + + ops_.div * mflux[0]; + residual_.mass_balance[1] = pvdt * (state.saturation[1] - old_state.saturation[1]) + + ops_.div * mflux[1]; // Mass balance equation for polymer - residual_[2] = pvdt * (state.saturation[0] * state.concentration + residual_.mass_balance[2] = pvdt * (state.saturation[0] * state.concentration - old_state.saturation[0] * old_state.concentration) * (1. - dead_pore_vol) + pvdt * rho_r * (1. - phi) / phi * ads - + ops_.div * mflux[2] - source[2]; + + ops_.div * mflux[2]; + + // -------- Well equation, and well contributions to the mass balance equations -------- + + // Contribution to mass balance will have to wait. + + const int nc = grid_.number_of_cells; + const int np = wells_.number_of_phases; + const int nw = wells_.number_of_wells; + const int nperf = wells_.well_connpos[nw]; + + const std::vector well_cells(wells_.well_cells, wells_.well_cells + nperf); + const V transw = Eigen::Map(wells_.WI, nperf); + + const ADB& bhp = state.bhp; + + const DataBlock well_s = wops_.w2p * Eigen::Map(wells_.comp_frac, nw, np).matrix(); + + // Extract variables for perforation cell pressures + // and corresponding perforation well pressures. + const ADB p_perfcell = subset(state.pressure, well_cells); + // Finally construct well perforation pressures and well flows. + + // Compute well pressure differentials. + // Construct pressure difference vector for wells. + const int dim = grid_.dimensions; + if (gravity_) { + for (int dd = 0; dd < dim -1; ++dd) { + assert(g[dd] == 0.0); + } + } + ADB cell_rho_total = ADB::constant(V::Zero(nc), state.pressure.blockPattern()); + for (int phase = 0; phase < 2; ++phase) { + // For incompressible flow cell rho is the same. + const ADB cell_rho = fluidDensity(phase, state.pressure); + cell_rho_total += state.saturation[phase] * cell_rho; + } + ADB inj_rho_total = ADB::constant(V::Zero(nperf), state.pressure.blockPattern()); + assert(np == wells_.number_of_phases); + const DataBlock compi = Eigen::Map(wells_.comp_frac, nw, np); + for (int phase = 0; phase < 2; ++phase) { + const ADB cell_rho = fluidDensity(phase, state.pressure); + const V fraction = compi.col(phase); + inj_rho_total += (wops_.w2p * fraction.matrix()).array() * subset(cell_rho, well_cells); + } + const V rho_perf_cell = subset(cell_rho_total, well_cells).value(); + const V rho_perf_well = inj_rho_total.value(); + V prodperfs = V::Constant(nperf, -1.0); + for (int w = 0; w < nw; ++w) { + if (wells_.type[w] == PRODUCER) { + std::fill(prodperfs.data() + wells_.well_connpos[w], + prodperfs.data() + wells_.well_connpos[w+1], 1.0); + } + } + const Selector producer(prodperfs); + const V rho_perf = producer.select(rho_perf_cell, rho_perf_well); + const V well_perf_dp = computePerfPress(grid_, wells_, rho_perf, gravity_ ? gravity_[dim - 1] : 0.0); + + const ADB p_perfwell = wops_.w2p * bhp + well_perf_dp; + const ADB nkgradp_well = transw * (p_perfcell - p_perfwell); + // DUMP(nkgradp_well); + const Selector cell_to_well_selector(nkgradp_well.value()); + ADB well_rates_all = ADB::constant(V::Zero(nw*np), state.bhp.blockPattern()); + + ADB perf_total_mob = subset(mob_[0], well_cells) + subset(mob_[1], well_cells); + + std::vector well_contribs(np, ADB::null()); + std::vector well_perf_rates(np, ADB::null()); + for (int phase = 0; phase < np; ++phase) { +// const ADB& cell_b = rq_[phase].b; + // const ADB perf_b = subset(cell_b, well_cells); + const ADB& cell_mob = mob_[phase]; + const V well_fraction = compi.col(phase); + // Using total mobilities for all phases for injection. + const ADB perf_mob_injector = (wops_.w2p * well_fraction.matrix()).array() * perf_total_mob; + const ADB perf_mob = producer.select(subset(cell_mob, well_cells), + perf_mob_injector); + const ADB perf_flux = perf_mob * (nkgradp_well); // No gravity term for perforations. + well_perf_rates[phase] = perf_flux; + const ADB well_rates = wops_.p2w * well_perf_rates[phase]; + well_rates_all += superset(well_rates, Span(nw, 1, phase*nw), nw*np); + + // const ADB well_contrib = superset(perf_flux*perf_b, well_cells, nc); + well_contribs[phase] = superset(perf_flux, well_cells, nc); + // DUMP(well_contribs[phase]); + residual_.mass_balance[phase] += well_contribs[phase]; + } + + // well rates contribs to polymer mass balance eqn. + // for injection wells. + const V polyin = Eigen::Map(& polymer_inflow[0], nc); + const V poly_in_perf = subset(polyin, well_cells); + const V poly_c_cell = subset(state.concentration, well_cells).value(); + const V poly_c = producer.select(poly_c_cell, poly_in_perf); + residual_.mass_balance[2] += superset(well_perf_rates[0] * poly_c, well_cells, nc); + + // Set the well flux equation + residual_.well_flux_eq = state.qs + well_rates_all; + // DUMP(residual_.well_flux_eq); + + // Handling BHP and SURFACE_RATE wells. + V bhp_targets(nw); + V rate_targets(nw); + M rate_distr(nw, np*nw); + for (int w = 0; w < nw; ++w) { + const WellControls* wc = wells_.ctrls[w]; + if (wc->type[wc->current] == BHP) { + bhp_targets[w] = wc->target[wc->current]; + rate_targets[w] = -1e100; + } else if (wc->type[wc->current] == SURFACE_RATE) { + bhp_targets[w] = -1e100; + rate_targets[w] = wc->target[wc->current]; + for (int phase = 0; phase < np; ++phase) { + rate_distr.insert(w, phase*nw + w) = wc->distr[phase]; + } + } else { + OPM_THROW(std::runtime_error, "Can only handle BHP type controls."); + } + } + const ADB bhp_residual = bhp - bhp_targets; + const ADB rate_residual = rate_distr * state.qs - rate_targets; + // Choose bhp residual for positive bhp targets. + Selector bhp_selector(bhp_targets); + residual_.well_eq = bhp_selector.select(bhp_residual, rate_residual); + // residual_.well_eq = bhp_residual; } @@ -289,24 +527,39 @@ typedef Eigen::Array mflux; ADB inv_wat_eff_vis = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mus); - ADB wat_mob = krw_eff * inv_wat_eff_vis; - ADB oil_mob = kro / V::Constant(kro.size(), 1, mus[1]); - ADB poly_mob = mc * krw_eff * inv_wat_eff_vis; + mob_[0] = krw_eff * inv_wat_eff_vis; + mob_[1] = kro / V::Constant(kro.size(), 1, mus[1]); + mob_[2] = mc * krw_eff * inv_wat_eff_vis; - const ADB dp = ops_.ngrad * state.pressure; + const int nc = grid_.number_of_cells; + V z(nc); + // Compute z coordinates + for (int c = 0; c < nc; ++c){ + z[c] = grid_.cell_centroids[c * 3 + 2]; + } + for (int phase = 0; phase < 2; ++phase) { + const ADB rho = fluidDensity(phase, state.pressure); + const ADB rhoavg = ops_.caver * rho; + const ADB dp = ops_.ngrad * state.pressure + - gravity_[2] * (rhoavg * (ops_.ngrad * z.matrix())); + const ADB head = trans * dp; + UpwindSelector upwind(grid_, ops_, head.value()); + mflux.push_back(upwind.select(mob_[phase])*head); + } + // polymer mass flux. + const ADB rho = fluidDensity(0, state.pressure); + const ADB rhoavg = ops_.caver * rho; + const ADB dp = ops_.ngrad * state.pressure + - gravity_[2] * (rhoavg * (ops_.ngrad * z.matrix())); const ADB head = trans * dp; UpwindSelector upwind(grid_, ops_, head.value()); - - mflux.push_back(upwind.select(wat_mob)*head); - mflux.push_back(upwind.select(oil_mob)*head); - mflux.push_back(upwind.select(poly_mob)*head); - + mflux.push_back(upwind.select(mob_[2])*head); return mflux; } @@ -363,16 +616,12 @@ typedef Eigen::Array fracflow; - fracflow.push_back(wat_mob / total_mob); - fracflow.push_back(oil_mob / total_mob); + fracflow.push_back(mob_[0] / total_mob); + fracflow.push_back(mob_[1] / total_mob); return fracflow; } @@ -388,15 +637,17 @@ typedef Eigen::Array matr = mass_res.derivative()[0]; - V dx(V::Zero(mass_res.size())); + const Eigen::SparseMatrix matr = total_res.derivative()[0]; + V dx(V::Zero(total_res.size())); Opm::LinearSolverInterface::LinearSolverReport rep = linsolver_.solve(matr.rows(), matr.nonZeros(), matr.outerIndexPtr(), matr.innerIndexPtr(), matr.valuePtr(), - mass_res.value().data(), dx.data()); + total_res.value().data(), dx.data()); if (!rep.converged) { OPM_THROW(std::runtime_error, "FullyImplicitBlackoilSolver::solveJacobianSystem(): " @@ -410,13 +661,12 @@ typedef Eigen::Array(dqs.data(), np, nw).transpose(); + const V dwr = Eigen::Map(wwr.data(), nw*np); + const V wr_old = Eigen::Map(&well_state.wellRates()[0], nw*np); + const V wr = wr_old - dwr; + std::copy(&wr[0], &wr[0] + wr.size(), well_state.wellRates().begin()); + + + // Bhp update. + const V bhp_old = Eigen::Map(&well_state.bhp()[0], nw, 1); + const V bhp = bhp_old - dbhp; + std::copy(&bhp[0], &bhp[0] + bhp.size(), well_state.bhp().begin()); + } @@ -486,18 +758,31 @@ typedef Eigen::Array::const_iterator - b = residual_.begin(), - e = residual_.end(); + b = residual_.mass_balance.begin(), + e = residual_.mass_balance.end(); b != e; ++b) { r = std::max(r, (*b).value().matrix().norm()); } + r = std::max(r, residual_.well_flux_eq.value().matrix().norm()); + r = std::max(r, residual_.well_eq.value().matrix().norm()); return r; } + + ADB + FullyImplicitTwophasePolymerSolver::fluidDensity(const int phase, + const ADB p) const + { + const double* rhos = fluid_.surfaceDensity(); + ADB rho = ADB::constant(V::Constant(grid_.number_of_cells, 1, rhos[phase]), + p.blockPattern()); + + return rho; + } V diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp index 1dffd7a17..5e646a269 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp @@ -12,10 +12,11 @@ struct UnstructuredGrid; +struct Wells; namespace Opm { class LinearSolverInterface; class PolymerState; - + class WellState; class FullyImplicitTwophasePolymerSolver { @@ -23,13 +24,14 @@ namespace Opm { FullyImplicitTwophasePolymerSolver(const UnstructuredGrid& grid, const IncompPropsAdInterface& fluid, const PolymerPropsAd& polymer_props_ad, - const LinearSolverInterface& linsolver); + const LinearSolverInterface& linsolver, + const Wells& wells, + const double* gravity); void step(const double dt, PolymerState& state, - const std::vector& src, - const std::vector& polymer_inflow - ); + WellState& well_state, + const std::vector& polymer_inflow); private: typedef AutoDiffBlock ADB; typedef ADB::V V; @@ -43,29 +45,47 @@ namespace Opm { ADB pressure; std::vector saturation; ADB concentration; + ADB qs; + ADB bhp; + }; + struct WellOps { + WellOps(const Wells& wells); + M w2p; // well -> perf (scatter) + M p2w; // perf -> well (gather) }; const UnstructuredGrid& grid_; const IncompPropsAdInterface& fluid_; const PolymerPropsAd& polymer_props_ad_; const LinearSolverInterface& linsolver_; + const Wells& wells_; + const double* gravity_; const std::vector cells_; HelperOps ops_; - std::vector residual_; + const WellOps wops_; + std::vector mob_; + struct { + std::vector mass_balance; + ADB well_eq; + ADB well_flux_eq; + } residual_; SolutionState - constantState(const PolymerState& x); + constantState(const PolymerState& x, + const WellState& xw); SolutionState - variableState(const PolymerState& x); + variableState(const PolymerState& x, + const WellState& xw); void assemble(const V& pvdt, const SolutionState& old_state, - const PolymerState& x, - const std::vector& src, + const PolymerState& x, + const WellState& xw, const std::vector& polymer_inflow); V solveJacobianSystem() const; void updateState(const V& dx, - PolymerState& x) const; + PolymerState& x, + WellState& xw) const; std::vector computeRelPerm(const SolutionState& state) const; V @@ -76,7 +96,7 @@ namespace Opm { const ADB& mc, const ADB& kro, const ADB& krw_eff, - const SolutionState& state ) const; + const SolutionState& state ); std::vector accumSource(const ADB& kro, @@ -109,6 +129,9 @@ namespace Opm { const double fluidDensity(const int phase) const; ADB + fluidDensity(const int phase, + const ADB p) const; + ADB transMult(const ADB& p) const; }; diff --git a/opm/polymer/fullyimplicit/PolymerWellState.hpp b/opm/polymer/fullyimplicit/PolymerWellState.hpp new file mode 100644 index 000000000..33fbcd3d2 --- /dev/null +++ b/opm/polymer/fullyimplicit/PolymerWellState.hpp @@ -0,0 +1,104 @@ +/* + Copyright 2012 SINTEF ICT, Applied Mathematics. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#ifndef OPM_WELLSTATE_HEADER_INCLUDED +#define OPM_WELLSTATE_HEADER_INCLUDED + +#include +#include + +namespace Opm +{ + + /// The state of a set of wells. + class WellState + { + public: + /// Allocate and initialize if wells is non-null. + /// Also tries to give useful initial values to the bhp() and + /// wellRates() fields, depending on controls. The + /// perfRates() field is filled with zero, and perfPress() + /// with -1e100. + template + void init(const Wells* wells, const State& state) + { + if (wells) { + const int nw = wells->number_of_wells; + const int np = wells->number_of_phases + 1; + bhp_.resize(nw); + wellrates_.resize(nw * np, 0.0); + for (int w = 0; w < nw; ++w) { + const WellControls* ctrl = wells->ctrls[w]; + // Initialize bhp to be target pressure if + // bhp-controlled well, otherwise set to a little + // above or below (depending on if the well is an + // injector or producer) pressure in first perforation + // cell. + if ((ctrl->current < 0) || // SHUT + (ctrl->type[ctrl->current] != BHP)) { + const int first_cell = wells->well_cells[wells->well_connpos[w]]; + const double safety_factor = (wells->type[w] == INJECTOR) ? 1.01 : 0.99; + bhp_[w] = safety_factor*state.pressure()[first_cell]; + } else { + bhp_[w] = ctrl->target[ctrl->current]; + } + // Initialize well rates to match controls if type is SURFACE_RATE + if ((ctrl->current >= 0) && // open well + (ctrl->type[ctrl->current] == SURFACE_RATE)) { + const double rate_target = ctrl->target[ctrl->current]; + for (int p = 0; p < np; ++p) { + const double phase_distr = ctrl->distr[np * ctrl->current + p]; + wellrates_[np*w + p] = rate_target * phase_distr; + } + } + } + // The perforation rates and perforation pressures are + // not expected to be consistent with bhp_ and wellrates_ + // after init(). + perfrates_.resize(wells->well_connpos[nw], 0.0); + perfpress_.resize(wells->well_connpos[nw], -1e100); + } + } + + /// One bhp pressure per well. + std::vector& bhp() { return bhp_; } + const std::vector& bhp() const { return bhp_; } + + /// One rate per well and phase. + std::vector& wellRates() { return wellrates_; } + const std::vector& wellRates() const { return wellrates_; } + + /// One rate per well connection. + std::vector& perfRates() { return perfrates_; } + const std::vector& perfRates() const { return perfrates_; } + + /// One pressure per well connection. + std::vector& perfPress() { return perfpress_; } + const std::vector& perfPress() const { return perfpress_; } + + private: + std::vector bhp_; + std::vector wellrates_; + std::vector perfrates_; + std::vector perfpress_; + }; + +} // namespace Opm + +#endif // OPM_WELLSTATE_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp index e9dce9b50..18f3fcfae 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,7 @@ #include #include +#include #include #include @@ -61,11 +63,13 @@ namespace Opm const IncompPropsAdInterface& props, const PolymerPropsAd& polymer_props, LinearSolverInterface& linsolver, - const PolymerInflowInterface& polymer_inflow, - std::vector& src); + WellsManager& wells_manager, + PolymerInflowInterface& polymer_inflow, + const double* gravity); SimulatorReport run(SimulatorTimer& timer, - PolymerState& state); + PolymerState& state, + WellState& well_state); private: @@ -75,12 +79,15 @@ namespace Opm std::string output_dir_; int output_interval_; // Parameters for well control + bool check_well_controls_; + int max_well_control_iterations_; // Observed objects. const UnstructuredGrid& grid_; const IncompPropsAdInterface& props_; const PolymerPropsAd& polymer_props_; - const PolymerInflowInterface& polymer_inflow_; - const std::vector& src_; + WellsManager& wells_manager_; + const Wells* wells_; + PolymerInflowInterface& polymer_inflow_; // Solvers FullyImplicitTwophasePolymerSolver solver_; // Misc. data @@ -90,15 +97,17 @@ namespace Opm - SimulatorFullyImplicitTwophasePolymer::SimulatorFullyImplicitTwophasePolymer(const parameter::ParameterGroup& param, - const UnstructuredGrid& grid, - const IncompPropsAdInterface& props, - const PolymerPropsAd& polymer_props, - LinearSolverInterface& linsolver, - const PolymerInflowInterface& polymer_inflow, - std::vector& src) + SimulatorFullyImplicitTwophasePolymer:: + SimulatorFullyImplicitTwophasePolymer(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const IncompPropsAdInterface& props, + const PolymerPropsAd& polymer_props, + LinearSolverInterface& linsolver, + WellsManager& wells_manager, + PolymerInflowInterface& polymer_inflow, + const double* gravity) { - pimpl_.reset(new Impl(param, grid, props, polymer_props, linsolver, polymer_inflow, src)); + pimpl_.reset(new Impl(param, grid, props, polymer_props, linsolver, wells_manager, polymer_inflow, gravity)); } @@ -106,9 +115,10 @@ namespace Opm SimulatorReport SimulatorFullyImplicitTwophasePolymer::run(SimulatorTimer& timer, - PolymerState& state) + PolymerState& state, + WellState& well_state) { - return pimpl_->run(timer, state); + return pimpl_->run(timer, state, well_state); } @@ -204,6 +214,37 @@ namespace Opm } */ + static void outputWellStateMatlab(WellState& well_state, + const int step, + const std::string& output_dir) + { + Opm::DataMap dm; + dm["bhp"] = &well_state.bhp(); + dm["wellrates"] = &well_state.wellRates(); + + // Write data (not grid) in Matlab format + for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { + std::ostringstream fname; + fname << output_dir << "/" << it->first; + boost::filesystem::path fpath = fname.str(); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error,"Creating directories failed: " << fpath); + } + fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; + std::ofstream file(fname.str().c_str()); + if (!file) { + OPM_THROW(std::runtime_error,"Failed to open " << fname.str()); + } + file.precision(15); + const std::vector& d = *(it->second); + std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); + } + } + + SimulatorFullyImplicitTwophasePolymer::Impl::Impl(const parameter::ParameterGroup& param, @@ -211,14 +252,16 @@ namespace Opm const IncompPropsAdInterface& props, const PolymerPropsAd& polymer_props, LinearSolverInterface& linsolver, - const PolymerInflowInterface& polymer_inflow, - std::vector& src) + WellsManager& wells_manager, + PolymerInflowInterface& polymer_inflow, + const double* gravity) : grid_(grid), props_(props), polymer_props_(polymer_props), + wells_manager_(wells_manager), + wells_(wells_manager.c_wells()), polymer_inflow_(polymer_inflow), - src_ (src), - solver_(grid_, props_, polymer_props_, linsolver) + solver_(grid_, props_, polymer_props_, linsolver, *wells_manager.c_wells(), gravity) { // For output. @@ -245,7 +288,8 @@ namespace Opm } SimulatorReport SimulatorFullyImplicitTwophasePolymer::Impl::run(SimulatorTimer& timer, - PolymerState& state) + PolymerState& state, + WellState& well_state) { // Initialisation. @@ -287,18 +331,22 @@ namespace Opm outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); } outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); + outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); } SimulatorReport sreport; + bool well_control_passed = !check_well_controls_; + int well_control_iteration = 0; + do { // Run solver. - // Find inflow rate. const double current_time = timer.currentTime(); double stepsize = timer.currentStepLength(); polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c); solver_timer.start(); - solver_.step(timer.currentStepLength(), state, src_, polymer_inflow_c); + std::vector initial_pressure = state.pressure(); + solver_.step(timer.currentStepLength(), state, well_state, polymer_inflow_c); // Stop timer and report. solver_timer.stop(); @@ -308,6 +356,27 @@ namespace Opm stime += st; sreport.pressure_time = st; + // Optionally, check if well controls are satisfied. + if (check_well_controls_) { + Opm::computePhaseFlowRatesPerWell(*wells_, + well_state.perfRates(), + fractional_flows, + well_resflows_phase); + std::cout << "Checking well conditions." << std::endl; + // For testing we set surface := reservoir + well_control_passed = wells_manager_.conditionsMet(well_state.bhp(), well_resflows_phase, well_resflows_phase); + ++well_control_iteration; + if (!well_control_passed && well_control_iteration > max_well_control_iterations_) { + OPM_THROW(std::runtime_error, "Could not satisfy well conditions in " << max_well_control_iterations_ << " tries."); + } + if (!well_control_passed) { + std::cout << "Well controls not passed, solving again." << std::endl; + } else { + std::cout << "Well conditions met." << std::endl; + } + } + } while (!well_control_passed); + // Update pore volumes if rock is compressible. @@ -369,6 +438,7 @@ namespace Opm outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); } outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); + outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); #if 0 outputWaterCut(watercut, output_dir_); if (wells_) { diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp index f4ec87ca5..b8cdb2a66 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp @@ -24,7 +24,7 @@ #include struct UnstructuredGrid; - +struct Wells; namespace Opm { namespace parameter { class ParameterGroup; } @@ -34,6 +34,8 @@ namespace Opm class PolymerState; class PolymerPropsAd; class PolymerInflowInterface; + class WellsManager; + class WellState; struct SimulatorReport; /// Class collecting all necessary components for a two-phase simulation. @@ -64,8 +66,9 @@ namespace Opm const IncompPropsAdInterface& props, const PolymerPropsAd& polymer_props, LinearSolverInterface& linsolver, - const PolymerInflowInterface& polymer_inflow, - std::vector& src); + WellsManager& wells_manager, + PolymerInflowInterface& polymer_inflow, + const double* gravity); /// Run the simulation. /// This will run succesive timesteps until timer.done() is true. It will @@ -75,7 +78,8 @@ namespace Opm /// \param[in,out] well_state state of wells: bhp, perforation rates /// \return simulation report, with timing data SimulatorReport run(SimulatorTimer& timer, - PolymerState& state); + PolymerState& state, + WellState& well_state); private: class Impl; From cf28164a5a8dabe3cdc370d6a812defad40f1cba Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 27 Dec 2013 15:48:04 +0800 Subject: [PATCH 19/83] new simulator for fully implicit compressible twophase polymer works. --- examples/sim_poly_fi2p_comp_ad.cpp | 272 +++++ opm/polymer/PolymerBlackoilState.hpp | 4 + opm/polymer/fullyimplicit/BlackoilPropsAd.cpp | 588 +++++++++++ opm/polymer/fullyimplicit/BlackoilPropsAd.hpp | 248 +++++ .../fullyimplicit/BlackoilPropsAdFromDeck.cpp | 682 +++++++++++++ .../fullyimplicit/BlackoilPropsAdFromDeck.hpp | 253 +++++ .../BlackoilPropsAdInterface.cpp | 24 + .../BlackoilPropsAdInterface.hpp | 250 +++++ ...FullyImplicitCompressiblePolymerSolver.cpp | 964 ++++++++++++++++++ ...FullyImplicitCompressiblePolymerSolver.hpp | 229 +++++ .../FullyImplicitTwophasePolymerSolver.cpp | 4 +- opm/polymer/fullyimplicit/GeoProps.hpp | 110 ++ ...ulatorFullyImplicitCompressiblePolymer.cpp | 522 ++++++++++ ...ulatorFullyImplicitCompressiblePolymer.hpp | 98 ++ .../SimulatorFullyImplicitTwophasePolymer.cpp | 2 + 15 files changed, 4247 insertions(+), 3 deletions(-) create mode 100644 examples/sim_poly_fi2p_comp_ad.cpp create mode 100644 opm/polymer/fullyimplicit/BlackoilPropsAd.cpp create mode 100644 opm/polymer/fullyimplicit/BlackoilPropsAd.hpp create mode 100644 opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.cpp create mode 100644 opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.hpp create mode 100644 opm/polymer/fullyimplicit/BlackoilPropsAdInterface.cpp create mode 100644 opm/polymer/fullyimplicit/BlackoilPropsAdInterface.hpp create mode 100644 opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp create mode 100644 opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp create mode 100644 opm/polymer/fullyimplicit/GeoProps.hpp create mode 100644 opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp create mode 100644 opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp diff --git a/examples/sim_poly_fi2p_comp_ad.cpp b/examples/sim_poly_fi2p_comp_ad.cpp new file mode 100644 index 000000000..426e13489 --- /dev/null +++ b/examples/sim_poly_fi2p_comp_ad.cpp @@ -0,0 +1,272 @@ +/* + Copyright 2013 SINTEF ICT, Applied Mathematics. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + + +#if HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + + +namespace +{ + void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param) + { + if (param.anyUnused()) { + std::cout << "-------------------- Unused parameters: --------------------\n"; + param.displayUsage(); + std::cout << "----------------------------------------------------------------" << std::endl; + } + } +} // anon namespace + + + +// ----------------- Main program ----------------- +int +main(int argc, char** argv) +try +{ + using namespace Opm; + + std::cout << "\n================ Test program for fully implicit three-phase black-oil flow ===============\n\n"; + parameter::ParameterGroup param(argc, argv, false); + std::cout << "--------------- Reading parameters ---------------" << std::endl; + + // If we have a "deck_filename", grid and props will be read from that. + bool use_deck = param.has("deck_filename"); + if (!use_deck) { + OPM_THROW(std::runtime_error, "This program must be run with an input deck. " + "Specify the deck with deck_filename=deckname.data (for example)."); + } + boost::scoped_ptr deck; + boost::scoped_ptr grid; + boost::scoped_ptr props; + boost::scoped_ptr new_props; + boost::scoped_ptr rock_comp; + PolymerBlackoilState state; + // bool check_well_controls = false; + // int max_well_control_iterations = 0; + double gravity[3] = { 0.0 }; + std::string deck_filename = param.get("deck_filename"); + deck.reset(new EclipseGridParser(deck_filename)); + // Grid init + grid.reset(new GridManager(*deck)); + + // use the capitalized part of the deck's filename between the + // last '/' and the last '.' character as base name. + std::string baseName = deck_filename; + auto charPos = baseName.rfind('/'); + if (charPos != std::string::npos) + baseName = baseName.substr(charPos + 1); + charPos = baseName.rfind('.'); + if (charPos != std::string::npos) + baseName = baseName.substr(0, charPos); + baseName = boost::to_upper_copy(baseName); + + Opm::EclipseWriter outputWriter(param, share_obj(*deck), share_obj(*grid->c_grid())); + // Rock and fluid init + props.reset(new BlackoilPropertiesFromDeck(*deck, *grid->c_grid(), param)); + new_props.reset(new BlackoilPropsAdFromDeck(*deck, *grid->c_grid())); + PolymerProperties polymer_props(*deck); + PolymerPropsAd polymer_props_ad(polymer_props); + // check_well_controls = param.getDefault("check_well_controls", false); + // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); + // Rock compressibility. + rock_comp.reset(new RockCompressibility(*deck)); + // Gravity. + gravity[2] = deck->hasField("NOGRAV") ? 0.0 : unit::gravity; + // Init state variables (saturation and pressure). + if (param.has("init_saturation")) { + initStateBasic(*grid->c_grid(), *props, param, gravity[2], state); + initBlackoilSurfvol(*grid->c_grid(), *props, state); + } else { + initStateFromDeck(*grid->c_grid(), *props, *deck, gravity[2], state); + } + + bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); + const double *grav = use_gravity ? &gravity[0] : 0; + + // Linear solver. + LinearSolverFactory linsolver(param); + + // Write parameters used for later reference. + bool output = param.getDefault("output", true); + std::ofstream epoch_os; + std::string output_dir; + if (output) { + output_dir = + param.getDefault("output_dir", std::string("output")); + boost::filesystem::path fpath(output_dir); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + std::string filename = output_dir + "/epoch_timing.param"; + epoch_os.open(filename.c_str(), std::fstream::trunc | std::fstream::out); + // open file to clean it. The file is appended to in SimulatorTwophase + filename = output_dir + "/step_timing.param"; + std::fstream step_os(filename.c_str(), std::fstream::trunc | std::fstream::out); + step_os.close(); + param.writeParam(output_dir + "/simulation.param"); + } + + + std::cout << "\n\n================ Starting main simulation loop ===============\n" + << " (number of epochs: " + << (deck->numberOfEpochs()) << ")\n\n" << std::flush; + + SimulatorReport rep; + // With a deck, we may have more epochs etc. + WellState well_state; + int step = 0; + SimulatorTimer simtimer; + // Use timer for last epoch to obtain total time. + deck->setCurrentEpoch(deck->numberOfEpochs() - 1); + simtimer.init(*deck); + const double total_time = simtimer.totalTime(); + // Check for WPOLYMER presence in last epoch to decide + // polymer injection control type. + const bool use_wpolymer = deck->hasField("WPOLYMER"); + if (use_wpolymer) { + if (param.has("poly_start_days")) { + OPM_MESSAGE("Warning: Using WPOLYMER to control injection since it was found in deck. " + "You seem to be trying to control it via parameter poly_start_days (etc.) as well."); + } + } + for (int epoch = 0; epoch < deck->numberOfEpochs(); ++epoch) { + // Set epoch index. + deck->setCurrentEpoch(epoch); + + // Update the timer. + if (deck->hasField("TSTEP")) { + simtimer.init(*deck); + } else { + if (epoch != 0) { + OPM_THROW(std::runtime_error, "No TSTEP in deck for epoch " << epoch); + } + simtimer.init(param); + } + simtimer.setCurrentStepNum(step); + simtimer.setTotalTime(total_time); + + // Report on start of epoch. + std::cout << "\n\n-------------- Starting epoch " << epoch << " --------------" + << "\n (number of steps: " + << simtimer.numSteps() - step << ")\n\n" << std::flush; + + // Create new wells, polymer inflow controls. + WellsManager wells(*deck, *grid->c_grid(), props->permeability()); + boost::scoped_ptr polymer_inflow; + if (use_wpolymer) { + if (wells.c_wells() == 0) { + OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells."); + } + polymer_inflow.reset(new PolymerInflowFromDeck(*deck, *wells.c_wells(), props->numCells())); + } else { + polymer_inflow.reset(new PolymerInflowBasic(param.getDefault("poly_start_days", 300.0)*Opm::unit::day, + param.getDefault("poly_end_days", 800.0)*Opm::unit::day, + param.getDefault("poly_amount", polymer_props.cMax()))); + } + // @@@ HACK: we should really make a new well state and + // properly transfer old well state to it every epoch, + // since number of wells may change etc. + if (epoch == 0) { + well_state.init(wells.c_wells(), state); + } + + // Create and run simulator. + SimulatorFullyImplicitCompressiblePolymer simulator(param, + *grid->c_grid(), + *new_props, + polymer_props_ad, + rock_comp->isActive() ? rock_comp.get() : 0, + wells, + *polymer_inflow, + linsolver, + grav); + if (epoch == 0) { + warnIfUnusedParams(param); + } + SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state); + if (output) { + epoch_rep.reportParam(epoch_os); + } + // Update total timing report and remember step number. + rep += epoch_rep; + step = simtimer.currentStepNum(); + } + + std::cout << "\n\n================ End of simulation ===============\n\n"; + rep.report(std::cout); + + if (output) { + std::string filename = output_dir + "/walltime.param"; + std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out); + rep.reportParam(tot_os); + } + +} +catch (const std::exception &e) { + std::cerr << "Program threw an exception: " << e.what() << "\n"; + throw; +} + diff --git a/opm/polymer/PolymerBlackoilState.hpp b/opm/polymer/PolymerBlackoilState.hpp index d6f714ae0..8344397a0 100644 --- a/opm/polymer/PolymerBlackoilState.hpp +++ b/opm/polymer/PolymerBlackoilState.hpp @@ -39,6 +39,10 @@ namespace Opm concentration_.resize(g.number_of_cells, 0.0); cmax_.resize(g.number_of_cells, 0.0); } + int numPhases() const + { + return state_blackoil_.numPhases(); + } enum ExtremalSat { MinSat = BlackoilState::MinSat, MaxSat = BlackoilState::MaxSat }; diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAd.cpp b/opm/polymer/fullyimplicit/BlackoilPropsAd.cpp new file mode 100644 index 000000000..d83efc938 --- /dev/null +++ b/opm/polymer/fullyimplicit/BlackoilPropsAd.cpp @@ -0,0 +1,588 @@ +/* + Copyright 2013 SINTEF ICT, Applied Mathematics. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#include + +#include +#include +#include +#include +#include + +namespace Opm +{ + + // Making these typedef to make the code more readable. + typedef BlackoilPropsAd::ADB ADB; + typedef BlackoilPropsAd::V V; + typedef Eigen::Array Block; + + /// Constructor wrapping an opm-core black oil interface. + BlackoilPropsAd::BlackoilPropsAd(const BlackoilPropertiesInterface& props) + : props_(props), + pu_(props.phaseUsage()) + { + } + + //////////////////////////// + // Rock interface // + //////////////////////////// + + /// \return D, the number of spatial dimensions. + int BlackoilPropsAd::numDimensions() const + { + return props_.numDimensions(); + } + + /// \return N, the number of cells. + int BlackoilPropsAd::numCells() const + { + return props_.numCells(); + } + + /// \return Array of N porosity values. + const double* BlackoilPropsAd::porosity() const + { + return props_.porosity(); + } + + /// \return Array of ND^2 permeability values. + /// The D^2 permeability values for a cell are organized as a matrix, + /// which is symmetric (so ordering does not matter). + const double* BlackoilPropsAd::permeability() const + { + return props_.permeability(); + } + + + //////////////////////////// + // Fluid interface // + //////////////////////////// + + /// \return Number of active phases (also the number of components). + int BlackoilPropsAd::numPhases() const + { + return props_.numPhases(); + } + + /// \return Object describing the active phases. + PhaseUsage BlackoilPropsAd::phaseUsage() const + { + return props_.phaseUsage(); + } + + // ------ Density ------ + + /// Densities of stock components at surface conditions. + /// \return Array of 3 density values. + const double* BlackoilPropsAd::surfaceDensity() const + { + return props_.surfaceDensity(); + } + + + // ------ Viscosity ------ + + /// Water viscosity. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + V BlackoilPropsAd::muWat(const V& pw, + const Cells& cells) const + { + if (!pu_.phase_used[Water]) { + OPM_THROW(std::runtime_error, "Cannot call muWat(): water phase not present."); + } + const int n = cells.size(); + assert(pw.size() == n); + const int np = props_.numPhases(); + Block z = Block::Zero(n, np); + Block mu(n, np); + props_.viscosity(n, pw.data(), z.data(), cells.data(), mu.data(), 0); + return mu.col(pu_.phase_pos[Water]); + } + + /// Oil viscosity. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + V BlackoilPropsAd::muOil(const V& po, + const V& rs, + const Cells& cells) const + { + if (!pu_.phase_used[Oil]) { + OPM_THROW(std::runtime_error, "Cannot call muOil(): oil phase not present."); + } + const int n = cells.size(); + assert(po.size() == n); + const int np = props_.numPhases(); + Block z = Block::Zero(n, np); + if (pu_.phase_used[Gas]) { + // Faking a z with the right ratio: + // rs = zg/zo + z.col(pu_.phase_pos[Oil]) = V::Ones(n, 1); + z.col(pu_.phase_pos[Gas]) = rs; + } + Block mu(n, np); + props_.viscosity(n, po.data(), z.data(), cells.data(), mu.data(), 0); + return mu.col(pu_.phase_pos[Oil]); + } + + /// Gas viscosity. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + V BlackoilPropsAd::muGas(const V& pg, + const Cells& cells) const + { + if (!pu_.phase_used[Gas]) { + OPM_THROW(std::runtime_error, "Cannot call muGas(): gas phase not present."); + } + const int n = cells.size(); + assert(pg.size() == n); + const int np = props_.numPhases(); + Block z = Block::Zero(n, np); + Block mu(n, np); + props_.viscosity(n, pg.data(), z.data(), cells.data(), mu.data(), 0); + return mu.col(pu_.phase_pos[Gas]); + } + + /// Water viscosity. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + ADB BlackoilPropsAd::muWat(const ADB& pw, + const Cells& cells) const + { +#if 1 + return ADB::constant(muWat(pw.value(), cells), pw.blockPattern()); +#else + if (!pu_.phase_used[Water]) { + OPM_THROW(std::runtime_error, "Cannot call muWat(): water phase not present."); + } + const int n = cells.size(); + assert(pw.value().size() == n); + const int np = props_.numPhases(); + Block z = Block::Zero(n, np); + Block mu(n, np); + Block dmu(n, np); + props_.viscosity(n, pw.value().data(), z.data(), cells.data(), mu.data(), dmu.data()); + ADB::M dmu_diag = spdiag(dmu.col(pu_.phase_pos[Water])); + const int num_blocks = pw.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = dmu_diag * pw.derivative()[block]; + } + return ADB::function(mu.col(pu_.phase_pos[Water]), jacs); +#endif + } + + /// Oil viscosity. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + ADB BlackoilPropsAd::muOil(const ADB& po, + const ADB& rs, + const Cells& cells) const + { +#if 1 + return ADB::constant(muOil(po.value(), rs.value(), cells), po.blockPattern()); +#else + if (!pu_.phase_used[Oil]) { + OPM_THROW(std::runtime_error, "Cannot call muOil(): oil phase not present."); + } + const int n = cells.size(); + assert(po.value().size() == n); + const int np = props_.numPhases(); + Block z = Block::Zero(n, np); + if (pu_.phase_used[Gas]) { + // Faking a z with the right ratio: + // rs = zg/zo + z.col(pu_.phase_pos[Oil]) = V::Ones(n, 1); + z.col(pu_.phase_pos[Gas]) = rs.value(); + } + Block mu(n, np); + Block dmu(n, np); + props_.viscosity(n, po.value().data(), z.data(), cells.data(), mu.data(), dmu.data()); + ADB::M dmu_diag = spdiag(dmu.col(pu_.phase_pos[Oil])); + const int num_blocks = po.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + // For now, we deliberately ignore the derivative with respect to rs, + // since the BlackoilPropertiesInterface class does not evaluate it. + // We would add to the next line: + dmu_drs_diag * rs.derivative()[block] + jacs[block] = dmu_diag * po.derivative()[block]; + } + return ADB::function(mu.col(pu_.phase_pos[Oil]), jacs); +#endif + } + + /// Gas viscosity. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + ADB BlackoilPropsAd::muGas(const ADB& pg, + const Cells& cells) const + { +#if 1 + return ADB::constant(muGas(pg.value(), cells), pg.blockPattern()); +#else + if (!pu_.phase_used[Gas]) { + OPM_THROW(std::runtime_error, "Cannot call muGas(): gas phase not present."); + } + const int n = cells.size(); + assert(pg.value().size() == n); + const int np = props_.numPhases(); + Block z = Block::Zero(n, np); + Block mu(n, np); + Block dmu(n, np); + props_.viscosity(n, pg.value().data(), z.data(), cells.data(), mu.data(), dmu.data()); + ADB::M dmu_diag = spdiag(dmu.col(pu_.phase_pos[Gas])); + const int num_blocks = pg.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = dmu_diag * pg.derivative()[block]; + } + return ADB::function(mu.col(pu_.phase_pos[Gas]), jacs); +#endif + } + + + // ------ Formation volume factor (b) ------ + + // These methods all call the matrix() method, after which the variable + // (also) called 'matrix' contains, in each row, the A = RB^{-1} matrix for + // a cell. For three-phase black oil: + // A = [ bw 0 0 + // 0 bo 0 + // 0 b0*rs bw ] + // Where b = B^{-1}. + // Therefore, we extract the correct diagonal element, and are done. + // When we need the derivatives (w.r.t. p, since we don't do w.r.t. rs), + // we also get the following derivative matrix: + // A = [ dbw 0 0 + // 0 dbo 0 + // 0 db0*rs dbw ] + // Again, we just extract a diagonal element. + + /// Water formation volume factor. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + V BlackoilPropsAd::bWat(const V& pw, + const Cells& cells) const + { + if (!pu_.phase_used[Water]) { + OPM_THROW(std::runtime_error, "Cannot call bWat(): water phase not present."); + } + const int n = cells.size(); + assert(pw.size() == n); + const int np = props_.numPhases(); + Block z = Block::Zero(n, np); + Block matrix(n, np*np); + props_.matrix(n, pw.data(), z.data(), cells.data(), matrix.data(), 0); + const int wi = pu_.phase_pos[Water]; + return matrix.col(wi*np + wi); + } + + /// Oil formation volume factor. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + V BlackoilPropsAd::bOil(const V& po, + const V& rs, + const Cells& cells) const + { + if (!pu_.phase_used[Oil]) { + OPM_THROW(std::runtime_error, "Cannot call bOil(): oil phase not present."); + } + const int n = cells.size(); + assert(po.size() == n); + const int np = props_.numPhases(); + Block z = Block::Zero(n, np); + if (pu_.phase_used[Gas]) { + // Faking a z with the right ratio: + // rs = zg/zo + z.col(pu_.phase_pos[Oil]) = V::Ones(n, 1); + z.col(pu_.phase_pos[Gas]) = rs; + } + Block matrix(n, np*np); + props_.matrix(n, po.data(), z.data(), cells.data(), matrix.data(), 0); + const int oi = pu_.phase_pos[Oil]; + return matrix.col(oi*np + oi); + } + + /// Gas formation volume factor. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + V BlackoilPropsAd::bGas(const V& pg, + const Cells& cells) const + { + if (!pu_.phase_used[Gas]) { + OPM_THROW(std::runtime_error, "Cannot call bGas(): gas phase not present."); + } + const int n = cells.size(); + assert(pg.size() == n); + const int np = props_.numPhases(); + Block z = Block::Zero(n, np); + Block matrix(n, np*np); + props_.matrix(n, pg.data(), z.data(), cells.data(), matrix.data(), 0); + const int gi = pu_.phase_pos[Gas]; + return matrix.col(gi*np + gi); + } + + /// Water formation volume factor. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + ADB BlackoilPropsAd::bWat(const ADB& pw, + const Cells& cells) const + { + if (!pu_.phase_used[Water]) { + OPM_THROW(std::runtime_error, "Cannot call muWat(): water phase not present."); + } + const int n = cells.size(); + assert(pw.value().size() == n); + const int np = props_.numPhases(); + Block z = Block::Zero(n, np); + Block matrix(n, np*np); + Block dmatrix(n, np*np); + props_.matrix(n, pw.value().data(), z.data(), cells.data(), matrix.data(), dmatrix.data()); + const int phase_ind = pu_.phase_pos[Water]; + const int column = phase_ind*np + phase_ind; // Index of our sought diagonal column. + ADB::M db_diag = spdiag(dmatrix.col(column)); + const int num_blocks = pw.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = db_diag * pw.derivative()[block]; + } + return ADB::function(matrix.col(column), jacs); + } + + /// Oil formation volume factor. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + ADB BlackoilPropsAd::bOil(const ADB& po, + const ADB& rs, + const Cells& cells) const + { + if (!pu_.phase_used[Oil]) { + OPM_THROW(std::runtime_error, "Cannot call muOil(): oil phase not present."); + } + const int n = cells.size(); + assert(po.value().size() == n); + const int np = props_.numPhases(); + Block z = Block::Zero(n, np); + if (pu_.phase_used[Gas]) { + // Faking a z with the right ratio: + // rs = zg/zo + z.col(pu_.phase_pos[Oil]) = V::Ones(n, 1); + z.col(pu_.phase_pos[Gas]) = rs.value(); + } + Block matrix(n, np*np); + Block dmatrix(n, np*np); + props_.matrix(n, po.value().data(), z.data(), cells.data(), matrix.data(), dmatrix.data()); + const int phase_ind = pu_.phase_pos[Oil]; + const int column = phase_ind*np + phase_ind; // Index of our sought diagonal column. + ADB::M db_diag = spdiag(dmatrix.col(column)); + const int num_blocks = po.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + // For now, we deliberately ignore the derivative with respect to rs, + // since the BlackoilPropertiesInterface class does not evaluate it. + // We would add to the next line: + db_drs_diag * rs.derivative()[block] + jacs[block] = db_diag * po.derivative()[block]; + } + return ADB::function(matrix.col(column), jacs); + } + + /// Gas formation volume factor. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + ADB BlackoilPropsAd::bGas(const ADB& pg, + const Cells& cells) const + { + if (!pu_.phase_used[Gas]) { + OPM_THROW(std::runtime_error, "Cannot call muGas(): gas phase not present."); + } + const int n = cells.size(); + assert(pg.value().size() == n); + const int np = props_.numPhases(); + Block z = Block::Zero(n, np); + Block matrix(n, np*np); + Block dmatrix(n, np*np); + props_.matrix(n, pg.value().data(), z.data(), cells.data(), matrix.data(), dmatrix.data()); + const int phase_ind = pu_.phase_pos[Gas]; + const int column = phase_ind*np + phase_ind; // Index of our sought diagonal column. + ADB::M db_diag = spdiag(dmatrix.col(column)); + const int num_blocks = pg.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = db_diag * pg.derivative()[block]; + } + return ADB::function(matrix.col(column), jacs); + } + + + // ------ Rs bubble point curve ------ + + /// Bubble point curve for Rs as function of oil pressure. + /// \param[in] po Array of n oil pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n bubble point values for Rs. + V BlackoilPropsAd::rsMax(const V& po, + const Cells& cells) const + { + // Suppress warning about "unused parameters". + static_cast(po); + static_cast(cells); + + OPM_THROW(std::runtime_error, "Method rsMax() not implemented."); + } + + /// Bubble point curve for Rs as function of oil pressure. + /// \param[in] po Array of n oil pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n bubble point values for Rs. + ADB BlackoilPropsAd::rsMax(const ADB& po, + const Cells& cells) const + { + // Suppress warning about "unused parameters". + static_cast(po); + static_cast(cells); + + OPM_THROW(std::runtime_error, "Method rsMax() not implemented."); + } + + // ------ Relative permeability ------ + + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] sg Array of n gas saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 3 elements, each an array of n relperm values, + /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. + std::vector BlackoilPropsAd::relperm(const V& sw, + const V& so, + const V& sg, + const Cells& cells) const + { + const int n = cells.size(); + const int np = props_.numPhases(); + Block s_all(n, np); + if (pu_.phase_used[Water]) { + assert(sw.size() == n); + s_all.col(pu_.phase_pos[Water]) = sw; + } + if (pu_.phase_used[Oil]) { + assert(so.size() == n); + s_all.col(pu_.phase_pos[Oil]) = so; + } + if (pu_.phase_used[Gas]) { + assert(sg.size() == n); + s_all.col(pu_.phase_pos[Gas]) = sg; + } + Block kr(n, np); + props_.relperm(n, s_all.data(), cells.data(), kr.data(), 0); + std::vector relperms; + relperms.reserve(3); + for (int phase = 0; phase < 3; ++phase) { + if (pu_.phase_used[phase]) { + relperms.emplace_back(kr.col(pu_.phase_pos[phase])); + } else { + relperms.emplace_back(); + } + } + return relperms; + } + + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] sg Array of n gas saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 3 elements, each an array of n relperm values, + /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. + std::vector BlackoilPropsAd::relperm(const ADB& sw, + const ADB& so, + const ADB& sg, + const Cells& cells) const + { + const int n = cells.size(); + const int np = props_.numPhases(); + Block s_all(n, np); + if (pu_.phase_used[Water]) { + assert(sw.value().size() == n); + s_all.col(pu_.phase_pos[Water]) = sw.value(); + } + if (pu_.phase_used[Oil]) { + assert(so.value().size() == n); + s_all.col(pu_.phase_pos[Oil]) = so.value(); + } else { + OPM_THROW(std::runtime_error, "BlackoilPropsAd::relperm() assumes oil phase is active."); + } + if (pu_.phase_used[Gas]) { + assert(sg.value().size() == n); + s_all.col(pu_.phase_pos[Gas]) = sg.value(); + } + Block kr(n, np); + Block dkr(n, np*np); + props_.relperm(n, s_all.data(), cells.data(), kr.data(), dkr.data()); + const int num_blocks = so.numBlocks(); + std::vector relperms; + relperms.reserve(3); + typedef const ADB* ADBPtr; + ADBPtr s[3] = { &sw, &so, &sg }; + for (int phase1 = 0; phase1 < 3; ++phase1) { + if (pu_.phase_used[phase1]) { + const int phase1_pos = pu_.phase_pos[phase1]; + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = ADB::M(n, s[phase1]->derivative()[block].cols()); + } + for (int phase2 = 0; phase2 < 3; ++phase2) { + if (!pu_.phase_used[phase2]) { + continue; + } + const int phase2_pos = pu_.phase_pos[phase2]; + // Assemble dkr1/ds2. + const int column = phase1_pos + np*phase2_pos; // Recall: Fortran ordering from props_.relperm() + ADB::M dkr1_ds2_diag = spdiag(dkr.col(column)); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] += dkr1_ds2_diag * s[phase2]->derivative()[block]; + } + } + relperms.emplace_back(ADB::function(kr.col(phase1_pos), jacs)); + } else { + relperms.emplace_back(ADB::null()); + } + } + return relperms; + } + +} // namespace Opm + diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAd.hpp b/opm/polymer/fullyimplicit/BlackoilPropsAd.hpp new file mode 100644 index 000000000..b6fd925d5 --- /dev/null +++ b/opm/polymer/fullyimplicit/BlackoilPropsAd.hpp @@ -0,0 +1,248 @@ +/* + Copyright 2013 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_BLACKOILPROPSAD_HEADER_INCLUDED +#define OPM_BLACKOILPROPSAD_HEADER_INCLUDED + +#include +#include +#include + +namespace Opm +{ + + class BlackoilPropertiesInterface; + + /// This class implements the AD-adapted fluid interface for + /// three-phase black-oil. + /// + /// It is implemented by wrapping a BlackoilPropertiesInterface + /// object (the interface class defined in opm-core) and calling + /// its methods. This class does not implement rsMax() because the + /// required information is not available when wrapping a + /// BlackoilPropertiesInterface. Consequently, class + /// BlackoilPropsAd cannot be used to simulate problems involving + /// miscibility. + /// + /// Most methods are available in two overloaded versions, one + /// taking a constant vector and returning the same, and one + /// taking an AD type and returning the same. Derivatives are not + /// returned separately by any method, only implicitly with the AD + /// version of the methods. + class BlackoilPropsAd : public BlackoilPropsAdInterface + { + public: + /// Constructor wrapping an opm-core black oil interface. + explicit BlackoilPropsAd(const BlackoilPropertiesInterface& props); + + //////////////////////////// + // Rock interface // + //////////////////////////// + + /// \return D, the number of spatial dimensions. + int numDimensions() const; + + /// \return N, the number of cells. + int numCells() const; + + /// \return Array of N porosity values. + const double* porosity() const; + + /// \return Array of ND^2 permeability values. + /// The D^2 permeability values for a cell are organized as a matrix, + /// which is symmetric (so ordering does not matter). + const double* permeability() const; + + + //////////////////////////// + // Fluid interface // + //////////////////////////// + + typedef AutoDiffBlock ADB; + typedef ADB::V V; + typedef std::vector Cells; + + /// \return Number of active phases (also the number of components). + virtual int numPhases() const; + + /// \return Object describing the active phases. + virtual PhaseUsage phaseUsage() const; + + // ------ Canonical named indices for each phase ------ + + /// Canonical named indices for each phase. + enum PhaseIndex { Water = 0, Oil = 1, Gas = 2 }; + + + // ------ Density ------ + + /// Densities of stock components at surface conditions. + /// \return Array of 3 density values. + const double* surfaceDensity() const; + + + // ------ Viscosity ------ + + /// Water viscosity. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + V muWat(const V& pw, + const Cells& cells) const; + + /// Oil viscosity. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + V muOil(const V& po, + const V& rs, + const Cells& cells) const; + + /// Gas viscosity. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + V muGas(const V& pg, + const Cells& cells) const; + + /// Water viscosity. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + ADB muWat(const ADB& pw, + const Cells& cells) const; + + /// Oil viscosity. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + ADB muOil(const ADB& po, + const ADB& rs, + const Cells& cells) const; + + /// Gas viscosity. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + ADB muGas(const ADB& pg, + const Cells& cells) const; + + + // ------ Formation volume factor (b) ------ + + /// Water formation volume factor. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + V bWat(const V& pw, + const Cells& cells) const; + + /// Oil formation volume factor. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + V bOil(const V& po, + const V& rs, + const Cells& cells) const; + + /// Gas formation volume factor. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + V bGas(const V& pg, + const Cells& cells) const; + + /// Water formation volume factor. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + ADB bWat(const ADB& pw, + const Cells& cells) const; + + /// Oil formation volume factor. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + ADB bOil(const ADB& po, + const ADB& rs, + const Cells& cells) const; + + /// Gas formation volume factor. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + ADB bGas(const ADB& pg, + const Cells& cells) const; + + + // ------ Rs bubble point curve ------ + + /// Bubble point curve for Rs as function of oil pressure. + /// \param[in] po Array of n oil pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n bubble point values for Rs. + V rsMax(const V& po, + const Cells& cells) const; + + /// Bubble point curve for Rs as function of oil pressure. + /// \param[in] po Array of n oil pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n bubble point values for Rs. + ADB rsMax(const ADB& po, + const Cells& cells) const; + + + // ------ Relative permeability ------ + + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] sg Array of n gas saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 3 elements, each an array of n relperm values, + /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. + std::vector relperm(const V& sw, + const V& so, + const V& sg, + const Cells& cells) const; + + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] sg Array of n gas saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 3 elements, each an array of n relperm values, + /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. + std::vector relperm(const ADB& sw, + const ADB& so, + const ADB& sg, + const Cells& cells) const; + + private: + const BlackoilPropertiesInterface& props_; + PhaseUsage pu_; + }; + +} // namespace Opm + +#endif // OPM_BLACKOILPROPSAD_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.cpp b/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.cpp new file mode 100644 index 000000000..c9ec833ab --- /dev/null +++ b/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.cpp @@ -0,0 +1,682 @@ +/* + Copyright 2013 SINTEF ICT, Applied Mathematics. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Opm +{ + + // Making these typedef to make the code more readable. + typedef BlackoilPropsAdFromDeck::ADB ADB; + typedef BlackoilPropsAdFromDeck::V V; + typedef Eigen::Array Block; + enum { Aqua = BlackoilPhases::Aqua, + Liquid = BlackoilPhases::Liquid, + Vapour = BlackoilPhases::Vapour }; + + /// Constructor wrapping an opm-core black oil interface. + BlackoilPropsAdFromDeck::BlackoilPropsAdFromDeck(const EclipseGridParser& deck, + const UnstructuredGrid& grid, + const bool init_rock) + { + if (init_rock){ + rock_.init(deck, grid); + } + const int samples = 0; + const int region_number = 0; + + phase_usage_ = phaseUsageFromDeck(deck); + + // Surface densities. Accounting for different orders in eclipse and our code. + if (deck.hasField("DENSITY")) { + const std::vector& d = deck.getDENSITY().densities_[region_number]; + enum { ECL_oil = 0, ECL_water = 1, ECL_gas = 2 }; + if (phase_usage_.phase_used[Aqua]) { + densities_[phase_usage_.phase_pos[Aqua]] = d[ECL_water]; + } + if (phase_usage_.phase_used[Vapour]) { + densities_[phase_usage_.phase_pos[Vapour]] = d[ECL_gas]; + } + if (phase_usage_.phase_used[Liquid]) { + densities_[phase_usage_.phase_pos[Liquid]] = d[ECL_oil]; + } + } else { + OPM_THROW(std::runtime_error, "Input is missing DENSITY\n"); + } + + // Set the properties. + props_.resize(phase_usage_.num_phases); + // Water PVT + if (phase_usage_.phase_used[Aqua]) { + if (deck.hasField("PVTW")) { + props_[phase_usage_.phase_pos[Aqua]].reset(new SinglePvtConstCompr(deck.getPVTW().pvtw_)); + } else { + // Eclipse 100 default. + props_[phase_usage_.phase_pos[Aqua]].reset(new SinglePvtConstCompr(0.5*Opm::prefix::centi*Opm::unit::Poise)); + } + } + // Oil PVT + if (phase_usage_.phase_used[Liquid]) { + if (deck.hasField("PVDO")) { + if (samples > 0) { + props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDeadSpline(deck.getPVDO().pvdo_, samples)); + } else { + props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDead(deck.getPVDO().pvdo_)); + } + } else if (deck.hasField("PVTO")) { + + props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtLiveOil(deck.getPVTO().pvto_)); + } else if (deck.hasField("PVCDO")) { + props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtConstCompr(deck.getPVCDO().pvcdo_)); + } else { + OPM_THROW(std::runtime_error, "Input is missing PVDO or PVTO\n"); + } + } + // Gas PVT + if (phase_usage_.phase_used[Vapour]) { + if (deck.hasField("PVDG")) { + if (samples > 0) { + props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDeadSpline(deck.getPVDG().pvdg_, samples)); + } else { + props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDead(deck.getPVDG().pvdg_)); + } + // } else if (deck.hasField("PVTG")) { + // props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtLiveGas(deck.getPVTG().pvtg_)); + } else { + OPM_THROW(std::runtime_error, "Input is missing PVDG or PVTG\n"); + } + } + + SaturationPropsFromDeck* ptr + = new SaturationPropsFromDeck(); + satprops_.reset(ptr); + ptr->init(deck, grid, -1); + + if (phase_usage_.num_phases != satprops_->numPhases()) { + OPM_THROW(std::runtime_error, "BlackoilPropsAdFromDeck::BlackoilPropsAdFromDeck() - " + "Inconsistent number of phases in pvt data (" << phase_usage_.num_phases + << ") and saturation-dependent function data (" << satprops_->numPhases() << ")."); + } + } + + + //////////////////////////// + // Rock interface // + //////////////////////////// + + /// \return D, the number of spatial dimensions. + int BlackoilPropsAdFromDeck::numDimensions() const + { + return rock_.numDimensions(); + } + + /// \return N, the number of cells. + int BlackoilPropsAdFromDeck::numCells() const + { + return rock_.numCells(); + } + + /// \return Array of N porosity values. + const double* BlackoilPropsAdFromDeck::porosity() const + { + return rock_.porosity(); + } + + /// \return Array of ND^2 permeability values. + /// The D^2 permeability values for a cell are organized as a matrix, + /// which is symmetric (so ordering does not matter). + const double* BlackoilPropsAdFromDeck::permeability() const + { + return rock_.permeability(); + } + + + //////////////////////////// + // Fluid interface // + //////////////////////////// + + /// \return Number of active phases (also the number of components). + int BlackoilPropsAdFromDeck::numPhases() const + { + return phase_usage_.num_phases; + } + + /// \return Object describing the active phases. + PhaseUsage BlackoilPropsAdFromDeck::phaseUsage() const + { + return phase_usage_; + } + + // ------ Density ------ + + /// Densities of stock components at surface conditions. + /// \return Array of 3 density values. + const double* BlackoilPropsAdFromDeck::surfaceDensity() const + { + return densities_; + } + + + // ------ Viscosity ------ + + /// Water viscosity. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + V BlackoilPropsAdFromDeck::muWat(const V& pw, + const Cells& cells) const + { + if (!phase_usage_.phase_used[Water]) { + OPM_THROW(std::runtime_error, "Cannot call muWat(): water phase not present."); + } + const int n = cells.size(); + assert(pw.size() == n); + V mu(n); + V dmudp(n); + V dmudr(n); + const double* rs = 0; + + props_[phase_usage_.phase_pos[Water]]->mu(n, pw.data(), rs, + mu.data(), dmudp.data(), dmudr.data()); + return mu; + } + + /// Oil viscosity. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + V BlackoilPropsAdFromDeck::muOil(const V& po, + const V& rs, + const Cells& cells) const + { + if (!phase_usage_.phase_used[Oil]) { + OPM_THROW(std::runtime_error, "Cannot call muOil(): oil phase not present."); + } + const int n = cells.size(); + assert(po.size() == n); + V mu(n); + V dmudp(n); + V dmudr(n); + + props_[phase_usage_.phase_pos[Oil]]->mu(n, po.data(), rs.data(), + mu.data(), dmudp.data(), dmudr.data()); + return mu; + } + + /// Gas viscosity. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + V BlackoilPropsAdFromDeck::muGas(const V& pg, + const Cells& cells) const + { + if (!phase_usage_.phase_used[Gas]) { + OPM_THROW(std::runtime_error, "Cannot call muGas(): gas phase not present."); + } + const int n = cells.size(); + assert(pg.size() == n); + V mu(n); + V dmudp(n); + V dmudr(n); + const double* rs = 0; + + props_[phase_usage_.phase_pos[Gas]]->mu(n, pg.data(), rs, + mu.data(), dmudp.data(), dmudr.data()); + return mu; + } + + /// Water viscosity. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + ADB BlackoilPropsAdFromDeck::muWat(const ADB& pw, + const Cells& cells) const + { + if (!phase_usage_.phase_used[Water]) { + OPM_THROW(std::runtime_error, "Cannot call muWat(): water phase not present."); + } + const int n = cells.size(); + assert(pw.size() == n); + V mu(n); + V dmudp(n); + V dmudr(n); + const double* rs = 0; + + props_[phase_usage_.phase_pos[Water]]->mu(n, pw.value().data(), rs, + mu.data(), dmudp.data(), dmudr.data()); + ADB::M dmudp_diag = spdiag(dmudp); + const int num_blocks = pw.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = dmudp_diag * pw.derivative()[block]; + } + return ADB::function(mu, jacs); + } + + /// Oil viscosity. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + ADB BlackoilPropsAdFromDeck::muOil(const ADB& po, + const ADB& rs, + const Cells& cells) const + { + if (!phase_usage_.phase_used[Oil]) { + OPM_THROW(std::runtime_error, "Cannot call muOil(): oil phase not present."); + } + const int n = cells.size(); + assert(po.size() == n); + V mu(n); + V dmudp(n); + V dmudr(n); + + props_[phase_usage_.phase_pos[Oil]]->mu(n, po.value().data(), rs.value().data(), + mu.data(), dmudp.data(), dmudr.data()); + + ADB::M dmudp_diag = spdiag(dmudp); + ADB::M dmudr_diag = spdiag(dmudr); + const int num_blocks = po.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = dmudp_diag * po.derivative()[block] + dmudr_diag * rs.derivative()[block]; + } + return ADB::function(mu, jacs); + } + + /// Gas viscosity. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + ADB BlackoilPropsAdFromDeck::muGas(const ADB& pg, + const Cells& cells) const + { + if (!phase_usage_.phase_used[Gas]) { + OPM_THROW(std::runtime_error, "Cannot call muGas(): gas phase not present."); + } + const int n = cells.size(); + assert(pg.value().size() == n); + V mu(n); + V dmudp(n); + V dmudr(n); + const double* rs = 0; + + props_[phase_usage_.phase_pos[Gas]]->mu(n, pg.value().data(), rs, + mu.data(), dmudp.data(), dmudr.data()); + + ADB::M dmudp_diag = spdiag(dmudp); + const int num_blocks = pg.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = dmudp_diag * pg.derivative()[block]; + } + return ADB::function(mu, jacs); + } + + + // ------ Formation volume factor (b) ------ + + // These methods all call the matrix() method, after which the variable + // (also) called 'matrix' contains, in each row, the A = RB^{-1} matrix for + // a cell. For three-phase black oil: + // A = [ bw 0 0 + // 0 bo 0 + // 0 b0*rs bw ] + // Where b = B^{-1}. + // Therefore, we extract the correct diagonal element, and are done. + // When we need the derivatives (w.r.t. p, since we don't do w.r.t. rs), + // we also get the following derivative matrix: + // A = [ dbw 0 0 + // 0 dbo 0 + // 0 db0*rs dbw ] + // Again, we just extract a diagonal element. + + /// Water formation volume factor. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + V BlackoilPropsAdFromDeck::bWat(const V& pw, + const Cells& cells) const + { + if (!phase_usage_.phase_used[Water]) { + OPM_THROW(std::runtime_error, "Cannot call bWat(): water phase not present."); + } + const int n = cells.size(); + assert(pw.size() == n); + + V b(n); + V dbdp(n); + V dbdr(n); + const double* rs = 0; + + props_[phase_usage_.phase_pos[Water]]->b(n, pw.data(), rs, + b.data(), dbdp.data(), dbdr.data()); + + return b; + } + + /// Oil formation volume factor. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + V BlackoilPropsAdFromDeck::bOil(const V& po, + const V& rs, + const Cells& cells) const + { + if (!phase_usage_.phase_used[Oil]) { + OPM_THROW(std::runtime_error, "Cannot call bOil(): oil phase not present."); + } + const int n = cells.size(); + assert(po.size() == n); + + V b(n); + V dbdp(n); + V dbdr(n); + + props_[phase_usage_.phase_pos[Oil]]->b(n, po.data(), rs.data(), + b.data(), dbdp.data(), dbdr.data()); + + return b; + } + + /// Gas formation volume factor. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + V BlackoilPropsAdFromDeck::bGas(const V& pg, + const Cells& cells) const + { + if (!phase_usage_.phase_used[Gas]) { + OPM_THROW(std::runtime_error, "Cannot call bGas(): gas phase not present."); + } + const int n = cells.size(); + assert(pg.size() == n); + + V b(n); + V dbdp(n); + V dbdr(n); + const double* rs = 0; + + props_[phase_usage_.phase_pos[Gas]]->b(n, pg.data(), rs, + b.data(), dbdp.data(), dbdr.data()); + + return b; + } + + /// Water formation volume factor. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + ADB BlackoilPropsAdFromDeck::bWat(const ADB& pw, + const Cells& cells) const + { + if (!phase_usage_.phase_used[Water]) { + OPM_THROW(std::runtime_error, "Cannot call muWat(): water phase not present."); + } + const int n = cells.size(); + assert(pw.size() == n); + + V b(n); + V dbdp(n); + V dbdr(n); + const double* rs = 0; + + props_[phase_usage_.phase_pos[Water]]->b(n, pw.value().data(), rs, + b.data(), dbdp.data(), dbdr.data()); + + ADB::M dbdp_diag = spdiag(dbdp); + const int num_blocks = pw.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = dbdp_diag * pw.derivative()[block]; + } + return ADB::function(b, jacs); + } + + /// Oil formation volume factor. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + ADB BlackoilPropsAdFromDeck::bOil(const ADB& po, + const ADB& rs, + const Cells& cells) const + { + if (!phase_usage_.phase_used[Oil]) { + OPM_THROW(std::runtime_error, "Cannot call muOil(): oil phase not present."); + } + const int n = cells.size(); + assert(po.size() == n); + + V b(n); + V dbdp(n); + V dbdr(n); + + props_[phase_usage_.phase_pos[Oil]]->b(n, po.value().data(), rs.value().data(), + b.data(), dbdp.data(), dbdr.data()); + + ADB::M dbdp_diag = spdiag(dbdp); + ADB::M dbdr_diag = spdiag(dbdr); + const int num_blocks = po.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = dbdp_diag * po.derivative()[block] + dbdr_diag * rs.derivative()[block]; + } + return ADB::function(b, jacs); + } + + /// Gas formation volume factor. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + ADB BlackoilPropsAdFromDeck::bGas(const ADB& pg, + const Cells& cells) const + { + if (!phase_usage_.phase_used[Gas]) { + OPM_THROW(std::runtime_error, "Cannot call muGas(): gas phase not present."); + } + const int n = cells.size(); + assert(pg.size() == n); + + V b(n); + V dbdp(n); + V dbdr(n); + const double* rs = 0; + + props_[phase_usage_.phase_pos[Gas]]->b(n, pg.value().data(), rs, + b.data(), dbdp.data(), dbdr.data()); + + ADB::M dbdp_diag = spdiag(dbdp); + const int num_blocks = pg.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = dbdp_diag * pg.derivative()[block]; + } + return ADB::function(b, jacs); + } + + + + // ------ Rs bubble point curve ------ + + /// Bubble point curve for Rs as function of oil pressure. + /// \param[in] po Array of n oil pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n bubble point values for Rs. + V BlackoilPropsAdFromDeck::rsMax(const V& po, + const Cells& cells) const + { + if (!phase_usage_.phase_used[Oil]) { + OPM_THROW(std::runtime_error, "Cannot call rsMax(): oil phase not present."); + } + const int n = cells.size(); + assert(po.size() == n); + V rbub(n); + V drbubdp(n); + props_[Oil]->rbub(n, po.data(), rbub.data(), drbubdp.data()); + return rbub; + } + + /// Bubble point curve for Rs as function of oil pressure. + /// \param[in] po Array of n oil pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n bubble point values for Rs. + ADB BlackoilPropsAdFromDeck::rsMax(const ADB& po, + const Cells& cells) const + { + if (!phase_usage_.phase_used[Oil]) { + OPM_THROW(std::runtime_error, "Cannot call rsMax(): oil phase not present."); + } + const int n = cells.size(); + assert(po.size() == n); + V rbub(n); + V drbubdp(n); + props_[Oil]->rbub(n, po.value().data(), rbub.data(), drbubdp.data()); + ADB::M drbubdp_diag = spdiag(drbubdp); + const int num_blocks = po.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = drbubdp_diag * po.derivative()[block]; + } + return ADB::function(rbub, jacs); + } + + // ------ Relative permeability ------ + + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] sg Array of n gas saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 3 elements, each an array of n relperm values, + /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. + std::vector BlackoilPropsAdFromDeck::relperm(const V& sw, + const V& so, + const V& sg, + const Cells& cells) const + { + const int n = cells.size(); + const int np = numPhases(); + Block s_all(n, np); + if (phase_usage_.phase_used[Water]) { + assert(sw.size() == n); + s_all.col(phase_usage_.phase_pos[Water]) = sw; + } + if (phase_usage_.phase_used[Oil]) { + assert(so.size() == n); + s_all.col(phase_usage_.phase_pos[Oil]) = so; + } + if (phase_usage_.phase_used[Gas]) { + assert(sg.size() == n); + s_all.col(phase_usage_.phase_pos[Gas]) = sg; + } + Block kr(n, np); + satprops_->relperm(n, s_all.data(), cells.data(), kr.data(), 0); + std::vector relperms; + relperms.reserve(3); + for (int phase = 0; phase < 3; ++phase) { + if (phase_usage_.phase_used[phase]) { + relperms.emplace_back(kr.col(phase_usage_.phase_pos[phase])); + } else { + relperms.emplace_back(); + } + } + return relperms; + } + + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] sg Array of n gas saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 3 elements, each an array of n relperm values, + /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. + std::vector BlackoilPropsAdFromDeck::relperm(const ADB& sw, + const ADB& so, + const ADB& sg, + const Cells& cells) const + { + const int n = cells.size(); + const int np = numPhases(); + Block s_all(n, np); + if (phase_usage_.phase_used[Water]) { + assert(sw.value().size() == n); + s_all.col(phase_usage_.phase_pos[Water]) = sw.value(); + } + if (phase_usage_.phase_used[Oil]) { + assert(so.value().size() == n); + s_all.col(phase_usage_.phase_pos[Oil]) = so.value(); + } else { + OPM_THROW(std::runtime_error, "BlackoilPropsAdFromDeck::relperm() assumes oil phase is active."); + } + if (phase_usage_.phase_used[Gas]) { + assert(sg.value().size() == n); + s_all.col(phase_usage_.phase_pos[Gas]) = sg.value(); + } + Block kr(n, np); + Block dkr(n, np*np); + satprops_->relperm(n, s_all.data(), cells.data(), kr.data(), dkr.data()); + const int num_blocks = so.numBlocks(); + std::vector relperms; + relperms.reserve(3); + typedef const ADB* ADBPtr; + ADBPtr s[3] = { &sw, &so, &sg }; + for (int phase1 = 0; phase1 < 3; ++phase1) { + if (phase_usage_.phase_used[phase1]) { + const int phase1_pos = phase_usage_.phase_pos[phase1]; + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = ADB::M(n, s[phase1]->derivative()[block].cols()); + } + for (int phase2 = 0; phase2 < 3; ++phase2) { + if (!phase_usage_.phase_used[phase2]) { + continue; + } + const int phase2_pos = phase_usage_.phase_pos[phase2]; + // Assemble dkr1/ds2. + const int column = phase1_pos + np*phase2_pos; // Recall: Fortran ordering from props_.relperm() + ADB::M dkr1_ds2_diag = spdiag(dkr.col(column)); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] += dkr1_ds2_diag * s[phase2]->derivative()[block]; + } + } + relperms.emplace_back(ADB::function(kr.col(phase1_pos), jacs)); + } else { + relperms.emplace_back(ADB::null()); + } + } + return relperms; + } + +} // namespace Opm + diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.hpp b/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.hpp new file mode 100644 index 000000000..5e0b983f4 --- /dev/null +++ b/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.hpp @@ -0,0 +1,253 @@ +/* + Copyright 2013 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_BLACKOILPROPSADFROMDECK_HEADER_INCLUDED +#define OPM_BLACKOILPROPSADFROMDECK_HEADER_INCLUDED + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace Opm +{ + + class SinglePvtInterface; + + /// This class implements the AD-adapted fluid interface for + /// three-phase black-oil. It requires an input deck from which it + /// reads all relevant property data. + /// + /// Most methods are available in two overloaded versions, one + /// taking a constant vector and returning the same, and one + /// taking an AD type and returning the same. Derivatives are not + /// returned separately by any method, only implicitly with the AD + /// version of the methods. + class BlackoilPropsAdFromDeck : public BlackoilPropsAdInterface + { + public: + /// Constructor wrapping an opm-core black oil interface. + BlackoilPropsAdFromDeck(const EclipseGridParser& deck, + const UnstructuredGrid& grid, + const bool init_rock = true ); + + //////////////////////////// + // Rock interface // + //////////////////////////// + + /// \return D, the number of spatial dimensions. + int numDimensions() const; + + /// \return N, the number of cells. + int numCells() const; + + /// \return Array of N porosity values. + const double* porosity() const; + + /// \return Array of ND^2 permeability values. + /// The D^2 permeability values for a cell are organized as a matrix, + /// which is symmetric (so ordering does not matter). + const double* permeability() const; + + + //////////////////////////// + // Fluid interface // + //////////////////////////// + + typedef AutoDiffBlock ADB; + typedef ADB::V V; + typedef std::vector Cells; + + /// \return Number of active phases (also the number of components). + int numPhases() const; + + /// \return Object describing the active phases. + PhaseUsage phaseUsage() const; + + // ------ Canonical named indices for each phase ------ + + /// Canonical named indices for each phase. + enum PhaseIndex { Water = 0, Oil = 1, Gas = 2 }; + + + // ------ Density ------ + + /// Densities of stock components at surface conditions. + /// \return Array of 3 density values. + const double* surfaceDensity() const; + + + // ------ Viscosity ------ + + /// Water viscosity. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + V muWat(const V& pw, + const Cells& cells) const; + + /// Oil viscosity. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + V muOil(const V& po, + const V& rs, + const Cells& cells) const; + + /// Gas viscosity. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + V muGas(const V& pg, + const Cells& cells) const; + + /// Water viscosity. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + ADB muWat(const ADB& pw, + const Cells& cells) const; + + /// Oil viscosity. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + ADB muOil(const ADB& po, + const ADB& rs, + const Cells& cells) const; + + /// Gas viscosity. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + ADB muGas(const ADB& pg, + const Cells& cells) const; + + + // ------ Formation volume factor (b) ------ + + /// Water formation volume factor. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + V bWat(const V& pw, + const Cells& cells) const; + + /// Oil formation volume factor. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + V bOil(const V& po, + const V& rs, + const Cells& cells) const; + + /// Gas formation volume factor. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + V bGas(const V& pg, + const Cells& cells) const; + + /// Water formation volume factor. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + ADB bWat(const ADB& pw, + const Cells& cells) const; + + /// Oil formation volume factor. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + ADB bOil(const ADB& po, + const ADB& rs, + const Cells& cells) const; + + /// Gas formation volume factor. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + ADB bGas(const ADB& pg, + const Cells& cells) const; + + + // ------ Rs bubble point curve ------ + + /// Bubble point curve for Rs as function of oil pressure. + /// \param[in] po Array of n oil pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n bubble point values for Rs. + V rsMax(const V& po, + const Cells& cells) const; + + /// Bubble point curve for Rs as function of oil pressure. + /// \param[in] po Array of n oil pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n bubble point values for Rs. + ADB rsMax(const ADB& po, + const Cells& cells) const; + + + // ------ Relative permeability ------ + + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] sg Array of n gas saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 3 elements, each an array of n relperm values, + /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. + std::vector relperm(const V& sw, + const V& so, + const V& sg, + const Cells& cells) const; + + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] sg Array of n gas saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 3 elements, each an array of n relperm values, + /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. + std::vector relperm(const ADB& sw, + const ADB& so, + const ADB& sg, + const Cells& cells) const; + + private: + RockFromDeck rock_; + boost::scoped_ptr satprops_; + PhaseUsage phase_usage_; + std::vector > props_; + double densities_[BlackoilPhases::MaxNumPhases]; + }; + + +} // namespace Opm + +#endif // OPM_BLACKOILPROPSADFROMDECK_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAdInterface.cpp b/opm/polymer/fullyimplicit/BlackoilPropsAdInterface.cpp new file mode 100644 index 000000000..f50318245 --- /dev/null +++ b/opm/polymer/fullyimplicit/BlackoilPropsAdInterface.cpp @@ -0,0 +1,24 @@ +/* + Copyright 2013 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 + +Opm::BlackoilPropsAdInterface::~BlackoilPropsAdInterface() +{ +} diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAdInterface.hpp b/opm/polymer/fullyimplicit/BlackoilPropsAdInterface.hpp new file mode 100644 index 000000000..a1417b7d6 --- /dev/null +++ b/opm/polymer/fullyimplicit/BlackoilPropsAdInterface.hpp @@ -0,0 +1,250 @@ +/* + Copyright 2013 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_BLACKOILPROPSADINTERFACE_HEADER_INCLUDED +#define OPM_BLACKOILPROPSADINTERFACE_HEADER_INCLUDED + +#include +#include + +namespace Opm +{ + + /// This class is intended to present a fluid interface for + /// three-phase black-oil that is easy to use with the AD-using + /// simulators. + /// + /// Most methods are available in two overloaded versions, one + /// taking a constant vector and returning the same, and one + /// taking an AD type and returning the same. Derivatives are not + /// returned separately by any method, only implicitly with the AD + /// version of the methods. + class BlackoilPropsAdInterface + { + public: + /// Virtual destructor for inheritance. + virtual ~BlackoilPropsAdInterface(); + + //////////////////////////// + // Rock interface // + //////////////////////////// + + /// \return D, the number of spatial dimensions. + virtual int numDimensions() const = 0; + + /// \return N, the number of cells. + virtual int numCells() const = 0; + + /// \return Array of N porosity values. + virtual const double* porosity() const = 0; + + /// \return Array of ND^2 permeability values. + /// The D^2 permeability values for a cell are organized as a matrix, + /// which is symmetric (so ordering does not matter). + virtual const double* permeability() const = 0; + + + //////////////////////////// + // Fluid interface // + //////////////////////////// + + typedef AutoDiffBlock ADB; + typedef ADB::V V; + typedef ADB::M M; + typedef std::vector Cells; + + /// \return Number of active phases (also the number of components). + virtual int numPhases() const = 0; + + /// \return Object describing the active phases. + virtual PhaseUsage phaseUsage() const = 0; + + // ------ Canonical named indices for each phase ------ + + /// Canonical named indices for each phase. + enum PhaseIndex { Water = 0, Oil = 1, Gas = 2 }; + + // ------ Density ------ + + /// Densities of stock components at surface conditions. + /// \return Array of 3 density values. + virtual const double* surfaceDensity() const = 0; + + + // ------ Viscosity ------ + + /// Water viscosity. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + virtual + V muWat(const V& pw, + const Cells& cells) const = 0; + + /// Oil viscosity. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + virtual + V muOil(const V& po, + const V& rs, + const Cells& cells) const = 0; + + /// Gas viscosity. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + virtual + V muGas(const V& pg, + const Cells& cells) const = 0; + + /// Water viscosity. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + virtual + ADB muWat(const ADB& pw, + const Cells& cells) const = 0; + + /// Oil viscosity. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + virtual + ADB muOil(const ADB& po, + const ADB& rs, + const Cells& cells) const = 0; + + /// Gas viscosity. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n viscosity values. + virtual + ADB muGas(const ADB& pg, + const Cells& cells) const = 0; + + + // ------ Formation volume factor (b) ------ + + /// Water formation volume factor. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + virtual + V bWat(const V& pw, + const Cells& cells) const = 0; + + /// Oil formation volume factor. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + virtual + V bOil(const V& po, + const V& rs, + const Cells& cells) const = 0; + + /// Gas formation volume factor. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + virtual + V bGas(const V& pg, + const Cells& cells) const = 0; + + /// Water formation volume factor. + /// \param[in] pw Array of n water pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + virtual + ADB bWat(const ADB& pw, + const Cells& cells) const = 0; + + /// Oil formation volume factor. + /// \param[in] po Array of n oil pressure values. + /// \param[in] rs Array of n gas solution factor values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + virtual + ADB bOil(const ADB& po, + const ADB& rs, + const Cells& cells) const = 0; + + /// Gas formation volume factor. + /// \param[in] pg Array of n gas pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n formation volume factor values. + virtual + ADB bGas(const ADB& pg, + const Cells& cells) const = 0; + + + // ------ Rs bubble point curve ------ + + /// Bubble point curve for Rs as function of oil pressure. + /// \param[in] po Array of n oil pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n bubble point values for Rs. + virtual + V rsMax(const V& po, + const Cells& cells) const = 0; + + /// Bubble point curve for Rs as function of oil pressure. + /// \param[in] po Array of n oil pressure values. + /// \param[in] cells Array of n cell indices to be associated with the pressure values. + /// \return Array of n bubble point values for Rs. + virtual + ADB rsMax(const ADB& po, + const Cells& cells) const = 0; + + // ------ Relative permeability ------ + + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] sg Array of n gas saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 3 elements, each an array of n relperm values, + /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. + virtual + std::vector relperm(const V& sw, + const V& so, + const V& sg, + const Cells& cells) const = 0; + + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] sg Array of n gas saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 3 elements, each an array of n relperm values, + /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. + virtual + std::vector relperm(const ADB& sw, + const ADB& so, + const ADB& sg, + const Cells& cells) const = 0; + + }; + +} // namespace Opm + +#endif // OPM_BLACKOILPROPSADINTERFACE_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp new file mode 100644 index 000000000..289912d32 --- /dev/null +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -0,0 +1,964 @@ +/* + Copyright 2013 SINTEF ICT, Applied Mathematics. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#include + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// A debugging utility. +#define DUMP(foo) \ + do { \ + std::cout << "==========================================\n" \ + << #foo ":\n" \ + << collapseJacs(foo) << std::endl; \ + } while (0) + + + +namespace Opm { + +typedef AutoDiffBlock ADB; +typedef ADB::V V; +typedef ADB::M M; +typedef Eigen::Array DataBlock; + + +namespace { + + + std::vector + buildAllCells(const int nc) + { + std::vector all_cells(nc); + + for (int c = 0; c < nc; ++c) { all_cells[c] = c; } + + return all_cells; + } + + + + template + AutoDiffBlock::M + gravityOperator(const UnstructuredGrid& grid, + const HelperOps& ops , + const GeoProps& geo ) + { + const int nc = grid.number_of_cells; + + std::vector f2hf(2 * grid.number_of_faces, -1); + for (int c = 0, i = 0; c < nc; ++c) { + for (; i < grid.cell_facepos[c + 1]; ++i) { + const int f = grid.cell_faces[ i ]; + const int p = 0 + (grid.face_cells[2*f + 0] != c); + + f2hf[2*f + p] = i; + } + } + + typedef AutoDiffBlock::V V; + typedef AutoDiffBlock::M M; + + const V& gpot = geo.gravityPotential(); + const V& trans = geo.transmissibility(); + + const HelperOps::IFaces::Index ni = ops.internal_faces.size(); + + typedef Eigen::Triplet Tri; + std::vector grav; grav.reserve(2 * ni); + for (HelperOps::IFaces::Index i = 0; i < ni; ++i) { + const int f = ops.internal_faces[ i ]; + const int c1 = grid.face_cells[2*f + 0]; + const int c2 = grid.face_cells[2*f + 1]; + + assert ((c1 >= 0) && (c2 >= 0)); + + const double dG1 = gpot[ f2hf[2*f + 0] ]; + const double dG2 = gpot[ f2hf[2*f + 1] ]; + const double t = trans[ f ]; + + grav.push_back(Tri(i, c1, t * dG1)); + grav.push_back(Tri(i, c2, - t * dG2)); + } + + M G(ni, nc); G.setFromTriplets(grav.begin(), grav.end()); + + return G; + } + + + + V computePerfPress(const UnstructuredGrid& grid, const Wells& wells, const V& rho, const double grav) + { + const int nw = wells.number_of_wells; + const int nperf = wells.well_connpos[nw]; + const int dim = grid.dimensions; + V wdp = V::Zero(nperf,1); + assert(wdp.size() == rho.size()); + + // Main loop, iterate over all perforations, + // using the following formula: + // wdp(perf) = g*(perf_z - well_ref_z)*rho(perf) + // where the total density rho(perf) is taken to be + // sum_p (rho_p*saturation_p) in the perforation cell. + // [although this is computed on the outside of this function]. + for (int w = 0; w < nw; ++w) { + const double ref_depth = wells.depth_ref[w]; + for (int j = wells.well_connpos[w]; j < wells.well_connpos[w + 1]; ++j) { + const int cell = wells.well_cells[j]; + const double cell_depth = grid.cell_centroids[dim * cell + dim - 1]; + wdp[j] = rho[j]*grav*(cell_depth - ref_depth); + } + } + return wdp; + } + + + + +} // Anonymous namespace + + + + + FullyImplicitCompressiblePolymerSolver:: + FullyImplicitCompressiblePolymerSolver(const UnstructuredGrid& grid , + const BlackoilPropsAdInterface& fluid, + const DerivedGeology& geo , + const RockCompressibility* rock_comp_props, + const PolymerPropsAd& polymer_props_ad, + const Wells& wells, + const LinearSolverInterface& linsolver) + : grid_ (grid) + , fluid_ (fluid) + , geo_ (geo) + , rock_comp_props_(rock_comp_props) + , polymer_props_ad_(polymer_props_ad) + , wells_ (wells) + , linsolver_ (linsolver) + , cells_ (buildAllCells(grid.number_of_cells)) + , ops_ (grid) + , wops_ (wells) + , grav_ (gravityOperator(grid_, ops_, geo_)) + , rq_ (fluid.numPhases() + 1) + , residual_ ( { std::vector(fluid.numPhases() + 1, ADB::null()), + ADB::null(), + ADB::null() } ) + { + } + + + + + + void + FullyImplicitCompressiblePolymerSolver:: + step(const double dt, + PolymerBlackoilState& x , + WellState& xw, + const std::vector& polymer_inflow) + { + const V pvdt = geo_.poreVolume() / dt; + + { + const SolutionState state = constantState(x, xw); + computeAccum(state, 0); + } + + const double atol = 1.0e-12; + const double rtol = 5.0e-8; + const int maxit = 15; + + assemble(pvdt, x, xw, polymer_inflow); + + const double r0 = residualNorm(); + int it = 0; + std::cout << "\nIteration Residual\n" + << std::setw(9) << it << std::setprecision(9) + << std::setw(18) << r0 << std::endl; + bool resTooLarge = r0 > atol; + while (resTooLarge && (it < maxit)) { + const V dx = solveJacobianSystem(); + + updateState(dx, x, xw); + + assemble(pvdt, x, xw, polymer_inflow); + + const double r = residualNorm(); + + resTooLarge = (r > atol) && (r > rtol*r0); + + it += 1; + std::cout << std::setw(9) << it << std::setprecision(9) + << std::setw(18) << r << std::endl; + } + + if (resTooLarge) { + std::cerr << "Failed to compute converged solution in " << it << " iterations. Ignoring!\n"; + // OPM_THROW(std::runtime_error, "Failed to compute converged solution in " << it << " iterations."); + } + } + + + + + + FullyImplicitCompressiblePolymerSolver::ReservoirResidualQuant::ReservoirResidualQuant() + : accum(2, ADB::null()) + , mflux( ADB::null()) + , b ( ADB::null()) + , head ( ADB::null()) + , mob ( ADB::null()) + { + } + + + + + + FullyImplicitCompressiblePolymerSolver::SolutionState::SolutionState(const int np) + : pressure ( ADB::null()) + , saturation(np, ADB::null()) + , concentration( ADB::null()) + , qs ( ADB::null()) + , bhp ( ADB::null()) + { + } + + + + + + FullyImplicitCompressiblePolymerSolver:: + WellOps::WellOps(const Wells& wells) + : w2p(wells.well_connpos[ wells.number_of_wells ], + wells.number_of_wells) + , p2w(wells.number_of_wells, + wells.well_connpos[ wells.number_of_wells ]) + { + const int nw = wells.number_of_wells; + const int* const wpos = wells.well_connpos; + + typedef Eigen::Triplet Tri; + + std::vector scatter, gather; + scatter.reserve(wpos[nw]); + gather .reserve(wpos[nw]); + + for (int w = 0, i = 0; w < nw; ++w) { + for (; i < wpos[ w + 1 ]; ++i) { + scatter.push_back(Tri(i, w, 1.0)); + gather .push_back(Tri(w, i, 1.0)); + } + } + + w2p.setFromTriplets(scatter.begin(), scatter.end()); + p2w.setFromTriplets(gather .begin(), gather .end()); + } + + + + + + FullyImplicitCompressiblePolymerSolver::SolutionState + FullyImplicitCompressiblePolymerSolver::constantState(const PolymerBlackoilState& x, + const WellState& xw) + { + const int nc = grid_.number_of_cells; + const int np = x.numPhases(); + + // The block pattern assumes the following primary variables: + // pressure + // water saturation (if water present) + // polymer concentration + // well rates per active phase and well + // well bottom-hole pressure + // Note that oil is assumed to always be present, but is never + // a primary variable. + std::vector bpat(np + 1, nc); + bpat.push_back(xw.bhp().size() * np); + bpat.push_back(xw.bhp().size()); + + SolutionState state(np); + + // Pressure. + assert (not x.pressure().empty()); + const V p = Eigen::Map(& x.pressure()[0], nc, 1); + state.pressure = ADB::constant(p, bpat); + + // Saturation. + assert (not x.saturation().empty()); + const DataBlock s = Eigen::Map(& x.saturation()[0], nc, np); + V so = V::Ones(nc, 1); + const V sw = s.col(0); + so -= sw; + state.saturation[0] = ADB::constant(sw, bpat); + state.saturation[1] = ADB::constant(so, bpat); + + // Concentration + assert(not x.concentration().empty()); + const V c = Eigen::Map(&x.concentration()[0], nc); + state.concentration = ADB::constant(c); + + // Well rates. + assert (not xw.wellRates().empty()); + // Need to reshuffle well rates, from ordered by wells, then phase, + // to ordered by phase, then wells. + const int nw = wells_.number_of_wells; + // The transpose() below switches the ordering. + const DataBlock wrates = Eigen::Map(& xw.wellRates()[0], nw, np).transpose(); + const V qs = Eigen::Map(wrates.data(), nw*np); + state.qs = ADB::constant(qs, bpat); + + // Well bottom-hole pressure. + assert (not xw.bhp().empty()); + const V bhp = Eigen::Map(& xw.bhp()[0], xw.bhp().size()); + state.bhp = ADB::constant(bhp, bpat); + + return state; + } + + + + + + FullyImplicitCompressiblePolymerSolver::SolutionState + FullyImplicitCompressiblePolymerSolver::variableState(const PolymerBlackoilState& x, + const WellState& xw) + { + const int nc = grid_.number_of_cells; + const int np = x.numPhases(); + + std::vector vars0; + + // Initial pressure. + assert (not x.pressure().empty()); + const V p = Eigen::Map(& x.pressure()[0], nc, 1); + vars0.push_back(p); + + // Initial saturation. + assert (not x.saturation().empty()); + const DataBlock s = Eigen::Map(& x.saturation()[0], nc, np); + const V sw = s.col(0); + vars0.push_back(sw); + + // Initial concentration. + assert (not x.concentration().empty()); + const V c = Eigen::Map(&x.concentration()[0], nc); + vars0.push_back(c); + + // Initial well rates. + assert (not xw.wellRates().empty()); + // Need to reshuffle well rates, from ordered by wells, then phase, + // to ordered by phase, then wells. + const int nw = wells_.number_of_wells; + // The transpose() below switches the ordering. + const DataBlock wrates = Eigen::Map(& xw.wellRates()[0], nw, np).transpose(); + const V qs = Eigen::Map(wrates.data(), nw*np); + vars0.push_back(qs); + + // Initial well bottom-hole pressure. + assert (not xw.bhp().empty()); + const V bhp = Eigen::Map(& xw.bhp()[0], xw.bhp().size()); + vars0.push_back(bhp); + + std::vector vars = ADB::variables(vars0); + + SolutionState state(np); + + // Pressure. + int nextvar = 0; + state.pressure = vars[ nextvar++ ]; + + // Saturation. + const std::vector& bpat = vars[0].blockPattern(); + { + ADB so = ADB::constant(V::Ones(nc, 1), bpat); + ADB& sw = vars[ nextvar++ ]; + state.saturation[0] = sw; + so = so - sw; + state.saturation[1] = so; + } + + // Concentration. + state.concentration = vars[nextvar++]; + + // Qs. + state.qs = vars[ nextvar++ ]; + + // Bhp. + state.bhp = vars[ nextvar++ ]; + + assert(nextvar == int(vars.size())); + + return state; + } + + + + + + void + FullyImplicitCompressiblePolymerSolver::computeAccum(const SolutionState& state, + const int aix ) + { + + const ADB& press = state.pressure; + const std::vector& sat = state.saturation; + const ADB& c = state.concentration; + const ADB pv_mult = poroMult(press); + + for (int phase = 0; phase < 2; ++phase) { + rq_[phase].b = fluidReciprocFVF(phase, press, cells_); + } + rq_[0].accum[aix] = pv_mult * rq_[0].b * sat[0]; + rq_[1].accum[aix] = pv_mult * rq_[1].b * sat[1]; + rq_[2].accum[aix] = pv_mult * rq_[0].b * sat[0] * c; + + } + + + + ADB + FullyImplicitCompressiblePolymerSolver:: + computeCmax(const ADB& c) const + { + const int nc = c.value().size(); + V cmax(nc); + + for (int i = 0; i < nc; ++i) { + cmax(i) = (cmax(i) > c.value()(i)) ? cmax(i) : c.value()(i); + } + + return ADB::constant(cmax, c.blockPattern()); + } + + void + FullyImplicitCompressiblePolymerSolver:: + assemble(const V& pvdt, + const PolymerBlackoilState& x , + const WellState& xw, + const std::vector& polymer_inflow) + { + // Create the primary variables. + const SolutionState state = variableState(x, xw); + + // -------- Mass balance equations -------- + + // Compute b_p and the accumulation term b_p*s_p for each phase, + // except gas. For gas, we compute b_g*s_g + Rs*b_o*s_o. + // These quantities are stored in rq_[phase].accum[1]. + // The corresponding accumulation terms from the start of + // the timestep (b^0_p*s^0_p etc.) were already computed + // in step() and stored in rq_[phase].accum[0]. + computeAccum(state, 1); + + // Set up the common parts of the mass balance equations + // for each active phase. + const V trans = subset(geo_.transmissibility(), ops_.internal_faces); + const std::vector kr = computeRelPerm(state); + const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); + const ADB cmax = computeCmax(state.concentration); + const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); + const ADB krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, cmax, kr[0], state.saturation[0]); + const ADB mc = computeMc(state); + computeMassFlux(trans, mc, kr[1], krw_eff, state); + const double rho_rock = polymer_props_ad_.rockDensity(); + const V phi = Eigen::Map(&fluid_.porosity()[0], grid_.number_of_cells, 1); + residual_.mass_balance[0] = pvdt*(rq_[0].accum[1] - rq_[0].accum[0]) + + ops_.div*rq_[0].mflux; + residual_.mass_balance[1] = pvdt*(rq_[1].accum[1] - rq_[1].accum[0]) + + ops_.div*rq_[1].mflux; + residual_.mass_balance[2] = pvdt*(rq_[2].accum[1] - rq_[2].accum[0]) * (1. - dead_pore_vol) + + pvdt * rho_rock * (1. - phi) / phi * ads + + ops_.div*rq_[2].mflux; + + + // -------- Extra (optional) sg or rs equation, and rs contributions to the mass balance equations -------- + + // Add the extra (flux) terms to the gas mass balance equations + // from gas dissolved in the oil phase. + // The extra terms in the accumulation part of the equation are already handled. + + // -------- Well equation, and well contributions to the mass balance equations -------- + + // Contribution to mass balance will have to wait. + + const int nc = grid_.number_of_cells; + const int np = wells_.number_of_phases; + const int nw = wells_.number_of_wells; + const int nperf = wells_.well_connpos[nw]; + + const std::vector well_cells(wells_.well_cells, wells_.well_cells + nperf); + const V transw = Eigen::Map(wells_.WI, nperf); + + const ADB& bhp = state.bhp; + + const DataBlock well_s = wops_.w2p * Eigen::Map(wells_.comp_frac, nw, np).matrix(); + + // Extract variables for perforation cell pressures + // and corresponding perforation well pressures. + const ADB p_perfcell = subset(state.pressure, well_cells); + // Finally construct well perforation pressures and well flows. + + // Compute well pressure differentials. + // Construct pressure difference vector for wells. + const int dim = grid_.dimensions; + const double* g = geo_.gravity(); + if (g) { + // Guard against gravity in anything but last dimension. + for (int dd = 0; dd < dim - 1; ++dd) { + assert(g[dd] == 0.0); + } + } + ADB cell_rho_total = ADB::constant(V::Zero(nc), state.pressure.blockPattern()); + for (int phase = 0; phase < 2; ++phase) { + const ADB cell_rho = fluidDensity(phase, state.pressure, cells_); + cell_rho_total += state.saturation[phase] * cell_rho; + } + ADB inj_rho_total = ADB::constant(V::Zero(nperf), state.pressure.blockPattern()); + assert(np == wells_.number_of_phases); + const DataBlock compi = Eigen::Map(wells_.comp_frac, nw, np); + for (int phase = 0; phase < 2; ++phase) { + const ADB cell_rho = fluidDensity(phase, state.pressure, cells_); + const V fraction = compi.col(phase); + inj_rho_total += (wops_.w2p * fraction.matrix()).array() * subset(cell_rho, well_cells); + } + const V rho_perf_cell = subset(cell_rho_total, well_cells).value(); + const V rho_perf_well = inj_rho_total.value(); + V prodperfs = V::Constant(nperf, -1.0); + for (int w = 0; w < nw; ++w) { + if (wells_.type[w] == PRODUCER) { + std::fill(prodperfs.data() + wells_.well_connpos[w], + prodperfs.data() + wells_.well_connpos[w+1], 1.0); + } + } + const Selector producer(prodperfs); + const V rho_perf = producer.select(rho_perf_cell, rho_perf_well); + const V well_perf_dp = computePerfPress(grid_, wells_, rho_perf, g ? g[dim-1] : 0.0); + + const ADB p_perfwell = wops_.w2p * bhp + well_perf_dp; + const ADB nkgradp_well = transw * (p_perfcell - p_perfwell); + // DUMP(nkgradp_well); + const Selector cell_to_well_selector(nkgradp_well.value()); + ADB well_rates_all = ADB::constant(V::Zero(nw*np), state.bhp.blockPattern()); + ADB perf_total_mob = subset(rq_[0].mob, well_cells) + subset(rq_[1].mob, well_cells); + std::vector well_contribs(np, ADB::null()); + std::vector well_perf_rates(np, ADB::null()); + for (int phase = 0; phase < np; ++phase) { + const ADB& cell_b = rq_[phase].b; + const ADB perf_b = subset(cell_b, well_cells); + const ADB& cell_mob = rq_[phase].mob; + const V well_fraction = compi.col(phase); + // Using total mobilities for all phases for injection. + const ADB perf_mob_injector = (wops_.w2p * well_fraction.matrix()).array() * perf_total_mob; + const ADB perf_mob = producer.select(subset(cell_mob, well_cells), + perf_mob_injector); + const ADB perf_flux = perf_mob * (nkgradp_well); // No gravity term for perforations. + well_perf_rates[phase] = (perf_flux*perf_b); + const ADB well_rates = wops_.p2w * well_perf_rates[phase]; + well_rates_all += superset(well_rates, Span(nw, 1, phase*nw), nw*np); + + // const ADB well_contrib = superset(perf_flux*perf_b, well_cells, nc); + well_contribs[phase] = superset(perf_flux*perf_b, well_cells, nc); + // DUMP(well_contribs[phase]); + residual_.mass_balance[phase] += well_contribs[phase]; + } + + // well rates contribs to polymer mass balance eqn. + // for injection wells. + const V polyin = Eigen::Map(& polymer_inflow[0], nc); + const V poly_in_perf = subset(polyin, well_cells); + const V poly_c_cell = subset(state.concentration, well_cells).value(); + const V poly_c = producer.select(poly_c_cell, poly_in_perf); + residual_.mass_balance[2] += superset(well_perf_rates[0] * poly_c, well_cells, nc); + // Set the well flux equation + residual_.well_flux_eq = state.qs + well_rates_all; + // DUMP(residual_.well_flux_eq); + + // Handling BHP and SURFACE_RATE wells. + V bhp_targets(nw); + V rate_targets(nw); + M rate_distr(nw, np*nw); + for (int w = 0; w < nw; ++w) { + const WellControls* wc = wells_.ctrls[w]; + if (wc->type[wc->current] == BHP) { + bhp_targets[w] = wc->target[wc->current]; + rate_targets[w] = -1e100; + } else if (wc->type[wc->current] == SURFACE_RATE) { + bhp_targets[w] = -1e100; + rate_targets[w] = wc->target[wc->current]; + for (int phase = 0; phase < np; ++phase) { + rate_distr.insert(w, phase*nw + w) = wc->distr[phase]; + } + } else { + OPM_THROW(std::runtime_error, "Can only handle BHP and SURFACE_RATE type controls."); + } + } + const ADB bhp_residual = bhp - bhp_targets; + const ADB rate_residual = rate_distr * state.qs - rate_targets; + // Choose bhp residual for positive bhp targets. + Selector bhp_selector(bhp_targets); + residual_.well_eq = bhp_selector.select(bhp_residual, rate_residual); + // DUMP(residual_.well_eq); + } + + + + + + V FullyImplicitCompressiblePolymerSolver::solveJacobianSystem() const + { + ADB mass_res = vertcat(residual_.mass_balance[0], residual_.mass_balance[1]); + mass_res = vertcat(mass_res, residual_.mass_balance[2]); + const ADB well_res = vertcat(residual_.well_flux_eq, residual_.well_eq); + const ADB total_residual = collapseJacs(vertcat(mass_res, well_res)); + // DUMP(total_residual); + + const Eigen::SparseMatrix matr = total_residual.derivative()[0]; + + V dx(V::Zero(total_residual.size())); + Opm::LinearSolverInterface::LinearSolverReport rep + = linsolver_.solve(matr.rows(), matr.nonZeros(), + matr.outerIndexPtr(), matr.innerIndexPtr(), matr.valuePtr(), + total_residual.value().data(), dx.data()); + if (!rep.converged) { + OPM_THROW(std::runtime_error, + "FullyImplicitCompressiblePolymerSolver::solveJacobianSystem(): " + "Linear solver convergence failure."); + } + return dx; + } + + + + + + namespace { + struct Chop01 { + double operator()(double x) const { return std::max(std::min(x, 1.0), 0.0); } + }; + } + + + + + + void FullyImplicitCompressiblePolymerSolver::updateState(const V& dx, + PolymerBlackoilState& state, + WellState& well_state) const + { + const int np = fluid_.numPhases(); + const int nc = grid_.number_of_cells; + const int nw = wells_.number_of_wells; + const V one = V::Constant(nc, 1.0); + const V zero = V::Zero(nc); + // Extract parts of dx corresponding to each part. + const V dp = subset(dx, Span(nc)); + int varstart = nc; + const V dsw =subset(dx, Span(nc, 1, varstart)); + varstart += dsw.size(); + const V dc = subset(dx, Span(nc, 1, varstart)); + varstart += dc.size(); + const V dqs = subset(dx, Span(np*nw, 1, varstart)); + varstart += dqs.size(); + const V dbhp = subset(dx, Span(nw, 1, varstart)); + varstart += dbhp.size(); + assert(varstart == dx.size()); + + // Pressure update. + const double dpmaxrel = 0.8; + const V p_old = Eigen::Map(&state.pressure()[0], nc, 1); + const V absdpmax = dpmaxrel*p_old.abs(); + const V dp_limited = sign(dp) * dp.abs().min(absdpmax); + const V p = (p_old - dp_limited).max(zero); + std::copy(&p[0], &p[0] + nc, state.pressure().begin()); + + // Saturation updates. + const double dsmax = 0.3; + const DataBlock s_old = Eigen::Map(& state.saturation()[0], nc, np); + V so = one; + const V sw_old = s_old.col(0); + const V dsw_limited = sign(dsw) * dsw.abs().min(dsmax); + const V sw = (sw_old - dsw_limited).unaryExpr(Chop01()); + so -= sw; + for (int c = 0; c < nc; ++c) { + state.saturation()[c*np] = sw[c]; + state.saturation()[c*np + 1] = so[c]; + } + + // Concentration updates. + const V c_old = Eigen::Map(&state.concentration()[0], nc); + const V c = c_old - dc; + std::copy(&c[0], &c[0] + nc, state.concentration().begin()); + + // Qs update. + // Since we need to update the wellrates, that are ordered by wells, + // from dqs which are ordered by phase, the simplest is to compute + // dwr, which is the data from dqs but ordered by wells. + const DataBlock wwr = Eigen::Map(dqs.data(), np, nw).transpose(); + const V dwr = Eigen::Map(wwr.data(), nw*np); + const V wr_old = Eigen::Map(&well_state.wellRates()[0], nw*np); + const V wr = wr_old - dwr; + std::copy(&wr[0], &wr[0] + wr.size(), well_state.wellRates().begin()); + + // Bhp update. + const V bhp_old = Eigen::Map(&well_state.bhp()[0], nw, 1); + const V bhp = bhp_old - dbhp; + std::copy(&bhp[0], &bhp[0] + bhp.size(), well_state.bhp().begin()); + + } + + + + + + std::vector + FullyImplicitCompressiblePolymerSolver::computeRelPerm(const SolutionState& state) const + { + const int nc = grid_.number_of_cells; + const std::vector& bpat = state.pressure.blockPattern(); + + const ADB null = ADB::constant(V::Zero(nc, 1), bpat); + + const ADB sw = state.saturation[0]; + const ADB so = state.saturation[1]; + const ADB sg = null; + return fluid_.relperm(sw, so, sg, cells_); + } + + + + + + std::vector + FullyImplicitCompressiblePolymerSolver::computeRelPermWells(const SolutionState& state, + const DataBlock& well_s, + const std::vector& well_cells) const + { + const int nw = wells_.number_of_wells; + const int nperf = wells_.well_connpos[nw]; + const std::vector& bpat = state.pressure.blockPattern(); + + const ADB null = ADB::constant(V::Zero(nperf), bpat); + + const ADB sw = state.saturation[0]; + const ADB so = state.saturation[1]; + const ADB sg = null; + return fluid_.relperm(sw, so, sg, well_cells); + } + + + + + + void + FullyImplicitCompressiblePolymerSolver::computeMassFlux( + const V& transi, + const ADB& mc, + const ADB& kro, + const ADB& krw_eff, + const SolutionState& state ) + { + const ADB tr_mult = transMult(state.pressure); + + const ADB mu_w = fluidViscosity(0, state.pressure, cells_); + ADB inv_wat_eff_vis = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mu_w.value().data()); + rq_[0].mob = tr_mult * krw_eff * inv_wat_eff_vis; + rq_[2].mob = tr_mult * mc * krw_eff * inv_wat_eff_vis; + const ADB mu_o = fluidViscosity(1, state.pressure, cells_); + rq_[1].mob = tr_mult * kro / mu_o; + for (int phase = 0; phase < 2; ++phase) { + const ADB rho = fluidDensity(phase, state.pressure, cells_); + ADB& head = rq_[ phase ].head; + // compute gravity potensial using the face average as in eclipse and MRST + const ADB rhoavg = ops_.caver * rho; + const ADB dp = ops_.ngrad * state.pressure - geo_.gravity()[2] * (rhoavg * (ops_.ngrad * geo_.z().matrix())); + head = transi*dp; + UpwindSelector upwind(grid_, ops_, head.value()); + const ADB& b = rq_[ phase ].b; + const ADB& mob = rq_[ phase ].mob; + rq_[ phase ].mflux = upwind.select(b * mob) * head; + } + rq_[2].b = rq_[0].b; + rq_[2].head = rq_[0].head; + UpwindSelector upwind(grid_, ops_, rq_[2].head.value()); + rq_[2].mflux = upwind.select(rq_[2].b * rq_[2].mob) * rq_[2].head; + } + + + + + + double + FullyImplicitCompressiblePolymerSolver::residualNorm() const + { + double r = 0; + for (std::vector::const_iterator + b = residual_.mass_balance.begin(), + e = residual_.mass_balance.end(); + b != e; ++b) + { + r = std::max(r, (*b).value().matrix().norm()); + } + r = std::max(r, residual_.well_flux_eq.value().matrix().norm()); + r = std::max(r, residual_.well_eq.value().matrix().norm()); + + return r; + } + + + + + + ADB + FullyImplicitCompressiblePolymerSolver::fluidViscosity(const int phase, + const ADB& p , + const std::vector& cells) const + { + const ADB null = ADB::constant(V::Zero(grid_.number_of_cells, 1), p.blockPattern()); + switch (phase) { + case Water: + return fluid_.muWat(p, cells); + case Oil: { + return fluid_.muOil(p, null, cells); + } + default: + OPM_THROW(std::runtime_error, "Unknown phase index " << phase); + } + } + + + + + + ADB + FullyImplicitCompressiblePolymerSolver::fluidReciprocFVF(const int phase, + const ADB& p , + const std::vector& cells) const + { + const ADB null = ADB::constant(V::Zero(grid_.number_of_cells, 1), p.blockPattern()); + switch (phase) { + case Water: + return fluid_.bWat(p, cells); + case Oil: { + return fluid_.bOil(p, null, cells); + } + default: + OPM_THROW(std::runtime_error, "Unknown phase index " << phase); + } + } + + + + + + ADB + FullyImplicitCompressiblePolymerSolver::fluidDensity(const int phase, + const ADB& p , + const std::vector& cells) const + { + const double* rhos = fluid_.surfaceDensity(); + ADB b = fluidReciprocFVF(phase, p, cells); + ADB rho = V::Constant(p.size(), 1, rhos[phase]) * b; + return rho; + } + + + // here mc means m(c) * c. + ADB + FullyImplicitCompressiblePolymerSolver::computeMc(const SolutionState& state) const + { + ADB c = state.concentration; + return polymer_props_ad_.polymerWaterVelocityRatio(c); + } + + + + ADB + FullyImplicitCompressiblePolymerSolver::poroMult(const ADB& p) const + { + const int n = p.size(); + if (rock_comp_props_ && rock_comp_props_->isActive()) { + V pm(n); + V dpm(n); + for (int i = 0; i < n; ++i) { + pm[i] = rock_comp_props_->poroMult(p.value()[i]); + dpm[i] = rock_comp_props_->poroMultDeriv(p.value()[i]); + } + ADB::M dpm_diag = spdiag(dpm); + const int num_blocks = p.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = dpm_diag * p.derivative()[block]; + } + return ADB::function(pm, jacs); + } else { + return ADB::constant(V::Constant(n, 1.0), p.blockPattern()); + } + } + + + + + + ADB + FullyImplicitCompressiblePolymerSolver::transMult(const ADB& p) const + { + const int n = p.size(); + if (rock_comp_props_ && rock_comp_props_->isActive()) { + V tm(n); + V dtm(n); + for (int i = 0; i < n; ++i) { + tm[i] = rock_comp_props_->transMult(p.value()[i]); + dtm[i] = rock_comp_props_->transMultDeriv(p.value()[i]); + } + ADB::M dtm_diag = spdiag(dtm); + const int num_blocks = p.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = dtm_diag * p.derivative()[block]; + } + return ADB::function(tm, jacs); + } else { + return ADB::constant(V::Constant(n, 1.0), p.blockPattern()); + } + } + + +} // namespace Opm diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp new file mode 100644 index 000000000..27f9596c6 --- /dev/null +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp @@ -0,0 +1,229 @@ +/* + Copyright 2013 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_FULLYIMPLICITBLACKOILSOLVER_HEADER_INCLUDED +#define OPM_FULLYIMPLICITBLACKOILSOLVER_HEADER_INCLUDED + +#include +#include +#include +#include +#include + +struct UnstructuredGrid; +struct Wells; + +namespace Opm { + + class DerivedGeology; + class RockCompressibility; + class LinearSolverInterface; + class PolymerBlackoilState; + class WellState; + + /// A fully implicit solver for the black-oil problem. + /// + /// The simulator is capable of handling three-phase problems + /// where gas can be dissolved in oil (but not vice versa). It + /// uses an industry-standard TPFA discretization with per-phase + /// upwind weighting of mobilities. + /// + /// It uses automatic differentiation via the class AutoDiffBlock + /// to simplify assembly of the jacobian matrix. + class FullyImplicitCompressiblePolymerSolver + { + public: + /// Construct a solver. It will retain references to the + /// arguments of this functions, and they are expected to + /// remain in scope for the lifetime of the solver. + /// \param[in] grid grid data structure + /// \param[in] fluid fluid properties + /// \param[in] geo rock properties + /// \param[in] rock_comp_props if non-null, rock compressibility properties + /// \param[in] wells well structure + /// \param[in] linsolver linear solver + FullyImplicitCompressiblePolymerSolver(const UnstructuredGrid& grid , + const BlackoilPropsAdInterface& fluid, + const DerivedGeology& geo , + const RockCompressibility* rock_comp_props, + const PolymerPropsAd& polymer_props_ad, + const Wells& wells, + const LinearSolverInterface& linsolver); + + /// Take a single forward step, modifiying + /// state.pressure() + /// state.faceflux() + /// state.saturation() + /// state.gasoilratio() + /// wstate.bhp() + /// \param[in] dt time step size + /// \param[in] state reservoir state + /// \param[in] wstate well state + void + step(const double dt , + PolymerBlackoilState& state , + WellState& wstate, + const std::vector& polymer_inflow); + + private: + // Types and enums + typedef AutoDiffBlock ADB; + typedef ADB::V V; + typedef ADB::M M; + typedef Eigen::Array DataBlock; + + struct ReservoirResidualQuant { + ReservoirResidualQuant(); + std::vector accum; // Accumulations + ADB mflux; // Mass flux (surface conditions) + ADB b; // Reciprocal FVF + ADB head; // Pressure drop across int. interfaces + ADB mob; // Phase mobility (per cell) + }; + + struct SolutionState { + SolutionState(const int np); + ADB pressure; + std::vector saturation; + ADB concentration; + ADB qs; + ADB bhp; + }; + + struct WellOps { + WellOps(const Wells& wells); + M w2p; // well -> perf (scatter) + M p2w; // perf -> well (gather) + }; + + enum { Water = BlackoilPropsAdInterface::Water, + Oil = BlackoilPropsAdInterface::Oil }; + + // Member data + const UnstructuredGrid& grid_; + const BlackoilPropsAdInterface& fluid_; + const DerivedGeology& geo_; + const RockCompressibility* rock_comp_props_; + const PolymerPropsAd& polymer_props_ad_; + const Wells& wells_; + const LinearSolverInterface& linsolver_; + const std::vector cells_; // All grid cells + HelperOps ops_; + const WellOps wops_; + const M grav_; + + std::vector rq_; + + // The mass_balance vector has one element for each active phase, + // each of which has size equal to the number of cells. + // The well_eq has size equal to the number of wells. + struct { + std::vector mass_balance; + ADB well_flux_eq; + ADB well_eq; + } residual_; + + // Private methods. + SolutionState + constantState(const PolymerBlackoilState& x, + const WellState& xw); + + SolutionState + variableState(const PolymerBlackoilState& x, + const WellState& xw); + + void + computeAccum(const SolutionState& state, + const int aix ); + + void + assemble(const V& pvdt, + const PolymerBlackoilState& x, + const WellState& xw, + const std::vector& polymer_inflow); + + V solveJacobianSystem() const; + + void updateState(const V& dx, + PolymerBlackoilState& state, + WellState& well_state) const; + + std::vector + computeRelPerm(const SolutionState& state) const; + + std::vector + computeRelPermWells(const SolutionState& state, + const DataBlock& well_s, + const std::vector& well_cells) const; + + void + computeMassFlux(const int actph , + const V& transi, + const std::vector& kr , + const SolutionState& state ); + void + computeMassFlux(const V& trans, + const ADB& mc, + const ADB& kro, + const ADB& krw_eff, + const SolutionState& state); + + std::vector + computeFracFlow(const ADB& kro, + const ADB& krw_eff, + const ADB& c) const; + ADB + computeCmax(const ADB& c) const; + ADB + computeMc(const SolutionState& state) const; + ADB + rockPorosity(const ADB& p) const; + ADB + rockPermeability(const ADB& p) const; + double + residualNorm() const; + + ADB + fluidViscosity(const int phase, + const ADB& p , + const std::vector& cells) const; + + ADB + fluidReciprocFVF(const int phase, + const ADB& p , + const std::vector& cells) const; + + ADB + fluidDensity(const int phase, + const ADB& p , + const std::vector& cells) const; + + ADB + poroMult(const ADB& p) const; + + ADB + transMult(const ADB& p) const; + }; +} // namespace Opm + + +#endif // OPM_FULLYIMPLICITBLACKOILSOLVER_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index 87335048e..1e719a8a5 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -698,8 +698,6 @@ namespace { so -= sw; for (int c = 0; c < nc; ++c) { state.saturation()[c*np] = sw[c]; - } - for (int c = 0; c < nc; ++c) { state.saturation()[c*np + 1] = so[c]; } @@ -793,7 +791,7 @@ namespace { V trans(grid_.cell_facepos[nc]); UnstructuredGrid* ug = const_cast(& grid_); tpfa_htrans_compute(ug, fluid_.permeability(), htrans.data()); - tpfa_trans_compute (ug, htrans.data() , trans.data()); + tpfa_trans_compute (ug, htrans.data(), trans.data()); return trans; } diff --git a/opm/polymer/fullyimplicit/GeoProps.hpp b/opm/polymer/fullyimplicit/GeoProps.hpp new file mode 100644 index 000000000..112cbc1de --- /dev/null +++ b/opm/polymer/fullyimplicit/GeoProps.hpp @@ -0,0 +1,110 @@ +/* + Copyright 2013 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_GEOPROPS_HEADER_INCLUDED +#define OPM_GEOPROPS_HEADER_INCLUDED + +#include +#include +#include + +namespace Opm +{ + + /// Class containing static geological properties that are + /// derived from grid and petrophysical properties: + /// - pore volume + /// - transmissibilities + /// - gravity potentials + class DerivedGeology + { + public: + typedef Eigen::ArrayXd Vector; + + /// Construct contained derived geological properties + /// from grid and property information. + template + DerivedGeology(const UnstructuredGrid& grid, + const Props& props , + const double* grav = 0) + : pvol_ (grid.number_of_cells) + , trans_(grid.number_of_faces) + , gpot_ (Vector::Zero(grid.cell_facepos[ grid.number_of_cells ], 1)) + , z_(grid.number_of_cells) + { + // Pore volume + const typename Vector::Index nc = grid.number_of_cells; + std::transform(grid.cell_volumes, grid.cell_volumes + nc, + props.porosity(), pvol_.data(), + std::multiplies()); + + // Transmissibility + Vector htrans(grid.cell_facepos[nc]); + UnstructuredGrid* ug = const_cast(& grid); + tpfa_htrans_compute(ug, props.permeability(), htrans.data()); + tpfa_trans_compute (ug, htrans.data() , trans_.data()); + + // Compute z coordinates + for (int c = 0; c. +*/ + + +#if HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + + +namespace Opm +{ + + class SimulatorFullyImplicitCompressiblePolymer::Impl + { + public: + Impl(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const BlackoilPropsAdInterface& props, + const PolymerPropsAd& polymer_props, + const RockCompressibility* rock_comp_props, + WellsManager& wells_manager, + PolymerInflowInterface& polymer_inflow, + LinearSolverInterface& linsolver, + const double* gravity); + + SimulatorReport run(SimulatorTimer& timer, + PolymerBlackoilState& state, + WellState& well_state); + + private: + // Data. + + // Parameters for output. + bool output_; + bool output_vtk_; + std::string output_dir_; + int output_interval_; + // Parameters for well control + bool check_well_controls_; + int max_well_control_iterations_; + // Observed objects. + const UnstructuredGrid& grid_; + const BlackoilPropsAdInterface& props_; + const PolymerPropsAd& polymer_props_; + const RockCompressibility* rock_comp_props_; + WellsManager& wells_manager_; + const Wells* wells_; + PolymerInflowInterface& polymer_inflow_; + const double* gravity_; + // Solvers + DerivedGeology geo_; + FullyImplicitCompressiblePolymerSolver solver_; + // Misc. data + std::vector allcells_; + }; + + + + + SimulatorFullyImplicitCompressiblePolymer:: + SimulatorFullyImplicitCompressiblePolymer(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const BlackoilPropsAdInterface& props, + const PolymerPropsAd& polymer_props, + const RockCompressibility* rock_comp_props, + WellsManager& wells_manager, + PolymerInflowInterface& polymer_inflow, + LinearSolverInterface& linsolver, + const double* gravity) + + { + pimpl_.reset(new Impl(param, grid, props, polymer_props, rock_comp_props, wells_manager, polymer_inflow, linsolver, gravity)); + } + + + + + + SimulatorReport SimulatorFullyImplicitCompressiblePolymer::run(SimulatorTimer& timer, + PolymerBlackoilState& state, + WellState& well_state) + { + return pimpl_->run(timer, state, well_state); + } + + + + static void outputStateVtk(const UnstructuredGrid& grid, + const Opm::PolymerBlackoilState& state, + const int step, + const std::string& output_dir) + { + // Write data in VTK format. + std::ostringstream vtkfilename; + vtkfilename << output_dir << "/vtk_files"; + boost::filesystem::path fpath(vtkfilename.str()); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + vtkfilename << "/output-" << std::setw(3) << std::setfill('0') << step << ".vtu"; + std::ofstream vtkfile(vtkfilename.str().c_str()); + if (!vtkfile) { + OPM_THROW(std::runtime_error, "Failed to open " << vtkfilename.str()); + } + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + dm["concentration"] = &state.concentration(); + std::vector cell_velocity; + Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); + dm["velocity"] = &cell_velocity; + Opm::writeVtkData(grid, dm, vtkfile); + } + + + static void outputStateMatlab(const UnstructuredGrid& grid, + const Opm::PolymerBlackoilState& state, + const int step, + const std::string& output_dir) + { + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + dm["concentration"] = &state.concentration(); + dm["surfvolume"] = &state.surfacevol(); + std::vector cell_velocity; + Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); + dm["velocity"] = &cell_velocity; + + // Write data (not grid) in Matlab format + for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { + std::ostringstream fname; + fname << output_dir << "/" << it->first; + boost::filesystem::path fpath = fname.str(); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; + std::ofstream file(fname.str().c_str()); + if (!file) { + OPM_THROW(std::runtime_error, "Failed to open " << fname.str()); + } + file.precision(15); + const std::vector& d = *(it->second); + std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); + } + } + static void outputWellStateMatlab(const Opm::WellState& well_state, + const int step, + const std::string& output_dir) + { + Opm::DataMap dm; + dm["bhp"] = &well_state.bhp(); + dm["wellrates"] = &well_state.wellRates(); + + // Write data (not grid) in Matlab format + for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { + std::ostringstream fname; + fname << output_dir << "/" << it->first; + boost::filesystem::path fpath = fname.str(); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error,"Creating directories failed: " << fpath); + } + fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; + std::ofstream file(fname.str().c_str()); + if (!file) { + OPM_THROW(std::runtime_error,"Failed to open " << fname.str()); + } + file.precision(15); + const std::vector& d = *(it->second); + std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); + } + } + +#if 0 + static void outputWaterCut(const Opm::Watercut& watercut, + const std::string& output_dir) + { + // Write water cut curve. + std::string fname = output_dir + "/watercut.txt"; + std::ofstream os(fname.c_str()); + if (!os) { + OPM_THROW(std::runtime_error, "Failed to open " << fname); + } + watercut.write(os); + } + + static void outputWellReport(const Opm::WellReport& wellreport, + const std::string& output_dir) + { + // Write well report. + std::string fname = output_dir + "/wellreport.txt"; + std::ofstream os(fname.c_str()); + if (!os) { + OPM_THROW(std::runtime_error, "Failed to open " << fname); + } + wellreport.write(os); + } +#endif + + + // \TODO: Treat bcs. + SimulatorFullyImplicitCompressiblePolymer::Impl::Impl(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const BlackoilPropsAdInterface& props, + const PolymerPropsAd& polymer_props, + const RockCompressibility* rock_comp_props, + WellsManager& wells_manager, + PolymerInflowInterface& polymer_inflow, + LinearSolverInterface& linsolver, + const double* gravity) + : grid_(grid), + props_(props), + polymer_props_(polymer_props), + rock_comp_props_(rock_comp_props), + wells_manager_(wells_manager), + wells_(wells_manager.c_wells()), + polymer_inflow_(polymer_inflow), + gravity_(gravity), + geo_(grid_, props_, gravity_), + solver_(grid_, props_, geo_, rock_comp_props, polymer_props, *wells_manager.c_wells(), linsolver) + + /* param.getDefault("nl_pressure_residual_tolerance", 0.0), + param.getDefault("nl_pressure_change_tolerance", 1.0), + param.getDefault("nl_pressure_maxiter", 10), + gravity, */ + { + // For output. + output_ = param.getDefault("output", true); + if (output_) { + output_vtk_ = param.getDefault("output_vtk", true); + output_dir_ = param.getDefault("output_dir", std::string("output")); + // Ensure that output dir exists + boost::filesystem::path fpath(output_dir_); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + output_interval_ = param.getDefault("output_interval", 1); + } + + // Well control related init. + check_well_controls_ = param.getDefault("check_well_controls", false); + max_well_control_iterations_ = param.getDefault("max_well_control_iterations", 10); + + // Misc init. + const int num_cells = grid.number_of_cells; + allcells_.resize(num_cells); + for (int cell = 0; cell < num_cells; ++cell) { + allcells_[cell] = cell; + } + } + + + + + SimulatorReport SimulatorFullyImplicitCompressiblePolymer::Impl::run(SimulatorTimer& timer, + PolymerBlackoilState& state, + WellState& well_state) + { + + // Initialisation. + std::vector porevol; + if (rock_comp_props_ && rock_comp_props_->isActive()) { + computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol); + } else { + computePorevolume(grid_, props_.porosity(), porevol); + } + // const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); + std::vector initial_porevol = porevol; + + std::vector polymer_inflow_c(grid_.number_of_cells); + // Main simulation loop. + Opm::time::StopWatch solver_timer; + double stime = 0.0; + Opm::time::StopWatch step_timer; + Opm::time::StopWatch total_timer; + total_timer.start(); +#if 0 + // These must be changed for three-phase. + double init_surfvol[2] = { 0.0 }; + double inplace_surfvol[2] = { 0.0 }; + double tot_injected[2] = { 0.0 }; + double tot_produced[2] = { 0.0 }; + Opm::computeSaturatedVol(porevol, state.surfacevol(), init_surfvol); + Opm::Watercut watercut; + watercut.push(0.0, 0.0, 0.0); + Opm::WellReport wellreport; +#endif + std::vector fractional_flows; + std::vector well_resflows_phase; + if (wells_) { + well_resflows_phase.resize((wells_->number_of_phases)*(wells_->number_of_wells), 0.0); +#if 0 + wellreport.push(props_, *wells_, + state.pressure(), state.surfacevol(), state.saturation(), + 0.0, well_state.bhp(), well_state.perfRates()); +#endif + } + std::fstream tstep_os; + if (output_) { + std::string filename = output_dir_ + "/step_timing.param"; + tstep_os.open(filename.c_str(), std::fstream::out | std::fstream::app); + } + while (!timer.done()) { + // Report timestep and (optionally) write state to disk. + step_timer.start(); + timer.report(std::cout); + if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); + outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); + + } + + SimulatorReport sreport; + + // Solve pressure equation. + // if (check_well_controls_) { + // computeFractionalFlow(props_, allcells_, + // state.pressure(), state.surfacevol(), state.saturation(), + // fractional_flows); + // wells_manager_.applyExplicitReinjectionControls(well_resflows_phase, well_resflows_phase); + // } + bool well_control_passed = !check_well_controls_; + int well_control_iteration = 0; + do { + // Run solver. + const double current_time = timer.currentTime(); + double stepsize = timer.currentStepLength(); + polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c); + solver_timer.start(); + std::vector initial_pressure = state.pressure(); + solver_.step(timer.currentStepLength(), state, well_state, polymer_inflow_c); + + // Stop timer and report. + solver_timer.stop(); + const double st = solver_timer.secsSinceStart(); + std::cout << "Fully implicit solver took: " << st << " seconds." << std::endl; + + stime += st; + sreport.pressure_time = st; + + // Optionally, check if well controls are satisfied. + if (check_well_controls_) { + Opm::computePhaseFlowRatesPerWell(*wells_, + well_state.perfRates(), + fractional_flows, + well_resflows_phase); + std::cout << "Checking well conditions." << std::endl; + // For testing we set surface := reservoir + well_control_passed = wells_manager_.conditionsMet(well_state.bhp(), well_resflows_phase, well_resflows_phase); + ++well_control_iteration; + if (!well_control_passed && well_control_iteration > max_well_control_iterations_) { + OPM_THROW(std::runtime_error, "Could not satisfy well conditions in " << max_well_control_iterations_ << " tries."); + } + if (!well_control_passed) { + std::cout << "Well controls not passed, solving again." << std::endl; + } else { + std::cout << "Well conditions met." << std::endl; + } + } + } while (!well_control_passed); + + // Update pore volumes if rock is compressible. + if (rock_comp_props_ && rock_comp_props_->isActive()) { + initial_porevol = porevol; + computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol); + } + + // The reports below are geared towards two phases only. +#if 0 + // Report mass balances. + double injected[2] = { 0.0 }; + double produced[2] = { 0.0 }; + Opm::computeInjectedProduced(props_, state, transport_src, stepsize, + injected, produced); + Opm::computeSaturatedVol(porevol, state.surfacevol(), inplace_surfvol); + tot_injected[0] += injected[0]; + tot_injected[1] += injected[1]; + tot_produced[0] += produced[0]; + tot_produced[1] += produced[1]; + std::cout.precision(5); + const int width = 18; + std::cout << "\nMass balance report.\n"; + std::cout << " Injected surface volumes: " + << std::setw(width) << injected[0] + << std::setw(width) << injected[1] << std::endl; + std::cout << " Produced surface volumes: " + << std::setw(width) << produced[0] + << std::setw(width) << produced[1] << std::endl; + std::cout << " Total inj surface volumes: " + << std::setw(width) << tot_injected[0] + << std::setw(width) << tot_injected[1] << std::endl; + std::cout << " Total prod surface volumes: " + << std::setw(width) << tot_produced[0] + << std::setw(width) << tot_produced[1] << std::endl; + const double balance[2] = { init_surfvol[0] - inplace_surfvol[0] - tot_produced[0] + tot_injected[0], + init_surfvol[1] - inplace_surfvol[1] - tot_produced[1] + tot_injected[1] }; + std::cout << " Initial - inplace + inj - prod: " + << std::setw(width) << balance[0] + << std::setw(width) << balance[1] + << std::endl; + std::cout << " Relative mass error: " + << std::setw(width) << balance[0]/(init_surfvol[0] + tot_injected[0]) + << std::setw(width) << balance[1]/(init_surfvol[1] + tot_injected[1]) + << std::endl; + std::cout.precision(8); + + // Make well reports. + watercut.push(timer.currentTime() + timer.currentStepLength(), + produced[0]/(produced[0] + produced[1]), + tot_produced[0]/tot_porevol_init); + if (wells_) { + wellreport.push(props_, *wells_, + state.pressure(), state.surfacevol(), state.saturation(), + timer.currentTime() + timer.currentStepLength(), + well_state.bhp(), well_state.perfRates()); + } +#endif + sreport.total_time = step_timer.secsSinceStart(); + if (output_) { + sreport.reportParam(tstep_os); + + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); + outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); +#if 0 + outputWaterCut(watercut, output_dir_); + if (wells_) { + outputWellReport(wellreport, output_dir_); + } +#endif + tstep_os.close(); + } + + // advance to next timestep before reporting at this location + ++timer; + + // write an output file for later inspection + } + + total_timer.stop(); + + SimulatorReport report; + report.pressure_time = stime; + report.transport_time = 0.0; + report.total_time = total_timer.secsSinceStart(); + return report; + } + + +} // namespace Opm diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp new file mode 100644 index 000000000..6698eadaa --- /dev/null +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp @@ -0,0 +1,98 @@ +/* + Copyright 2013 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_SIMULATORFULLYIMPLICITBLACKOIL_HEADER_INCLUDED +#define OPM_SIMULATORFULLYIMPLICITBLACKOIL_HEADER_INCLUDED + +#include +#include + +struct UnstructuredGrid; +struct Wells; + +namespace Opm +{ + namespace parameter { class ParameterGroup; } + class BlackoilPropsAdInterface; + class RockCompressibility; + class WellsManager; + class LinearSolverInterface; + class SimulatorTimer; + class PolymerBlackoilState; + class WellState; + class PolymerPropsAd; + class PolymerInflowInterface; + struct SimulatorReport; + + /// Class collecting all necessary components for a two-phase simulation. + class SimulatorFullyImplicitCompressiblePolymer + { + public: + /// Initialise from parameters and objects to observe. + /// \param[in] param parameters, this class accepts the following: + /// parameter (default) effect + /// ----------------------------------------------------------- + /// output (true) write output to files? + /// output_dir ("output") output directoty + /// output_interval (1) output every nth step + /// nl_pressure_residual_tolerance (0.0) pressure solver residual tolerance (in Pascal) + /// nl_pressure_change_tolerance (1.0) pressure solver change tolerance (in Pascal) + /// nl_pressure_maxiter (10) max nonlinear iterations in pressure + /// nl_maxiter (30) max nonlinear iterations in transport + /// nl_tolerance (1e-9) transport solver absolute residual tolerance + /// num_transport_substeps (1) number of transport steps per pressure step + /// use_segregation_split (false) solve for gravity segregation (if false, + /// segregation is ignored). + /// + /// \param[in] grid grid data structure + /// \param[in] props fluid and rock properties + /// \param[in] rock_comp_props if non-null, rock compressibility properties + /// \param[in] well_manager well manager, may manage no (null) wells + /// \param[in] linsolver linear solver + /// \param[in] gravity if non-null, gravity vector + SimulatorFullyImplicitCompressiblePolymer(const parameter::ParameterGroup& param, + const UnstructuredGrid& grid, + const BlackoilPropsAdInterface& props, + const PolymerPropsAd& polymer_props, + const RockCompressibility* rock_comp_props, + WellsManager& wells_manager, + PolymerInflowInterface& polymer_inflow, + LinearSolverInterface& linsolver, + const double* gravity); + + /// Run the simulation. + /// This will run succesive timesteps until timer.done() is true. It will + /// modify the reservoir and well states. + /// \param[in,out] timer governs the requested reporting timesteps + /// \param[in,out] state state of reservoir: pressure, fluxes + /// \param[in,out] well_state state of wells: bhp, perforation rates + /// \return simulation report, with timing data + SimulatorReport run(SimulatorTimer& timer, + PolymerBlackoilState& state, + WellState& well_state); + + private: + class Impl; + // Using shared_ptr instead of scoped_ptr since scoped_ptr requires complete type for Impl. + boost::shared_ptr pimpl_; + }; + +} // namespace Opm + +#endif // OPM_SIMULATORFULLYIMPLICITBLACKOIL_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp index 18f3fcfae..34d19a8ea 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp @@ -146,6 +146,7 @@ namespace Opm Opm::DataMap dm; dm["saturation"] = &state.saturation(); dm["pressure"] = &state.pressure(); + dm["concentration"] = &state.concentration(); std::vector cell_velocity; Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); dm["velocity"] = &cell_velocity; @@ -161,6 +162,7 @@ namespace Opm Opm::DataMap dm; dm["saturation"] = &state.saturation(); dm["pressure"] = &state.pressure(); + dm["concentration"] = &state.concentration(); std::vector cell_velocity; Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); dm["velocity"] = &cell_velocity; From ff91428fe0111b0e4cc8c6360db84614c42c18bc Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 3 Jan 2014 15:27:48 +0800 Subject: [PATCH 20/83] fix bug: check bhp vector is not empty. --- .../fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index 1e719a8a5..cfc500d6e 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -309,7 +309,7 @@ namespace { vars0.push_back(qs); // Initial well bottom hole pressure. - assert (not xw.bhp().size()); + assert (not xw.bhp().empty()); const V bhp = Eigen::Map(& xw.bhp()[0], xw.bhp().size()); vars0.push_back(bhp); From 391287283db51dd5fdb126b4f28fae90ebcdaac2 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 3 Jan 2014 15:35:00 +0800 Subject: [PATCH 21/83] mistake use of typename. --- .../fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index cfc500d6e..b9a9b475d 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -155,7 +155,7 @@ namespace { V pvol(grid_.number_of_cells); // Pore volume - const typename V::Index nc = grid_.number_of_cells; + const V::Index nc = grid_.number_of_cells; V rho = V::Constant(pvol.size(), 1, *fluid_.porosity()); std::transform(grid_.cell_volumes, grid_.cell_volumes + nc, rho.data(), pvol.data(), From 689a3505b7171f7f7648e70e10c2e302a27e9ec3 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 9 Jan 2014 16:29:38 +0800 Subject: [PATCH 22/83] change residual norm from l2 to lp(p can be infinity). --- ...FullyImplicitCompressiblePolymerSolver.cpp | 18 +- .../FullyImplicitTwoPhaseSolver.cpp | 649 ------------------ .../FullyImplicitTwoPhaseSolver.hpp | 127 ---- .../FullyImplicitTwophasePolymerSolver.cpp | 7 +- .../FullyImplicitTwophasePolymerSolver.hpp | 2 - .../SimulatorFullyImplicitTwophase.cpp | 470 ------------- .../SimulatorFullyImplicitTwophase.hpp | 90 --- 7 files changed, 12 insertions(+), 1351 deletions(-) delete mode 100644 opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp delete mode 100644 opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.hpp delete mode 100644 opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.cpp delete mode 100644 opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.hpp diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index 289912d32..672a84077 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -834,10 +834,10 @@ namespace { e = residual_.mass_balance.end(); b != e; ++b) { - r = std::max(r, (*b).value().matrix().norm()); + r = std::max(r, (*b).value().matrix().lpNorm()); } - r = std::max(r, residual_.well_flux_eq.value().matrix().norm()); - r = std::max(r, residual_.well_eq.value().matrix().norm()); + r = std::max(r, residual_.well_flux_eq.value().matrix().lpNorm()); + r = std::max(r, residual_.well_eq.value().matrix().lpNorm()); return r; } @@ -848,8 +848,8 @@ namespace { ADB FullyImplicitCompressiblePolymerSolver::fluidViscosity(const int phase, - const ADB& p , - const std::vector& cells) const + const ADB& p , + const std::vector& cells) const { const ADB null = ADB::constant(V::Zero(grid_.number_of_cells, 1), p.blockPattern()); switch (phase) { @@ -869,8 +869,8 @@ namespace { ADB FullyImplicitCompressiblePolymerSolver::fluidReciprocFVF(const int phase, - const ADB& p , - const std::vector& cells) const + const ADB& p , + const std::vector& cells) const { const ADB null = ADB::constant(V::Zero(grid_.number_of_cells, 1), p.blockPattern()); switch (phase) { @@ -890,8 +890,8 @@ namespace { ADB FullyImplicitCompressiblePolymerSolver::fluidDensity(const int phase, - const ADB& p , - const std::vector& cells) const + const ADB& p , + const std::vector& cells) const { const double* rhos = fluid_.surfaceDensity(); ADB b = fluidReciprocFVF(phase, p, cells); diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp deleted file mode 100644 index d258b4bc3..000000000 --- a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.cpp +++ /dev/null @@ -1,649 +0,0 @@ -/**/ - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -namespace Opm { - -typedef AutoDiffBlock ADB; -typedef ADB::V V; -typedef ADB::M M; -typedef Eigen::Array DataBlock; - -namespace { - - std::vector - buildAllCells(const int nc) - { - std::vector all_cells(nc); - for (int c = 0; c < nc; ++c) { all_cells[c] = c; } - - return all_cells; - } - struct Chop01 { - double operator()(double x) const { return std::max(std::min(x, 1.0), 0.0); } - }; - - - V computePerfPress(const UnstructuredGrid& g, - const Wells& wells, - const V& rho, - const double grav) - { - const int nw = wells.number_of_wells; - const int nperf = wells.well_connpos[nw]; - const int dim = g.dimensions; - - V wdp = V::Zero(nperf, 1); - assert(wdp.size() == rho.size()); - - for (int w = 0; w < nw; ++w) { - const double ref_depth = wells.depth_ref[w]; - for (int j = wells.well_connpos[w]; j < wells.well_connpos[nw + 1]; ++j) { - const int cell = wells.well_cells[j]; - const double cell_depth = g.cell_centroids[dim * cell + dim - 1]; - wdp(j) = rho(j) * grav * (cell_depth - ref_depth); - } - } - - return wdp; - } - -}//anonymous namespace - - - - - - - - - - FullyImplicitTwoPhaseSolver:: - FullyImplicitTwoPhaseSolver(const UnstructuredGrid& grid, - const IncompPropsAdInterface& fluid, - const Wells& wells, - const LinearSolverInterface& linsolver, - const double* gravity) - : grid_ (grid) - , fluid_(fluid) - , wells_(wells) - , linsolver_(linsolver) - , grav_(gravity) - , cells_ (buildAllCells(grid.number_of_cells)) - , ops_(grid) - , wops_(wells) - , mob_ (fluid.numPhases(), ADB::null()) - , residual_ ({ std::vector(fluid.numPhases(), ADB::null()), - ADB::null(), - ADB::null() }) - { - } - - - - FullyImplicitTwoPhaseSolver:: - WellOps::WellOps(const Wells& wells) - : w2p(wells.well_connpos[ wells.number_of_wells ], - wells.number_of_wells) - , p2w(wells.number_of_wells, - wells.well_connpos[ wells.number_of_wells ]) - { - const int nw = wells.number_of_wells; - const int* const wpos = wells.well_connpos; - - typedef Eigen::Triplet Tri; - - std::vector scatter, gather; - scatter.reserve(wpos[nw]); - gather .reserve(wpos[nw]); - - for (int w = 0, i = 0; w < nw; ++w) { - for (; i < wpos[ w + 1 ]; ++i) { - scatter.push_back(Tri(i, w, 1.0)); - gather .push_back(Tri(w, i, 1.0)); - } - } - - w2p.setFromTriplets(scatter.begin(), scatter.end()); - p2w.setFromTriplets(gather .begin(), gather .end()); - } - - - - void - FullyImplicitTwoPhaseSolver:: - step(const double dt, - TwophaseState& x, - const std::vector& src, - WellState& xw) - { - - V pvol(grid_.number_of_cells); - // Pore volume - const typename V::Index nc = grid_.number_of_cells; - V rho = V::Constant(pvol.size(), 1, *fluid_.porosity()); - std::transform(grid_.cell_volumes, grid_.cell_volumes + nc, - rho.data(), pvol.data(), - std::multiplies()); - - const V pvdt = pvol / dt; - - const SolutionState old_state = constantState(x, xw); - const double atol = 1.0e-12; - const double rtol = 5.0e-8; - const int maxit = 15; - - assemble(pvdt, old_state, x, xw, src); - - const double r0 = residualNorm(); - int it = 0; - std::cout << "\nIteration Residual\n" - << std::setw(9) << it << std::setprecision(9) - << std::setw(18) << r0 << std::endl; - bool resTooLarge = r0 > atol; - while (resTooLarge && (it < maxit)) { - const V dx = solveJacobianSystem(); - updateState(dx, x, xw); - - assemble(pvdt, old_state, x, xw, src); - - const double r = residualNorm(); - - resTooLarge = (r > atol) && (r > rtol*r0); - - it += 1; - std::cout << std::setw(9) << it << std::setprecision(9) - << std::setw(18) << r << std::endl; - } - - if (resTooLarge) { - std::cerr << "Failed to compute converged solution in " << it << " iterations. Ignoring!\n"; - // OPM_THROW(std::runtime_error, "Failed to compute converged solution in " << it << " iterations."); - } - } - - - - - - FullyImplicitTwoPhaseSolver::SolutionState::SolutionState(const int np) - : pressure ( ADB::null()) - , saturation (np, ADB::null()) - , bhp ( ADB::null()) - { - } - - - - - - FullyImplicitTwoPhaseSolver::SolutionState - FullyImplicitTwoPhaseSolver::constantState(const TwophaseState& x, - const WellState& xw) - { - const int nc = grid_.number_of_cells; - const int np = x.numPhases(); - std::vector bpat(np ,nc); - bpat.push_back(xw.bhp().size()); - - SolutionState state(np); - - // Pressure. - assert (not x.pressure().empty()); - const V p = Eigen::Map(& x.pressure()[0], nc); - state.pressure = ADB::constant(p, bpat); - - // Saturation. - assert (not x.saturation().empty()); - const DataBlock s_all = Eigen::Map(& x.saturation()[0], nc, np); - for (int phase = 0; phase < np; ++phase) { - state.saturation[phase] = ADB::constant(s_all.col(phase), bpat); - // state.saturation[1] = ADB::constant(s_all.col(1)); - } - - // BHP - assert (not x.bhp().empty()); - const V bhp = Eigen::Map(& xw.bhp()[0], xw.bhp().size()); - state.bhp = ADB::constant(bhp, bpat); - - return state; - } - - - - - - FullyImplicitTwoPhaseSolver::SolutionState - FullyImplicitTwoPhaseSolver::variableState(const TwophaseState& x, - const WellState& xw) - { - const int nc = grid_.number_of_cells; - const int np = x.numPhases(); - - std::vector vars0; - vars0.reserve(np); - - // Initial pressure. - assert (not x.pressure().empty()); - const V p = Eigen::Map(& x.pressure()[0], nc); - vars0.push_back(p); - - // Initial saturation. - assert (not x.saturation().empty()); - const DataBlock s_all = Eigen::Map(& x.saturation()[0], nc, np); - const V sw = s_all.col(0); - vars0.push_back(sw); - - // Initial Bottom-hole Pressure - assert (not xw.bhp().empty()); - const V bhp = Eigen::Map(& xw.bhp()[0], xw.bhp().size()); - vars0.push_back(bhp); - - std::vector vars = ADB::variables(vars0); - - SolutionState state(np); - - // Pressure. - int nextvar = 0; - state.pressure = vars[ nextvar++ ]; - - // Saturation. - const std::vector& bpat = vars[0].blockPattern(); - { - ADB so = ADB::constant(V::Ones(nc, 1), bpat); - ADB& sw = vars[ nextvar++ ]; - state.saturation[0] = sw; - so = so - sw; - state.saturation[1] = so; - } - // Bottom-hole pressure. - state.pressure = vars[ nextvar++ ]; - - assert(nextvar == int(vars.size())); - - return state; - } - - - - - - void - FullyImplicitTwoPhaseSolver:: - assemble(const V& pvdt, - const SolutionState& old_state, - const TwophaseState& x , - const WellState& xw, - const std::vector& src) - { - // Create the primary variables. - const SolutionState state = variableState(x, xw); - - // -------- Mass balance equations -------- - const V trans = subset(transmissibility(), ops_.internal_faces); - const std::vector kr = computeRelPerm(state); - for (int phase = 0; phase < fluid_.numPhases(); ++phase) { - const ADB mflux = computeMassFlux(phase, trans, kr, state); - residual_.mass_balance[phase] = - pvdt*(state.saturation[phase] - old_state.saturation[phase]) - + ops_.div*mflux; - if (not src.empty()) { - ADB source = accumSource(phase, kr, src); - residual_.mass_balance[phase] = residual_.mass_balance[phase] - source; - } - } -#if 0 - // -------- Well equation, and well contributions to the mass balance equations -------- - - // Contribution to mass balance will have to wait. - const int nc = grid_.number_of_cells; - const int np = wells_.number_of_phases; - const int nw = wells_.number_of_wells; - const int nperf = wells_.well_connpos[nw]; - - const std::vector well_cells(wells_.well_cells, wells_.well_cells + nperf); - const V transw = Eigen::Map(wells_.WI, nperf); - - const ADB& bhp = state.bhp; - - const DataBlock well_s = wops_.w2p * Eigen::Map(wells_.comp_frac, nw, np).matrix(); - - // Extract variables for perforation cell pressures - // and corresponding perforation well pressures. - const ADB p_perfcell = subset(state.pressure, well_cells); - // Finally construct well perforation pressures and well flows. - - // Compute well pressure differentials. - // Construct pressure difference vector for wells. - const int dim = grid_.dimensions; - const double* g = gravity(); - if (g) { - // Guard against gravity in anything but last dimension. - for (int dd = 0; dd < dim - 1; ++dd) { - assert(g[dd] == 0.0); - } - } - ADB cell_rho_total = ADB::constant(V::Zero(nc), state.pressure.blockPattern()); - for (int phase = 0; phase < 2; ++phase) { - const ADB cell_rho = fluidDensity(phase, state.pressure); - cell_rho_total += state.saturation[phase] * cell_rho; - } - ADB inj_rho_total = ADB::constant(V::Zero(nperf), state.pressure.blockPattern()); - assert(np == wells_.number_of_phases); - const DataBlock compi = Eigen::Map(wells_.comp_frac, nw, np); - for (int phase = 0; phase < 2; ++phase) { - const ADB cell_rho = fluidDensity(phase, state.pressure); - const V fraction = compi.col(phase); - inj_rho_total += (wops_.w2p * fraction.matrix()).array() * subset(cell_rho, well_cells); - } - const V rho_perf_cell = subset(cell_rho_total, well_cells).value(); - const V rho_perf_well = inj_rho_total.value(); - V prodperfs = V::Constant(nperf, -1.0); - for (int w = 0; w < nw; ++w) { - if (wells_.type[w] == PRODUCER) { - std::fill(prodperfs.data() + wells_.well_connpos[w], - prodperfs.data() + wells_.well_connpos[w+1], 1.0); - } - } - const Selector producer(prodperfs); - const V rho_perf = producer.select(rho_perf_cell, rho_perf_well); - const V well_perf_dp = computePerfPress(grid_, wells_, rho_perf, g ? g[dim-1] : 0.0); - - const ADB p_perfwell = wops_.w2p * bhp + well_perf_dp; - const ADB nkgradp_well = transw * (p_perfcell - p_perfwell); - - const Selector cell_to_well_selector(nkgradp_well.value()); - ADB well_rates_all = ADB::constant(V::Zero(nw*np), state.bhp.blockPattern()); - ADB perf_total_mob = subset(mob_[0], well_cells) - + subset(mob_[1], well_cells); - std::vector well_contribs(np, ADB::null()); - std::vector well_perf_rates(np, ADB::null()); - for (int phase = 0; phase < np; ++phase) { - // const ADB& cell_b = rq_[phase].b; - // const ADB perf_b = subset(cell_b, well_cells); - const ADB& cell_mob = mob_[phase]; - const V well_fraction = compi.col(phase); - // Using total mobilities for all phases for injection. - const ADB perf_mob_injector = (wops_.w2p * well_fraction.matrix()).array() * perf_total_mob; - const ADB perf_mob = producer.select(subset(cell_mob, well_cells), - perf_mob_injector); - const ADB perf_flux = perf_mob * (nkgradp_well); // No gravity term for perforations. - well_contribs[phase] = superset(perf_flux, well_cells, nc); - residual_.mass_balance[phase] += well_contribs[phase]; - } - - - // Handling BHP and SURFACE_RATE wells. - V bhp_targets(nw); - for (int w = 0; w < nw; ++w) { - const WellControls* wc = wells_.ctrls[w]; - if (wc->type[wc->current] == BHP) { - bhp_targets[w] = wc->target[wc->current]; - } else { - OPM_THROW(std::runtime_error, "Can only handle BHP type controls."); - } - } - const ADB bhp_residual = bhp - bhp_targets; - // Choose bhp residual for positive bhp targets. - residual_.well_eq = bhp_residual; -#endif - } - - - - - - - ADB - FullyImplicitTwoPhaseSolver::accumSource(const int phase, - const std::vector& kr, - const std::vector& src) const - { - //extract the source to out and in source. - std::vector outsrc; - std::vector insrc; - std::vector::const_iterator it; - for (it = src.begin(); it != src.end(); ++it) { - if (*it < 0) { - outsrc.push_back(*it); - insrc.push_back(0.0); - } else if (*it > 0) { - insrc.push_back(*it); - outsrc.push_back(0.0); - } else { - outsrc.emplace_back(0); - insrc.emplace_back(0); - } - } - const V source = Eigen::Map(& src[0], grid_.number_of_cells); - const V outSrc = Eigen::Map(& outsrc[0], grid_.number_of_cells); - const V inSrc = Eigen::Map(& insrc[0], grid_.number_of_cells); - - // compute the out-fracflow. - ADB f_out = computeFracFlow(phase); - // compute the in-fracflow. - V f_in; - if (phase == 1) { - f_in = V::Zero(grid_.number_of_cells); - } else if (phase == 0) { - f_in = V::Ones(grid_.number_of_cells); - } - return f_out * outSrc + f_in * inSrc; - } - - - - - - ADB - FullyImplicitTwoPhaseSolver::computeFracFlow(const int phase) const - { - ADB total_mob = mob_[0] + mob_[1]; - ADB f = mob_[phase] / total_mob; - - return f; - } - - - - - - V - FullyImplicitTwoPhaseSolver::solveJacobianSystem() const - { - const int np = fluid_.numPhases(); - if (np != 2) { - OPM_THROW(std::logic_error, "Only two-phase ok in FullyImplicitTwoPhaseSolver."); - } - const ADB mass_res = vertcat(residual_.mass_balance[0], residual_.mass_balance[1]); - const ADB total_res = collapseJacs(vertcat(mass_res, residual_.well_eq)); - - const Eigen::SparseMatrix matr = total_res.derivative()[0]; - V dx(V::Zero(total_res.size())); - Opm::LinearSolverInterface::LinearSolverReport rep - = linsolver_.solve(matr.rows(), matr.nonZeros(), - matr.outerIndexPtr(), matr.innerIndexPtr(), matr.valuePtr(), - total_res.value().data(), dx.data()); - if (!rep.converged) { - OPM_THROW(std::runtime_error, - "FullyImplicitBlackoilSolver::solveJacobianSystem(): " - "Linear solver convergence failure."); - } - return dx; - } - - - - - - void FullyImplicitTwoPhaseSolver::updateState(const V& dx, - TwophaseState& state, - WellState& well_state) const - { - const int np = fluid_.numPhases(); - const int nc = grid_.number_of_cells; - const int nw = wells_.number_of_wells; - const V null; - assert(null.size() == 0); - const V zero = V::Zero(nc); - const V one = V::Constant(nc, 1.0); - - // Extract parts of dx corresponding to each part. - const V dp = subset(dx, Span(nc)); - int varstart = nc; - const V dsw = subset(dx, Span(nc, 1, varstart)); - varstart += dsw.size(); - const V dbhp = subset(dx, Span(nc, 1, varstart)); - varstart += dbhp.size(); - - assert(varstart == dx.size()); - - - // Pressure update. - const V p_old = Eigen::Map(&state.pressure()[0], nc); - const V p = p_old - dp; - std::copy(&p[0], &p[0] + nc, state.pressure().begin()); - - - // Saturation updates. - const double dsmax = 0.3; - const DataBlock s_old = Eigen::Map(& state.saturation()[0], nc, np); - V so = one; - const V sw_old = s_old.col(0); - const V dsw_limited = sign(dsw) * dsw.abs().min(dsmax); - const V sw = (sw_old - dsw_limited).unaryExpr(Chop01()); - so -= sw; - for (int c = 0; c < nc; ++c) { - state.saturation()[c*np] = sw[c]; - } - for (int c = 0; c < nc; ++c) { - state.saturation()[c*np + 1] = so[c]; - } - - // Bhp update. - const V bhp_old = Eigen::Map(&well_state.bhp()[0], nw, 1); - const V bhp = bhp_old - dbhp; - std::copy(&p[0], &p[0] + nc, well_state.bhp().begin()); - } - - - - - - std::vector - FullyImplicitTwoPhaseSolver::computeRelPerm(const SolutionState& state) const - { - - const ADB sw = state.saturation[0]; - const ADB so = state.saturation[1]; - - return fluid_.relperm(sw, so, cells_); - } - - - - - - - - - - - ADB - FullyImplicitTwoPhaseSolver::computeMassFlux(const int phase , - const V& trans , - const std::vector& kr , - const SolutionState& state ) - { -// const ADB tr_mult = transMult(state.pressure); - const double* mus = fluid_.viscosity(); - // ADB& mob = mob_[phase]; - mob_[phase] = kr[phase] / V::Constant(kr[phase].size(), 1, mus[phase]); - // ADB mob = kr[phase] / V::Constant(kr[phase].size(), 1, mus[phase]); - V z(grid_.number_of_cells); - for (int c = 0; c < grid_.number_of_cells; ++c) { - z(c) = grid_.cell_centroids[c * 3 + 2]; - } - const double* grav = gravity(); - const ADB rho = fluidDensity(phase, state.pressure); - const ADB rhoavg = ops_.caver * rho; - const ADB dp = ops_.ngrad * state.pressure;// - grav[2] * (rhoavg * (ops_.ngrad * z.matrix())); - const ADB head = trans * dp; - - UpwindSelector upwind(grid_, ops_, head.value()); - - return upwind.select(mob_[phase]) * head; - } - - - - - - double - FullyImplicitTwoPhaseSolver::residualNorm() const - { - double r = 0; - for (std::vector::const_iterator - b = residual_.mass_balance.begin(), - e = residual_.mass_balance.end(); - b != e; ++b) - { - r = std::max(r, (*b).value().matrix().norm()); - } - - r = std::max(r, residual_.well_eq.value().matrix().norm()); - return r; - } - - - - - - V - FullyImplicitTwoPhaseSolver::transmissibility() const - { - const V::Index nc = grid_.number_of_cells; - V htrans(grid_.cell_facepos[nc]); - V trans(grid_.cell_facepos[nc]); - UnstructuredGrid* ug = const_cast(& grid_); - tpfa_htrans_compute(ug, fluid_.permeability(), htrans.data()); - tpfa_trans_compute (ug, htrans.data() , trans.data()); - - return trans; - } - - ADB - FullyImplicitTwoPhaseSolver::fluidDensity(const int phase, - const ADB& p) const - { - const double* rhos = fluid_.surfaceDensity(); - ADB rho = ADB::constant(V::Constant(grid_.number_of_cells, 1, rhos[phase]), p.blockPattern()); - - return rho; - } - - - - -}//namespace Opm diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.hpp deleted file mode 100644 index 16ad6b37c..000000000 --- a/opm/polymer/fullyimplicit/FullyImplicitTwoPhaseSolver.hpp +++ /dev/null @@ -1,127 +0,0 @@ -/**/ - -#ifndef OPM_FULLYIMPLICITTWOPHASESOLVER_HEADER_INCLUDED -#define OPM_FULLYIMPLICITTWOPHASESOLVER_HEADER_INCLUDED - -#include -#include -#include -#include -#include -#include - -struct UnstructuredGrid; -namespace Opm { -// struct HelperOps; - class LinearSolverInterface; - class TwophaseState; - - - class FullyImplicitTwoPhaseSolver - { - public: - FullyImplicitTwoPhaseSolver(const UnstructuredGrid& grid, - const IncompPropsAdInterface& fluid, - // const Wells& wells, - const LinearSolverInterface& linsolver); - // const double* gravity); - - void step(const double dt, - TwophaseState& state, - const std::vector& src); -// WellState& wstate); - private: - typedef AutoDiffBlock ADB; - typedef ADB::V V; - typedef ADB::M M; - typedef Eigen::Array DataBlock; - struct SolutionState { - SolutionState(const int np); - ADB pressure; - std::vector saturation; - ADB bhp; - }; - /* - struct Source { - Wells& wells; - std::vector src; - } source; - */ - /* - struct WellOps { - WellOps(const Wells& wells); - M w2p; // well->perf - M p2w; // perf->well - }; -*/ - const UnstructuredGrid& grid_; - const IncompPropsAdInterface& fluid_; - // const Wells& wells_; - const LinearSolverInterface& linsolver_; - // const double* grav_; - const std::vector cells_; - HelperOps ops_; - // const WellOps wops_; - - std::vector mob_; - - struct { - std::vector mass_balance; - ADB well_flux_eq; - ADB well_eq; - } residual_; - - - SolutionState - constantState(const TwophaseState& x, - const WellState& xw); - SolutionState - variableState(const TwophaseState& x, - const WellState& xw); - void - assemble(const V& pvdt, - const SolutionState& old_state, - const TwophaseState& x, - const WellState& xw, - const std::vector& src); - V solveJacobianSystem() const; - void updateState(const V& dx, - TwophaseState& x, - WellState& xw)const; - std::vector - computeRelPerm(const SolutionState& state) const; - V - transmissibility() const; - ADB - computeFracFlow(const int phase) const; - ADB - accumSource(const int phase, - const std::vector& kr, - const std::vector& src) const; - ADB - computeMassFlux(const int phase, - const V& trans, - const std::vector& kr, - const SolutionState& state); - double - residualNorm() const; - - ADB - rockPorosity(const ADB& p) const; - ADB - rockPermeability(const ADB& p) const; - const double - fluidDensity(const int phase) const; - ADB - transMult(const ADB& p) const; - - ADB - fluidDensity(const int phase, - const ADB& p) const; - const double* gravity() const { return grav_; } - }; -} // namespace Opm -#endif// OPM_FULLYIMPLICITTWOPHASESOLVER_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index b9a9b475d..d869b3133 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -1,4 +1,3 @@ -/**/ #include @@ -760,11 +759,11 @@ namespace { e = residual_.mass_balance.end(); b != e; ++b) { - r = std::max(r, (*b).value().matrix().norm()); + r = std::max(r, (*b).value().matrix().lpNorm()); } - r = std::max(r, residual_.well_flux_eq.value().matrix().norm()); - r = std::max(r, residual_.well_eq.value().matrix().norm()); + r = std::max(r, residual_.well_flux_eq.value().matrix().lpNorm()); + r = std::max(r, residual_.well_eq.value().matrix().lpNorm()); return r; } diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp index 5e646a269..865332ec1 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp @@ -1,5 +1,3 @@ -/**/ - #ifndef OPM_FULLYIMPLICITTWOPHASEPOLYMERSOLVER_HEADER_INCLUDED #define OPM_FULLYIMPLICITTWOPHASEPOLYMERSOLVER_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.cpp deleted file mode 100644 index be908b0a4..000000000 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.cpp +++ /dev/null @@ -1,470 +0,0 @@ -/* - Copyright 2013 SINTEF ICT, Applied Mathematics. - - This file is part of the Open Porous Media project (OPM). - - OPM is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OPM is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with OPM. If not, see . -*/ - -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - - -#include - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -namespace Opm -{ - - - - class SimulatorFullyImplicitTwophase::Impl - { - public: - Impl(const parameter::ParameterGroup& param, - const UnstructuredGrid& grid, - const IncompPropsAdInterface& props, - WellsManager& wells_manager, - LinearSolverInterface& linsolver, - std::vector& src, - const double* gravity); - - SimulatorReport run(SimulatorTimer& timer, - TwophaseState& state, - std::vector& src, - WellState& well_state); - - private: - - // Parameters for output. - bool output_; - bool output_vtk_; - std::string output_dir_; - int output_interval_; - // Parameters for well control - bool check_well_controls_; - int max_well_control_iterations_; - // Observed objects. - const UnstructuredGrid& grid_; - const IncompPropsAdInterface& props_; - WellsManager& wells_manager_; - const Wells* wells_; - const std::vector& src_; - const double* gravity_; - // Solvers - FullyImplicitTwoPhaseSolver solver_; - // Misc. data - std::vector allcells_; - }; - - - - - SimulatorFullyImplicitTwophase::SimulatorFullyImplicitTwophase(const parameter::ParameterGroup& param, - const UnstructuredGrid& grid, - const IncompPropsAdInterface& props, - WellsManager& wells_manager, - LinearSolverInterface& linsolver, - std::vector& src, - const double* gravity) - { - pimpl_.reset(new Impl(param, grid, props, wells_manager, linsolver, src, gravity)); - } - - - - - - SimulatorReport SimulatorFullyImplicitTwophase::run(SimulatorTimer& timer, - TwophaseState& state, - std::vector& src, - WellState& well_state) - { - return pimpl_->run(timer, state, src, well_state); - } - - - - static void outputStateVtk(const UnstructuredGrid& grid, - const Opm::TwophaseState& state, - const int step, - const std::string& output_dir) - { - // Write data in VTK format. - std::ostringstream vtkfilename; - vtkfilename << output_dir << "/vtk_files"; - boost::filesystem::path fpath(vtkfilename.str()); - try { - create_directories(fpath); - } - catch (...) { - OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); - } - vtkfilename << "/output-" << std::setw(3) << std::setfill('0') << step << ".vtu"; - std::ofstream vtkfile(vtkfilename.str().c_str()); - if (!vtkfile) { - OPM_THROW(std::runtime_error, "Failed to open " << vtkfilename.str()); - } - Opm::DataMap dm; - dm["saturation"] = &state.saturation(); - dm["pressure"] = &state.pressure(); - std::vector cell_velocity; - Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); - dm["velocity"] = &cell_velocity; - Opm::writeVtkData(grid, dm, vtkfile); - } - - - static void outputStateMatlab(const UnstructuredGrid& grid, - const Opm::TwophaseState& state, - const int step, - const std::string& output_dir) - { - Opm::DataMap dm; - dm["saturation"] = &state.saturation(); - dm["pressure"] = &state.pressure(); - std::vector cell_velocity; - Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); - dm["velocity"] = &cell_velocity; - - // Write data (not grid) in Matlab format - for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { - std::ostringstream fname; - fname << output_dir << "/" << it->first; - boost::filesystem::path fpath = fname.str(); - try { - create_directories(fpath); - } - catch (...) { - OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); - } - fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; - std::ofstream file(fname.str().c_str()); - if (!file) { - OPM_THROW(std::runtime_error, "Failed to open " << fname.str()); - } - file.precision(15); - const std::vector& d = *(it->second); - std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); - } - } - - static void outputWellStateMatlab(const Opm::WellState& well_state, - const int step, - const std::string& output_dir) - { - Opm::DataMap dm; - dm["bhp"] = &well_state.bhp(); - dm["wellrates"] = &well_state.wellRates(); - - // Write data (not grid) in Matlab format - for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { - std::ostringstream fname; - fname << output_dir << "/" << it->first; - boost::filesystem::path fpath = fname.str(); - try { - create_directories(fpath); - } - catch (...) { - OPM_THROW(std::runtime_error,"Creating directories failed: " << fpath); - } - fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; - std::ofstream file(fname.str().c_str()); - if (!file) { - OPM_THROW(std::runtime_error,"Failed to open " << fname.str()); - } - file.precision(15); - const std::vector& d = *(it->second); - std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); - } - } - - - - -/* - static void outputWaterCut(const Opm::Watercut& watercut, - const std::string& output_dir) - { - // Write water cut curve. - std::string fname = output_dir + "/watercut.txt"; - std::ofstream os(fname.c_str()); - if (!os) { - OPM_THROW(std::runtime_error, "Failed to open " << fname); - } - watercut.write(os); - } - static void outputWellReport(const Opm::WellReport& wellreport, - const std::string& output_dir) - { - // Write well report. - std::string fname = output_dir + "/wellreport.txt"; - std::ofstream os(fname.c_str()); - if (!os) { - OPM_THROW(std::runtime_error, "Failed to open " << fname); - } - wellreport.write(os); - } - */ - - - - SimulatorFullyImplicitTwophase::Impl::Impl(const parameter::ParameterGroup& param, - const UnstructuredGrid& grid, - const IncompPropsAdInterface& props, - WellsManager& wells_manager, - LinearSolverInterface& linsolver, - std::vector& src, - const double* gravity) - : grid_(grid), - props_(props), - wells_manager_(wells_manager), - wells_ (wells_manager.c_wells()), - src_ (src), - gravity_(gravity), - solver_(grid_, props_, *wells_manager.c_wells(), linsolver, gravity_) - - { - // For output. - output_ = param.getDefault("output", true); - if (output_) { - output_vtk_ = param.getDefault("output_vtk", true); - output_dir_ = param.getDefault("output_dir", std::string("output")); - // Ensure that output dir exists - boost::filesystem::path fpath(output_dir_); - try { - create_directories(fpath); - } - catch (...) { - OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); - } - output_interval_ = param.getDefault("output_interval", 1); - } - // Well control related init. - check_well_controls_ = param.getDefault("check_well_controls", false); - max_well_control_iterations_ = param.getDefault("max_well_control_iterations", 10); - - // Misc init. - const int num_cells = grid.number_of_cells; - allcells_.resize(num_cells); - for (int cell = 0; cell < num_cells; ++cell) { - allcells_[cell] = cell; - } - } - - SimulatorReport SimulatorFullyImplicitTwophase::Impl::run(SimulatorTimer& timer, - TwophaseState& state, - std::vector& src, - WellState& well_state) - { - - // Initialisation. - std::vector porevol; - Opm::computePorevolume(grid_, props_.porosity(), porevol); - - // const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); - std::vector initial_porevol = porevol; - - // Main simulation loop. - Opm::time::StopWatch solver_timer; - double stime = 0.0; - Opm::time::StopWatch step_timer; - Opm::time::StopWatch total_timer; - total_timer.start(); -#if 0 - // These must be changed for three-phase. - double init_surfvol[2] = { 0.0 }; - double inplace_surfvol[2] = { 0.0 }; - double tot_injected[2] = { 0.0 }; - double tot_produced[2] = { 0.0 }; - Opm::computeSaturatedVol(porevol, state.surfacevol(), init_surfvol); - Opm::Watercut watercut; - watercut.push(0.0, 0.0, 0.0); -#endif - std::vector fractional_flows; - std::vector well_resflows_phase; - if (wells_) { - well_resflows_phase.resize((wells_->number_of_phases)*(wells_->number_of_wells), 0.0); - } - std::fstream tstep_os; - if (output_) { - std::string filename = output_dir_ + "/step_timing.param"; - tstep_os.open(filename.c_str(), std::fstream::out | std::fstream::app); - } - while (!timer.done()) { - // Report timestep and (optionally) write state to disk. - step_timer.start(); - timer.report(std::cout); - if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { - if (output_vtk_) { - outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); - } - outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); - outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); - - } - - SimulatorReport sreport; - - bool well_control_passed = !check_well_controls_; - int well_control_iteration = 0; - do { - // Run solver. - solver_timer.start(); - std::vector initial_pressure = state.pressure(); - solver_.step(timer.currentStepLength(), state, src, well_state); - - // Stop timer and report. - solver_timer.stop(); - const double st = solver_timer.secsSinceStart(); - std::cout << "Fully implicit solver took: " << st << " seconds." << std::endl; - - stime += st; - sreport.pressure_time = st; - - // Optionally, check if well controls are satisfied. - if (check_well_controls_) { - Opm::computePhaseFlowRatesPerWell(*wells_, - well_state.perfRates(), - fractional_flows, - well_resflows_phase); - std::cout << "Checking well conditions." << std::endl; - // For testing we set surface := reservoir - well_control_passed = wells_manager_.conditionsMet(well_state.bhp(), well_resflows_phase, well_resflows_phase); - ++well_control_iteration; - if (!well_control_passed && well_control_iteration > max_well_control_iterations_) { - OPM_THROW(std::runtime_error, "Could not satisfy well conditions in " << max_well_control_iterations_ << " tries."); - } - if (!well_control_passed) { - std::cout << "Well controls not passed, solving again." << std::endl; - } else { - std::cout << "Well conditions met." << std::endl; - } - } - } while (!well_control_passed); - - // Update pore volumes if rock is compressible. - initial_porevol = porevol; - - // The reports below are geared towards two phases only. -#if 0 - // Report mass balances. - double injected[2] = { 0.0 }; - double produced[2] = { 0.0 }; - Opm::computeInjectedProduced(props_, state, transport_src, stepsize, - injected, produced); - Opm::computeSaturatedVol(porevol, state.surfacevol(), inplace_surfvol); - tot_injected[0] += injected[0]; - tot_injected[1] += injected[1]; - tot_produced[0] += produced[0]; - tot_produced[1] += produced[1]; - std::cout.precision(5); - const int width = 18; - std::cout << "\nMass balance report.\n"; - std::cout << " Injected surface volumes: " - << std::setw(width) << injected[0] - << std::setw(width) << injected[1] << std::endl; - std::cout << " Produced surface volumes: " - << std::setw(width) << produced[0] - << std::setw(width) << produced[1] << std::endl; - std::cout << " Total inj surface volumes: " - << std::setw(width) << tot_injected[0] - << std::setw(width) << tot_injected[1] << std::endl; - std::cout << " Total prod surface volumes: " - << std::setw(width) << tot_produced[0] - << std::setw(width) << tot_produced[1] << std::endl; - const double balance[2] = { init_surfvol[0] - inplace_surfvol[0] - tot_produced[0] + tot_injected[0], - init_surfvol[1] - inplace_surfvol[1] - tot_produced[1] + tot_injected[1] }; - std::cout << " Initial - inplace + inj - prod: " - << std::setw(width) << balance[0] - << std::setw(width) << balance[1] - << std::endl; - std::cout << " Relative mass error: " - << std::setw(width) << balance[0]/(init_surfvol[0] + tot_injected[0]) - << std::setw(width) << balance[1]/(init_surfvol[1] + tot_injected[1]) - << std::endl; - std::cout.precision(8); - - // Make well reports. - watercut.push(timer.currentTime() + timer.currentStepLength(), - produced[0]/(produced[0] + produced[1]), - tot_produced[0]/tot_porevol_init); - if (wells_) { - wellreport.push(props_, *wells_, - state.pressure(), state.surfacevol(), state.saturation(), - timer.currentTime() + timer.currentStepLength(), - well_state.bhp(), well_state.perfRates()); - } -#endif - sreport.total_time = step_timer.secsSinceStart(); - if (output_) { - sreport.reportParam(tstep_os); - - if (output_vtk_) { - outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); - } - outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); - outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); -#if 0 - outputWaterCut(watercut, output_dir_); - if (wells_) { - outputWellReport(wellreport, output_dir_); - } -#endif - tstep_os.close(); - } - - // advance to next timestep before reporting at this location - ++timer; - - // write an output file for later inspection - } - - total_timer.stop(); - - SimulatorReport report; - report.pressure_time = stime; - report.transport_time = 0.0; - report.total_time = total_timer.secsSinceStart(); - return report; - } - - - - - -} // namespace Opm diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.hpp deleted file mode 100644 index c70a65e77..000000000 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophase.hpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - Copyright 2013 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_SIMULATORFULLYIMPLICITTWOPHASE_HEADER_INCLUDED -#define OPM_SIMULATORFULLYIMPLICITTWOPHASE_HEADER_INCLUDED - -#include -#include - -struct UnstructuredGrid; - -namespace Opm -{ - namespace parameter { class ParameterGroup; } - class IncompPropsAdInterface; - class LinearSolverInterface; - class SimulatorTimer; - class TwophaseState; - class WellsManager; - class WellState; - struct SimulatorReport; - - /// Class collecting all necessary components for a two-phase simulation. - class SimulatorFullyImplicitTwophase - { - public: - /// Initialise from parameters and objects to observe. - /// \param[in] param parameters, this class accepts the following: - /// parameter (default) effect - /// ----------------------------------------------------------- - /// output (true) write output to files? - /// output_dir ("output") output directoty - /// output_interval (1) output every nth step - /// nl_pressure_residual_tolerance (0.0) pressure solver residual tolerance (in Pascal) - /// nl_pressure_change_tolerance (1.0) pressure solver change tolerance (in Pascal) - /// nl_pressure_maxiter (10) max nonlinear iterations in pressure - /// nl_maxiter (30) max nonlinear iterations in transport - /// nl_tolerance (1e-9) transport solver absolute residual tolerance - /// num_transport_substeps (1) number of transport steps per pressure step - /// use_segregation_split (false) solve for gravity segregation (if false, - /// segregation is ignored). - /// - /// \param[in] grid grid data structure - /// \param[in] props fluid and rock properties - /// \param[in] linsolver linear solver - SimulatorFullyImplicitTwophase(const parameter::ParameterGroup& param, - const UnstructuredGrid& grid, - const IncompPropsAdInterface& props, - WellsManager& wells_manager, - LinearSolverInterface& linsolver, - std::vector& src, - const double* gravity); - - /// Run the simulation. - /// This will run succesive timesteps until timer.done() is true. It will - /// modify the reservoir and well states. - /// \param[in,out] timer governs the requested reporting timesteps - /// \param[in,out] state state of reservoir: pressure, fluxes - /// \param[in,out] well_state state of wells: bhp, perforation rates - /// \return simulation report, with timing data - SimulatorReport run(SimulatorTimer& timer, - TwophaseState& state, - std::vector& src, - WellState& well_state); - - private: - class Impl; - // Using shared_ptr instead of scoped_ptr since scoped_ptr requires complete type for Impl. - boost::shared_ptr pimpl_; - }; - -} // namespace Opm - -#endif // OPM_SIMULATORFULLYIMPLICITBLACKOIL_HEADER_INCLUDED From 139bfb7e625ae2604a7546cbcdedb88d11bcaffa Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 10 Jan 2014 14:15:24 +0800 Subject: [PATCH 23/83] refactor well controls use new well controls gourp. --- ...FullyImplicitCompressiblePolymerSolver.cpp | 21 +++++++++++-------- .../FullyImplicitTwophasePolymerSolver.cpp | 19 ++++++++++------- opm/polymer/fullyimplicit/PolymerPropsAd.cpp | 9 +++++--- opm/polymer/fullyimplicit/PolymerPropsAd.hpp | 6 +++++- 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index 672a84077..96ce6bcdc 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -31,7 +31,7 @@ #include #include #include - +#include #include #include #include @@ -590,7 +590,7 @@ namespace { const ADB perf_mob = producer.select(subset(cell_mob, well_cells), perf_mob_injector); const ADB perf_flux = perf_mob * (nkgradp_well); // No gravity term for perforations. - well_perf_rates[phase] = (perf_flux*perf_b); + well_perf_rates[phase] = (perf_flux * perf_b); const ADB well_rates = wops_.p2w * well_perf_rates[phase]; well_rates_all += superset(well_rates, Span(nw, 1, phase*nw), nw*np); @@ -617,15 +617,18 @@ namespace { M rate_distr(nw, np*nw); for (int w = 0; w < nw; ++w) { const WellControls* wc = wells_.ctrls[w]; - if (wc->type[wc->current] == BHP) { - bhp_targets[w] = wc->target[wc->current]; + if (well_controls_get_current_type(wc) == BHP) { + bhp_targets[w] = well_controls_get_current_target(wc); rate_targets[w] = -1e100; - } else if (wc->type[wc->current] == SURFACE_RATE) { + } else if (well_controls_get_current_type(wc) == SURFACE_RATE) { bhp_targets[w] = -1e100; - rate_targets[w] = wc->target[wc->current]; - for (int phase = 0; phase < np; ++phase) { - rate_distr.insert(w, phase*nw + w) = wc->distr[phase]; - } + rate_targets[w] = well_controls_get_current_target(wc); + { + const double* distr = well_controls_get_current_distr(wc); + for (int phase = 0; phase < np; ++phase) { + rate_distr.insert(w, phase*nw + w) = distr[phase]; + } + } } else { OPM_THROW(std::runtime_error, "Can only handle BHP and SURFACE_RATE type controls."); } diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index d869b3133..98a78deee 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -15,7 +15,7 @@ #include #include #include - +#include #include #include #include @@ -499,15 +499,18 @@ namespace { M rate_distr(nw, np*nw); for (int w = 0; w < nw; ++w) { const WellControls* wc = wells_.ctrls[w]; - if (wc->type[wc->current] == BHP) { - bhp_targets[w] = wc->target[wc->current]; + if (well_controls_get_current_type(wc) == BHP) { + bhp_targets[w] = well_controls_get_current_target(wc); rate_targets[w] = -1e100; - } else if (wc->type[wc->current] == SURFACE_RATE) { + } else if (well_controls_get_current_type(wc) == SURFACE_RATE) { bhp_targets[w] = -1e100; - rate_targets[w] = wc->target[wc->current]; - for (int phase = 0; phase < np; ++phase) { - rate_distr.insert(w, phase*nw + w) = wc->distr[phase]; - } + rate_targets[w] = well_controls_get_current_target(wc); + { + const double* distr = well_controls_get_current_distr(wc); + for (int phase = 0; phase < np; ++phase) { + rate_distr.insert(w, phase*nw + w) = distr[phase]; + } + } } else { OPM_THROW(std::runtime_error, "Can only handle BHP type controls."); } diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp index 37ce0cfb0..89b657f17 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp @@ -1,4 +1,3 @@ - #include #include #include @@ -170,6 +169,8 @@ namespace Opm { } + + double PolymerPropsAd::deadPoreVol() const { @@ -360,8 +361,10 @@ namespace Opm { double res_factor = polymer_props_.resFactor(); double factor = (res_factor - 1.) / max_ads; ADB rk = one + ads * factor; - ADB dkrw_ds = krw / rk.value(); - ADB dkrw_dc = -krw.value() / (rk.value() * rk.value()) * ads * factor; +// ADB dkrw_ds = krw / rk.value(); + ADB dkrw_ds = krw / rk; +// ADB dkrw_dc = -krw.value() / (rk.value() * rk.value()) * ads * factor; + ADB dkrw_dc = -factor * krw / (rk * rk) * ads ; const int num_blocks = c.numBlocks(); std::vector jacs(num_blocks); diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp index d0d72caaa..f3396bbc5 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp @@ -65,8 +65,10 @@ namespace Opm { */ double rockDensity() const; double deadPoreVol() const; - typedef AutoDiffBlock ADB; + + typedef AutoDiffBlock ADB; typedef ADB::V V; + PolymerPropsAd(const PolymerProperties& polymer_props); ~PolymerPropsAd(); @@ -94,6 +96,8 @@ namespace Opm { ADB effectiveRelPerm(const ADB& c, const ADB& cmax_cells, const ADB& krw, const ADB& sw) const; + + private: const PolymerProperties& polymer_props_; }; From 97f5c5ace5585780873552122763bf6fd86a72f0 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 10 Jan 2014 17:20:08 +0800 Subject: [PATCH 24/83] fix bug when computing the maxconcentration that the cell experienced. --- ...FullyImplicitCompressiblePolymerSolver.cpp | 27 ++++++++++--------- ...FullyImplicitCompressiblePolymerSolver.hpp | 4 +-- .../FullyImplicitTwophasePolymerSolver.cpp | 25 +++++++++-------- .../FullyImplicitTwophasePolymerSolver.hpp | 8 +++--- 4 files changed, 31 insertions(+), 33 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index 96ce6bcdc..e7f71dbb6 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -297,7 +297,7 @@ namespace { FullyImplicitCompressiblePolymerSolver::SolutionState FullyImplicitCompressiblePolymerSolver::constantState(const PolymerBlackoilState& x, - const WellState& xw) + const WellState& xw) { const int nc = grid_.number_of_cells; const int np = x.numPhases(); @@ -342,7 +342,7 @@ namespace { const int nw = wells_.number_of_wells; // The transpose() below switches the ordering. const DataBlock wrates = Eigen::Map(& xw.wellRates()[0], nw, np).transpose(); - const V qs = Eigen::Map(wrates.data(), nw*np); + const V qs = Eigen::Map(wrates.data(), nw * np); state.qs = ADB::constant(qs, bpat); // Well bottom-hole pressure. @@ -359,7 +359,7 @@ namespace { FullyImplicitCompressiblePolymerSolver::SolutionState FullyImplicitCompressiblePolymerSolver::variableState(const PolymerBlackoilState& x, - const WellState& xw) + const WellState& xw) { const int nc = grid_.number_of_cells; const int np = x.numPhases(); @@ -435,7 +435,7 @@ namespace { void FullyImplicitCompressiblePolymerSolver::computeAccum(const SolutionState& state, - const int aix ) + const int aix ) { const ADB& press = state.pressure; @@ -454,18 +454,18 @@ namespace { - ADB + V FullyImplicitCompressiblePolymerSolver:: - computeCmax(const ADB& c) const + computeCmax(const PolymerBlackoilState& x) const { const int nc = c.value().size(); - V cmax(nc); - - for (int i = 0; i < nc; ++i) { - cmax(i) = (cmax(i) > c.value()(i)) ? cmax(i) : c.value()(i); + const V cmax = Eigen::Map(& x.maxconcentration()[0], nc); + const V c = Eigen::Map(& x.concentration()[0], nc); + for (int i = 0; i < nc; ++i) { + cmax(i) = std::max(cmax(i), c(i)); } - - return ADB::constant(cmax, c.blockPattern()); + + return cmax; } void @@ -493,7 +493,8 @@ namespace { const V trans = subset(geo_.transmissibility(), ops_.internal_faces); const std::vector kr = computeRelPerm(state); const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); - const ADB cmax = computeCmax(state.concentration); + const V cmax_v = computeCmax(x); + const ADB cmax = ADB::constant(cmax_v, state.concentration.blockPattern()); const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); const ADB krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, cmax, kr[0], state.saturation[0]); const ADB mc = computeMc(state); diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp index 27f9596c6..77d1348ec 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp @@ -191,8 +191,8 @@ namespace Opm { computeFracFlow(const ADB& kro, const ADB& krw_eff, const ADB& c) const; - ADB - computeCmax(const ADB& c) const; + V + computeCmax(const PolymerBlackoilState& x) const; ADB computeMc(const SolutionState& state) const; ADB diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index 98a78deee..a1e4fa730 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -1,4 +1,3 @@ - #include #include @@ -304,7 +303,7 @@ namespace { const int nw = wells_.number_of_wells; // The transpose() below switches the ordering. const DataBlock wrates = Eigen::Map(& xw.wellRates()[0], nw, np).transpose(); - const V qs = Eigen::Map(wrates.data(), nw*np); + const V qs = Eigen::Map(wrates.data(), nw * np); vars0.push_back(qs); // Initial well bottom hole pressure. @@ -335,6 +334,7 @@ namespace { // Qs. state.qs = vars[ nextvar++ ]; + // BHP. state.bhp = vars[ nextvar++ ]; assert(nextvar == int(vars.size())); @@ -343,18 +343,18 @@ namespace { } - ADB + V FullyImplicitTwophasePolymerSolver:: - computeCmax(const ADB& c) const + computeCmax(const PolymerState& x) const { const int nc = c.value().size(); - V cmax(nc); - + const V cmax = Eigen::Map(& x.maxconcentration()[0], nc); + const V c = Eigen::Map(& x.concentration()[0], nc); for (int i = 0; i < nc; ++i) { - cmax(i) = (cmax(i) > c.value()(i)) ? cmax(i) : c.value()(i); + cmax(i) = std::max(cmax(i), c(i)); } - return ADB::constant(cmax, c.blockPattern()); + return cmax; } @@ -374,7 +374,8 @@ namespace { const V trans = subset(transmissibility(), ops_.internal_faces); const std::vector kr = computeRelPerm(state); - const ADB cmax = computeCmax(state.concentration); + const V cmax_v = computeCmax(x); + const ADB cmax = ADB::constant(cmax, state.concentration.blockPattern()); const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); const ADB krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, cmax, kr[0], state.saturation[0]); const ADB mc = computeMc(state); @@ -594,7 +595,7 @@ namespace { const V inSrc = Eigen::Map(& insrc[0], grid_.number_of_cells); const V polyin = Eigen::Map(& polymer_inflow_c[0], grid_.number_of_cells); // compute the out-fracflow. - const std::vector f = computeFracFlow(kro, krw_eff, c); + const std::vector f = computeFracFlow(); // compute the in-fracflow. V zero = V::Zero(grid_.number_of_cells); V one = V::Ones(grid_.number_of_cells); @@ -614,9 +615,7 @@ namespace { std::vector - FullyImplicitTwophasePolymerSolver::computeFracFlow(const ADB& kro, - const ADB& krw_eff, - const ADB& c) const + FullyImplicitTwophasePolymerSolver::computeFracFlow() const { ADB total_mob = mob_[0] + mob_[1]; diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp index 865332ec1..1a6c5e438 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp @@ -105,9 +105,7 @@ namespace Opm { std::vector - computeFracFlow(const ADB& kro, - const ADB& krw_eff, - const ADB& c) const; + computeFracFlow() const; double residualNorm() const; ADB @@ -116,8 +114,8 @@ namespace Opm { const std::vector& polymer_inflow_c, const SolutionState& state) const; - ADB - computeCmax(const ADB& c) const; + V + computeCmax(const PolymerState& x) const; ADB computeMc(const SolutionState& state) const; ADB From 65764ce6f21c2ca980651fdef8aee18df4856780 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 10 Jan 2014 17:53:54 +0800 Subject: [PATCH 25/83] add a private member cmax_ to store the max concentration for per cell. --- .../fullyimplicit/.AutoDiffBlock.hpp.swp | Bin 0 -> 16384 bytes .../FullyImplicitCompressiblePolymerSolver.cpp | 16 +++++++--------- .../FullyImplicitCompressiblePolymerSolver.hpp | 6 +++--- .../FullyImplicitTwophasePolymerSolver.cpp | 16 +++++++--------- .../FullyImplicitTwophasePolymerSolver.hpp | 6 +++--- 5 files changed, 20 insertions(+), 24 deletions(-) create mode 100644 opm/polymer/fullyimplicit/.AutoDiffBlock.hpp.swp diff --git a/opm/polymer/fullyimplicit/.AutoDiffBlock.hpp.swp b/opm/polymer/fullyimplicit/.AutoDiffBlock.hpp.swp new file mode 100644 index 0000000000000000000000000000000000000000..5fce83a022031f678262bf8c54008a49d4c0e374 GIT binary patch literal 16384 zcmeHNON<;x8E)(l?0^YMPMr9`Qnb6w&g`xw5*%xl@jmPk-iLN)<5&opnx5*Nvb(!F z)!jRuSOz5=B3`*5aX}z)0x2gR3d#Wq3B&;uBn~`sMhaXxZ~_D0UtQfjGf5o81qpR) zznz(`uD|~J>VH&K*I?z@-VQz4Uh!~!#Pi-f`^$sZPyh5>{!!11RH$;k{~p&ss$0w9 zolmV^So`#o4|ukd7=26nSx0Kq$x~@MkRPe|xTBMZv7G2|65&4oF24tP7*14_gvwW0 zr<-SbQw720Q2V#rqa=AidreWpK*PW^1LwR8Ypd4P&wt|M^!Q^((}m6VhJl8GhJl8G zhJl8GhJl8GhJl8G|8oYiHhGz>HhGz>HhGz>HhGz>HhGz>Hh zGz>HhyaNMb=y^}!{?~XtjrafM`v3l;p7(3uSHLU4*MU9Y4DgE&d){||Zv#W%Jn*M; zp7&$mM}P)afMuWsoC5y%AzcLYvDNE)j!q>(t0Bq&uc$c07Hf#-yjpZpV- zArTKLU2J>&iyJAf1)WGEGHpKlxkbt}9m~u%sp6qJQNvt>Rof{oWFwK0N-1f1T)h^f z0}@as8yP9<3N1=CO>|DNltY$|Gam%W@(u~#muWgDrR-(erd^#$h(mKs1Nk~mGn4yJ zl#I-B6Q}6(3C@yeGbg0a&*Lvop?o;8y-E4_h)grmNv3p6DzIuaa{lpo_=9;sql-%|s&76sKoM$F84Ft+}quv~KNyh0Z82uX-^^3oF*%k`}GnFNG1U z+3)FM3qv{0LT!7**W1;f)4nj4OhpoAUUmhVRodgU(6JFwJyh9PyAez;R!`lf)s+^l zS*+rSzF;BmOYH2^Ps{bXUK!K zRoV=3G)Wo=st0YV)i!n0)Sh^PV%E0tEC~RIG6)xY)OPLS7j56i0^+zMLuu3r`gH=U z8mnvsS4b1s9=n!hvy@SOQY12??$o0T>&XO$%`p?_(EyPKz4LXLM{p|kR@Teoc4f8SnuFL=cs-PQH$k+y;;bl2&zTV=BBjmuyonMl_5O9mAHD&Oyvdza8r_ zjYrBKRRa2>av)N1$aYumFn6?yj zaKi}O=U9yZRu8C)Udp%k3#&jjNQ6P`C`P}IV4)1MR-yT{&6X*Eqo#9%vEu4QWOTBN zuw_GyyyHt3Nb8~C9E1ad!eAH>b+uDn7$YX=e$BC%OzAZ;L5wTWhv1d)^Nz?!>@0yY}6V;7`OlZ0esqZ)>#Qia?1 zL$+@4GRRek^buQ8Mg33>j4+cH#?x7Eao_g1lu~|@%|IMsIHkbDi?qh#5tC#P!i`x9 zLXypva2`#8F!e|$Mxr}IL|ko&O#Kle7I@m@=syrCOb)YzY!t~1UuWp5w5a?A10rs7 zgDOLWOBK&IbgiU~UOKNww;GqqkP$R)<(yLCcpwP1k#6A_MXoh;Y;>O3E>&}q2L-TV zykYw(ldOw*O_`f1&8M8Lj#@*NBGSXrtA4S674dQ=3RDCjptI}Q)a+tX5$pxkV#{EN zK!+R;Wv0!7RVh7d#7;U@(daQ;XsXs+S*6Cj?HP&TO<^6ykYI)-jGbUih5R;cAs$yE zYV&kf&Z;aFsxP{(ab$P15Vrf0&QeDCIsJ^&tQ_y6r*Mzj5WCJR*ouL%FvI0Y4-w>X zG#4QU##d!QH5Haz2Jr5BF#;dj@g?mg5ta`s_QMZExc%IQfy`w&LcW==3?!f`X-FVv4piwGMkfzwP>_Khx24-Q9j=4Y37esN8fWKls0%>@_ zdEy9x3Ke->kwF?35tuGuoq$u{E$-<8N_e8?CVq=ZiW3TvA+acuCUwb>Ti)ii$Ds}~ zG>u<6kB8R!S|kw_>*wi0$+068>7D0+XdhKL5JDK_M|SGYKOVp~0)S2T=?GmKOzN%% ztWD~)DdurL@;O!%6$jiR+_Hnwfd8lfug0mEI0XvJJMhI(pj37*0mJ(mQ8lFtxz} kJzZ5A7{$9*?)`1C?=!PwU+hs~U%rs%Hm~_5K&_MaZ!}ZYBme*a literal 0 HcmV?d00001 diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index e7f71dbb6..c5bf17752 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -175,6 +175,7 @@ namespace { , ops_ (grid) , wops_ (wells) , grav_ (gravityOperator(grid_, ops_, geo_)) + , cmax_(V::Zero(grid.number_of_cells)) , rq_ (fluid.numPhases() + 1) , residual_ ( { std::vector(fluid.numPhases() + 1, ADB::null()), ADB::null(), @@ -454,18 +455,16 @@ namespace { - V + ADB FullyImplicitCompressiblePolymerSolver:: - computeCmax(const PolymerBlackoilState& x) const + computeCmax(const ADB& c) { - const int nc = c.value().size(); - const V cmax = Eigen::Map(& x.maxconcentration()[0], nc); - const V c = Eigen::Map(& x.concentration()[0], nc); + const int nc = grid_.number_of_cells; for (int i = 0; i < nc; ++i) { - cmax(i) = std::max(cmax(i), c(i)); + cmax_(i) = std::max(cmax_(i), c.value()(i)); } - return cmax; + return ADB::constant(cmax_, c.blockPattern()); } void @@ -493,8 +492,7 @@ namespace { const V trans = subset(geo_.transmissibility(), ops_.internal_faces); const std::vector kr = computeRelPerm(state); const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); - const V cmax_v = computeCmax(x); - const ADB cmax = ADB::constant(cmax_v, state.concentration.blockPattern()); + const ADB cmax = computeCmax(state.concentration); const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); const ADB krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, cmax, kr[0], state.saturation[0]); const ADB mc = computeMc(state); diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp index 77d1348ec..c61d46ce4 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp @@ -130,7 +130,7 @@ namespace Opm { HelperOps ops_; const WellOps wops_; const M grav_; - + V cmax_; std::vector rq_; // The mass_balance vector has one element for each active phase, @@ -191,8 +191,8 @@ namespace Opm { computeFracFlow(const ADB& kro, const ADB& krw_eff, const ADB& c) const; - V - computeCmax(const PolymerBlackoilState& x) const; + ADB + computeCmax(const ADB& c); ADB computeMc(const SolutionState& state) const; ADB diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index a1e4fa730..d1c503c02 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -104,6 +104,7 @@ namespace { , ops_(grid) , wops_(wells) , mob_(std::vector(fluid.numPhases() + 1, ADB::null())) + , cmax_(V::Zero(grid.number_of_cells)) , residual_( { std::vector(fluid.numPhases() + 1, ADB::null()), ADB::null(), ADB::null()}) { } @@ -343,18 +344,16 @@ namespace { } - V + ADB FullyImplicitTwophasePolymerSolver:: - computeCmax(const PolymerState& x) const + computeCmax(const ADB& c) { - const int nc = c.value().size(); - const V cmax = Eigen::Map(& x.maxconcentration()[0], nc); - const V c = Eigen::Map(& x.concentration()[0], nc); + const int nc = grid_.number_of_cells; for (int i = 0; i < nc; ++i) { - cmax(i) = std::max(cmax(i), c(i)); + cmax_(i) = std::max(cmax_(i), c.value()(i)); } - return cmax; + return ADB::constant(cmax_, c.blockPattern()); } @@ -374,8 +373,7 @@ namespace { const V trans = subset(transmissibility(), ops_.internal_faces); const std::vector kr = computeRelPerm(state); - const V cmax_v = computeCmax(x); - const ADB cmax = ADB::constant(cmax, state.concentration.blockPattern()); + const ADB cmax = computeCmax(state.concentration); const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); const ADB krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, cmax, kr[0], state.saturation[0]); const ADB mc = computeMc(state); diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp index 1a6c5e438..5bfe7d7c7 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp @@ -61,7 +61,7 @@ namespace Opm { HelperOps ops_; const WellOps wops_; std::vector mob_; - + V cmax_; struct { std::vector mass_balance; ADB well_eq; @@ -114,8 +114,8 @@ namespace Opm { const std::vector& polymer_inflow_c, const SolutionState& state) const; - V - computeCmax(const PolymerState& x) const; + ADB + computeCmax(const ADB& c); ADB computeMc(const SolutionState& state) const; ADB From 6f6a98659511c59a3802d88eb5c01a755bd98d8e Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 14 Jan 2014 12:59:45 +0800 Subject: [PATCH 26/83] fix the bug when compute the adsorption term in the mass conservation equation. --- ...FullyImplicitCompressiblePolymerSolver.cpp | 37 +++++++++++++------ opm/polymer/fullyimplicit/PolymerPropsAd.cpp | 8 +++- opm/polymer/fullyimplicit/PolymerPropsAd.hpp | 2 +- ...ulatorFullyImplicitCompressiblePolymer.cpp | 2 +- 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index c5bf17752..ddbe753c4 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -217,7 +217,6 @@ namespace { const V dx = solveJacobianSystem(); updateState(dx, x, xw); - assemble(pvdt, x, xw, polymer_inflow); const double r = residualNorm(); @@ -449,9 +448,15 @@ namespace { } rq_[0].accum[aix] = pv_mult * rq_[0].b * sat[0]; rq_[1].accum[aix] = pv_mult * rq_[1].b * sat[1]; - rq_[2].accum[aix] = pv_mult * rq_[0].b * sat[0] * c; + const ADB cmax = computeCmax(state.concentration); + const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); + const double rho_rock = polymer_props_ad_.rockDensity(); + const V phi = Eigen::Map(&fluid_.porosity()[0], grid_.number_of_cells, 1); + const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); + rq_[2].accum[aix] = pv_mult * rq_[0].b * sat[0] * c * (1. - dead_pore_vol) + rho_rock * (1. - phi) / phi * ads; } + @@ -460,10 +465,13 @@ namespace { computeCmax(const ADB& c) { const int nc = grid_.number_of_cells; +// const V cmax = Eigen::Map(&state.maxconcentration()[0], nc, 1); +// cmax_ = &cmax; for (int i = 0; i < nc; ++i) { cmax_(i) = std::max(cmax_(i), c.value()(i)); } +// std::copy(&cmax_[0], &cmax_[0] + nc, state.maxconcentration().begin()); return ADB::constant(cmax_, c.blockPattern()); } @@ -491,20 +499,19 @@ namespace { // for each active phase. const V trans = subset(geo_.transmissibility(), ops_.internal_faces); const std::vector kr = computeRelPerm(state); - const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); const ADB cmax = computeCmax(state.concentration); - const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); + // const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); const ADB krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, cmax, kr[0], state.saturation[0]); const ADB mc = computeMc(state); computeMassFlux(trans, mc, kr[1], krw_eff, state); - const double rho_rock = polymer_props_ad_.rockDensity(); - const V phi = Eigen::Map(&fluid_.porosity()[0], grid_.number_of_cells, 1); + // const double rho_rock = polymer_props_ad_.rockDensity(); + // const V phi = Eigen::Map(&fluid_.porosity()[0], grid_.number_of_cells, 1); residual_.mass_balance[0] = pvdt*(rq_[0].accum[1] - rq_[0].accum[0]) + ops_.div*rq_[0].mflux; residual_.mass_balance[1] = pvdt*(rq_[1].accum[1] - rq_[1].accum[0]) + ops_.div*rq_[1].mflux; - residual_.mass_balance[2] = pvdt*(rq_[2].accum[1] - rq_[2].accum[0]) * (1. - dead_pore_vol) - + pvdt * rho_rock * (1. - phi) / phi * ads + // residual_.mass_balance[2] = pvdt*(rq_[2].accum[1] - rq_[2].accum[0]) * (1. - dead_pore_vol) + residual_.mass_balance[2] = pvdt*(rq_[2].accum[1] - rq_[2].accum[0]) + ops_.div*rq_[2].mflux; @@ -675,6 +682,9 @@ namespace { struct Chop01 { double operator()(double x) const { return std::max(std::min(x, 1.0), 0.0); } }; + struct Chop02 { + double operator()(double x) const { return std::max(std::min(x, 1.25), 0.0); } + }; } @@ -713,7 +723,7 @@ namespace { // Saturation updates. const double dsmax = 0.3; - const DataBlock s_old = Eigen::Map(& state.saturation()[0], nc, np); + const DataBlock s_old = Eigen::Map(&state.saturation()[0], nc, np); V so = one; const V sw_old = s_old.col(0); const V dsw_limited = sign(dsw) * dsw.abs().min(dsmax); @@ -725,8 +735,13 @@ namespace { } // Concentration updates. - const V c_old = Eigen::Map(&state.concentration()[0], nc); - const V c = c_old - dc; + const double dcmax = 0.3 * polymer_props_ad_.cMax(); +// std::cout << "\n the max concentration: " << dcmax / 0.3 << std::endl; + const V c_old = Eigen::Map(&state.concentration()[0], nc, 1); +// const V absdcmax = dcmax*c_old.abs(); + const V dc_limited = sign(dc) * dc.abs().min(dcmax); + const V c = (c_old - dc_limited);//.unaryExpr(Chop02()); + // const V c = (c_old - dc); std::copy(&c[0], &c[0] + nc, state.concentration().begin()); // Qs update. diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp index 89b657f17..1d1e0031a 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp @@ -177,8 +177,12 @@ namespace Opm { return polymer_props_.deadPoreVol(); } - - + double + PolymerPropsAd::cMax() const + { + return polymer_props_.cMax(); + } + PolymerPropsAd::PolymerPropsAd(const PolymerProperties& polymer_props) : polymer_props_ (polymer_props) diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp index f3396bbc5..6f30b220a 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp @@ -65,7 +65,7 @@ namespace Opm { */ double rockDensity() const; double deadPoreVol() const; - + double cMax() const; typedef AutoDiffBlock ADB; typedef ADB::V V; diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp index 48999faa5..992ca7147 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp @@ -235,7 +235,6 @@ namespace Opm } } -#if 0 static void outputWaterCut(const Opm::Watercut& watercut, const std::string& output_dir) { @@ -247,6 +246,7 @@ namespace Opm } watercut.write(os); } +#if 0 static void outputWellReport(const Opm::WellReport& wellreport, const std::string& output_dir) From 5ac5df6b866887b83711dfc75b4013b3ccf14592 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 14 Jan 2014 15:33:32 +0800 Subject: [PATCH 27/83] fix bug when compute the adsorption term in mass balance equation. --- .../fullyimplicit/.AutoDiffBlock.hpp.swp | Bin 16384 -> 0 bytes ...FullyImplicitCompressiblePolymerSolver.cpp | 1 - .../FullyImplicitTwophasePolymerSolver.cpp | 104 +++++++++++------- .../FullyImplicitTwophasePolymerSolver.hpp | 21 +++- 4 files changed, 85 insertions(+), 41 deletions(-) delete mode 100644 opm/polymer/fullyimplicit/.AutoDiffBlock.hpp.swp diff --git a/opm/polymer/fullyimplicit/.AutoDiffBlock.hpp.swp b/opm/polymer/fullyimplicit/.AutoDiffBlock.hpp.swp deleted file mode 100644 index 5fce83a022031f678262bf8c54008a49d4c0e374..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeHNON<;x8E)(l?0^YMPMr9`Qnb6w&g`xw5*%xl@jmPk-iLN)<5&opnx5*Nvb(!F z)!jRuSOz5=B3`*5aX}z)0x2gR3d#Wq3B&;uBn~`sMhaXxZ~_D0UtQfjGf5o81qpR) zznz(`uD|~J>VH&K*I?z@-VQz4Uh!~!#Pi-f`^$sZPyh5>{!!11RH$;k{~p&ss$0w9 zolmV^So`#o4|ukd7=26nSx0Kq$x~@MkRPe|xTBMZv7G2|65&4oF24tP7*14_gvwW0 zr<-SbQw720Q2V#rqa=AidreWpK*PW^1LwR8Ypd4P&wt|M^!Q^((}m6VhJl8GhJl8G zhJl8GhJl8GhJl8G|8oYiHhGz>HhGz>HhGz>HhGz>HhGz>Hh zGz>HhyaNMb=y^}!{?~XtjrafM`v3l;p7(3uSHLU4*MU9Y4DgE&d){||Zv#W%Jn*M; zp7&$mM}P)afMuWsoC5y%AzcLYvDNE)j!q>(t0Bq&uc$c07Hf#-yjpZpV- zArTKLU2J>&iyJAf1)WGEGHpKlxkbt}9m~u%sp6qJQNvt>Rof{oWFwK0N-1f1T)h^f z0}@as8yP9<3N1=CO>|DNltY$|Gam%W@(u~#muWgDrR-(erd^#$h(mKs1Nk~mGn4yJ zl#I-B6Q}6(3C@yeGbg0a&*Lvop?o;8y-E4_h)grmNv3p6DzIuaa{lpo_=9;sql-%|s&76sKoM$F84Ft+}quv~KNyh0Z82uX-^^3oF*%k`}GnFNG1U z+3)FM3qv{0LT!7**W1;f)4nj4OhpoAUUmhVRodgU(6JFwJyh9PyAez;R!`lf)s+^l zS*+rSzF;BmOYH2^Ps{bXUK!K zRoV=3G)Wo=st0YV)i!n0)Sh^PV%E0tEC~RIG6)xY)OPLS7j56i0^+zMLuu3r`gH=U z8mnvsS4b1s9=n!hvy@SOQY12??$o0T>&XO$%`p?_(EyPKz4LXLM{p|kR@Teoc4f8SnuFL=cs-PQH$k+y;;bl2&zTV=BBjmuyonMl_5O9mAHD&Oyvdza8r_ zjYrBKRRa2>av)N1$aYumFn6?yj zaKi}O=U9yZRu8C)Udp%k3#&jjNQ6P`C`P}IV4)1MR-yT{&6X*Eqo#9%vEu4QWOTBN zuw_GyyyHt3Nb8~C9E1ad!eAH>b+uDn7$YX=e$BC%OzAZ;L5wTWhv1d)^Nz?!>@0yY}6V;7`OlZ0esqZ)>#Qia?1 zL$+@4GRRek^buQ8Mg33>j4+cH#?x7Eao_g1lu~|@%|IMsIHkbDi?qh#5tC#P!i`x9 zLXypva2`#8F!e|$Mxr}IL|ko&O#Kle7I@m@=syrCOb)YzY!t~1UuWp5w5a?A10rs7 zgDOLWOBK&IbgiU~UOKNww;GqqkP$R)<(yLCcpwP1k#6A_MXoh;Y;>O3E>&}q2L-TV zykYw(ldOw*O_`f1&8M8Lj#@*NBGSXrtA4S674dQ=3RDCjptI}Q)a+tX5$pxkV#{EN zK!+R;Wv0!7RVh7d#7;U@(daQ;XsXs+S*6Cj?HP&TO<^6ykYI)-jGbUih5R;cAs$yE zYV&kf&Z;aFsxP{(ab$P15Vrf0&QeDCIsJ^&tQ_y6r*Mzj5WCJR*ouL%FvI0Y4-w>X zG#4QU##d!QH5Haz2Jr5BF#;dj@g?mg5ta`s_QMZExc%IQfy`w&LcW==3?!f`X-FVv4piwGMkfzwP>_Khx24-Q9j=4Y37esN8fWKls0%>@_ zdEy9x3Ke->kwF?35tuGuoq$u{E$-<8N_e8?CVq=ZiW3TvA+acuCUwb>Ti)ii$Ds}~ zG>u<6kB8R!S|kw_>*wi0$+068>7D0+XdhKL5JDK_M|SGYKOVp~0)S2T=?GmKOzN%% ztWD~)DdurL@;O!%6$jiR+_Hnwfd8lfug0mEI0XvJJMhI(pj37*0mJ(mQ8lFtxz} kJzZ5A7{$9*?)`1C?=!PwU+hs~U%rs%Hm~_5K&_MaZ!}ZYBme*a diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index ddbe753c4..fa113ae0b 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -510,7 +510,6 @@ namespace { + ops_.div*rq_[0].mflux; residual_.mass_balance[1] = pvdt*(rq_[1].accum[1] - rq_[1].accum[0]) + ops_.div*rq_[1].mflux; - // residual_.mass_balance[2] = pvdt*(rq_[2].accum[1] - rq_[2].accum[0]) * (1. - dead_pore_vol) residual_.mass_balance[2] = pvdt*(rq_[2].accum[1] - rq_[2].accum[0]) + ops_.div*rq_[2].mflux; diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index d1c503c02..9b64b69ec 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -103,8 +103,9 @@ namespace { , cells_ (buildAllCells(grid.number_of_cells)) , ops_(grid) , wops_(wells) - , mob_(std::vector(fluid.numPhases() + 1, ADB::null())) +// , mob_(std::vector(fluid.numPhases() + 1, ADB::null())) , cmax_(V::Zero(grid.number_of_cells)) + , rq_(fluid.numPhases() + 1) , residual_( { std::vector(fluid.numPhases() + 1, ADB::null()), ADB::null(), ADB::null()}) { } @@ -163,6 +164,7 @@ namespace { const V pvdt = pvol / dt; const SolutionState old_state = constantState(x, xw); + computeAccum(old_state, 0); const double atol = 1.0e-12; const double rtol = 5.0e-8; const int maxit = 40; @@ -197,6 +199,15 @@ namespace { + FullyImplicitTwophasePolymerSolver::ReservoirResidualQuant::ReservoirResidualQuant() + : accum(2, ADB::null()) + , mflux( ADB::null()) + , b ( ADB::null()) + , head ( ADB::null()) + , mob ( ADB::null()) + { + } + FullyImplicitTwophasePolymerSolver::SolutionState::SolutionState(const int np) @@ -356,6 +367,25 @@ namespace { return ADB::constant(cmax_, c.blockPattern()); } + void + FullyImplicitTwophasePolymerSolver:: + computeAccum(const SolutionState& state, + const int aix ) + { + + const std::vector& sat = state.saturation; + const ADB& c = state.concentration; + + rq_[0].accum[aix] = sat[0]; + rq_[1].accum[aix] = sat[1]; + const ADB cmax = computeCmax(state.concentration); + const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); + const double rho_rock = polymer_props_ad_.rockDensity(); + const V phi = Eigen::Map(&fluid_.porosity()[0], grid_.number_of_cells, 1); + + const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); + rq_[2].accum[aix] = sat[0] * c * (1. - dead_pore_vol) + rho_rock * (1. - phi) / phi * ads; + } void @@ -368,31 +398,37 @@ namespace { { // Create the primary variables. const SolutionState state = variableState(x, xw); - + computeAccum(state, 1); // -------- Mass balance equations for water and oil -------- const V trans = subset(transmissibility(), ops_.internal_faces); const std::vector kr = computeRelPerm(state); const ADB cmax = computeCmax(state.concentration); - const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); + // const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); const ADB krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, cmax, kr[0], state.saturation[0]); const ADB mc = computeMc(state); - const std::vector mflux = computeMassFlux(trans, mc, kr[1], krw_eff, state); + computeMassFlux(trans, mc, kr[1], krw_eff, state); //const std::vector source = accumSource(kr[1], krw_eff, state.concentration, src, polymer_inflow); // const std::vector source = polymerSource(); - const double rho_r = polymer_props_ad_.rockDensity(); - const V phi = V::Constant(pvdt.size(), 1, *fluid_.porosity()); + // const double rho_r = polymer_props_ad_.rockDensity(); +// const V phi = V::Constant(pvdt.size(), 1, *fluid_.porosity()); - const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); - residual_.mass_balance[0] = pvdt * (state.saturation[0] - old_state.saturation[0]) - + ops_.div * mflux[0]; - residual_.mass_balance[1] = pvdt * (state.saturation[1] - old_state.saturation[1]) - + ops_.div * mflux[1]; +// const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); +// residual_.mass_balance[0] = pvdt * (state.saturation[0] - old_state.saturation[0]) +// + ops_.div * mflux[0]; +// residual_.mass_balance[1] = pvdt * (state.saturation[1] - old_state.saturation[1]) +// + ops_.div * mflux[1]; // Mass balance equation for polymer - residual_.mass_balance[2] = pvdt * (state.saturation[0] * state.concentration - - old_state.saturation[0] * old_state.concentration) * (1. - dead_pore_vol) - + pvdt * rho_r * (1. - phi) / phi * ads - + ops_.div * mflux[2]; +// residual_.mass_balance[2] = pvdt * (state.saturation[0] * state.concentration +// - old_state.saturation[0] * old_state.concentration) * (1. - dead_pore_vol) +// + pvdt * rho_r * (1. - phi) / phi * ads + // + ops_.div * mflux[2]; + residual_.mass_balance[0] = pvdt*(rq_[0].accum[1] - rq_[0].accum[0]) + + ops_.div*rq_[0].mflux; + residual_.mass_balance[1] = pvdt*(rq_[1].accum[1] - rq_[1].accum[0]) + + ops_.div*rq_[1].mflux; + residual_.mass_balance[2] = pvdt*(rq_[2].accum[1] - rq_[2].accum[0]) + + ops_.div*rq_[2].mflux; // -------- Well equation, and well contributions to the mass balance equations -------- @@ -456,14 +492,14 @@ namespace { const Selector cell_to_well_selector(nkgradp_well.value()); ADB well_rates_all = ADB::constant(V::Zero(nw*np), state.bhp.blockPattern()); - ADB perf_total_mob = subset(mob_[0], well_cells) + subset(mob_[1], well_cells); + ADB perf_total_mob = subset(rq_[0].mob, well_cells) + subset(rq_[1].mob, well_cells); std::vector well_contribs(np, ADB::null()); std::vector well_perf_rates(np, ADB::null()); for (int phase = 0; phase < np; ++phase) { // const ADB& cell_b = rq_[phase].b; // const ADB perf_b = subset(cell_b, well_cells); - const ADB& cell_mob = mob_[phase]; + const ADB& cell_mob = rq_[phase].mob; const V well_fraction = compi.col(phase); // Using total mobilities for all phases for injection. const ADB perf_mob_injector = (wops_.w2p * well_fraction.matrix()).array() * perf_total_mob; @@ -523,7 +559,7 @@ namespace { } - std::vector + void FullyImplicitTwophasePolymerSolver::computeMassFlux(const V& trans, const ADB& mc, const ADB& kro, @@ -531,11 +567,10 @@ namespace { const SolutionState& state ) { const double* mus = fluid_.viscosity(); - std::vector mflux; ADB inv_wat_eff_vis = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mus); - mob_[0] = krw_eff * inv_wat_eff_vis; - mob_[1] = kro / V::Constant(kro.size(), 1, mus[1]); - mob_[2] = mc * krw_eff * inv_wat_eff_vis; + rq_[0].mob = krw_eff * inv_wat_eff_vis; + rq_[1].mob = kro / V::Constant(kro.size(), 1, mus[1]); + rq_[2].mob = mc * krw_eff * inv_wat_eff_vis; const int nc = grid_.number_of_cells; @@ -546,23 +581,18 @@ namespace { } for (int phase = 0; phase < 2; ++phase) { const ADB rho = fluidDensity(phase, state.pressure); + ADB& head = rq_[phase].head; const ADB rhoavg = ops_.caver * rho; const ADB dp = ops_.ngrad * state.pressure - gravity_[2] * (rhoavg * (ops_.ngrad * z.matrix())); - const ADB head = trans * dp; + head = trans * dp; UpwindSelector upwind(grid_, ops_, head.value()); - mflux.push_back(upwind.select(mob_[phase])*head); + const ADB& mob = rq_[phase].mob; + rq_[phase].mflux = upwind.select(mob) * head; } - // polymer mass flux. - const ADB rho = fluidDensity(0, state.pressure); - const ADB rhoavg = ops_.caver * rho; - const ADB dp = ops_.ngrad * state.pressure - - gravity_[2] * (rhoavg * (ops_.ngrad * z.matrix())); - const ADB head = trans * dp; - UpwindSelector upwind(grid_, ops_, head.value()); - mflux.push_back(upwind.select(mob_[2])*head); - - return mflux; + rq_[2].head = rq_[0].head; + UpwindSelector upwind(grid_, ops_, rq_[2].head.value()); + rq_[2].mflux = upwind.select(rq_[2].mob) * rq_[2].head; } @@ -615,12 +645,12 @@ namespace { std::vector FullyImplicitTwophasePolymerSolver::computeFracFlow() const { - ADB total_mob = mob_[0] + mob_[1]; + ADB total_mob = rq_[0].mob + rq_[1].mob; std::vector fracflow; - fracflow.push_back(mob_[0] / total_mob); - fracflow.push_back(mob_[1] / total_mob); + fracflow.push_back(rq_[0].mob / total_mob); + fracflow.push_back(rq_[1].mob / total_mob); return fracflow; } diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp index 5bfe7d7c7..0849a07bd 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp @@ -38,6 +38,16 @@ namespace Opm { Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> DataBlock; + + struct ReservoirResidualQuant { + ReservoirResidualQuant(); + std::vector accum; // Accumulations + ADB mflux; // Mass flux (surface conditions) + ADB b; // Reciprocal FVF + ADB head; // Pressure drop across int. interfaces + ADB mob; // Phase mobility (per cell) + }; + struct SolutionState { SolutionState(const int np); ADB pressure; @@ -46,11 +56,13 @@ namespace Opm { ADB qs; ADB bhp; }; + struct WellOps { WellOps(const Wells& wells); M w2p; // well -> perf (scatter) M p2w; // perf -> well (gather) }; + const UnstructuredGrid& grid_; const IncompPropsAdInterface& fluid_; const PolymerPropsAd& polymer_props_ad_; @@ -60,8 +72,8 @@ namespace Opm { const std::vector cells_; HelperOps ops_; const WellOps wops_; - std::vector mob_; - V cmax_; + V cmax_; + std::vector rq_; struct { std::vector mass_balance; ADB well_eq; @@ -89,7 +101,7 @@ namespace Opm { V transmissibility() const; - std::vector + void computeMassFlux(const V& trans, const ADB& mc, const ADB& kro, @@ -116,6 +128,9 @@ namespace Opm { ADB computeCmax(const ADB& c); + void + computeAccum(const SolutionState& state, + const int aix ); ADB computeMc(const SolutionState& state) const; ADB From f7626845646790c9899e71a1115bc20dfa6ed855 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Mon, 20 Jan 2014 16:25:30 +0800 Subject: [PATCH 28/83] computing adsorption using last time step's value of cmax, fix the bug that the front cells' concentrations is negative. --- ...FullyImplicitCompressiblePolymerSolver.cpp | 68 ++++++++++--------- ...FullyImplicitCompressiblePolymerSolver.hpp | 7 +- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index fa113ae0b..d72a3e168 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -194,38 +194,40 @@ namespace { WellState& xw, const std::vector& polymer_inflow) { - const V pvdt = geo_.poreVolume() / dt; - { - const SolutionState state = constantState(x, xw); - computeAccum(state, 0); - } + const SolutionState state = constantState(x, xw); + computeCmax(state.concentration); + computeAccum(state, 0); const double atol = 1.0e-12; const double rtol = 5.0e-8; const int maxit = 15; - assemble(pvdt, x, xw, polymer_inflow); + assemble(dt, x, xw, polymer_inflow); const double r0 = residualNorm(); + const double r_polymer = residual_.mass_balance[2].value().matrix().lpNorm(); int it = 0; - std::cout << "\nIteration Residual\n" + std::cout << "\nIteration Residual Polymer Res\n" << std::setw(9) << it << std::setprecision(9) - << std::setw(18) << r0 << std::endl; + << std::setw(18) << r0 << std::setprecision(9) + << std::setw(18) << r_polymer << std::endl; bool resTooLarge = r0 > atol; while (resTooLarge && (it < maxit)) { const V dx = solveJacobianSystem(); updateState(dx, x, xw); - assemble(pvdt, x, xw, polymer_inflow); + assemble(dt, x, xw, polymer_inflow); const double r = residualNorm(); + const double rr_polymer = residual_.mass_balance[2].value().matrix().lpNorm(); resTooLarge = (r > atol) && (r > rtol*r0); it += 1; std::cout << std::setw(9) << it << std::setprecision(9) - << std::setw(18) << r << std::endl; + << std::setw(18) << r << std::setprecision(9) + << std::setw(18) << rr_polymer << std::endl; } if (resTooLarge) { @@ -244,6 +246,7 @@ namespace { , b ( ADB::null()) , head ( ADB::null()) , mob ( ADB::null()) + , ads (2, ADB::null()) { } @@ -448,7 +451,7 @@ namespace { } rq_[0].accum[aix] = pv_mult * rq_[0].b * sat[0]; rq_[1].accum[aix] = pv_mult * rq_[1].b * sat[1]; - const ADB cmax = computeCmax(state.concentration); + const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern()); const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); const double rho_rock = polymer_props_ad_.rockDensity(); const V phi = Eigen::Map(&fluid_.porosity()[0], grid_.number_of_cells, 1); @@ -460,31 +463,28 @@ namespace { - ADB + void FullyImplicitCompressiblePolymerSolver:: computeCmax(const ADB& c) { const int nc = grid_.number_of_cells; -// const V cmax = Eigen::Map(&state.maxconcentration()[0], nc, 1); -// cmax_ = &cmax; for (int i = 0; i < nc; ++i) { cmax_(i) = std::max(cmax_(i), c.value()(i)); } - -// std::copy(&cmax_[0], &cmax_[0] + nc, state.maxconcentration().begin()); - return ADB::constant(cmax_, c.blockPattern()); + // return ADB::constant(cmax_, c.blockPattern()); + } void FullyImplicitCompressiblePolymerSolver:: - assemble(const V& pvdt, + assemble(const double dt, const PolymerBlackoilState& x , const WellState& xw, const std::vector& polymer_inflow) { // Create the primary variables. const SolutionState state = variableState(x, xw); - + const V pvdt = geo_.poreVolume() / dt; // -------- Mass balance equations -------- // Compute b_p and the accumulation term b_p*s_p for each phase, @@ -499,18 +499,16 @@ namespace { // for each active phase. const V trans = subset(geo_.transmissibility(), ops_.internal_faces); const std::vector kr = computeRelPerm(state); - const ADB cmax = computeCmax(state.concentration); - // const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); +// const ADB cmax = computeCmax(state.concentration); + const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern()); const ADB krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, cmax, kr[0], state.saturation[0]); const ADB mc = computeMc(state); computeMassFlux(trans, mc, kr[1], krw_eff, state); - // const double rho_rock = polymer_props_ad_.rockDensity(); - // const V phi = Eigen::Map(&fluid_.porosity()[0], grid_.number_of_cells, 1); residual_.mass_balance[0] = pvdt*(rq_[0].accum[1] - rq_[0].accum[0]) + ops_.div*rq_[0].mflux; residual_.mass_balance[1] = pvdt*(rq_[1].accum[1] - rq_[1].accum[0]) + ops_.div*rq_[1].mflux; - residual_.mass_balance[2] = pvdt*(rq_[2].accum[1] - rq_[2].accum[0]) + residual_.mass_balance[2] = pvdt*(rq_[2].accum[1] - rq_[2].accum[0]) //+ cell / dt * (rq_[2].ads[1] - rq_[2].ads[0]) + ops_.div*rq_[2].mflux; @@ -608,10 +606,14 @@ namespace { // well rates contribs to polymer mass balance eqn. // for injection wells. const V polyin = Eigen::Map(& polymer_inflow[0], nc); +// std::cout<< "Polymer in flow:" << polyin << std::endl; const V poly_in_perf = subset(polyin, well_cells); - const V poly_c_cell = subset(state.concentration, well_cells).value(); - const V poly_c = producer.select(poly_c_cell, poly_in_perf); - residual_.mass_balance[2] += superset(well_perf_rates[0] * poly_c, well_cells, nc); + //const V poly_c_cell = subset(state.concentration, well_cells).value(); + const V poly_mc_cell = subset(mc, well_cells).value(); + const V poly_in_c = poly_in_perf;// * poly_mc_cell; + const V poly_mc = producer.select(poly_mc_cell, poly_in_c); + + residual_.mass_balance[2] += superset(well_perf_rates[0] * poly_mc, well_cells, nc); // Set the well flux equation residual_.well_flux_eq = state.qs + well_rates_all; // DUMP(residual_.well_flux_eq); @@ -682,7 +684,7 @@ namespace { double operator()(double x) const { return std::max(std::min(x, 1.0), 0.0); } }; struct Chop02 { - double operator()(double x) const { return std::max(std::min(x, 1.25), 0.0); } + double operator()(double x) const { return std::min(x, 1.1*1.25); } }; } @@ -727,6 +729,7 @@ namespace { const V sw_old = s_old.col(0); const V dsw_limited = sign(dsw) * dsw.abs().min(dsmax); const V sw = (sw_old - dsw_limited).unaryExpr(Chop01()); + // const V sw = (sw_old - dsw); so -= sw; for (int c = 0; c < nc; ++c) { state.saturation()[c*np] = sw[c]; @@ -734,13 +737,12 @@ namespace { } // Concentration updates. - const double dcmax = 0.3 * polymer_props_ad_.cMax(); +// const double dcmax = 0.3 * polymer_props_ad_.cMax(); // std::cout << "\n the max concentration: " << dcmax / 0.3 << std::endl; const V c_old = Eigen::Map(&state.concentration()[0], nc, 1); -// const V absdcmax = dcmax*c_old.abs(); - const V dc_limited = sign(dc) * dc.abs().min(dcmax); - const V c = (c_old - dc_limited);//.unaryExpr(Chop02()); - // const V c = (c_old - dc); +// const V dc_limited = sign(dc) * dc.abs().unaryExpr(Chop02()); +// const V c = (c_old - dc_limited).max(zero);//unaryExpr(Chop02()); + const V c = (c_old - dc);//.max(zero); std::copy(&c[0], &c[0] + nc, state.concentration().begin()); // Qs update. diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp index c61d46ce4..d7c7f3195 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp @@ -98,6 +98,7 @@ namespace Opm { ADB b; // Reciprocal FVF ADB head; // Pressure drop across int. interfaces ADB mob; // Phase mobility (per cell) + std::vector ads; // }; struct SolutionState { @@ -130,7 +131,7 @@ namespace Opm { HelperOps ops_; const WellOps wops_; const M grav_; - V cmax_; + V cmax_; std::vector rq_; // The mass_balance vector has one element for each active phase, @@ -156,7 +157,7 @@ namespace Opm { const int aix ); void - assemble(const V& pvdt, + assemble(const double dt, const PolymerBlackoilState& x, const WellState& xw, const std::vector& polymer_inflow); @@ -191,7 +192,7 @@ namespace Opm { computeFracFlow(const ADB& kro, const ADB& krw_eff, const ADB& c) const; - ADB + void computeCmax(const ADB& c); ADB computeMc(const SolutionState& state) const; From 8987a4c1d7bf339ac3a88c4c096ce23d83ad51e4 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 21 Jan 2014 13:45:02 +0800 Subject: [PATCH 29/83] write max concentration to PolymerBlackoilState. --- .../FullyImplicitCompressiblePolymerSolver.cpp | 15 +++++++-------- .../FullyImplicitCompressiblePolymerSolver.hpp | 3 ++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index d72a3e168..c06a283c9 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -196,7 +196,7 @@ namespace { { const SolutionState state = constantState(x, xw); - computeCmax(state.concentration); + computeCmax(x, state.concentration); computeAccum(state, 0); const double atol = 1.0e-12; @@ -457,7 +457,7 @@ namespace { const V phi = Eigen::Map(&fluid_.porosity()[0], grid_.number_of_cells, 1); const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); - rq_[2].accum[aix] = pv_mult * rq_[0].b * sat[0] * c * (1. - dead_pore_vol) + rho_rock * (1. - phi) / phi * ads; + rq_[2].accum[aix] = pv_mult * rq_[0].b * sat[0] * c * (1. - dead_pore_vol) + pv_mult * rho_rock * (1. - phi) / phi * ads; } @@ -465,13 +465,15 @@ namespace { void FullyImplicitCompressiblePolymerSolver:: - computeCmax(const ADB& c) + computeCmax(PolymerBlackoilState& state, + const ADB& c) { const int nc = grid_.number_of_cells; for (int i = 0; i < nc; ++i) { cmax_(i) = std::max(cmax_(i), c.value()(i)); } // return ADB::constant(cmax_, c.blockPattern()); + std::copy(&cmax_[0], &cmax_[0] + nc, state.maxconcentration().begin()); } @@ -683,9 +685,6 @@ namespace { struct Chop01 { double operator()(double x) const { return std::max(std::min(x, 1.0), 0.0); } }; - struct Chop02 { - double operator()(double x) const { return std::min(x, 1.1*1.25); } - }; } @@ -740,9 +739,9 @@ namespace { // const double dcmax = 0.3 * polymer_props_ad_.cMax(); // std::cout << "\n the max concentration: " << dcmax / 0.3 << std::endl; const V c_old = Eigen::Map(&state.concentration()[0], nc, 1); -// const V dc_limited = sign(dc) * dc.abs().unaryExpr(Chop02()); +// const V dc_limited = sign(dc) * dc.abs().min(dcmax); // const V c = (c_old - dc_limited).max(zero);//unaryExpr(Chop02()); - const V c = (c_old - dc);//.max(zero); + const V c = (c_old - dc).max(zero); std::copy(&c[0], &c[0] + nc, state.concentration().begin()); // Qs update. diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp index d7c7f3195..74d4781ab 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp @@ -193,7 +193,8 @@ namespace Opm { const ADB& krw_eff, const ADB& c) const; void - computeCmax(const ADB& c); + computeCmax(PolymerBlackoilState& state, + const ADB& c); ADB computeMc(const SolutionState& state) const; ADB From 12318fe7549ab42ca34ddc58e7a2121c949273c5 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Wed, 22 Jan 2014 14:17:04 +0800 Subject: [PATCH 30/83] output watercut by using utilities.cpp/computeInjectedProduced function. --- examples/sim_poly_fi2p_comp_ad.cpp | 1 - ...FullyImplicitCompressiblePolymerSolver.cpp | 27 ++- ...FullyImplicitCompressiblePolymerSolver.hpp | 6 +- ...ulatorFullyImplicitCompressiblePolymer.cpp | 75 ++++++-- opm/polymer/fullyimplicit/utilities.cpp | 174 ++++++++++++++++++ opm/polymer/fullyimplicit/utilities.hpp | 81 ++++++++ 6 files changed, 334 insertions(+), 30 deletions(-) create mode 100644 opm/polymer/fullyimplicit/utilities.cpp create mode 100644 opm/polymer/fullyimplicit/utilities.hpp diff --git a/examples/sim_poly_fi2p_comp_ad.cpp b/examples/sim_poly_fi2p_comp_ad.cpp index 426e13489..edf682231 100644 --- a/examples/sim_poly_fi2p_comp_ad.cpp +++ b/examples/sim_poly_fi2p_comp_ad.cpp @@ -142,7 +142,6 @@ try bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); const double *grav = use_gravity ? &gravity[0] : 0; - // Linear solver. LinearSolverFactory linsolver(param); diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index c06a283c9..24760f593 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -163,7 +163,8 @@ namespace { const RockCompressibility* rock_comp_props, const PolymerPropsAd& polymer_props_ad, const Wells& wells, - const LinearSolverInterface& linsolver) + const LinearSolverInterface& linsolver + ) : grid_ (grid) , fluid_ (fluid) , geo_ (geo) @@ -192,7 +193,8 @@ namespace { step(const double dt, PolymerBlackoilState& x , WellState& xw, - const std::vector& polymer_inflow) + const std::vector& polymer_inflow, + std::vector& src) { const SolutionState state = constantState(x, xw); @@ -202,8 +204,7 @@ namespace { const double atol = 1.0e-12; const double rtol = 5.0e-8; const int maxit = 15; - - assemble(dt, x, xw, polymer_inflow); + assemble(dt, x, xw, polymer_inflow, src); const double r0 = residualNorm(); const double r_polymer = residual_.mass_balance[2].value().matrix().lpNorm(); @@ -217,7 +218,7 @@ namespace { const V dx = solveJacobianSystem(); updateState(dx, x, xw); - assemble(dt, x, xw, polymer_inflow); + assemble(dt, x, xw, polymer_inflow, src); const double r = residualNorm(); @@ -482,9 +483,11 @@ namespace { assemble(const double dt, const PolymerBlackoilState& x , const WellState& xw, - const std::vector& polymer_inflow) + const std::vector& polymer_inflow, + std::vector& src) { // Create the primary variables. + // const SolutionState state = variableState(x, xw); const V pvdt = geo_.poreVolume() / dt; // -------- Mass balance equations -------- @@ -519,7 +522,6 @@ namespace { // Add the extra (flux) terms to the gas mass balance equations // from gas dissolved in the oil phase. // The extra terms in the accumulation part of the equation are already handled. - // -------- Well equation, and well contributions to the mass balance equations -------- // Contribution to mass balance will have to wait. @@ -528,6 +530,9 @@ namespace { const int np = wells_.number_of_phases; const int nw = wells_.number_of_wells; const int nperf = wells_.well_connpos[nw]; + for (int i = 0; i < nc; ++i) { + src[i] = 0.0; + } const std::vector well_cells(wells_.well_cells, wells_.well_cells + nperf); const V transw = Eigen::Map(wells_.WI, nperf); @@ -603,6 +608,9 @@ namespace { well_contribs[phase] = superset(perf_flux*perf_b, well_cells, nc); // DUMP(well_contribs[phase]); residual_.mass_balance[phase] += well_contribs[phase]; + for (int cell = 0; cell < nc; ++cell) { + src[cell] += well_contribs[phase].value()[cell]; + } } // well rates contribs to polymer mass balance eqn. @@ -647,6 +655,11 @@ namespace { // Choose bhp residual for positive bhp targets. Selector bhp_selector(bhp_targets); residual_.well_eq = bhp_selector.select(bhp_residual, rate_residual); +// for (int i = 0; i < nc; ++i) { +// std::cout << src[i] << " "; +// if ((i+1) % 10 == 0) +// std::cout<& polymer_inflow); + const std::vector& polymer_inflow, + std::vector& src); private: // Types and enums @@ -160,7 +161,8 @@ namespace Opm { assemble(const double dt, const PolymerBlackoilState& x, const WellState& xw, - const std::vector& polymer_inflow); + const std::vector& polymer_inflow, + std::vector& src); V solveJacobianSystem() const; diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp index 992ca7147..23b6363a4 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp @@ -29,7 +29,7 @@ #include #include #include - +#include #include #include #include @@ -162,6 +162,7 @@ namespace Opm Opm::DataMap dm; dm["saturation"] = &state.saturation(); dm["pressure"] = &state.pressure(); + dm["cmax"] = &state.maxconcentration(); dm["concentration"] = &state.concentration(); std::vector cell_velocity; Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); @@ -178,6 +179,7 @@ namespace Opm Opm::DataMap dm; dm["saturation"] = &state.saturation(); dm["pressure"] = &state.pressure(); + dm["cmax"] = &state.maxconcentration(); dm["concentration"] = &state.concentration(); dm["surfvolume"] = &state.surfacevol(); std::vector cell_velocity; @@ -205,6 +207,10 @@ namespace Opm std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); } } +#if 0 + + //well rate should be modified by using effective viscosity + //and effective relperm for water static void outputWellStateMatlab(const Opm::WellState& well_state, const int step, const std::string& output_dir) @@ -234,9 +240,9 @@ namespace Opm std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); } } - - static void outputWaterCut(const Opm::Watercut& watercut, - const std::string& output_dir) +#endif + static void outputWaterCut(const Opm::Watercut& watercut, + const std::string& output_dir) { // Write water cut curve. std::string fname = output_dir + "/watercut.txt"; @@ -244,7 +250,7 @@ namespace Opm if (!os) { OPM_THROW(std::runtime_error, "Failed to open " << fname); } - watercut.write(os); + watercut.write(os); } #if 0 @@ -331,25 +337,26 @@ namespace Opm } else { computePorevolume(grid_, props_.porosity(), porevol); } - // const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); + const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); std::vector initial_porevol = porevol; std::vector polymer_inflow_c(grid_.number_of_cells); + std::vector transport_src(grid_.number_of_cells); // Main simulation loop. Opm::time::StopWatch solver_timer; double stime = 0.0; Opm::time::StopWatch step_timer; Opm::time::StopWatch total_timer; total_timer.start(); + double tot_injected[2] = { 0.0 }; + double tot_produced[2] = { 0.0 }; + Opm::Watercut watercut; + watercut.push(0.0, 0.0, 0.0); #if 0 // These must be changed for three-phase. double init_surfvol[2] = { 0.0 }; double inplace_surfvol[2] = { 0.0 }; - double tot_injected[2] = { 0.0 }; - double tot_produced[2] = { 0.0 }; Opm::computeSaturatedVol(porevol, state.surfacevol(), init_surfvol); - Opm::Watercut watercut; - watercut.push(0.0, 0.0, 0.0); Opm::WellReport wellreport; #endif std::vector fractional_flows; @@ -376,7 +383,7 @@ namespace Opm outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); } outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); - outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); + // outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); } @@ -392,14 +399,15 @@ namespace Opm bool well_control_passed = !check_well_controls_; int well_control_iteration = 0; do { + // Process transport sources (to include bdy terms and well flows). +// Opm::computeTransportSource(props_, wells_, well_state, transport_src); // Run solver. const double current_time = timer.currentTime(); double stepsize = timer.currentStepLength(); polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c); solver_timer.start(); std::vector initial_pressure = state.pressure(); - solver_.step(timer.currentStepLength(), state, well_state, polymer_inflow_c); - + solver_.step(timer.currentStepLength(), state, well_state, polymer_inflow_c, transport_src); // Stop timer and report. solver_timer.stop(); const double st = solver_timer.secsSinceStart(); @@ -428,13 +436,44 @@ namespace Opm } } } while (!well_control_passed); - // Update pore volumes if rock is compressible. if (rock_comp_props_ && rock_comp_props_->isActive()) { initial_porevol = porevol; computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol); } + double injected[2] = { 0.0 }; + double produced[2] = { 0.0 }; + double polyinj = 0; + double polyprod = 0; + + Opm::computeInjectedProduced(props_, polymer_props_, + state, + transport_src, polymer_inflow_c, timer.currentStepLength(), + injected, produced, + polyinj, polyprod); + tot_injected[0] += injected[0]; + tot_injected[1] += injected[1]; + tot_produced[0] += produced[0]; + tot_produced[1] += produced[1]; + watercut.push(timer.currentTime() + timer.currentStepLength(), + produced[0]/(produced[0] + produced[1]), + tot_produced[0]/tot_porevol_init); + std::cout.precision(5); + const int width = 18; + std::cout << "\nMass balance report.\n"; + std::cout << " Injected reservoir volumes: " + << std::setw(width) << injected[0] + << std::setw(width) << injected[1] << std::endl; + std::cout << " Produced reservoir volumes: " + << std::setw(width) << produced[0] + << std::setw(width) << produced[1] << std::endl; + std::cout << " Total inj reservoir volumes: " + << std::setw(width) << tot_injected[0] + << std::setw(width) << tot_injected[1] << std::endl; + std::cout << " Total prod reservoir volumes: " + << std::setw(width) << tot_produced[0] + << std::setw(width) << tot_produced[1] << std::endl; // The reports below are geared towards two phases only. #if 0 // Report mass balances. @@ -443,10 +482,6 @@ namespace Opm Opm::computeInjectedProduced(props_, state, transport_src, stepsize, injected, produced); Opm::computeSaturatedVol(porevol, state.surfacevol(), inplace_surfvol); - tot_injected[0] += injected[0]; - tot_injected[1] += injected[1]; - tot_produced[0] += produced[0]; - tot_produced[1] += produced[1]; std::cout.precision(5); const int width = 18; std::cout << "\nMass balance report.\n"; @@ -493,9 +528,9 @@ namespace Opm outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); } outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); - outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); -#if 0 + // outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); outputWaterCut(watercut, output_dir_); +#if 0 if (wells_) { outputWellReport(wellreport, output_dir_); } diff --git a/opm/polymer/fullyimplicit/utilities.cpp b/opm/polymer/fullyimplicit/utilities.cpp new file mode 100644 index 000000000..cec4ed7df --- /dev/null +++ b/opm/polymer/fullyimplicit/utilities.cpp @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace Opm +{ + + typedef AutoDiffBlock ADB; + typedef ADB::V V; + typedef ADB::M M; + typedef Eigen::Array DataBlock; + /// Compute two-phase transport source terms from well terms. + /// Note: Unlike the incompressible version of this function, + /// this version computes surface volume injection rates, + /// production rates are still total reservoir volumes. + /// \param[in] props Fluid and rock properties. + /// \param[in] wells Wells data structure. + /// \param[in] well_state Well pressures and fluxes. + /// \param[out] transport_src The transport source terms. They are to be interpreted depending on sign: + /// (+) positive inflow of first (water) phase (reservoir volume), + /// (-) negative total outflow of both phases (reservoir volume). + void computeTransportSource(const BlackoilPropsAdInterface& props, + const Wells* wells, + const WellState& well_state, + std::vector& transport_src) + { + int nc = props.numCells(); + transport_src.clear(); + transport_src.resize(nc, 0.0); + // Well contributions. + if (wells) { + const int nw = wells->number_of_wells; + const int np = wells->number_of_phases; + if (np != 2) { + OPM_THROW(std::runtime_error, "computeTransportSource() requires a 2 phase case."); + } + std::vector A(np*np); + for (int w = 0; w < nw; ++w) { + const double* comp_frac = wells->comp_frac + np*w; + for (int perf = wells->well_connpos[w]; perf < wells->well_connpos[w + 1]; ++perf) { + const int perf_cell = wells->well_cells[perf]; + double perf_rate = well_state.perfRates()[perf]; + if (perf_rate > 0.0) { + // perf_rate is a total inflow reservoir rate, we want a surface water rate. + if (wells->type[w] != INJECTOR) { + std::cout << "**** Warning: crossflow in well " + << w << " perf " << perf - wells->well_connpos[w] + << " ignored. Reservoir rate was " + << perf_rate/Opm::unit::day << " m^3/day." << std::endl; + perf_rate = 0.0; + } else { + assert(std::fabs(comp_frac[0] + comp_frac[1] - 1.0) < 1e-6); + perf_rate *= comp_frac[0]; // Water reservoir volume rate. + } + } + transport_src[perf_cell] += perf_rate; + } + } + } + } + + + /// @brief Computes injected and produced volumes of all phases, + /// and injected and produced polymer mass - in the compressible case. + /// Note 1: assumes that only the first phase is injected. + /// Note 2: assumes that transport has been done with an + /// implicit method, i.e. that the current state + /// gives the mobilities used for the preceding timestep. + /// @param[in] props fluid and rock properties. + /// @param[in] polyprops polymer properties + /// @param[in] state state variables (pressure, fluxes etc.) + /// @param[in] transport_src if < 0: total reservoir volume outflow, + /// if > 0: first phase *surface volume* inflow. + /// @param[in] inj_c injected concentration by cell + /// @param[in] dt timestep used + /// @param[out] injected must point to a valid array with P elements, + /// where P = s.size()/transport_src.size(). + /// @param[out] produced must also point to a valid array with P elements. + /// @param[out] polyinj injected mass of polymer + /// @param[out] polyprod produced mass of polymer + void computeInjectedProduced(const BlackoilPropsAdInterface& props, + const Opm::PolymerPropsAd& polymer_props, + const PolymerBlackoilState& state, + const std::vector& transport_src, + const std::vector& inj_c, + const double dt, + double* injected, + double* produced, + double& polyinj, + double& polyprod) + { + const int num_cells = transport_src.size(); + if (props.numCells() != num_cells) { + OPM_THROW(std::runtime_error, "Size of transport_src vector does not match number of cells in props."); + } + const int np = props.numPhases(); + if (int(state.saturation().size()) != num_cells*np) { + OPM_THROW(std::runtime_error, "Sizes of state vectors do not match number of cells."); + } + std::vector cells(num_cells); + const V p = Eigen::Map(&state.pressure()[0], num_cells, 1); + const DataBlock s = Eigen::Map(&state.saturation()[0], num_cells, np); + const V sw = s.col(0); + const V so = s.col(1); + const V c = Eigen::Map(&state.concentration()[0], num_cells, 1); + const V cmax = Eigen::Map(&state.maxconcentration()[0], num_cells, 1); + const V trans_src = Eigen::Map(&transport_src[0], num_cells, 1); + V src = V::Constant(num_cells, -1.0); // negative is injec, positive is producer. + for (int cell = 0; cell < num_cells; ++cell) { + cells[cell] = cell; + if(transport_src[cell] > 0.0) { + src[cell] = 1.0; + } + } + const Selector src_selector(src); + const V one = V::Constant(num_cells, 1.0); + const V zero = V::Zero(num_cells); + const std::vector kr = props.relperm(sw, so, zero, cells); + const V muw = props.muWat(p, cells); + const V muo = props.muOil(p, zero, cells); + const V krw_eff = polymer_props.effectiveRelPerm(c, cmax, kr[0]); + const V inv_muw_eff = polymer_props.effectiveInvWaterVisc(c, muw.data()); + std::vector mob(np); + mob[0] = krw_eff * inv_muw_eff; + mob[1] = kr[1] / muo; + + const V watmob_c = src_selector.select(mob[0], one); + const V oilmob_c = src_selector.select(mob[1], zero); + const V flux = trans_src * dt; + const V totmob_c = watmob_c + oilmob_c; + const V wat_src = flux * (watmob_c / totmob_c); + const V oil_src = flux * (oilmob_c / totmob_c); + const V mc = polymer_props.polymerWaterVelocityRatio(c); + + polyinj = 0.0; + polyprod = 0.0; + std::fill(injected, injected + np , 0.0); + std::fill(produced, produced + np , 0.0); + for (int cell = 0; cell < num_cells; ++cell) { + if (wat_src[cell] < 0) { + injected[0] += wat_src[cell]; + polyinj += injected[0] * inj_c[cell]; + } else { + produced[0] += wat_src[cell]; + produced[1] += oil_src[cell]; + polyprod += produced[0] * mc[cell]; + } + } + } + + + +} diff --git a/opm/polymer/fullyimplicit/utilities.hpp b/opm/polymer/fullyimplicit/utilities.hpp new file mode 100644 index 000000000..685396d7b --- /dev/null +++ b/opm/polymer/fullyimplicit/utilities.hpp @@ -0,0 +1,81 @@ +#ifndef OPM_UTILITIES_HEADER_INCLUDED +#define OPM_UTILITIES_HEADER_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace Opm +{ + + typedef AutoDiffBlock ADB; + typedef ADB::V V; + typedef ADB::M M; + typedef Eigen::Array DataBlock; + /// Compute two-phase transport source terms from well terms. + /// Note: Unlike the incompressible version of this function, + /// this version computes surface volume injection rates, + /// production rates are still total reservoir volumes. + /// \param[in] props Fluid and rock properties. + /// \param[in] wells Wells data structure. + /// \param[in] well_state Well pressures and fluxes. + /// \param[out] transport_src The transport source terms. They are to be interpreted depending on sign: + /// (+) positive inflow of first (water) phase (reservoir volume), + /// (-) negative total outflow of both phases (reservoir volume). + void computeTransportSource(const BlackoilPropsAdInterface& props, + const Wells* wells, + const WellState& well_state, + std::vector& transport_src); + + + /// @brief Computes injected and produced volumes of all phases, + /// and injected and produced polymer mass - in the compressible case. + /// Note 1: assumes that only the first phase is injected. + /// Note 2: assumes that transport has been done with an + /// implicit method, i.e. that the current state + /// gives the mobilities used for the preceding timestep. + /// @param[in] props fluid and rock properties. + /// @param[in] polyprops polymer properties + /// @param[in] state state variables (pressure, fluxes etc.) + /// @param[in] transport_src if < 0: total reservoir volume outflow, + /// if > 0: first phase *surface volume* inflow. + /// @param[in] inj_c injected concentration by cell + /// @param[in] dt timestep used + /// @param[out] injected must point to a valid array with P elements, + /// where P = s.size()/transport_src.size(). + /// @param[out] produced must also point to a valid array with P elements. + /// @param[out] polyinj injected mass of polymer + /// @param[out] polyprod produced mass of polymer + void computeInjectedProduced(const BlackoilPropsAdInterface& props, + const Opm::PolymerPropsAd& polymer_props, + const PolymerBlackoilState& state, + const std::vector& transport_src, + const std::vector& inj_c, + const double dt, + double* injected, + double* produced, + double& polyinj, + double& polyprod); + +} //namespace Opm + +#endif //OPM_UTILITIES_HEADER_INCLUDED From 4d2f2fb6a5b1e134a3e085723538207e054b70ee Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Wed, 22 Jan 2014 16:59:51 +0800 Subject: [PATCH 31/83] using last time step's cmax value when computing adsorption for inconp solver --- opm/polymer/fullyimplicit/.utilities.cpp.swp | Bin 0 -> 16384 bytes .../FullyImplicitTwophasePolymerSolver.cpp | 15 +++++++++------ .../FullyImplicitTwophasePolymerSolver.hpp | 5 +++-- 3 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 opm/polymer/fullyimplicit/.utilities.cpp.swp diff --git a/opm/polymer/fullyimplicit/.utilities.cpp.swp b/opm/polymer/fullyimplicit/.utilities.cpp.swp new file mode 100644 index 0000000000000000000000000000000000000000..b7460437f9637d580d00a4119b128a6abf20961e GIT binary patch literal 16384 zcmeHN%a0sK8E^6cCW#3o1VM5suM;z~kD1-Il)&smti6UfVzbu9UL!4=p{J*Mx9omV zRlVblv2w}*Bu+sbA`S>4F5CzQE?kfzB_dq-2T;W0fKbE%35g5*zUt@9&aC4T2vy5J z&vre&@2lVU)mPnB-Cx|jvc+C#FFLqB?Kpqy{bA?n2Y!9#p|3ekC<2jE`8}>a7dIDz z+s`juSpM=0r?l-PGQP<@-Qlt9q>9T9iiRh?vHJYh{#XVq1C{~H zfMvikU>UFsSOzQumI2FvWxz7OraGr(USbDZ~qUjW|+z6D$W z{`IKi{0cY%mVv)N;y7;ucYt-^_YKEMfX9J%Kj}C>0B!(J0%w4~e!_8n3;Yy#14w{1 zpaFdNamV={@Eu?W_#*J9haKm2U={f1L$C=r02Y7`u(9!7;48of*d+NS@Fvg&{s_+A z1KtL113qAQ1eL6I-5t53ijyT&s-+vLB$m3Tq_>xF>GwPy1p91mjhQ>?YQEx7)A4Jv zKx#F;hH6W(Y<7)#ahUXc>3VyM`zx%|VK<;pGRg6&#!)OJJBouebvjw))UTm>dw!{~nZ%rN_DZ0yepg6nZOw&r`)qvL6VtaAA zv{50et5wg=b`o~GX(V*FJ8+K~>SkE@>V*z&+B0-(#>JySa3A7lm^g@H$p>w=QxZx{ zvZ1Tkj5BJ_v^yvtW(LtfVOMTANE{+m)WSOb^t?EVl348x_fM5;#^J@OHX>(n!R0c- z7dc(#$yg~rh=)u>M)oN-3ZFG|_4+vBP|MD)u`4gXbotV??VZ*sE<=!29utbWOvl;{ zpwpAN1?nh>2Dkz`t>VVz=PnmvO!?FYPO&zc!`Pic})LU!Gp8xmc+yXDdWS$?R}+sHKfV^jKV>fMn5-6b{F=nE*S&CttB#&GfCajV44! z$=F0cnWna{ZuPFcxU+pDOCvIkvF~~U41(h2pj~- zGD{9!qzn)LwP(gOQe;F41?uv+*_9K~p!8mphJ7x3vEMUN)hH{qKEzAznW~Q|sJfI> zrmmY(E(Hu^X({^n)(z2AW!jPTp^YJm*LXAX=1ovnqDPx~0e5AyHg<}x$b*Ua8ZS|8 zSa^vdvS@PW)QmxSX@XPFkFH&U~)g%*qWfvd$4$C7Qb6*8zvSFz9fu|CYA zPO)Z{zOt3GmHF}>3g%i2xf>rjl7c)DLb5L;GE37WJhU)@eNsa;gl{IUbi+N^wm<3} zcI}W?B}|!#!@xwS+EjPUT!{uT>vKwETqcrhK41f$V9mqgz~YE0tY%%~DH=_#OGNvu z=1C&a#4`jaBy5_K(SZ3udK=$wPVKCypSh^BVgpwr-)otA8ZsvLGqGHBV_ywYL;~rO zv3JY(7LOzJ(WpahD*n-J0I`^6=Aj1mVdM&*mqg4mdNT^0vC>UMYH;g)+dmg{U2PPiC=%_`5F)^YPVazo)|c1~_r~nxkBB4N5gg2t;QZ1>fy0C z|FHYGa~;p!6Qz%|x=c?2TP{7MPO`+bRU%9S@>Zu@XC}2RzBA8>DV7McE}hnX^t<$; z+xW^dNrJJ?WUNlTTb82AHEC+kKtu?WPCsx}FK`j3x7s5yR8l&m^f{&OK#BoX(fj|; z@Gky2yu;J`e{p{Q4&L{F1NnWzTUPlalRbOXj|f)u9EDrlT0dy3^I@oI2#T8%eJ?7;Rf2+ f&$f7aFa;a@`JFk--R0QA6Z#kjSt4$FI2HI0YMeuL literal 0 HcmV?d00001 diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index 9b64b69ec..c3e99d1b2 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -164,6 +164,7 @@ namespace { const V pvdt = pvol / dt; const SolutionState old_state = constantState(x, xw); + computeCmax(x, old_state.concentration); computeAccum(old_state, 0); const double atol = 1.0e-12; const double rtol = 5.0e-8; @@ -355,16 +356,17 @@ namespace { } - ADB + void FullyImplicitTwophasePolymerSolver:: - computeCmax(const ADB& c) + computeCmax(PolymerState& state, + const ADB& c) { const int nc = grid_.number_of_cells; for (int i = 0; i < nc; ++i) { cmax_(i) = std::max(cmax_(i), c.value()(i)); } + std::copy(&cmax_[0], &cmax_[0] + nc, state.maxconcentration().begin()); - return ADB::constant(cmax_, c.blockPattern()); } void @@ -378,7 +380,7 @@ namespace { rq_[0].accum[aix] = sat[0]; rq_[1].accum[aix] = sat[1]; - const ADB cmax = computeCmax(state.concentration); + const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern()); const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); const double rho_rock = polymer_props_ad_.rockDensity(); const V phi = Eigen::Map(&fluid_.porosity()[0], grid_.number_of_cells, 1); @@ -403,7 +405,7 @@ namespace { const V trans = subset(transmissibility(), ops_.internal_faces); const std::vector kr = computeRelPerm(state); - const ADB cmax = computeCmax(state.concentration); + const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern()); // const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); const ADB krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, cmax, kr[0], state.saturation[0]); const ADB mc = computeMc(state); @@ -697,6 +699,7 @@ namespace { const int nc = grid_.number_of_cells; const int nw = wells_.number_of_wells; const V one = V::Constant(nc, 1.0); + const V zero = V::Zero(nc); // Extract parts of dx corresponding to each part. const V dp = subset(dx, Span(nc)); @@ -732,7 +735,7 @@ namespace { // Concentration updates. const V c_old = Eigen::Map(&state.concentration()[0], nc); - const V c = c_old - dc; + const V c = (c_old - dc).max(zero); std::copy(&c[0], &c[0] + nc, state.concentration().begin()); diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp index 0849a07bd..d96b19750 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp @@ -126,8 +126,9 @@ namespace Opm { const std::vector& polymer_inflow_c, const SolutionState& state) const; - ADB - computeCmax(const ADB& c); + void + computeCmax(PolymerState& state, + const ADB& c); void computeAccum(const SolutionState& state, const int aix ); From b9d3b8b1c4ad598791d0a8ed338e301021a1d1db Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Wed, 22 Jan 2014 17:07:28 +0800 Subject: [PATCH 32/83] add computeinjecprod function for incomp solver. --- opm/polymer/fullyimplicit/utilities.cpp | 90 +++++++++++++++++++++++++ opm/polymer/fullyimplicit/utilities.hpp | 31 +++++++++ 2 files changed, 121 insertions(+) diff --git a/opm/polymer/fullyimplicit/utilities.cpp b/opm/polymer/fullyimplicit/utilities.cpp index cec4ed7df..bb03ff1cd 100644 --- a/opm/polymer/fullyimplicit/utilities.cpp +++ b/opm/polymer/fullyimplicit/utilities.cpp @@ -3,7 +3,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -80,6 +82,94 @@ namespace Opm } } + /// @brief Computes injected and produced volumes of all phases, + /// and injected and produced polymer mass - in the compressible case. + /// Note 1: assumes that only the first phase is injected. + /// Note 2: assumes that transport has been done with an + /// implicit method, i.e. that the current state + /// gives the mobilities used for the preceding timestep. + /// @param[in] props fluid and rock properties. + /// @param[in] polyprops polymer properties + /// @param[in] state state variables (pressure, fluxes etc.) + /// @param[in] transport_src if < 0: total reservoir volume outflow, + /// if > 0: first phase *surface volume* inflow. + /// @param[in] inj_c injected concentration by cell + /// @param[in] dt timestep used + /// @param[out] injected must point to a valid array with P elements, + /// where P = s.size()/transport_src.size(). + /// @param[out] produced must also point to a valid array with P elements. + /// @param[out] polyinj injected mass of polymer + /// @param[out] polyprod produced mass of polymer + void computeInjectedProduced(const IncompPropsAdInterface& props, + const Opm::PolymerPropsAd& polymer_props, + const PolymerState& state, + const std::vector& transport_src, + const std::vector& inj_c, + const double dt, + double* injected, + double* produced, + double& polyinj, + double& polyprod) + { + const int num_cells = transport_src.size(); + if (props.numCells() != num_cells) { + OPM_THROW(std::runtime_error, "Size of transport_src vector does not match number of cells in props."); + } + const int np = props.numPhases(); + if (int(state.saturation().size()) != num_cells*np) { + OPM_THROW(std::runtime_error, "Sizes of state vectors do not match number of cells."); + } + std::vector cells(num_cells); + const V p = Eigen::Map(&state.pressure()[0], num_cells, 1); + const DataBlock s = Eigen::Map(&state.saturation()[0], num_cells, np); + const V sw = s.col(0); + const V so = s.col(1); + const V c = Eigen::Map(&state.concentration()[0], num_cells, 1); + const V cmax = Eigen::Map(&state.maxconcentration()[0], num_cells, 1); + const V trans_src = Eigen::Map(&transport_src[0], num_cells, 1); + V src = V::Constant(num_cells, -1.0); // negative is injec, positive is producer. + for (int cell = 0; cell < num_cells; ++cell) { + cells[cell] = cell; + if(transport_src[cell] > 0.0) { + src[cell] = 1.0; + } + } + const Selector src_selector(src); + const V one = V::Constant(num_cells, 1.0); + const V zero = V::Zero(num_cells); + const std::vector kr = props.relperm(sw, so, cells); + + const V krw_eff = polymer_props.effectiveRelPerm(c, cmax, kr[0]); + const double* mus = props.viscosity(); + const V inv_muw_eff = polymer_props.effectiveInvWaterVisc(c, mus); + std::vector mob(np); + mob[0] = krw_eff * inv_muw_eff; + mob[1] = kr[1] / mu[1]; + + const V watmob_c = src_selector.select(mob[0], one); + const V oilmob_c = src_selector.select(mob[1], zero); + const V flux = trans_src * dt; + const V totmob_c = watmob_c + oilmob_c; + const V wat_src = flux * (watmob_c / totmob_c); + const V oil_src = flux * (oilmob_c / totmob_c); + const V mc = polymer_props.polymerWaterVelocityRatio(c); + + polyinj = 0.0; + polyprod = 0.0; + std::fill(injected, injected + np , 0.0); + std::fill(produced, produced + np , 0.0); + for (int cell = 0; cell < num_cells; ++cell) { + if (wat_src[cell] < 0) { + injected[0] += wat_src[cell]; + polyinj += injected[0] * inj_c[cell]; + } else { + produced[0] += wat_src[cell]; + produced[1] += oil_src[cell]; + polyprod += produced[0] * mc[cell]; + } + } + } + /// @brief Computes injected and produced volumes of all phases, /// and injected and produced polymer mass - in the compressible case. diff --git a/opm/polymer/fullyimplicit/utilities.hpp b/opm/polymer/fullyimplicit/utilities.hpp index 685396d7b..1b74b83df 100644 --- a/opm/polymer/fullyimplicit/utilities.hpp +++ b/opm/polymer/fullyimplicit/utilities.hpp @@ -5,7 +5,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -46,6 +48,35 @@ namespace Opm const WellState& well_state, std::vector& transport_src); + /// @brief Computes injected and produced volumes of all phases, + /// and injected and produced polymer mass - in the compressible case. + /// Note 1: assumes that only the first phase is injected. + /// Note 2: assumes that transport has been done with an + /// implicit method, i.e. that the current state + /// gives the mobilities used for the preceding timestep. + /// @param[in] props fluid and rock properties. + /// @param[in] polyprops polymer properties + /// @param[in] state state variables (pressure, fluxes etc.) + /// @param[in] transport_src if < 0: total reservoir volume outflow, + /// if > 0: first phase *surface volume* inflow. + /// @param[in] inj_c injected concentration by cell + /// @param[in] dt timestep used + /// @param[out] injected must point to a valid array with P elements, + /// where P = s.size()/transport_src.size(). + /// @param[out] produced must also point to a valid array with P elements. + /// @param[out] polyinj injected mass of polymer + /// @param[out] polyprod produced mass of polymer + void computeInjectedProduced(const IncompPropsAdInterface& props, + const Opm::PolymerPropsAd& polymer_props, + const PolymerState& state, + const std::vector& transport_src, + const std::vector& inj_c, + const double dt, + double* injected, + double* produced, + double& polyinj, + double& polyprod); + /// @brief Computes injected and produced volumes of all phases, /// and injected and produced polymer mass - in the compressible case. From 0055ae1def5bcaabf0900f976c5adbc0214d2e2c Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 23 Jan 2014 08:48:42 +0800 Subject: [PATCH 33/83] output watercut for incomp polymer solver. --- opm/polymer/fullyimplicit/.utilities.cpp.swp | Bin 16384 -> 0 bytes .../FullyImplicitTwophasePolymerSolver.cpp | 35 ++++-------- .../FullyImplicitTwophasePolymerSolver.hpp | 7 ++- .../SimulatorFullyImplicitTwophasePolymer.cpp | 52 ++++++++++++++++-- opm/polymer/fullyimplicit/utilities.cpp | 2 +- 5 files changed, 62 insertions(+), 34 deletions(-) delete mode 100644 opm/polymer/fullyimplicit/.utilities.cpp.swp diff --git a/opm/polymer/fullyimplicit/.utilities.cpp.swp b/opm/polymer/fullyimplicit/.utilities.cpp.swp deleted file mode 100644 index b7460437f9637d580d00a4119b128a6abf20961e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeHN%a0sK8E^6cCW#3o1VM5suM;z~kD1-Il)&smti6UfVzbu9UL!4=p{J*Mx9omV zRlVblv2w}*Bu+sbA`S>4F5CzQE?kfzB_dq-2T;W0fKbE%35g5*zUt@9&aC4T2vy5J z&vre&@2lVU)mPnB-Cx|jvc+C#FFLqB?Kpqy{bA?n2Y!9#p|3ekC<2jE`8}>a7dIDz z+s`juSpM=0r?l-PGQP<@-Qlt9q>9T9iiRh?vHJYh{#XVq1C{~H zfMvikU>UFsSOzQumI2FvWxz7OraGr(USbDZ~qUjW|+z6D$W z{`IKi{0cY%mVv)N;y7;ucYt-^_YKEMfX9J%Kj}C>0B!(J0%w4~e!_8n3;Yy#14w{1 zpaFdNamV={@Eu?W_#*J9haKm2U={f1L$C=r02Y7`u(9!7;48of*d+NS@Fvg&{s_+A z1KtL113qAQ1eL6I-5t53ijyT&s-+vLB$m3Tq_>xF>GwPy1p91mjhQ>?YQEx7)A4Jv zKx#F;hH6W(Y<7)#ahUXc>3VyM`zx%|VK<;pGRg6&#!)OJJBouebvjw))UTm>dw!{~nZ%rN_DZ0yepg6nZOw&r`)qvL6VtaAA zv{50et5wg=b`o~GX(V*FJ8+K~>SkE@>V*z&+B0-(#>JySa3A7lm^g@H$p>w=QxZx{ zvZ1Tkj5BJ_v^yvtW(LtfVOMTANE{+m)WSOb^t?EVl348x_fM5;#^J@OHX>(n!R0c- z7dc(#$yg~rh=)u>M)oN-3ZFG|_4+vBP|MD)u`4gXbotV??VZ*sE<=!29utbWOvl;{ zpwpAN1?nh>2Dkz`t>VVz=PnmvO!?FYPO&zc!`Pic})LU!Gp8xmc+yXDdWS$?R}+sHKfV^jKV>fMn5-6b{F=nE*S&CttB#&GfCajV44! z$=F0cnWna{ZuPFcxU+pDOCvIkvF~~U41(h2pj~- zGD{9!qzn)LwP(gOQe;F41?uv+*_9K~p!8mphJ7x3vEMUN)hH{qKEzAznW~Q|sJfI> zrmmY(E(Hu^X({^n)(z2AW!jPTp^YJm*LXAX=1ovnqDPx~0e5AyHg<}x$b*Ua8ZS|8 zSa^vdvS@PW)QmxSX@XPFkFH&U~)g%*qWfvd$4$C7Qb6*8zvSFz9fu|CYA zPO)Z{zOt3GmHF}>3g%i2xf>rjl7c)DLb5L;GE37WJhU)@eNsa;gl{IUbi+N^wm<3} zcI}W?B}|!#!@xwS+EjPUT!{uT>vKwETqcrhK41f$V9mqgz~YE0tY%%~DH=_#OGNvu z=1C&a#4`jaBy5_K(SZ3udK=$wPVKCypSh^BVgpwr-)otA8ZsvLGqGHBV_ywYL;~rO zv3JY(7LOzJ(WpahD*n-J0I`^6=Aj1mVdM&*mqg4mdNT^0vC>UMYH;g)+dmg{U2PPiC=%_`5F)^YPVazo)|c1~_r~nxkBB4N5gg2t;QZ1>fy0C z|FHYGa~;p!6Qz%|x=c?2TP{7MPO`+bRU%9S@>Zu@XC}2RzBA8>DV7McE}hnX^t<$; z+xW^dNrJJ?WUNlTTb82AHEC+kKtu?WPCsx}FK`j3x7s5yR8l&m^f{&OK#BoX(fj|; z@Gky2yu;J`e{p{Q4&L{F1NnWzTUPlalRbOXj|f)u9EDrlT0dy3^I@oI2#T8%eJ?7;Rf2+ f&$f7aFa;a@`JFk--R0QA6Z#kjSt4$FI2HI0YMeuL diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index c3e99d1b2..f24ac775a 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -150,7 +150,8 @@ namespace { step(const double dt, PolymerState& x, WellState& xw, - const std::vector& polymer_inflow) + const std::vector& polymer_inflow, + std::vector& src) { V pvol(grid_.number_of_cells); @@ -169,7 +170,7 @@ namespace { const double atol = 1.0e-12; const double rtol = 5.0e-8; const int maxit = 40; - assemble(pvdt, old_state, x, xw, polymer_inflow); + assemble(pvdt, x, xw, polymer_inflow, src); const double r0 = residualNorm(); int it = 0; @@ -181,7 +182,7 @@ namespace { const V dx = solveJacobianSystem(); updateState(dx, x, xw); - assemble(pvdt, old_state, x, xw, polymer_inflow); + assemble(pvdt, x, xw, polymer_inflow, src); const double r = residualNorm(); @@ -393,10 +394,10 @@ namespace { void FullyImplicitTwophasePolymerSolver:: assemble(const V& pvdt, - const SolutionState& old_state, const PolymerState& x, const WellState& xw, - const std::vector& polymer_inflow) + const std::vector& polymer_inflow, + std::vector& src) { // Create the primary variables. const SolutionState state = variableState(x, xw); @@ -404,27 +405,10 @@ namespace { // -------- Mass balance equations for water and oil -------- const V trans = subset(transmissibility(), ops_.internal_faces); const std::vector kr = computeRelPerm(state); - const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern()); - // const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); const ADB krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, cmax, kr[0], state.saturation[0]); const ADB mc = computeMc(state); computeMassFlux(trans, mc, kr[1], krw_eff, state); - //const std::vector source = accumSource(kr[1], krw_eff, state.concentration, src, polymer_inflow); - // const std::vector source = polymerSource(); - // const double rho_r = polymer_props_ad_.rockDensity(); -// const V phi = V::Constant(pvdt.size(), 1, *fluid_.porosity()); - -// const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); -// residual_.mass_balance[0] = pvdt * (state.saturation[0] - old_state.saturation[0]) -// + ops_.div * mflux[0]; -// residual_.mass_balance[1] = pvdt * (state.saturation[1] - old_state.saturation[1]) -// + ops_.div * mflux[1]; - // Mass balance equation for polymer -// residual_.mass_balance[2] = pvdt * (state.saturation[0] * state.concentration -// - old_state.saturation[0] * old_state.concentration) * (1. - dead_pore_vol) -// + pvdt * rho_r * (1. - phi) / phi * ads - // + ops_.div * mflux[2]; residual_.mass_balance[0] = pvdt*(rq_[0].accum[1] - rq_[0].accum[0]) + ops_.div*rq_[0].mflux; residual_.mass_balance[1] = pvdt*(rq_[1].accum[1] - rq_[1].accum[0]) @@ -516,14 +500,17 @@ namespace { well_contribs[phase] = superset(perf_flux, well_cells, nc); // DUMP(well_contribs[phase]); residual_.mass_balance[phase] += well_contribs[phase]; + for (int i = 0; i < nc; ++i) { + src[i] += well_contribs[phase].value()[i]; + } } // well rates contribs to polymer mass balance eqn. // for injection wells. const V polyin = Eigen::Map(& polymer_inflow[0], nc); const V poly_in_perf = subset(polyin, well_cells); - const V poly_c_cell = subset(state.concentration, well_cells).value(); - const V poly_c = producer.select(poly_c_cell, poly_in_perf); + const V poly_mc_cell = subset(mc, well_cells).value(); + const V poly_c = producer.select(poly_mc_cell, poly_in_perf); residual_.mass_balance[2] += superset(well_perf_rates[0] * poly_c, well_cells, nc); // Set the well flux equation diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp index d96b19750..525b80a7d 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp @@ -29,7 +29,8 @@ namespace Opm { void step(const double dt, PolymerState& state, WellState& well_state, - const std::vector& polymer_inflow); + const std::vector& polymer_inflow, + std::vector& src); private: typedef AutoDiffBlock ADB; typedef ADB::V V; @@ -88,10 +89,10 @@ namespace Opm { const WellState& xw); void assemble(const V& pvdt, - const SolutionState& old_state, const PolymerState& x, const WellState& xw, - const std::vector& polymer_inflow); + const std::vector& polymer_inflow, + std::vector& src); V solveJacobianSystem() const; void updateState(const V& dx, PolymerState& x, diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp index 34d19a8ea..291207b08 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -146,6 +147,7 @@ namespace Opm Opm::DataMap dm; dm["saturation"] = &state.saturation(); dm["pressure"] = &state.pressure(); + dm["cmax"] = &state.maxconcentration(); dm["concentration"] = &state.concentration(); std::vector cell_velocity; Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); @@ -162,6 +164,7 @@ namespace Opm Opm::DataMap dm; dm["saturation"] = &state.saturation(); dm["pressure"] = &state.pressure(); + dm["cmax"] = &state.maxconcentration(); dm["concentration"] = &state.concentration(); std::vector cell_velocity; Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); @@ -191,7 +194,6 @@ namespace Opm -/* static void outputWaterCut(const Opm::Watercut& watercut, const std::string& output_dir) { @@ -203,6 +205,7 @@ namespace Opm } watercut.write(os); } +/* static void outputWellReport(const Opm::WellReport& wellreport, const std::string& output_dir) { @@ -298,8 +301,9 @@ namespace Opm std::vector porevol; Opm::computePorevolume(grid_, props_.porosity(), porevol); - // const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); + const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); std::vector polymer_inflow_c(grid_.number_of_cells); + std::vector transport_src(grid_.number_of_cells); // Main simulation loop. Opm::time::StopWatch solver_timer; @@ -307,6 +311,10 @@ namespace Opm Opm::time::StopWatch step_timer; Opm::time::StopWatch total_timer; total_timer.start(); + double tot_injected[2] = { 0.0 }; + double tot_produced[2] = { 0.0 }; + Opm::Watercut watercut; + watercut.push(0.0, 0.0, 0.0); #if 0 // These must be changed for three-phase. double init_surfvol[2] = { 0.0 }; @@ -333,7 +341,7 @@ namespace Opm outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); } outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); - outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); + // outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); } @@ -348,7 +356,7 @@ namespace Opm polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c); solver_timer.start(); std::vector initial_pressure = state.pressure(); - solver_.step(timer.currentStepLength(), state, well_state, polymer_inflow_c); + solver_.step(timer.currentStepLength(), state, well_state, polymer_inflow_c, transport_src); // Stop timer and report. solver_timer.stop(); @@ -378,6 +386,38 @@ namespace Opm } } } while (!well_control_passed); + double injected[2] = { 0.0 }; + double produced[2] = { 0.0 }; + double polyinj = 0; + double polyprod = 0; + + Opm::computeInjectedProduced(props_, polymer_props_, + state, + transport_src, polymer_inflow_c, timer.currentStepLength(), + injected, produced, + polyinj, polyprod); + tot_injected[0] += injected[0]; + tot_injected[1] += injected[1]; + tot_produced[0] += produced[0]; + tot_produced[1] += produced[1]; + watercut.push(timer.currentTime() + timer.currentStepLength(), + produced[0]/(produced[0] + produced[1]), + tot_produced[0]/tot_porevol_init); + std::cout.precision(5); + const int width = 18; + std::cout << "\nMass balance report.\n"; + std::cout << " Injected reservoir volumes: " + << std::setw(width) << injected[0] + << std::setw(width) << injected[1] << std::endl; + std::cout << " Produced reservoir volumes: " + << std::setw(width) << produced[0] + << std::setw(width) << produced[1] << std::endl; + std::cout << " Total inj reservoir volumes: " + << std::setw(width) << tot_injected[0] + << std::setw(width) << tot_injected[1] << std::endl; + std::cout << " Total prod reservoir volumes: " + << std::setw(width) << tot_produced[0] + << std::setw(width) << tot_produced[1] << std::endl; // Update pore volumes if rock is compressible. @@ -440,9 +480,9 @@ namespace Opm outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); } outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); - outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); -#if 0 outputWaterCut(watercut, output_dir_); +#if 0 + outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); if (wells_) { outputWellReport(wellreport, output_dir_); } diff --git a/opm/polymer/fullyimplicit/utilities.cpp b/opm/polymer/fullyimplicit/utilities.cpp index bb03ff1cd..2008795a0 100644 --- a/opm/polymer/fullyimplicit/utilities.cpp +++ b/opm/polymer/fullyimplicit/utilities.cpp @@ -144,7 +144,7 @@ namespace Opm const V inv_muw_eff = polymer_props.effectiveInvWaterVisc(c, mus); std::vector mob(np); mob[0] = krw_eff * inv_muw_eff; - mob[1] = kr[1] / mu[1]; + mob[1] = kr[1] / mus[1]; const V watmob_c = src_selector.select(mob[0], one); const V oilmob_c = src_selector.select(mob[1], zero); From c4d567c5e499ed2743ec023808a4e2c2687439e1 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Mon, 27 Jan 2014 14:48:26 +0800 Subject: [PATCH 34/83] add capPress functionality for PEDs, just use phase pressure to compute laplace term, all the properties are computed by reference pressure which maybe oil pressure in OPM. --- opm/polymer/fullyimplicit/BlackoilPropsAd.cpp | 59 +++++++++++++++++++ opm/polymer/fullyimplicit/BlackoilPropsAd.hpp | 12 ++++ .../fullyimplicit/BlackoilPropsAdFromDeck.cpp | 58 ++++++++++++++++++ .../fullyimplicit/BlackoilPropsAdFromDeck.hpp | 13 ++++ .../BlackoilPropsAdInterface.hpp | 15 +++++ ...FullyImplicitCompressiblePolymerSolver.cpp | 31 +++++++++- ...FullyImplicitCompressiblePolymerSolver.hpp | 2 + 7 files changed, 188 insertions(+), 2 deletions(-) diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAd.cpp b/opm/polymer/fullyimplicit/BlackoilPropsAd.cpp index d83efc938..b09e21f7b 100644 --- a/opm/polymer/fullyimplicit/BlackoilPropsAd.cpp +++ b/opm/polymer/fullyimplicit/BlackoilPropsAd.cpp @@ -584,5 +584,64 @@ namespace Opm return relperms; } + std::vector BlackoilPropsAd::capPress(const ADB& sw, + const ADB& so, + const ADB& sg, + const Cells& cells) const + + { + const int numCells = cells.size(); + const int numActivePhases = numPhases(); + const int numBlocks = so.numBlocks(); + + Block activeSat(numCells, numActivePhases); + if (pu_.phase_used[Water]) { + assert(sw.value().size() == numCells); + activeSat.col(pu_.phase_pos[Water]) = sw.value(); + } + if (pu_.phase_used[Oil]) { + assert(so.value().size() == numCells); + activeSat.col(pu_.phase_pos[Oil]) = so.value(); + } else { + OPM_THROW(std::runtime_error, "BlackoilPropsAdFromDeck::relperm() assumes oil phase is active."); + } + if (pu_.phase_used[Gas]) { + assert(sg.value().size() == numCells); + activeSat.col(pu_.phase_pos[Gas]) = sg.value(); + } + + Block pc(numCells, numActivePhases); + Block dpc(numCells, numActivePhases*numActivePhases); + props_.capPress(numCells, activeSat.data(), cells.data(), pc.data(), dpc.data()); + + std::vector adbCapPressures; + adbCapPressures.reserve(3); + const ADB* s[3] = { &sw, &so, &sg }; + for (int phase1 = 0; phase1 < 3; ++phase1) { + if (pu_.phase_used[phase1]) { + const int phase1_pos = pu_.phase_pos[phase1]; + std::vector jacs(numBlocks); + for (int block = 0; block < numBlocks; ++block) { + jacs[block] = ADB::M(numCells, s[phase1]->derivative()[block].cols()); + } + for (int phase2 = 0; phase2 < 3; ++phase2) { + if (!pu_.phase_used[phase2]) + continue; + const int phase2_pos = pu_.phase_pos[phase2]; + // Assemble dpc1/ds2. + const int column = phase1_pos + numActivePhases*phase2_pos; // Recall: Fortran ordering from props_.relperm() + ADB::M dpc1_ds2_diag = spdiag(dpc.col(column)); + for (int block = 0; block < numBlocks; ++block) { + jacs[block] += dpc1_ds2_diag * s[phase2]->derivative()[block]; + } + } + adbCapPressures.emplace_back(ADB::function(pc.col(phase1_pos), jacs)); + } else { + adbCapPressures.emplace_back(ADB::null()); + } + } + return adbCapPressures; + } + } // namespace Opm diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAd.hpp b/opm/polymer/fullyimplicit/BlackoilPropsAd.hpp index b6fd925d5..f62793e84 100644 --- a/opm/polymer/fullyimplicit/BlackoilPropsAd.hpp +++ b/opm/polymer/fullyimplicit/BlackoilPropsAd.hpp @@ -237,6 +237,18 @@ namespace Opm const ADB& so, const ADB& sg, const Cells& cells) const; + /// Capillary pressure for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] sg Array of n gas saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 3 elements, each an array of n capillary pressure values, + /// containing the offsets for each p_g, p_o, p_w. The capillary pressure between + /// two arbitrary phases alpha and beta is then given as p_alpha - p_beta. + std::vector capPress(const ADB& sw, + const ADB& so, + const ADB& sg, + const Cells& cells) const; private: const BlackoilPropertiesInterface& props_; diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.cpp b/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.cpp index c9ec833ab..4333a6a3c 100644 --- a/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.cpp +++ b/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.cpp @@ -678,5 +678,63 @@ namespace Opm return relperms; } + std::vector BlackoilPropsAdFromDeck::capPress(const ADB& sw, + const ADB& so, + const ADB& sg, + const Cells& cells) const + { + const int numCells = cells.size(); + const int numActivePhases = numPhases(); + const int numBlocks = so.numBlocks(); + + Block activeSat(numCells, numActivePhases); + if (phase_usage_.phase_used[Water]) { + assert(sw.value().size() == numCells); + activeSat.col(phase_usage_.phase_pos[Water]) = sw.value(); + } + if (phase_usage_.phase_used[Oil]) { + assert(so.value().size() == numCells); + activeSat.col(phase_usage_.phase_pos[Oil]) = so.value(); + } else { + OPM_THROW(std::runtime_error, "BlackoilPropsAdFromDeck::relperm() assumes oil phase is active."); + } + if (phase_usage_.phase_used[Gas]) { + assert(sg.value().size() == numCells); + activeSat.col(phase_usage_.phase_pos[Gas]) = sg.value(); + } + + Block pc(numCells, numActivePhases); + Block dpc(numCells, numActivePhases*numActivePhases); + satprops_->capPress(numCells, activeSat.data(), cells.data(), pc.data(), dpc.data()); + + std::vector adbCapPressures; + adbCapPressures.reserve(3); + const ADB* s[3] = { &sw, &so, &sg }; + for (int phase1 = 0; phase1 < 3; ++phase1) { + if (phase_usage_.phase_used[phase1]) { + const int phase1_pos = phase_usage_.phase_pos[phase1]; + std::vector jacs(numBlocks); + for (int block = 0; block < numBlocks; ++block) { + jacs[block] = ADB::M(numCells, s[phase1]->derivative()[block].cols()); + } + for (int phase2 = 0; phase2 < 3; ++phase2) { + if (!phase_usage_.phase_used[phase2]) + continue; + const int phase2_pos = phase_usage_.phase_pos[phase2]; + // Assemble dpc1/ds2. + const int column = phase1_pos + numActivePhases*phase2_pos; // Recall: Fortran ordering from props_.relperm() + ADB::M dpc1_ds2_diag = spdiag(dpc.col(column)); + for (int block = 0; block < numBlocks; ++block) { + jacs[block] += dpc1_ds2_diag * s[phase2]->derivative()[block]; + } + } + adbCapPressures.emplace_back(ADB::function(pc.col(phase1_pos), jacs)); + } else { + adbCapPressures.emplace_back(ADB::null()); + } + } + return adbCapPressures; + } + } // namespace Opm diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.hpp b/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.hpp index 5e0b983f4..d584ffb90 100644 --- a/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.hpp +++ b/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.hpp @@ -238,6 +238,19 @@ namespace Opm const ADB& so, const ADB& sg, const Cells& cells) const; + /// Capillary pressure for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] sg Array of n gas saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 3 elements, each an array of n capillary pressure values, + /// containing the offsets for each p_g, p_o, p_w. The capillary pressure between + /// two arbitrary phases alpha and beta is then given as p_alpha - p_beta. + std::vector capPress(const ADB& sw, + const ADB& so, + const ADB& sg, + const Cells& cells) const; + private: RockFromDeck rock_; diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAdInterface.hpp b/opm/polymer/fullyimplicit/BlackoilPropsAdInterface.hpp index a1417b7d6..153d993aa 100644 --- a/opm/polymer/fullyimplicit/BlackoilPropsAdInterface.hpp +++ b/opm/polymer/fullyimplicit/BlackoilPropsAdInterface.hpp @@ -243,6 +243,21 @@ namespace Opm const ADB& sg, const Cells& cells) const = 0; + + /// Capillary pressure for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] sg Array of n gas saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 3 elements, each an array of n capillary pressure values, + /// containing the offsets for each p_g, p_o, p_w. The capillary pressure between + /// two arbitrary phases alpha and beta is then given as p_alpha - p_beta. + virtual + std::vector capPress(const ADB& sw, + const ADB& so, + const ADB& sg, + const Cells& cells) const = 0; + }; } // namespace Opm diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index 24760f593..6566b7b2a 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -504,7 +504,6 @@ namespace { // for each active phase. const V trans = subset(geo_.transmissibility(), ops_.internal_faces); const std::vector kr = computeRelPerm(state); -// const ADB cmax = computeCmax(state.concentration); const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern()); const ADB krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, cmax, kr[0], state.saturation[0]); const ADB mc = computeMc(state); @@ -815,6 +814,33 @@ namespace { + std::vector + FullyImplicitCompressiblePolymerSolver:: + computePressures(const SolutionState& state) const + { + const int nc = grid_.number_of_cells; + const std::vector& bpat = state.pressure.blockPattern(); + + const ADB null = ADB::constant(V::Zero(nc, 1), bpat); + + const ADB sw = state.saturation[0]; + + const ADB so = state.saturation[1]; + + const ADB sg = null; + + // convert the pressure offsets to the capillary pressures + std::vector pressure = fluid_.capPress(sw, so, sg, cells_); + pressure[0] = pressure[0] - pressure[0]; + + // add the total pressure to the capillary pressures + for (int phaseIdx = 0; phaseIdx < 2; ++phaseIdx) { + pressure[phaseIdx] += state.pressure; + } + + return pressure; + } + void @@ -833,12 +859,13 @@ namespace { rq_[2].mob = tr_mult * mc * krw_eff * inv_wat_eff_vis; const ADB mu_o = fluidViscosity(1, state.pressure, cells_); rq_[1].mob = tr_mult * kro / mu_o; + std::vector press = computePressures(state); for (int phase = 0; phase < 2; ++phase) { const ADB rho = fluidDensity(phase, state.pressure, cells_); ADB& head = rq_[ phase ].head; // compute gravity potensial using the face average as in eclipse and MRST const ADB rhoavg = ops_.caver * rho; - const ADB dp = ops_.ngrad * state.pressure - geo_.gravity()[2] * (rhoavg * (ops_.ngrad * geo_.z().matrix())); + const ADB dp = ops_.ngrad * press[phase] - geo_.gravity()[2] * (rhoavg * (ops_.ngrad * geo_.z().matrix())); head = transi*dp; UpwindSelector upwind(grid_, ops_, head.value()); const ADB& b = rq_[ phase ].b; diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp index 3d2d44634..6d11d3da8 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp @@ -177,6 +177,8 @@ namespace Opm { computeRelPermWells(const SolutionState& state, const DataBlock& well_s, const std::vector& well_cells) const; + std::vector + computePressures(const SolutionState& state) const; void computeMassFlux(const int actph , From 075e16dc360e72f4366beb288e24ef9a812bc020 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Mon, 27 Jan 2014 16:26:48 +0800 Subject: [PATCH 35/83] add capillary pressure for incom solver. --- ...FullyImplicitCompressiblePolymerSolver.cpp | 2 +- .../FullyImplicitTwophasePolymerSolver.cpp | 23 +++++++++- .../FullyImplicitTwophasePolymerSolver.hpp | 2 + .../fullyimplicit/IncompPropsAdFromDeck.cpp | 42 +++++++++++++++++++ .../fullyimplicit/IncompPropsAdFromDeck.hpp | 11 +++++ .../fullyimplicit/IncompPropsAdInterface.hpp | 12 ++++++ 6 files changed, 89 insertions(+), 3 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index 6566b7b2a..dfe6ddc09 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -831,7 +831,7 @@ namespace { // convert the pressure offsets to the capillary pressures std::vector pressure = fluid_.capPress(sw, so, sg, cells_); - pressure[0] = pressure[0] - pressure[0]; + pressure[0] = pressure[0] - pressure[1]; // add the total pressure to the capillary pressures for (int phaseIdx = 0; phaseIdx < 2; ++phaseIdx) { diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index f24ac775a..5887bd947 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -548,6 +548,25 @@ namespace { } + std::vector + FullyImplicitTwophasePolymerSolver:: + computePressures(const SolutionState& state) const + { + const ADB sw = state.saturation[0]; + const ADB so = state.saturation[1]; + + // convert the pressure offsets to the capillary pressures + std::vector pressure = fluid_.capPress(sw, so, cells_); + pressure[0] = pressure[0] - pressure[1]; + + // add the total pressure to the capillary pressures + for (int phaseIdx = 0; phaseIdx < 2; ++phaseIdx) { + pressure[phaseIdx] += state.pressure; + } + + return pressure; + } + void FullyImplicitTwophasePolymerSolver::computeMassFlux(const V& trans, const ADB& mc, @@ -561,18 +580,18 @@ namespace { rq_[1].mob = kro / V::Constant(kro.size(), 1, mus[1]); rq_[2].mob = mc * krw_eff * inv_wat_eff_vis; - const int nc = grid_.number_of_cells; V z(nc); // Compute z coordinates for (int c = 0; c < nc; ++c){ z[c] = grid_.cell_centroids[c * 3 + 2]; } + std::vector press = computePressures(state); for (int phase = 0; phase < 2; ++phase) { const ADB rho = fluidDensity(phase, state.pressure); ADB& head = rq_[phase].head; const ADB rhoavg = ops_.caver * rho; - const ADB dp = ops_.ngrad * state.pressure + const ADB dp = ops_.ngrad * press[phase] - gravity_[2] * (rhoavg * (ops_.ngrad * z.matrix())); head = trans * dp; UpwindSelector upwind(grid_, ops_, head.value()); diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp index 525b80a7d..9e0f5234a 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp @@ -102,6 +102,8 @@ namespace Opm { V transmissibility() const; + std::vector + computePressures(const SolutionState& state) const; void computeMassFlux(const V& trans, const ADB& mc, diff --git a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp index 4781c85b8..4ca86247b 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp +++ b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp @@ -132,5 +132,47 @@ namespace Opm } return relperms; } + std::vector + IncompPropsAdFromDeck::capPress(const ADB& sw, + const ADB& so, + const Cells& cells) const + { + + const int n = cells.size(); + const int np = numPhases(); + const int num_blocks = so.numBlocks(); + Block s_all(n, np); + assert(sw.size() == n && so.size() == n); + s_all.col(0) = sw.value(); + s_all.col(1) = so.value(); + + Block pc(n, np); + Block dpc(n, np * np); + + satprops_.capPress(n, s_all.data(), cells.data(), pc.data(), dpc.data()); + + std::vector capPressures; + capPressures.reserve(2); + const ADB* s[2] = { &sw, &so}; + for (int phase1 = 0; phase1 < 3; ++phase1) { + const int phase1_pos = phase1; + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = ADB::M(n, s[phase1]->derivative()[block].cols()); + } + for (int phase2 = 0; phase2 < 3; ++phase2) { + const int phase2_pos = phase2; + // Assemble dpc1/ds2. + const int column = phase1_pos + phase2_pos; // Recall: Fortran ordering from props_.relperm() + ADB::M dpc1_ds2_diag = spdiag(dpc.col(column)); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] += dpc1_ds2_diag * s[phase2]->derivative()[block]; + } + } + capPressures.emplace_back(ADB::function(pc.col(phase1_pos), jacs)); + } + return capPressures; + } + } //namespace Opm diff --git a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp index 8058611d0..c82a20685 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp +++ b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp @@ -46,6 +46,17 @@ namespace Opm const ADB& so, const Cells& cells) const; + /// Capillary pressure for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 3 elements, each an array of n capillary pressure values, + /// containing the offsets for each p_g, p_o, p_w. The capillary pressure between + /// two arbitrary phases alpha and beta is then given as p_alpha - p_beta. + std::vector capPress(const ADB& sw, + const ADB& so, + const Cells& cells) const; + private: RockFromDeck rock_; PvtPropertiesIncompFromDeck pvt_; diff --git a/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp b/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp index b74004c93..817cf7820 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp +++ b/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp @@ -82,6 +82,18 @@ namespace Opm std::vector relperm(const ADB& sw, const ADB& so, const Cells& cells) const = 0; + /// Capillary pressure for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 3 elements, each an array of n capillary pressure values, + /// containing the offsets for each p_g, p_o, p_w. The capillary pressure between + /// two arbitrary phases alpha and beta is then given as p_alpha - p_beta. + virtual + std::vector capPress(const ADB& sw, + const ADB& so, + const Cells& cells) const = 0; + }; } // namespace Opm From f01c4dac10bd5659e7e3c45ee14c7049930d9ba2 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Mon, 27 Jan 2014 16:53:45 +0800 Subject: [PATCH 36/83] fix eigen bug when compute the capPress. --- .../fullyimplicit/IncompPropsAdFromDeck.cpp | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp index 4ca86247b..45a946c8b 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp +++ b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp @@ -137,42 +137,42 @@ namespace Opm const ADB& so, const Cells& cells) const { - - const int n = cells.size(); - const int np = numPhases(); - const int num_blocks = so.numBlocks(); - Block s_all(n, np); - assert(sw.size() == n && so.size() == n); + const int numCells = cells.size(); + const int numActivePhases = numPhases(); + const int numBlocks = so.numBlocks(); + assert(sw.value().size() == numCells); + assert(so.value().size() == numCells); + Block s_all(numCells, numActivePhases); s_all.col(0) = sw.value(); s_all.col(1) = so.value(); - Block pc(n, np); - Block dpc(n, np * np); + Block pc(numCells, numActivePhases); + Block dpc(numCells, numActivePhases*numActivePhases); + satprops_.capPress(numCells, s_all.data(), cells.data(), pc.data(), dpc.data()); - satprops_.capPress(n, s_all.data(), cells.data(), pc.data(), dpc.data()); - - std::vector capPressures; - capPressures.reserve(2); + std::vector adbCapPressures; + adbCapPressures.reserve(2); const ADB* s[2] = { &sw, &so}; - for (int phase1 = 0; phase1 < 3; ++phase1) { - const int phase1_pos = phase1; - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = ADB::M(n, s[phase1]->derivative()[block].cols()); + for (int phase1 = 0; phase1 < 2; ++phase1) { + const int phase1_pos = phase1; + std::vector jacs(numBlocks); + for (int block = 0; block < numBlocks; ++block) { + jacs[block] = ADB::M(numCells, s[phase1]->derivative()[block].cols()); } - for (int phase2 = 0; phase2 < 3; ++phase2) { + for (int phase2 = 0; phase2 < 2; ++phase2) { const int phase2_pos = phase2; - // Assemble dpc1/ds2. - const int column = phase1_pos + phase2_pos; // Recall: Fortran ordering from props_.relperm() + // Assemble dpc1/ds2. + const int column = phase1_pos + numActivePhases*phase2_pos; // Recall: Fortran ordering from props_.relperm() ADB::M dpc1_ds2_diag = spdiag(dpc.col(column)); - for (int block = 0; block < num_blocks; ++block) { + for (int block = 0; block < numBlocks; ++block) { jacs[block] += dpc1_ds2_diag * s[phase2]->derivative()[block]; } - } - capPressures.emplace_back(ADB::function(pc.col(phase1_pos), jacs)); - } - return capPressures; + } + adbCapPressures.emplace_back(ADB::function(pc.col(phase1_pos), jacs)); + } + return adbCapPressures; } + } //namespace Opm From e63e318d40098b6051b9a2ffc4f98cab7a9f8bcb Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 20 Feb 2014 16:58:02 +0800 Subject: [PATCH 37/83] apply PVT function changes. --- .../fullyimplicit/.PolymerPropsAd.cpp.swp | Bin 0 -> 16384 bytes .../fullyimplicit/BlackoilPropsAdFromDeck.cpp | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 opm/polymer/fullyimplicit/.PolymerPropsAd.cpp.swp diff --git a/opm/polymer/fullyimplicit/.PolymerPropsAd.cpp.swp b/opm/polymer/fullyimplicit/.PolymerPropsAd.cpp.swp new file mode 100644 index 0000000000000000000000000000000000000000..fb8319f398a8c5d5f8127f9271647d19984edd8c GIT binary patch literal 16384 zcmeI3du$v>9mgkaAp{y;6)A;MmZVtc#PQ`(QuQ1goTgP>>Lx1bwb-qW%idnH@$KDq z_bzc7g-Rfxm7wL1wxSe*kkTR&5C|kd{KGT+L4t~aN=Upagv3+gDJm5~`TS<~b?3X- zP7_t2UFmbTyEF6K-+bpcvomw{-c9AHY3th2O^V~yN=<#}C%5fXW0%a_td!?A+>YE| zn?U<$5>9WV*+fVact@EUmT6-xaB{tkbGzrrc_1{{ZD z@JaXtJPsY0gfX}ro_)DeKZYN{Nw^bkg=ts=tKoO6l=>~4f$za7_!fK<9)^J#jgfcZ^ZIC z)AAvI5;8odq=f!BqCe$w&|$ZlVcRMX#f2?>Lk-Fv-_I1A9?`kWR`=hdluE{(8*tR* zuC+RQ8;+HFjW}ZOsQHpplhJLqt)^{_Tg{GFv7JUEEJT%Bygp(jwlleNhtz9$iFN8z zG^!hh7Syi0DHf}tv7NM|*Fl!C-g=jDmituv26vy+ES09K{`N@eiNcWAaq9KiO?QtV z4tl8AW*Q}O$K-PF#!A?(l}di=P=V+uxWgkY&)HWUw$^78! zf;5GTsZC>iZRf#b)`ku4uyrJTd{@=A5%MREoZ8dg9OQ4FTETV`3KPcJLU(nlA%70l z=B71{ozC=V=-%rThNB1&&(2SjM8YmNLNdh$M*dP`LrHDY=8HNx(N-$eJ59UowwknE zh5mh@YKPKtTph|ZeV6uZLy!6qz{tYVsUnFqh-%YsMIK2bF2G!ggshw z0{39G?H)w-C`;OmNm%HzCN#_`;X!}Z=b?m&KI;mVYzXLDPETk*3TeUq&79cfdG;u| zcZlQ3upsp@_R@+F{=U?xK-IFnC2Bv(h_SvMO^YEKMmHwI z`Ch5iaGOpwm}<72fWO~E&Q^10Dy&^OSZ#zAX{^eKZnKKlX|&DV5h_V?2mR@v8A%fH z7wMB(q2s=09PH_AGUQY|gpeU3d1&T|NgcborG36TWj2u4YdNx`cE9mgJFH(`qz+m3 zh#ry5u#`r6)TPM0tj?2$ds6>6Zp74pxbZq^%p~PfUkDOpo;_=X6c&q%^A`_Z$HaoT zCC7zq;IlVxzo`S!7|%?kX$Q4yJOiqj7CWM(CecJ=!F-z~GHLL6FytlAZ?I=>W) z_H=bVYfV2rZSW}gWN$p&Gc)O)BnrJ8dVc)4a z^#wE8vQ;*{XHW(iet~(1YF0fb^sBaG@s5*O;5xTyH##-P+P>sJRNwFW6H6*L*=e_S zy7l@^PQ!PCaCF%P^(>*cn2D~e2ewyj@1Ibz{y)WfZi+RctpDTR?|;Sm{tTRk zg=x4Nu7Wqgm9PeW!`l4>+yirPGfcq-Q1AoR?T!%A2I3Z7@3{w#bK?uU25 z6>u5+fi?Q?;S8LHV{i++6J8I`uueY-AAnhyguk#B{~CN19)MY};NxifQFsXU!d);6 zqWMjbAGrd#0{`O*94!{Dt`|l2r6sSgz6;&C;82APj%cl$S>&b@DkuHY^+yhtA*-NSF30h zf;rBEIoU<1)nJ0~@RjX1%AugB)2Xp-&mCX^` zH1iPgN-W{=xYcSp)&}iy@*qn^e#bNYsH)>s(g`ck)ke0phVftK@>DoA9W4(sS;sBk z{4ZUFf6Md3$|z|!YWd=gM|ogWF0zMfJ%wa7Fj>=^5{ZLl5kbP5?0mBO#jeV2PGh$d zc!Wx)6n~*6!g7?eVH<^_?r~|BD3JO`?TZ(*#($~7qVbhx zmF`I_E-}8$R_-~(6+ciQ^*MwU1=J}TiG!v?NZ`VjZ`+VFkG{{$7M-P68og}47X&BhQB-9_I|cj z)A3aQsF5~%Jc^pz^3aj!;hAw&`(}QjGLGTlrnJvylC^8ysY~6LEt~4=A3VgO9J;%m z`@l>HE?Cj0(nOmGW}M_Dks0}tH%{EElz5XiRy6)$D>L?`$g)09zbDY+RR1DmU0 Z_q0#iF8$l%1qm)bcImy~r56uM{{{4EU1b0O literal 0 HcmV?d00001 diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.cpp b/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.cpp index 4333a6a3c..62120f4ca 100644 --- a/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.cpp +++ b/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.cpp @@ -543,7 +543,7 @@ namespace Opm assert(po.size() == n); V rbub(n); V drbubdp(n); - props_[Oil]->rbub(n, po.data(), rbub.data(), drbubdp.data()); + props_[Oil]->rsSat(n, po.data(), rbub.data(), drbubdp.data()); return rbub; } @@ -561,7 +561,7 @@ namespace Opm assert(po.size() == n); V rbub(n); V drbubdp(n); - props_[Oil]->rbub(n, po.value().data(), rbub.data(), drbubdp.data()); + props_[Oil]->rsSat(n, po.value().data(), rbub.data(), drbubdp.data()); ADB::M drbubdp_diag = spdiag(drbubdp); const int num_blocks = po.numBlocks(); std::vector jacs(num_blocks); From 979d503b7daadbe4fb1288af0a9f35381a36f462 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 25 Feb 2014 09:52:10 +0800 Subject: [PATCH 38/83] Add license and documentation. --- ...FullyImplicitCompressiblePolymerSolver.cpp | 59 +++--- ...FullyImplicitCompressiblePolymerSolver.hpp | 56 +++--- .../FullyImplicitTwophasePolymerSolver.cpp | 86 +++++++-- .../FullyImplicitTwophasePolymerSolver.hpp | 70 ++++++- opm/polymer/fullyimplicit/IncompPropsAd.cpp | 5 - opm/polymer/fullyimplicit/IncompPropsAd.hpp | 59 ------ .../fullyimplicit/IncompPropsAdBasic.cpp | 57 +++++- .../fullyimplicit/IncompPropsAdBasic.hpp | 127 +++++++++--- .../fullyimplicit/IncompPropsAdFromDeck.cpp | 74 ++++++- .../fullyimplicit/IncompPropsAdFromDeck.hpp | 90 ++++++++- .../fullyimplicit/IncompPropsAdInterface.cpp | 19 ++ .../fullyimplicit/IncompPropsAdInterface.hpp | 40 ++-- opm/polymer/fullyimplicit/PolymerPropsAd.cpp | 182 +++--------------- opm/polymer/fullyimplicit/PolymerPropsAd.hpp | 130 ++++++------- ...ulatorFullyImplicitCompressiblePolymer.cpp | 18 +- ...ulatorFullyImplicitCompressiblePolymer.hpp | 23 ++- .../SimulatorFullyImplicitTwophasePolymer.hpp | 18 +- 17 files changed, 668 insertions(+), 445 deletions(-) delete mode 100644 opm/polymer/fullyimplicit/IncompPropsAd.cpp delete mode 100644 opm/polymer/fullyimplicit/IncompPropsAd.hpp diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index dfe6ddc09..74b0c21af 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -58,9 +58,11 @@ typedef Eigen::Array DataBlock; -namespace { + +namespace { + std::vector buildAllCells(const int nc) { @@ -73,6 +75,8 @@ namespace { + + template AutoDiffBlock::M gravityOperator(const UnstructuredGrid& grid, @@ -123,6 +127,8 @@ namespace { + + V computePerfPress(const UnstructuredGrid& grid, const Wells& wells, const V& rho, const double grav) { const int nw = wells.number_of_wells; @@ -148,23 +154,20 @@ namespace { return wdp; } - - - } // Anonymous namespace + FullyImplicitCompressiblePolymerSolver:: - FullyImplicitCompressiblePolymerSolver(const UnstructuredGrid& grid , - const BlackoilPropsAdInterface& fluid, - const DerivedGeology& geo , - const RockCompressibility* rock_comp_props, - const PolymerPropsAd& polymer_props_ad, - const Wells& wells, - const LinearSolverInterface& linsolver - ) + FullyImplicitCompressiblePolymerSolver(const UnstructuredGrid& grid, + const BlackoilPropsAdInterface& fluid, + const DerivedGeology& geo , + const RockCompressibility* rock_comp_props, + const PolymerPropsAd& polymer_props_ad, + const Wells& wells, + const LinearSolverInterface& linsolver) : grid_ (grid) , fluid_ (fluid) , geo_ (geo) @@ -464,6 +467,7 @@ namespace { + void FullyImplicitCompressiblePolymerSolver:: computeCmax(PolymerBlackoilState& state, @@ -478,6 +482,10 @@ namespace { } + + + + void FullyImplicitCompressiblePolymerSolver:: assemble(const double dt, @@ -515,7 +523,6 @@ namespace { residual_.mass_balance[2] = pvdt*(rq_[2].accum[1] - rq_[2].accum[0]) //+ cell / dt * (rq_[2].ads[1] - rq_[2].ads[0]) + ops_.div*rq_[2].mflux; - // -------- Extra (optional) sg or rs equation, and rs contributions to the mass balance equations -------- // Add the extra (flux) terms to the gas mass balance equations @@ -615,9 +622,7 @@ namespace { // well rates contribs to polymer mass balance eqn. // for injection wells. const V polyin = Eigen::Map(& polymer_inflow[0], nc); -// std::cout<< "Polymer in flow:" << polyin << std::endl; const V poly_in_perf = subset(polyin, well_cells); - //const V poly_c_cell = subset(state.concentration, well_cells).value(); const V poly_mc_cell = subset(mc, well_cells).value(); const V poly_in_c = poly_in_perf;// * poly_mc_cell; const V poly_mc = producer.select(poly_mc_cell, poly_in_c); @@ -654,11 +659,6 @@ namespace { // Choose bhp residual for positive bhp targets. Selector bhp_selector(bhp_targets); residual_.well_eq = bhp_selector.select(bhp_residual, rate_residual); -// for (int i = 0; i < nc; ++i) { -// std::cout << src[i] << " "; -// if ((i+1) % 10 == 0) -// std::cout< FullyImplicitCompressiblePolymerSolver:: computePressures(const SolutionState& state) const @@ -843,6 +845,8 @@ namespace { + + void FullyImplicitCompressiblePolymerSolver::computeMassFlux( const V& transi, @@ -957,6 +961,9 @@ namespace { } + + + // here mc means m(c) * c. ADB FullyImplicitCompressiblePolymerSolver::computeMc(const SolutionState& state) const @@ -967,6 +974,8 @@ namespace { + + ADB FullyImplicitCompressiblePolymerSolver::poroMult(const ADB& p) const { @@ -1018,4 +1027,4 @@ namespace { } -} // namespace Opm +} //namespace Opm diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp index 6d11d3da8..93199e5a7 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp @@ -1,5 +1,6 @@ /* - Copyright 2013 SINTEF ICT, Applied Mathematics. + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL. This file is part of the Open Porous Media project (OPM). @@ -37,11 +38,10 @@ namespace Opm { class PolymerBlackoilState; class WellState; - /// A fully implicit solver for the black-oil problem. + /// A fully implicit solver for the oil-water with polymer problem. /// - /// The simulator is capable of handling three-phase problems - /// where gas can be dissolved in oil (but not vice versa). It - /// uses an industry-standard TPFA discretization with per-phase + /// The simulator is capable of handling oil-water-polymer problems + /// It uses an industry-standard TPFA discretization with per-phase /// upwind weighting of mobilities. /// /// It uses automatic differentiation via the class AutoDiffBlock @@ -56,34 +56,36 @@ namespace Opm { /// \param[in] fluid fluid properties /// \param[in] geo rock properties /// \param[in] rock_comp_props if non-null, rock compressibility properties + /// \param[in] polymer_props_ad polymer properties /// \param[in] wells well structure /// \param[in] linsolver linear solver FullyImplicitCompressiblePolymerSolver(const UnstructuredGrid& grid , - const BlackoilPropsAdInterface& fluid, - const DerivedGeology& geo , - const RockCompressibility* rock_comp_props, - const PolymerPropsAd& polymer_props_ad, - const Wells& wells, - const LinearSolverInterface& linsolver); + const BlackoilPropsAdInterface& fluid, + const DerivedGeology& geo , + const RockCompressibility* rock_comp_props, + const PolymerPropsAd& polymer_props_ad, + const Wells& wells, + const LinearSolverInterface& linsolver); /// Take a single forward step, modifiying /// state.pressure() /// state.faceflux() /// state.saturation() - /// state.gasoilratio() + /// state.concentration() /// wstate.bhp() /// \param[in] dt time step size /// \param[in] state reservoir state /// \param[in] wstate well state + /// \param[in] polymer_inflow polymer influx + /// \param[in] src to caculate wc void - step(const double dt , - PolymerBlackoilState& state , - WellState& wstate, + step(const double dt, + PolymerBlackoilState& state , + WellState& wstate, const std::vector& polymer_inflow, - std::vector& src); + std::vector& src); private: - // Types and enums typedef AutoDiffBlock ADB; typedef ADB::V V; typedef ADB::M M; @@ -99,7 +101,7 @@ namespace Opm { ADB b; // Reciprocal FVF ADB head; // Pressure drop across int. interfaces ADB mob; // Phase mobility (per cell) - std::vector ads; // + std::vector ads; // Adsorption term. }; struct SolutionState { @@ -158,11 +160,11 @@ namespace Opm { const int aix ); void - assemble(const double dt, + assemble(const double dt, const PolymerBlackoilState& x, - const WellState& xw, - const std::vector& polymer_inflow, - std::vector& src); + const WellState& xw, + const std::vector& polymer_inflow, + std::vector& src); V solveJacobianSystem() const; @@ -185,6 +187,7 @@ namespace Opm { const V& transi, const std::vector& kr , const SolutionState& state ); + void computeMassFlux(const V& trans, const ADB& mc, @@ -196,15 +199,20 @@ namespace Opm { computeFracFlow(const ADB& kro, const ADB& krw_eff, const ADB& c) const; + void computeCmax(PolymerBlackoilState& state, const ADB& c); + ADB computeMc(const SolutionState& state) const; + ADB - rockPorosity(const ADB& p) const; + rockPorosity(const ADB& p) const; + ADB - rockPermeability(const ADB& p) const; + rockPermeability(const ADB& p) const; + double residualNorm() const; diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index 5887bd947..926101eb8 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -1,3 +1,23 @@ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + #include #include @@ -15,17 +35,20 @@ #include #include #include + #include #include #include #include #include #include + namespace Opm { + typedef AutoDiffBlock ADB; typedef ADB::V V; typedef ADB::M M; @@ -37,6 +60,7 @@ typedef Eigen::Array @@ -80,8 +104,7 @@ namespace { return wdp; } -}//anonymous namespace - +} //anonymous namespace @@ -201,6 +224,8 @@ namespace { + + FullyImplicitTwophasePolymerSolver::ReservoirResidualQuant::ReservoirResidualQuant() : accum(2, ADB::null()) , mflux( ADB::null()) @@ -212,6 +237,8 @@ namespace { + + FullyImplicitTwophasePolymerSolver::SolutionState::SolutionState(const int np) : pressure ( ADB::null()) , saturation (np, ADB::null()) @@ -243,7 +270,6 @@ namespace { bpat.push_back(xw.bhp().size() * np); bpat.push_back(xw.bhp().size()); - SolutionState state(np); // Pressure. @@ -357,6 +383,9 @@ namespace { } + + + void FullyImplicitTwophasePolymerSolver:: computeCmax(PolymerState& state, @@ -370,6 +399,10 @@ namespace { } + + + + void FullyImplicitTwophasePolymerSolver:: computeAccum(const SolutionState& state, @@ -381,16 +414,20 @@ namespace { rq_[0].accum[aix] = sat[0]; rq_[1].accum[aix] = sat[1]; + const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern()); const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); const double rho_rock = polymer_props_ad_.rockDensity(); const V phi = Eigen::Map(&fluid_.porosity()[0], grid_.number_of_cells, 1); - const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); + rq_[2].accum[aix] = sat[0] * c * (1. - dead_pore_vol) + rho_rock * (1. - phi) / phi * ads; } + + + void FullyImplicitTwophasePolymerSolver:: assemble(const V& pvdt, @@ -547,6 +584,10 @@ namespace { // residual_.well_eq = bhp_residual; } + + + + std::vector FullyImplicitTwophasePolymerSolver:: @@ -567,6 +608,10 @@ namespace { return pressure; } + + + + void FullyImplicitTwophasePolymerSolver::computeMassFlux(const V& trans, const ADB& mc, @@ -604,6 +649,9 @@ namespace { } + + + std::vector FullyImplicitTwophasePolymerSolver::accumSource(const ADB& kro, const ADB& krw_eff, @@ -650,6 +698,7 @@ namespace { + std::vector FullyImplicitTwophasePolymerSolver::computeFracFlow() const { @@ -744,8 +793,6 @@ namespace { const V c = (c_old - dc).max(zero); std::copy(&c[0], &c[0] + nc, state.concentration().begin()); - - // Qs update. // Since we need to update the wellrates, that are ordered by wells, // from dqs which are ordered by phase, the simplest is to compute @@ -756,12 +803,10 @@ namespace { const V wr = wr_old - dwr; std::copy(&wr[0], &wr[0] + wr.size(), well_state.wellRates().begin()); - // Bhp update. const V bhp_old = Eigen::Map(&well_state.bhp()[0], nw, 1); const V bhp = bhp_old - dbhp; std::copy(&bhp[0], &bhp[0] + bhp.size(), well_state.bhp().begin()); - } @@ -782,13 +827,6 @@ namespace { - - - - - - - double FullyImplicitTwophasePolymerSolver::residualNorm() const { @@ -803,15 +841,17 @@ namespace { r = std::max(r, residual_.well_flux_eq.value().matrix().lpNorm()); r = std::max(r, residual_.well_eq.value().matrix().lpNorm()); + return r; } + ADB FullyImplicitTwophasePolymerSolver::fluidDensity(const int phase, - const ADB p) const + const ADB p) const { const double* rhos = fluid_.surfaceDensity(); ADB rho = ADB::constant(V::Constant(grid_.number_of_cells, 1, rhos[phase]), @@ -819,7 +859,10 @@ namespace { return rho; } - + + + + V FullyImplicitTwophasePolymerSolver::transmissibility() const @@ -834,6 +877,10 @@ namespace { return trans; } + + + + // here mc means m(c) * c. ADB FullyImplicitTwophasePolymerSolver::computeMc(const SolutionState& state) const @@ -843,4 +890,7 @@ namespace { } -}//namespace Opm + + + +} //namespace Opm diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp index 9e0f5234a..c050f0468 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp @@ -1,3 +1,23 @@ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL. + + 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_FULLYIMPLICITTWOPHASEPOLYMERSOLVER_HEADER_INCLUDED #define OPM_FULLYIMPLICITTWOPHASEPOLYMERSOLVER_HEADER_INCLUDED @@ -8,24 +28,53 @@ #include #include - struct UnstructuredGrid; struct Wells; + namespace Opm { + class LinearSolverInterface; class PolymerState; class WellState; + /// A fully implicit solver for the incompressible oil-water flow wtih polymer problem. + /// + /// The simulator is capable of handling oil-water with polymer problems + /// It uses an industry-standard TPFA discretization with per-phase + /// upwind weighting of mobilities. + /// + /// It uses automatic differentiation via the class AutoDiffBlock + /// to simplify assembly of the jacobian matrix. class FullyImplicitTwophasePolymerSolver { public: + /// Construct a solver. It will retain references to the + /// arguments of this functions, and they are expected to + /// remain in scope for the lifetime of the solver. + /// \param[in] grid grid data structure + /// \param[in] fluid fluid properties + /// \param[in] polymer_props_ad polymer properties + /// \param[in] wells well structure + /// \param[in] linsolver linear solver + /// \param[in] gravity gravity FullyImplicitTwophasePolymerSolver(const UnstructuredGrid& grid, const IncompPropsAdInterface& fluid, const PolymerPropsAd& polymer_props_ad, - const LinearSolverInterface& linsolver, - const Wells& wells, - const double* gravity); + const LinearSolverInterface& linsolver, + const Wells& wells, + const double* gravity); + /// Take a single forward step, modifiying + /// state.pressure() + /// state.faceflux() + /// state.saturation() + /// state.concentration() + /// wstate.bhp() + /// \param[in] dt time step size + /// \param[in] state reservoir state with polymer + /// \param[in] wstate well state + /// \param[in] polymer_inflow polymer influx + /// \param[in] src to caculate wc void step(const double dt, PolymerState& state, WellState& well_state, @@ -75,6 +124,7 @@ namespace Opm { const WellOps wops_; V cmax_; std::vector rq_; + struct { std::vector mass_balance; ADB well_eq; @@ -93,17 +143,21 @@ namespace Opm { const WellState& xw, const std::vector& polymer_inflow, std::vector& src); + V solveJacobianSystem() const; + void updateState(const V& dx, PolymerState& x, WellState& xw) const; std::vector computeRelPerm(const SolutionState& state) const; + V transmissibility() const; std::vector computePressures(const SolutionState& state) const; + void computeMassFlux(const V& trans, const ADB& mc, @@ -121,8 +175,10 @@ namespace Opm { std::vector computeFracFlow() const; + double residualNorm() const; + ADB polymerSource(const std::vector& kr, const std::vector& src, @@ -137,19 +193,25 @@ namespace Opm { const int aix ); ADB computeMc(const SolutionState& state) const; + ADB rockPorosity(const ADB& p) const; + ADB rockPermeability(const ADB& p) const; + const double fluidDensity(const int phase) const; + ADB fluidDensity(const int phase, const ADB p) const; + ADB transMult(const ADB& p) const; }; } // namespace Opm + #endif// OPM_FULLYIMPLICITTWOPHASESOLVER_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/IncompPropsAd.cpp b/opm/polymer/fullyimplicit/IncompPropsAd.cpp deleted file mode 100644 index d98fb7c15..000000000 --- a/opm/polymer/fullyimplicit/IncompPropsAd.cpp +++ /dev/null @@ -1,5 +0,0 @@ -/* -Author : Liu Ming -Date : 2013-11-28 in Oslo -Email : miliu@statoil.com -*/ diff --git a/opm/polymer/fullyimplicit/IncompPropsAd.hpp b/opm/polymer/fullyimplicit/IncompPropsAd.hpp deleted file mode 100644 index b6f5f379b..000000000 --- a/opm/polymer/fullyimplicit/IncompPropsAd.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* -Author : Liu Ming -Data : 2013-11-28 in Oslo -Email : miliu@statoil.com - -Properties for incompressible immiscible two-phase flow -*/ - -#ifndef OPM_INCOMPROPSAD_HEADER_INCLUDED -#define OPM_INCOMPROPSAD_HEADER_INCLUDED -#include -#include -#include -#include -#include - - -namespace Opm -{ - -// class BlackoilPhases; - - class IncompropsAd : public IncompPropertiesBasic - { - public: - IncomPropsAd(const parameter::ParameterGroup& param, - const int dim, - const int num_cells); - IncompPropsAd(const int num_phases, - const SaturationPropsBasic::RelPermFunc& relpermfunc, - const std::vector& rho, - const std::vector& mu, - const double porosity, - const double permeability, - const int dim, - const int num_cells); - - ~IncompPropsAd(); - - typedef AutoDiffBlock ADB; - typedef ADB::V V; - V relperm(const V& sw, - const V& so, - const std::vector& cells); - ADB relperm(const ADB& sw, - const ADB& so, - const std::vector& cells); - - V capPress(const V& sw, - const V& so, - const std::vector& cells); - ADB capPress(const ADB& sw, - const ADB& so, - const std::vector& cells); - - } -} - -#endif// OPM_INCOMPROPSAD_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp b/opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp index 33691fb82..23ec4c6a8 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp +++ b/opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp @@ -1,4 +1,22 @@ -/**/ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ #include #include @@ -9,6 +27,7 @@ namespace Opm { + /// Constructor. IncompPropsAdBasic::IncompPropsAdBasic(const parameter::ParameterGroup& param, const int dim, const int num_cells) @@ -28,7 +47,7 @@ namespace Opm pvt_.mu(1, 0, 0, &viscosity_[0]); } - + /// Constructor. IncompPropsAdBasic::IncompPropsAdBasic(const int num_phases, const SaturationPropsBasic::RelPermFunc& relpermfunc, const std::vector& rho, @@ -48,10 +67,16 @@ namespace Opm viscosity_.resize(pvt_.numPhases()); pvt_.mu(1, 0, 0, &viscosity_[0]); } + + /// Destructor. IncompPropsAdBasic::~IncompPropsAdBasic() { } + //////////////////////////// + // Rock interface // + //////////////////////////// + /// \return D, the number of spatial dimensions. int IncompPropsAdBasic::numDimensions() const { @@ -79,7 +104,9 @@ namespace Opm } - // ---- Fluid interface ---- + //////////////////////////// + // Fluid interface // + //////////////////////////// /// \return P, the number of phases (also the number of components). int IncompPropsAdBasic::numPhases() const @@ -92,21 +119,34 @@ namespace Opm { return &viscosity_[0]; } - /// \return Array of P density values. + + /// Densities of fluid phases at reservoir conditions. + /// \return Array of P density values. const double* IncompPropsAdBasic::density() const { return pvt_.surfaceDensities(); } + /// Densities of fluid phases at surface conditions. + /// \return Array of P density values. const double* IncompPropsAdBasic::surfaceDensity() const { return pvt_.surfaceDensities(); } + typedef IncompPropsAdBasic::ADB ADB; typedef IncompPropsAdBasic::V V; typedef Eigen::Array Block; typedef std::vector Cells; + // ------ Relative permeability ------ + + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 2 elements, each an array of n relperm values, + /// containing krw, kro. std::vector IncompPropsAdBasic::relperm(const V& sw, const V& so, @@ -130,6 +170,12 @@ namespace Opm return relperms; } + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 2 elements, each an array of n relperm values, + /// containing krw, kro. std::vector IncompPropsAdBasic::relperm(const ADB& sw, const ADB& so, @@ -169,4 +215,5 @@ namespace Opm } return relperms; } -} + +} //namespace Opm diff --git a/opm/polymer/fullyimplicit/IncompPropsAdBasic.hpp b/opm/polymer/fullyimplicit/IncompPropsAdBasic.hpp index 3fc901298..5a0a46c66 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAdBasic.hpp +++ b/opm/polymer/fullyimplicit/IncompPropsAdBasic.hpp @@ -1,4 +1,22 @@ -/**/ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL + + 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_INCOMPPROPSADBASIC_HEADER_INCLUDED #define OPM_INCOMPPROPSADBASIC_HEADER_INCLUDED @@ -8,47 +26,104 @@ #include #include #include + namespace Opm { + /// This class implements the AD-adapted fluid interface for + /// two-phase oil-water. It requires an input deck from which it + /// reads all relevant property data. + /// + /// Most methods are available in two overloaded versions, one + /// taking a constant vector and returning the same, and one + /// taking an AD type and returning the same. Derivatives are not + /// returned separately by any method, only implicitly with the AD + /// version of the methods. class IncompPropsAdBasic : public IncompPropsAdInterface { public: + /// Constructor. IncompPropsAdBasic(const parameter::ParameterGroup& param, const int dim, const int num_cells); + + /// Constructor. IncompPropsAdBasic(const int num_phases, - const SaturationPropsBasic::RelPermFunc& relpermfunc, - const std::vector& rho, - const std::vector& mu, - const double porosity, - const double permeability, - const int dim, - const int num_cells); + const SaturationPropsBasic::RelPermFunc& relpermfunc, + const std::vector& rho, + const std::vector& mu, + const double porosity, + const double permeability, + const int dim, + const int num_cells); + /// Destructor. + ~IncompPropsAdBasic(); - ~IncompPropsAdBasic(); - int numDimensions() const; - int numCells() const; - const double* porosity() const; - const double* permeability() const; + //////////////////////////// + // Rock interface // + //////////////////////////// + + /// \return D, the number of spatial dimensions. + int numDimensions() const; + + /// \return N, the number of cells. + int numCells() const; + + /// \return Array of N porosity values. + const double* porosity() const; + + /// \return Array of ND^2 permeability values. + /// The D^2 permeability values for a cell are organized as a matrix, + /// which is symmetric (so ordering does not matter). + const double* permeability() const; - int numPhases() const; - const double* viscosity() const; - const double* density() const; - const double* surfaceDensity() const; + //////////////////////////// + // Fluid interface // + //////////////////////////// - typedef AutoDiffBlock ADB; - typedef ADB::V V; - std::vector relperm(const V& sw, - const V& so, - const std::vector& cells) const; - std::vector relperm(const ADB& sw, - const ADB& so, - const std::vector& cells) const; + typedef AutoDiffBlock ADB; + typedef ADB::V V; + + /// \return P, Number of active phases (also the number of components). + int numPhases() const; + + /// \return Array of P viscosity values. + const double* viscosity() const; + + /// Densities of fluid phases at reservoir conditions. + /// \return Array of P density values. + const double* density() const; + + /// Densities of fluid phases at surface conditions. + /// \return Array of P density values. + const double* surfaceDensity() const; + + // ------ Relative permeability ------ + + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 2 elements, each an array of n relperm values, + /// containing krw, kro. + std::vector relperm(const V& sw, + const V& so, + const std::vector& cells) const; + + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 2 elements, each an array of n relperm values, + /// containing krw, kro. + std::vector relperm(const ADB& sw, + const ADB& so, + const std::vector& cells) const; private: RockBasic rock_; PvtPropertiesBasic pvt_; SaturationPropsBasic satprops_; std::vector viscosity_; }; -} +} //namespace Opm + #endif // OPM_INCOMPPROPSADBASIC_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp index 45a946c8b..90a10c67e 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp +++ b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp @@ -1,4 +1,23 @@ -/**/ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + #include #include #include @@ -6,7 +25,8 @@ #include namespace Opm -{ +{ + /// Constructor wrapping an opm-core two-phase interface. IncompPropsAdFromDeck::IncompPropsAdFromDeck(const EclipseGridParser& deck, const UnstructuredGrid& grid) { @@ -22,44 +42,62 @@ namespace Opm IncompPropsAdFromDeck::~IncompPropsAdFromDeck() { } - // rock interface + + //////////////////////////// + // Rock interface // + //////////////////////////// + + /// \return D, the number of spatial dimensions. int IncompPropsAdFromDeck::numDimensions() const { return rock_.numDimensions(); } + + /// \return N, the number of cells. int IncompPropsAdFromDeck::numCells() const { return rock_.numCells(); } + /// \return Array of N porosity values. const double* IncompPropsAdFromDeck::porosity() const { return rock_.porosity(); } + + /// \return Array of ND^2 permeability values. + /// The D^2 permeability values for a cell are organized as a matrix, + /// which is symmetric (so ordering does not matter). const double* IncompPropsAdFromDeck::permeability() const { return rock_.permeability(); } - // fluid interface + //////////////////////////// + // Fluid interface // + //////////////////////////// + + /// \return P, Number of active phases (also the number of components). int IncompPropsAdFromDeck::numPhases() const { return pvt_.numPhases(); } - + /// \return Array of P viscosity values. const double* IncompPropsAdFromDeck::viscosity() const { return pvt_.viscosity(); } - + ///Densities of fluid phases at reservoir conditions. + /// \return Array of P density values. const double* IncompPropsAdFromDeck::density() const { return pvt_.reservoirDensities(); } - + /// Densities of fluid phases at surface conditions. + /// \return Array of P density values. const double* IncompPropsAdFromDeck::surfaceDensity() const { return pvt_.surfaceDensities(); @@ -71,6 +109,14 @@ namespace Opm typedef Eigen::Array Block; + // ------ Relative permeability ------ + + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 2 elements, each an array of n relperm values, + /// containing krw, kro. std::vector IncompPropsAdFromDeck::relperm(const V& sw, const V& so, @@ -94,6 +140,12 @@ namespace Opm + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 2 elements, each an array of n relperm values, + /// containing krw, kro. std::vector IncompPropsAdFromDeck::relperm(const ADB& sw, const ADB& so, @@ -132,6 +184,14 @@ namespace Opm } return relperms; } + + /// Capillary pressure for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 2 elements, each an array of n capillary pressure values, + /// containing the offsets for each p_o, p_w. The capillary pressure between + /// two arbitrary phases alpha and beta is then given as p_alpha - p_beta. std::vector IncompPropsAdFromDeck::capPress(const ADB& sw, const ADB& so, diff --git a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp index c82a20685..2f52e541f 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp +++ b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp @@ -1,6 +1,26 @@ -/**/ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL + + 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_INCOMPPROPSADFROMDECK_HEADER_INCLUDED #define OPM_INCOMPPROPSADFROMDECK_HEADER_INCLUDED + #include #include #include @@ -14,34 +34,84 @@ struct UnstructuredGrid; namespace Opm { + /// This class implements the AD-adapted fluid interface for + /// two-phase oil-water. It requires an input deck from which it + /// reads all relevant property data. + /// + /// Most methods are available in two overloaded versions, one + /// taking a constant vector and returning the same, and one + /// taking an AD type and returning the same. Derivatives are not + /// returned separately by any method, only implicitly with the AD + /// version of the methods. class IncompPropsAdFromDeck : public IncompPropsAdInterface { public: + /// Constructor wrapping an opm-core two-phase interface. IncompPropsAdFromDeck(const EclipseGridParser& deck, - const UnstructuredGrid& grid); + const UnstructuredGrid& grid); + + /// Destructor. ~IncompPropsAdFromDeck(); - //--Rock interface-- + //////////////////////////// + // Rock interface // + //////////////////////////// + + /// \return D, the number of spatial dimensions. int numDimensions() const; + + /// \return N, the number of cells. int numCells() const; + + /// \return Array of N porosity values. const double* porosity() const; + + /// \return Array of ND^2 permeability values. + /// The D^2 permeability values for a cell are organized as a matrix, + /// which is symmetric (so ordering does not matter). const double* permeability() const; - // --Fluid interface-- - int numPhases() const; - const double* viscosity() const; - const double* density() const; - const double* surfaceDensity() const; + //////////////////////////// + // Fluid interface // + //////////////////////////// typedef AutoDiffBlock ADB; typedef ADB::V V; typedef std::vector Cells; + /// \return P, Number of active phases (also the number of components). + int numPhases() const; + /// \return Array of P viscosity values. + const double* viscosity() const; + + /// Densities of fluid phases at reservoir conditions. + /// \return Array of P density values. + const double* density() const; + + /// Densities of fluid phases at surface conditions. + /// \return Array of P density values. + const double* surfaceDensity() const; + + + // ------ Relative permeability ------ + + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 2 elements, each an array of n relperm values, + /// containing krw, kro. std::vector relperm(const V& sw, const V& so, const Cells& cells) const; + /// Relative permeabilities for all phases. + /// \param[in] sw Array of n water saturation values. + /// \param[in] so Array of n oil saturation values. + /// \param[in] cells Array of n cell indices to be associated with the saturation values. + /// \return An std::vector with 2 elements, each an array of n relperm values, + /// containing krw, kro. std::vector relperm(const ADB& sw, const ADB& so, const Cells& cells) const; @@ -50,8 +120,8 @@ namespace Opm /// \param[in] sw Array of n water saturation values. /// \param[in] so Array of n oil saturation values. /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 3 elements, each an array of n capillary pressure values, - /// containing the offsets for each p_g, p_o, p_w. The capillary pressure between + /// \return An std::vector with 2 elements, each an array of n capillary pressure values, + /// containing the offsets for each p_o, p_w. The capillary pressure between /// two arbitrary phases alpha and beta is then given as p_alpha - p_beta. std::vector capPress(const ADB& sw, const ADB& so, diff --git a/opm/polymer/fullyimplicit/IncompPropsAdInterface.cpp b/opm/polymer/fullyimplicit/IncompPropsAdInterface.cpp index af33535ea..6217f3b7b 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAdInterface.cpp +++ b/opm/polymer/fullyimplicit/IncompPropsAdInterface.cpp @@ -1,3 +1,22 @@ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL. + + 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 diff --git a/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp b/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp index 817cf7820..7d6421f27 100644 --- a/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp +++ b/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp @@ -1,9 +1,26 @@ /* - + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL. + + 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_INCOMPPROPSADINTERFACE_HEADER_INCLUDED #define OPM_INCOMPPROPSADINTERFACE_HEADER_INCLUDED + #include namespace Opm @@ -11,6 +28,7 @@ namespace Opm class IncompPropsAdInterface { public: + /// Virtual destructor for inheritance. virtual ~IncompPropsAdInterface(); //////////////////////////// @@ -31,7 +49,6 @@ namespace Opm /// which is symmetric (so ordering does not matter). virtual const double* permeability() const = 0; - //////////////////////////// // Fluid interface // //////////////////////////// @@ -41,24 +58,21 @@ namespace Opm typedef ADB::M M; typedef std::vector Cells; - /// \return Number of active phases (also the number of components). + /// \return P, Number of active phases (also the number of components). virtual int numPhases() const = 0; // ------ Density ------ /// Densities of stock components at surface conditions. - /// \return Array of 2 density values. + /// \return Array of P density values. virtual const double* surfaceDensity() const = 0; - // ------ Viscosity ------ /// Viscosity of stock components at surface conditions. - /// \return Array of 2 viscosity values. + /// \return Array of P viscosity values. virtual const double* viscosity() const = 0; - - // ------ Relative permeability ------ /// Relative permeabilities for all phases. @@ -66,7 +80,7 @@ namespace Opm /// \param[in] so Array of n oil saturation values. /// \param[in] cells Array of n cell indices to be associated with the saturation values. /// \return An std::vector with 2 elements, each an array of n relperm values, - /// containing krw, kro. Use PhaseIndex for indexing into the result. + /// containing krw, kro. virtual std::vector relperm(const V& sw, const V& so, @@ -77,24 +91,24 @@ namespace Opm /// \param[in] so Array of n oil saturation values. /// \param[in] cells Array of n cell indices to be associated with the saturation values. /// \return An std::vector with 2 elements, each an array of n relperm values, - /// containing krw, kro. Use PhaseIndex for indexing into the result. + /// containing krw, kro. virtual std::vector relperm(const ADB& sw, const ADB& so, const Cells& cells) const = 0; + /// Capillary pressure for all phases. /// \param[in] sw Array of n water saturation values. /// \param[in] so Array of n oil saturation values. /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 3 elements, each an array of n capillary pressure values, - /// containing the offsets for each p_g, p_o, p_w. The capillary pressure between + /// \return An std::vector with 2 elements, each an array of n capillary pressure values, + /// containing the offsets for each p_o, p_w. The capillary pressure between /// two arbitrary phases alpha and beta is then given as p_alpha - p_beta. virtual std::vector capPress(const ADB& sw, const ADB& so, const Cells& cells) const = 0; - }; } // namespace Opm diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp index 1d1e0031a..2554078e3 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp @@ -7,161 +7,16 @@ namespace Opm { + + + typedef PolymerPropsAd::ADB ADB; typedef PolymerPropsAd::V V; -/* - PolymerPropsAd::PolymerPropsAd() - { - } - - - PolymerPropsAd::PolymerPropsAd(const int num_cells, - const double c_max, - const double mix_param, - const double std::vector& c_vals_visc, - const double std::vector& visc_mult_vals) - : num_cells_ (num_cells) - , c_max_ (c_max) - , mix_param_(mix_param) - , c_vals_visc_ (c_vals_visc) - , visc_mult_vals_ (visc_mult_vals) - { - } - - - double PolymerPropsAd::num_cells() const - { - return num__cells_; - } - double PolymerPropsAd::cMax() const - { - return c_max_; - } - - - - double PolymerPropsAd::mixParam() const - { - return mix_param_; - } - - - - V PolymerPropsAd::muM(const V& c, - const double* visc) const - { - const int nc = num_cells(); - assert(nc == c.size()); - std::vector mu(nc); - - for (int i = 0; i < nc; ++i) { - mu[i] = Opm::linearInterpolation(c_vals_visc_, visc_mult_vals_, c(i)); - } - - const V muM = Eigen::Map(&mu[0], nc); - - const double mu_w = visc[0]; - - return muM * mu_w; - } - - - - ADB PolymerPropsAd::muM(const ADB& c, - const double* visc) const - { - const int nc = num_cells(); - assert(nc == c.size()); - - V mu_m = muM(c.value()); - - std::vector dmu_dc(nc); - - for (int i = 0; i < nc; ++i) { - dmu_dc[i] = Opm::linearInterpolationDerivative(c_vals_visc_, visc_mult_vals_, c.value()(i)); - } - - const V dmu = Eigen::Map(&dmu_dc[0], nc); - - ADB::M dmu_diag = spdiag(dmu); - const int num_blocks = c.numBlocks(); - std::vector jacs(num_blocks); - - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = dmu_diag * c.derivative()[block]; - } - - const double mu_w = visc[0] - - return ADB::function(mu_m, jacs) * mu_w; - } - - - - V PolymerPropsAd::ToddLongstaff(const double mix_param, - const V& muM, - const V& mu) const - { - const int nc = num_cells(); - assert(nc == muM.size()); - - std::vector mueff(nc); - const double omega = mix_param; - - for (int i = 0; i < nc; ++i) { - mueff[i] = std::pow(muM(i),omega) * std::pow(mu(i), 1. - omega); - } - - const V muEff = Eigen::Map(&mueff[0], nc); - - return muEff; - } - - - ADB PolymerPropsAd::ToddLongstaff(const double mix_param, - const ADB& muM, - const ADB& mu) const - { - const int nc = num_cells(); - - } - - - V PolymerPropsAd::muPolyEff(const double mix_param, - const V& muM, - const V& muPoly) const - { - - return ToddLongstaff(mix_param, muM, muPoly); - } - - V PolymerPropsAd::muWatEff(const double mix_param, - const std::vecotr& c_max, - const V& c, - const V& muM, - const V& muWat, - const V& muPolyEff) const - { - const int nc = num_cells(); - assert(nc == c.size()); - V muWate = ToddLongstaff(mix_param, muM, muWat); - -// V cmax = V::Constant(nc, 1, c_max); - const V cmax = Eigen::Map(&c_max[0], nc); - const V one = V::Ones(nc, 1); - V inv_muWatEff = (one - c / cmax) / muWate + c / cmax / muPolyEff; - - V muWatEff = one / inv_muWatEff; - - return muWatEff; - - } -*/ double PolymerPropsAd::rockDensity() const { @@ -171,17 +26,25 @@ namespace Opm { + double PolymerPropsAd::deadPoreVol() const { return polymer_props_.deadPoreVol(); } + + + + double PolymerPropsAd::cMax() const { return polymer_props_.cMax(); } + + + PolymerPropsAd::PolymerPropsAd(const PolymerProperties& polymer_props) @@ -192,6 +55,7 @@ namespace Opm { + PolymerPropsAd::~PolymerPropsAd() { } @@ -199,6 +63,7 @@ namespace Opm { + V PolymerPropsAd::effectiveInvWaterVisc(const V& c, const double* visc) const { @@ -217,7 +82,6 @@ namespace Opm { - ADB PolymerPropsAd::effectiveInvWaterVisc(const ADB& c, const double* visc) const { @@ -242,6 +106,7 @@ namespace Opm { + V PolymerPropsAd::polymerWaterVelocityRatio(const V& c) const { const int nc = c.size(); @@ -288,6 +153,8 @@ namespace Opm { + + V PolymerPropsAd::adsorption(const V& c, const V& cmax_cells) const { const int nc = c.size(); @@ -303,6 +170,10 @@ namespace Opm { return ads; } + + + + ADB PolymerPropsAd::adsorption(const ADB& c, const ADB& cmax_cells) const { const int nc = c.value().size(); @@ -329,6 +200,9 @@ namespace Opm { } + + + V PolymerPropsAd::effectiveRelPerm(const V& c, const V& cmax_cells, @@ -348,6 +222,9 @@ namespace Opm { } + + + ADB PolymerPropsAd::effectiveRelPerm(const ADB& c, const ADB& cmax_cells, @@ -355,9 +232,7 @@ namespace Opm { const ADB& sw) const { const int nc = c.value().size(); - V one = V::Ones(nc); - ADB ads = adsorption(c, cmax_cells); V krw_eff = effectiveRelPerm(c.value(), cmax_cells.value(), krw.value()); @@ -365,15 +240,14 @@ namespace Opm { double res_factor = polymer_props_.resFactor(); double factor = (res_factor - 1.) / max_ads; ADB rk = one + ads * factor; -// ADB dkrw_ds = krw / rk.value(); ADB dkrw_ds = krw / rk; -// ADB dkrw_dc = -krw.value() / (rk.value() * rk.value()) * ads * factor; ADB dkrw_dc = -factor * krw / (rk * rk) * ads ; const int num_blocks = c.numBlocks(); std::vector jacs(num_blocks); for (int block = 0; block < num_blocks; ++block) { - jacs[block] = dkrw_ds.derivative()[block] * sw.derivative()[block] + dkrw_dc.derivative()[block] * c.derivative()[block]; + jacs[block] = dkrw_ds.derivative()[block] * sw.derivative()[block] + + dkrw_dc.derivative()[block] * c.derivative()[block]; } return ADB::function(krw_eff, jacs); diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp index 6f30b220a..26ca56790 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp @@ -1,120 +1,112 @@ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL. + + 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_POLYMERPROPSAD_HEADED_INLCUDED #define OPM_POLYMERPROPSAD_HEADED_INLCUDED - #include #include #include #include #include -namespace Opm { +namespace Opm { class PolymerPropsAd { public: -/* PolymerPropsAd(const int num_cells; - const double mix_param, - const std::vector& c_max, - const std::vector& c_vals_visc, - const std::vector& visc_mult_vals); - - - double num_cells() const; - double cMax() const; - double mixParam() const; - - typedef AutoDiffBlock ADB; - typedef ADB::V V; - - - V muM(const V& c, - const double* visc) const; - ADB muM(const ADB& c - const double* visc) const; - - V ToddLongstaff(const double mix_param, - const V& muM, - const V& mu) const; - - ADB ToddLongstaff(const double mix_param, - const ADB& muM, - const ADB& mu) const; - - V muPolyEff(const double mix_param, - const V& muM, - const V& muPoly) const; - - ADB muPolyEff(const double mix_param, - const ADB& muM, - const ADB& muPoly) const; - - V muWatEff(const double mix_param, - const std::vector& c_max, - const V& c, - const V& muM, - const V& muWat, - const V& muPolyEff) const; - - ADB muWatEff(const double mix_param, - const std::vector& c_max, - const ADB& c, - const ADB& muM, - const ADB& muWat, - const ADB& muPolyEff) const; -*/ + /// \return Reference rock density. double rockDensity() const; + + /// \return The value of dead pore volume. double deadPoreVol() const; + + /// \return The max concentration injected. double cMax() const; + typedef AutoDiffBlock ADB; typedef ADB::V V; + /// Constructor wrapping a polymer props. PolymerPropsAd(const PolymerProperties& polymer_props); + /// Destructor. ~PolymerPropsAd(); - + + /// \param[in] c Array of n polymer concentraion values. + /// \param[in] visc Array of 2 viscosity value. + /// \return value of inverse effective water viscosity. V effectiveInvWaterVisc(const V& c,const double* visc) const; + /// \param[in] c Array of n polymer concentraion values. + /// \param[in] visc Array of 2 viscosity value + /// \return value of inverse effective water viscosity. ADB effectiveInvWaterVisc(const ADB& c,const double* visc) const; - + + /// \param[in] c Array of n polymer concentraion values. + /// \return Array of n mc values, here mc means m(c) * c. V polymerWaterVelocityRatio(const V& c) const; + /// \param[in] c Array of n polymer concentraion values. + /// \return Array of n mc values, here mc means m(c) * c. ADB polymerWaterVelocityRatio(const ADB& c) const; + /// \param[in] c Array of n polymer concentraion values. + /// \param[in] cmax_cells Array of n polymer concentraion values + /// that the cell experienced. + /// \return Array of n adsorption values. V adsorption(const V& c, const V& cmax_cells) const; + /// \param[in] c Array of n polymer concentraion values. + /// \param[in] cmax_cells Array of n polymer concentraion values + /// that the cell experienced. + /// \return Array of n adsorption values. ADB adsorption(const ADB& c, const ADB& cmax_cells) const; + /// \param[in] c Array of n polymer concentraion values. + /// \param[in] cmax_cells Array of n polymer concentraion values + /// that the cell experienced. + /// \param[in] relperm Array of n relative water relperm values. + /// \return Array of n adsorption values. V effectiveRelPerm(const V& c, const V& cmax_cells, const V& relperm) const; + + /// \param[in] c Array of n polymer concentraion values. + /// \param[in] cmax_cells Array of n polymer concentraion values + /// that the cell experienced. + /// \param[in] relperm Array of n relative water relperm values. + /// \return Array of n adsorption values. ADB effectiveRelPerm(const ADB& c, const ADB& cmax_cells, const ADB& krw, const ADB& sw) const; - private: const PolymerProperties& polymer_props_; }; - -} - - - - - - - - - - - - +} //namespace Opm #endif// OPM_POLYMERPROPSAD_HEADED_INLCUDED diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp index 23b6363a4..f4e4000bd 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp @@ -242,7 +242,7 @@ namespace Opm } #endif static void outputWaterCut(const Opm::Watercut& watercut, - const std::string& output_dir) + const std::string& output_dir) { // Write water cut curve. std::string fname = output_dir + "/watercut.txt"; @@ -270,14 +270,14 @@ namespace Opm // \TODO: Treat bcs. SimulatorFullyImplicitCompressiblePolymer::Impl::Impl(const parameter::ParameterGroup& param, - const UnstructuredGrid& grid, - const BlackoilPropsAdInterface& props, - const PolymerPropsAd& polymer_props, - const RockCompressibility* rock_comp_props, - WellsManager& wells_manager, - PolymerInflowInterface& polymer_inflow, - LinearSolverInterface& linsolver, - const double* gravity) + const UnstructuredGrid& grid, + const BlackoilPropsAdInterface& props, + const PolymerPropsAd& polymer_props, + const RockCompressibility* rock_comp_props, + WellsManager& wells_manager, + PolymerInflowInterface& polymer_inflow, + LinearSolverInterface& linsolver, + const double* gravity) : grid_(grid), props_(props), polymer_props_(polymer_props), diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp index 6698eadaa..bb493520b 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp @@ -1,5 +1,6 @@ /* - Copyright 2013 SINTEF ICT, Applied Mathematics. + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL. This file is part of the Open Porous Media project (OPM). @@ -62,19 +63,21 @@ namespace Opm /// /// \param[in] grid grid data structure /// \param[in] props fluid and rock properties - /// \param[in] rock_comp_props if non-null, rock compressibility properties + /// \param[in] polymer_props polymer properties + /// \param[in] rock_comp_props if non-null, rock compressibility properties /// \param[in] well_manager well manager, may manage no (null) wells + /// \param[in] polymer_inflow polymer influx. /// \param[in] linsolver linear solver /// \param[in] gravity if non-null, gravity vector SimulatorFullyImplicitCompressiblePolymer(const parameter::ParameterGroup& param, - const UnstructuredGrid& grid, - const BlackoilPropsAdInterface& props, - const PolymerPropsAd& polymer_props, - const RockCompressibility* rock_comp_props, - WellsManager& wells_manager, - PolymerInflowInterface& polymer_inflow, - LinearSolverInterface& linsolver, - const double* gravity); + const UnstructuredGrid& grid, + const BlackoilPropsAdInterface& props, + const PolymerPropsAd& polymer_props, + const RockCompressibility* rock_comp_props, + WellsManager& wells_manager, + PolymerInflowInterface& polymer_inflow, + LinearSolverInterface& linsolver, + const double* gravity); /// Run the simulation. /// This will run succesive timesteps until timer.done() is true. It will diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp index b8cdb2a66..993448524 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp @@ -60,15 +60,19 @@ namespace Opm /// /// \param[in] grid grid data structure /// \param[in] props fluid and rock properties + /// \param[in] polymer_props polymer properties /// \param[in] linsolver linear solver + /// \param[in] well_manager well manager, may manage no (null) wells + /// \param[in] polymer_inflow polymer influx. + /// \param[in] gravity if non-null, gravity vector SimulatorFullyImplicitTwophasePolymer(const parameter::ParameterGroup& param, - const UnstructuredGrid& grid, - const IncompPropsAdInterface& props, - const PolymerPropsAd& polymer_props, - LinearSolverInterface& linsolver, - WellsManager& wells_manager, - PolymerInflowInterface& polymer_inflow, - const double* gravity); + const UnstructuredGrid& grid, + const IncompPropsAdInterface& props, + const PolymerPropsAd& polymer_props, + LinearSolverInterface& linsolver, + WellsManager& wells_manager, + PolymerInflowInterface& polymer_inflow, + const double* gravity); /// Run the simulation. /// This will run succesive timesteps until timer.done() is true. It will From 010676ad30813579c0da106fd6a7706b0196f70d Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Mon, 3 Mar 2014 10:20:44 +0800 Subject: [PATCH 39/83] add license statement. --- opm/polymer/fullyimplicit/utilities.cpp | 22 +++++++++++++++++++++- opm/polymer/fullyimplicit/utilities.hpp | 20 ++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/opm/polymer/fullyimplicit/utilities.cpp b/opm/polymer/fullyimplicit/utilities.cpp index 2008795a0..9dec80952 100644 --- a/opm/polymer/fullyimplicit/utilities.cpp +++ b/opm/polymer/fullyimplicit/utilities.cpp @@ -1,3 +1,23 @@ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + #include #include #include @@ -261,4 +281,4 @@ namespace Opm -} +} //namespace Opm diff --git a/opm/polymer/fullyimplicit/utilities.hpp b/opm/polymer/fullyimplicit/utilities.hpp index 1b74b83df..47fed5d2d 100644 --- a/opm/polymer/fullyimplicit/utilities.hpp +++ b/opm/polymer/fullyimplicit/utilities.hpp @@ -1,3 +1,23 @@ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL + + 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_UTILITIES_HEADER_INCLUDED #define OPM_UTILITIES_HEADER_INCLUDED From 84fab85860503126aec3c6c9705dd44deaacc3cf Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 14 Mar 2014 14:12:26 +0800 Subject: [PATCH 40/83] Remove opm/autodiff/ files, fix header files. --- opm/polymer/fullyimplicit/AutoDiff.hpp | 302 ------- opm/polymer/fullyimplicit/AutoDiffBlock.hpp | 506 ------------ opm/polymer/fullyimplicit/AutoDiffHelpers.hpp | 513 ------------ opm/polymer/fullyimplicit/BlackoilPropsAd.cpp | 647 --------------- opm/polymer/fullyimplicit/BlackoilPropsAd.hpp | 260 ------ .../fullyimplicit/BlackoilPropsAdFromDeck.cpp | 740 ------------------ .../fullyimplicit/BlackoilPropsAdFromDeck.hpp | 266 ------- .../BlackoilPropsAdInterface.cpp | 24 - .../BlackoilPropsAdInterface.hpp | 265 ------- ...FullyImplicitCompressiblePolymerSolver.cpp | 11 +- ...FullyImplicitCompressiblePolymerSolver.hpp | 6 +- .../FullyImplicitTwophasePolymerSolver.cpp | 6 +- .../FullyImplicitTwophasePolymerSolver.hpp | 6 +- opm/polymer/fullyimplicit/GeoProps.hpp | 110 --- .../fullyimplicit/IncompPropsAdBasic.cpp | 219 ------ .../fullyimplicit/IncompPropsAdBasic.hpp | 129 --- .../fullyimplicit/IncompPropsAdFromDeck.cpp | 238 ------ .../fullyimplicit/IncompPropsAdFromDeck.hpp | 138 ---- .../fullyimplicit/IncompPropsAdInterface.cpp | 25 - .../fullyimplicit/IncompPropsAdInterface.hpp | 115 --- opm/polymer/fullyimplicit/PolymerPropsAd.cpp | 28 +- opm/polymer/fullyimplicit/PolymerPropsAd.hpp | 4 +- .../fullyimplicit/PolymerWellState.hpp | 104 --- ...ulatorFullyImplicitCompressiblePolymer.cpp | 7 +- .../SimulatorFullyImplicitTwophasePolymer.cpp | 5 +- .../SimulatorFullyImplicitTwophasePolymer.hpp | 3 +- opm/polymer/fullyimplicit/utilities.cpp | 24 +- opm/polymer/fullyimplicit/utilities.hpp | 21 +- 28 files changed, 70 insertions(+), 4652 deletions(-) delete mode 100644 opm/polymer/fullyimplicit/AutoDiff.hpp delete mode 100644 opm/polymer/fullyimplicit/AutoDiffBlock.hpp delete mode 100644 opm/polymer/fullyimplicit/AutoDiffHelpers.hpp delete mode 100644 opm/polymer/fullyimplicit/BlackoilPropsAd.cpp delete mode 100644 opm/polymer/fullyimplicit/BlackoilPropsAd.hpp delete mode 100644 opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.cpp delete mode 100644 opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.hpp delete mode 100644 opm/polymer/fullyimplicit/BlackoilPropsAdInterface.cpp delete mode 100644 opm/polymer/fullyimplicit/BlackoilPropsAdInterface.hpp delete mode 100644 opm/polymer/fullyimplicit/GeoProps.hpp delete mode 100644 opm/polymer/fullyimplicit/IncompPropsAdBasic.cpp delete mode 100644 opm/polymer/fullyimplicit/IncompPropsAdBasic.hpp delete mode 100644 opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp delete mode 100644 opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp delete mode 100644 opm/polymer/fullyimplicit/IncompPropsAdInterface.cpp delete mode 100644 opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp delete mode 100644 opm/polymer/fullyimplicit/PolymerWellState.hpp diff --git a/opm/polymer/fullyimplicit/AutoDiff.hpp b/opm/polymer/fullyimplicit/AutoDiff.hpp deleted file mode 100644 index a542abe89..000000000 --- a/opm/polymer/fullyimplicit/AutoDiff.hpp +++ /dev/null @@ -1,302 +0,0 @@ -/* - Copyright 2013 SINTEF ICT, Applied Mathematics. - Copyright 2013 Statoil ASA. - - This file is part of the Open Porous Media Project (OPM). - - OPM is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OPM is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with OPM. If not, see . -*/ - -#ifndef OPM_AUTODIFF_HPP_HEADER -#define OPM_AUTODIFF_HPP_HEADER - -#include - -namespace Opm -{ - - /// A simple class for forward-mode automatic differentiation. - /// - /// The class represents a single value and a single derivative. - /// Only basic arithmetic operators and a few functions are - /// implemented for it, it is mostly intended for simple - /// experimentation. - template - class AutoDiff - { - public: - /// Create an AutoDiff object representing a constant, that - /// is, its derivative is zero. - static AutoDiff - constant(const Scalar x) - { - return function(x, Scalar(0)); - } - - /// Create an AutoDiff object representing a primary variable, - /// that is, its derivative is one. - static AutoDiff - variable(const Scalar x) - { - return function(x, Scalar(1)); - } - - /// Create an AutoDiff object representing a function value - /// and its derivative. - static AutoDiff - function(const Scalar x, const Scalar dx) - { - return AutoDiff(x, dx); - } - - void - operator +=(const Scalar& rhs) - { - val_ += rhs; - } - - void - operator +=(const AutoDiff& rhs) - { - val_ += rhs.val_; - der_ += rhs.der_; - } - - void - operator -=(const Scalar& rhs) - { - val_ -= rhs; - } - - void - operator -=(const AutoDiff& rhs) - { - val_ -= rhs.val_; - der_ -= rhs.der_; - } - - void - operator *=(const Scalar& rhs) - { - val_ *= rhs; - der_ *= rhs; - } - - void - operator *=(const AutoDiff& rhs) - { - der_ = der_*rhs.val_ + val_*rhs.der_; - val_ *= rhs.val_; - } - - void - operator /=(const Scalar& rhs) - { - val_ /= rhs; - der_ /= rhs; - } - - void - operator /=(const AutoDiff& rhs) - { - der_ = (der_*rhs.val_ - val_*rhs.der_) / (rhs.val_ * rhs.val_); - val_ /= rhs.val_; - } - - template - Ostream& - print(Ostream& os) const - { - os << "(x,dx) = (" << val_ << ',' << der_ << ")"; - - return os; - } - - const Scalar val() const { return val_; } - const Scalar der() const { return der_; } - - private: - AutoDiff(const Scalar x, const Scalar dx) - : val_(x), der_(dx) - {} - - Scalar val_; - Scalar der_; - }; - - - template - Ostream& - operator<<(Ostream& os, const AutoDiff& fw) - { - return fw.print(os); - } - - template - AutoDiff - operator +(const AutoDiff& lhs, - const AutoDiff& rhs) - { - AutoDiff ret = lhs; - ret += rhs; - - return ret; - } - - template - AutoDiff - operator +(const T lhs, - const AutoDiff& rhs) - { - AutoDiff ret = rhs; - ret += Scalar(lhs); - - return ret; - } - - template - AutoDiff - operator +(const AutoDiff& lhs, - const T rhs) - { - AutoDiff ret = lhs; - ret += Scalar(rhs); - - return ret; - } - - template - AutoDiff - operator -(const AutoDiff& lhs, - const AutoDiff& rhs) - { - AutoDiff ret = lhs; - ret -= rhs; - - return ret; - } - - template - AutoDiff - operator -(const T lhs, - const AutoDiff& rhs) - { - return AutoDiff::function(Scalar(lhs) - rhs.val(), -rhs.der()); - } - - template - AutoDiff - operator -(const AutoDiff& lhs, - const T rhs) - { - AutoDiff ret = lhs; - ret -= Scalar(rhs); - - return ret; - } - - template - AutoDiff - operator *(const AutoDiff& lhs, - const AutoDiff& rhs) - { - AutoDiff ret = lhs; - ret *= rhs; - - return ret; - } - - template - AutoDiff - operator *(const T lhs, - const AutoDiff& rhs) - { - AutoDiff ret = rhs; - ret *= Scalar(lhs); - - return ret; - } - - template - AutoDiff - operator *(const AutoDiff& lhs, - const T rhs) - { - AutoDiff ret = lhs; - ret *= Scalar(rhs); - - return ret; - } - - template - AutoDiff - operator /(const AutoDiff& lhs, - const AutoDiff& rhs) - { - AutoDiff ret = lhs; - ret /= rhs; - - return ret; - } - - template - AutoDiff - operator /(const T lhs, - const AutoDiff& rhs) - { - Scalar a = Scalar(lhs) / rhs.val(); - Scalar b = -Scalar(lhs) / (rhs.val() * rhs.val()); - - return AutoDiff::function(a, b); - } - - template - AutoDiff - operator /(const AutoDiff& lhs, - const T rhs) - { - Scalar a = lhs.val() / Scalar(rhs); - Scalar b = lhs.der() / Scalar(rhs); - - return AutoDiff::function(a, b); - } - - template - AutoDiff - cos(const AutoDiff& x) - { - Scalar a = std::cos(x.val()); - Scalar b = -std::sin(x.val()) * x.der(); - - return AutoDiff::function(a, b); - } - - template - AutoDiff - sqrt(const AutoDiff& x) - { - Scalar a = std::sqrt(x.val()); - Scalar b = (Scalar(1.0) / (Scalar(2.0) * a)) * x.der(); - - return AutoDiff::function(a, b); - } - -} // namespace Opm - -namespace std { - using Opm::cos; - using Opm::sqrt; -} - -#endif /* OPM_AUTODIFF_HPP_HEADER */ diff --git a/opm/polymer/fullyimplicit/AutoDiffBlock.hpp b/opm/polymer/fullyimplicit/AutoDiffBlock.hpp deleted file mode 100644 index 927927826..000000000 --- a/opm/polymer/fullyimplicit/AutoDiffBlock.hpp +++ /dev/null @@ -1,506 +0,0 @@ -/* - Copyright 2013 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_AUTODIFFBLOCK_HEADER_INCLUDED -#define OPM_AUTODIFFBLOCK_HEADER_INCLUDED - -#include -#include -#include -#include - -namespace Opm -{ - - /// A class for forward-mode automatic differentiation with vector - /// values and sparse jacobian matrices. - /// - /// The class contains a (column) vector of values and multiple - /// sparse matrices representing its partial derivatives. Each - /// such matrix has a number of rows equal to the number of rows - /// in the value vector, and a number of columns equal to the - /// number of discrete variables we want to compute the - /// derivatives with respect to. The reason to have multiple such - /// jacobians instead of just one is to allow simpler grouping of - /// variables, making it easier to implement various - /// preconditioning schemes. Only basic arithmetic operators are - /// implemented for this class, reflecting our needs so far. - /// - /// The class is built on the Eigen library, using an Eigen array - /// type to contain the values and Eigen sparse matrices for the - /// jacobians. The overloaded operators are intended to behave in - /// a similar way to Eigen arrays, meaning for example that the * - /// operator is elementwise multiplication. The only exception is - /// multiplication with a sparse matrix from the left, which is - /// treated as an Eigen matrix operation. - /// - /// There are no public constructors, instead we use the Named - /// Constructor pattern. In general, one needs to know which - /// variables one wants to compute the derivatives with respect to - /// before constructing an AutoDiffBlock. Some of the constructors - /// require you to pass a block pattern. This should be a vector - /// containing the number of columns you want for each jacobian - /// matrix. - /// - /// For example: you want the derivatives with respect to three - /// different variables p, r and s. Assuming that there are 10 - /// elements in p, and 20 in each of r and s, the block pattern is - /// { 10, 20, 20 }. When creating the variables p, r and s in your - /// program you have two options: - /// - Use the variable() constructor three times, passing the - /// index (0 for p, 1 for r and 2 for s), initial value of - /// each variable and the block pattern. - /// - Use the variables() constructor passing only the initial - /// values of each variable. The block pattern will be - /// inferred from the size of the initial value vectors. - /// This is usually the simplest option if you have multiple - /// variables. Note that this constructor returns a vector - /// of all three variables, so you need to use index access - /// (operator[]) to get the individual variables (that is p, - /// r and s). - /// - /// After this, the r variable for example will have a size() of - /// 20 and three jacobian matrices. The first is a 20 by 10 zero - /// matrix, the second is a 20 by 20 identity matrix, and the - /// third is a 20 by 20 zero matrix. - template - class AutoDiffBlock - { - public: - /// Underlying type for values. - typedef Eigen::Array V; - /// Underlying type for jacobians. - typedef Eigen::SparseMatrix M; - - /// Construct an empty AutoDiffBlock. - static AutoDiffBlock null() - { - V val; - std::vector jac; - return AutoDiffBlock(val, jac); - } - - /// Create an AutoDiffBlock representing a constant. - /// \param[in] val values - static AutoDiffBlock constant(const V& val) - { - return AutoDiffBlock(val); - } - - /// Create an AutoDiffBlock representing a constant. - /// This variant requires specifying the block sizes used - /// for the Jacobians even though the Jacobian matrices - /// themselves will be zero. - /// \param[in] val values - /// \param[in] blocksizes block pattern - static AutoDiffBlock constant(const V& val, const std::vector& blocksizes) - { - std::vector jac; - const int num_elem = val.size(); - const int num_blocks = blocksizes.size(); - // For constants, all jacobian blocks are zero. - jac.resize(num_blocks); - for (int i = 0; i < num_blocks; ++i) { - jac[i] = M(num_elem, blocksizes[i]); - } - return AutoDiffBlock(val, jac); - } - - /// Create an AutoDiffBlock representing a single variable block. - /// \param[in] index index of the variable you are constructing - /// \param[in] val values - /// \param[in] blocksizes block pattern - /// The resulting object will have size() equal to block_pattern[index]. - /// Its jacobians will all be zero, except for derivative()[index], which - /// will be an identity matrix. - static AutoDiffBlock variable(const int index, const V& val, const std::vector& blocksizes) - { - std::vector jac; - const int num_elem = val.size(); - const int num_blocks = blocksizes.size(); - // First, set all jacobian blocks to zero... - jac.resize(num_blocks); - for (int i = 0; i < num_blocks; ++i) { - jac[i] = M(num_elem, blocksizes[i]); - } - // ... then set the one corrresponding to this variable to identity. - assert(blocksizes[index] == num_elem); - jac[index].reserve(Eigen::VectorXi::Constant(val.size(), 1)); - for (typename M::Index row = 0; row < val.size(); ++row) { - jac[index].insert(row, row) = Scalar(1.0); - } - return AutoDiffBlock(val, jac); - } - - /// Create an AutoDiffBlock by directly specifying values and jacobians. - /// \param[in] val values - /// \param[in] jac vector of jacobians - static AutoDiffBlock function(const V& val, const std::vector& jac) - { - return AutoDiffBlock(val, jac); - } - - /// Construct a set of primary variables, each initialized to - /// a given vector. - static std::vector variables(const std::vector& initial_values) - { - const int num_vars = initial_values.size(); - std::vector bpat; - for (int v = 0; v < num_vars; ++v) { - bpat.push_back(initial_values[v].size()); - } - std::vector vars; - for (int v = 0; v < num_vars; ++v) { - vars.emplace_back(variable(v, initial_values[v], bpat)); - } - return vars; - } - - /// Elementwise operator += - AutoDiffBlock& operator+=(const AutoDiffBlock& rhs) - { - if (jac_.empty()) { - jac_ = rhs.jac_; - } else if (!rhs.jac_.empty()) { - assert (numBlocks() == rhs.numBlocks()); - assert (value().size() == rhs.value().size()); - - const int num_blocks = numBlocks(); - for (int block = 0; block < num_blocks; ++block) { - assert(jac_[block].rows() == rhs.jac_[block].rows()); - assert(jac_[block].cols() == rhs.jac_[block].cols()); - jac_[block] += rhs.jac_[block]; - } - } - - val_ += rhs.val_; - - return *this; - } - - /// Elementwise operator + - AutoDiffBlock operator+(const AutoDiffBlock& rhs) const - { - if (jac_.empty() && rhs.jac_.empty()) { - return constant(val_ + rhs.val_); - } - if (jac_.empty()) { - return val_ + rhs; - } - if (rhs.jac_.empty()) { - return *this + rhs.val_; - } - std::vector jac = jac_; - assert(numBlocks() == rhs.numBlocks()); - int num_blocks = numBlocks(); - for (int block = 0; block < num_blocks; ++block) { - assert(jac[block].rows() == rhs.jac_[block].rows()); - assert(jac[block].cols() == rhs.jac_[block].cols()); - jac[block] += rhs.jac_[block]; - } - return function(val_ + rhs.val_, jac); - } - - /// Elementwise operator - - AutoDiffBlock operator-(const AutoDiffBlock& rhs) const - { - if (jac_.empty() && rhs.jac_.empty()) { - return constant(val_ - rhs.val_); - } - if (jac_.empty()) { - return val_ - rhs; - } - if (rhs.jac_.empty()) { - return *this - rhs.val_; - } - std::vector jac = jac_; - assert(numBlocks() == rhs.numBlocks()); - int num_blocks = numBlocks(); - for (int block = 0; block < num_blocks; ++block) { - assert(jac[block].rows() == rhs.jac_[block].rows()); - assert(jac[block].cols() == rhs.jac_[block].cols()); - jac[block] -= rhs.jac_[block]; - } - return function(val_ - rhs.val_, jac); - } - - /// Elementwise operator * - AutoDiffBlock operator*(const AutoDiffBlock& rhs) const - { - if (jac_.empty() && rhs.jac_.empty()) { - return constant(val_ * rhs.val_); - } - if (jac_.empty()) { - return val_ * rhs; - } - if (rhs.jac_.empty()) { - return *this * rhs.val_; - } - int num_blocks = numBlocks(); - std::vector jac(num_blocks); - assert(numBlocks() == rhs.numBlocks()); - typedef Eigen::DiagonalMatrix D; - D D1 = val_.matrix().asDiagonal(); - D D2 = rhs.val_.matrix().asDiagonal(); - for (int block = 0; block < num_blocks; ++block) { - assert(jac_[block].rows() == rhs.jac_[block].rows()); - assert(jac_[block].cols() == rhs.jac_[block].cols()); - jac[block] = D2*jac_[block] + D1*rhs.jac_[block]; - } - return function(val_ * rhs.val_, jac); - } - - /// Elementwise operator / - AutoDiffBlock operator/(const AutoDiffBlock& rhs) const - { - if (jac_.empty() && rhs.jac_.empty()) { - return constant(val_ / rhs.val_); - } - if (jac_.empty()) { - return val_ / rhs; - } - if (rhs.jac_.empty()) { - return *this / rhs.val_; - } - int num_blocks = numBlocks(); - std::vector jac(num_blocks); - assert(numBlocks() == rhs.numBlocks()); - typedef Eigen::DiagonalMatrix D; - D D1 = val_.matrix().asDiagonal(); - D D2 = rhs.val_.matrix().asDiagonal(); - D D3 = (1.0/(rhs.val_*rhs.val_)).matrix().asDiagonal(); - for (int block = 0; block < num_blocks; ++block) { - assert(jac_[block].rows() == rhs.jac_[block].rows()); - assert(jac_[block].cols() == rhs.jac_[block].cols()); - jac[block] = D3 * (D2*jac_[block] - D1*rhs.jac_[block]); - } - return function(val_ / rhs.val_, jac); - } - - /// I/O. - template - Ostream& - print(Ostream& os) const - { - int num_blocks = jac_.size(); - os << "Value =\n" << val_ << "\n\nJacobian =\n"; - for (int i = 0; i < num_blocks; ++i) { - os << "Sub Jacobian #" << i << '\n' << jac_[i] << "\n"; - } - return os; - } - - /// Number of elements - int size() const - { - return val_.size(); - } - - /// Number of Jacobian blocks. - int numBlocks() const - { - return jac_.size(); - } - - /// Sizes (number of columns) of Jacobian blocks. - std::vector blockPattern() const - { - const int nb = numBlocks(); - std::vector bp(nb); - for (int block = 0; block < nb; ++block) { - bp[block] = jac_[block].cols(); - } - return bp; - } - - /// Function value. - const V& value() const - { - return val_; - } - - /// Function derivatives. - const std::vector& derivative() const - { - return jac_; - } - - private: - AutoDiffBlock(const V& val) - : val_(val) - { - } - - AutoDiffBlock(const V& val, - const std::vector& jac) - : val_(val), jac_(jac) - { -#ifndef NDEBUG - const int num_elem = val_.size(); - const int num_blocks = jac_.size(); - for (int block = 0; block < num_blocks; ++block) { - assert(num_elem == jac_[block].rows()); - } -#endif - } - - V val_; - std::vector jac_; - }; - - - // --------- Free functions and operators for AutoDiffBlock --------- - - /// Stream output. - template - Ostream& - operator<<(Ostream& os, const AutoDiffBlock& fw) - { - return fw.print(os); - } - - - /// Multiply with sparse matrix from the left. - template - AutoDiffBlock operator*(const typename AutoDiffBlock::M& lhs, - const AutoDiffBlock& rhs) - { - int num_blocks = rhs.numBlocks(); - std::vector::M> jac(num_blocks); - assert(lhs.cols() == rhs.value().rows()); - for (int block = 0; block < num_blocks; ++block) { - jac[block] = lhs*rhs.derivative()[block]; - } - typename AutoDiffBlock::V val = lhs*rhs.value().matrix(); - return AutoDiffBlock::function(val, jac); - } - - - /// Elementwise multiplication with constant on the left. - template - AutoDiffBlock operator*(const typename AutoDiffBlock::V& lhs, - const AutoDiffBlock& rhs) - { - return AutoDiffBlock::constant(lhs, rhs.blockPattern()) * rhs; - } - - - /// Elementwise multiplication with constant on the right. - template - AutoDiffBlock operator*(const AutoDiffBlock& lhs, - const typename AutoDiffBlock::V& rhs) - { - return rhs * lhs; // Commutative operation. - } - - - /// Elementwise addition with constant on the left. - template - AutoDiffBlock operator+(const typename AutoDiffBlock::V& lhs, - const AutoDiffBlock& rhs) - { - return AutoDiffBlock::constant(lhs, rhs.blockPattern()) + rhs; - } - - - /// Elementwise addition with constant on the right. - template - AutoDiffBlock operator+(const AutoDiffBlock& lhs, - const typename AutoDiffBlock::V& rhs) - { - return rhs + lhs; // Commutative operation. - } - - - /// Elementwise subtraction with constant on the left. - template - AutoDiffBlock operator-(const typename AutoDiffBlock::V& lhs, - const AutoDiffBlock& rhs) - { - return AutoDiffBlock::constant(lhs, rhs.blockPattern()) - rhs; - } - - - /// Elementwise subtraction with constant on the right. - template - AutoDiffBlock operator-(const AutoDiffBlock& lhs, - const typename AutoDiffBlock::V& rhs) - { - return lhs - AutoDiffBlock::constant(rhs, lhs.blockPattern()); - } - - - /// Elementwise division with constant on the left. - template - AutoDiffBlock operator/(const typename AutoDiffBlock::V& lhs, - const AutoDiffBlock& rhs) - { - return AutoDiffBlock::constant(lhs, rhs.blockPattern()) / rhs; - } - - - /// Elementwise division with constant on the right. - template - AutoDiffBlock operator/(const AutoDiffBlock& lhs, - const typename AutoDiffBlock::V& rhs) - { - return lhs / AutoDiffBlock::constant(rhs, lhs.blockPattern()); - } - - - /** - * @brief Operator for multiplication with a scalar on the right-hand side - * - * @param lhs The left-hand side AD forward block - * @param rhs The scalar to multiply with - * @return The product - */ - template - AutoDiffBlock operator*(const AutoDiffBlock& lhs, - const Scalar& rhs) - { - std::vector< typename AutoDiffBlock::M > jac; - jac.reserve( lhs.numBlocks() ); - for (int block=0; block::function( lhs.value() * rhs, jac ); - } - - - /** - * @brief Operator for multiplication with a scalar on the left-hand side - * - * @param lhs The scalar to multiply with - * @param rhs The right-hand side AD forward block - * @return The product - */ - template - AutoDiffBlock operator*(const Scalar& lhs, - const AutoDiffBlock& rhs) - { - return rhs * lhs; // Commutative operation. - } - - -} // namespace Opm - - - -#endif // OPM_AUTODIFFBLOCK_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/AutoDiffHelpers.hpp b/opm/polymer/fullyimplicit/AutoDiffHelpers.hpp deleted file mode 100644 index ba154a08b..000000000 --- a/opm/polymer/fullyimplicit/AutoDiffHelpers.hpp +++ /dev/null @@ -1,513 +0,0 @@ -/* - Copyright 2013 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_AUTODIFFHELPERS_HEADER_INCLUDED -#define OPM_AUTODIFFHELPERS_HEADER_INCLUDED - -#include -#include -#include - -#include -#include - -namespace Opm -{ - -// -------------------- class HelperOps -------------------- - -/// Contains vectors and sparse matrices that represent subsets or -/// operations on (AD or regular) vectors of data. -struct HelperOps -{ - typedef AutoDiffBlock::M M; - typedef AutoDiffBlock::V V; - - /// A list of internal faces. - typedef Eigen::Array IFaces; - IFaces internal_faces; - - /// Extract for each internal face the difference of its adjacent cells' values (first - second). - M ngrad; - /// Extract for each face the difference of its adjacent cells' values (second - first). - M grad; - /// Extract for each face the average of its adjacent cells' values. - M caver; - /// Extract for each cell the sum of its adjacent interior faces' (signed) values. - M div; - /// Extract for each face the difference of its adjacent cells' values (first - second). - /// For boundary faces, one of the entries per row (corresponding to the outside) is zero. - M fullngrad; - /// Extract for each cell the sum of all its adjacent faces' (signed) values. - M fulldiv; - - /// Constructs all helper vectors and matrices. - HelperOps(const UnstructuredGrid& grid) - { - const int nc = grid.number_of_cells; - const int nf = grid.number_of_faces; - // Define some neighbourhood-derived helper arrays. - typedef Eigen::Array OneColBool; - typedef Eigen::Array TwoColInt; - typedef Eigen::Array TwoColBool; - TwoColInt nb = Eigen::Map(grid.face_cells, nf, 2); - // std::cout << "nb = \n" << nb << std::endl; - TwoColBool nbib = nb >= 0; - OneColBool ifaces = nbib.rowwise().all(); - const int num_internal = ifaces.cast().sum(); - // std::cout << num_internal << " internal faces." << std::endl; - TwoColInt nbi(num_internal, 2); - internal_faces.resize(num_internal); - int fi = 0; - for (int f = 0; f < nf; ++f) { - if (ifaces[f]) { - internal_faces[fi] = f; - nbi.row(fi) = nb.row(f); - ++fi; - } - } - // std::cout << "nbi = \n" << nbi << std::endl; - // Create matrices. - ngrad.resize(num_internal, nc); - caver.resize(num_internal, nc); - typedef Eigen::Triplet Tri; - std::vector ngrad_tri; - std::vector caver_tri; - ngrad_tri.reserve(2*num_internal); - caver_tri.reserve(2*num_internal); - for (int i = 0; i < num_internal; ++i) { - ngrad_tri.emplace_back(i, nbi(i,0), 1.0); - ngrad_tri.emplace_back(i, nbi(i,1), -1.0); - caver_tri.emplace_back(i, nbi(i,0), 0.5); - caver_tri.emplace_back(i, nbi(i,1), 0.5); - } - ngrad.setFromTriplets(ngrad_tri.begin(), ngrad_tri.end()); - caver.setFromTriplets(caver_tri.begin(), caver_tri.end()); - grad = -ngrad; - div = ngrad.transpose(); - std::vector fullngrad_tri; - fullngrad_tri.reserve(2*nf); - for (int i = 0; i < nf; ++i) { - if (nb(i,0) >= 0) { - fullngrad_tri.emplace_back(i, nb(i,0), 1.0); - } - if (nb(i,1) >= 0) { - fullngrad_tri.emplace_back(i, nb(i,1), -1.0); - } - } - fullngrad.resize(nf, nc); - fullngrad.setFromTriplets(fullngrad_tri.begin(), fullngrad_tri.end()); - fulldiv = fullngrad.transpose(); - } -}; - -// -------------------- upwinding helper class -------------------- - - - /// Upwind selection in absence of counter-current flow (i.e., - /// without effects of gravity and/or capillary pressure). - template - class UpwindSelector { - public: - typedef AutoDiffBlock ADB; - - UpwindSelector(const UnstructuredGrid& g, - const HelperOps& h, - const typename ADB::V& ifaceflux) - { - typedef HelperOps::IFaces::Index IFIndex; - const IFIndex nif = h.internal_faces.size(); - assert(nif == ifaceflux.size()); - - // Define selector structure. - typedef typename Eigen::Triplet Triplet; - std::vector s; s.reserve(nif); - for (IFIndex iface = 0; iface < nif; ++iface) { - const int f = h.internal_faces[iface]; - const int c1 = g.face_cells[2*f + 0]; - const int c2 = g.face_cells[2*f + 1]; - - assert ((c1 >= 0) && (c2 >= 0)); - - // Select upwind cell. - const int c = (ifaceflux[iface] >= 0) ? c1 : c2; - - s.push_back(Triplet(iface, c, Scalar(1))); - } - - // Assemble explicit selector operator. - select_.resize(nif, g.number_of_cells); - select_.setFromTriplets(s.begin(), s.end()); - } - - /// Apply selector to multiple per-cell quantities. - std::vector - select(const std::vector& xc) const - { - // Absence of counter-current flow means that the same - // selector applies to all quantities, 'x', defined per - // cell. - std::vector xf; xf.reserve(xc.size()); - for (typename std::vector::const_iterator - b = xc.begin(), e = xc.end(); b != e; ++b) - { - xf.push_back(select_ * (*b)); - } - - return xf; - } - - /// Apply selector to single per-cell ADB quantity. - ADB select(const ADB& xc) const - { - return select_*xc; - } - - /// Apply selector to single per-cell constant quantity. - typename ADB::V select(const typename ADB::V& xc) const - { - return (select_*xc.matrix()).array(); - } - - private: - typename ADB::M select_; - }; - - - -namespace { - - template - Eigen::SparseMatrix - constructSubsetSparseMatrix(const int full_size, const IntVec& indices) - { - typedef Eigen::Triplet Tri; - const int subset_size = indices.size(); - std::vector triplets(subset_size); - for (int i = 0; i < subset_size; ++i) { - triplets[i] = Tri(i, indices[i], 1); - } - Eigen::SparseMatrix sub(subset_size, full_size); - sub.setFromTriplets(triplets.begin(), triplets.end()); - return sub; - } - - template - Eigen::SparseMatrix - constructSupersetSparseMatrix(const int full_size, const IntVec& indices) - { - return constructSubsetSparseMatrix(full_size, indices).transpose(); - } - -} // anon namespace - - -/// Returns x(indices). -template -AutoDiffBlock -subset(const AutoDiffBlock& x, - const IntVec& indices) -{ - return constructSubsetSparseMatrix(x.value().size(), indices) * x; -} - - - -/// Returns x(indices). -template -Eigen::Array -subset(const Eigen::Array& x, - const IntVec& indices) -{ - return (constructSubsetSparseMatrix(x.size(), indices) * x.matrix()).array(); -} - - - -/// Returns v where v(indices) == x, v(!indices) == 0 and v.size() == n. -template -AutoDiffBlock -superset(const AutoDiffBlock& x, - const IntVec& indices, - const int n) -{ - return constructSupersetSparseMatrix(n, indices) * x; -} - - - -/// Returns v where v(indices) == x, v(!indices) == 0 and v.size() == n. -template -Eigen::Array -superset(const Eigen::Array& x, - const IntVec& indices, - const int n) -{ - return constructSupersetSparseMatrix(n, indices) * x.matrix(); -} - - - -/// Construct square sparse matrix with the -/// elements of d on the diagonal. -/// Need to mark this as inline since it is defined in a header and not a template. -inline -AutoDiffBlock::M -spdiag(const AutoDiffBlock::V& d) -{ - typedef AutoDiffBlock::M M; - - const int n = d.size(); - M mat(n, n); - mat.reserve(Eigen::ArrayXi::Ones(n, 1)); - for (M::Index i = 0; i < n; ++i) { - mat.insert(i, i) = d[i]; - } - - return mat; -} - - - - - /// Selection. Choose first of two elements if selection basis element is nonnegative. - template - class Selector { - public: - typedef AutoDiffBlock ADB; - - Selector(const typename ADB::V& selection_basis) - { - // Define selector structure. - const int n = selection_basis.size(); - // Over-reserving so we do not have to count. - left_elems_.reserve(n); - right_elems_.reserve(n); - for (int i = 0; i < n; ++i) { - if (selection_basis[i] < 0.0) { - right_elems_.push_back(i); - } else { - left_elems_.push_back(i); - } - } - } - - /// Apply selector to ADB quantities. - ADB select(const ADB& x1, const ADB& x2) const - { - if (right_elems_.empty()) { - return x1; - } else if (left_elems_.empty()) { - return x2; - } else { - return superset(subset(x1, left_elems_), left_elems_, x1.size()) - + superset(subset(x2, right_elems_), right_elems_, x2.size()); - } - } - - /// Apply selector to ADB quantities. - typename ADB::V select(const typename ADB::V& x1, const typename ADB::V& x2) const - { - if (right_elems_.empty()) { - return x1; - } else if (left_elems_.empty()) { - return x2; - } else { - return superset(subset(x1, left_elems_), left_elems_, x1.size()) - + superset(subset(x2, right_elems_), right_elems_, x2.size()); - } - } - - private: - std::vector left_elems_; - std::vector right_elems_; - }; - - - - -/// Returns the input expression, but with all Jacobians collapsed to one. -inline -AutoDiffBlock -collapseJacs(const AutoDiffBlock& x) -{ - typedef AutoDiffBlock ADB; - const int nb = x.numBlocks(); - typedef Eigen::Triplet Tri; - int nnz = 0; - for (int block = 0; block < nb; ++block) { - nnz += x.derivative()[block].nonZeros(); - } - std::vector t; - t.reserve(nnz); - int block_col_start = 0; - for (int block = 0; block < nb; ++block) { - const ADB::M& jac = x.derivative()[block]; - for (ADB::M::Index k = 0; k < jac.outerSize(); ++k) { - for (ADB::M::InnerIterator i(jac, k); i ; ++i) { - t.push_back(Tri(i.row(), - i.col() + block_col_start, - i.value())); - } - } - block_col_start += jac.cols(); - } - // Build final jacobian. - std::vector jacs(1); - jacs[0].resize(x.size(), block_col_start); - jacs[0].setFromTriplets(t.begin(), t.end()); - return ADB::function(x.value(), jacs); -} - - - - -/// Returns the vertical concatenation [ x; y ] of the inputs. -inline -AutoDiffBlock -vertcat(const AutoDiffBlock& x, - const AutoDiffBlock& y) -{ - const int nx = x.size(); - const int ny = y.size(); - const int n = nx + ny; - std::vector xind(nx); - for (int i = 0; i < nx; ++i) { - xind[i] = i; - } - std::vector yind(ny); - for (int i = 0; i < ny; ++i) { - yind[i] = nx + i; - } - return superset(x, xind, n) + superset(y, yind, n); -} - - - - - -class Span -{ -public: - explicit Span(const int num) - : num_(num), - stride_(1), - start_(0) - { - } - Span(const int num, const int stride, const int start) - : num_(num), - stride_(stride), - start_(start) - { - } - int operator[](const int i) const - { - assert(i >= 0 && i < num_); - return start_ + i*stride_; - } - int size() const - { - return num_; - } - - - class SpanIterator - { - public: - SpanIterator(const Span* span, const int index) - : span_(span), - index_(index) - { - } - SpanIterator operator++() - { - ++index_; - return *this; - } - SpanIterator operator++(int) - { - SpanIterator before_increment(*this); - ++index_; - return before_increment; - } - bool operator<(const SpanIterator& rhs) const - { - assert(span_ == rhs.span_); - return index_ < rhs.index_; - } - bool operator==(const SpanIterator& rhs) const - { - assert(span_ == rhs.span_); - return index_ == rhs.index_; - } - bool operator!=(const SpanIterator& rhs) const - { - assert(span_ == rhs.span_); - return index_ != rhs.index_; - } - int operator*() - { - return (*span_)[index_]; - } - private: - const Span* span_; - int index_; - }; - - typedef SpanIterator iterator; - typedef SpanIterator const_iterator; - - SpanIterator begin() const - { - return SpanIterator(this, 0); - } - - SpanIterator end() const - { - return SpanIterator(this, num_); - } - - bool operator==(const Span& rhs) - { - return num_ == rhs.num_ && start_ == rhs.start_ && stride_ == rhs.stride_; - } - -private: - const int num_; - const int stride_; - const int start_; -}; - - - -/// Return a vector of (-1.0, 0.0 or 1.0), depending on sign per element. -inline Eigen::ArrayXd sign (const Eigen::ArrayXd& x) -{ - const int n = x.size(); - Eigen::ArrayXd retval(n); - for (int i = 0; i < n; ++i) { - retval[i] = x[i] < 0.0 ? -1.0 : (x[i] > 0.0 ? 1.0 : 0.0); - } - return retval; -} - -} // namespace Opm - -#endif // OPM_AUTODIFFHELPERS_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAd.cpp b/opm/polymer/fullyimplicit/BlackoilPropsAd.cpp deleted file mode 100644 index b09e21f7b..000000000 --- a/opm/polymer/fullyimplicit/BlackoilPropsAd.cpp +++ /dev/null @@ -1,647 +0,0 @@ -/* - Copyright 2013 SINTEF ICT, Applied Mathematics. - - This file is part of the Open Porous Media project (OPM). - - OPM is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OPM is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with OPM. If not, see . -*/ - -#include - -#include -#include -#include -#include -#include - -namespace Opm -{ - - // Making these typedef to make the code more readable. - typedef BlackoilPropsAd::ADB ADB; - typedef BlackoilPropsAd::V V; - typedef Eigen::Array Block; - - /// Constructor wrapping an opm-core black oil interface. - BlackoilPropsAd::BlackoilPropsAd(const BlackoilPropertiesInterface& props) - : props_(props), - pu_(props.phaseUsage()) - { - } - - //////////////////////////// - // Rock interface // - //////////////////////////// - - /// \return D, the number of spatial dimensions. - int BlackoilPropsAd::numDimensions() const - { - return props_.numDimensions(); - } - - /// \return N, the number of cells. - int BlackoilPropsAd::numCells() const - { - return props_.numCells(); - } - - /// \return Array of N porosity values. - const double* BlackoilPropsAd::porosity() const - { - return props_.porosity(); - } - - /// \return Array of ND^2 permeability values. - /// The D^2 permeability values for a cell are organized as a matrix, - /// which is symmetric (so ordering does not matter). - const double* BlackoilPropsAd::permeability() const - { - return props_.permeability(); - } - - - //////////////////////////// - // Fluid interface // - //////////////////////////// - - /// \return Number of active phases (also the number of components). - int BlackoilPropsAd::numPhases() const - { - return props_.numPhases(); - } - - /// \return Object describing the active phases. - PhaseUsage BlackoilPropsAd::phaseUsage() const - { - return props_.phaseUsage(); - } - - // ------ Density ------ - - /// Densities of stock components at surface conditions. - /// \return Array of 3 density values. - const double* BlackoilPropsAd::surfaceDensity() const - { - return props_.surfaceDensity(); - } - - - // ------ Viscosity ------ - - /// Water viscosity. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - V BlackoilPropsAd::muWat(const V& pw, - const Cells& cells) const - { - if (!pu_.phase_used[Water]) { - OPM_THROW(std::runtime_error, "Cannot call muWat(): water phase not present."); - } - const int n = cells.size(); - assert(pw.size() == n); - const int np = props_.numPhases(); - Block z = Block::Zero(n, np); - Block mu(n, np); - props_.viscosity(n, pw.data(), z.data(), cells.data(), mu.data(), 0); - return mu.col(pu_.phase_pos[Water]); - } - - /// Oil viscosity. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - V BlackoilPropsAd::muOil(const V& po, - const V& rs, - const Cells& cells) const - { - if (!pu_.phase_used[Oil]) { - OPM_THROW(std::runtime_error, "Cannot call muOil(): oil phase not present."); - } - const int n = cells.size(); - assert(po.size() == n); - const int np = props_.numPhases(); - Block z = Block::Zero(n, np); - if (pu_.phase_used[Gas]) { - // Faking a z with the right ratio: - // rs = zg/zo - z.col(pu_.phase_pos[Oil]) = V::Ones(n, 1); - z.col(pu_.phase_pos[Gas]) = rs; - } - Block mu(n, np); - props_.viscosity(n, po.data(), z.data(), cells.data(), mu.data(), 0); - return mu.col(pu_.phase_pos[Oil]); - } - - /// Gas viscosity. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - V BlackoilPropsAd::muGas(const V& pg, - const Cells& cells) const - { - if (!pu_.phase_used[Gas]) { - OPM_THROW(std::runtime_error, "Cannot call muGas(): gas phase not present."); - } - const int n = cells.size(); - assert(pg.size() == n); - const int np = props_.numPhases(); - Block z = Block::Zero(n, np); - Block mu(n, np); - props_.viscosity(n, pg.data(), z.data(), cells.data(), mu.data(), 0); - return mu.col(pu_.phase_pos[Gas]); - } - - /// Water viscosity. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - ADB BlackoilPropsAd::muWat(const ADB& pw, - const Cells& cells) const - { -#if 1 - return ADB::constant(muWat(pw.value(), cells), pw.blockPattern()); -#else - if (!pu_.phase_used[Water]) { - OPM_THROW(std::runtime_error, "Cannot call muWat(): water phase not present."); - } - const int n = cells.size(); - assert(pw.value().size() == n); - const int np = props_.numPhases(); - Block z = Block::Zero(n, np); - Block mu(n, np); - Block dmu(n, np); - props_.viscosity(n, pw.value().data(), z.data(), cells.data(), mu.data(), dmu.data()); - ADB::M dmu_diag = spdiag(dmu.col(pu_.phase_pos[Water])); - const int num_blocks = pw.numBlocks(); - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = dmu_diag * pw.derivative()[block]; - } - return ADB::function(mu.col(pu_.phase_pos[Water]), jacs); -#endif - } - - /// Oil viscosity. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - ADB BlackoilPropsAd::muOil(const ADB& po, - const ADB& rs, - const Cells& cells) const - { -#if 1 - return ADB::constant(muOil(po.value(), rs.value(), cells), po.blockPattern()); -#else - if (!pu_.phase_used[Oil]) { - OPM_THROW(std::runtime_error, "Cannot call muOil(): oil phase not present."); - } - const int n = cells.size(); - assert(po.value().size() == n); - const int np = props_.numPhases(); - Block z = Block::Zero(n, np); - if (pu_.phase_used[Gas]) { - // Faking a z with the right ratio: - // rs = zg/zo - z.col(pu_.phase_pos[Oil]) = V::Ones(n, 1); - z.col(pu_.phase_pos[Gas]) = rs.value(); - } - Block mu(n, np); - Block dmu(n, np); - props_.viscosity(n, po.value().data(), z.data(), cells.data(), mu.data(), dmu.data()); - ADB::M dmu_diag = spdiag(dmu.col(pu_.phase_pos[Oil])); - const int num_blocks = po.numBlocks(); - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - // For now, we deliberately ignore the derivative with respect to rs, - // since the BlackoilPropertiesInterface class does not evaluate it. - // We would add to the next line: + dmu_drs_diag * rs.derivative()[block] - jacs[block] = dmu_diag * po.derivative()[block]; - } - return ADB::function(mu.col(pu_.phase_pos[Oil]), jacs); -#endif - } - - /// Gas viscosity. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - ADB BlackoilPropsAd::muGas(const ADB& pg, - const Cells& cells) const - { -#if 1 - return ADB::constant(muGas(pg.value(), cells), pg.blockPattern()); -#else - if (!pu_.phase_used[Gas]) { - OPM_THROW(std::runtime_error, "Cannot call muGas(): gas phase not present."); - } - const int n = cells.size(); - assert(pg.value().size() == n); - const int np = props_.numPhases(); - Block z = Block::Zero(n, np); - Block mu(n, np); - Block dmu(n, np); - props_.viscosity(n, pg.value().data(), z.data(), cells.data(), mu.data(), dmu.data()); - ADB::M dmu_diag = spdiag(dmu.col(pu_.phase_pos[Gas])); - const int num_blocks = pg.numBlocks(); - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = dmu_diag * pg.derivative()[block]; - } - return ADB::function(mu.col(pu_.phase_pos[Gas]), jacs); -#endif - } - - - // ------ Formation volume factor (b) ------ - - // These methods all call the matrix() method, after which the variable - // (also) called 'matrix' contains, in each row, the A = RB^{-1} matrix for - // a cell. For three-phase black oil: - // A = [ bw 0 0 - // 0 bo 0 - // 0 b0*rs bw ] - // Where b = B^{-1}. - // Therefore, we extract the correct diagonal element, and are done. - // When we need the derivatives (w.r.t. p, since we don't do w.r.t. rs), - // we also get the following derivative matrix: - // A = [ dbw 0 0 - // 0 dbo 0 - // 0 db0*rs dbw ] - // Again, we just extract a diagonal element. - - /// Water formation volume factor. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - V BlackoilPropsAd::bWat(const V& pw, - const Cells& cells) const - { - if (!pu_.phase_used[Water]) { - OPM_THROW(std::runtime_error, "Cannot call bWat(): water phase not present."); - } - const int n = cells.size(); - assert(pw.size() == n); - const int np = props_.numPhases(); - Block z = Block::Zero(n, np); - Block matrix(n, np*np); - props_.matrix(n, pw.data(), z.data(), cells.data(), matrix.data(), 0); - const int wi = pu_.phase_pos[Water]; - return matrix.col(wi*np + wi); - } - - /// Oil formation volume factor. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - V BlackoilPropsAd::bOil(const V& po, - const V& rs, - const Cells& cells) const - { - if (!pu_.phase_used[Oil]) { - OPM_THROW(std::runtime_error, "Cannot call bOil(): oil phase not present."); - } - const int n = cells.size(); - assert(po.size() == n); - const int np = props_.numPhases(); - Block z = Block::Zero(n, np); - if (pu_.phase_used[Gas]) { - // Faking a z with the right ratio: - // rs = zg/zo - z.col(pu_.phase_pos[Oil]) = V::Ones(n, 1); - z.col(pu_.phase_pos[Gas]) = rs; - } - Block matrix(n, np*np); - props_.matrix(n, po.data(), z.data(), cells.data(), matrix.data(), 0); - const int oi = pu_.phase_pos[Oil]; - return matrix.col(oi*np + oi); - } - - /// Gas formation volume factor. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - V BlackoilPropsAd::bGas(const V& pg, - const Cells& cells) const - { - if (!pu_.phase_used[Gas]) { - OPM_THROW(std::runtime_error, "Cannot call bGas(): gas phase not present."); - } - const int n = cells.size(); - assert(pg.size() == n); - const int np = props_.numPhases(); - Block z = Block::Zero(n, np); - Block matrix(n, np*np); - props_.matrix(n, pg.data(), z.data(), cells.data(), matrix.data(), 0); - const int gi = pu_.phase_pos[Gas]; - return matrix.col(gi*np + gi); - } - - /// Water formation volume factor. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - ADB BlackoilPropsAd::bWat(const ADB& pw, - const Cells& cells) const - { - if (!pu_.phase_used[Water]) { - OPM_THROW(std::runtime_error, "Cannot call muWat(): water phase not present."); - } - const int n = cells.size(); - assert(pw.value().size() == n); - const int np = props_.numPhases(); - Block z = Block::Zero(n, np); - Block matrix(n, np*np); - Block dmatrix(n, np*np); - props_.matrix(n, pw.value().data(), z.data(), cells.data(), matrix.data(), dmatrix.data()); - const int phase_ind = pu_.phase_pos[Water]; - const int column = phase_ind*np + phase_ind; // Index of our sought diagonal column. - ADB::M db_diag = spdiag(dmatrix.col(column)); - const int num_blocks = pw.numBlocks(); - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = db_diag * pw.derivative()[block]; - } - return ADB::function(matrix.col(column), jacs); - } - - /// Oil formation volume factor. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - ADB BlackoilPropsAd::bOil(const ADB& po, - const ADB& rs, - const Cells& cells) const - { - if (!pu_.phase_used[Oil]) { - OPM_THROW(std::runtime_error, "Cannot call muOil(): oil phase not present."); - } - const int n = cells.size(); - assert(po.value().size() == n); - const int np = props_.numPhases(); - Block z = Block::Zero(n, np); - if (pu_.phase_used[Gas]) { - // Faking a z with the right ratio: - // rs = zg/zo - z.col(pu_.phase_pos[Oil]) = V::Ones(n, 1); - z.col(pu_.phase_pos[Gas]) = rs.value(); - } - Block matrix(n, np*np); - Block dmatrix(n, np*np); - props_.matrix(n, po.value().data(), z.data(), cells.data(), matrix.data(), dmatrix.data()); - const int phase_ind = pu_.phase_pos[Oil]; - const int column = phase_ind*np + phase_ind; // Index of our sought diagonal column. - ADB::M db_diag = spdiag(dmatrix.col(column)); - const int num_blocks = po.numBlocks(); - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - // For now, we deliberately ignore the derivative with respect to rs, - // since the BlackoilPropertiesInterface class does not evaluate it. - // We would add to the next line: + db_drs_diag * rs.derivative()[block] - jacs[block] = db_diag * po.derivative()[block]; - } - return ADB::function(matrix.col(column), jacs); - } - - /// Gas formation volume factor. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - ADB BlackoilPropsAd::bGas(const ADB& pg, - const Cells& cells) const - { - if (!pu_.phase_used[Gas]) { - OPM_THROW(std::runtime_error, "Cannot call muGas(): gas phase not present."); - } - const int n = cells.size(); - assert(pg.value().size() == n); - const int np = props_.numPhases(); - Block z = Block::Zero(n, np); - Block matrix(n, np*np); - Block dmatrix(n, np*np); - props_.matrix(n, pg.value().data(), z.data(), cells.data(), matrix.data(), dmatrix.data()); - const int phase_ind = pu_.phase_pos[Gas]; - const int column = phase_ind*np + phase_ind; // Index of our sought diagonal column. - ADB::M db_diag = spdiag(dmatrix.col(column)); - const int num_blocks = pg.numBlocks(); - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = db_diag * pg.derivative()[block]; - } - return ADB::function(matrix.col(column), jacs); - } - - - // ------ Rs bubble point curve ------ - - /// Bubble point curve for Rs as function of oil pressure. - /// \param[in] po Array of n oil pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n bubble point values for Rs. - V BlackoilPropsAd::rsMax(const V& po, - const Cells& cells) const - { - // Suppress warning about "unused parameters". - static_cast(po); - static_cast(cells); - - OPM_THROW(std::runtime_error, "Method rsMax() not implemented."); - } - - /// Bubble point curve for Rs as function of oil pressure. - /// \param[in] po Array of n oil pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n bubble point values for Rs. - ADB BlackoilPropsAd::rsMax(const ADB& po, - const Cells& cells) const - { - // Suppress warning about "unused parameters". - static_cast(po); - static_cast(cells); - - OPM_THROW(std::runtime_error, "Method rsMax() not implemented."); - } - - // ------ Relative permeability ------ - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] sg Array of n gas saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 3 elements, each an array of n relperm values, - /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. - std::vector BlackoilPropsAd::relperm(const V& sw, - const V& so, - const V& sg, - const Cells& cells) const - { - const int n = cells.size(); - const int np = props_.numPhases(); - Block s_all(n, np); - if (pu_.phase_used[Water]) { - assert(sw.size() == n); - s_all.col(pu_.phase_pos[Water]) = sw; - } - if (pu_.phase_used[Oil]) { - assert(so.size() == n); - s_all.col(pu_.phase_pos[Oil]) = so; - } - if (pu_.phase_used[Gas]) { - assert(sg.size() == n); - s_all.col(pu_.phase_pos[Gas]) = sg; - } - Block kr(n, np); - props_.relperm(n, s_all.data(), cells.data(), kr.data(), 0); - std::vector relperms; - relperms.reserve(3); - for (int phase = 0; phase < 3; ++phase) { - if (pu_.phase_used[phase]) { - relperms.emplace_back(kr.col(pu_.phase_pos[phase])); - } else { - relperms.emplace_back(); - } - } - return relperms; - } - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] sg Array of n gas saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 3 elements, each an array of n relperm values, - /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. - std::vector BlackoilPropsAd::relperm(const ADB& sw, - const ADB& so, - const ADB& sg, - const Cells& cells) const - { - const int n = cells.size(); - const int np = props_.numPhases(); - Block s_all(n, np); - if (pu_.phase_used[Water]) { - assert(sw.value().size() == n); - s_all.col(pu_.phase_pos[Water]) = sw.value(); - } - if (pu_.phase_used[Oil]) { - assert(so.value().size() == n); - s_all.col(pu_.phase_pos[Oil]) = so.value(); - } else { - OPM_THROW(std::runtime_error, "BlackoilPropsAd::relperm() assumes oil phase is active."); - } - if (pu_.phase_used[Gas]) { - assert(sg.value().size() == n); - s_all.col(pu_.phase_pos[Gas]) = sg.value(); - } - Block kr(n, np); - Block dkr(n, np*np); - props_.relperm(n, s_all.data(), cells.data(), kr.data(), dkr.data()); - const int num_blocks = so.numBlocks(); - std::vector relperms; - relperms.reserve(3); - typedef const ADB* ADBPtr; - ADBPtr s[3] = { &sw, &so, &sg }; - for (int phase1 = 0; phase1 < 3; ++phase1) { - if (pu_.phase_used[phase1]) { - const int phase1_pos = pu_.phase_pos[phase1]; - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = ADB::M(n, s[phase1]->derivative()[block].cols()); - } - for (int phase2 = 0; phase2 < 3; ++phase2) { - if (!pu_.phase_used[phase2]) { - continue; - } - const int phase2_pos = pu_.phase_pos[phase2]; - // Assemble dkr1/ds2. - const int column = phase1_pos + np*phase2_pos; // Recall: Fortran ordering from props_.relperm() - ADB::M dkr1_ds2_diag = spdiag(dkr.col(column)); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] += dkr1_ds2_diag * s[phase2]->derivative()[block]; - } - } - relperms.emplace_back(ADB::function(kr.col(phase1_pos), jacs)); - } else { - relperms.emplace_back(ADB::null()); - } - } - return relperms; - } - - std::vector BlackoilPropsAd::capPress(const ADB& sw, - const ADB& so, - const ADB& sg, - const Cells& cells) const - - { - const int numCells = cells.size(); - const int numActivePhases = numPhases(); - const int numBlocks = so.numBlocks(); - - Block activeSat(numCells, numActivePhases); - if (pu_.phase_used[Water]) { - assert(sw.value().size() == numCells); - activeSat.col(pu_.phase_pos[Water]) = sw.value(); - } - if (pu_.phase_used[Oil]) { - assert(so.value().size() == numCells); - activeSat.col(pu_.phase_pos[Oil]) = so.value(); - } else { - OPM_THROW(std::runtime_error, "BlackoilPropsAdFromDeck::relperm() assumes oil phase is active."); - } - if (pu_.phase_used[Gas]) { - assert(sg.value().size() == numCells); - activeSat.col(pu_.phase_pos[Gas]) = sg.value(); - } - - Block pc(numCells, numActivePhases); - Block dpc(numCells, numActivePhases*numActivePhases); - props_.capPress(numCells, activeSat.data(), cells.data(), pc.data(), dpc.data()); - - std::vector adbCapPressures; - adbCapPressures.reserve(3); - const ADB* s[3] = { &sw, &so, &sg }; - for (int phase1 = 0; phase1 < 3; ++phase1) { - if (pu_.phase_used[phase1]) { - const int phase1_pos = pu_.phase_pos[phase1]; - std::vector jacs(numBlocks); - for (int block = 0; block < numBlocks; ++block) { - jacs[block] = ADB::M(numCells, s[phase1]->derivative()[block].cols()); - } - for (int phase2 = 0; phase2 < 3; ++phase2) { - if (!pu_.phase_used[phase2]) - continue; - const int phase2_pos = pu_.phase_pos[phase2]; - // Assemble dpc1/ds2. - const int column = phase1_pos + numActivePhases*phase2_pos; // Recall: Fortran ordering from props_.relperm() - ADB::M dpc1_ds2_diag = spdiag(dpc.col(column)); - for (int block = 0; block < numBlocks; ++block) { - jacs[block] += dpc1_ds2_diag * s[phase2]->derivative()[block]; - } - } - adbCapPressures.emplace_back(ADB::function(pc.col(phase1_pos), jacs)); - } else { - adbCapPressures.emplace_back(ADB::null()); - } - } - return adbCapPressures; - } - -} // namespace Opm - diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAd.hpp b/opm/polymer/fullyimplicit/BlackoilPropsAd.hpp deleted file mode 100644 index f62793e84..000000000 --- a/opm/polymer/fullyimplicit/BlackoilPropsAd.hpp +++ /dev/null @@ -1,260 +0,0 @@ -/* - Copyright 2013 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_BLACKOILPROPSAD_HEADER_INCLUDED -#define OPM_BLACKOILPROPSAD_HEADER_INCLUDED - -#include -#include -#include - -namespace Opm -{ - - class BlackoilPropertiesInterface; - - /// This class implements the AD-adapted fluid interface for - /// three-phase black-oil. - /// - /// It is implemented by wrapping a BlackoilPropertiesInterface - /// object (the interface class defined in opm-core) and calling - /// its methods. This class does not implement rsMax() because the - /// required information is not available when wrapping a - /// BlackoilPropertiesInterface. Consequently, class - /// BlackoilPropsAd cannot be used to simulate problems involving - /// miscibility. - /// - /// Most methods are available in two overloaded versions, one - /// taking a constant vector and returning the same, and one - /// taking an AD type and returning the same. Derivatives are not - /// returned separately by any method, only implicitly with the AD - /// version of the methods. - class BlackoilPropsAd : public BlackoilPropsAdInterface - { - public: - /// Constructor wrapping an opm-core black oil interface. - explicit BlackoilPropsAd(const BlackoilPropertiesInterface& props); - - //////////////////////////// - // Rock interface // - //////////////////////////// - - /// \return D, the number of spatial dimensions. - int numDimensions() const; - - /// \return N, the number of cells. - int numCells() const; - - /// \return Array of N porosity values. - const double* porosity() const; - - /// \return Array of ND^2 permeability values. - /// The D^2 permeability values for a cell are organized as a matrix, - /// which is symmetric (so ordering does not matter). - const double* permeability() const; - - - //////////////////////////// - // Fluid interface // - //////////////////////////// - - typedef AutoDiffBlock ADB; - typedef ADB::V V; - typedef std::vector Cells; - - /// \return Number of active phases (also the number of components). - virtual int numPhases() const; - - /// \return Object describing the active phases. - virtual PhaseUsage phaseUsage() const; - - // ------ Canonical named indices for each phase ------ - - /// Canonical named indices for each phase. - enum PhaseIndex { Water = 0, Oil = 1, Gas = 2 }; - - - // ------ Density ------ - - /// Densities of stock components at surface conditions. - /// \return Array of 3 density values. - const double* surfaceDensity() const; - - - // ------ Viscosity ------ - - /// Water viscosity. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - V muWat(const V& pw, - const Cells& cells) const; - - /// Oil viscosity. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - V muOil(const V& po, - const V& rs, - const Cells& cells) const; - - /// Gas viscosity. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - V muGas(const V& pg, - const Cells& cells) const; - - /// Water viscosity. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - ADB muWat(const ADB& pw, - const Cells& cells) const; - - /// Oil viscosity. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - ADB muOil(const ADB& po, - const ADB& rs, - const Cells& cells) const; - - /// Gas viscosity. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - ADB muGas(const ADB& pg, - const Cells& cells) const; - - - // ------ Formation volume factor (b) ------ - - /// Water formation volume factor. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - V bWat(const V& pw, - const Cells& cells) const; - - /// Oil formation volume factor. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - V bOil(const V& po, - const V& rs, - const Cells& cells) const; - - /// Gas formation volume factor. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - V bGas(const V& pg, - const Cells& cells) const; - - /// Water formation volume factor. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - ADB bWat(const ADB& pw, - const Cells& cells) const; - - /// Oil formation volume factor. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - ADB bOil(const ADB& po, - const ADB& rs, - const Cells& cells) const; - - /// Gas formation volume factor. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - ADB bGas(const ADB& pg, - const Cells& cells) const; - - - // ------ Rs bubble point curve ------ - - /// Bubble point curve for Rs as function of oil pressure. - /// \param[in] po Array of n oil pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n bubble point values for Rs. - V rsMax(const V& po, - const Cells& cells) const; - - /// Bubble point curve for Rs as function of oil pressure. - /// \param[in] po Array of n oil pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n bubble point values for Rs. - ADB rsMax(const ADB& po, - const Cells& cells) const; - - - // ------ Relative permeability ------ - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] sg Array of n gas saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 3 elements, each an array of n relperm values, - /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. - std::vector relperm(const V& sw, - const V& so, - const V& sg, - const Cells& cells) const; - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] sg Array of n gas saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 3 elements, each an array of n relperm values, - /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. - std::vector relperm(const ADB& sw, - const ADB& so, - const ADB& sg, - const Cells& cells) const; - /// Capillary pressure for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] sg Array of n gas saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 3 elements, each an array of n capillary pressure values, - /// containing the offsets for each p_g, p_o, p_w. The capillary pressure between - /// two arbitrary phases alpha and beta is then given as p_alpha - p_beta. - std::vector capPress(const ADB& sw, - const ADB& so, - const ADB& sg, - const Cells& cells) const; - - private: - const BlackoilPropertiesInterface& props_; - PhaseUsage pu_; - }; - -} // namespace Opm - -#endif // OPM_BLACKOILPROPSAD_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.cpp b/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.cpp deleted file mode 100644 index 62120f4ca..000000000 --- a/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.cpp +++ /dev/null @@ -1,740 +0,0 @@ -/* - Copyright 2013 SINTEF ICT, Applied Mathematics. - - This file is part of the Open Porous Media project (OPM). - - OPM is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OPM is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with OPM. If not, see . -*/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Opm -{ - - // Making these typedef to make the code more readable. - typedef BlackoilPropsAdFromDeck::ADB ADB; - typedef BlackoilPropsAdFromDeck::V V; - typedef Eigen::Array Block; - enum { Aqua = BlackoilPhases::Aqua, - Liquid = BlackoilPhases::Liquid, - Vapour = BlackoilPhases::Vapour }; - - /// Constructor wrapping an opm-core black oil interface. - BlackoilPropsAdFromDeck::BlackoilPropsAdFromDeck(const EclipseGridParser& deck, - const UnstructuredGrid& grid, - const bool init_rock) - { - if (init_rock){ - rock_.init(deck, grid); - } - const int samples = 0; - const int region_number = 0; - - phase_usage_ = phaseUsageFromDeck(deck); - - // Surface densities. Accounting for different orders in eclipse and our code. - if (deck.hasField("DENSITY")) { - const std::vector& d = deck.getDENSITY().densities_[region_number]; - enum { ECL_oil = 0, ECL_water = 1, ECL_gas = 2 }; - if (phase_usage_.phase_used[Aqua]) { - densities_[phase_usage_.phase_pos[Aqua]] = d[ECL_water]; - } - if (phase_usage_.phase_used[Vapour]) { - densities_[phase_usage_.phase_pos[Vapour]] = d[ECL_gas]; - } - if (phase_usage_.phase_used[Liquid]) { - densities_[phase_usage_.phase_pos[Liquid]] = d[ECL_oil]; - } - } else { - OPM_THROW(std::runtime_error, "Input is missing DENSITY\n"); - } - - // Set the properties. - props_.resize(phase_usage_.num_phases); - // Water PVT - if (phase_usage_.phase_used[Aqua]) { - if (deck.hasField("PVTW")) { - props_[phase_usage_.phase_pos[Aqua]].reset(new SinglePvtConstCompr(deck.getPVTW().pvtw_)); - } else { - // Eclipse 100 default. - props_[phase_usage_.phase_pos[Aqua]].reset(new SinglePvtConstCompr(0.5*Opm::prefix::centi*Opm::unit::Poise)); - } - } - // Oil PVT - if (phase_usage_.phase_used[Liquid]) { - if (deck.hasField("PVDO")) { - if (samples > 0) { - props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDeadSpline(deck.getPVDO().pvdo_, samples)); - } else { - props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDead(deck.getPVDO().pvdo_)); - } - } else if (deck.hasField("PVTO")) { - - props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtLiveOil(deck.getPVTO().pvto_)); - } else if (deck.hasField("PVCDO")) { - props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtConstCompr(deck.getPVCDO().pvcdo_)); - } else { - OPM_THROW(std::runtime_error, "Input is missing PVDO or PVTO\n"); - } - } - // Gas PVT - if (phase_usage_.phase_used[Vapour]) { - if (deck.hasField("PVDG")) { - if (samples > 0) { - props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDeadSpline(deck.getPVDG().pvdg_, samples)); - } else { - props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDead(deck.getPVDG().pvdg_)); - } - // } else if (deck.hasField("PVTG")) { - // props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtLiveGas(deck.getPVTG().pvtg_)); - } else { - OPM_THROW(std::runtime_error, "Input is missing PVDG or PVTG\n"); - } - } - - SaturationPropsFromDeck* ptr - = new SaturationPropsFromDeck(); - satprops_.reset(ptr); - ptr->init(deck, grid, -1); - - if (phase_usage_.num_phases != satprops_->numPhases()) { - OPM_THROW(std::runtime_error, "BlackoilPropsAdFromDeck::BlackoilPropsAdFromDeck() - " - "Inconsistent number of phases in pvt data (" << phase_usage_.num_phases - << ") and saturation-dependent function data (" << satprops_->numPhases() << ")."); - } - } - - - //////////////////////////// - // Rock interface // - //////////////////////////// - - /// \return D, the number of spatial dimensions. - int BlackoilPropsAdFromDeck::numDimensions() const - { - return rock_.numDimensions(); - } - - /// \return N, the number of cells. - int BlackoilPropsAdFromDeck::numCells() const - { - return rock_.numCells(); - } - - /// \return Array of N porosity values. - const double* BlackoilPropsAdFromDeck::porosity() const - { - return rock_.porosity(); - } - - /// \return Array of ND^2 permeability values. - /// The D^2 permeability values for a cell are organized as a matrix, - /// which is symmetric (so ordering does not matter). - const double* BlackoilPropsAdFromDeck::permeability() const - { - return rock_.permeability(); - } - - - //////////////////////////// - // Fluid interface // - //////////////////////////// - - /// \return Number of active phases (also the number of components). - int BlackoilPropsAdFromDeck::numPhases() const - { - return phase_usage_.num_phases; - } - - /// \return Object describing the active phases. - PhaseUsage BlackoilPropsAdFromDeck::phaseUsage() const - { - return phase_usage_; - } - - // ------ Density ------ - - /// Densities of stock components at surface conditions. - /// \return Array of 3 density values. - const double* BlackoilPropsAdFromDeck::surfaceDensity() const - { - return densities_; - } - - - // ------ Viscosity ------ - - /// Water viscosity. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - V BlackoilPropsAdFromDeck::muWat(const V& pw, - const Cells& cells) const - { - if (!phase_usage_.phase_used[Water]) { - OPM_THROW(std::runtime_error, "Cannot call muWat(): water phase not present."); - } - const int n = cells.size(); - assert(pw.size() == n); - V mu(n); - V dmudp(n); - V dmudr(n); - const double* rs = 0; - - props_[phase_usage_.phase_pos[Water]]->mu(n, pw.data(), rs, - mu.data(), dmudp.data(), dmudr.data()); - return mu; - } - - /// Oil viscosity. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - V BlackoilPropsAdFromDeck::muOil(const V& po, - const V& rs, - const Cells& cells) const - { - if (!phase_usage_.phase_used[Oil]) { - OPM_THROW(std::runtime_error, "Cannot call muOil(): oil phase not present."); - } - const int n = cells.size(); - assert(po.size() == n); - V mu(n); - V dmudp(n); - V dmudr(n); - - props_[phase_usage_.phase_pos[Oil]]->mu(n, po.data(), rs.data(), - mu.data(), dmudp.data(), dmudr.data()); - return mu; - } - - /// Gas viscosity. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - V BlackoilPropsAdFromDeck::muGas(const V& pg, - const Cells& cells) const - { - if (!phase_usage_.phase_used[Gas]) { - OPM_THROW(std::runtime_error, "Cannot call muGas(): gas phase not present."); - } - const int n = cells.size(); - assert(pg.size() == n); - V mu(n); - V dmudp(n); - V dmudr(n); - const double* rs = 0; - - props_[phase_usage_.phase_pos[Gas]]->mu(n, pg.data(), rs, - mu.data(), dmudp.data(), dmudr.data()); - return mu; - } - - /// Water viscosity. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - ADB BlackoilPropsAdFromDeck::muWat(const ADB& pw, - const Cells& cells) const - { - if (!phase_usage_.phase_used[Water]) { - OPM_THROW(std::runtime_error, "Cannot call muWat(): water phase not present."); - } - const int n = cells.size(); - assert(pw.size() == n); - V mu(n); - V dmudp(n); - V dmudr(n); - const double* rs = 0; - - props_[phase_usage_.phase_pos[Water]]->mu(n, pw.value().data(), rs, - mu.data(), dmudp.data(), dmudr.data()); - ADB::M dmudp_diag = spdiag(dmudp); - const int num_blocks = pw.numBlocks(); - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = dmudp_diag * pw.derivative()[block]; - } - return ADB::function(mu, jacs); - } - - /// Oil viscosity. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - ADB BlackoilPropsAdFromDeck::muOil(const ADB& po, - const ADB& rs, - const Cells& cells) const - { - if (!phase_usage_.phase_used[Oil]) { - OPM_THROW(std::runtime_error, "Cannot call muOil(): oil phase not present."); - } - const int n = cells.size(); - assert(po.size() == n); - V mu(n); - V dmudp(n); - V dmudr(n); - - props_[phase_usage_.phase_pos[Oil]]->mu(n, po.value().data(), rs.value().data(), - mu.data(), dmudp.data(), dmudr.data()); - - ADB::M dmudp_diag = spdiag(dmudp); - ADB::M dmudr_diag = spdiag(dmudr); - const int num_blocks = po.numBlocks(); - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = dmudp_diag * po.derivative()[block] + dmudr_diag * rs.derivative()[block]; - } - return ADB::function(mu, jacs); - } - - /// Gas viscosity. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - ADB BlackoilPropsAdFromDeck::muGas(const ADB& pg, - const Cells& cells) const - { - if (!phase_usage_.phase_used[Gas]) { - OPM_THROW(std::runtime_error, "Cannot call muGas(): gas phase not present."); - } - const int n = cells.size(); - assert(pg.value().size() == n); - V mu(n); - V dmudp(n); - V dmudr(n); - const double* rs = 0; - - props_[phase_usage_.phase_pos[Gas]]->mu(n, pg.value().data(), rs, - mu.data(), dmudp.data(), dmudr.data()); - - ADB::M dmudp_diag = spdiag(dmudp); - const int num_blocks = pg.numBlocks(); - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = dmudp_diag * pg.derivative()[block]; - } - return ADB::function(mu, jacs); - } - - - // ------ Formation volume factor (b) ------ - - // These methods all call the matrix() method, after which the variable - // (also) called 'matrix' contains, in each row, the A = RB^{-1} matrix for - // a cell. For three-phase black oil: - // A = [ bw 0 0 - // 0 bo 0 - // 0 b0*rs bw ] - // Where b = B^{-1}. - // Therefore, we extract the correct diagonal element, and are done. - // When we need the derivatives (w.r.t. p, since we don't do w.r.t. rs), - // we also get the following derivative matrix: - // A = [ dbw 0 0 - // 0 dbo 0 - // 0 db0*rs dbw ] - // Again, we just extract a diagonal element. - - /// Water formation volume factor. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - V BlackoilPropsAdFromDeck::bWat(const V& pw, - const Cells& cells) const - { - if (!phase_usage_.phase_used[Water]) { - OPM_THROW(std::runtime_error, "Cannot call bWat(): water phase not present."); - } - const int n = cells.size(); - assert(pw.size() == n); - - V b(n); - V dbdp(n); - V dbdr(n); - const double* rs = 0; - - props_[phase_usage_.phase_pos[Water]]->b(n, pw.data(), rs, - b.data(), dbdp.data(), dbdr.data()); - - return b; - } - - /// Oil formation volume factor. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - V BlackoilPropsAdFromDeck::bOil(const V& po, - const V& rs, - const Cells& cells) const - { - if (!phase_usage_.phase_used[Oil]) { - OPM_THROW(std::runtime_error, "Cannot call bOil(): oil phase not present."); - } - const int n = cells.size(); - assert(po.size() == n); - - V b(n); - V dbdp(n); - V dbdr(n); - - props_[phase_usage_.phase_pos[Oil]]->b(n, po.data(), rs.data(), - b.data(), dbdp.data(), dbdr.data()); - - return b; - } - - /// Gas formation volume factor. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - V BlackoilPropsAdFromDeck::bGas(const V& pg, - const Cells& cells) const - { - if (!phase_usage_.phase_used[Gas]) { - OPM_THROW(std::runtime_error, "Cannot call bGas(): gas phase not present."); - } - const int n = cells.size(); - assert(pg.size() == n); - - V b(n); - V dbdp(n); - V dbdr(n); - const double* rs = 0; - - props_[phase_usage_.phase_pos[Gas]]->b(n, pg.data(), rs, - b.data(), dbdp.data(), dbdr.data()); - - return b; - } - - /// Water formation volume factor. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - ADB BlackoilPropsAdFromDeck::bWat(const ADB& pw, - const Cells& cells) const - { - if (!phase_usage_.phase_used[Water]) { - OPM_THROW(std::runtime_error, "Cannot call muWat(): water phase not present."); - } - const int n = cells.size(); - assert(pw.size() == n); - - V b(n); - V dbdp(n); - V dbdr(n); - const double* rs = 0; - - props_[phase_usage_.phase_pos[Water]]->b(n, pw.value().data(), rs, - b.data(), dbdp.data(), dbdr.data()); - - ADB::M dbdp_diag = spdiag(dbdp); - const int num_blocks = pw.numBlocks(); - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = dbdp_diag * pw.derivative()[block]; - } - return ADB::function(b, jacs); - } - - /// Oil formation volume factor. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - ADB BlackoilPropsAdFromDeck::bOil(const ADB& po, - const ADB& rs, - const Cells& cells) const - { - if (!phase_usage_.phase_used[Oil]) { - OPM_THROW(std::runtime_error, "Cannot call muOil(): oil phase not present."); - } - const int n = cells.size(); - assert(po.size() == n); - - V b(n); - V dbdp(n); - V dbdr(n); - - props_[phase_usage_.phase_pos[Oil]]->b(n, po.value().data(), rs.value().data(), - b.data(), dbdp.data(), dbdr.data()); - - ADB::M dbdp_diag = spdiag(dbdp); - ADB::M dbdr_diag = spdiag(dbdr); - const int num_blocks = po.numBlocks(); - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = dbdp_diag * po.derivative()[block] + dbdr_diag * rs.derivative()[block]; - } - return ADB::function(b, jacs); - } - - /// Gas formation volume factor. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - ADB BlackoilPropsAdFromDeck::bGas(const ADB& pg, - const Cells& cells) const - { - if (!phase_usage_.phase_used[Gas]) { - OPM_THROW(std::runtime_error, "Cannot call muGas(): gas phase not present."); - } - const int n = cells.size(); - assert(pg.size() == n); - - V b(n); - V dbdp(n); - V dbdr(n); - const double* rs = 0; - - props_[phase_usage_.phase_pos[Gas]]->b(n, pg.value().data(), rs, - b.data(), dbdp.data(), dbdr.data()); - - ADB::M dbdp_diag = spdiag(dbdp); - const int num_blocks = pg.numBlocks(); - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = dbdp_diag * pg.derivative()[block]; - } - return ADB::function(b, jacs); - } - - - - // ------ Rs bubble point curve ------ - - /// Bubble point curve for Rs as function of oil pressure. - /// \param[in] po Array of n oil pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n bubble point values for Rs. - V BlackoilPropsAdFromDeck::rsMax(const V& po, - const Cells& cells) const - { - if (!phase_usage_.phase_used[Oil]) { - OPM_THROW(std::runtime_error, "Cannot call rsMax(): oil phase not present."); - } - const int n = cells.size(); - assert(po.size() == n); - V rbub(n); - V drbubdp(n); - props_[Oil]->rsSat(n, po.data(), rbub.data(), drbubdp.data()); - return rbub; - } - - /// Bubble point curve for Rs as function of oil pressure. - /// \param[in] po Array of n oil pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n bubble point values for Rs. - ADB BlackoilPropsAdFromDeck::rsMax(const ADB& po, - const Cells& cells) const - { - if (!phase_usage_.phase_used[Oil]) { - OPM_THROW(std::runtime_error, "Cannot call rsMax(): oil phase not present."); - } - const int n = cells.size(); - assert(po.size() == n); - V rbub(n); - V drbubdp(n); - props_[Oil]->rsSat(n, po.value().data(), rbub.data(), drbubdp.data()); - ADB::M drbubdp_diag = spdiag(drbubdp); - const int num_blocks = po.numBlocks(); - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = drbubdp_diag * po.derivative()[block]; - } - return ADB::function(rbub, jacs); - } - - // ------ Relative permeability ------ - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] sg Array of n gas saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 3 elements, each an array of n relperm values, - /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. - std::vector BlackoilPropsAdFromDeck::relperm(const V& sw, - const V& so, - const V& sg, - const Cells& cells) const - { - const int n = cells.size(); - const int np = numPhases(); - Block s_all(n, np); - if (phase_usage_.phase_used[Water]) { - assert(sw.size() == n); - s_all.col(phase_usage_.phase_pos[Water]) = sw; - } - if (phase_usage_.phase_used[Oil]) { - assert(so.size() == n); - s_all.col(phase_usage_.phase_pos[Oil]) = so; - } - if (phase_usage_.phase_used[Gas]) { - assert(sg.size() == n); - s_all.col(phase_usage_.phase_pos[Gas]) = sg; - } - Block kr(n, np); - satprops_->relperm(n, s_all.data(), cells.data(), kr.data(), 0); - std::vector relperms; - relperms.reserve(3); - for (int phase = 0; phase < 3; ++phase) { - if (phase_usage_.phase_used[phase]) { - relperms.emplace_back(kr.col(phase_usage_.phase_pos[phase])); - } else { - relperms.emplace_back(); - } - } - return relperms; - } - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] sg Array of n gas saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 3 elements, each an array of n relperm values, - /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. - std::vector BlackoilPropsAdFromDeck::relperm(const ADB& sw, - const ADB& so, - const ADB& sg, - const Cells& cells) const - { - const int n = cells.size(); - const int np = numPhases(); - Block s_all(n, np); - if (phase_usage_.phase_used[Water]) { - assert(sw.value().size() == n); - s_all.col(phase_usage_.phase_pos[Water]) = sw.value(); - } - if (phase_usage_.phase_used[Oil]) { - assert(so.value().size() == n); - s_all.col(phase_usage_.phase_pos[Oil]) = so.value(); - } else { - OPM_THROW(std::runtime_error, "BlackoilPropsAdFromDeck::relperm() assumes oil phase is active."); - } - if (phase_usage_.phase_used[Gas]) { - assert(sg.value().size() == n); - s_all.col(phase_usage_.phase_pos[Gas]) = sg.value(); - } - Block kr(n, np); - Block dkr(n, np*np); - satprops_->relperm(n, s_all.data(), cells.data(), kr.data(), dkr.data()); - const int num_blocks = so.numBlocks(); - std::vector relperms; - relperms.reserve(3); - typedef const ADB* ADBPtr; - ADBPtr s[3] = { &sw, &so, &sg }; - for (int phase1 = 0; phase1 < 3; ++phase1) { - if (phase_usage_.phase_used[phase1]) { - const int phase1_pos = phase_usage_.phase_pos[phase1]; - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = ADB::M(n, s[phase1]->derivative()[block].cols()); - } - for (int phase2 = 0; phase2 < 3; ++phase2) { - if (!phase_usage_.phase_used[phase2]) { - continue; - } - const int phase2_pos = phase_usage_.phase_pos[phase2]; - // Assemble dkr1/ds2. - const int column = phase1_pos + np*phase2_pos; // Recall: Fortran ordering from props_.relperm() - ADB::M dkr1_ds2_diag = spdiag(dkr.col(column)); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] += dkr1_ds2_diag * s[phase2]->derivative()[block]; - } - } - relperms.emplace_back(ADB::function(kr.col(phase1_pos), jacs)); - } else { - relperms.emplace_back(ADB::null()); - } - } - return relperms; - } - - std::vector BlackoilPropsAdFromDeck::capPress(const ADB& sw, - const ADB& so, - const ADB& sg, - const Cells& cells) const - { - const int numCells = cells.size(); - const int numActivePhases = numPhases(); - const int numBlocks = so.numBlocks(); - - Block activeSat(numCells, numActivePhases); - if (phase_usage_.phase_used[Water]) { - assert(sw.value().size() == numCells); - activeSat.col(phase_usage_.phase_pos[Water]) = sw.value(); - } - if (phase_usage_.phase_used[Oil]) { - assert(so.value().size() == numCells); - activeSat.col(phase_usage_.phase_pos[Oil]) = so.value(); - } else { - OPM_THROW(std::runtime_error, "BlackoilPropsAdFromDeck::relperm() assumes oil phase is active."); - } - if (phase_usage_.phase_used[Gas]) { - assert(sg.value().size() == numCells); - activeSat.col(phase_usage_.phase_pos[Gas]) = sg.value(); - } - - Block pc(numCells, numActivePhases); - Block dpc(numCells, numActivePhases*numActivePhases); - satprops_->capPress(numCells, activeSat.data(), cells.data(), pc.data(), dpc.data()); - - std::vector adbCapPressures; - adbCapPressures.reserve(3); - const ADB* s[3] = { &sw, &so, &sg }; - for (int phase1 = 0; phase1 < 3; ++phase1) { - if (phase_usage_.phase_used[phase1]) { - const int phase1_pos = phase_usage_.phase_pos[phase1]; - std::vector jacs(numBlocks); - for (int block = 0; block < numBlocks; ++block) { - jacs[block] = ADB::M(numCells, s[phase1]->derivative()[block].cols()); - } - for (int phase2 = 0; phase2 < 3; ++phase2) { - if (!phase_usage_.phase_used[phase2]) - continue; - const int phase2_pos = phase_usage_.phase_pos[phase2]; - // Assemble dpc1/ds2. - const int column = phase1_pos + numActivePhases*phase2_pos; // Recall: Fortran ordering from props_.relperm() - ADB::M dpc1_ds2_diag = spdiag(dpc.col(column)); - for (int block = 0; block < numBlocks; ++block) { - jacs[block] += dpc1_ds2_diag * s[phase2]->derivative()[block]; - } - } - adbCapPressures.emplace_back(ADB::function(pc.col(phase1_pos), jacs)); - } else { - adbCapPressures.emplace_back(ADB::null()); - } - } - return adbCapPressures; - } - -} // namespace Opm - diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.hpp b/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.hpp deleted file mode 100644 index d584ffb90..000000000 --- a/opm/polymer/fullyimplicit/BlackoilPropsAdFromDeck.hpp +++ /dev/null @@ -1,266 +0,0 @@ -/* - Copyright 2013 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_BLACKOILPROPSADFROMDECK_HEADER_INCLUDED -#define OPM_BLACKOILPROPSADFROMDECK_HEADER_INCLUDED - -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace Opm -{ - - class SinglePvtInterface; - - /// This class implements the AD-adapted fluid interface for - /// three-phase black-oil. It requires an input deck from which it - /// reads all relevant property data. - /// - /// Most methods are available in two overloaded versions, one - /// taking a constant vector and returning the same, and one - /// taking an AD type and returning the same. Derivatives are not - /// returned separately by any method, only implicitly with the AD - /// version of the methods. - class BlackoilPropsAdFromDeck : public BlackoilPropsAdInterface - { - public: - /// Constructor wrapping an opm-core black oil interface. - BlackoilPropsAdFromDeck(const EclipseGridParser& deck, - const UnstructuredGrid& grid, - const bool init_rock = true ); - - //////////////////////////// - // Rock interface // - //////////////////////////// - - /// \return D, the number of spatial dimensions. - int numDimensions() const; - - /// \return N, the number of cells. - int numCells() const; - - /// \return Array of N porosity values. - const double* porosity() const; - - /// \return Array of ND^2 permeability values. - /// The D^2 permeability values for a cell are organized as a matrix, - /// which is symmetric (so ordering does not matter). - const double* permeability() const; - - - //////////////////////////// - // Fluid interface // - //////////////////////////// - - typedef AutoDiffBlock ADB; - typedef ADB::V V; - typedef std::vector Cells; - - /// \return Number of active phases (also the number of components). - int numPhases() const; - - /// \return Object describing the active phases. - PhaseUsage phaseUsage() const; - - // ------ Canonical named indices for each phase ------ - - /// Canonical named indices for each phase. - enum PhaseIndex { Water = 0, Oil = 1, Gas = 2 }; - - - // ------ Density ------ - - /// Densities of stock components at surface conditions. - /// \return Array of 3 density values. - const double* surfaceDensity() const; - - - // ------ Viscosity ------ - - /// Water viscosity. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - V muWat(const V& pw, - const Cells& cells) const; - - /// Oil viscosity. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - V muOil(const V& po, - const V& rs, - const Cells& cells) const; - - /// Gas viscosity. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - V muGas(const V& pg, - const Cells& cells) const; - - /// Water viscosity. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - ADB muWat(const ADB& pw, - const Cells& cells) const; - - /// Oil viscosity. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - ADB muOil(const ADB& po, - const ADB& rs, - const Cells& cells) const; - - /// Gas viscosity. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - ADB muGas(const ADB& pg, - const Cells& cells) const; - - - // ------ Formation volume factor (b) ------ - - /// Water formation volume factor. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - V bWat(const V& pw, - const Cells& cells) const; - - /// Oil formation volume factor. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - V bOil(const V& po, - const V& rs, - const Cells& cells) const; - - /// Gas formation volume factor. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - V bGas(const V& pg, - const Cells& cells) const; - - /// Water formation volume factor. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - ADB bWat(const ADB& pw, - const Cells& cells) const; - - /// Oil formation volume factor. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - ADB bOil(const ADB& po, - const ADB& rs, - const Cells& cells) const; - - /// Gas formation volume factor. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - ADB bGas(const ADB& pg, - const Cells& cells) const; - - - // ------ Rs bubble point curve ------ - - /// Bubble point curve for Rs as function of oil pressure. - /// \param[in] po Array of n oil pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n bubble point values for Rs. - V rsMax(const V& po, - const Cells& cells) const; - - /// Bubble point curve for Rs as function of oil pressure. - /// \param[in] po Array of n oil pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n bubble point values for Rs. - ADB rsMax(const ADB& po, - const Cells& cells) const; - - - // ------ Relative permeability ------ - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] sg Array of n gas saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 3 elements, each an array of n relperm values, - /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. - std::vector relperm(const V& sw, - const V& so, - const V& sg, - const Cells& cells) const; - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] sg Array of n gas saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 3 elements, each an array of n relperm values, - /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. - std::vector relperm(const ADB& sw, - const ADB& so, - const ADB& sg, - const Cells& cells) const; - /// Capillary pressure for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] sg Array of n gas saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 3 elements, each an array of n capillary pressure values, - /// containing the offsets for each p_g, p_o, p_w. The capillary pressure between - /// two arbitrary phases alpha and beta is then given as p_alpha - p_beta. - std::vector capPress(const ADB& sw, - const ADB& so, - const ADB& sg, - const Cells& cells) const; - - - private: - RockFromDeck rock_; - boost::scoped_ptr satprops_; - PhaseUsage phase_usage_; - std::vector > props_; - double densities_[BlackoilPhases::MaxNumPhases]; - }; - - -} // namespace Opm - -#endif // OPM_BLACKOILPROPSADFROMDECK_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAdInterface.cpp b/opm/polymer/fullyimplicit/BlackoilPropsAdInterface.cpp deleted file mode 100644 index f50318245..000000000 --- a/opm/polymer/fullyimplicit/BlackoilPropsAdInterface.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - Copyright 2013 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 - -Opm::BlackoilPropsAdInterface::~BlackoilPropsAdInterface() -{ -} diff --git a/opm/polymer/fullyimplicit/BlackoilPropsAdInterface.hpp b/opm/polymer/fullyimplicit/BlackoilPropsAdInterface.hpp deleted file mode 100644 index 153d993aa..000000000 --- a/opm/polymer/fullyimplicit/BlackoilPropsAdInterface.hpp +++ /dev/null @@ -1,265 +0,0 @@ -/* - Copyright 2013 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_BLACKOILPROPSADINTERFACE_HEADER_INCLUDED -#define OPM_BLACKOILPROPSADINTERFACE_HEADER_INCLUDED - -#include -#include - -namespace Opm -{ - - /// This class is intended to present a fluid interface for - /// three-phase black-oil that is easy to use with the AD-using - /// simulators. - /// - /// Most methods are available in two overloaded versions, one - /// taking a constant vector and returning the same, and one - /// taking an AD type and returning the same. Derivatives are not - /// returned separately by any method, only implicitly with the AD - /// version of the methods. - class BlackoilPropsAdInterface - { - public: - /// Virtual destructor for inheritance. - virtual ~BlackoilPropsAdInterface(); - - //////////////////////////// - // Rock interface // - //////////////////////////// - - /// \return D, the number of spatial dimensions. - virtual int numDimensions() const = 0; - - /// \return N, the number of cells. - virtual int numCells() const = 0; - - /// \return Array of N porosity values. - virtual const double* porosity() const = 0; - - /// \return Array of ND^2 permeability values. - /// The D^2 permeability values for a cell are organized as a matrix, - /// which is symmetric (so ordering does not matter). - virtual const double* permeability() const = 0; - - - //////////////////////////// - // Fluid interface // - //////////////////////////// - - typedef AutoDiffBlock ADB; - typedef ADB::V V; - typedef ADB::M M; - typedef std::vector Cells; - - /// \return Number of active phases (also the number of components). - virtual int numPhases() const = 0; - - /// \return Object describing the active phases. - virtual PhaseUsage phaseUsage() const = 0; - - // ------ Canonical named indices for each phase ------ - - /// Canonical named indices for each phase. - enum PhaseIndex { Water = 0, Oil = 1, Gas = 2 }; - - // ------ Density ------ - - /// Densities of stock components at surface conditions. - /// \return Array of 3 density values. - virtual const double* surfaceDensity() const = 0; - - - // ------ Viscosity ------ - - /// Water viscosity. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - virtual - V muWat(const V& pw, - const Cells& cells) const = 0; - - /// Oil viscosity. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - virtual - V muOil(const V& po, - const V& rs, - const Cells& cells) const = 0; - - /// Gas viscosity. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - virtual - V muGas(const V& pg, - const Cells& cells) const = 0; - - /// Water viscosity. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - virtual - ADB muWat(const ADB& pw, - const Cells& cells) const = 0; - - /// Oil viscosity. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - virtual - ADB muOil(const ADB& po, - const ADB& rs, - const Cells& cells) const = 0; - - /// Gas viscosity. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n viscosity values. - virtual - ADB muGas(const ADB& pg, - const Cells& cells) const = 0; - - - // ------ Formation volume factor (b) ------ - - /// Water formation volume factor. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - virtual - V bWat(const V& pw, - const Cells& cells) const = 0; - - /// Oil formation volume factor. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - virtual - V bOil(const V& po, - const V& rs, - const Cells& cells) const = 0; - - /// Gas formation volume factor. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - virtual - V bGas(const V& pg, - const Cells& cells) const = 0; - - /// Water formation volume factor. - /// \param[in] pw Array of n water pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - virtual - ADB bWat(const ADB& pw, - const Cells& cells) const = 0; - - /// Oil formation volume factor. - /// \param[in] po Array of n oil pressure values. - /// \param[in] rs Array of n gas solution factor values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - virtual - ADB bOil(const ADB& po, - const ADB& rs, - const Cells& cells) const = 0; - - /// Gas formation volume factor. - /// \param[in] pg Array of n gas pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n formation volume factor values. - virtual - ADB bGas(const ADB& pg, - const Cells& cells) const = 0; - - - // ------ Rs bubble point curve ------ - - /// Bubble point curve for Rs as function of oil pressure. - /// \param[in] po Array of n oil pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n bubble point values for Rs. - virtual - V rsMax(const V& po, - const Cells& cells) const = 0; - - /// Bubble point curve for Rs as function of oil pressure. - /// \param[in] po Array of n oil pressure values. - /// \param[in] cells Array of n cell indices to be associated with the pressure values. - /// \return Array of n bubble point values for Rs. - virtual - ADB rsMax(const ADB& po, - const Cells& cells) const = 0; - - // ------ Relative permeability ------ - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] sg Array of n gas saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 3 elements, each an array of n relperm values, - /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. - virtual - std::vector relperm(const V& sw, - const V& so, - const V& sg, - const Cells& cells) const = 0; - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] sg Array of n gas saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 3 elements, each an array of n relperm values, - /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. - virtual - std::vector relperm(const ADB& sw, - const ADB& so, - const ADB& sg, - const Cells& cells) const = 0; - - - /// Capillary pressure for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] sg Array of n gas saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 3 elements, each an array of n capillary pressure values, - /// containing the offsets for each p_g, p_o, p_w. The capillary pressure between - /// two arbitrary phases alpha and beta is then given as p_alpha - p_beta. - virtual - std::vector capPress(const ADB& sw, - const ADB& so, - const ADB& sg, - const Cells& cells) const = 0; - - }; - -} // namespace Opm - -#endif // OPM_BLACKOILPROPSADINTERFACE_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index 74b0c21af..5488a13b2 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -1,5 +1,6 @@ /* - Copyright 2013 SINTEF ICT, Applied Mathematics. + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2013 STATOIL. This file is part of the Open Porous Media project (OPM). @@ -20,10 +21,10 @@ #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp index 93199e5a7..4da5840f2 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp @@ -21,9 +21,9 @@ #ifndef OPM_FULLYIMPLICITBLACKOILSOLVER_HEADER_INCLUDED #define OPM_FULLYIMPLICITBLACKOILSOLVER_HEADER_INCLUDED -#include -#include -#include +#include +#include +#include #include #include diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index 926101eb8..2e5bd8880 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -21,9 +21,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include #include #include diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp index c050f0468..b1d09c7ff 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp @@ -21,9 +21,9 @@ #ifndef OPM_FULLYIMPLICITTWOPHASEPOLYMERSOLVER_HEADER_INCLUDED #define OPM_FULLYIMPLICITTWOPHASEPOLYMERSOLVER_HEADER_INCLUDED -#include -#include -#include +#include +#include +#include #include #include #include diff --git a/opm/polymer/fullyimplicit/GeoProps.hpp b/opm/polymer/fullyimplicit/GeoProps.hpp deleted file mode 100644 index 112cbc1de..000000000 --- a/opm/polymer/fullyimplicit/GeoProps.hpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - Copyright 2013 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_GEOPROPS_HEADER_INCLUDED -#define OPM_GEOPROPS_HEADER_INCLUDED - -#include -#include -#include - -namespace Opm -{ - - /// Class containing static geological properties that are - /// derived from grid and petrophysical properties: - /// - pore volume - /// - transmissibilities - /// - gravity potentials - class DerivedGeology - { - public: - typedef Eigen::ArrayXd Vector; - - /// Construct contained derived geological properties - /// from grid and property information. - template - DerivedGeology(const UnstructuredGrid& grid, - const Props& props , - const double* grav = 0) - : pvol_ (grid.number_of_cells) - , trans_(grid.number_of_faces) - , gpot_ (Vector::Zero(grid.cell_facepos[ grid.number_of_cells ], 1)) - , z_(grid.number_of_cells) - { - // Pore volume - const typename Vector::Index nc = grid.number_of_cells; - std::transform(grid.cell_volumes, grid.cell_volumes + nc, - props.porosity(), pvol_.data(), - std::multiplies()); - - // Transmissibility - Vector htrans(grid.cell_facepos[nc]); - UnstructuredGrid* ug = const_cast(& grid); - tpfa_htrans_compute(ug, props.permeability(), htrans.data()); - tpfa_trans_compute (ug, htrans.data() , trans_.data()); - - // Compute z coordinates - for (int c = 0; c. -*/ - -#include -#include -#include -#include -#include -#include - -namespace Opm -{ - /// Constructor. - IncompPropsAdBasic::IncompPropsAdBasic(const parameter::ParameterGroup& param, - const int dim, - const int num_cells) - { - double poro = param.getDefault("porosity", 1.0); - using namespace Opm::unit; - using namespace Opm::prefix; - double perm = param.getDefault("permeability", 100) * milli * darcy; - rock_.init(dim, num_cells, poro, perm); - pvt_.init(param); - satprops_.init(param); - if (pvt_.numPhases() != satprops_.numPhases()) { - OPM_THROW(std::runtime_error, "IncompPropsAdBasic::IncompPropsAdBasic() - Inconsistent number of phases in pvt data (" - << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); - } - viscosity_.resize(pvt_.numPhases()); - pvt_.mu(1, 0, 0, &viscosity_[0]); - } - - /// Constructor. - IncompPropsAdBasic::IncompPropsAdBasic(const int num_phases, - const SaturationPropsBasic::RelPermFunc& relpermfunc, - const std::vector& rho, - const std::vector& mu, - const double por, //porosity - const double perm, - const int dim, - const int num_cells) - { - rock_.init(dim, num_cells, por, perm); - pvt_.init(num_phases, rho, mu); - satprops_.init(num_phases, relpermfunc); - if (pvt_.numPhases() != satprops_.numPhases()) { - OPM_THROW(std::runtime_error, "IncompPropsAdBasic::IncompPropsAdBasic() - Inconsistent number of phases in pvt data (" - << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); - } - viscosity_.resize(pvt_.numPhases()); - pvt_.mu(1, 0, 0, &viscosity_[0]); - } - - /// Destructor. - IncompPropsAdBasic::~IncompPropsAdBasic() - { - } - - //////////////////////////// - // Rock interface // - //////////////////////////// - - /// \return D, the number of spatial dimensions. - int IncompPropsAdBasic::numDimensions() const - { - return rock_.numDimensions(); - } - - /// \return N, the number of cells. - int IncompPropsAdBasic::numCells() const - { - return rock_.numCells(); - } - - /// \return Array of N porosity values. - const double* IncompPropsAdBasic::porosity() const - { - return rock_.porosity(); - } - - /// \return Array of ND^2 permeability values. - /// The D^2 permeability values for a cell are organized as a matrix, - /// which is symmetric (so ordering does not matter). - const double* IncompPropsAdBasic::permeability() const - { - return rock_.permeability(); - } - - - //////////////////////////// - // Fluid interface // - //////////////////////////// - - /// \return P, the number of phases (also the number of components). - int IncompPropsAdBasic::numPhases() const - { - return pvt_.numPhases(); - } - - /// \return Array of P viscosity values. - const double* IncompPropsAdBasic::viscosity() const - { - return &viscosity_[0]; - } - - /// Densities of fluid phases at reservoir conditions. - /// \return Array of P density values. - const double* IncompPropsAdBasic::density() const - { - return pvt_.surfaceDensities(); - } - - /// Densities of fluid phases at surface conditions. - /// \return Array of P density values. - const double* IncompPropsAdBasic::surfaceDensity() const - { - return pvt_.surfaceDensities(); - } - - typedef IncompPropsAdBasic::ADB ADB; - typedef IncompPropsAdBasic::V V; - typedef Eigen::Array Block; - typedef std::vector Cells; - - // ------ Relative permeability ------ - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 2 elements, each an array of n relperm values, - /// containing krw, kro. - std::vector - IncompPropsAdBasic::relperm(const V& sw, - const V& so, - const Cells& cells) const - { - const int n = cells.size(); - const int np = numPhases(); - Block s_all(n, np); - assert(sw.size() == n && so.size() == n); - s_all.col(0) = sw; - s_all.col(1) = so; - Block kr(n, np); -// satprops_.relperm(n, s_all.data(), cells.data(), kr.data(), 0); - satprops_.relperm(n, s_all.data(), kr.data(), 0); - - std::vector relperms; - relperms.reserve(2); - for (int phase = 0; phase < 2; ++phase) { - relperms.emplace_back(kr.col(phase)); - } - return relperms; - } - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 2 elements, each an array of n relperm values, - /// containing krw, kro. - std::vector - IncompPropsAdBasic::relperm(const ADB& sw, - const ADB& so, - const Cells& cells) const - { - const int n = cells.size(); - const int np = numPhases(); - Block s_all(n, np); - assert(sw.size() == n && so.size() == n); - s_all.col(0) = sw.value(); - s_all.col(1) = so.value(); - Block kr(n, np); - Block dkr(n, np*np); -// satprops_.relperm(n, s_all.data(), cells.data(), kr.data(), dkr.data()); - satprops_.relperm(n, s_all.data(), kr.data(), dkr.data()); - const int num_blocks = so.numBlocks(); - std::vector relperms; - relperms.reserve(2); - typedef const ADB* ADBPtr; - ADBPtr s[2] = { &sw, &so }; - for (int phase1 = 0; phase1 < 2; ++phase1) { - const int phase1_pos = phase1; - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = ADB::M(n, s[phase1]->derivative()[block].cols()); - } - for (int phase2 = 0; phase2 < 2; ++phase2) { - const int phase2_pos = phase2; - // Assemble dkr1/ds2. - const int column = phase1_pos + np*phase2_pos; // Recall: Fortran ordering from props_.relperm() - ADB::M dkr1_ds2_diag = spdiag(dkr.col(column)); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] += dkr1_ds2_diag * s[phase2]->derivative()[block]; - } - } - relperms.emplace_back(ADB::function(kr.col(phase1_pos), jacs)); - } - return relperms; - } - -} //namespace Opm diff --git a/opm/polymer/fullyimplicit/IncompPropsAdBasic.hpp b/opm/polymer/fullyimplicit/IncompPropsAdBasic.hpp deleted file mode 100644 index 5a0a46c66..000000000 --- a/opm/polymer/fullyimplicit/IncompPropsAdBasic.hpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL - - 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_INCOMPPROPSADBASIC_HEADER_INCLUDED -#define OPM_INCOMPPROPSADBASIC_HEADER_INCLUDED - -#include -#include -#include -#include -#include - -namespace Opm -{ - /// This class implements the AD-adapted fluid interface for - /// two-phase oil-water. It requires an input deck from which it - /// reads all relevant property data. - /// - /// Most methods are available in two overloaded versions, one - /// taking a constant vector and returning the same, and one - /// taking an AD type and returning the same. Derivatives are not - /// returned separately by any method, only implicitly with the AD - /// version of the methods. - class IncompPropsAdBasic : public IncompPropsAdInterface - { - public: - /// Constructor. - IncompPropsAdBasic(const parameter::ParameterGroup& param, - const int dim, - const int num_cells); - - /// Constructor. - IncompPropsAdBasic(const int num_phases, - const SaturationPropsBasic::RelPermFunc& relpermfunc, - const std::vector& rho, - const std::vector& mu, - const double porosity, - const double permeability, - const int dim, - const int num_cells); - /// Destructor. - ~IncompPropsAdBasic(); - - //////////////////////////// - // Rock interface // - //////////////////////////// - - /// \return D, the number of spatial dimensions. - int numDimensions() const; - - /// \return N, the number of cells. - int numCells() const; - - /// \return Array of N porosity values. - const double* porosity() const; - - /// \return Array of ND^2 permeability values. - /// The D^2 permeability values for a cell are organized as a matrix, - /// which is symmetric (so ordering does not matter). - const double* permeability() const; - - //////////////////////////// - // Fluid interface // - //////////////////////////// - - typedef AutoDiffBlock ADB; - typedef ADB::V V; - - /// \return P, Number of active phases (also the number of components). - int numPhases() const; - - /// \return Array of P viscosity values. - const double* viscosity() const; - - /// Densities of fluid phases at reservoir conditions. - /// \return Array of P density values. - const double* density() const; - - /// Densities of fluid phases at surface conditions. - /// \return Array of P density values. - const double* surfaceDensity() const; - - // ------ Relative permeability ------ - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 2 elements, each an array of n relperm values, - /// containing krw, kro. - std::vector relperm(const V& sw, - const V& so, - const std::vector& cells) const; - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 2 elements, each an array of n relperm values, - /// containing krw, kro. - std::vector relperm(const ADB& sw, - const ADB& so, - const std::vector& cells) const; - private: - RockBasic rock_; - PvtPropertiesBasic pvt_; - SaturationPropsBasic satprops_; - std::vector viscosity_; - }; -} //namespace Opm - -#endif // OPM_INCOMPPROPSADBASIC_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp deleted file mode 100644 index 90a10c67e..000000000 --- a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.cpp +++ /dev/null @@ -1,238 +0,0 @@ -/* - Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL. - - This file is part of the Open Porous Media project (OPM). - - OPM is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OPM is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with OPM. If not, see . -*/ - -#include -#include -#include -#include -#include - -namespace Opm -{ - /// Constructor wrapping an opm-core two-phase interface. - IncompPropsAdFromDeck::IncompPropsAdFromDeck(const EclipseGridParser& deck, - const UnstructuredGrid& grid) - { - rock_.init(deck, grid); - pvt_.init(deck); - satprops_.init(deck, grid, 200); - if (pvt_.numPhases() != satprops_.numPhases()) { - OPM_THROW(std::runtime_error, "BlackoilPropsAdFromDeck::BlackoilPropsAdFromDeck() - " - "Inconsistent number of phases in pvt data (" << pvt_.numPhases() - << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); - } - } - IncompPropsAdFromDeck::~IncompPropsAdFromDeck() - { - } - - //////////////////////////// - // Rock interface // - //////////////////////////// - - /// \return D, the number of spatial dimensions. - int IncompPropsAdFromDeck::numDimensions() const - { - return rock_.numDimensions(); - } - - /// \return N, the number of cells. - int IncompPropsAdFromDeck::numCells() const - { - return rock_.numCells(); - } - - /// \return Array of N porosity values. - const double* IncompPropsAdFromDeck::porosity() const - { - return rock_.porosity(); - } - - /// \return Array of ND^2 permeability values. - /// The D^2 permeability values for a cell are organized as a matrix, - /// which is symmetric (so ordering does not matter). - const double* IncompPropsAdFromDeck::permeability() const - { - return rock_.permeability(); - } - - //////////////////////////// - // Fluid interface // - //////////////////////////// - - /// \return P, Number of active phases (also the number of components). - int IncompPropsAdFromDeck::numPhases() const - { - return pvt_.numPhases(); - } - - /// \return Array of P viscosity values. - const double* IncompPropsAdFromDeck::viscosity() const - { - return pvt_.viscosity(); - } - - ///Densities of fluid phases at reservoir conditions. - /// \return Array of P density values. - const double* IncompPropsAdFromDeck::density() const - { - return pvt_.reservoirDensities(); - } - - /// Densities of fluid phases at surface conditions. - /// \return Array of P density values. - const double* IncompPropsAdFromDeck::surfaceDensity() const - { - return pvt_.surfaceDensities(); - } - - - typedef IncompPropsAdFromDeck::ADB ADB; - typedef IncompPropsAdFromDeck::V V; - typedef Eigen::Array Block; - - - // ------ Relative permeability ------ - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 2 elements, each an array of n relperm values, - /// containing krw, kro. - std::vector - IncompPropsAdFromDeck::relperm(const V& sw, - const V& so, - const Cells& cells) const - { - const int n = cells.size(); - const int np = numPhases(); - Block s_all(n, np); - assert(sw.size() == n && so.size() == n); - s_all.col(0) = sw; - s_all.col(1) = so; - Block kr(n, np); - satprops_.relperm(n, s_all.data(), cells.data(), kr.data(), 0); - std::vector relperms; - relperms.reserve(np); - for (int phase = 0; phase < np; ++phase) { - relperms.emplace_back(kr.col(phase)); - } - return relperms; - } - - - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 2 elements, each an array of n relperm values, - /// containing krw, kro. - std::vector - IncompPropsAdFromDeck::relperm(const ADB& sw, - const ADB& so, - const Cells& cells) const - { - const int n = cells.size(); - const int np = numPhases(); - Block s_all(n, np); - assert(sw.size() == n && so.size() == n); - s_all.col(0) = sw.value(); - s_all.col(1) = so.value(); - Block kr(n, np); - Block dkr(n, np*np); - satprops_.relperm(n, s_all.data(), cells.data(), kr.data(), dkr.data()); - const int num_blocks = so.numBlocks(); - std::vector relperms; - relperms.reserve(np); - typedef const ADB* ADBPtr; - ADBPtr s[2] = { &sw, &so }; - for (int phase1 = 0; phase1 < np; ++phase1) { - const int phase1_pos = phase1; - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] = ADB::M(n, s[phase1]->derivative()[block].cols()); - } - for (int phase2 = 0; phase2 < np; ++phase2) { - const int phase2_pos = phase2; - // Assemble dkr1/ds2. - const int column = phase1_pos + np*phase2_pos; // Recall: Fortran ordering from props_.relperm() - ADB::M dkr1_ds2_diag = spdiag(dkr.col(column)); - for (int block = 0; block < num_blocks; ++block) { - jacs[block] += dkr1_ds2_diag * s[phase2]->derivative()[block]; - } - } - relperms.emplace_back(ADB::function(kr.col(phase1_pos), jacs)); - } - return relperms; - } - - /// Capillary pressure for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 2 elements, each an array of n capillary pressure values, - /// containing the offsets for each p_o, p_w. The capillary pressure between - /// two arbitrary phases alpha and beta is then given as p_alpha - p_beta. - std::vector - IncompPropsAdFromDeck::capPress(const ADB& sw, - const ADB& so, - const Cells& cells) const - { - const int numCells = cells.size(); - const int numActivePhases = numPhases(); - const int numBlocks = so.numBlocks(); - assert(sw.value().size() == numCells); - assert(so.value().size() == numCells); - Block s_all(numCells, numActivePhases); - s_all.col(0) = sw.value(); - s_all.col(1) = so.value(); - - Block pc(numCells, numActivePhases); - Block dpc(numCells, numActivePhases*numActivePhases); - satprops_.capPress(numCells, s_all.data(), cells.data(), pc.data(), dpc.data()); - - std::vector adbCapPressures; - adbCapPressures.reserve(2); - const ADB* s[2] = { &sw, &so}; - for (int phase1 = 0; phase1 < 2; ++phase1) { - const int phase1_pos = phase1; - std::vector jacs(numBlocks); - for (int block = 0; block < numBlocks; ++block) { - jacs[block] = ADB::M(numCells, s[phase1]->derivative()[block].cols()); - } - for (int phase2 = 0; phase2 < 2; ++phase2) { - const int phase2_pos = phase2; - // Assemble dpc1/ds2. - const int column = phase1_pos + numActivePhases*phase2_pos; // Recall: Fortran ordering from props_.relperm() - ADB::M dpc1_ds2_diag = spdiag(dpc.col(column)); - for (int block = 0; block < numBlocks; ++block) { - jacs[block] += dpc1_ds2_diag * s[phase2]->derivative()[block]; - } - } - adbCapPressures.emplace_back(ADB::function(pc.col(phase1_pos), jacs)); - } - return adbCapPressures; - } - - -} //namespace Opm - diff --git a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp b/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp deleted file mode 100644 index 2f52e541f..000000000 --- a/opm/polymer/fullyimplicit/IncompPropsAdFromDeck.hpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL - - 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_INCOMPPROPSADFROMDECK_HEADER_INCLUDED -#define OPM_INCOMPPROPSADFROMDECK_HEADER_INCLUDED - -#include -#include -#include -#include -#include -#include - -#include -#include - -struct UnstructuredGrid; -namespace Opm -{ - /// This class implements the AD-adapted fluid interface for - /// two-phase oil-water. It requires an input deck from which it - /// reads all relevant property data. - /// - /// Most methods are available in two overloaded versions, one - /// taking a constant vector and returning the same, and one - /// taking an AD type and returning the same. Derivatives are not - /// returned separately by any method, only implicitly with the AD - /// version of the methods. - class IncompPropsAdFromDeck : public IncompPropsAdInterface - { - public: - /// Constructor wrapping an opm-core two-phase interface. - IncompPropsAdFromDeck(const EclipseGridParser& deck, - const UnstructuredGrid& grid); - - /// Destructor. - ~IncompPropsAdFromDeck(); - - //////////////////////////// - // Rock interface // - //////////////////////////// - - /// \return D, the number of spatial dimensions. - int numDimensions() const; - - /// \return N, the number of cells. - int numCells() const; - - /// \return Array of N porosity values. - const double* porosity() const; - - /// \return Array of ND^2 permeability values. - /// The D^2 permeability values for a cell are organized as a matrix, - /// which is symmetric (so ordering does not matter). - const double* permeability() const; - - //////////////////////////// - // Fluid interface // - //////////////////////////// - - typedef AutoDiffBlock ADB; - typedef ADB::V V; - typedef std::vector Cells; - - /// \return P, Number of active phases (also the number of components). - int numPhases() const; - - /// \return Array of P viscosity values. - const double* viscosity() const; - - /// Densities of fluid phases at reservoir conditions. - /// \return Array of P density values. - const double* density() const; - - /// Densities of fluid phases at surface conditions. - /// \return Array of P density values. - const double* surfaceDensity() const; - - - // ------ Relative permeability ------ - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 2 elements, each an array of n relperm values, - /// containing krw, kro. - std::vector relperm(const V& sw, - const V& so, - const Cells& cells) const; - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 2 elements, each an array of n relperm values, - /// containing krw, kro. - std::vector relperm(const ADB& sw, - const ADB& so, - const Cells& cells) const; - - /// Capillary pressure for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 2 elements, each an array of n capillary pressure values, - /// containing the offsets for each p_o, p_w. The capillary pressure between - /// two arbitrary phases alpha and beta is then given as p_alpha - p_beta. - std::vector capPress(const ADB& sw, - const ADB& so, - const Cells& cells) const; - - private: - RockFromDeck rock_; - PvtPropertiesIncompFromDeck pvt_; - SaturationPropsFromDeck satprops_; - }; - -} //namespace Opm - -#endif// OPM_INCOMPPROPSADFROMDECK_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/IncompPropsAdInterface.cpp b/opm/polymer/fullyimplicit/IncompPropsAdInterface.cpp deleted file mode 100644 index 6217f3b7b..000000000 --- a/opm/polymer/fullyimplicit/IncompPropsAdInterface.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL. - - 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 - -Opm::IncompPropsAdInterface::~IncompPropsAdInterface() -{ -} diff --git a/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp b/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp deleted file mode 100644 index 7d6421f27..000000000 --- a/opm/polymer/fullyimplicit/IncompPropsAdInterface.hpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL. - - 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_INCOMPPROPSADINTERFACE_HEADER_INCLUDED -#define OPM_INCOMPPROPSADINTERFACE_HEADER_INCLUDED - -#include - -namespace Opm -{ - class IncompPropsAdInterface - { - public: - /// Virtual destructor for inheritance. - virtual ~IncompPropsAdInterface(); - - //////////////////////////// - // Rock interface // - //////////////////////////// - - /// \return D, the number of spatial dimensions. - virtual int numDimensions() const = 0; - - /// \return N, the number of cells. - virtual int numCells() const = 0; - - /// \return Array of N porosity values. - virtual const double* porosity() const = 0; - - /// \return Array of ND^2 permeability values. - /// The D^2 permeability values for a cell are organized as a matrix, - /// which is symmetric (so ordering does not matter). - virtual const double* permeability() const = 0; - - //////////////////////////// - // Fluid interface // - //////////////////////////// - - typedef AutoDiffBlock ADB; - typedef ADB::V V; - typedef ADB::M M; - typedef std::vector Cells; - - /// \return P, Number of active phases (also the number of components). - virtual int numPhases() const = 0; - - // ------ Density ------ - - /// Densities of stock components at surface conditions. - /// \return Array of P density values. - virtual const double* surfaceDensity() const = 0; - - // ------ Viscosity ------ - - /// Viscosity of stock components at surface conditions. - /// \return Array of P viscosity values. - virtual const double* viscosity() const = 0; - - // ------ Relative permeability ------ - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 2 elements, each an array of n relperm values, - /// containing krw, kro. - virtual - std::vector relperm(const V& sw, - const V& so, - const Cells& cells) const = 0; - - /// Relative permeabilities for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 2 elements, each an array of n relperm values, - /// containing krw, kro. - virtual - std::vector relperm(const ADB& sw, - const ADB& so, - const Cells& cells) const = 0; - - /// Capillary pressure for all phases. - /// \param[in] sw Array of n water saturation values. - /// \param[in] so Array of n oil saturation values. - /// \param[in] cells Array of n cell indices to be associated with the saturation values. - /// \return An std::vector with 2 elements, each an array of n capillary pressure values, - /// containing the offsets for each p_o, p_w. The capillary pressure between - /// two arbitrary phases alpha and beta is then given as p_alpha - p_beta. - virtual - std::vector capPress(const ADB& sw, - const ADB& so, - const Cells& cells) const = 0; - - }; -} // namespace Opm - -#endif// OPM_INCOMPPROPSADINTERFACE_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp index 2554078e3..dcbf2b628 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp @@ -1,15 +1,31 @@ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + #include #include -#include -#include +#include +#include #include namespace Opm { - - - - typedef PolymerPropsAd::ADB ADB; typedef PolymerPropsAd::V V; diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp index 26ca56790..9f9fffa52 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp @@ -23,8 +23,8 @@ #include #include -#include -#include +#include +#include #include namespace Opm { diff --git a/opm/polymer/fullyimplicit/PolymerWellState.hpp b/opm/polymer/fullyimplicit/PolymerWellState.hpp deleted file mode 100644 index 33fbcd3d2..000000000 --- a/opm/polymer/fullyimplicit/PolymerWellState.hpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - Copyright 2012 SINTEF ICT, Applied Mathematics. - - This file is part of the Open Porous Media project (OPM). - - OPM is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OPM is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with OPM. If not, see . -*/ - -#ifndef OPM_WELLSTATE_HEADER_INCLUDED -#define OPM_WELLSTATE_HEADER_INCLUDED - -#include -#include - -namespace Opm -{ - - /// The state of a set of wells. - class WellState - { - public: - /// Allocate and initialize if wells is non-null. - /// Also tries to give useful initial values to the bhp() and - /// wellRates() fields, depending on controls. The - /// perfRates() field is filled with zero, and perfPress() - /// with -1e100. - template - void init(const Wells* wells, const State& state) - { - if (wells) { - const int nw = wells->number_of_wells; - const int np = wells->number_of_phases + 1; - bhp_.resize(nw); - wellrates_.resize(nw * np, 0.0); - for (int w = 0; w < nw; ++w) { - const WellControls* ctrl = wells->ctrls[w]; - // Initialize bhp to be target pressure if - // bhp-controlled well, otherwise set to a little - // above or below (depending on if the well is an - // injector or producer) pressure in first perforation - // cell. - if ((ctrl->current < 0) || // SHUT - (ctrl->type[ctrl->current] != BHP)) { - const int first_cell = wells->well_cells[wells->well_connpos[w]]; - const double safety_factor = (wells->type[w] == INJECTOR) ? 1.01 : 0.99; - bhp_[w] = safety_factor*state.pressure()[first_cell]; - } else { - bhp_[w] = ctrl->target[ctrl->current]; - } - // Initialize well rates to match controls if type is SURFACE_RATE - if ((ctrl->current >= 0) && // open well - (ctrl->type[ctrl->current] == SURFACE_RATE)) { - const double rate_target = ctrl->target[ctrl->current]; - for (int p = 0; p < np; ++p) { - const double phase_distr = ctrl->distr[np * ctrl->current + p]; - wellrates_[np*w + p] = rate_target * phase_distr; - } - } - } - // The perforation rates and perforation pressures are - // not expected to be consistent with bhp_ and wellrates_ - // after init(). - perfrates_.resize(wells->well_connpos[nw], 0.0); - perfpress_.resize(wells->well_connpos[nw], -1e100); - } - } - - /// One bhp pressure per well. - std::vector& bhp() { return bhp_; } - const std::vector& bhp() const { return bhp_; } - - /// One rate per well and phase. - std::vector& wellRates() { return wellrates_; } - const std::vector& wellRates() const { return wellrates_; } - - /// One rate per well connection. - std::vector& perfRates() { return perfrates_; } - const std::vector& perfRates() const { return perfrates_; } - - /// One pressure per well connection. - std::vector& perfPress() { return perfpress_; } - const std::vector& perfPress() const { return perfpress_; } - - private: - std::vector bhp_; - std::vector wellrates_; - std::vector perfrates_; - std::vector perfpress_; - }; - -} // namespace Opm - -#endif // OPM_WELLSTATE_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp index f4e4000bd..c811f44b9 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp @@ -1,5 +1,6 @@ /* - Copyright 2013 SINTEF ICT, Applied Mathematics. + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL. This file is part of the Open Porous Media project (OPM). @@ -26,9 +27,9 @@ #include #include -#include +#include +#include #include -#include #include #include #include diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp index 291207b08..e0627a0dc 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp @@ -1,5 +1,6 @@ /* - Copyright 2013 SINTEF ICT, Applied Mathematics. + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL. This file is part of the Open Porous Media project (OPM). @@ -22,7 +23,7 @@ #include #include -#include +#include #include #include #include diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp index 993448524..f5d0f2ca1 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp @@ -1,5 +1,6 @@ /* - Copyright 2013 SINTEF ICT, Applied Mathematics. + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL. This file is part of the Open Porous Media project (OPM). diff --git a/opm/polymer/fullyimplicit/utilities.cpp b/opm/polymer/fullyimplicit/utilities.cpp index 9dec80952..6f951ec49 100644 --- a/opm/polymer/fullyimplicit/utilities.cpp +++ b/opm/polymer/fullyimplicit/utilities.cpp @@ -1,6 +1,6 @@ /* Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL + Copyright 2014 STATOIL. This file is part of the Open Porous Media project (OPM). @@ -22,16 +22,16 @@ #include #include #include -#include -#include +#include +#include #include #include #include #include #include -#include -#include +#include +#include #include #include #include @@ -45,13 +45,13 @@ namespace Opm { - typedef AutoDiffBlock ADB; - typedef ADB::V V; - typedef ADB::M M; - typedef Eigen::Array DataBlock; + typedef AutoDiffBlock ADB; + typedef ADB::V V; + typedef ADB::M M; + typedef Eigen::Array DataBlock; /// Compute two-phase transport source terms from well terms. /// Note: Unlike the incompressible version of this function, /// this version computes surface volume injection rates, diff --git a/opm/polymer/fullyimplicit/utilities.hpp b/opm/polymer/fullyimplicit/utilities.hpp index 47fed5d2d..5b0589f70 100644 --- a/opm/polymer/fullyimplicit/utilities.hpp +++ b/opm/polymer/fullyimplicit/utilities.hpp @@ -1,6 +1,6 @@ /* Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL + Copyright 2014 STATOIL. This file is part of the Open Porous Media project (OPM). @@ -24,8 +24,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include @@ -46,13 +46,13 @@ namespace Opm { - typedef AutoDiffBlock ADB; - typedef ADB::V V; - typedef ADB::M M; - typedef Eigen::Array DataBlock; + typedef AutoDiffBlock ADB; + typedef ADB::V V; + typedef ADB::M M; + typedef Eigen::Array DataBlock; /// Compute two-phase transport source terms from well terms. /// Note: Unlike the incompressible version of this function, /// this version computes surface volume injection rates, @@ -97,7 +97,6 @@ namespace Opm double& polyinj, double& polyprod); - /// @brief Computes injected and produced volumes of all phases, /// and injected and produced polymer mass - in the compressible case. /// Note 1: assumes that only the first phase is injected. From 06bed8d94531f98a1084fac705a6ffe65c548cd3 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Mon, 17 Mar 2014 10:54:29 +0800 Subject: [PATCH 41/83] fix copyright statement. --- .../fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp | 2 +- .../fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp | 2 +- .../fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp | 2 +- .../fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp | 2 +- opm/polymer/fullyimplicit/PolymerPropsAd.cpp | 2 +- opm/polymer/fullyimplicit/PolymerPropsAd.hpp | 2 +- .../fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp | 2 +- .../fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp | 2 +- .../fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp | 2 +- .../fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp | 2 +- opm/polymer/fullyimplicit/utilities.cpp | 2 +- opm/polymer/fullyimplicit/utilities.hpp | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index 5488a13b2..b0612f2f5 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -1,6 +1,6 @@ /* Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2013 STATOIL. + Copyright 2013 STATOIL ASA. This file is part of the Open Porous Media project (OPM). diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp index 4da5840f2..9c3292718 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp @@ -1,6 +1,6 @@ /* Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL. + Copyright 2014 STATOIL ASA. This file is part of the Open Porous Media project (OPM). diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp index 2e5bd8880..6a83b826d 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.cpp @@ -1,6 +1,6 @@ /* Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL. + Copyright 2014 STATOIL ASA. This file is part of the Open Porous Media project (OPM). diff --git a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp index b1d09c7ff..cb7be065f 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitTwophasePolymerSolver.hpp @@ -1,6 +1,6 @@ /* Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL. + Copyright 2014 STATOIL ASA. This file is part of the Open Porous Media project (OPM). diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp index dcbf2b628..897a8f3d9 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.cpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.cpp @@ -1,6 +1,6 @@ /* Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL. + Copyright 2014 STATOIL ASA. This file is part of the Open Porous Media project (OPM). diff --git a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp index 9f9fffa52..4b691144d 100644 --- a/opm/polymer/fullyimplicit/PolymerPropsAd.hpp +++ b/opm/polymer/fullyimplicit/PolymerPropsAd.hpp @@ -1,6 +1,6 @@ /* Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL. + Copyright 2014 STATOIL ASA. This file is part of the Open Porous Media project (OPM). diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp index c811f44b9..2a5b7f5e7 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp @@ -1,6 +1,6 @@ /* Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL. + Copyright 2014 STATOIL ASA. This file is part of the Open Porous Media project (OPM). diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp index bb493520b..387590161 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp @@ -1,6 +1,6 @@ /* Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL. + Copyright 2014 STATOIL ASA. This file is part of the Open Porous Media project (OPM). diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp index e0627a0dc..ba2fdfd06 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.cpp @@ -1,6 +1,6 @@ /* Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL. + Copyright 2014 STATOIL ASA. This file is part of the Open Porous Media project (OPM). diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp index f5d0f2ca1..d6cd62e6b 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitTwophasePolymer.hpp @@ -1,6 +1,6 @@ /* Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL. + Copyright 2014 STATOIL ASA. This file is part of the Open Porous Media project (OPM). diff --git a/opm/polymer/fullyimplicit/utilities.cpp b/opm/polymer/fullyimplicit/utilities.cpp index 6f951ec49..83c85d86b 100644 --- a/opm/polymer/fullyimplicit/utilities.cpp +++ b/opm/polymer/fullyimplicit/utilities.cpp @@ -1,6 +1,6 @@ /* Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL. + Copyright 2014 STATOIL ASA. This file is part of the Open Porous Media project (OPM). diff --git a/opm/polymer/fullyimplicit/utilities.hpp b/opm/polymer/fullyimplicit/utilities.hpp index 5b0589f70..8edca6e8b 100644 --- a/opm/polymer/fullyimplicit/utilities.hpp +++ b/opm/polymer/fullyimplicit/utilities.hpp @@ -1,6 +1,6 @@ /* Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL. + Copyright 2014 STATOIL ASA. This file is part of the Open Porous Media project (OPM). From d5f8cbeec0832cd4c3e06b61015cbbc562fbf5b5 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Wed, 24 Sep 2014 15:44:21 +0800 Subject: [PATCH 42/83] move all the output functions to anonymous namespace and clean up unused functions. --- ...ulatorFullyImplicitCompressiblePolymer.cpp | 287 +++++++----------- 1 file changed, 105 insertions(+), 182 deletions(-) diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp index 2a5b7f5e7..1121d197f 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp @@ -65,6 +65,22 @@ namespace Opm { + + + namespace + { + static void outputStateVtk(const UnstructuredGrid& grid, + const Opm::PolymerBlackoilState& state, + const int step, + const std::string& output_dir); + static void outputStateMatlab(const UnstructuredGrid& grid, + const Opm::PolymerBlackoilState& state, + const int step, + const std::string& output_dir); + static void outputWaterCut(const Opm::Watercut& watercut, + const std::string& output_dir); + } // anonymous namespace + class SimulatorFullyImplicitCompressiblePolymer::Impl { public: @@ -140,133 +156,6 @@ namespace Opm - static void outputStateVtk(const UnstructuredGrid& grid, - const Opm::PolymerBlackoilState& state, - const int step, - const std::string& output_dir) - { - // Write data in VTK format. - std::ostringstream vtkfilename; - vtkfilename << output_dir << "/vtk_files"; - boost::filesystem::path fpath(vtkfilename.str()); - try { - create_directories(fpath); - } - catch (...) { - OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); - } - vtkfilename << "/output-" << std::setw(3) << std::setfill('0') << step << ".vtu"; - std::ofstream vtkfile(vtkfilename.str().c_str()); - if (!vtkfile) { - OPM_THROW(std::runtime_error, "Failed to open " << vtkfilename.str()); - } - Opm::DataMap dm; - dm["saturation"] = &state.saturation(); - dm["pressure"] = &state.pressure(); - dm["cmax"] = &state.maxconcentration(); - dm["concentration"] = &state.concentration(); - std::vector cell_velocity; - Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); - dm["velocity"] = &cell_velocity; - Opm::writeVtkData(grid, dm, vtkfile); - } - - - static void outputStateMatlab(const UnstructuredGrid& grid, - const Opm::PolymerBlackoilState& state, - const int step, - const std::string& output_dir) - { - Opm::DataMap dm; - dm["saturation"] = &state.saturation(); - dm["pressure"] = &state.pressure(); - dm["cmax"] = &state.maxconcentration(); - dm["concentration"] = &state.concentration(); - dm["surfvolume"] = &state.surfacevol(); - std::vector cell_velocity; - Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); - dm["velocity"] = &cell_velocity; - - // Write data (not grid) in Matlab format - for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { - std::ostringstream fname; - fname << output_dir << "/" << it->first; - boost::filesystem::path fpath = fname.str(); - try { - create_directories(fpath); - } - catch (...) { - OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); - } - fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; - std::ofstream file(fname.str().c_str()); - if (!file) { - OPM_THROW(std::runtime_error, "Failed to open " << fname.str()); - } - file.precision(15); - const std::vector& d = *(it->second); - std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); - } - } -#if 0 - - //well rate should be modified by using effective viscosity - //and effective relperm for water - static void outputWellStateMatlab(const Opm::WellState& well_state, - const int step, - const std::string& output_dir) - { - Opm::DataMap dm; - dm["bhp"] = &well_state.bhp(); - dm["wellrates"] = &well_state.wellRates(); - - // Write data (not grid) in Matlab format - for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { - std::ostringstream fname; - fname << output_dir << "/" << it->first; - boost::filesystem::path fpath = fname.str(); - try { - create_directories(fpath); - } - catch (...) { - OPM_THROW(std::runtime_error,"Creating directories failed: " << fpath); - } - fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; - std::ofstream file(fname.str().c_str()); - if (!file) { - OPM_THROW(std::runtime_error,"Failed to open " << fname.str()); - } - file.precision(15); - const std::vector& d = *(it->second); - std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); - } - } -#endif - static void outputWaterCut(const Opm::Watercut& watercut, - const std::string& output_dir) - { - // Write water cut curve. - std::string fname = output_dir + "/watercut.txt"; - std::ofstream os(fname.c_str()); - if (!os) { - OPM_THROW(std::runtime_error, "Failed to open " << fname); - } - watercut.write(os); - } -#if 0 - - static void outputWellReport(const Opm::WellReport& wellreport, - const std::string& output_dir) - { - // Write well report. - std::string fname = output_dir + "/wellreport.txt"; - std::ofstream os(fname.c_str()); - if (!os) { - OPM_THROW(std::runtime_error, "Failed to open " << fname); - } - wellreport.write(os); - } -#endif // \TODO: Treat bcs. @@ -403,7 +292,7 @@ namespace Opm // Process transport sources (to include bdy terms and well flows). // Opm::computeTransportSource(props_, wells_, well_state, transport_src); // Run solver. - const double current_time = timer.currentTime(); + const double current_time = timer.currentTimeElapsed(); double stepsize = timer.currentStepLength(); polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c); solver_timer.start(); @@ -457,7 +346,7 @@ namespace Opm tot_injected[1] += injected[1]; tot_produced[0] += produced[0]; tot_produced[1] += produced[1]; - watercut.push(timer.currentTime() + timer.currentStepLength(), + watercut.push(timer.currentTimeElapsed() + timer.currentStepLength(), produced[0]/(produced[0] + produced[1]), tot_produced[0]/tot_porevol_init); std::cout.precision(5); @@ -475,52 +364,6 @@ namespace Opm std::cout << " Total prod reservoir volumes: " << std::setw(width) << tot_produced[0] << std::setw(width) << tot_produced[1] << std::endl; - // The reports below are geared towards two phases only. -#if 0 - // Report mass balances. - double injected[2] = { 0.0 }; - double produced[2] = { 0.0 }; - Opm::computeInjectedProduced(props_, state, transport_src, stepsize, - injected, produced); - Opm::computeSaturatedVol(porevol, state.surfacevol(), inplace_surfvol); - std::cout.precision(5); - const int width = 18; - std::cout << "\nMass balance report.\n"; - std::cout << " Injected surface volumes: " - << std::setw(width) << injected[0] - << std::setw(width) << injected[1] << std::endl; - std::cout << " Produced surface volumes: " - << std::setw(width) << produced[0] - << std::setw(width) << produced[1] << std::endl; - std::cout << " Total inj surface volumes: " - << std::setw(width) << tot_injected[0] - << std::setw(width) << tot_injected[1] << std::endl; - std::cout << " Total prod surface volumes: " - << std::setw(width) << tot_produced[0] - << std::setw(width) << tot_produced[1] << std::endl; - const double balance[2] = { init_surfvol[0] - inplace_surfvol[0] - tot_produced[0] + tot_injected[0], - init_surfvol[1] - inplace_surfvol[1] - tot_produced[1] + tot_injected[1] }; - std::cout << " Initial - inplace + inj - prod: " - << std::setw(width) << balance[0] - << std::setw(width) << balance[1] - << std::endl; - std::cout << " Relative mass error: " - << std::setw(width) << balance[0]/(init_surfvol[0] + tot_injected[0]) - << std::setw(width) << balance[1]/(init_surfvol[1] + tot_injected[1]) - << std::endl; - std::cout.precision(8); - - // Make well reports. - watercut.push(timer.currentTime() + timer.currentStepLength(), - produced[0]/(produced[0] + produced[1]), - tot_produced[0]/tot_porevol_init); - if (wells_) { - wellreport.push(props_, *wells_, - state.pressure(), state.surfacevol(), state.saturation(), - timer.currentTime() + timer.currentStepLength(), - well_state.bhp(), well_state.perfRates()); - } -#endif sreport.total_time = step_timer.secsSinceStart(); if (output_) { sreport.reportParam(tstep_os); @@ -529,20 +372,13 @@ namespace Opm outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); } outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); - // outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); outputWaterCut(watercut, output_dir_); -#if 0 - if (wells_) { - outputWellReport(wellreport, output_dir_); - } -#endif tstep_os.close(); } // advance to next timestep before reporting at this location ++timer; - // write an output file for later inspection } total_timer.stop(); @@ -555,4 +391,91 @@ namespace Opm } + + namespace + { + + static void outputStateVtk(const UnstructuredGrid& grid, + const Opm::PolymerBlackoilState& state, + const int step, + const std::string& output_dir) + { + // Write data in VTK format. + std::ostringstream vtkfilename; + vtkfilename << output_dir << "/vtk_files"; + boost::filesystem::path fpath(vtkfilename.str()); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + vtkfilename << "/output-" << std::setw(3) << std::setfill('0') << step << ".vtu"; + std::ofstream vtkfile(vtkfilename.str().c_str()); + if (!vtkfile) { + OPM_THROW(std::runtime_error, "Failed to open " << vtkfilename.str()); + } + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + dm["cmax"] = &state.maxconcentration(); + dm["concentration"] = &state.concentration(); + std::vector cell_velocity; + Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); + dm["velocity"] = &cell_velocity; + Opm::writeVtkData(grid, dm, vtkfile); + } + + + static void outputStateMatlab(const UnstructuredGrid& grid, + const Opm::PolymerBlackoilState& state, + const int step, + const std::string& output_dir) + { + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + dm["cmax"] = &state.maxconcentration(); + dm["concentration"] = &state.concentration(); + dm["surfvolume"] = &state.surfacevol(); + std::vector cell_velocity; + Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity); + dm["velocity"] = &cell_velocity; + + // Write data (not grid) in Matlab format + for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { + std::ostringstream fname; + fname << output_dir << "/" << it->first; + boost::filesystem::path fpath = fname.str(); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; + std::ofstream file(fname.str().c_str()); + if (!file) { + OPM_THROW(std::runtime_error, "Failed to open " << fname.str()); + } + file.precision(15); + const std::vector& d = *(it->second); + std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); + } + } + + + static void outputWaterCut(const Opm::Watercut& watercut, + const std::string& output_dir) + { + // Write water cut curve. + std::string fname = output_dir + "/watercut.txt"; + std::ofstream os(fname.c_str()); + if (!os) { + OPM_THROW(std::runtime_error, "Failed to open " << fname); + } + watercut.write(os); + } + } + } // namespace Opm From aa30b4567c82a6d8a367e18b5d255413f3017c17 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 25 Sep 2014 11:16:56 +0800 Subject: [PATCH 43/83] add phaseCondition for new API of class BlackoilAdInterface --- ...FullyImplicitCompressiblePolymerSolver.cpp | 58 +++++++++++++------ ...FullyImplicitCompressiblePolymerSolver.hpp | 29 ++++++---- 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index b0612f2f5..99021f6a2 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -1,6 +1,6 @@ /* Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2013 STATOIL ASA. + Copyright 2014 STATOIL ASA. This file is part of the Open Porous Media project (OPM). @@ -181,6 +181,7 @@ namespace { , wops_ (wells) , grav_ (gravityOperator(grid_, ops_, geo_)) , cmax_(V::Zero(grid.number_of_cells)) + , phaseCondition_ (grid.number_of_cells) , rq_ (fluid.numPhases() + 1) , residual_ ( { std::vector(fluid.numPhases() + 1, ADB::null()), ADB::null(), @@ -449,10 +450,13 @@ namespace { const ADB& press = state.pressure; const std::vector& sat = state.saturation; const ADB& c = state.concentration; + + const std::vector cond = phaseCondition(); + const ADB pv_mult = poroMult(press); for (int phase = 0; phase < 2; ++phase) { - rq_[phase].b = fluidReciprocFVF(phase, press, cells_); + rq_[phase].b = fluidReciprocFVF(phase, press, cond, cells_); } rq_[0].accum[aix] = pv_mult * rq_[0].b * sat[0]; rq_[1].accum[aix] = pv_mult * rq_[1].b * sat[1]; @@ -478,7 +482,6 @@ namespace { for (int i = 0; i < nc; ++i) { cmax_(i) = std::max(cmax_(i), c.value()(i)); } - // return ADB::constant(cmax_, c.blockPattern()); std::copy(&cmax_[0], &cmax_[0] + nc, state.maxconcentration().begin()); } @@ -857,16 +860,17 @@ namespace { const SolutionState& state ) { const ADB tr_mult = transMult(state.pressure); + const std::vector cond = phaseCondition(); - const ADB mu_w = fluidViscosity(0, state.pressure, cells_); + const ADB mu_w = fluidViscosity(0, state.pressure, cond, cells_); ADB inv_wat_eff_vis = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mu_w.value().data()); rq_[0].mob = tr_mult * krw_eff * inv_wat_eff_vis; rq_[2].mob = tr_mult * mc * krw_eff * inv_wat_eff_vis; - const ADB mu_o = fluidViscosity(1, state.pressure, cells_); + const ADB mu_o = fluidViscosity(1, state.pressure, cond, cells_); rq_[1].mob = tr_mult * kro / mu_o; std::vector press = computePressures(state); for (int phase = 0; phase < 2; ++phase) { - const ADB rho = fluidDensity(phase, state.pressure, cells_); + const ADB rho = fluidDensity(phase, state.pressure, cond, cells_); ADB& head = rq_[ phase ].head; // compute gravity potensial using the face average as in eclipse and MRST const ADB rhoavg = ops_.caver * rho; @@ -909,16 +913,17 @@ namespace { ADB - FullyImplicitCompressiblePolymerSolver::fluidViscosity(const int phase, - const ADB& p , - const std::vector& cells) const + FullyImplicitCompressiblePolymerSolver::fluidViscosity(const int phase, + const ADB& p , + const std::vector& cond, + const std::vector& cells) const { const ADB null = ADB::constant(V::Zero(grid_.number_of_cells, 1), p.blockPattern()); switch (phase) { case Water: return fluid_.muWat(p, cells); case Oil: { - return fluid_.muOil(p, null, cells); + return fluid_.muOil(p, null, cond, cells); } default: OPM_THROW(std::runtime_error, "Unknown phase index " << phase); @@ -930,16 +935,17 @@ namespace { ADB - FullyImplicitCompressiblePolymerSolver::fluidReciprocFVF(const int phase, - const ADB& p , - const std::vector& cells) const + FullyImplicitCompressiblePolymerSolver::fluidReciprocFVF(const int phase, + const ADB& p , + const std::vector& cond + const std::vector& cells) const { const ADB null = ADB::constant(V::Zero(grid_.number_of_cells, 1), p.blockPattern()); switch (phase) { case Water: return fluid_.bWat(p, cells); case Oil: { - return fluid_.bOil(p, null, cells); + return fluid_.bOil(p, null, cond, cells); } default: OPM_THROW(std::runtime_error, "Unknown phase index " << phase); @@ -951,12 +957,13 @@ namespace { ADB - FullyImplicitCompressiblePolymerSolver::fluidDensity(const int phase, - const ADB& p , - const std::vector& cells) const + FullyImplicitCompressiblePolymerSolver::fluidDensity(const int phase, + const ADB& p , + const std::vector& cond + const std::vector& cells) const { const double* rhos = fluid_.surfaceDensity(); - ADB b = fluidReciprocFVF(phase, p, cells); + ADB b = fluidReciprocFVF(phase, p, cond, cells); ADB rho = V::Constant(p.size(), 1, rhos[phase]) * b; return rho; } @@ -1028,4 +1035,19 @@ namespace { } + void + FullyImplicitCompressiblePolymerSolver::classifyCondition(const PolymerBlackoilState& state) + { + const nc = grid_.number_of_cells; + const DataBlock s = Eigen::Map(& state.saturation()[0], nc, 2); + + const V so = s.col(1); + for (V::Index c = 0; e = so.size(); c != e; ++c) { + phaseConditon_[c].setFreeWater(); + if (so[c] > 0) { + phaseCondition_[c].setFreeOil(); + } + } + } + } //namespace Opm diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp index 9c3292718..855d0e7ea 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp @@ -136,7 +136,7 @@ namespace Opm { const M grav_; V cmax_; std::vector rq_; - + std::vector phaseCondition_; // The mass_balance vector has one element for each active phase, // each of which has size equal to the number of cells. // The well_eq has size equal to the number of wells. @@ -217,25 +217,34 @@ namespace Opm { residualNorm() const; ADB - fluidViscosity(const int phase, - const ADB& p , - const std::vector& cells) const; + fluidViscosity(const int phase, + const ADB& p , + const std::vector& cond, + const std::vector& cells) const; ADB - fluidReciprocFVF(const int phase, - const ADB& p , - const std::vector& cells) const; + fluidReciprocFVF(const int phase, + const ADB& p , + const std::vector& cond + const std::vector& cells) const; ADB - fluidDensity(const int phase, - const ADB& p , - const std::vector& cells) const; + fluidDensity(const int phase, + const ADB& p , + const std::vector& cond, + const std::vector& cells) const; ADB poroMult(const ADB& p) const; ADB transMult(const ADB& p) const; + + const std::vector + phaseCondition() const { return phaseConditon_; } + + void + classifyCondition(const PolymerBlackoilState& state); }; } // namespace Opm From 219f46a4065145d69772e043cfce0fa15a6eea90 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 25 Sep 2014 11:24:59 +0800 Subject: [PATCH 44/83] update compurePressure(). --- .../fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index 99021f6a2..39591aee5 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -840,9 +840,8 @@ namespace { pressure[0] = pressure[0] - pressure[1]; // add the total pressure to the capillary pressures - for (int phaseIdx = 0; phaseIdx < 2; ++phaseIdx) { - pressure[phaseIdx] += state.pressure; - } + pressure[0] = state.pressure - pressure[0]; + pressure[1] = state.pressure + pressure[1]; return pressure; } From 12f134287082842caed8c05d00eeeb19e1582f17 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 25 Sep 2014 14:23:21 +0800 Subject: [PATCH 45/83] use phasePress to compute viscosity, density. --- .../FullyImplicitCompressiblePolymerSolver.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index 39591aee5..e2b1d92ce 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -860,25 +860,25 @@ namespace { { const ADB tr_mult = transMult(state.pressure); const std::vector cond = phaseCondition(); + std::vector press = computePressures(state); - const ADB mu_w = fluidViscosity(0, state.pressure, cond, cells_); + const ADB mu_w = fluidViscosity(0, press[0], cond, cells_); ADB inv_wat_eff_vis = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mu_w.value().data()); rq_[0].mob = tr_mult * krw_eff * inv_wat_eff_vis; rq_[2].mob = tr_mult * mc * krw_eff * inv_wat_eff_vis; - const ADB mu_o = fluidViscosity(1, state.pressure, cond, cells_); + const ADB mu_o = fluidViscosity(1, press[1], cond, cells_); rq_[1].mob = tr_mult * kro / mu_o; - std::vector press = computePressures(state); for (int phase = 0; phase < 2; ++phase) { - const ADB rho = fluidDensity(phase, state.pressure, cond, cells_); + const ADB rho = fluidDensity(phase, press[phase], cond, cells_); ADB& head = rq_[ phase ].head; // compute gravity potensial using the face average as in eclipse and MRST const ADB rhoavg = ops_.caver * rho; const ADB dp = ops_.ngrad * press[phase] - geo_.gravity()[2] * (rhoavg * (ops_.ngrad * geo_.z().matrix())); head = transi*dp; UpwindSelector upwind(grid_, ops_, head.value()); - const ADB& b = rq_[ phase ].b; - const ADB& mob = rq_[ phase ].mob; - rq_[ phase ].mflux = upwind.select(b * mob) * head; + const ADB& b = rq_[phase].b; + const ADB& mob = rq_[phase].mob; + rq_[phase].mflux = upwind.select(b * mob) * head; } rq_[2].b = rq_[0].b; rq_[2].head = rq_[0].head; From ca6baf7c736e2e8e8277d542a07ba068c87d2cfb Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 25 Sep 2014 15:14:40 +0800 Subject: [PATCH 46/83] use phase pressure to compute FVF and density. --- .../FullyImplicitCompressiblePolymerSolver.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index e2b1d92ce..936bd9191 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -452,11 +452,12 @@ namespace { const ADB& c = state.concentration; const std::vector cond = phaseCondition(); + std::vector pressure = computePressures(state); const ADB pv_mult = poroMult(press); for (int phase = 0; phase < 2; ++phase) { - rq_[phase].b = fluidReciprocFVF(phase, press, cond, cells_); + rq_[phase].b = fluidReciprocFVF(phase, pressure[phase], cond, cells_); } rq_[0].accum[aix] = pv_mult * rq_[0].b * sat[0]; rq_[1].accum[aix] = pv_mult * rq_[1].b * sat[1]; @@ -567,15 +568,16 @@ namespace { } } ADB cell_rho_total = ADB::constant(V::Zero(nc), state.pressure.blockPattern()); + std::vector press = computePressures(state); for (int phase = 0; phase < 2; ++phase) { - const ADB cell_rho = fluidDensity(phase, state.pressure, cells_); + const ADB cell_rho = fluidDensity(phase, press[phase], cells_); cell_rho_total += state.saturation[phase] * cell_rho; } ADB inj_rho_total = ADB::constant(V::Zero(nperf), state.pressure.blockPattern()); assert(np == wells_.number_of_phases); const DataBlock compi = Eigen::Map(wells_.comp_frac, nw, np); for (int phase = 0; phase < 2; ++phase) { - const ADB cell_rho = fluidDensity(phase, state.pressure, cells_); + const ADB cell_rho = fluidDensity(phase, press[phase], cells_); const V fraction = compi.col(phase); inj_rho_total += (wops_.w2p * fraction.matrix()).array() * subset(cell_rho, well_cells); } From fb0f4f7a92fab29d4cb782a9abcb60c941fb40b0 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 25 Sep 2014 16:28:35 +0800 Subject: [PATCH 47/83] comment header files and function for incompProps. --- opm/polymer/fullyimplicit/utilities.cpp | 6 ++++-- opm/polymer/fullyimplicit/utilities.hpp | 10 ++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/opm/polymer/fullyimplicit/utilities.cpp b/opm/polymer/fullyimplicit/utilities.cpp index 83c85d86b..c75185ac0 100644 --- a/opm/polymer/fullyimplicit/utilities.cpp +++ b/opm/polymer/fullyimplicit/utilities.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +//#include #include #include #include @@ -120,6 +120,8 @@ namespace Opm /// @param[out] produced must also point to a valid array with P elements. /// @param[out] polyinj injected mass of polymer /// @param[out] polyprod produced mass of polymer + // This function need a incompProps based on Ad. + /* void computeInjectedProduced(const IncompPropsAdInterface& props, const Opm::PolymerPropsAd& polymer_props, const PolymerState& state, @@ -190,7 +192,7 @@ namespace Opm } } - + */ /// @brief Computes injected and produced volumes of all phases, /// and injected and produced polymer mass - in the compressible case. /// Note 1: assumes that only the first phase is injected. diff --git a/opm/polymer/fullyimplicit/utilities.hpp b/opm/polymer/fullyimplicit/utilities.hpp index 8edca6e8b..a1d854a33 100644 --- a/opm/polymer/fullyimplicit/utilities.hpp +++ b/opm/polymer/fullyimplicit/utilities.hpp @@ -25,15 +25,15 @@ #include #include #include -#include +//#include #include #include #include #include #include -#include -#include +#include +#include #include #include #include @@ -86,6 +86,8 @@ namespace Opm /// @param[out] produced must also point to a valid array with P elements. /// @param[out] polyinj injected mass of polymer /// @param[out] polyprod produced mass of polymer + // This function need a incompProps based on Ad. + /* void computeInjectedProduced(const IncompPropsAdInterface& props, const Opm::PolymerPropsAd& polymer_props, const PolymerState& state, @@ -96,7 +98,7 @@ namespace Opm double* produced, double& polyinj, double& polyprod); - + */ /// @brief Computes injected and produced volumes of all phases, /// and injected and produced polymer mass - in the compressible case. /// Note 1: assumes that only the first phase is injected. From e22398c098eef801216d157498e64af7c82f045f Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 25 Sep 2014 16:40:01 +0800 Subject: [PATCH 48/83] make Opm::DerivedGeology as a direct arguement for simulator. --- examples/sim_poly_fi2p_comp_ad.cpp | 74 ++++++++----------- ...ulatorFullyImplicitCompressiblePolymer.cpp | 3 +- ...ulatorFullyImplicitCompressiblePolymer.hpp | 2 + 3 files changed, 34 insertions(+), 45 deletions(-) diff --git a/examples/sim_poly_fi2p_comp_ad.cpp b/examples/sim_poly_fi2p_comp_ad.cpp index edf682231..dbc7b932d 100644 --- a/examples/sim_poly_fi2p_comp_ad.cpp +++ b/examples/sim_poly_fi2p_comp_ad.cpp @@ -1,5 +1,6 @@ /* - Copyright 2013 SINTEF ICT, Applied Mathematics. + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 Statoil ASA. This file is part of the Open Porous Media project (OPM). @@ -35,7 +36,6 @@ #include #include -#include #include #include #include @@ -46,14 +46,15 @@ #include #include -#include -#include #include #include #include #include -#include +#include +#include +#include +#include #include #include #include @@ -95,22 +96,26 @@ try OPM_THROW(std::runtime_error, "This program must be run with an input deck. " "Specify the deck with deck_filename=deckname.data (for example)."); } - boost::scoped_ptr deck; boost::scoped_ptr grid; boost::scoped_ptr props; boost::scoped_ptr new_props; boost::scoped_ptr rock_comp; + Opm::DeckConstPtr deck; + EclipseStateConstPtr eclipseState; PolymerBlackoilState state; // bool check_well_controls = false; // int max_well_control_iterations = 0; double gravity[3] = { 0.0 }; std::string deck_filename = param.get("deck_filename"); - deck.reset(new EclipseGridParser(deck_filename)); + ParserPtr parser(new Opm::Parser()); + deck = parser->parseFile(deck_filename); + eclipseState.reset(new Opm::EclipseState(deck)); // Grid init - grid.reset(new GridManager(*deck)); + grid.reset(new GridManager(deck)); // use the capitalized part of the deck's filename between the // last '/' and the last '.' character as base name. +/* std::string baseName = deck_filename; auto charPos = baseName.rfind('/'); if (charPos != std::string::npos) @@ -121,15 +126,16 @@ try baseName = boost::to_upper_copy(baseName); Opm::EclipseWriter outputWriter(param, share_obj(*deck), share_obj(*grid->c_grid())); +*/ // Rock and fluid init - props.reset(new BlackoilPropertiesFromDeck(*deck, *grid->c_grid(), param)); - new_props.reset(new BlackoilPropsAdFromDeck(*deck, *grid->c_grid())); - PolymerProperties polymer_props(*deck); + props.reset(new BlackoilPropertiesFromDeck(deck, eclipseStae, *grid->c_grid())); + new_props.reset(new BlackoilPropsAdFromDeck(deck, eclipseState, *grid->c_grid())); + PolymerProperties polymer_props(eclipseState); PolymerPropsAd polymer_props_ad(polymer_props); // check_well_controls = param.getDefault("check_well_controls", false); // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); // Rock compressibility. - rock_comp.reset(new RockCompressibility(*deck)); + rock_comp.reset(new RockCompressibility(deck, eclipseState)); // Gravity. gravity[2] = deck->hasField("NOGRAV") ? 0.0 : unit::gravity; // Init state variables (saturation and pressure). @@ -137,7 +143,7 @@ try initStateBasic(*grid->c_grid(), *props, param, gravity[2], state); initBlackoilSurfvol(*grid->c_grid(), *props, state); } else { - initStateFromDeck(*grid->c_grid(), *props, *deck, gravity[2], state); + initStateFromDeck(*grid->c_grid(), *props, deck, gravity[2], state); } bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); @@ -147,7 +153,6 @@ try // Write parameters used for later reference. bool output = param.getDefault("output", true); - std::ofstream epoch_os; std::string output_dir; if (output) { output_dir = @@ -159,12 +164,6 @@ try catch (...) { OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); } - std::string filename = output_dir + "/epoch_timing.param"; - epoch_os.open(filename.c_str(), std::fstream::trunc | std::fstream::out); - // open file to clean it. The file is appended to in SimulatorTwophase - filename = output_dir + "/step_timing.param"; - std::fstream step_os(filename.c_str(), std::fstream::trunc | std::fstream::out); - step_os.close(); param.writeParam(output_dir + "/simulation.param"); } @@ -177,49 +176,35 @@ try // With a deck, we may have more epochs etc. WellState well_state; int step = 0; + Opm::TimeMapPtr timeMap(new Opm::TimeMap(deck)); SimulatorTimer simtimer; - // Use timer for last epoch to obtain total time. - deck->setCurrentEpoch(deck->numberOfEpochs() - 1); - simtimer.init(*deck); + simtimer.init(timeMap); const double total_time = simtimer.totalTime(); // Check for WPOLYMER presence in last epoch to decide // polymer injection control type. - const bool use_wpolymer = deck->hasField("WPOLYMER"); + const bool use_wpolymer = deck->hasKeyword("WPOLYMER"); if (use_wpolymer) { if (param.has("poly_start_days")) { OPM_MESSAGE("Warning: Using WPOLYMER to control injection since it was found in deck. " "You seem to be trying to control it via parameter poly_start_days (etc.) as well."); } } - for (int epoch = 0; epoch < deck->numberOfEpochs(); ++epoch) { - // Set epoch index. - deck->setCurrentEpoch(epoch); + for (size_t reportStepIdx = 0; reportStepIdx < timeMap->numTimesteps(); ++reportStepIdx) { + simtimer.setCurrentStepNum(reportStepIdx); - // Update the timer. - if (deck->hasField("TSTEP")) { - simtimer.init(*deck); - } else { - if (epoch != 0) { - OPM_THROW(std::runtime_error, "No TSTEP in deck for epoch " << epoch); - } - simtimer.init(param); - } - simtimer.setCurrentStepNum(step); - simtimer.setTotalTime(total_time); - - // Report on start of epoch. + // Report on start of step. std::cout << "\n\n-------------- Starting epoch " << epoch << " --------------" << "\n (number of steps: " << simtimer.numSteps() - step << ")\n\n" << std::flush; // Create new wells, polymer inflow controls. - WellsManager wells(*deck, *grid->c_grid(), props->permeability()); + WellsManager wells(eclipseState, reportStepIdx, *grid->c_grid(), props->permeability()); boost::scoped_ptr polymer_inflow; if (use_wpolymer) { if (wells.c_wells() == 0) { OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells."); } - polymer_inflow.reset(new PolymerInflowFromDeck(*deck, *wells.c_wells(), props->numCells())); + polymer_inflow.reset(new PolymerInflowFromDeck(deck, *wells.c_wells(), props->numCells())); } else { polymer_inflow.reset(new PolymerInflowBasic(param.getDefault("poly_start_days", 300.0)*Opm::unit::day, param.getDefault("poly_end_days", 800.0)*Opm::unit::day, @@ -228,11 +213,12 @@ try // @@@ HACK: we should really make a new well state and // properly transfer old well state to it every epoch, // since number of wells may change etc. - if (epoch == 0) { + if (reportStepIdx == 0) { well_state.init(wells.c_wells(), state); } // Create and run simulator. + Opm::DerivedGeology geology(*grid->c_grid(), *new_props, eclipseState, grav); SimulatorFullyImplicitCompressiblePolymer simulator(param, *grid->c_grid(), *new_props, @@ -242,7 +228,7 @@ try *polymer_inflow, linsolver, grav); - if (epoch == 0) { + if (reportStepIdx == 0) { warnIfUnusedParams(param); } SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state); diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp index 1121d197f..eafa8daba 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp @@ -161,6 +161,7 @@ namespace Opm // \TODO: Treat bcs. SimulatorFullyImplicitCompressiblePolymer::Impl::Impl(const parameter::ParameterGroup& param, const UnstructuredGrid& grid, + const DerivedGeology& geo, const BlackoilPropsAdInterface& props, const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, @@ -176,7 +177,7 @@ namespace Opm wells_(wells_manager.c_wells()), polymer_inflow_(polymer_inflow), gravity_(gravity), - geo_(grid_, props_, gravity_), + geo_(geo), solver_(grid_, props_, geo_, rock_comp_props, polymer_props, *wells_manager.c_wells(), linsolver) /* param.getDefault("nl_pressure_residual_tolerance", 0.0), diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp index 387590161..515268844 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp @@ -32,6 +32,7 @@ namespace Opm namespace parameter { class ParameterGroup; } class BlackoilPropsAdInterface; class RockCompressibility; + class DerivedGeology; class WellsManager; class LinearSolverInterface; class SimulatorTimer; @@ -71,6 +72,7 @@ namespace Opm /// \param[in] gravity if non-null, gravity vector SimulatorFullyImplicitCompressiblePolymer(const parameter::ParameterGroup& param, const UnstructuredGrid& grid, + const DerivedGeology& geo, const BlackoilPropsAdInterface& props, const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, From 8620ce26418bc901ed61834cd86a1b3bae2ee458 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 26 Sep 2014 14:03:55 +0800 Subject: [PATCH 49/83] use std::shared_ptr instead of boost::scoped_ptr. --- examples/sim_poly_fi2p_comp_ad.cpp | 37 +++++++++++++++++------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/examples/sim_poly_fi2p_comp_ad.cpp b/examples/sim_poly_fi2p_comp_ad.cpp index dbc7b932d..bea3f57b4 100644 --- a/examples/sim_poly_fi2p_comp_ad.cpp +++ b/examples/sim_poly_fi2p_comp_ad.cpp @@ -50,15 +50,18 @@ #include #include #include + #include #include +#include #include #include -#include + #include #include +#include #include #include #include @@ -96,10 +99,10 @@ try OPM_THROW(std::runtime_error, "This program must be run with an input deck. " "Specify the deck with deck_filename=deckname.data (for example)."); } - boost::scoped_ptr grid; - boost::scoped_ptr props; - boost::scoped_ptr new_props; - boost::scoped_ptr rock_comp; + std::shared_ptr grid; + std::shared_ptr props; + std::shared_ptr new_props; + std::shared_ptr rock_comp; Opm::DeckConstPtr deck; EclipseStateConstPtr eclipseState; PolymerBlackoilState state; @@ -111,7 +114,12 @@ try deck = parser->parseFile(deck_filename); eclipseState.reset(new Opm::EclipseState(deck)); // Grid init - grid.reset(new GridManager(deck)); + std::vector porv; + if (eclipseState->hasDoubleGridProperty("PORV")) { + porv = eclipseState->getDoubleGridProperty("PORV")->getData(); + } + grid.reset(new GridManager(eclipseState->getEclipseGrid(), porv)); +// grid.reset(new GridManager(deck)); // use the capitalized part of the deck's filename between the // last '/' and the last '.' character as base name. @@ -128,16 +136,16 @@ try Opm::EclipseWriter outputWriter(param, share_obj(*deck), share_obj(*grid->c_grid())); */ // Rock and fluid init - props.reset(new BlackoilPropertiesFromDeck(deck, eclipseStae, *grid->c_grid())); + props.reset(new BlackoilPropertiesFromDeck(deck, eclipseState, *grid->c_grid())); new_props.reset(new BlackoilPropsAdFromDeck(deck, eclipseState, *grid->c_grid())); - PolymerProperties polymer_props(eclipseState); + PolymerProperties polymer_props(eclipseState); PolymerPropsAd polymer_props_ad(polymer_props); // check_well_controls = param.getDefault("check_well_controls", false); // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); // Rock compressibility. rock_comp.reset(new RockCompressibility(deck, eclipseState)); // Gravity. - gravity[2] = deck->hasField("NOGRAV") ? 0.0 : unit::gravity; + gravity[2] = deck->hasKeyword("NOGRAV") ? 0.0 : unit::gravity; // Init state variables (saturation and pressure). if (param.has("init_saturation")) { initStateBasic(*grid->c_grid(), *props, param, gravity[2], state); @@ -169,8 +177,7 @@ try std::cout << "\n\n================ Starting main simulation loop ===============\n" - << " (number of epochs: " - << (deck->numberOfEpochs()) << ")\n\n" << std::flush; + << std::flush; SimulatorReport rep; // With a deck, we may have more epochs etc. @@ -193,8 +200,8 @@ try simtimer.setCurrentStepNum(reportStepIdx); // Report on start of step. - std::cout << "\n\n-------------- Starting epoch " << epoch << " --------------" - << "\n (number of steps: " + std::cout << "\n\n-------------- Starting report step " << reportStepIdx << " --------------" + << "\n (number of remaining steps: " << simtimer.numSteps() - step << ")\n\n" << std::flush; // Create new wells, polymer inflow controls. @@ -221,6 +228,7 @@ try Opm::DerivedGeology geology(*grid->c_grid(), *new_props, eclipseState, grav); SimulatorFullyImplicitCompressiblePolymer simulator(param, *grid->c_grid(), + geology, *new_props, polymer_props_ad, rock_comp->isActive() ? rock_comp.get() : 0, @@ -232,9 +240,6 @@ try warnIfUnusedParams(param); } SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state); - if (output) { - epoch_rep.reportParam(epoch_os); - } // Update total timing report and remember step number. rep += epoch_rep; step = simtimer.currentStepNum(); From 1cb1e8475a7f6306df0d9078980a774ddba4365a Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 26 Sep 2014 14:05:17 +0800 Subject: [PATCH 50/83] add PhasePresence, make muOil() function happy. --- opm/polymer/fullyimplicit/utilities.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/opm/polymer/fullyimplicit/utilities.cpp b/opm/polymer/fullyimplicit/utilities.cpp index c75185ac0..8b029b255 100644 --- a/opm/polymer/fullyimplicit/utilities.cpp +++ b/opm/polymer/fullyimplicit/utilities.cpp @@ -245,12 +245,18 @@ namespace Opm src[cell] = 1.0; } } + //Add PhasePresence make muOil() happy. + std::vector phaseCondition; + for (int c = 0; c < num_cells; ++c) { + phaseCondition[c].setFreeWater(); + phaseCondition[c].setFreeOil(); + } const Selector src_selector(src); const V one = V::Constant(num_cells, 1.0); const V zero = V::Zero(num_cells); const std::vector kr = props.relperm(sw, so, zero, cells); const V muw = props.muWat(p, cells); - const V muo = props.muOil(p, zero, cells); + const V muo = props.muOil(p, zero, phaseCondition, cells); const V krw_eff = polymer_props.effectiveRelPerm(c, cmax, kr[0]); const V inv_muw_eff = polymer_props.effectiveInvWaterVisc(c, muw.data()); std::vector mob(np); From ca70d67d832aea1b7e4b04ed9dbaec177d5d6250 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 26 Sep 2014 14:06:01 +0800 Subject: [PATCH 51/83] some changes, mostly of them are API changes from opm-autodiff and opm-parser. --- .../FullyImplicitCompressiblePolymerSolver.cpp | 15 ++++++++------- .../FullyImplicitCompressiblePolymerSolver.hpp | 6 +++--- .../SimulatorFullyImplicitCompressiblePolymer.cpp | 8 +++++--- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index 936bd9191..41721d932 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -569,15 +569,16 @@ namespace { } ADB cell_rho_total = ADB::constant(V::Zero(nc), state.pressure.blockPattern()); std::vector press = computePressures(state); + const std::vector cond = phaseCondition(); for (int phase = 0; phase < 2; ++phase) { - const ADB cell_rho = fluidDensity(phase, press[phase], cells_); + const ADB cell_rho = fluidDensity(phase, press[phase], cond, cells_); cell_rho_total += state.saturation[phase] * cell_rho; } ADB inj_rho_total = ADB::constant(V::Zero(nperf), state.pressure.blockPattern()); assert(np == wells_.number_of_phases); const DataBlock compi = Eigen::Map(wells_.comp_frac, nw, np); for (int phase = 0; phase < 2; ++phase) { - const ADB cell_rho = fluidDensity(phase, press[phase], cells_); + const ADB cell_rho = fluidDensity(phase, press[phase], cond, cells_); const V fraction = compi.col(phase); inj_rho_total += (wops_.w2p * fraction.matrix()).array() * subset(cell_rho, well_cells); } @@ -938,7 +939,7 @@ namespace { ADB FullyImplicitCompressiblePolymerSolver::fluidReciprocFVF(const int phase, const ADB& p , - const std::vector& cond + const std::vector& cond, const std::vector& cells) const { const ADB null = ADB::constant(V::Zero(grid_.number_of_cells, 1), p.blockPattern()); @@ -960,7 +961,7 @@ namespace { ADB FullyImplicitCompressiblePolymerSolver::fluidDensity(const int phase, const ADB& p , - const std::vector& cond + const std::vector& cond, const std::vector& cells) const { const double* rhos = fluid_.surfaceDensity(); @@ -1039,12 +1040,12 @@ namespace { void FullyImplicitCompressiblePolymerSolver::classifyCondition(const PolymerBlackoilState& state) { - const nc = grid_.number_of_cells; + const int nc = grid_.number_of_cells; const DataBlock s = Eigen::Map(& state.saturation()[0], nc, 2); const V so = s.col(1); - for (V::Index c = 0; e = so.size(); c != e; ++c) { - phaseConditon_[c].setFreeWater(); + for (V::Index c = 0, e = so.size(); c != e; ++c) { + phaseCondition_[c].setFreeWater(); if (so[c] > 0) { phaseCondition_[c].setFreeOil(); } diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp index 855d0e7ea..b93fe206e 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp @@ -135,8 +135,8 @@ namespace Opm { const WellOps wops_; const M grav_; V cmax_; - std::vector rq_; std::vector phaseCondition_; + std::vector rq_; // The mass_balance vector has one element for each active phase, // each of which has size equal to the number of cells. // The well_eq has size equal to the number of wells. @@ -225,7 +225,7 @@ namespace Opm { ADB fluidReciprocFVF(const int phase, const ADB& p , - const std::vector& cond + const std::vector& cond, const std::vector& cells) const; ADB @@ -241,7 +241,7 @@ namespace Opm { transMult(const ADB& p) const; const std::vector - phaseCondition() const { return phaseConditon_; } + phaseCondition() const { return phaseCondition_; } void classifyCondition(const PolymerBlackoilState& state); diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp index eafa8daba..1c58031c4 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp @@ -86,6 +86,7 @@ namespace Opm public: Impl(const parameter::ParameterGroup& param, const UnstructuredGrid& grid, + const DerivedGeology& geo, const BlackoilPropsAdInterface& props, const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, @@ -131,6 +132,7 @@ namespace Opm SimulatorFullyImplicitCompressiblePolymer:: SimulatorFullyImplicitCompressiblePolymer(const parameter::ParameterGroup& param, const UnstructuredGrid& grid, + const DerivedGeology& geo, const BlackoilPropsAdInterface& props, const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, @@ -140,7 +142,7 @@ namespace Opm const double* gravity) { - pimpl_.reset(new Impl(param, grid, props, polymer_props, rock_comp_props, wells_manager, polymer_inflow, linsolver, gravity)); + pimpl_.reset(new Impl(param, grid, geo, props, polymer_props, rock_comp_props, wells_manager, polymer_inflow, linsolver, gravity)); } @@ -293,7 +295,7 @@ namespace Opm // Process transport sources (to include bdy terms and well flows). // Opm::computeTransportSource(props_, wells_, well_state, transport_src); // Run solver. - const double current_time = timer.currentTimeElapsed(); + const double current_time = timer.simulationTimeElapsed(); double stepsize = timer.currentStepLength(); polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c); solver_timer.start(); @@ -347,7 +349,7 @@ namespace Opm tot_injected[1] += injected[1]; tot_produced[0] += produced[0]; tot_produced[1] += produced[1]; - watercut.push(timer.currentTimeElapsed() + timer.currentStepLength(), + watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(), produced[0]/(produced[0] + produced[1]), tot_produced[0]/tot_porevol_init); std::cout.precision(5); From 979c2dc0f772866065e85ce0886ad5b3e25c800d Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 26 Sep 2014 14:54:57 +0800 Subject: [PATCH 52/83] use Newton iteration solver interface from opm-autodiff, prepare for CPR supporting. --- examples/sim_poly_fi2p_comp_ad.cpp | 13 ++++-- ...FullyImplicitCompressiblePolymerSolver.cpp | 40 +++++-------------- ...FullyImplicitCompressiblePolymerSolver.hpp | 15 +++---- ...ulatorFullyImplicitCompressiblePolymer.cpp | 6 +-- ...ulatorFullyImplicitCompressiblePolymer.hpp | 4 +- 5 files changed, 32 insertions(+), 46 deletions(-) diff --git a/examples/sim_poly_fi2p_comp_ad.cpp b/examples/sim_poly_fi2p_comp_ad.cpp index bea3f57b4..4f5198cf7 100644 --- a/examples/sim_poly_fi2p_comp_ad.cpp +++ b/examples/sim_poly_fi2p_comp_ad.cpp @@ -41,6 +41,8 @@ #include #include +#include +#include #include #include @@ -156,8 +158,13 @@ try bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); const double *grav = use_gravity ? &gravity[0] : 0; - // Linear solver. - LinearSolverFactory linsolver(param); + // Solver for Newton iterations. + std::unique_ptr fis_solver; + if (param.getDefault("use_cpr", true)) { + fis_solver.reset(new NewtonIterationBlackoilCPR(param)); + } else { + fis_solver.reset(new NewtonIterationBlackoilSimple(param)); + } // Write parameters used for later reference. bool output = param.getDefault("output", true); @@ -234,7 +241,7 @@ try rock_comp->isActive() ? rock_comp.get() : 0, wells, *polymer_inflow, - linsolver, + *fis_solver, grav); if (reportStepIdx == 0) { warnIfUnusedParams(param); diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index 41721d932..e5a5dfd22 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -168,7 +168,7 @@ namespace { const RockCompressibility* rock_comp_props, const PolymerPropsAd& polymer_props_ad, const Wells& wells, - const LinearSolverInterface& linsolver) + const NewtonIterationBlackoilInterface& linsolver) : grid_ (grid) , fluid_ (fluid) , geo_ (geo) @@ -212,7 +212,7 @@ namespace { assemble(dt, x, xw, polymer_inflow, src); const double r0 = residualNorm(); - const double r_polymer = residual_.mass_balance[2].value().matrix().lpNorm(); + const double r_polymer = residual_.material_balance_eq[2].value().matrix().lpNorm(); int it = 0; std::cout << "\nIteration Residual Polymer Res\n" << std::setw(9) << it << std::setprecision(9) @@ -227,7 +227,7 @@ namespace { const double r = residualNorm(); - const double rr_polymer = residual_.mass_balance[2].value().matrix().lpNorm(); + const double rr_polymer = residual_.material_balance_eq[2].value().matrix().lpNorm(); resTooLarge = (r > atol) && (r > rtol*r0); it += 1; @@ -521,11 +521,11 @@ namespace { const ADB krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, cmax, kr[0], state.saturation[0]); const ADB mc = computeMc(state); computeMassFlux(trans, mc, kr[1], krw_eff, state); - residual_.mass_balance[0] = pvdt*(rq_[0].accum[1] - rq_[0].accum[0]) + residual_.material_balance_eq[0] = pvdt*(rq_[0].accum[1] - rq_[0].accum[0]) + ops_.div*rq_[0].mflux; - residual_.mass_balance[1] = pvdt*(rq_[1].accum[1] - rq_[1].accum[0]) + residual_.material_balance_eq[1] = pvdt*(rq_[1].accum[1] - rq_[1].accum[0]) + ops_.div*rq_[1].mflux; - residual_.mass_balance[2] = pvdt*(rq_[2].accum[1] - rq_[2].accum[0]) //+ cell / dt * (rq_[2].ads[1] - rq_[2].ads[0]) + residual_.material_balance_eq[2] = pvdt*(rq_[2].accum[1] - rq_[2].accum[0]) //+ cell / dt * (rq_[2].ads[1] - rq_[2].ads[0]) + ops_.div*rq_[2].mflux; // -------- Extra (optional) sg or rs equation, and rs contributions to the mass balance equations -------- @@ -620,7 +620,7 @@ namespace { // const ADB well_contrib = superset(perf_flux*perf_b, well_cells, nc); well_contribs[phase] = superset(perf_flux*perf_b, well_cells, nc); // DUMP(well_contribs[phase]); - residual_.mass_balance[phase] += well_contribs[phase]; + residual_.material_balance_eq[phase] += well_contribs[phase]; for (int cell = 0; cell < nc; ++cell) { src[cell] += well_contribs[phase].value()[cell]; } @@ -634,7 +634,7 @@ namespace { const V poly_in_c = poly_in_perf;// * poly_mc_cell; const V poly_mc = producer.select(poly_mc_cell, poly_in_c); - residual_.mass_balance[2] += superset(well_perf_rates[0] * poly_mc, well_cells, nc); + residual_.material_balance_eq[2] += superset(well_perf_rates[0] * poly_mc, well_cells, nc); // Set the well flux equation residual_.well_flux_eq = state.qs + well_rates_all; // DUMP(residual_.well_flux_eq); @@ -675,25 +675,7 @@ namespace { V FullyImplicitCompressiblePolymerSolver::solveJacobianSystem() const { - ADB mass_res = vertcat(residual_.mass_balance[0], residual_.mass_balance[1]); - mass_res = vertcat(mass_res, residual_.mass_balance[2]); - const ADB well_res = vertcat(residual_.well_flux_eq, residual_.well_eq); - const ADB total_residual = collapseJacs(vertcat(mass_res, well_res)); - // DUMP(total_residual); - - const Eigen::SparseMatrix matr = total_residual.derivative()[0]; - - V dx(V::Zero(total_residual.size())); - Opm::LinearSolverInterface::LinearSolverReport rep - = linsolver_.solve(matr.rows(), matr.nonZeros(), - matr.outerIndexPtr(), matr.innerIndexPtr(), matr.valuePtr(), - total_residual.value().data(), dx.data()); - if (!rep.converged) { - OPM_THROW(std::runtime_error, - "FullyImplicitCompressiblePolymerSolver::solveJacobianSystem(): " - "Linear solver convergence failure."); - } - return dx; + return linsolver_.computeNewtonIncrement(residual_); } @@ -898,8 +880,8 @@ namespace { { double r = 0; for (std::vector::const_iterator - b = residual_.mass_balance.begin(), - e = residual_.mass_balance.end(); + b = residual_.material_balance_eq.begin(), + e = residual_.material_balance_eq.end(); b != e; ++b) { r = std::max(r, (*b).value().matrix().lpNorm()); diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp index b93fe206e..393eabbbb 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include @@ -34,7 +36,7 @@ namespace Opm { class DerivedGeology; class RockCompressibility; - class LinearSolverInterface; + class NewtonIterationBlackoilInterface; class PolymerBlackoilState; class WellState; @@ -65,7 +67,7 @@ namespace Opm { const RockCompressibility* rock_comp_props, const PolymerPropsAd& polymer_props_ad, const Wells& wells, - const LinearSolverInterface& linsolver); + const NewtonIterationBlackoilInterface& linsolver); /// Take a single forward step, modifiying /// state.pressure() @@ -129,7 +131,7 @@ namespace Opm { const RockCompressibility* rock_comp_props_; const PolymerPropsAd& polymer_props_ad_; const Wells& wells_; - const LinearSolverInterface& linsolver_; + const NewtonIterationBlackoilInterface& linsolver_; const std::vector cells_; // All grid cells HelperOps ops_; const WellOps wops_; @@ -140,12 +142,7 @@ namespace Opm { // The mass_balance vector has one element for each active phase, // each of which has size equal to the number of cells. // The well_eq has size equal to the number of wells. - struct { - std::vector mass_balance; - ADB well_flux_eq; - ADB well_eq; - } residual_; - + LinearisedBlackoilResidual residual_; // Private methods. SolutionState constantState(const PolymerBlackoilState& x, diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp index 1c58031c4..6152474af 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp @@ -92,7 +92,7 @@ namespace Opm const RockCompressibility* rock_comp_props, WellsManager& wells_manager, PolymerInflowInterface& polymer_inflow, - LinearSolverInterface& linsolver, + NewtonIterationBlackoilInterface& linsolver, const double* gravity); SimulatorReport run(SimulatorTimer& timer, @@ -138,7 +138,7 @@ namespace Opm const RockCompressibility* rock_comp_props, WellsManager& wells_manager, PolymerInflowInterface& polymer_inflow, - LinearSolverInterface& linsolver, + NewtonIterationBlackoilInterface& linsolver, const double* gravity) { @@ -169,7 +169,7 @@ namespace Opm const RockCompressibility* rock_comp_props, WellsManager& wells_manager, PolymerInflowInterface& polymer_inflow, - LinearSolverInterface& linsolver, + NewtonIterationBlackoilInterface& linsolver, const double* gravity) : grid_(grid), props_(props), diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp index 515268844..11016a262 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp @@ -34,7 +34,7 @@ namespace Opm class RockCompressibility; class DerivedGeology; class WellsManager; - class LinearSolverInterface; + class NewtonIterationBlackoilInterface; class SimulatorTimer; class PolymerBlackoilState; class WellState; @@ -78,7 +78,7 @@ namespace Opm const RockCompressibility* rock_comp_props, WellsManager& wells_manager, PolymerInflowInterface& polymer_inflow, - LinearSolverInterface& linsolver, + NewtonIterationBlackoilInterface& linsolver, const double* gravity); /// Run the simulation. From 617f439318048e054c4188a78677cc1a1458a3fc Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Mon, 29 Sep 2014 16:38:48 +0800 Subject: [PATCH 53/83] add polymer properties and functions. --- .../FullyImplicitBlackoilPolymerSolver.hpp | 373 +++ ...ullyImplicitBlackoilPolymerSolver_impl.hpp | 2333 +++++++++++++++++ 2 files changed, 2706 insertions(+) create mode 100644 opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp create mode 100644 opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp new file mode 100644 index 000000000..b938a3922 --- /dev/null +++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp @@ -0,0 +1,373 @@ +/* + Copyright 2013 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL ASA. + + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#ifndef OPM_FULLYIMPLICITBLACKOILPOLYMERSOLVER_HEADER_INCLUDED +#define OPM_FULLYIMPLICITBLACKOILPOLYMERSOLVER_HEADER_INCLUDED + +#include +#include +#include +#include +#include +#include +#include + +struct UnstructuredGrid; +struct Wells; + +namespace Opm { + + namespace parameter { class ParameterGroup; } + class DerivedGeology; + class RockCompressibility; + class NewtonIterationBlackoilInterface; + class PolymerBlackoilState; + class WellStateFullyImplicitBlackoil; + + + /// A fully implicit solver for the black-oil-polymer problem. + /// + /// The simulator is capable of handling three-phase problems + /// where gas can be dissolved in oil (but not vice versa). It + /// uses an industry-standard TPFA discretization with per-phase + /// upwind weighting of mobilities. + /// + /// It uses automatic differentiation via the class AutoDiffBlock + /// to simplify assembly of the jacobian matrix. + template + class FullyImplicitBlackoilPolymerSolver + { + public: + /// \brief The type of the grid that we use. + typedef T Grid; + /// Construct a solver. It will retain references to the + /// arguments of this functions, and they are expected to + /// remain in scope for the lifetime of the solver. + /// \param[in] param parameters + /// \param[in] grid grid data structure + /// \param[in] fluid fluid properties + /// \param[in] geo rock properties + /// \param[in] rock_comp_props if non-null, rock compressibility properties + /// \param[in] wells well structure + /// \param[in] linsolver linear solver + FullyImplicitBlackoilSolver(const parameter::ParameterGroup& param, + const Grid& grid , + const BlackoilPropsAdInterface& fluid, + const DerivedGeology& geo , + const RockCompressibility* rock_comp_props, + const PolymerPropsAd& polymer_props_ad, + const Wells& wells, + const NewtonIterationBlackoilInterface& linsolver, + const bool has_disgas, + const bool has_vapoil, + const bool has_polymer); + + /// \brief Set threshold pressures that prevent or reduce flow. + /// This prevents flow across faces if the potential + /// difference is less than the threshold. If the potential + /// difference is greater, the threshold value is subtracted + /// before calculating flow. This is treated symmetrically, so + /// flow is prevented or reduced in both directions equally. + /// \param[in] threshold_pressures_by_face array of size equal to the number of faces + /// of the grid passed in the constructor. + void setThresholdPressures(const std::vector& threshold_pressures_by_face); + + /// Take a single forward step, modifiying + /// state.pressure() + /// state.faceflux() + /// state.saturation() + /// state.gasoilratio() + /// wstate.bhp() + /// \param[in] dt time step size + /// \param[in] state reservoir state + /// \param[in] wstate well state + void + step(const double dt , + PolymerBlackoilState& state , + WellStateFullyImplicitBlackoil& wstate); + + private: + // Types and enums + typedef AutoDiffBlock ADB; + typedef ADB::V V; + typedef ADB::M M; + typedef Eigen::Array DataBlock; + + struct ReservoirResidualQuant { + ReservoirResidualQuant(); + std::vector accum; // Accumulations + ADB mflux; // Mass flux (surface conditions) + ADB b; // Reciprocal FVF + ADB head; // Pressure drop across int. interfaces + ADB mob; // Phase mobility (per cell) + }; + + struct SolutionState { + SolutionState(const int np); + ADB pressure; + std::vector saturation; + ADB rs; + ADB rv; + ADB concentration; + ADB qs; + ADB bhp; + }; + + struct WellOps { + WellOps(const Wells& wells); + M w2p; // well -> perf (scatter) + M p2w; // perf -> well (gather) + }; + + enum { Water = BlackoilPropsAdInterface::Water, + Oil = BlackoilPropsAdInterface::Oil , + Gas = BlackoilPropsAdInterface::Gas }; + + // the Newton relaxation type + enum RelaxType { DAMPEN, SOR }; + enum PrimalVariables { Sg = 0, RS = 1, RV = 2 }; + + // Member data + const Grid& grid_; + const BlackoilPropsAdInterface& fluid_; + const DerivedGeology& geo_; + const RockCompressibility* rock_comp_props_; + const PolymerPropsAd& polymer_props_ad_; + const Wells& wells_; + const NewtonIterationBlackoilInterface& linsolver_; + // For each canonical phase -> true if active + const std::vector active_; + // Size = # active phases. Maps active -> canonical phase indices. + const std::vector canph_; + const std::vector cells_; // All grid cells + HelperOps ops_; + const WellOps wops_; + V cmax_; + const bool has_disgas_; + const bool has_vapoil_; + const bool has_polymer_; + const int poly_pos_; + double dp_max_rel_; + double ds_max_; + double drs_max_rel_; + enum RelaxType relax_type_; + double relax_max_; + double relax_increment_; + double relax_rel_tol_; + int max_iter_; + bool use_threshold_pressure_; + V threshold_pressures_by_interior_face_; + + std::vector rq_; + std::vector phaseCondition_; + V well_perforation_pressure_diffs_; // Diff to bhp for each well perforation. + + LinearisedBlackoilResidual residual_; + + std::vector primalVariable_; + + // Private methods. + SolutionState + constantState(const PolymerBlackoilState& x, + const WellStateFullyImplicitBlackoil& xw); + + SolutionState + variableState(const PolymerBlackoilState& x, + const WellStateFullyImplicitBlackoil& xw); + + void + computeAccum(const SolutionState& state, + const int aix ); + + void computeWellConnectionPressures(const SolutionState& state, + const WellStateFullyImplicitBlackoil& xw); + + void + addWellControlEq(const SolutionState& state, + const WellStateFullyImplicitBlackoil& xw, + const V& aliveWells); + + void + addWellEq(const SolutionState& state, + WellStateFullyImplicitBlackoil& xw, + V& aliveWells); + + void updateWellControls(ADB& bhp, + ADB& well_phase_flow_rate, + WellStateFullyImplicitBlackoil& xw) const; + + void + assemble(const V& dtpv, + const PolymerBlackoilState& x, + WellStateFullyImplicitBlackoil& xw); + + V solveJacobianSystem() const; + + void updateState(const V& dx, + PolymerBlackoilState& state, + WellStateFullyImplicitBlackoil& well_state); + + std::vector + computePressures(const SolutionState& state) const; + + std::vector + computeRelPerm(const SolutionState& state) const; + + std::vector + computeRelPermWells(const SolutionState& state, + const DataBlock& well_s, + const std::vector& well_cells) const; + + void + computeMassFlux(const int actph , + const V& transi, + const ADB& kr , + const ADB& p , + const SolutionState& state ); + + void + computeMassFlux(const V& trans, + const ADB& mc, + const ADB& kro, + const ADB& krw_eff, + const ADB& krg, + const SolutionState& state); + + void + computeCmax(PolymerBlackoilState& state, + const ADB& c); + + ADB + computeMc(const SolutionState& state) const; + + ADB + rockPorosity(const ADB& p) const; + + ADB + rockPermeability(const ADB& p) const; + void applyThresholdPressures(ADB& dp); + + double + residualNorm() const; + + std::vector residuals() const; + + ADB + fluidViscosity(const int phase, + const ADB& p , + const ADB& rs , + const ADB& rv , + const std::vector& cond, + const std::vector& cells) const; + + ADB + fluidReciprocFVF(const int phase, + const ADB& p , + const ADB& rs , + const ADB& rv , + const std::vector& cond, + const std::vector& cells) const; + + ADB + fluidDensity(const int phase, + const ADB& p , + const ADB& rs , + const ADB& rv , + const std::vector& cond, + const std::vector& cells) const; + + V + fluidRsSat(const V& p, + const V& so, + const std::vector& cells) const; + + ADB + fluidRsSat(const ADB& p, + const ADB& so, + const std::vector& cells) const; + + V + fluidRvSat(const V& p, + const V& so, + const std::vector& cells) const; + + ADB + fluidRvSat(const ADB& p, + const ADB& so, + const std::vector& cells) const; + + ADB + computeMc(const SolutionState& state) const; + + ADB + poroMult(const ADB& p) const; + + ADB + transMult(const ADB& p) const; + + void + classifyCondition(const SolutionState& state, + std::vector& cond ) const; + + const std::vector + phaseCondition() const {return phaseCondition_;} + + void + classifyCondition(const PolymerBlackoilState& state); + + + /// update the primal variable for Sg, Rv or Rs. The Gas phase must + /// be active to call this method. + void + updatePrimalVariableFromState(const PolymerBlackoilState& state); + + /// Update the phaseCondition_ member based on the primalVariable_ member. + void + updatePhaseCondFromPrimalVariable(); + + /// Compute convergence based on total mass balance (tol_mb) and maximum + /// residual mass balance (tol_cnv). + bool getConvergence(const double dt); + + void detectNewtonOscillations(const std::vector>& residual_history, + const int it, const double relaxRelTol, + bool& oscillate, bool& stagnate) const; + + void stablizeNewton(V& dx, V& dxOld, const double omega, const RelaxType relax_type) const; + + double dpMaxRel() const { return dp_max_rel_; } + double dsMax() const { return ds_max_; } + double drsMaxRel() const { return drs_max_rel_; } + enum RelaxType relaxType() const { return relax_type_; } + double relaxMax() const { return relax_max_; }; + double relaxIncrement() const { return relax_increment_; }; + double relaxRelTol() const { return relax_rel_tol_; }; + double maxIter() const { return max_iter_; } + + }; +} // namespace Opm + +#include "FullyImplicitBlackoilSolver_impl.hpp" + +#endif // OPM_FULLYIMPLICITBLACKOILSOLVER_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp new file mode 100644 index 000000000..365c937bb --- /dev/null +++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp @@ -0,0 +1,2333 @@ +/* + Copyright 2013 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL ASA. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +//#include + +// A debugging utility. +#define DUMP(foo) \ + do { \ + std::cout << "==========================================\n" \ + << #foo ":\n" \ + << collapseJacs(foo) << std::endl; \ + } while (0) + +#define DUMPVAL(foo) \ + do { \ + std::cout << "==========================================\n" \ + << #foo ":\n" \ + << foo.value() << std::endl; \ + } while (0) + +#define DISKVAL(foo) \ + do { \ + std::ofstream os(#foo); \ + os.precision(16); \ + os << foo.value() << std::endl; \ + } while (0) + + +namespace Opm { + +typedef AutoDiffBlock ADB; +typedef ADB::V V; +typedef ADB::M M; +typedef Eigen::Array DataBlock; + + +namespace { + + + std::vector + buildAllCells(const int nc) + { + std::vector all_cells(nc); + + for (int c = 0; c < nc; ++c) { all_cells[c] = c; } + + return all_cells; + } + + + + template + V computePerfPress(const Grid& grid, const Wells& wells, const V& rho, const double grav) + { + using namespace Opm::AutoDiffGrid; + const int nw = wells.number_of_wells; + const int nperf = wells.well_connpos[nw]; + const int dim = dimensions(grid); + V wdp = V::Zero(nperf,1); + assert(wdp.size() == rho.size()); + + // Main loop, iterate over all perforations, + // using the following formula: + // wdp(perf) = g*(perf_z - well_ref_z)*rho(perf) + // where the total density rho(perf) is taken to be + // sum_p (rho_p*saturation_p) in the perforation cell. + // [although this is computed on the outside of this function]. + for (int w = 0; w < nw; ++w) { + const double ref_depth = wells.depth_ref[w]; + for (int j = wells.well_connpos[w]; j < wells.well_connpos[w + 1]; ++j) { + const int cell = wells.well_cells[j]; + const double cell_depth = cellCentroid(grid, cell)[dim - 1]; + wdp[j] = rho[j]*grav*(cell_depth - ref_depth); + } + } + return wdp; + } + + + + template + std::vector + activePhases(const PU& pu) + { + const int maxnp = Opm::BlackoilPhases::MaxNumPhases; + std::vector active(maxnp, false); + + for (int p = 0; p < pu.MaxNumPhases; ++p) { + active[ p ] = pu.phase_used[ p ] != 0; + } + + return active; + } + + + + template + std::vector + active2Canonical(const PU& pu) + { + const int maxnp = Opm::BlackoilPhases::MaxNumPhases; + std::vector act2can(maxnp, -1); + + for (int phase = 0; phase < maxnp; ++phase) { + if (pu.phase_used[ phase ]) { + act2can[ pu.phase_pos[ phase ] ] = phase; + } + } + + return act2can; + } + + + template + int polymerPos(const PU& pu) + { + const int maxnp = Opm::BlackoilPhases::MaxNumPhases; + int pos = 0; + for (int p = 0; p < maxnp; ++p) { + pos += pu.phase_used[p]; + } + + return pos; + } +} // Anonymous namespace + + + + template + FullyImplicitBlackoilPolymerSolver:: + FullyImplicitBlackoilPolymerSolver(const parameter::ParameterGroup& param, + const Grid& grid , + const BlackoilPropsAdInterface& fluid, + const DerivedGeology& geo , + const RockCompressibility* rock_comp_props, + const PolymerPropsAd& polymer_props_ad, + const Wells& wells, + const NewtonIterationBlackoilInterface& linsolver, + const bool has_disgas, + const bool has_vapoil, + const bool has_polymer) + : grid_ (grid) + , fluid_ (fluid) + , geo_ (geo) + , rock_comp_props_(rock_comp_props) + , polymer_props_ad_(polymer_props_ad) + , wells_ (wells) + , linsolver_ (linsolver) + , active_(activePhases(fluid.phaseUsage())) + , canph_ (active2Canonical(fluid.phaseUsage())) + , cells_ (buildAllCells(Opm::AutoDiffGrid::numCells(grid))) + , ops_ (grid) + , wops_ (wells) + , cmax_(V::Zero(numCells(grid))) + , has_disgas_(has_disgas) + , has_vapoil_(has_vapoil) + , has_polymer_(has_polymer) + , poly_pos_(polymerPos(fluid.phaseUsage())) + , dp_max_rel_ (1.0e9) + , ds_max_ (0.2) + , drs_max_rel_ (1.0e9) + , relax_type_ (DAMPEN) + , relax_max_ (0.5) + , relax_increment_ (0.1) + , relax_rel_tol_ (0.2) + , max_iter_ (15) + , use_threshold_pressure_(false) + , rq_ (fluid.numPhases() + 1) + , phaseCondition_(AutoDiffGrid::numCells(grid)) + , residual_ ( { std::vector(fluid.numPhases() + 1, ADB::null()), + ADB::null(), + ADB::null() } ) + { + if (has_polymer_) { + if (!active_[Water]) { + OPM_THROW(std::logic_error, "Polymer must solved in water!\n"); + } + } + dp_max_rel_ = param.getDefault("dp_max_rel", dp_max_rel_); + ds_max_ = param.getDefault("ds_max", ds_max_); + drs_max_rel_ = param.getDefault("drs_max_rel", drs_max_rel_); + relax_max_ = param.getDefault("relax_max", relax_max_); + max_iter_ = param.getDefault("max_iter", max_iter_); + + std::string relaxation_type = param.getDefault("relax_type", std::string("dampen")); + if (relaxation_type == "dampen") { + relax_type_ = DAMPEN; + } else if (relaxation_type == "sor") { + relax_type_ = SOR; + } else { + OPM_THROW(std::runtime_error, "Unknown Relaxtion Type " << relaxation_type); + } + } + + + + template + void + FullyImplicitBlackoilPolymerSolver:: + setThresholdPressures(const std::vector& threshold_pressures_by_face) + { + const int num_faces = AutoDiffGrid::numFaces(grid_); + if (int(threshold_pressures_by_face.size()) != num_faces) { + OPM_THROW(std::runtime_error, "Illegal size of threshold_pressures_by_face input, must be equal to number of faces."); + } + use_threshold_pressure_ = true; + // Map to interior faces. + const int num_ifaces = ops_.internal_faces.size(); + threshold_pressures_by_interior_face_.resize(num_ifaces); + for (int ii = 0; ii < num_ifaces; ++ii) { + threshold_pressures_by_interior_face_[ii] = threshold_pressures_by_face[ops_.internal_faces[ii]]; + } + } + + + + + template + void + FullyImplicitBlackoilPolymerSolver:: + step(const double dt, + PolymerBlackoilState& x , + WellStateFullyImplicitBlackoil& xw) + { + const V pvdt = geo_.poreVolume() / dt; + + if (active_[Gas]) { updatePrimalVariableFromState(x); } + + { + const SolutionState state = constantState(x, xw); + computeAccum(state, 0); + computeWellConnectionPressures(state, xw); + } + + + std::vector> residual_history; + + assemble(pvdt, x, xw); + + + bool converged = false; + double omega = 1.; + const double r0 = residualNorm(); + + residual_history.push_back(residuals()); + + converged = getConvergence(dt); + + int it = 0; + std::cout << "\nIteration Residual\n" + << std::setw(9) << it << std::setprecision(9) + << std::setw(18) << r0 << std::endl; + + const int sizeNonLinear = residual_.sizeNonLinear(); + + V dxOld = V::Zero(sizeNonLinear); + + bool isOscillate = false; + bool isStagnate = false; + const enum RelaxType relaxtype = relaxType(); + + while ((!converged) && (it < maxIter())) { + V dx = solveJacobianSystem(); + + detectNewtonOscillations(residual_history, it, relaxRelTol(), isOscillate, isStagnate); + + if (isOscillate) { + omega -= relaxIncrement(); + omega = std::max(omega, relaxMax()); + std::cout << " Oscillating behavior detected: Relaxation set to " << omega << std::endl; + } + + stablizeNewton(dx, dxOld, omega, relaxtype); + + updateState(dx, x, xw); + + assemble(pvdt, x, xw); + + const double r = residualNorm(); + + residual_history.push_back(residuals()); + + converged = getConvergence(dt); + + it += 1; + std::cout << std::setw(9) << it << std::setprecision(9) + << std::setw(18) << r << std::endl; + } + + if (!converged) { + std::cerr << "Failed to compute converged solution in " << it << " iterations. Ignoring!\n"; + // OPM_THROW(std::runtime_error, "Failed to compute converged solution in " << it << " iterations."); + } + } + + + + + + template + FullyImplicitBlackoilPolymerSolver::ReservoirResidualQuant::ReservoirResidualQuant() + : accum(2, ADB::null()) + , mflux( ADB::null()) + , b ( ADB::null()) + , head ( ADB::null()) + , mob ( ADB::null()) + { + } + + + + + + template + FullyImplicitBlackoilPolymerSolver::SolutionState::SolutionState(const int np) + : pressure ( ADB::null()) + , saturation(np, ADB::null()) + , rs ( ADB::null()) + , rv ( ADB::null()) + , concentration( ADB::null()) + , qs ( ADB::null()) + , bhp ( ADB::null()) + { + } + + + + + + template + FullyImplicitBlackoilPolymerSolver:: + WellOps::WellOps(const Wells& wells) + : w2p(wells.well_connpos[ wells.number_of_wells ], + wells.number_of_wells) + , p2w(wells.number_of_wells, + wells.well_connpos[ wells.number_of_wells ]) + { + const int nw = wells.number_of_wells; + const int* const wpos = wells.well_connpos; + + typedef Eigen::Triplet Tri; + + std::vector scatter, gather; + scatter.reserve(wpos[nw]); + gather .reserve(wpos[nw]); + + for (int w = 0, i = 0; w < nw; ++w) { + for (; i < wpos[ w + 1 ]; ++i) { + scatter.push_back(Tri(i, w, 1.0)); + gather .push_back(Tri(w, i, 1.0)); + } + } + + w2p.setFromTriplets(scatter.begin(), scatter.end()); + p2w.setFromTriplets(gather .begin(), gather .end()); + } + + + + + + template + typename FullyImplicitBlackoilPolymerSolver::SolutionState + FullyImplicitBlackoilPolymerSolver::constantState(const PolymerBlackoilState& x, + const WellStateFullyImplicitBlackoil& xw) + { + auto state = variableState(x, xw); + + // HACK: throw away the derivatives. this may not be the most + // performant way to do things, but it will make the state + // automatically consistent with variableState() (and doing + // things automatically is all the rage in this module ;) + state.pressure = ADB::constant(state.pressure.value()); + state.rs = ADB::constant(state.rs.value()); + state.rv = ADB::constant(state.rv.value()); + state.concentration = ADB::constant(state.concentration.value()); + for (int phaseIdx= 0; phaseIdx < x.numPhases(); ++ phaseIdx) + state.saturation[phaseIdx] = ADB::constant(state.saturation[phaseIdx].value()); + state.qs = ADB::constant(state.qs.value()); + state.bhp = ADB::constant(state.bhp.value()); + + return state; + } + + + + + + template + typename FullyImplicitBlackoilPolymerSolver::SolutionState + FullyImplicitBlackoilPolymerSolver::variableState(const PolymerBlackoilState& x, + const WellStateFullyImplicitBlackoil& xw) + { + using namespace Opm::AutoDiffGrid; + const int nc = numCells(grid_); + const int np = x.numPhases(); + + std::vector vars0; + // p, Sw and Rs, Rv or Sg, concentration are used as primary depending on solution conditions + vars0.reserve(np + 2); + // Initial pressure. + assert (not x.pressure().empty()); + const V p = Eigen::Map(& x.pressure()[0], nc, 1); + vars0.push_back(p); + + // Initial saturation. + assert (not x.saturation().empty()); + const DataBlock s = Eigen::Map(& x.saturation()[0], nc, np); + const Opm::PhaseUsage pu = fluid_.phaseUsage(); + // We do not handle a Water/Gas situation correctly, guard against it. + assert (active_[ Oil]); + if (active_[ Water ]) { + const V sw = s.col(pu.phase_pos[ Water ]); + vars0.push_back(sw); + } + + // store cell status in vectors + V isRs = V::Zero(nc,1); + V isRv = V::Zero(nc,1); + V isSg = V::Zero(nc,1); + + if (active_[ Gas ]){ + for (int c = 0; c < nc ; c++ ) { + switch (primalVariable_[c]) { + case PrimalVariables::RS: + isRs[c] = 1; + break; + + case PrimalVariables::RV: + isRv[c] = 1; + break; + + default: + isSg[c] = 1; + break; + } + } + + + // define new primary variable xvar depending on solution condition + V xvar(nc); + const V sg = s.col(pu.phase_pos[ Gas ]); + const V rs = Eigen::Map(& x.gasoilratio()[0], x.gasoilratio().size()); + const V rv = Eigen::Map(& x.rv()[0], x.rv().size()); + xvar = isRs*rs + isRv*rv + isSg*sg; + vars0.push_back(xvar); + } + + // Initial polymer concentration. + if (has_polymer_) { + assert (not x.concentration().empty()); + const V c = Eigen::Map(& x.concentration()[0], nc, 1); + vars0.push_back(c); + } + + // Initial well rates. + assert (not xw.wellRates().empty()); + // Need to reshuffle well rates, from phase running fastest + // to wells running fastest. + const int nw = wells_.number_of_wells; + // The transpose() below switches the ordering. + const DataBlock wrates = Eigen::Map(& xw.wellRates()[0], nw, np).transpose(); + const V qs = Eigen::Map(wrates.data(), nw*np); + vars0.push_back(qs); + + // Initial well bottom-hole pressure. + assert (not xw.bhp().empty()); + const V bhp = Eigen::Map(& xw.bhp()[0], xw.bhp().size()); + vars0.push_back(bhp); + + std::vector vars = ADB::variables(vars0); + + SolutionState state(np); + + // Pressure. + int nextvar = 0; + state.pressure = vars[ nextvar++ ]; + + // Saturations + const std::vector& bpat = vars[0].blockPattern(); + { + ADB so = ADB::constant(V::Ones(nc, 1), bpat); + + if (active_[ Water ]) { + ADB& sw = vars[ nextvar++ ]; + state.saturation[pu.phase_pos[ Water ]] = sw; + so = so - sw; + } + + if (active_[ Gas]) { + // Define Sg Rs and Rv in terms of xvar. + const ADB& xvar = vars[ nextvar++ ]; + const ADB& sg = isSg*xvar + isRv* so; + state.saturation[ pu.phase_pos[ Gas ] ] = sg; + so = so - sg; + const ADB rsSat = fluidRsSat(state.pressure, so, cells_); + const ADB rvSat = fluidRvSat(state.pressure, so, cells_); + + if (has_disgas_) { + state.rs = (1-isRs) * rsSat + isRs*xvar; + } else { + state.rs = rsSat; + } + if (has_vapoil_) { + state.rv = (1-isRv) * rvSat + isRv*xvar; + } else { + state.rv = rvSat; + } + } + + if (active_[ Oil ]) { + // Note that so is never a primary variable. + state.saturation[ pu.phase_pos[ Oil ] ] = so; + } + } + + // Concentration. + if (has_polymer_) { + state.concentration = vars[nextvar++]; + } + // Qs. + state.qs = vars[ nextvar++ ]; + + // Bhp. + state.bhp = vars[ nextvar++ ]; + + assert(nextvar == int(vars.size())); + + return state; + } + + + + + + template + void + FullyImplicitBlackoilPolymerSolver::computeAccum(const SolutionState& state, + const int aix ) + { + const Opm::PhaseUsage& pu = fluid_.phaseUsage(); + + const ADB& press = state.pressure; + const std::vector& sat = state.saturation; + const ADB& rs = state.rs; + const ADB& rv = state.rv; + const ADB& c = state.concentration; + + const std::vector cond = phaseCondition(); + std::vector pressure = computePressures(state); + + const ADB pv_mult = poroMult(press); + + const int maxnp = Opm::BlackoilPhases::MaxNumPhases; + for (int phase = 0; phase < maxnp; ++phase) { + if (active_[ phase ]) { + const int pos = pu.phase_pos[ phase ]; + rq_[pos].b = fluidReciprocFVF(phase, pressure, rs, rv, cond, cells_); + rq_[pos].accum[aix] = pv_mult * rq_[pos].b * sat[pos]; + // DUMP(rq_[pos].b); + // DUMP(rq_[pos].accum[aix]); + } + } + + if (active_[ Oil ] && active_[ Gas ]) { + // Account for gas dissolved in oil and vaporized oil + const int po = pu.phase_pos[ Oil ]; + const int pg = pu.phase_pos[ Gas ]; + + rq_[pg].accum[aix] += state.rs * rq_[po].accum[aix]; + rq_[po].accum[aix] += state.rv * rq_[pg].accum[aix]; + //DUMP(rq_[pg].accum[aix]); + } + if (has_polymer_) { + // compute polymer properties. + const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern()); + const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); + const double rho_rock = polymer_props_ad_.rockDensity(); + const V phi = Eigen::Map(& fluid_.porosity()[0], numCells(grid_), 1); + const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); + // compute total phases and determin polymer position. + rq_[poly_pos_].accum[aix] = pv_mult * rq_[pu.phase_pos[Water]].b * sat[pu.phase_pos[Water]] * c * (1. - dead_pore_vol) + pv_mult * rho_rock * (1. - phi) / phi * ads; + } + + } + + + + + + template + void FullyImplicitBlackoilPolymerSovler::computeCmax(PolymerBlackoilState& state, + const ADB& c) + { + const int nc = numCells(grid_); + for (int i = 0; i < nc; ++i) { + cmax_(i) = std::max(cmax_(i), c.value()(i)); + } + std::copy(&cmax_[0], &cmax_[0] + nc, state.maxconcentration().begin()); + } + + + + + + template + void FullyImplicitBlackoilPolymerSolver::computeWellConnectionPressures(const SolutionState& state, + const WellStateFullyImplicitBlackoil& xw) + { + using namespace Opm::AutoDiffGrid; + // 1. Compute properties required by computeConnectionPressureDelta(). + // Note that some of the complexity of this part is due to the function + // taking std::vector arguments, and not Eigen objects. + const int nperf = wells_.well_connpos[wells_.number_of_wells]; + const std::vector well_cells(wells_.well_cells, wells_.well_cells + nperf); + // Compute b, rsmax, rvmax values for perforations. + const ADB perf_press = subset(state.pressure, well_cells); + std::vector perf_cond(nperf); + const std::vector& pc = phaseCondition(); + for (int perf = 0; perf < nperf; ++perf) { + perf_cond[perf] = pc[well_cells[perf]]; + } + const PhaseUsage& pu = fluid_.phaseUsage(); + DataBlock b(nperf, pu.num_phases); + std::vector rssat_perf(nperf, 0.0); + std::vector rvsat_perf(nperf, 0.0); + if (pu.phase_used[BlackoilPhases::Aqua]) { + const ADB bw = fluid_.bWat(perf_press, well_cells); + b.col(pu.phase_pos[BlackoilPhases::Aqua]) = bw.value(); + } + assert(active_[Oil]); + const ADB perf_so = subset(state.saturation[pu.phase_pos[Oil]], well_cells); + if (pu.phase_used[BlackoilPhases::Liquid]) { + const ADB perf_rs = subset(state.rs, well_cells); + const ADB bo = fluid_.bOil(perf_press, perf_rs, perf_cond, well_cells); + b.col(pu.phase_pos[BlackoilPhases::Liquid]) = bo.value(); + const V rssat = fluidRsSat(perf_press.value(), perf_so.value(), well_cells); + rssat_perf.assign(rssat.data(), rssat.data() + nperf); + } + if (pu.phase_used[BlackoilPhases::Vapour]) { + const ADB perf_rv = subset(state.rv, well_cells); + const ADB bg = fluid_.bGas(perf_press, perf_rv, perf_cond, well_cells); + b.col(pu.phase_pos[BlackoilPhases::Vapour]) = bg.value(); + const V rvsat = fluidRvSat(perf_press.value(), perf_so.value(), well_cells); + rvsat_perf.assign(rvsat.data(), rvsat.data() + nperf); + } + // b is row major, so can just copy data. + std::vector b_perf(b.data(), b.data() + nperf * pu.num_phases); + // Extract well connection depths. + const V depth = cellCentroidsZToEigen(grid_); + const V pdepth = subset(depth, well_cells); + std::vector perf_depth(pdepth.data(), pdepth.data() + nperf); + // Surface density. + std::vector surf_dens(fluid_.surfaceDensity(), fluid_.surfaceDensity() + pu.num_phases); + // Gravity + double grav = 0.0; + const double* g = geo_.gravity(); + const int dim = dimensions(grid_); + if (g) { + // Guard against gravity in anything but last dimension. + for (int dd = 0; dd < dim - 1; ++dd) { + assert(g[dd] == 0.0); + } + grav = g[dim - 1]; + } + + // 2. Compute pressure deltas, and store the results. + std::vector cdp = WellDensitySegmented + ::computeConnectionPressureDelta(wells_, xw, fluid_.phaseUsage(), + b_perf, rssat_perf, rvsat_perf, perf_depth, + surf_dens, grav); + well_perforation_pressure_diffs_ = Eigen::Map(cdp.data(), nperf); + } + + + + + + template + void + FullyImplicitBlackoilPolymerSolver:: + assemble(const V& pvdt, + const BlackoilState& x , + WellStateFullyImplicitBlackoil& xw ) + { + using namespace Opm::AutoDiffGrid; + // Create the primary variables. + SolutionState state = variableState(x, xw); + + // DISKVAL(state.pressure); + // DISKVAL(state.saturation[0]); + // DISKVAL(state.saturation[1]); + // DISKVAL(state.saturation[2]); + // DISKVAL(state.rs); + // DISKVAL(state.rv); + // DISKVAL(state.qs); + // DISKVAL(state.bhp); + + // -------- Mass balance equations -------- + + // Compute b_p and the accumulation term b_p*s_p for each phase, + // except gas. For gas, we compute b_g*s_g + Rs*b_o*s_o. + // These quantities are stored in rq_[phase].accum[1]. + // The corresponding accumulation terms from the start of + // the timestep (b^0_p*s^0_p etc.) were already computed + // in step() and stored in rq_[phase].accum[0]. + computeAccum(state, 1); + + // Set up the common parts of the mass balance equations + // for each active phase. + const V transi = subset(geo_.transmissibility(), ops_.internal_faces); + const std::vector kr = computeRelPerm(state); + const std::vector pressures = computePressures(state); + computeMassFlux(transi, kr, pressures, state); + for (int phaseIdx = 0; phaseIdx < fluid_.numPhases(); ++phaseIdx) { + // std::cout << "===== kr[" << phase << "] = \n" << std::endl; + // std::cout << kr[phase]; + // std::cout << "===== rq_[" << phase << "].mflux = \n" << std::endl; + // std::cout << rq_[phase].mflux; + residual_.material_balance_eq[ phaseIdx ] = + pvdt*(rq_[phaseIdx].accum[1] - rq_[phaseIdx].accum[0]) + + ops_.div*rq_[phaseIdx].mflux; + + + // DUMP(ops_.div*rq_[phase].mflux); + // DUMP(residual_.material_balance_eq[phase]); + } + + // -------- Extra (optional) rs and rv contributions to the mass balance equations -------- + + // Add the extra (flux) terms to the mass balance equations + // From gas dissolved in the oil phase (rs) and oil vaporized in the gas phase (rv) + // The extra terms in the accumulation part of the equation are already handled. + if (active_[ Oil ] && active_[ Gas ]) { + const int po = fluid_.phaseUsage().phase_pos[ Oil ]; + const int pg = fluid_.phaseUsage().phase_pos[ Gas ]; + + const UpwindSelector upwindOil(grid_, ops_, + rq_[po].head.value()); + const ADB rs_face = upwindOil.select(state.rs); + + const UpwindSelector upwindGas(grid_, ops_, + rq_[pg].head.value()); + const ADB rv_face = upwindGas.select(state.rv); + + residual_.material_balance_eq[ pg ] += ops_.div * (rs_face * rq_[po].mflux); + residual_.material_balance_eq[ po ] += ops_.div * (rv_face * rq_[pg].mflux); + + // DUMP(residual_.material_balance_eq[ Gas ]); + + } + + const Opm::PhaseUsage& pu = fluid_.phaseUsage(); + // Add polymer equation. + if (has_polymer) { + residula_.material_balance_eq[poly_pos_] = pvdt*(rq_[poly_pos_].accum[1] - rq_[poly_pos_].accum[0]) + + ops_.div*rq_[poly_pos_].mflux; + } + + // Note: updateWellControls() can change all its arguments if + // a well control is switched. + updateWellControls(state.bhp, state.qs, xw); + V aliveWells; + addWellEq(state, xw, aliveWells); + addWellControlEq(state, xw, aliveWells); + } + + + + + + template + void FullyImplicitBlackoilPolymerSolver::addWellEq(const SolutionState& state, + WellStateFullyImplicitBlackoil& xw, + V& aliveWells) + { + const int nc = Opm::AutoDiffGrid::numCells(grid_); + const int np = wells_.number_of_phases; + const int nw = wells_.number_of_wells; + const int nperf = wells_.well_connpos[nw]; + const Opm::PhaseUsage& pu = fluid_.phaseUsage(); + V Tw = Eigen::Map(wells_.WI, nperf); + const std::vector well_cells(wells_.well_cells, wells_.well_cells + nperf); + + // pressure diffs computed already (once per step, not changing per iteration) + const V& cdp = well_perforation_pressure_diffs_; + + // Extract variables for perforation cell pressures + // and corresponding perforation well pressures. + const ADB p_perfcell = subset(state.pressure, well_cells); + + // DUMPVAL(p_perfcell); + // DUMPVAL(state.bhp); + // DUMPVAL(ADB::constant(cdp)); + + // Pressure drawdown (also used to determine direction of flow) + const ADB drawdown = p_perfcell - (wops_.w2p * state.bhp + cdp); + + // current injecting connections + auto connInjInx = drawdown.value() < 0; + + // injector == 1, producer == 0 + V isInj = V::Zero(nw); + for (int w = 0; w < nw; ++w) { + if (wells_.type[w] == INJECTOR) { + isInj[w] = 1; + } + } + +// // A cross-flow connection is defined as a connection which has opposite +// // flow-direction to the well total flow +// V isInjPerf = (wops_.w2p * isInj); +// auto crossFlowConns = (connInjInx != isInjPerf); + +// bool allowCrossFlow = true; + +// if (not allowCrossFlow) { +// auto closedConns = crossFlowConns; +// for (int c = 0; c < nperf; ++c) { +// if (closedConns[c]) { +// Tw[c] = 0; +// } +// } +// connInjInx = !closedConns; +// } +// TODO: not allow for crossflow + + + V isInjInx = V::Zero(nperf); + V isNotInjInx = V::Zero(nperf); + for (int c = 0; c < nperf; ++c){ + if (connInjInx[c]) + isInjInx[c] = 1; + else + isNotInjInx[c] = 1; + } + + + // HANDLE FLOW INTO WELLBORE + + // compute phase volumerates standard conditions + std::vector cq_ps(np, ADB::null()); + for (int phase = 0; phase < np; ++phase) { + const ADB& wellcell_mob = subset ( rq_[phase].mob, well_cells); + const ADB cq_p = -(isNotInjInx * Tw) * (wellcell_mob * drawdown); + cq_ps[phase] = subset(rq_[phase].b,well_cells) * cq_p; + } + if (active_[Oil] && active_[Gas]) { + const int oilpos = pu.phase_pos[Oil]; + const int gaspos = pu.phase_pos[Gas]; + ADB cq_psOil = cq_ps[oilpos]; + ADB cq_psGas = cq_ps[gaspos]; + cq_ps[gaspos] += subset(state.rs,well_cells) * cq_psOil; + cq_ps[oilpos] += subset(state.rv,well_cells) * cq_psGas; + } + + // phase rates at std. condtions + std::vector q_ps(np, ADB::null()); + for (int phase = 0; phase < np; ++phase) { + q_ps[phase] = wops_.p2w * cq_ps[phase]; + } + + // total rates at std + ADB qt_s = ADB::constant(V::Zero(nw), state.bhp.blockPattern()); + for (int phase = 0; phase < np; ++phase) { + qt_s += subset(state.qs, Span(nw, 1, phase*nw)); + } + + // compute avg. and total wellbore phase volumetric rates at std. conds + const DataBlock compi = Eigen::Map(wells_.comp_frac, nw, np); + std::vector wbq(np, ADB::null()); + ADB wbqt = ADB::constant(V::Zero(nw), state.pressure.blockPattern()); + for (int phase = 0; phase < np; ++phase) { + const int pos = pu.phase_pos[phase]; + wbq[phase] = (isInj * compi.col(pos)) * qt_s - q_ps[phase]; + wbqt += wbq[phase]; + } + // DUMPVAL(wbqt); + + // check for dead wells + aliveWells = V::Constant(nw, 1.0); + for (int w = 0; w < nw; ++w) { + if (wbqt.value()[w] == 0) { + aliveWells[w] = 0.0; + } + } + // compute wellbore mixture at std conds + Selector notDeadWells_selector(wbqt.value(), Selector::Zero); + std::vector mix_s(np, ADB::null()); + for (int phase = 0; phase < np; ++phase) { + const int pos = pu.phase_pos[phase]; + mix_s[phase] = notDeadWells_selector.select(ADB::constant(compi.col(pos)), wbq[phase]/wbqt); + } + + + // HANDLE FLOW OUT FROM WELLBORE + + // Total mobilities + ADB mt = subset(rq_[0].mob,well_cells); + for (int phase = 1; phase < np; ++phase) { + mt += subset(rq_[phase].mob,well_cells); + } + + // DUMPVAL(ADB::constant(isInjInx)); + // DUMPVAL(ADB::constant(Tw)); + // DUMPVAL(mt); + // DUMPVAL(drawdown); + + // injection connections total volumerates + ADB cqt_i = -(isInjInx * Tw) * (mt * drawdown); + + // compute volume ratio between connection at standard conditions + ADB volRat = ADB::constant(V::Zero(nperf), state.pressure.blockPattern()); + std::vector cmix_s(np, ADB::null()); + for (int phase = 0; phase < np; ++phase) { + cmix_s[phase] = wops_.w2p * mix_s[phase]; + } + + ADB well_rv = subset(state.rv,well_cells); + ADB well_rs = subset(state.rs,well_cells); + ADB d = V::Constant(nperf,1.0) - well_rv * well_rs; + + for (int phase = 0; phase < np; ++phase) { + ADB tmp = cmix_s[phase]; + + if (phase == Oil && active_[Gas]) { + const int gaspos = pu.phase_pos[Gas]; + tmp = tmp - subset(state.rv,well_cells) * cmix_s[gaspos] / d; + } + if (phase == Gas && active_[Oil]) { + const int oilpos = pu.phase_pos[Oil]; + tmp = tmp - subset(state.rs,well_cells) * cmix_s[oilpos] / d; + } + volRat += tmp / subset(rq_[phase].b,well_cells); + } + + // DUMPVAL(cqt_i); + // DUMPVAL(volRat); + + // injecting connections total volumerates at std cond + ADB cqt_is = cqt_i/volRat; + + // connection phase volumerates at std cond + std::vector cq_s(np, ADB::null()); + for (int phase = 0; phase < np; ++phase) { + cq_s[phase] = cq_ps[phase] + (wops_.w2p * mix_s[phase])*cqt_is; + } + + // DUMPVAL(mix_s[2]); + // DUMPVAL(cq_ps[2]); + + // Add well contributions to mass balance equations + for (int phase = 0; phase < np; ++phase) { + residual_.material_balance_eq[phase] -= superset(cq_s[phase],well_cells,nc); + } + + + // Add WELL EQUATIONS + ADB qs = state.qs; + for (int phase = 0; phase < np; ++phase) { + qs -= superset(wops_.p2w * cq_s[phase], Span(nw, 1, phase*nw), nw*np); + + } + + + V cq = superset(cq_s[0].value(), Span(nperf, np, 0), nperf*np); + for (int phase = 1; phase < np; ++phase) { + cq += superset(cq_s[phase].value(), Span(nperf, np, phase), nperf*np); + } + + std::vector cq_d(cq.data(), cq.data() + nperf*np); + xw.perfPhaseRates() = cq_d; + + residual_.well_flux_eq = qs; + } + + + + + + namespace + { + double rateToCompare(const ADB& well_phase_flow_rate, + const int well, + const int num_phases, + const double* distr) + { + const int num_wells = well_phase_flow_rate.size() / num_phases; + double rate = 0.0; + for (int phase = 0; phase < num_phases; ++phase) { + // Important: well_phase_flow_rate is ordered with all rates for first + // phase coming first, then all for second phase etc. + rate += well_phase_flow_rate.value()[well + phase*num_wells] * distr[phase]; + } + return rate; + } + + bool constraintBroken(const ADB& bhp, + const ADB& well_phase_flow_rate, + const int well, + const int num_phases, + const WellType& well_type, + const WellControls* wc, + const int ctrl_index) + { + const WellControlType ctrl_type = well_controls_iget_type(wc, ctrl_index); + const double target = well_controls_iget_target(wc, ctrl_index); + const double* distr = well_controls_iget_distr(wc, ctrl_index); + + bool broken = false; + + switch (well_type) { + case INJECTOR: + { + switch (ctrl_type) { + case BHP: + broken = bhp.value()[well] > target; + break; + + case RESERVOIR_RATE: // Intentional fall-through + case SURFACE_RATE: + broken = rateToCompare(well_phase_flow_rate, + well, num_phases, distr) > target; + break; + } + } + break; + + case PRODUCER: + { + switch (ctrl_type) { + case BHP: + broken = bhp.value()[well] < target; + break; + + case RESERVOIR_RATE: // Intentional fall-through + case SURFACE_RATE: + // Note that the rates compared below are negative, + // so breaking the constraints means: too high flow rate + // (as for injection). + broken = rateToCompare(well_phase_flow_rate, + well, num_phases, distr) < target; + break; + } + } + break; + + default: + OPM_THROW(std::logic_error, "Can only handle INJECTOR and PRODUCER wells."); + } + + return broken; + } + } // anonymous namespace + + + + + template + void FullyImplicitBlackoilPolymerSolver::updateWellControls(ADB& bhp, + ADB& well_phase_flow_rate, + WellStateFullyImplicitBlackoil& xw) const + { + std::string modestring[3] = { "BHP", "RESERVOIR_RATE", "SURFACE_RATE" }; + // Find, for each well, if any constraints are broken. If so, + // switch control to first broken constraint. + const int np = wells_.number_of_phases; + const int nw = wells_.number_of_wells; + bool bhp_changed = false; + bool rates_changed = false; + for (int w = 0; w < nw; ++w) { + const WellControls* wc = wells_.ctrls[w]; + // The current control in the well state overrides + // the current control set in the Wells struct, which + // is instead treated as a default. + const int current = xw.currentControls()[w]; + // Loop over all controls except the current one, and also + // skip any RESERVOIR_RATE controls, since we cannot + // handle those. + const int nwc = well_controls_get_num(wc); + int ctrl_index = 0; + for (; ctrl_index < nwc; ++ctrl_index) { + if (ctrl_index == current) { + // This is the currently used control, so it is + // used as an equation. So this is not used as an + // inequality constraint, and therefore skipped. + continue; + } + if (constraintBroken(bhp, well_phase_flow_rate, w, np, wells_.type[w], wc, ctrl_index)) { + // ctrl_index will be the index of the broken constraint after the loop. + break; + } + } + if (ctrl_index != nwc) { + // Constraint number ctrl_index was broken, switch to it. + std::cout << "Switching control mode for well " << wells_.name[w] + << " from " << modestring[well_controls_iget_type(wc, current)] + << " to " << modestring[well_controls_iget_type(wc, ctrl_index)] << std::endl; + xw.currentControls()[w] = ctrl_index; + // Also updating well state and primary variables. + // We can only be switching to BHP and SURFACE_RATE + // controls since we do not support RESERVOIR_RATE. + const double target = well_controls_iget_target(wc, ctrl_index); + const double* distr = well_controls_iget_distr(wc, ctrl_index); + switch (well_controls_iget_type(wc, ctrl_index)) { + case BHP: + xw.bhp()[w] = target; + bhp_changed = true; + break; + + case RESERVOIR_RATE: + // No direct change to any observable quantity at + // surface condition. In this case, use existing + // flow rates as initial conditions as reservoir + // rate acts only in aggregate. + // + // Just record the fact that we need to recompute + // the 'well_phase_flow_rate'. + rates_changed = true; + break; + + case SURFACE_RATE: + for (int phase = 0; phase < np; ++phase) { + if (distr[phase] > 0.0) { + xw.wellRates()[np*w + phase] = target * distr[phase]; + } + } + rates_changed = true; + break; + } + } + } + + // Update primary variables, if necessary. + if (bhp_changed) { + ADB::V new_bhp = Eigen::Map(xw.bhp().data(), nw); + bhp = ADB::function(new_bhp, bhp.derivative()); + } + if (rates_changed) { + // Need to reshuffle well rates, from phase running fastest + // to wells running fastest. + // The transpose() below switches the ordering. + const DataBlock wrates = Eigen::Map(xw.wellRates().data(), nw, np).transpose(); + const ADB::V new_qs = Eigen::Map(wrates.data(), nw*np); + well_phase_flow_rate = ADB::function(new_qs, well_phase_flow_rate.derivative()); + } + } + + + + + template + void FullyImplicitBlackoilPolymerSolver::addWellControlEq(const SolutionState& state, + const WellStateFullyImplicitBlackoil& xw, + const V& aliveWells) + { + const int np = wells_.number_of_phases; + const int nw = wells_.number_of_wells; + + V bhp_targets = V::Zero(nw); + V rate_targets = V::Zero(nw); + M rate_distr(nw, np*nw); + for (int w = 0; w < nw; ++w) { + const WellControls* wc = wells_.ctrls[w]; + // The current control in the well state overrides + // the current control set in the Wells struct, which + // is instead treated as a default. + const int current = xw.currentControls()[w]; + + switch (well_controls_iget_type(wc, current)) { + case BHP: + { + bhp_targets (w) = well_controls_iget_target(wc, current); + rate_targets(w) = -1e100; + } + break; + + case RESERVOIR_RATE: // Intentional fall-through + case SURFACE_RATE: + { + // RESERVOIR and SURFACE rates look the same, from a + // high-level point of view, in the system of + // simultaneous linear equations. + + const double* const distr = + well_controls_iget_distr(wc, current); + + for (int p = 0; p < np; ++p) { + rate_distr.insert(w, p*nw + w) = distr[p]; + } + + bhp_targets (w) = -1.0e100; + rate_targets(w) = well_controls_iget_target(wc, current); + } + break; + } + } + const ADB bhp_residual = state.bhp - bhp_targets; + const ADB rate_residual = rate_distr * state.qs - rate_targets; + // Choose bhp residual for positive bhp targets. + Selector bhp_selector(bhp_targets); + residual_.well_eq = bhp_selector.select(bhp_residual, rate_residual); + // For wells that are dead (not flowing), and therefore not communicating + // with the reservoir, we set the equation to be equal to the well's total + // flow. This will be a solution only if the target rate is also zero. + M rate_summer(nw, np*nw); + for (int w = 0; w < nw; ++w) { + for (int phase = 0; phase < np; ++phase) { + rate_summer.insert(w, phase*nw + w) = 1.0; + } + } + Selector alive_selector(aliveWells, Selector::NotEqualZero); + residual_.well_eq = alive_selector.select(residual_.well_eq, rate_summer * state.qs); + // DUMP(residual_.well_eq); + } + + + + + + template + V FullyImplicitBlackoilPolymerSolver::solveJacobianSystem() const + { + return linsolver_.computeNewtonIncrement(residual_); + } + + + + + + namespace { + struct Chop01 { + double operator()(double x) const { return std::max(std::min(x, 1.0), 0.0); } + }; + } + + + + + + template + void FullyImplicitBlackoilPolymerSolver::updateState(const V& dx, + PolymerBlackoilState& state, + WellStateFullyImplicitBlackoil& well_state) + { + using namespace Opm::AutoDiffGrid; + const int np = fluid_.numPhases(); + const int nc = numCells(grid_); + const int nw = wells_.number_of_wells; + const V null; + assert(null.size() == 0); + const V zero = V::Zero(nc); + const V one = V::Constant(nc, 1.0); + + // store cell status in vectors + V isRs = V::Zero(nc,1); + V isRv = V::Zero(nc,1); + V isSg = V::Zero(nc,1); + if (active_[Gas]) { + for (int c = 0; c < nc; ++c) { + switch (primalVariable_[c]) { + case PrimalVariables::RS: + isRs[c] = 1; + break; + + case PrimalVariables::RV: + isRv[c] = 1; + break; + + default: + isSg[c] = 1; + break; + } + } + } + + // Extract parts of dx corresponding to each part. + const V dp = subset(dx, Span(nc)); + int varstart = nc; + const V dsw = active_[Water] ? subset(dx, Span(nc, 1, varstart)) : null; + varstart += dsw.size(); + + const V dxvar = active_[Gas] ? subset(dx, Span(nc, 1, varstart)): null; + varstart += dxvar.size(); + + if (has_polymer_) { + const V dc = subset(dx, Span(nc, 1, varstart)); + } else { + const V dc = null; + } + const V dqs = subset(dx, Span(np*nw, 1, varstart)); + varstart += dqs.size(); + const V dbhp = subset(dx, Span(nw, 1, varstart)); + varstart += dbhp.size(); + assert(varstart == dx.size()); + + // Pressure update. + const double dpmaxrel = dpMaxRel(); + const V p_old = Eigen::Map(&state.pressure()[0], nc, 1); + const V absdpmax = dpmaxrel*p_old.abs(); + const V dp_limited = sign(dp) * dp.abs().min(absdpmax); + const V p = (p_old - dp_limited).max(zero); + std::copy(&p[0], &p[0] + nc, state.pressure().begin()); + + + // Saturation updates. + const Opm::PhaseUsage& pu = fluid_.phaseUsage(); + const DataBlock s_old = Eigen::Map(& state.saturation()[0], nc, np); + const double dsmax = dsMax(); + V so; + V sw; + V sg; + + { + V maxVal = zero; + V dso = zero; + if (active_[Water]){ + maxVal = dsw.abs().max(maxVal); + dso = dso - dsw; + } + + V dsg; + if (active_[Gas]){ + dsg = isSg * dxvar - isRv * dsw; + maxVal = dsg.abs().max(maxVal); + dso = dso - dsg; + } + + maxVal = dso.abs().max(maxVal); + + V step = dsmax/maxVal; + step = step.min(1.); + + if (active_[Water]) { + const int pos = pu.phase_pos[ Water ]; + const V sw_old = s_old.col(pos); + sw = sw_old - step * dsw; + } + + if (active_[Gas]) { + const int pos = pu.phase_pos[ Gas ]; + const V sg_old = s_old.col(pos); + sg = sg_old - step * dsg; + } + + const int pos = pu.phase_pos[ Oil ]; + const V so_old = s_old.col(pos); + so = so_old - step * dso; + } + + // Appleyard chop process. + auto ixg = sg < 0; + for (int c = 0; c < nc; ++c) { + if (ixg[c]) { + sw[c] = sw[c] / (1-sg[c]); + so[c] = so[c] / (1-sg[c]); + sg[c] = 0; + } + } + + + auto ixo = so < 0; + for (int c = 0; c < nc; ++c) { + if (ixo[c]) { + sw[c] = sw[c] / (1-so[c]); + sg[c] = sg[c] / (1-so[c]); + so[c] = 0; + } + } + + auto ixw = sw < 0; + for (int c = 0; c < nc; ++c) { + if (ixw[c]) { + so[c] = so[c] / (1-sw[c]); + sg[c] = sg[c] / (1-sw[c]); + sw[c] = 0; + } + } + + const V sumSat = sw + so + sg; + sw = sw / sumSat; + so = so / sumSat; + sg = sg / sumSat; + + // Update the state + for (int c = 0; c < nc; ++c) { + state.saturation()[c*np + pu.phase_pos[ Water ]] = sw[c]; + } + + for (int c = 0; c < nc; ++c) { + state.saturation()[c*np + pu.phase_pos[ Gas ]] = sg[c]; + } + + if (active_[ Oil ]) { + const int pos = pu.phase_pos[ Oil ]; + for (int c = 0; c < nc; ++c) { + state.saturation()[c*np + pos] = so[c]; + } + } + + // Update rs and rv + const double drsmaxrel = drsMaxRel(); + const double drvmax = 1e9;//% same as in Mrst + V rs; + if (has_disgas_) { + const V rs_old = Eigen::Map(&state.gasoilratio()[0], nc); + const V drs = isRs * dxvar; + const V drs_limited = sign(drs) * drs.abs().min(rs_old.abs()*drsmaxrel); + rs = rs_old - drs_limited; + } + V rv; + if (has_vapoil_) { + const V rv_old = Eigen::Map(&state.rv()[0], nc); + const V drv = isRv * dxvar; + const V drv_limited = sign(drv) * drv.abs().min(drvmax); + rv = rv_old - drv_limited; + } + + // Update the state + if (has_disgas_) + std::copy(&rs[0], &rs[0] + nc, state.gasoilratio().begin()); + + if (has_vapoil_) + std::copy(&rv[0], &rv[0] + nc, state.rv().begin()); + + // Sg is used as primal variable for water only cells. + const double epsilon = std::sqrt(std::numeric_limits::epsilon()); + auto watOnly = sw > (1 - epsilon); + + // phase translation sg <-> rs + const V rsSat0 = fluidRsSat(p_old, s_old.col(pu.phase_pos[Oil]), cells_); + const V rsSat = fluidRsSat(p, so, cells_); + + std::fill(primalVariable_.begin(), primalVariable_.end(), PrimalVariables::Sg); + + if (has_disgas_) { + // The obvious case + auto hasGas = (sg > 0 && isRs == 0); + + // keep oil saturated if previous sg is sufficient large: + const int pos = pu.phase_pos[ Gas ]; + auto hadGas = (sg <= 0 && s_old.col(pos) > epsilon); + // Set oil saturated if previous rs is sufficiently large + const V rs_old = Eigen::Map(&state.gasoilratio()[0], nc); + auto gasVaporized = ( (rs > rsSat * (1+epsilon) && isRs == 1 ) && (rs_old > rsSat0 * (1-epsilon)) ); + + auto useSg = watOnly || hasGas || hadGas || gasVaporized; + for (int c = 0; c < nc; ++c) { + if (useSg[c]) { rs[c] = rsSat[c];} + else { primalVariable_[c] = PrimalVariables::RS; } + + } + } + + // phase transitions so <-> rv + const V rvSat0 = fluidRvSat(p_old, s_old.col(pu.phase_pos[Oil]), cells_); + const V rvSat = fluidRvSat(p, so, cells_); + + if (has_vapoil_) { + // The obvious case + auto hasOil = (so > 0 && isRv == 0); + + // keep oil saturated if previous so is sufficient large: + const int pos = pu.phase_pos[ Oil ]; + auto hadOil = (so <= 0 && s_old.col(pos) > epsilon ); + // Set oil saturated if previous rv is sufficiently large + const V rv_old = Eigen::Map(&state.rv()[0], nc); + auto oilCondensed = ( (rv > rvSat * (1+epsilon) && isRv == 1) && (rv_old > rvSat0 * (1-epsilon)) ); + auto useSg = watOnly || hasOil || hadOil || oilCondensed; + for (int c = 0; c < nc; ++c) { + if (useSg[c]) { rv[c] = rvSat[c]; } + else {primalVariable_[c] = PrimalVariables::RV; } + + } + + } + + //Polymer concentration updates. + if (has_polymer_) { + const V c_old = Eigen::Map(&state.concentration()[0], nc, 1); + const V c = (c_old - dc).max(zero); + std::copy(&c[0], &c[0] + nc, state.concentration().begin()); + } + + // Qs update. + // Since we need to update the wellrates, that are ordered by wells, + // from dqs which are ordered by phase, the simplest is to compute + // dwr, which is the data from dqs but ordered by wells. + const DataBlock wwr = Eigen::Map(dqs.data(), np, nw).transpose(); + const V dwr = Eigen::Map(wwr.data(), nw*np); + const V wr_old = Eigen::Map(&well_state.wellRates()[0], nw*np); + const V wr = wr_old - dwr; + std::copy(&wr[0], &wr[0] + wr.size(), well_state.wellRates().begin()); + + // Bhp update. + const V bhp_old = Eigen::Map(&well_state.bhp()[0], nw, 1); + const V dbhp_limited = sign(dbhp) * dbhp.abs().min(bhp_old.abs()*dpmaxrel); + const V bhp = bhp_old - dbhp_limited; + std::copy(&bhp[0], &bhp[0] + bhp.size(), well_state.bhp().begin()); + + // Update phase conditions used for property calculations. + updatePhaseCondFromPrimalVariable(); + } + + + + + + template + std::vector + FullyImplicitBlackoilPolymerSolver::computeRelPerm(const SolutionState& state) const + { + using namespace Opm::AutoDiffGrid; + const int nc = numCells(grid_); + const std::vector& bpat = state.pressure.blockPattern(); + + const ADB null = ADB::constant(V::Zero(nc, 1), bpat); + + const Opm::PhaseUsage& pu = fluid_.phaseUsage(); + const ADB sw = (active_[ Water ] + ? state.saturation[ pu.phase_pos[ Water ] ] + : null); + + const ADB so = (active_[ Oil ] + ? state.saturation[ pu.phase_pos[ Oil ] ] + : null); + + const ADB sg = (active_[ Gas ] + ? state.saturation[ pu.phase_pos[ Gas ] ] + : null); + + return fluid_.relperm(sw, so, sg, cells_); + } + + + template + std::vector + FullyImplicitBlackoilPolymerSolver::computePressures(const SolutionState& state) const + { + using namespace Opm::AutoDiffGrid; + const int nc = numCells(grid_); + const std::vector& bpat = state.pressure.blockPattern(); + + const ADB null = ADB::constant(V::Zero(nc, 1), bpat); + + const Opm::PhaseUsage& pu = fluid_.phaseUsage(); + const ADB sw = (active_[ Water ] + ? state.saturation[ pu.phase_pos[ Water ] ] + : null); + + const ADB so = (active_[ Oil ] + ? state.saturation[ pu.phase_pos[ Oil ] ] + : null); + + const ADB sg = (active_[ Gas ] + ? state.saturation[ pu.phase_pos[ Gas ] ] + : null); + + // convert the pressure offsets to the capillary pressures + std::vector pressure = fluid_.capPress(sw, so, sg, cells_); + for (int phaseIdx = 0; phaseIdx < BlackoilPhases::MaxNumPhases; ++phaseIdx) { + // The reference pressure is always the liquid phase (oil) pressure. + if (phaseIdx == BlackoilPhases::Liquid) + continue; + pressure[phaseIdx] = pressure[phaseIdx] - pressure[BlackoilPhases::Liquid]; + } + + // Since pcow = po - pw, but pcog = pg - po, + // we have + // pw = po - pcow + // pg = po + pcgo + // This is an unfortunate inconsistency, but a convention we must handle. + for (int phaseIdx = 0; phaseIdx < BlackoilPhases::MaxNumPhases; ++phaseIdx) { + if (phaseIdx == BlackoilPhases::Aqua) { + pressure[phaseIdx] = state.pressure - pressure[phaseIdx]; + } else { + pressure[phaseIdx] += state.pressure; + } + } + + return pressure; + } + + + + + template + std::vector + FullyImplicitBlackoilPolymerSolver::computeRelPermWells(const SolutionState& state, + const DataBlock& well_s, + const std::vector& well_cells) const + { + const int nw = wells_.number_of_wells; + const int nperf = wells_.well_connpos[nw]; + const std::vector& bpat = state.pressure.blockPattern(); + + const ADB null = ADB::constant(V::Zero(nperf), bpat); + + const Opm::PhaseUsage& pu = fluid_.phaseUsage(); + const ADB sw = (active_[ Water ] + ? ADB::constant(well_s.col(pu.phase_pos[ Water ]), bpat) + : null); + + const ADB so = (active_[ Oil ] + ? ADB::constant(well_s.col(pu.phase_pos[ Oil ]), bpat) + : null); + + const ADB sg = (active_[ Gas ] + ? ADB::constant(well_s.col(pu.phase_pos[ Gas ]), bpat) + : null); + + return fluid_.relperm(sw, so, sg, well_cells); + } + + + + + + template + void + FullyImplicitBlackoilPolymerSolver::computeMassFlux(const V& transi, + const std::vector& kr , + const std::vector& phasePressure, + const SolutionState& state) + { + if (has_polymer_) { + const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern()); + ADB krw_eff = ADB::null(); + const ADB mc = computeMc(state); + ADB inv_wat_eff_vis = ADB::null(); + } + + for (int phase = 0; phase < fluid_.numPhases(); ++phase) { + const int canonicalPhaseIdx = canph_[phase]; + const std::vector cond = phaseCondition(); + + const ADB tr_mult = transMult(state.pressure); + const ADB mu = fluidViscosity(canonicalPhaseIdx, phasePressure[canonicalPhaseIdx], state.rs, state.rv, cond, cells_); + rq_[canonicalPhaseIdx].mob = tr_mult * kr[canonicalPhaseIdx] / mu; + if (cononicalPhaseIdx == Water) { + if(has_polymer_) { + krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, + cmax, + kr[cononicalPhaseIdx], + state.saturation[cononicalPhaseIdx]); + inv_wat_eff_vis = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mu.value().data()); + rq_[canonicalPhaseIdx].mob = tr_mult * krw_eff * inv_wat_eff_visc; + } + } + + const ADB rho = fluidDensity(canonicalPhaseIdx, phasePressure[canonicalPhaseIdx], state.rs, state.rv, cond, cells_); + + ADB& head = rq_[canonicalPhaseIdx].head; + + // compute gravity potensial using the face average as in eclipse and MRST + const ADB rhoavg = ops_.caver * rho; + + ADB dp = ops_.ngrad * phasePressure[canonicalPhaseIdx] - geo_.gravity()[2] * (rhoavg * (ops_.ngrad * geo_.z().matrix())); + + if (use_threshold_pressure_) { + applyThresholdPressures(dp); + } + + head = transi*dp; + //head = transi*(ops_.ngrad * phasePressure) + gflux; + + UpwindSelector upwind(grid_, ops_, head.value()); + + const ADB& b = rq_[canonicalPhaseIdx].b; + const ADB& mob = rq_[canonicalPhaseIdx].mob; + rq_[canonicalPhaseIdx].mflux = upwind.select(b * mob) * head; + } + + if (has_polymer_) { + rq_[poly_pos_].mob = tr_mult * mc * krw_eff * inv_wat_eff_visc; + rq_[poly_pos_].b = rq_[canph_[Water]].b; + rq_[poly_pos_].head = rq_[canph_[Water]].head; + UpwindSelector upwind(grid_, ops_, rq_[poly_pos_].head.value()); + rq_[poly_pos_].mflux = upwind.select(rq_[poly_pos_].b * rq_[poly_pos_].mob) * rq_[poly_pos_].head; + } + } + + + + template + void + FullyImplicitBlackoilPolymerSolver::applyThresholdPressures(ADB& dp) + { + // We support reversible threshold pressures only. + // Method: if the potential difference is lower (in absolute + // value) than the threshold for any face, then the potential + // (and derivatives) is set to zero. If it is above the + // threshold, the threshold pressure is subtracted from the + // absolute potential (the potential is moved towards zero). + + // Identify the set of faces where the potential is under the + // threshold, that shall have zero flow. Storing the bool + // Array as a V (a double Array) with 1 and 0 elements, a + // 1 where flow is allowed, a 0 where it is not. + const V high_potential = (dp.value().abs() >= threshold_pressures_by_interior_face_).template cast(); + + // Create a sparse vector that nullifies the low potential elements. + const M keep_high_potential = spdiag(high_potential); + + // Find the current sign for the threshold modification + const V sign_dp = sign(dp.value()); + const V threshold_modification = sign_dp * threshold_pressures_by_interior_face_; + + // Modify potential and nullify where appropriate. + dp = keep_high_potential * (dp - threshold_modification); + } + + + + + + template + double + FullyImplicitBlackoilPolymerSolver::residualNorm() const + { + double globalNorm = 0; + std::vector::const_iterator quantityIt = residual_.material_balance_eq.begin(); + const std::vector::const_iterator endQuantityIt = residual_.material_balance_eq.end(); + for (; quantityIt != endQuantityIt; ++quantityIt) { + const double quantityResid = (*quantityIt).value().matrix().norm(); + if (!std::isfinite(quantityResid)) { + const int trouble_phase = quantityIt - residual_.material_balance_eq.begin(); + OPM_THROW(Opm::NumericalProblem, + "Encountered a non-finite residual in material balance equation " + << trouble_phase); + } + globalNorm = std::max(globalNorm, quantityResid); + } + globalNorm = std::max(globalNorm, residual_.well_flux_eq.value().matrix().norm()); + globalNorm = std::max(globalNorm, residual_.well_eq.value().matrix().norm()); + + return globalNorm; + } + + + template + std::vector + FullyImplicitBlackoilPolymerSolver::residuals() const + { + std::vector residual; + + std::vector::const_iterator massBalanceIt = residual_.material_balance_eq.begin(); + const std::vector::const_iterator endMassBalanceIt = residual_.material_balance_eq.end(); + + for (; massBalanceIt != endMassBalanceIt; ++massBalanceIt) { + const double massBalanceResid = (*massBalanceIt).value().matrix().template lpNorm(); + if (!std::isfinite(massBalanceResid)) { + OPM_THROW(Opm::NumericalProblem, + "Encountered a non-finite residual"); + } + residual.push_back(massBalanceResid); + } + + // the following residuals are not used in the oscillation detection now + const double wellFluxResid = residual_.well_flux_eq.value().matrix().template lpNorm(); + if (!std::isfinite(wellFluxResid)) { + OPM_THROW(Opm::NumericalProblem, + "Encountered a non-finite residual"); + } + residual.push_back(wellFluxResid); + + const double wellResid = residual_.well_eq.value().matrix().template lpNorm(); + if (!std::isfinite(wellResid)) { + OPM_THROW(Opm::NumericalProblem, + "Encountered a non-finite residual"); + } + residual.push_back(wellResid); + + return residual; + } + + template + void + FullyImplicitBlackoilPolymerSolver::detectNewtonOscillations(const std::vector>& residual_history, + const int it, const double relaxRelTol, + bool& oscillate, bool& stagnate) const + { + // The detection of oscillation in two primary variable results in the report of the detection + // of oscillation for the solver. + // Only the saturations are used for oscillation detection for the black oil model. + // Stagnate is not used for any treatment here. + + if ( it < 2 ) { + oscillate = false; + stagnate = false; + return; + } + + stagnate = true; + int oscillatePhase = 0; + + for (int phaseIdx= 0; phaseIdx < fluid_.numPhases(); ++ phaseIdx){ + if (active_[phaseIdx]) { + double relChange1 = std::fabs((residual_history[it][phaseIdx] - residual_history[it - 2][phaseIdx]) / + residual_history[it][phaseIdx]); + double relChange2 = std::fabs((residual_history[it][phaseIdx] - residual_history[it - 1][phaseIdx]) / + residual_history[it][phaseIdx]); + oscillatePhase += (relChange1 < relaxRelTol) && (relChange2 > relaxRelTol); + + double relChange3 = std::fabs((residual_history[it - 1][phaseIdx] - residual_history[it - 2][phaseIdx]) / + residual_history[it - 2][phaseIdx]); + if (relChange3 > 1.e-3) { + stagnate = false; + } + } + } + + oscillate = (oscillatePhase > 1); + } + + + template + void + FullyImplicitBlackoilPolymerSolver::stablizeNewton(V& dx, V& dxOld, const double omega, + const RelaxType relax_type) const + { + // The dxOld is updated with dx. + // If omega is equal to 1., no relaxtion will be appiled. + + const V tempDxOld = dxOld; + dxOld = dx; + + switch (relax_type) { + case DAMPEN: + if (omega == 1.) { + return; + } + dx = dx*omega; + return; + case SOR: + if (omega == 1.) { + return; + } + dx = dx*omega + (1.-omega)*tempDxOld; + return; + default: + OPM_THROW(std::runtime_error, "Can only handle DAMPEN and SOR relaxation type."); + } + + return; + } + + template + bool + FullyImplicitBlackoilPolymerSolver::getConvergence(const double dt) + { + const double tol_mb = 1.0e-7; + const double tol_cnv = 1.0e-3; + + const int nc = Opm::AutoDiffGrid::numCells(grid_); + const Opm::PhaseUsage& pu = fluid_.phaseUsage(); + + const V pv = geo_.poreVolume(); + const double pvSum = pv.sum(); + + const std::vector cond = phaseCondition(); + + double CNVW = 0.; + double CNVO = 0.; + double CNVG = 0.; + + double RW_sum = 0.; + double RO_sum = 0.; + double RG_sum = 0.; + + double BW_avg = 0.; + double BO_avg = 0.; + double BG_avg = 0.; + + if (active_[Water]) { + const int pos = pu.phase_pos[Water]; + const ADB& tempBW = rq_[pos].b; + V BW = 1./tempBW.value(); + V RW = residual_.material_balance_eq[Water].value(); + BW_avg = BW.sum()/nc; + const V tempV = RW.abs()/pv; + + CNVW = BW_avg * dt * tempV.maxCoeff(); + RW_sum = RW.sum(); + } + + if (active_[Oil]) { + // Omit the disgas here. We should add it. + const int pos = pu.phase_pos[Oil]; + const ADB& tempBO = rq_[pos].b; + V BO = 1./tempBO.value(); + V RO = residual_.material_balance_eq[Oil].value(); + BO_avg = BO.sum()/nc; + const V tempV = RO.abs()/pv; + + CNVO = BO_avg * dt * tempV.maxCoeff(); + RO_sum = RO.sum(); + } + + if (active_[Gas]) { + // Omit the vapoil here. We should add it. + const int pos = pu.phase_pos[Gas]; + const ADB& tempBG = rq_[pos].b; + V BG = 1./tempBG.value(); + V RG = residual_.material_balance_eq[Gas].value(); + BG_avg = BG.sum()/nc; + const V tempV = RG.abs()/pv; + + CNVG = BG_avg * dt * tempV.maxCoeff(); + RG_sum = RG.sum(); + } + + double tempValue = tol_mb * pvSum /dt; + + bool converged_MB = (fabs(BW_avg*RW_sum) < tempValue) + && (fabs(BO_avg*RO_sum) < tempValue) + && (fabs(BG_avg*RG_sum) < tempValue); + + bool converged_CNV = (CNVW < tol_cnv) && (CNVO < tol_cnv) && (CNVG < tol_cnv); + + double residualWellFlux = residual_.well_flux_eq.value().matrix().template lpNorm(); + double residualWell = residual_.well_eq.value().matrix().template lpNorm(); + + bool converged_Well = (residualWellFlux < 1./Opm::unit::day) && (residualWell < Opm::unit::barsa); + + bool converged = converged_MB && converged_CNV && converged_Well; + +#ifdef OPM_VERBOSE + std::cout << " CNVW " << CNVW << " CNVO " << CNVO << " CNVG " << CNVG << std::endl; + std::cout << " converged_MB " << converged_MB << " converged_CNV " << converged_CNV + << " converged_Well " << converged_Well << " converged " << converged << std::endl; +#endif + return converged; + } + + + template + ADB + FullyImplicitBlackoilPolymerSolver::fluidViscosity(const int phase, + const ADB& p , + const ADB& rs , + const ADB& rv , + const std::vector& cond, + const std::vector& cells) const + { + switch (phase) { + case Water: + return fluid_.muWat(p, cells); + case Oil: { + return fluid_.muOil(p, rs, cond, cells); + } + case Gas: + return fluid_.muGas(p, rv, cond, cells); + default: + OPM_THROW(std::runtime_error, "Unknown phase index " << phase); + } + } + + + + + + template + ADB + FullyImplicitBlackoilPolymerSolver::fluidReciprocFVF(const int phase, + const ADB& p , + const ADB& rs , + const ADB& rv , + const std::vector& cond, + const std::vector& cells) const + { + switch (phase) { + case Water: + return fluid_.bWat(p, cells); + case Oil: { + return fluid_.bOil(p, rs, cond, cells); + } + case Gas: + return fluid_.bGas(p, rv, cond, cells); + default: + OPM_THROW(std::runtime_error, "Unknown phase index " << phase); + } + } + + + + + + template + ADB + FullyImplicitBlackoilPolymerSolver::fluidDensity(const int phase, + const ADB& p , + const ADB& rs , + const ADB& rv , + const std::vector& cond, + const std::vector& cells) const + { + const double* rhos = fluid_.surfaceDensity(); + ADB b = fluidReciprocFVF(phase, p, rs, rv, cond, cells); + ADB rho = V::Constant(p.size(), 1, rhos[phase]) * b; + if (phase == Oil && active_[Gas]) { + // It is correct to index into rhos with canonical phase indices. + rho += V::Constant(p.size(), 1, rhos[Gas]) * rs * b; + } + if (phase == Gas && active_[Oil]) { + // It is correct to index into rhos with canonical phase indices. + rho += V::Constant(p.size(), 1, rhos[Oil]) * rv * b; + } + return rho; + } + + + + + + template + V + FullyImplicitBlackoilPolymerSolver::fluidRsSat(const V& p, + const V& satOil, + const std::vector& cells) const + { + return fluid_.rsSat(p, satOil, cells); + } + + + + + + template + ADB + FullyImplicitBlackoilPolymerSolver::fluidRsSat(const ADB& p, + const ADB& satOil, + const std::vector& cells) const + { + return fluid_.rsSat(p, satOil, cells); + } + + template + V + FullyImplicitBlackoilPolymerSolver::fluidRvSat(const V& p, + const V& satOil, + const std::vector& cells) const + { + return fluid_.rvSat(p, satOil, cells); + } + + + + + + template + ADB + FullyImplicitBlackoilPolymerSolver::fluidRvSat(const ADB& p, + const ADB& satOil, + const std::vector& cells) const + { + return fluid_.rvSat(p, satOil, cells); + } + + + + + + template + ADB + FullyImplicitBlackoilPolymerSovler::computeMc(const SolutionState& state) const + { + return polymer_props_ad_.polymerWaterVelocityRatio(state.concentration); + } + + + + + template + ADB + FullyImplicitBlackoilPolymerSolver::poroMult(const ADB& p) const + { + const int n = p.size(); + if (rock_comp_props_ && rock_comp_props_->isActive()) { + V pm(n); + V dpm(n); + for (int i = 0; i < n; ++i) { + pm[i] = rock_comp_props_->poroMult(p.value()[i]); + dpm[i] = rock_comp_props_->poroMultDeriv(p.value()[i]); + } + ADB::M dpm_diag = spdiag(dpm); + const int num_blocks = p.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = dpm_diag * p.derivative()[block]; + } + return ADB::function(pm, jacs); + else { + return ADB::constant(V::Constant(n, 1.0), p.blockPattern()); + } + } + + + + + + template + ADB + FullyImplicitBlackoilPolymerSolver::transMult(const ADB& p) const + { + const int n = p.size(); + if (rock_comp_props_ && rock_comp_props_->isActive()) { + V tm(n); + V dtm(n); + for (int i = 0; i < n; ++i) { + tm[i] = rock_comp_props_->transMult(p.value()[i]); + dtm[i] = rock_comp_props_->transMultDeriv(p.value()[i]); + } + ADB::M dtm_diag = spdiag(dtm); + const int num_blocks = p.numBlocks(); + std::vector jacs(num_blocks); + for (int block = 0; block < num_blocks; ++block) { + jacs[block] = dtm_diag * p.derivative()[block]; + } + return ADB::function(tm, jacs); + } else { + return ADB::constant(V::Constant(n, 1.0), p.blockPattern()); + } + } + + + /* + template + void + FullyImplicitBlackoilPolymerSolver:: + classifyCondition(const SolutionState& state, + std::vector& cond ) const + { + const PhaseUsage& pu = fluid_.phaseUsage(); + + if (active_[ Gas ]) { + // Oil/Gas or Water/Oil/Gas system + const int po = pu.phase_pos[ Oil ]; + const int pg = pu.phase_pos[ Gas ]; + + const V& so = state.saturation[ po ].value(); + const V& sg = state.saturation[ pg ].value(); + + cond.resize(sg.size()); + + for (V::Index c = 0, e = sg.size(); c != e; ++c) { + if (so[c] > 0) { cond[c].setFreeOil (); } + if (sg[c] > 0) { cond[c].setFreeGas (); } + if (active_[ Water ]) { cond[c].setFreeWater(); } + } + } + else { + // Water/Oil system + assert (active_[ Water ]); + + const int po = pu.phase_pos[ Oil ]; + const V& so = state.saturation[ po ].value(); + + cond.resize(so.size()); + + for (V::Index c = 0, e = so.size(); c != e; ++c) { + cond[c].setFreeWater(); + + if (so[c] > 0) { cond[c].setFreeOil(); } + } + } + } */ + + + template + void + FullyImplicitBlackoilPolymerSolver::classifyCondition(const PolymerBlackoilState& state) + { + using namespace Opm::AutoDiffGrid; + const int nc = numCells(grid_); + const int np = state.numPhases(); + + const PhaseUsage& pu = fluid_.phaseUsage(); + const DataBlock s = Eigen::Map(& state.saturation()[0], nc, np); + if (active_[ Gas ]) { + // Oil/Gas or Water/Oil/Gas system + const V so = s.col(pu.phase_pos[ Oil ]); + const V sg = s.col(pu.phase_pos[ Gas ]); + + for (V::Index c = 0, e = sg.size(); c != e; ++c) { + if (so[c] > 0) { phaseCondition_[c].setFreeOil (); } + if (sg[c] > 0) { phaseCondition_[c].setFreeGas (); } + if (active_[ Water ]) { phaseCondition_[c].setFreeWater(); } + } + } + else { + // Water/Oil system + assert (active_[ Water ]); + + const V so = s.col(pu.phase_pos[ Oil ]); + + + for (V::Index c = 0, e = so.size(); c != e; ++c) { + phaseCondition_[c].setFreeWater(); + + if (so[c] > 0) { phaseCondition_[c].setFreeOil(); } + } + } + + + } + + template + void + FullyImplicitBlackoilPolymerSolver::updatePrimalVariableFromState(const PolymerBlackoilState& state) + { + using namespace Opm::AutoDiffGrid; + const int nc = numCells(grid_); + const int np = state.numPhases(); + + const PhaseUsage& pu = fluid_.phaseUsage(); + const DataBlock s = Eigen::Map(& state.saturation()[0], nc, np); + + // Water/Oil/Gas system + assert (active_[ Gas ]); + + // reset the primary variables if RV and RS is not set Sg is used as primary variable. + primalVariable_.resize(nc); + std::fill(primalVariable_.begin(), primalVariable_.end(), PrimalVariables::Sg); + + const V sg = s.col(pu.phase_pos[ Gas ]); + const V so = s.col(pu.phase_pos[ Oil ]); + const V sw = s.col(pu.phase_pos[ Water ]); + + const double epsilon = std::sqrt(std::numeric_limits::epsilon()); + auto watOnly = sw > (1 - epsilon); + auto hasOil = so > 0; + auto hasGas = sg > 0; + + // For oil only cells Rs is used as primal variable. For cells almost full of water + // the default primal variable (Sg) is used. + if (has_disgas_) { + for (V::Index c = 0, e = sg.size(); c != e; ++c) { + if ( !watOnly[c] && hasOil[c] && !hasGas[c] ) {primalVariable_[c] = PrimalVariables::RS; } + } + } + + // For gas only cells Rv is used as primal variable. For cells almost full of water + // the default primal variable (Sg) is used. + if (has_vapoil_) { + for (V::Index c = 0, e = so.size(); c != e; ++c) { + if ( !watOnly[c] && hasGas[c] && !hasOil[c] ) {primalVariable_[c] = PrimalVariables::RV; } + } + } + updatePhaseCondFromPrimalVariable(); + } + + + + + + /// Update the phaseCondition_ member based on the primalVariable_ member. + template + void + FullyImplicitBlackoilPolymerSolver::updatePhaseCondFromPrimalVariable() + { + if (! active_[Gas]) { + OPM_THROW(std::logic_error, "updatePhaseCondFromPrimarVariable() logic requires active gas phase."); + } + const int nc = primalVariable_.size(); + for (int c = 0; c < nc; ++c) { + phaseCondition_[c] = PhasePresence(); // No free phases. + phaseCondition_[c].setFreeWater(); // Not necessary for property calculation usage. + switch (primalVariable_[c]) { + case PrimalVariables::Sg: + phaseCondition_[c].setFreeOil(); + phaseCondition_[c].setFreeGas(); + break; + case PrimalVariables::RS: + phaseCondition_[c].setFreeOil(); + break; + case PrimalVariables::RV: + phaseCondition_[c].setFreeGas(); + break; + default: + OPM_THROW(std::logic_error, "Unknown primary variable enum value in cell " << c << ": " << primalVariable_[c]); + } + } + } + + + + +} // namespace Opm From 9db24a22d282a92de794cd133159911da9ace1f0 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Mon, 29 Sep 2014 16:39:50 +0800 Subject: [PATCH 54/83] let PolymerBlackoilState return rs() and rv(). --- opm/polymer/PolymerBlackoilState.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/opm/polymer/PolymerBlackoilState.hpp b/opm/polymer/PolymerBlackoilState.hpp index 7e91f83c1..b04d0049a 100644 --- a/opm/polymer/PolymerBlackoilState.hpp +++ b/opm/polymer/PolymerBlackoilState.hpp @@ -65,6 +65,8 @@ namespace Opm std::vector& facepressure() { return state_blackoil_.facepressure(); } std::vector& faceflux () { return state_blackoil_.faceflux(); } std::vector& saturation () { return state_blackoil_.saturation(); } + std::vector& gasoilratio () { return state_blackoil_.gasoilratio(); } + std::vector& rv () { return state_blackoil_.rv(); } std::vector& concentration() { return concentration_; } std::vector& maxconcentration() { return cmax_; } @@ -73,6 +75,8 @@ namespace Opm const std::vector& facepressure() const { return state_blackoil_.facepressure(); } const std::vector& faceflux () const { return state_blackoil_.faceflux(); } const std::vector& saturation () const { return state_blackoil_.saturation(); } + const std::vector& gasoilration() const { return state_blackoil_.gasoilratio(); } + const std::vector& rv () const { return state_blackoil_.rv(); } const std::vector& concentration() const { return concentration_; } const std::vector& maxconcentration() const { return cmax_; } From 9beaf4e03b1a3d0a37decb1b8dbade1fa8c86f60 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 30 Sep 2014 15:22:50 +0800 Subject: [PATCH 55/83] add well rate contribution to polymer mass balance equation. --- .../FullyImplicitBlackoilPolymerSolver.hpp | 9 ++++--- ...ullyImplicitBlackoilPolymerSolver_impl.hpp | 24 ++++++++++++++----- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp index b938a3922..b60f47dea 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp @@ -102,7 +102,8 @@ namespace Opm { void step(const double dt , PolymerBlackoilState& state , - WellStateFullyImplicitBlackoil& wstate); + WellStateFullyImplicitBlackoil& wstate, + const std::vector& polymer_inflow); private: // Types and enums @@ -211,7 +212,8 @@ namespace Opm { void addWellEq(const SolutionState& state, WellStateFullyImplicitBlackoil& xw, - V& aliveWells); + V& aliveWells, + const std::vector& polymer_inflow); void updateWellControls(ADB& bhp, ADB& well_phase_flow_rate, @@ -220,7 +222,8 @@ namespace Opm { void assemble(const V& dtpv, const PolymerBlackoilState& x, - WellStateFullyImplicitBlackoil& xw); + WellStateFullyImplicitBlackoil& xw, + const std::vector& polymer_inflow); V solveJacobianSystem() const; diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp index 365c937bb..efff9e7ee 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp @@ -649,7 +649,7 @@ namespace { template void FullyImplicitBlackoilPolymerSolver::computeWellConnectionPressures(const SolutionState& state, - const WellStateFullyImplicitBlackoil& xw) + const WellStateFullyImplicitBlackoil& xw) { using namespace Opm::AutoDiffGrid; // 1. Compute properties required by computeConnectionPressureDelta(). @@ -725,7 +725,8 @@ namespace { FullyImplicitBlackoilPolymerSolver:: assemble(const V& pvdt, const BlackoilState& x , - WellStateFullyImplicitBlackoil& xw ) + WellStateFullyImplicitBlackoil& xw, + const std::vector& polymer_inflow) { using namespace Opm::AutoDiffGrid; // Create the primary variables. @@ -796,7 +797,7 @@ namespace { const Opm::PhaseUsage& pu = fluid_.phaseUsage(); // Add polymer equation. - if (has_polymer) { + if (has_polymer_) { residula_.material_balance_eq[poly_pos_] = pvdt*(rq_[poly_pos_].accum[1] - rq_[poly_pos_].accum[0]) + ops_.div*rq_[poly_pos_].mflux; } @@ -805,7 +806,7 @@ namespace { // a well control is switched. updateWellControls(state.bhp, state.qs, xw); V aliveWells; - addWellEq(state, xw, aliveWells); + addWellEq(state, xw, aliveWells, polymer_inflow); addWellControlEq(state, xw, aliveWells); } @@ -815,8 +816,9 @@ namespace { template void FullyImplicitBlackoilPolymerSolver::addWellEq(const SolutionState& state, - WellStateFullyImplicitBlackoil& xw, - V& aliveWells) + WellStateFullyImplicitBlackoil& xw, + V& aliveWells, + const std::vector& polymer_inflow) { const int nc = Opm::AutoDiffGrid::numCells(grid_); const int np = wells_.number_of_phases; @@ -998,6 +1000,16 @@ namespace { residual_.material_balance_eq[phase] -= superset(cq_s[phase],well_cells,nc); } + // Add well contributions to polymer mass_balance equation + if (has_polmer_) { + const ADB mc = computeMc(state); + const V polyin = Eigen::Map(&polymer_inflow[0], nc); + const V poly_in_perf = subset(polyin, well_cells); + const V poly_mc_perf = subset(mc, well_cells).value(); + residual_.material_balance_eq[poly_pos_] += superset(cq_ps[pu.phase_pos[Water]]*poly_mc_perf + + (wops_.w2p * mix_s[pu.phase_pos[Water]])*cqt_is*poly_in_perf, + well_cells, nc); + } // Add WELL EQUATIONS ADB qs = state.qs; From 4250d4eda586ab9bb970a9b4d8cacedb407e1bce Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Wed, 8 Oct 2014 10:23:10 +0800 Subject: [PATCH 56/83] use phase pressure to compute fluidRecipeFVF(). --- .../fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp index efff9e7ee..00de81243 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp @@ -599,7 +599,7 @@ namespace { for (int phase = 0; phase < maxnp; ++phase) { if (active_[ phase ]) { const int pos = pu.phase_pos[ phase ]; - rq_[pos].b = fluidReciprocFVF(phase, pressure, rs, rv, cond, cells_); + rq_[pos].b = fluidReciprocFVF(phase, pressure[phase], rs, rv, cond, cells_); rq_[pos].accum[aix] = pv_mult * rq_[pos].b * sat[pos]; // DUMP(rq_[pos].b); // DUMP(rq_[pos].accum[aix]); From 9c72ca40c83add991929e90ef24548482998adfa Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Wed, 8 Oct 2014 11:22:25 +0800 Subject: [PATCH 57/83] add concentration and maxconcentration for PolymerBlackoilState. --- ...atorFullyImplicitBlackoilPolymerOutput.cpp | 211 ++++++++++++++++++ ...atorFullyImplicitBlackoilPolymerOutput.hpp | 96 ++++++++ 2 files changed, 307 insertions(+) create mode 100644 opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymerOutput.cpp create mode 100644 opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymerOutput.hpp diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymerOutput.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymerOutput.cpp new file mode 100644 index 000000000..cb3d710db --- /dev/null +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymerOutput.cpp @@ -0,0 +1,211 @@ +#include "config.h" + +#include "SimulatorFullyImplicitBlackoilPolymerOutput.hpp" + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#ifdef HAVE_DUNE_CORNERPOINT +#include +#include +#include +#include +#endif +namespace Opm +{ + + + void outputStateVtk(const UnstructuredGrid& grid, + const PolymerBlackoilState& state, + const int step, + const std::string& output_dir) + { + // Write data in VTK format. + std::ostringstream vtkfilename; + vtkfilename << output_dir << "/vtk_files"; + boost::filesystem::path fpath(vtkfilename.str()); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + vtkfilename << "/output-" << std::setw(3) << std::setfill('0') << step << ".vtu"; + std::ofstream vtkfile(vtkfilename.str().c_str()); + if (!vtkfile) { + OPM_THROW(std::runtime_error, "Failed to open " << vtkfilename.str()); + } + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + dm["concentration"] = &state.concentration(); + dm["maxconcentration"] = &state.maxconcentration(); + std::vector cell_velocity; + Opm::estimateCellVelocity(AutoDiffGrid::numCells(grid), + AutoDiffGrid::numFaces(grid), + AutoDiffGrid::beginFaceCentroids(grid), + AutoDiffGrid::faceCells(grid), + AutoDiffGrid::beginCellCentroids(grid), + AutoDiffGrid::beginCellVolumes(grid), + AutoDiffGrid::dimensions(grid), + state.faceflux(), cell_velocity); + dm["velocity"] = &cell_velocity; + Opm::writeVtkData(grid, dm, vtkfile); + } + + + void outputStateMatlab(const UnstructuredGrid& grid, + const PolymerBlackoilState& state, + const int step, + const std::string& output_dir) + { + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + dm["surfvolume"] = &state.surfacevol(); + dm["rs"] = &state.gasoilratio(); + dm["rv"] = &state.rv(); + dm["concentration"] = &state.concentration(); + dm["maxconcentration"] = &state.maxconcentration(); + std::vector cell_velocity; + Opm::estimateCellVelocity(AutoDiffGrid::numCells(grid), + AutoDiffGrid::numFaces(grid), + AutoDiffGrid::beginFaceCentroids(grid), + UgGridHelpers::faceCells(grid), + AutoDiffGrid::beginCellCentroids(grid), + AutoDiffGrid::beginCellVolumes(grid), + AutoDiffGrid::dimensions(grid), + state.faceflux(), cell_velocity); + dm["velocity"] = &cell_velocity; + + // Write data (not grid) in Matlab format + for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { + std::ostringstream fname; + fname << output_dir << "/" << it->first; + boost::filesystem::path fpath = fname.str(); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; + std::ofstream file(fname.str().c_str()); + if (!file) { + OPM_THROW(std::runtime_error, "Failed to open " << fname.str()); + } + file.precision(15); + const std::vector& d = *(it->second); + std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); + } + } + void outputWellStateMatlab(const Opm::WellState& well_state, + const int step, + const std::string& output_dir) + { + Opm::DataMap dm; + dm["bhp"] = &well_state.bhp(); + dm["wellrates"] = &well_state.wellRates(); + + // Write data (not grid) in Matlab format + for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { + std::ostringstream fname; + fname << output_dir << "/" << it->first; + boost::filesystem::path fpath = fname.str(); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error,"Creating directories failed: " << fpath); + } + fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; + std::ofstream file(fname.str().c_str()); + if (!file) { + OPM_THROW(std::runtime_error,"Failed to open " << fname.str()); + } + file.precision(15); + const std::vector& d = *(it->second); + std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); + } + } + +#if 0 + void outputWaterCut(const Opm::Watercut& watercut, + const std::string& output_dir) + { + // Write water cut curve. + std::string fname = output_dir + "/watercut.txt"; + std::ofstream os(fname.c_str()); + if (!os) { + OPM_THROW(std::runtime_error, "Failed to open " << fname); + } + watercut.write(os); + } + + void outputWellReport(const Opm::WellReport& wellreport, + const std::string& output_dir) + { + // Write well report. + std::string fname = output_dir + "/wellreport.txt"; + std::ofstream os(fname.c_str()); + if (!os) { + OPM_THROW(std::runtime_error, "Failed to open " << fname); + } + wellreport.write(os); + } +#endif + +#ifdef HAVE_DUNE_CORNERPOINT + void outputStateVtk(const Dune::CpGrid& grid, + const PolymerBlackoilState& state, + const int step, + const std::string& output_dir) + { + // Write data in VTK format. + std::ostringstream vtkfilename; + std::ostringstream vtkpath; + vtkpath << output_dir << "/vtk_files"; + vtkpath << "/output-" << std::setw(3) << std::setfill('0') << step; + boost::filesystem::path fpath(vtkpath.str()); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + vtkfilename << "output-" << std::setw(3) << std::setfill('0') << step; +#if DUNE_VERSION_NEWER(DUNE_GRID, 2, 3) + Dune::VTKWriter writer(grid.leafGridView(), Dune::VTK::nonconforming); +#else + Dune::VTKWriter writer(grid.leafView(), Dune::VTK::nonconforming); +#endif + writer.addCellData(state.saturation(), "saturation", state.numPhases()); + writer.addCellData(state.pressure(), "pressure", 1); + writer.addCellData(state.concentration(), "concentration", 1); + writer.addCellData(state.maxconcentration(), "maxconcentration", 1); + + std::vector cell_velocity; + Opm::estimateCellVelocity(AutoDiffGrid::numCells(grid), + AutoDiffGrid::numFaces(grid), + AutoDiffGrid::beginFaceCentroids(grid), + AutoDiffGrid::faceCells(grid), + AutoDiffGrid::beginCellCentroids(grid), + AutoDiffGrid::beginCellVolumes(grid), + AutoDiffGrid::dimensions(grid), + state.faceflux(), cell_velocity); + writer.addCellData(cell_velocity, "velocity", Dune::CpGrid::dimension); + writer.pwrite(vtkfilename.str(), vtkpath.str(), std::string("."), Dune::VTK::ascii); + } +#endif + +} diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymerOutput.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymerOutput.hpp new file mode 100644 index 000000000..55a364324 --- /dev/null +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymerOutput.hpp @@ -0,0 +1,96 @@ +#ifndef OPM_SIMULATORFULLYIMPLICITBLACKOILPOLYMEROUTPUT_HEADER_INCLUDED +#define OPM_SIMULATORFULLYIMPLICITBLACKOILPOLYMEROUTPUT_HEADER_INCLUDED +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#ifdef HAVE_DUNE_CORNERPOINT +#include +#endif +namespace Opm +{ + + void outputStateVtk(const UnstructuredGrid& grid, + const PolymerBlackoilState& state, + const int step, + const std::string& output_dir); + + + void outputStateMatlab(const UnstructuredGrid& grid, + const PolymerBlackoilState& state, + const int step, + const std::string& output_dir); + + void outputWellStateMatlab(const Opm::WellState& well_state, + const int step, + const std::string& output_dir); +#ifdef HAVE_DUNE_CORNERPOINT + void outputStateVtk(const Dune::CpGrid& grid, + const PolymerBlackoilState& state, + const int step, + const std::string& output_dir); +#endif + + template + void outputStateMatlab(const Grid& grid, + const PolymerBlackoilState& state, + const int step, + const std::string& output_dir) + { + Opm::DataMap dm; + dm["saturation"] = &state.saturation(); + dm["pressure"] = &state.pressure(); + dm["surfvolume"] = &state.surfacevol(); + dm["rs"] = &state.gasoilratio(); + dm["rv"] = &state.rv(); + dm["concentration"] = &state.concentration(); + dm["maxconcentration"] = &state.maxconcentration(); + + std::vector cell_velocity; + Opm::estimateCellVelocity(AutoDiffGrid::numCells(grid), + AutoDiffGrid::numFaces(grid), + AutoDiffGrid::beginFaceCentroids(grid), + UgGridHelpers::faceCells(grid), + AutoDiffGrid::beginCellCentroids(grid), + AutoDiffGrid::beginCellVolumes(grid), + AutoDiffGrid::dimensions(grid), + state.faceflux(), cell_velocity); + dm["velocity"] = &cell_velocity; + + // Write data (not grid) in Matlab format + for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { + std::ostringstream fname; + fname << output_dir << "/" << it->first; + boost::filesystem::path fpath = fname.str(); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; + std::ofstream file(fname.str().c_str()); + if (!file) { + OPM_THROW(std::runtime_error, "Failed to open " << fname.str()); + } + file.precision(15); + const std::vector& d = *(it->second); + std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); + } + } + + +} +#endif From e56ba2afaed83b43c63b941f92d62d0009880975 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Wed, 8 Oct 2014 13:54:45 +0800 Subject: [PATCH 58/83] modify them, let them adapt to polymer simulator. --- .../SimulatorFullyImplicitBlackoilPolymer.hpp | 116 ++++ ...latorFullyImplicitBlackoilPolymer_impl.hpp | 615 ++++++++++++++++++ 2 files changed, 731 insertions(+) create mode 100644 opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp create mode 100644 opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp new file mode 100644 index 000000000..da915f653 --- /dev/null +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp @@ -0,0 +1,116 @@ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL ASA. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#ifndef OPM_SIMULATORFULLYIMPLICITBLACKOILPOLYMER_HEADER_INCLUDED +#define OPM_SIMULATORFULLYIMPLICITBLACKOILPOLYMER_HEADER_INCLUDED + +#include +#include + +struct UnstructuredGrid; +struct Wells; + +namespace Opm +{ + namespace parameter { class ParameterGroup; } + class BlackoilPropsAdInterface; + class RockCompressibility; + class DerivedGeology; + class NewtonIterationBlackoilInterface; + class SimulatorTimer; + class PolymerBlackoilState; + class WellStateFullyImplicitBlackoil; + class EclipseState; + class EclipseWriter; + class PolymerPropsAd; + class PolymerInflowInterface; + struct SimulatorReport; + + /// Class collecting all necessary components for a two-phase simulation. + template + class SimulatorFullyImplicitBlackoilPolymer + { + public: + /// \brief The type of the grid that we use. + typedef T Grid; + /// Initialise from parameters and objects to observe. + /// \param[in] param parameters, this class accepts the following: + /// parameter (default) effect + /// ----------------------------------------------------------- + /// output (true) write output to files? + /// output_dir ("output") output directoty + /// output_interval (1) output every nth step + /// nl_pressure_residual_tolerance (0.0) pressure solver residual tolerance (in Pascal) + /// nl_pressure_change_tolerance (1.0) pressure solver change tolerance (in Pascal) + /// nl_pressure_maxiter (10) max nonlinear iterations in pressure + /// nl_maxiter (30) max nonlinear iterations in transport + /// nl_tolerance (1e-9) transport solver absolute residual tolerance + /// num_transport_substeps (1) number of transport steps per pressure step + /// use_segregation_split (false) solve for gravity segregation (if false, + /// segregation is ignored). + /// + /// \param[in] grid grid data structure + /// \param[in] geo derived geological properties + /// \param[in] props fluid and rock properties + /// \param[in] polymer_props polymer properties + /// \param[in] rock_comp_props if non-null, rock compressibility properties + /// \param[in] linsolver linear solver + /// \param[in] gravity if non-null, gravity vector + /// \param[in] disgas true for dissolved gas option + /// \param[in] vapoil true for vaporized oil option + /// \param[in] eclipse_state + /// \param[in] output_writer + /// \param[in] threshold_pressures_by_face if nonempty, threshold pressures that inhibit flow + SimulatorFullyImplicitBlackoilPolymer(const parameter::ParameterGroup& param, + const Grid& grid, + const DerivedGeology& geo, + BlackoilPropsAdInterface& props, + const PolymerPropsAd& polymer_props, + const RockCompressibility* rock_comp_props, + NewtonIterationBlackoilInterface& linsolver, + PolymerInflowInterface& polymer_inflow, + const double* gravity, + const bool disgas, + const bool vapoil, + const bool polymer, + std::shared_ptr eclipse_state, + EclipseWriter& output_writer, + const std::vector& threshold_pressures_by_face); + + /// Run the simulation. + /// This will run succesive timesteps until timer.done() is true. It will + /// modify the reservoir and well states. + /// \param[in,out] timer governs the requested reporting timesteps + /// \param[in,out] state state of reservoir: pressure, fluxes + /// \param[in,out] well_state state of wells: bhp, perforation rates + /// \return simulation report, with timing data + SimulatorReport run(SimulatorTimer& timer, + PolymerBlackoilState& state); + + private: + class Impl; + // Using shared_ptr instead of scoped_ptr since scoped_ptr requires complete type for Impl. + std::shared_ptr pimpl_; + }; + +} // namespace Opm + +#include "SimulatorFullyImplicitBlackoilPolymer_impl.hpp" +#endif // OPM_SIMULATORFULLYIMPLICITBLACKOILPOLYMER_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp new file mode 100644 index 000000000..3654b525f --- /dev/null +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp @@ -0,0 +1,615 @@ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL ASA. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Opm +{ + template + class SimulatorFullyImplicitBlackoilPolymer::Impl + { + public: + Impl(const parameter::ParameterGroup& param, + const Grid& grid, + const DerivedGeology& geo, + BlackoilPropsAdInterface& props, + const PolymerPropsAd& polymer_props, + const RockCompressibility* rock_comp_props, + NewtonIterationBlackoilInterface& linsolver, + PolymerInflowInterface& polymer_inflow, + const double* gravity, + bool has_disgas, + bool has_vapoil, + bool has_polymer, + std::shared_ptr eclipse_state, + EclipseWriter& output_writer, + const std::vector& threshold_pressures_by_face); + + SimulatorReport run(SimulatorTimer& timer, + PolymerBlackoilState& state); + + private: + // Data. + typedef RateConverter:: + SurfaceToReservoirVoidage< BlackoilPropsAdInterface, + std::vector > RateConverterType; + + const parameter::ParameterGroup param_; + + // Parameters for output. + bool output_; + bool output_vtk_; + std::string output_dir_; + int output_interval_; + // Observed objects. + const Grid& grid_; + BlackoilPropsAdInterface& props_; + const PolymerPropsAd& polymer_props_; + const RockCompressibility* rock_comp_props_; + const double* gravity_; + // Solvers + const DerivedGeology& geo_; + NewtonIterationBlackoilInterface& solver_; + PolymerInflowInterface& polymer_inflow_; + // Misc. data + std::vector allcells_; + const bool has_disgas_; + const bool has_vapoil_; + const bool has_polymer_; + // eclipse_state + std::shared_ptr eclipse_state_; + // output_writer + EclipseWriter& output_writer_; + RateConverterType rateConverter_; + // Threshold pressures. + std::vector threshold_pressures_by_face_; + + void + computeRESV(const std::size_t step, + const Wells* wells, + const PolymerBlackoilState& x, + WellStateFullyImplicitBlackoil& xw); + }; + + + + + template + SimulatorFullyImplicitBlackoilPolymer::SimulatorFullyImplicitBlackoilPolymer(const parameter::ParameterGroup& param, + const Grid& grid, + const DerivedGeology& geo, + BlackoilPropsAdInterface& props, + const PolymerPropsAd& polymer_props, + const RockCompressibility* rock_comp_props, + NewtonIterationBlackoilInterface& linsolver, + const PolymerInflowInterface& polymer_inflow; + const double* gravity, + const bool has_disgas, + const bool has_vapoil, + const bool has_polymer, + std::shared_ptr eclipse_state, + EclipseWriter& output_writer, + const std::vector& threshold_pressures_by_face) + + { + pimpl_.reset(new Impl(param, grid, geo, props, polymer_props, rock_comp_props, linsolver, polymer_inflow, gravity, has_disgas, + has_vapoil, has_polymer, eclipse_state, output_writer, threshold_pressures_by_face)); + } + + + + + + template + SimulatorReport SimulatorFullyImplicitBlackoilPolymer::run(SimulatorTimer& timer, + PolymerBlackoilState& state) + { + return pimpl_->run(timer, state); + } + + + + static void outputWellStateMatlab(const Opm::WellStateFullyImplicitBlackoil& well_state, + const int step, + const std::string& output_dir) + { + Opm::DataMap dm; + dm["bhp"] = &well_state.bhp(); + dm["wellrates"] = &well_state.wellRates(); + + // Write data (not grid) in Matlab format + for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) { + std::ostringstream fname; + fname << output_dir << "/" << it->first; + boost::filesystem::path fpath = fname.str(); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error,"Creating directories failed: " << fpath); + } + fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt"; + std::ofstream file(fname.str().c_str()); + if (!file) { + OPM_THROW(std::runtime_error,"Failed to open " << fname.str()); + } + file.precision(15); + const std::vector& d = *(it->second); + std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n")); + } + } + +#if 0 + static void outputWaterCut(const Opm::Watercut& watercut, + const std::string& output_dir) + { + // Write water cut curve. + std::string fname = output_dir + "/watercut.txt"; + std::ofstream os(fname.c_str()); + if (!os) { + OPM_THROW(std::runtime_error, "Failed to open " << fname); + } + watercut.write(os); + } + + static void outputWellReport(const Opm::WellReport& wellreport, + const std::string& output_dir) + { + // Write well report. + std::string fname = output_dir + "/wellreport.txt"; + std::ofstream os(fname.c_str()); + if (!os) { + OPM_THROW(std::runtime_error, "Failed to open " << fname); + } + wellreport.write(os); + } +#endif + + + // \TODO: Treat bcs. + template + SimulatorFullyImplicitBlackoilPolymer::Impl::Impl(const parameter::ParameterGroup& param, + const Grid& grid, + const DerivedGeology& geo, + BlackoilPropsAdInterface& props, + const PolymerPropdAd& polymer_props, + const RockCompressibility* rock_comp_props, + NewtonIterationBlackoilInterface& linsolver, + PolymerInflowInterface& polymer_inflow, + const double* gravity, + const bool has_disgas, + const bool has_vapoil, + const bool has_polymer, + std::shared_ptr eclipse_state, + EclipseWriter& output_writer, + const std::vector& threshold_pressures_by_face) + : param_(param), + grid_(grid), + props_(props), + polymer_props_(polymer_props), + rock_comp_props_(rock_comp_props), + gravity_(gravity), + geo_(geo), + solver_(linsolver), + polymer_inflow_(polymer_inflow), + has_disgas_(has_disgas), + has_vapoil_(has_vapoil), + has_polymer_(has_polymer), + eclipse_state_(eclipse_state), + output_writer_(output_writer), + rateConverter_(props_, std::vector(AutoDiffGrid::numCells(grid_), 0)), + threshold_pressures_by_face_(threshold_pressures_by_face) + { + // For output. + output_ = param.getDefault("output", true); + if (output_) { + output_vtk_ = param.getDefault("output_vtk", true); + output_dir_ = param.getDefault("output_dir", std::string("output")); + // Ensure that output dir exists + boost::filesystem::path fpath(output_dir_); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + output_interval_ = param.getDefault("output_interval", 1); + } + + // Misc init. + const int num_cells = AutoDiffGrid::numCells(grid); + allcells_.resize(num_cells); + for (int cell = 0; cell < num_cells; ++cell) { + allcells_[cell] = cell; + } + } + + + + + template + SimulatorReport SimulatorFullyImplicitBlackoilPolymer::Impl::run(SimulatorTimer& timer, + PolymerBlackoilState& state) + { + WellStateFullyImplicitBlackoil prev_well_state; + + // Create timers and file for writing timing info. + Opm::time::StopWatch solver_timer; + double stime = 0.0; + Opm::time::StopWatch step_timer; + Opm::time::StopWatch total_timer; + total_timer.start(); + std::string tstep_filename = output_dir_ + "/step_timing.txt"; + std::ofstream tstep_os(tstep_filename.c_str()); + + // Main simulation loop. + while (!timer.done()) { + // Report timestep. + step_timer.start(); + timer.report(std::cout); + + // Create wells and well state. + WellsManager wells_manager(eclipse_state_, + timer.currentStepNum(), + Opm::UgGridHelpers::numCells(grid_), + Opm::UgGridHelpers::globalCell(grid_), + Opm::UgGridHelpers::cartDims(grid_), + Opm::UgGridHelpers::dimensions(grid_), + Opm::UgGridHelpers::beginCellCentroids(grid_), + Opm::UgGridHelpers::cell2Faces(grid_), + Opm::UgGridHelpers::beginFaceCentroids(grid_), + props_.permeability()); + const Wells* wells = wells_manager.c_wells(); + WellStateFullyImplicitBlackoil well_state; + well_state.init(wells, state); + if (timer.currentStepNum() != 0) { + // Transfer previous well state to current. + well_state.partialCopy(prev_well_state, *wells, prev_well_state.numWells()); + } + + // Output state at start of time step. + if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); + outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); + } + if (output_) { + if (timer.currentStepNum() == 0) { + output_writer_.writeInit(timer); + } + output_writer_.writeTimeStep(timer, state, well_state.basicWellState()); + } + + // Max oil saturation (for VPPARS), hysteresis update. + props_.updateSatOilMax(state.saturation()); + props_.updateSatHyst(state.saturation(), allcells_); + + // Compute reservoir volumes for RESV controls. + computeRESV(timer.currentStepNum(), wells, state, well_state); + + // compute polymer inflow + if (has_polymer_) { + std::vector polymer_inflow_c(Opm::UgGridHelpers::numCells(grid_)); + polymer_inflow_.getInflowValues(timer.simulationTimeElapsed(), + timer.simulationTimeElapsed() + timer.currentStepLength(), + polymer_inflow_c); + } + // Run a single step of the solver. + solver_timer.start(); + FullyImplicitBlackoilPolymerSolver solver(param_, grid_, props_, geo_, rock_comp_props_, polymer_props_, *wells, solver_, has_disgas_, has_vapoil_, has_polymer_); + if (!threshold_pressures_by_face_.empty()) { + solver.setThresholdPressures(threshold_pressures_by_face_); + } + solver.step(timer.currentStepLength(), state, well_state, polymer_inflow_c); + solver_timer.stop(); + + // Report timing. + const double st = solver_timer.secsSinceStart(); + std::cout << "Fully implicit solver took: " << st << " seconds." << std::endl; + stime += st; + if (output_) { + SimulatorReport step_report; + step_report.pressure_time = st; + step_report.total_time = step_timer.secsSinceStart(); + step_report.reportParam(tstep_os); + } + + // Increment timer, remember well state. + ++timer; + prev_well_state = well_state; + } + + // Write final simulation state. + if (output_) { + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); + outputWellStateMatlab(prev_well_state, timer.currentStepNum(), output_dir_); + output_writer_.writeTimeStep(timer, state, prev_well_state.basicWellState()); + } + + // Stop timer and create timing report + total_timer.stop(); + SimulatorReport report; + report.pressure_time = stime; + report.transport_time = 0.0; + report.total_time = total_timer.secsSinceStart(); + return report; + } + + namespace SimFIBODetails { + typedef std::unordered_map WellMap; + + inline WellMap + mapWells(const std::vector& wells) + { + WellMap wmap; + + for (std::vector::const_iterator + w = wells.begin(), e = wells.end(); + w != e; ++w) + { + wmap.insert(std::make_pair((*w)->name(), *w)); + } + + return wmap; + } + + inline int + resv_control(const WellControls* ctrl) + { + int i, n = well_controls_get_num(ctrl); + + bool match = false; + for (i = 0; (! match) && (i < n); ++i) { + match = well_controls_iget_type(ctrl, i) == RESERVOIR_RATE; + } + + if (! match) { i = 0; } + + return i - 1; // -1 if no match, undo final "++" otherwise + } + + inline bool + is_resv_prod(const Wells& wells, + const int w) + { + return ((wells.type[w] == PRODUCER) && + (0 <= resv_control(wells.ctrls[w]))); + } + + inline bool + is_resv_prod(const WellMap& wmap, + const std::string& name, + const std::size_t step) + { + bool match = false; + + WellMap::const_iterator i = wmap.find(name); + + if (i != wmap.end()) { + WellConstPtr wp = i->second; + + match = (wp->isProducer(step) && + wp->getProductionProperties(step) + .hasProductionControl(WellProducer::RESV)); + } + + return match; + } + + inline std::vector + resvProducers(const Wells& wells, + const std::size_t step, + const WellMap& wmap) + { + std::vector resv_prod; + + for (int w = 0, nw = wells.number_of_wells; w < nw; ++w) { + if (is_resv_prod(wells, w) || + ((wells.name[w] != 0) && + is_resv_prod(wmap, wells.name[w], step))) + { + resv_prod.push_back(w); + } + } + + return resv_prod; + } + + inline void + historyRates(const PhaseUsage& pu, + const WellProductionProperties& p, + std::vector& rates) + { + assert (! p.predictionMode); + assert (rates.size() == + std::vector::size_type(pu.num_phases)); + + if (pu.phase_used[ BlackoilPhases::Aqua ]) { + const std::vector::size_type + i = pu.phase_pos[ BlackoilPhases::Aqua ]; + + rates[i] = p.WaterRate; + } + + if (pu.phase_used[ BlackoilPhases::Liquid ]) { + const std::vector::size_type + i = pu.phase_pos[ BlackoilPhases::Liquid ]; + + rates[i] = p.OilRate; + } + + if (pu.phase_used[ BlackoilPhases::Vapour ]) { + const std::vector::size_type + i = pu.phase_pos[ BlackoilPhases::Vapour ]; + + rates[i] = p.GasRate; + } + } + } // namespace SimFIBODetails + + template + void + SimulatorFullyImplicitBlackoilPolymer:: + Impl::computeRESV(const std::size_t step, + const Wells* wells, + const PolymerBlackoilState& x, + WellStateFullyImplicitBlackoil& xw) + { + typedef SimFIBODetails::WellMap WellMap; + + const std::vector& w_ecl = eclipse_state_->getSchedule()->getWells(step); + const WellMap& wmap = SimFIBODetails::mapWells(w_ecl); + + const std::vector& resv_prod = + SimFIBODetails::resvProducers(*wells, step, wmap); + + if (! resv_prod.empty()) { + const PhaseUsage& pu = props_.phaseUsage(); + const std::vector::size_type np = props_.numPhases(); + + rateConverter_.defineState(x); + + std::vector distr (np); + std::vector hrates(np); + std::vector prates(np); + + for (std::vector::const_iterator + rp = resv_prod.begin(), e = resv_prod.end(); + rp != e; ++rp) + { + WellControls* ctrl = wells->ctrls[*rp]; + + // RESV control mode, all wells + { + const int rctrl = SimFIBODetails::resv_control(ctrl); + + if (0 <= rctrl) { + const std::vector::size_type off = (*rp) * np; + + // Convert to positive rates to avoid issues + // in coefficient calculations. + std::transform(xw.wellRates().begin() + (off + 0*np), + xw.wellRates().begin() + (off + 1*np), + prates.begin(), std::negate()); + + const int fipreg = 0; // Hack. Ignore FIP regions. + rateConverter_.calcCoeff(prates, fipreg, distr); + + well_controls_iset_distr(ctrl, rctrl, & distr[0]); + } + } + + // RESV control, WCONHIST wells. A bit of duplicate + // work, regrettably. + if (wells->name[*rp] != 0) { + WellMap::const_iterator i = wmap.find(wells->name[*rp]); + + if (i != wmap.end()) { + WellConstPtr wp = i->second; + + const WellProductionProperties& p = + wp->getProductionProperties(step); + + if (! p.predictionMode) { + // History matching (WCONHIST/RESV) + SimFIBODetails::historyRates(pu, p, hrates); + + const int fipreg = 0; // Hack. Ignore FIP regions. + rateConverter_.calcCoeff(hrates, fipreg, distr); + + // WCONHIST/RESV target is sum of all + // observed phase rates translated to + // reservoir conditions. Recall sign + // convention: Negative for producers. + const double target = + - std::inner_product(distr.begin(), distr.end(), + hrates.begin(), 0.0); + + well_controls_clear(ctrl); + well_controls_assert_number_of_phases(ctrl, int(np)); + + const int ok = + well_controls_add_new(RESERVOIR_RATE, target, + & distr[0], ctrl); + + if (ok != 0) { + xw.currentControls()[*rp] = 0; + well_controls_set_current(ctrl, 0); + } + } + } + } + } + } + } +} // namespace Opm From f3553d73c766e59dbd56f70e3301e7664dc9f9d8 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Wed, 8 Oct 2014 14:45:42 +0800 Subject: [PATCH 59/83] since time iteration was moved to the Simulator class, pass PolymerInflowInterface as a shared_ptr. --- .../SimulatorFullyImplicitBlackoilPolymer.hpp | 2 +- .../SimulatorFullyImplicitBlackoilPolymer_impl.hpp | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp index da915f653..a8722e0e1 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp @@ -85,7 +85,7 @@ namespace Opm const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, NewtonIterationBlackoilInterface& linsolver, - PolymerInflowInterface& polymer_inflow, + std::shared_ptr polymer_inflow, const double* gravity, const bool disgas, const bool vapoil, diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp index 3654b525f..14772ac2e 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp @@ -81,7 +81,7 @@ namespace Opm const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, NewtonIterationBlackoilInterface& linsolver, - PolymerInflowInterface& polymer_inflow, + std::shared_ptr polymer_inflow, const double* gravity, bool has_disgas, bool has_vapoil, @@ -115,7 +115,7 @@ namespace Opm // Solvers const DerivedGeology& geo_; NewtonIterationBlackoilInterface& solver_; - PolymerInflowInterface& polymer_inflow_; + std::shared_ptr polymer_inflow, // Misc. data std::vector allcells_; const bool has_disgas_; @@ -147,7 +147,7 @@ namespace Opm const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, NewtonIterationBlackoilInterface& linsolver, - const PolymerInflowInterface& polymer_inflow; + std::shared_ptr polymer_inflow, const double* gravity, const bool has_disgas, const bool has_vapoil, @@ -240,7 +240,7 @@ namespace Opm const PolymerPropdAd& polymer_props, const RockCompressibility* rock_comp_props, NewtonIterationBlackoilInterface& linsolver, - PolymerInflowInterface& polymer_inflow, + std::shared_ptr polymer_inflow, const double* gravity, const bool has_disgas, const bool has_vapoil, @@ -357,6 +357,7 @@ namespace Opm // compute polymer inflow if (has_polymer_) { std::vector polymer_inflow_c(Opm::UgGridHelpers::numCells(grid_)); + polymer_inflow_.reset(new PolymerFromDeck(deck, wells, Opm::UgGridHelpers::numCells(grid_))); polymer_inflow_.getInflowValues(timer.simulationTimeElapsed(), timer.simulationTimeElapsed() + timer.currentStepLength(), polymer_inflow_c); From be927741b815dc42474488d58a80f98e06da045b Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Wed, 8 Oct 2014 14:46:43 +0800 Subject: [PATCH 60/83] create a blackoil+polymer simulator. --- examples/sim_poly_fibo_ad.cpp | 257 ++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 examples/sim_poly_fibo_ad.cpp diff --git a/examples/sim_poly_fibo_ad.cpp b/examples/sim_poly_fibo_ad.cpp new file mode 100644 index 000000000..285e06268 --- /dev/null +++ b/examples/sim_poly_fibo_ad.cpp @@ -0,0 +1,257 @@ +/* + Copyright 2014 SINTEF ICT, Applied Mathematics. + Copyright 2014 STATOIL ASA. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ +#include "config.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + + +namespace +{ + void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param) + { + if (param.anyUnused()) { + std::cout << "-------------------- Unused parameters: --------------------\n"; + param.displayUsage(); + std::cout << "----------------------------------------------------------------" << std::endl; + } + } +} // anon namespace + + + +// ----------------- Main program ----------------- +int +main(int argc, char** argv) +try +{ + using namespace Opm; + + std::cout << "\n================ Test program for fully implicit three-phase black-oil flow ===============\n\n"; + parameter::ParameterGroup param(argc, argv, false); + std::cout << "--------------- Reading parameters ---------------" << std::endl; + + // If we have a "deck_filename", grid and props will be read from that. + bool use_deck = param.has("deck_filename"); + if (!use_deck) { + OPM_THROW(std::runtime_error, "This program must be run with an input deck. " + "Specify the deck with deck_filename=deckname.data (for example)."); + } + std::shared_ptr grid; + std::shared_ptr props; + std::shared_ptr new_props; + std::shared_ptr rock_comp; + PolymerBlackoilState state; + // bool check_well_controls = false; + // int max_well_control_iterations = 0; + double gravity[3] = { 0.0 }; + std::string deck_filename = param.get("deck_filename"); + + Opm::ParserPtr newParser(new Opm::Parser() ); + bool strict_parsing = param.getDefault("strict_parsing", true); + Opm::DeckConstPtr deck = newParser->parseFile(deck_filename, strict_parsing); + std::shared_ptr eclipseState(new EclipseState(deck)); + + // Grid init + std::vector porv; + if (eclipseState->hasDoubleGridProperty("PORV")) { + porv = eclipseState->getDoubleGridProperty("PORV")->getData(); + } + grid.reset(new GridManager(eclipseState->getEclipseGrid(), porv)); + auto &cGrid = *grid->c_grid(); + const PhaseUsage pu = Opm::phaseUsageFromDeck(deck); + Opm::EclipseWriter outputWriter(param, + eclipseState, + pu, + cGrid.number_of_cells, + cGrid.global_cell); + + // Rock and fluid init + props.reset(new BlackoilPropertiesFromDeck(deck, eclipseState, *grid->c_grid(), param)); + new_props.reset(new BlackoilPropsAdFromDeck(deck, eclipseState, *grid->c_grid())); + PolymerProperties polymer_props(eclipseState); + PolymerPropsAd polymer_props_ad(polymer_props); + // check_well_controls = param.getDefault("check_well_controls", false); + // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); + // Rock compressibility. + rock_comp.reset(new RockCompressibility(deck, eclipseState)); + + // Gravity. + gravity[2] = deck->hasKeyword("NOGRAV") ? 0.0 : unit::gravity; + + // Init state variables (saturation and pressure). + if (param.has("init_saturation")) { + initStateBasic(*grid->c_grid(), *props, param, gravity[2], state); + initBlackoilSurfvol(*grid->c_grid(), *props, state); + enum { Oil = BlackoilPhases::Liquid, Gas = BlackoilPhases::Vapour }; + if (pu.phase_used[Oil] && pu.phase_used[Gas]) { + const int np = props->numPhases(); + const int nc = grid->c_grid()->number_of_cells; + for (int c = 0; c < nc; ++c) { + state.gasoilratio()[c] = state.surfacevol()[c*np + pu.phase_pos[Gas]] + / state.surfacevol()[c*np + pu.phase_pos[Oil]]; + } + } + } else if (deck->hasKeyword("EQUIL") && props->numPhases() == 3) { + state.init(*grid->c_grid(), props->numPhases()); + const double grav = param.getDefault("gravity", unit::gravity); + initStateEquil(*grid->c_grid(), *props, deck, eclipseState, grav, state); + state.faceflux().resize(grid->c_grid()->number_of_faces, 0.0); + } else { + initBlackoilStateFromDeck(*grid->c_grid(), *props, deck, gravity[2], state); + } + + bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); + const double *grav = use_gravity ? &gravity[0] : 0; + + // Solver for Newton iterations. + std::unique_ptr fis_solver; + if (param.getDefault("use_cpr", true)) { + fis_solver.reset(new NewtonIterationBlackoilCPR(param)); + } else { + fis_solver.reset(new NewtonIterationBlackoilSimple(param)); + } + + // Write parameters used for later reference. + bool output = param.getDefault("output", true); + std::string output_dir; + if (output) { + // Create output directory if needed. + output_dir = + param.getDefault("output_dir", std::string("output")); + boost::filesystem::path fpath(output_dir); + try { + create_directories(fpath); + } + catch (...) { + OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); + } + // Write simulation parameters. + param.writeParam(output_dir + "/simulation.param"); + } + + Opm::TimeMapConstPtr timeMap(eclipseState->getSchedule()->getTimeMap()); + SimulatorTimer simtimer; + + // initialize variables + simtimer.init(timeMap); + //Check for WPOLYMER presence in last report step to decide + //polymer injection control type. + const bool polymer = deck->hasKeywrod("POLYMER"); + const bool use_wpolymer = deck->hasKeyword("WPOLYMER"); + if (polymer){ + if (!use_wpolymer) { + OPM_MESSAGE("Warning: simulate polymer injection without WPOLYMER."); + } else { + if (param.has("polymer_start_days")) { + OPM_MESSAGE("Warning: Using WPOLYMER to control injection since it was found in deck." + "You seem to be trying to control it via parameter poly_start_days (etc.) as well."); + } + std::shared_ptr polymer_inflow; + } + } else { + if (use_wpolymer) { + OPM_MESSAGE("Warning: use WPOLYMER in a non-polymer scenario."); + } + } + + Opm::DerivedGeology geology(*grid->c_grid(), *new_props, eclipseState, grav); + + std::vector threshold_pressures = thresholdPressures(deck, eclipseState, *grid->c_grid()); + + SimulatorFullyImplicitBlackoilPolymer simulator(param, + *grid->c_grid(), + geology, + *new_props, + polymer_props_ad, + rock_comp->isActive() ? rock_comp.get() : 0, + *fis_solver, + polymer_inflow, + grav, + deck->hasKeyword("DISGAS"), + deck->hasKeyword("VAPOIL"), + polymer, + eclipseState, + outputWriter, + threshold_pressures); + + std::cout << "\n\n================ Starting main simulation loop ===============\n" + << std::flush; + + SimulatorReport fullReport = simulator.run(simtimer, state); + + std::cout << "\n\n================ End of simulation ===============\n\n"; + fullReport.report(std::cout); + + if (output) { + std::string filename = output_dir + "/walltime.txt"; + std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out); + fullReport.reportParam(tot_os); + warnIfUnusedParams(param); + } +} +catch (const std::exception &e) { + std::cerr << "Program threw an exception: " << e.what() << "\n"; + throw; +} + From 73c20329741c9fd70be0b3d2fea75c29a74be6ff Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 9 Oct 2014 10:04:17 +0800 Subject: [PATCH 61/83] fix some bugs. --- examples/sim_poly_fibo_ad.cpp | 4 +- .../FullyImplicitBlackoilPolymerSolver.hpp | 40 +++++++------- ...ullyImplicitBlackoilPolymerSolver_impl.hpp | 52 ++++++++----------- ...latorFullyImplicitBlackoilPolymer_impl.hpp | 13 ++--- 4 files changed, 50 insertions(+), 59 deletions(-) diff --git a/examples/sim_poly_fibo_ad.cpp b/examples/sim_poly_fibo_ad.cpp index 285e06268..892c070f8 100644 --- a/examples/sim_poly_fibo_ad.cpp +++ b/examples/sim_poly_fibo_ad.cpp @@ -197,8 +197,9 @@ try simtimer.init(timeMap); //Check for WPOLYMER presence in last report step to decide //polymer injection control type. - const bool polymer = deck->hasKeywrod("POLYMER"); + const bool polymer = deck->hasKeyword("POLYMER"); const bool use_wpolymer = deck->hasKeyword("WPOLYMER"); + std::shared_ptr polymer_inflow; if (polymer){ if (!use_wpolymer) { OPM_MESSAGE("Warning: simulate polymer injection without WPOLYMER."); @@ -207,7 +208,6 @@ try OPM_MESSAGE("Warning: Using WPOLYMER to control injection since it was found in deck." "You seem to be trying to control it via parameter poly_start_days (etc.) as well."); } - std::shared_ptr polymer_inflow; } } else { if (use_wpolymer) { diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp index b60f47dea..ab3e3f38c 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp @@ -68,17 +68,17 @@ namespace Opm { /// \param[in] rock_comp_props if non-null, rock compressibility properties /// \param[in] wells well structure /// \param[in] linsolver linear solver - FullyImplicitBlackoilSolver(const parameter::ParameterGroup& param, - const Grid& grid , - const BlackoilPropsAdInterface& fluid, - const DerivedGeology& geo , - const RockCompressibility* rock_comp_props, - const PolymerPropsAd& polymer_props_ad, - const Wells& wells, - const NewtonIterationBlackoilInterface& linsolver, - const bool has_disgas, - const bool has_vapoil, - const bool has_polymer); + FullyImplicitBlackoilPolymerSolver(const parameter::ParameterGroup& param, + const Grid& grid , + const BlackoilPropsAdInterface& fluid, + const DerivedGeology& geo , + const RockCompressibility* rock_comp_props, + const PolymerPropsAd& polymer_props_ad, + const Wells& wells, + const NewtonIterationBlackoilInterface& linsolver, + const bool has_disgas, + const bool has_vapoil, + const bool has_polymer); /// \brief Set threshold pressures that prevent or reduce flow. /// This prevents flow across faces if the potential @@ -251,18 +251,16 @@ namespace Opm { void computeMassFlux(const V& trans, - const ADB& mc, - const ADB& kro, - const ADB& krw_eff, - const ADB& krg, + const std::vector& kr, + const std::vector& phasePressure, const SolutionState& state); void - computeCmax(PolymerBlackoilState& state, - const ADB& c); + computeCmax(PolymerBlackoilState& state, + const ADB& c); ADB - computeMc(const SolutionState& state) const; + computeMc(const SolutionState& state) const; ADB rockPorosity(const ADB& p) const; @@ -320,8 +318,6 @@ namespace Opm { const ADB& so, const std::vector& cells) const; - ADB - computeMc(const SolutionState& state) const; ADB poroMult(const ADB& p) const; @@ -371,6 +367,6 @@ namespace Opm { }; } // namespace Opm -#include "FullyImplicitBlackoilSolver_impl.hpp" +#include "FullyImplicitBlackoilPolymerSolver_impl.hpp" -#endif // OPM_FULLYIMPLICITBLACKOILSOLVER_HEADER_INCLUDED +#endif // OPM_FULLYIMPLICITBLACKOILPOLYMERSOLVER_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp index 00de81243..d2ca62cbc 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp @@ -264,7 +264,8 @@ namespace { FullyImplicitBlackoilPolymerSolver:: step(const double dt, PolymerBlackoilState& x , - WellStateFullyImplicitBlackoil& xw) + WellStateFullyImplicitBlackoil& xw, + const std::vector& polymer_inflow) { const V pvdt = geo_.poreVolume() / dt; @@ -625,7 +626,6 @@ namespace { // compute total phases and determin polymer position. rq_[poly_pos_].accum[aix] = pv_mult * rq_[pu.phase_pos[Water]].b * sat[pu.phase_pos[Water]] * c * (1. - dead_pore_vol) + pv_mult * rho_rock * (1. - phi) / phi * ads; } - } @@ -633,7 +633,7 @@ namespace { template - void FullyImplicitBlackoilPolymerSovler::computeCmax(PolymerBlackoilState& state, + void FullyImplicitBlackoilPolymerSolver::computeCmax(PolymerBlackoilState& state, const ADB& c) { const int nc = numCells(grid_); @@ -724,7 +724,7 @@ namespace { void FullyImplicitBlackoilPolymerSolver:: assemble(const V& pvdt, - const BlackoilState& x , + const PolymerBlackoilState& x , WellStateFullyImplicitBlackoil& xw, const std::vector& polymer_inflow) { @@ -798,7 +798,7 @@ namespace { const Opm::PhaseUsage& pu = fluid_.phaseUsage(); // Add polymer equation. if (has_polymer_) { - residula_.material_balance_eq[poly_pos_] = pvdt*(rq_[poly_pos_].accum[1] - rq_[poly_pos_].accum[0]) + residual_.material_balance_eq[poly_pos_] = pvdt*(rq_[poly_pos_].accum[1] - rq_[poly_pos_].accum[0]) + ops_.div*rq_[poly_pos_].mflux; } @@ -1001,7 +1001,7 @@ namespace { } // Add well contributions to polymer mass_balance equation - if (has_polmer_) { + if (has_polymer_) { const ADB mc = computeMc(state); const V polyin = Eigen::Map(&polymer_inflow[0], nc); const V poly_in_perf = subset(polyin, well_cells); @@ -1533,6 +1533,8 @@ namespace { //Polymer concentration updates. if (has_polymer_) { + const V dc = subset(dx, Span(nc)); + int varstart = nc; const V c_old = Eigen::Map(&state.concentration()[0], nc, 1); const V c = (c_old - dc).max(zero); std::copy(&c[0], &c[0] + nc, state.concentration().begin()); @@ -1679,13 +1681,6 @@ namespace { const std::vector& phasePressure, const SolutionState& state) { - if (has_polymer_) { - const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern()); - ADB krw_eff = ADB::null(); - const ADB mc = computeMc(state); - ADB inv_wat_eff_vis = ADB::null(); - } - for (int phase = 0; phase < fluid_.numPhases(); ++phase) { const int canonicalPhaseIdx = canph_[phase]; const std::vector cond = phaseCondition(); @@ -1693,14 +1688,21 @@ namespace { const ADB tr_mult = transMult(state.pressure); const ADB mu = fluidViscosity(canonicalPhaseIdx, phasePressure[canonicalPhaseIdx], state.rs, state.rv, cond, cells_); rq_[canonicalPhaseIdx].mob = tr_mult * kr[canonicalPhaseIdx] / mu; - if (cononicalPhaseIdx == Water) { + if (canonicalPhaseIdx == Water) { if(has_polymer_) { - krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, - cmax, - kr[cononicalPhaseIdx], - state.saturation[cononicalPhaseIdx]); - inv_wat_eff_vis = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mu.value().data()); + const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern()); + const ADB mc = computeMc(state); + ADB krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, + cmax, + kr[canonicalPhaseIdx], + state.saturation[canonicalPhaseIdx]); + ADB inv_wat_eff_visc = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mu.value().data()); rq_[canonicalPhaseIdx].mob = tr_mult * krw_eff * inv_wat_eff_visc; + rq_[poly_pos_].mob = tr_mult * mc * krw_eff * inv_wat_eff_visc; + rq_[poly_pos_].b = rq_[canph_[Water]].b; + rq_[poly_pos_].head = rq_[canph_[Water]].head; + UpwindSelector upwind(grid_, ops_, rq_[poly_pos_].head.value()); + rq_[poly_pos_].mflux = upwind.select(rq_[poly_pos_].b * rq_[poly_pos_].mob) * rq_[poly_pos_].head; } } @@ -1726,14 +1728,6 @@ namespace { const ADB& mob = rq_[canonicalPhaseIdx].mob; rq_[canonicalPhaseIdx].mflux = upwind.select(b * mob) * head; } - - if (has_polymer_) { - rq_[poly_pos_].mob = tr_mult * mc * krw_eff * inv_wat_eff_visc; - rq_[poly_pos_].b = rq_[canph_[Water]].b; - rq_[poly_pos_].head = rq_[canph_[Water]].head; - UpwindSelector upwind(grid_, ops_, rq_[poly_pos_].head.value()); - rq_[poly_pos_].mflux = upwind.select(rq_[poly_pos_].b * rq_[poly_pos_].mob) * rq_[poly_pos_].head; - } } @@ -2119,7 +2113,7 @@ namespace { template ADB - FullyImplicitBlackoilPolymerSovler::computeMc(const SolutionState& state) const + FullyImplicitBlackoilPolymerSolver::computeMc(const SolutionState& state) const { return polymer_props_ad_.polymerWaterVelocityRatio(state.concentration); } @@ -2146,7 +2140,7 @@ namespace { jacs[block] = dpm_diag * p.derivative()[block]; } return ADB::function(pm, jacs); - else { + } else { return ADB::constant(V::Constant(n, 1.0), p.blockPattern()); } } diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp index 14772ac2e..86a766db9 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp @@ -20,8 +20,9 @@ #include #include -#include +#include #include +#include #include #include @@ -115,7 +116,7 @@ namespace Opm // Solvers const DerivedGeology& geo_; NewtonIterationBlackoilInterface& solver_; - std::shared_ptr polymer_inflow, + std::shared_ptr polymer_inflow_; // Misc. data std::vector allcells_; const bool has_disgas_; @@ -237,7 +238,7 @@ namespace Opm const Grid& grid, const DerivedGeology& geo, BlackoilPropsAdInterface& props, - const PolymerPropdAd& polymer_props, + const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, NewtonIterationBlackoilInterface& linsolver, std::shared_ptr polymer_inflow, @@ -355,10 +356,10 @@ namespace Opm computeRESV(timer.currentStepNum(), wells, state, well_state); // compute polymer inflow + std::vector polymer_inflow_c(Opm::UgGridHelpers::numCells(grid_)); if (has_polymer_) { - std::vector polymer_inflow_c(Opm::UgGridHelpers::numCells(grid_)); - polymer_inflow_.reset(new PolymerFromDeck(deck, wells, Opm::UgGridHelpers::numCells(grid_))); - polymer_inflow_.getInflowValues(timer.simulationTimeElapsed(), + polymer_inflow_.reset(new PolymerInflowFromDeck(deck, wells, Opm::UgGridHelpers::numCells(grid_))); + *polymer_inflow_.getInflowValues(timer.simulationTimeElapsed(), timer.simulationTimeElapsed() + timer.currentStepLength(), polymer_inflow_c); } From c2d634727185e9ea6cb58b12bf09ac3049581109 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 9 Oct 2014 14:23:36 +0800 Subject: [PATCH 62/83] make some tricks for initialing polymer_inflow, this maybe wrong, but now no bugs. --- examples/sim_poly_fibo_ad.cpp | 59 +++++++++++-------- opm/polymer/PolymerBlackoilState.hpp | 2 +- ...ullyImplicitBlackoilPolymerSolver_impl.hpp | 17 +++--- .../SimulatorFullyImplicitBlackoilPolymer.hpp | 2 +- ...latorFullyImplicitBlackoilPolymer_impl.hpp | 23 ++++---- 5 files changed, 54 insertions(+), 49 deletions(-) diff --git a/examples/sim_poly_fibo_ad.cpp b/examples/sim_poly_fibo_ad.cpp index 892c070f8..2fdf1cec6 100644 --- a/examples/sim_poly_fibo_ad.cpp +++ b/examples/sim_poly_fibo_ad.cpp @@ -155,10 +155,10 @@ try } else if (deck->hasKeyword("EQUIL") && props->numPhases() == 3) { state.init(*grid->c_grid(), props->numPhases()); const double grav = param.getDefault("gravity", unit::gravity); - initStateEquil(*grid->c_grid(), *props, deck, eclipseState, grav, state); + initStateEquil(*grid->c_grid(), *props, deck, eclipseState, grav, state.blackoilState()); state.faceflux().resize(grid->c_grid()->number_of_faces, 0.0); } else { - initBlackoilStateFromDeck(*grid->c_grid(), *props, deck, gravity[2], state); + initBlackoilStateFromDeck(*grid->c_grid(), *props, deck, gravity[2], state.blackoilState()); } bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); @@ -199,7 +199,6 @@ try //polymer injection control type. const bool polymer = deck->hasKeyword("POLYMER"); const bool use_wpolymer = deck->hasKeyword("WPOLYMER"); - std::shared_ptr polymer_inflow; if (polymer){ if (!use_wpolymer) { OPM_MESSAGE("Warning: simulate polymer injection without WPOLYMER."); @@ -214,32 +213,42 @@ try OPM_MESSAGE("Warning: use WPOLYMER in a non-polymer scenario."); } } - - Opm::DerivedGeology geology(*grid->c_grid(), *new_props, eclipseState, grav); - - std::vector threshold_pressures = thresholdPressures(deck, eclipseState, *grid->c_grid()); - - SimulatorFullyImplicitBlackoilPolymer simulator(param, - *grid->c_grid(), - geology, - *new_props, - polymer_props_ad, - rock_comp->isActive() ? rock_comp.get() : 0, - *fis_solver, - polymer_inflow, - grav, - deck->hasKeyword("DISGAS"), - deck->hasKeyword("VAPOIL"), - polymer, - eclipseState, - outputWriter, - threshold_pressures); - std::cout << "\n\n================ Starting main simulation loop ===============\n" << std::flush; + SimulatorReport fullReport; + for (size_t reportStepIdx = 0; reportStepIdx < timeMap->numTimesteps(); ++reportStepIdx) { + simtimer.setCurrentStepNum(reportStepIdx); + Opm::DerivedGeology geology(*grid->c_grid(), *new_props, eclipseState, grav); - SimulatorReport fullReport = simulator.run(simtimer, state); + std::vector threshold_pressures = thresholdPressures(deck, eclipseState, *grid->c_grid()); + //Create new wells, polymer inflow controls. + WellsManager wells(eclipseState, reportStepIdx, *grid->c_grid(), props->permeability()); + std::shared_ptr polymer_inflow; + if (use_wpolymer) { + if (wells.c_wells() == 0) { + OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells."); + } + polymer_inflow.reset(new PolymerInflowFromDeck(deck, *wells.c_wells(), props->numCells())); + } + SimulatorFullyImplicitBlackoilPolymer simulator(param, + *grid->c_grid(), + geology, + *new_props, + polymer_props_ad, + rock_comp->isActive() ? rock_comp.get() : 0, + *fis_solver, + *polymer_inflow, + grav, + deck->hasKeyword("DISGAS"), + deck->hasKeyword("VAPOIL"), + polymer, + eclipseState, + outputWriter, + threshold_pressures); + + fullReport = simulator.run(simtimer, state); + } std::cout << "\n\n================ End of simulation ===============\n\n"; fullReport.report(std::cout); diff --git a/opm/polymer/PolymerBlackoilState.hpp b/opm/polymer/PolymerBlackoilState.hpp index b04d0049a..078ac27b2 100644 --- a/opm/polymer/PolymerBlackoilState.hpp +++ b/opm/polymer/PolymerBlackoilState.hpp @@ -75,7 +75,7 @@ namespace Opm const std::vector& facepressure() const { return state_blackoil_.facepressure(); } const std::vector& faceflux () const { return state_blackoil_.faceflux(); } const std::vector& saturation () const { return state_blackoil_.saturation(); } - const std::vector& gasoilration() const { return state_blackoil_.gasoilratio(); } + const std::vector& gasoilratio() const { return state_blackoil_.gasoilratio(); } const std::vector& rv () const { return state_blackoil_.rv(); } const std::vector& concentration() const { return concentration_; } const std::vector& maxconcentration() const { return cmax_; } diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp index d2ca62cbc..5aa90faeb 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp @@ -195,7 +195,7 @@ namespace { , cells_ (buildAllCells(Opm::AutoDiffGrid::numCells(grid))) , ops_ (grid) , wops_ (wells) - , cmax_(V::Zero(numCells(grid))) + , cmax_(V::Zero(Opm::AutoDiffGrid::numCells(grid))) , has_disgas_(has_disgas) , has_vapoil_(has_vapoil) , has_polymer_(has_polymer) @@ -280,7 +280,7 @@ namespace { std::vector> residual_history; - assemble(pvdt, x, xw); + assemble(pvdt, x, xw, polymer_inflow); bool converged = false; @@ -319,7 +319,7 @@ namespace { updateState(dx, x, xw); - assemble(pvdt, x, xw); + assemble(pvdt, x, xw, polymer_inflow); const double r = residualNorm(); @@ -621,7 +621,7 @@ namespace { const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern()); const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); const double rho_rock = polymer_props_ad_.rockDensity(); - const V phi = Eigen::Map(& fluid_.porosity()[0], numCells(grid_), 1); + const V phi = Eigen::Map(& fluid_.porosity()[0], AutoDiffGrid::numCells(grid_), 1); const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); // compute total phases and determin polymer position. rq_[poly_pos_].accum[aix] = pv_mult * rq_[pu.phase_pos[Water]].b * sat[pu.phase_pos[Water]] * c * (1. - dead_pore_vol) + pv_mult * rho_rock * (1. - phi) / phi * ads; @@ -636,7 +636,7 @@ namespace { void FullyImplicitBlackoilPolymerSolver::computeCmax(PolymerBlackoilState& state, const ADB& c) { - const int nc = numCells(grid_); + const int nc = AutoDiffGrid::numCells(grid_); for (int i = 0; i < nc; ++i) { cmax_(i) = std::max(cmax_(i), c.value()(i)); } @@ -795,7 +795,6 @@ namespace { } - const Opm::PhaseUsage& pu = fluid_.phaseUsage(); // Add polymer equation. if (has_polymer_) { residual_.material_balance_eq[poly_pos_] = pvdt*(rq_[poly_pos_].accum[1] - rq_[poly_pos_].accum[0]) @@ -1339,10 +1338,10 @@ namespace { const V dxvar = active_[Gas] ? subset(dx, Span(nc, 1, varstart)): null; varstart += dxvar.size(); + const V dc = null; if (has_polymer_) { const V dc = subset(dx, Span(nc, 1, varstart)); - } else { - const V dc = null; + varstart += dc.size(); } const V dqs = subset(dx, Span(np*nw, 1, varstart)); varstart += dqs.size(); @@ -1533,8 +1532,6 @@ namespace { //Polymer concentration updates. if (has_polymer_) { - const V dc = subset(dx, Span(nc)); - int varstart = nc; const V c_old = Eigen::Map(&state.concentration()[0], nc, 1); const V c = (c_old - dc).max(zero); std::copy(&c[0], &c[0] + nc, state.concentration().begin()); diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp index a8722e0e1..3b46f8ae8 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp @@ -85,7 +85,7 @@ namespace Opm const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, NewtonIterationBlackoilInterface& linsolver, - std::shared_ptr polymer_inflow, + const PolymerInflowInterface& polymer_inflow, const double* gravity, const bool disgas, const bool vapoil, diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp index 86a766db9..4b789f576 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp @@ -82,7 +82,7 @@ namespace Opm const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, NewtonIterationBlackoilInterface& linsolver, - std::shared_ptr polymer_inflow, + const PolymerInflowInterface& polymer_inflow, const double* gravity, bool has_disgas, bool has_vapoil, @@ -116,7 +116,7 @@ namespace Opm // Solvers const DerivedGeology& geo_; NewtonIterationBlackoilInterface& solver_; - std::shared_ptr polymer_inflow_; + const PolymerInflowInterface& polymer_inflow_; // Misc. data std::vector allcells_; const bool has_disgas_; @@ -148,7 +148,7 @@ namespace Opm const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, NewtonIterationBlackoilInterface& linsolver, - std::shared_ptr polymer_inflow, + const PolymerInflowInterface& polymer_inflow, const double* gravity, const bool has_disgas, const bool has_vapoil, @@ -241,7 +241,7 @@ namespace Opm const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, NewtonIterationBlackoilInterface& linsolver, - std::shared_ptr polymer_inflow, + const PolymerInflowInterface& polymer_inflow, const double* gravity, const bool has_disgas, const bool has_vapoil, @@ -327,7 +327,7 @@ namespace Opm props_.permeability()); const Wells* wells = wells_manager.c_wells(); WellStateFullyImplicitBlackoil well_state; - well_state.init(wells, state); + well_state.init(wells, state.blackoilState()); if (timer.currentStepNum() != 0) { // Transfer previous well state to current. well_state.partialCopy(prev_well_state, *wells, prev_well_state.numWells()); @@ -345,7 +345,7 @@ namespace Opm if (timer.currentStepNum() == 0) { output_writer_.writeInit(timer); } - output_writer_.writeTimeStep(timer, state, well_state.basicWellState()); + output_writer_.writeTimeStep(timer, state.blackoilState(), well_state.basicWellState()); } // Max oil saturation (for VPPARS), hysteresis update. @@ -358,10 +358,9 @@ namespace Opm // compute polymer inflow std::vector polymer_inflow_c(Opm::UgGridHelpers::numCells(grid_)); if (has_polymer_) { - polymer_inflow_.reset(new PolymerInflowFromDeck(deck, wells, Opm::UgGridHelpers::numCells(grid_))); - *polymer_inflow_.getInflowValues(timer.simulationTimeElapsed(), - timer.simulationTimeElapsed() + timer.currentStepLength(), - polymer_inflow_c); + polymer_inflow_.getInflowValues(timer.simulationTimeElapsed(), + timer.simulationTimeElapsed() + timer.currentStepLength(), + polymer_inflow_c); } // Run a single step of the solver. solver_timer.start(); @@ -395,7 +394,7 @@ namespace Opm } outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); outputWellStateMatlab(prev_well_state, timer.currentStepNum(), output_dir_); - output_writer_.writeTimeStep(timer, state, prev_well_state.basicWellState()); + output_writer_.writeTimeStep(timer, state.blackoilState(), prev_well_state.basicWellState()); } // Stop timer and create timing report @@ -539,7 +538,7 @@ namespace Opm const PhaseUsage& pu = props_.phaseUsage(); const std::vector::size_type np = props_.numPhases(); - rateConverter_.defineState(x); + rateConverter_.defineState(x.blackoilState()); std::vector distr (np); std::vector hrates(np); From d457211cf26dca1338070e311e07c9489466f790 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 10 Oct 2014 10:11:04 +0800 Subject: [PATCH 63/83] make sure polymer position is the last. --- .../FullyImplicitBlackoilPolymerSolver_impl.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp index 5aa90faeb..a9b8f7a42 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp @@ -160,8 +160,10 @@ namespace { { const int maxnp = Opm::BlackoilPhases::MaxNumPhases; int pos = 0; - for (int p = 0; p < maxnp; ++p) { - pos += pu.phase_used[p]; + for (int phase = 0; phase < maxnp; ++phase) { + if (pu.phase_used[phase]) { + pos++; + } } return pos; From ddb7b8833fde9ed965e4f88dfb0ba911d7321af3 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 10 Oct 2014 10:17:40 +0800 Subject: [PATCH 64/83] if deck has_polymer, rq_ should resize to store polymer equation. --- .../fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp index a9b8f7a42..1c14cf915 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp @@ -213,7 +213,7 @@ namespace { , use_threshold_pressure_(false) , rq_ (fluid.numPhases() + 1) , phaseCondition_(AutoDiffGrid::numCells(grid)) - , residual_ ( { std::vector(fluid.numPhases() + 1, ADB::null()), + , residual_ ( { std::vector(fluid.numPhases(), ADB::null()), ADB::null(), ADB::null() } ) { @@ -626,6 +626,7 @@ namespace { const V phi = Eigen::Map(& fluid_.porosity()[0], AutoDiffGrid::numCells(grid_), 1); const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); // compute total phases and determin polymer position. + rq_.resize(fluid_.numPhases()+1); rq_[poly_pos_].accum[aix] = pv_mult * rq_[pu.phase_pos[Water]].b * sat[pu.phase_pos[Water]] * c * (1. - dead_pore_vol) + pv_mult * rho_rock * (1. - phi) / phi * ads; } } From dce2047e41c55564ea2fe3448a50692afc39eee8 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 10 Oct 2014 15:42:49 +0800 Subject: [PATCH 65/83] fix bugs caused by canonical phase index and active phase index. residual_ and rq_ resize at a proper position. --- ...ullyImplicitBlackoilPolymerSolver_impl.hpp | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp index 1c14cf915..70e61ace7 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp @@ -211,7 +211,7 @@ namespace { , relax_rel_tol_ (0.2) , max_iter_ (15) , use_threshold_pressure_(false) - , rq_ (fluid.numPhases() + 1) + , rq_ (fluid.numPhases()) , phaseCondition_(AutoDiffGrid::numCells(grid)) , residual_ ( { std::vector(fluid.numPhases(), ADB::null()), ADB::null(), @@ -221,6 +221,10 @@ namespace { if (!active_[Water]) { OPM_THROW(std::logic_error, "Polymer must solved in water!\n"); } + // If deck has polymer, residual_ should contain polymer equation. + rq_.resize(fluid_.numPhases()+1); + residual_.material_balance_eq.resize(fluid_.numPhases()+1); + assert(poly_pos_ == fluid_.numPhases()); } dp_max_rel_ = param.getDefault("dp_max_rel", dp_max_rel_); ds_max_ = param.getDefault("ds_max", ds_max_); @@ -279,7 +283,6 @@ namespace { computeWellConnectionPressures(state, xw); } - std::vector> residual_history; assemble(pvdt, x, xw, polymer_inflow); @@ -626,8 +629,8 @@ namespace { const V phi = Eigen::Map(& fluid_.porosity()[0], AutoDiffGrid::numCells(grid_), 1); const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); // compute total phases and determin polymer position. - rq_.resize(fluid_.numPhases()+1); - rq_[poly_pos_].accum[aix] = pv_mult * rq_[pu.phase_pos[Water]].b * sat[pu.phase_pos[Water]] * c * (1. - dead_pore_vol) + pv_mult * rho_rock * (1. - phi) / phi * ads; + rq_[poly_pos_].accum[aix] = pv_mult * rq_[pu.phase_pos[Water]].b * sat[pu.phase_pos[Water]] * c * (1. - dead_pore_vol) + + pv_mult * rho_rock * (1. - phi) / phi * ads; } } @@ -1002,7 +1005,7 @@ namespace { residual_.material_balance_eq[phase] -= superset(cq_s[phase],well_cells,nc); } - // Add well contributions to polymer mass_balance equation + // Add well contributions to polymer mass balance equation if (has_polymer_) { const ADB mc = computeMc(state); const V polyin = Eigen::Map(&polymer_inflow[0], nc); @@ -1341,11 +1344,9 @@ namespace { const V dxvar = active_[Gas] ? subset(dx, Span(nc, 1, varstart)): null; varstart += dxvar.size(); - const V dc = null; - if (has_polymer_) { - const V dc = subset(dx, Span(nc, 1, varstart)); - varstart += dc.size(); - } + const V dc = (has_polymer_) ? subset(dx, Span(nc, 1, varstart)) : null; + varstart += dc.size(); + const V dqs = subset(dx, Span(np*nw, 1, varstart)); varstart += dqs.size(); const V dbhp = subset(dx, Span(nw, 1, varstart)); @@ -1687,7 +1688,7 @@ namespace { const ADB tr_mult = transMult(state.pressure); const ADB mu = fluidViscosity(canonicalPhaseIdx, phasePressure[canonicalPhaseIdx], state.rs, state.rv, cond, cells_); - rq_[canonicalPhaseIdx].mob = tr_mult * kr[canonicalPhaseIdx] / mu; + rq_[phase].mob = tr_mult * kr[canonicalPhaseIdx] / mu; if (canonicalPhaseIdx == Water) { if(has_polymer_) { const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern()); @@ -1697,10 +1698,10 @@ namespace { kr[canonicalPhaseIdx], state.saturation[canonicalPhaseIdx]); ADB inv_wat_eff_visc = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mu.value().data()); - rq_[canonicalPhaseIdx].mob = tr_mult * krw_eff * inv_wat_eff_visc; + rq_[phase].mob = tr_mult * krw_eff * inv_wat_eff_visc; rq_[poly_pos_].mob = tr_mult * mc * krw_eff * inv_wat_eff_visc; - rq_[poly_pos_].b = rq_[canph_[Water]].b; - rq_[poly_pos_].head = rq_[canph_[Water]].head; + rq_[poly_pos_].b = rq_[phase].b; + rq_[poly_pos_].head = rq_[phase].head; UpwindSelector upwind(grid_, ops_, rq_[poly_pos_].head.value()); rq_[poly_pos_].mflux = upwind.select(rq_[poly_pos_].b * rq_[poly_pos_].mob) * rq_[poly_pos_].head; } @@ -1708,7 +1709,7 @@ namespace { const ADB rho = fluidDensity(canonicalPhaseIdx, phasePressure[canonicalPhaseIdx], state.rs, state.rv, cond, cells_); - ADB& head = rq_[canonicalPhaseIdx].head; + ADB& head = rq_[phase].head; // compute gravity potensial using the face average as in eclipse and MRST const ADB rhoavg = ops_.caver * rho; @@ -1724,9 +1725,9 @@ namespace { UpwindSelector upwind(grid_, ops_, head.value()); - const ADB& b = rq_[canonicalPhaseIdx].b; - const ADB& mob = rq_[canonicalPhaseIdx].mob; - rq_[canonicalPhaseIdx].mflux = upwind.select(b * mob) * head; + const ADB& b = rq_[phase].b; + const ADB& mob = rq_[phase].mob; + rq_[phase].mflux = upwind.select(b * mob) * head; } } From b18f13bd7fc6f0738a5f82b921dc52ee81f52d66 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 10 Oct 2014 17:06:05 +0800 Subject: [PATCH 66/83] fix phaseIdx problem and make the function more readable. --- ...ullyImplicitBlackoilPolymerSolver_impl.hpp | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp index 70e61ace7..984d4372b 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp @@ -1842,23 +1842,17 @@ namespace { return; } - stagnate = true; int oscillatePhase = 0; + const std::vector& F0 = residual_history[it]; + const std::vector& F1 = residual_history[it - 1]; + const std::vector& F2 = residual_history[it - 2]; + for (int p= 0; p < fluid_.numPhases(); ++p){ + const double d1 = std::abs((F0[p] - F2[p]) / F0[p]); + const double d2 = std::abs((F0[p] - F1[p]) / F0[p]); - for (int phaseIdx= 0; phaseIdx < fluid_.numPhases(); ++ phaseIdx){ - if (active_[phaseIdx]) { - double relChange1 = std::fabs((residual_history[it][phaseIdx] - residual_history[it - 2][phaseIdx]) / - residual_history[it][phaseIdx]); - double relChange2 = std::fabs((residual_history[it][phaseIdx] - residual_history[it - 1][phaseIdx]) / - residual_history[it][phaseIdx]); - oscillatePhase += (relChange1 < relaxRelTol) && (relChange2 > relaxRelTol); + oscillatePhase += (d1 < relaxRelTol) && (relaxRelTol < d2); - double relChange3 = std::fabs((residual_history[it - 1][phaseIdx] - residual_history[it - 2][phaseIdx]) / - residual_history[it - 2][phaseIdx]); - if (relChange3 > 1.e-3) { - stagnate = false; - } - } + stagnate = ! (std::abs((F1[p] - F2[p]) / F2[p]) > 1.0e-3); } oscillate = (oscillatePhase > 1); From 6d76151b6dfe3b3efe38d8c8b5623c6ff950b607 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Sat, 11 Oct 2014 16:38:59 +0800 Subject: [PATCH 67/83] use std::unique_ptr instead of std::shared_ptr for polymerInflowInterface. --- examples/sim_poly_fibo_ad.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/sim_poly_fibo_ad.cpp b/examples/sim_poly_fibo_ad.cpp index 2fdf1cec6..bc65513de 100644 --- a/examples/sim_poly_fibo_ad.cpp +++ b/examples/sim_poly_fibo_ad.cpp @@ -223,7 +223,7 @@ try std::vector threshold_pressures = thresholdPressures(deck, eclipseState, *grid->c_grid()); //Create new wells, polymer inflow controls. WellsManager wells(eclipseState, reportStepIdx, *grid->c_grid(), props->permeability()); - std::shared_ptr polymer_inflow; + std::unique_ptr polymer_inflow; if (use_wpolymer) { if (wells.c_wells() == 0) { OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells."); From f1f0fa9c29c846bccdc7be2b590efcea65e3d5b9 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 16 Oct 2014 09:59:59 +0800 Subject: [PATCH 68/83] resize and use ADB::null() to initialize. --- .../fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp index 984d4372b..80c2cdade 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp @@ -223,7 +223,7 @@ namespace { } // If deck has polymer, residual_ should contain polymer equation. rq_.resize(fluid_.numPhases()+1); - residual_.material_balance_eq.resize(fluid_.numPhases()+1); + residual_.material_balance_eq.resize(fluid_.numPhases()+1, ADB::null()); assert(poly_pos_ == fluid_.numPhases()); } dp_max_rel_ = param.getDefault("dp_max_rel", dp_max_rel_); From 6a051b7e4e21511c63483c58821677a6c3072ec7 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 16 Oct 2014 10:03:13 +0800 Subject: [PATCH 69/83] adapte the API changes of PolymerProperties class. --- examples/sim_poly_fi2p_comp_ad.cpp | 2 +- examples/sim_poly_fibo_ad.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/sim_poly_fi2p_comp_ad.cpp b/examples/sim_poly_fi2p_comp_ad.cpp index 4f5198cf7..33168fda8 100644 --- a/examples/sim_poly_fi2p_comp_ad.cpp +++ b/examples/sim_poly_fi2p_comp_ad.cpp @@ -140,7 +140,7 @@ try // Rock and fluid init props.reset(new BlackoilPropertiesFromDeck(deck, eclipseState, *grid->c_grid())); new_props.reset(new BlackoilPropsAdFromDeck(deck, eclipseState, *grid->c_grid())); - PolymerProperties polymer_props(eclipseState); + PolymerProperties polymer_props(deck, eclipseState); PolymerPropsAd polymer_props_ad(polymer_props); // check_well_controls = param.getDefault("check_well_controls", false); // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); diff --git a/examples/sim_poly_fibo_ad.cpp b/examples/sim_poly_fibo_ad.cpp index bc65513de..5f3719bc3 100644 --- a/examples/sim_poly_fibo_ad.cpp +++ b/examples/sim_poly_fibo_ad.cpp @@ -129,7 +129,7 @@ try // Rock and fluid init props.reset(new BlackoilPropertiesFromDeck(deck, eclipseState, *grid->c_grid(), param)); new_props.reset(new BlackoilPropsAdFromDeck(deck, eclipseState, *grid->c_grid())); - PolymerProperties polymer_props(eclipseState); + PolymerProperties polymer_props(deck, eclipseState); PolymerPropsAd polymer_props_ad(polymer_props); // check_well_controls = param.getDefault("check_well_controls", false); // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); From 89120ed57f54736020612cd32d120afb66c65cdd Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 16 Oct 2014 16:55:36 +0800 Subject: [PATCH 70/83] output parameters for debugging and remove double loop of timer. --- opm/polymer/PolymerProperties.hpp | 31 +++++++++++++------ ...ulatorFullyImplicitCompressiblePolymer.cpp | 6 ++-- opm/polymer/fullyimplicit/utilities.cpp | 3 +- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/opm/polymer/PolymerProperties.hpp b/opm/polymer/PolymerProperties.hpp index 9cf4d20de..8662db576 100644 --- a/opm/polymer/PolymerProperties.hpp +++ b/opm/polymer/PolymerProperties.hpp @@ -121,14 +121,16 @@ namespace Opm { // We assume NTMISC=1 const auto& plymaxTable = eclipseState->getPlymaxTables()[0]; - const auto tlmixparRecord = deck->getKeyword("TLMIXPAR")->getRecord(0); + const auto plmixparRecord = deck->getKeyword("PLMIXPAR")->getRecord(0); // We also assume that each table has exactly one row... assert(plymaxTable.numRows() == 1); c_max_ = plymaxTable.getPolymerConcentrationColumn()[0]; - mix_param_ = tlmixparRecord->getItem("TL_VISCOSITY_PARAMETER")->getSIDouble(0); + mix_param_ = plmixparRecord->getItem("TODD_LONGSTAFF")->getSIDouble(0); + std::cout << "Debug output:\n"; + std::cout << "mix_param: " << mix_param_ <<" cmax: " << c_max_; // We assume NTSFUN=1 const auto& plyrockTable = eclipseState->getPlyrockTables()[0]; @@ -141,23 +143,32 @@ namespace Opm ads_index_ = static_cast(plyrockTable.getAdsorbtionIndexColumn()[0]); c_max_ads_ = plyrockTable.getMaxAdsorbtionColumn()[0]; + std::cout << " IPV: " << dead_pore_vol_ <<" rs: " << res_factor_ << " rock_den: " << rock_density_ + << " ads_index: " << ads_index_ << " cmax_ads: " << c_max_ads_ << std::endl; // We assume NTPVT=1 const auto& plyviscTable = eclipseState->getPlyviscTables()[0]; - // We also assume that each table has exactly one row... - assert(plyviscTable.numRows() == 1); - c_vals_visc_[0] = plyviscTable.getPolymerConcentrationColumn()[0]; - visc_mult_vals_[0] = plyviscTable.getViscosityMultiplierColumn()[0]; + c_vals_visc_ = plyviscTable.getPolymerConcentrationColumn(); + visc_mult_vals_ = plyviscTable.getViscosityMultiplierColumn(); + + std::cout << "PLYVISC\n"; + auto N = c_vals_visc_.size(); + for (size_t i = 0; i < N; ++i) { + std::cout << c_vals_visc_[i] << " " << visc_mult_vals_[i] << "\n"; + } // We assume NTSFUN=1 const auto& plyadsTable = eclipseState->getPlyadsTables()[0]; - // We also assume that each table has exactly one row... - assert(plyadsTable.numRows() == 1); - c_vals_ads_[0] = plyadsTable.getPolymerConcentrationColumn()[0]; - ads_vals_[0] = plyadsTable.getAdsorbedPolymerColumn()[0]; + c_vals_ads_ = plyadsTable.getPolymerConcentrationColumn(); + ads_vals_ = plyadsTable.getAdsorbedPolymerColumn(); + std::cout << "PLYADS\n"; + auto M = c_vals_ads_.size(); + for (size_t i = 0; i < M; ++i) { + std::cout << c_vals_ads_[i] << " " << ads_vals_[i] << "\n"; + } } double cMax() const; diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp index 6152474af..105ae299c 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp @@ -267,7 +267,7 @@ namespace Opm std::string filename = output_dir_ + "/step_timing.param"; tstep_os.open(filename.c_str(), std::fstream::out | std::fstream::app); } - while (!timer.done()) { +// while (!timer.done()) { // Report timestep and (optionally) write state to disk. step_timer.start(); timer.report(std::cout); @@ -380,9 +380,9 @@ namespace Opm } // advance to next timestep before reporting at this location - ++timer; + // ++timer; - } + // } total_timer.stop(); diff --git a/opm/polymer/fullyimplicit/utilities.cpp b/opm/polymer/fullyimplicit/utilities.cpp index 8b029b255..24cf784a9 100644 --- a/opm/polymer/fullyimplicit/utilities.cpp +++ b/opm/polymer/fullyimplicit/utilities.cpp @@ -246,8 +246,9 @@ namespace Opm } } //Add PhasePresence make muOil() happy. - std::vector phaseCondition; + std::vector phaseCondition(num_cells); for (int c = 0; c < num_cells; ++c) { + phaseCondition[c] = PhasePresence(); phaseCondition[c].setFreeWater(); phaseCondition[c].setFreeOil(); } From 88a1ec6e9fb6de22507bd574a58c4372ac5fb110 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Mon, 20 Oct 2014 13:44:12 +0800 Subject: [PATCH 71/83] fix bugs: polymer head should be dp*trans. --- ...ullyImplicitBlackoilPolymerSolver_impl.hpp | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp index 80c2cdade..b09314bb8 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp @@ -1689,6 +1689,21 @@ namespace { const ADB tr_mult = transMult(state.pressure); const ADB mu = fluidViscosity(canonicalPhaseIdx, phasePressure[canonicalPhaseIdx], state.rs, state.rv, cond, cells_); rq_[phase].mob = tr_mult * kr[canonicalPhaseIdx] / mu; + + const ADB rho = fluidDensity(canonicalPhaseIdx, phasePressure[canonicalPhaseIdx], state.rs, state.rv, cond, cells_); + + ADB& head = rq_[phase].head; + + // compute gravity potensial using the face average as in eclipse and MRST + const ADB rhoavg = ops_.caver * rho; + + ADB dp = ops_.ngrad * phasePressure[canonicalPhaseIdx] - geo_.gravity()[2] * (rhoavg * (ops_.ngrad * geo_.z().matrix())); + + if (use_threshold_pressure_) { + applyThresholdPressures(dp); + } + + head = transi*dp; if (canonicalPhaseIdx == Water) { if(has_polymer_) { const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern()); @@ -1706,21 +1721,6 @@ namespace { rq_[poly_pos_].mflux = upwind.select(rq_[poly_pos_].b * rq_[poly_pos_].mob) * rq_[poly_pos_].head; } } - - const ADB rho = fluidDensity(canonicalPhaseIdx, phasePressure[canonicalPhaseIdx], state.rs, state.rv, cond, cells_); - - ADB& head = rq_[phase].head; - - // compute gravity potensial using the face average as in eclipse and MRST - const ADB rhoavg = ops_.caver * rho; - - ADB dp = ops_.ngrad * phasePressure[canonicalPhaseIdx] - geo_.gravity()[2] * (rhoavg * (ops_.ngrad * geo_.z().matrix())); - - if (use_threshold_pressure_) { - applyThresholdPressures(dp); - } - - head = transi*dp; //head = transi*(ops_.ngrad * phasePressure) + gflux; UpwindSelector upwind(grid_, ops_, head.value()); From 61c76c429493490c1c69660541218834fb6fe843 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Mon, 20 Oct 2014 17:29:59 +0800 Subject: [PATCH 72/83] use std::unique_ptr instead of boost::scoped_ptr. --- examples/sim_poly_fi2p_comp_ad.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/sim_poly_fi2p_comp_ad.cpp b/examples/sim_poly_fi2p_comp_ad.cpp index 33168fda8..c41850a63 100644 --- a/examples/sim_poly_fi2p_comp_ad.cpp +++ b/examples/sim_poly_fi2p_comp_ad.cpp @@ -213,7 +213,7 @@ try // Create new wells, polymer inflow controls. WellsManager wells(eclipseState, reportStepIdx, *grid->c_grid(), props->permeability()); - boost::scoped_ptr polymer_inflow; + std::unique_ptr polymer_inflow; if (use_wpolymer) { if (wells.c_wells() == 0) { OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells."); From ba6fef4fbd1e6a064397f6f430a7dd1b28b93fc1 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 24 Oct 2014 14:04:57 +0800 Subject: [PATCH 73/83] pass wells manager from sim_poly_fibo to Simulator class, instead of directly create it in Simulator class, since polymer inflow need deck and wells. --- examples/sim_poly_fibo_ad.cpp | 26 ++++++- .../SimulatorFullyImplicitBlackoilPolymer.hpp | 5 +- ...latorFullyImplicitBlackoilPolymer_impl.hpp | 76 ++++++++++--------- 3 files changed, 68 insertions(+), 39 deletions(-) diff --git a/examples/sim_poly_fibo_ad.cpp b/examples/sim_poly_fibo_ad.cpp index 9075c6dfd..338a4bc07 100644 --- a/examples/sim_poly_fibo_ad.cpp +++ b/examples/sim_poly_fibo_ad.cpp @@ -218,19 +218,35 @@ try std::cout << "\n\n================ Starting main simulation loop ===============\n" << std::flush; SimulatorReport fullReport; + WellStateFullyImplicitBlackoil prev_well_state; for (size_t reportStepIdx = 0; reportStepIdx < timeMap->numTimesteps(); ++reportStepIdx) { simtimer.setCurrentStepNum(reportStepIdx); Opm::DerivedGeology geology(*grid->c_grid(), *new_props, eclipseState, grav); std::vector threshold_pressures = thresholdPressures(deck, eclipseState, *grid->c_grid()); //Create new wells, polymer inflow controls. - WellsManager wells(eclipseState, reportStepIdx, *grid->c_grid(), props->permeability()); + WellsManager wells_manager(eclipseState, + simtimer.currentStepNum(), + Opm::UgGridHelpers::numCells(*grid->c_grid()), + Opm::UgGridHelpers::globalCell(*grid->c_grid()), + Opm::UgGridHelpers::cartDims(*grid->c_grid()), + Opm::UgGridHelpers::dimensions(*grid->c_grid()), + Opm::UgGridHelpers::beginCellCentroids(*grid->c_grid()), + Opm::UgGridHelpers::cell2Faces(*grid->c_grid()), + Opm::UgGridHelpers::beginFaceCentroids(*grid->c_grid()), + props->permeability()); + WellStateFullyImplicitBlackoil well_state; + well_state.init(wells_manager.c_wells(), state.blackoilState()); + if (reportStepIdx != 0) { + // Transfer previous well state to current. + well_state.partialCopy(prev_well_state, *wells_manager.c_wells(), prev_well_state.numWells()); + } std::unique_ptr polymer_inflow; if (use_wpolymer) { - if (wells.c_wells() == 0) { + if (wells_manager.c_wells() == 0) { OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells."); } - polymer_inflow.reset(new PolymerInflowFromDeck(deck, *wells.c_wells(), props->numCells())); + polymer_inflow.reset(new PolymerInflowFromDeck(deck, *wells_manager.c_wells(), props->numCells())); } else { polymer_inflow.reset(new PolymerInflowBasic(0.0*Opm::unit::day, 1.0*Opm::unit::day, @@ -244,6 +260,7 @@ try rock_comp->isActive() ? rock_comp.get() : 0, *fis_solver, *polymer_inflow, + wells_manager, grav, deck->hasKeyword("DISGAS"), deck->hasKeyword("VAPOIL"), @@ -253,7 +270,8 @@ try threshold_pressures); - fullReport = simulator.run(simtimer, state); + fullReport = simulator.run(simtimer, state, well_state); + prev_well_state = well_state; } std::cout << "\n\n================ End of simulation ===============\n\n"; fullReport.report(std::cout); diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp index 3b46f8ae8..48fd7e5e0 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp @@ -37,6 +37,7 @@ namespace Opm class SimulatorTimer; class PolymerBlackoilState; class WellStateFullyImplicitBlackoil; + class WellsManager; class EclipseState; class EclipseWriter; class PolymerPropsAd; @@ -86,6 +87,7 @@ namespace Opm const RockCompressibility* rock_comp_props, NewtonIterationBlackoilInterface& linsolver, const PolymerInflowInterface& polymer_inflow, + WellsManager& well_manager, const double* gravity, const bool disgas, const bool vapoil, @@ -102,7 +104,8 @@ namespace Opm /// \param[in,out] well_state state of wells: bhp, perforation rates /// \return simulation report, with timing data SimulatorReport run(SimulatorTimer& timer, - PolymerBlackoilState& state); + PolymerBlackoilState& state, + WellStateFullyImplicitBlackoil& well_state); private: class Impl; diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp index 4b789f576..0936f0d96 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp @@ -83,6 +83,7 @@ namespace Opm const RockCompressibility* rock_comp_props, NewtonIterationBlackoilInterface& linsolver, const PolymerInflowInterface& polymer_inflow, + WellsManager& wells_manager, const double* gravity, bool has_disgas, bool has_vapoil, @@ -92,7 +93,8 @@ namespace Opm const std::vector& threshold_pressures_by_face); SimulatorReport run(SimulatorTimer& timer, - PolymerBlackoilState& state); + PolymerBlackoilState& state, + WellStateFullyImplicitBlackoil& well_state); private: // Data. @@ -117,6 +119,7 @@ namespace Opm const DerivedGeology& geo_; NewtonIterationBlackoilInterface& solver_; const PolymerInflowInterface& polymer_inflow_; + WellsManager& wells_manager_; // Misc. data std::vector allcells_; const bool has_disgas_; @@ -149,6 +152,7 @@ namespace Opm const RockCompressibility* rock_comp_props, NewtonIterationBlackoilInterface& linsolver, const PolymerInflowInterface& polymer_inflow, + WellsManager& wells_manager, const double* gravity, const bool has_disgas, const bool has_vapoil, @@ -158,7 +162,7 @@ namespace Opm const std::vector& threshold_pressures_by_face) { - pimpl_.reset(new Impl(param, grid, geo, props, polymer_props, rock_comp_props, linsolver, polymer_inflow, gravity, has_disgas, + pimpl_.reset(new Impl(param, grid, geo, props, polymer_props, rock_comp_props, linsolver, polymer_inflow, wells_manager, gravity, has_disgas, has_vapoil, has_polymer, eclipse_state, output_writer, threshold_pressures_by_face)); } @@ -168,9 +172,10 @@ namespace Opm template SimulatorReport SimulatorFullyImplicitBlackoilPolymer::run(SimulatorTimer& timer, - PolymerBlackoilState& state) + PolymerBlackoilState& state, + WellStateFullyImplicitBlackoil& well_state) { - return pimpl_->run(timer, state); + return pimpl_->run(timer, state, well_state); } @@ -242,6 +247,7 @@ namespace Opm const RockCompressibility* rock_comp_props, NewtonIterationBlackoilInterface& linsolver, const PolymerInflowInterface& polymer_inflow, + WellsManager& wells_manager, const double* gravity, const bool has_disgas, const bool has_vapoil, @@ -258,6 +264,7 @@ namespace Opm geo_(geo), solver_(linsolver), polymer_inflow_(polymer_inflow), + wells_manager_(wells_manager), has_disgas_(has_disgas), has_vapoil_(has_vapoil), has_polymer_(has_polymer), @@ -295,7 +302,8 @@ namespace Opm template SimulatorReport SimulatorFullyImplicitBlackoilPolymer::Impl::run(SimulatorTimer& timer, - PolymerBlackoilState& state) + PolymerBlackoilState& state, + WellStateFullyImplicitBlackoil& well_state) { WellStateFullyImplicitBlackoil prev_well_state; @@ -309,29 +317,29 @@ namespace Opm std::ofstream tstep_os(tstep_filename.c_str()); // Main simulation loop. - while (!timer.done()) { +// while (!timer.done()) { // Report timestep. step_timer.start(); timer.report(std::cout); // Create wells and well state. - WellsManager wells_manager(eclipse_state_, - timer.currentStepNum(), - Opm::UgGridHelpers::numCells(grid_), - Opm::UgGridHelpers::globalCell(grid_), - Opm::UgGridHelpers::cartDims(grid_), - Opm::UgGridHelpers::dimensions(grid_), - Opm::UgGridHelpers::beginCellCentroids(grid_), - Opm::UgGridHelpers::cell2Faces(grid_), - Opm::UgGridHelpers::beginFaceCentroids(grid_), - props_.permeability()); - const Wells* wells = wells_manager.c_wells(); - WellStateFullyImplicitBlackoil well_state; - well_state.init(wells, state.blackoilState()); - if (timer.currentStepNum() != 0) { - // Transfer previous well state to current. - well_state.partialCopy(prev_well_state, *wells, prev_well_state.numWells()); - } +// WellsManager wells_manager(eclipse_state_, +// timer.currentStepNum(), +// Opm::UgGridHelpers::numCells(grid_), +// Opm::UgGridHelpers::globalCell(grid_), +// Opm::UgGridHelpers::cartDims(grid_), +// Opm::UgGridHelpers::dimensions(grid_), +// Opm::UgGridHelpers::beginCellCentroids(grid_), +// Opm::UgGridHelpers::cell2Faces(grid_), +// Opm::UgGridHelpers::beginFaceCentroids(grid_), +// props_.permeability()); + const Wells* wells = wells_manager_.c_wells(); +// WellStateFullyImplicitBlackoil well_state; +// well_state.init(wells, state.blackoilState()); +// if (timer.currentStepNum() != 0) { +// // Transfer previous well state to current. +// well_state.partialCopy(prev_well_state, *wells, prev_well_state.numWells()); +// } // Output state at start of time step. if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { @@ -383,19 +391,19 @@ namespace Opm } // Increment timer, remember well state. - ++timer; - prev_well_state = well_state; - } +// ++timer; +// prev_well_state = well_state; +// } // Write final simulation state. - if (output_) { - if (output_vtk_) { - outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); - } - outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); - outputWellStateMatlab(prev_well_state, timer.currentStepNum(), output_dir_); - output_writer_.writeTimeStep(timer, state.blackoilState(), prev_well_state.basicWellState()); - } +// if (output_) { +// if (output_vtk_) { +// outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); +// } +// outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); +// outputWellStateMatlab(prev_well_state, timer.currentStepNum(), output_dir_); +// output_writer_.writeTimeStep(timer, state.blackoilState(), prev_well_state.basicWellState()); +// } // Stop timer and create timing report total_timer.stop(); From 5535dd99cca36cee7fb286b1af3d7300016bb8fe Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Mon, 27 Oct 2014 14:10:40 +0800 Subject: [PATCH 74/83] source term should be '-' not '+' to mass balance equation... --- .../fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp index b09314bb8..3c7f8c8e1 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp @@ -1011,7 +1011,7 @@ namespace { const V polyin = Eigen::Map(&polymer_inflow[0], nc); const V poly_in_perf = subset(polyin, well_cells); const V poly_mc_perf = subset(mc, well_cells).value(); - residual_.material_balance_eq[poly_pos_] += superset(cq_ps[pu.phase_pos[Water]]*poly_mc_perf + residual_.material_balance_eq[poly_pos_] -= superset(cq_ps[pu.phase_pos[Water]]*poly_mc_perf + (wops_.w2p * mix_s[pu.phase_pos[Water]])*cqt_is*poly_in_perf, well_cells, nc); } From 5242d6bbf7ca81e7becce2d921b0afcd1f485ac2 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 28 Oct 2014 13:14:44 +0800 Subject: [PATCH 75/83] move time iterations to Simulator class and pass deck to create polymer inflows. This should solve the issues: write the final state to eclipse binaries. --- examples/sim_poly_fibo_ad.cpp | 73 +++-------- .../SimulatorFullyImplicitBlackoilPolymer.hpp | 6 +- ...latorFullyImplicitBlackoilPolymer_impl.hpp | 115 +++++++++--------- 3 files changed, 79 insertions(+), 115 deletions(-) diff --git a/examples/sim_poly_fibo_ad.cpp b/examples/sim_poly_fibo_ad.cpp index 338a4bc07..3f1536a16 100644 --- a/examples/sim_poly_fibo_ad.cpp +++ b/examples/sim_poly_fibo_ad.cpp @@ -198,9 +198,6 @@ try // initialize variables simtimer.init(timeMap); - //Check for WPOLYMER presence in last report step to decide - //polymer injection control type. - std::cout << polymer << " " << use_wpolymer << std::endl; if (polymer){ if (!use_wpolymer) { OPM_MESSAGE("Warning: simulate polymer injection without WPOLYMER."); @@ -218,61 +215,27 @@ try std::cout << "\n\n================ Starting main simulation loop ===============\n" << std::flush; SimulatorReport fullReport; - WellStateFullyImplicitBlackoil prev_well_state; - for (size_t reportStepIdx = 0; reportStepIdx < timeMap->numTimesteps(); ++reportStepIdx) { - simtimer.setCurrentStepNum(reportStepIdx); - Opm::DerivedGeology geology(*grid->c_grid(), *new_props, eclipseState, grav); + Opm::DerivedGeology geology(*grid->c_grid(), *new_props, eclipseState, grav); - std::vector threshold_pressures = thresholdPressures(deck, eclipseState, *grid->c_grid()); - //Create new wells, polymer inflow controls. - WellsManager wells_manager(eclipseState, - simtimer.currentStepNum(), - Opm::UgGridHelpers::numCells(*grid->c_grid()), - Opm::UgGridHelpers::globalCell(*grid->c_grid()), - Opm::UgGridHelpers::cartDims(*grid->c_grid()), - Opm::UgGridHelpers::dimensions(*grid->c_grid()), - Opm::UgGridHelpers::beginCellCentroids(*grid->c_grid()), - Opm::UgGridHelpers::cell2Faces(*grid->c_grid()), - Opm::UgGridHelpers::beginFaceCentroids(*grid->c_grid()), - props->permeability()); - WellStateFullyImplicitBlackoil well_state; - well_state.init(wells_manager.c_wells(), state.blackoilState()); - if (reportStepIdx != 0) { - // Transfer previous well state to current. - well_state.partialCopy(prev_well_state, *wells_manager.c_wells(), prev_well_state.numWells()); - } - std::unique_ptr polymer_inflow; - if (use_wpolymer) { - if (wells_manager.c_wells() == 0) { - OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells."); - } - polymer_inflow.reset(new PolymerInflowFromDeck(deck, *wells_manager.c_wells(), props->numCells())); - } else { - polymer_inflow.reset(new PolymerInflowBasic(0.0*Opm::unit::day, - 1.0*Opm::unit::day, - 0.0)); - } - SimulatorFullyImplicitBlackoilPolymer simulator(param, - *grid->c_grid(), - geology, - *new_props, - polymer_props_ad, - rock_comp->isActive() ? rock_comp.get() : 0, - *fis_solver, - *polymer_inflow, - wells_manager, - grav, - deck->hasKeyword("DISGAS"), - deck->hasKeyword("VAPOIL"), - polymer, - eclipseState, - outputWriter, - threshold_pressures); + std::vector threshold_pressures = thresholdPressures(deck, eclipseState, *grid->c_grid()); + SimulatorFullyImplicitBlackoilPolymer simulator(param, + *grid->c_grid(), + geology, + *new_props, + polymer_props_ad, + rock_comp->isActive() ? rock_comp.get() : 0, + *fis_solver, + grav, + deck->hasKeyword("DISGAS"), + deck->hasKeyword("VAPOIL"), + polymer, + eclipseState, + outputWriter, + deck, + threshold_pressures); - fullReport = simulator.run(simtimer, state, well_state); - prev_well_state = well_state; - } + fullReport = simulator.run(simtimer, state); std::cout << "\n\n================ End of simulation ===============\n\n"; fullReport.report(std::cout); diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp index 48fd7e5e0..8b165d8a2 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp @@ -86,14 +86,13 @@ namespace Opm const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, NewtonIterationBlackoilInterface& linsolver, - const PolymerInflowInterface& polymer_inflow, - WellsManager& well_manager, const double* gravity, const bool disgas, const bool vapoil, const bool polymer, std::shared_ptr eclipse_state, EclipseWriter& output_writer, + Opm::DeckConstPtr& deck, const std::vector& threshold_pressures_by_face); /// Run the simulation. @@ -104,8 +103,7 @@ namespace Opm /// \param[in,out] well_state state of wells: bhp, perforation rates /// \return simulation report, with timing data SimulatorReport run(SimulatorTimer& timer, - PolymerBlackoilState& state, - WellStateFullyImplicitBlackoil& well_state); + PolymerBlackoilState& state); private: class Impl; diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp index 0936f0d96..acd2caad0 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -82,19 +83,17 @@ namespace Opm const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, NewtonIterationBlackoilInterface& linsolver, - const PolymerInflowInterface& polymer_inflow, - WellsManager& wells_manager, const double* gravity, bool has_disgas, bool has_vapoil, bool has_polymer, std::shared_ptr eclipse_state, EclipseWriter& output_writer, + Opm::DeckConstPtr& deck, const std::vector& threshold_pressures_by_face); SimulatorReport run(SimulatorTimer& timer, - PolymerBlackoilState& state, - WellStateFullyImplicitBlackoil& well_state); + PolymerBlackoilState& state); private: // Data. @@ -118,8 +117,6 @@ namespace Opm // Solvers const DerivedGeology& geo_; NewtonIterationBlackoilInterface& solver_; - const PolymerInflowInterface& polymer_inflow_; - WellsManager& wells_manager_; // Misc. data std::vector allcells_; const bool has_disgas_; @@ -129,6 +126,7 @@ namespace Opm std::shared_ptr eclipse_state_; // output_writer EclipseWriter& output_writer_; + Opm::DeckConstPtr& deck_; RateConverterType rateConverter_; // Threshold pressures. std::vector threshold_pressures_by_face_; @@ -151,19 +149,18 @@ namespace Opm const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, NewtonIterationBlackoilInterface& linsolver, - const PolymerInflowInterface& polymer_inflow, - WellsManager& wells_manager, const double* gravity, const bool has_disgas, const bool has_vapoil, const bool has_polymer, std::shared_ptr eclipse_state, EclipseWriter& output_writer, + Opm::DeckConstPtr& deck, const std::vector& threshold_pressures_by_face) { - pimpl_.reset(new Impl(param, grid, geo, props, polymer_props, rock_comp_props, linsolver, polymer_inflow, wells_manager, gravity, has_disgas, - has_vapoil, has_polymer, eclipse_state, output_writer, threshold_pressures_by_face)); + pimpl_.reset(new Impl(param, grid, geo, props, polymer_props, rock_comp_props, linsolver, gravity, has_disgas, + has_vapoil, has_polymer, eclipse_state, output_writer, deck, threshold_pressures_by_face)); } @@ -172,10 +169,9 @@ namespace Opm template SimulatorReport SimulatorFullyImplicitBlackoilPolymer::run(SimulatorTimer& timer, - PolymerBlackoilState& state, - WellStateFullyImplicitBlackoil& well_state) + PolymerBlackoilState& state) { - return pimpl_->run(timer, state, well_state); + return pimpl_->run(timer, state); } @@ -246,14 +242,13 @@ namespace Opm const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, NewtonIterationBlackoilInterface& linsolver, - const PolymerInflowInterface& polymer_inflow, - WellsManager& wells_manager, const double* gravity, const bool has_disgas, const bool has_vapoil, const bool has_polymer, std::shared_ptr eclipse_state, EclipseWriter& output_writer, + Opm::DeckConstPtr& deck, const std::vector& threshold_pressures_by_face) : param_(param), grid_(grid), @@ -263,13 +258,12 @@ namespace Opm gravity_(gravity), geo_(geo), solver_(linsolver), - polymer_inflow_(polymer_inflow), - wells_manager_(wells_manager), has_disgas_(has_disgas), has_vapoil_(has_vapoil), has_polymer_(has_polymer), eclipse_state_(eclipse_state), output_writer_(output_writer), + deck_(deck), rateConverter_(props_, std::vector(AutoDiffGrid::numCells(grid_), 0)), threshold_pressures_by_face_(threshold_pressures_by_face) { @@ -302,8 +296,7 @@ namespace Opm template SimulatorReport SimulatorFullyImplicitBlackoilPolymer::Impl::run(SimulatorTimer& timer, - PolymerBlackoilState& state, - WellStateFullyImplicitBlackoil& well_state) + PolymerBlackoilState& state) { WellStateFullyImplicitBlackoil prev_well_state; @@ -317,30 +310,47 @@ namespace Opm std::ofstream tstep_os(tstep_filename.c_str()); // Main simulation loop. -// while (!timer.done()) { + while (!timer.done()) { // Report timestep. step_timer.start(); timer.report(std::cout); // Create wells and well state. -// WellsManager wells_manager(eclipse_state_, -// timer.currentStepNum(), -// Opm::UgGridHelpers::numCells(grid_), -// Opm::UgGridHelpers::globalCell(grid_), -// Opm::UgGridHelpers::cartDims(grid_), -// Opm::UgGridHelpers::dimensions(grid_), -// Opm::UgGridHelpers::beginCellCentroids(grid_), -// Opm::UgGridHelpers::cell2Faces(grid_), -// Opm::UgGridHelpers::beginFaceCentroids(grid_), -// props_.permeability()); - const Wells* wells = wells_manager_.c_wells(); -// WellStateFullyImplicitBlackoil well_state; -// well_state.init(wells, state.blackoilState()); -// if (timer.currentStepNum() != 0) { -// // Transfer previous well state to current. -// well_state.partialCopy(prev_well_state, *wells, prev_well_state.numWells()); -// } - + WellsManager wells_manager(eclipse_state_, + timer.currentStepNum(), + Opm::UgGridHelpers::numCells(grid_), + Opm::UgGridHelpers::globalCell(grid_), + Opm::UgGridHelpers::cartDims(grid_), + Opm::UgGridHelpers::dimensions(grid_), + Opm::UgGridHelpers::beginCellCentroids(grid_), + Opm::UgGridHelpers::cell2Faces(grid_), + Opm::UgGridHelpers::beginFaceCentroids(grid_), + props_.permeability()); + const Wells* wells = wells_manager.c_wells(); + WellStateFullyImplicitBlackoil well_state; + well_state.init(wells, state.blackoilState()); + if (timer.currentStepNum() != 0) { + // Transfer previous well state to current. + well_state.partialCopy(prev_well_state, *wells, prev_well_state.numWells()); + } + + // compute polymer inflow + std::unique_ptr polymer_inflow_ptr; + if (deck_->hasKeyword("WPOLYMER")) { + if (wells_manager.c_wells() == 0) { + OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells."); + } + polymer_inflow_ptr.reset(new PolymerInflowFromDeck(deck_, *wells, Opm::UgGridHelpers::numCells(grid_))); + } else { + polymer_inflow_ptr.reset(new PolymerInflowBasic(0.0*Opm::unit::day, + 1.0*Opm::unit::day, + 0.0)); + } + std::vector polymer_inflow_c(Opm::UgGridHelpers::numCells(grid_)); + const PolymerInflowInterface& polymer_inflow = *polymer_inflow_ptr; + polymer_inflow.getInflowValues(timer.simulationTimeElapsed(), + timer.simulationTimeElapsed() + timer.currentStepLength(), + polymer_inflow_c); // Output state at start of time step. if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { if (output_vtk_) { @@ -363,13 +373,6 @@ namespace Opm // Compute reservoir volumes for RESV controls. computeRESV(timer.currentStepNum(), wells, state, well_state); - // compute polymer inflow - std::vector polymer_inflow_c(Opm::UgGridHelpers::numCells(grid_)); - if (has_polymer_) { - polymer_inflow_.getInflowValues(timer.simulationTimeElapsed(), - timer.simulationTimeElapsed() + timer.currentStepLength(), - polymer_inflow_c); - } // Run a single step of the solver. solver_timer.start(); FullyImplicitBlackoilPolymerSolver solver(param_, grid_, props_, geo_, rock_comp_props_, polymer_props_, *wells, solver_, has_disgas_, has_vapoil_, has_polymer_); @@ -391,19 +394,19 @@ namespace Opm } // Increment timer, remember well state. -// ++timer; -// prev_well_state = well_state; -// } + ++timer; + prev_well_state = well_state; + } // Write final simulation state. -// if (output_) { -// if (output_vtk_) { -// outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); -// } -// outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); -// outputWellStateMatlab(prev_well_state, timer.currentStepNum(), output_dir_); -// output_writer_.writeTimeStep(timer, state.blackoilState(), prev_well_state.basicWellState()); -// } + if (output_) { + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); + outputWellStateMatlab(prev_well_state, timer.currentStepNum(), output_dir_); + output_writer_.writeTimeStep(timer, state.blackoilState(), prev_well_state.basicWellState()); + } // Stop timer and create timing report total_timer.stop(); From c0a61c9655b1ab345604a00a1c1b261394a47d53 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 28 Oct 2014 13:31:35 +0800 Subject: [PATCH 76/83] due to the new timer, simulator class should not do time iteration once again. --- opm/polymer/SimulatorCompressiblePolymer.cpp | 380 +++++++++--------- opm/polymer/SimulatorPolymer.cpp | 354 ++++++++-------- ...ulatorFullyImplicitCompressiblePolymer.cpp | 193 ++++----- 3 files changed, 453 insertions(+), 474 deletions(-) diff --git a/opm/polymer/SimulatorCompressiblePolymer.cpp b/opm/polymer/SimulatorCompressiblePolymer.cpp index c58148495..dbb2efe81 100644 --- a/opm/polymer/SimulatorCompressiblePolymer.cpp +++ b/opm/polymer/SimulatorCompressiblePolymer.cpp @@ -292,208 +292,206 @@ namespace Opm wellreport.push(props_, *wells_, state.pressure(), state.surfacevol(), state.saturation(), 0.0, well_state.bhp(), well_state.perfRates()); } -// for (; !timer.done(); ++timer) { - // Report timestep and (optionally) write state to disk. - timer.report(std::cout); - if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { - if (output_vtk_) { - outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + // Report timestep and (optionally) write state to disk. + timer.report(std::cout); + if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); + } + + initial_pressure = state.pressure(); + + // Solve pressure equation. + if (check_well_controls_) { + computeFractionalFlow(props_, poly_props_, allcells_, + state.pressure(), state.surfacevol(), state.saturation(), + state.concentration(), state.maxconcentration(), + fractional_flows); + wells_manager_.applyExplicitReinjectionControls(well_resflows_phase, well_resflows_phase); + } + bool well_control_passed = !check_well_controls_; + int well_control_iteration = 0; + do { + // Run solver + pressure_timer.start(); + psolver_.solve(timer.currentStepLength(), state, well_state); + + // Renormalize pressure if both fluids and rock are + // incompressible, and there are no pressure + // conditions (bcs or wells). It is deemed sufficient + // for now to renormalize using geometric volume + // instead of pore volume. + if (psolver_.singularPressure()) { + // Compute average pressures of previous and last + // step, and total volume. + double av_prev_press = 0.0; + double av_press = 0.0; + double tot_vol = 0.0; + const int num_cells = grid_.number_of_cells; + for (int cell = 0; cell < num_cells; ++cell) { + av_prev_press += initial_pressure[cell]*grid_.cell_volumes[cell]; + av_press += state.pressure()[cell]*grid_.cell_volumes[cell]; + tot_vol += grid_.cell_volumes[cell]; + } + // Renormalization constant + const double ren_const = (av_prev_press - av_press)/tot_vol; + for (int cell = 0; cell < num_cells; ++cell) { + state.pressure()[cell] += ren_const; + } + const int num_wells = (wells_ == NULL) ? 0 : wells_->number_of_wells; + for (int well = 0; well < num_wells; ++well) { + well_state.bhp()[well] += ren_const; } - outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); } - initial_pressure = state.pressure(); + // Stop timer and report + pressure_timer.stop(); + double pt = pressure_timer.secsSinceStart(); + std::cout << "Pressure solver took: " << pt << " seconds." << std::endl; + ptime += pt; - // Solve pressure equation. + // Optionally, check if well controls are satisfied. if (check_well_controls_) { - computeFractionalFlow(props_, poly_props_, allcells_, - state.pressure(), state.surfacevol(), state.saturation(), - state.concentration(), state.maxconcentration(), - fractional_flows); - wells_manager_.applyExplicitReinjectionControls(well_resflows_phase, well_resflows_phase); - } - bool well_control_passed = !check_well_controls_; - int well_control_iteration = 0; - do { - // Run solver - pressure_timer.start(); - psolver_.solve(timer.currentStepLength(), state, well_state); - - // Renormalize pressure if both fluids and rock are - // incompressible, and there are no pressure - // conditions (bcs or wells). It is deemed sufficient - // for now to renormalize using geometric volume - // instead of pore volume. - if (psolver_.singularPressure()) { - // Compute average pressures of previous and last - // step, and total volume. - double av_prev_press = 0.0; - double av_press = 0.0; - double tot_vol = 0.0; - const int num_cells = grid_.number_of_cells; - for (int cell = 0; cell < num_cells; ++cell) { - av_prev_press += initial_pressure[cell]*grid_.cell_volumes[cell]; - av_press += state.pressure()[cell]*grid_.cell_volumes[cell]; - tot_vol += grid_.cell_volumes[cell]; - } - // Renormalization constant - const double ren_const = (av_prev_press - av_press)/tot_vol; - for (int cell = 0; cell < num_cells; ++cell) { - state.pressure()[cell] += ren_const; - } - const int num_wells = (wells_ == NULL) ? 0 : wells_->number_of_wells; - for (int well = 0; well < num_wells; ++well) { - well_state.bhp()[well] += ren_const; - } + Opm::computePhaseFlowRatesPerWell(*wells_, + well_state.perfRates(), + fractional_flows, + well_resflows_phase); + std::cout << "Checking well conditions." << std::endl; + // For testing we set surface := reservoir + well_control_passed = wells_manager_.conditionsMet(well_state.bhp(), well_resflows_phase, well_resflows_phase); + ++well_control_iteration; + if (!well_control_passed && well_control_iteration > max_well_control_iterations_) { + OPM_THROW(std::runtime_error, "Could not satisfy well conditions in " << max_well_control_iterations_ << " tries."); } - - // Stop timer and report - pressure_timer.stop(); - double pt = pressure_timer.secsSinceStart(); - std::cout << "Pressure solver took: " << pt << " seconds." << std::endl; - ptime += pt; - - // Optionally, check if well controls are satisfied. - if (check_well_controls_) { - Opm::computePhaseFlowRatesPerWell(*wells_, - well_state.perfRates(), - fractional_flows, - well_resflows_phase); - std::cout << "Checking well conditions." << std::endl; - // For testing we set surface := reservoir - well_control_passed = wells_manager_.conditionsMet(well_state.bhp(), well_resflows_phase, well_resflows_phase); - ++well_control_iteration; - if (!well_control_passed && well_control_iteration > max_well_control_iterations_) { - OPM_THROW(std::runtime_error, "Could not satisfy well conditions in " << max_well_control_iterations_ << " tries."); - } - if (!well_control_passed) { - std::cout << "Well controls not passed, solving again." << std::endl; - } else { - std::cout << "Well conditions met." << std::endl; - } - } - } while (!well_control_passed); - - // Update pore volumes if rock is compressible. - if (rock_comp_props_ && rock_comp_props_->isActive()) { - initial_porevol = porevol; - computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol); - } - - // Process transport sources (to include bdy terms and well flows). - Opm::computeTransportSource(props_, wells_, well_state, transport_src); - - // Find inflow rate. - const double current_time = timer.simulationTimeElapsed(); - double stepsize = timer.currentStepLength(); - polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c); - - - // Solve transport. - transport_timer.start(); - if (num_transport_substeps_ != 1) { - stepsize /= double(num_transport_substeps_); - std::cout << "Making " << num_transport_substeps_ << " transport substeps." << std::endl; - } - double injected[2] = { 0.0 }; - double produced[2] = { 0.0 }; - double polyinj = 0.0; - double polyprod = 0.0; - for (int tr_substep = 0; tr_substep < num_transport_substeps_; ++tr_substep) { - tsolver_.solve(&state.faceflux()[0], initial_pressure, - state.pressure(), &initial_porevol[0], &porevol[0], - &transport_src[0], &polymer_inflow_c[0], stepsize, - state.saturation(), state.surfacevol(), - state.concentration(), state.maxconcentration()); - double substep_injected[2] = { 0.0 }; - double substep_produced[2] = { 0.0 }; - double substep_polyinj = 0.0; - double substep_polyprod = 0.0; - Opm::computeInjectedProduced(props_, poly_props_, - state, - transport_src, polymer_inflow_c, stepsize, - substep_injected, substep_produced, - substep_polyinj, substep_polyprod); - injected[0] += substep_injected[0]; - injected[1] += substep_injected[1]; - produced[0] += substep_produced[0]; - produced[1] += substep_produced[1]; - polyinj += substep_polyinj; - polyprod += substep_polyprod; - if (gravity_ != 0 && use_segregation_split_) { - tsolver_.solveGravity(columns_, stepsize, - state.saturation(), state.surfacevol(), - state.concentration(), state.maxconcentration()); + if (!well_control_passed) { + std::cout << "Well controls not passed, solving again." << std::endl; + } else { + std::cout << "Well conditions met." << std::endl; } } - transport_timer.stop(); - double tt = transport_timer.secsSinceStart(); - std::cout << "Transport solver took: " << tt << " seconds." << std::endl; - ttime += tt; + } while (!well_control_passed); - // Report volume balances. - Opm::computeSaturatedVol(porevol, state.surfacevol(), inplace_surfvol); - polymass = Opm::computePolymerMass(porevol, state.saturation(), state.concentration(), poly_props_.deadPoreVol()); - polymass_adsorbed = Opm::computePolymerAdsorbed(grid_, props_, poly_props_, - state, rock_comp_props_); - tot_injected[0] += injected[0]; - tot_injected[1] += injected[1]; - tot_produced[0] += produced[0]; - tot_produced[1] += produced[1]; - tot_polyinj += polyinj; - tot_polyprod += polyprod; - std::cout.precision(5); - const int width = 18; - std::cout << "\nMass balance: " - " water(surfvol) oil(surfvol) polymer(kg)\n"; - std::cout << " In-place: " - << std::setw(width) << inplace_surfvol[0] - << std::setw(width) << inplace_surfvol[1] - << std::setw(width) << polymass << std::endl; - std::cout << " Adsorbed: " - << std::setw(width) << 0.0 - << std::setw(width) << 0.0 - << std::setw(width) << polymass_adsorbed << std::endl; - std::cout << " Injected: " - << std::setw(width) << injected[0] - << std::setw(width) << injected[1] - << std::setw(width) << polyinj << std::endl; - std::cout << " Produced: " - << std::setw(width) << produced[0] - << std::setw(width) << produced[1] - << std::setw(width) << polyprod << std::endl; - std::cout << " Total inj: " - << std::setw(width) << tot_injected[0] - << std::setw(width) << tot_injected[1] - << std::setw(width) << tot_polyinj << std::endl; - std::cout << " Total prod: " - << std::setw(width) << tot_produced[0] - << std::setw(width) << tot_produced[1] - << std::setw(width) << tot_polyprod << std::endl; - const double balance[3] = { init_surfvol[0] - inplace_surfvol[0] - tot_produced[0] + tot_injected[0], - init_surfvol[1] - inplace_surfvol[1] - tot_produced[1] + tot_injected[1], - init_polymass - polymass - tot_polyprod + tot_polyinj - polymass_adsorbed }; - std::cout << " Initial - inplace + inj - prod: " - << std::setw(width) << balance[0] - << std::setw(width) << balance[1] - << std::setw(width) << balance[2] - << std::endl; - std::cout << " Relative mass error: " - << std::setw(width) << balance[0]/(init_surfvol[0] + tot_injected[0]) - << std::setw(width) << balance[1]/(init_surfvol[1] + tot_injected[1]) - << std::setw(width) << balance[2]/(init_polymass + tot_polyinj) - << std::endl; - std::cout.precision(8); + // Update pore volumes if rock is compressible. + if (rock_comp_props_ && rock_comp_props_->isActive()) { + initial_porevol = porevol; + computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol); + } - watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(), - produced[0]/(produced[0] + produced[1]), - tot_produced[0]/tot_porevol_init); - if (wells_) { - wellreport.push(props_, *wells_, state.pressure(), state.surfacevol(), - state.saturation(), timer.simulationTimeElapsed() + timer.currentStepLength(), - well_state.bhp(), well_state.perfRates()); + // Process transport sources (to include bdy terms and well flows). + Opm::computeTransportSource(props_, wells_, well_state, transport_src); + + // Find inflow rate. + const double current_time = timer.simulationTimeElapsed(); + double stepsize = timer.currentStepLength(); + polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c); + + + // Solve transport. + transport_timer.start(); + if (num_transport_substeps_ != 1) { + stepsize /= double(num_transport_substeps_); + std::cout << "Making " << num_transport_substeps_ << " transport substeps." << std::endl; + } + double injected[2] = { 0.0 }; + double produced[2] = { 0.0 }; + double polyinj = 0.0; + double polyprod = 0.0; + for (int tr_substep = 0; tr_substep < num_transport_substeps_; ++tr_substep) { + tsolver_.solve(&state.faceflux()[0], initial_pressure, + state.pressure(), &initial_porevol[0], &porevol[0], + &transport_src[0], &polymer_inflow_c[0], stepsize, + state.saturation(), state.surfacevol(), + state.concentration(), state.maxconcentration()); + double substep_injected[2] = { 0.0 }; + double substep_produced[2] = { 0.0 }; + double substep_polyinj = 0.0; + double substep_polyprod = 0.0; + Opm::computeInjectedProduced(props_, poly_props_, + state, + transport_src, polymer_inflow_c, stepsize, + substep_injected, substep_produced, + substep_polyinj, substep_polyprod); + injected[0] += substep_injected[0]; + injected[1] += substep_injected[1]; + produced[0] += substep_produced[0]; + produced[1] += substep_produced[1]; + polyinj += substep_polyinj; + polyprod += substep_polyprod; + if (gravity_ != 0 && use_segregation_split_) { + tsolver_.solveGravity(columns_, stepsize, + state.saturation(), state.surfacevol(), + state.concentration(), state.maxconcentration()); } -// } + } + transport_timer.stop(); + double tt = transport_timer.secsSinceStart(); + std::cout << "Transport solver took: " << tt << " seconds." << std::endl; + ttime += tt; + + // Report volume balances. + Opm::computeSaturatedVol(porevol, state.surfacevol(), inplace_surfvol); + polymass = Opm::computePolymerMass(porevol, state.saturation(), state.concentration(), poly_props_.deadPoreVol()); + polymass_adsorbed = Opm::computePolymerAdsorbed(grid_, props_, poly_props_, + state, rock_comp_props_); + tot_injected[0] += injected[0]; + tot_injected[1] += injected[1]; + tot_produced[0] += produced[0]; + tot_produced[1] += produced[1]; + tot_polyinj += polyinj; + tot_polyprod += polyprod; + std::cout.precision(5); + const int width = 18; + std::cout << "\nMass balance: " + " water(surfvol) oil(surfvol) polymer(kg)\n"; + std::cout << " In-place: " + << std::setw(width) << inplace_surfvol[0] + << std::setw(width) << inplace_surfvol[1] + << std::setw(width) << polymass << std::endl; + std::cout << " Adsorbed: " + << std::setw(width) << 0.0 + << std::setw(width) << 0.0 + << std::setw(width) << polymass_adsorbed << std::endl; + std::cout << " Injected: " + << std::setw(width) << injected[0] + << std::setw(width) << injected[1] + << std::setw(width) << polyinj << std::endl; + std::cout << " Produced: " + << std::setw(width) << produced[0] + << std::setw(width) << produced[1] + << std::setw(width) << polyprod << std::endl; + std::cout << " Total inj: " + << std::setw(width) << tot_injected[0] + << std::setw(width) << tot_injected[1] + << std::setw(width) << tot_polyinj << std::endl; + std::cout << " Total prod: " + << std::setw(width) << tot_produced[0] + << std::setw(width) << tot_produced[1] + << std::setw(width) << tot_polyprod << std::endl; + const double balance[3] = { init_surfvol[0] - inplace_surfvol[0] - tot_produced[0] + tot_injected[0], + init_surfvol[1] - inplace_surfvol[1] - tot_produced[1] + tot_injected[1], + init_polymass - polymass - tot_polyprod + tot_polyinj - polymass_adsorbed }; + std::cout << " Initial - inplace + inj - prod: " + << std::setw(width) << balance[0] + << std::setw(width) << balance[1] + << std::setw(width) << balance[2] + << std::endl; + std::cout << " Relative mass error: " + << std::setw(width) << balance[0]/(init_surfvol[0] + tot_injected[0]) + << std::setw(width) << balance[1]/(init_surfvol[1] + tot_injected[1]) + << std::setw(width) << balance[2]/(init_polymass + tot_polyinj) + << std::endl; + std::cout.precision(8); + + watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(), + produced[0]/(produced[0] + produced[1]), + tot_produced[0]/tot_porevol_init); + if (wells_) { + wellreport.push(props_, *wells_, state.pressure(), state.surfacevol(), + state.saturation(), timer.simulationTimeElapsed() + timer.currentStepLength(), + well_state.bhp(), well_state.perfRates()); + } if (output_) { if (output_vtk_) { diff --git a/opm/polymer/SimulatorPolymer.cpp b/opm/polymer/SimulatorPolymer.cpp index 827b80ddd..8d943d328 100644 --- a/opm/polymer/SimulatorPolymer.cpp +++ b/opm/polymer/SimulatorPolymer.cpp @@ -315,196 +315,194 @@ namespace Opm well_resflows_phase.resize((wells_->number_of_phases)*(wells_->number_of_wells), 0.0); wellreport.push(props_, *wells_, state.saturation(), 0.0, well_state.bhp(), well_state.perfRates()); } - for (; !timer.done(); ++timer) { - // Report timestep and (optionally) write state to disk. - timer.report(std::cout); - if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { - if (output_vtk_) { - outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + // Report timestep and (optionally) write state to disk. + timer.report(std::cout); + if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + if (output_binary_) { + outputStateBinary(grid_, state, timer, output_dir_); + } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); + } + + // Solve pressure. + if (check_well_controls_) { + computeFractionalFlow(props_, poly_props_, allcells_, + state.saturation(), state.concentration(), state.maxconcentration(), + fractional_flows); + wells_manager_.applyExplicitReinjectionControls(well_resflows_phase, well_resflows_phase); + } + bool well_control_passed = !check_well_controls_; + int well_control_iteration = 0; + do { + // Run solver. + pressure_timer.start(); + std::vector initial_pressure = state.pressure(); + psolver_.solve(timer.currentStepLength(), state, well_state); + + // Renormalize pressure if rock is incompressible, and + // there are no pressure conditions (bcs or wells). + // It is deemed sufficient for now to renormalize + // using geometric volume instead of pore volume. + if ((rock_comp_props_ == NULL || !rock_comp_props_->isActive()) + && allNeumannBCs(bcs_) && allRateWells(wells_)) { + // Compute average pressures of previous and last + // step, and total volume. + double av_prev_press = 0.0; + double av_press = 0.0; + double tot_vol = 0.0; + const int num_cells = grid_.number_of_cells; + for (int cell = 0; cell < num_cells; ++cell) { + av_prev_press += initial_pressure[cell]*grid_.cell_volumes[cell]; + av_press += state.pressure()[cell]*grid_.cell_volumes[cell]; + tot_vol += grid_.cell_volumes[cell]; } - if (output_binary_) { - outputStateBinary(grid_, state, timer, output_dir_); + // Renormalization constant + const double ren_const = (av_prev_press - av_press)/tot_vol; + for (int cell = 0; cell < num_cells; ++cell) { + state.pressure()[cell] += ren_const; + } + const int num_wells = (wells_ == NULL) ? 0 : wells_->number_of_wells; + for (int well = 0; well < num_wells; ++well) { + well_state.bhp()[well] += ren_const; } - outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); } - // Solve pressure. + // Stop timer and report. + pressure_timer.stop(); + double pt = pressure_timer.secsSinceStart(); + std::cout << "Pressure solver took: " << pt << " seconds." << std::endl; + ptime += pt; + + // Optionally, check if well controls are satisfied. if (check_well_controls_) { - computeFractionalFlow(props_, poly_props_, allcells_, - state.saturation(), state.concentration(), state.maxconcentration(), - fractional_flows); - wells_manager_.applyExplicitReinjectionControls(well_resflows_phase, well_resflows_phase); - } - bool well_control_passed = !check_well_controls_; - int well_control_iteration = 0; - do { - // Run solver. - pressure_timer.start(); - std::vector initial_pressure = state.pressure(); - psolver_.solve(timer.currentStepLength(), state, well_state); - - // Renormalize pressure if rock is incompressible, and - // there are no pressure conditions (bcs or wells). - // It is deemed sufficient for now to renormalize - // using geometric volume instead of pore volume. - if ((rock_comp_props_ == NULL || !rock_comp_props_->isActive()) - && allNeumannBCs(bcs_) && allRateWells(wells_)) { - // Compute average pressures of previous and last - // step, and total volume. - double av_prev_press = 0.0; - double av_press = 0.0; - double tot_vol = 0.0; - const int num_cells = grid_.number_of_cells; - for (int cell = 0; cell < num_cells; ++cell) { - av_prev_press += initial_pressure[cell]*grid_.cell_volumes[cell]; - av_press += state.pressure()[cell]*grid_.cell_volumes[cell]; - tot_vol += grid_.cell_volumes[cell]; - } - // Renormalization constant - const double ren_const = (av_prev_press - av_press)/tot_vol; - for (int cell = 0; cell < num_cells; ++cell) { - state.pressure()[cell] += ren_const; - } - const int num_wells = (wells_ == NULL) ? 0 : wells_->number_of_wells; - for (int well = 0; well < num_wells; ++well) { - well_state.bhp()[well] += ren_const; - } + Opm::computePhaseFlowRatesPerWell(*wells_, + well_state.perfRates(), + fractional_flows, + well_resflows_phase); + std::cout << "Checking well conditions." << std::endl; + // For testing we set surface := reservoir + well_control_passed = wells_manager_.conditionsMet(well_state.bhp(), well_resflows_phase, well_resflows_phase); + ++well_control_iteration; + if (!well_control_passed && well_control_iteration > max_well_control_iterations_) { + OPM_THROW(std::runtime_error, "Could not satisfy well conditions in " << max_well_control_iterations_ << " tries."); } - - // Stop timer and report. - pressure_timer.stop(); - double pt = pressure_timer.secsSinceStart(); - std::cout << "Pressure solver took: " << pt << " seconds." << std::endl; - ptime += pt; - - // Optionally, check if well controls are satisfied. - if (check_well_controls_) { - Opm::computePhaseFlowRatesPerWell(*wells_, - well_state.perfRates(), - fractional_flows, - well_resflows_phase); - std::cout << "Checking well conditions." << std::endl; - // For testing we set surface := reservoir - well_control_passed = wells_manager_.conditionsMet(well_state.bhp(), well_resflows_phase, well_resflows_phase); - ++well_control_iteration; - if (!well_control_passed && well_control_iteration > max_well_control_iterations_) { - OPM_THROW(std::runtime_error, "Could not satisfy well conditions in " << max_well_control_iterations_ << " tries."); - } - if (!well_control_passed) { - std::cout << "Well controls not passed, solving again." << std::endl; - } else { - std::cout << "Well conditions met." << std::endl; - } - } - } while (!well_control_passed); - - // Update pore volumes if rock is compressible. - if (rock_comp_props_ && rock_comp_props_->isActive()) { - initial_porevol = porevol; - computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol); - } - - // Process transport sources (to include bdy terms and well flows). - Opm::computeTransportSource(grid_, src_, state.faceflux(), 1.0, - wells_, well_state.perfRates(), transport_src); - - // Find inflow rate. - const double current_time = timer.simulationTimeElapsed(); - double stepsize = timer.currentStepLength(); - polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c); - - // Solve transport. - transport_timer.start(); - if (num_transport_substeps_ != 1) { - stepsize /= double(num_transport_substeps_); - std::cout << "Making " << num_transport_substeps_ << " transport substeps." << std::endl; - } - double substep_injected[2] = { 0.0 }; - double substep_produced[2] = { 0.0 }; - double substep_polyinj = 0.0; - double substep_polyprod = 0.0; - injected[0] = injected[1] = produced[0] = produced[1] = polyinj = polyprod = 0.0; - for (int tr_substep = 0; tr_substep < num_transport_substeps_; ++tr_substep) { - tsolver_.solve(&state.faceflux()[0], &initial_porevol[0], &transport_src[0], &polymer_inflow_c[0], stepsize, - state.saturation(), state.concentration(), state.maxconcentration()); - Opm::computeInjectedProduced(props_, poly_props_, - state, - transport_src, polymer_inflow_c, stepsize, - substep_injected, substep_produced, substep_polyinj, substep_polyprod); - injected[0] += substep_injected[0]; - injected[1] += substep_injected[1]; - produced[0] += substep_produced[0]; - produced[1] += substep_produced[1]; - polyinj += substep_polyinj; - polyprod += substep_polyprod; - if (use_segregation_split_) { - tsolver_.solveGravity(columns_, &porevol[0], stepsize, - state.saturation(), state.concentration(), state.maxconcentration()); + if (!well_control_passed) { + std::cout << "Well controls not passed, solving again." << std::endl; + } else { + std::cout << "Well conditions met." << std::endl; } } - transport_timer.stop(); - double tt = transport_timer.secsSinceStart(); - std::cout << "Transport solver took: " << tt << " seconds." << std::endl; - ttime += tt; + } while (!well_control_passed); - // Report volume balances. - Opm::computeSaturatedVol(porevol, state.saturation(), satvol); - polymass = Opm::computePolymerMass(porevol, state.saturation(), state.concentration(), poly_props_.deadPoreVol()); - polymass_adsorbed = Opm::computePolymerAdsorbed(props_, poly_props_, porevol, state.maxconcentration()); - tot_injected[0] += injected[0]; - tot_injected[1] += injected[1]; - tot_produced[0] += produced[0]; - tot_produced[1] += produced[1]; - tot_polyinj += polyinj; - tot_polyprod += polyprod; - std::cout.precision(5); - const int width = 18; - std::cout << "\nVolume and polymer mass balance: " - " water(pv) oil(pv) polymer(kg)\n"; - std::cout << " Saturated volumes: " - << std::setw(width) << satvol[0]/tot_porevol_init - << std::setw(width) << satvol[1]/tot_porevol_init - << std::setw(width) << polymass << std::endl; - std::cout << " Adsorbed volumes: " - << std::setw(width) << 0.0 - << std::setw(width) << 0.0 - << std::setw(width) << polymass_adsorbed << std::endl; - std::cout << " Injected volumes: " - << std::setw(width) << injected[0]/tot_porevol_init - << std::setw(width) << injected[1]/tot_porevol_init - << std::setw(width) << polyinj << std::endl; - std::cout << " Produced volumes: " - << std::setw(width) << produced[0]/tot_porevol_init - << std::setw(width) << produced[1]/tot_porevol_init - << std::setw(width) << polyprod << std::endl; - std::cout << " Total inj volumes: " - << std::setw(width) << tot_injected[0]/tot_porevol_init - << std::setw(width) << tot_injected[1]/tot_porevol_init - << std::setw(width) << tot_polyinj << std::endl; - std::cout << " Total prod volumes: " - << std::setw(width) << tot_produced[0]/tot_porevol_init - << std::setw(width) << tot_produced[1]/tot_porevol_init - << std::setw(width) << tot_polyprod << std::endl; - std::cout << " In-place + prod - inj: " - << std::setw(width) << (satvol[0] + tot_produced[0] - tot_injected[0])/tot_porevol_init - << std::setw(width) << (satvol[1] + tot_produced[1] - tot_injected[1])/tot_porevol_init - << std::setw(width) << (polymass + tot_polyprod - tot_polyinj + polymass_adsorbed) << std::endl; - std::cout << " Init - now - pr + inj: " - << std::setw(width) << (init_satvol[0] - satvol[0] - tot_produced[0] + tot_injected[0])/tot_porevol_init - << std::setw(width) << (init_satvol[1] - satvol[1] - tot_produced[1] + tot_injected[1])/tot_porevol_init - << std::setw(width) << (init_polymass - polymass - tot_polyprod + tot_polyinj - polymass_adsorbed) - << std::endl; - std::cout.precision(8); + // Update pore volumes if rock is compressible. + if (rock_comp_props_ && rock_comp_props_->isActive()) { + initial_porevol = porevol; + computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol); + } - watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(), - produced[0]/(produced[0] + produced[1]), - tot_produced[0]/tot_porevol_init); - if (wells_) { - wellreport.push(props_, *wells_, state.saturation(), - timer.simulationTimeElapsed() + timer.currentStepLength(), - well_state.bhp(), well_state.perfRates()); + // Process transport sources (to include bdy terms and well flows). + Opm::computeTransportSource(grid_, src_, state.faceflux(), 1.0, + wells_, well_state.perfRates(), transport_src); + + // Find inflow rate. + const double current_time = timer.simulationTimeElapsed(); + double stepsize = timer.currentStepLength(); + polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c); + + // Solve transport. + transport_timer.start(); + if (num_transport_substeps_ != 1) { + stepsize /= double(num_transport_substeps_); + std::cout << "Making " << num_transport_substeps_ << " transport substeps." << std::endl; + } + double substep_injected[2] = { 0.0 }; + double substep_produced[2] = { 0.0 }; + double substep_polyinj = 0.0; + double substep_polyprod = 0.0; + injected[0] = injected[1] = produced[0] = produced[1] = polyinj = polyprod = 0.0; + for (int tr_substep = 0; tr_substep < num_transport_substeps_; ++tr_substep) { + tsolver_.solve(&state.faceflux()[0], &initial_porevol[0], &transport_src[0], &polymer_inflow_c[0], stepsize, + state.saturation(), state.concentration(), state.maxconcentration()); + Opm::computeInjectedProduced(props_, poly_props_, + state, + transport_src, polymer_inflow_c, stepsize, + substep_injected, substep_produced, substep_polyinj, substep_polyprod); + injected[0] += substep_injected[0]; + injected[1] += substep_injected[1]; + produced[0] += substep_produced[0]; + produced[1] += substep_produced[1]; + polyinj += substep_polyinj; + polyprod += substep_polyprod; + if (use_segregation_split_) { + tsolver_.solveGravity(columns_, &porevol[0], stepsize, + state.saturation(), state.concentration(), state.maxconcentration()); } } + transport_timer.stop(); + double tt = transport_timer.secsSinceStart(); + std::cout << "Transport solver took: " << tt << " seconds." << std::endl; + ttime += tt; + + // Report volume balances. + Opm::computeSaturatedVol(porevol, state.saturation(), satvol); + polymass = Opm::computePolymerMass(porevol, state.saturation(), state.concentration(), poly_props_.deadPoreVol()); + polymass_adsorbed = Opm::computePolymerAdsorbed(props_, poly_props_, porevol, state.maxconcentration()); + tot_injected[0] += injected[0]; + tot_injected[1] += injected[1]; + tot_produced[0] += produced[0]; + tot_produced[1] += produced[1]; + tot_polyinj += polyinj; + tot_polyprod += polyprod; + std::cout.precision(5); + const int width = 18; + std::cout << "\nVolume and polymer mass balance: " + " water(pv) oil(pv) polymer(kg)\n"; + std::cout << " Saturated volumes: " + << std::setw(width) << satvol[0]/tot_porevol_init + << std::setw(width) << satvol[1]/tot_porevol_init + << std::setw(width) << polymass << std::endl; + std::cout << " Adsorbed volumes: " + << std::setw(width) << 0.0 + << std::setw(width) << 0.0 + << std::setw(width) << polymass_adsorbed << std::endl; + std::cout << " Injected volumes: " + << std::setw(width) << injected[0]/tot_porevol_init + << std::setw(width) << injected[1]/tot_porevol_init + << std::setw(width) << polyinj << std::endl; + std::cout << " Produced volumes: " + << std::setw(width) << produced[0]/tot_porevol_init + << std::setw(width) << produced[1]/tot_porevol_init + << std::setw(width) << polyprod << std::endl; + std::cout << " Total inj volumes: " + << std::setw(width) << tot_injected[0]/tot_porevol_init + << std::setw(width) << tot_injected[1]/tot_porevol_init + << std::setw(width) << tot_polyinj << std::endl; + std::cout << " Total prod volumes: " + << std::setw(width) << tot_produced[0]/tot_porevol_init + << std::setw(width) << tot_produced[1]/tot_porevol_init + << std::setw(width) << tot_polyprod << std::endl; + std::cout << " In-place + prod - inj: " + << std::setw(width) << (satvol[0] + tot_produced[0] - tot_injected[0])/tot_porevol_init + << std::setw(width) << (satvol[1] + tot_produced[1] - tot_injected[1])/tot_porevol_init + << std::setw(width) << (polymass + tot_polyprod - tot_polyinj + polymass_adsorbed) << std::endl; + std::cout << " Init - now - pr + inj: " + << std::setw(width) << (init_satvol[0] - satvol[0] - tot_produced[0] + tot_injected[0])/tot_porevol_init + << std::setw(width) << (init_satvol[1] - satvol[1] - tot_produced[1] + tot_injected[1])/tot_porevol_init + << std::setw(width) << (init_polymass - polymass - tot_polyprod + tot_polyinj - polymass_adsorbed) + << std::endl; + std::cout.precision(8); + + watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(), + produced[0]/(produced[0] + produced[1]), + tot_produced[0]/tot_porevol_init); + if (wells_) { + wellreport.push(props_, *wells_, state.saturation(), + timer.simulationTimeElapsed() + timer.currentStepLength(), + well_state.bhp(), well_state.perfRates()); + } if (output_) { if (output_vtk_) { diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp index 105ae299c..6cdd93e53 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp @@ -267,122 +267,105 @@ namespace Opm std::string filename = output_dir_ + "/step_timing.param"; tstep_os.open(filename.c_str(), std::fstream::out | std::fstream::app); } -// while (!timer.done()) { // Report timestep and (optionally) write state to disk. - step_timer.start(); - timer.report(std::cout); - if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { - if (output_vtk_) { - outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); - } - outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); - // outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); - + step_timer.start(); + timer.report(std::cout); + if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); + } - SimulatorReport sreport; + SimulatorReport sreport; - // Solve pressure equation. - // if (check_well_controls_) { - // computeFractionalFlow(props_, allcells_, - // state.pressure(), state.surfacevol(), state.saturation(), - // fractional_flows); - // wells_manager_.applyExplicitReinjectionControls(well_resflows_phase, well_resflows_phase); - // } - bool well_control_passed = !check_well_controls_; - int well_control_iteration = 0; - do { - // Process transport sources (to include bdy terms and well flows). -// Opm::computeTransportSource(props_, wells_, well_state, transport_src); - // Run solver. - const double current_time = timer.simulationTimeElapsed(); - double stepsize = timer.currentStepLength(); - polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c); - solver_timer.start(); - std::vector initial_pressure = state.pressure(); - solver_.step(timer.currentStepLength(), state, well_state, polymer_inflow_c, transport_src); - // Stop timer and report. - solver_timer.stop(); - const double st = solver_timer.secsSinceStart(); - std::cout << "Fully implicit solver took: " << st << " seconds." << std::endl; + bool well_control_passed = !check_well_controls_; + int well_control_iteration = 0; + do { + // Run solver. + const double current_time = timer.simulationTimeElapsed(); + double stepsize = timer.currentStepLength(); + polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c); + solver_timer.start(); + std::vector initial_pressure = state.pressure(); + solver_.step(timer.currentStepLength(), state, well_state, polymer_inflow_c, transport_src); + // Stop timer and report. + solver_timer.stop(); + const double st = solver_timer.secsSinceStart(); + std::cout << "Fully implicit solver took: " << st << " seconds." << std::endl; - stime += st; - sreport.pressure_time = st; + stime += st; + sreport.pressure_time = st; - // Optionally, check if well controls are satisfied. - if (check_well_controls_) { - Opm::computePhaseFlowRatesPerWell(*wells_, - well_state.perfRates(), - fractional_flows, - well_resflows_phase); - std::cout << "Checking well conditions." << std::endl; - // For testing we set surface := reservoir - well_control_passed = wells_manager_.conditionsMet(well_state.bhp(), well_resflows_phase, well_resflows_phase); - ++well_control_iteration; - if (!well_control_passed && well_control_iteration > max_well_control_iterations_) { - OPM_THROW(std::runtime_error, "Could not satisfy well conditions in " << max_well_control_iterations_ << " tries."); - } - if (!well_control_passed) { - std::cout << "Well controls not passed, solving again." << std::endl; - } else { - std::cout << "Well conditions met." << std::endl; - } + // Optionally, check if well controls are satisfied. + if (check_well_controls_) { + Opm::computePhaseFlowRatesPerWell(*wells_, + well_state.perfRates(), + fractional_flows, + well_resflows_phase); + std::cout << "Checking well conditions." << std::endl; + // For testing we set surface := reservoir + well_control_passed = wells_manager_.conditionsMet(well_state.bhp(), well_resflows_phase, well_resflows_phase); + ++well_control_iteration; + if (!well_control_passed && well_control_iteration > max_well_control_iterations_) { + OPM_THROW(std::runtime_error, "Could not satisfy well conditions in " << max_well_control_iterations_ << " tries."); } - } while (!well_control_passed); - // Update pore volumes if rock is compressible. - if (rock_comp_props_ && rock_comp_props_->isActive()) { - initial_porevol = porevol; - computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol); - } - - double injected[2] = { 0.0 }; - double produced[2] = { 0.0 }; - double polyinj = 0; - double polyprod = 0; - - Opm::computeInjectedProduced(props_, polymer_props_, - state, - transport_src, polymer_inflow_c, timer.currentStepLength(), - injected, produced, - polyinj, polyprod); - tot_injected[0] += injected[0]; - tot_injected[1] += injected[1]; - tot_produced[0] += produced[0]; - tot_produced[1] += produced[1]; - watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(), - produced[0]/(produced[0] + produced[1]), - tot_produced[0]/tot_porevol_init); - std::cout.precision(5); - const int width = 18; - std::cout << "\nMass balance report.\n"; - std::cout << " Injected reservoir volumes: " - << std::setw(width) << injected[0] - << std::setw(width) << injected[1] << std::endl; - std::cout << " Produced reservoir volumes: " - << std::setw(width) << produced[0] - << std::setw(width) << produced[1] << std::endl; - std::cout << " Total inj reservoir volumes: " - << std::setw(width) << tot_injected[0] - << std::setw(width) << tot_injected[1] << std::endl; - std::cout << " Total prod reservoir volumes: " - << std::setw(width) << tot_produced[0] - << std::setw(width) << tot_produced[1] << std::endl; - sreport.total_time = step_timer.secsSinceStart(); - if (output_) { - sreport.reportParam(tstep_os); - - if (output_vtk_) { - outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + if (!well_control_passed) { + std::cout << "Well controls not passed, solving again." << std::endl; + } else { + std::cout << "Well conditions met." << std::endl; } - outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); - outputWaterCut(watercut, output_dir_); - tstep_os.close(); } + } while (!well_control_passed); + // Update pore volumes if rock is compressible. + if (rock_comp_props_ && rock_comp_props_->isActive()) { + initial_porevol = porevol; + computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol); + } - // advance to next timestep before reporting at this location - // ++timer; + double injected[2] = { 0.0 }; + double produced[2] = { 0.0 }; + double polyinj = 0; + double polyprod = 0; - // } + Opm::computeInjectedProduced(props_, polymer_props_, + state, + transport_src, polymer_inflow_c, timer.currentStepLength(), + injected, produced, + polyinj, polyprod); + tot_injected[0] += injected[0]; + tot_injected[1] += injected[1]; + tot_produced[0] += produced[0]; + tot_produced[1] += produced[1]; + watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(), + produced[0]/(produced[0] + produced[1]), + tot_produced[0]/tot_porevol_init); + std::cout.precision(5); + const int width = 18; + std::cout << "\nMass balance report.\n"; + std::cout << " Injected reservoir volumes: " + << std::setw(width) << injected[0] + << std::setw(width) << injected[1] << std::endl; + std::cout << " Produced reservoir volumes: " + << std::setw(width) << produced[0] + << std::setw(width) << produced[1] << std::endl; + std::cout << " Total inj reservoir volumes: " + << std::setw(width) << tot_injected[0] + << std::setw(width) << tot_injected[1] << std::endl; + std::cout << " Total prod reservoir volumes: " + << std::setw(width) << tot_produced[0] + << std::setw(width) << tot_produced[1] << std::endl; + sreport.total_time = step_timer.secsSinceStart(); + if (output_) { + sreport.reportParam(tstep_os); + + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); + outputWaterCut(watercut, output_dir_); + tstep_os.close(); + } total_timer.stop(); From a23e4ca63b9cf4123275234af920632a9731561c Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 28 Oct 2014 14:28:34 +0800 Subject: [PATCH 77/83] remove historical and un-used files. --- examples/sim_2p_fim.cpp | 262 ----------------- examples/sim_2p_fincomp_ad.cpp | 115 -------- examples/sim_fi2p_incomp_ad.cpp | 242 ---------------- examples/sim_poly2p_fincomp_ad.cpp | 157 ---------- examples/sim_poly_fi2p_incomp_ad.cpp | 274 ------------------ .../fullyimplicit/.PolymerPropsAd.cpp.swp | Bin 16384 -> 0 bytes 6 files changed, 1050 deletions(-) delete mode 100644 examples/sim_2p_fim.cpp delete mode 100644 examples/sim_2p_fincomp_ad.cpp delete mode 100644 examples/sim_fi2p_incomp_ad.cpp delete mode 100644 examples/sim_poly2p_fincomp_ad.cpp delete mode 100644 examples/sim_poly_fi2p_incomp_ad.cpp delete mode 100644 opm/polymer/fullyimplicit/.PolymerPropsAd.cpp.swp diff --git a/examples/sim_2p_fim.cpp b/examples/sim_2p_fim.cpp deleted file mode 100644 index 1e150fceb..000000000 --- a/examples/sim_2p_fim.cpp +++ /dev/null @@ -1,262 +0,0 @@ -/* -*/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - - -namespace -{ - void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param) - { - if (param.anyUnused()) { - std::cout << "-------------------- Unused parameters: --------------------\n"; - param.displayUsage(); - std::cout << "----------------------------------------------------------------" << std::endl; - } - } -} // anon namespace - - - -// ----------------- Main program ----------------- -int -main(int argc, char** argv) -try -{ - using namespace Opm; - - std::cout << "\n================ Test program for incompressible two-phase flow ===============\n\n"; - parameter::ParameterGroup param(argc, argv, false); - std::cout << "--------------- Reading parameters ---------------" << std::endl; - - - // If we have a "deck_filename", grid and props will be read from that. - bool use_deck = param.has("deck_filename"); - boost::scoped_ptr deck; - boost::scoped_ptr grid; - boost::scoped_ptr props; - TwophaseState state; - double gravity[3] = { 0.0 }; - if (use_deck) { - std::string deck_filename = param.get("deck_filename"); - deck.reset(new EclipseGridParser(deck_filename)); - // Grid init - grid.reset(new GridManager(*deck)); - // Rock and fluid init - props.reset(new IncompPropsAdFromDeck(*deck, *grid->c_grid())); - // Gravity. - gravity[2] = deck->hasField("NOGRAV") ? 0.0 : unit::gravity; - // Init state variables (saturation and pressure). - int num_cells = grid->c_grid()->number_of_cells; - if (param.has("init_saturation")) { - //initStateBasic(*grid->c_grid(), *props, param, gravity[2], state); - const double init_saturation = param.get("init_saturation"); - for (int c = 0; c < num_cells; ++c) { - state.saturation()[2*c] = init_saturation; - state.saturation()[2*c+1] = 1. - init_saturation; - } - } else { - if (deck->hasField("PRESSURE")) { - // Set saturations from SWAT/SGAS, pressure from PRESSURE. - std::vector& s = state.saturation(); - std::vector& p = state.pressure(); - const std::vector& p_deck = deck->getFloatingPointValue("PRESSURE"); - // water-oil or water-gas: we require SWAT - if (!deck->hasField("SWAT")) { - OPM_THROW(std::runtime_error, "initStateFromDeck(): missing SWAT keyword in 2-phase init"); - } - const std::vector& sw_deck = deck->getFloatingPointValue("SWAT"); - for (int c = 0; c < num_cells; ++c) { - int c_deck = (grid->c_grid()->global_cell == NULL) ? c : grid->c_grid()->global_cell[c]; - s[2*c] = sw_deck[c_deck]; - s[2*c + 1] = 1.0 - sw_deck[c_deck]; - p[c] = p_deck[c_deck]; - } - } - } - } else { - // Grid init. - const int nx = param.getDefault("nx", 100); - const int ny = param.getDefault("ny", 100); - const int nz = param.getDefault("nz", 1); - const double dx = param.getDefault("dx", 1.0); - const double dy = param.getDefault("dy", 1.0); - const double dz = param.getDefault("dz", 1.0); - grid.reset(new GridManager(nx, ny, nz, dx, dy, dz)); - // Rock and fluid init. - props.reset(new IncompPropsAdBasic(param, grid->c_grid()->dimensions, grid->c_grid()->number_of_cells)); - // Rock compressibility. - // Gravity. - gravity[2] = param.getDefault("gravity", 0.0); - int num_cells = grid->c_grid()->number_of_cells; - } - - // Warn if gravity but no density difference. - bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); - const double *grav = use_gravity ? &gravity[0] : 0; - - // Initialising src - std::vector src(num_cells, 0.0); - if (use_deck) { - // Do nothing, wells will be the driving force, not source terms. - if (deck->hasField("SRC")) { - const std::vector& src_deck = deck->getFloatingPointValue("SRC"); - for (int c = 0; c < num_cells; ++c) { - int c_deck = (grid->c_grid()->global_cell == NULL) ? c : grid->c_grid()->global_cell[c]; - src[c] = src_deck[c_deck]; - } - } - } else { - // Compute pore volumes, in order to enable specifying injection rate - // terms of total pore volume. - std::vector porevol; - computePorevolume(*grid->c_grid(), props->porosity(), porevol); - const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); - const double default_injection = use_gravity ? 0.0 : 0.1; - const double flow_per_sec = param.getDefault("injected_porevolumes_per_day", default_injection) - *tot_porevol_init/unit::day; - src[0] = flow_per_sec; - src[num_cells - 1] = -flow_per_sec; - } - - in: num_cells = grid->c_grid()->number_of_cells; - - // Linear solver. - LinearSolverFactory linsolver(param); - - // Write parameters used for later reference. - bool output = param.getDefault("output", true); - std::ofstream epoch_os; - std::string output_dir; - if (output) { - output_dir = - param.getDefault("output_dir", std::string("output")); - boost::filesystem::path fpath(output_dir); - try { - create_directories(fpath); - } - catch (...) { - OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); - } - std::string filename = output_dir + "/epoch_timing.param"; - epoch_os.open(filename.c_str(), std::fstream::trunc | std::fstream::out); - // open file to clean it. The file is appended to in SimulatorTwophase - filename = output_dir + "/step_timing.param"; - std::fstream step_os(filename.c_str(), std::fstream::trunc | std::fstream::out); - step_os.close(); - param.writeParam(output_dir + "/simulation.param"); - } - - - std::cout << "\n\n================ Starting main simulation loop ===============\n" - << " (number of epochs: " - << (use_deck ? deck->numberOfEpochs() : 1) << ")\n\n" << std::flush; - - SimulatorReport rep; - if (!use_deck) { - // Simple simulation without a deck. - SimulatorFullyImplicitTwophase simulator(param, - *grid->c_grid(), - *props, - linsolver, - src); - SimulatorTimer simtimer; - simtimer.init(param); - warnIfUnusedParams(param); - rep = simulator.run(simtimer, state, src); - } else { - // With a deck, we may have more epochs etc. - int step = 0; - SimulatorTimer simtimer; - // Use timer for last epoch to obtain total time. - deck->setCurrentEpoch(deck->numberOfEpochs() - 1); - simtimer.init(*deck); - const double total_time = simtimer.totalTime(); - for (int epoch = 0; epoch < deck->numberOfEpochs(); ++epoch) { - // Set epoch index. - deck->setCurrentEpoch(epoch); - - // Update the timer. - if (deck->hasField("TSTEP")) { - simtimer.init(*deck); - } else { - if (epoch != 0) { - OPM_THROW(std::runtime_error, "No TSTEP in deck for epoch " << epoch); - } - simtimer.init(param); - } - simtimer.setCurrentStepNum(step); - simtimer.setTotalTime(total_time); - - // Report on start of epoch. - std::cout << "\n\n-------------- Starting epoch " << epoch << " --------------" - << "\n (number of steps: " - << simtimer.numSteps() - step << ")\n\n" << std::flush; - - - // Create and run simulator. - SimulatorFullyImplicitTwophase simulator(param, - *grid->c_grid(), - *props, - linsolver, - src); - if (epoch == 0) { - warnIfUnusedParams(param); - } - SimulatorReport epoch_rep = simulator.run(simtimer, state, src); - if (output) { - epoch_rep.reportParam(epoch_os); - } - // Update total timing report and remember step number. - rep += epoch_rep; - step = simtimer.currentStepNum(); - } - } - - std::cout << "\n\n================ End of simulation ===============\n\n"; - rep.report(std::cout); - - if (output) { - std::string filename = output_dir + "/walltime.param"; - std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out); - rep.reportParam(tot_os); - } - -} -catch (const std::exception &e) { - std::cerr << "Program threw an exception: " << e.what() << "\n"; - throw; -} - diff --git a/examples/sim_2p_fincomp_ad.cpp b/examples/sim_2p_fincomp_ad.cpp deleted file mode 100644 index 91b026fad..000000000 --- a/examples/sim_2p_fincomp_ad.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - - -#include -#include - -#include -#include -#include - -int main (int argc, char** argv) -try -{ - - using namespace Opm; - parameter::ParameterGroup param(argc, argv, false); - - bool use_deck = param.has("deck_filename"); - if (!use_deck) { - OPM_THROW(std::runtime_error, "FullyImplicitTwoPhaseSolver cannot run without deckfile."); - } - double gravity[3] = { 0.0 }; - std::string deck_filename = param.get("deck_filename"); - EclipseGridParser deck = EclipseGridParser(deck_filename); - int nx = param.getDefault("nx", 30); - int ny = param.getDefault("ny", 1); - int nz = 1; - double dx = 10./nx; - double dy = 1.0; - double dz = 1.0; - GridManager grid_manager(nx, ny, nz, dx, dy, dz); - const UnstructuredGrid& grid = *grid_manager.c_grid(); - int num_cells = grid.number_of_cells; - int num_phases = 2; - using namespace Opm::unit; - using namespace Opm::prefix; - std::vector density(num_phases, 1000.0); - std::vector viscosity(num_phases, 1.0*centi*Poise); - viscosity[0] = 0.5 * centi * Poise; - viscosity[1] = 5 * centi * Poise; - double porosity = 0.35; - double permeability = 10.0*milli*darcy; - SaturationPropsBasic::RelPermFunc rel_perm_func = SaturationPropsBasic::Linear; - IncompPropsAdBasic props(num_phases, rel_perm_func, density, viscosity, - porosity, permeability, grid.dimensions, num_cells); -/* - std::vector src(num_cells, 0.0); - src[0] = 1. / day; - src[num_cells-1] = -1. / day; - */ - - FlowBCManager bcs; - LinearSolverUmfpack linsolver; - TwophaseState state; - state.init(grid, 2); - WellState well_state; - WellsManager wells(deck, grid, props.permeability()); - well_state.init(wells.c_wells(), state); - FullyImplicitTwoPhaseSolver solver(grid, props, *wells.c_wells(), linsolver, gravity); - std::vector porevol; - Opm::computePorevolume(grid, props.porosity(), porevol); - const double dt = param.getDefault("dt", 10.) * day; - const int num_time_steps = param.getDefault("nsteps", 10); - std::vector allcells(num_cells); - for (int cell = 0; cell < num_cells; ++cell) { - allcells[cell] = cell; - } - std::vector src; // empty src term. - gravity[2] = param.getDefault("gravity", 0.0); - //initial sat - for (int c = 0; c < num_cells; ++c) { - state.saturation()[2*c] = 0.2; - state.saturation()[2*c+1] = 0.8; - } - std::vector p(num_cells, 100*Opm::unit::barsa); - state.pressure() = p; - std::ostringstream vtkfilename; - vtkfilename.str(""); - vtkfilename << "sim_2p_fincomp_" << std::setw(3) << std::setfill('0') << 0 << ".vtu"; - std::ofstream vtkfile(vtkfilename.str().c_str()); - Opm::DataMap dm; - dm["saturation"] = &state.saturation(); - dm["pressure"] = &state.pressure(); - Opm::writeVtkData(grid, dm, vtkfile); - for (int i = 0; i < num_time_steps; ++i) { - solver.step(dt, state, src, well_state); - vtkfilename.str(""); - vtkfilename << "sim_2p_fincomp_" << std::setw(3) << std::setfill('0') << i + 1 << ".vtu"; - std::ofstream vtkfile(vtkfilename.str().c_str()); - Opm::DataMap dm; - dm["saturation"] = &state.saturation(); - dm["pressure"] = &state.pressure(); - Opm::writeVtkData(grid, dm, vtkfile); - } -} -catch (const std::exception &e) { - std::cerr << "Program threw an exception: " << e.what() << "\n"; - throw; -} diff --git a/examples/sim_fi2p_incomp_ad.cpp b/examples/sim_fi2p_incomp_ad.cpp deleted file mode 100644 index 4cee73252..000000000 --- a/examples/sim_fi2p_incomp_ad.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/* - Copyright 2013 SINTEF ICT, Applied Mathematics. - - This file is part of the Open Porous Media project (OPM). - - OPM is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OPM is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with OPM. If not, see . -*/ - - - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - - -namespace -{ - void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param) - { - if (param.anyUnused()) { - std::cout << "-------------------- Unused parameters: --------------------\n"; - param.displayUsage(); - std::cout << "----------------------------------------------------------------" << std::endl; - } - } -} // anon namespace - - - -// ----------------- Main program ----------------- -int -main(int argc, char** argv) -try -{ - using namespace Opm; - - std::cout << "\n================ Test program for fully implicit three-phase black-oil flow ===============\n\n"; - parameter::ParameterGroup param(argc, argv, false); - std::cout << "--------------- Reading parameters ---------------" << std::endl; - - // If we have a "deck_filename", grid and props will be read from that. - bool use_deck = param.has("deck_filename"); - if (!use_deck) { - OPM_THROW(std::runtime_error, "This program must be run with an input deck. " - "Specify the deck with deck_filename=deckname.data (for example)."); - } - boost::scoped_ptr deck; - boost::scoped_ptr grid; - boost::scoped_ptr props; - boost::scoped_ptr new_props; - TwophaseState state; - // bool check_well_controls = false; - // int max_well_control_iterations = 0; - double gravity[3] = { 0.0 }; - std::string deck_filename = param.get("deck_filename"); - deck.reset(new EclipseGridParser(deck_filename)); - // Grid init - grid.reset(new GridManager(*deck)); - - // use the capitalized part of the deck's filename between the - // last '/' and the last '.' character as base name. - std::string baseName = deck_filename; - auto charPos = baseName.rfind('/'); - if (charPos != std::string::npos) - baseName = baseName.substr(charPos + 1); - charPos = baseName.rfind('.'); - if (charPos != std::string::npos) - baseName = baseName.substr(0, charPos); - baseName = boost::to_upper_copy(baseName); - - // Rock and fluid init - props.reset(new IncompPropertiesFromDeck(*deck, *grid->c_grid())); - new_props.reset(new IncompPropsAdFromDeck(*deck, *grid->c_grid())); - // check_well_controls = param.getDefault("check_well_controls", false); - // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); - // Rock compressibility. - // Gravity. - gravity[2] = deck->hasField("NOGRAV") ? 0.0 : unit::gravity; - // Init state variables (saturation and pressure). - - if (param.has("init_saturation")) { - initStateBasic(*grid->c_grid(), *props, param, gravity[2], state); - } else { - initStateFromDeck(*grid->c_grid(), *props, *deck, gravity[2], state); - } - - bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); - const double *grav = use_gravity ? &gravity[0] : 0; - - // Linear solver. - LinearSolverFactory linsolver(param); - - // Write parameters used for later reference. - bool output = param.getDefault("output", true); - std::ofstream epoch_os; - std::string output_dir; - if (output) { - output_dir = - param.getDefault("output_dir", std::string("output")); - boost::filesystem::path fpath(output_dir); - try { - create_directories(fpath); - } - catch (...) { - OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); - } - std::string filename = output_dir + "/epoch_timing.param"; - epoch_os.open(filename.c_str(), std::fstream::trunc | std::fstream::out); - // open file to clean it. The file is appended to in SimulatorTwophase - filename = output_dir + "/step_timing.param"; - std::fstream step_os(filename.c_str(), std::fstream::trunc | std::fstream::out); - step_os.close(); - param.writeParam(output_dir + "/simulation.param"); - } - - - std::cout << "\n\n================ Starting main simulation loop ===============\n" - << " (number of epochs: " - << (deck->numberOfEpochs()) << ")\n\n" << std::flush; - - SimulatorReport rep; - // With a deck, we may have more epochs etc. - WellState well_state; - int step = 0; - SimulatorTimer simtimer; - // Use timer for last epoch to obtain total time. - deck->setCurrentEpoch(deck->numberOfEpochs() - 1); - simtimer.init(*deck); - const double total_time = simtimer.totalTime(); - for (int epoch = 0; epoch < deck->numberOfEpochs(); ++epoch) { - // Set epoch index. - deck->setCurrentEpoch(epoch); - - // Update the timer. - if (deck->hasField("TSTEP")) { - simtimer.init(*deck); - } else { - if (epoch != 0) { - OPM_THROW(std::runtime_error, "No TSTEP in deck for epoch " << epoch); - } - simtimer.init(param); - } - simtimer.setCurrentStepNum(step); - simtimer.setTotalTime(total_time); - - // Report on start of epoch. - std::cout << "\n\n-------------- Starting epoch " << epoch << " --------------" - << "\n (number of steps: " - << simtimer.numSteps() - step << ")\n\n" << std::flush; - - // Create new wells, well_state - WellsManager wells(*deck, *grid->c_grid(), props->permeability()); - // @@@ HACK: we should really make a new well state and - // properly transfer old well state to it every epoch, - // since number of wells may change etc. - if (epoch == 0) { - well_state.init(wells.c_wells(), state); - } - - // Create and run simulator. - std::vector src(grid->c_grid()->number_of_cells, 0.0); - src[0] = 10. / Opm::unit::day; - src[grid->c_grid()->number_of_cells-1] = -10. / Opm::unit::day; - SimulatorFullyImplicitTwophase simulator(param, - *grid->c_grid(), - *new_props, - wells, - linsolver, - src, - grav); - if (epoch == 0) { - warnIfUnusedParams(param); - } - SimulatorReport epoch_rep = simulator.run(simtimer, state, src, well_state); - if (output) { - epoch_rep.reportParam(epoch_os); - } - // Update total timing report and remember step number. - rep += epoch_rep; - step = simtimer.currentStepNum(); - } - - std::cout << "\n\n================ End of simulation ===============\n\n"; - rep.report(std::cout); - - if (output) { - std::string filename = output_dir + "/walltime.param"; - std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out); - rep.reportParam(tot_os); - } - -} -catch (const std::exception &e) { - std::cerr << "Program threw an exception: " << e.what() << "\n"; - throw; -} - diff --git a/examples/sim_poly2p_fincomp_ad.cpp b/examples/sim_poly2p_fincomp_ad.cpp deleted file mode 100644 index 68efdb751..000000000 --- a/examples/sim_poly2p_fincomp_ad.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -int main (int argc, char** argv) -try -{ - using namespace Opm; - parameter::ParameterGroup param(argc, argv, false); - bool use_poly_deck = param.has("deck_filename"); - if (!use_poly_deck) { - OPM_THROW(std::runtime_error, "Polymer Properties must be read from deck_filename\n"); - } - std::string deck_filename = param.get("deck_filename"); - EclipseGridParser deck = EclipseGridParser(deck_filename); - int nx = param.getDefault("nx", 30); - int ny = param.getDefault("ny", 30); - int nz = 1; - double dx = 10./nx; - double dy = 1.0; - double dz = 1.0; - GridManager grid_manager(nx, ny, nz, dx, dy, dz); - const UnstructuredGrid& grid = *grid_manager.c_grid(); - int num_cells = grid.number_of_cells; - int num_phases = 2; - using namespace Opm::unit; - using namespace Opm::prefix; - std::vector density(num_phases, 1000.0); - std::vector viscosity(num_phases, 1.0*centi*Poise); - viscosity[0] = 0.5 * centi * Poise; - viscosity[1] = 5 * centi * Poise; - double porosity = 0.35; - double permeability = 10.0*milli*darcy; - SaturationPropsBasic::RelPermFunc rel_perm_func = SaturationPropsBasic::Linear; - IncompPropsAdBasic props(num_phases, rel_perm_func, density, viscosity, - porosity, permeability, grid.dimensions, num_cells); - - // Init polymer properties. - // Setting defaults to provide a simple example case. - PolymerProperties polymer_props(deck); - #if 0 - if (use_poly_deck) { - } else { - double c_max = param.getDefault("c_max_limit", 5.0); - double mix_param = param.getDefault("mix_param", 1.0); - double rock_density = param.getDefault("rock_density", 1000.0); - double dead_pore_vol = param.getDefault("dead_pore_vol", 0.15); - double res_factor = param.getDefault("res_factor", 1.) ; // res_factor = 1 gives no change in permeability - double c_max_ads = param.getDefault("c_max_ads", 1.); - int ads_index = param.getDefault("ads_index", Opm::PolymerProperties::NoDesorption); - std::vector c_vals_visc(2, -1e100); - c_vals_visc[0] = 0.0; - c_vals_visc[1] = 7.0; - std::vector visc_mult_vals(2, -1e100); - visc_mult_vals[0] = 1.0; - // poly_props.visc_mult_vals[1] = param.getDefault("c_max_viscmult", 30.0); - visc_mult_vals[1] = 20.0; - std::vector c_vals_ads(3, -1e100); - c_vals_ads[0] = 0.0; - c_vals_ads[1] = 2.0; - c_vals_ads[2] = 8.0; - std::vector ads_vals(3, -1e100); - ads_vals[0] = 0.0; - ads_vals[1] = 0.0015; - ads_vals[2] = 0.0025; - PolymerProperties polymer_props; - polymer_props.set(c_max, mix_param, rock_density, dead_pore_vol, res_factor, c_max_ads, - static_cast(ads_index), - c_vals_visc, visc_mult_vals, c_vals_ads, ads_vals); - } - #endif - PolymerPropsAd polymer_props_ad(polymer_props); - std::vector omega; - std::vector src(num_cells, 0.0); - std::vector src_polymer(num_cells); - src[0] = param.getDefault("insrc", 1.) / day; - src[num_cells-1] = -param.getDefault("insrc", 1.) / day; - - PolymerInflowBasic polymer_inflow(param.getDefault("poly_start_days", 300.0)*Opm::unit::day, - param.getDefault("poly_end_days", 800.0)*Opm::unit::day, - param.getDefault("poly_amount", polymer_props.cMax())); - FlowBCManager bcs; - LinearSolverUmfpack linsolver; - FullyImplicitTwophasePolymerSolver solver(grid, props,polymer_props_ad, linsolver); - std::vector porevol; - Opm::computePorevolume(grid, props.porosity(), porevol); - const double dt = param.getDefault("dt", 10.) * day; - const int num_time_steps = param.getDefault("nsteps", 10); - std::vector allcells(num_cells); - for (int cell = 0; cell < num_cells; ++cell) { - allcells[cell] = cell; - } - PolymerState state; - state.init(grid, 2); - //initial sat - for (int c = 0; c < num_cells; ++c) { - state.saturation()[2*c] = 0.2; - state.saturation()[2*c+1] = 0.8; - } - std::vector p(num_cells, 100*Opm::unit::barsa); - state.pressure() = p; - - std::vector c(num_cells, 0.0); - state.concentration() = c; - std::ostringstream vtkfilename; - double currentime = 0; - - // Write the initial state. - vtkfilename.str(""); - vtkfilename << "sim_poly2p_fincomp_ad_" << std::setw(3) << std::setfill('0') << 0<< ".vtu"; - std::ofstream vtkfile(vtkfilename.str().c_str()); - Opm::DataMap dm; - dm["saturation"] = &state.saturation(); - dm["pressure"] = &state.pressure(); - dm["concentration"] = &state.concentration(); - Opm::writeVtkData(grid, dm, vtkfile); - for (int i = 0; i < num_time_steps; ++i) { - currentime += dt; - polymer_inflow.getInflowValues(currentime, currentime+dt, src_polymer); - solver.step(dt, state, src, src_polymer); - vtkfilename.str(""); - vtkfilename << "sim_poly2p_fincomp_ad_" << std::setw(3) << std::setfill('0') << i + 1<< ".vtu"; - std::ofstream vtkfile(vtkfilename.str().c_str()); - Opm::DataMap dm; - dm["saturation"] = &state.saturation(); - dm["pressure"] = &state.pressure(); - dm["concentration"] = &state.concentration(); - Opm::writeVtkData(grid, dm, vtkfile); - } -} -catch (const std::exception &e) { - std::cerr << "Program threw an exception: " << e.what() << "\n"; - throw; -} diff --git a/examples/sim_poly_fi2p_incomp_ad.cpp b/examples/sim_poly_fi2p_incomp_ad.cpp deleted file mode 100644 index 5fecc687d..000000000 --- a/examples/sim_poly_fi2p_incomp_ad.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/* - Copyright 2013 SINTEF ICT, Applied Mathematics. - - This file is part of the Open Porous Media project (OPM). - - OPM is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OPM is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with OPM. If not, see . -*/ - - - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - - -namespace -{ - void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param) - { - if (param.anyUnused()) { - std::cout << "-------------------- Unused parameters: --------------------\n"; - param.displayUsage(); - std::cout << "----------------------------------------------------------------" << std::endl; - } - } -} // anon namespace - - - -// ----------------- Main program ----------------- -int -main(int argc, char** argv) -try -{ - using namespace Opm; - - std::cout << "\n================ Test program for fully implicit three-phase black-oil flow ===============\n\n"; - parameter::ParameterGroup param(argc, argv, false); - std::cout << "--------------- Reading parameters ---------------" << std::endl; - - // If we have a "deck_filename", grid and props will be read from that. - bool use_deck = param.has("deck_filename"); - if (!use_deck) { - OPM_THROW(std::runtime_error, "This program must be run with an input deck. " - "Specify the deck with deck_filename=deckname.data (for example)."); - } - boost::scoped_ptr deck; - boost::scoped_ptr grid; - boost::scoped_ptr props; - boost::scoped_ptr new_props; -// boost::scoped_ptr polymer_props; - PolymerState state; -// bool check_well_controls = false; -// int max_well_control_iterations = 0; - double gravity[3] = { 0.0 }; - std::string deck_filename = param.get("deck_filename"); - deck.reset(new EclipseGridParser(deck_filename)); - // Grid init - grid.reset(new GridManager(*deck)); - - // use the capitalized part of the deck's filename between the - // last '/' and the last '.' character as base name. - std::string baseName = deck_filename; - auto charPos = baseName.rfind('/'); - if (charPos != std::string::npos) - baseName = baseName.substr(charPos + 1); - charPos = baseName.rfind('.'); - if (charPos != std::string::npos) - baseName = baseName.substr(0, charPos); - baseName = boost::to_upper_copy(baseName); - - // Rock and fluid init - props.reset(new IncompPropertiesFromDeck(*deck, *grid->c_grid())); - new_props.reset(new IncompPropsAdFromDeck(*deck, *grid->c_grid())); - PolymerProperties polymer_props(*deck); - PolymerPropsAd polymer_props_ad(polymer_props); -// polymer_props.reset(new PolymerPropsAd(*deck, *grid->c_grid())); -// check_well_controls = param.getDefault("check_well_controls", false); -// max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); - // Gravity. - gravity[2] = deck->hasField("NOGRAV") ? 0.0 : unit::gravity; - // Init state variables (saturation and pressure). - - if (param.has("init_saturation")) { - initStateBasic(*grid->c_grid(), *props, param, gravity[2], state); - } else { - initStateFromDeck(*grid->c_grid(), *props, *deck, gravity[2], state); - } - - bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); - const double* grav = use_gravity ? &gravity[0] : 0; - - // Linear solver. - LinearSolverFactory linsolver(param); - - // Write parameters used for later reference. - bool output = param.getDefault("output", true); - std::ofstream epoch_os; - std::string output_dir; - if (output) { - output_dir = - param.getDefault("output_dir", std::string("output")); - boost::filesystem::path fpath(output_dir); - try { - create_directories(fpath); - } - catch (...) { - OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); - } - std::string filename = output_dir + "/epoch_timing.param"; - epoch_os.open(filename.c_str(), std::fstream::trunc | std::fstream::out); - // open file to clean it. The file is appended to in SimulatorTwophase - filename = output_dir + "/step_timing.param"; - std::fstream step_os(filename.c_str(), std::fstream::trunc | std::fstream::out); - step_os.close(); - param.writeParam(output_dir + "/simulation.param"); - } - - - std::cout << "\n\n================ Starting main simulation loop ===============\n" - << " (number of epochs: " - << (deck->numberOfEpochs()) << ")\n\n" << std::flush; - - SimulatorReport rep; - // With a deck, we may have more epochs etc. - WellState well_state; - int step = 0; - SimulatorTimer simtimer; - // Use timer for last epoch to obtain total time. - deck->setCurrentEpoch(deck->numberOfEpochs() - 1); - simtimer.init(*deck); - const double total_time = simtimer.totalTime(); - // Check for WPOLYMER presence in last epoch to decide - // polymer injection control type. - const bool use_wpolymer = deck->hasField("WPOLYMER"); - if (use_wpolymer) { - if (param.has("poly_start_days")) { - OPM_MESSAGE("Warning: Using WPOLYMER to control injection since it was found in deck. " - "You seem to be trying to control it via parameter poly_start_days (etc.) as well."); - } - } - for (int epoch = 0; epoch < deck->numberOfEpochs(); ++epoch) { - // Set epoch index. - deck->setCurrentEpoch(epoch); - - // Update the timer. - if (deck->hasField("TSTEP")) { - simtimer.init(*deck); - } else { - if (epoch != 0) { - OPM_THROW(std::runtime_error, "No TSTEP in deck for epoch " << epoch); - } - simtimer.init(param); - } - simtimer.setCurrentStepNum(step); - simtimer.setTotalTime(total_time); - - // Report on start of epoch. - std::cout << "\n\n-------------- Starting epoch " << epoch << " --------------" - << "\n (number of steps: " - << simtimer.numSteps() - step << ")\n\n" << std::flush; - - // Create new wells, polymer inflow controls. - WellsManager wells(*deck, *grid->c_grid(), props->permeability()); - boost::scoped_ptr polymer_inflow; - if (use_wpolymer) { - if (wells.c_wells() == 0) { - OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells."); - } - polymer_inflow.reset(new PolymerInflowFromDeck(*deck, *wells.c_wells(), props->numCells())); - } else { - polymer_inflow.reset(new PolymerInflowBasic(param.getDefault("poly_start_days", 300.0)*Opm::unit::day, - param.getDefault("poly_end_days", 800.0)*Opm::unit::day, - param.getDefault("poly_amount", polymer_props.cMax()))); - } - // @@@ HACK: we should really make a new well state and - // properly transfer old well state to it every epoch, - // since number of wells may change etc. - if (epoch == 0) { - well_state.init(wells.c_wells(), state); - } - - // Create and run simulator. - #if 0 - std::vector src(grid->c_grid()->number_of_cells, 0.0); - src[0] = 10. / Opm::unit::day; - src[grid->c_grid()->number_of_cells-1] = -10. / Opm::unit::day; - PolymerInflowBasic polymer_inflow(param.getDefault("poly_start_days", 300.0)*Opm::unit::day, - param.getDefault("poly_end_days", 800.0)*Opm::unit::day, - param.getDefault("poly_amount", polymer_props.cMax())); - #endif - SimulatorFullyImplicitTwophasePolymer simulator(param, - *grid->c_grid(), - *new_props, - polymer_props_ad, - linsolver, - wells, - *polymer_inflow, - grav); - if (epoch == 0) { - warnIfUnusedParams(param); - } - SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state); - if (output) { - epoch_rep.reportParam(epoch_os); - } - // Update total timing report and remember step number. - rep += epoch_rep; - step = simtimer.currentStepNum(); - } - - std::cout << "\n\n================ End of simulation ===============\n\n"; - rep.report(std::cout); - - if (output) { - std::string filename = output_dir + "/walltime.param"; - std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out); - rep.reportParam(tot_os); - } - -} -catch (const std::exception &e) { - std::cerr << "Program threw an exception: " << e.what() << "\n"; - throw; -} - diff --git a/opm/polymer/fullyimplicit/.PolymerPropsAd.cpp.swp b/opm/polymer/fullyimplicit/.PolymerPropsAd.cpp.swp deleted file mode 100644 index fb8319f398a8c5d5f8127f9271647d19984edd8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI3du$v>9mgkaAp{y;6)A;MmZVtc#PQ`(QuQ1goTgP>>Lx1bwb-qW%idnH@$KDq z_bzc7g-Rfxm7wL1wxSe*kkTR&5C|kd{KGT+L4t~aN=Upagv3+gDJm5~`TS<~b?3X- zP7_t2UFmbTyEF6K-+bpcvomw{-c9AHY3th2O^V~yN=<#}C%5fXW0%a_td!?A+>YE| zn?U<$5>9WV*+fVact@EUmT6-xaB{tkbGzrrc_1{{ZD z@JaXtJPsY0gfX}ro_)DeKZYN{Nw^bkg=ts=tKoO6l=>~4f$za7_!fK<9)^J#jgfcZ^ZIC z)AAvI5;8odq=f!BqCe$w&|$ZlVcRMX#f2?>Lk-Fv-_I1A9?`kWR`=hdluE{(8*tR* zuC+RQ8;+HFjW}ZOsQHpplhJLqt)^{_Tg{GFv7JUEEJT%Bygp(jwlleNhtz9$iFN8z zG^!hh7Syi0DHf}tv7NM|*Fl!C-g=jDmituv26vy+ES09K{`N@eiNcWAaq9KiO?QtV z4tl8AW*Q}O$K-PF#!A?(l}di=P=V+uxWgkY&)HWUw$^78! zf;5GTsZC>iZRf#b)`ku4uyrJTd{@=A5%MREoZ8dg9OQ4FTETV`3KPcJLU(nlA%70l z=B71{ozC=V=-%rThNB1&&(2SjM8YmNLNdh$M*dP`LrHDY=8HNx(N-$eJ59UowwknE zh5mh@YKPKtTph|ZeV6uZLy!6qz{tYVsUnFqh-%YsMIK2bF2G!ggshw z0{39G?H)w-C`;OmNm%HzCN#_`;X!}Z=b?m&KI;mVYzXLDPETk*3TeUq&79cfdG;u| zcZlQ3upsp@_R@+F{=U?xK-IFnC2Bv(h_SvMO^YEKMmHwI z`Ch5iaGOpwm}<72fWO~E&Q^10Dy&^OSZ#zAX{^eKZnKKlX|&DV5h_V?2mR@v8A%fH z7wMB(q2s=09PH_AGUQY|gpeU3d1&T|NgcborG36TWj2u4YdNx`cE9mgJFH(`qz+m3 zh#ry5u#`r6)TPM0tj?2$ds6>6Zp74pxbZq^%p~PfUkDOpo;_=X6c&q%^A`_Z$HaoT zCC7zq;IlVxzo`S!7|%?kX$Q4yJOiqj7CWM(CecJ=!F-z~GHLL6FytlAZ?I=>W) z_H=bVYfV2rZSW}gWN$p&Gc)O)BnrJ8dVc)4a z^#wE8vQ;*{XHW(iet~(1YF0fb^sBaG@s5*O;5xTyH##-P+P>sJRNwFW6H6*L*=e_S zy7l@^PQ!PCaCF%P^(>*cn2D~e2ewyj@1Ibz{y)WfZi+RctpDTR?|;Sm{tTRk zg=x4Nu7Wqgm9PeW!`l4>+yirPGfcq-Q1AoR?T!%A2I3Z7@3{w#bK?uU25 z6>u5+fi?Q?;S8LHV{i++6J8I`uueY-AAnhyguk#B{~CN19)MY};NxifQFsXU!d);6 zqWMjbAGrd#0{`O*94!{Dt`|l2r6sSgz6;&C;82APj%cl$S>&b@DkuHY^+yhtA*-NSF30h zf;rBEIoU<1)nJ0~@RjX1%AugB)2Xp-&mCX^` zH1iPgN-W{=xYcSp)&}iy@*qn^e#bNYsH)>s(g`ck)ke0phVftK@>DoA9W4(sS;sBk z{4ZUFf6Md3$|z|!YWd=gM|ogWF0zMfJ%wa7Fj>=^5{ZLl5kbP5?0mBO#jeV2PGh$d zc!Wx)6n~*6!g7?eVH<^_?r~|BD3JO`?TZ(*#($~7qVbhx zmF`I_E-}8$R_-~(6+ciQ^*MwU1=J}TiG!v?NZ`VjZ`+VFkG{{$7M-P68og}47X&BhQB-9_I|cj z)A3aQsF5~%Jc^pz^3aj!;hAw&`(}QjGLGTlrnJvylC^8ysY~6LEt~4=A3VgO9J;%m z`@l>HE?Cj0(nOmGW}M_Dks0}tH%{EElz5XiRy6)$D>L?`$g)09zbDY+RR1DmU0 Z_q0#iF8$l%1qm)bcImy~r56uM{{{4EU1b0O From d44e8e6d9fba0fc333951f16fdc5d8e6015dc361 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 28 Oct 2014 16:29:22 +0800 Subject: [PATCH 78/83] remove unnecessary referencer. --- .../SimulatorFullyImplicitBlackoilPolymer_impl.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp index acd2caad0..cd275cd3f 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp @@ -347,10 +347,9 @@ namespace Opm 0.0)); } std::vector polymer_inflow_c(Opm::UgGridHelpers::numCells(grid_)); - const PolymerInflowInterface& polymer_inflow = *polymer_inflow_ptr; - polymer_inflow.getInflowValues(timer.simulationTimeElapsed(), - timer.simulationTimeElapsed() + timer.currentStepLength(), - polymer_inflow_c); + polymer_inflow_ptr->getInflowValues(timer.simulationTimeElapsed(), + timer.simulationTimeElapsed() + timer.currentStepLength(), + polymer_inflow_c); // Output state at start of time step. if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { if (output_vtk_) { From 3102c4dab2814f13d7796321f4e1f5bdea7a144a Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Wed, 29 Oct 2014 10:25:06 +0800 Subject: [PATCH 79/83] use WellStateFullyImplicitBlackoil instead of WellState. --- .../FullyImplicitCompressiblePolymerSolver.cpp | 12 ++++++------ .../FullyImplicitCompressiblePolymerSolver.hpp | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index e5a5dfd22..35fcd5836 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -25,12 +25,12 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include @@ -197,7 +197,7 @@ namespace { FullyImplicitCompressiblePolymerSolver:: step(const double dt, PolymerBlackoilState& x , - WellState& xw, + WellStateFullyImplicitBlackoil& xw, const std::vector& polymer_inflow, std::vector& src) { @@ -306,7 +306,7 @@ namespace { FullyImplicitCompressiblePolymerSolver::SolutionState FullyImplicitCompressiblePolymerSolver::constantState(const PolymerBlackoilState& x, - const WellState& xw) + const WellStateFullyImplicitBlackoil& xw) { const int nc = grid_.number_of_cells; const int np = x.numPhases(); @@ -368,7 +368,7 @@ namespace { FullyImplicitCompressiblePolymerSolver::SolutionState FullyImplicitCompressiblePolymerSolver::variableState(const PolymerBlackoilState& x, - const WellState& xw) + const WellStateFullyImplicitBlackoil& xw) { const int nc = grid_.number_of_cells; const int np = x.numPhases(); @@ -495,7 +495,7 @@ namespace { FullyImplicitCompressiblePolymerSolver:: assemble(const double dt, const PolymerBlackoilState& x , - const WellState& xw, + const WellStateFullyImplicitBlackoil& xw, const std::vector& polymer_inflow, std::vector& src) { @@ -695,7 +695,7 @@ namespace { void FullyImplicitCompressiblePolymerSolver:: updateState(const V& dx, PolymerBlackoilState& state, - WellState& well_state) const + WellStateFullyImplicitBlackoil& well_state) const { const int np = fluid_.numPhases(); const int nc = grid_.number_of_cells; diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp index 393eabbbb..c9449d061 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp @@ -38,7 +38,7 @@ namespace Opm { class RockCompressibility; class NewtonIterationBlackoilInterface; class PolymerBlackoilState; - class WellState; + class WellStateFullyImplicitBlackoil; /// A fully implicit solver for the oil-water with polymer problem. /// @@ -83,7 +83,7 @@ namespace Opm { void step(const double dt, PolymerBlackoilState& state , - WellState& wstate, + WellStateFullyImplicitBlackoil& wstate, const std::vector& polymer_inflow, std::vector& src); @@ -146,11 +146,11 @@ namespace Opm { // Private methods. SolutionState constantState(const PolymerBlackoilState& x, - const WellState& xw); + const WellStateFullyImplicitBlackoil& xw); SolutionState variableState(const PolymerBlackoilState& x, - const WellState& xw); + const WellStateFullyImplicitBlackoil& xw); void computeAccum(const SolutionState& state, @@ -159,7 +159,7 @@ namespace Opm { void assemble(const double dt, const PolymerBlackoilState& x, - const WellState& xw, + const WellStateFullyImplicitBlackoil& xw, const std::vector& polymer_inflow, std::vector& src); @@ -167,7 +167,7 @@ namespace Opm { void updateState(const V& dx, PolymerBlackoilState& state, - WellState& well_state) const; + WellStateFullyImplicitBlackoil& well_state) const; std::vector computeRelPerm(const SolutionState& state) const; From ac764bd8a80300c2d56e6ee699f613234ada985c Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Wed, 29 Oct 2014 13:23:50 +0800 Subject: [PATCH 80/83] let sim_poly_fi2p_comp_ad works as the same way as sim_poly_fibo_ad. - move time iteration to simulator class - add EclipseWriter and output Eclipse binaries. - add DeckConstPtr for polymer inflow. --- examples/sim_poly_fi2p_comp_ad.cpp | 120 +++---- ...ulatorFullyImplicitCompressiblePolymer.cpp | 300 +++++++++--------- ...ulatorFullyImplicitCompressiblePolymer.hpp | 29 +- 3 files changed, 211 insertions(+), 238 deletions(-) diff --git a/examples/sim_poly_fi2p_comp_ad.cpp b/examples/sim_poly_fi2p_comp_ad.cpp index c41850a63..53220be7f 100644 --- a/examples/sim_poly_fi2p_comp_ad.cpp +++ b/examples/sim_poly_fi2p_comp_ad.cpp @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -58,7 +59,7 @@ #include #include -#include +#include #include #include @@ -105,45 +106,34 @@ try std::shared_ptr props; std::shared_ptr new_props; std::shared_ptr rock_comp; - Opm::DeckConstPtr deck; - EclipseStateConstPtr eclipseState; PolymerBlackoilState state; // bool check_well_controls = false; // int max_well_control_iterations = 0; double gravity[3] = { 0.0 }; std::string deck_filename = param.get("deck_filename"); - ParserPtr parser(new Opm::Parser()); - deck = parser->parseFile(deck_filename); - eclipseState.reset(new Opm::EclipseState(deck)); + + Opm::ParserPtr newParser(new Opm::Parser()); + bool strict_parsing = param.getDefault("strict_parsing", true); + Opm::DeckConstPtr deck = newParser->parseFile(deck_filename, strict_parsing); + std::shared_ptr eclipseState(new EclipseState(deck)); // Grid init std::vector porv; if (eclipseState->hasDoubleGridProperty("PORV")) { porv = eclipseState->getDoubleGridProperty("PORV")->getData(); } grid.reset(new GridManager(eclipseState->getEclipseGrid(), porv)); -// grid.reset(new GridManager(deck)); - - // use the capitalized part of the deck's filename between the - // last '/' and the last '.' character as base name. -/* - std::string baseName = deck_filename; - auto charPos = baseName.rfind('/'); - if (charPos != std::string::npos) - baseName = baseName.substr(charPos + 1); - charPos = baseName.rfind('.'); - if (charPos != std::string::npos) - baseName = baseName.substr(0, charPos); - baseName = boost::to_upper_copy(baseName); - - Opm::EclipseWriter outputWriter(param, share_obj(*deck), share_obj(*grid->c_grid())); -*/ + auto &cGrid = *grid->c_grid(); + const PhaseUsage pu = Opm::phaseUsageFromDeck(deck); + Opm::EclipseWriter outputWriter(param, + eclipseState, + pu, + cGrid.number_of_cells, + cGrid.global_cell); // Rock and fluid init - props.reset(new BlackoilPropertiesFromDeck(deck, eclipseState, *grid->c_grid())); + props.reset(new BlackoilPropertiesFromDeck(deck, eclipseState, *grid->c_grid(), param)); new_props.reset(new BlackoilPropsAdFromDeck(deck, eclipseState, *grid->c_grid())); PolymerProperties polymer_props(deck, eclipseState); PolymerPropsAd polymer_props_ad(polymer_props); - // check_well_controls = param.getDefault("check_well_controls", false); - // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); // Rock compressibility. rock_comp.reset(new RockCompressibility(deck, eclipseState)); // Gravity. @@ -182,18 +172,14 @@ try param.writeParam(output_dir + "/simulation.param"); } + Opm::TimeMapConstPtr timeMap(eclipseState->getSchedule()->getTimeMap()); + SimulatorTimer simtimer; + simtimer.init(timeMap); - std::cout << "\n\n================ Starting main simulation loop ===============\n" - << std::flush; SimulatorReport rep; // With a deck, we may have more epochs etc. WellState well_state; - int step = 0; - Opm::TimeMapPtr timeMap(new Opm::TimeMap(deck)); - SimulatorTimer simtimer; - simtimer.init(timeMap); - const double total_time = simtimer.totalTime(); // Check for WPOLYMER presence in last epoch to decide // polymer injection control type. const bool use_wpolymer = deck->hasKeyword("WPOLYMER"); @@ -203,62 +189,32 @@ try "You seem to be trying to control it via parameter poly_start_days (etc.) as well."); } } - for (size_t reportStepIdx = 0; reportStepIdx < timeMap->numTimesteps(); ++reportStepIdx) { - simtimer.setCurrentStepNum(reportStepIdx); - - // Report on start of step. - std::cout << "\n\n-------------- Starting report step " << reportStepIdx << " --------------" - << "\n (number of remaining steps: " - << simtimer.numSteps() - step << ")\n\n" << std::flush; - - // Create new wells, polymer inflow controls. - WellsManager wells(eclipseState, reportStepIdx, *grid->c_grid(), props->permeability()); - std::unique_ptr polymer_inflow; - if (use_wpolymer) { - if (wells.c_wells() == 0) { - OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells."); - } - polymer_inflow.reset(new PolymerInflowFromDeck(deck, *wells.c_wells(), props->numCells())); - } else { - polymer_inflow.reset(new PolymerInflowBasic(param.getDefault("poly_start_days", 300.0)*Opm::unit::day, - param.getDefault("poly_end_days", 800.0)*Opm::unit::day, - param.getDefault("poly_amount", polymer_props.cMax()))); - } - // @@@ HACK: we should really make a new well state and - // properly transfer old well state to it every epoch, - // since number of wells may change etc. - if (reportStepIdx == 0) { - well_state.init(wells.c_wells(), state); - } - - // Create and run simulator. - Opm::DerivedGeology geology(*grid->c_grid(), *new_props, eclipseState, grav); - SimulatorFullyImplicitCompressiblePolymer simulator(param, - *grid->c_grid(), - geology, - *new_props, - polymer_props_ad, - rock_comp->isActive() ? rock_comp.get() : 0, - wells, - *polymer_inflow, - *fis_solver, - grav); - if (reportStepIdx == 0) { - warnIfUnusedParams(param); - } - SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state); - // Update total timing report and remember step number. - rep += epoch_rep; - step = simtimer.currentStepNum(); - } + std::cout << "\n\n================ Starting main simulation loop ===============\n" + << std::flush; + SimulatorReport fullReport; + // Create and run simulator. + Opm::DerivedGeology geology(*grid->c_grid(), *new_props, eclipseState, grav); + SimulatorFullyImplicitCompressiblePolymer simulator(param, + *grid->c_grid(), + geology, + *new_props, + polymer_props_ad, + rock_comp->isActive() ? rock_comp.get() : 0, + eclipseState, + outputWriter, + deck, + *fis_solver, + grav); + fullReport= simulator.run(simtimer, state); std::cout << "\n\n================ End of simulation ===============\n\n"; - rep.report(std::cout); + fullReport.report(std::cout); if (output) { std::string filename = output_dir + "/walltime.param"; std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out); - rep.reportParam(tot_os); + fullReport.reportParam(tot_os); + warnIfUnusedParams(param); } } diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp index 6cdd93e53..0d1de549e 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp @@ -29,6 +29,8 @@ #include #include +#include + #include #include #include @@ -53,6 +55,11 @@ #include #include +#include +#include +#include +#include +#include #include #include #include @@ -90,14 +97,14 @@ namespace Opm const BlackoilPropsAdInterface& props, const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, - WellsManager& wells_manager, - PolymerInflowInterface& polymer_inflow, + std::shared_ptr eclipse_state, + EclipseWriter& output_writer, + Opm::DeckConstPtr& deck, NewtonIterationBlackoilInterface& linsolver, const double* gravity); SimulatorReport run(SimulatorTimer& timer, - PolymerBlackoilState& state, - WellState& well_state); + PolymerBlackoilState& state); private: // Data. @@ -115,13 +122,13 @@ namespace Opm const BlackoilPropsAdInterface& props_; const PolymerPropsAd& polymer_props_; const RockCompressibility* rock_comp_props_; - WellsManager& wells_manager_; - const Wells* wells_; - PolymerInflowInterface& polymer_inflow_; + std::shared_ptr eclipse_state_; + EclipseWriter& output_writer_; + Opm::DeckConstPtr& deck_; + NewtonIterationBlackoilInterface& linsolver_; const double* gravity_; // Solvers DerivedGeology geo_; - FullyImplicitCompressiblePolymerSolver solver_; // Misc. data std::vector allcells_; }; @@ -131,18 +138,19 @@ namespace Opm SimulatorFullyImplicitCompressiblePolymer:: SimulatorFullyImplicitCompressiblePolymer(const parameter::ParameterGroup& param, - const UnstructuredGrid& grid, - const DerivedGeology& geo, - const BlackoilPropsAdInterface& props, - const PolymerPropsAd& polymer_props, - const RockCompressibility* rock_comp_props, - WellsManager& wells_manager, - PolymerInflowInterface& polymer_inflow, - NewtonIterationBlackoilInterface& linsolver, - const double* gravity) + const UnstructuredGrid& grid, + const DerivedGeology& geo, + const BlackoilPropsAdInterface& props, + const PolymerPropsAd& polymer_props, + const RockCompressibility* rock_comp_props, + std::shared_ptr eclipse_state, + EclipseWriter& output_writer, + Opm::DeckConstPtr& deck, + NewtonIterationBlackoilInterface& linsolver, + const double* gravity) { - pimpl_.reset(new Impl(param, grid, geo, props, polymer_props, rock_comp_props, wells_manager, polymer_inflow, linsolver, gravity)); + pimpl_.reset(new Impl(param, grid, geo, props, polymer_props, rock_comp_props, eclipse_state, output_writer, deck, linsolver, gravity)); } @@ -150,10 +158,9 @@ namespace Opm SimulatorReport SimulatorFullyImplicitCompressiblePolymer::run(SimulatorTimer& timer, - PolymerBlackoilState& state, - WellState& well_state) + PolymerBlackoilState& state) { - return pimpl_->run(timer, state, well_state); + return pimpl_->run(timer, state); } @@ -165,27 +172,23 @@ namespace Opm const UnstructuredGrid& grid, const DerivedGeology& geo, const BlackoilPropsAdInterface& props, - const PolymerPropsAd& polymer_props, + const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, - WellsManager& wells_manager, - PolymerInflowInterface& polymer_inflow, + std::shared_ptr eclipse_state, + EclipseWriter& output_writer, + Opm::DeckConstPtr& deck, NewtonIterationBlackoilInterface& linsolver, const double* gravity) : grid_(grid), props_(props), polymer_props_(polymer_props), rock_comp_props_(rock_comp_props), - wells_manager_(wells_manager), - wells_(wells_manager.c_wells()), - polymer_inflow_(polymer_inflow), + eclipse_state_(eclipse_state), + output_writer_(output_writer), + deck_(deck), + linsolver_(linsolver), gravity_(gravity), - geo_(geo), - solver_(grid_, props_, geo_, rock_comp_props, polymer_props, *wells_manager.c_wells(), linsolver) - - /* param.getDefault("nl_pressure_residual_tolerance", 0.0), - param.getDefault("nl_pressure_change_tolerance", 1.0), - param.getDefault("nl_pressure_maxiter", 10), - gravity, */ + geo_(geo) { // For output. output_ = param.getDefault("output", true); @@ -219,10 +222,9 @@ namespace Opm SimulatorReport SimulatorFullyImplicitCompressiblePolymer::Impl::run(SimulatorTimer& timer, - PolymerBlackoilState& state, - WellState& well_state) + PolymerBlackoilState& state) { - + WellStateFullyImplicitBlackoil prev_well_state; // Initialisation. std::vector porevol; if (rock_comp_props_ && rock_comp_props_->isActive()) { @@ -241,134 +243,146 @@ namespace Opm Opm::time::StopWatch step_timer; Opm::time::StopWatch total_timer; total_timer.start(); - double tot_injected[2] = { 0.0 }; - double tot_produced[2] = { 0.0 }; - Opm::Watercut watercut; - watercut.push(0.0, 0.0, 0.0); + std::string tstep_filename = output_dir_ + "/step_timing.txt"; + std::ofstream tstep_os(tstep_filename.c_str()); + + //Main simulation loop. + while (!timer.done()) { + double tot_injected[2] = { 0.0 }; + double tot_produced[2] = { 0.0 }; + Opm::Watercut watercut; + watercut.push(0.0, 0.0, 0.0); #if 0 - // These must be changed for three-phase. - double init_surfvol[2] = { 0.0 }; - double inplace_surfvol[2] = { 0.0 }; - Opm::computeSaturatedVol(porevol, state.surfacevol(), init_surfvol); - Opm::WellReport wellreport; -#endif - std::vector fractional_flows; - std::vector well_resflows_phase; - if (wells_) { - well_resflows_phase.resize((wells_->number_of_phases)*(wells_->number_of_wells), 0.0); -#if 0 - wellreport.push(props_, *wells_, - state.pressure(), state.surfacevol(), state.saturation(), - 0.0, well_state.bhp(), well_state.perfRates()); -#endif - } - std::fstream tstep_os; - if (output_) { - std::string filename = output_dir_ + "/step_timing.param"; - tstep_os.open(filename.c_str(), std::fstream::out | std::fstream::app); - } - // Report timestep and (optionally) write state to disk. - step_timer.start(); - timer.report(std::cout); - if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { - if (output_vtk_) { - outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + std::vector fractional_flows; + std::vector well_resflows_phase; + if (wells_) { + well_resflows_phase.resize((wells_->number_of_phases)*(wells_->number_of_wells), 0.0); } - outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); - } + std::fstream tstep_os; + if (output_) { + std::string filename = output_dir_ + "/step_timing.param"; + tstep_os.open(filename.c_str(), std::fstream::out | std::fstream::app); + } +#endif + // Report timestep and (optionally) write state to disk. - SimulatorReport sreport; + step_timer.start(); + timer.report(std::cout); - bool well_control_passed = !check_well_controls_; - int well_control_iteration = 0; - do { + WellsManager wells_manager(eclipse_state_, + timer.currentStepNum(), + Opm::UgGridHelpers::numCells(grid_), + Opm::UgGridHelpers::globalCell(grid_), + Opm::UgGridHelpers::cartDims(grid_), + Opm::UgGridHelpers::dimensions(grid_), + Opm::UgGridHelpers::beginCellCentroids(grid_), + Opm::UgGridHelpers::cell2Faces(grid_), + Opm::UgGridHelpers::beginFaceCentroids(grid_), + props_.permeability()); + const Wells* wells = wells_manager.c_wells(); + WellStateFullyImplicitBlackoil well_state; + well_state.init(wells, state.blackoilState()); + if (timer.currentStepNum() != 0) { + // Transfer previous well state to current. + well_state.partialCopy(prev_well_state, *wells, prev_well_state.numWells()); + } + //Compute polymer inflow. + std::unique_ptr polymer_inflow_ptr; + if (deck_->hasKeyword("WPOLYMER")) { + if (wells_manager.c_wells() == 0) { + OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells."); + } + polymer_inflow_ptr.reset(new PolymerInflowFromDeck(deck_, *wells, Opm::UgGridHelpers::numCells(grid_))); + } else { + polymer_inflow_ptr.reset(new PolymerInflowBasic(0.0*Opm::unit::day, + 1.0*Opm::unit::day, + 0.0)); + } + std::vector polymer_inflow_c(Opm::UgGridHelpers::numCells(grid_)); + polymer_inflow_ptr->getInflowValues(timer.simulationTimeElapsed(), + timer.simulationTimeElapsed() + timer.currentStepLength(), + polymer_inflow_c); + + if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { + if (output_vtk_) { + outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); + } + outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); + } + if (output_) { + if (timer.currentStepNum() == 0) { + output_writer_.writeInit(timer); + } + output_writer_.writeTimeStep(timer, state.blackoilState(), well_state.basicWellState()); + } // Run solver. - const double current_time = timer.simulationTimeElapsed(); - double stepsize = timer.currentStepLength(); - polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c); solver_timer.start(); - std::vector initial_pressure = state.pressure(); - solver_.step(timer.currentStepLength(), state, well_state, polymer_inflow_c, transport_src); + FullyImplicitCompressiblePolymerSolver solver(grid_, props_, geo_, rock_comp_props_, polymer_props_, *wells_manager.c_wells(), linsolver_); + solver.step(timer.currentStepLength(), state, well_state, polymer_inflow_c, transport_src); // Stop timer and report. solver_timer.stop(); const double st = solver_timer.secsSinceStart(); std::cout << "Fully implicit solver took: " << st << " seconds." << std::endl; stime += st; - sreport.pressure_time = st; - - // Optionally, check if well controls are satisfied. - if (check_well_controls_) { - Opm::computePhaseFlowRatesPerWell(*wells_, - well_state.perfRates(), - fractional_flows, - well_resflows_phase); - std::cout << "Checking well conditions." << std::endl; - // For testing we set surface := reservoir - well_control_passed = wells_manager_.conditionsMet(well_state.bhp(), well_resflows_phase, well_resflows_phase); - ++well_control_iteration; - if (!well_control_passed && well_control_iteration > max_well_control_iterations_) { - OPM_THROW(std::runtime_error, "Could not satisfy well conditions in " << max_well_control_iterations_ << " tries."); - } - if (!well_control_passed) { - std::cout << "Well controls not passed, solving again." << std::endl; - } else { - std::cout << "Well conditions met." << std::endl; - } + // Update pore volumes if rock is compressible. + if (rock_comp_props_ && rock_comp_props_->isActive()) { + initial_porevol = porevol; + computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol); } - } while (!well_control_passed); - // Update pore volumes if rock is compressible. - if (rock_comp_props_ && rock_comp_props_->isActive()) { - initial_porevol = porevol; - computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol); + + double injected[2] = { 0.0 }; + double produced[2] = { 0.0 }; + double polyinj = 0; + double polyprod = 0; + + Opm::computeInjectedProduced(props_, polymer_props_, + state, + transport_src, polymer_inflow_c, timer.currentStepLength(), + injected, produced, + polyinj, polyprod); + tot_injected[0] += injected[0]; + tot_injected[1] += injected[1]; + tot_produced[0] += produced[0]; + tot_produced[1] += produced[1]; + watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(), + produced[0]/(produced[0] + produced[1]), + tot_produced[0]/tot_porevol_init); + std::cout.precision(5); + const int width = 18; + std::cout << "\nMass balance report.\n"; + std::cout << " Injected reservoir volumes: " + << std::setw(width) << injected[0] + << std::setw(width) << injected[1] << std::endl; + std::cout << " Produced reservoir volumes: " + << std::setw(width) << produced[0] + << std::setw(width) << produced[1] << std::endl; + std::cout << " Total inj reservoir volumes: " + << std::setw(width) << tot_injected[0] + << std::setw(width) << tot_injected[1] << std::endl; + std::cout << " Total prod reservoir volumes: " + << std::setw(width) << tot_produced[0] + << std::setw(width) << tot_produced[1] << std::endl; + if (output_) { + SimulatorReport step_report; + step_report.pressure_time = st; + step_report.total_time = step_timer.secsSinceStart(); + step_report.reportParam(tstep_os); + outputWaterCut(watercut, output_dir_); + } + ++timer; + prev_well_state = well_state; } - - double injected[2] = { 0.0 }; - double produced[2] = { 0.0 }; - double polyinj = 0; - double polyprod = 0; - - Opm::computeInjectedProduced(props_, polymer_props_, - state, - transport_src, polymer_inflow_c, timer.currentStepLength(), - injected, produced, - polyinj, polyprod); - tot_injected[0] += injected[0]; - tot_injected[1] += injected[1]; - tot_produced[0] += produced[0]; - tot_produced[1] += produced[1]; - watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(), - produced[0]/(produced[0] + produced[1]), - tot_produced[0]/tot_porevol_init); - std::cout.precision(5); - const int width = 18; - std::cout << "\nMass balance report.\n"; - std::cout << " Injected reservoir volumes: " - << std::setw(width) << injected[0] - << std::setw(width) << injected[1] << std::endl; - std::cout << " Produced reservoir volumes: " - << std::setw(width) << produced[0] - << std::setw(width) << produced[1] << std::endl; - std::cout << " Total inj reservoir volumes: " - << std::setw(width) << tot_injected[0] - << std::setw(width) << tot_injected[1] << std::endl; - std::cout << " Total prod reservoir volumes: " - << std::setw(width) << tot_produced[0] - << std::setw(width) << tot_produced[1] << std::endl; - sreport.total_time = step_timer.secsSinceStart(); + // Write final simulation state. if (output_) { - sreport.reportParam(tstep_os); - if (output_vtk_) { outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); } outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); - outputWaterCut(watercut, output_dir_); - tstep_os.close(); + output_writer_.writeTimeStep(timer, state.blackoilState(), prev_well_state.basicWellState()); } total_timer.stop(); - SimulatorReport report; report.pressure_time = stime; report.transport_time = 0.0; diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp index 11016a262..df868c8af 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp @@ -18,11 +18,12 @@ along with OPM. If not, see . */ -#ifndef OPM_SIMULATORFULLYIMPLICITBLACKOIL_HEADER_INCLUDED -#define OPM_SIMULATORFULLYIMPLICITBLACKOIL_HEADER_INCLUDED +#ifndef OPM_SIMULATORFULLYIMPLICITCOMPRESSIBLEPOLYMER_HEADER_INCLUDED +#define OPM_SIMULATORFULLYIMPLICITCOMPRESSIBLEPOLYMER_HEADER_INCLUDED -#include +#include #include +#include struct UnstructuredGrid; struct Wells; @@ -33,11 +34,13 @@ namespace Opm class BlackoilPropsAdInterface; class RockCompressibility; class DerivedGeology; + class WellStateFullyImplicitBlackoil; class WellsManager; + class EclipseWriter; + class EclipseState; class NewtonIterationBlackoilInterface; class SimulatorTimer; class PolymerBlackoilState; - class WellState; class PolymerPropsAd; class PolymerInflowInterface; struct SimulatorReport; @@ -66,8 +69,9 @@ namespace Opm /// \param[in] props fluid and rock properties /// \param[in] polymer_props polymer properties /// \param[in] rock_comp_props if non-null, rock compressibility properties - /// \param[in] well_manager well manager, may manage no (null) wells - /// \param[in] polymer_inflow polymer influx. + /// \param[in] eclipse_state + /// \param[in] eclipse_writer + /// \param[in] deck /// \param[in] linsolver linear solver /// \param[in] gravity if non-null, gravity vector SimulatorFullyImplicitCompressiblePolymer(const parameter::ParameterGroup& param, @@ -76,8 +80,9 @@ namespace Opm const BlackoilPropsAdInterface& props, const PolymerPropsAd& polymer_props, const RockCompressibility* rock_comp_props, - WellsManager& wells_manager, - PolymerInflowInterface& polymer_inflow, + std::shared_ptr eclipse_state, + EclipseWriter& eclipse_writer, + Opm::DeckConstPtr& deck, NewtonIterationBlackoilInterface& linsolver, const double* gravity); @@ -86,18 +91,16 @@ namespace Opm /// modify the reservoir and well states. /// \param[in,out] timer governs the requested reporting timesteps /// \param[in,out] state state of reservoir: pressure, fluxes - /// \param[in,out] well_state state of wells: bhp, perforation rates /// \return simulation report, with timing data SimulatorReport run(SimulatorTimer& timer, - PolymerBlackoilState& state, - WellState& well_state); + PolymerBlackoilState& state); private: class Impl; // Using shared_ptr instead of scoped_ptr since scoped_ptr requires complete type for Impl. - boost::shared_ptr pimpl_; + std::shared_ptr pimpl_; }; } // namespace Opm -#endif // OPM_SIMULATORFULLYIMPLICITBLACKOIL_HEADER_INCLUDED +#endif // OPM_SIMULATORFULLYIMPLICITCOMPRESSIBLEPOLYMER_HEADER_INCLUDED From 58bdc701e31f45d34d217696de8df952903b29a7 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Wed, 29 Oct 2014 13:38:15 +0800 Subject: [PATCH 81/83] since two phase polymer simulator support EclipseWriter, need to remove historical files for outputing water cut. --- ...FullyImplicitCompressiblePolymerSolver.cpp | 22 +- ...FullyImplicitCompressiblePolymerSolver.hpp | 11 +- ...ulatorFullyImplicitCompressiblePolymer.cpp | 15 +- opm/polymer/fullyimplicit/utilities.cpp | 293 ------------------ opm/polymer/fullyimplicit/utilities.hpp | 133 -------- 5 files changed, 17 insertions(+), 457 deletions(-) delete mode 100644 opm/polymer/fullyimplicit/utilities.cpp delete mode 100644 opm/polymer/fullyimplicit/utilities.hpp diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp index 35fcd5836..3cfbedb01 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp @@ -197,9 +197,8 @@ namespace { FullyImplicitCompressiblePolymerSolver:: step(const double dt, PolymerBlackoilState& x , - WellStateFullyImplicitBlackoil& xw, - const std::vector& polymer_inflow, - std::vector& src) + WellStateFullyImplicitBlackoil& xw, + const std::vector& polymer_inflow) { const SolutionState state = constantState(x, xw); @@ -209,7 +208,7 @@ namespace { const double atol = 1.0e-12; const double rtol = 5.0e-8; const int maxit = 15; - assemble(dt, x, xw, polymer_inflow, src); + assemble(dt, x, xw, polymer_inflow); const double r0 = residualNorm(); const double r_polymer = residual_.material_balance_eq[2].value().matrix().lpNorm(); @@ -223,7 +222,7 @@ namespace { const V dx = solveJacobianSystem(); updateState(dx, x, xw); - assemble(dt, x, xw, polymer_inflow, src); + assemble(dt, x, xw, polymer_inflow); const double r = residualNorm(); @@ -494,10 +493,9 @@ namespace { void FullyImplicitCompressiblePolymerSolver:: assemble(const double dt, - const PolymerBlackoilState& x , - const WellStateFullyImplicitBlackoil& xw, - const std::vector& polymer_inflow, - std::vector& src) + const PolymerBlackoilState& x, + const WellStateFullyImplicitBlackoil& xw, + const std::vector& polymer_inflow) { // Create the primary variables. // @@ -541,9 +539,6 @@ namespace { const int np = wells_.number_of_phases; const int nw = wells_.number_of_wells; const int nperf = wells_.well_connpos[nw]; - for (int i = 0; i < nc; ++i) { - src[i] = 0.0; - } const std::vector well_cells(wells_.well_cells, wells_.well_cells + nperf); const V transw = Eigen::Map(wells_.WI, nperf); @@ -621,9 +616,6 @@ namespace { well_contribs[phase] = superset(perf_flux*perf_b, well_cells, nc); // DUMP(well_contribs[phase]); residual_.material_balance_eq[phase] += well_contribs[phase]; - for (int cell = 0; cell < nc; ++cell) { - src[cell] += well_contribs[phase].value()[cell]; - } } // well rates contribs to polymer mass balance eqn. diff --git a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp index c9449d061..282241511 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp @@ -79,13 +79,11 @@ namespace Opm { /// \param[in] state reservoir state /// \param[in] wstate well state /// \param[in] polymer_inflow polymer influx - /// \param[in] src to caculate wc void step(const double dt, PolymerBlackoilState& state , - WellStateFullyImplicitBlackoil& wstate, - const std::vector& polymer_inflow, - std::vector& src); + WellStateFullyImplicitBlackoil& wstate, + const std::vector& polymer_inflow); private: typedef AutoDiffBlock ADB; @@ -159,9 +157,8 @@ namespace Opm { void assemble(const double dt, const PolymerBlackoilState& x, - const WellStateFullyImplicitBlackoil& xw, - const std::vector& polymer_inflow, - std::vector& src); + const WellStateFullyImplicitBlackoil& xw, + const std::vector& polymer_inflow); V solveJacobianSystem() const; diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp index 0d1de549e..edb555ae2 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp @@ -32,7 +32,6 @@ #include #include -#include #include #include #include @@ -232,11 +231,9 @@ namespace Opm } else { computePorevolume(grid_, props_.porosity(), porevol); } - const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); std::vector initial_porevol = porevol; std::vector polymer_inflow_c(grid_.number_of_cells); - std::vector transport_src(grid_.number_of_cells); // Main simulation loop. Opm::time::StopWatch solver_timer; double stime = 0.0; @@ -248,11 +245,11 @@ namespace Opm //Main simulation loop. while (!timer.done()) { +#if 0 double tot_injected[2] = { 0.0 }; double tot_produced[2] = { 0.0 }; Opm::Watercut watercut; watercut.push(0.0, 0.0, 0.0); -#if 0 std::vector fractional_flows; std::vector well_resflows_phase; if (wells_) { @@ -318,7 +315,7 @@ namespace Opm // Run solver. solver_timer.start(); FullyImplicitCompressiblePolymerSolver solver(grid_, props_, geo_, rock_comp_props_, polymer_props_, *wells_manager.c_wells(), linsolver_); - solver.step(timer.currentStepLength(), state, well_state, polymer_inflow_c, transport_src); + solver.step(timer.currentStepLength(), state, well_state, polymer_inflow_c); // Stop timer and report. solver_timer.stop(); const double st = solver_timer.secsSinceStart(); @@ -330,12 +327,11 @@ namespace Opm initial_porevol = porevol; computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol); } - +/* double injected[2] = { 0.0 }; double produced[2] = { 0.0 }; double polyinj = 0; double polyprod = 0; - Opm::computeInjectedProduced(props_, polymer_props_, state, transport_src, polymer_inflow_c, timer.currentStepLength(), @@ -363,12 +359,12 @@ namespace Opm std::cout << " Total prod reservoir volumes: " << std::setw(width) << tot_produced[0] << std::setw(width) << tot_produced[1] << std::endl; +*/ if (output_) { SimulatorReport step_report; step_report.pressure_time = st; step_report.total_time = step_timer.secsSinceStart(); step_report.reportParam(tstep_os); - outputWaterCut(watercut, output_dir_); } ++timer; prev_well_state = well_state; @@ -464,7 +460,7 @@ namespace Opm } } - +#if 0 static void outputWaterCut(const Opm::Watercut& watercut, const std::string& output_dir) { @@ -476,6 +472,7 @@ namespace Opm } watercut.write(os); } +#endif } } // namespace Opm diff --git a/opm/polymer/fullyimplicit/utilities.cpp b/opm/polymer/fullyimplicit/utilities.cpp deleted file mode 100644 index 24cf784a9..000000000 --- a/opm/polymer/fullyimplicit/utilities.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/* - Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL ASA. - - This file is part of the Open Porous Media project (OPM). - - OPM is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OPM is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with OPM. If not, see . -*/ - -#include -#include -#include -#include -#include -//#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace Opm -{ - - typedef AutoDiffBlock ADB; - typedef ADB::V V; - typedef ADB::M M; - typedef Eigen::Array DataBlock; - /// Compute two-phase transport source terms from well terms. - /// Note: Unlike the incompressible version of this function, - /// this version computes surface volume injection rates, - /// production rates are still total reservoir volumes. - /// \param[in] props Fluid and rock properties. - /// \param[in] wells Wells data structure. - /// \param[in] well_state Well pressures and fluxes. - /// \param[out] transport_src The transport source terms. They are to be interpreted depending on sign: - /// (+) positive inflow of first (water) phase (reservoir volume), - /// (-) negative total outflow of both phases (reservoir volume). - void computeTransportSource(const BlackoilPropsAdInterface& props, - const Wells* wells, - const WellState& well_state, - std::vector& transport_src) - { - int nc = props.numCells(); - transport_src.clear(); - transport_src.resize(nc, 0.0); - // Well contributions. - if (wells) { - const int nw = wells->number_of_wells; - const int np = wells->number_of_phases; - if (np != 2) { - OPM_THROW(std::runtime_error, "computeTransportSource() requires a 2 phase case."); - } - std::vector A(np*np); - for (int w = 0; w < nw; ++w) { - const double* comp_frac = wells->comp_frac + np*w; - for (int perf = wells->well_connpos[w]; perf < wells->well_connpos[w + 1]; ++perf) { - const int perf_cell = wells->well_cells[perf]; - double perf_rate = well_state.perfRates()[perf]; - if (perf_rate > 0.0) { - // perf_rate is a total inflow reservoir rate, we want a surface water rate. - if (wells->type[w] != INJECTOR) { - std::cout << "**** Warning: crossflow in well " - << w << " perf " << perf - wells->well_connpos[w] - << " ignored. Reservoir rate was " - << perf_rate/Opm::unit::day << " m^3/day." << std::endl; - perf_rate = 0.0; - } else { - assert(std::fabs(comp_frac[0] + comp_frac[1] - 1.0) < 1e-6); - perf_rate *= comp_frac[0]; // Water reservoir volume rate. - } - } - transport_src[perf_cell] += perf_rate; - } - } - } - } - - /// @brief Computes injected and produced volumes of all phases, - /// and injected and produced polymer mass - in the compressible case. - /// Note 1: assumes that only the first phase is injected. - /// Note 2: assumes that transport has been done with an - /// implicit method, i.e. that the current state - /// gives the mobilities used for the preceding timestep. - /// @param[in] props fluid and rock properties. - /// @param[in] polyprops polymer properties - /// @param[in] state state variables (pressure, fluxes etc.) - /// @param[in] transport_src if < 0: total reservoir volume outflow, - /// if > 0: first phase *surface volume* inflow. - /// @param[in] inj_c injected concentration by cell - /// @param[in] dt timestep used - /// @param[out] injected must point to a valid array with P elements, - /// where P = s.size()/transport_src.size(). - /// @param[out] produced must also point to a valid array with P elements. - /// @param[out] polyinj injected mass of polymer - /// @param[out] polyprod produced mass of polymer - // This function need a incompProps based on Ad. - /* - void computeInjectedProduced(const IncompPropsAdInterface& props, - const Opm::PolymerPropsAd& polymer_props, - const PolymerState& state, - const std::vector& transport_src, - const std::vector& inj_c, - const double dt, - double* injected, - double* produced, - double& polyinj, - double& polyprod) - { - const int num_cells = transport_src.size(); - if (props.numCells() != num_cells) { - OPM_THROW(std::runtime_error, "Size of transport_src vector does not match number of cells in props."); - } - const int np = props.numPhases(); - if (int(state.saturation().size()) != num_cells*np) { - OPM_THROW(std::runtime_error, "Sizes of state vectors do not match number of cells."); - } - std::vector cells(num_cells); - const V p = Eigen::Map(&state.pressure()[0], num_cells, 1); - const DataBlock s = Eigen::Map(&state.saturation()[0], num_cells, np); - const V sw = s.col(0); - const V so = s.col(1); - const V c = Eigen::Map(&state.concentration()[0], num_cells, 1); - const V cmax = Eigen::Map(&state.maxconcentration()[0], num_cells, 1); - const V trans_src = Eigen::Map(&transport_src[0], num_cells, 1); - V src = V::Constant(num_cells, -1.0); // negative is injec, positive is producer. - for (int cell = 0; cell < num_cells; ++cell) { - cells[cell] = cell; - if(transport_src[cell] > 0.0) { - src[cell] = 1.0; - } - } - const Selector src_selector(src); - const V one = V::Constant(num_cells, 1.0); - const V zero = V::Zero(num_cells); - const std::vector kr = props.relperm(sw, so, cells); - - const V krw_eff = polymer_props.effectiveRelPerm(c, cmax, kr[0]); - const double* mus = props.viscosity(); - const V inv_muw_eff = polymer_props.effectiveInvWaterVisc(c, mus); - std::vector mob(np); - mob[0] = krw_eff * inv_muw_eff; - mob[1] = kr[1] / mus[1]; - - const V watmob_c = src_selector.select(mob[0], one); - const V oilmob_c = src_selector.select(mob[1], zero); - const V flux = trans_src * dt; - const V totmob_c = watmob_c + oilmob_c; - const V wat_src = flux * (watmob_c / totmob_c); - const V oil_src = flux * (oilmob_c / totmob_c); - const V mc = polymer_props.polymerWaterVelocityRatio(c); - - polyinj = 0.0; - polyprod = 0.0; - std::fill(injected, injected + np , 0.0); - std::fill(produced, produced + np , 0.0); - for (int cell = 0; cell < num_cells; ++cell) { - if (wat_src[cell] < 0) { - injected[0] += wat_src[cell]; - polyinj += injected[0] * inj_c[cell]; - } else { - produced[0] += wat_src[cell]; - produced[1] += oil_src[cell]; - polyprod += produced[0] * mc[cell]; - } - } - } - - */ - /// @brief Computes injected and produced volumes of all phases, - /// and injected and produced polymer mass - in the compressible case. - /// Note 1: assumes that only the first phase is injected. - /// Note 2: assumes that transport has been done with an - /// implicit method, i.e. that the current state - /// gives the mobilities used for the preceding timestep. - /// @param[in] props fluid and rock properties. - /// @param[in] polyprops polymer properties - /// @param[in] state state variables (pressure, fluxes etc.) - /// @param[in] transport_src if < 0: total reservoir volume outflow, - /// if > 0: first phase *surface volume* inflow. - /// @param[in] inj_c injected concentration by cell - /// @param[in] dt timestep used - /// @param[out] injected must point to a valid array with P elements, - /// where P = s.size()/transport_src.size(). - /// @param[out] produced must also point to a valid array with P elements. - /// @param[out] polyinj injected mass of polymer - /// @param[out] polyprod produced mass of polymer - void computeInjectedProduced(const BlackoilPropsAdInterface& props, - const Opm::PolymerPropsAd& polymer_props, - const PolymerBlackoilState& state, - const std::vector& transport_src, - const std::vector& inj_c, - const double dt, - double* injected, - double* produced, - double& polyinj, - double& polyprod) - { - const int num_cells = transport_src.size(); - if (props.numCells() != num_cells) { - OPM_THROW(std::runtime_error, "Size of transport_src vector does not match number of cells in props."); - } - const int np = props.numPhases(); - if (int(state.saturation().size()) != num_cells*np) { - OPM_THROW(std::runtime_error, "Sizes of state vectors do not match number of cells."); - } - std::vector cells(num_cells); - const V p = Eigen::Map(&state.pressure()[0], num_cells, 1); - const DataBlock s = Eigen::Map(&state.saturation()[0], num_cells, np); - const V sw = s.col(0); - const V so = s.col(1); - const V c = Eigen::Map(&state.concentration()[0], num_cells, 1); - const V cmax = Eigen::Map(&state.maxconcentration()[0], num_cells, 1); - const V trans_src = Eigen::Map(&transport_src[0], num_cells, 1); - V src = V::Constant(num_cells, -1.0); // negative is injec, positive is producer. - for (int cell = 0; cell < num_cells; ++cell) { - cells[cell] = cell; - if(transport_src[cell] > 0.0) { - src[cell] = 1.0; - } - } - //Add PhasePresence make muOil() happy. - std::vector phaseCondition(num_cells); - for (int c = 0; c < num_cells; ++c) { - phaseCondition[c] = PhasePresence(); - phaseCondition[c].setFreeWater(); - phaseCondition[c].setFreeOil(); - } - const Selector src_selector(src); - const V one = V::Constant(num_cells, 1.0); - const V zero = V::Zero(num_cells); - const std::vector kr = props.relperm(sw, so, zero, cells); - const V muw = props.muWat(p, cells); - const V muo = props.muOil(p, zero, phaseCondition, cells); - const V krw_eff = polymer_props.effectiveRelPerm(c, cmax, kr[0]); - const V inv_muw_eff = polymer_props.effectiveInvWaterVisc(c, muw.data()); - std::vector mob(np); - mob[0] = krw_eff * inv_muw_eff; - mob[1] = kr[1] / muo; - - const V watmob_c = src_selector.select(mob[0], one); - const V oilmob_c = src_selector.select(mob[1], zero); - const V flux = trans_src * dt; - const V totmob_c = watmob_c + oilmob_c; - const V wat_src = flux * (watmob_c / totmob_c); - const V oil_src = flux * (oilmob_c / totmob_c); - const V mc = polymer_props.polymerWaterVelocityRatio(c); - - polyinj = 0.0; - polyprod = 0.0; - std::fill(injected, injected + np , 0.0); - std::fill(produced, produced + np , 0.0); - for (int cell = 0; cell < num_cells; ++cell) { - if (wat_src[cell] < 0) { - injected[0] += wat_src[cell]; - polyinj += injected[0] * inj_c[cell]; - } else { - produced[0] += wat_src[cell]; - produced[1] += oil_src[cell]; - polyprod += produced[0] * mc[cell]; - } - } - } - - - -} //namespace Opm diff --git a/opm/polymer/fullyimplicit/utilities.hpp b/opm/polymer/fullyimplicit/utilities.hpp deleted file mode 100644 index a1d854a33..000000000 --- a/opm/polymer/fullyimplicit/utilities.hpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - Copyright 2014 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL ASA. - - This file is part of the Open Porous Media project (OPM). - - OPM is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OPM is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with OPM. If not, see . -*/ - -#ifndef OPM_UTILITIES_HEADER_INCLUDED -#define OPM_UTILITIES_HEADER_INCLUDED - -#include -#include -#include -#include -//#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace Opm -{ - - typedef AutoDiffBlock ADB; - typedef ADB::V V; - typedef ADB::M M; - typedef Eigen::Array DataBlock; - /// Compute two-phase transport source terms from well terms. - /// Note: Unlike the incompressible version of this function, - /// this version computes surface volume injection rates, - /// production rates are still total reservoir volumes. - /// \param[in] props Fluid and rock properties. - /// \param[in] wells Wells data structure. - /// \param[in] well_state Well pressures and fluxes. - /// \param[out] transport_src The transport source terms. They are to be interpreted depending on sign: - /// (+) positive inflow of first (water) phase (reservoir volume), - /// (-) negative total outflow of both phases (reservoir volume). - void computeTransportSource(const BlackoilPropsAdInterface& props, - const Wells* wells, - const WellState& well_state, - std::vector& transport_src); - - /// @brief Computes injected and produced volumes of all phases, - /// and injected and produced polymer mass - in the compressible case. - /// Note 1: assumes that only the first phase is injected. - /// Note 2: assumes that transport has been done with an - /// implicit method, i.e. that the current state - /// gives the mobilities used for the preceding timestep. - /// @param[in] props fluid and rock properties. - /// @param[in] polyprops polymer properties - /// @param[in] state state variables (pressure, fluxes etc.) - /// @param[in] transport_src if < 0: total reservoir volume outflow, - /// if > 0: first phase *surface volume* inflow. - /// @param[in] inj_c injected concentration by cell - /// @param[in] dt timestep used - /// @param[out] injected must point to a valid array with P elements, - /// where P = s.size()/transport_src.size(). - /// @param[out] produced must also point to a valid array with P elements. - /// @param[out] polyinj injected mass of polymer - /// @param[out] polyprod produced mass of polymer - // This function need a incompProps based on Ad. - /* - void computeInjectedProduced(const IncompPropsAdInterface& props, - const Opm::PolymerPropsAd& polymer_props, - const PolymerState& state, - const std::vector& transport_src, - const std::vector& inj_c, - const double dt, - double* injected, - double* produced, - double& polyinj, - double& polyprod); - */ - /// @brief Computes injected and produced volumes of all phases, - /// and injected and produced polymer mass - in the compressible case. - /// Note 1: assumes that only the first phase is injected. - /// Note 2: assumes that transport has been done with an - /// implicit method, i.e. that the current state - /// gives the mobilities used for the preceding timestep. - /// @param[in] props fluid and rock properties. - /// @param[in] polyprops polymer properties - /// @param[in] state state variables (pressure, fluxes etc.) - /// @param[in] transport_src if < 0: total reservoir volume outflow, - /// if > 0: first phase *surface volume* inflow. - /// @param[in] inj_c injected concentration by cell - /// @param[in] dt timestep used - /// @param[out] injected must point to a valid array with P elements, - /// where P = s.size()/transport_src.size(). - /// @param[out] produced must also point to a valid array with P elements. - /// @param[out] polyinj injected mass of polymer - /// @param[out] polyprod produced mass of polymer - void computeInjectedProduced(const BlackoilPropsAdInterface& props, - const Opm::PolymerPropsAd& polymer_props, - const PolymerBlackoilState& state, - const std::vector& transport_src, - const std::vector& inj_c, - const double dt, - double* injected, - double* produced, - double& polyinj, - double& polyprod); - -} //namespace Opm - -#endif //OPM_UTILITIES_HEADER_INCLUDED From 9a5c889656015d57b23a137ff55805fef4f12758 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 14 Nov 2014 13:16:22 +0800 Subject: [PATCH 82/83] adapt to API changes of opm-autodiff. --- .../SimulatorFullyImplicitBlackoilPolymer_impl.hpp | 7 +------ .../SimulatorFullyImplicitCompressiblePolymer.cpp | 6 +----- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp index cd275cd3f..7a745ef41 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp @@ -328,12 +328,7 @@ namespace Opm props_.permeability()); const Wells* wells = wells_manager.c_wells(); WellStateFullyImplicitBlackoil well_state; - well_state.init(wells, state.blackoilState()); - if (timer.currentStepNum() != 0) { - // Transfer previous well state to current. - well_state.partialCopy(prev_well_state, *wells, prev_well_state.numWells()); - } - + well_state.init(wells, state.blackoilState(), prev_well_state); // compute polymer inflow std::unique_ptr polymer_inflow_ptr; if (deck_->hasKeyword("WPOLYMER")) { diff --git a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp index edb555ae2..f4d35ed51 100644 --- a/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp +++ b/opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.cpp @@ -278,11 +278,7 @@ namespace Opm props_.permeability()); const Wells* wells = wells_manager.c_wells(); WellStateFullyImplicitBlackoil well_state; - well_state.init(wells, state.blackoilState()); - if (timer.currentStepNum() != 0) { - // Transfer previous well state to current. - well_state.partialCopy(prev_well_state, *wells, prev_well_state.numWells()); - } + well_state.init(wells, state.blackoilState(), prev_well_state); //Compute polymer inflow. std::unique_ptr polymer_inflow_ptr; if (deck_->hasKeyword("WPOLYMER")) { From 4ce5661d9ebf8a6b25eb3fa96b69d0857aed54c7 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Mon, 17 Nov 2014 17:23:04 +0800 Subject: [PATCH 83/83] compute cmax every time step. --- .../fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp index 3c7f8c8e1..100040572 100644 --- a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp +++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp @@ -279,6 +279,7 @@ namespace { { const SolutionState state = constantState(x, xw); + computeCmax(x, state.concentration); computeAccum(state, 0); computeWellConnectionPressures(state, xw); }