Merge pull request #2031 from bska/schedule-apply-wellpi
Apply WELPI CTF Scaling at Schedule Level
This commit is contained in:
commit
bf2f049a4b
@ -106,12 +106,6 @@ namespace Opm
|
||||
* New explicit well productivity/injectivity assignment.
|
||||
*/
|
||||
WELL_PRODUCTIVITY_INDEX = (1 << 16),
|
||||
|
||||
/*
|
||||
* Well's internal WellConnections structure changed.
|
||||
* Rerun WELPI scaling if applicable.
|
||||
*/
|
||||
WELL_CONNECTIONS_UPDATED = (1 << 17),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -272,6 +272,7 @@ namespace Opm
|
||||
RestartConfig& restart();
|
||||
|
||||
void applyAction(std::size_t reportStep, const Action::ActionX& action, const Action::Result& result);
|
||||
void applyWellProdIndexScaling(const std::string& well_name, const std::size_t reportStep, const double scalingFactor);
|
||||
int getNupcol(std::size_t reportStep) const;
|
||||
|
||||
|
||||
|
@ -123,7 +123,7 @@ namespace RestartIO {
|
||||
void setComplnum(int compnum);
|
||||
void scaleWellPi(double wellPi);
|
||||
bool prepareWellPIScaling();
|
||||
void applyWellPIScaling(const double scaleFactor);
|
||||
bool applyWellPIScaling(const double scaleFactor);
|
||||
void updateSegmentRST(int segment_number_arg,
|
||||
double center_depth_arg);
|
||||
void updateSegment(int segment_number_arg,
|
||||
|
@ -598,8 +598,11 @@ public:
|
||||
bool updateHasProduced();
|
||||
bool cmp_structure(const Well& other) const;
|
||||
bool operator==(const Well& data) const;
|
||||
bool hasSameConnectionsPointers(const Well& other) const;
|
||||
void setInsertIndex(std::size_t index);
|
||||
void applyWellProdIndexScaling(const double currentEffectivePI);
|
||||
double getWellPIScalingFactor(const double currentEffectivePI) const;
|
||||
void applyWellProdIndexScaling(const double scalingFactor,
|
||||
std::vector<bool>& scalingApplicable);
|
||||
|
||||
template<class Serializer>
|
||||
void serializeOp(Serializer& serializer)
|
||||
|
@ -111,8 +111,13 @@ namespace Opm {
|
||||
|
||||
/// Scale pertinent connections' CF value by supplied value. Scaling
|
||||
/// factor typically derived from 'WELPI' input keyword and a dynamic
|
||||
/// productivity index calculation.
|
||||
void applyWellPIScaling(const double scaleFactor);
|
||||
/// productivity index calculation. Applicability array specifies
|
||||
/// whether or not a particular connection is exempt from scaling.
|
||||
/// Empty array means "apply scaling to all eligible connections".
|
||||
/// This array is updated on return (entries set to 'false' if
|
||||
/// corresponding connection is not eligible).
|
||||
void applyWellPIScaling(const double scaleFactor,
|
||||
std::vector<bool>& scalingApplicable);
|
||||
|
||||
template<class Serializer>
|
||||
void serializeOp(Serializer& serializer)
|
||||
|
@ -154,10 +154,8 @@ namespace {
|
||||
auto connections = std::shared_ptr<WellConnections>( new WellConnections( well2->getConnections()));
|
||||
connections->loadCOMPDAT(record, handlerContext.grid, handlerContext.fieldPropsManager);
|
||||
|
||||
if (well2->updateConnections(connections, handlerContext.grid, handlerContext.fieldPropsManager.get_int("PVTNUM"))) {
|
||||
this->updateWell(well2, handlerContext.currentStep);
|
||||
this->addWellGroupEvent(name, ScheduleEvents::WELL_CONNECTIONS_UPDATED, handlerContext.currentStep);
|
||||
}
|
||||
if (well2->updateConnections(connections, handlerContext.grid, handlerContext.fieldPropsManager.get_int("PVTNUM")))
|
||||
this->updateWell(std::move(well2), handlerContext.currentStep);
|
||||
|
||||
this->addWellGroupEvent(name, ScheduleEvents::COMPLETION_CHANGE, handlerContext.currentStep);
|
||||
}
|
||||
|
@ -17,16 +17,17 @@
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
|
||||
#include <fnmatch.h>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
@ -666,10 +667,9 @@ private:
|
||||
{
|
||||
auto& dynamic_state = this->wells_static.at(wname);
|
||||
auto well_ptr = std::make_shared<Well>( *dynamic_state[currentStep] );
|
||||
if (well_ptr->handleWELOPEN(record, comp_status, action_mode)) {
|
||||
if (well_ptr->handleWELOPEN(record, comp_status, action_mode))
|
||||
// The updateWell call breaks test at line 825 and 831 in ScheduleTests
|
||||
this->updateWell(well_ptr, currentStep);
|
||||
}
|
||||
this->updateWell(std::move(well_ptr), currentStep);
|
||||
}
|
||||
|
||||
m_events.addEvent( ScheduleEvents::COMPLETION_CHANGE, currentStep );
|
||||
@ -1499,6 +1499,38 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void Schedule::applyWellProdIndexScaling(const std::string& well_name, const std::size_t reportStep, const double scalingFactor) {
|
||||
auto wstat = this->wells_static.find(well_name);
|
||||
if (wstat == this->wells_static.end())
|
||||
return;
|
||||
|
||||
auto unique_well_instances = wstat->second.unique();
|
||||
|
||||
auto end = unique_well_instances.end();
|
||||
auto start = std::lower_bound(unique_well_instances.begin(), end, reportStep,
|
||||
[](const auto& time_well_pair, const auto lookup) -> bool
|
||||
{
|
||||
// time < reportStep
|
||||
return time_well_pair.first < lookup;
|
||||
});
|
||||
|
||||
if (start == end)
|
||||
// Report step after last?
|
||||
return;
|
||||
|
||||
// Relies on wells_static being OrderedMap<string, DynamicState<shared_ptr<>>>
|
||||
// which means unique_well_instances is a vector<pair<report_step, shared_ptr<>>>
|
||||
std::vector<bool> scalingApplicable;
|
||||
auto wellPtr = start->second;
|
||||
wellPtr->applyWellProdIndexScaling(scalingFactor, scalingApplicable);
|
||||
|
||||
for (; start != end; ++start)
|
||||
if (! wellPtr->hasSameConnectionsPointers(*start->second)) {
|
||||
wellPtr = start->second;
|
||||
wellPtr->applyWellProdIndexScaling(scalingFactor, scalingApplicable);
|
||||
}
|
||||
}
|
||||
|
||||
RestartConfig& Schedule::restart() {
|
||||
return this->restart_config;
|
||||
}
|
||||
|
@ -256,11 +256,12 @@ const std::optional<std::pair<double, double>>& Connection::perf_range() const {
|
||||
return update;
|
||||
}
|
||||
|
||||
void Connection::applyWellPIScaling(const double scaleFactor) {
|
||||
bool Connection::applyWellPIScaling(const double scaleFactor) {
|
||||
if (! this->m_subject_to_welpi)
|
||||
return;
|
||||
return false;
|
||||
|
||||
this->scaleWellPi(scaleFactor);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Connection::str() const {
|
||||
|
@ -821,12 +821,35 @@ const std::string& Well::name() const {
|
||||
return this->wname;
|
||||
}
|
||||
|
||||
bool Well::hasSameConnectionsPointers(const Well& other) const
|
||||
{
|
||||
// Note: This is *supposed* to be a pointer comparison. We need to know
|
||||
// if the two connection structures represent the exact same object, not
|
||||
// just if they have the same value.
|
||||
return this->connections == other.connections;
|
||||
}
|
||||
|
||||
void Well::setInsertIndex(std::size_t index) {
|
||||
this->insert_index = index;
|
||||
}
|
||||
|
||||
void Well::applyWellProdIndexScaling(const double currentEffectivePI) {
|
||||
double Well::getWellPIScalingFactor(const double currentEffectivePI) const {
|
||||
if (this->connections->empty())
|
||||
// No connections for this well. Unexpected.
|
||||
return 1.0;
|
||||
|
||||
if (!this->productivity_index)
|
||||
// WELPI not activated. Nothing to do.
|
||||
return 1.0;
|
||||
|
||||
if (this->productivity_index->pi_value == currentEffectivePI)
|
||||
// No change in scaling.
|
||||
return 1.0;
|
||||
|
||||
return this->productivity_index->pi_value / currentEffectivePI;
|
||||
}
|
||||
|
||||
void Well::applyWellProdIndexScaling(const double scalingFactor, std::vector<bool>& scalingApplicable) {
|
||||
if (this->connections->empty())
|
||||
// No connections for this well. Unexpected.
|
||||
return;
|
||||
@ -835,11 +858,11 @@ void Well::applyWellProdIndexScaling(const double currentEffectivePI) {
|
||||
// WELPI not activated. Nothing to do.
|
||||
return;
|
||||
|
||||
if (this->productivity_index->pi_value == currentEffectivePI)
|
||||
if (scalingFactor == 1.0)
|
||||
// No change in scaling.
|
||||
return;
|
||||
|
||||
this->connections->applyWellPIScaling(this->productivity_index->pi_value / currentEffectivePI);
|
||||
this->connections->applyWellPIScaling(scalingFactor, scalingApplicable);
|
||||
}
|
||||
|
||||
const WellConnections& Well::getConnections() const {
|
||||
|
@ -195,10 +195,17 @@ inline std::array< size_t, 3> directionIndices(const Opm::Connection::Direction
|
||||
return update;
|
||||
}
|
||||
|
||||
void WellConnections::applyWellPIScaling(const double scaleFactor)
|
||||
void WellConnections::applyWellPIScaling(const double scaleFactor,
|
||||
std::vector<bool>& scalingApplicable)
|
||||
{
|
||||
for (auto& conn : this->m_connections)
|
||||
conn.applyWellPIScaling(scaleFactor);
|
||||
scalingApplicable.resize(std::max(scalingApplicable.size(), this->m_connections.size()), true);
|
||||
|
||||
auto i = std::size_t{0};
|
||||
for (auto& conn : this->m_connections) {
|
||||
if (scalingApplicable[i])
|
||||
scalingApplicable[i] = conn.applyWellPIScaling(scaleFactor);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void WellConnections::addConnection(int i, int j , int k ,
|
||||
|
@ -408,17 +408,26 @@ END
|
||||
BOOST_CHECK_CLOSE(conn.CF(), expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
connP.applyWellPIScaling(2.0); // No "prepare" -> no change.
|
||||
for (const auto& conn : connP) {
|
||||
BOOST_CHECK_CLOSE(conn.CF(), expectCF, 1.0e-10);
|
||||
{
|
||||
std::vector<bool> scalingApplicable;
|
||||
|
||||
connP.applyWellPIScaling(2.0, scalingApplicable); // No "prepare" -> no change.
|
||||
for (const auto& conn : connP) {
|
||||
BOOST_CHECK_CLOSE(conn.CF(), expectCF, 1.0e-10);
|
||||
}
|
||||
}
|
||||
|
||||
// All CFs scaled by factor 2.
|
||||
BOOST_CHECK_MESSAGE( connP.prepareWellPIScaling(), "First call to prepareWellPIScaling must be a state change");
|
||||
BOOST_CHECK_MESSAGE(!connP.prepareWellPIScaling(), "Second call to prepareWellPIScaling must NOT be a state change");
|
||||
connP.applyWellPIScaling(2.0);
|
||||
for (const auto& conn : connP) {
|
||||
BOOST_CHECK_CLOSE(conn.CF(), 2.0*expectCF, 1.0e-10);
|
||||
|
||||
{
|
||||
std::vector<bool> scalingApplicable;
|
||||
|
||||
connP.applyWellPIScaling(2.0, scalingApplicable); // No "prepare" -> no change.
|
||||
for (const auto& conn : connP) {
|
||||
BOOST_CHECK_CLOSE(conn.CF(), 2.0*expectCF, 1.0e-10);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset CF -- simulating COMPDAT record (inactive cell)
|
||||
@ -440,20 +449,27 @@ END
|
||||
BOOST_CHECK_CLOSE(connP[2].CF(), 50.0*cp_rm3_per_db(), 1.0e-10);
|
||||
|
||||
// Should not apply to connection whose CF was manually specified
|
||||
connP.applyWellPIScaling(2.0);
|
||||
{
|
||||
std::vector<bool> scalingApplicable;
|
||||
connP.applyWellPIScaling(2.0, scalingApplicable);
|
||||
|
||||
BOOST_CHECK_CLOSE(connP[0].CF(), 4.0*expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[1].CF(), 4.0*expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[2].CF(), 50.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[0].CF(), 4.0*expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[1].CF(), 4.0*expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[2].CF(), 50.0*cp_rm3_per_db(), 1.0e-10);
|
||||
}
|
||||
|
||||
// Prepare new scaling. Simulating new WELPI record.
|
||||
// New scaling applies to all connections.
|
||||
BOOST_CHECK_MESSAGE(connP.prepareWellPIScaling(), "Third call to prepareWellPIScaling must be a state change");
|
||||
connP.applyWellPIScaling(2.0);
|
||||
|
||||
BOOST_CHECK_CLOSE(connP[0].CF(), 8.0*expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[1].CF(), 8.0*expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[2].CF(), 100.0*cp_rm3_per_db(), 1.0e-10);
|
||||
{
|
||||
std::vector<bool> scalingApplicable;
|
||||
connP.applyWellPIScaling(2.0, scalingApplicable);
|
||||
|
||||
BOOST_CHECK_CLOSE(connP[0].CF(), 8.0*expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[1].CF(), 8.0*expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[2].CF(), 100.0*cp_rm3_per_db(), 1.0e-10);
|
||||
}
|
||||
|
||||
// Reset CF -- simulating COMPDAT record (active cell)
|
||||
connP.addConnection(8, 9, 1, // 10, 10, 2
|
||||
@ -468,12 +484,16 @@ END
|
||||
1);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(connP.size(), std::size_t{4});
|
||||
connP.applyWellPIScaling(2.0);
|
||||
|
||||
BOOST_CHECK_CLOSE(connP[0].CF(), 16.0*expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[1].CF(), 16.0*expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[2].CF(), 200.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[3].CF(), 50.0*cp_rm3_per_db(), 1.0e-10);
|
||||
{
|
||||
std::vector<bool> scalingApplicable;
|
||||
connP.applyWellPIScaling(2.0, scalingApplicable);
|
||||
|
||||
BOOST_CHECK_CLOSE(connP[0].CF(), 16.0*expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[1].CF(), 16.0*expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[2].CF(), 200.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[3].CF(), 50.0*cp_rm3_per_db(), 1.0e-10);
|
||||
}
|
||||
|
||||
const auto& grid = es.getInputGrid();
|
||||
const auto actCells = Opm::ActiveGridCells {
|
||||
@ -489,8 +509,12 @@ END
|
||||
BOOST_CHECK_CLOSE(connP[1].CF(), 16.0*expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[2].CF(), 50.0*cp_rm3_per_db(), 1.0e-10);
|
||||
|
||||
connP.applyWellPIScaling(2.0);
|
||||
BOOST_CHECK_CLOSE(connP[0].CF(), 32.0*expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[1].CF(), 32.0*expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[2].CF(), 50.0*cp_rm3_per_db(), 1.0e-10);
|
||||
{
|
||||
std::vector<bool> scalingApplicable;
|
||||
|
||||
connP.applyWellPIScaling(2.0, scalingApplicable);
|
||||
BOOST_CHECK_CLOSE(connP[0].CF(), 32.0*expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[1].CF(), 32.0*expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[2].CF(), 50.0*cp_rm3_per_db(), 1.0e-10);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
@ -3779,10 +3780,15 @@ END
|
||||
const auto expectCF = 100.0*cp_rm3_per_db();
|
||||
auto wellP = sched.getWell("P", 0);
|
||||
|
||||
wellP.applyWellProdIndexScaling(2.7182818);
|
||||
std::vector<bool> scalingApplicable;
|
||||
wellP.applyWellProdIndexScaling(2.7182818, scalingApplicable);
|
||||
for (const auto& conn : wellP.getConnections()) {
|
||||
BOOST_CHECK_CLOSE(conn.CF(), expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
for (const bool applicable : scalingApplicable) {
|
||||
BOOST_CHECK_MESSAGE(! applicable, "No connection must be eligible for WELPI scaling");
|
||||
}
|
||||
}
|
||||
|
||||
// Apply WELPI after seeing WELPI data.
|
||||
@ -3790,10 +3796,18 @@ END
|
||||
const auto expectCF = (200.0 / 100.0) * 100.0*cp_rm3_per_db();
|
||||
auto wellP = sched.getWell("P", 1);
|
||||
|
||||
wellP.applyWellProdIndexScaling(100.0*liquid_PI_unit());
|
||||
const auto scalingFactor = wellP.getWellPIScalingFactor(100.0*liquid_PI_unit());
|
||||
BOOST_CHECK_CLOSE(scalingFactor, 2.0, 1.0e-10);
|
||||
|
||||
std::vector<bool> scalingApplicable;
|
||||
wellP.applyWellProdIndexScaling(scalingFactor, scalingApplicable);
|
||||
for (const auto& conn : wellP.getConnections()) {
|
||||
BOOST_CHECK_CLOSE(conn.CF(), expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
for (const bool applicable : scalingApplicable) {
|
||||
BOOST_CHECK_MESSAGE(applicable, "All connections must be eligible for WELPI scaling");
|
||||
}
|
||||
}
|
||||
|
||||
// Apply WELPI after new COMPDAT.
|
||||
@ -3801,29 +3815,329 @@ END
|
||||
const auto expectCF = (200.0 / 100.0) * 100.0*cp_rm3_per_db();
|
||||
auto wellP = sched.getWell("P", 2);
|
||||
|
||||
wellP.applyWellProdIndexScaling(100.0*liquid_PI_unit());
|
||||
const auto scalingFactor = wellP.getWellPIScalingFactor(100.0*liquid_PI_unit());
|
||||
BOOST_CHECK_CLOSE(scalingFactor, 2.0, 1.0e-10);
|
||||
|
||||
std::vector<bool> scalingApplicable;
|
||||
wellP.applyWellProdIndexScaling(scalingFactor, scalingApplicable);
|
||||
const auto& connP = wellP.getConnections();
|
||||
BOOST_CHECK_CLOSE(connP[0].CF(), expectCF , 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[1].CF(), 50*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(connP[2].CF(), expectCF , 1.0e-10);
|
||||
|
||||
BOOST_CHECK_MESSAGE(bool(scalingApplicable[0]), "Connection[0] must be eligible for WELPI scaling");
|
||||
BOOST_CHECK_MESSAGE(! scalingApplicable[1] , "Connection[1] must NOT be eligible for WELPI scaling");
|
||||
BOOST_CHECK_MESSAGE(bool(scalingApplicable[0]), "Connection[2] must be eligible for WELPI scaling");
|
||||
}
|
||||
|
||||
BOOST_CHECK_MESSAGE(sched.hasWellGroupEvent("P", ScheduleEvents::WELL_CONNECTIONS_UPDATED, 0),
|
||||
"Well P must have WELL_CONNECTIONS_UPDATED event at report step 0");
|
||||
|
||||
BOOST_CHECK_MESSAGE(!sched.hasWellGroupEvent("P", ScheduleEvents::WELL_CONNECTIONS_UPDATED, 1),
|
||||
"Well P must NOT have WELL_CONNECTIONS_UPDATED event at report step 1");
|
||||
|
||||
BOOST_CHECK_MESSAGE(sched.hasWellGroupEvent("P", ScheduleEvents::WELL_CONNECTIONS_UPDATED, 2),
|
||||
"Well P must have WELL_CONNECTIONS_UPDATED event at report step 2");
|
||||
|
||||
BOOST_CHECK_MESSAGE(!sched.hasWellGroupEvent("P", ScheduleEvents::WELL_CONNECTIONS_UPDATED, 3),
|
||||
"Well P must NOT have WELL_CONNECTIONS_UPDATED event at report step 3");
|
||||
|
||||
BOOST_CHECK_MESSAGE(sched.hasWellGroupEvent("P", ScheduleEvents::WELL_PRODUCTIVITY_INDEX, 1),
|
||||
"Must have WELL_PRODUCTIVITY_INDEX event at report step 1");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Schedule_ApplyWellProdIndexScaling) {
|
||||
const auto deck = Parser{}.parseString(R"(RUNSPEC
|
||||
START
|
||||
7 OCT 2020 /
|
||||
|
||||
DIMENS
|
||||
10 10 3 /
|
||||
|
||||
GRID
|
||||
DXV
|
||||
10*100.0 /
|
||||
DYV
|
||||
10*100.0 /
|
||||
DZV
|
||||
3*10.0 /
|
||||
|
||||
DEPTHZ
|
||||
121*2000.0 /
|
||||
|
||||
PERMX
|
||||
300*100.0 /
|
||||
PERMY
|
||||
300*100.0 /
|
||||
PERMZ
|
||||
300*10.0 /
|
||||
PORO
|
||||
300*0.3 /
|
||||
|
||||
SCHEDULE
|
||||
WELSPECS -- 0
|
||||
'P' 'G' 10 10 2005 'LIQ' /
|
||||
/
|
||||
COMPDAT
|
||||
'P' 0 0 1 3 OPEN 1 100 /
|
||||
/
|
||||
|
||||
TSTEP -- 1
|
||||
10
|
||||
/
|
||||
|
||||
WELPI -- 1
|
||||
'P' 200.0 /
|
||||
/
|
||||
|
||||
TSTEP -- 2
|
||||
10
|
||||
/
|
||||
|
||||
COMPDAT -- 2
|
||||
'P' 0 0 2 2 OPEN 1 50 /
|
||||
/
|
||||
|
||||
TSTEP -- 3
|
||||
10
|
||||
/
|
||||
|
||||
WELPI --3
|
||||
'P' 50.0 /
|
||||
/
|
||||
|
||||
TSTEP -- 4
|
||||
10
|
||||
/
|
||||
|
||||
COMPDAT -- 4
|
||||
'P' 10 9 2 2 OPEN 1 100 1.0 3* 'Y' /
|
||||
'P' 10 8 2 2 OPEN 1 75 1.0 3* 'Y' /
|
||||
'P' 10 7 2 2 OPEN 1 25 1.0 3* 'Y' /
|
||||
/
|
||||
|
||||
TSTEP -- 5
|
||||
10
|
||||
/
|
||||
|
||||
END
|
||||
)");
|
||||
|
||||
const auto es = EclipseState{ deck };
|
||||
auto sched = Schedule{ deck, es };
|
||||
|
||||
BOOST_REQUIRE_EQUAL(sched.getTimeMap().size(), std::size_t{6});
|
||||
BOOST_REQUIRE_EQUAL(sched.getTimeMap().numTimesteps(), std::size_t{5});
|
||||
BOOST_REQUIRE_EQUAL(sched.getTimeMap().last(), std::size_t{5});
|
||||
|
||||
BOOST_REQUIRE_MESSAGE(sched.hasWellGroupEvent("P", ScheduleEvents::Events::WELL_PRODUCTIVITY_INDEX, 1),
|
||||
"Schedule must have WELL_PRODUCTIVITY_INDEX Event at report step 1");
|
||||
|
||||
BOOST_REQUIRE_MESSAGE(sched.hasWellGroupEvent("P", ScheduleEvents::Events::WELL_PRODUCTIVITY_INDEX, 3),
|
||||
"Schedule must have WELL_PRODUCTIVITY_INDEX Event at report step 3");
|
||||
|
||||
auto getScalingFactor = [&sched](const std::size_t report_step, const double wellPI) -> double
|
||||
{
|
||||
return sched.getWell("P", report_step).getWellPIScalingFactor(wellPI);
|
||||
};
|
||||
|
||||
auto applyWellPIScaling = [&sched](const std::size_t report_step, const double scalingFactor)
|
||||
{
|
||||
sched.applyWellProdIndexScaling("P", report_step, scalingFactor);
|
||||
};
|
||||
|
||||
auto getConnections = [&sched](const std::size_t report_step)
|
||||
{
|
||||
return sched.getWell("P", report_step).getConnections();
|
||||
};
|
||||
|
||||
// Apply WELPI scaling after end of time series => no change to CTFs
|
||||
{
|
||||
const auto report_step = std::size_t{1};
|
||||
const auto scalingFactor = getScalingFactor(report_step, 100.0*liquid_PI_unit());
|
||||
|
||||
BOOST_CHECK_CLOSE(scalingFactor, 2.0, 1.0e-10);
|
||||
|
||||
applyWellPIScaling(1729, scalingFactor);
|
||||
|
||||
{
|
||||
const auto expectCF = 100.0*cp_rm3_per_db();
|
||||
|
||||
const auto& conns = getConnections(0);
|
||||
BOOST_REQUIRE_EQUAL(conns.size(), 3);
|
||||
|
||||
BOOST_CHECK_CLOSE(conns[0].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[1].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[2].CF(), expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectCF = 100.0*cp_rm3_per_db();
|
||||
|
||||
const auto& conns = getConnections(1);
|
||||
BOOST_REQUIRE_EQUAL(conns.size(), 3);
|
||||
|
||||
BOOST_CHECK_CLOSE(conns[0].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[1].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[2].CF(), expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectCF = 100.0*cp_rm3_per_db();
|
||||
|
||||
const auto& conns = getConnections(2);
|
||||
BOOST_REQUIRE_EQUAL(conns.size(), 3);
|
||||
|
||||
BOOST_CHECK_CLOSE(conns[0].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[1].CF(), 50.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[2].CF(), expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectCF = 100.0*cp_rm3_per_db();
|
||||
|
||||
const auto& conns = getConnections(3);
|
||||
BOOST_REQUIRE_EQUAL(conns.size(), 3);
|
||||
|
||||
BOOST_CHECK_CLOSE(conns[0].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[1].CF(), 50.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[2].CF(), expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
{
|
||||
const auto& conns = getConnections(4);
|
||||
BOOST_REQUIRE_EQUAL(conns.size(), 6);
|
||||
|
||||
BOOST_CHECK_CLOSE(conns[0].CF(), 100.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[1].CF(), 50.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[2].CF(), 100.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[3].CF(), 100.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[4].CF(), 75.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[5].CF(), 25.0*cp_rm3_per_db(), 1.0e-10);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply WELPI scaling after first WELPI specification
|
||||
{
|
||||
const auto report_step = std::size_t{1};
|
||||
const auto scalingFactor = getScalingFactor(report_step, 100.0*liquid_PI_unit());
|
||||
|
||||
BOOST_CHECK_CLOSE(scalingFactor, 2.0, 1.0e-10);
|
||||
|
||||
applyWellPIScaling(report_step, scalingFactor);
|
||||
|
||||
{
|
||||
const auto expectCF = 100.0*cp_rm3_per_db();
|
||||
|
||||
const auto& conns = getConnections(0);
|
||||
BOOST_REQUIRE_EQUAL(conns.size(), 3);
|
||||
|
||||
BOOST_CHECK_CLOSE(conns[0].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[1].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[2].CF(), expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectCF = 200.0*cp_rm3_per_db();
|
||||
|
||||
const auto& conns = getConnections(1);
|
||||
BOOST_REQUIRE_EQUAL(conns.size(), 3);
|
||||
|
||||
BOOST_CHECK_CLOSE(conns[0].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[1].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[2].CF(), expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectCF = 200.0*cp_rm3_per_db();
|
||||
|
||||
const auto& conns = getConnections(2);
|
||||
BOOST_REQUIRE_EQUAL(conns.size(), 3);
|
||||
|
||||
BOOST_CHECK_CLOSE(conns[0].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[1].CF(), 50.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[2].CF(), expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectCF = 200.0*cp_rm3_per_db();
|
||||
|
||||
const auto& conns = getConnections(3);
|
||||
BOOST_REQUIRE_EQUAL(conns.size(), 3);
|
||||
|
||||
BOOST_CHECK_CLOSE(conns[0].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[1].CF(), 50.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[2].CF(), expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectCF = 200.0*cp_rm3_per_db();
|
||||
|
||||
const auto& conns = getConnections(4);
|
||||
BOOST_REQUIRE_EQUAL(conns.size(), 6);
|
||||
|
||||
BOOST_CHECK_CLOSE(conns[0].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[1].CF(), 50.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[2].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[3].CF(), 100.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[4].CF(), 75.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[5].CF(), 25.0*cp_rm3_per_db(), 1.0e-10);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply WELPI scaling after second WELPI specification
|
||||
{
|
||||
const auto report_step = std::size_t{3};
|
||||
const auto scalingFactor = getScalingFactor(report_step, 200.0*liquid_PI_unit());
|
||||
|
||||
BOOST_CHECK_CLOSE(scalingFactor, 0.25, 1.0e-10);
|
||||
|
||||
applyWellPIScaling(report_step, scalingFactor);
|
||||
|
||||
{
|
||||
const auto expectCF = 100.0*cp_rm3_per_db();
|
||||
|
||||
const auto& conns = getConnections(0);
|
||||
BOOST_REQUIRE_EQUAL(conns.size(), 3);
|
||||
|
||||
BOOST_CHECK_CLOSE(conns[0].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[1].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[2].CF(), expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectCF = 200.0*cp_rm3_per_db();
|
||||
|
||||
const auto& conns = getConnections(1);
|
||||
BOOST_REQUIRE_EQUAL(conns.size(), 3);
|
||||
|
||||
BOOST_CHECK_CLOSE(conns[0].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[1].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[2].CF(), expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectCF = 200.0*cp_rm3_per_db();
|
||||
|
||||
const auto& conns = getConnections(2);
|
||||
BOOST_REQUIRE_EQUAL(conns.size(), 3);
|
||||
|
||||
BOOST_CHECK_CLOSE(conns[0].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[1].CF(), 50.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[2].CF(), expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectCF = 50.0*cp_rm3_per_db();
|
||||
|
||||
const auto& conns = getConnections(3);
|
||||
BOOST_REQUIRE_EQUAL(conns.size(), 3);
|
||||
|
||||
BOOST_CHECK_CLOSE(conns[0].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[1].CF(), 0.25*expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[2].CF(), expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectCF = 50.0*cp_rm3_per_db();
|
||||
|
||||
const auto& conns = getConnections(4);
|
||||
BOOST_REQUIRE_EQUAL(conns.size(), 6);
|
||||
|
||||
BOOST_CHECK_CLOSE(conns[0].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[1].CF(), 0.25*expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[2].CF(), expectCF, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[3].CF(), 100.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[4].CF(), 75.0*cp_rm3_per_db(), 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(conns[5].CF(), 25.0*cp_rm3_per_db(), 1.0e-10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmp_vector(const std::vector<double>&v1, const std::vector<double>& v2) {
|
||||
BOOST_CHECK_EQUAL(v1.size(), v2.size());
|
||||
@ -3848,4 +4162,3 @@ BOOST_AUTO_TEST_CASE(VFPPROD_SCALING) {
|
||||
cmp_vector(gfr, vfp_table.getGFRAxis());
|
||||
cmp_vector(alq, vfp_table.getALQAxis());
|
||||
}
|
||||
|
||||
|
@ -17,9 +17,11 @@
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#define BOOST_TEST_MODULE WellTest
|
||||
#include <boost/test/unit_test.hpp>
|
||||
@ -1197,7 +1199,7 @@ COMPDAT
|
||||
END
|
||||
)");
|
||||
|
||||
using WellPI = Well::WellProductivityIndex;
|
||||
using WellPIType = Well::WellProductivityIndex;
|
||||
|
||||
const auto es = EclipseState{ deck };
|
||||
const auto sched = Schedule{ deck, es };
|
||||
@ -1211,9 +1213,16 @@ END
|
||||
}
|
||||
|
||||
// Simulate applying WELPI before WELPI keyword. No effect.
|
||||
wellP.applyWellProdIndexScaling(2.7182818);
|
||||
for (const auto& conn : wellP.getConnections()) {
|
||||
BOOST_CHECK_CLOSE(conn.CF(), expectCF, 1.0e-10);
|
||||
{
|
||||
std::vector<bool> scalingApplicable;
|
||||
wellP.applyWellProdIndexScaling(2.7182818, scalingApplicable);
|
||||
for (const auto& conn : wellP.getConnections()) {
|
||||
BOOST_CHECK_CLOSE(conn.CF(), expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
for (const bool applicable : scalingApplicable) {
|
||||
BOOST_CHECK_MESSAGE(! applicable, "No connection must be eligible for WELPI scaling");
|
||||
}
|
||||
}
|
||||
|
||||
// Simulate applying WELPI after seeing
|
||||
@ -1223,37 +1232,125 @@ END
|
||||
// /
|
||||
//
|
||||
// (ignoring units of measure)
|
||||
BOOST_CHECK_MESSAGE(wellP.updateWellProductivityIndex(WellPI{ 2.0, Phase::GAS }),
|
||||
BOOST_CHECK_MESSAGE(wellP.updateWellProductivityIndex(WellPIType{ 2.0, Phase::GAS }),
|
||||
"First call to updateWellProductivityIndex() must be a state change");
|
||||
BOOST_CHECK_MESSAGE(!wellP.updateWellProductivityIndex(WellPI{ 2.0, Phase::GAS }),
|
||||
BOOST_CHECK_MESSAGE(!wellP.updateWellProductivityIndex(WellPIType{ 2.0, Phase::GAS }),
|
||||
"Second call to updateWellProductivityIndex() must NOT be a state change");
|
||||
|
||||
// Want PI=2, but actual/effective PI=1 => scale CF by 2.0/1.0.
|
||||
wellP.applyWellProdIndexScaling(1.0);
|
||||
for (const auto& conn : wellP.getConnections()) {
|
||||
BOOST_CHECK_CLOSE(conn.CF(), 2.0*expectCF, 1.0e-10);
|
||||
{
|
||||
const auto scalingFactor = wellP.getWellPIScalingFactor(1.0);
|
||||
BOOST_CHECK_CLOSE(scalingFactor, 2.0, 1.0e-10);
|
||||
|
||||
std::vector<bool> scalingApplicable;
|
||||
wellP.applyWellProdIndexScaling(scalingFactor, scalingApplicable);
|
||||
for (const auto& conn : wellP.getConnections()) {
|
||||
BOOST_CHECK_CLOSE(conn.CF(), 2.0*expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
for (const bool applicable : scalingApplicable) {
|
||||
BOOST_CHECK_MESSAGE(applicable, "All connections must be eligible for WELPI scaling");
|
||||
}
|
||||
}
|
||||
|
||||
// Repeated application of WELPI multiplies scaling factors.
|
||||
wellP.applyWellProdIndexScaling(1.0);
|
||||
for (const auto& conn : wellP.getConnections()) {
|
||||
BOOST_CHECK_CLOSE(conn.CF(), 4.0*expectCF, 1.0e-10);
|
||||
{
|
||||
const auto scalingFactor = wellP.getWellPIScalingFactor(1.0);
|
||||
BOOST_CHECK_CLOSE(scalingFactor, 2.0, 1.0e-10);
|
||||
|
||||
std::vector<bool> scalingApplicable;
|
||||
wellP.applyWellProdIndexScaling(scalingFactor, scalingApplicable);
|
||||
for (const auto& conn : wellP.getConnections()) {
|
||||
BOOST_CHECK_CLOSE(conn.CF(), 4.0*expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
for (const bool applicable : scalingApplicable) {
|
||||
BOOST_CHECK_MESSAGE(applicable, "All connections must be eligible for WELPI scaling");
|
||||
}
|
||||
}
|
||||
|
||||
// New WELPI record does not reset the scaling factors
|
||||
wellP.updateWellProductivityIndex(WellPI{ 3.0, Phase::GAS });
|
||||
wellP.updateWellProductivityIndex(WellPIType{ 3.0, Phase::GAS });
|
||||
for (const auto& conn : wellP.getConnections()) {
|
||||
BOOST_CHECK_CLOSE(conn.CF(), 4.0*expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
// Effective PI=desired PI => no scaling change
|
||||
wellP.applyWellProdIndexScaling(3.0);
|
||||
for (const auto& conn : wellP.getConnections()) {
|
||||
BOOST_CHECK_CLOSE(conn.CF(), 4.0*expectCF, 1.0e-10);
|
||||
{
|
||||
const auto scalingFactor = wellP.getWellPIScalingFactor(3.0);
|
||||
BOOST_CHECK_CLOSE(scalingFactor, 1.0, 1.0e-10);
|
||||
|
||||
std::vector<bool> scalingApplicable;
|
||||
wellP.applyWellProdIndexScaling(scalingFactor, scalingApplicable);
|
||||
for (const auto& conn : wellP.getConnections()) {
|
||||
BOOST_CHECK_CLOSE(conn.CF(), 4.0*expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
for (const bool applicable : scalingApplicable) {
|
||||
BOOST_CHECK_MESSAGE(applicable, "All connections must be eligible for WELPI scaling");
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_CHECK_MESSAGE(wellP.updateWellProductivityIndex(WellPI{ 3.0, Phase::OIL }),
|
||||
BOOST_CHECK_MESSAGE(wellP.updateWellProductivityIndex(WellPIType{ 3.0, Phase::OIL }),
|
||||
"Fourth call to updateWellProductivityIndex() must be a state change");
|
||||
BOOST_CHECK_MESSAGE(!wellP.updateWellProductivityIndex(WellPI{ 3.0, Phase::OIL }),
|
||||
BOOST_CHECK_MESSAGE(!wellP.updateWellProductivityIndex(WellPIType{ 3.0, Phase::OIL }),
|
||||
"Fifth call to updateWellProductivityIndex() must NOT be a state change");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Has_Same_Connections_Pointers) {
|
||||
const auto deck = Parser{}.parseString(R"(RUNSPEC
|
||||
START
|
||||
7 OCT 2020 /
|
||||
|
||||
DIMENS
|
||||
10 10 3 /
|
||||
|
||||
GRID
|
||||
DXV
|
||||
10*100.0 /
|
||||
DYV
|
||||
10*100.0 /
|
||||
DZV
|
||||
3*10.0 /
|
||||
|
||||
DEPTHZ
|
||||
121*2000.0 /
|
||||
|
||||
PERMX
|
||||
300*100.0 /
|
||||
PERMY
|
||||
300*100.0 /
|
||||
PERMZ
|
||||
300*10.0 /
|
||||
PORO
|
||||
300*0.3 /
|
||||
|
||||
SCHEDULE
|
||||
WELSPECS
|
||||
'P' 'G' 10 10 2005 'LIQ' /
|
||||
/
|
||||
COMPDAT
|
||||
'P' 0 0 1 3 OPEN 1 100 /
|
||||
/
|
||||
|
||||
END
|
||||
)");
|
||||
|
||||
const auto es = EclipseState{ deck };
|
||||
const auto sched = Schedule{ deck, es };
|
||||
|
||||
const auto wellP = sched.getWell("P", 0);
|
||||
auto wellQ = wellP;
|
||||
|
||||
BOOST_CHECK_MESSAGE(wellP.hasSameConnectionsPointers(wellQ),
|
||||
"P and Q must have the same internal connections pointers");
|
||||
|
||||
auto connQ = std::make_shared<WellConnections>(wellP.getConnections());
|
||||
wellQ.forceUpdateConnections(std::move(connQ));
|
||||
BOOST_CHECK_MESSAGE(! wellP.hasSameConnectionsPointers(wellQ),
|
||||
"P and Q must NOT have the same internal connections pointers "
|
||||
"after forcibly updating the connections structure");
|
||||
|
||||
BOOST_CHECK_MESSAGE(wellP.getConnections() == wellQ.getConnections(),
|
||||
"P and Q must have same WellConnections VALUE");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user