Merge pull request #2256 from joakim-hove/vfp-pointer-update

Pack only unique VFP tables
This commit is contained in:
Joakim Hove
2021-02-01 17:06:30 +01:00
committed by GitHub
5 changed files with 259 additions and 100 deletions

View File

@@ -363,6 +363,9 @@ namespace Opm
pack_unpack<UDQActive, Serializer>(serializer);
pack_unpack<NameOrder, Serializer>(serializer);
pack_unpack<GroupOrder, Serializer>(serializer);
pack_unpack_map<int, VFPProdTable, Serializer>(serializer);
pack_unpack_map<int, VFPInjTable, Serializer>(serializer);
}
template <typename T, class Serializer>
@@ -413,6 +416,78 @@ namespace Opm
}
template <typename K, typename T, class Serializer>
void pack_unpack_map(Serializer& serializer) {
std::vector<T> value_list;
std::vector<std::size_t> index_list;
if (serializer.isSerializing())
pack_map<K,T>(value_list, index_list);
serializer.vector(value_list);
serializer.template vector<std::size_t, false>(index_list);
if (!serializer.isSerializing())
unpack_map<K,T>(value_list, index_list);
}
template <typename K, typename T>
void pack_map(std::vector<T>& value_list,
std::vector<std::size_t>& index_list) {
const auto& last_map = this->snapshots.back().get_map<K,T>();
std::vector<K> key_list{ last_map.keys() };
std::unordered_map<K,T> 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<K,T>();
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 <typename K, typename T>
void unpack_map(const std::vector<T>& value_list,
const std::vector<std::size_t>& index_list) {
std::unordered_map<K, std::vector<std::pair<std::size_t, T>>> 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<K,T>();
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<K,T>();
forward_map.update( key, map_value );
}
}
}
}
private:
template<class Key, class Value> using Map2 = std::map<Key,Value>;

View File

@@ -112,6 +112,94 @@ namespace Opm {
std::shared_ptr<T> 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<T>, 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 <typename K, typename T>
class map_member {
public:
std::vector<K> keys() const {
std::vector<K> 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<T> 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<T>( std::move(object) );
}
void update(const K& key, const map_member<K,T>& other) {
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 {
return *this->m_data.at(key);
}
std::vector<std::reference_wrapper<const T>> operator()() const {
std::vector<std::reference_wrapper<const T>> 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<K,T>& other) const {
if (this->m_data.size() != other.m_data.size())
return false;
for (const auto& [key1, ptr1] : this->m_data) {
const auto& ptr2 = other.get_ptr(key1);
if (!ptr2)
return false;
if (!(*ptr1 == *ptr2))
return false;
}
return true;
}
static map_member<K,T> serializeObject() {
map_member<K,T> map_object;
T value_object = T::serializeObject();
K key = value_object.name();
map_object.m_data.emplace( key, std::make_shared<T>( std::move(value_object) ));
return map_object;
}
private:
std::unordered_map<K, std::shared_ptr<T>> 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);
@@ -170,6 +258,8 @@ namespace Opm {
ptr_member<NameOrder> well_order;
ptr_member<GroupOrder> group_order;
template <typename T> struct always_false1 : std::false_type {};
template <typename T>
ptr_member<T>& get() {
if constexpr ( std::is_same_v<T, PAvg> )
@@ -194,18 +284,22 @@ namespace Opm {
return this->well_order;
else if constexpr ( std::is_same_v<T, GroupOrder> )
return this->group_order;
else
static_assert(always_false1<T>::value, "Template type <T> not supported in get()");
}
std::vector<std::reference_wrapper<const VFPProdTable>> vfpprod() const;
const VFPProdTable& vfpprod(int table_id) const;
void update_vfpprod(VFPProdTable vfpprod);
std::optional<std::reference_wrapper<const VFPProdTable>> try_vfpprod(int table_id) const;
template <typename K, typename T> struct always_false2 : std::false_type {};
template <typename K, typename T>
map_member<K,T>& get_map() {
if constexpr ( std::is_same_v<T, VFPProdTable> )
return this->vfpprod;
if constexpr ( std::is_same_v<T, VFPInjTable> )
return this->vfpinj;
}
std::vector<std::reference_wrapper<const VFPInjTable>> vfpinj() const;
const VFPInjTable& vfpinj(int table_id) const;
void update_vfpinj(VFPInjTable vfpinj);
std::optional<std::reference_wrapper<const VFPInjTable>> try_vfpinj(int table_id) const;
map_member<int, VFPProdTable> vfpprod;
map_member<int, VFPInjTable> vfpinj;
template<class Serializer>
void serializeOp(Serializer& serializer) {
@@ -219,8 +313,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 +328,6 @@ namespace Opm {
std::vector<DeckKeyword> m_geo_keywords;
MessageLimits m_message_limits;
Well::ProducerCMode m_whistctl_mode = Well::ProducerCMode::CMODE_UNDEFINED;
std::map<int, std::shared_ptr<VFPProdTable>> m_vfpprod;
std::map<int, std::shared_ptr<VFPInjTable>> m_vfpinj;
};
}

View File

@@ -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) {

View File

@@ -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>(VFPProdTable::serializeObject() )));
ts.m_vfpprod.emplace( std::make_pair(78, std::make_shared<VFPProdTable>(VFPProdTable::serializeObject() )));
ts.m_vfpinj.emplace( std::make_pair(177, std::make_shared<VFPInjTable>(VFPInjTable::serializeObject() )));
ts.m_vfpinj.emplace( std::make_pair(178, std::make_shared<VFPInjTable>(VFPInjTable::serializeObject() )));
ts.vfpprod = map_member<int, VFPProdTable>::serializeObject();
ts.vfpinj = map_member<int, VFPInjTable>::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<std::reference_wrapper<const VFPProdTable>> ScheduleState::vfpprod() const {
std::vector<std::reference_wrapper<const VFPProdTable>> 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<VFPProdTable>( std::move(vfpprod) );
}
std::optional<std::reference_wrapper<const VFPProdTable>> 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<std::reference_wrapper<const VFPInjTable>> ScheduleState::vfpinj() const {
std::vector<std::reference_wrapper<const VFPInjTable>> tables;
for (const auto& [_, table] : this->m_vfpinj) {
(void)_;
tables.push_back( std::cref( *table ));
}
return tables;
}
std::optional<std::reference_wrapper<const VFPInjTable>> 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<VFPInjTable>( std::move(vfpinj) );
}
}

View File

@@ -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<Python>();
@@ -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<Opm::VFPInjTable> value_list;
std::vector<std::size_t> index_list;
sched.pack_map<int, Opm::VFPInjTable>( value_list, index_list );
BOOST_CHECK_EQUAL( value_list.size(), 2 );
sched0.unpack_map<int, Opm::VFPInjTable>( 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);
}