Merge branch 'Adding_PLYSHLOG_RELATED' into Adding_SHRATE_RELATED

Conflicts:
	opm/polymer/PolymerProperties.hpp
This commit is contained in:
Kai Bao 2015-06-23 10:27:25 +02:00
commit d2602cc73f
10 changed files with 321 additions and 502 deletions

102
opm/polymer/Point2D.hpp Normal file
View File

@ -0,0 +1,102 @@
/*
Copyright 2015 SINTEF ICT, Applied Mathematics.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_POINT2D_HEADER_INCLUDED
#define OPM_POINT2D_HEADER_INCLUDED
namespace Opm {
namespace detail {
class Point2D
{
public:
Point2D(const double xi, const double yi)
: x_(xi),
y_(yi) {
}
Point2D()
{
}
const double getX() const
{
return x_;
}
const double getY() const
{
return y_;
}
void setX(const double x)
{
x_ = x;
}
void setY(const double y){
y_ = y;
}
/// Finding the intersection point of a line segment and a line.
/// return true, if found.
static bool findIntersection(Point2D line_segment1[2], Point2D line2[2], Point2D& intersection_point)
{
const double x1 = line_segment1[0].getX();
const double y1 = line_segment1[0].getY();
const double x2 = line_segment1[1].getX();
const double y2 = line_segment1[1].getY();
const double x3 = line2[0].getX();
const double y3 = line2[0].getY();
const double x4 = line2[1].getX();
const double y4 = line2[1].getY();
const double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if (d == 0.) {
return false;
}
const double x = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
const double y = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
if (x >= std::min(x1,x2) && x <= std::max(x1,x2)) {
intersection_point.setX(x);
intersection_point.setY(y);
return true;
} else {
return false;
}
}
private:
double x_;
double y_;
}; // class Point2D
} // namespace detail
} // namespace Opm
#endif // OPM_POINT2D_HEADER_INCLUDED

View File

@ -20,6 +20,7 @@
#include <config.h>
#include <opm/polymer/PolymerProperties.hpp>
#include <opm/polymer/Point2D.hpp>
#include <cmath>
#include <vector>
#include <opm/core/utility/linearInterpolation.hpp>
@ -424,4 +425,98 @@ namespace Opm
dmc_dc = 0.;
}
}
bool PolymerProperties::computeShearMultLog(std::vector<double>& water_vel, std::vector<double>& visc_mult, std::vector<double>& shear_mult) const
{
double refConcentration = plyshlogRefConc();
double refViscMult = viscMult(refConcentration);
std::vector<double> shear_water_vel = shearWaterVelocity();
std::vector<double> shear_vrf = shearViscosityReductionFactor();
std::vector<double> logShearWaterVel;
std::vector<double> logShearVRF;
logShearWaterVel.resize(shear_water_vel.size());
logShearVRF.resize(shear_water_vel.size());
// converting the table using the reference condition
for (size_t i = 0; i < shear_vrf.size(); ++i) {
shear_vrf[i] = (refViscMult * shear_vrf[i] - 1.) / (refViscMult - 1);
logShearWaterVel[i] = std::log(shear_water_vel[i]);
}
shear_mult.resize(water_vel.size());
// the mimum velocity to apply the shear-thinning
const double minShearVel = shear_water_vel[0];
const double maxShearVel = shear_water_vel.back();
const double epsilon = std::sqrt(std::numeric_limits<double>::epsilon());
for (size_t i = 0; i < water_vel.size(); ++i) {
if (visc_mult[i] - 1. < epsilon || std::abs(water_vel[i]) < minShearVel) {
shear_mult[i] = 1.0;
continue;
}
for (size_t j = 0; j < shear_vrf.size(); ++j) {
logShearVRF[j] = (1 + (visc_mult[i] - 1.0) * shear_vrf[j]) / visc_mult[i];
logShearVRF[j] = std::log(logShearVRF[j]);
}
// const double logWaterVelO = std::log(water_vel[i]);
const double logWaterVelO = std::log(std::abs(water_vel[i]));
size_t iIntersection; // finding the intersection on the iIntersectionth table segment
bool foundSegment = false;
for (iIntersection = 0; iIntersection < shear_vrf.size() - 1; ++iIntersection) {
double temp1 = logShearVRF[iIntersection] + logShearWaterVel[iIntersection] - logWaterVelO;
double temp2 = logShearVRF[iIntersection + 1] + logShearWaterVel[iIntersection + 1] - logWaterVelO;
// ignore the cases the temp1 or temp2 is zero first for simplicity.
// several more complicated cases remain to be implemented.
if( temp1 * temp2 < 0.){
foundSegment = true;
break;
}
}
if (foundSegment == true) {
detail::Point2D lineSegment[2];
lineSegment[0] = detail::Point2D{logShearWaterVel[iIntersection], logShearVRF[iIntersection]};
lineSegment[1] = detail::Point2D{logShearWaterVel[iIntersection + 1], logShearVRF[iIntersection + 1]};
detail::Point2D line[2];
line[0] = detail::Point2D{0, logWaterVelO};
line[1] = detail::Point2D{logWaterVelO, 0};
detail::Point2D intersectionPoint;
bool foundIntersection = detail::Point2D::findIntersection(lineSegment, line, intersectionPoint);
if (foundIntersection) {
shear_mult[i] = std::exp(intersectionPoint.getY());
} else {
std::cerr << " failed in finding the solution for shear-thinning multiplier " << std::endl;
return false; // failed in finding the solution.
}
} else {
if (std::abs(water_vel[i]) < maxShearVel) {
std::cout << " the veclocity is " << water_vel[i] << std::endl;
std::cout << " max shear velocity is " << maxShearVel << std::endl;
std::cerr << " something wrong happend in finding segment" << std::endl;
return false;
} else {
shear_mult[i] = std::exp(logShearVRF.back());
}
}
}
return true;
}
}

View File

@ -190,14 +190,14 @@ namespace Opm
plyshlog_ref_conc_ = plyshlogTable.getRefPolymerConcentration();
if(plyshlogTable.hasRefSalinity()) {
if (plyshlogTable.hasRefSalinity()) {
has_plyshlog_ref_salinity_ = true;
plyshlog_ref_salinity_ = plyshlogTable.getRefSalinity();
} else {
has_plyshlog_ref_salinity_ = false;
}
if(plyshlogTable.hasRefTemperature()) {
if (plyshlogTable.hasRefTemperature()) {
has_plyshlog_ref_temp_ = true;
plyshlog_ref_temp_ = plyshlogTable.getRefTemperature();
} else {
@ -351,6 +351,9 @@ namespace Opm
void computeMcBoth(const double& c, double& mc,
double& dmc_dc, bool if_with_der) const;
/// Computing the shear multiplier based on the water velocity/shear rate with PLYSHLOG keyword
bool computeShearMultLog(std::vector<double>& water_vel, std::vector<double>& visc_mult, std::vector<double>& shear_mult) const;
private:
double c_max_;
double mix_param_;
@ -396,6 +399,7 @@ namespace Opm
double& deff_relperm_wat_ds,
double& deff_relperm_wat_dc,
bool if_with_der) const;
};
} // namespace Opm

View File

@ -203,7 +203,6 @@ namespace Opm {
using Base::drMaxRel;
using Base::maxResidualAllowed;
// using Base::addWellEq;
using Base::updateWellControls;
using Base::computeWellConnectionPressures;
using Base::addWellControlEq;
@ -233,17 +232,9 @@ namespace Opm {
assembleMassBalanceEq(const SolutionState& state);
void
addWellEq(const SolutionState& state,
WellState& xw,
V& aliveWells);
void
extraAddWellEq(const SolutionState& state,
const WellState& xw,
const std::vector<ADB>& cq_ps,
const std::vector<ADB>& cmix_s,
const ADB& cqt_is,
const std::vector<int>& well_cells);
addWellContributionToMassBalanceEq(const std::vector<ADB>& cq_s,
const SolutionState& state,
WellState& xw);
void
computeMassFlux(const int actph ,
@ -291,28 +282,16 @@ namespace Opm {
int nc,
int nw) const;
struct Point2D {
double x;
double y;
};
/// Finding the intersection point of a line segment and a line.
/// return true, if found.
bool findIntersection (Point2D line_segment1[2], Point2D line2[2], Point2D& intersection_point);
/// Computing the shear multiplier based on the water velocity/shear rate with PLYSHLOG keyword
bool computeShearMultLog( std::vector<double>& water_vel, std::vector<double>& visc_mult, std::vector<double>& shear_mult);
/// Computing the water velocity without shear-thinning for the cell faces.
/// The water velocity will be used for shear-thinning calculation.
void computeWaterShearVelocityFaces(const V& transi, const std::vector<ADB>& kr,
const std::vector<ADB>& phasePressure, const SolutionState& state,
std::vector<double>& water_vel, std::vector<double>& visc_mult);
void computeWaterShearVelocityWells(const SolutionState& state, WellState& well_state,
V& aliveWells, std::vector<double>& water_vel_wells, std::vector<double>& visc_mult_wells);
/// Computing the water velocity without shear-thinning for the well perforations based on the water flux rate.
/// The water velocity will be used for shear-thinning calculation.
void computeWaterShearVelocityWells(const SolutionState& state, WellState& xw, const ADB& cq_sw,
std::vector<double>& water_vel_wells, std::vector<double>& visc_mult_wells);
};

View File

@ -291,7 +291,7 @@ namespace Opm {
std::vector<double> visc_mult;
computeWaterShearVelocityFaces(transi, kr, state.canonical_phase_pressures, state, water_vel, visc_mult);
if(!computeShearMultLog(water_vel, visc_mult, shear_mult_faces_)) {
if ( !polymer_props_ad_.computeShearMultLog(water_vel, visc_mult, shear_mult_faces_) ) {
// std::cerr << " failed in calculating the shear-multiplier " << std::endl;
OPM_THROW(std::runtime_error, " failed in calculating the shear-multiplier. ");
}
@ -341,23 +341,26 @@ namespace Opm {
template <class Grid>
void BlackoilPolymerModel<Grid>::extraAddWellEq(const SolutionState& state,
const WellState& xw,
const std::vector<ADB>& cq_ps,
const std::vector<ADB>& cmix_s,
const ADB& cqt_is,
const std::vector<int>& well_cells)
void BlackoilPolymerModel<Grid>::addWellContributionToMassBalanceEq(const std::vector<ADB>& cq_s,
const SolutionState& state,
WellState& xw)
{
Base::addWellContributionToMassBalanceEq(cq_s, state, xw);
// Add well contributions to polymer mass balance equation
if (has_polymer_) {
const ADB mc = computeMc(state);
const int nc = xw.polymerInflow().size();
const V polyin = Eigen::Map<const V>(xw.polymerInflow().data(), nc);
const int nperf = wells().well_connpos[wells().number_of_wells];
const std::vector<int> well_cells(wells().well_cells, wells().well_cells + nperf);
const V poly_in_perf = subset(polyin, well_cells);
const V poly_mc_perf = subset(mc, well_cells).value();
const PhaseUsage& pu = fluid_.phaseUsage();
const ADB cq_s_poly = cq_ps[pu.phase_pos[Water]] * poly_mc_perf
+ cmix_s[pu.phase_pos[Water]] * cqt_is * poly_in_perf;
const V poly_mc_perf = subset(mc.value(), well_cells);
const ADB& cq_s_water = cq_s[fluid_.phaseUsage().phase_pos[Water]];
Selector<double> injector_selector(cq_s_water.value());
const V poly_perf = injector_selector.select(poly_in_perf, poly_mc_perf);
const ADB cq_s_poly = cq_s_water * poly_perf;
residual_.material_balance_eq[poly_pos_] -= superset(cq_s_poly, well_cells, nc);
}
}
@ -573,30 +576,53 @@ namespace Opm {
assembleMassBalanceEq(state);
// -------- Well equations ----------
if ( ! wellsActive() ) {
return;
}
V aliveWells;
const int np = wells().number_of_phases;
std::vector<ADB> cq_s(np, ADB::null());
const int nw = wells().number_of_wells;
const int nperf = wells().well_connpos[nw];
const std::vector<int> well_cells(wells().well_cells, wells().well_cells + nperf);
std::vector<ADB> mob_perfcells(np, ADB::null());
std::vector<ADB> b_perfcells(np, ADB::null());
for (int phase = 0; phase < np; ++phase) {
mob_perfcells[phase] = subset(rq_[phase].mob, well_cells);
b_perfcells[phase] = subset(rq_[phase].b, well_cells);
}
if (param_.solve_welleq_initially_ && initial_assembly) {
// solve the well equations as a pre-processing step
Base::solveWellEq(mob_perfcells, b_perfcells, state, well_state);
}
Base::computeWellFlux(state, mob_perfcells, b_perfcells, aliveWells, cq_s);
if (has_plyshlog_) {
std::vector<double> water_vel_wells;
std::vector<double> visc_mult_wells;
computeWaterShearVelocityWells(state, well_state, aliveWells, water_vel_wells, visc_mult_wells);
const int water_pos = fluid_.phaseUsage().phase_pos[Water];
computeWaterShearVelocityWells(state, well_state, cq_s[water_pos], water_vel_wells, visc_mult_wells);
if (!computeShearMultLog(water_vel_wells, visc_mult_wells, shear_mult_wells_)) {
// std::cout << " failed in calculating the shear factors for wells " << std::endl;
if ( !polymer_props_ad_.computeShearMultLog(water_vel_wells, visc_mult_wells, shear_mult_wells_) ) {
OPM_THROW(std::runtime_error, " failed in calculating the shear factors for wells ");
}
/* const int nw = wells().number_of_wells;
const int nperf = wells().well_connpos[nw];
const std::vector<int> well_cells(wells().well_cells, wells().well_cells + nperf);
// assuming the water phase is the first phase
const int nw = wells().number_of_wells;
mob_perfcells = subset(rq_[0].mob,well_cells); */
// applying the shear-thinning to the water phase
V shear_mult_wells_v = Eigen::Map<V>(shear_mult_wells_.data(), shear_mult_wells_.size());
ADB shear_mult_wells_adb = ADB::constant(shear_mult_wells_v);
mob_perfcells[water_pos] = mob_perfcells[water_pos] / shear_mult_wells_adb;
}
addWellEq(state, well_state, aliveWells);
Base::computeWellFlux(state, mob_perfcells, b_perfcells, aliveWells, cq_s);
Base::updatePerfPhaseRatesAndPressures(cq_s, state, well_state);
Base::addWellFluxEq(cq_s, state);
addWellContributionToMassBalanceEq(cq_s, state, well_state);
addWellControlEq(state, well_state, aliveWells);
}
@ -723,307 +749,6 @@ namespace Opm {
return polymer_props_ad_.polymerWaterVelocityRatio(state.concentration);
}
template <class Grid>
void
BlackoilPolymerModel<Grid>::addWellEq(const SolutionState& state,
WellState& xw,
V& aliveWells)
{
if( ! wellsActive() ) return ;
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<const V>(wells().WI, nperf);
const std::vector<int> 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 needed quantities for the perforation cells
const ADB& p_perfcells = subset(state.pressure, well_cells);
const ADB& rv_perfcells = subset(state.rv,well_cells);
const ADB& rs_perfcells = subset(state.rs,well_cells);
std::vector<ADB> mob_perfcells(np, ADB::null());
std::vector<ADB> b_perfcells(np, ADB::null());
for (int phase = 0; phase < np; ++phase) {
mob_perfcells[phase] = subset(rq_[phase].mob,well_cells);
b_perfcells[phase] = subset(rq_[phase].b,well_cells);
}
// applying the shear-thinning to the water face
if (has_plyshlog_) {
V shear_mult_wells_v = Eigen::Map<V>(shear_mult_wells_.data(), shear_mult_wells_.size());
ADB shear_mult_wells_adb = ADB::constant(shear_mult_wells_v);
mob_perfcells[0] = mob_perfcells[0] / shear_mult_wells_adb;
}
// Perforation pressure
const ADB perfpressure = (wops_.w2p * state.bhp) + cdp;
std::vector<double> perfpressure_d(perfpressure.value().data(), perfpressure.value().data() + nperf);
xw.perfPress() = perfpressure_d;
// Pressure drawdown (also used to determine direction of flow)
const ADB drawdown = p_perfcells - perfpressure;
// Compute vectors with zero and ones that
// selects the wanted quantities.
// selects injection perforations
V selectInjectingPerforations = V::Zero(nperf);
// selects producing perforations
V selectProducingPerforations = V::Zero(nperf);
for (int c = 0; c < nperf; ++c){
if (drawdown.value()[c] < 0)
selectInjectingPerforations[c] = 1;
else
selectProducingPerforations[c] = 1;
}
// HANDLE FLOW INTO WELLBORE
// compute phase volumetric rates at standard conditions
std::vector<ADB> cq_ps(np, ADB::null());
for (int phase = 0; phase < np; ++phase) {
const ADB cq_p = -(selectProducingPerforations * Tw) * (mob_perfcells[phase] * drawdown);
cq_ps[phase] = b_perfcells[phase] * cq_p;
}
if (active_[Oil] && active_[Gas]) {
const int oilpos = pu.phase_pos[Oil];
const int gaspos = pu.phase_pos[Gas];
const ADB cq_psOil = cq_ps[oilpos];
const ADB cq_psGas = cq_ps[gaspos];
cq_ps[gaspos] += rs_perfcells * cq_psOil;
cq_ps[oilpos] += rv_perfcells * cq_psGas;
}
// HANDLE FLOW OUT FROM WELLBORE
// Using total mobilities
ADB total_mob = mob_perfcells[0];
for (int phase = 1; phase < np; ++phase) {
total_mob += mob_perfcells[phase];
}
// injection perforations total volume rates
const ADB cqt_i = -(selectInjectingPerforations * Tw) * (total_mob * drawdown);
// compute wellbore mixture for injecting perforations
// The wellbore mixture depends on the inflow from the reservoar
// and the well injection rates.
// compute avg. and total wellbore phase volumetric rates at standard conds
const DataBlock compi = Eigen::Map<const DataBlock>(wells().comp_frac, nw, np);
std::vector<ADB> wbq(np, ADB::null());
ADB wbqt = ADB::constant(V::Zero(nw));
for (int phase = 0; phase < np; ++phase) {
const ADB& q_ps = wops_.p2w * cq_ps[phase];
const ADB& q_s = subset(state.qs, Span(nw, 1, phase*nw));
Selector<double> injectingPhase_selector(q_s.value(), Selector<double>::GreaterZero);
const int pos = pu.phase_pos[phase];
wbq[phase] = (compi.col(pos) * injectingPhase_selector.select(q_s,ADB::constant(V::Zero(nw)))) - q_ps;
wbqt += wbq[phase];
}
// compute wellbore mixture at standard conditions.
Selector<double> notDeadWells_selector(wbqt.value(), Selector<double>::Zero);
std::vector<ADB> cmix_s(np, ADB::null());
for (int phase = 0; phase < np; ++phase) {
const int pos = pu.phase_pos[phase];
cmix_s[phase] = wops_.w2p * notDeadWells_selector.select(ADB::constant(compi.col(pos)), wbq[phase]/wbqt);
}
// compute volume ratio between connection at standard conditions
ADB volumeRatio = ADB::constant(V::Zero(nperf));
const ADB d = V::Constant(nperf,1.0) - rv_perfcells * rs_perfcells;
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 - rv_perfcells * cmix_s[gaspos] / d;
}
if (phase == Gas && active_[Oil]) {
const int oilpos = pu.phase_pos[Oil];
tmp = tmp - rs_perfcells * cmix_s[oilpos] / d;
}
volumeRatio += tmp / b_perfcells[phase];
}
// injecting connections total volumerates at standard conditions
ADB cqt_is = cqt_i/volumeRatio;
// connection phase volumerates at standard conditions
std::vector<ADB> cq_s(np, ADB::null());
for (int phase = 0; phase < np; ++phase) {
cq_s[phase] = cq_ps[phase] + cmix_s[phase]*cqt_is;
}
// 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);
}
// 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);
}
// check for dead wells (used in the well controll equations)
aliveWells = V::Constant(nw, 1.0);
for (int w = 0; w < nw; ++w) {
if (wbqt.value()[w] == 0) {
aliveWells[w] = 0.0;
}
}
// Update the perforation phase rates (used to calculate the pressure drop in the wellbore)
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<double> cq_d(cq.data(), cq.data() + nperf*np);
xw.perfPhaseRates() = cq_d;
residual_.well_flux_eq = qs;
extraAddWellEq(state, xw, cq_ps, cmix_s, cqt_is, well_cells);
}
template<class Grid>
bool
BlackoilPolymerModel<Grid>::findIntersection (Point2D line_segment1[2], Point2D line2[2], Point2D& intersection_point)
{
const double x1 = line_segment1[0].x;
const double y1 = line_segment1[0].y;
const double x2 = line_segment1[1].x;
const double y2 = line_segment1[1].y;
const double x3 = line2[0].x;
const double y3 = line2[0].y;
const double x4 = line2[1].x;
const double y4 = line2[1].y;
const double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if (d == 0.) { return false; }
const double x = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
const double y = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
if( x >= std::min(x1,x2) && x <= std::max(x1,x2) ){
intersection_point.x = x;
intersection_point.y = y;
return true;
} else {
return false;
}
}
template<class Grid>
bool
BlackoilPolymerModel<Grid>::computeShearMultLog( std::vector<double>& water_vel, std::vector<double>& visc_mult, std::vector<double>& shear_mult)
{
double refConcentration = polymer_props_ad_.plyshlogRefConc();
double refViscMult = polymer_props_ad_.viscMult(refConcentration);
std::vector<double> shear_water_vel = polymer_props_ad_.shearWaterVelocity();
std::vector<double> shear_vrf = polymer_props_ad_.shearViscosityReductionFactor();
std::vector<double> logShearWaterVel;
std::vector<double> logShearVRF;
logShearWaterVel.resize(shear_water_vel.size());
logShearVRF.resize(shear_water_vel.size());
// converting the table using the reference condition
for(int i = 0; i < shear_vrf.size(); ++i){
shear_vrf[i] = (refViscMult * shear_vrf[i] - 1.) / (refViscMult - 1);
logShearWaterVel[i] = std::log(shear_water_vel[i]);
}
shear_mult.resize(water_vel.size());
// the mimum velocity to apply the shear-thinning
const double minShearVel = shear_water_vel[0];
const double maxShearVel = shear_water_vel.back();
const double epsilon = std::sqrt(std::numeric_limits<double>::epsilon());
for(int i = 0; i < water_vel.size(); ++i){
if( visc_mult[i] - 1. < epsilon || std::abs(water_vel[i]) < minShearVel ) {
shear_mult[i] = 1.0;
continue;
}
for(int j = 0; j < shear_vrf.size(); ++j){
logShearVRF[j] = (1 + (visc_mult[i] - 1.0) * shear_vrf[j]) / visc_mult[i];
logShearVRF[j] = std::log(logShearVRF[j]);
}
// const double logWaterVelO = std::log(water_vel[i]);
const double logWaterVelO = std::log(std::abs(water_vel[i]));
int iIntersection; // finding the intersection on the iIntersectionth table segment
bool foundSegment = false;
for(iIntersection = 0; iIntersection < shear_vrf.size() - 1; ++iIntersection){
double temp1 = logShearVRF[iIntersection] + logShearWaterVel[iIntersection] - logWaterVelO;
double temp2 = logShearVRF[iIntersection + 1] + logShearWaterVel[iIntersection + 1] - logWaterVelO;
// ignore the cases the temp1 or temp2 is zero first for simplicity.
// several more complicated cases remain to be implemented.
if( temp1 * temp2 < 0.){
foundSegment = true;
break;
}
}
if(foundSegment == true){
Point2D lineSegment[2];
lineSegment[0] = Point2D{logShearWaterVel[iIntersection], logShearVRF[iIntersection]};
lineSegment[1] = Point2D{logShearWaterVel[iIntersection + 1], logShearVRF[iIntersection + 1]};
Point2D line[2];
line[0] = Point2D{0, logWaterVelO};
line[1] = Point2D{logWaterVelO, 0};
Point2D intersectionPoint;
bool foundIntersection = findIntersection(lineSegment, line, intersectionPoint);
if(foundIntersection){
shear_mult[i] = std::exp(intersectionPoint.y);
}else{
std::cerr << " failed in finding the solution for shear-thinning multiplier " << std::endl;
return false; // failed in finding the solution.
}
}else{
if (std::abs(water_vel[i]) < maxShearVel) {
std::cout << " the veclocity is " << water_vel[i] << std::endl;
std::cout << " max shear velocity is " << maxShearVel << std::endl;
std::cerr << " something wrong happend in finding segment" << std::endl;
return false;
} else {
shear_mult[i] = std::exp(logShearVRF.back());
}
}
}
return true;
}
template<class Grid>
void
BlackoilPolymerModel<Grid>::computeWaterShearVelocityFaces(const V& transi, const std::vector<ADB>& kr,
@ -1033,7 +758,7 @@ namespace Opm {
std::vector<double> b_faces;
const int phase = fluid_.phaseUsage().phase_pos[BlackoilPhases::Aqua]; // water position
const int phase = fluid_.phaseUsage().phase_pos[Water]; // water position
const int canonicalPhaseIdx = canph_[phase];
@ -1074,9 +799,9 @@ namespace Opm {
rq_[ phase ].mflux = upwind.select(b * mob) * (transi * dh);
const auto& tempb_faces = upwind.select(b);
b_faces.resize(tempb_faces.size());
std::copy(&(tempb_faces.value()[0]), &(tempb_faces.value()[0]) + tempb_faces.size(), b_faces.begin());
const auto& b_faces_adb = upwind.select(b);
b_faces.resize(b_faces_adb.size());
std::copy(&(b_faces_adb.value()[0]), &(b_faces_adb.value()[0]) + b_faces_adb.size(), b_faces.begin());
const auto& internal_faces = ops_.internal_faces;
@ -1088,11 +813,11 @@ namespace Opm {
}
const ADB phi = Opm::AutoDiffBlock<double>::constant(Eigen::Map<const V>(& fluid_.porosity()[0], AutoDiffGrid::numCells(grid_), 1));
const ADB temp_phiavg = ops_.caver * phi;
const ADB phiavg_adb = ops_.caver * phi;
std::vector<double> phiavg;
phiavg.resize(temp_phiavg.size());
std::copy(&(temp_phiavg.value()[0]), &(temp_phiavg.value()[0]) + temp_phiavg.size(), phiavg.begin());
phiavg.resize(phiavg_adb.size());
std::copy(&(phiavg_adb.value()[0]), &(phiavg_adb.value()[0]) + phiavg_adb.size(), phiavg.begin());
water_vel.resize(nface);
std::copy(&(rq_[0].mflux.value()[0]), &(rq_[0].mflux.value()[0]) + nface, water_vel.begin());
@ -1149,145 +874,42 @@ namespace Opm {
template<class Grid>
void
BlackoilPolymerModel<Grid>::computeWaterShearVelocityWells(const SolutionState& state, WellState& xw,
V& aliveWells, std::vector<double>& water_vel_wells, std::vector<double>& visc_mult_wells)
BlackoilPolymerModel<Grid>::computeWaterShearVelocityWells(const SolutionState& state, WellState& xw, const ADB& cq_sw,
std::vector<double>& water_vel_wells, std::vector<double>& visc_mult_wells)
{
if( ! wellsActive() ) return ;
// 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<const V>(wells().WI, nperf);
const std::vector<int> 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 needed quantities for the perforation cells
const ADB& p_perfcells = subset(state.pressure, well_cells);
const ADB& rv_perfcells = subset(state.rv,well_cells);
const ADB& rs_perfcells = subset(state.rs,well_cells);
std::vector<ADB> mob_perfcells(np, ADB::null());
std::vector<ADB> b_perfcells(np, ADB::null());
for (int phase = 0; phase < np; ++phase) {
mob_perfcells[phase] = subset(rq_[phase].mob,well_cells);
b_perfcells[phase] = subset(rq_[phase].b,well_cells);
}
// Perforation pressure
const ADB perfpressure = (wops_.w2p * state.bhp) + cdp;
std::vector<double> perfpressure_d(perfpressure.value().data(), perfpressure.value().data() + nperf);
xw.perfPress() = perfpressure_d;
// Pressure drawdown (also used to determine direction of flow)
const ADB drawdown = p_perfcells - perfpressure;
// Compute vectors with zero and ones that
// selects the wanted quantities.
// selects injection perforations
V selectInjectingPerforations = V::Zero(nperf);
// selects producing perforations
V selectProducingPerforations = V::Zero(nperf);
for (int c = 0; c < nperf; ++c) {
if (drawdown.value()[c] < 0)
selectInjectingPerforations[c] = 1;
else
selectProducingPerforations[c] = 1;
}
// HANDLE FLOW INTO WELLBORE
// compute phase volumetric rates at standard conditions
std::vector<ADB> cq_ps(np, ADB::null());
for (int phase = 0; phase < np; ++phase) {
const ADB cq_p = -(selectProducingPerforations * Tw) * (mob_perfcells[phase] * drawdown);
cq_ps[phase] = b_perfcells[phase] * cq_p;
}
if (active_[Oil] && active_[Gas]) {
const int oilpos = pu.phase_pos[Oil];
const int gaspos = pu.phase_pos[Gas];
const ADB cq_psOil = cq_ps[oilpos];
const ADB cq_psGas = cq_ps[gaspos];
cq_ps[gaspos] += rs_perfcells * cq_psOil;
cq_ps[oilpos] += rv_perfcells * cq_psGas;
}
// HANDLE FLOW OUT FROM WELLBORE
// Using total mobilities
ADB total_mob = mob_perfcells[0];
for (int phase = 1; phase < np; ++phase) {
total_mob += mob_perfcells[phase];
}
// injection perforations total volume rates
const ADB cqt_i = -(selectInjectingPerforations * Tw) * (total_mob * drawdown);
// compute wellbore mixture for injecting perforations
// The wellbore mixture depends on the inflow from the reservoar
// and the well injection rates.
// compute avg. and total wellbore phase volumetric rates at standard conds
const DataBlock compi = Eigen::Map<const DataBlock>(wells().comp_frac, nw, np);
std::vector<ADB> wbq(np, ADB::null());
ADB wbqt = ADB::constant(V::Zero(nw));
for (int phase = 0; phase < np; ++phase) {
const ADB& q_ps = wops_.p2w * cq_ps[phase];
const ADB& q_s = subset(state.qs, Span(nw, 1, phase*nw));
Selector<double> injectingPhase_selector(q_s.value(), Selector<double>::GreaterZero);
const int pos = pu.phase_pos[phase];
wbq[phase] = (compi.col(pos) * injectingPhase_selector.select(q_s,ADB::constant(V::Zero(nw)))) - q_ps;
wbqt += wbq[phase];
}
// compute wellbore mixture at standard conditions.
Selector<double> notDeadWells_selector(wbqt.value(), Selector<double>::Zero);
std::vector<ADB> cmix_s(np, ADB::null());
for (int phase = 0; phase < np; ++phase) {
const int pos = pu.phase_pos[phase];
cmix_s[phase] = wops_.w2p * notDeadWells_selector.select(ADB::constant(compi.col(pos)), wbq[phase]/wbqt);
}
// compute volume ratio between connection at standard conditions
ADB volumeRatio = ADB::constant(V::Zero(nperf));
const ADB d = V::Constant(nperf,1.0) - rv_perfcells * rs_perfcells;
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 - rv_perfcells * cmix_s[gaspos] / d;
}
if (phase == Gas && active_[Oil]) {
const int oilpos = pu.phase_pos[Oil];
tmp = tmp - rs_perfcells * cmix_s[oilpos] / d;
}
volumeRatio += tmp / b_perfcells[phase];
}
// injecting connections total volumerates at standard conditions
ADB cqt_is = cqt_i/volumeRatio;
// connection phase volumerates at standard conditions
std::vector<ADB> cq_s(np, ADB::null());
for (int phase = 0; phase < np; ++phase) {
cq_s[phase] = cq_ps[phase] + cmix_s[phase]*cqt_is;
}
water_vel_wells.resize(cq_s[0].size());
std::copy(&(cq_s[0].value()[0]), &(cq_s[0].value()[0]) + cq_s[0].size(), water_vel_wells.begin());
water_vel_wells.resize(cq_sw.size());
std::copy(&(cq_sw.value()[0]), &(cq_sw.value()[0]) + cq_sw.size(), water_vel_wells.begin());
const V& polymer_conc = state.concentration.value();
V visc_mult_cells = polymer_props_ad_.viscMult(polymer_conc);
V visc_mult_wells_v = subset(visc_mult_cells, well_cells);
V temp_visc_mult_wells = subset(visc_mult_cells, well_cells);
visc_mult_wells.resize(visc_mult_wells_v.size());
std::copy(&(visc_mult_wells_v[0]), &(visc_mult_wells_v[0]) + visc_mult_wells_v.size(), visc_mult_wells.begin());
visc_mult_wells.resize(temp_visc_mult_wells.size());
std::copy(&(temp_visc_mult_wells[0]), &(temp_visc_mult_wells[0]) + temp_visc_mult_wells.size(), visc_mult_wells.begin());
const int water_pos = fluid_.phaseUsage().phase_pos[Water];
ADB b_perfcells = subset(rq_[water_pos].b, well_cells);
const ADB& p_perfcells = subset(state.pressure, well_cells);
const V& cdp = well_perforation_pressure_diffs_;
const ADB perfpressure = (wops_.w2p * state.bhp) + cdp;
// Pressure drawdown (also used to determine direction of flow)
const ADB drawdown = p_perfcells - perfpressure;
// selects injection perforations
V selectInjectingPerforations = V::Zero(nperf);
for (int c = 0; c < nperf; ++c) {
if (drawdown.value()[c] < 0) {
selectInjectingPerforations[c] = 1;
}
}
// for the injection wells
for (int i = 0; i < well_cells.size(); ++i) {
@ -1297,18 +919,15 @@ namespace Opm {
}
const ADB phi = Opm::AutoDiffBlock<double>::constant(Eigen::Map<const V>(& fluid_.porosity()[0], AutoDiffGrid::numCells(grid_), 1));
const ADB temp_phi_wells = subset(phi, well_cells);
const ADB phi_wells_adb = subset(phi, well_cells);
std::vector<double> phi_wells;
phi_wells.resize(temp_phi_wells.size());
std::copy(&(temp_phi_wells.value()[0]), &(temp_phi_wells.value()[0]) + temp_phi_wells.size(), phi_wells.begin());
phi_wells.resize(phi_wells_adb.size());
std::copy(&(phi_wells_adb.value()[0]), &(phi_wells_adb.value()[0]) + phi_wells_adb.size(), phi_wells.begin());
std::vector<double> b_wells;
b_wells.resize(b_perfcells[0].size());
std::copy(&(b_perfcells[0].value()[0]), &(b_perfcells[0].value()[0]) + b_perfcells[0].size(), b_wells.begin());
b_wells.resize(b_perfcells.size());
std::copy(&(b_perfcells.value()[0]), &(b_perfcells.value()[0]) + b_perfcells.size(), b_wells.begin());
for (int i = 0; i < water_vel_wells.size(); ++i) {
water_vel_wells[i] = b_wells[i] * water_vel_wells[i] / (phi_wells[i] * 2. * M_PI * wells_rep_radius_[i] * wells_perf_length_[i]);

View File

@ -29,6 +29,7 @@
#include <opm/polymer/PolymerProperties.hpp>
#include <opm/polymer/fullyimplicit/WellStateFullyImplicitBlackoilPolymer.hpp>
#include <opm/polymer/fullyimplicit/PolymerPropsAd.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp>
struct UnstructuredGrid;
struct Wells;
@ -88,6 +89,9 @@ namespace Opm {
int newtonIterations() const;
int linearIterations() const;
/// Not used by this class except to satisfy interface requirements.
typedef parameter::ParameterGroup SolverParameters;
private:
typedef AutoDiffBlock<double> ADB;
typedef ADB::V V;

View File

@ -327,4 +327,12 @@ namespace Opm {
return krw / rk;
}
bool
PolymerPropsAd::computeShearMultLog(std::vector<double>& water_vel, std::vector<double>& visc_mult, std::vector<double>& shear_mult) const
{
return polymer_props_.computeShearMultLog(water_vel, visc_mult, shear_mult);
}
}// namespace Opm

View File

@ -133,6 +133,14 @@ namespace Opm {
ADB
effectiveRelPerm(const ADB& c, const ADB& cmax_cells, const ADB& krw) const;
/// \param[in] water_vel Array of the n values of water velocity or shear rate.
/// \param[in] visc_mult Array of the n values of the viscosity multiplier from PLYVISC table.
/// \parma[out] shear_mult Array of the n values of calculated shear multiplier with PLYSHLOG keyword.
/// \return TRUE if the calculation of shear multiplier is sucessful,
/// FALSE if the calculation of shear multplier is failed.
bool computeShearMultLog(std::vector<double>& water_vel, std::vector<double>& visc_mult, std::vector<double>& shear_mult) const;
private:
const PolymerProperties& polymer_props_;
};

View File

@ -66,12 +66,9 @@ namespace Opm
-> std::unique_ptr<Solver>
{
typedef typename Traits::Model Model;
typedef typename Model::ModelParameters ModelParams;
ModelParams modelParams( BaseType::param_ );
typedef NewtonSolver<Model> Solver;
auto model = std::unique_ptr<Model>(new Model(modelParams,
auto model = std::unique_ptr<Model>(new Model(BaseType::model_param_,
BaseType::grid_,
BaseType::props_,
BaseType::geo_,
@ -93,9 +90,7 @@ namespace Opm
model->setThresholdPressures(BaseType::threshold_pressures_by_face_);
}
typedef typename Solver::SolverParameters SolverParams;
SolverParams solverParams( BaseType::param_ );
return std::unique_ptr<Solver>(new Solver(solverParams, std::move(model)));
return std::unique_ptr<Solver>(new Solver(BaseType::solver_param_, std::move(model)));
}

View File

@ -79,6 +79,11 @@ namespace Opm
typedef BlackoilOutputWriter OutputWriter;
typedef GridT Grid;
typedef FullyImplicitCompressiblePolymerSolver Solver;
/// Dummy class, this Solver does not use a Model.
struct Model
{
typedef parameter::ParameterGroup ModelParameters;
};
};
/// Class collecting all necessary components for a two-phase simulation.