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:
Tor Harald Sandve 2016-09-06 08:27:57 +02:00
parent 1d2a5d7f1b
commit 952ccf8338
2 changed files with 259 additions and 1183 deletions

View File

@ -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,

View File

@ -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_;