Add Facility for Recording ACTIONX Well Structure Changes

This commit adds a new member, well_structure_change, to the
SimulatorUpdate structure.  The member defaults to 'false', but
will be set to 'true' if an ACTIONX block contains at least one of
a select group of keywords that affect the model's well topology.

In particular, set this member to 'true' if the ACTIONX block has
at least one of the keywords

  - COMPDAT
  - WELOPEN
  - WELSPECS

This will enable adding simulator logic to open or create wells in
the middle of a report step.
This commit is contained in:
Bård Skaflestad
2023-07-12 17:26:10 +02:00
parent 684dbb21b4
commit 5f0941677a
4 changed files with 90 additions and 34 deletions

View File

@@ -25,23 +25,27 @@
namespace Opm {
/*
This struct is used to communicate back from the Schdule::applyAction() what
needs to be updated in the simulator when execution is returned to the
simulator code.
*/
/// This struct is used to communicate back from the Schedule::applyAction()
/// what needs to be updated in the simulator when execution is returned to
/// the simulator code.
struct SimulatorUpdate {
// These wells have been affected by the ACTIONX and the simulator needs to
struct SimulatorUpdate
{
// Wells affected by ACTIONX and for which the simulator needs to
// reapply rates and state from the newly updated Schedule object.
std::unordered_set<std::string> affected_wells;
// If one of the transmissibility multiplier keywords has been invoked as an
// ACTIONX keyword the simulator needs to recalculate the transmissibility.
// If one of the transmissibility multiplier keywords has been invoked
// as an ACTIONX keyword the simulator needs to recalculate the
// transmissibility.
bool tran_update{false};
/// Whether or not well structure changed in processing an ACTIONX
/// block. Typically because of a keyword like WELSPECS, COMPDAT,
/// and/or WELOPEN.
bool well_structure_changed{false};
};
}
} // namespace Opm
#endif
#endif // SIMULATOR_UPDATE_HPP

View File

@@ -514,11 +514,11 @@ namespace Opm
const bool actionx_mode;
const ParseContext& parseContext;
ErrorGuard& errors;
SimulatorUpdate * sim_update;
const std::unordered_map<std::string, double> * target_wellpi;
std::unordered_map<std::string, double>* wpimult_global_factor;
WelSegsSet *welsegs_wells;
std::set<std::string>*compsegs_wells;
SimulatorUpdate* sim_update{nullptr};
const std::unordered_map<std::string, double>* target_wellpi{nullptr};
std::unordered_map<std::string, double>* wpimult_global_factor{nullptr};
WelSegsSet* welsegs_wells{nullptr};
std::set<std::string>* compsegs_wells{nullptr};
const ScheduleGrid& grid;
/// \param welsegs_wells All wells with a WELSEGS entry for checks.
@@ -531,8 +531,8 @@ namespace Opm
bool actionx_mode_,
const ParseContext& parseContext_,
ErrorGuard& errors_,
SimulatorUpdate * sim_update_,
const std::unordered_map<std::string, double> * target_wellpi_,
SimulatorUpdate* sim_update_,
const std::unordered_map<std::string, double>* target_wellpi_,
std::unordered_map<std::string, double>* wpimult_global_factor_,
WelSegsSet* welsegs_wells_,
std::set<std::string>* compsegs_wells_)
@@ -552,6 +552,7 @@ namespace Opm
{}
void affected_well(const std::string& well_name);
void record_well_structure_change();
/// \brief Mark that the well occured in a WELSEGS keyword
void welsegs_handled(const std::string& well_name)

View File

