Merge pull request #2064 from bska/deferred-welpi-uconv
Defer Unit Conversion for WELPI Values
This commit is contained in:
commit
3a8694fee1
@ -433,28 +433,6 @@ public:
|
||||
double getBHPLimit() const;
|
||||
};
|
||||
|
||||
struct WellProductivityIndex {
|
||||
double pi_value;
|
||||
Phase preferred_phase;
|
||||
|
||||
bool operator==(const WellProductivityIndex& rhs) const
|
||||
{
|
||||
return (this->pi_value == rhs.pi_value)
|
||||
&& (this->preferred_phase == rhs.preferred_phase);
|
||||
}
|
||||
|
||||
bool operator!=(const WellProductivityIndex& rhs) const
|
||||
{
|
||||
return ! (*this == rhs);
|
||||
}
|
||||
|
||||
template <class Serializer>
|
||||
void serializeOp(Serializer& serializer)
|
||||
{
|
||||
serializer(this->pi_value);
|
||||
serializer(this->preferred_phase);
|
||||
}
|
||||
};
|
||||
|
||||
Well() = default;
|
||||
Well(const std::string& wname,
|
||||
@ -520,7 +498,6 @@ public:
|
||||
const WellPolymerProperties& getPolymerProperties() const;
|
||||
const WellBrineProperties& getBrineProperties() const;
|
||||
const WellTracerProperties& getTracerProperties() const;
|
||||
const WellProductivityIndex& getWellProductivityIndex() const;
|
||||
/* The rate of a given phase under the following assumptions:
|
||||
* * Returns zero if production is requested for an injector (and vice
|
||||
* versa)
|
||||
@ -580,7 +557,7 @@ public:
|
||||
bool updateEconLimits(std::shared_ptr<WellEconProductionLimits> econ_limits);
|
||||
bool updateProduction(std::shared_ptr<WellProductionProperties> production);
|
||||
bool updateInjection(std::shared_ptr<WellInjectionProperties> injection);
|
||||
bool updateWellProductivityIndex(const WellProductivityIndex& prodIndex);
|
||||
bool updateWellProductivityIndex(const double prodIndex);
|
||||
bool updateWSEGSICD(const std::vector<std::pair<int, SICD> >& sicd_pairs);
|
||||
bool updateWSEGVALV(const std::vector<std::pair<int, Valve> >& valve_pairs);
|
||||
|
||||
@ -639,7 +616,7 @@ public:
|
||||
serializer(has_produced);
|
||||
serializer(has_injected);
|
||||
serializer(prediction_mode);
|
||||
serializer.optional(productivity_index);
|
||||
serializer(productivity_index);
|
||||
serializer(econ_limits);
|
||||
serializer(foam_properties);
|
||||
serializer(polymer_properties);
|
||||
@ -677,7 +654,7 @@ private:
|
||||
bool has_produced = false;
|
||||
bool has_injected = false;
|
||||
bool prediction_mode = true;
|
||||
std::optional<WellProductivityIndex> productivity_index{ std::nullopt };
|
||||
std::optional<double> productivity_index{ std::nullopt };
|
||||
|
||||
std::shared_ptr<WellEconProductionLimits> econ_limits;
|
||||
std::shared_ptr<WellFoamProperties> foam_properties;
|
||||
|
@ -1108,10 +1108,6 @@ namespace {
|
||||
using WELL_NAME = ParserKeywords::WELPI::WELL_NAME;
|
||||
using PI = ParserKeywords::WELPI::STEADY_STATE_PRODUCTIVITY_OR_INJECTIVITY_INDEX_VALUE;
|
||||
|
||||
const auto& usys = handlerContext.section.unitSystem();
|
||||
const auto gasPI = UnitSystem::measure::gas_productivity_index;
|
||||
const auto liqPI = UnitSystem::measure::liquid_productivity_index;
|
||||
|
||||
for (const auto& record : handlerContext.keyword) {
|
||||
const auto well_names = this->wellNames(record.getItem<WELL_NAME>().getTrimmedString(0),
|
||||
handlerContext.currentStep);
|
||||
@ -1123,29 +1119,22 @@ namespace {
|
||||
|
||||
const auto rawProdIndex = record.getItem<PI>().get<double>(0);
|
||||
for (const auto& well_name : well_names) {
|
||||
// All wells in a single record *hopefully* have the same preferred phase...
|
||||
const auto& well = this->getWell(well_name, handlerContext.currentStep);
|
||||
const auto preferred = well.getPreferredPhase();
|
||||
const auto unitPI = (preferred == Phase::GAS) ? gasPI : liqPI;
|
||||
|
||||
const auto wellPI = Well::WellProductivityIndex {
|
||||
usys.to_si(unitPI, rawProdIndex),
|
||||
preferred
|
||||
};
|
||||
auto well2 = std::make_shared<Well>(this->getWell(well_name, handlerContext.currentStep));
|
||||
|
||||
// Note: Need to ensure we have an independent copy of
|
||||
// well's connections because
|
||||
// Well::updateWellProductivityIndex() implicitly mutates
|
||||
// internal state in the WellConnections class.
|
||||
auto well2 = std::make_shared<Well>(well);
|
||||
auto connections = std::make_shared<WellConnections>(well2->getConnections());
|
||||
well2->forceUpdateConnections(std::move(connections));
|
||||
if (well2->updateWellProductivityIndex(wellPI))
|
||||
if (well2->updateWellProductivityIndex(rawProdIndex))
|
||||
this->updateWell(std::move(well2), handlerContext.currentStep);
|
||||
|
||||
this->addWellGroupEvent(well_name, ScheduleEvents::WELL_PRODUCTIVITY_INDEX, handlerContext.currentStep);
|
||||
}
|
||||
}
|
||||
|
||||
this->m_events.addEvent(ScheduleEvents::WELL_PRODUCTIVITY_INDEX, handlerContext.currentStep);
|
||||
}
|
||||
|
||||
void Schedule::handleWELSEGS(const HandlerContext& handlerContext, const ParseContext&, ErrorGuard&) {
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <opm/io/eclipse/rst/well.hpp>
|
||||
#include <opm/output/eclipse/VectorItems/well.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParserKeywords/W.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Runspec.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQActive.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellInjectionProperties.hpp>
|
||||
@ -376,7 +377,7 @@ Well Well::serializeObject()
|
||||
result.efficiency_factor = 8.0;
|
||||
result.solvent_fraction = 9.0;
|
||||
result.prediction_mode = false;
|
||||
result.productivity_index = WellProductivityIndex { 10.0, Phase::GAS };
|
||||
result.productivity_index = 10.0;
|
||||
result.econ_limits = std::make_shared<Opm::WellEconProductionLimits>(Opm::WellEconProductionLimits::serializeObject());
|
||||
result.foam_properties = std::make_shared<WellFoamProperties>(WellFoamProperties::serializeObject());
|
||||
result.polymer_properties = std::make_shared<WellPolymerProperties>(WellPolymerProperties::serializeObject());
|
||||
@ -492,7 +493,7 @@ bool Well::updateInjection(std::shared_ptr<WellInjectionProperties> injection_ar
|
||||
return update;
|
||||
}
|
||||
|
||||
bool Well::updateWellProductivityIndex(const WellProductivityIndex& prodIndex) {
|
||||
bool Well::updateWellProductivityIndex(const double prodIndex) {
|
||||
const auto update = this->productivity_index != prodIndex;
|
||||
if (update)
|
||||
this->productivity_index = prodIndex;
|
||||
@ -837,6 +838,32 @@ void Well::setInsertIndex(std::size_t index) {
|
||||
this->insert_index = index;
|
||||
}
|
||||
|
||||
namespace {
|
||||
double convertWellPIToSI(const double rawWellPI,
|
||||
const Phase preferred_phase,
|
||||
const UnitSystem& unit_system)
|
||||
{
|
||||
using M = UnitSystem::measure;
|
||||
|
||||
// XXX: Should really have LIQUID here too, but the 'Phase' type does
|
||||
// not provide that enumerator.
|
||||
switch (preferred_phase) {
|
||||
case Phase::GAS:
|
||||
return unit_system.to_si(M::gas_productivity_index, rawWellPI);
|
||||
|
||||
case Phase::OIL:
|
||||
case Phase::WATER:
|
||||
return unit_system.to_si(M::liquid_productivity_index, rawWellPI);
|
||||
|
||||
default:
|
||||
throw std::invalid_argument {
|
||||
"Preferred phase " + std::to_string(static_cast<int>(preferred_phase)) +
|
||||
" is not supported. Must be one of 'OIL', 'GAS', or 'WATER'"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double Well::getWellPIScalingFactor(const double currentEffectivePI) const {
|
||||
if (this->connections->empty())
|
||||
// No connections for this well. Unexpected.
|
||||
@ -846,11 +873,10 @@ double Well::getWellPIScalingFactor(const double currentEffectivePI) const {
|
||||
// WELPI not activated. Nothing to do.
|
||||
return 1.0;
|
||||
|
||||
if (this->productivity_index->pi_value == currentEffectivePI)
|
||||
// No change in scaling.
|
||||
return 1.0;
|
||||
const double requestedWellPI_SI =
|
||||
convertWellPIToSI(*this->productivity_index, this->getPreferredPhase(), this->unit_system);
|
||||
|
||||
return this->productivity_index->pi_value / currentEffectivePI;
|
||||
return requestedWellPI_SI / currentEffectivePI;
|
||||
}
|
||||
|
||||
void Well::applyWellProdIndexScaling(const double scalingFactor, std::vector<bool>& scalingApplicable) {
|
||||
@ -898,16 +924,6 @@ const WellTracerProperties& Well::getTracerProperties() const {
|
||||
return *this->tracer_properties;
|
||||
}
|
||||
|
||||
const Well::WellProductivityIndex& Well::getWellProductivityIndex() const
|
||||
{
|
||||
if (this->productivity_index)
|
||||
return *this->productivity_index;
|
||||
else
|
||||
throw std::logic_error {
|
||||
"WELPI not activated in well " + this->name()
|
||||
};
|
||||
}
|
||||
|
||||
const WellEconProductionLimits& Well::getEconLimits() const {
|
||||
return *this->econ_limits;
|
||||
}
|
||||
|
@ -67,8 +67,7 @@ namespace {
|
||||
|
||||
double cp_rm3_per_db()
|
||||
{
|
||||
return prefix::centi*unit::Poise * unit::cubic(unit::meter)
|
||||
/ (unit::day * unit::barsa);
|
||||
return UnitSystem::newMETRIC().to_si(UnitSystem::measure::transmissibility, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3936,9 +3935,15 @@ END
|
||||
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");
|
||||
R"(Schedule must have WELL_PRODUCTIVITY_INDEX Event for well "P" at report step 1)");
|
||||
|
||||
BOOST_REQUIRE_MESSAGE(sched.hasWellGroupEvent("P", ScheduleEvents::Events::WELL_PRODUCTIVITY_INDEX, 3),
|
||||
R"(Schedule must have WELL_PRODUCTIVITY_INDEX Event for well "P" at report step 3)");
|
||||
|
||||
BOOST_REQUIRE_MESSAGE(sched.getEvents().hasEvent(ScheduleEvents::Events::WELL_PRODUCTIVITY_INDEX, 1),
|
||||
"Schedule must have WELL_PRODUCTIVITY_INDEX Event at report step 1");
|
||||
|
||||
BOOST_REQUIRE_MESSAGE(sched.getEvents().hasEvent(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
|
||||
|
@ -53,10 +53,14 @@
|
||||
using namespace Opm;
|
||||
|
||||
namespace {
|
||||
double liquid_PI_unit()
|
||||
{
|
||||
return UnitSystem::newMETRIC().to_si(UnitSystem::measure::liquid_productivity_index, 1.0);
|
||||
}
|
||||
|
||||
double cp_rm3_per_db()
|
||||
{
|
||||
return prefix::centi*unit::Poise * unit::cubic(unit::meter)
|
||||
/ (unit::day * unit::barsa);
|
||||
return UnitSystem::newMETRIC().to_si(UnitSystem::measure::transmissibility, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1199,8 +1203,6 @@ COMPDAT
|
||||
END
|
||||
)");
|
||||
|
||||
using WellPIType = Well::WellProductivityIndex;
|
||||
|
||||
const auto es = EclipseState{ deck };
|
||||
const auto sched = Schedule{ deck, es };
|
||||
|
||||
@ -1232,14 +1234,14 @@ END
|
||||
// /
|
||||
//
|
||||
// (ignoring units of measure)
|
||||
BOOST_CHECK_MESSAGE(wellP.updateWellProductivityIndex(WellPIType{ 2.0, Phase::GAS }),
|
||||
BOOST_CHECK_MESSAGE(wellP.updateWellProductivityIndex(2.0),
|
||||
"First call to updateWellProductivityIndex() must be a state change");
|
||||
BOOST_CHECK_MESSAGE(!wellP.updateWellProductivityIndex(WellPIType{ 2.0, Phase::GAS }),
|
||||
BOOST_CHECK_MESSAGE(!wellP.updateWellProductivityIndex(2.0),
|
||||
"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.
|
||||
{
|
||||
const auto scalingFactor = wellP.getWellPIScalingFactor(1.0);
|
||||
const auto scalingFactor = wellP.getWellPIScalingFactor(1.0*liquid_PI_unit());
|
||||
BOOST_CHECK_CLOSE(scalingFactor, 2.0, 1.0e-10);
|
||||
|
||||
std::vector<bool> scalingApplicable;
|
||||
@ -1255,7 +1257,7 @@ END
|
||||
|
||||
// Repeated application of WELPI multiplies scaling factors.
|
||||
{
|
||||
const auto scalingFactor = wellP.getWellPIScalingFactor(1.0);
|
||||
const auto scalingFactor = wellP.getWellPIScalingFactor(1.0*liquid_PI_unit());
|
||||
BOOST_CHECK_CLOSE(scalingFactor, 2.0, 1.0e-10);
|
||||
|
||||
std::vector<bool> scalingApplicable;
|
||||
@ -1270,14 +1272,14 @@ END
|
||||
}
|
||||
|
||||
// New WELPI record does not reset the scaling factors
|
||||
wellP.updateWellProductivityIndex(WellPIType{ 3.0, Phase::GAS });
|
||||
wellP.updateWellProductivityIndex(3.0);
|
||||
for (const auto& conn : wellP.getConnections()) {
|
||||
BOOST_CHECK_CLOSE(conn.CF(), 4.0*expectCF, 1.0e-10);
|
||||
}
|
||||
|
||||
// Effective PI=desired PI => no scaling change
|
||||
{
|
||||
const auto scalingFactor = wellP.getWellPIScalingFactor(3.0);
|
||||
const auto scalingFactor = wellP.getWellPIScalingFactor(3.0*liquid_PI_unit());
|
||||
BOOST_CHECK_CLOSE(scalingFactor, 1.0, 1.0e-10);
|
||||
|
||||
std::vector<bool> scalingApplicable;
|
||||
@ -1290,11 +1292,6 @@ END
|
||||
BOOST_CHECK_MESSAGE(applicable, "All connections must be eligible for WELPI scaling");
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_CHECK_MESSAGE(wellP.updateWellProductivityIndex(WellPIType{ 3.0, Phase::OIL }),
|
||||
"Fourth call to updateWellProductivityIndex() must be a state change");
|
||||
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) {
|
||||
|
Loading…
Reference in New Issue
Block a user