Merge pull request #3703 from bska/welspecs-extension

Support Changing Selected Well Properties Through WELSPECS
This commit is contained in:
Markus Blatt
2023-10-25 09:51:51 +02:00
committed by GitHub
6 changed files with 542 additions and 178 deletions

View File

@@ -659,6 +659,16 @@ namespace Opm
bool isWList(std::size_t report_step, const std::string& pattern) const;
void welspecsCreateNewWell(const DeckRecord& record,
const std::string& wellName,
const std::string& groupName,
HandlerContext& handlerContext);
void welspecsUpdateExistingWells(const DeckRecord& record,
const std::vector<std::string>& wellNames,
const std::string& groupName,
HandlerContext& handlerContext);
void applyEXIT(const DeckKeyword&, std::size_t currentStep);
SimulatorUpdate applyAction(std::size_t reportStep, const std::string& action_name, const std::vector<std::string>& matching_wells);

View File

@@ -445,11 +445,11 @@ public:
bool updatePrediction(bool prediction_mode);
bool updateAutoShutin(bool auto_shutin);
bool updateCrossFlow(bool allow_cross_flow);
bool updatePVTTable(int pvt_table);
bool updateHead(int I, int J);
bool updatePVTTable(std::optional<int> pvt_table);
bool updateHead(std::optional<int> I, std::optional<int> J);
void updateRefDepth();
bool updateRefDepth(const std::optional<double>& ref_dpeth);
bool updateDrainageRadius(double drainage_radius);
bool updateRefDepth(std::optional<double> ref_dpeth);
bool updateDrainageRadius(std::optional<double> drainage_radius);
void updateSegments(std::shared_ptr<WellSegments> segments_arg);
bool updateConnections(std::shared_ptr<WellConnections> connections, bool force);
bool updateConnections(std::shared_ptr<WellConnections> connections, const ScheduleGrid& grid);
@@ -539,6 +539,7 @@ public:
serializer(has_produced);
serializer(has_injected);
serializer(prediction_mode);
serializer(derive_refdepth_from_conns_);
serializer(econ_limits);
serializer(foam_properties);
serializer(polymer_properties);
@@ -586,6 +587,7 @@ private:
bool has_produced = false;
bool has_injected = false;
bool prediction_mode = true;
bool derive_refdepth_from_conns_ { true };
std::shared_ptr<WellEconProductionLimits> econ_limits;
std::shared_ptr<WellFoamProperties> foam_properties;

View File

