@@ -688,6 +688,7 @@ namespace Opm
|
||||
void handleCOMPORD (HandlerContext&);
|
||||
void handleCOMPSEGS (HandlerContext&);
|
||||
void handleCOMPTRAJ (HandlerContext&);
|
||||
void handleCSKIN (HandlerContext&);
|
||||
void handleDRSDT (HandlerContext&);
|
||||
void handleDRSDTCON (HandlerContext&);
|
||||
void handleDRSDTR (HandlerContext&);
|
||||
|
||||
@@ -116,6 +116,7 @@ namespace RestartIO {
|
||||
int complnum() const;
|
||||
int segment() const;
|
||||
double CF() const;
|
||||
double wpimult() const;
|
||||
double Kh() const;
|
||||
double rw() const;
|
||||
double r0() const;
|
||||
@@ -134,6 +135,8 @@ namespace RestartIO {
|
||||
|
||||
void setState(State state);
|
||||
void setComplnum(int compnum);
|
||||
void setSkinFactor(double skin_factor);
|
||||
void setCF(double CF);
|
||||
void scaleWellPi(double wellPi);
|
||||
bool prepareWellPIScaling();
|
||||
bool applyWellPIScaling(const double scaleFactor);
|
||||
@@ -264,6 +267,9 @@ namespace RestartIO {
|
||||
// Whether or not this Connection is subject to WELPI scaling.
|
||||
bool m_subject_to_welpi = false;
|
||||
|
||||
// For applying last known WPIMULT to when calculating connection transmissibilty factor in CSKIN
|
||||
double m_wpimult = 1.0;
|
||||
|
||||
std::optional<FilterCake> m_filter_cake;
|
||||
|
||||
static std::string CTFKindToString(const CTFKind);
|
||||
|
||||
@@ -479,6 +479,7 @@ public:
|
||||
bool handleWELSEGS(const DeckKeyword& keyword);
|
||||
bool handleCOMPSEGS(const DeckKeyword& keyword, const ScheduleGrid& grid, const ParseContext& parseContext, ErrorGuard& errors);
|
||||
bool handleWELOPENConnections(const DeckRecord& record, Connection::State status);
|
||||
bool handleCSKINConnections(const DeckRecord& record);
|
||||
bool handleCOMPLUMP(const DeckRecord& record);
|
||||
bool handleWPIMULT(const DeckRecord& record);
|
||||
bool handleWINJCLN(const DeckRecord& record, const KeywordLocation& location);
|
||||
|
||||
@@ -351,6 +351,27 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno)
|
||||
handlerContext.compsegs_handled(wname);
|
||||
}
|
||||
|
||||
void Schedule::handleCSKIN(HandlerContext& handlerContext) {
|
||||
// Get CSKIN keyword info and current step
|
||||
const auto& keyword = handlerContext.keyword;
|
||||
const auto& currentStep = handlerContext.currentStep;
|
||||
|
||||
// Loop over records in CSKIN
|
||||
for (const auto& record: keyword) {
|
||||
// Get well names
|
||||
const auto& wellNamePattern = record.getItem( "WELL" ).getTrimmedString(0);
|
||||
const auto well_names = this->wellNames(wellNamePattern, handlerContext);
|
||||
|
||||
// Loop over well(s) in record
|
||||
for (const auto& wname : well_names) {
|
||||
// Get well information, modify connection skin factor, and update well
|
||||
auto well = this->snapshots[currentStep].wells.get(wname);
|
||||
well.handleCSKINConnections(record);
|
||||
this->snapshots[currentStep].wells.update( std::move(well) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Schedule::handleDRSDT(HandlerContext& handlerContext) {
|
||||
std::size_t numPvtRegions = this->m_static.m_runspec.tabdims().getNumPVTTables();
|
||||
std::vector<double> maximums(numPvtRegions);
|
||||
@@ -2590,6 +2611,7 @@ Well{0} entered with 'FIELD' parent group:
|
||||
{ "COMPORD" , &Schedule::handleCOMPORD },
|
||||
{ "COMPSEGS", &Schedule::handleCOMPSEGS },
|
||||
{ "COMPTRAJ", &Schedule::handleCOMPTRAJ },
|
||||
{ "CSKIN", &Schedule::handleCSKIN },
|
||||
{ "DRSDT" , &Schedule::handleDRSDT },
|
||||
{ "DRSDTCON", &Schedule::handleDRSDTCON },
|
||||
{ "DRSDTR" , &Schedule::handleDRSDTR },
|
||||
|
||||
@@ -210,10 +210,22 @@ const std::optional<std::pair<double, double>>& Connection::perf_range() const {
|
||||
this->m_complnum = complnum;
|
||||
}
|
||||
|
||||
void Connection::setSkinFactor(double skin_factor) {
|
||||
this->m_skin_factor = skin_factor;
|
||||
}
|
||||
|
||||
void Connection::setCF(double CF) {
|
||||
this->m_CF = CF;
|
||||
}
|
||||
|
||||
double Connection::CF() const {
|
||||
return this->m_CF;
|
||||
}
|
||||
|
||||
double Connection::wpimult() const {
|
||||
return this->m_wpimult;
|
||||
}
|
||||
|
||||
double Connection::Kh() const {
|
||||
return this->m_Kh;
|
||||
}
|
||||
@@ -263,6 +275,7 @@ const std::optional<std::pair<double, double>>& Connection::perf_range() const {
|
||||
}
|
||||
|
||||
void Connection::scaleWellPi(double wellPi) {
|
||||
this->m_wpimult *= wellPi;
|
||||
this->m_CF *= wellPi;
|
||||
}
|
||||
|
||||
@@ -273,6 +286,7 @@ const std::optional<std::pair<double, double>>& Connection::perf_range() const {
|
||||
|
||||
return update;
|
||||
}
|
||||
|
||||
|
||||
bool Connection::applyWellPIScaling(const double scaleFactor) {
|
||||
if (! this->m_subject_to_welpi)
|
||||
|
||||
@@ -1248,9 +1248,46 @@ bool Well::handleWELOPENConnections(const DeckRecord& record, Connection::State
|
||||
return this->updateConnections(std::move(new_connections), false);
|
||||
}
|
||||
|
||||
bool Well::handleCSKINConnections(const DeckRecord& record) {
|
||||
// Lambda expression to check if record coordinates match connection coordinates
|
||||
auto match = [=]( const Connection &c) -> bool {
|
||||
if (!match_eq(c.getI(), record, "I" , -1)) return false;
|
||||
if (!match_eq(c.getJ(), record, "J" , -1)) return false;
|
||||
if (!match_ge(c.getK(), record, "K_UPPER", -1)) return false;
|
||||
if (!match_le(c.getK(), record, "K_LOWER", -1)) return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Generate a new connection which will be updated with new connection skin factor
|
||||
auto new_connections = std::make_shared<WellConnections>(this->connections->ordering(), this->headI, this->headJ);
|
||||
|
||||
// Update skin factor
|
||||
double skin_factor = record.getItem("CONNECTION_SKIN_FACTOR").get<double>(0);
|
||||
const double angle = 6.2831853071795864769252867665590057683943387987502116419498;
|
||||
for (auto c : *this->connections) {
|
||||
if (match(c)) {
|
||||
// Check for potential negative new CF
|
||||
if ((std::log(c.r0() / std::min(c.rw(), c.r0())) + skin_factor) < 0.0) {
|
||||
throw std::runtime_error("Negative connection transmissibility factor produced by CSKIN for well "
|
||||
+ name());
|
||||
}
|
||||
|
||||
// Calculate new connection transmissibility factor
|
||||
double CF = angle * c.Kh() / (std::log(c.r0() / std::min(c.rw(), c.r0())) + skin_factor);
|
||||
|
||||
// Apply last known WPIMULT (defaulted to 1.0)
|
||||
CF *= c.wpimult();
|
||||
|
||||
// Set skin factor and connection factor
|
||||
c.setSkinFactor(skin_factor);
|
||||
c.setCF(CF);
|
||||
}
|
||||
new_connections->add(c);
|
||||
}
|
||||
|
||||
return this->updateConnections(std::move(new_connections), false);
|
||||
}
|
||||
|
||||
bool Well::handleCOMPLUMP(const DeckRecord& record) {
|
||||
|
||||
|
||||
@@ -443,8 +443,12 @@ namespace Opm {
|
||||
if (KhItem.defaultApplied(0) || KhItem.getSIDouble(0) < 0) {
|
||||
Kh = CF * (std::log(r0 / std::min(r0, rw)) + skin_factor) / angle;
|
||||
} else {
|
||||
if (Kh < 0)
|
||||
if (Kh < 0) {
|
||||
Kh = std::sqrt(K[0] * K[1]) * D[2];
|
||||
|
||||
// Compute r0 to be consistent with other parameters
|
||||
r0 = RestartIO::RstConnection::inverse_peaceman(CF, Kh, rw, skin_factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -678,6 +678,249 @@ BOOST_AUTO_TEST_CASE(TestCrossFlowHandling) {
|
||||
BOOST_CHECK(Well::Status::OPEN == schedule.getWell("BAN", 5).getStatus());
|
||||
}
|
||||
|
||||
static std::string createDeckWithWellsAndSkinFactorChanges() {
|
||||
std::string input = R"(
|
||||
START -- 0
|
||||
1 NOV 1979 /
|
||||
GRID
|
||||
PORO
|
||||
1000*0.1 /
|
||||
PERMX
|
||||
1000*1 /
|
||||
PERMY
|
||||
1000*0.1 /
|
||||
PERMZ
|
||||
1000*0.01 /
|
||||
SCHEDULE
|
||||
DATES -- 1
|
||||
1 DES 1979/
|
||||
/
|
||||
WELSPECS
|
||||
'OP_1' 'OP' 9 9 1* 'OIL' 1* 1* 1* 1* 1* 1* 1* /
|
||||
'OP_2' 'OP' 8 8 1* 'OIL' 1* 1* 1* 1* 1* 1* 1* /
|
||||
'OP_3' 'OP' 7 7 1* 'OIL' 1* 1* 1* 1* 1* 1* 1* /
|
||||
/
|
||||
COMPDAT
|
||||
'OP_1' 9 9 1 1 'OPEN' 1* 32.948 0.311 3047.839 1* 1* 'X' 22.100 /
|
||||
'OP_1' 9 9 2 2 'OPEN' 1* 46.825 0.311 4332.346 1* 1* 'X' 22.123 /
|
||||
'OP_2' 8 8 1 3 'OPEN' 1* 1.168 0.311 107.872 1* 1* 'Y' 21.925 /
|
||||
'OP_2' 8 7 3 3 'OPEN' 1* 15.071 0.311 1391.859 1* 1* 'Y' 21.920 /
|
||||
'OP_2' 8 7 3 6 'OPEN' 1* 6.242 0.311 576.458 1* 1* 'Y' 21.915 /
|
||||
'OP_3' 7 7 1 1 'OPEN' 1* 27.412 0.311 2445.337 1* 1* 'Y' 18.521 /
|
||||
'OP_3' 7 7 2 2 'OPEN' 1* 55.195 0.311 4923.842 1* 1* 'Y' 18.524 /
|
||||
/
|
||||
DATES -- 2
|
||||
10 JUL 2007 /
|
||||
/
|
||||
|
||||
CSKIN
|
||||
'OP_1' 9 9 1 1 1.5 /
|
||||
'OP_2' 4* -1.0 /
|
||||
'OP_3' 2* 1 2 10.0 /
|
||||
'OP_3' 7 7 1 1 -1.15 /
|
||||
/
|
||||
|
||||
)";
|
||||
return input;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CreateScheduleDeckWellsAndSkinFactorChanges) {
|
||||
Opm::UnitSystem units(Opm::UnitSystem::UnitType::UNIT_TYPE_METRIC);
|
||||
const auto& schedule = make_schedule(createDeckWithWellsAndSkinFactorChanges());
|
||||
|
||||
// OP_1
|
||||
{
|
||||
const auto& cs = schedule.getWell("OP_1", 2).getConnections();
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).skinFactor(), 1.5, 1e-10);
|
||||
double CF = 25.290608354096133;
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, CF), 1e-5);
|
||||
}
|
||||
// OP_2
|
||||
{
|
||||
const auto& well = schedule.getWell("OP_2", 2);
|
||||
const auto& cs = well.getConnections();
|
||||
for (size_t i = 0; i < cs.size(); i++) {
|
||||
BOOST_CHECK_CLOSE(cs.get(i).skinFactor(), -1.0, 1e-10);
|
||||
}
|
||||
double CF = 7.822338909386947;
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(7, 6, 2).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, CF), 1e-5);
|
||||
}
|
||||
// OP_3
|
||||
{
|
||||
const auto& well = schedule.getWell("OP_3", 2);
|
||||
const auto& cs = well.getConnections();
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(6, 6, 0).skinFactor(), -1.15, 1e-10);
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(6, 6, 1).skinFactor(), 10.0, 1e-10);
|
||||
double CF1 = 36.09169888375442;
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(6, 6, 0).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, CF1), 1e-5);
|
||||
double CF2 = 17.848489977420336;
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(6, 6, 1).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, CF2), 1e-5);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string createDeckWithWPIMULTandWELPIandCSKIN() {
|
||||
std::string input = R"(
|
||||
START -- 0
|
||||
1 NOV 1979 /
|
||||
GRID
|
||||
PORO
|
||||
1000*0.1 /
|
||||
PERMX
|
||||
1000*1 /
|
||||
PERMY
|
||||
1000*0.1 /
|
||||
PERMZ
|
||||
1000*0.01 /
|
||||
SCHEDULE
|
||||
DATES -- 1
|
||||
1 DES 1979/
|
||||
/
|
||||
WELSPECS
|
||||
'OP_1' 'OP' 9 9 1* 'OIL' 1* 1* 1* 1* 1* 1* 1* /
|
||||
/
|
||||
COMPDAT
|
||||
'OP_1' 9 9 1 1 'OPEN' 1* 32.948 0.311 3047.839 1* 1* 'X' 22.100 /
|
||||
/
|
||||
|
||||
DATES -- 2
|
||||
10 JUL 2007 /
|
||||
/
|
||||
CSKIN
|
||||
'OP_1' 9 9 1 1 1.5 /
|
||||
/
|
||||
|
||||
DATES -- 3
|
||||
10 AUG 2007 /
|
||||
/
|
||||
WPIMULT
|
||||
OP_1 1.30 /
|
||||
/
|
||||
WPIMULT
|
||||
OP_1 1.30 /
|
||||
/
|
||||
|
||||
DATES -- 4
|
||||
10 SEP 2007 /
|
||||
/
|
||||
CSKIN
|
||||
'OP_1' 9 9 1 1 0.5 /
|
||||
/
|
||||
|
||||
DATES -- 5
|
||||
10 OCT 2007 /
|
||||
/
|
||||
WPIMULT
|
||||
OP_1 1.30 /
|
||||
/
|
||||
|
||||
DATES -- 6
|
||||
10 NOV 2007 /
|
||||
/
|
||||
WELPI
|
||||
OP_1 50 /
|
||||
/
|
||||
|
||||
DATES -- 7
|
||||
10 DEC 2007 /
|
||||
/
|
||||
CSKIN
|
||||
'OP_1' 9 9 1 1 5.0 /
|
||||
/
|
||||
|
||||
DATES -- 8
|
||||
10 JAN 2008 /
|
||||
/
|
||||
COMPDAT
|
||||
'OP_1' 9 9 1 1 'OPEN' 1* 32.948 0.311 3047.839 1* 1* 'X' 22.100 /
|
||||
/
|
||||
|
||||
DATES -- 9
|
||||
10 FEB 2008 /
|
||||
/
|
||||
CSKIN
|
||||
'OP_1' 9 9 1 1 -1.0 /
|
||||
/
|
||||
|
||||
)";
|
||||
return input;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CreateScheduleDeckWPIMULTandWELPIandCSKIN) {
|
||||
// Setup
|
||||
Opm::UnitSystem units(Opm::UnitSystem::UnitType::UNIT_TYPE_METRIC);
|
||||
auto schedule = make_schedule(createDeckWithWPIMULTandWELPIandCSKIN());
|
||||
|
||||
// Report step 2
|
||||
{
|
||||
const auto& cs = schedule.getWell("OP_1", 2).getConnections();
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).skinFactor(), 1.5, 1e-10);
|
||||
double CF = 25.290608354096133;
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, CF), 1e-5);
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).wpimult(), 1.0, 1e-5);
|
||||
}
|
||||
|
||||
// Report step 3
|
||||
{
|
||||
const auto& cs_prev = schedule.getWell("OP_1", 2).getConnections();
|
||||
const auto& cs_curr = schedule.getWell("OP_1", 3).getConnections();
|
||||
BOOST_CHECK_CLOSE(cs_curr.getFromIJK(8, 8, 0).CF() / cs_prev.getFromIJK(8, 8, 0).CF(), 1.3, 1e-5);
|
||||
BOOST_CHECK_CLOSE(cs_curr.getFromIJK(8, 8, 0).wpimult(), 1.3, 1e-5);
|
||||
}
|
||||
|
||||
// Report step 4
|
||||
{
|
||||
const auto& cs = schedule.getWell("OP_1", 4).getConnections();
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).skinFactor(), 0.5, 1e-10);
|
||||
double CF = 38.90302007377862; // CF from CSKIN multiplied by 1.3 from WPIMULT
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, CF), 1e-5);
|
||||
}
|
||||
|
||||
// Report step 5
|
||||
{
|
||||
const auto& cs_prev = schedule.getWell("OP_1", 4).getConnections();
|
||||
const auto& cs_curr = schedule.getWell("OP_1", 5).getConnections();
|
||||
BOOST_CHECK_CLOSE(cs_curr.getFromIJK(8, 8, 0).CF() / cs_prev.getFromIJK(8, 8, 0).CF(), 1.3, 1e-5);
|
||||
BOOST_CHECK_CLOSE(cs_curr.getFromIJK(8, 8, 0).wpimult(), 1.3 * 1.3, 1e-5);
|
||||
}
|
||||
|
||||
// Report step 6
|
||||
{
|
||||
const auto& cs = schedule.getWell("OP_1", 6).getConnections();
|
||||
double init_pi = 100.0;
|
||||
schedule.applyWellProdIndexScaling("OP_1", 6, units.to_si(Opm::UnitSystem::measure::liquid_productivity_index, init_pi));
|
||||
const auto& target_pi = schedule[6].target_wellpi.at("OP_1");
|
||||
BOOST_CHECK_CLOSE(target_pi, 50.0, 1e-5);
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).wpimult(), 1.3 * 1.3 * (target_pi / init_pi), 1e-5);
|
||||
}
|
||||
|
||||
// Report step 7
|
||||
{
|
||||
const auto& cs_prev = schedule.getWell("OP_1", 6).getConnections();
|
||||
const auto& cs_curr = schedule.getWell("OP_1", 7).getConnections();
|
||||
BOOST_CHECK_CLOSE(cs_curr.getFromIJK(8, 8, 0).skinFactor(), 5.0, 1e-10);
|
||||
double CF = 13.858329011932668; // CF from CSKIN multiplied by 0.845 from WPIMULT and WELPI previous
|
||||
BOOST_CHECK_CLOSE(cs_curr.getFromIJK(8, 8, 0).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, CF), 1e-5);
|
||||
BOOST_CHECK_CLOSE(cs_curr.getFromIJK(8, 8, 0).wpimult(), cs_prev.getFromIJK(8, 8, 0).wpimult(), 1e-5);
|
||||
}
|
||||
|
||||
// Report step 8
|
||||
{
|
||||
const auto& cs = schedule.getWell("OP_1", 8).getConnections();
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, 32.948), 1e-5);
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).wpimult(), 1.0, 1e-5);
|
||||
}
|
||||
|
||||
// Report step 9
|
||||
{
|
||||
const auto& cs = schedule.getWell("OP_1", 9).getConnections();
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).skinFactor(), -1.0, 1e-10);
|
||||
double CF = 41.27026972084714; // CF from CSKIN with WPIMULT and WELLPI multiplier reset to 1.0
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).CF(), units.to_si(Opm::UnitSystem::measure::transmissibility, CF), 1e-5);
|
||||
BOOST_CHECK_CLOSE(cs.getFromIJK(8, 8, 0).wpimult(), 1.0, 1e-5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static std::string createDeckWithWellsAndConnectionDataWithWELOPEN() {
|
||||
std::string input = R"(
|
||||
START -- 0
|
||||
|
||||
Reference in New Issue
Block a user