/// Solve the linear system Ax = b, with A being the /// combined derivative matrix of the residual and b /// being the residual itself. /// \param[in] residual residual object containing A and b. /// \return the solution x NewtonIterationBlackoilSimple::SolutionVector NewtonIterationBlackoilSimple::computeNewtonIncrement(const LinearisedBlackoilResidual& residual) const { typedef LinearisedBlackoilResidual::ADB ADB; const int np = residual.material_balance_eq.size(); ADB mass_res = residual.material_balance_eq[0]; for (int phase = 1; phase < np; ++phase) { mass_res = vertcat(mass_res, residual.material_balance_eq[phase]); } const ADB well_res = vertcat(residual.well_flux_eq, residual.well_eq); const ADB total_residual = collapseJacs(vertcat(mass_res, well_res)); Eigen::SparseMatrix<double, Eigen::RowMajor> matr; total_residual.derivative()[0].toSparse(matr); SolutionVector dx(SolutionVector::Zero(total_residual.size())); Opm::LinearSolverInterface::LinearSolverReport rep = linsolver_->solve(matr.rows(), matr.nonZeros(), matr.outerIndexPtr(), matr.innerIndexPtr(), matr.valuePtr(), total_residual.value().data(), dx.data(), parallelInformation_); // store iterations iterations_ = rep.iterations; if (!rep.converged) { OPM_THROW(LinearSolverProblem, "FullyImplicitBlackoilSolver::solveJacobianSystem(): " "Linear solver convergence failure."); } return dx; }
ADB PolymerPropsAd::polymerWaterVelocityRatio(const ADB& c) const { const int nc = c.size(); V mc(nc); V dmc(nc); for (int i = 0; i < nc; ++i) { double m = 0; double dm = 0; polymer_props_.computeMcWithDer(c.value()(i), m, dm); mc(i) = m; dmc(i) = dm; } ADB::M dmc_diag(dmc.matrix().asDiagonal()); const int num_blocks = c.numBlocks(); std::vector<ADB::M> jacs(num_blocks); for (int block = 0; block < num_blocks; ++block) { jacs[block] = dmc_diag * c.derivative()[block]; } return ADB::function(std::move(mc), std::move(jacs)); }
/// Gas formation volume factor. /// \param[in] pg Array of n gas pressure values. /// \param[in] cells Array of n cell indices to be associated with the pressure values. /// \return Array of n formation volume factor values. ADB BlackoilPropsAdFromDeck::bGas(const ADB& pg, const Cells& cells) const { if (!phase_usage_.phase_used[Gas]) { OPM_THROW(std::runtime_error, "Cannot call muGas(): gas phase not present."); } const int n = cells.size(); assert(pg.size() == n); V b(n); V dbdp(n); V dbdr(n); const double* rs = 0; props_[phase_usage_.phase_pos[Gas]]->b(n, pg.value().data(), rs, b.data(), dbdp.data(), dbdr.data()); ADB::M dbdp_diag = spdiag(dbdp); const int num_blocks = pg.numBlocks(); std::vector<ADB::M> jacs(num_blocks); for (int block = 0; block < num_blocks; ++block) { jacs[block] = dbdp_diag * pg.derivative()[block]; } return ADB::function(b, jacs); }
/// Oil formation volume factor. /// \param[in] po Array of n oil pressure values. /// \param[in] rs Array of n gas solution factor values. /// \param[in] cells Array of n cell indices to be associated with the pressure values. /// \return Array of n formation volume factor values. ADB BlackoilPropsAdFromDeck::bOil(const ADB& po, const ADB& rs, const Cells& cells) const { if (!phase_usage_.phase_used[Oil]) { OPM_THROW(std::runtime_error, "Cannot call muOil(): oil phase not present."); } const int n = cells.size(); assert(po.size() == n); V b(n); V dbdp(n); V dbdr(n); props_[phase_usage_.phase_pos[Oil]]->b(n, po.value().data(), rs.value().data(), b.data(), dbdp.data(), dbdr.data()); ADB::M dbdp_diag = spdiag(dbdp); ADB::M dbdr_diag = spdiag(dbdr); const int num_blocks = po.numBlocks(); std::vector<ADB::M> jacs(num_blocks); for (int block = 0; block < num_blocks; ++block) { jacs[block] = dbdp_diag * po.derivative()[block] + dbdr_diag * rs.derivative()[block]; } return ADB::function(b, jacs); }
ADB SolventPropsAdFromDeck::miscibleResidualOilSaturationFunction (const ADB& Sw, const Cells& cells) const { if (sorwmis_.size()>0) { return SolventPropsAdFromDeck::makeADBfromTables(Sw, cells, cellMiscRegionIdx_, sorwmis_); } // return zeros if not specified return ADB::constant(V::Zero(Sw.size())); }
ADB SolventPropsAdFromDeck::pressureMiscibilityFunction(const ADB& po, const Cells& cells) const { if (pmisc_.size() > 0) { return SolventPropsAdFromDeck::makeADBfromTables(po, cells, cellMiscRegionIdx_, pmisc_); } // return ones if not specified i.e. no effect. return ADB::constant(V::Constant(po.size(), 1.0)); }
ADB PolymerPropsAd::effectiveInvWaterVisc(const ADB& c, const V& mu_w) const { assert(c.size() == mu_w.size()); const int nc = c.size(); V inv_mu_w_eff(nc); V dinv_mu_w_eff(nc); for (int i = 0; i < nc; ++i) { double im = 0, dim = 0; polymer_props_.effectiveInvViscWithDer(c.value()(i), mu_w(i), im, dim); inv_mu_w_eff(i) = im; dinv_mu_w_eff(i) = dim; } ADB::M dim_diag(dinv_mu_w_eff.matrix().asDiagonal()); const int num_blocks = c.numBlocks(); std::vector<ADB::M> jacs(num_blocks); for (int block = 0; block < num_blocks; ++block) { jacs[block] = dim_diag * c.derivative()[block]; } return ADB::function(std::move(inv_mu_w_eff), std::move(jacs)); }
/// Bubble point curve for Rs as function of oil pressure. /// \param[in] po Array of n oil pressure values. /// \param[in] cells Array of n cell indices to be associated with the pressure values. /// \return Array of n bubble point values for Rs. ADB BlackoilPropsAdFromDeck::rsMax(const ADB& po, const Cells& cells) const { if (!phase_usage_.phase_used[Oil]) { OPM_THROW(std::runtime_error, "Cannot call rsMax(): oil phase not present."); } const int n = cells.size(); assert(po.size() == n); V rbub(n); V drbubdp(n); props_[Oil]->rbub(n, po.value().data(), rbub.data(), drbubdp.data()); ADB::M drbubdp_diag = spdiag(drbubdp); const int num_blocks = po.numBlocks(); std::vector<ADB::M> jacs(num_blocks); for (int block = 0; block < num_blocks; ++block) { jacs[block] = drbubdp_diag * po.derivative()[block]; } return ADB::function(rbub, jacs); }
ADB PolymerPropsAd::viscMult(const ADB& c) const { const int nc = c.size(); V visc_mult(nc); V dvisc_mult(nc); for (int i = 0; i < nc; ++i) { double im = 0, dim = 0; im = polymer_props_.viscMultWithDer(c.value()(i), &dim); visc_mult(i) = im; dvisc_mult(i) = dim; } ADB::M dim_diag(dvisc_mult.matrix().asDiagonal()); const int num_blocks = c.numBlocks(); std::vector<ADB::M> jacs(num_blocks); for (int block = 0; block < num_blocks; ++block) { jacs[block] = dim_diag * c.derivative()[block]; } return ADB::function(std::move(visc_mult), std::move(jacs)); }
/// Water viscosity. /// \param[in] pw Array of n water pressure values. /// \param[in] cells Array of n cell indices to be associated with the pressure values. /// \return Array of n viscosity values. ADB BlackoilPropsAdFromDeck::muWat(const ADB& pw, const Cells& cells) const { if (!phase_usage_.phase_used[Water]) { OPM_THROW(std::runtime_error, "Cannot call muWat(): water phase not present."); } const int n = cells.size(); assert(pw.size() == n); V mu(n); V dmudp(n); V dmudr(n); const double* rs = 0; props_[phase_usage_.phase_pos[Water]]->mu(n, pw.value().data(), rs, mu.data(), dmudp.data(), dmudr.data()); ADB::M dmudp_diag = spdiag(dmudp); const int num_blocks = pw.numBlocks(); std::vector<ADB::M> jacs(num_blocks); for (int block = 0; block < num_blocks; ++block) { jacs[block] = dmudp_diag * pw.derivative()[block]; } return ADB::function(mu, jacs); }
VFPProdProperties::ADB VFPProdProperties::bhp(const std::vector<int>& table_id, const ADB& aqua, const ADB& liquid, const ADB& vapour, const ADB& thp_arg, const ADB& alq) const { const int nw = thp_arg.size(); std::vector<int> block_pattern = detail::commonBlockPattern(aqua, liquid, vapour, thp_arg, alq); assert(static_cast<int>(table_id.size()) == nw); assert(aqua.size() == nw); assert(liquid.size() == nw); assert(vapour.size() == nw); assert(thp_arg.size() == nw); assert(alq.size() == nw); //Allocate data for bhp's and partial derivatives ADB::V value = ADB::V::Zero(nw); ADB::V dthp = ADB::V::Zero(nw); ADB::V dwfr = ADB::V::Zero(nw); ADB::V dgfr = ADB::V::Zero(nw); ADB::V dalq = ADB::V::Zero(nw); ADB::V dflo = ADB::V::Zero(nw); //Get the table for each well std::vector<const VFPProdTable*> well_tables(nw, nullptr); for (int i=0; i<nw; ++i) { if (table_id[i] >= 0) { well_tables[i] = detail::getTable(m_tables, table_id[i]); } } //Get the right FLO/GFR/WFR variable for each well as a single ADB const ADB flo = detail::combineADBVars<VFPProdTable::FLO_TYPE>(well_tables, aqua, liquid, vapour); const ADB wfr = detail::combineADBVars<VFPProdTable::WFR_TYPE>(well_tables, aqua, liquid, vapour); const ADB gfr = detail::combineADBVars<VFPProdTable::GFR_TYPE>(well_tables, aqua, liquid, vapour); //Compute the BHP for each well independently for (int i=0; i<nw; ++i) { const VFPProdTable* table = well_tables[i]; if (table != nullptr) { //First, find the values to interpolate between //Value of FLO is negative in OPM for producers, but positive in VFP table auto flo_i = detail::findInterpData(-flo.value()[i], table->getFloAxis()); auto thp_i = detail::findInterpData( thp_arg.value()[i], table->getTHPAxis()); auto wfr_i = detail::findInterpData( wfr.value()[i], table->getWFRAxis()); auto gfr_i = detail::findInterpData( gfr.value()[i], table->getGFRAxis()); auto alq_i = detail::findInterpData( alq.value()[i], table->getALQAxis()); detail::VFPEvaluation bhp_val = detail::interpolate(table->getTable(), flo_i, thp_i, wfr_i, gfr_i, alq_i); value[i] = bhp_val.value; dthp[i] = bhp_val.dthp; dwfr[i] = bhp_val.dwfr; dgfr[i] = bhp_val.dgfr; dalq[i] = bhp_val.dalq; dflo[i] = bhp_val.dflo; } else { value[i] = -1e100; //Signal that this value has not been calculated properly, due to "missing" table } } //Create diagonal matrices from ADB::Vs ADB::M dthp_diag(dthp.matrix().asDiagonal()); ADB::M dwfr_diag(dwfr.matrix().asDiagonal()); ADB::M dgfr_diag(dgfr.matrix().asDiagonal()); ADB::M dalq_diag(dalq.matrix().asDiagonal()); ADB::M dflo_diag(dflo.matrix().asDiagonal()); //Calculate the Jacobians const int num_blocks = block_pattern.size(); std::vector<ADB::M> jacs(num_blocks); for (int block = 0; block < num_blocks; ++block) { //Could have used fastSparseProduct and temporary variables //but may not save too much on that. jacs[block] = ADB::M(nw, block_pattern[block]); if (!thp_arg.derivative().empty()) { jacs[block] += dthp_diag * thp_arg.derivative()[block]; } if (!wfr.derivative().empty()) { jacs[block] += dwfr_diag * wfr.derivative()[block]; } if (!gfr.derivative().empty()) { jacs[block] += dgfr_diag * gfr.derivative()[block]; } if (!alq.derivative().empty()) { jacs[block] += dalq_diag * alq.derivative()[block]; } if (!flo.derivative().empty()) { jacs[block] -= dflo_diag * flo.derivative()[block]; } } ADB retval = ADB::function(std::move(value), std::move(jacs)); return retval; }