mirror of
https://github.com/OPM/opm-simulators.git
synced 2024-11-26 03:00:17 -06:00
Added TransportModelPolymer class.
This commit is contained in:
parent
b30e64630f
commit
8cb2af77e7
@ -7,9 +7,11 @@ SUBDIRS = . examples
|
||||
lib_LTLIBRARIES = libopmpolymer.la
|
||||
|
||||
libopmpolymer_la_SOURCES = \
|
||||
opm/polymer/TransportModelPolymer.cpp \
|
||||
opm/polymer/polymermodel.cpp \
|
||||
opm/polymer/polymertransport.cpp
|
||||
|
||||
nobase_include_HEADERS = \
|
||||
opm/polymer/TransportModelPolymer.hpp \
|
||||
opm/polymer/polymermodel.hpp \
|
||||
opm/polymer/polymertransport.hpp
|
||||
|
270
opm/polymer/TransportModelPolymer.cpp
Normal file
270
opm/polymer/TransportModelPolymer.cpp
Normal file
@ -0,0 +1,270 @@
|
||||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <opm/polymer/TransportModelPolymer.hpp>
|
||||
#include <opm/core/fluid/IncompPropertiesInterface.hpp>
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/utility/RootFinders.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
|
||||
TransportModelPolymer::TransportModelPolymer(const UnstructuredGrid& grid,
|
||||
const double* porosity,
|
||||
const double* porevolume,
|
||||
const IncompPropertiesInterface& props,
|
||||
const PolymerData& polyprops)
|
||||
: grid_(grid),
|
||||
porosity_(porosity),
|
||||
porevolume_(porevolume),
|
||||
props_(props),
|
||||
polyprops_(polyprops),
|
||||
darcyflux_(0),
|
||||
source_(0),
|
||||
dt_(0.0),
|
||||
inflow_c_(0.0),
|
||||
saturation_(0),
|
||||
concentration_(0),
|
||||
cmax_(0),
|
||||
fractionalflow_(grid.number_of_cells, -1.0),
|
||||
mc_(grid.number_of_cells, -1.0)
|
||||
{
|
||||
if (props.numPhases() != 2) {
|
||||
THROW("Property object must have 2 phases");
|
||||
}
|
||||
visc_ = props.viscosity();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TransportModelPolymer::solve(const double* darcyflux,
|
||||
const double* source,
|
||||
const double dt,
|
||||
const double inflow_c,
|
||||
double* saturation,
|
||||
double* concentration,
|
||||
double* cmax)
|
||||
{
|
||||
darcyflux_ = darcyflux;
|
||||
source_ = source;
|
||||
dt_ = dt;
|
||||
inflow_c_ = inflow_c;
|
||||
saturation_ = saturation;
|
||||
concentration_ = concentration;
|
||||
cmax_ = cmax;
|
||||
reorderAndTransport(grid_, darcyflux);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Residual for saturation equation, single-cell implicit Euler transport
|
||||
//
|
||||
// r(s) = s - s0 + dt/pv*( influx + outflux*f(s) )
|
||||
//
|
||||
// where influx is water influx, outflux is total outflux.
|
||||
// Influxes are negative, outfluxes positive.
|
||||
struct TransportModelPolymer::ResidualS
|
||||
{
|
||||
const TransportModelPolymer& tm_;
|
||||
const int cell_;
|
||||
const double s0_;
|
||||
const double influx_; // sum_j min(v_ij, 0)*f(s_j)
|
||||
const double outflux_; // sum_j max(v_ij, 0)
|
||||
const double dtpv_; // dt/pv(i)
|
||||
const double c_;
|
||||
explicit ResidualS(const TransportModelPolymer& tmodel,
|
||||
const int cell,
|
||||
const double s0,
|
||||
const double influx,
|
||||
const double outflux,
|
||||
const double dtpv,
|
||||
const double c)
|
||||
: tm_(tmodel),
|
||||
cell_(cell),
|
||||
s0_(s0),
|
||||
influx_(influx),
|
||||
outflux_(outflux),
|
||||
dtpv_(dtpv),
|
||||
c_(c)
|
||||
{
|
||||
}
|
||||
double operator()(double s) const
|
||||
{
|
||||
return s - s0_ + dtpv_*(outflux_*tm_.fracFlow(s, c_, cell_) + influx_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Residual for concentration equation, single-cell implicit Euler transport
|
||||
//
|
||||
// \TODO doc me
|
||||
// where ...
|
||||
// Influxes are negative, outfluxes positive.
|
||||
struct TransportModelPolymer::ResidualC
|
||||
{
|
||||
int cell;
|
||||
double s0;
|
||||
double c0;
|
||||
double cmax0;
|
||||
double influx; // sum_j min(v_ij, 0)*f(s_j)
|
||||
double influx_polymer; // sum_j min(v_ij, 0)*f(s_j)*mc(c_j)
|
||||
double outflux; // sum_j max(v_ij, 0)
|
||||
double porosity;
|
||||
double dtpv; // dt/pv(i)
|
||||
mutable double s; // Mutable in order to change it with every operator() call to be the last computed s value.
|
||||
const TransportModelPolymer& tm;
|
||||
explicit ResidualC(const TransportModelPolymer& tmodel, int cell_index)
|
||||
: tm(tmodel)
|
||||
{
|
||||
cell = cell_index;
|
||||
s0 = tm.saturation_[cell];
|
||||
c0 = tm.concentration_[cell];
|
||||
cmax0 = tm.cmax_[cell];
|
||||
double dflux = -tm.source_[cell];
|
||||
bool src_is_inflow = dflux < 0.0;
|
||||
influx = src_is_inflow ? dflux : 0.0;
|
||||
influx_polymer = src_is_inflow ? dflux*tm.computeMc(tm.inflow_c_) : 0.0;
|
||||
outflux = !src_is_inflow ? dflux : 0.0;
|
||||
dtpv = tm.dt_/tm.porevolume_[cell];
|
||||
porosity = tm.porosity_[cell];
|
||||
s = -1e100;
|
||||
|
||||
for (int i = tm.grid_.cell_facepos[cell]; i < tm.grid_.cell_facepos[cell+1]; ++i) {
|
||||
int f = tm.grid_.cell_faces[i];
|
||||
double flux;
|
||||
int other;
|
||||
// Compute cell flux
|
||||
if (cell == tm.grid_.face_cells[2*f]) {
|
||||
flux = tm.darcyflux_[f];
|
||||
other = tm.grid_.face_cells[2*f+1];
|
||||
} else {
|
||||
flux =-tm.darcyflux_[f];
|
||||
other = tm.grid_.face_cells[2*f];
|
||||
}
|
||||
// Add flux to influx or outflux, if interior.
|
||||
if (other != -1) {
|
||||
if (flux < 0.0) {
|
||||
influx += flux*tm.fractionalflow_[other];
|
||||
influx_polymer += flux*tm.fractionalflow_[other]*tm.mc_[other];
|
||||
} else {
|
||||
outflux += flux;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
double operator()(double c) const
|
||||
{
|
||||
ResidualS res_s(tm, cell, s0, influx, outflux, dtpv, c);
|
||||
const double a = 0.2; // TODO: Make this a proper s_min value.
|
||||
const double b = 1.0;
|
||||
const int maxit = 20;
|
||||
const double tol = 1e-9;
|
||||
int iters_used;
|
||||
// Solve for s first.
|
||||
s = modifiedRegulaFalsi(res_s, a, b, maxit, tol, iters_used);
|
||||
double ff = tm.fracFlow(s, c, cell);
|
||||
double mc = tm.computeMc(c);
|
||||
double dps = tm.polyprops_.dps;
|
||||
double rhor = tm.polyprops_.rhor;
|
||||
double ads0 = tm.polyprops_.adsorbtion(std::max(c0, cmax0));
|
||||
double ads = tm.polyprops_.adsorbtion(std::max(c, cmax0));
|
||||
double res = (s - dps)*c - (s0 - dps)*c0
|
||||
+ rhor*((1.0 - porosity)/porosity)*(ads - ads0)
|
||||
+ dtpv*(outflux*ff*mc + influx_polymer);
|
||||
#ifdef EXTRA_DEBUG_OUTPUT
|
||||
std::cout << "c = " << c << " s = " << s << " c-residual = " << res << std::endl;
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
double lastSaturation() const
|
||||
{
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
void TransportModelPolymer::solveSingleCell(int cell)
|
||||
{
|
||||
ResidualC res(*this, cell);
|
||||
const double a = 0.0;
|
||||
const double b = polyprops_.c_max_limit;
|
||||
const int maxit = 20;
|
||||
const double tol = 1e-9;
|
||||
int iters_used;
|
||||
concentration_[cell] = modifiedRegulaFalsi(res, a, b, maxit, tol, iters_used);
|
||||
cmax_[cell] = std::max(cmax_[cell], concentration_[cell]);
|
||||
saturation_[cell] = res.lastSaturation();
|
||||
fractionalflow_[cell] = fracFlow(saturation_[cell], concentration_[cell], cell);
|
||||
mc_[cell] = computeMc(concentration_[cell]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
double TransportModelPolymer::fracFlow(double s, double c, int cell) const
|
||||
{
|
||||
double c_max_limit = polyprops_.c_max_limit;
|
||||
double cbar = c/c_max_limit;
|
||||
double mu_w = visc_[0];
|
||||
double mu_m = polyprops_.viscMult(c)*mu_w;
|
||||
double mu_p = polyprops_.viscMult(polyprops_.c_max_limit)*mu_w;
|
||||
double omega = polyprops_.omega;
|
||||
double mu_m_omega = std::pow(mu_m, omega);
|
||||
double mu_w_e = mu_m_omega*std::pow(mu_w, 1.0 - omega);
|
||||
double mu_p_eff = mu_m_omega*std::pow(mu_p, 1.0 - omega);
|
||||
double inv_mu_w_eff = (1.0 - cbar)/mu_w_e + cbar/mu_p_eff;
|
||||
double inv_visc_eff[2] = { inv_mu_w_eff, 1.0/visc_[1] };
|
||||
double sat[2] = { s, 1.0 - s };
|
||||
double mob[2];
|
||||
props_.relperm(1, sat, &cell, mob, 0);
|
||||
mob[0] *= inv_visc_eff[0];
|
||||
mob[1] *= inv_visc_eff[1];
|
||||
return mob[0]/(mob[0] + mob[1]);
|
||||
}
|
||||
|
||||
|
||||
double TransportModelPolymer::computeMc(double c) const
|
||||
{
|
||||
double c_max_limit = polyprops_.c_max_limit;
|
||||
double cbar = c/c_max_limit;
|
||||
double mu_w = visc_[0];
|
||||
double mu_m = polyprops_.viscMult(c)*mu_w;
|
||||
double mu_p = polyprops_.viscMult(polyprops_.c_max_limit)*mu_w;
|
||||
double omega = polyprops_.omega;
|
||||
double mu_m_omega = std::pow(mu_m, omega);
|
||||
double mu_w_e = mu_m_omega*std::pow(mu_w, 1.0 - omega);
|
||||
double mu_p_eff = mu_m_omega*std::pow(mu_p, 1.0 - omega);
|
||||
double inv_mu_w_eff = (1.0 - cbar)/mu_w_e + cbar/mu_p_eff;
|
||||
return c/(inv_mu_w_eff*mu_p_eff);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
|
||||
|
||||
/* Local Variables: */
|
||||
/* c-basic-offset:4 */
|
||||
/* End: */
|
113
opm/polymer/TransportModelPolymer.hpp
Normal file
113
opm/polymer/TransportModelPolymer.hpp
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_TRANSPORTMODELPOLYMER_HEADER_INCLUDED
|
||||
#define OPM_TRANSPORTMODELPOLYMER_HEADER_INCLUDED
|
||||
|
||||
#include <opm/core/transport/reorder/TransportModelInterface.hpp>
|
||||
#include <opm/core/utility/linearInterpolation.hpp>
|
||||
#include <vector>
|
||||
|
||||
class UnstructuredGrid;
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
class IncompPropertiesInterface;
|
||||
|
||||
|
||||
/// Containing all the extra information needed to model
|
||||
/// polymer-affected flow behaviour. This as an alternative
|
||||
/// to changing the IncompPropertiesInterface class.
|
||||
/// \TODO Improve encapsulation.
|
||||
struct PolymerData
|
||||
{
|
||||
double c_max_limit;
|
||||
double omega;
|
||||
double viscMult(double c) const
|
||||
{
|
||||
return Opm::linearInterpolation(c_vals_visc, visc_mult_vals, c);
|
||||
}
|
||||
double rhor;
|
||||
double dps;
|
||||
double adsorbtion(double c) const
|
||||
{
|
||||
return Opm::linearInterpolation(c_vals_ads, ads_vals, c);
|
||||
}
|
||||
|
||||
std::vector<double> c_vals_visc;
|
||||
std::vector<double> visc_mult_vals;
|
||||
std::vector<double> c_vals_ads;
|
||||
std::vector<double> ads_vals;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// A transport model for two-phase flow with polymer in the
|
||||
/// water phase.
|
||||
/// \TODO Include permeability reduction effect.
|
||||
class TransportModelPolymer : public TransportModelInterface
|
||||
{
|
||||
public:
|
||||
TransportModelPolymer(const UnstructuredGrid& grid,
|
||||
const double* porosity,
|
||||
const double* porevolume,
|
||||
const IncompPropertiesInterface& props,
|
||||
const PolymerData& polyprops);
|
||||
|
||||
/// Solve transport eqn with implicit Euler scheme, reordered.
|
||||
/// \TODO Now saturation is expected to be one sw value per cell,
|
||||
/// change to [sw so] per cell.
|
||||
void solve(const double* darcyflux,
|
||||
const double* source,
|
||||
const double dt,
|
||||
const double inflow_c,
|
||||
double* saturation,
|
||||
double* concentration,
|
||||
double* cmax);
|
||||
|
||||
virtual void solveSingleCell(int cell);
|
||||
|
||||
private:
|
||||
const UnstructuredGrid& grid_;
|
||||
const double* porosity_;
|
||||
const double* porevolume_; // one volume per cell
|
||||
const IncompPropertiesInterface& props_;
|
||||
const PolymerData& polyprops_;
|
||||
|
||||
const double* darcyflux_; // one flux per grid face
|
||||
const double* source_; // one source per cell
|
||||
double dt_;
|
||||
double inflow_c_;
|
||||
double* saturation_; // one per cell
|
||||
double* concentration_;
|
||||
double* cmax_;
|
||||
std::vector<double> fractionalflow_; // one per cell
|
||||
std::vector<double> mc_; // one per cell
|
||||
const double* visc_;
|
||||
|
||||
struct ResidualC;
|
||||
struct ResidualS;
|
||||
double fracFlow(double s, double c, int cell) const;
|
||||
double computeMc(double c) const;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_TRANSPORTMODELPOLYMER_HEADER_INCLUDED
|
Loading…
Reference in New Issue
Block a user