From 0eb54ca90a6a47b81b44fd01e8ce26ae036f77f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Mon, 1 Oct 2012 16:39:35 +0200 Subject: [PATCH 1/7] Added perfPress() to WellState. --- opm/core/simulator/WellState.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/opm/core/simulator/WellState.hpp b/opm/core/simulator/WellState.hpp index 7ddc886e27..981ccd9bab 100644 --- a/opm/core/simulator/WellState.hpp +++ b/opm/core/simulator/WellState.hpp @@ -49,7 +49,8 @@ namespace Opm bhp_[w] = state.pressure()[cell]; } } - perfrates_.resize(wells->well_connpos[nw]); + perfrates_.resize(wells->well_connpos[nw], 0.0); + perfpress_.resize(wells->well_connpos[nw], -1e100); } } @@ -61,9 +62,14 @@ namespace Opm 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 perfrates_; + std::vector perfpress_; }; } // namespace Opm From 6fb248d403ef80a1bbd15a81f5a83c3dcf95030c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Mon, 1 Oct 2012 16:40:10 +0200 Subject: [PATCH 2/7] Update WellState::perfPress() after pressure solve. --- opm/core/pressure/CompressibleTpfa.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/opm/core/pressure/CompressibleTpfa.cpp b/opm/core/pressure/CompressibleTpfa.cpp index 42b3fba75f..8edb41a0d3 100644 --- a/opm/core/pressure/CompressibleTpfa.cpp +++ b/opm/core/pressure/CompressibleTpfa.cpp @@ -632,6 +632,23 @@ namespace Opm &state.pressure()[0], &state.faceflux()[0], &state.facepressure()[0]); + + // Compute well perforation pressures (not done by the C code). + if (wells_ != 0) { + const int nw = wells_->number_of_wells; + const int np = props_.numPhases(); + for (int w = 0; w < nw; ++w) { + const double* comp_frac = &wells_->comp_frac[np*w]; + for (int j = wells_->well_connpos[w]; j < wells_->well_connpos[w+1]; ++j) { + const double bhp = well_state.bhp()[w]; + double perf_p = bhp; + for (int phase = 0; phase < np; ++phase) { + perf_p += wellperf_gpot_[np*j + phase]*comp_frac[phase]; + } + well_state.perfPress()[j] = perf_p; + } + } + } } } // namespace Opm From 17c1be65418acd6959ecd9f0ca3cfd3c3d876a3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Mon, 1 Oct 2012 16:40:47 +0200 Subject: [PATCH 3/7] Modified functions dealing with transport source. In preparation for switching to new convention for inflow sources in the compressible case: source being surface volumes, not reservoir volumes. --- opm/core/utility/miscUtilitiesBlackoil.cpp | 78 +++++++++++++++++++++- opm/core/utility/miscUtilitiesBlackoil.hpp | 19 +++++- 2 files changed, 93 insertions(+), 4 deletions(-) diff --git a/opm/core/utility/miscUtilitiesBlackoil.cpp b/opm/core/utility/miscUtilitiesBlackoil.cpp index 6f4f75565f..b4877e7e26 100644 --- a/opm/core/utility/miscUtilitiesBlackoil.cpp +++ b/opm/core/utility/miscUtilitiesBlackoil.cpp @@ -21,7 +21,10 @@ #include #include #include +#include #include +#include +#include #include #include #include @@ -31,16 +34,20 @@ namespace Opm { - /// @brief Computes injected and produced volumes of all phases. + /// @brief Computes injected and produced surface volumes of all phases. /// Note 1: assumes that only the first phase is injected. /// Note 2: assumes that transport has been done with an /// implicit method, i.e. that the current state /// gives the mobilities used for the preceding timestep. + /// Note 3: Gives surface volume values, not reservoir volumes + /// (as the incompressible version of the function does). + /// Also, assumes that src is given in surface volumes + /// for injector terms! /// @param[in] props fluid and rock properties. /// @param[in] p pressure (one value per cell) /// @param[in] z surface-volume values (for all P phases) /// @param[in] s saturation values (for all P phases) - /// @param[in] src if < 0: total outflow, if > 0: first phase inflow. + /// @param[in] src if < 0: total outflow, if > 0: first phase inflow /// @param[in] dt timestep used /// @param[out] injected must point to a valid array with P elements, /// where P = s.size()/src.size(). @@ -63,6 +70,9 @@ namespace Opm std::fill(produced, produced + np, 0.0); std::vector visc(np); std::vector mob(np); + std::vector A(np*np); + std::vector prod_resv_phase(np); + std::vector prod_surfvol(np); for (int c = 0; c < num_cells; ++c) { if (src[c] > 0.0) { injected[0] += src[c]*dt; @@ -71,13 +81,21 @@ namespace Opm const double* sat = &s[np*c]; props.relperm(1, sat, &c, &mob[0], 0); props.viscosity(1, &press[c], &z[np*c], &c, &visc[0], 0); + props.matrix(1, &press[c], &z[np*c], &c, &A[0], 0); double totmob = 0.0; for (int p = 0; p < np; ++p) { mob[p] /= visc[p]; totmob += mob[p]; } + std::fill(prod_surfvol.begin(), prod_surfvol.end(), 0.0); for (int p = 0; p < np; ++p) { - produced[p] += (mob[p]/totmob)*flux; + prod_resv_phase[p] = (mob[p]/totmob)*flux; + for (int q = 0; q < np; ++q) { + prod_surfvol[q] += prod_resv_phase[p]*A[q + np*p]; + } + } + for (int p = 0; p < np; ++p) { + produced[p] += prod_surfvol[p]; } } } @@ -251,4 +269,58 @@ namespace Opm } } + + /// Compute two-phase transport source terms from well terms. + /// Note: Unlike the incompressible version of this function, + /// this version computes surface volume injection rates, + /// production rates are still total reservoir volumes. + /// \param[in] props Fluid and rock properties. + /// \param[in] wells Wells data structure. + /// \param[in] well_state Well pressures and fluxes. + /// \param[out] transport_src The transport source terms. They are to be interpreted depending on sign: + /// (+) positive inflow of first (water) phase (surface volume), + /// (-) negative total outflow of both phases (reservoir volume). + void computeTransportSource(const BlackoilPropertiesInterface& props, + const Wells* wells, + const WellState& well_state, + std::vector& transport_src) + { + int nc = props.numCells(); + transport_src.clear(); + transport_src.resize(nc, 0.0); + // Well contributions. + if (wells) { + const int nw = wells->number_of_wells; + const int np = wells->number_of_phases; + if (np != 2) { + THROW("computeTransportSource() requires a 2 phase case."); + } + std::vector A(np, np); + for (int w = 0; w < nw; ++w) { + const double* comp_frac = wells->comp_frac + np*w; + for (int perf = wells->well_connpos[w]; perf < wells->well_connpos[w + 1]; ++perf) { + const int perf_cell = wells->well_cells[perf]; + double perf_rate = well_state.perfRates()[perf]; + if (perf_rate > 0.0) { + // perf_rate is a total inflow reservoir rate, we want a surface water rate. + if (wells->type[w] != INJECTOR) { + std::cout << "**** Warning: crossflow in well " + << w << " perf " << perf - wells->well_connpos[w] + << " ignored. Reservoir rate was " + << perf_rate/Opm::unit::day << " m^3/day." << std::endl; + perf_rate = 0.0; + } else { + ASSERT(std::fabs(comp_frac[0] + comp_frac[1] - 1.0) < 1e-6); + perf_rate *= comp_frac[0]; // Water reservoir volume rate. + props.matrix(1, &well_state.perfPress()[perf], comp_frac, &perf_cell, &A[0], 0); + perf_rate *= A[0]; // Water surface volume rate. + } + } + transport_src[perf_cell] += perf_rate; + } + } + } + } + + } // namespace Opm diff --git a/opm/core/utility/miscUtilitiesBlackoil.hpp b/opm/core/utility/miscUtilitiesBlackoil.hpp index 565acc6ef9..0225787acf 100644 --- a/opm/core/utility/miscUtilitiesBlackoil.hpp +++ b/opm/core/utility/miscUtilitiesBlackoil.hpp @@ -22,12 +22,13 @@ #include -struct UnstructuredGrid; +struct Wells; namespace Opm { class BlackoilPropertiesInterface; + class WellState; /// @brief Computes injected and produced volumes of all phases. /// Note 1: assumes that only the first phase is injected. @@ -131,6 +132,22 @@ namespace Opm const double* saturation, double* surfacevol); + + /// Compute two-phase transport source terms from well terms. + /// Note: Unlike the incompressible version of this function, + /// this version computes surface volume injection rates, + /// production rates are still total reservoir volumes. + /// \param[in] props Fluid and rock properties. + /// \param[in] wells Wells data structure. + /// \param[in] well_state Well pressures and fluxes. + /// \param[out] transport_src The transport source terms. They are to be interpreted depending on sign: + /// (+) positive inflow of first (water) phase (surface volume), + /// (-) negative total outflow of both phases (reservoir volume). + void computeTransportSource(const BlackoilPropertiesInterface& props, + const Wells* wells, + const WellState& well_state, + std::vector& transport_src); + } // namespace Opm #endif // OPM_MISCUTILITIESBLACKOIL_HEADER_INCLUDED From 017663cc5b0f84b91edcd34ed6aca55010b08317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Tue, 2 Oct 2012 11:12:23 +0200 Subject: [PATCH 4/7] Change interface for (blackoil) computeInjectedProduced(). Also use new computeInjectedProduced() and computeTransportSource() functions in SimulatorCompressibleTwophase. --- opm/core/utility/miscUtilitiesBlackoil.cpp | 46 +++++++++++----------- opm/core/utility/miscUtilitiesBlackoil.hpp | 32 ++++++++------- 2 files changed, 42 insertions(+), 36 deletions(-) diff --git a/opm/core/utility/miscUtilitiesBlackoil.cpp b/opm/core/utility/miscUtilitiesBlackoil.cpp index b4877e7e26..4f49cd6aa5 100644 --- a/opm/core/utility/miscUtilitiesBlackoil.cpp +++ b/opm/core/utility/miscUtilitiesBlackoil.cpp @@ -41,31 +41,33 @@ namespace Opm /// gives the mobilities used for the preceding timestep. /// Note 3: Gives surface volume values, not reservoir volumes /// (as the incompressible version of the function does). - /// Also, assumes that src is given in surface volumes + /// Also, assumes that transport_src is given in surface volumes /// for injector terms! - /// @param[in] props fluid and rock properties. - /// @param[in] p pressure (one value per cell) - /// @param[in] z surface-volume values (for all P phases) - /// @param[in] s saturation values (for all P phases) - /// @param[in] src if < 0: total outflow, if > 0: first phase inflow - /// @param[in] dt timestep used - /// @param[out] injected must point to a valid array with P elements, - /// where P = s.size()/src.size(). - /// @param[out] produced must also point to a valid array with P elements. + /// @param[in] props fluid and rock properties. + /// @param[in] state state variables (pressure, sat, surfvol) + /// @param[in] transport_src if < 0: total resv outflow, if > 0: first phase surfv inflow + /// @param[in] dt timestep used + /// @param[out] injected must point to a valid array with P elements, + /// where P = s.size()/src.size(). + /// @param[out] produced must also point to a valid array with P elements. void computeInjectedProduced(const BlackoilPropertiesInterface& props, - const std::vector& press, - const std::vector& z, - const std::vector& s, - const std::vector& src, + const BlackoilState& state, + const std::vector& transport_src, const double dt, double* injected, double* produced) { - const int num_cells = src.size(); - const int np = s.size()/src.size(); - if (int(s.size()) != num_cells*np) { - THROW("Sizes of s and src vectors do not match."); + const int num_cells = transport_src.size(); + if (props.numCells() != num_cells) { + THROW("Size of transport_src vector does not match number of cells in props."); } + const int np = props.numPhases(); + if (int(state.saturation().size()) != num_cells*np) { + THROW("Sizes of state vectors do not match number of cells."); + } + const std::vector& press = state.pressure(); + const std::vector& s = state.saturation(); + const std::vector& z = state.surfacevol(); std::fill(injected, injected + np, 0.0); std::fill(produced, produced + np, 0.0); std::vector visc(np); @@ -74,10 +76,10 @@ namespace Opm std::vector prod_resv_phase(np); std::vector prod_surfvol(np); for (int c = 0; c < num_cells; ++c) { - if (src[c] > 0.0) { - injected[0] += src[c]*dt; - } else if (src[c] < 0.0) { - const double flux = -src[c]*dt; + if (transport_src[c] > 0.0) { + injected[0] += transport_src[c]*dt; + } else if (transport_src[c] < 0.0) { + const double flux = -transport_src[c]*dt; const double* sat = &s[np*c]; props.relperm(1, sat, &c, &mob[0], 0); props.viscosity(1, &press[c], &z[np*c], &c, &visc[0], 0); diff --git a/opm/core/utility/miscUtilitiesBlackoil.hpp b/opm/core/utility/miscUtilitiesBlackoil.hpp index 0225787acf..f4949ce2db 100644 --- a/opm/core/utility/miscUtilitiesBlackoil.hpp +++ b/opm/core/utility/miscUtilitiesBlackoil.hpp @@ -28,31 +28,34 @@ namespace Opm { class BlackoilPropertiesInterface; + class BlackoilState; class WellState; - /// @brief Computes injected and produced volumes of all phases. + + /// @brief Computes injected and produced surface volumes of all phases. /// Note 1: assumes that only the first phase is injected. /// Note 2: assumes that transport has been done with an /// implicit method, i.e. that the current state /// gives the mobilities used for the preceding timestep. - /// @param[in] props fluid and rock properties. - /// @param[in] p pressure (one value per cell) - /// @param[in] z surface-volume values (for all P phases) - /// @param[in] s saturation values (for all P phases) - /// @param[in] src if < 0: total outflow, if > 0: first phase inflow. - /// @param[in] dt timestep used - /// @param[out] injected must point to a valid array with P elements, - /// where P = s.size()/src.size(). - /// @param[out] produced must also point to a valid array with P elements. + /// Note 3: Gives surface volume values, not reservoir volumes + /// (as the incompressible version of the function does). + /// Also, assumes that transport_src is given in surface volumes + /// for injector terms! + /// @param[in] props fluid and rock properties. + /// @param[in] state state variables (pressure, sat, surfvol) + /// @param[in] transport_src if < 0: total resv outflow, if > 0: first phase surfv inflow + /// @param[in] dt timestep used + /// @param[out] injected must point to a valid array with P elements, + /// where P = s.size()/src.size(). + /// @param[out] produced must also point to a valid array with P elements. void computeInjectedProduced(const BlackoilPropertiesInterface& props, - const std::vector& p, - const std::vector& z, - const std::vector& s, - const std::vector& src, + const BlackoilState& state, + const std::vector& transport_src, const double dt, double* injected, double* produced); + /// @brief Computes total mobility for a set of saturation values. /// @param[in] props rock and fluid properties /// @param[in] cells cells with which the saturation values are associated @@ -67,6 +70,7 @@ namespace Opm const std::vector& s, std::vector& totmob); + /// @brief Computes total mobility and omega for a set of saturation values. /// @param[in] props rock and fluid properties /// @param[in] cells cells with which the saturation values are associated From 4f276a887056ddf22fd1264f0717a9c9b96061a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Tue, 2 Oct 2012 14:35:28 +0200 Subject: [PATCH 5/7] Bugfix: size of vector for A should be np*np. --- opm/core/utility/miscUtilitiesBlackoil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/core/utility/miscUtilitiesBlackoil.cpp b/opm/core/utility/miscUtilitiesBlackoil.cpp index 4f49cd6aa5..62f1799327 100644 --- a/opm/core/utility/miscUtilitiesBlackoil.cpp +++ b/opm/core/utility/miscUtilitiesBlackoil.cpp @@ -297,7 +297,7 @@ namespace Opm if (np != 2) { THROW("computeTransportSource() requires a 2 phase case."); } - std::vector A(np, np); + 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) { From 5acbf00b14b4dfb781b509ceac5d75bfa7a84dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Tue, 2 Oct 2012 14:37:18 +0200 Subject: [PATCH 6/7] Update compressible transport solver for new src convention. Namely, that inflowing transport sources are water *surface* volumes, not water *reservoir* volumes. --- .../transport/reorder/TransportModelCompressibleTwophase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/core/transport/reorder/TransportModelCompressibleTwophase.cpp b/opm/core/transport/reorder/TransportModelCompressibleTwophase.cpp index 7b4a72e688..bad7126303 100644 --- a/opm/core/transport/reorder/TransportModelCompressibleTwophase.cpp +++ b/opm/core/transport/reorder/TransportModelCompressibleTwophase.cpp @@ -152,7 +152,7 @@ namespace Opm B_cell = 1.0/tm.A_[np*np*cell + 0]; double src_flux = -tm.source_[cell]; bool src_is_inflow = src_flux < 0.0; - influx = src_is_inflow ? src_flux : 0.0; + influx = src_is_inflow ? B_cell* src_flux : 0.0; outflux = !src_is_inflow ? src_flux : 0.0; comp_term = (tm.porevolume_[cell] - tm.porevolume0_[cell])/tm.porevolume0_[cell]; dtpv = tm.dt_/tm.porevolume0_[cell]; From a9783eefc7e22cc77eb88cd13d11e8346c47e819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Tue, 2 Oct 2012 15:46:33 +0200 Subject: [PATCH 7/7] Add explanatory comment. --- opm/core/utility/miscUtilitiesBlackoil.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/opm/core/utility/miscUtilitiesBlackoil.cpp b/opm/core/utility/miscUtilitiesBlackoil.cpp index 62f1799327..ae806e9a48 100644 --- a/opm/core/utility/miscUtilitiesBlackoil.cpp +++ b/opm/core/utility/miscUtilitiesBlackoil.cpp @@ -77,8 +77,12 @@ namespace Opm std::vector prod_surfvol(np); for (int c = 0; c < num_cells; ++c) { if (transport_src[c] > 0.0) { + // Inflowing transport source is a surface volume flux + // for the first phase. injected[0] += transport_src[c]*dt; } else if (transport_src[c] < 0.0) { + // Outflowing transport source is a total reservoir + // volume flux. const double flux = -transport_src[c]*dt; const double* sat = &s[np*c]; props.relperm(1, sat, &c, &mob[0], 0);