Track Pending Assignments in UDQ Configuration Object
The existing implementation used the UDQ State object to track
pending ASSIGN operations, mainly in terms of the report step index,
but this implies that the logic implicitly assumes an ASSIGN
operation can run at most once per report step. That assumption
usually holds, but fails if the ASSIGN operation is triggered from
an ACTIONX block that happens to run multiple times within a report
step.
This commit instead introduces a new data member,
UDQConfig::pending_assignments_
that keeps track of all ASSIGN operations that have been added for
the current report step. We clear those pending assignments when
forming a new Schedule block (ScheduleState object), under the
assumption that all pending ASSIGN operations have been affected at
the previous time level (report step).
In effect, this new data member assumes the role of
UDQState::assignments
and we therefore remove that data member and update the signature of
UDQState::add_assign()
to account for the fact that the 'report_step' parameter is no
longer needed.
This commit is contained in:
@@ -101,6 +101,8 @@ namespace Opm {
|
||||
const std::vector<std::string>& expression,
|
||||
std::size_t report_step);
|
||||
|
||||
bool clear_pending_assignments();
|
||||
|
||||
void eval_assign(std::size_t report_step,
|
||||
const Schedule& sched,
|
||||
const WellMatcher& wm,
|
||||
@@ -147,6 +149,7 @@ namespace Opm {
|
||||
serializer(units);
|
||||
serializer(input_index);
|
||||
serializer(type_count);
|
||||
serializer(pending_assignments_);
|
||||
|
||||
// The UDQFunction table is constant up to udq_params, so we can
|
||||
// just construct a new instance here.
|
||||
@@ -174,12 +177,13 @@ namespace Opm {
|
||||
OrderedMap<UDQIndex> input_index;
|
||||
std::map<UDQVarType, std::size_t> type_count;
|
||||
|
||||
mutable std::vector<std::string> pending_assignments_{};
|
||||
|
||||
void add_node(const std::string& quantity, UDQAction action);
|
||||
UDQAction action_type(const std::string& udq_key) const;
|
||||
|
||||
void eval_assign(std::size_t report_step,
|
||||
const Schedule& sched,
|
||||
UDQState& udq_state,
|
||||
UDQContext& context) const;
|
||||
|
||||
void eval_define(std::size_t report_step,
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Opm {
|
||||
std::optional<double> get_segment_var(const std::string& well, const std::string& var, std::size_t segment) const;
|
||||
|
||||
void add(const std::string& key, double value);
|
||||
void update_assign(std::size_t report_step, const std::string& keyword, const UDQSet& udq_result);
|
||||
void update_assign(const std::string& keyword, const UDQSet& udq_result);
|
||||
void update_define(std::size_t report_step, const std::string& keyword, const UDQSet& udq_result);
|
||||
|
||||
const UDQFunctionTable& function_table() const;
|
||||
|
||||
@@ -53,8 +53,7 @@ public:
|
||||
double get_segment_var(const std::string& well, const std::string& var, const std::size_t segment) const;
|
||||
|
||||
void add_define(std::size_t report_step, const std::string& udq_key, const UDQSet& result);
|
||||
void add_assign(std::size_t report_step, const std::string& udq_key, const UDQSet& result);
|
||||
bool assign(std::size_t report_step, const std::string& udq_key) const;
|
||||
void add_assign(const std::string& udq_key, const UDQSet& result);
|
||||
bool define(const std::string& udq_key, const std::pair<UDQUpdate, std::size_t>& update_status) const;
|
||||
double undefined_value() const;
|
||||
|
||||
@@ -70,7 +69,6 @@ public:
|
||||
serializer(this->well_values);
|
||||
serializer(this->group_values);
|
||||
serializer(this->segment_values);
|
||||
serializer(this->assignments);
|
||||
serializer(this->defines);
|
||||
}
|
||||
|
||||
@@ -87,7 +85,6 @@ private:
|
||||
// [var][well][segment] -> double
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, std::unordered_map<std::size_t, double>>> segment_values{};
|
||||
|
||||
std::unordered_map<std::string, std::size_t> assignments;
|
||||
std::unordered_map<std::string, std::size_t> defines;
|
||||
|
||||
void add(const std::string& udq_key, const UDQSet& result);
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
|
||||
#include <opm/input/eclipse/Schedule/Action/Actions.hpp>
|
||||
#include <opm/input/eclipse/Schedule/GasLiftOpt.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Group/GConSump.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Group/GConSale.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Group/GConSump.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Group/GroupEconProductionLimits.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Group/GuideRateConfig.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Network/Balance.hpp>
|
||||
@@ -31,39 +31,51 @@
|
||||
#include <opm/input/eclipse/Schedule/RPTConfig.hpp>
|
||||
#include <opm/input/eclipse/Schedule/UDQ/UDQActive.hpp>
|
||||
#include <opm/input/eclipse/Schedule/UDQ/UDQConfig.hpp>
|
||||
#include <opm/input/eclipse/Schedule/VFPProdTable.hpp>
|
||||
#include <opm/input/eclipse/Schedule/VFPInjTable.hpp>
|
||||
#include <opm/input/eclipse/Schedule/VFPProdTable.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Well/WellMatcher.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Well/WellTestConfig.hpp>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <ctime>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm {
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace {
|
||||
|
||||
/*
|
||||
This is to ensure that only time_points which can be represented with
|
||||
std::time_t are used. The reason for clamping to std::time_t resolution is
|
||||
that the serialization code in
|
||||
opm-simulators:opm/simulators/utils/ParallelRestart.cpp goes via std::time_t.
|
||||
*/
|
||||
time_point clamp_time(time_point t) {
|
||||
return TimeService::from_time_t( TimeService::to_time_t( t ) );
|
||||
}
|
||||
// This is to ensure that only time_points which can be represented with
|
||||
// std::time_t are used. The reason for clamping to std::time_t
|
||||
// resolution is that the serialization code in
|
||||
// opm-simulators:opm/simulators/utils/ParallelRestart.cpp goes via
|
||||
// std::time_t.
|
||||
Opm::time_point clamp_time(Opm::time_point t)
|
||||
{
|
||||
return Opm::TimeService::from_time_t(Opm::TimeService::to_time_t(t));
|
||||
}
|
||||
|
||||
std::pair<std::size_t, std::size_t> date_diff(const time_point& t2, const time_point& t1) {
|
||||
auto ts1 = TimeStampUTC(TimeService::to_time_t(t1));
|
||||
auto ts2 = TimeStampUTC(TimeService::to_time_t(t2));
|
||||
auto year_diff = ts2.year() - ts1.year();
|
||||
auto month_diff = year_diff*12 + ts2.month() - ts1.month();
|
||||
return { year_diff, month_diff };
|
||||
}
|
||||
std::pair<std::size_t, std::size_t>
|
||||
date_diff(const Opm::time_point& t2, const Opm::time_point& t1)
|
||||
{
|
||||
const auto ts1 = Opm::TimeStampUTC { Opm::TimeService::to_time_t(t1) };
|
||||
const auto ts2 = Opm::TimeStampUTC { Opm::TimeService::to_time_t(t2) };
|
||||
|
||||
}
|
||||
const auto year_diff = ts2.year() - ts1.year();
|
||||
const auto month_diff = year_diff*12 + ts2.month() - ts1.month();
|
||||
|
||||
return { year_diff, month_diff };
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
namespace Opm {
|
||||
|
||||
void ScheduleState::updateSAVE(bool save) {
|
||||
this->m_save_step = save;
|
||||
@@ -73,27 +85,23 @@ bool ScheduleState::save() const {
|
||||
return this->m_save_step;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ScheduleState::ScheduleState(const time_point& t1):
|
||||
m_start_time(clamp_time(t1)),
|
||||
m_first_in_month(true),
|
||||
m_first_in_year(true)
|
||||
ScheduleState::ScheduleState(const time_point& t1)
|
||||
: m_start_time(clamp_time(t1))
|
||||
, m_first_in_month(true)
|
||||
, m_first_in_year(true)
|
||||
{
|
||||
auto ts1 = TimeStampUTC(TimeService::to_time_t(this->m_start_time));
|
||||
this->m_month_num = ts1.month() - 1;
|
||||
}
|
||||
|
||||
ScheduleState::ScheduleState(const time_point& start_time, const time_point& end_time) :
|
||||
ScheduleState(start_time)
|
||||
ScheduleState::ScheduleState(const time_point& start_time, const time_point& end_time)
|
||||
: ScheduleState(start_time)
|
||||
{
|
||||
this->m_end_time = clamp_time(end_time);
|
||||
}
|
||||
|
||||
void ScheduleState::update_date(const time_point& prev_time) {
|
||||
void ScheduleState::update_date(const time_point& prev_time)
|
||||
{
|
||||
auto [year_diff, month_diff] = date_diff(this->m_start_time, prev_time);
|
||||
this->m_year_num += year_diff;
|
||||
this->m_first_in_month = (month_diff > 0);
|
||||
@@ -103,11 +111,8 @@ void ScheduleState::update_date(const time_point& prev_time) {
|
||||
this->m_month_num = ts1.month() - 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ScheduleState::ScheduleState(const ScheduleState& src, const time_point& start_time) :
|
||||
ScheduleState(src)
|
||||
ScheduleState::ScheduleState(const ScheduleState& src, const time_point& start_time)
|
||||
: ScheduleState { src } // Copy constructor
|
||||
{
|
||||
this->m_start_time = clamp_time(start_time);
|
||||
this->m_end_time = std::nullopt;
|
||||
@@ -118,15 +123,22 @@ ScheduleState::ScheduleState(const ScheduleState& src, const time_point& start_t
|
||||
this->target_wellpi.clear();
|
||||
this->m_save_step = false;
|
||||
|
||||
auto next_rft = this->rft_config().next();
|
||||
if (next_rft.has_value())
|
||||
this->rft_config.update( std::move(*next_rft) );
|
||||
{
|
||||
auto next_rft = this->rft_config().next();
|
||||
|
||||
this->update_date(src.m_start_time);
|
||||
if (this->rst_config().save) {
|
||||
auto new_rst = this->rst_config();
|
||||
new_rst.save = false;
|
||||
this->rst_config.update( std::move(new_rst) );
|
||||
if (next_rft.has_value()) {
|
||||
this->rft_config.update(std::move(*next_rft));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
this->update_date(src.m_start_time);
|
||||
|
||||
if (this->rst_config().save) {
|
||||
auto new_rst = this->rst_config();
|
||||
new_rst.save = false;
|
||||
this->rst_config.update(std::move(new_rst));
|
||||
}
|
||||
}
|
||||
|
||||
if (this->next_tstep.has_value()) {
|
||||
@@ -136,16 +148,26 @@ ScheduleState::ScheduleState(const ScheduleState& src, const time_point& start_t
|
||||
// Need to signal an event also for the persistance to take effect
|
||||
this->events().addEvent(ScheduleEvents::TUNING_CHANGE);
|
||||
}
|
||||
|
||||
{
|
||||
auto new_udq = this->udq();
|
||||
|
||||
if (new_udq.clear_pending_assignments()) {
|
||||
// New report step. All ASSIGNments from previous report steps
|
||||
// have been performed.
|
||||
this->udq.update(std::move(new_udq));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ScheduleState::ScheduleState(const ScheduleState& src, const time_point& start_time, const time_point& end_time) :
|
||||
ScheduleState(src, start_time)
|
||||
ScheduleState::ScheduleState(const ScheduleState& src,
|
||||
const time_point& start_time,
|
||||
const time_point& end_time)
|
||||
: ScheduleState { src, start_time }
|
||||
{
|
||||
this->m_end_time = end_time;
|
||||
}
|
||||
|
||||
|
||||
time_point ScheduleState::start_time() const {
|
||||
return this->m_start_time;
|
||||
}
|
||||
|
||||
@@ -176,6 +176,7 @@ namespace Opm {
|
||||
result.units = {{"test3", "test4"}};
|
||||
result.input_index.insert({"test5", UDQIndex::serializationTestObject()});
|
||||
result.type_count = {{UDQVarType::SCALAR, 5}};
|
||||
result.pending_assignments_.push_back("test2");
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -219,6 +220,10 @@ namespace Opm {
|
||||
this->add_named_assign(quantity, selector, value, report_step);
|
||||
break;
|
||||
}
|
||||
|
||||
if (this->m_assignments.find(quantity) != this->m_assignments.end()) {
|
||||
this->pending_assignments_.push_back(quantity);
|
||||
}
|
||||
}
|
||||
|
||||
void UDQConfig::add_assign(const std::string& quantity,
|
||||
@@ -333,6 +338,14 @@ namespace Opm {
|
||||
}
|
||||
}
|
||||
|
||||
bool UDQConfig::clear_pending_assignments()
|
||||
{
|
||||
const auto update = ! this->pending_assignments_.empty();
|
||||
this->pending_assignments_.clear();
|
||||
|
||||
return update;
|
||||
}
|
||||
|
||||
const UDQAssign& UDQConfig::assign(const std::string& key) const
|
||||
{
|
||||
return this->m_assignments.at(key);
|
||||
@@ -522,14 +535,18 @@ namespace Opm {
|
||||
&& (this->units == data.units)
|
||||
&& (this->input_index == data.input_index)
|
||||
&& (this->type_count == data.type_count)
|
||||
&& (this->pending_assignments_ == data.pending_assignments_)
|
||||
;
|
||||
}
|
||||
|
||||
void UDQConfig::eval_assign(const std::size_t report_step,
|
||||
const Schedule& sched,
|
||||
UDQState& udq_state,
|
||||
UDQContext& context) const
|
||||
{
|
||||
if (this->pending_assignments_.empty()) {
|
||||
return; // Nothing to do
|
||||
}
|
||||
|
||||
const auto handlers = std::map<UDQVarType, EvalAssign> {
|
||||
{ UDQVarType::FIELD_VAR , EvalAssign::field() },
|
||||
{ UDQVarType::GROUP_VAR , EvalAssign::group(report_step, sched) },
|
||||
@@ -537,24 +554,30 @@ namespace Opm {
|
||||
{ UDQVarType::SEGMENT_VAR, EvalAssign::segment(context) },
|
||||
};
|
||||
|
||||
for (const auto& index_pair : this->input_index) {
|
||||
const auto& keyword = index_pair.first;
|
||||
auto asgn_pos = this->m_assignments.find(keyword);
|
||||
if ((asgn_pos == this->m_assignments.end()) ||
|
||||
! udq_state.assign(asgn_pos->second.report_step(), keyword))
|
||||
{
|
||||
// No such ASSIGN or ASSIGN not active
|
||||
// Recall: pending_assignments_ is mutable.
|
||||
auto pending = std::vector<std::string>{};
|
||||
this->pending_assignments_.swap(pending);
|
||||
|
||||
{
|
||||
std::sort(pending.begin(), pending.end());
|
||||
auto u = std::unique(pending.begin(), pending.end());
|
||||
pending.erase(u, pending.end());
|
||||
}
|
||||
|
||||
for (const auto& assignment : pending) {
|
||||
auto asgn_pos = this->m_assignments.find(assignment);
|
||||
if (asgn_pos == this->m_assignments.end()) {
|
||||
// No such ASSIGNment. Unexpected.
|
||||
continue;
|
||||
}
|
||||
|
||||
auto handler = handlers.find(asgn_pos->second.var_type());
|
||||
if (handler == handlers.end()) {
|
||||
// Unhandled variable type.
|
||||
// Unhandled/unsupported variable type.
|
||||
continue;
|
||||
}
|
||||
|
||||
context.update_assign(report_step, keyword,
|
||||
handler->second(asgn_pos->second));
|
||||
context.update_assign(assignment, handler->second(asgn_pos->second));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -607,7 +630,7 @@ namespace Opm {
|
||||
UDQContext context {
|
||||
this->function_table(), wm, std::move(create_segment_matcher), st, udq_state
|
||||
};
|
||||
this->eval_assign(report_step, sched, udq_state, context);
|
||||
this->eval_assign(report_step, sched, context);
|
||||
this->eval_define(report_step, udq_state, context);
|
||||
}
|
||||
|
||||
@@ -621,7 +644,7 @@ namespace Opm {
|
||||
UDQContext context {
|
||||
this->function_table(), wm, std::move(create_segment_matcher), st, udq_state
|
||||
};
|
||||
this->eval_assign(report_step, sched, udq_state, context);
|
||||
this->eval_assign(report_step, sched, context);
|
||||
}
|
||||
|
||||
void UDQConfig::required_summary(std::unordered_set<std::string>& summary_keys) const
|
||||
|
||||
@@ -218,11 +218,10 @@ namespace Opm {
|
||||
return this->udqft;
|
||||
}
|
||||
|
||||
void UDQContext::update_assign(const std::size_t report_step,
|
||||
const std::string& keyword,
|
||||
void UDQContext::update_assign(const std::string& keyword,
|
||||
const UDQSet& udq_result)
|
||||
{
|
||||
this->udq_state.add_assign(report_step, keyword, udq_result);
|
||||
this->udq_state.add_assign(keyword, udq_result);
|
||||
this->summary_state.update_udq(udq_result, this->udq_state.undefined_value());
|
||||
}
|
||||
|
||||
|
||||
@@ -291,9 +291,8 @@ void UDQState::add_define(std::size_t report_step, const std::string& udq_key, c
|
||||
this->add(udq_key, result);
|
||||
}
|
||||
|
||||
void UDQState::add_assign(std::size_t report_step, const std::string& udq_key, const UDQSet& result)
|
||||
void UDQState::add_assign(const std::string& udq_key, const UDQSet& result)
|
||||
{
|
||||
this->assignments[udq_key] = report_step;
|
||||
this->add(udq_key, result);
|
||||
}
|
||||
|
||||
@@ -364,7 +363,6 @@ bool UDQState::operator==(const UDQState& other) const
|
||||
&& (this->well_values == other.well_values)
|
||||
&& (this->group_values == other.group_values)
|
||||
&& (this->segment_values == other.segment_values)
|
||||
&& (this->assignments == other.assignments)
|
||||
&& (this->defines == other.defines);
|
||||
}
|
||||
|
||||
@@ -373,7 +371,6 @@ UDQState UDQState::serializationTestObject()
|
||||
UDQState st;
|
||||
st.undef_value = 78;
|
||||
st.scalar_values = {{"FU1", 100}, {"FU2", 200}};
|
||||
st.assignments = {{"GU1", 99}, {"GU2", 199}};
|
||||
st.defines = {{"DU1", 299}, {"DU2", 399}};
|
||||
|
||||
st.well_values.emplace("W1", std::unordered_map<std::string, double>{{"U1", 100}, {"U2", 200}});
|
||||
@@ -410,14 +407,6 @@ UDQState UDQState::serializationTestObject()
|
||||
return st;
|
||||
}
|
||||
|
||||
bool UDQState::assign(std::size_t report_step, const std::string& udq_key) const
|
||||
{
|
||||
auto assign_iter = this->assignments.find(udq_key);
|
||||
|
||||
return (assign_iter == this->assignments.end())
|
||||
|| (report_step > assign_iter->second);
|
||||
}
|
||||
|
||||
bool UDQState::define(const std::string& udq_key,
|
||||
const std::pair<UDQUpdate, std::size_t>& update_status) const
|
||||
{
|
||||
|
||||
@@ -2851,7 +2851,7 @@ UDQ
|
||||
const auto& udq = schedule.getUDQConfig(1);
|
||||
{
|
||||
const auto& ass = udq.assign("FUBHPP1");
|
||||
context.update_assign(1, "FUBHPP1", ass.eval());
|
||||
context.update_assign("FUBHPP1", ass.eval());
|
||||
}
|
||||
const auto& def = udq.define("WUDELTA");
|
||||
auto res = def.eval(context);
|
||||
|
||||
Reference in New Issue
Block a user