Merge pull request #1891 from joakim-hove/summary-region
Add basic support for xxx_REG summary keywords
This commit is contained in:
@@ -55,6 +55,7 @@ struct SummaryNode {
|
||||
Type type;
|
||||
std::string wgname;
|
||||
int number;
|
||||
std::string fip_region;
|
||||
|
||||
constexpr static int default_number { std::numeric_limits<int>::min() };
|
||||
|
||||
|
||||
@@ -20,23 +20,25 @@
|
||||
#ifndef OPM_REGION_CACHE_HPP
|
||||
#define OPM_REGION_CACHE_HPP
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm {
|
||||
class Schedule;
|
||||
class EclipseGrid;
|
||||
class FieldPropsManager;
|
||||
|
||||
namespace out {
|
||||
class RegionCache {
|
||||
public:
|
||||
RegionCache() = default;
|
||||
RegionCache(const std::vector<int>& fipnum, const EclipseGrid& grid, const Schedule& schedule);
|
||||
const std::vector<std::pair<std::string,size_t>>& connections( int region_id ) const;
|
||||
RegionCache(const std::set<std::string>& fip_regions, const FieldPropsManager& fp, const EclipseGrid& grid, const Schedule& schedule);
|
||||
const std::vector<std::pair<std::string,size_t>>& connections( const std::string& region_name, int region_id ) const;
|
||||
|
||||
private:
|
||||
std::vector<std::pair<std::string,size_t>> connections_empty;
|
||||
|
||||
std::map<int , std::vector<std::pair<std::string,size_t>>> connection_map;
|
||||
std::map<std::pair<std::string, int> , std::vector<std::pair<std::string,size_t>>> connection_map;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ namespace Opm {
|
||||
SummaryConfigNode& namedEntity(std::string name);
|
||||
SummaryConfigNode& number(const int num);
|
||||
SummaryConfigNode& isUserDefined(const bool userDefined);
|
||||
SummaryConfigNode& fip_region(const std::string& fip_region);
|
||||
|
||||
const std::string& keyword() const { return this->keyword_; }
|
||||
Category category() const { return this->category_; }
|
||||
@@ -58,12 +59,13 @@ namespace Opm {
|
||||
const std::string& namedEntity() const { return this->name_; }
|
||||
int number() const { return this->number_; }
|
||||
bool isUserDefined() const { return this->userDefined_; }
|
||||
const std::string& fip_region() const { return this->fip_region_; }
|
||||
|
||||
std::string uniqueNodeKey() const;
|
||||
const Location& location( ) const { return this->loc; }
|
||||
|
||||
operator Opm::EclIO::SummaryNode() const {
|
||||
return { keyword_, category_, type_, name_, number_ };
|
||||
return { keyword_, category_, type_, name_, number_, fip_region_ };
|
||||
}
|
||||
|
||||
template<class Serializer>
|
||||
@@ -75,6 +77,7 @@ namespace Opm {
|
||||
serializer(type_);
|
||||
serializer(name_);
|
||||
serializer(number_);
|
||||
serializer(fip_region_);
|
||||
serializer(userDefined_);
|
||||
}
|
||||
|
||||
@@ -85,6 +88,7 @@ namespace Opm {
|
||||
Type type_{ Type::Undefined };
|
||||
std::string name_{};
|
||||
int number_{std::numeric_limits<int>::min()};
|
||||
std::string fip_region_;
|
||||
bool userDefined_{false};
|
||||
};
|
||||
|
||||
@@ -164,6 +168,14 @@ namespace Opm {
|
||||
*/
|
||||
bool hasKeyword( const std::string& keyword ) const;
|
||||
|
||||
|
||||
/*
|
||||
Will check if the SummaryConfig object contains any keyword
|
||||
matching the pattern argument. The matching is done with
|
||||
fnmatch().
|
||||
*/
|
||||
bool match(const std::string& keywordPattern) const;
|
||||
|
||||
/*
|
||||
The hasSummaryKey() method will look for fully
|
||||
qualified keys like 'RPR:3' and 'BPR:10,15,20.
|
||||
@@ -174,6 +186,7 @@ namespace Opm {
|
||||
is required to calculate the summary variables.
|
||||
*/
|
||||
bool require3DField( const std::string& keyword) const;
|
||||
std::set<std::string> fip_regions() const;
|
||||
|
||||
bool operator==(const SummaryConfig& data) const;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Grid/FieldPropsManager.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Connection.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp>
|
||||
@@ -28,25 +29,30 @@
|
||||
namespace Opm {
|
||||
namespace out {
|
||||
|
||||
RegionCache::RegionCache(const std::vector<int>& fipnum, const EclipseGrid& grid, const Schedule& schedule) {
|
||||
RegionCache::RegionCache(const std::set<std::string>& fip_regions, const FieldPropsManager& fp, const EclipseGrid& grid, const Schedule& schedule) {
|
||||
for (const auto& fip_name : fip_regions) {
|
||||
const auto& fip_region = fp.get_int(fip_name);
|
||||
|
||||
const auto& wells = schedule.getWellsatEnd();
|
||||
for (const auto& well : wells) {
|
||||
const auto& connections = well.getConnections( );
|
||||
for (const auto& c : connections) {
|
||||
if (grid.cellActive(c.getI(), c.getJ(), c.getK())) {
|
||||
size_t active_index = grid.activeIndex(c.getI(), c.getJ(), c.getK());
|
||||
int region_id = fipnum[active_index];
|
||||
auto& well_index_list = this->connection_map[ region_id ];
|
||||
well_index_list.push_back( { well.name() , active_index } );
|
||||
const auto& wells = schedule.getWellsatEnd();
|
||||
for (const auto& well : wells) {
|
||||
const auto& connections = well.getConnections( );
|
||||
for (const auto& c : connections) {
|
||||
if (grid.cellActive(c.getI(), c.getJ(), c.getK())) {
|
||||
size_t active_index = grid.activeIndex(c.getI(), c.getJ(), c.getK());
|
||||
int region_id = fip_region[active_index];
|
||||
auto key = std::make_pair(fip_name, region_id);
|
||||
auto& well_index_list = this->connection_map[ key ];
|
||||
well_index_list.push_back( { well.name() , active_index } );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const std::vector<std::pair<std::string,size_t>>& RegionCache::connections( int region_id ) const {
|
||||
const auto iter = this->connection_map.find( region_id );
|
||||
const std::vector<std::pair<std::string,size_t>>& RegionCache::connections( const std::string& region_name, int region_id ) const {
|
||||
auto key = std::make_pair(region_name, region_id);
|
||||
const auto iter = this->connection_map.find( key );
|
||||
if (iter == this->connection_map.end())
|
||||
return this->connections_empty;
|
||||
else
|
||||
|
||||
@@ -173,7 +173,7 @@ namespace {
|
||||
const std::string& name) -> void
|
||||
{
|
||||
for (const auto& vector : vectors) {
|
||||
entities.push_back({kwpref + vector.kw, cat, vector.type, name, Opm::EclIO::SummaryNode::default_number });
|
||||
entities.push_back({kwpref + vector.kw, cat, vector.type, name, Opm::EclIO::SummaryNode::default_number, "" });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -183,7 +183,7 @@ namespace {
|
||||
const std::string& wgname) -> void
|
||||
{
|
||||
for (const auto &extra_vector : extra_vectors) {
|
||||
entities.push_back({ extra_vector.kw, category, extra_vector.type, wgname, Opm::EclIO::SummaryNode::default_number });
|
||||
entities.push_back({ extra_vector.kw, category, extra_vector.type, wgname, Opm::EclIO::SummaryNode::default_number, "" });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -224,7 +224,7 @@ namespace {
|
||||
const int segNumber) -> void
|
||||
{
|
||||
for (const auto &requiredVector : requiredVectors) {
|
||||
ret.push_back({requiredVector.first, category, requiredVector.second, well, segNumber});
|
||||
ret.push_back({requiredVector.first, category, requiredVector.second, well, segNumber, ""});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -379,6 +379,7 @@ struct fn_args {
|
||||
double duration;
|
||||
const int sim_step;
|
||||
int num;
|
||||
const std::string fip_region;
|
||||
const Opm::SummaryState& st;
|
||||
const Opm::data::Wells& wells;
|
||||
const Opm::data::GroupValues& groups;
|
||||
@@ -716,7 +717,7 @@ inline quantity duration( const fn_args& args ) {
|
||||
template<rt phase , bool injection>
|
||||
quantity region_rate( const fn_args& args ) {
|
||||
double sum = 0;
|
||||
const auto& well_connections = args.regionCache.connections( args.num );
|
||||
const auto& well_connections = args.regionCache.connections( args.fip_region, args.num );
|
||||
|
||||
for (const auto& pair : well_connections) {
|
||||
|
||||
@@ -1437,7 +1438,7 @@ inline std::vector<Opm::Well> find_wells( const Opm::Schedule& schedule,
|
||||
|
||||
const auto region = node.number;
|
||||
|
||||
for ( const auto& connection : regionCache.connections( region ) ){
|
||||
for ( const auto& connection : regionCache.connections( node.fip_region, region ) ){
|
||||
const auto& w_name = connection.first;
|
||||
if (schedule.hasWell(w_name, sim_step)) {
|
||||
const auto& well = schedule.getWell( w_name, sim_step );
|
||||
@@ -1640,6 +1641,7 @@ namespace Evaluator {
|
||||
const fn_args args {
|
||||
wells, group_name, stepSize, static_cast<int>(sim_step),
|
||||
std::max(0, this->node_.number),
|
||||
this->node_.fip_region,
|
||||
st, simRes.wellSol, simRes.groupSol, input.reg, input.grid,
|
||||
std::move(efac.factors)
|
||||
};
|
||||
@@ -2094,6 +2096,7 @@ namespace Evaluator {
|
||||
|
||||
const fn_args args {
|
||||
{}, "", 0.0, 0, std::max(0, this->node_->number),
|
||||
this->node_->fip_region,
|
||||
this->st_, {}, {}, reg, this->grid_,
|
||||
{}
|
||||
};
|
||||
@@ -2390,7 +2393,7 @@ SummaryImplementation(const EclipseState& es,
|
||||
const Schedule& sched,
|
||||
const std::string& basename)
|
||||
: grid_ (std::cref(grid))
|
||||
, regCache_ (es.globalFieldProps().get_int("FIPNUM"), grid, sched)
|
||||
, regCache_ (sumcfg.fip_regions(), es.globalFieldProps(), grid, sched)
|
||||
, deferredSMSpec_(makeDeferredSMSpecCreation(es, grid, sched))
|
||||
, rset_ (makeResultSet(es.cfg().io(), basename))
|
||||
, fmt_ { es.cfg().io().getFMTOUT() }
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <fnmatch.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
@@ -460,19 +462,25 @@ inline void keywordR2R( SummaryConfig::keyword_list& /* list */,
|
||||
}
|
||||
|
||||
|
||||
inline void keywordR( SummaryConfig::keyword_list& list,
|
||||
const DeckKeyword& keyword,
|
||||
const TableManager& tables,
|
||||
const ParseContext& parseContext,
|
||||
ErrorGuard& errors ) {
|
||||
inline void keywordR( SummaryConfig::keyword_list& list,
|
||||
const DeckKeyword& deck_keyword,
|
||||
const TableManager& tables,
|
||||
const ParseContext& parseContext,
|
||||
ErrorGuard& errors ) {
|
||||
|
||||
if( is_region_to_region(keyword.name()) ) {
|
||||
keywordR2R( list, parseContext, errors, keyword );
|
||||
auto keyword = deck_keyword.name();
|
||||
if( is_region_to_region(keyword) ) {
|
||||
keywordR2R( list, parseContext, errors, deck_keyword );
|
||||
return;
|
||||
}
|
||||
std::string region_name = "FIPNUM";
|
||||
if (keyword.size() > 5) {
|
||||
auto dash_pos = keyword.find("_");
|
||||
region_name = "FIP" + keyword.substr(5,3);
|
||||
}
|
||||
|
||||
const size_t numfip = tables.numFIPRegions( );
|
||||
const auto& item = keyword.getDataRecord().getDataItem();
|
||||
const auto& item = deck_keyword.getDataRecord().getDataItem();
|
||||
std::vector<int> regions;
|
||||
|
||||
if (item.data_size() > 0)
|
||||
@@ -482,11 +490,11 @@ inline void keywordR2R( SummaryConfig::keyword_list& /* list */,
|
||||
regions.push_back( region );
|
||||
}
|
||||
|
||||
// Don't (currently) need parameter type for region keywords
|
||||
auto param = SummaryConfigNode {
|
||||
keyword.name(), SummaryConfigNode::Category::Region, keyword.location()
|
||||
keyword, SummaryConfigNode::Category::Region, deck_keyword.location()
|
||||
}
|
||||
.isUserDefined( is_udq(keyword.name()) );
|
||||
.fip_region( region_name )
|
||||
.isUserDefined( is_udq(keyword) );
|
||||
|
||||
for( const int region : regions ) {
|
||||
if (region >= 1 && region <= static_cast<int>(numfip))
|
||||
@@ -872,6 +880,12 @@ SummaryConfigNode SummaryConfigNode::serializeObject()
|
||||
return result;
|
||||
}
|
||||
|
||||
SummaryConfigNode& SummaryConfigNode::fip_region(const std::string& fip_region)
|
||||
{
|
||||
this->fip_region_ = fip_region;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SummaryConfigNode& SummaryConfigNode::parameterType(const Type type)
|
||||
{
|
||||
this->type_ = type;
|
||||
@@ -1120,6 +1134,17 @@ bool SummaryConfig::hasSummaryKey(const std::string& keyword ) const {
|
||||
}
|
||||
|
||||
|
||||
bool SummaryConfig::match(const std::string& keywordPattern) const {
|
||||
int flags = 0;
|
||||
for (const auto& keyword : this->short_keywords) {
|
||||
if (fnmatch(keywordPattern.c_str(), keyword.c_str(), flags) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_t SummaryConfig::size() const {
|
||||
return this->keywords.size();
|
||||
}
|
||||
@@ -1149,6 +1174,18 @@ bool SummaryConfig::require3DField( const std::string& keyword ) const {
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::set<std::string> SummaryConfig::fip_regions() const {
|
||||
std::set<std::string> reg_set;
|
||||
for (const auto& node : this->keywords) {
|
||||
const auto& fip_region = node.fip_region();
|
||||
if (fip_region.size() > 0)
|
||||
reg_set.insert( fip_region );
|
||||
}
|
||||
return reg_set;
|
||||
}
|
||||
|
||||
|
||||
bool SummaryConfig::operator==(const Opm::SummaryConfig& data) const {
|
||||
return this->keywords == data.keywords &&
|
||||
this->short_keywords == data.short_keywords &&
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <opm/io/eclipse/SummaryNode.hpp>
|
||||
#include <opm/parser/eclipse/Python/Python.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
@@ -96,6 +97,8 @@ static Deck createDeck( const std::string& summary ) {
|
||||
"REGIONS\n"
|
||||
"FIPNUM\n"
|
||||
"200*1 300*2 500*3 /\n"
|
||||
"FIPREG\n"
|
||||
"200*10 300*20 500*30 /\n"
|
||||
"SCHEDULE\n"
|
||||
"WELSPECS\n"
|
||||
" \'W_1\' \'OP\' 1 1 3.33 \'OIL\' 7* / \n"
|
||||
@@ -1037,3 +1040,30 @@ RUNSUM
|
||||
BOOST_CHECK( summary_config2.createRunSummary());
|
||||
BOOST_CHECK(!summary_config2.hasKeyword("RUNSUM"));
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(FIPREG) {
|
||||
std::string deck_string = R"(
|
||||
RPR__REG
|
||||
/
|
||||
|
||||
ROPT_REG
|
||||
/
|
||||
)";
|
||||
const auto& summary_config = createSummary(deck_string);
|
||||
BOOST_CHECK(summary_config.hasKeyword("RPR__REG"));
|
||||
BOOST_CHECK(summary_config.hasKeyword("ROPT_REG"));
|
||||
BOOST_CHECK(!summary_config.hasKeyword("RPR"));
|
||||
BOOST_CHECK(!summary_config.match("BPR*"));
|
||||
BOOST_CHECK(summary_config.match("RPR*"));
|
||||
for (const auto& node : summary_config) {
|
||||
if (node.category() == EclIO::SummaryNode::Category::Region)
|
||||
BOOST_CHECK_EQUAL( node.fip_region(), "FIPREG" );
|
||||
}
|
||||
|
||||
const auto& fip_regions = summary_config.fip_regions();
|
||||
BOOST_CHECK_EQUAL(fip_regions.size(), 1);
|
||||
|
||||
auto reg_iter = fip_regions.find("FIPREG");
|
||||
BOOST_CHECK( reg_iter != fip_regions.end() );
|
||||
}
|
||||
|
||||
@@ -35,14 +35,14 @@ BOOST_AUTO_TEST_CASE(UniqueKey) {
|
||||
using Category = Opm::EclIO::SummaryNode::Category;
|
||||
using Type = Opm::EclIO::SummaryNode::Type;
|
||||
|
||||
expect_key( { "KEYW", Category::Well, Type::Rate, "NORA", 1 }, "KEYW:NORA" );
|
||||
expect_key( { "KEYW", Category::Group, Type::Rate, "NORA", 2 }, "KEYW:NORA" );
|
||||
expect_key( { "KEYW", Category::Field, Type::Rate, "NORA", 3 }, "KEYW" );
|
||||
expect_key( { "KEYW", Category::Region, Type::Rate, "NORA", 4 }, "KEYW:4" );
|
||||
expect_key( { "KEYW", Category::Block, Type::Rate, "NORA", 5 }, "KEYW:5" );
|
||||
expect_key( { "KEYW", Category::Connection, Type::Rate, "NORA", 6 }, "KEYW:NORA:6" );
|
||||
expect_key( { "KEYW", Category::Segment, Type::Rate, "NORA", 7 }, "KEYW:NORA:7" );
|
||||
expect_key( { "KEYW", Category::Miscellaneous, Type::Rate, "NORA", 8 }, "KEYW" );
|
||||
expect_key( { "KEYW", Category::Well, Type::Rate, "NORA", 1 ,""}, "KEYW:NORA" );
|
||||
expect_key( { "KEYW", Category::Group, Type::Rate, "NORA", 2 ,""}, "KEYW:NORA" );
|
||||
expect_key( { "KEYW", Category::Field, Type::Rate, "NORA", 3 ,""}, "KEYW" );
|
||||
expect_key( { "KEYW", Category::Region, Type::Rate, "NORA", 4 ,""}, "KEYW:4" );
|
||||
expect_key( { "KEYW", Category::Block, Type::Rate, "NORA", 5 ,""}, "KEYW:5" );
|
||||
expect_key( { "KEYW", Category::Connection, Type::Rate, "NORA", 6 ,""}, "KEYW:NORA:6" );
|
||||
expect_key( { "KEYW", Category::Segment, Type::Rate, "NORA", 7 ,""}, "KEYW:NORA:7" );
|
||||
expect_key( { "KEYW", Category::Miscellaneous, Type::Rate, "NORA", 8 ,""}, "KEYW" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(InjectedNumberRenderer) {
|
||||
@@ -54,7 +54,8 @@ BOOST_AUTO_TEST_CASE(InjectedNumberRenderer) {
|
||||
Category::Region,
|
||||
Type::Undefined,
|
||||
"-",
|
||||
2
|
||||
2,
|
||||
""
|
||||
};
|
||||
|
||||
Opm::EclIO::SummaryNode negativeNode {
|
||||
@@ -62,7 +63,8 @@ BOOST_AUTO_TEST_CASE(InjectedNumberRenderer) {
|
||||
Category::Region,
|
||||
Type::Undefined,
|
||||
"-",
|
||||
-2
|
||||
-2,
|
||||
""
|
||||
};
|
||||
|
||||
auto chooseSign = [](const Opm::EclIO::SummaryNode& node) -> std::string {
|
||||
@@ -74,7 +76,7 @@ BOOST_AUTO_TEST_CASE(InjectedNumberRenderer) {
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(user_defined) {
|
||||
auto summary_node = Opm::EclIO::SummaryNode{"FU_VAR1", Opm::EclIO::SummaryNode::Category::Field, Opm::EclIO::SummaryNode::Type::Undefined, "", -1 };
|
||||
auto summary_node = Opm::EclIO::SummaryNode{"FU_VAR1", Opm::EclIO::SummaryNode::Category::Field, Opm::EclIO::SummaryNode::Type::Undefined, "", -1 , ""};
|
||||
BOOST_CHECK( summary_node.is_user_defined() );
|
||||
}
|
||||
|
||||
|
||||
@@ -46,14 +46,14 @@ BOOST_AUTO_TEST_CASE(create) {
|
||||
EclipseState es(deck);
|
||||
const EclipseGrid& grid = es.getInputGrid();
|
||||
Schedule schedule( deck, es, python);
|
||||
out::RegionCache rc(es.fieldProps().get_int("FIPNUM"), grid, schedule);
|
||||
out::RegionCache rc({"FIPNUM"}, es.fieldProps(), grid, schedule);
|
||||
{
|
||||
const auto& empty = rc.connections( 4 );
|
||||
const auto& empty = rc.connections( "FIPNUM", 4 );
|
||||
BOOST_CHECK_EQUAL( empty.size() , 0 );
|
||||
}
|
||||
|
||||
{
|
||||
const auto& top_layer = rc.connections( 1 );
|
||||
const auto& top_layer = rc.connections( "FIPNUM", 1 );
|
||||
BOOST_CHECK_EQUAL( top_layer.size() , 3 );
|
||||
{
|
||||
auto pair = top_layer[0];
|
||||
|
||||
Reference in New Issue
Block a user