@@ -213,6 +213,10 @@ namespace {
well.updateRefDepth();
this->snapshots.back().wells.update( std::move(well));
}
if (! wells.empty()) {
handlerContext.record_well_structure_change();
}
}
void Schedule::handleWELTRAJ(HandlerContext& handlerContext) {
@@ -226,6 +230,7 @@ namespace {
connections->loadWELTRAJ(record, handlerContext.grid, name, handlerContext.keyword.location());
if (well2.updateConnections(connections, handlerContext.grid)) {
this->snapshots.back().wells.update( well2 );
handlerContext.record_well_structure_change();
}
this->snapshots.back().wellgroup_events().addEvent( name, ScheduleEvents::COMPLETION_CHANGE);
const auto& md = connections->getMD();
@@ -279,6 +284,10 @@ namespace {
well.updateRefDepth();
this->snapshots.back().wells.update( std::move(well));
}
if (! wells.empty()) {
handlerContext.record_well_structure_change();
}
}
void Schedule::handleCOMPLUMP(HandlerContext& handlerContext) {
@@ -288,8 +297,11 @@ namespace {
for (const auto& wname : well_names) {
auto well = this->snapshots.back().wells.get(wname);
if (well.handleCOMPLUMP(record))
if (well.handleCOMPLUMP(record)) {
this->snapshots.back().wells.update( std::move(well) );
handlerContext.record_well_structure_change();
}
}
}
}
@@ -330,7 +342,10 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno)
}
if (well.handleCOMPSEGS(handlerContext.keyword, handlerContext.grid, handlerContext.parseContext, handlerContext.errors))
{
this->snapshots.back().wells.update( std::move(well) );
handlerContext.record_well_structure_change();
}
handlerContext.compsegs_handled(wname);
}
@@ -1411,8 +1426,10 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno)
const auto did_update_well_status =
this->updateWellStatus(wname, currentStep, new_well_status);
if (handlerContext.sim_update) {
handlerContext.sim_update->affected_wells.insert(wname);
handlerContext.affected_well(wname);
if (did_update_well_status) {
handlerContext.record_well_structure_change();
}
if (did_update_well_status && (new_well_status == open)) {
@@ -1451,10 +1468,10 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno)
this->snapshots[currentStep].wells.update( std::move(well) );
}
auto* sim_update = handlerContext.sim_update;
if (sim_update)
sim_update->affected_wells.insert(wname);
this->snapshots.back().events().addEvent( ScheduleEvents::COMPLETION_CHANGE);
handlerContext.affected_well(wname);
handlerContext.record_well_structure_change();
this->snapshots.back().events().addEvent(ScheduleEvents::COMPLETION_CHANGE);
}
}
}
@@ -1537,8 +1554,10 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno)
const auto& wname = record1.getItem("WELL").getTrimmedString(0);
if (this->hasWell(wname, handlerContext.currentStep)) {
auto well = this->snapshots.back().wells.get(wname);
if (well.handleWELSEGS(handlerContext.keyword))
if (well.handleWELSEGS(handlerContext.keyword)) {
this->snapshots.back().wells.update( std::move(well) );
handlerContext.record_well_structure_change();
}
handlerContext.welsegs_handled(wname);
} else {
const auto& location = handlerContext.keyword.location();
@@ -1645,6 +1664,10 @@ Well{0} entered with 'FIELD' parent group:
handlerContext.keyword.location(),
handlerContext.errors );
}
if (! handlerContext.keyword.empty()) {
handlerContext.record_well_structure_change();
}
}
/*

View File

@@ -1511,7 +1511,12 @@ File {} line {}.)", pattern, location.keyword, location.filename, location.linen
}
SimulatorUpdate Schedule::applyAction(std::size_t reportStep, const Action::ActionX& action, const std::vector<std::string>& matching_wells, const std::unordered_map<std::string, double>& target_wellpi) {
SimulatorUpdate
Schedule::applyAction(std::size_t reportStep,
const Action::ActionX& action,
const std::vector<std::string>& matching_wells,
const std::unordered_map<std::string, double>& target_wellpi)
{
const std::string prefix = "| ";
ParseContext parseContext;
ErrorGuard errors;
@@ -1519,14 +1524,21 @@ File {} line {}.)", pattern, location.keyword, location.filename, location.linen
ScheduleGrid grid(this->completed_cells);
OpmLog::debug("/----------------------------------------------------------------------");
OpmLog::debug(fmt::format("{0}Action {1} triggered. Will add action keywords and\n{0}rerun Schedule section.\n{0}", prefix, action.name()));
OpmLog::debug(fmt::format("{0}Action {1} triggered. Will add action "
"keywords and\n{0}rerun Schedule section.\n{0}",
prefix, action.name()));
this->snapshots.resize(reportStep + 1);
auto& input_block = this->m_sched_deck[reportStep];
std::unordered_map<std::string, double> wpimult_global_factor;
for (const auto& keyword : action) {
input_block.push_back(keyword);
const auto& location = keyword.location();
OpmLog::debug(fmt::format("{}Processing keyword {} from {} line {}", prefix, location.keyword, location.filename, location.lineno));
OpmLog::debug(fmt::format("{}Processing keyword {} from {} line {}", prefix,
location.keyword, location.filename, location.lineno));
this->handleKeyword(reportStep,
input_block,
keyword,
@@ -1539,12 +1551,20 @@ File {} line {}.)", pattern, location.keyword, location.filename, location.linen
&target_wellpi,
&wpimult_global_factor);
}
this->applyGlobalWPIMULT(wpimult_global_factor);
this->end_report(reportStep);
if (!sim_update.affected_wells.empty()) {
this->snapshots.back().events().addEvent( ScheduleEvents::ACTIONX_WELL_EVENT );
for (const auto& well: sim_update.affected_wells)
this->snapshots.back().wellgroup_events().addEvent(well, ScheduleEvents::ACTIONX_WELL_EVENT);
if (! sim_update.affected_wells.empty()) {
this->snapshots.back().events()
.addEvent(ScheduleEvents::ACTIONX_WELL_EVENT);
auto& wgEvents = this->snapshots.back().wellgroup_events();
for (const auto& well: sim_update.affected_wells) {
wgEvents.addEvent(well, ScheduleEvents::ACTIONX_WELL_EVENT);
}
}
if (reportStep < this->m_sched_deck.size() - 1) {
@@ -1553,6 +1573,7 @@ File {} line {}.)", pattern, location.keyword, location.filename, location.linen
parseContext, errors, grid, &target_wellpi,
prefix, log_to_debug);
}
OpmLog::debug("\\----------------------------------------------------------------------");
return sim_update;
@@ -2326,4 +2347,11 @@ void Schedule::HandlerContext::affected_well(const std::string& well_name)
this->sim_update->affected_wells.insert(well_name);
}
void Schedule::HandlerContext::record_well_structure_change()
{
if (this->sim_update != nullptr) {
this->sim_update->well_structure_changed = true;
}
}
}