From 5c72211325a7e7ae353784aa8a5c25a57f0c3714 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Sat, 30 Jan 2021 14:51:55 +0100 Subject: [PATCH 1/4] Pack pointers for VFP maps --- .../EclipseState/Schedule/Schedule.hpp | 75 ++++++++++++ .../EclipseState/Schedule/ScheduleState.hpp | 108 ++++++++++++++++-- .../EclipseState/Schedule/KeywordHandlers.cpp | 4 +- .../EclipseState/Schedule/ScheduleState.cpp | 91 +-------------- tests/parser/ScheduleSerializeTest.cpp | 75 ++++++++++++ 5 files changed, 253 insertions(+), 100 deletions(-) diff --git a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp index b0d3fb73a..ea8663fb2 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp @@ -361,6 +361,9 @@ namespace Opm pack_unpack(serializer); pack_unpack(serializer); pack_unpack(serializer); + + pack_unpack_map(serializer); + pack_unpack_map(serializer); } template @@ -411,6 +414,78 @@ namespace Opm } + template + void pack_unpack_map(Serializer& serializer) { + std::vector value_list; + std::vector index_list; + + if (serializer.isSerializing()) + pack_map(value_list, index_list); + + serializer.vector(value_list); + serializer.template vector(index_list); + + if (!serializer.isSerializing()) + unpack_map(value_list, index_list); + } + + + template + void pack_map(std::vector& value_list, + std::vector& index_list) { + + const auto& last_map = this->snapshots.back().get_map(); + std::vector key_list{ last_map.keys() }; + std::unordered_map current_value; + + for (std::size_t index = 0; index < this->snapshots.size(); index++) { + auto& state = this->snapshots[index]; + const auto& current_map = state.template get_map(); + for (const auto& key : key_list) { + auto& value = current_map.get_ptr(key); + if (value) { + if (!(*value == current_value[key])) { + value_list.push_back( *value ); + index_list.push_back( index ); + + current_value[key] = *value; + } + } + } + } + } + + + template + void unpack_map(const std::vector& value_list, + const std::vector& index_list) { + + std::unordered_map>> storage; + for (std::size_t storage_index = 0; storage_index < value_list.size(); storage_index++) { + const auto& value = value_list[storage_index]; + const auto& time_index = index_list[storage_index]; + + storage[ value.name() ].emplace_back( time_index, value ); + } + + for (const auto& [key, values] : storage) { + for (std::size_t unique_index = 0; unique_index < values.size(); unique_index++) { + const auto& [time_index, value] = values[unique_index]; + auto last_index = this->snapshots.size(); + if (unique_index < (values.size() - 1)) + last_index = values[unique_index + 1].first; + + auto& map_value = this->snapshots[time_index].template get_map(); + map_value.update(std::move(value)); + + for (std::size_t index=time_index + 1; index < last_index; index++) { + auto& forward_map = this->snapshots[index].template get_map(); + forward_map.update( key, map_value ); + } + } + } + } + private: template using Map2 = std::map; diff --git a/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp b/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp index 364fb9e15..decbaf611 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp @@ -112,6 +112,93 @@ namespace Opm { std::shared_ptr m_data; }; + + /* + The map_member class is a quite specialized class used to internalize + the map variables managed in the ScheduleState. The actual value + objects will be stored as std::shared_ptr, and only the unique + objects have dedicated storage. The class T must implement the method: + + const K& T::name() const; + + Which is used to get the storage key for the objects. + */ + + template + class map_member { + public: + std::vector keys() const { + std::vector key_vector; + std::transform( this->m_data.begin(), this->m_data.end(), std::back_inserter(key_vector), [](const auto& pair) { return pair.first; }); + return key_vector; + } + + + const std::shared_ptr get_ptr(const K& key) const { + auto iter = this->m_data.find(key); + if (iter != this->m_data.end()) + return iter->second; + + return {}; + } + + + void update(T object) { + auto key = object.name(); + this->m_data[key] = std::make_shared( std::move(object) ); + } + + void update(const K& key, const map_member& other) { + this->m_data[key] = other.get_ptr(key); + } + + const T& operator()(const K& key) const { + return *this->m_data.at(key); + } + + std::vector> operator()() const { + std::vector> as_vector; + for (const auto& [_, elm_ptr] : this->m_data) { + (void)_; + as_vector.push_back( std::cref(*elm_ptr)); + } + return as_vector; + } + + + bool operator==(const map_member& other) const { + if (this->m_data.size() != other.m_data.size()) + return false; + + auto it2 = other.m_data.begin(); + for (const auto& it1 : this->m_data) { + if (it1.first != it2->first) + return false; + + if (!(*it1.second == *it2->second)) + return false; + + ++it2; + } + + return true; + } + + static map_member serializeObject() { + map_member map_object; + T value_object = T::serializeObject(); + K key = value_object.name(); + map_object.m_data.emplace( key, std::make_shared( std::move(value_object) )); + return map_object; + } + + + private: + std::unordered_map> m_data; + }; + + + ScheduleState() = default; explicit ScheduleState(const std::chrono::system_clock::time_point& start_time); ScheduleState(const std::chrono::system_clock::time_point& start_time, const std::chrono::system_clock::time_point& end_time); @@ -197,15 +284,16 @@ namespace Opm { } - std::vector> vfpprod() const; - const VFPProdTable& vfpprod(int table_id) const; - void update_vfpprod(VFPProdTable vfpprod); - std::optional> try_vfpprod(int table_id) const; + template + map_member& get_map() { + if constexpr ( std::is_same_v ) + return this->vfpprod; + if constexpr ( std::is_same_v ) + return this->vfpinj; + } - std::vector> vfpinj() const; - const VFPInjTable& vfpinj(int table_id) const; - void update_vfpinj(VFPInjTable vfpinj); - std::optional> try_vfpinj(int table_id) const; + map_member vfpprod; + map_member vfpinj; template void serializeOp(Serializer& serializer) { @@ -219,8 +307,6 @@ namespace Opm { serializer.vector(m_geo_keywords); m_message_limits.serializeOp(serializer); serializer(m_whistctl_mode); - serializer.map(m_vfpprod); - serializer.map(m_vfpinj); } @@ -236,8 +322,6 @@ namespace Opm { std::vector m_geo_keywords; MessageLimits m_message_limits; Well::ProducerCMode m_whistctl_mode = Well::ProducerCMode::CMODE_UNDEFINED; - std::map> m_vfpprod; - std::map> m_vfpinj; }; } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp index 1fd493e9c..15abe87a9 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp @@ -840,13 +840,13 @@ namespace { void Schedule::handleVFPINJ(const HandlerContext& handlerContext, const ParseContext&, ErrorGuard&) { auto table = VFPInjTable(handlerContext.keyword, this->m_static.m_unit_system); this->snapshots.back().events().addEvent( ScheduleEvents::VFPINJ_UPDATE ); - this->snapshots.back().update_vfpinj( std::move(table) ); + this->snapshots.back().vfpinj.update( std::move(table) ); } void Schedule::handleVFPPROD(const HandlerContext& handlerContext, const ParseContext&, ErrorGuard&) { auto table = VFPProdTable(handlerContext.keyword, this->m_static.m_unit_system); this->snapshots.back().events().addEvent( ScheduleEvents::VFPPROD_UPDATE ); - this->snapshots.back().update_vfpprod( std::move(table) ); + this->snapshots.back().vfpprod.update( std::move(table) ); } void Schedule::handleWCONHIST(const HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard& errors) { diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.cpp index c33346b02..060679495 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.cpp @@ -135,28 +135,6 @@ void ScheduleState::update_whistctl(Well::ProducerCMode whistctl) { } bool ScheduleState::operator==(const ScheduleState& other) const { - auto&& map_equal = [](const auto& map1, const auto& map2) { - if (map1.size() != map2.size()) - return false; - - auto it2 = map2.begin(); - for (const auto& it1 : map1) { - if (it1.first != it2->first) - return false; - - if (!(*it1.second == *it2->second)) - return false; - - ++it2; - } - return true; - }; - - if (!map_equal(this->m_vfpprod, other.m_vfpprod)) - return false; - - if (!map_equal(this->m_vfpinj, other.m_vfpinj)) - return false; return this->m_start_time == other.m_start_time && this->m_oilvap == other.m_oilvap && @@ -175,17 +153,17 @@ bool ScheduleState::operator==(const ScheduleState& other) const { this->gconsump.get() == other.gconsump.get() && this->wlist_manager.get() == other.wlist_manager.get() && this->rpt_config.get() == other.rpt_config.get() && - this->udq_active.get() == other.udq_active.get(); + this->udq_active.get() == other.udq_active.get() && + this->vfpprod == other.vfpprod && + this->vfpinj == other.vfpinj; } ScheduleState ScheduleState::serializeObject() { auto t1 = std::chrono::system_clock::now(); auto t2 = t1 + std::chrono::hours(48); ScheduleState ts(t1, t2); - ts.m_vfpprod.emplace( std::make_pair(77, std::make_shared(VFPProdTable::serializeObject() ))); - ts.m_vfpprod.emplace( std::make_pair(78, std::make_shared(VFPProdTable::serializeObject() ))); - ts.m_vfpinj.emplace( std::make_pair(177, std::make_shared(VFPInjTable::serializeObject() ))); - ts.m_vfpinj.emplace( std::make_pair(178, std::make_shared(VFPInjTable::serializeObject() ))); + ts.vfpprod = map_member::serializeObject(); + ts.vfpinj = map_member::serializeObject(); ts.m_events = Events::serializeObject(); ts.update_nupcol(77); ts.update_oilvap( Opm::OilVaporizationProperties::serializeObject() ); @@ -243,63 +221,4 @@ const WellGroupEvents& ScheduleState::wellgroup_events() const { return this->m_wellgroup_events; } -std::vector> ScheduleState::vfpprod() const { - std::vector> tables; - for (const auto& [_, table] : this->m_vfpprod) { - (void)_; - tables.push_back( std::cref( *table )); - } - return tables; -} - -const VFPProdTable& ScheduleState::vfpprod(int table_id) const { - auto vfp_iter = this->m_vfpprod.find(table_id); - if (vfp_iter == this->m_vfpprod.end()) - throw std::logic_error(fmt::format("No VFPPROD table with id: {} has been registered", table_id)); - - return *vfp_iter->second; -} - -void ScheduleState::update_vfpprod(VFPProdTable vfpprod) { - int table_id = vfpprod.getTableNum(); - this->m_vfpprod[table_id] = std::make_shared( std::move(vfpprod) ); -} - -std::optional> ScheduleState::try_vfpprod(int table_id) const { - auto vfp_iter = this->m_vfpprod.find(table_id); - if (vfp_iter != this->m_vfpprod.end()) - return std::cref(*vfp_iter->second); - return {}; -} - - -std::vector> ScheduleState::vfpinj() const { - std::vector> tables; - for (const auto& [_, table] : this->m_vfpinj) { - (void)_; - tables.push_back( std::cref( *table )); - } - return tables; -} - -std::optional> ScheduleState::try_vfpinj(int table_id) const { - auto vfp_iter = this->m_vfpinj.find(table_id); - if (vfp_iter != this->m_vfpinj.end()) - return std::cref(*vfp_iter->second); - return {}; -} - -const VFPInjTable& ScheduleState::vfpinj(int table_id) const { - auto vfp_iter = this->m_vfpinj.find(table_id); - if (vfp_iter == this->m_vfpinj.end()) - throw std::logic_error(fmt::format("No VFPINJ table with id: {} has been registered", table_id)); - - return *vfp_iter->second; -} - -void ScheduleState::update_vfpinj(VFPInjTable vfpinj) { - int table_id = vfpinj.getTableNum(); - this->m_vfpinj[table_id] = std::make_shared( std::move(vfpinj) ); -} - } diff --git a/tests/parser/ScheduleSerializeTest.cpp b/tests/parser/ScheduleSerializeTest.cpp index 96def5b15..c8c774a8f 100644 --- a/tests/parser/ScheduleSerializeTest.cpp +++ b/tests/parser/ScheduleSerializeTest.cpp @@ -255,6 +255,57 @@ DATES -- 7 )"; +std::string VFP_deck1 = R"( +START -- 0 +10 MAI 2007 / +SCHEDULE + +VFPINJ +5 32.9 WAT THP METRIC BHP / +1 3 5 / +7 11 / +1 1.5 2.5 3.5 / +2 4.5 5.5 6.5 / + +DATES -- 1 + 10 JUN 2007 / +/ + +DATES -- 2 + 15 JUN 2007 / +/ + +DATES -- 3 + 20 JUN 2007 / +/ + +VFPINJ +5 32.9 WAT THP METRIC BHP / +1 3 5 / +7 11 / +1 1.5 2.5 3.4 / +2 4.5 5.5 6.4 / + +DATES -- 4 + 10 JUL 2007 / +/ + +DATES -- 5 + 10 AUG 2007 / +/ + +DATES -- 6 + 10 SEP 2007 / +/ + +DATES -- 7 + 10 NOV 2007 / +/ +)"; + + + + static Schedule make_schedule(const std::string& deck_string) { const auto& deck = Parser{}.parseString(deck_string); auto python = std::make_shared(); @@ -361,6 +412,30 @@ BOOST_AUTO_TEST_CASE(SerializeGCONSUMP) { } +BOOST_AUTO_TEST_CASE(SerializeVFP) { + auto sched = make_schedule(VFP_deck1); + auto sched0 = make_schedule(deck0); + auto vfpinj1 = sched[0].vfpinj; + auto vfpinj2 = sched[3].vfpinj; + + { + std::vector value_list; + std::vector index_list; + sched.pack_map( value_list, index_list ); + BOOST_CHECK_EQUAL( value_list.size(), 2 ); + sched0.unpack_map( value_list, index_list ); + } + + BOOST_CHECK( vfpinj1 == sched0[0].vfpinj); + BOOST_CHECK( vfpinj1 == sched0[1].vfpinj); + BOOST_CHECK( vfpinj1 == sched0[2].vfpinj); + + BOOST_CHECK( vfpinj2 == sched0[3].vfpinj); + BOOST_CHECK( vfpinj2 == sched0[4].vfpinj); + BOOST_CHECK( vfpinj2 == sched0[5].vfpinj); +} + + From 1032e16947dd8012789cc618635ecab95827ab26 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Mon, 1 Feb 2021 12:25:36 +0100 Subject: [PATCH 2/4] Use order independent map== --- .../eclipse/EclipseState/Schedule/ScheduleState.hpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp b/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp index decbaf611..93a53bd1a 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp @@ -170,17 +170,14 @@ namespace Opm { if (this->m_data.size() != other.m_data.size()) return false; - auto it2 = other.m_data.begin(); - for (const auto& it1 : this->m_data) { - if (it1.first != it2->first) + for (const auto& [key1, ptr1] : this->m_data) { + const auto& ptr2 = other.get_ptr(key1); + if (!ptr2) return false; - if (!(*it1.second == *it2->second)) + if (!(*ptr1 == *ptr2)) return false; - - ++it2; } - return true; } From 1893daec8b0704069ccae7b186b780ecc8377a54 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Mon, 1 Feb 2021 12:26:41 +0100 Subject: [PATCH 3/4] Will refuse pointer update with empty pointer --- opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp b/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp index 93a53bd1a..b841d64ff 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp @@ -149,7 +149,11 @@ namespace Opm { } void update(const K& key, const map_member& other) { - this->m_data[key] = other.get_ptr(key); + auto other_ptr = other.get_ptr(key); + if (other_ptr) + this->m_data[key] = other.get_ptr(key); + else + throw std::logic_error(std::string{"Tried to update member: "} + std::to_string(key) + std::string{"with uninitialized object"}); } const T& operator()(const K& key) const { From 5f21fe291f660b6d0c0ab23f2d797c0be9c55a79 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Mon, 1 Feb 2021 12:28:07 +0100 Subject: [PATCH 4/4] ScheduleState::get will give compile error for invalid T --- opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp b/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp index b841d64ff..e8c9cfbba 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp @@ -258,6 +258,8 @@ namespace Opm { ptr_member well_order; ptr_member group_order; + template struct always_false1 : std::false_type {}; + template ptr_member& get() { if constexpr ( std::is_same_v ) @@ -282,9 +284,12 @@ namespace Opm { return this->well_order; else if constexpr ( std::is_same_v ) return this->group_order; + else + static_assert(always_false1::value, "Template type not supported in get()"); } + template struct always_false2 : std::false_type {}; template map_member& get_map() { if constexpr ( std::is_same_v )