mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
Some cleaning and small changes
- unused code is removed - the scaled normed is stored in residual_norm_history for usage in stabilized newton - number of linear iterations is outputted - linear solver tolerance is reduced to 0.01 - make compute wellFlux local - rewrite ADB::V to std::vector<double>
This commit is contained in:
parent
1d2a5d7f1b
commit
952ccf8338
@ -159,31 +159,23 @@ namespace Opm {
|
||||
eclState().getTableManager().getVFPProdTables())
|
||||
, linsolver_ (linsolver)
|
||||
, active_(detail::activePhases(fluid.phaseUsage()))
|
||||
, canph_ (detail::active2Canonical(fluid.phaseUsage()))
|
||||
, cells_ (detail::buildAllCells(Opm::AutoDiffGrid::numCells(grid_)))
|
||||
, ops_ (grid_, geo.nnc())
|
||||
, has_disgas_(FluidSystem::enableDissolvedGas())
|
||||
, has_vapoil_(FluidSystem::enableVaporizedOil())
|
||||
, param_( param )
|
||||
, use_threshold_pressure_(false)
|
||||
, rq_ (fluid.numPhases())
|
||||
, phaseCondition_(AutoDiffGrid::numCells(grid_))
|
||||
, well_model_ (well_model)
|
||||
, isRs_(V::Zero(AutoDiffGrid::numCells(grid_)))
|
||||
, isRv_(V::Zero(AutoDiffGrid::numCells(grid_)))
|
||||
, isSg_(V::Zero(AutoDiffGrid::numCells(grid_)))
|
||||
, residual_ ( { std::vector<ADB>(fluid.numPhases(), ADB::null()),
|
||||
ADB::null(),
|
||||
ADB::null(),
|
||||
{ 1.1169, 1.0031, 0.0031 }, // the default magic numbers
|
||||
false } )
|
||||
, terminal_output_ (terminal_output)
|
||||
, current_relaxation_(1.0)
|
||||
, isBeginReportStep_(false)
|
||||
{
|
||||
const double gravity = detail::getGravity(geo_.gravity(), UgGridHelpers::dimensions(grid_));
|
||||
const V depth = Opm::AutoDiffGrid::cellCentroidsZToEigen(grid_);
|
||||
well_model_.init(&fluid_, &active_, &phaseCondition_, &vfp_properties_, gravity, depth);
|
||||
const std::vector<double> pv(geo_.poreVolume().data(), geo_.poreVolume().data() + geo_.poreVolume().size());
|
||||
const std::vector<double> depth(geo_.z().data(), geo_.z().data() + geo_.z().size());
|
||||
well_model_.init(&fluid_, &active_, &phaseCondition_, &vfp_properties_, gravity, depth, pv);
|
||||
wellModel().setWellsActive( localWellsActive() );
|
||||
global_nc_ = Opm::AutoDiffGrid::numCells(grid_);
|
||||
}
|
||||
@ -221,7 +213,6 @@ namespace Opm {
|
||||
ReservoirState& reservoir_state,
|
||||
WellState& well_state)
|
||||
{
|
||||
const double dt = timer.currentStepLength();
|
||||
if (iteration == 0) {
|
||||
// For each iteration we store in a vector the norms of the residual of
|
||||
// the mass balance for each active phase, the well flux and the well equations.
|
||||
@ -230,15 +221,17 @@ namespace Opm {
|
||||
dx_old_ = V::Zero(sizeNonLinear());
|
||||
}
|
||||
IterationReport iter_report = assemble(timer, iteration, reservoir_state, well_state);
|
||||
residual_norms_history_.push_back(computeResidualNorms());
|
||||
const bool converged = getConvergence(timer, iteration);
|
||||
const bool must_solve = (iteration < nonlinear_solver.minIter()) || (!converged);
|
||||
std::vector<double> residual_norms;
|
||||
const bool converged = getConvergence(timer, iteration,residual_norms);
|
||||
residual_norms_history_.push_back(residual_norms);
|
||||
const bool must_solve = true;
|
||||
Dune::InverseOperatorResult result;
|
||||
if (must_solve) {
|
||||
// enable single precision for solvers when dt is smaller then 20 days
|
||||
//residual_.singlePrecision = (unit::convert::to(dt, unit::day) < 20.) ;
|
||||
|
||||
// Compute the nonlinear update.
|
||||
V dx = solveJacobianSystem();
|
||||
V dx = solveJacobianSystem(result);
|
||||
|
||||
// Stabilize the nonlinear update.
|
||||
bool isOscillate = false;
|
||||
@ -260,7 +253,7 @@ namespace Opm {
|
||||
updateState(dx, reservoir_state, well_state);
|
||||
}
|
||||
const bool failed = false; // Not needed in this model.
|
||||
const int linear_iters = must_solve ? linearIterationsLastSolve() : 0;
|
||||
const int linear_iters = must_solve ? result.iterations : 0;
|
||||
return IterationReport{ failed, converged, linear_iters, iter_report.well_iterations };
|
||||
}
|
||||
|
||||
@ -295,7 +288,7 @@ namespace Opm {
|
||||
IterationReport iter_report;
|
||||
try
|
||||
{
|
||||
iter_report = wellModel().assemble(ebosSimulator_, iterationIdx, dt, well_state, residual_);
|
||||
iter_report = wellModel().assemble(ebosSimulator_, iterationIdx, dt, well_state);
|
||||
}
|
||||
catch ( const Dune::FMatrixError& e )
|
||||
{
|
||||
@ -312,56 +305,13 @@ namespace Opm {
|
||||
auto& ebosJac = ebosSimulator_.model().linearizer().matrix();
|
||||
auto& ebosResid = ebosSimulator_.model().linearizer().residual();
|
||||
|
||||
convertResults2(ebosResid, ebosJac);
|
||||
convertResults(ebosResid, ebosJac);
|
||||
wellModel().addRhs(ebosResid, ebosJac);
|
||||
|
||||
return iter_report;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// \brief Compute the residual norms of the mass balance for each phase,
|
||||
/// the well flux, and the well equation.
|
||||
/// \return a vector that contains for each phase the norm of the mass balance
|
||||
/// and afterwards the norm of the residual of the well flux and the well equation.
|
||||
std::vector<double> computeResidualNorms() const
|
||||
{
|
||||
std::vector<double> residualNorms;
|
||||
|
||||
std::vector<ADB>::const_iterator massBalanceIt = residual_.material_balance_eq.begin();
|
||||
const std::vector<ADB>::const_iterator endMassBalanceIt = residual_.material_balance_eq.end();
|
||||
|
||||
for (; massBalanceIt != endMassBalanceIt; ++massBalanceIt) {
|
||||
const double massBalanceResid = detail::infinityNorm( (*massBalanceIt),
|
||||
linsolver_.parallelInformation() );
|
||||
if (!std::isfinite(massBalanceResid)) {
|
||||
OPM_THROW(Opm::NumericalProblem,
|
||||
"Encountered a non-finite residual");
|
||||
}
|
||||
residualNorms.push_back(massBalanceResid);
|
||||
}
|
||||
|
||||
// the following residuals are not used in the oscillation detection now
|
||||
const double wellFluxResid = detail::infinityNormWell( residual_.well_flux_eq,
|
||||
linsolver_.parallelInformation() );
|
||||
if (!std::isfinite(wellFluxResid)) {
|
||||
OPM_THROW(Opm::NumericalProblem,
|
||||
"Encountered a non-finite residual");
|
||||
}
|
||||
residualNorms.push_back(wellFluxResid);
|
||||
|
||||
const double wellResid = detail::infinityNormWell( residual_.well_eq,
|
||||
linsolver_.parallelInformation() );
|
||||
if (!std::isfinite(wellResid)) {
|
||||
OPM_THROW(Opm::NumericalProblem,
|
||||
"Encountered a non-finite residual");
|
||||
}
|
||||
residualNorms.push_back(wellResid);
|
||||
|
||||
return residualNorms;
|
||||
}
|
||||
|
||||
|
||||
/// \brief compute the relative change between to simulation states
|
||||
// \return || u^n+1 - u^n || / || u^n+1 ||
|
||||
double relativeChange( const SimulationDataContainer& previous, const SimulationDataContainer& current ) const
|
||||
@ -405,7 +355,9 @@ namespace Opm {
|
||||
/// The size (number of unknowns) of the nonlinear system of equations.
|
||||
int sizeNonLinear() const
|
||||
{
|
||||
return residual_.sizeNonLinear();
|
||||
const int nc = Opm::AutoDiffGrid::numCells(grid_);
|
||||
const int nw = wellModel().wells().number_of_wells;
|
||||
return numPhases() * (nc + nw);
|
||||
}
|
||||
|
||||
/// Number of linear iterations used in last call to solveJacobianSystem().
|
||||
@ -417,7 +369,7 @@ namespace Opm {
|
||||
|
||||
/// Solve the Jacobian system Jx = r where J is the Jacobian and
|
||||
/// r is the residual.
|
||||
V solveJacobianSystem() const
|
||||
V solveJacobianSystem(Dune::InverseOperatorResult& result) const
|
||||
{
|
||||
|
||||
typedef double Scalar;
|
||||
@ -437,26 +389,18 @@ namespace Opm {
|
||||
Dune::SeqScalarProduct<BVector> sp;
|
||||
|
||||
|
||||
|
||||
wellModel().apply(ebosJac, ebosResid);
|
||||
Dune::BiCGSTABSolver<BVector> linsolve(opA, sp, precond,
|
||||
0.001,
|
||||
0.01,
|
||||
100,
|
||||
false);
|
||||
|
||||
|
||||
const int np = numPhases();
|
||||
const int nc = AutoDiffGrid::numCells(grid_);
|
||||
// std::cout << "ebosResid" << std::endl;
|
||||
// for( int p=0; p<np; ++p) {
|
||||
// for (int cell = 0 ; cell < 1; ++cell) {
|
||||
// std::cout << ebosResid[cell][p] << std::endl;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// Solve system.
|
||||
Dune::InverseOperatorResult result;
|
||||
BVector x(ebosJac.M());
|
||||
x = 0.0;
|
||||
linsolve.apply(x, ebosResid, result);
|
||||
@ -466,9 +410,7 @@ namespace Opm {
|
||||
wellModel().recoverVariable(x, xw);
|
||||
|
||||
// convert to V;
|
||||
|
||||
|
||||
V dx( (nc + nw) * np);
|
||||
V dx( sizeNonLinear() );
|
||||
for( int p=0; p<np; ++p) {
|
||||
for (int i = 0; i < nc; ++i) {
|
||||
int idx = i + nc*ebosCompToFlowPhaseIdx(p);
|
||||
@ -741,7 +683,7 @@ namespace Opm {
|
||||
/// \param[in] timer simulation timer
|
||||
/// \param[in] dt timestep length
|
||||
/// \param[in] iteration current iteration number
|
||||
bool getConvergence(const SimulatorTimerInterface& timer, const int iteration)
|
||||
bool getConvergence(const SimulatorTimerInterface& timer, const int iteration, std::vector<double>& residual_norms)
|
||||
{
|
||||
const double dt = timer.currentStepLength();
|
||||
const double tol_mb = param_.tolerance_mb_;
|
||||
@ -750,7 +692,6 @@ namespace Opm {
|
||||
|
||||
const int nc = Opm::AutoDiffGrid::numCells(grid_);
|
||||
const int np = numPhases();
|
||||
assert(int(rq_.size()) == np);
|
||||
|
||||
const V& pv = geo_.poreVolume();
|
||||
|
||||
@ -785,14 +726,7 @@ namespace Opm {
|
||||
|
||||
for ( int idx = 0; idx < np; ++idx )
|
||||
{
|
||||
//const ADB& tempB = rq_[idx].b;
|
||||
//B.col(idx) = 1./tempB.value();
|
||||
//R.col(idx) = residual_.material_balance_eq[idx].value();
|
||||
tempV.col(idx) = R2.col(idx).abs()/pv;
|
||||
// std::cout << "------R------- " << idx << std::endl;
|
||||
// std::cout << R.col(idx)[0] << std::endl;
|
||||
// std::cout << "------R2------- " << idx << std::endl;
|
||||
// std::cout << R2.col(idx)[0] << std::endl;
|
||||
}
|
||||
|
||||
std::vector<double> pv_vector (geo_.poreVolume().data(), geo_.poreVolume().data() + geo_.poreVolume().size());
|
||||
@ -821,16 +755,11 @@ namespace Opm {
|
||||
well_flux_residual[idx] = B_avg[idx] * maxNormWell[idx];
|
||||
converged_Well = converged_Well && (well_flux_residual[idx] < tol_wells);
|
||||
}
|
||||
residual_norms.push_back(CNV[idx]);
|
||||
}
|
||||
|
||||
const double residualWell = detail::infinityNormWell(residual_.well_eq,
|
||||
linsolver_.parallelInformation());
|
||||
converged_Well = converged_Well && (residualWell < Opm::unit::barsa);
|
||||
const bool converged = converged_MB && converged_CNV && converged_Well;
|
||||
|
||||
// Residual in Pascal can have high values and still be ok.
|
||||
const double maxWellResidualAllowed = 1000.0 * maxResidualAllowed();
|
||||
|
||||
if ( terminal_output_ )
|
||||
{
|
||||
// Only rank 0 does print to std::cout
|
||||
@ -848,7 +777,6 @@ namespace Opm {
|
||||
const std::string& phaseName = FluidSystem::phaseName(flowPhaseToEbosPhaseIdx(phaseIdx));
|
||||
msg += " W-FLUX(" + phaseName + ")";
|
||||
}
|
||||
// std::cout << " WELL-CONT ";
|
||||
OpmLog::note(msg);
|
||||
}
|
||||
std::ostringstream ss;
|
||||
@ -864,7 +792,6 @@ namespace Opm {
|
||||
for (int idx = 0; idx < np; ++idx) {
|
||||
ss << std::setw(11) << well_flux_residual[idx];
|
||||
}
|
||||
// std::cout << std::setw(11) << residualWell;
|
||||
ss.precision(oprec);
|
||||
ss.flags(oflags);
|
||||
OpmLog::note(ss.str());
|
||||
@ -884,9 +811,6 @@ namespace Opm {
|
||||
OPM_THROW(Opm::NumericalProblem, "Too large residual for phase " << phaseName);
|
||||
}
|
||||
}
|
||||
if (std::isnan(residualWell) || residualWell > maxWellResidualAllowed) {
|
||||
OPM_THROW(Opm::NumericalProblem, "NaN or too large residual for well control equation");
|
||||
}
|
||||
|
||||
return converged;
|
||||
}
|
||||
@ -898,35 +822,6 @@ namespace Opm {
|
||||
return fluid_.numPhases();
|
||||
}
|
||||
|
||||
/// Update the scaling factors for mass balance equations
|
||||
void updateEquationsScaling()
|
||||
{
|
||||
ADB::V B;
|
||||
const Opm::PhaseUsage& pu = fluid_.phaseUsage();
|
||||
for ( int idx=0; idx<MaxNumPhases; ++idx )
|
||||
{
|
||||
if (active_[idx]) {
|
||||
const int pos = pu.phase_pos[idx];
|
||||
const ADB& temp_b = rq_[pos].b;
|
||||
B = 1. / temp_b.value();
|
||||
#if HAVE_MPI
|
||||
if ( linsolver_.parallelInformation().type() == typeid(ParallelISTLInformation) )
|
||||
{
|
||||
const ParallelISTLInformation& real_info =
|
||||
boost::any_cast<const ParallelISTLInformation&>(linsolver_.parallelInformation());
|
||||
double B_global_sum = 0;
|
||||
real_info.computeReduction(B, Reduction::makeGlobalSumFunctor<double>(), B_global_sum);
|
||||
residual_.matbalscale[idx] = B_global_sum / global_nc_;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
residual_.matbalscale[idx] = B.mean();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
@ -937,19 +832,6 @@ namespace Opm {
|
||||
Eigen::Dynamic,
|
||||
Eigen::RowMajor> DataBlock;
|
||||
|
||||
struct ReservoirResidualQuant {
|
||||
ReservoirResidualQuant()
|
||||
: b ( ADB::null())
|
||||
, dh ( ADB::null())
|
||||
, mob ( ADB::null())
|
||||
{
|
||||
}
|
||||
|
||||
ADB b; // Reciprocal FVF
|
||||
ADB dh; // Pressure drop across int. interfaces
|
||||
ADB mob; // Phase mobility (per cell)
|
||||
};
|
||||
|
||||
// --------- Data members ---------
|
||||
|
||||
Simulator& ebosSimulator_;
|
||||
@ -962,17 +844,11 @@ namespace Opm {
|
||||
// For each canonical phase -> true if active
|
||||
const std::vector<bool> active_;
|
||||
// Size = # active phases. Maps active -> canonical phase indices.
|
||||
const std::vector<int> canph_;
|
||||
const std::vector<int> cells_; // All grid cells
|
||||
HelperOps ops_;
|
||||
const bool has_disgas_;
|
||||
const bool has_vapoil_;
|
||||
|
||||
ModelParameters param_;
|
||||
bool use_threshold_pressure_;
|
||||
V threshold_pressures_by_connection_;
|
||||
|
||||
std::vector<ReservoirResidualQuant> rq_;
|
||||
std::vector<PhasePresence> phaseCondition_;
|
||||
|
||||
// Well Model
|
||||
@ -982,8 +858,6 @@ namespace Opm {
|
||||
V isRv_;
|
||||
V isSg_;
|
||||
|
||||
LinearisedBlackoilResidual residual_;
|
||||
|
||||
/// \brief Whether we print something to std::cout
|
||||
bool terminal_output_;
|
||||
/// \brief The number of cells of the global grid.
|
||||
@ -1137,110 +1011,8 @@ namespace Opm {
|
||||
|
||||
|
||||
private:
|
||||
void convertResults(const Simulator& simulator)
|
||||
{
|
||||
const auto& ebosJac = simulator.model().linearizer().matrix();
|
||||
const auto& ebosResid = simulator.model().linearizer().residual();
|
||||
|
||||
const int numPhases = wells().number_of_phases;
|
||||
const int numCells = ebosJac.N();
|
||||
const int cols = ebosJac.M();
|
||||
assert( numCells == cols );
|
||||
|
||||
// create the matrices and the right hand sides in a format which is more
|
||||
// appropriate for the conversion from what's produced ebos to the flow stuff
|
||||
typedef Eigen::SparseMatrix<double, Eigen::RowMajor> M;
|
||||
typedef ADB::V V;
|
||||
std::vector< std::vector< M > > jacs( numPhases );
|
||||
std::vector< V > resid (numPhases);
|
||||
for( int eqIdx = 0; eqIdx < numPhases; ++eqIdx )
|
||||
{
|
||||
jacs[ eqIdx ].resize( numPhases );
|
||||
resid[ eqIdx ].resize( numCells );
|
||||
for( int pvIdx = 0; pvIdx < numPhases; ++pvIdx )
|
||||
{
|
||||
jacs[ eqIdx ][ pvIdx ] = M( numCells, cols );
|
||||
jacs[ eqIdx ][ pvIdx ].reserve( ebosJac.nonzeroes() );
|
||||
}
|
||||
}
|
||||
|
||||
// write the right-hand-side values from the ebosJac into the objects
|
||||
// allocated above.
|
||||
const auto endrow = ebosJac.end();
|
||||
for( int cellIdx = 0; cellIdx < numCells; ++cellIdx )
|
||||
{
|
||||
const double cellVolume = simulator.model().dofTotalVolume(cellIdx);
|
||||
const auto& cellRes = ebosResid[ cellIdx ];
|
||||
|
||||
for( int flowPhaseIdx = 0; flowPhaseIdx < numPhases; ++flowPhaseIdx )
|
||||
{
|
||||
const double refDens = FluidSystem::referenceDensity( flowPhaseToEbosPhaseIdx( flowPhaseIdx ), 0 );
|
||||
double ebosVal = cellRes[ flowPhaseToEbosCompIdx( flowPhaseIdx ) ] / refDens * cellVolume;
|
||||
|
||||
resid[ flowPhaseIdx ][ cellIdx ] = ebosVal;
|
||||
}
|
||||
}
|
||||
|
||||
for( auto row = ebosJac.begin(); row != endrow; ++row )
|
||||
{
|
||||
const int rowIdx = row.index();
|
||||
const double cellVolume = simulator.model().dofTotalVolume(rowIdx);
|
||||
|
||||
for( int flowPhaseIdx = 0; flowPhaseIdx < numPhases; ++flowPhaseIdx )
|
||||
{
|
||||
for( int pvIdx = 0; pvIdx < numPhases; ++pvIdx )
|
||||
{
|
||||
jacs[flowPhaseIdx][pvIdx].startVec(rowIdx);
|
||||
}
|
||||
}
|
||||
|
||||
// translate the Jacobian of the residual from the format used by ebos to
|
||||
// the one expected by flow
|
||||
const auto endcol = row->end();
|
||||
for( auto col = row->begin(); col != endcol; ++col )
|
||||
{
|
||||
const int colIdx = col.index();
|
||||
for( int flowPhaseIdx = 0; flowPhaseIdx < numPhases; ++flowPhaseIdx )
|
||||
{
|
||||
const double refDens = FluidSystem::referenceDensity( flowPhaseToEbosPhaseIdx( flowPhaseIdx ), 0 );
|
||||
for( int pvIdx=0; pvIdx<numPhases; ++pvIdx )
|
||||
{
|
||||
double ebosVal = (*col)[flowPhaseToEbosCompIdx(flowPhaseIdx)][flowToEbosPvIdx(pvIdx)]/refDens*cellVolume;
|
||||
if (ebosVal != 0.0)
|
||||
jacs[flowPhaseIdx][pvIdx].insertBackByOuterInnerUnordered(rowIdx, colIdx) = ebosVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convert the resulting matrices from/ row major ordering to colum major.
|
||||
typedef typename ADB::M ADBJacobianMatrix;
|
||||
std::vector< std::vector< ADBJacobianMatrix > > adbJacs( numPhases );
|
||||
|
||||
for( int flowPhaseIdx = 0; flowPhaseIdx < numPhases; ++flowPhaseIdx )
|
||||
{
|
||||
adbJacs[ flowPhaseIdx ].resize( numPhases + 1 );
|
||||
for( int pvIdx = 0; pvIdx < numPhases; ++pvIdx )
|
||||
{
|
||||
jacs[ flowPhaseIdx ][ pvIdx ].finalize();
|
||||
adbJacs[ flowPhaseIdx ][ pvIdx ].assign( std::move(jacs[ flowPhaseIdx ][ pvIdx ]) );
|
||||
}
|
||||
// add two "dummy" matrices for the well primary variables
|
||||
const int numWells = wells().number_of_wells;
|
||||
for( int pvIdx = numPhases; pvIdx < numPhases + 1; ++pvIdx ) {
|
||||
adbJacs[ flowPhaseIdx ][ pvIdx ] = ADBJacobianMatrix( numPhases*numWells, numPhases*numWells );
|
||||
//sparsityPattern.derivative()[pvIdx];
|
||||
}
|
||||
}
|
||||
|
||||
for( int eqIdx = 0; eqIdx < numPhases; ++eqIdx )
|
||||
{
|
||||
residual_.material_balance_eq[ eqIdx ] =
|
||||
ADB::function(std::move(resid[eqIdx]),
|
||||
std::move(adbJacs[eqIdx]));
|
||||
}
|
||||
}
|
||||
void convertResults2(BVector& ebosResid, Mat& ebosJac) const
|
||||
void convertResults(BVector& ebosResid, Mat& ebosJac) const
|
||||
{
|
||||
const int numPhases = wells().number_of_phases;
|
||||
const int numCells = ebosJac.N();
|
||||
@ -1293,230 +1065,6 @@ namespace Opm {
|
||||
return flowToEbos[ phaseIdx ];
|
||||
}
|
||||
|
||||
void updateLegacyState(const Simulator& simulator, SolutionState& legacyState)
|
||||
{
|
||||
const int numPhases = 3;
|
||||
const int numCells = simulator.model().numGridDof();
|
||||
|
||||
typedef Eigen::SparseMatrix<double, Eigen::ColMajor> EigenMatrix;
|
||||
|
||||
///////
|
||||
// create the value vectors for the legacy state
|
||||
///////
|
||||
V poVal;
|
||||
V TVal;
|
||||
std::vector<V> SVal(numPhases);
|
||||
std::vector<V> mobVal(numPhases);
|
||||
std::vector<V> bVal(numPhases);
|
||||
std::vector<V> pVal(numPhases);
|
||||
V RsVal;
|
||||
V RvVal;
|
||||
|
||||
poVal.resize(numCells);
|
||||
TVal.resize(numCells);
|
||||
for (int phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) {
|
||||
SVal[phaseIdx].resize(numCells);
|
||||
mobVal[phaseIdx].resize(numCells);
|
||||
bVal[phaseIdx].resize(numCells);
|
||||
pVal[phaseIdx].resize(numCells);
|
||||
}
|
||||
RsVal.resize(numCells);
|
||||
RvVal.resize(numCells);
|
||||
|
||||
///////
|
||||
// create the Jacobian matrices for the legacy state. here we assume that the
|
||||
// sparsity pattern of the inputs is already correct
|
||||
///////
|
||||
std::vector<EigenMatrix> poJac(numPhases + 1);
|
||||
//std::vector<EigenMatrix> TJac(numPhases + 2);
|
||||
std::vector<std::vector<EigenMatrix>> SJac(numPhases);
|
||||
std::vector<std::vector<EigenMatrix>> mobJac(numPhases);
|
||||
std::vector<std::vector<EigenMatrix>> bJac(numPhases);
|
||||
std::vector<std::vector<EigenMatrix>> pJac(numPhases);
|
||||
std::vector<EigenMatrix> RsJac(numPhases + 1);
|
||||
std::vector<EigenMatrix> RvJac(numPhases + 1);
|
||||
|
||||
// reservoir stuff
|
||||
for (int pvIdx = 0; pvIdx < numPhases; ++ pvIdx) {
|
||||
poJac[pvIdx].resize(numCells, numCells);
|
||||
//TJac[pvIdx].resize(numCells, numCells);
|
||||
RsJac[pvIdx].resize(numCells, numCells);
|
||||
RvJac[pvIdx].resize(numCells, numCells);
|
||||
|
||||
poJac[pvIdx].reserve(numCells);
|
||||
//TJac[pvIdx].reserve(numCells);
|
||||
RsJac[pvIdx].reserve(numCells);
|
||||
RvJac[pvIdx].reserve(numCells);
|
||||
}
|
||||
|
||||
// auxiliary equations
|
||||
for (int pvIdx = numPhases; pvIdx < numPhases + 1; ++ pvIdx) {
|
||||
legacyState.pressure.derivative()[pvIdx].toSparse(poJac[pvIdx]);
|
||||
//legacyState.temperature.derivative()[pvIdx].toSparse(TJac[pvIdx]);
|
||||
legacyState.rs.derivative()[pvIdx].toSparse(RsJac[pvIdx]);
|
||||
legacyState.rv.derivative()[pvIdx].toSparse(RvJac[pvIdx]);
|
||||
}
|
||||
|
||||
for (int phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) {
|
||||
SJac[phaseIdx].resize(numPhases + 1);
|
||||
mobJac[phaseIdx].resize(numPhases + 1);
|
||||
bJac[phaseIdx].resize(numPhases + 1);
|
||||
pJac[phaseIdx].resize(numPhases + 1);
|
||||
for (int pvIdx = 0; pvIdx < numPhases; ++ pvIdx) {
|
||||
SJac[phaseIdx][pvIdx].resize(numCells, numCells);
|
||||
SJac[phaseIdx][pvIdx].reserve(numCells);
|
||||
|
||||
mobJac[phaseIdx][pvIdx].resize(numCells, numCells);
|
||||
mobJac[phaseIdx][pvIdx].reserve(numCells);
|
||||
|
||||
bJac[phaseIdx][pvIdx].resize(numCells, numCells);
|
||||
bJac[phaseIdx][pvIdx].reserve(numCells);
|
||||
|
||||
pJac[phaseIdx][pvIdx].resize(numCells, numCells);
|
||||
pJac[phaseIdx][pvIdx].reserve(numCells);
|
||||
}
|
||||
|
||||
// auxiliary equations for the saturations and pressures
|
||||
for (int pvIdx = numPhases; pvIdx < numPhases + 1; ++ pvIdx) {
|
||||
legacyState.saturation[phaseIdx].derivative()[pvIdx].toSparse(SJac[phaseIdx][pvIdx]);
|
||||
legacyState.saturation[phaseIdx].derivative()[pvIdx].toSparse(mobJac[phaseIdx][pvIdx]);
|
||||
legacyState.saturation[phaseIdx].derivative()[pvIdx].toSparse(bJac[phaseIdx][pvIdx]);
|
||||
legacyState.canonical_phase_pressures[phaseIdx].derivative()[pvIdx].toSparse(pJac[phaseIdx][pvIdx]);
|
||||
}
|
||||
}
|
||||
|
||||
///////
|
||||
// write the values and the derivatives into the data structures for the
|
||||
// legacy state.
|
||||
///////
|
||||
for( int cellIdx = 0; cellIdx < numCells; ++cellIdx )
|
||||
{
|
||||
const auto& intQuants = *(ebosSimulator_.model().cachedIntensiveQuantities(cellIdx, /*timeIdx=*/0));
|
||||
const auto& fs = intQuants.fluidState();
|
||||
|
||||
poVal[cellIdx] = fs.pressure(FluidSystem::oilPhaseIdx).value;
|
||||
TVal[cellIdx] = fs.temperature(0).value;
|
||||
RsVal[cellIdx] = fs.Rs().value;
|
||||
RvVal[cellIdx] = fs.Rv().value;
|
||||
|
||||
for (int pvIdx = 0; pvIdx < numPhases; ++pvIdx) {
|
||||
poJac[pvIdx].startVec(cellIdx);
|
||||
//TJac[pvIdx].startVec(cellIdx);
|
||||
RsJac[pvIdx].startVec(cellIdx);
|
||||
RvJac[pvIdx].startVec(cellIdx);
|
||||
|
||||
poJac[pvIdx].insertBackByOuterInnerUnordered(cellIdx, cellIdx) = fs.pressure(FluidSystem::oilPhaseIdx).derivatives[flowToEbosPvIdx(pvIdx)];
|
||||
//TJac[pvIdx].insertBackByOuterInnerUnordered(cellIdx, cellIdx) = fs.temperature(FluidSystem::oilPhaseIdx).derivatives[flowToEbosPvIdx(pvIdx)];
|
||||
RsJac[pvIdx].insertBackByOuterInnerUnordered(cellIdx, cellIdx) = fs.Rs().derivatives[flowToEbosPvIdx(pvIdx)];
|
||||
RvJac[pvIdx].insertBackByOuterInnerUnordered(cellIdx, cellIdx) = fs.Rv().derivatives[flowToEbosPvIdx(pvIdx)];
|
||||
}
|
||||
|
||||
for( int flowPhaseIdx = 0; flowPhaseIdx < numPhases; ++flowPhaseIdx )
|
||||
{
|
||||
int ebosPhaseIdx = flowPhaseToEbosPhaseIdx(flowPhaseIdx);
|
||||
SVal[flowPhaseIdx][cellIdx] = fs.saturation(ebosPhaseIdx).value;
|
||||
mobVal[flowPhaseIdx][cellIdx] = intQuants.mobility(ebosPhaseIdx).value;
|
||||
bVal[flowPhaseIdx][cellIdx] = fs.invB(ebosPhaseIdx).value;
|
||||
pVal[flowPhaseIdx][cellIdx] = fs.pressure(ebosPhaseIdx).value;
|
||||
|
||||
for (int pvIdx = 0; pvIdx < numPhases; ++pvIdx) {
|
||||
SJac[flowPhaseIdx][pvIdx].startVec(cellIdx);
|
||||
mobJac[flowPhaseIdx][pvIdx].startVec(cellIdx);
|
||||
bJac[flowPhaseIdx][pvIdx].startVec(cellIdx);
|
||||
pJac[flowPhaseIdx][pvIdx].startVec(cellIdx);
|
||||
SJac[flowPhaseIdx][pvIdx].insertBackByOuterInnerUnordered(cellIdx, cellIdx) = fs.saturation(ebosPhaseIdx).derivatives[flowToEbosPvIdx(pvIdx)];
|
||||
mobJac[flowPhaseIdx][pvIdx].insertBackByOuterInnerUnordered(cellIdx, cellIdx) = intQuants.mobility(ebosPhaseIdx).derivatives[flowToEbosPvIdx(pvIdx)];
|
||||
bJac[flowPhaseIdx][pvIdx].insertBackByOuterInnerUnordered(cellIdx, cellIdx) = fs.invB(ebosPhaseIdx).derivatives[flowToEbosPvIdx(pvIdx)];
|
||||
pJac[flowPhaseIdx][pvIdx].insertBackByOuterInnerUnordered(cellIdx, cellIdx) = fs.pressure(ebosPhaseIdx).derivatives[flowToEbosPvIdx(pvIdx)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// finalize all Jacobian matrices
|
||||
for (int pvIdx = 0; pvIdx < numPhases; ++pvIdx) {
|
||||
poJac[pvIdx].finalize();
|
||||
//TJac[pvIdx].finalize();
|
||||
RsJac[pvIdx].finalize();
|
||||
RvJac[pvIdx].finalize();
|
||||
|
||||
for (int phaseIdx = 0; phaseIdx < 3; ++ phaseIdx) {
|
||||
SJac[phaseIdx][pvIdx].finalize();
|
||||
mobJac[phaseIdx][pvIdx].finalize();
|
||||
bJac[phaseIdx][pvIdx].finalize();
|
||||
pJac[phaseIdx][pvIdx].finalize();
|
||||
}
|
||||
}
|
||||
|
||||
///////
|
||||
// create Opm::AutoDiffMatrix objects from Eigen::SparseMatrix
|
||||
// objects. (Opm::AutoDiffMatrix is not directly assignable, wtf?)
|
||||
///////
|
||||
typedef typename ADB::M ADBJacobianMatrix;
|
||||
|
||||
std::vector<ADBJacobianMatrix> poAdbJacs;
|
||||
std::vector<ADBJacobianMatrix> RsAdbJacs;
|
||||
std::vector<ADBJacobianMatrix> RvAdbJacs;
|
||||
|
||||
poAdbJacs.resize(numPhases + 1);
|
||||
RsAdbJacs.resize(numPhases + 1);
|
||||
RvAdbJacs.resize(numPhases + 1);
|
||||
for(int pvIdx = 0; pvIdx < numPhases + 1; ++pvIdx)
|
||||
{
|
||||
poAdbJacs[pvIdx].assign(poJac[pvIdx]);
|
||||
RsAdbJacs[pvIdx].assign(RsJac[pvIdx]);
|
||||
RvAdbJacs[pvIdx].assign(RvJac[pvIdx]);
|
||||
}
|
||||
|
||||
std::vector<std::vector<ADBJacobianMatrix>> SAdbJacs(numPhases);
|
||||
std::vector<std::vector<ADBJacobianMatrix>> mobAdbJacs(numPhases);
|
||||
std::vector<std::vector<ADBJacobianMatrix>> bAdbJacs(numPhases);
|
||||
std::vector<std::vector<ADBJacobianMatrix>> pAdbJacs(numPhases);
|
||||
for(int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx)
|
||||
{
|
||||
SAdbJacs[phaseIdx].resize(numPhases + 1);
|
||||
mobAdbJacs[phaseIdx].resize(numPhases + 1);
|
||||
bAdbJacs[phaseIdx].resize(numPhases + 1);
|
||||
pAdbJacs[phaseIdx].resize(numPhases + 1);
|
||||
for(int pvIdx = 0; pvIdx < numPhases + 1; ++pvIdx)
|
||||
{
|
||||
SAdbJacs[phaseIdx][pvIdx].assign(SJac[phaseIdx][pvIdx]);
|
||||
mobAdbJacs[phaseIdx][pvIdx].assign(mobJac[phaseIdx][pvIdx]);
|
||||
bAdbJacs[phaseIdx][pvIdx].assign(bJac[phaseIdx][pvIdx]);
|
||||
pAdbJacs[phaseIdx][pvIdx].assign(pJac[phaseIdx][pvIdx]);
|
||||
}
|
||||
}
|
||||
|
||||
///////
|
||||
// create the ADB objects in the legacy state
|
||||
///////
|
||||
legacyState.pressure =
|
||||
ADB::function(std::move(poVal),
|
||||
std::move(poAdbJacs));
|
||||
legacyState.temperature =
|
||||
ADB::constant(std::move(TVal));
|
||||
legacyState.rs =
|
||||
ADB::function(std::move(RsVal),
|
||||
std::move(RsAdbJacs));
|
||||
legacyState.rv =
|
||||
ADB::function(std::move(RvVal),
|
||||
std::move(RvAdbJacs));
|
||||
|
||||
for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx)
|
||||
{
|
||||
legacyState.saturation[phaseIdx] =
|
||||
ADB::function(std::move(SVal[phaseIdx]),
|
||||
std::move(SAdbJacs[phaseIdx]));
|
||||
legacyState.canonical_phase_pressures[phaseIdx] =
|
||||
ADB::function(std::move(pVal[phaseIdx]),
|
||||
std::move(pAdbJacs[phaseIdx]));
|
||||
rq_[phaseIdx].mob =
|
||||
ADB::function(std::move(mobVal[phaseIdx]),
|
||||
std::move(mobAdbJacs[phaseIdx]));
|
||||
rq_[phaseIdx].b =
|
||||
ADB::function(std::move(bVal[phaseIdx]),
|
||||
std::move(bAdbJacs[phaseIdx]));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void beginReportStep()
|
||||
@ -1574,55 +1122,13 @@ namespace Opm {
|
||||
//convertResults(ebosSimulator_);
|
||||
|
||||
if (param_.update_equations_scaling_) {
|
||||
std::cout << "scaling" << std::endl;
|
||||
std::cout << "equation scaling not suported yet" << std::endl;
|
||||
//updateEquationsScaling();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
std::vector<ADB>
|
||||
computePressures(const ADB& po,
|
||||
const ADB& sw,
|
||||
const ADB& so,
|
||||
const ADB& sg) const
|
||||
{
|
||||
// convert the pressure offsets to the capillary pressures
|
||||
std::vector<ADB> 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;
|
||||
if (active_[phaseIdx]) {
|
||||
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 (active_[phaseIdx]) {
|
||||
if (phaseIdx == BlackoilPhases::Aqua) {
|
||||
pressure[phaseIdx] = po - pressure[phaseIdx];
|
||||
} else {
|
||||
pressure[phaseIdx] += po;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pressure;
|
||||
}
|
||||
|
||||
V computeGasPressure(const V& po,
|
||||
const V& sw,
|
||||
const V& so,
|
||||
|
@ -65,39 +65,14 @@ namespace Opm {
|
||||
public:
|
||||
struct WellOps {
|
||||
WellOps(const Wells* wells)
|
||||
: w2p(),
|
||||
p2w(),
|
||||
well_cells()
|
||||
: well_cells()
|
||||
{
|
||||
if( wells )
|
||||
{
|
||||
w2p = Eigen::SparseMatrix<double>(wells->well_connpos[ wells->number_of_wells ], wells->number_of_wells);
|
||||
p2w = Eigen::SparseMatrix<double>(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<double> Tri;
|
||||
|
||||
std::vector<Tri> 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());
|
||||
|
||||
well_cells.assign(wells->well_cells, wells->well_cells + wells->well_connpos[wells->number_of_wells]);
|
||||
}
|
||||
}
|
||||
Eigen::SparseMatrix<double> w2p; // well -> perf (scatter)
|
||||
Eigen::SparseMatrix<double> p2w; // perf -> well (gather)
|
||||
std::vector<int> well_cells; // the set of perforated cells
|
||||
};
|
||||
|
||||
@ -132,7 +107,6 @@ namespace Opm {
|
||||
const std::vector<double>& pv)
|
||||
: wells_active_(wells_arg!=nullptr)
|
||||
, wells_(wells_arg)
|
||||
, wops_(wells_arg)
|
||||
, fluid_(nullptr)
|
||||
, active_(nullptr)
|
||||
, phase_condition_(nullptr)
|
||||
@ -144,7 +118,6 @@ namespace Opm {
|
||||
, F0_(wells_arg->number_of_wells * wells_arg->number_of_phases)
|
||||
, param_(param)
|
||||
, terminal_output_(terminal_output)
|
||||
, pv_(pv)
|
||||
//, resWell(wells_->number_of_wells)
|
||||
//, duneD( Mat(2, 2, 3, 0.4, Mat::implicit))
|
||||
//, duneB( Mat(300, 2, 6, 0.4, Mat::implicit))
|
||||
@ -157,8 +130,13 @@ namespace Opm {
|
||||
IterationReport assemble(const Simulator& ebosSimulator,
|
||||
const int iterationIdx,
|
||||
const double dt,
|
||||
WellState& well_state,
|
||||
LinearisedBlackoilResidual& residual) {
|
||||
WellState& well_state) {
|
||||
|
||||
IterationReport iter_report = {false, false, 0, 0};
|
||||
if ( ! localWellsActive() ) {
|
||||
return iter_report;
|
||||
}
|
||||
|
||||
resetWellControlFromState(well_state);
|
||||
updateWellControls(well_state);
|
||||
// Set the primary variables for the wells
|
||||
@ -169,56 +147,86 @@ namespace Opm {
|
||||
computeAccumWells();
|
||||
}
|
||||
|
||||
IterationReport iter_report = {false, false, 0, 0};
|
||||
if ( ! wellsActive() ) {
|
||||
return iter_report;
|
||||
}
|
||||
//wellModel().extractWellPerfProperties(state, rq_, mob_perfcells, b_perfcells);
|
||||
|
||||
if (param_.solve_welleq_initially_ && iterationIdx == 0) {
|
||||
// solve the well equations as a pre-processing step
|
||||
iter_report = solveWellEq(ebosSimulator, dt, well_state);
|
||||
}
|
||||
assembleWellEq(ebosSimulator, dt, well_state);
|
||||
|
||||
std::vector<ADB> cq_s;
|
||||
int nw = wells_->number_of_wells;
|
||||
|
||||
if (param_.compute_well_potentials_) {
|
||||
//wellModel().computeWellPotentials(mob_perfcells, b_perfcells, state0, well_state);
|
||||
}
|
||||
return iter_report;
|
||||
}
|
||||
|
||||
template <typename Simulator>
|
||||
void assembleWellEq(Simulator& ebosSimulator,
|
||||
const double dt,
|
||||
WellState& well_state) {
|
||||
const int np = wells().number_of_phases;
|
||||
const int nw = wells().number_of_wells;
|
||||
int nc = numCells();
|
||||
|
||||
//[A B [x = [ res
|
||||
// C D] x_well] res_well]
|
||||
|
||||
Mat duneD (nw, nw, 9, 0.4, Mat::implicit);
|
||||
Mat duneB (nc, nw, 9, 0.4, Mat::implicit);
|
||||
Mat duneC (nw, nc, 9, 0.4, Mat::implicit);
|
||||
Mat duneA (nc, nc, 9, 0.4, Mat::implicit);
|
||||
Mat duneB (nc, nw, 6, 0.4, Mat::implicit);
|
||||
Mat duneC (nw, nc, 6, 0.4, Mat::implicit);
|
||||
Mat duneA (nc, nc, 3, 0.4, Mat::implicit); // this is only the well part of the A matrix.
|
||||
BVector rhs(nc);
|
||||
BVector resWell(nw);
|
||||
computeWellFluxDense(ebosSimulator, cq_s, 4, duneA, duneB, duneC, duneD, rhs, resWell, well_state);
|
||||
addWellFluxEq(cq_s, dt, 4, residual, duneD, resWell);
|
||||
|
||||
//updatePerfPhaseRatesAndPressures(cq_s, well_state);
|
||||
//addWellContributionToMassBalanceEq(cq_s,residual);
|
||||
const V& cdp = wellPerforationPressureDiffs();
|
||||
const double volume = 0.002831684659200; // 0.1 cu ft;
|
||||
for (int w = 0; w < nw; ++w) {
|
||||
|
||||
for (int perf = wells().well_connpos[w] ; perf < wells().well_connpos[w+1]; ++perf) {
|
||||
|
||||
const int cell_idx = wells().well_cells[perf];
|
||||
const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0));
|
||||
std::vector<EvalWell> cq_s(np);
|
||||
computeWellFlux(w, wells().WI[perf], intQuants, cdp[perf], cq_s);
|
||||
|
||||
for (int p1 = 0; p1 < np; ++p1) {
|
||||
// subtract sum of phase fluxes in the reservoir equation.
|
||||
rhs[cell_idx][flowPhaseToEbosCompIdx(p1)] -= cq_s[p1].value;
|
||||
|
||||
// subtract sum of phase fluxes in the well equations.
|
||||
resWell[w][flowPhaseToEbosCompIdx(p1)] -= cq_s[p1].value;
|
||||
|
||||
// assemble the jacobians
|
||||
for (int p2 = 0; p2 < np; ++p2) {
|
||||
duneA.entry(cell_idx, cell_idx)[flowPhaseToEbosCompIdx(p1)][flowToEbosPvIdx(p2)] -= cq_s[p1].derivatives[p2];
|
||||
duneB.entry(cell_idx, w)[flowPhaseToEbosCompIdx(p1)][flowToEbosPvIdx(p2)] -= cq_s[p1].derivatives[p2+3];
|
||||
duneC.entry(w, cell_idx)[flowPhaseToEbosCompIdx(p1)][flowToEbosPvIdx(p2)] -= cq_s[p1].derivatives[p2];
|
||||
duneD.entry(w, w)[flowPhaseToEbosCompIdx(p1)][flowToEbosPvIdx(p2)] -= cq_s[p1].derivatives[p2+3];
|
||||
}
|
||||
|
||||
// Store the perforation phase flux for later usage.
|
||||
well_state.perfPhaseRates()[perf*np + p1] = cq_s[p1].value;
|
||||
}
|
||||
// Store the perforation pressure for later usage.
|
||||
well_state.perfPress()[perf] = well_state.bhp()[w] + cdp[perf];
|
||||
}
|
||||
|
||||
// add vol * dF/dt + Q to the well equations;
|
||||
for (int p1 = 0; p1 < np; ++p1) {
|
||||
EvalWell resWell_loc = (wellVolumeFraction(w, p1) - F0_[w + nw*p1]) * volume / dt;
|
||||
resWell_loc += getQs(w, p1);
|
||||
for (int p2 = 0; p2 < np; ++p2) {
|
||||
duneD.entry(w,w)[flowPhaseToEbosCompIdx(p1)][flowToEbosPvIdx(p2)] += resWell_loc.derivatives[p2+3];
|
||||
}
|
||||
resWell[w][flowPhaseToEbosCompIdx(p1)] += resWell_loc.value;
|
||||
}
|
||||
}
|
||||
|
||||
//std::cout << residual.well_flux_eq << std::endl;
|
||||
duneA.compress();
|
||||
duneB.compress();
|
||||
duneC.compress();
|
||||
duneD.compress();
|
||||
|
||||
//std::cout << "B" << std::endl;
|
||||
//print(duneB);
|
||||
//std::cout << "C" << std::endl;
|
||||
//print(duneC);
|
||||
//print(duneD);
|
||||
|
||||
|
||||
// V resWellEigen = residual.well_flux_eq.value();
|
||||
// const int np = numPhases();
|
||||
// BVector resWell2(nw);
|
||||
// for (int i = 0; i < nw; ++i){
|
||||
// for( int p = 0; p < np; ++p ) {
|
||||
// int idx = i + p * nw;
|
||||
// resWell2[i][flowPhaseToEbosCompIdx(p)] = resWellEigen(idx);
|
||||
// std::cout << resWell[i][flowPhaseToEbosCompIdx(p)] << " " << resWell2[i][flowPhaseToEbosCompIdx(p)]<< std::endl;
|
||||
// }
|
||||
// }
|
||||
|
||||
resWell_ = resWell;
|
||||
rhs_ = rhs;
|
||||
duneB_ = duneB;
|
||||
@ -226,15 +234,8 @@ namespace Opm {
|
||||
localInvert(duneD);
|
||||
invDuneD_ = duneD;
|
||||
duneA_ = duneA;
|
||||
//std::cout << "duneA_" << std::endl;
|
||||
//print(duneA_);
|
||||
|
||||
|
||||
if (param_.compute_well_potentials_) {
|
||||
//wellModel().computeWellPotentials(mob_perfcells, b_perfcells, state0, well_state);
|
||||
}
|
||||
return iter_report;
|
||||
}
|
||||
|
||||
void localInvert(Mat& istlA) const {
|
||||
for (auto row = istlA.begin(), rowend = istlA.end(); row != rowend; ++row ) {
|
||||
for (auto col = row->begin(), colend = row->end(); col != colend; ++col ) {
|
||||
@ -308,8 +309,8 @@ namespace Opm {
|
||||
assert(x.size() == rhs.size());
|
||||
x += rhs_;
|
||||
// jac = A + duneA
|
||||
jac = matAdd( jac, duneA_ );
|
||||
//jac += duneA_;
|
||||
//jac = matAdd( jac, duneA_ );
|
||||
jac += duneA_;
|
||||
}
|
||||
|
||||
void apply( Mat& A,
|
||||
@ -342,16 +343,6 @@ namespace Opm {
|
||||
}
|
||||
|
||||
|
||||
|
||||
void addDuneMatrix(Eigen::SparseMatrix<double, Eigen::RowMajor> eigen, Mat& dune, int p1, int p2) {
|
||||
//Mat dune( eigen.rows(), eigen.cols(), eigen.nonZeros(), 0.4,Mat::implicit) ;
|
||||
const int* ia = eigen.outerIndexPtr();
|
||||
const int* ja = eigen.innerIndexPtr();
|
||||
for (int row = 0; row < eigen.rows(); ++row) {
|
||||
dune.entry(ia[row],ja[row])[p1][p2] = eigen.coeff(ia[row] ,ja[row]);
|
||||
}
|
||||
}
|
||||
|
||||
int flowPhaseToEbosCompIdx( const int phaseIdx ) const
|
||||
{
|
||||
const int phaseToComp[ 3 ] = { FluidSystem::waterCompIdx, FluidSystem::oilCompIdx, FluidSystem::gasCompIdx };
|
||||
@ -381,21 +372,31 @@ namespace Opm {
|
||||
const std::vector<PhasePresence>* pc_arg,
|
||||
const VFPProperties* vfp_properties_arg,
|
||||
const double gravity_arg,
|
||||
const Vector& depth_arg)
|
||||
const std::vector<double>& depth_arg,
|
||||
const std::vector<double>& pv_arg)
|
||||
{
|
||||
fluid_ = fluid_arg;
|
||||
active_ = active_arg;
|
||||
phase_condition_ = pc_arg;
|
||||
vfp_properties_ = vfp_properties_arg;
|
||||
gravity_ = gravity_arg;
|
||||
perf_cell_depth_ = subset(depth_arg, wellOps().well_cells);
|
||||
cell_depths_ = extractPerfData(depth_arg);
|
||||
pv_ = pv_arg;
|
||||
}
|
||||
|
||||
const WellOps& wellOps() const
|
||||
{
|
||||
return wops_;
|
||||
std::vector<double>
|
||||
extractPerfData(const std::vector<double>& in) const {
|
||||
const int nw = wells().number_of_wells;
|
||||
const int nperf = wells().well_connpos[nw];
|
||||
std::vector<double> out(nperf);
|
||||
for (int w = 0; w < nw; ++w) {
|
||||
for (int perf = wells().well_connpos[w] ; perf < wells().well_connpos[w+1]; ++perf) {
|
||||
const int well_idx = wells().well_cells[perf];
|
||||
out[perf] = in[well_idx];
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
int numPhases() const { return wells().number_of_phases; }
|
||||
|
||||
@ -467,6 +468,7 @@ namespace Opm {
|
||||
return well_perforation_pressure_diffs_;
|
||||
}
|
||||
|
||||
|
||||
typedef DenseAd::Evaluation<double, /*size=*/3> Eval;
|
||||
EvalWell extendEval(Eval in) const {
|
||||
EvalWell out = 0.0;
|
||||
@ -508,252 +510,21 @@ namespace Opm {
|
||||
}
|
||||
}
|
||||
|
||||
EvalWell extractDenseAD(const ADB& data, int i, int j) const
|
||||
{
|
||||
EvalWell output = 0.0;
|
||||
output.value = data.value()[i];
|
||||
const int np = wells().number_of_phases;
|
||||
const std::vector<Opm::AutoDiffMatrix>& jac = data.derivative();
|
||||
//std::cout << jac.size() << std::endl;
|
||||
int numblocs = jac.size();
|
||||
for (int b = 0; b < numblocs; ++b) {
|
||||
if (b < np) { // don't copy well blocks)
|
||||
//std::cout << jac[b].coeff(i,j) << std::endl;
|
||||
output.derivatives[b] = jac[b].coeff(i,j);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
EvalWell extractDenseADWell(const ADB& data, int i) const
|
||||
{
|
||||
EvalWell output = 0.0;
|
||||
output.value = data.value()[i];
|
||||
const int nw = wells().number_of_wells;
|
||||
const int np = wells().number_of_phases;
|
||||
const std::vector<Opm::AutoDiffMatrix>& jac = data.derivative();
|
||||
//std::cout << jac.size() << std::endl;
|
||||
int numblocs = jac.size();
|
||||
for (int b = 0; b < np; ++b) {
|
||||
output.derivatives[b+np] = jac[numblocs-1].coeff(i, b*nw + i);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
const ADB convertToADB(const std::vector<EvalWell>& local, const std::vector<int>& well_cells, const int nc, const std::vector<int>& well_id, const int nw, const int numVars) const
|
||||
{
|
||||
typedef typename ADB::M M;
|
||||
const int nLocal = local.size();
|
||||
typename ADB::V value( nLocal );
|
||||
//const int numVars = 5;
|
||||
const int np = wells().number_of_phases;
|
||||
|
||||
std::vector<Eigen::SparseMatrix<double>> mat(np, Eigen::SparseMatrix<double>(nLocal,nc));
|
||||
Eigen::SparseMatrix<double> matFlux(nLocal,np*nw);
|
||||
Eigen::SparseMatrix<double> matBHP(nLocal,nw);
|
||||
|
||||
for( int i=0; i<nLocal; ++i )
|
||||
{
|
||||
value[ i ] = local[ i ].value;
|
||||
for( int d=0; d<np; ++d ) {
|
||||
//std::cout << i << " " <<d << " "<<local[i].derivatives[d] << std::endl;
|
||||
mat[d].insert(i, well_cells[i]) = local[i].derivatives[d];
|
||||
}
|
||||
|
||||
for (int phase = 0; phase < np; ++phase) {
|
||||
//std::cout << "well: "<< i << " " << phase << " " << local[i].derivatives[np + phase] << std::endl;
|
||||
|
||||
matFlux.insert(i, nw*phase + well_id[i]) = local[i].derivatives[np + phase];
|
||||
}
|
||||
//matBHP.insert(i,well_id[i]) = local[i].derivatives[2*np];
|
||||
}
|
||||
|
||||
std::vector< M > jacs( numVars );
|
||||
if (numVars == 4) {
|
||||
for( int d=0; d<np; ++d ) {
|
||||
//Eigen::DiagonalMatrix<double>(deri[d]);
|
||||
jacs[ d ] = M(mat[d]);
|
||||
}
|
||||
|
||||
jacs[3] = M(matFlux);
|
||||
//jacs[4] = M(matBHP);
|
||||
}
|
||||
else if (numVars == 1) {
|
||||
jacs[0] = M(matFlux);
|
||||
//jacs[1] = M(matBHP);
|
||||
}
|
||||
//std::cout << numVars << std::endl;
|
||||
|
||||
return ADB::function( std::move( value ), std::move( jacs ));
|
||||
}
|
||||
|
||||
|
||||
template <class WellState>
|
||||
void updatePerfPhaseRatesAndPressures(const std::vector<ADB>& cq_s,
|
||||
WellState& xw) const
|
||||
{
|
||||
if ( !localWellsActive() )
|
||||
{
|
||||
// If there are no wells in the subdomain of the proces then
|
||||
// cq_s has zero size and will cause a segmentation fault below.
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the perforation phase rates (used to calculate the pressure drop in the wellbore).
|
||||
const int np = wells().number_of_phases;
|
||||
const int nw = wells().number_of_wells;
|
||||
const int nperf = wells().well_connpos[nw];
|
||||
|
||||
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);
|
||||
}
|
||||
xw.perfPhaseRates().assign(cq.data(), cq.data() + nperf*np);
|
||||
|
||||
|
||||
|
||||
// Update the perforation pressures.
|
||||
const V& cdp = wellPerforationPressureDiffs();
|
||||
for (int w = 0; w < nw; ++w ) {
|
||||
for (int perf = wells().well_connpos[w] ; perf < wells().well_connpos[w+1]; ++perf) {
|
||||
xw.perfPress()[perf] = cdp[perf] + xw.bhp()[w];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename intensiveQuants>
|
||||
void
|
||||
addWellFluxEq(std::vector<ADB> cq_s,
|
||||
const double dt,
|
||||
const int numBlocks,
|
||||
LinearisedBlackoilResidual& residual,
|
||||
Mat& duneD,
|
||||
BVector& resWell)
|
||||
computeWellFlux(const int& w, const double& Tw, const intensiveQuants& intQuants, const double& cdp, std::vector<EvalWell>& cq_s) const
|
||||
{
|
||||
|
||||
const int np = wells().number_of_phases;
|
||||
const int nw = wells().number_of_wells;
|
||||
|
||||
double volume = 0.002831684659200; // 0.1 cu ft;
|
||||
//std::vector<ADB> F = wellVolumeFractions(state);
|
||||
//std::cout << F0_[0] << std::endl;
|
||||
//std::cout << F[0] << std::endl;
|
||||
//std::cout << "før Ebos" <<residual_.well_flux_eq << std::endl;
|
||||
//ADB qs = ADB::constant(ADB::V::Zero(np*nw));
|
||||
for (int p = 0; p < np; ++p) {
|
||||
|
||||
std::vector<EvalWell> res_vec(nw);
|
||||
for (int w = 0; w < nw; ++w) {
|
||||
|
||||
EvalWell res = (wellVolumeFraction(w, p) - F0_[w + nw*p]) * volume / dt;
|
||||
res += getQs(w, p);
|
||||
//for (int perf = wells().well_connpos[w] ; perf < wells().well_connpos[w+1]; ++perf) {
|
||||
// res -= cq_s[perf*np + p];
|
||||
//}
|
||||
res_vec[w] = res;
|
||||
for (int i = 0; i < np; ++i) {
|
||||
duneD.entry(w,w)[flowPhaseToEbosCompIdx(p)][flowToEbosPvIdx(i)] += res.derivatives[i+3];
|
||||
}
|
||||
resWell[w][flowPhaseToEbosCompIdx(p)] += res.value;
|
||||
}
|
||||
|
||||
// ADB tmp = convertToADBWell(res_vec, numBlocks);
|
||||
// qs += superset(tmp,Span(nw,1,p*nw), nw*np);
|
||||
}
|
||||
|
||||
//wellModel().convertToADB(res_vec, well_cells, nc, well_id, nw, numBlocks);
|
||||
//ADB qs = state.qs;
|
||||
// for (int phase = 0; phase < np; ++phase) {
|
||||
// qs -= superset(wellOps().p2w * cq_s[phase], Span(nw, 1, phase*nw), nw*np);
|
||||
// //qs += superset((F[phase]-F0_[phase]) * vol_dt, Span(nw,1,phase*nw), nw*np);
|
||||
// }
|
||||
|
||||
// residual.well_flux_eq = qs;
|
||||
//std::cout << "etter Ebos" << residual_.well_flux_eq << std::endl;
|
||||
|
||||
}
|
||||
const AutoDiffBlock<double> convertToADBWell(const std::vector<EvalWell>& local, const int numVars) const
|
||||
{
|
||||
typedef typename ADB::M M;
|
||||
const int nLocal = local.size();
|
||||
typename ADB::V value( nLocal );
|
||||
//const int numVars = 5;
|
||||
const int np = wells().number_of_phases;
|
||||
const int nw = wells().number_of_wells;
|
||||
|
||||
Eigen::SparseMatrix<double> matFlux(nLocal,np*nw);
|
||||
|
||||
for( int i=0; i<nLocal; ++i )
|
||||
{
|
||||
value[ i ] = local[ i ].value;
|
||||
for (int phase = 0; phase < np; ++phase) {
|
||||
matFlux.insert(i, nw*phase + i) = local[i].derivatives[np + phase];
|
||||
}
|
||||
}
|
||||
|
||||
std::vector< M > jacs( numVars );
|
||||
if (numVars == 4) {
|
||||
for( int d=0; d<np; ++d ) {
|
||||
//Eigen::DiagonalMatrix<double>(deri[d]);
|
||||
//jacs[ d ] = M(mat[d]);
|
||||
}
|
||||
|
||||
jacs[3] = M(matFlux);
|
||||
//jacs[4] = M(matBHP);
|
||||
}
|
||||
else if (numVars == 1) {
|
||||
jacs[0] = M(matFlux);
|
||||
//jacs[1] = M(matBHP);
|
||||
}
|
||||
//std::cout << numVars << std::endl;
|
||||
|
||||
return ADB::function( std::move( value ), std::move( jacs ));
|
||||
}
|
||||
|
||||
|
||||
template<typename Simulator>
|
||||
void
|
||||
computeWellFluxDense(const Simulator& ebosSimulator,
|
||||
std::vector<ADB>& cq_s,
|
||||
const int numBlocks,
|
||||
Mat& duneA,
|
||||
Mat& duneB,
|
||||
Mat& duneC,
|
||||
Mat& duneD,
|
||||
BVector& rhs,
|
||||
BVector& resWell,
|
||||
WellState& well_state) const
|
||||
{
|
||||
if( ! localWellsActive() ) return ;
|
||||
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 = wellOps().well_cells;
|
||||
std::vector<int> well_id(nperf);
|
||||
std::vector<std::vector<EvalWell>> cq_s_dense(np, std::vector<EvalWell>(nperf,0.0));
|
||||
|
||||
|
||||
// pressure diffs computed already (once per step, not changing per iteration)
|
||||
const V& cdp = wellPerforationPressureDiffs();
|
||||
|
||||
for (int w = 0; w < nw; ++w) {
|
||||
|
||||
EvalWell bhp = getBhp(w);
|
||||
|
||||
// TODO: fix for 2-phase case
|
||||
const int np = wells().number_of_phases;
|
||||
std::vector<EvalWell> cmix_s(np,0.0);
|
||||
for (int phase = 0; phase < np; ++phase) {
|
||||
//int ebosPhaseIdx = flowPhaseToEbosPhaseIdx(phase);
|
||||
cmix_s[phase] = wellVolumeFraction(w,phase);
|
||||
}
|
||||
|
||||
for (int perf = wells().well_connpos[w] ; perf < wells().well_connpos[w+1]; ++perf) {
|
||||
const int cell_idx = well_cells[perf];
|
||||
const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0));
|
||||
const auto& fs = intQuants.fluidState();
|
||||
well_id[perf] = w;
|
||||
EvalWell pressure = extendEval(fs.pressure(FluidSystem::oilPhaseIdx));
|
||||
EvalWell rs = extendEval(fs.Rs());
|
||||
EvalWell rv = extendEval(fs.Rv());
|
||||
@ -766,9 +537,7 @@ namespace Opm {
|
||||
}
|
||||
|
||||
// Pressure drawdown (also used to determine direction of flow)
|
||||
EvalWell well_pressure = bhp + cdp[perf];
|
||||
// Store the perforation pressure for later usage.
|
||||
well_state.perfPress()[perf] = well_pressure.value;
|
||||
EvalWell well_pressure = bhp + cdp;
|
||||
EvalWell drawdown = pressure - well_pressure;
|
||||
|
||||
// injection perforations
|
||||
@ -776,15 +545,14 @@ namespace Opm {
|
||||
|
||||
//Do nothing if crossflow is not allowed
|
||||
if (!wells().allow_cf[w] && wells().type[w] == INJECTOR)
|
||||
continue;
|
||||
return;
|
||||
// compute phase volumetric rates at standard conditions
|
||||
std::vector<EvalWell> cq_ps(np, 0.0);
|
||||
for (int phase = 0; phase < np; ++phase) {
|
||||
const EvalWell cq_p = - Tw[perf] * (mob_perfcells_dense[phase] * drawdown);
|
||||
const EvalWell cq_p = - Tw * (mob_perfcells_dense[phase] * drawdown);
|
||||
cq_ps[phase] = b_perfcells_dense[phase] * cq_p;
|
||||
}
|
||||
|
||||
|
||||
if ((*active_)[Oil] && (*active_)[Gas]) {
|
||||
const int oilpos = pu.phase_pos[Oil];
|
||||
const int gaspos = pu.phase_pos[Gas];
|
||||
@ -794,17 +562,15 @@ namespace Opm {
|
||||
cq_ps[oilpos] += rv * cq_psGas;
|
||||
}
|
||||
|
||||
|
||||
// map to ADB
|
||||
for (int phase = 0; phase < np; ++phase) {
|
||||
cq_s_dense[phase][perf] = cq_ps[phase];
|
||||
|
||||
cq_s[phase] = cq_ps[phase];
|
||||
}
|
||||
|
||||
} else {
|
||||
//Do nothing if crossflow is not allowed
|
||||
if (!wells().allow_cf[w] && wells().type[w] == PRODUCER)
|
||||
continue;
|
||||
return;
|
||||
|
||||
// Using total mobilities
|
||||
EvalWell total_mob_dense = mob_perfcells_dense[0];
|
||||
@ -812,7 +578,7 @@ namespace Opm {
|
||||
total_mob_dense += mob_perfcells_dense[phase];
|
||||
}
|
||||
// injection perforations total volume rates
|
||||
const EvalWell cqt_i = - Tw[perf] * (total_mob_dense * drawdown);
|
||||
const EvalWell cqt_i = - Tw * (total_mob_dense * drawdown);
|
||||
|
||||
// compute volume ratio between connection at standard conditions
|
||||
EvalWell volumeRatio = 0.0;
|
||||
@ -871,33 +637,11 @@ namespace Opm {
|
||||
EvalWell cqt_is = cqt_i/volumeRatio;
|
||||
//std::cout << "volrat " << volumeRatio << " " << volrat_perf_[perf] << std::endl;
|
||||
for (int phase = 0; phase < np; ++phase) {
|
||||
cq_s_dense[phase][perf] = cmix_s[phase] * cqt_is; // * b_perfcells_dense[phase];
|
||||
cq_s[phase] = cmix_s[phase] * cqt_is; // * b_perfcells_dense[phase];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int p1 = 0; p1 < np; ++p1) {
|
||||
EvalWell tmp = cq_s_dense[p1][perf];
|
||||
for (int p2 = 0; p2 < np; ++p2) {
|
||||
duneC.entry(w, cell_idx)[flowPhaseToEbosCompIdx(p1)][flowToEbosPvIdx(p2)] -= tmp.derivatives[p2];
|
||||
duneB.entry(cell_idx, w)[flowPhaseToEbosCompIdx(p1)][flowToEbosPvIdx(p2)] -= tmp.derivatives[p2+3];
|
||||
duneA.entry(cell_idx, cell_idx)[flowPhaseToEbosCompIdx(p1)][flowToEbosPvIdx(p2)] -= tmp.derivatives[p2];
|
||||
duneD.entry(w, w)[flowPhaseToEbosCompIdx(p1)][flowToEbosPvIdx(p2)] -= tmp.derivatives[p2+3];
|
||||
}
|
||||
rhs[cell_idx][flowPhaseToEbosCompIdx(p1)] -= tmp.value;
|
||||
resWell[w][flowPhaseToEbosCompIdx(p1)] -= tmp.value;
|
||||
// Store the perforation phase flux for later usage.
|
||||
well_state.perfPhaseRates()[perf*np + p1] = tmp.value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// cq_s.resize(np, ADB::null());
|
||||
// for (int phase = 0; phase < np; ++phase) {
|
||||
// cq_s[phase] = convertToADB(cq_s_dense[phase], well_cells, numCells(), well_id, nw, numBlocks);
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
template <typename Simulator>
|
||||
IterationReport solveWellEq(Simulator& ebosSimulator,
|
||||
@ -905,35 +649,13 @@ namespace Opm {
|
||||
WellState& well_state)
|
||||
{
|
||||
const int np = wells().number_of_phases;
|
||||
std::vector<ADB> cq_s(np, ADB::null());
|
||||
const int nw = wells().number_of_wells;
|
||||
WellState well_state0 = well_state;
|
||||
|
||||
LinearisedBlackoilResidual residual( { std::vector<ADB>(3, ADB::null()),
|
||||
ADB::null(),
|
||||
ADB::null(),
|
||||
{ 1.1169, 1.0031, 0.0031 }, // the default magic numbers
|
||||
false } );
|
||||
|
||||
int it = 0;
|
||||
bool converged;
|
||||
do {
|
||||
// bhp and Q for the wells
|
||||
int nw = wells().number_of_wells;
|
||||
int nc = numCells();
|
||||
Mat duneDlo( nw, nw, 9, 0.4,Mat::implicit);
|
||||
Mat duneBlo( nc, nw, 9, 0.4, Mat::implicit);
|
||||
Mat duneClo( nw, nc, 9, 0.4, Mat::implicit);
|
||||
Mat duneAlo( nc, nc, 9, 0.4, Mat::implicit);
|
||||
BVector rhslo(nc);
|
||||
BVector resWell(nw);
|
||||
computeWellFluxDense(ebosSimulator, cq_s, 1, duneAlo, duneBlo, duneClo, duneDlo, rhslo, resWell, well_state);
|
||||
//updatePerfPhaseRatesAndPressures(cq_s, well_state);
|
||||
addWellFluxEq(cq_s, dt, 1, residual, duneDlo, resWell);
|
||||
|
||||
duneDlo.compress();
|
||||
localInvert(duneDlo);
|
||||
|
||||
resWell_ = resWell;
|
||||
assembleWellEq(ebosSimulator, dt, well_state);
|
||||
converged = getWellConvergence(ebosSimulator, it);
|
||||
|
||||
if (converged) {
|
||||
@ -944,7 +666,7 @@ namespace Opm {
|
||||
if( localWellsActive() )
|
||||
{
|
||||
BVector dx_new (nw);
|
||||
duneDlo.mv(resWell_, dx_new);
|
||||
invDuneD_.mv(resWell_, dx_new);
|
||||
|
||||
V dx_new_eigen(np*nw);
|
||||
for( int p=0; p<np; ++p) {
|
||||
@ -1082,12 +804,7 @@ namespace Opm {
|
||||
std::vector<double> rvmax_perf;
|
||||
std::vector<double> surf_dens_perf;
|
||||
computePropertiesForWellConnectionPressures(ebosSimulator, xw, b_perf, rsmax_perf, rvmax_perf, surf_dens_perf);
|
||||
|
||||
const V& pdepth = perf_cell_depth_;
|
||||
const int nperf = wells().well_connpos[wells().number_of_wells];
|
||||
const std::vector<double> depth_perf(pdepth.data(), pdepth.data() + nperf);
|
||||
|
||||
computeWellConnectionDensitesPressures(xw, b_perf, rsmax_perf, rvmax_perf, surf_dens_perf, depth_perf, gravity_);
|
||||
computeWellConnectionDensitesPressures(xw, b_perf, rsmax_perf, rvmax_perf, surf_dens_perf, cell_depths_, gravity_);
|
||||
|
||||
}
|
||||
|
||||
@ -1102,29 +819,23 @@ namespace Opm {
|
||||
{
|
||||
const int nperf = wells().well_connpos[wells().number_of_wells];
|
||||
const int nw = wells().number_of_wells;
|
||||
const std::vector<int>& well_cells = wellOps().well_cells;
|
||||
const PhaseUsage& pu = fluid_->phaseUsage();
|
||||
const int np = fluid_->numPhases();
|
||||
b_perf.resize(nperf*np);
|
||||
rsmax_perf.resize(nperf);
|
||||
rvmax_perf.resize(nperf);
|
||||
|
||||
std::vector<PhasePresence> perf_cond(nperf);
|
||||
for (int perf = 0; perf < nperf; ++perf) {
|
||||
perf_cond[perf] = (*phase_condition_)[well_cells[perf]];
|
||||
}
|
||||
surf_dens_perf.resize(nperf*np);
|
||||
|
||||
// Compute the average pressure in each well block
|
||||
const V perf_press = Eigen::Map<const V>(xw.perfPress().data(), nperf);
|
||||
//V avg_press = perf_press*0;
|
||||
for (int w = 0; w < nw; ++w) {
|
||||
for (int perf = wells().well_connpos[w]; perf < wells().well_connpos[w+1]; ++perf) {
|
||||
const int cell_idx = well_cells[perf];
|
||||
|
||||
const int cell_idx = wells().well_cells[perf];
|
||||
const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0));
|
||||
const auto& fs = intQuants.fluidState();
|
||||
|
||||
const double p_above = perf == wells().well_connpos[w] ? xw.bhp()[w] : perf_press[perf - 1];
|
||||
const double p_avg = (perf_press[perf] + p_above)/2;
|
||||
const double p_above = perf == wells().well_connpos[w] ? xw.bhp()[w] : xw.perfPress()[perf - 1];
|
||||
const double p_avg = (xw.perfPress()[perf] + p_above)/2;
|
||||
double temperature = fs.temperature(FluidSystem::oilPhaseIdx).value;
|
||||
|
||||
if (pu.phase_used[BlackoilPhases::Aqua]) {
|
||||
@ -1132,10 +843,11 @@ namespace Opm {
|
||||
FluidSystem::waterPvt().inverseFormationVolumeFactor(fs.pvtRegionIndex(), temperature, p_avg);
|
||||
}
|
||||
|
||||
const PhasePresence& perf_cond = (*phase_condition_)[wells().well_cells[perf]];
|
||||
|
||||
if (pu.phase_used[BlackoilPhases::Vapour]) {
|
||||
int gaspos = pu.phase_pos[BlackoilPhases::Vapour] + perf * pu.num_phases;
|
||||
if (perf_cond[perf].hasFreeOil()) {
|
||||
if (perf_cond.hasFreeOil()) {
|
||||
b_perf[gaspos] = FluidSystem::gasPvt().saturatedInverseFormationVolumeFactor(fs.pvtRegionIndex(), temperature, p_avg);
|
||||
}
|
||||
else {
|
||||
@ -1146,7 +858,7 @@ namespace Opm {
|
||||
|
||||
if (pu.phase_used[BlackoilPhases::Liquid]) {
|
||||
int oilpos = pu.phase_pos[BlackoilPhases::Liquid] + perf * pu.num_phases;
|
||||
if (perf_cond[perf].hasFreeGas()) {
|
||||
if (perf_cond.hasFreeGas()) {
|
||||
b_perf[oilpos] = FluidSystem::oilPvt().saturatedInverseFormationVolumeFactor(fs.pvtRegionIndex(), temperature, p_avg);
|
||||
}
|
||||
else {
|
||||
@ -1160,38 +872,15 @@ namespace Opm {
|
||||
rvmax_perf[perf] = FluidSystem::gasPvt().saturatedOilVaporizationFactor(fs.pvtRegionIndex(), temperature, p_avg);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Surface density.
|
||||
// The compute density segment wants the surface densities as
|
||||
// an np * number of wells cells array
|
||||
V rho = superset(fluid_->surfaceDensity(0 , well_cells), Span(nperf, pu.num_phases, 0), nperf*pu.num_phases);
|
||||
for (int phase = 1; phase < pu.num_phases; ++phase) {
|
||||
rho += superset(fluid_->surfaceDensity(phase , well_cells), Span(nperf, pu.num_phases, phase), nperf*pu.num_phases);
|
||||
for (int p = 0; p < pu.num_phases; ++p) {
|
||||
surf_dens_perf[np*perf + p] = FluidSystem::referenceDensity( flowPhaseToEbosPhaseIdx( p ), fs.pvtRegionIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
surf_dens_perf.assign(rho.data(), rho.data() + nperf * pu.num_phases);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
addWellContributionToMassBalanceEq(const std::vector<ADB>& cq_s,
|
||||
LinearisedBlackoilResidual& residual)
|
||||
{
|
||||
if ( !localWellsActive() )
|
||||
{
|
||||
// If there are no wells in the subdomain of the proces then
|
||||
// cq_s has zero size and will cause a segmentation fault below.
|
||||
return;
|
||||
}
|
||||
|
||||
// Add well contributions to mass balance equations
|
||||
const int np = numPhases();
|
||||
for (int phase = 0; phase < np; ++phase) {
|
||||
residual.material_balance_eq[phase] -= superset(cq_s[phase], wellOps().well_cells, numCells());
|
||||
}
|
||||
}
|
||||
|
||||
template <class WellState>
|
||||
void updateWellState(const Vector& dwells,
|
||||
@ -1636,125 +1325,6 @@ namespace Opm {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename Simulator, class WellState>
|
||||
void computeWellConnectionPressures(const Simulator& ebosSimulator,
|
||||
const WellState& xw)
|
||||
{
|
||||
if( ! localWellsActive() ) return ;
|
||||
// 1. Compute properties required by computeConnectionPressureDelta().
|
||||
// Note that some of the complexity of this part is due to the function
|
||||
// taking std::vector<double> arguments, and not Eigen objects.
|
||||
std::vector<double> b_perf;
|
||||
std::vector<double> rsmax_perf;
|
||||
std::vector<double> rvmax_perf;
|
||||
std::vector<double> surf_dens_perf;
|
||||
computePropertiesForWellConnectionPressures(ebosSimulator, xw, b_perf, rsmax_perf, rvmax_perf, surf_dens_perf);
|
||||
|
||||
const Vector& pdepth = perf_cell_depth_;
|
||||
const int nperf = wells().well_connpos[wells().number_of_wells];
|
||||
const std::vector<double> depth_perf(pdepth.data(), pdepth.data() + nperf);
|
||||
|
||||
computeWellConnectionDensitesPressures(xw, b_perf, rsmax_perf, rvmax_perf, surf_dens_perf, depth_perf, gravity_);
|
||||
|
||||
}
|
||||
|
||||
// state0 is non-constant, while it will not be used outside of the function
|
||||
template <class SolutionState, class WellState>
|
||||
void
|
||||
computeWellPotentials(const std::vector<ADB>& mob_perfcells,
|
||||
const std::vector<ADB>& b_perfcells,
|
||||
SolutionState& state0,
|
||||
WellState& well_state)
|
||||
{
|
||||
const int nw = wells().number_of_wells;
|
||||
const int np = wells().number_of_phases;
|
||||
const Opm::PhaseUsage& pu = fluid_->phaseUsage();
|
||||
|
||||
Vector bhps = Vector::Zero(nw);
|
||||
for (int w = 0; w < nw; ++w) {
|
||||
const WellControls* ctrl = wells().ctrls[w];
|
||||
const int nwc = well_controls_get_num(ctrl);
|
||||
//Loop over all controls until we find a BHP control
|
||||
//or a THP control that specifies what we need.
|
||||
//Pick the value that gives the most restrictive flow
|
||||
for (int ctrl_index=0; ctrl_index < nwc; ++ctrl_index) {
|
||||
|
||||
if (well_controls_iget_type(ctrl, ctrl_index) == BHP) {
|
||||
bhps[w] = well_controls_iget_target(ctrl, ctrl_index);
|
||||
}
|
||||
|
||||
if (well_controls_iget_type(ctrl, ctrl_index) == THP) {
|
||||
double aqua = 0.0;
|
||||
double liquid = 0.0;
|
||||
double vapour = 0.0;
|
||||
|
||||
if ((*active_)[ Water ]) {
|
||||
aqua = well_state.wellRates()[w*np + pu.phase_pos[ Water ] ];
|
||||
}
|
||||
if ((*active_)[ Oil ]) {
|
||||
liquid = well_state.wellRates()[w*np + pu.phase_pos[ Oil ] ];
|
||||
}
|
||||
if ((*active_)[ Gas ]) {
|
||||
vapour = well_state.wellRates()[w*np + pu.phase_pos[ Gas ] ];
|
||||
}
|
||||
|
||||
const int vfp = well_controls_iget_vfp(ctrl, ctrl_index);
|
||||
const double& thp = well_controls_iget_target(ctrl, ctrl_index);
|
||||
const double& alq = well_controls_iget_alq(ctrl, ctrl_index);
|
||||
|
||||
//Set *BHP* target by calculating bhp from THP
|
||||
const WellType& well_type = wells().type[w];
|
||||
|
||||
if (well_type == INJECTOR) {
|
||||
double dp = wellhelpers::computeHydrostaticCorrection(
|
||||
wells(), w, vfp_properties_->getInj()->getTable(vfp)->getDatumDepth(),
|
||||
wellPerforationDensities(), gravity_);
|
||||
const double bhp = vfp_properties_->getInj()->bhp(vfp, aqua, liquid, vapour, thp) - dp;
|
||||
// apply the strictest of the bhp controlls i.e. smallest bhp for injectors
|
||||
if ( bhp < bhps[w]) {
|
||||
bhps[w] = bhp;
|
||||
}
|
||||
}
|
||||
else if (well_type == PRODUCER) {
|
||||
double dp = wellhelpers::computeHydrostaticCorrection(
|
||||
wells(), w, vfp_properties_->getProd()->getTable(vfp)->getDatumDepth(),
|
||||
wellPerforationDensities(), gravity_);
|
||||
|
||||
const double bhp = vfp_properties_->getProd()->bhp(vfp, aqua, liquid, vapour, thp, alq) - dp;
|
||||
// apply the strictest of the bhp controlls i.e. largest bhp for producers
|
||||
if ( bhp > bhps[w]) {
|
||||
bhps[w] = bhp;
|
||||
}
|
||||
}
|
||||
else {
|
||||
OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type of well");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// use bhp limit from control
|
||||
state0.bhp = ADB::constant(bhps);
|
||||
|
||||
// compute well potentials
|
||||
Vector aliveWells;
|
||||
std::vector<ADB> well_potentials;
|
||||
computeWellFlux(state0, mob_perfcells, b_perfcells, aliveWells, well_potentials);
|
||||
|
||||
// store well potentials in the well state
|
||||
// transform to a single vector instead of separate vectors pr phase
|
||||
const int nperf = wells().well_connpos[nw];
|
||||
Vector cq = superset(well_potentials[0].value(), Span(nperf, np, 0), nperf*np);
|
||||
for (int phase = 1; phase < np; ++phase) {
|
||||
cq += superset(well_potentials[phase].value(), Span(nperf, np, phase), nperf*np);
|
||||
}
|
||||
well_state.wellPotentials().assign(cq.data(), cq.data() + nperf*np);
|
||||
}
|
||||
|
||||
|
||||
/// If set, computeWellFlux() will additionally store the
|
||||
/// total reservoir volume perforation fluxes.
|
||||
void setStoreWellPerforationFluxesFlag(const bool store_fluxes)
|
||||
@ -1910,7 +1480,6 @@ namespace Opm {
|
||||
protected:
|
||||
bool wells_active_;
|
||||
const Wells* wells_;
|
||||
const WellOps wops_;
|
||||
ModelParameters param_;
|
||||
bool terminal_output_;
|
||||
|
||||
@ -1921,7 +1490,8 @@ namespace Opm {
|
||||
double gravity_;
|
||||
// the depth of the all the cell centers
|
||||
// for standard Wells, it the same with the perforation depth
|
||||
Vector perf_cell_depth_;
|
||||
std::vector<double> cell_depths_;
|
||||
std::vector<double> pv_;
|
||||
|
||||
Vector well_perforation_densities_;
|
||||
Vector well_perforation_pressure_diffs_;
|
||||
@ -1931,7 +1501,7 @@ namespace Opm {
|
||||
|
||||
std::vector<EvalWell> wellVariables_;
|
||||
std::vector<double> F0_;
|
||||
const std::vector<double>& pv_;
|
||||
|
||||
Mat duneA_;
|
||||
Mat duneB_;
|
||||
Mat duneC_;
|
||||
|
Loading…
Reference in New Issue
Block a user