From f933b0e113681243be5fa6573c40cd2cf9b3d8fd Mon Sep 17 00:00:00 2001 From: Svenn Tveit Date: Mon, 11 Sep 2023 15:25:42 +0200 Subject: [PATCH 1/6] Implementation of CSKIN keyword. To work with WPIMULT, the PI scaling factor had to be stored and applied to calculated CF. --- opm/input/eclipse/Schedule/Schedule.hpp | 1 + .../eclipse/Schedule/Well/Connection.hpp | 6 +++ opm/input/eclipse/Schedule/Well/Well.hpp | 1 + .../eclipse/Schedule/KeywordHandlers.cpp | 22 +++++++++++ .../eclipse/Schedule/Well/Connection.cpp | 14 +++++++ src/opm/input/eclipse/Schedule/Well/Well.cpp | 37 +++++++++++++++++++ 6 files changed, 81 insertions(+) diff --git a/opm/input/eclipse/Schedule/Schedule.hpp b/opm/input/eclipse/Schedule/Schedule.hpp index d0da80a24..42c01d56b 100644 --- a/opm/input/eclipse/Schedule/Schedule.hpp +++ b/opm/input/eclipse/Schedule/Schedule.hpp @@ -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&); diff --git a/opm/input/eclipse/Schedule/Well/Connection.hpp b/opm/input/eclipse/Schedule/Well/Connection.hpp index ac2da506e..ff73ae5b4 100644 --- a/opm/input/eclipse/Schedule/Well/Connection.hpp +++ b/opm/input/eclipse/Schedule/Well/Connection.hpp @@ -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 m_filter_cake; static std::string CTFKindToString(const CTFKind); diff --git a/opm/input/eclipse/Schedule/Well/Well.hpp b/opm/input/eclipse/Schedule/Well/Well.hpp index f6b90e7e4..76bde9442 100644 --- a/opm/input/eclipse/Schedule/Well/Well.hpp +++ b/opm/input/eclipse/Schedule/Well/Well.hpp @@ -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); diff --git a/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp b/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp index 56d9a5f81..f57358d54 100644 --- a/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp +++ b/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp @@ -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 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 }, diff --git a/src/opm/input/eclipse/Schedule/Well/Connection.cpp b/src/opm/input/eclipse/Schedule/Well/Connection.cpp index c214055ee..b4a30391a 100644 --- a/src/opm/input/eclipse/Schedule/Well/Connection.cpp +++ b/src/opm/input/eclipse/Schedule/Well/Connection.cpp @@ -210,10 +210,22 @@ const std::optional>& 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>& Connection::perf_range() const { } void Connection::scaleWellPi(double wellPi) { + this->m_wpimult = wellPi; this->m_CF *= wellPi; } @@ -273,6 +286,7 @@ const std::optional>& Connection::perf_range() const { return update; } + bool Connection::applyWellPIScaling(const double scaleFactor) { if (! this->m_subject_to_welpi) diff --git a/src/opm/input/eclipse/Schedule/Well/Well.cpp b/src/opm/input/eclipse/Schedule/Well/Well.cpp index eec217798..6893345bd 100644 --- a/src/opm/input/eclipse/Schedule/Well/Well.cpp +++ b/src/opm/input/eclipse/Schedule/Well/Well.cpp @@ -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(this->connections->ordering(), this->headI, this->headJ); + // Update skin factor + double skin_factor = record.getItem("CONNECTION_SKIN_FACTOR").get(0); + const double angle = 6.2831853071795864769252867665590057683943387987502116419498; + for (auto c : *this->connections) { + if (match(c)) { + // 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(); + + // Check if new CF is negative + if (CF < 0.0) { + throw std::runtime_error("Negative connection transmissibility factor produced by CSKIN for well " + + name()); + } + + // 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) { From d410d8c23249212d0b45ea2854e0aa6798460b6f Mon Sep 17 00:00:00 2001 From: Svenn Tveit Date: Mon, 11 Sep 2023 15:28:26 +0200 Subject: [PATCH 2/6] Recalculate pressure equivalent radius when Kh=0. Kh is calculated from grid block info and r0 must be consistent with that. --- src/opm/input/eclipse/Schedule/Well/WellConnections.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/opm/input/eclipse/Schedule/Well/WellConnections.cpp b/src/opm/input/eclipse/Schedule/Well/WellConnections.cpp index 32e39b641..256db8325 100644 --- a/src/opm/input/eclipse/Schedule/Well/WellConnections.cpp +++ b/src/opm/input/eclipse/Schedule/Well/WellConnections.cpp @@ -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); + } } } } From 30cd8b814156d0688237000f1656bc5e42293eb2 Mon Sep 17 00:00:00 2001 From: Svenn Tveit Date: Thu, 14 Sep 2023 09:48:55 +0200 Subject: [PATCH 3/6] Test CSKIN input --- tests/parser/ScheduleTests.cpp | 80 ++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/tests/parser/ScheduleTests.cpp b/tests/parser/ScheduleTests.cpp index 5f72b8731..4c8685172 100644 --- a/tests/parser/ScheduleTests.cpp +++ b/tests/parser/ScheduleTests.cpp @@ -678,6 +678,86 @@ 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 createDeckWithWellsAndConnectionDataWithWELOPEN() { std::string input = R"( START -- 0 From 0f40d2c42cfb046219078ec9e22155b7434616f6 Mon Sep 17 00:00:00 2001 From: Svenn Tveit Date: Fri, 15 Sep 2023 18:49:55 +0200 Subject: [PATCH 4/6] wpimult must be multiplied and not overwritten. Mulitple WPIMULT and WELPI entries are cumulative on same connections. --- src/opm/input/eclipse/Schedule/Well/Connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/opm/input/eclipse/Schedule/Well/Connection.cpp b/src/opm/input/eclipse/Schedule/Well/Connection.cpp index b4a30391a..82b2bee5b 100644 --- a/src/opm/input/eclipse/Schedule/Well/Connection.cpp +++ b/src/opm/input/eclipse/Schedule/Well/Connection.cpp @@ -275,7 +275,7 @@ const std::optional>& Connection::perf_range() const { } void Connection::scaleWellPi(double wellPi) { - this->m_wpimult = wellPi; + this->m_wpimult *= wellPi; this->m_CF *= wellPi; } From a89bac6fdb65fadddf3a8acc36250b85bf8094e5 Mon Sep 17 00:00:00 2001 From: Svenn Tveit Date: Fri, 15 Sep 2023 18:51:35 +0200 Subject: [PATCH 5/6] Test CSKIN with WPIMULT, WELPI and COMPDAT --- tests/parser/ScheduleTests.cpp | 163 +++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/tests/parser/ScheduleTests.cpp b/tests/parser/ScheduleTests.cpp index 4c8685172..e422f4e09 100644 --- a/tests/parser/ScheduleTests.cpp +++ b/tests/parser/ScheduleTests.cpp @@ -758,6 +758,169 @@ BOOST_AUTO_TEST_CASE(CreateScheduleDeckWellsAndSkinFactorChanges) { } } +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 From 44f6a915fc7a35513d4fcf9990f87b62986eda4b Mon Sep 17 00:00:00 2001 From: Svenn Tveit Date: Fri, 15 Sep 2023 19:06:01 +0200 Subject: [PATCH 6/6] Check for negative CF before calclating it --- src/opm/input/eclipse/Schedule/Well/Well.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/opm/input/eclipse/Schedule/Well/Well.cpp b/src/opm/input/eclipse/Schedule/Well/Well.cpp index 6893345bd..6ce34f26d 100644 --- a/src/opm/input/eclipse/Schedule/Well/Well.cpp +++ b/src/opm/input/eclipse/Schedule/Well/Well.cpp @@ -1267,18 +1267,18 @@ bool Well::handleCSKINConnections(const DeckRecord& record) { 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(); - // Check if new CF is negative - if (CF < 0.0) { - throw std::runtime_error("Negative connection transmissibility factor produced by CSKIN for well " - + name()); - } - // Set skin factor and connection factor c.setSkinFactor(skin_factor); c.setCF(CF);