Update Well Topology if Triggered From ACTIONX

This commit adds a new flag data member,

    wellStructureChangedDynamically_

to the generic black-oil well model.  This flag captures the

    well_structure_changed

value from the 'SimulatorUpdate' structure in the updateEclWells()
member function.  Then, in BlackoilWellModel::beginTimeStep(), we
key a well structure update off this flag when set.  This, in turn,
enables creating or opening wells as a result of an ACTIONX block
updating the structure in the middle of a report step.
This commit is contained in:
Bård Skaflestad 2023-07-13 17:44:28 +02:00
parent b22f9853db
commit abfb5c9d82
4 changed files with 86 additions and 34 deletions

View File

@ -188,21 +188,24 @@ void EclActionHandler::applySimulatorUpdate(const int report_step,
const SimulatorUpdate& sim_update,
bool& commit_wellstate,
const TransFunc& updateTrans)
{
OPM_TIMEBLOCK(applySimulatorUpdate);
this->wellModel_.updateEclWells(report_step, sim_update.affected_wells, summaryState_);
if (!sim_update.affected_wells.empty())
commit_wellstate = true;
{
OPM_TIMEBLOCK(applySimulatorUpdate);
if (sim_update.tran_update) {
const auto& keywords = schedule_[report_step].geo_keywords();
ecl_state_.apply_schedule_keywords( keywords );
eclBroadcast(comm_, ecl_state_.getTransMult() );
this->wellModel_.updateEclWells(report_step, sim_update, this->summaryState_);
// re-compute transmissibility
updateTrans(true);
}
}
if (!sim_update.affected_wells.empty()) {
commit_wellstate = true;
}
if (sim_update.tran_update) {
const auto& keywords = schedule_[report_step].geo_keywords();
ecl_state_.apply_schedule_keywords( keywords );
eclBroadcast(comm_, ecl_state_.getTransMult() );
// re-compute transmissibility
updateTrans(true);
}
}
std::unordered_map<std::string, double>
EclActionHandler::fetchWellPI(const int reportStep,

View File

@ -32,6 +32,9 @@
#include <opm/output/eclipse/RestartValue.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
#include <opm/input/eclipse/Schedule/Action/SimulatorUpdate.hpp>
#include <opm/input/eclipse/Schedule/Group/GConSale.hpp>
#include <opm/input/eclipse/Schedule/Group/GroupEconProductionLimits.hpp>
#include <opm/input/eclipse/Schedule/Group/GConSump.hpp>
@ -42,7 +45,7 @@
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTestConfig.hpp>
#include <opm/input/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
#include <opm/input/eclipse/Units/Units.hpp>
#include <opm/simulators/utils/DeferredLogger.hpp>
@ -669,10 +672,10 @@ checkGroupHigherConstraints(const Group& group,
void
BlackoilWellModelGeneric::
updateEclWells(const int timeStepIdx,
const std::unordered_set<std::string>& wells,
const SimulatorUpdate& sim_update,
const SummaryState& st)
{
for (const auto& wname : wells) {
for (const auto& wname : sim_update.affected_wells) {
auto well_iter = std::find_if(this->wells_ecl_.begin(), this->wells_ecl_.end(),
[&wname] (const auto& well) -> bool
{
@ -683,25 +686,36 @@ updateEclWells(const int timeStepIdx,
continue;
}
auto well_index = std::distance(this->wells_ecl_.begin(), well_iter);
this->wells_ecl_[well_index] = schedule_.getWell(wname, timeStepIdx);
const auto well_index = std::distance(this->wells_ecl_.begin(), well_iter);
const auto& well = this->wells_ecl_[well_index];
auto& pd = this->well_perf_data_[well_index];
auto pdIter = pd.begin();
for (const auto& conn : well.getConnections()) {
if (conn.state() != Connection::State::SHUT) {
pdIter->connection_transmissibility_factor = conn.CF();
++pdIter;
const auto& well = this->wells_ecl_[well_index] =
this->schedule_.getWell(wname, timeStepIdx);
auto& pd = this->well_perf_data_[well_index];
{
auto pdIter = pd.begin();
for (const auto& conn : well.getConnections()) {
if (conn.state() != Connection::State::SHUT) {
pdIter->connection_transmissibility_factor = conn.CF();
++pdIter;
}
}
}
auto& ws = this->wellState().well(well_index);
ws.updateStatus( well.getStatus() );
ws.reset_connection_factors(pd);
ws.update_targets(well, st);
{
auto& ws = this->wellState().well(well_index);
ws.updateStatus(well.getStatus());
ws.reset_connection_factors(pd);
ws.update_targets(well, st);
}
this->prod_index_calc_[well_index].reInit(well);
}
this->wellStructureChangedDynamically_ = sim_update.well_structure_changed;
}
double

View File

@ -54,14 +54,15 @@
namespace Opm {
class DeferredLogger;
class EclipseState;
class GasLiftGroupInfo;
class GasLiftSingleWellGeneric;
class GasLiftWellState;
class GasLiftGroupInfo;
class Group;
class GuideRateConfig;
class ParallelWellInfo;
class RestartValue;
class Schedule;
struct SimulatorUpdate;
class SummaryConfig;
class VFPProperties;
class WellInterfaceGeneric;
@ -152,7 +153,7 @@ public:
double wellPI(const std::string& well_name) const;
void updateEclWells(const int timeStepIdx,
const std::unordered_set<std::string>& wells,
const SimulatorUpdate& sim_update,
const SummaryState& st);
void initFromRestartFile(const RestartValue& restartValues,
@ -579,6 +580,8 @@ protected:
double last_glift_opt_time_ = -1.0;
bool wellStructureChangedDynamically_{false};
std::map<std::string, std::string> switched_prod_groups_;
std::map<std::pair<std::string, Opm::Phase>, std::string> switched_inj_groups_;

View File

@ -272,6 +272,8 @@ namespace Opm {
// Store the current well and group states in order to recover in
// the case of failed iterations
this->commitWGState();
this->wellStructureChangedDynamically_ = false;
}
@ -371,16 +373,46 @@ namespace Opm {
beginTimeStep()
{
OPM_TIMEBLOCK(beginTimeStep);
updateAverageFormationFactor();
this->updateAverageFormationFactor();
DeferredLogger local_deferredLogger;
switched_prod_groups_.clear();
switched_inj_groups_.clear();
this->switched_prod_groups_.clear();
this->switched_inj_groups_.clear();
if (this->wellStructureChangedDynamically_) {
// Something altered the well structure/topology. Possibly
// WELSPECS/COMPDAT and/or WELOPEN run from an ACTIONX block.
// Reconstruct the local wells to account for the new well
// structure.
const auto reportStepIdx =
this->ebosSimulator_.episodeIndex();
// Disable WELPI scaling when well structure is updated in the
// middle of a report step.
const auto enableWellPIScaling = false;
this->initializeLocalWellStructure(reportStepIdx, enableWellPIScaling);
this->initializeGroupStructure(reportStepIdx);
this->commitWGState();
// Reset topology flag to signal that we've handled this
// structure change. That way we don't end up here in
// subsequent calls to beginTimeStep() unless there's a new
// dynamic change to the well structure during a report step.
this->wellStructureChangedDynamically_ = false;
}
this->resetWGState();
const int reportStepIdx = ebosSimulator_.episodeIndex();
updateAndCommunicateGroupData(reportStepIdx,
ebosSimulator_.model().newtonMethod().numIterations());
this->wellState().gliftTimeStepInit();
const double simulationTime = ebosSimulator_.time();
OPM_BEGIN_PARALLEL_TRY_CATCH();
{