Merge pull request #1394 from joakim-hove/schedule-update-well

Schedule update well
This commit is contained in:
Joakim Hove
2020-01-11 17:03:56 +01:00
committed by GitHub
8 changed files with 101 additions and 42 deletions

View File

@@ -210,6 +210,9 @@ namespace Opm
const Well& getWellatEnd(const std::string& well_name) const;
std::vector<Well> getWells(size_t timeStep) const;
std::vector<Well> getWellsatEnd() const;
void shut_well(const std::string& well_name, std::size_t report_step);
void stop_well(const std::string& well_name, std::size_t report_step);
void open_well(const std::string& well_name, std::size_t report_step);
std::vector<const Group*> getChildGroups2(const std::string& group_name, size_t timeStep) const;
std::vector<Well> getChildWells2(const std::string& group_name, size_t timeStep) const;
@@ -310,7 +313,7 @@ namespace Opm
void updateGroup(std::shared_ptr<Group> group, size_t reportStep);
bool checkGroups(const ParseContext& parseContext, ErrorGuard& errors);
void updateUDQActive( std::size_t timeStep, std::shared_ptr<UDQActive> udq );
bool updateWellStatus( const std::string& well, size_t reportStep , Well::Status status);
bool updateWellStatus( const std::string& well, size_t reportStep , Well::Status status, bool update_connections);
void addWellToGroup( const std::string& group_name, const std::string& well_name , size_t timeStep);
void iterateScheduleSection(const ParseContext& parseContext , ErrorGuard& errors, const SCHEDULESection& , const EclipseGrid& grid,
const FieldPropsManager& fp,

View File

@@ -497,7 +497,7 @@ public:
bool updateRefDepth(double ref_dpeth);
bool updateDrainageRadius(double drainage_radius);
bool updateConnections(const std::shared_ptr<WellConnections> connections);
bool updateStatus(Status status);
bool updateStatus(Status status, bool update_connections);
bool updateGroup(const std::string& group);
bool updateProducer(bool is_producer);
bool updateWellGuideRate(bool available, double guide_rate, GuideRateTarget guide_phase, double scale_factor);

View File

@@ -96,6 +96,9 @@ void python::common::export_Schedule(py::module& module) {
.def_property_readonly( "start", &get_start_time )
.def_property_readonly( "end", &get_end_time )
.def_property_readonly( "timesteps", &get_timesteps )
.def( "shut_well", &Schedule::shut_well)
.def( "open_well", &Schedule::open_well)
.def( "stop_well", &Schedule::stop_well)
.def( "get_wells", &Schedule::getWells)
.def( "get_well", &get_well)
.def( "__contains__", &has_well )

View File

@@ -57,6 +57,17 @@ class TestSchedule(unittest.TestCase):
with self.assertRaises(ValueError):
self.sch.group('foo', 0)
def test_open_shut(self):
deck = Parser().parse(test_path('spe3/SPE3CASE1.DATA'))
state = EclipseState(deck)
sch = Schedule( deck, state )
prod = sch.get_well("PROD", 1)
self.assertEqual(prod.status(), "OPEN")
sch.shut_well("PROD", 10)
prod = sch.get_well("PROD", 10)
self.assertEqual(prod.status(), "SHUT")
if __name__ == "__main__":
unittest.main()

View File

@@ -813,7 +813,7 @@ namespace {
invalidNamePattern(wellNamePattern, parseContext, errors, keyword);
for( const auto& well_name : well_names) {
updateWellStatus( well_name , currentStep , status );
updateWellStatus( well_name , currentStep , status, false );
{
auto& dynamic_state = this->wells_static.at(well_name);
auto well2 = std::make_shared<Well>(*dynamic_state[currentStep]);
@@ -856,7 +856,7 @@ namespace {
"Well " + well2->name() + " is a history matched well with zero rate where crossflow is banned. " +
"This well will be closed at " + std::to_string ( m_timeMap.getTimePassedUntil(currentStep) / (60*60*24) ) + " days";
OpmLog::note(msg);
updateWellStatus( well_name, currentStep, Well::Status::SHUT );
updateWellStatus( well_name, currentStep, Well::Status::SHUT, false );
}
}
}
@@ -877,7 +877,7 @@ namespace {
for( const auto& well_name : well_names) {
updateWellStatus( well_name , currentStep , status );
updateWellStatus( well_name , currentStep , status, false );
{
auto& dynamic_state = this->wells_static.at(well_name);
auto well2 = std::make_shared<Well>(*dynamic_state[currentStep]);
@@ -917,6 +917,18 @@ namespace {
}
void Schedule::shut_well(const std::string& well_name, std::size_t report_step) {
this->updateWellStatus(well_name, report_step, Well::Status::SHUT, true);
}
void Schedule::open_well(const std::string& well_name, std::size_t report_step) {
this->updateWellStatus(well_name, report_step, Well::Status::OPEN, true);
}
void Schedule::stop_well(const std::string& well_name, std::size_t report_step) {
this->updateWellStatus(well_name, report_step, Well::Status::STOP, true);
}
void Schedule::updateWell(std::shared_ptr<Well> well, size_t reportStep) {
auto& dynamic_state = this->wells_static.at(well->name());
dynamic_state.update(reportStep, well);
@@ -927,19 +939,17 @@ namespace {
Function is quite dangerous - because if this is called while holding a
Well pointer that will go stale and needs to be refreshed.
*/
bool Schedule::updateWellStatus( const std::string& well_name, size_t reportStep , Well::Status status) {
bool Schedule::updateWellStatus( const std::string& well_name, size_t reportStep , Well::Status status, bool update_connections) {
bool update = false;
{
auto& dynamic_state = this->wells_static.at(well_name);
auto well2 = std::make_shared<Well>(*dynamic_state[reportStep]);
if (well2->updateStatus(status)) {
m_events.addEvent( ScheduleEvents::WELL_STATUS_CHANGE, reportStep );
this->addWellGroupEvent( well2->name(), ScheduleEvents::WELL_STATUS_CHANGE, reportStep);
this->updateWell(well2, reportStep);
update = true;
if (status == Well::Status::OPEN)
this->rft_config.addWellOpen(well_name, reportStep);
}
auto& dynamic_state = this->wells_static.at(well_name);
auto well2 = std::make_shared<Well>(*dynamic_state[reportStep]);
if (well2->updateStatus(status, update_connections)) {
m_events.addEvent( ScheduleEvents::WELL_STATUS_CHANGE, reportStep );
this->addWellGroupEvent( well2->name(), ScheduleEvents::WELL_STATUS_CHANGE, reportStep);
this->updateWell(well2, reportStep);
update = true;
if (status == Well::Status::OPEN)
this->rft_config.addWellOpen(well_name, reportStep);
}
return update;
}
@@ -973,7 +983,7 @@ namespace {
for( const auto& well_name : well_names ) {
Well::Status status = Well::StatusFromString( record.getItem("STATUS").getTrimmedString(0));
updateWellStatus( well_name , currentStep , status );
updateWellStatus( well_name , currentStep , status, false );
{
bool update_well = false;
auto& dynamic_state = this->wells_static.at(well_name);
@@ -1006,14 +1016,14 @@ namespace {
if (injection->surfaceInjectionRate.is<double>()) {
if (injection->hasInjectionControl(Well::InjectorCMode::RATE) && injection->surfaceInjectionRate.zero()) {
OpmLog::note(msg);
updateWellStatus( well_name, currentStep, Well::Status::SHUT );
updateWellStatus( well_name, currentStep, Well::Status::SHUT, false );
}
}
if (injection->reservoirInjectionRate.is<double>()) {
if (injection->hasInjectionControl(Well::InjectorCMode::RESV) && injection->reservoirInjectionRate.zero()) {
OpmLog::note(msg);
updateWellStatus( well_name, currentStep, Well::Status::SHUT );
updateWellStatus( well_name, currentStep, Well::Status::SHUT, false );
}
}
}
@@ -1036,7 +1046,7 @@ namespace {
invalidNamePattern( wellNamePattern, parseContext, errors, keyword);
for (const auto& well_name : well_names) {
updateWellStatus( well_name, currentStep, status );
updateWellStatus( well_name, currentStep, status, false );
{
bool update_well = false;
auto& dynamic_state = this->wells_static.at(well_name);
@@ -1064,7 +1074,7 @@ namespace {
"Well " + well_name + " is an injector with zero rate where crossflow is banned. " +
"This well will be closed at " + std::to_string ( m_timeMap.getTimePassedUntil(currentStep) / (60*60*24) ) + " days";
OpmLog::note(msg);
updateWellStatus( well_name, currentStep, Well::Status::SHUT );
updateWellStatus( well_name, currentStep, Well::Status::SHUT, false );
}
}
}
@@ -1466,7 +1476,7 @@ namespace {
+ std::to_string( days ) + " days";
OpmLog::note(msg);
} else {
this->updateWellStatus( wname, currentStep, well_status );
this->updateWellStatus( wname, currentStep, well_status, false );
if (well_status == open)
this->rft_config.addWellOpen(wname, currentStep);
@@ -2062,7 +2072,7 @@ namespace {
}
}
}
void Schedule::handleWSEGVALV( const DeckKeyword& keyword, size_t currentStep) {
const std::map<std::string, std::vector<std::pair<int, Valve> > > valves = Valve::fromWSEGVALV(keyword);
@@ -2697,7 +2707,7 @@ void Schedule::handleGRUPTREE( const DeckKeyword& keyword, size_t currentStep, c
const auto& well = this->getWell(wname, timeStep);
const auto& connections = well.getConnections();
if (connections.allConnectionsShut())
this->updateWellStatus( well.name(), timeStep, Well::Status::SHUT);
this->updateWellStatus( well.name(), timeStep, Well::Status::SHUT, false);
}
}

View File

@@ -379,13 +379,41 @@ bool Well::updateHead(int I, int J) {
}
bool Well::updateStatus(Status status_arg) {
if (this->status != status_arg) {
this->status = status_arg;
return true;
bool Well::updateStatus(Status well_state, bool update_connections) {
bool update = false;
if (update_connections) {
Connection::State connection_state;
switch (well_state) {
case Status::OPEN:
connection_state = Connection::State::OPEN;
break;
case Status::SHUT:
connection_state = Connection::State::SHUT;
break;
case Status::AUTO:
connection_state = Connection::State::AUTO;
break;
case Status::STOP:
connection_state = Connection::State::SHUT;
break;
}
auto new_connections = std::make_shared<WellConnections>(this->headI, this->headJ);
for (auto c : *this->connections) {
c.setState(connection_state);
new_connections->add(c);
}
update = this->updateConnections(new_connections);
}
return false;
if (this->status != well_state) {
this->status = well_state;
update = true;
}
return update;
}
@@ -627,7 +655,7 @@ Phase Well::getPreferredPhase() const {
When all connections of a well are closed with the WELOPEN keywords, the well
itself should also be SHUT. In the main parsing code this is handled by the
function checkIfAllConnectionsIsShut() which is called at the end of every
report step in Schedule::iterateScheduleSection(). This is donn in this way
report step in Schedule::iterateScheduleSection(). This is done in this way
because there is some twisted logic aggregating connection changes over a
complete report step.
@@ -666,6 +694,10 @@ bool Well::handleWELOPEN(const DeckRecord& record, Connection::State state_arg,
return this->updateConnections(new_connections);
}
bool Well::handleCOMPLUMP(const DeckRecord& record) {
auto match = [=]( const Connection &c) -> bool {

View File

@@ -3351,9 +3351,9 @@ BOOST_AUTO_TEST_CASE(WELL_STATIC) {
BOOST_CHECK(ws.updateRefDepth(1.0));
BOOST_CHECK(!ws.updateRefDepth(1.0));
ws.updateStatus(Well::Status::OPEN);
BOOST_CHECK(!ws.updateStatus(Well::Status::OPEN));
BOOST_CHECK(ws.updateStatus(Well::Status::SHUT));
ws.updateStatus(Well::Status::OPEN, false);
BOOST_CHECK(!ws.updateStatus(Well::Status::OPEN, false));
BOOST_CHECK(ws.updateStatus(Well::Status::SHUT, false));
const auto& connections = ws.getConnections();
BOOST_CHECK_EQUAL(connections.size(), 0);

View File

@@ -78,13 +78,13 @@ BOOST_AUTO_TEST_CASE(WTEST_STATE2) {
std::vector<Well> wells;
wells.emplace_back("WELL_NAME", "A", 0, 0, 1, 1, 200., Phase::OIL, Well::ProducerCMode::NONE, Connection::Order::TRACK, us, 0.);
{
wells[0].updateStatus(Well::Status::SHUT);
wells[0].updateStatus(Well::Status::SHUT, false);
auto shut_wells = st.updateWells(wc, wells, 5000);
BOOST_CHECK_EQUAL(shut_wells.size(), 0);
}
{
wells[0].updateStatus(Well::Status::OPEN);
wells[0].updateStatus(Well::Status::OPEN, false);
auto shut_wells = st.updateWells(wc, wells, 5000);
BOOST_CHECK_EQUAL( shut_wells.size(), 1);
}
@@ -115,12 +115,12 @@ BOOST_AUTO_TEST_CASE(WTEST_STATE) {
WellTestConfig wc;
{
wells[0].updateStatus(Well::Status::SHUT);
wells[0].updateStatus(Well::Status::SHUT, false);
auto shut_wells = st.updateWells(wc, wells, 110. * day);
BOOST_CHECK_EQUAL(shut_wells.size(), 0);
}
{
wells[0].updateStatus(Well::Status::OPEN);
wells[0].updateStatus(Well::Status::OPEN, false);
auto shut_wells = st.updateWells(wc, wells, 110. * day);
BOOST_CHECK_EQUAL(shut_wells.size(), 0);
}
@@ -151,10 +151,10 @@ BOOST_AUTO_TEST_CASE(WTEST_STATE) {
wc.add_well("WELL_NAME", WellTestConfig::Reason::PHYSICAL, 1000. * day, 3, 0, 5);
wells[0].updateStatus(Well::Status::SHUT);
wells[0].updateStatus(Well::Status::SHUT, false);
BOOST_CHECK_EQUAL( st.updateWells(wc, wells, 4100. * day).size(), 0);
wells[0].updateStatus(Well::Status::OPEN);
wells[0].updateStatus(Well::Status::OPEN, false);
BOOST_CHECK_EQUAL( st.updateWells(wc, wells, 4100. * day).size(), 1);
BOOST_CHECK_EQUAL( st.updateWells(wc, wells, 5200. * day).size(), 1);
@@ -182,9 +182,9 @@ BOOST_AUTO_TEST_CASE(WTEST_STATE_COMPLETIONS) {
const UnitSystem us{};
std::vector<Well> wells;
wells.emplace_back("WELL_NAME", "A", 0, 0, 1, 1, 200., Phase::OIL, Well::ProducerCMode::NONE, Connection::Order::TRACK, us, 0.);
wells[0].updateStatus(Well::Status::OPEN);
wells[0].updateStatus(Well::Status::OPEN, false);
wells.emplace_back("WELLX", "A", 0, 0, 2, 2, 200., Phase::OIL, Well::ProducerCMode::NONE, Connection::Order::TRACK, us, 0.);
wells[1].updateStatus(Well::Status::OPEN);
wells[1].updateStatus(Well::Status::OPEN, false);
auto closed_completions = st.updateWells(wc, wells, 5000);
BOOST_CHECK_EQUAL( closed_completions.size(), 0);