/// Relative permeabilities for all phases. /// \param[in] sw Array of n water saturation values. /// \param[in] so Array of n oil saturation values. /// \param[in] sg Array of n gas saturation values. /// \param[in] cells Array of n cell indices to be associated with the saturation values. /// \return An std::vector with 3 elements, each an array of n relperm values, /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. std::vector<V> BlackoilPropsAdFromDeck::relperm(const V& sw, const V& so, const V& sg, const Cells& cells) const { const int n = cells.size(); const int np = numPhases(); Block s_all(n, np); if (phase_usage_.phase_used[Water]) { assert(sw.size() == n); s_all.col(phase_usage_.phase_pos[Water]) = sw; } if (phase_usage_.phase_used[Oil]) { assert(so.size() == n); s_all.col(phase_usage_.phase_pos[Oil]) = so; } if (phase_usage_.phase_used[Gas]) { assert(sg.size() == n); s_all.col(phase_usage_.phase_pos[Gas]) = sg; } Block kr(n, np); satprops_->relperm(n, s_all.data(), cells.data(), kr.data(), 0); std::vector<V> relperms; relperms.reserve(3); for (int phase = 0; phase < 3; ++phase) { if (phase_usage_.phase_used[phase]) { relperms.emplace_back(kr.col(phase_usage_.phase_pos[phase])); } else { relperms.emplace_back(); } } return relperms; }
void init(const UnstructuredGrid& g, const PhaseUsage& usedPhases) { usedPhases_ = usedPhases; press_.resize(g.number_of_cells, 0.0); fpress_.resize(g.number_of_faces, 0.0); flux_.resize(g.number_of_faces, 0.0); sat_.resize(numPhases() * g.number_of_cells, 0.0); for (int cell = 0; cell < g.number_of_cells; ++cell) { // Defaulting the second saturation to 1.0. // This will usually be oil in a water-oil case, // gas in an oil-gas case. // For proper initialization, one should not rely on this, // but use available phase information instead. sat_[numPhases()*cell + 1] = 1.0; } gor_.resize(g.number_of_cells, 0.0); }
std::vector<ADB> BlackoilPropsAd::capPress(const ADB& sw, const ADB& so, const ADB& sg, const Cells& cells) const { const int numCells = cells.size(); const int numActivePhases = numPhases(); const int numBlocks = so.numBlocks(); Block activeSat(numCells, numActivePhases); if (pu_.phase_used[Water]) { assert(sw.value().size() == numCells); activeSat.col(pu_.phase_pos[Water]) = sw.value(); } if (pu_.phase_used[Oil]) { assert(so.value().size() == numCells); activeSat.col(pu_.phase_pos[Oil]) = so.value(); } else { OPM_THROW(std::runtime_error, "BlackoilPropsAdFromDeck::relperm() assumes oil phase is active."); } if (pu_.phase_used[Gas]) { assert(sg.value().size() == numCells); activeSat.col(pu_.phase_pos[Gas]) = sg.value(); } Block pc(numCells, numActivePhases); Block dpc(numCells, numActivePhases*numActivePhases); props_.capPress(numCells, activeSat.data(), cells.data(), pc.data(), dpc.data()); std::vector<ADB> adbCapPressures; adbCapPressures.reserve(3); const ADB* s[3] = { &sw, &so, &sg }; for (int phase1 = 0; phase1 < 3; ++phase1) { if (pu_.phase_used[phase1]) { const int phase1_pos = pu_.phase_pos[phase1]; std::vector<ADB::M> jacs(numBlocks); for (int block = 0; block < numBlocks; ++block) { jacs[block] = ADB::M(numCells, s[phase1]->derivative()[block].cols()); } for (int phase2 = 0; phase2 < 3; ++phase2) { if (!pu_.phase_used[phase2]) continue; const int phase2_pos = pu_.phase_pos[phase2]; // Assemble dpc1/ds2. const int column = phase1_pos + numActivePhases*phase2_pos; // Recall: Fortran ordering from props_.relperm() ADB::M dpc1_ds2_diag = spdiag(dpc.col(column)); for (int block = 0; block < numBlocks; ++block) { jacs[block] += dpc1_ds2_diag * s[phase2]->derivative()[block]; } } adbCapPressures.emplace_back(ADB::function(pc.col(phase1_pos), jacs)); } else { adbCapPressures.emplace_back(ADB::null()); } } return adbCapPressures; }
/// Relative permeabilities for all phases. /// \param[in] sw Array of n water saturation values. /// \param[in] so Array of n oil saturation values. /// \param[in] sg Array of n gas saturation values. /// \param[in] cells Array of n cell indices to be associated with the saturation values. /// \return An std::vector with 3 elements, each an array of n relperm values, /// containing krw, kro, krg. Use PhaseIndex for indexing into the result. std::vector<ADB> BlackoilPropsAdFromDeck::relperm(const ADB& sw, const ADB& so, const ADB& sg, const Cells& cells) const { const int n = cells.size(); const int np = numPhases(); Block s_all(n, np); if (phase_usage_.phase_used[Water]) { assert(sw.value().size() == n); s_all.col(phase_usage_.phase_pos[Water]) = sw.value(); } if (phase_usage_.phase_used[Oil]) { assert(so.value().size() == n); s_all.col(phase_usage_.phase_pos[Oil]) = so.value(); } else { OPM_THROW(std::runtime_error, "BlackoilPropsAdFromDeck::relperm() assumes oil phase is active."); } if (phase_usage_.phase_used[Gas]) { assert(sg.value().size() == n); s_all.col(phase_usage_.phase_pos[Gas]) = sg.value(); } Block kr(n, np); Block dkr(n, np*np); satprops_->relperm(n, s_all.data(), cells.data(), kr.data(), dkr.data()); const int num_blocks = so.numBlocks(); std::vector<ADB> relperms; relperms.reserve(3); typedef const ADB* ADBPtr; ADBPtr s[3] = { &sw, &so, &sg }; for (int phase1 = 0; phase1 < 3; ++phase1) { if (phase_usage_.phase_used[phase1]) { const int phase1_pos = phase_usage_.phase_pos[phase1]; std::vector<ADB::M> jacs(num_blocks); for (int block = 0; block < num_blocks; ++block) { jacs[block] = ADB::M(n, s[phase1]->derivative()[block].cols()); } for (int phase2 = 0; phase2 < 3; ++phase2) { if (!phase_usage_.phase_used[phase2]) { continue; } const int phase2_pos = phase_usage_.phase_pos[phase2]; // Assemble dkr1/ds2. const int column = phase1_pos + np*phase2_pos; // Recall: Fortran ordering from props_.relperm() ADB::M dkr1_ds2_diag = spdiag(dkr.col(column)); for (int block = 0; block < num_blocks; ++block) { jacs[block] += dkr1_ds2_diag * s[phase2]->derivative()[block]; } } relperms.emplace_back(ADB::function(kr.col(phase1_pos), jacs)); } else { relperms.emplace_back(ADB::null()); } } return relperms; }
bool equals(const BlackoilState& other, double epsilon = 1e-8) const { bool equal = (numPhases() == other.numPhases()); for (int phaseIdx = 0; phaseIdx < BlackoilPhases::MaxNumPhases; ++ phaseIdx) { equal = equal && (usedPhases_.phase_used[phaseIdx] == other.usedPhases_.phase_used[phaseIdx]); if (usedPhases_.phase_used[phaseIdx]) equal = equal && (usedPhases_.phase_pos[phaseIdx] == other.usedPhases_.phase_pos[phaseIdx]); } equal = equal && (vectorApproxEqual( pressure() , other.pressure() , epsilon)); equal = equal && (vectorApproxEqual( facepressure() , other.facepressure() , epsilon)); equal = equal && (vectorApproxEqual( faceflux() , other.faceflux() , epsilon)); equal = equal && (vectorApproxEqual( surfacevol() , other.surfacevol() , epsilon)); equal = equal && (vectorApproxEqual( saturation() , other.saturation() , epsilon)); equal = equal && (vectorApproxEqual( gasoilratio() , other.gasoilratio() , epsilon)); return equal; }
/// \param[in] n Number of data points. /// \param[in] A Array of nP^2 values, where the P^2 values for a cell give the /// matrix A = RB^{-1} which relates z to u by z = Au. The matrices /// are assumed to be in Fortran order, and are typically the result /// of a call to the method matrix(). /// \param[in] cells The index of the grid cell of each data point. /// \param[out] rho Array of nP density values, array must be valid before calling. void BlackoilPropertiesFromDeck::density(const int n, const double* A, const int* cells, double* rho) const { const int np = numPhases(); // #pragma omp parallel for for (int i = 0; i < n; ++i) { int cellIdx = cells?cells[i]:i; int pvtRegionIdx = getTableIndex_(cellPvtRegionIndex(), cellIdx); const double* sdens = pvt_.surfaceDensities(pvtRegionIdx); for (int phase = 0; phase < np; ++phase) { rho[np*i + phase] = 0.0; for (int comp = 0; comp < np; ++comp) { rho[np*i + phase] += A[i*np*np + np*phase + comp]*sdens[comp]; } } } }
/// Construct from parameters. /// The following parameters are accepted (defaults): /// num_phases (2) Must be 1 or 2. /// relperm_func ("Linear") Must be "Constant", "Linear" or "Quadratic". /// rho1 [rho2, rho3] (1.0e3) Density in kg/m^3 /// mu1 [mu2, mu3] (1.0) Viscosity in cP /// porosity (1.0) Porosity /// permeability (100.0) Permeability in mD IncompPropertiesDefaultPolymer(const Opm::parameter::ParameterGroup& param, int dim, int num_cells) : Opm::IncompPropertiesBasic(param, dim, num_cells) { assert(numPhases() == 2); sw_.resize(3); sw_[0] = 0.2; sw_[1] = 0.7; sw_[2] = 1.0; krw_.resize(3); krw_[0] = 0.0; krw_[1] = 0.7; krw_[2] = 1.0; so_.resize(2); so_[0] = 0.3; so_[1] = 0.8; kro_.resize(2); kro_[0] = 0.0; kro_[1] = 1.0; }
/// Set the first saturation to either its min or max value in /// the indicated cells. The second saturation value s2 is set /// to (1.0 - s1) for each cell. Any further saturation values /// are unchanged. void setFirstSat(const std::vector<int>& cells, const Opm::BlackoilPropertiesInterface& props, ExtremalSat es) { if (cells.empty()) { return; } const int n = cells.size(); assert(n > 0); std::vector<double> smin(numPhases()*n); std::vector<double> smax(numPhases()*n); props.satRange(n, &cells[0], &smin[0], &smax[0]); const double* svals = (es == MinSat) ? &smin[0] : &smax[0]; for (int ci = 0; ci < n; ++ci) { const int cell = cells[ci]; sat_[numPhases()*cell] = svals[numPhases()*ci]; sat_[numPhases()*cell + 1] = 1.0 - sat_[numPhases()*cell]; } }
/// \param[in] n Number of data points. /// \param[in] p Array of n pressure values. /// \param[in] T Array of n temperature values. /// \param[in] z Array of nP surface volume values. /// \param[in] cells Array of n cell indices to be associated with the p and z values. /// \param[out] A Array of nP^2 values, array must be valid before calling. /// The P^2 values for a cell give the matrix A = RB^{-1} which /// relates z to u by z = Au. The matrices are output in Fortran order. /// \param[out] dAdp If non-null: array of nP^2 matrix derivative values, /// array must be valid before calling. The matrices are output /// in Fortran order. void BlackoilPropertiesFromDeck::matrix(const int n, const double* p, const double* T, const double* z, const int* cells, double* A, double* dAdp) const { const int np = numPhases(); const int *cellPvtTableIdx = cellPvtRegionIndex(); std::vector<int> pvtTableIdx(n); for (int i = 0; i < n; ++ i) pvtTableIdx[i] = cellPvtTableIdx[cells[i]]; B_.resize(n*np); R_.resize(n*np); if (dAdp) { dB_.resize(n*np); dR_.resize(n*np); pvt_.dBdp(n, &pvtTableIdx[0], p, T, z, &B_[0], &dB_[0]); pvt_.dRdp(n, &pvtTableIdx[0], p, z, &R_[0], &dR_[0]); } else { pvt_.B(n, &pvtTableIdx[0], p, T, z, &B_[0]); pvt_.R(n, &pvtTableIdx[0], p, z, &R_[0]); } const int* phase_pos = pvt_.phasePosition(); bool oil_and_gas = pvt_.phaseUsed()[BlackoilPhases::Liquid] && pvt_.phaseUsed()[BlackoilPhases::Vapour]; const int o = phase_pos[BlackoilPhases::Liquid]; const int g = phase_pos[BlackoilPhases::Vapour]; // Compute A matrix // #pragma omp parallel for for (int i = 0; i < n; ++i) { double* m = A + i*np*np; std::fill(m, m + np*np, 0.0); // Diagonal entries. for (int phase = 0; phase < np; ++phase) { m[phase + phase*np] = 1.0/B_[i*np + phase]; } // Off-diagonal entries. if (oil_and_gas) { m[o + g*np] = R_[i*np + g]/B_[i*np + g]; m[g + o*np] = R_[i*np + o]/B_[i*np + o]; } } // Derivative of A matrix. // A = R*inv(B) whence // // dA/dp = (dR/dp*inv(B) + R*d(inv(B))/dp) // = (dR/dp*inv(B) - R*inv(B)*(dB/dp)*inv(B)) // = (dR/dp - A*(dB/dp)) * inv(B) // // The B matrix is diagonal and that fact is exploited in the // following implementation. if (dAdp) { // #pragma omp parallel for // (1): dA/dp <- A std::copy(A, A + n*np*np, dAdp); for (int i = 0; i < n; ++i) { double* m = dAdp + i*np*np; // (2): dA/dp <- -dA/dp*(dB/dp) == -A*(dB/dp) const double* dB = & dB_[i * np]; for (int col = 0; col < np; ++col) { for (int row = 0; row < np; ++row) { m[col*np + row] *= - dB[ col ]; // Note sign. } } if (oil_and_gas) { // (2b): dA/dp += dR/dp (== dR/dp - A*(dB/dp)) const double* dR = & dR_[i * np]; m[o*np + g] += dR[ o ]; m[g*np + o] += dR[ g ]; } // (3): dA/dp *= inv(B) (== final result) const double* B = & B_[i * np]; for (int col = 0; col < np; ++col) { for (int row = 0; row < np; ++row) { m[col*np + row] /= B[ col ]; } } } } }