@@ -17,6 +17,81 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/common/OpmLog/OpmLog.hpp>
#include <opm/common/OpmLog/LogUtil.hpp>
#include <opm/common/utility/OpmInputError.hpp>
#include <opm/common/utility/numeric/cmp.hpp>
#include <opm/common/utility/String.hpp>
#include <opm/input/eclipse/Python/Python.hpp>
#include <opm/input/eclipse/EclipseState/Phase.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/AquiferFlux.hpp>
#include <opm/input/eclipse/Schedule/Action/ActionResult.hpp>
#include <opm/input/eclipse/Schedule/Action/ActionX.hpp>
#include <opm/input/eclipse/Schedule/Action/SimulatorUpdate.hpp>
#include <opm/input/eclipse/Schedule/Events.hpp>
#include <opm/input/eclipse/Schedule/GasLiftOpt.hpp>
#include <opm/input/eclipse/Schedule/Group/GConSale.hpp>
#include <opm/input/eclipse/Schedule/Group/GConSump.hpp>
#include <opm/input/eclipse/Schedule/Group/GroupEconProductionLimits.hpp>
#include <opm/input/eclipse/Schedule/Group/GuideRateConfig.hpp>
#include <opm/input/eclipse/Schedule/MSW/SICD.hpp>
#include <opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp>
#include <opm/input/eclipse/Schedule/MSW/Valve.hpp>
#include <opm/input/eclipse/Schedule/MSW/WellSegments.hpp>
#include <opm/input/eclipse/Schedule/Network/Balance.hpp>
#include <opm/input/eclipse/Schedule/Network/ExtNetwork.hpp>
#include <opm/input/eclipse/Schedule/OilVaporizationProperties.hpp>
#include <opm/input/eclipse/Schedule/RFTConfig.hpp>
#include <opm/input/eclipse/Schedule/RPTConfig.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Schedule/Tuning.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQActive.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQConfig.hpp>
#include <opm/input/eclipse/Schedule/Well/WList.hpp>
#include <opm/input/eclipse/Schedule/Well/WListManager.hpp>
#include <opm/input/eclipse/Schedule/Well/WVFPDP.hpp>
#include <opm/input/eclipse/Schedule/Well/WVFPEXP.hpp>
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
#include <opm/input/eclipse/Schedule/Well/WellBrineProperties.hpp>
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
#include <opm/input/eclipse/Schedule/Well/WellEconProductionLimits.hpp>
#include <opm/input/eclipse/Schedule/Well/WellFoamProperties.hpp>
#include <opm/input/eclipse/Schedule/Well/WellMICPProperties.hpp>
#include <opm/input/eclipse/Schedule/Well/WellPolymerProperties.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTestConfig.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTracerProperties.hpp>
#include <opm/input/eclipse/Units/Dimension.hpp>
#include <opm/input/eclipse/Units/UnitSystem.hpp>
#include <opm/input/eclipse/Units/Units.hpp>
#include <opm/input/eclipse/Deck/DeckItem.hpp>
#include <opm/input/eclipse/Deck/DeckKeyword.hpp>
#include <opm/input/eclipse/Deck/DeckRecord.hpp>
#include <opm/input/eclipse/Deck/DeckSection.hpp>
#include <opm/input/eclipse/Parser/ErrorGuard.hpp>
#include <opm/input/eclipse/Parser/ParseContext.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/B.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/C.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/D.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/G.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/L.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/N.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/P.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/T.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/V.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/W.hpp>
#include "Well/injection.hpp"
#include <algorithm>
#include <exception>
#include <functional>
#include <memory>
@@ -29,76 +104,6 @@
#include <fmt/format.h>
#include <opm/common/OpmLog/OpmLog.hpp>
#include <opm/common/OpmLog/LogUtil.hpp>
#include <opm/common/utility/OpmInputError.hpp>
#include <opm/common/utility/numeric/cmp.hpp>
#include <opm/common/utility/String.hpp>
#include <opm/input/eclipse/Python/Python.hpp>
#include <opm/input/eclipse/Deck/DeckItem.hpp>
#include <opm/input/eclipse/Deck/DeckKeyword.hpp>
#include <opm/input/eclipse/Deck/DeckRecord.hpp>
#include <opm/input/eclipse/Deck/DeckSection.hpp>
#include <opm/input/eclipse/Parser/ErrorGuard.hpp>
#include <opm/input/eclipse/Parser/ParseContext.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/B.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/C.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/D.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/G.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/L.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/N.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/P.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/T.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/V.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/W.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/W.hpp>
#include <opm/input/eclipse/EclipseState/Phase.hpp>
#include <opm/input/eclipse/EclipseState/Aquifer/AquiferFlux.hpp>
#include <opm/input/eclipse/Schedule/Action/ActionX.hpp>
#include <opm/input/eclipse/Schedule/Action/ActionResult.hpp>
#include <opm/input/eclipse/Schedule/Action/SimulatorUpdate.hpp>
#include <opm/input/eclipse/Schedule/Events.hpp>
#include <opm/input/eclipse/Schedule/GasLiftOpt.hpp>
#include <opm/input/eclipse/Schedule/Group/GConSump.hpp>
#include <opm/input/eclipse/Schedule/Group/GConSale.hpp>
#include <opm/input/eclipse/Schedule/Group/GroupEconProductionLimits.hpp>
#include <opm/input/eclipse/Schedule/Group/GuideRateConfig.hpp>
#include <opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp>
#include <opm/input/eclipse/Schedule/MSW/SICD.hpp>
#include <opm/input/eclipse/Schedule/MSW/Valve.hpp>
#include <opm/input/eclipse/Schedule/MSW/WellSegments.hpp>
#include <opm/input/eclipse/Schedule/Network/Balance.hpp>
#include <opm/input/eclipse/Schedule/Network/ExtNetwork.hpp>
#include <opm/input/eclipse/Schedule/RFTConfig.hpp>
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTestConfig.hpp>
#include <opm/input/eclipse/Schedule/OilVaporizationProperties.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQConfig.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQActive.hpp>
#include <opm/input/eclipse/Schedule/RPTConfig.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/Tuning.hpp>
#include <opm/input/eclipse/Schedule/Well/WList.hpp>
#include <opm/input/eclipse/Schedule/Well/WListManager.hpp>
#include <opm/input/eclipse/Schedule/Well/WellBrineProperties.hpp>
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
#include <opm/input/eclipse/Schedule/Well/WellEconProductionLimits.hpp>
#include <opm/input/eclipse/Schedule/Well/WellFoamProperties.hpp>
#include <opm/input/eclipse/Schedule/Well/WellMICPProperties.hpp>
#include <opm/input/eclipse/Schedule/Well/WellPolymerProperties.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTracerProperties.hpp>
#include <opm/input/eclipse/Schedule/Well/WVFPDP.hpp>
#include <opm/input/eclipse/Schedule/Well/WVFPEXP.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Units/Dimension.hpp>
#include <opm/input/eclipse/Units/UnitSystem.hpp>
#include <opm/input/eclipse/Units/Units.hpp>
#include "Well/injection.hpp"
#include <external/resinsight/LibGeometry/cvfBoundingBoxTree.h>
namespace Opm {
@@ -1666,6 +1671,8 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno)
void Schedule::handleWELSPECS(HandlerContext& handlerContext)
{
using Kw = ParserKeywords::WELSPECS;
auto getTrimmedName = [&handlerContext](const auto& item)
{
return trim_wgname(handlerContext.keyword,
@@ -1676,86 +1683,97 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno)
auto fieldWells = std::vector<std::string>{};
for (const auto& record : handlerContext.keyword) {
const auto wellName = getTrimmedName(record.getItem<ParserKeywords::WELSPECS::WELL>());
const auto groupName = getTrimmedName(record.getItem<ParserKeywords::WELSPECS::GROUP>());
if (const auto fip_region_number = record.getItem<Kw::FIP_REGION>().get<int>(0);
fip_region_number != Kw::FIP_REGION::defaultValue)
{
const auto& location = handlerContext.keyword.location();
const auto msg = fmt::format("Non-defaulted FIP region {} in WELSPECS keyword "
"in file {} line {} is not supported. "
"Reset to default value {}.",
fip_region_number,
location.filename,
location.lineno,
Kw::FIP_REGION::defaultValue);
OpmLog::warning(msg);
}
if (const auto& density_calc_type = record.getItem<Kw::DENSITY_CALC>().get<std::string>(0);
density_calc_type != Kw::DENSITY_CALC::defaultValue)
{
const auto& location = handlerContext.keyword.location();
const auto msg = fmt::format("Non-defaulted density calculation method '{}' "
"in WELSPECS keyword in file {} line {} is "
"not supported. Reset to default value {}.",
density_calc_type,
location.filename,
location.lineno,
Kw::DENSITY_CALC::defaultValue);
OpmLog::warning(msg);
}
const auto wellName = getTrimmedName(record.getItem<Kw::WELL>());
const auto groupName = getTrimmedName(record.getItem<Kw::GROUP>());
// We might get here from an ACTIONX context, or we might get
// called on a well (list) template, to reassign certain well
// properties--e.g, the well's controlling group--so check if
// 'wellName' matches any existing well names through pattern
// matching before treating the wellName as a simple well name.
//
// An empty list of well names is okay since that means we're
// creating a new well in this case.
const auto allowEmptyWellList = true;
const auto existingWells =
this->wellNames(wellName, handlerContext, allowEmptyWellList);
if (groupName == "FIELD") {
fieldWells.push_back(wellName);
}
const auto fip_region_number = record.getItem<ParserKeywords::WELSPECS::FIP_REGION>().get<int>(0);
if (fip_region_number != 0) {
const auto& location = handlerContext.keyword.location();
std::string msg = "The FIP_REGION item in the WELSPECS keyword in file: " + location.filename + " line: " + std::to_string(location.lineno) + " using default value: " + std::to_string(ParserKeywords::WELSPECS::FIP_REGION::defaultValue);
OpmLog::warning(msg);
}
const auto& density_calc_type = record.getItem<ParserKeywords::WELSPECS::DENSITY_CALC>().get<std::string>(0);
if (density_calc_type != "SEG") {
const auto& location = handlerContext.keyword.location();
std::string msg = "The DENSITY_CALC item in the WELSPECS keyword in file: " + location.filename + " line: " + std::to_string(location.lineno) + " using default value: " + ParserKeywords::WELSPECS::DENSITY_CALC::defaultValue;
OpmLog::warning(msg);
}
if (!this->snapshots.back().groups.has(groupName))
addGroup(groupName, handlerContext.currentStep);
if (!hasWell(wellName)) {
auto wellConnectionOrder = Connection::Order::TRACK;
const auto& compord = handlerContext.block.get("COMPORD");
if (compord.has_value()) {
for (std::size_t compordRecordNr = 0; compordRecordNr < compord->size(); compordRecordNr++) {
const auto& compordRecord = compord->getRecord(compordRecordNr);
const std::string& wellNamePattern = compordRecord.getItem(0).getTrimmedString(0);
if (Well::wellNameInWellNamePattern(wellName, wellNamePattern)) {
const std::string& compordString = compordRecord.getItem(1).getTrimmedString(0);
wellConnectionOrder = Connection::OrderFromString(compordString);
}
}
if (existingWells.empty()) {
fieldWells.push_back(wellName);
}
this->addWell(wellName, record, handlerContext.currentStep, wellConnectionOrder);
this->addWellToGroup(groupName, wellName, handlerContext.currentStep);
handlerContext.affected_well(wellName);
} else {
const auto headI = record.getItem<ParserKeywords::WELSPECS::HEAD_I>().get<int>(0) - 1;
const auto headJ = record.getItem<ParserKeywords::WELSPECS::HEAD_J>().get<int>(0) - 1;
const auto& refDepthItem = record.getItem<ParserKeywords::WELSPECS::REF_DEPTH>();
int pvt_table = record.getItem<ParserKeywords::WELSPECS::P_TABLE>().get<int>(0);
double drainageRadius = record.getItem<ParserKeywords::WELSPECS::D_RADIUS>().getSIDouble(0);
std::optional<double> ref_depth;
if (refDepthItem.hasValue(0))
ref_depth = refDepthItem.getSIDouble(0);
{
bool update = false;
auto well2 = this->snapshots.back().wells.get( wellName );
update = well2.updateHead(headI, headJ);
update |= well2.updateRefDepth(ref_depth);
update |= well2.updateDrainageRadius(drainageRadius);
update |= well2.updatePVTTable(pvt_table);
if (update) {
well2.updateRefDepth();
this->snapshots.back().wellgroup_events().addEvent( wellName, ScheduleEvents::WELL_WELSPECS_UPDATE);
this->snapshots.back().wells.update( std::move(well2) );
handlerContext.affected_well(wellName);
else {
for (const auto& existingWell : existingWells) {
fieldWells.push_back(existingWell);
}
}
}
this->addWellToGroup(groupName, wellName, handlerContext.currentStep);
if (! this->snapshots.back().groups.has(groupName)) {
this->addGroup(groupName, handlerContext.currentStep);
}
if (existingWells.empty()) {
// 'wellName' does not match any existing wells. Create a
// new Well object for this well.
this->welspecsCreateNewWell(record,
wellName,
groupName,
handlerContext);
}
else {
// 'wellName' matches one or more existing wells. Assign
// new properties for those wells.
this->welspecsUpdateExistingWells(record,
existingWells,
groupName,
handlerContext);
}
}
if (! fieldWells.empty()) {
std::sort(fieldWells.begin(), fieldWells.end());
fieldWells.erase(std::unique(fieldWells.begin(), fieldWells.end()),
fieldWells.end());
const auto* plural = (fieldWells.size() == 1) ? "" : "s";
const auto msg_fmt = fmt::format(R"(Well{0} is parented directly to 'FIELD', this is allowed but discouraged.
const auto msg_fmt = fmt::format(R"(Well{0} parented directly to 'FIELD'; this is allowed but discouraged.
Well{0} entered with 'FIELD' parent group:
* {1})", plural, fmt::join(fieldWells, "\n * "));
handlerContext.parseContext.handleError( ParseContext::SCHEDULE_WELL_IN_FIELD_GROUP,
msg_fmt,
handlerContext.keyword.location(),
handlerContext.errors );
handlerContext.parseContext.handleError(ParseContext::SCHEDULE_WELL_IN_FIELD_GROUP,
msg_fmt,
handlerContext.keyword.location(),
handlerContext.errors);
}
if (! handlerContext.keyword.empty()) {

View File

@@ -1772,6 +1772,83 @@ File {} line {}.)", pattern, location.keyword, location.filename, location.linen
return sched_state->wlist_manager.get().hasList(pattern);
}
void Schedule::welspecsCreateNewWell(const DeckRecord& record,
const std::string& wellName,
const std::string& groupName,
HandlerContext& handlerContext)
{
auto wellConnectionOrder = Connection::Order::TRACK;
if (const auto& compord = handlerContext.block.get("COMPORD");
compord.has_value())
{
const auto nrec = compord->size();
for (auto compordRecordNr = 0*nrec; compordRecordNr < nrec; ++compordRecordNr) {
const auto& compordRecord = compord->getRecord(compordRecordNr);
const std::string& wellNamePattern = compordRecord.getItem(0).getTrimmedString(0);
if (Well::wellNameInWellNamePattern(wellName, wellNamePattern)) {
const std::string& compordString = compordRecord.getItem(1).getTrimmedString(0);
wellConnectionOrder = Connection::OrderFromString(compordString);
}
}
}
this->addWell(wellName, record, handlerContext.currentStep, wellConnectionOrder);
this->addWellToGroup(groupName, wellName, handlerContext.currentStep);
handlerContext.affected_well(wellName);
}
void Schedule::welspecsUpdateExistingWells(const DeckRecord& record,
const std::vector<std::string>& wellNames,
const std::string& groupName,
HandlerContext& handlerContext)
{
using Kw = ParserKeywords::WELSPECS;
const auto& headI = record.getItem<Kw::HEAD_I>();
const auto& headJ = record.getItem<Kw::HEAD_J>();
const auto& pvt = record.getItem<Kw::P_TABLE>();
const auto& drad = record.getItem<Kw::D_RADIUS>();
const auto& ref_d = record.getItem<Kw::REF_DEPTH>();
const auto I = headI.defaultApplied(0) ? std::nullopt : std::optional<int> {headI.get<int>(0) - 1};
const auto J = headJ.defaultApplied(0) ? std::nullopt : std::optional<int> {headJ.get<int>(0) - 1};
const auto pvt_table = pvt.defaultApplied(0) ? std::nullopt : std::optional<int> { pvt.get<int>(0) };
const auto drainageRadius = drad.defaultApplied(0) ? std::nullopt : std::optional<double> { drad.getSIDouble(0) };
auto ref_depth = std::optional<double>{};
if (! ref_d.defaultApplied(0) && ref_d.hasValue(0)) {
ref_depth.emplace(ref_d.getSIDouble(0));
}
for (const auto& wellName : wellNames) {
auto well = this->snapshots.back().wells.get(wellName);
const auto updateHead = well.updateHead(I, J);
const auto updateRefD = well.updateRefDepth(ref_depth);
const auto updateDRad = well.updateDrainageRadius(drainageRadius);
const auto updatePVT = well.updatePVTTable(pvt_table);
if (updateHead || updateRefD || updateDRad || updatePVT) {
well.updateRefDepth();
this->snapshots.back().wellgroup_events()
.addEvent(wellName, ScheduleEvents::WELL_WELSPECS_UPDATE);
this->snapshots.back().wells.update(std::move(well));
handlerContext.affected_well(wellName);
}
this->addWellToGroup(groupName, wellName, handlerContext.currentStep);
}
}
const std::map< std::string, int >& Schedule::rst_keywords( size_t report_step ) const {
if (report_step == 0)
return this->m_static.rst_config.keywords;

View File

@@ -480,6 +480,7 @@ Well::Well(const std::string& wname_arg,
guide_rate({true, -1, Well::GuideRateTarget::UNDEFINED,ParserKeywords::WGRUPCON::SCALING_FACTOR::defaultValue}),
efficiency_factor(1.0),
solvent_fraction(0.0),
derive_refdepth_from_conns_{ ! ref_depth_arg.has_value() || (*ref_depth_arg < 0.0) },
econ_limits(std::make_shared<WellEconProductionLimits>()),
foam_properties(std::make_shared<WellFoamProperties>()),
polymer_properties(std::make_shared<WellPolymerProperties>()),
@@ -523,6 +524,7 @@ Well Well::serializationTestObject()
result.efficiency_factor = 8.0;
result.solvent_fraction = 9.0;
result.prediction_mode = false;
result.derive_refdepth_from_conns_ = false;
result.econ_limits = std::make_shared<Opm::WellEconProductionLimits>(Opm::WellEconProductionLimits::serializationTestObject());
result.foam_properties = std::make_shared<WellFoamProperties>(WellFoamProperties::serializationTestObject());
result.polymer_properties = std::make_shared<WellPolymerProperties>(WellPolymerProperties::serializationTestObject());
@@ -769,15 +771,17 @@ bool Well::updateGroup(const std::string& group_arg) {
}
bool Well::updateHead(int I, int J) {
bool Well::updateHead(std::optional<int> I, std::optional<int> J)
{
bool update = false;
if (this->headI != I) {
this->headI = I;
if (I.has_value() && (this->headI != *I)) {
this->headI = *I;
update = true;
}
if (this->headJ != J) {
this->headJ = J;
if (J.has_value() && (this->headJ != *J)) {
this->headJ = *J;
update = true;
}
@@ -795,8 +799,25 @@ bool Well::updateStatus(Status well_state) {
bool Well::updateRefDepth(const std::optional<double>& ref_depth_arg) {
if (this->ref_depth != ref_depth_arg) {
bool Well::updateRefDepth(std::optional<double> ref_depth_arg)
{
if (this->ref_depth == ref_depth_arg) {
return false;
}
if (ref_depth_arg.has_value()) {
this->derive_refdepth_from_conns_ = *ref_depth_arg < 0.0;
this->ref_depth = ref_depth_arg;
return true;
}
// If we get here, then this->ref_depth.has_value() &&
// !ref_depth_arg.has_value(). Assign the argument's value--i.e., reset
// this->ref_depth--if we're supposed to calculate the reference depth
// from the connections.
if (this->derive_refdepth_from_conns_) {
this->ref_depth = ref_depth_arg;
return true;
}
@@ -804,9 +825,12 @@ bool Well::updateRefDepth(const std::optional<double>& ref_depth_arg) {
return false;
}
bool Well::updateDrainageRadius(double drainage_radius_arg) {
if (this->drainage_radius != drainage_radius_arg) {
this->drainage_radius = drainage_radius_arg;
bool Well::updateDrainageRadius(std::optional<double> drainage_radius_arg)
{
if (drainage_radius_arg.has_value() &&
(this->drainage_radius != *drainage_radius_arg))
{
this->drainage_radius = *drainage_radius_arg;
return true;
}
@@ -1015,14 +1039,21 @@ double Well::getWPaveRefDepth() const {
return this->wpave_ref_depth.value_or( this->getRefDepth() );
}
void Well::updateRefDepth() {
if( !this->ref_depth ) {
// ref depth was defaulted and we get the depth of the first completion
void Well::updateRefDepth()
{
if ((! this->ref_depth.has_value() || (*this->ref_depth < 0.0)) &&
this->derive_refdepth_from_conns_)
{
// Reference depth was defaulted and we get the depth of the first
// connection.
if (this->connections->empty()) {
throw std::invalid_argument {
fmt::format("No reservoir connection defined for well {}. "
"Cannot infer reference depth.", this->name())
};
}
if( this->connections->empty() )
throw std::invalid_argument( "No completions defined for well: "
+ name()
+ ". Can not infer reference depth" );
this->ref_depth = this->connections->get(0).depth();
}
}
@@ -1453,6 +1484,7 @@ bool Opm::Well::applyGlobalWPIMULT(const double scaling_factor)
void Well::updateSegments(std::shared_ptr<WellSegments> segments_arg) {
this->segments = std::move(segments_arg);
this->updateRefDepth( this->segments->depthTopSegment() );
this->derive_refdepth_from_conns_ = false;
}
@@ -1466,12 +1498,14 @@ bool Well::handleWELSEGS(const DeckKeyword& keyword) {
return true;
}
bool Well::updatePVTTable(int pvt_table_) {
if (this->pvt_table != pvt_table_) {
this->pvt_table = pvt_table_;
bool Well::updatePVTTable(std::optional<int> pvt_table_)
{
if (pvt_table_.has_value() && (this->pvt_table != *pvt_table_)) {
this->pvt_table = *pvt_table_;
return true;
} else
return false;
}
return false;
}
@@ -1697,6 +1731,7 @@ bool Well::operator==(const Well& data) const {
&& (this->hasProduced() == data.hasProduced())
&& (this->hasInjected() == data.hasInjected())
&& (this->predictionMode() == data.predictionMode())
&& (this->derive_refdepth_from_conns_ == data.derive_refdepth_from_conns_)
&& (this->getTracerProperties() == data.getTracerProperties())
&& (this->getWVFPEXP() == data.getWVFPEXP())
&& (this->getProductionProperties() == data.getProductionProperties())
@@ -1807,4 +1842,4 @@ void Opm::Well::setFilterConc(const UDAValue& conc) {
double Opm::Well::evalFilterConc(const SummaryState& summary_sate) const {
return UDA::eval_well_uda(this->m_filter_concentration, this->name(), summary_sate, 0.);
}
}

View File

@@ -1515,10 +1515,8 @@ COMPDAT
TSTEP
1 /
WELSPECS
'W1' 'G' 1 1 2005 'LIQ' /
'W1' 'G' 1 1 1995.0 'LIQ' /
/
-- W2
@@ -1538,7 +1536,6 @@ WPAVEDEP
TSTEP
1 /
COMPDAT
'W1' 1 1 1 1 'OPEN' 1* 25.620 0.216 2086.842 2* 'Y' 8.486 /
/
@@ -1547,7 +1544,7 @@ TSTEP
1 /
WELSPECS
'W1' 'G' 1 1 1* 'LIQ' /
'W1' 'G' 1 1 -1.0 'LIQ' /
/
-- W5
@@ -1565,15 +1562,14 @@ END
const auto& w4 = sched.getWell("W1", 4);
const auto& w5 = sched.getWell("W1", 5);
BOOST_CHECK_EQUAL(w0.getRefDepth(), grid.getCellDepth(0,0,2));
BOOST_CHECK_EQUAL(w0.getRefDepth(), w0.getWPaveRefDepth());
BOOST_CHECK_EQUAL(w1.getRefDepth(), w0.getRefDepth());
BOOST_CHECK_EQUAL(w2.getRefDepth(), 2005 );
BOOST_CHECK_EQUAL(w3.getRefDepth(), grid.getCellDepth(0,0,1));
BOOST_CHECK_EQUAL(w4.getRefDepth(), w3.getRefDepth());
BOOST_CHECK_EQUAL(w5.getRefDepth(), grid.getCellDepth(0,0,0));
BOOST_CHECK_EQUAL(w5.getWPaveRefDepth(), 0);
BOOST_CHECK_CLOSE(w0.getRefDepth() , grid.getCellDepth(0, 0, 2), 1.0e-8);
BOOST_CHECK_CLOSE(w0.getRefDepth() , w0.getWPaveRefDepth() , 1.0e-8);
BOOST_CHECK_CLOSE(w1.getRefDepth() , w0.getRefDepth() , 1.0e-8);
BOOST_CHECK_CLOSE(w2.getRefDepth() , 1995.0 , 1.0e-8);
BOOST_CHECK_CLOSE(w3.getRefDepth() , 1995.0 , 1.0e-8);
BOOST_CHECK_CLOSE(w4.getRefDepth() , w3.getRefDepth() , 1.0e-8);
BOOST_CHECK_CLOSE(w5.getRefDepth() , grid.getCellDepth(0, 0, 0), 1.0e-8);
BOOST_CHECK_CLOSE(w5.getWPaveRefDepth(), 0.0 , 1.0e-8);
}
BOOST_AUTO_TEST_CASE(Missing_RefDepth) {
@@ -1648,3 +1644,229 @@ END
R"(Well "W1" must have a BHP reference depth at report=2)");
BOOST_CHECK_CLOSE(w1.getRefDepth(), es.getInputGrid().getCellDepth(0, 0, 1), 1.0e-8);
}
BOOST_AUTO_TEST_CASE(Update_Group_Single_Well)
{
const auto deck = Parser{}.parseString(R"(RUNSPEC
DIMENS
10 10 3 /
GRID
DXV
10*100.0 /
DYV
10*100.0 /
DZV
3*5.0 /
DEPTHZ
121*2000 /
PERMX
300*100.0 /
COPY
PERMX PERMY /
PERMX PERMZ /
/
MULTIPLY
PERMZ 0.1 /
/
PORO
300*0.3 /
SCHEDULE
WELSPECS
'P' 'G' 10 10 1* 'OIL' /
'I' 'G' 1 1 1* 'GAS' /
/
COMPDAT
'P' 10 10 1 3 'OPEN' /
'I' 1 1 1 1 'OPEN' /
/
WCONPROD
'P' 'OPEN' 'LRAT' 1* 1* 1* 1234.567 1* 12.34 /
/
WCONINJE
'I' 'GAS' 'OPEN' 'RATE' 20.0E3 /
/
TSTEP
30.0 /
WELSPECS
'P' 'G1' /
/
TSTEP
30.0 /
END
)");
const auto es = EclipseState { deck };
const auto sched = Schedule { deck, es };
{
const auto& wellP = sched.getWell("P", 0);
BOOST_CHECK_EQUAL(wellP.groupName(), "G");
}
{
const auto& wellP = sched.getWell("P", 1);
BOOST_CHECK_EQUAL(wellP.groupName(), "G1");
}
}
BOOST_AUTO_TEST_CASE(Update_Group_Multi_Well)
{
const auto deck = Parser{}.parseString(R"(RUNSPEC
DIMENS
10 10 3 /
GRID
DXV
10*100.0 /
DYV
10*100.0 /
DZV
3*5.0 /
DEPTHZ
121*2000 /
PERMX
300*100.0 /
COPY
PERMX PERMY /
PERMX PERMZ /
/
MULTIPLY
PERMZ 0.1 /
/
PORO
300*0.3 /
SCHEDULE
WELSPECS
'P1' 'G' 1 10 1* 'OIL' /
'P2' 'G' 10 1 1* 'OIL' /
'P3' 'G' 10 10 1* 'OIL' /
'I' 'G' 1 1 1* 'GAS' /
/
COMPDAT
'P1' 1 10 1 3 'OPEN' /
'P2' 10 1 1 3 'OPEN' /
'P3' 10 10 1 3 'OPEN' /
'I' 1 1 1 1 'OPEN' /
/
WCONPROD
'P*' 'OPEN' 'LRAT' 1* 1* 1* 1234.567 1* 12.34 /
/
WCONINJE
'I' 'GAS' 'OPEN' 'RATE' 20.0E3 /
/
TSTEP
30.0 /
WELSPECS
'P*' 'G1' /
/
TSTEP
30.0 /
END
)");
const auto es = EclipseState { deck };
const auto sched = Schedule { deck, es };
{
for (const auto* P : { "P1", "P2", "P3", }) {
const auto& wellP = sched.getWell(P, 0);
BOOST_CHECK_MESSAGE(wellP.groupName() == "G",
"Well " << P << " must have "
"controlling group \"G\" at time zero");
}
}
{
for (const auto* P : { "P1", "P2", "P3", }) {
const auto& wellP = sched.getWell(P, 1);
BOOST_CHECK_MESSAGE(wellP.groupName() == "G1",
"Well " << P << " must have "
"controlling group \"G1\" at time one");
}
}
}
BOOST_AUTO_TEST_CASE(Update_Group_WList)
{
const auto deck = Parser{}.parseString(R"(RUNSPEC
DIMENS
10 10 3 /
GRID
DXV
10*100.0 /
DYV
10*100.0 /
DZV
3*5.0 /
DEPTHZ
121*2000 /
PERMX
300*100.0 /
COPY
PERMX PERMY /
PERMX PERMZ /
/
MULTIPLY
PERMZ 0.1 /
/
PORO
300*0.3 /
SCHEDULE
WELSPECS
'P1' 'G' 1 10 1* 'OIL' /
'P2' 'G' 10 1 1* 'OIL' /
'P3' 'G' 10 10 1* 'OIL' /
'I' 'G' 1 1 1* 'GAS' /
/
COMPDAT
'P1' 1 10 1 3 'OPEN' /
'P2' 10 1 1 3 'OPEN' /
'P3' 10 10 1 3 'OPEN' /
'I' 1 1 1 1 'OPEN' /
/
WLIST
'*QFS' NEW 'I' 'P3' /
/
WCONPROD
'P*' 'OPEN' 'LRAT' 1* 1* 1* 1234.567 1* 12.34 /
/
WCONINJE
'I' 'GAS' 'OPEN' 'RATE' 20.0E3 /
/
TSTEP
30.0 /
WELSPECS
'*QFS' 'G1' /
/
TSTEP
30.0 /
END
)");
const auto es = EclipseState { deck };
const auto sched = Schedule { deck, es };
{
for (const auto* P : { "P1", "P2", "P3", "I", }) {
const auto& wellP = sched.getWell(P, 0);
BOOST_CHECK_MESSAGE(wellP.groupName() == "G",
"Well " << P << " must have "
"controlling group \"G\" at time zero");
}
}
{
for (const auto* P : { "P1", "P2", }) {
const auto& wellP = sched.getWell(P, 1);
BOOST_CHECK_MESSAGE(wellP.groupName() == "G",
"Well " << P << " must have "
"controlling group \"G\" at time one");
}
for (const auto* P : { "P3", "I", }) {
const auto& wellP = sched.getWell(P, 1);
BOOST_CHECK_MESSAGE(wellP.groupName() == "G1",
"Well " << P << " must have "
"controlling group \"G1\" at time one");
}
}
}