Merge pull request #3703 from bska/welspecs-extension
Support Changing Selected Well Properties Through WELSPECS
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user