mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-14 08:33:33 -06:00
Implemented support for VFPINJ tables. Runs through synthetic non-trivial example
This commit is contained in:
parent
2994d1d932
commit
c513ed4a17
@ -43,6 +43,7 @@ list (APPEND MAIN_SOURCE_FILES
|
||||
opm/autodiff/LinearisedBlackoilResidual.cpp
|
||||
opm/autodiff/VFPProperties.cpp
|
||||
opm/autodiff/VFPProdProperties.cpp
|
||||
opm/autodiff/VFPInjProperties.cpp
|
||||
)
|
||||
|
||||
# originally generated with the command:
|
||||
@ -136,5 +137,6 @@ list (APPEND PUBLIC_HEADER_FILES
|
||||
opm/autodiff/SimulatorFullyImplicitBlackoilOutput.hpp
|
||||
opm/autodiff/VFPProperties.hpp
|
||||
opm/autodiff/VFPProdProperties.hpp
|
||||
opm/autodiff/VFPInjProperties.hpp
|
||||
)
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <opm/autodiff/WellDensitySegmented.hpp>
|
||||
#include <opm/autodiff/VFPProperties.hpp>
|
||||
#include <opm/autodiff/VFPProdProperties.hpp>
|
||||
#include <opm/autodiff/VFPInjProperties.hpp>
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/linalg/LinearSolverInterface.hpp>
|
||||
@ -1246,7 +1247,16 @@ namespace detail {
|
||||
const double& alq = well_controls_iget_alq(wc, current);
|
||||
|
||||
//Set *BHP* target by calculating bhp from THP
|
||||
xw.bhp()[w] = vfp_properties_->getProd()->bhp(vfp, aqua, liquid, vapour, thp, alq);
|
||||
const WellType& well_type = wells().type[w];
|
||||
if (well_type == PRODUCER) {
|
||||
xw.bhp()[w] = vfp_properties_->getProd()->bhp(vfp, aqua, liquid, vapour, thp, alq);
|
||||
}
|
||||
else if (well_type == INJECTOR) {
|
||||
xw.bhp()[w] = vfp_properties_->getInj()->bhp(vfp, aqua, liquid, vapour, thp);
|
||||
}
|
||||
else {
|
||||
OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type of well");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1374,8 +1384,10 @@ namespace detail {
|
||||
const ADB& vapour = subset(state.qs, Span(nw, 1, BlackoilPhases::Vapour*nw));
|
||||
|
||||
//THP calculation variables
|
||||
std::vector<int> table_id(nw, -1);
|
||||
ADB::V thp_v = ADB::V::Zero(nw);
|
||||
std::vector<int> inj_table_id(nw, -1);
|
||||
std::vector<int> prod_table_id(nw, -1);
|
||||
ADB::V thp_inj_v = ADB::V::Zero(nw);
|
||||
ADB::V thp_prod_v = ADB::V::Zero(nw);
|
||||
ADB::V alq_v = ADB::V::Zero(nw);
|
||||
|
||||
//Target vars
|
||||
@ -1385,7 +1397,8 @@ namespace detail {
|
||||
|
||||
//Selection variables
|
||||
std::vector<int> bhp_elems;
|
||||
std::vector<int> thp_elems;
|
||||
std::vector<int> thp_inj_elems;
|
||||
std::vector<int> thp_prod_elems;
|
||||
std::vector<int> rate_elems;
|
||||
|
||||
//Run through all wells to calculate BHP/RATE targets
|
||||
@ -1409,11 +1422,24 @@ namespace detail {
|
||||
|
||||
case THP:
|
||||
{
|
||||
table_id[w] = well_controls_iget_vfp(wc, current);
|
||||
thp_v[w] = well_controls_iget_target(wc, current);
|
||||
alq_v[w] = well_controls_iget_alq(wc, current);
|
||||
const WellType& well_type = wells().type[w];
|
||||
if (well_type == INJECTOR) {
|
||||
inj_table_id[w] = well_controls_iget_vfp(wc, current);
|
||||
thp_inj_v[w] = well_controls_iget_target(wc, current);
|
||||
alq_v[w] = -1e100;
|
||||
|
||||
thp_elems.push_back(w);
|
||||
thp_inj_elems.push_back(w);
|
||||
}
|
||||
else if (well_type == PRODUCER) {
|
||||
prod_table_id[w] = well_controls_iget_vfp(wc, current);
|
||||
thp_prod_v[w] = well_controls_iget_target(wc, current);
|
||||
alq_v[w] = well_controls_iget_alq(wc, current);
|
||||
|
||||
thp_prod_elems.push_back(w);
|
||||
}
|
||||
else {
|
||||
OPM_THROW(std::logic_error, "Expected INJECTOR or PRODUCER type well");
|
||||
}
|
||||
bhp_targets(w) = -1e100;
|
||||
rate_targets(w) = -1e100;
|
||||
}
|
||||
@ -1442,18 +1468,21 @@ namespace detail {
|
||||
}
|
||||
|
||||
//Calculate BHP target from THP
|
||||
const ADB thp = ADB::constant(thp_v);
|
||||
const ADB thp = ADB::constant(thp_inj_v);
|
||||
const ADB alq = ADB::constant(alq_v);
|
||||
const ADB thp_targets = vfp_properties_->getProd()->bhp(table_id, aqua, liquid, vapour, thp, alq);
|
||||
const ADB thp_inj_targets = vfp_properties_->getInj()->bhp(inj_table_id, aqua, liquid, vapour, thp);
|
||||
const ADB thp_prod_targets = vfp_properties_->getProd()->bhp(prod_table_id, aqua, liquid, vapour, thp, alq);
|
||||
|
||||
//Calculate residuals
|
||||
const ADB thp_residual = state.bhp - thp_targets;
|
||||
const ADB thp_inj_residual = state.bhp - thp_inj_targets;
|
||||
const ADB thp_prod_residual = state.bhp - thp_prod_targets;
|
||||
const ADB bhp_residual = state.bhp - bhp_targets;
|
||||
const ADB rate_residual = rate_distr * state.qs - rate_targets;
|
||||
|
||||
//Select the right residual for each well
|
||||
residual_.well_eq = superset(subset(bhp_residual, bhp_elems), bhp_elems, bhp_residual.size()) +
|
||||
superset(subset(thp_residual, thp_elems), thp_elems, thp_residual.size()) +
|
||||
superset(subset(thp_inj_residual, thp_inj_elems), thp_inj_elems, thp_inj_residual.size()) +
|
||||
superset(subset(thp_prod_residual, thp_prod_elems), thp_prod_elems, thp_prod_residual.size()) +
|
||||
superset(subset(rate_residual, rate_elems), rate_elems, rate_residual.size());
|
||||
|
||||
// For wells that are dead (not flowing), and therefore not communicating
|
||||
@ -1832,7 +1861,16 @@ namespace detail {
|
||||
double alq = well_controls_iget_alq(wc, ctrl_index);
|
||||
int table_id = well_controls_iget_vfp(wc, ctrl_index);
|
||||
|
||||
well_state.thp()[w] = vfp_properties_->getProd()->thp(table_id, aqua, liquid, vapour, bhp[w], alq);
|
||||
const WellType& well_type = wells().type[w];
|
||||
if (well_type == INJECTOR) {
|
||||
well_state.thp()[w] = vfp_properties_->getInj()->thp(table_id, aqua, liquid, vapour, bhp[w]);
|
||||
}
|
||||
else if (well_type == PRODUCER) {
|
||||
well_state.thp()[w] = vfp_properties_->getProd()->thp(table_id, aqua, liquid, vapour, bhp[w], alq);
|
||||
}
|
||||
else {
|
||||
OPM_THROW(std::logic_error, "Expected INJECTOR or PRODUCER well");
|
||||
}
|
||||
|
||||
//Assume only one THP control specified for each well
|
||||
break;
|
||||
|
@ -53,7 +53,7 @@ namespace Opm
|
||||
output_writer_(output_writer),
|
||||
rateConverter_(props_, std::vector<int>(AutoDiffGrid::numCells(grid_), 0)),
|
||||
threshold_pressures_by_face_(threshold_pressures_by_face),
|
||||
vfpProperties_(eclipse_state->getVFPProdTables()),
|
||||
vfpProperties_(eclipse_state->getVFPInjTables(), eclipse_state->getVFPProdTables()),
|
||||
is_parallel_run_( false )
|
||||
{
|
||||
// Misc init.
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Tables/VFPProdTable.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Tables/VFPInjTable.hpp>
|
||||
#include <opm/autodiff/AutoDiffHelpers.hpp>
|
||||
|
||||
|
||||
/**
|
||||
@ -32,7 +34,7 @@ namespace Opm {
|
||||
namespace detail {
|
||||
|
||||
|
||||
typedef VFPProdProperties::ADB ADB;
|
||||
typedef AutoDiffBlock<double> ADB;
|
||||
|
||||
|
||||
|
||||
@ -311,6 +313,8 @@ inline adb_like operator*(
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("unroll-loops")
|
||||
#endif
|
||||
|
||||
|
||||
inline adb_like interpolate(
|
||||
const VFPProdTable::array_type& array,
|
||||
const InterpData& flo_i,
|
||||
@ -420,6 +424,75 @@ inline adb_like interpolate(
|
||||
return nn[0][0][0][0][0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
inline adb_like interpolate(
|
||||
const VFPInjTable::array_type& array,
|
||||
const InterpData& flo_i,
|
||||
const InterpData& thp_i) {
|
||||
|
||||
//Values and derivatives in a 5D hypercube
|
||||
adb_like nn[2][2];
|
||||
|
||||
|
||||
//Pick out nearest neighbors (nn) to our evaluation point
|
||||
//This is not really required, but performance-wise it may pay off, since the 32-elements
|
||||
//we copy to (nn) will fit better in cache than the full original table for the
|
||||
//interpolation below.
|
||||
//The following ladder of for loops will presumably be unrolled by a reasonable compiler.
|
||||
for (int t=0; t<=1; ++t) {
|
||||
for (int f=0; f<=1; ++f) {
|
||||
//Shorthands for indexing
|
||||
const int ti = thp_i.ind_[t];
|
||||
const int fi = flo_i.ind_[f];
|
||||
|
||||
//Copy element
|
||||
nn[t][f].value = array[ti][fi];
|
||||
}
|
||||
}
|
||||
|
||||
//Calculate derivatives
|
||||
//Note that the derivative of the two end points of a line aligned with the
|
||||
//"axis of the derivative" are equal
|
||||
for (int i=0; i<=1; ++i) {
|
||||
nn[0][i].dthp = (nn[1][i].value - nn[0][i].value) * thp_i.inv_dist_;
|
||||
nn[i][0].dwfr = -1e100;
|
||||
nn[i][0].dgfr = -1e100;
|
||||
nn[i][0].dalq = -1e100;
|
||||
nn[i][0].dflo = (nn[i][1].value - nn[i][0].value) * flo_i.inv_dist_;
|
||||
|
||||
nn[1][i].dthp = nn[0][i].dthp;
|
||||
nn[i][1].dwfr = nn[i][0].dwfr;
|
||||
nn[i][1].dgfr = nn[i][0].dgfr;
|
||||
nn[i][1].dalq = nn[i][0].dalq;
|
||||
nn[i][1].dflo = nn[i][0].dflo;
|
||||
}
|
||||
|
||||
double t1, t2; //interpolation variables, so that t1 = (1-t) and t2 = t.
|
||||
|
||||
// Remove dimensions one by one
|
||||
// Example: going from 3D to 2D to 1D, we start by interpolating along
|
||||
// the z axis first, leaving a 2D problem. Then interpolating along the y
|
||||
// axis, leaving a 1D, problem, etc.
|
||||
t2 = flo_i.factor_;
|
||||
t1 = (1.0-t2);
|
||||
for (int t=0; t<=1; ++t) {
|
||||
nn[t][0] = t1*nn[t][0] + t2*nn[t][1];
|
||||
}
|
||||
|
||||
t2 = thp_i.factor_;
|
||||
t1 = (1.0-t2);
|
||||
nn[0][0] = t1*nn[0][0] + t2*nn[1][0];
|
||||
|
||||
return nn[0][0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC pop_options //unroll loops
|
||||
#endif
|
||||
@ -456,6 +529,436 @@ inline adb_like bhp(const VFPProdTable* table,
|
||||
|
||||
|
||||
|
||||
inline adb_like bhp(const VFPInjTable* table,
|
||||
const double& aqua,
|
||||
const double& liquid,
|
||||
const double& vapour,
|
||||
const double& thp) {
|
||||
//Find interpolation variables
|
||||
double flo = detail::getFlo(aqua, liquid, vapour, table->getFloType());
|
||||
|
||||
//First, find the values to interpolate between
|
||||
auto flo_i = detail::findInterpData(flo, table->getFloAxis());
|
||||
auto thp_i = detail::findInterpData(thp, table->getTHPAxis());
|
||||
|
||||
//Then perform the interpolation itself
|
||||
detail::adb_like retval = detail::interpolate(table->getTable(), flo_i, thp_i);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the table from the map if found, or throws an exception
|
||||
*/
|
||||
template <typename T>
|
||||
const T* getTable(const std::map<int, T*> tables, int table_id) {
|
||||
auto entry = tables.find(table_id);
|
||||
if (entry == tables.end()) {
|
||||
OPM_THROW(std::invalid_argument, "Nonexistent table " << table_id << " referenced.");
|
||||
}
|
||||
else {
|
||||
return entry->second;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sets block_pattern to be the "union of x.blockPattern() and block_pattern".
|
||||
*/
|
||||
inline void extendBlockPattern(const ADB& x, std::vector<int>& block_pattern) {
|
||||
std::vector<int> x_block_pattern = x.blockPattern();
|
||||
|
||||
if (x_block_pattern.empty()) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (block_pattern.empty()) {
|
||||
block_pattern = x_block_pattern;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (x_block_pattern != block_pattern) {
|
||||
OPM_THROW(std::logic_error, "Block patterns do not match");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the common block pattern for all inputs
|
||||
*/
|
||||
inline std::vector<int> commonBlockPattern(
|
||||
const ADB& x1,
|
||||
const ADB& x2,
|
||||
const ADB& x3,
|
||||
const ADB& x4) {
|
||||
std::vector<int> block_pattern;
|
||||
|
||||
extendBlockPattern(x1, block_pattern);
|
||||
extendBlockPattern(x2, block_pattern);
|
||||
extendBlockPattern(x3, block_pattern);
|
||||
extendBlockPattern(x4, block_pattern);
|
||||
|
||||
return block_pattern;
|
||||
}
|
||||
|
||||
inline std::vector<int> commonBlockPattern(
|
||||
const ADB& x1,
|
||||
const ADB& x2,
|
||||
const ADB& x3,
|
||||
const ADB& x4,
|
||||
const ADB& x5) {
|
||||
std::vector<int> block_pattern = commonBlockPattern(x1, x2, x3, x4);
|
||||
extendBlockPattern(x5, block_pattern);
|
||||
|
||||
return block_pattern;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type variable for FLO/GFR/WFR for production tables
|
||||
*/
|
||||
template <typename TYPE, typename TABLE>
|
||||
TYPE getType(const TABLE* table);
|
||||
|
||||
template <>
|
||||
inline
|
||||
VFPProdTable::FLO_TYPE getType(const VFPProdTable* table) {
|
||||
return table->getFloType();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline
|
||||
VFPProdTable::WFR_TYPE getType(const VFPProdTable* table) {
|
||||
return table->getWFRType();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline
|
||||
VFPProdTable::GFR_TYPE getType(const VFPProdTable* table) {
|
||||
return table->getGFRType();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type variable for FLO for injection tables
|
||||
*/
|
||||
template <>
|
||||
inline
|
||||
VFPInjTable::FLO_TYPE getType(const VFPInjTable* table) {
|
||||
return table->getFloType();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the actual ADB for the type of FLO/GFR/WFR type
|
||||
*/
|
||||
template <typename TYPE>
|
||||
ADB getValue(
|
||||
const ADB& aqua,
|
||||
const ADB& liquid,
|
||||
const ADB& vapour, TYPE type);
|
||||
|
||||
template <>
|
||||
inline
|
||||
ADB getValue(
|
||||
const ADB& aqua,
|
||||
const ADB& liquid,
|
||||
const ADB& vapour,
|
||||
VFPProdTable::FLO_TYPE type) {
|
||||
return detail::getFlo(aqua, liquid, vapour, type);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline
|
||||
ADB getValue(
|
||||
const ADB& aqua,
|
||||
const ADB& liquid,
|
||||
const ADB& vapour,
|
||||
VFPProdTable::WFR_TYPE type) {
|
||||
return detail::getWFR(aqua, liquid, vapour, type);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline
|
||||
ADB getValue(
|
||||
const ADB& aqua,
|
||||
const ADB& liquid,
|
||||
const ADB& vapour,
|
||||
VFPProdTable::GFR_TYPE type) {
|
||||
return detail::getGFR(aqua, liquid, vapour, type);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline
|
||||
ADB getValue(
|
||||
const ADB& aqua,
|
||||
const ADB& liquid,
|
||||
const ADB& vapour,
|
||||
VFPInjTable::FLO_TYPE type) {
|
||||
return detail::getFlo(aqua, liquid, vapour, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given m wells and n types of VFP variables (e.g., FLO = {FLO_OIL, FLO_LIQ}
|
||||
* this function combines the n types of ADB objects, so that each of the
|
||||
* m wells gets the right ADB.
|
||||
* @param TYPE Type of variable to return, e.g., FLO_TYPE, WFR_TYPE, GFR_TYPE
|
||||
* @param TABLE Type of table to use, e.g., VFPInjTable, VFPProdTable.
|
||||
*/
|
||||
template <typename TYPE, typename TABLE>
|
||||
ADB gather_vars(const std::vector<const TABLE*>& well_tables,
|
||||
const ADB& aqua,
|
||||
const ADB& liquid,
|
||||
const ADB& vapour) {
|
||||
|
||||
const int num_wells = static_cast<int>(well_tables.size());
|
||||
assert(aqua.size() == num_wells);
|
||||
assert(liquid.size() == num_wells);
|
||||
assert(vapour.size() == num_wells);
|
||||
|
||||
//Caching variable for flo/wfr/gfr
|
||||
std::map<TYPE, ADB> map;
|
||||
|
||||
//Indexing variable used when combining the different ADB types
|
||||
std::map<TYPE, std::vector<int> > elems;
|
||||
|
||||
//Compute all of the different ADB types,
|
||||
//and record which wells use which types
|
||||
for (int i=0; i<num_wells; ++i) {
|
||||
const TABLE* table = well_tables[i];
|
||||
|
||||
//Only do something if this well is under THP control
|
||||
if (table != NULL) {
|
||||
TYPE type = getType<TYPE>(table);
|
||||
|
||||
//"Caching" of flo_type etc: Only calculate used types
|
||||
//Create type if it does not exist
|
||||
if (map.find(type) == map.end()) {
|
||||
map.insert(std::pair<TYPE, ADB>(
|
||||
type,
|
||||
detail::getValue<TYPE>(aqua, liquid, vapour, type)
|
||||
));
|
||||
}
|
||||
|
||||
//Add the index for assembly later in gather_vars
|
||||
elems[type].push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
//Loop over all types of ADB variables, and combine them
|
||||
//so that each well gets the proper variable
|
||||
ADB retval = ADB::constant(ADB::V::Zero(num_wells));
|
||||
for (const auto& entry : elems) {
|
||||
const auto& key = entry.first;
|
||||
const auto& value = entry.second;
|
||||
|
||||
//Get the ADB for this type of variable
|
||||
assert(map.find(key) != map.end());
|
||||
const ADB& values = map.find(key)->second;
|
||||
|
||||
//Get indices to all elements that should use this ADB
|
||||
const std::vector<int>& elems = value;
|
||||
|
||||
//Add these elements to retval
|
||||
retval = retval + superset(subset(values, elems), elems, values.size());
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that finds x for a given value of y for a line
|
||||
* *NOTE ORDER OF ARGUMENTS*
|
||||
*/
|
||||
inline double findX(const double& x0,
|
||||
const double& x1,
|
||||
const double& y0,
|
||||
const double& y1,
|
||||
const double& y) {
|
||||
const double dx = x1 - x0;
|
||||
const double dy = y1 - y0;
|
||||
|
||||
/**
|
||||
* y = y0 + (dy / dx) * (x - x0)
|
||||
* => x = x0 + (y - y0) * (dx / dy)
|
||||
*
|
||||
* If dy is zero, use x1 as the value.
|
||||
*/
|
||||
|
||||
double x = 0.0;
|
||||
|
||||
if (dy != 0.0) {
|
||||
x = x0 + (y-y0) * (dx/dy);
|
||||
}
|
||||
else {
|
||||
x = x1;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This function finds the value of THP given a specific BHP.
|
||||
* Essentially:
|
||||
* Given the function f(thp_array(x)) = bhp_array(x), which is piecewise linear,
|
||||
* find thp so that f(thp) = bhp.
|
||||
*/
|
||||
inline double findTHP(
|
||||
const std::vector<double>& bhp_array,
|
||||
const std::vector<double>& thp_array,
|
||||
double bhp) {
|
||||
int nthp = thp_array.size();
|
||||
|
||||
double thp = -1e100;
|
||||
|
||||
//Check that our thp axis is sorted
|
||||
assert(std::is_sorted(thp_array.begin(), thp_array.end()));
|
||||
|
||||
/**
|
||||
* Our *interpolated* bhp_array will be montonic increasing for increasing
|
||||
* THP if our input BHP values are monotonic increasing for increasing
|
||||
* THP values. However, if we have to *extrapolate* along any of the other
|
||||
* axes, this guarantee holds no more, and bhp_array may be "random"
|
||||
*/
|
||||
if (std::is_sorted(bhp_array.begin(), bhp_array.end())) {
|
||||
//Target bhp less than all values in array, extrapolate
|
||||
if (bhp <= bhp_array[0]) {
|
||||
//TODO: LOG extrapolation
|
||||
const double& x0 = thp_array[0];
|
||||
const double& x1 = thp_array[1];
|
||||
const double& y0 = bhp_array[0];
|
||||
const double& y1 = bhp_array[1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
//Target bhp greater than all values in array, extrapolate
|
||||
else if (bhp > bhp_array[nthp-1]) {
|
||||
//TODO: LOG extrapolation
|
||||
const double& x0 = thp_array[nthp-2];
|
||||
const double& x1 = thp_array[nthp-1];
|
||||
const double& y0 = bhp_array[nthp-2];
|
||||
const double& y1 = bhp_array[nthp-1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
//Target bhp within table ranges, interpolate
|
||||
else {
|
||||
//Loop over the values and find min(bhp_array(thp)) == bhp
|
||||
//so that we maximize the rate.
|
||||
|
||||
//Find i so that bhp_array[i-1] <= bhp <= bhp_array[i];
|
||||
//Assuming a small number of values in bhp_array, this should be quite
|
||||
//efficient. Other strategies might be bisection, etc.
|
||||
int i=0;
|
||||
bool found = false;
|
||||
for (; i<nthp-1; ++i) {
|
||||
const double& y0 = bhp_array[i ];
|
||||
const double& y1 = bhp_array[i+1];
|
||||
|
||||
if (y0 < bhp && bhp <= y1) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//Canary in a coal mine: shouldn't really be required
|
||||
assert(found == true);
|
||||
|
||||
const double& x0 = thp_array[i ];
|
||||
const double& x1 = thp_array[i+1];
|
||||
const double& y0 = bhp_array[i ];
|
||||
const double& y1 = bhp_array[i+1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
}
|
||||
//bhp_array not sorted, raw search.
|
||||
else {
|
||||
//Find i so that bhp_array[i-1] <= bhp <= bhp_array[i];
|
||||
//Since the BHP values might not be sorted, first search within
|
||||
//our interpolation values, and then try to extrapolate.
|
||||
int i=0;
|
||||
bool found = false;
|
||||
for (; i<nthp-1; ++i) {
|
||||
const double& y0 = bhp_array[i ];
|
||||
const double& y1 = bhp_array[i+1];
|
||||
|
||||
if (y0 < bhp && bhp <= y1) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
const double& x0 = thp_array[i ];
|
||||
const double& x1 = thp_array[i+1];
|
||||
const double& y0 = bhp_array[i ];
|
||||
const double& y1 = bhp_array[i+1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
else if (bhp <= bhp_array[0]) {
|
||||
//TODO: LOG extrapolation
|
||||
const double& x0 = thp_array[0];
|
||||
const double& x1 = thp_array[1];
|
||||
const double& y0 = bhp_array[0];
|
||||
const double& y1 = bhp_array[1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
//Target bhp greater than all values in array, extrapolate
|
||||
else if (bhp > bhp_array[nthp-1]) {
|
||||
//TODO: LOG extrapolation
|
||||
const double& x0 = thp_array[nthp-2];
|
||||
const double& x1 = thp_array[nthp-1];
|
||||
const double& y0 = bhp_array[nthp-2];
|
||||
const double& y1 = bhp_array[nthp-1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
else {
|
||||
OPM_THROW(std::logic_error, "Programmer error: Unable to find THP in THP array");
|
||||
}
|
||||
}
|
||||
|
||||
return thp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
|
229
opm/autodiff/VFPInjProperties.cpp
Normal file
229
opm/autodiff/VFPInjProperties.cpp
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
Copyright 2015 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <opm/autodiff/VFPInjProperties.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Tables/VFPProdTable.hpp>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <opm/core/utility/ErrorMacros.hpp>
|
||||
#include <opm/autodiff/AutoDiffHelpers.hpp>
|
||||
|
||||
#include <opm/autodiff/VFPHelpers.hpp>
|
||||
|
||||
|
||||
|
||||
namespace Opm {
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
} //Namespace detail
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
VFPInjProperties::VFPInjProperties() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
VFPInjProperties::VFPInjProperties(const VFPInjTable* table){
|
||||
m_tables[table->getTableNum()] = table;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
VFPInjProperties::VFPInjProperties(const std::map<int, VFPInjTable>& tables) {
|
||||
for (const auto& table : tables) {
|
||||
m_tables[table.first] = &table.second;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
VFPInjProperties::ADB VFPInjProperties::bhp(const std::vector<int>& table_id,
|
||||
const Wells& wells,
|
||||
const ADB& qs,
|
||||
const ADB& thp) const {
|
||||
const int np = wells.number_of_phases;
|
||||
const int nw = wells.number_of_wells;
|
||||
|
||||
//Short-hands for water / oil / gas phases
|
||||
//TODO enable support for two-phase.
|
||||
assert(np == 3);
|
||||
const ADB& w = subset(qs, Span(nw, 1, BlackoilPhases::Aqua*nw));
|
||||
const ADB& o = subset(qs, Span(nw, 1, BlackoilPhases::Liquid*nw));
|
||||
const ADB& g = subset(qs, Span(nw, 1, BlackoilPhases::Vapour*nw));
|
||||
|
||||
return bhp(table_id, w, o, g, thp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
VFPInjProperties::ADB VFPInjProperties::bhp(const std::vector<int>& table_id,
|
||||
const ADB& aqua,
|
||||
const ADB& liquid,
|
||||
const ADB& vapour,
|
||||
const ADB& thp) const {
|
||||
const int nw = thp.size();
|
||||
|
||||
std::vector<int> block_pattern = detail::commonBlockPattern(aqua, liquid, vapour, thp);
|
||||
|
||||
assert(static_cast<int>(table_id.size()) == nw);
|
||||
assert(aqua.size() == nw);
|
||||
assert(liquid.size() == nw);
|
||||
assert(vapour.size() == nw);
|
||||
assert(thp.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 dflo = ADB::V::Zero(nw);
|
||||
|
||||
//Get the table for each well
|
||||
std::vector<const VFPInjTable*> well_tables(nw, NULL);
|
||||
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 variable for each well as a single ADB
|
||||
const ADB flo = detail::gather_vars<VFPInjTable::FLO_TYPE>(well_tables, aqua, liquid, vapour);
|
||||
|
||||
//Compute the BHP for each well independently
|
||||
for (int i=0; i<nw; ++i) {
|
||||
const VFPInjTable* table = well_tables[i];
|
||||
if (table != NULL) {
|
||||
//First, find the values to interpolate between
|
||||
auto flo_i = detail::findInterpData(flo.value()[i], table->getFloAxis());
|
||||
auto thp_i = detail::findInterpData(thp.value()[i], table->getTHPAxis());
|
||||
|
||||
detail::adb_like bhp_val = detail::interpolate(table->getTable(), flo_i, thp_i);
|
||||
|
||||
value[i] = bhp_val.value;
|
||||
dthp[i] = bhp_val.dthp;
|
||||
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 = spdiag(dthp);
|
||||
ADB::M dflo_diag = spdiag(dflo);
|
||||
|
||||
//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.derivative().empty()) {
|
||||
jacs[block] += dthp_diag * thp.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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
double VFPInjProperties::bhp(int table_id,
|
||||
const double& aqua,
|
||||
const double& liquid,
|
||||
const double& vapour,
|
||||
const double& thp) const {
|
||||
const VFPInjTable* table = detail::getTable(m_tables, table_id);
|
||||
|
||||
detail::adb_like retval = detail::bhp(table, aqua, liquid, vapour, thp);
|
||||
return retval.value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
double VFPInjProperties::thp(int table_id,
|
||||
const double& aqua,
|
||||
const double& liquid,
|
||||
const double& vapour,
|
||||
const double& bhp) const {
|
||||
const VFPInjTable* table = detail::getTable(m_tables, table_id);
|
||||
const VFPInjTable::array_type& data = table->getTable();
|
||||
|
||||
//Find interpolation variables
|
||||
double flo = detail::getFlo(aqua, liquid, vapour, table->getFloType());
|
||||
|
||||
const std::vector<double> thp_array = table->getTHPAxis();
|
||||
int nthp = thp_array.size();
|
||||
|
||||
/**
|
||||
* Find the function bhp_array(thp) by creating a 1D view of the data
|
||||
* by interpolating for every value of thp. This might be somewhat
|
||||
* expensive, but let us assome that nthp is small
|
||||
*/
|
||||
auto flo_i = detail::findInterpData(flo, table->getFloAxis());
|
||||
std::vector<double> bhp_array(nthp);
|
||||
for (int i=0; i<nthp; ++i) {
|
||||
auto thp_i = detail::findInterpData(thp_array[i], thp_array);
|
||||
bhp_array[i] = detail::interpolate(data, flo_i, thp_i).value;
|
||||
}
|
||||
|
||||
double thp = detail::findTHP(bhp_array, thp_array, bhp);
|
||||
return thp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} //Namespace Opm
|
140
opm/autodiff/VFPInjProperties.hpp
Normal file
140
opm/autodiff/VFPInjProperties.hpp
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
Copyright 2015 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_AUTODIFF_VFPINJPROPERTIES_HPP_
|
||||
#define OPM_AUTODIFF_VFPINJPROPERTIES_HPP_
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Tables/VFPInjTable.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/autodiff/AutoDiffBlock.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
|
||||
|
||||
namespace Opm {
|
||||
|
||||
|
||||
class VFPInjProperties {
|
||||
public:
|
||||
typedef AutoDiffBlock<double> ADB;
|
||||
|
||||
/**
|
||||
* Empty constructor
|
||||
*/
|
||||
VFPInjProperties();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Takes *no* ownership of data.
|
||||
* @param inj_table A *single* VFPINJ table
|
||||
*/
|
||||
VFPInjProperties(const VFPInjTable* inj_table);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Takes *no* ownership of data.
|
||||
* @param inj_tables A map of different VFPINJ tables.
|
||||
*/
|
||||
VFPInjProperties(const std::map<int, VFPInjTable>& inj_tables);
|
||||
|
||||
/**
|
||||
* Linear interpolation of bhp as function of the input parameters.
|
||||
* @param table_id Table number to use
|
||||
* @param wells Wells structure with information about wells in qs
|
||||
* @param qs Flow quantities
|
||||
* @param thp Tubing head pressure
|
||||
*
|
||||
* @return The bottom hole pressure, interpolated/extrapolated linearly using
|
||||
* the above parameters from the values in the input table.
|
||||
*/
|
||||
ADB bhp(const std::vector<int>& table_id,
|
||||
const Wells& wells,
|
||||
const ADB& qs,
|
||||
const ADB& thp) const;
|
||||
|
||||
/**
|
||||
* Linear interpolation of bhp as a function of the input parameters given as ADBs
|
||||
* Each entry corresponds typically to one well.
|
||||
* @param table_id Table number to use. A negative entry (e.g., -1)
|
||||
* will indicate that no table is used, and the corresponding
|
||||
* BHP will be calculated as a constant -1e100.
|
||||
* @param aqua Water phase
|
||||
* @param liquid Oil phase
|
||||
* @param vapour Gas phase
|
||||
* @param thp Tubing head pressure
|
||||
*
|
||||
* @return The bottom hole pressure, interpolated/extrapolated linearly using
|
||||
* the above parameters from the values in the input table, for each entry in the
|
||||
* input ADB objects.
|
||||
*/
|
||||
ADB bhp(const std::vector<int>& table_id,
|
||||
const ADB& aqua,
|
||||
const ADB& liquid,
|
||||
const ADB& vapour,
|
||||
const ADB& thp) const;
|
||||
|
||||
/**
|
||||
* Linear interpolation of bhp as a function of the input parameters
|
||||
* @param table_id Table number to use
|
||||
* @param aqua Water phase
|
||||
* @param liquid Oil phase
|
||||
* @param vapour Gas phase
|
||||
* @param thp Tubing head pressure
|
||||
*
|
||||
* @return The bottom hole pressure, interpolated/extrapolated linearly using
|
||||
* the above parameters from the values in the input table.
|
||||
*/
|
||||
double bhp(int table_id,
|
||||
const double& aqua,
|
||||
const double& liquid,
|
||||
const double& vapour,
|
||||
const double& thp) const;
|
||||
|
||||
|
||||
/**
|
||||
* Linear interpolation of thp as a function of the input parameters
|
||||
* @param table_id Table number to use
|
||||
* @param aqua Water phase
|
||||
* @param liquid Oil phase
|
||||
* @param vapour Gas phase
|
||||
* @param bhp Bottom hole pressure
|
||||
*
|
||||
* @return The tubing hole pressure, interpolated/extrapolated linearly using
|
||||
* the above parameters from the values in the input table.
|
||||
*/
|
||||
double thp(int table_id,
|
||||
const double& aqua,
|
||||
const double& liquid,
|
||||
const double& vapour,
|
||||
const double& bhp) const;
|
||||
|
||||
private:
|
||||
// Map which connects the table number with the table itself
|
||||
std::map<int, const VFPInjTable*> m_tables;
|
||||
};
|
||||
|
||||
|
||||
|
||||
} //namespace
|
||||
|
||||
|
||||
|
||||
#endif /* OPM_AUTODIFF_VFPINJPROPERTIES_HPP_ */
|
@ -22,6 +22,8 @@
|
||||
#include <opm/autodiff/VFPProdProperties.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Tables/VFPProdTable.hpp>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <opm/core/utility/ErrorMacros.hpp>
|
||||
#include <opm/autodiff/AutoDiffHelpers.hpp>
|
||||
|
||||
#include <opm/autodiff/VFPHelpers.hpp>
|
||||
|
||||
@ -30,227 +32,6 @@
|
||||
namespace Opm {
|
||||
|
||||
|
||||
namespace detail {
|
||||
/**
|
||||
* Returns the type variable for FLO/GFR/WFR
|
||||
*/
|
||||
template <typename TYPE>
|
||||
TYPE getType(const VFPProdTable* table);
|
||||
|
||||
template <>
|
||||
VFPProdTable::FLO_TYPE getType(const VFPProdTable* table) {
|
||||
return table->getFloType();
|
||||
}
|
||||
|
||||
template <>
|
||||
VFPProdTable::WFR_TYPE getType(const VFPProdTable* table) {
|
||||
return table->getWFRType();
|
||||
}
|
||||
|
||||
template <>
|
||||
VFPProdTable::GFR_TYPE getType(const VFPProdTable* table) {
|
||||
return table->getGFRType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actual ADB for the type of FLO/GFR/WFR type
|
||||
*/
|
||||
template <typename TYPE>
|
||||
VFPProdProperties::ADB getValue(
|
||||
const VFPProdProperties::ADB& aqua,
|
||||
const VFPProdProperties::ADB& liquid,
|
||||
const VFPProdProperties::ADB& vapour, TYPE type);
|
||||
|
||||
template <>
|
||||
VFPProdProperties::ADB getValue(
|
||||
const VFPProdProperties::ADB& aqua,
|
||||
const VFPProdProperties::ADB& liquid,
|
||||
const VFPProdProperties::ADB& vapour,
|
||||
VFPProdTable::FLO_TYPE type) {
|
||||
return detail::getFlo(aqua, liquid, vapour, type);
|
||||
}
|
||||
|
||||
template <>
|
||||
VFPProdProperties::ADB getValue(
|
||||
const VFPProdProperties::ADB& aqua,
|
||||
const VFPProdProperties::ADB& liquid,
|
||||
const VFPProdProperties::ADB& vapour,
|
||||
VFPProdTable::WFR_TYPE type) {
|
||||
return detail::getWFR(aqua, liquid, vapour, type);
|
||||
}
|
||||
|
||||
template <>
|
||||
VFPProdProperties::ADB getValue(
|
||||
const VFPProdProperties::ADB& aqua,
|
||||
const VFPProdProperties::ADB& liquid,
|
||||
const VFPProdProperties::ADB& vapour,
|
||||
VFPProdTable::GFR_TYPE type) {
|
||||
return detail::getGFR(aqua, liquid, vapour, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given m wells and n types of VFP variables (e.g., FLO = {FLO_OIL, FLO_LIQ}
|
||||
* this function combines the n types of ADB objects, so that each of the
|
||||
* m wells gets the right ADB.
|
||||
*/
|
||||
template <typename TYPE>
|
||||
VFPProdProperties::ADB gather_vars(const std::vector<const VFPProdTable*>& well_tables,
|
||||
const VFPProdProperties::ADB& aqua,
|
||||
const VFPProdProperties::ADB& liquid,
|
||||
const VFPProdProperties::ADB& vapour) {
|
||||
|
||||
typedef VFPProdProperties::ADB ADB;
|
||||
|
||||
const int num_wells = static_cast<int>(well_tables.size());
|
||||
assert(aqua.size() == num_wells);
|
||||
assert(liquid.size() == num_wells);
|
||||
assert(vapour.size() == num_wells);
|
||||
|
||||
//Caching variable for flo/wfr/gfr
|
||||
std::map<TYPE, ADB> map;
|
||||
|
||||
//Indexing variable used when combining the different ADB types
|
||||
std::map<TYPE, std::vector<int> > elems;
|
||||
|
||||
//Compute all of the different ADB types,
|
||||
//and record which wells use which types
|
||||
for (int i=0; i<num_wells; ++i) {
|
||||
const VFPProdTable* table = well_tables[i];
|
||||
|
||||
//Only do something if this well is under THP control
|
||||
if (table != NULL) {
|
||||
TYPE type = getType<TYPE>(table);
|
||||
|
||||
//"Caching" of flo_type etc: Only calculate used types
|
||||
//Create type if it does not exist
|
||||
if (map.find(type) == map.end()) {
|
||||
map.insert(std::pair<TYPE, ADB>(
|
||||
type,
|
||||
detail::getValue<TYPE>(aqua, liquid, vapour, type)
|
||||
));
|
||||
}
|
||||
|
||||
//Add the index for assembly later in gather_vars
|
||||
elems[type].push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
//Loop over all types of ADB variables, and combine them
|
||||
//so that each well gets the proper variable
|
||||
ADB retval = ADB::constant(ADB::V::Zero(num_wells));
|
||||
for (const auto& entry : elems) {
|
||||
const auto& key = entry.first;
|
||||
const auto& value = entry.second;
|
||||
|
||||
//Get the ADB for this type of variable
|
||||
assert(map.find(key) != map.end());
|
||||
const ADB& values = map.find(key)->second;
|
||||
|
||||
//Get indices to all elements that should use this ADB
|
||||
const std::vector<int>& elems = value;
|
||||
|
||||
//Add these elements to retval
|
||||
retval = retval + superset(subset(values, elems), elems, values.size());
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sets block_pattern to be the "union of x.blockPattern() and block_pattern".
|
||||
*/
|
||||
void extendBlockPattern(const VFPProdProperties::ADB& x, std::vector<int>& block_pattern) {
|
||||
std::vector<int> x_block_pattern = x.blockPattern();
|
||||
|
||||
if (x_block_pattern.empty()) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (block_pattern.empty()) {
|
||||
block_pattern = x_block_pattern;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (x_block_pattern != block_pattern) {
|
||||
OPM_THROW(std::logic_error, "Block patterns do not match");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the common block pattern for all inputs
|
||||
*/
|
||||
std::vector<int> commonBlockPattern(
|
||||
const VFPProdProperties::ADB& x1,
|
||||
const VFPProdProperties::ADB& x2,
|
||||
const VFPProdProperties::ADB& x3,
|
||||
const VFPProdProperties::ADB& x4,
|
||||
const VFPProdProperties::ADB& x5) {
|
||||
std::vector<int> block_pattern;
|
||||
|
||||
extendBlockPattern(x1, block_pattern);
|
||||
extendBlockPattern(x2, block_pattern);
|
||||
extendBlockPattern(x3, block_pattern);
|
||||
extendBlockPattern(x4, block_pattern);
|
||||
extendBlockPattern(x5, block_pattern);
|
||||
|
||||
return block_pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that finds x for a given value of y for a line
|
||||
* *NOTE ORDER OF ARGUMENTS*
|
||||
*/
|
||||
double findX(const double& x0,
|
||||
const double& x1,
|
||||
const double& y0,
|
||||
const double& y1,
|
||||
const double& y) {
|
||||
const double dx = x1 - x0;
|
||||
const double dy = y1 - y0;
|
||||
|
||||
/**
|
||||
* y = y0 + (dy / dx) * (x - x0)
|
||||
* => x = x0 + (y - y0) * (dx / dy)
|
||||
*
|
||||
* If dy is zero, use x1 as the value.
|
||||
*/
|
||||
|
||||
double x = 0.0;
|
||||
|
||||
if (dy != 0.0) {
|
||||
x = x0 + (y-y0) * (dx/dy);
|
||||
}
|
||||
else {
|
||||
x = x1;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
} //Namespace
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
VFPProdProperties::VFPProdProperties() {
|
||||
@ -267,20 +48,14 @@ VFPProdProperties::VFPProdProperties(const VFPProdTable* table){
|
||||
|
||||
|
||||
VFPProdProperties::VFPProdProperties(const std::map<int, VFPProdTable>& tables) {
|
||||
init(tables);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void VFPProdProperties::init(const std::map<int, VFPProdTable>& prod_tables) {
|
||||
//Populate production table pointers
|
||||
for (const auto& table : prod_tables) {
|
||||
for (const auto& table : tables) {
|
||||
m_tables[table.first] = &table.second;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
VFPProdProperties::ADB VFPProdProperties::bhp(const std::vector<int>& table_id,
|
||||
const Wells& wells,
|
||||
const ADB& qs,
|
||||
@ -333,7 +108,7 @@ VFPProdProperties::ADB VFPProdProperties::bhp(const std::vector<int>& table_id,
|
||||
std::vector<const VFPProdTable*> well_tables(nw, NULL);
|
||||
for (int i=0; i<nw; ++i) {
|
||||
if (table_id[i] >= 0) {
|
||||
well_tables[i] = getProdTable(table_id[i]);
|
||||
well_tables[i] = detail::getTable(m_tables, table_id[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -411,7 +186,7 @@ double VFPProdProperties::bhp(int table_id,
|
||||
const double& vapour,
|
||||
const double& thp,
|
||||
const double& alq) const {
|
||||
const VFPProdTable* table = getProdTable(table_id);
|
||||
const VFPProdTable* table = detail::getTable(m_tables, table_id);
|
||||
|
||||
detail::adb_like retval = detail::bhp(table, aqua, liquid, vapour, thp, alq);
|
||||
return retval.value;
|
||||
@ -425,22 +200,16 @@ double VFPProdProperties::thp(int table_id,
|
||||
const double& vapour,
|
||||
const double& bhp,
|
||||
const double& alq) const {
|
||||
const VFPProdTable* table = getProdTable(table_id);
|
||||
const VFPProdTable* table = detail::getTable(m_tables, table_id);
|
||||
const VFPProdTable::array_type& data = table->getTable();
|
||||
|
||||
double thp = -1e100;
|
||||
|
||||
//Find interpolation variables
|
||||
double flo = detail::getFlo(aqua, liquid, vapour, table->getFloType());
|
||||
double wfr = detail::getWFR(aqua, liquid, vapour, table->getWFRType());
|
||||
double gfr = detail::getGFR(aqua, liquid, vapour, table->getGFRType());
|
||||
|
||||
/**
|
||||
* Get THP axis, assume that it is sorted
|
||||
*/
|
||||
const std::vector<double> thp_array = table->getTHPAxis();
|
||||
int nthp = thp_array.size();
|
||||
assert(std::is_sorted(thp_array.begin(), thp_array.end()));
|
||||
|
||||
/**
|
||||
* Find the function bhp_array(thp) by creating a 1D view of the data
|
||||
@ -457,105 +226,7 @@ double VFPProdProperties::thp(int table_id,
|
||||
bhp_array[i] = detail::interpolate(data, flo_i, thp_i, wfr_i, gfr_i, alq_i).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Our *interpolated* bhp_array will be montoic increasing for increasing
|
||||
* THP if our input BHP values are monotonic increasing for increasing
|
||||
* THP values. However, if we have to *extrapolate* along any of the other
|
||||
* axes, this guarantee holds no more, and bhp_array may be "random"
|
||||
*/
|
||||
if (std::is_sorted(bhp_array.begin(), bhp_array.end())) {
|
||||
//Target bhp less than all values in array, extrapolate
|
||||
if (bhp <= bhp_array[0]) {
|
||||
//TODO: LOG extrapolation
|
||||
const double& x0 = thp_array[0];
|
||||
const double& x1 = thp_array[1];
|
||||
const double& y0 = bhp_array[0];
|
||||
const double& y1 = bhp_array[1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
//Target bhp greater than all values in array, extrapolate
|
||||
else if (bhp > bhp_array[nthp-1]) {
|
||||
//TODO: LOG extrapolation
|
||||
const double& x0 = thp_array[nthp-2];
|
||||
const double& x1 = thp_array[nthp-1];
|
||||
const double& y0 = bhp_array[nthp-2];
|
||||
const double& y1 = bhp_array[nthp-1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
//Target bhp within table ranges, interpolate
|
||||
else {
|
||||
//Loop over the values and find min(bhp_array(thp)) == bhp
|
||||
//so that we maximize the rate.
|
||||
|
||||
//Find i so that bhp_array[i-1] <= bhp <= bhp_array[i];
|
||||
//Assuming a small number of values in bhp_array, this should be quite
|
||||
//efficient. Other strategies might be bisection, etc.
|
||||
int i=0;
|
||||
bool found = false;
|
||||
for (; i<nthp-1; ++i) {
|
||||
const double& y0 = bhp_array[i ];
|
||||
const double& y1 = bhp_array[i+1];
|
||||
|
||||
if (y0 < bhp && bhp <= y1) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//Canary in a coal mine: shouldn't really be required
|
||||
assert(found == true);
|
||||
|
||||
const double& x0 = thp_array[i ];
|
||||
const double& x1 = thp_array[i+1];
|
||||
const double& y0 = bhp_array[i ];
|
||||
const double& y1 = bhp_array[i+1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
}
|
||||
//bhp_array not sorted, raw search.
|
||||
else {
|
||||
//Find i so that bhp_array[i-1] <= bhp <= bhp_array[i];
|
||||
//Since the BHP values might not be sorted, first search within
|
||||
//our interpolation values, and then try to extrapolate.
|
||||
int i=0;
|
||||
bool found = false;
|
||||
for (; i<nthp-1; ++i) {
|
||||
const double& y0 = bhp_array[i ];
|
||||
const double& y1 = bhp_array[i+1];
|
||||
|
||||
if (y0 < bhp && bhp <= y1) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
const double& x0 = thp_array[i ];
|
||||
const double& x1 = thp_array[i+1];
|
||||
const double& y0 = bhp_array[i ];
|
||||
const double& y1 = bhp_array[i+1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
else if (bhp <= bhp_array[0]) {
|
||||
//TODO: LOG extrapolation
|
||||
const double& x0 = thp_array[0];
|
||||
const double& x1 = thp_array[1];
|
||||
const double& y0 = bhp_array[0];
|
||||
const double& y1 = bhp_array[1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
//Target bhp greater than all values in array, extrapolate
|
||||
else if (bhp > bhp_array[nthp-1]) {
|
||||
//TODO: LOG extrapolation
|
||||
const double& x0 = thp_array[nthp-2];
|
||||
const double& x1 = thp_array[nthp-1];
|
||||
const double& y0 = bhp_array[nthp-2];
|
||||
const double& y1 = bhp_array[nthp-1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
else {
|
||||
OPM_THROW(std::logic_error, "Programmer error: Unable to find THP in THP array");
|
||||
}
|
||||
}
|
||||
|
||||
double thp = detail::findTHP(bhp_array, thp_array, bhp);
|
||||
return thp;
|
||||
}
|
||||
|
||||
@ -564,21 +235,6 @@ double VFPProdProperties::thp(int table_id,
|
||||
|
||||
|
||||
|
||||
const VFPProdTable* VFPProdProperties::getProdTable(int table_id) const {
|
||||
auto entry = m_tables.find(table_id);
|
||||
if (entry == m_tables.end()) {
|
||||
OPM_THROW(std::invalid_argument, "Nonexistent table " << table_id << " referenced.");
|
||||
}
|
||||
else {
|
||||
return entry->second;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -23,8 +23,6 @@
|
||||
#include <opm/parser/eclipse/EclipseState/Tables/VFPProdTable.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/autodiff/AutoDiffBlock.hpp>
|
||||
#include <opm/core/utility/ErrorMacros.hpp>
|
||||
#include <opm/autodiff/AutoDiffHelpers.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
@ -143,19 +141,6 @@ public:
|
||||
private:
|
||||
// Map which connects the table number with the table itself
|
||||
std::map<int, const VFPProdTable*> m_tables;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Initialization routine
|
||||
*/
|
||||
void init(const std::map<int, VFPProdTable>& prod_tables);
|
||||
|
||||
/**
|
||||
* Misc helper functions
|
||||
*/
|
||||
const VFPProdTable* getProdTable(int table_id) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <opm/autodiff/VFPProperties.hpp>
|
||||
#include <opm/autodiff/VFPProdProperties.hpp>
|
||||
#include <opm/autodiff/VFPInjProperties.hpp>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
@ -30,8 +31,7 @@ VFPProperties::VFPProperties() {
|
||||
|
||||
VFPProperties::VFPProperties(const VFPInjTable* inj_table, const VFPProdTable* prod_table) {
|
||||
if (inj_table != NULL) {
|
||||
//FIXME: Implement VFPInjProperties
|
||||
OPM_THROW(std::logic_error, "VFPInjProperties not implemented yet");
|
||||
m_inj.reset(new VFPInjProperties(inj_table));
|
||||
}
|
||||
if (prod_table != NULL) {
|
||||
m_prod.reset(new VFPProdProperties(prod_table));
|
||||
@ -40,19 +40,7 @@ VFPProperties::VFPProperties(const VFPInjTable* inj_table, const VFPProdTable* p
|
||||
|
||||
VFPProperties::VFPProperties(const std::map<int, VFPInjTable>& inj_tables,
|
||||
const std::map<int, VFPProdTable>& prod_tables) {
|
||||
//FIXME: Implement VFPInjProperties
|
||||
OPM_THROW(std::logic_error, "VFPInjProperties not implemented yet");
|
||||
m_prod.reset(new VFPProdProperties(prod_tables));
|
||||
}
|
||||
|
||||
|
||||
VFPProperties::VFPProperties(const std::map<int, VFPInjTable>& inj_tables) {
|
||||
//FIXME: Implement VFPInjProperties
|
||||
OPM_THROW(std::logic_error, "VFPInjProperties not implemented yet");
|
||||
}
|
||||
|
||||
|
||||
VFPProperties::VFPProperties(const std::map<int, VFPProdTable>& prod_tables) {
|
||||
m_inj.reset(new VFPInjProperties(inj_tables));
|
||||
m_prod.reset(new VFPProdProperties(prod_tables));
|
||||
}
|
||||
|
||||
|
@ -56,23 +56,15 @@ public:
|
||||
const std::map<int, VFPProdTable>& prod_tables);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Takes *no* ownership of data.
|
||||
* @param inj_tables A map of different VFPINJ tables.
|
||||
* Returns the VFP properties for injection wells
|
||||
*/
|
||||
VFPProperties(const std::map<int, VFPInjTable>& inj_tables);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Takes *no* ownership of data.
|
||||
* @param prod_tables A map of different VFPPROD tables.
|
||||
*/
|
||||
VFPProperties(const std::map<int, VFPProdTable>& prod_tables);
|
||||
|
||||
const VFPInjProperties* getInj() const {
|
||||
return m_inj.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the VFP properties for production wells
|
||||
*/
|
||||
const VFPProdProperties* getProd() const {
|
||||
return m_prod.get();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user