Refactor GroupTree - create on demand

This commit is contained in:
Joakim Hove
2019-07-25 11:46:15 +02:00
parent dc4aa882be
commit 2d66d15d96
10 changed files with 290 additions and 21 deletions

View File

@@ -88,6 +88,7 @@ if(ENABLE_ECL_INPUT)
src/opm/parser/eclipse/EclipseState/Schedule/Events.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Group/Group.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Group/Group2.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Group/GTNode.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Group/GroupTree.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Well/injection.cpp
src/opm/parser/eclipse/EclipseState/Schedule/MessageLimits.cpp
@@ -543,6 +544,7 @@ if(ENABLE_ECL_INPUT)
opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp
opm/parser/eclipse/EclipseState/Schedule/Tuning.hpp
opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp
opm/parser/eclipse/EclipseState/Schedule/Group/GTNode.hpp
opm/parser/eclipse/EclipseState/Schedule/Group/Group2.hpp
opm/parser/eclipse/EclipseState/Schedule/MessageLimits.hpp
opm/parser/eclipse/EclipseState/Schedule/Events.hpp

View File

@@ -0,0 +1,52 @@
/*
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <vector>
#include <opm/parser/eclipse/EclipseState/Schedule/Group/Group2.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well2.hpp>
#ifndef GROUPTREE2
#define GROUPTREE2
namespace Opm {
class GTNode {
public:
GTNode(const Group2& group, const GTNode* parent);
void add_group(const GTNode& child_group);
void add_well(const Well2& well);
const std::vector<Well2>& wells() const;
const std::vector<GTNode>& groups() const;
const std::string& name() const;
const GTNode& parent() const;
const Group2& group() const;
private:
const Group2 m_group;
const GTNode * m_parent;
std::vector<GTNode> m_child_groups;
std::vector<Well2> m_wells;
};
}
#endif

View File

@@ -68,6 +68,9 @@ struct GroupProductionProperties {
int getGroupNetVFPTable() const;
bool updateNetVFPTable(int vfp_arg);
bool update_gefac(double gefac, bool transfer_gefac);
const std::string& parent() const;
bool updateParent(const std::string& parent);
bool updateInjection(const GroupInjectionProperties& injection);
bool updateProduction(const GroupProductionProperties& production);
bool isProductionGroup() const;
@@ -99,6 +102,7 @@ private:
bool transfer_gefac;
int vfp_table;
std::string parent_group;
IOrderSet<std::string> m_wells;
IOrderSet<std::string> m_groups;

View File

@@ -31,6 +31,7 @@
#include <opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Group/Group2.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Group/GroupTree.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Group/GTNode.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/OilVaporizationProperties.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleEnums.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Tuning.hpp>
@@ -142,6 +143,8 @@ namespace Opm
const Actions& actions() const;
void evalAction(const SummaryState& summary_state, size_t timeStep);
GTNode groupTree(const std::string& root_node, size_t time_step) const;
GTNode groupTree(std::size_t report_step) const;
const GroupTree& getGroupTree(size_t t) const;
std::vector< const Group* > getChildGroups(const std::string& group_name, size_t timeStep) const;
size_t numGroups() const;
@@ -198,13 +201,16 @@ namespace Opm
std::vector< Group* > getGroups(const std::string& groupNamePattern);
std::map<std::string,Events> well_events;
GTNode groupTree(const std::string& root_node, std::size_t report_step, const GTNode * parent) const;
void updateGroup(std::shared_ptr<Group2> group, size_t reportStep);
bool checkGroups(const ParseContext& parseContext, ErrorGuard& errors);
bool updateWellStatus( const std::string& well, size_t reportStep , WellCommon::StatusEnum status);
void addWellToGroup( Group& newGroup , const std::string& wellName , size_t timeStep);
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 Eclipse3DProperties& eclipseProperties);
bool handleGroupFromWELSPECS(const std::string& groupName, GroupTree& newTree) const;
void addGroupToGroup( const std::string& parent_group, const std::string& child_group, size_t timeStep);
void addGroupToGroup( const std::string& parent_group, const Group2& child_group, size_t timeStep);
void addGroup(const std::string& groupName , size_t timeStep);
void addWell(const std::string& wellName, const DeckRecord& record, size_t timeStep, WellCompletion::CompletionOrderEnum wellCompletionOrder, const UnitSystem& unit_system);
void handleUDQ(const DeckKeyword& keyword, size_t currentStep);

View File

@@ -0,0 +1,62 @@
/*
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <opm/parser/eclipse/EclipseState/Schedule/Group/GTNode.hpp>
namespace Opm {
GTNode::GTNode(const Group2& group_arg, const GTNode * parent_arg) :
m_group(group_arg),
m_parent(parent_arg)
{}
const std::string& GTNode::name() const {
return this->m_group.name();
}
const Group2& GTNode::group() const {
return this->m_group;
}
const GTNode& GTNode::parent() const {
if (this->m_parent)
return *this->m_parent;
throw std::invalid_argument("Tried to access parent of root in GroupTree. Root: " + this->name());
}
void GTNode::add_well(const Well2& well) {
this->m_wells.push_back(well);
}
void GTNode::add_group(const GTNode& child_group) {
this->m_child_groups.push_back(child_group);
}
const std::vector<Well2>& GTNode::wells() const {
return this->m_wells;
}
const std::vector<GTNode>& GTNode::groups() const {
return this->m_child_groups;
}
}

View File

@@ -30,7 +30,11 @@ Group2::Group2(const std::string& name, std::size_t insert_index_arg, std::size_
gefac(1),
transfer_gefac(true),
vfp_table(0)
{}
{
// All groups are initially created as children of the "FIELD" group.
if (name != "FIELD")
this->parent_group = "FIELD";
}
std::size_t Group2::insert_index() const {
return this->m_insert_index;
@@ -101,6 +105,22 @@ bool Group2::updateProduction(const GroupProductionProperties& production) {
}
const std::string& Group2::parent() const {
return this->parent_group;
}
bool Group2::updateParent(const std::string& parent) {
if (this->parent_group != parent) {
this->parent_group = parent;
return true;
}
return false;
}
bool Group2::GroupInjectionProperties::operator==(const GroupInjectionProperties& other) const {
return
this->phase == other.phase &&
@@ -173,7 +193,7 @@ const std::vector<std::string>& Group2::groups() const {
bool Group2::addWell(const std::string& well_name) {
if (!this->m_groups.empty())
throw std::logic_error("Groups can not mix group and well children");
throw std::logic_error("Groups can not mix group and well children. Trying to add well: " + well_name + " to group: " + this->name());
if (this->m_wells.count(well_name) == 0) {
this->m_wells.insert(well_name);
@@ -189,13 +209,13 @@ bool Group2::hasWell(const std::string& well_name) const {
void Group2::delWell(const std::string& well_name) {
auto rm_count = this->m_wells.erase(well_name);
if (rm_count == 0)
throw std::invalid_argument("Group does not have well: " + well_name);
throw std::invalid_argument("Group: " + this->name() + " does not have well: " + well_name);
}
bool Group2::addGroup(const std::string& group_name) {
if (!this->m_wells.empty())
throw std::logic_error("Groups can not mix group and well children");
throw std::logic_error("Groups can not mix group and well children. Trying to add group: " + group_name + " to group: " + this->name());
if (this->m_groups.count(group_name) == 0) {
this->m_groups.insert(group_name);

View File

@@ -579,7 +579,7 @@ namespace {
}
}
this->addWell(wellName, record, currentStep, wellConnectionOrder, unit_system);
this->addWellToGroup(this->m_groups.at( groupName ), wellName, currentStep);
this->addWellToGroup(groupName, wellName, currentStep);
} else {
const auto headI = record.getItem( "HEAD_I" ).get< int >( 0 ) - 1;
const auto headJ = record.getItem( "HEAD_J" ).get< int >( 0 ) - 1;
@@ -1798,6 +1798,8 @@ namespace {
if (!hasGroup(childName))
addGroup( childName , currentStep );
this->addGroupToGroup(parentName, childName, currentStep);
}
m_rootGroupTree.update(currentStep, newTree);
}
@@ -1874,6 +1876,33 @@ namespace {
return m_rootGroupTree.get(timeStep);
}
GTNode Schedule::groupTree(const std::string& root_node, std::size_t report_step, const GTNode * parent) const {
auto root_group = this->getGroup2(root_node, report_step);
GTNode tree(root_group, parent);
for (const auto& wname : root_group.wells()) {
const auto& well = this->getWell2(wname, report_step);
tree.add_well(well);
}
for (const auto& gname : root_group.groups()) {
auto child_group = this->groupTree(gname, report_step, std::addressof(tree));
tree.add_group(child_group);
}
return tree;
}
GTNode Schedule::groupTree(const std::string& root_node, std::size_t report_step) const {
return this->groupTree(root_node, report_step, nullptr);
}
GTNode Schedule::groupTree(std::size_t report_step) const {
return this->groupTree("FIELD", report_step);
}
void Schedule::addWell(const std::string& wellName,
const DeckRecord& record,
size_t timeStep,
@@ -1968,7 +1997,7 @@ namespace {
std::vector< Well2 > Schedule::getChildWells2(const std::string& group_name, size_t timeStep, GroupWellQueryMode query_mode) const {
if (!hasGroup(group_name))
throw std::invalid_argument("No such group: " + group_name);
throw std::invalid_argument("No such group: '" + group_name + "'");
{
const auto& group = getGroup( group_name );
std::vector<Well2> wells;
@@ -1994,7 +2023,7 @@ namespace {
std::vector< const Group* > Schedule::getChildGroups(const std::string& group_name, size_t timeStep) const {
if (!hasGroup(group_name))
throw std::invalid_argument("No such group: " + group_name);
throw std::invalid_argument("No such group: '" + group_name + "'");
{
const auto& group = getGroup( group_name );
std::vector<const Group*> child_groups;
@@ -2048,7 +2077,7 @@ namespace {
const Group2& Schedule::getGroup2(const std::string& groupName, size_t timeStep) const {
if (this->groups.count(groupName) == 0)
throw std::invalid_argument("No such group: " + groupName);
throw std::invalid_argument("No such group: '" + groupName + "'");
const auto& dynamic_state = this->groups.at(groupName);
auto& group_ptr = dynamic_state.get(timeStep);
@@ -2224,6 +2253,11 @@ namespace {
dynamic_state.update(timeStep, group_ptr);
m_events.addEvent( ScheduleEvents::NEW_GROUP , timeStep );
// All newly created groups are attached to the field group,
// can then be relocated with the GRUPTREE keyword.
if (groupName != "FIELD")
this->addGroupToGroup("FIELD", *group_ptr, timeStep);
}
size_t Schedule::numGroups() const {
@@ -2253,16 +2287,65 @@ namespace {
throw std::invalid_argument("Group: " + groupName + " does not exist");
}
void Schedule::addGroupToGroup( const std::string& parent_group, const Group2& child_group, size_t timeStep) {
// Add to new parent
auto& dynamic_state = this->groups.at(parent_group);
auto parent_ptr = std::make_shared<Group2>( *dynamic_state[timeStep] );
if (parent_ptr->addGroup(child_group.name()))
this->updateGroup(parent_ptr, timeStep);
void Schedule::addWellToGroup( Group& newGroup, const std::string& wellName , size_t timeStep) {
auto& dynamic_state = this->wells_static.at(wellName);
auto well_ptr = std::make_shared<Well2>( *dynamic_state[timeStep] );
if (well_ptr->groupName() != "")
this->m_groups.at(well_ptr->groupName()).delWell(timeStep, wellName);
// Check and update backreference in child
if (child_group.parent() != parent_group) {
auto old_parent = std::make_shared<Group2>( this->getGroup2(child_group.parent(), timeStep) );
old_parent->delGroup(child_group.name());
this->updateGroup(old_parent, timeStep);
well_ptr->updateGroup(newGroup.name());
newGroup.addWell(timeStep, well_ptr->name());
this->updateWell(well_ptr, timeStep);
auto child_ptr = std::make_shared<Group2>( child_group );
child_ptr->updateParent(parent_group);
this->updateGroup(child_ptr, timeStep);
}
}
void Schedule::addGroupToGroup( const std::string& parent_group, const std::string& child_group, size_t timeStep) {
this->addGroupToGroup(parent_group, this->getGroup2(child_group, timeStep), timeStep);
}
void Schedule::addWellToGroup( const std::string& group_name, const std::string& well_name , size_t timeStep) {
const auto& well = this->getWell2(well_name, timeStep);
const auto old_gname = well.groupName();
{
auto well_ptr = std::make_shared<Well2>( well );
well_ptr->updateGroup(group_name);
this->updateWell(well_ptr, timeStep);
}
// Remove well child reference from previous group
// Old Group implementation
if (old_gname != group_name) {
{
auto& group = this->m_groups.at(old_gname);
group.delWell(timeStep, well_name);
}
// New Group2 implementation
{
auto group = std::make_shared<Group2>(this->getGroup2(old_gname, timeStep));
group->delWell(well_name);
this->updateGroup(group, timeStep);
}
}
// Add well child reference to new parent group
{
auto& group = this->m_groups.at(group_name);
group.addWell(timeStep, well_name);
}
{
auto group = std::make_shared<Group2>(this->getGroup2(group_name, timeStep));
group->addWell(well_name);
this->updateGroup(group, timeStep);
}
}

View File

@@ -444,6 +444,36 @@ BOOST_AUTO_TEST_CASE(CreateScheduleDeckWellsOrderedGRUPTREE) {
}
BOOST_AUTO_TEST_CASE(GroupTree2TEST) {
auto deck = createDeckWithWellsOrderedGRUPTREE();
EclipseGrid grid(100,100,100);
TableManager table ( deck );
Eclipse3DProperties eclipseProperties ( deck , table, grid);
Runspec runspec (deck);
Schedule schedule(deck, grid , eclipseProperties, runspec);
BOOST_CHECK_THROW( schedule.groupTree("NO_SUCH_GROUP", 0), std::invalid_argument);
auto cg1 = schedule.getGroup2("CG1", 0);
BOOST_CHECK( cg1.hasWell("DW_0"));
BOOST_CHECK( cg1.hasWell("CW_1"));
auto cg1_tree = schedule.groupTree("CG1", 0);
BOOST_CHECK_EQUAL(cg1_tree.wells().size(), 2);
auto gt = schedule.groupTree(0);
BOOST_CHECK_EQUAL(gt.wells().size(), 0);
BOOST_CHECK_EQUAL(gt.group().name(), "FIELD");
BOOST_CHECK_THROW(gt.parent(), std::invalid_argument);
auto cg = gt.groups();
auto pg = cg[0];
BOOST_CHECK_EQUAL(cg.size(), 1);
BOOST_CHECK_EQUAL(pg.group().name(), "PLATFORM");
BOOST_CHECK_EQUAL(pg.parent().name(), "FIELD");
}
BOOST_AUTO_TEST_CASE(CreateScheduleDeckWithStart) {
auto deck = createDeck();
EclipseGrid grid(10,10,10);

View File

@@ -21,9 +21,9 @@ COPY
SCHEDULE
WELSPECS
'W_1' 'GROUP_BJARNE' 30 37 1* 'OIL' 7* /
'W_1' 'GROUP_BIRGER' 30 37 1* 'OIL' 7* /
'W_3' 'GROUP_ODD' 31 18 1* 'OIL' 7* /
'W_2' 'GROUP_BJARNE' 20 51 1* 'OIL' 7* /
'W_2' 'GROUP_NILS' 20 51 1* 'OIL' 7* /
/

View File

@@ -341,7 +341,7 @@ BOOST_AUTO_TEST_CASE(GroupTreeTest_WELSPECS_AND_GRUPTREE_correct_size ) {
Schedule schedule(deck, grid , eclipseProperties,runspec);
// Time 0, only from WELSPECS
BOOST_CHECK_EQUAL( 2U, schedule.getGroupTree(0).children("FIELD").size() );
BOOST_CHECK_EQUAL( 3U, schedule.getGroupTree(0).children("FIELD").size() );
// Time 1, a new group added in tree
BOOST_CHECK_EQUAL( 3U, schedule.getGroupTree(1).children("FIELD").size() );
@@ -360,7 +360,7 @@ BOOST_AUTO_TEST_CASE(GroupTreeTest_WELSPECS_AND_GRUPTREE_correct_tree) {
// Time 0, only from WELSPECS
const auto& tree0 = schedule.getGroupTree( 0 );
BOOST_CHECK( tree0.exists( "FIELD" ) );
BOOST_CHECK_EQUAL( "FIELD", tree0.parent( "GROUP_BJARNE" ) );
BOOST_CHECK_EQUAL( "FIELD", tree0.parent( "GROUP_NILS" ) );
BOOST_CHECK( tree0.exists("GROUP_ODD") );
// Time 1, now also from GRUPTREE
@@ -399,6 +399,16 @@ BOOST_AUTO_TEST_CASE(GroupTreeTest_GRUPTREE_WITH_REPARENT_correct_tree) {
BOOST_CHECK_EQUAL( "FIELD", tree0.parent( "GROUP_BJARNE" ) );
BOOST_CHECK_EQUAL( "GROUP_BJARNE", tree0.parent( "GROUP_BIRGER" ) );
BOOST_CHECK_EQUAL( "GROUP_NEW", tree0.parent( "GROUP_NILS" ) );
const auto& field_group = sched.getGroup2("FIELD", 1);
const auto& new_group = sched.getGroup2("GROUP_NEW", 1);
const auto& nils_group = sched.getGroup2("GROUP_NILS", 1);
BOOST_CHECK_EQUAL(field_group.groups().size(), 2);
BOOST_CHECK( field_group.hasGroup("GROUP_NEW"));
BOOST_CHECK( field_group.hasGroup("GROUP_BJARNE"));
BOOST_CHECK_EQUAL( new_group.parent(), "FIELD");
BOOST_CHECK( new_group.hasGroup("GROUP_NILS"));
BOOST_CHECK_EQUAL( nils_group.parent(), "GROUP_NEW");
}
BOOST_AUTO_TEST_CASE( WellTestGroups ) {