Differentiate better between UDQ ASSIGN and UDQ DEFINE

A quite typical situation is that a UDQ keyword is first initialized with UDQ
ASSIGN statement, and then subsequently a formula for updates every timestep is
entered with UDQ DEFINE:

  UDQ
     ASSIGN FU_VAR1 0 /
     DEFINE FU_VAR1 FU_VAR1 + 1 /
  /

Then the assign statement should be run once, and the define formula should be
evaluated for every subsequent timestep.
This commit is contained in:
Joakim Hove 2020-09-08 12:08:16 +02:00
parent 49460edf39
commit 5a060910a3
12 changed files with 215 additions and 78 deletions

View File

@ -42,10 +42,12 @@ public:
struct AssignRecord {
std::vector<std::string> selector;
double value;
std::size_t report_step;
bool operator==(const AssignRecord& data) const {
return selector == data.selector &&
value == data.value;
report_step == data.report_step &&
value == data.value;
}
template<class Serializer>
@ -53,19 +55,21 @@ public:
{
serializer(selector);
serializer(value);
serializer(report_step);
}
};
UDQAssign();
UDQAssign(const std::string& keyword, const std::vector<std::string>& selector, double value);
UDQAssign(const std::string& keyword, const std::vector<std::string>& selector, double value, std::size_t report_step);
static UDQAssign serializeObject();
const std::string& keyword() const;
UDQVarType var_type() const;
void add_record(const std::vector<std::string>& selector, double value);
void add_record(const std::vector<std::string>& selector, double value, std::size_t report_step);
UDQSet eval(const std::vector<std::string>& wells) const;
UDQSet eval() const;
std::size_t report_step() const;
bool operator==(const UDQAssign& data) const;

View File

@ -53,10 +53,10 @@ namespace Opm {
const std::string& unit(const std::string& key) const;
bool has_unit(const std::string& keyword) const;
bool has_keyword(const std::string& keyword) const;
void add_record(const DeckRecord& record);
void add_record(const DeckRecord& record, std::size_t report_step);
void add_unit(const std::string& keyword, const std::string& unit);
void add_assign(const std::string& quantity, const std::vector<std::string>& selector, double value);
void add_assign(const std::string& quantity, const std::vector<std::string>& selector, double value, std::size_t report_step);
void add_define(const std::string& quantity, const std::vector<std::string>& expression);
void eval(std::size_t report_step, SummaryState& st, UDQState& udq_state) const;
@ -96,6 +96,8 @@ namespace Opm {
private:
void add_node(const std::string& quantity, UDQAction action);
UDQAction action_type(const std::string& udq_key) const;
UDQParams udq_params;
UDQFunctionTable udqft;

View File

@ -42,7 +42,8 @@ namespace Opm {
std::optional<double> get_well_var(const std::string& well, const std::string& var) const;
std::optional<double> get_group_var(const std::string& group, const std::string& var) const;
void add(const std::string& key, double value);
void update(const std::string& keyword, const UDQSet& udq_result);
void update_assign(std::size_t report_step, const std::string& keyword, const UDQSet& udq_result);
void update_define(const std::string& keyword, const UDQSet& udq_result);
const UDQFunctionTable& function_table() const;
std::vector<std::string> wells() const;
std::vector<std::string> groups() const;

View File

@ -39,15 +39,19 @@ public:
double get(const std::string& key) const;
double get_group_var(const std::string& well, const std::string& var) const;
double get_well_var(const std::string& well, const std::string& var) const;
void add(const std::string& udq_key, const UDQSet& result);
void add_define(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;
std::vector<char> serialize() const;
void deserialize(const std::vector<char>& buffer);
bool operator==(const UDQState& other) const;
private:
void add(const std::string& udq_key, const UDQSet& result);
double get_wg_var(const std::string& well, const std::string& key, UDQVarType var_type) const;
double undefined_value;
std::unordered_map<std::string, UDQSet> values;
std::unordered_map<std::string, std::size_t> assignments;
};
}

View File

@ -1389,7 +1389,7 @@ Schedule::Schedule(const Deck& deck, const EclipseState& es, const ParseContext&
const auto& current = *this->udq_config.get(currentStep);
std::shared_ptr<UDQConfig> new_udq = std::make_shared<UDQConfig>(current);
for (const auto& record : keyword)
new_udq->add_record(record);
new_udq->add_record(record, currentStep);
this->udq_config.update(currentStep, new_udq);
}

View File

@ -27,11 +27,11 @@ UDQAssign::UDQAssign() :
{
}
UDQAssign::UDQAssign(const std::string& keyword, const std::vector<std::string>& selector, double value) :
UDQAssign::UDQAssign(const std::string& keyword, const std::vector<std::string>& selector, double value, std::size_t report_step) :
m_keyword(keyword),
m_var_type(UDQ::varType(keyword))
{
this->add_record(selector, value);
this->add_record(selector, value, report_step);
}
UDQAssign UDQAssign::serializeObject()
@ -39,13 +39,13 @@ UDQAssign UDQAssign::serializeObject()
UDQAssign result;
result.m_keyword = "test";
result.m_var_type = UDQVarType::CONNECTION_VAR;
result.records = {{{"test1"}, 1.0}};
result.records = {{{"test1"}, 1.0, 0}};
return result;
}
void UDQAssign::add_record(const std::vector<std::string>& selector, double value) {
this->records.push_back({selector, value});
void UDQAssign::add_record(const std::vector<std::string>& selector, double value, std::size_t report_step) {
this->records.push_back({selector, value, report_step});
}
const std::string& UDQAssign::keyword() const {
@ -56,6 +56,12 @@ UDQVarType UDQAssign::var_type() const {
return this->m_var_type;
}
std::size_t UDQAssign::report_step() const {
return this->records.back().report_step;
}
UDQSet UDQAssign::eval(const std::vector<std::string>& wells) const {
if (this->m_var_type == UDQVarType::WELL_VAR) {
UDQSet ws = UDQSet::wells(this->m_keyword, wells);

View File

@ -22,6 +22,7 @@
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQEnums.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQInput.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQState.hpp>
namespace Opm {
@ -75,13 +76,13 @@ namespace Opm {
index_iter->second.action = action;
}
void UDQConfig::add_assign(const std::string& quantity, const std::vector<std::string>& selector, double value) {
void UDQConfig::add_assign(const std::string& quantity, const std::vector<std::string>& selector, double value, std::size_t report_step) {
this->add_node(quantity, UDQAction::ASSIGN);
auto assignment = this->m_assignments.find(quantity);
if (assignment == this->m_assignments.end())
this->m_assignments.insert( std::make_pair(quantity, UDQAssign(quantity, selector, value )));
this->m_assignments.insert( std::make_pair(quantity, UDQAssign(quantity, selector, value, report_step )));
else
assignment->second.add_record(selector, value);
assignment->second.add_record(selector, value, report_step);
}
@ -113,12 +114,12 @@ namespace Opm {
keyword.
*/
if (!this->has_keyword(keyword))
this->add_assign(keyword, {}, 0);
this->add_assign(keyword, {}, 0, 0);
this->units[keyword] = unit;
}
void UDQConfig::add_record(const DeckRecord& record) {
void UDQConfig::add_record(const DeckRecord& record, std::size_t report_step) {
auto action = UDQ::actionType(record.getItem("ACTION").get<RawString>(0));
const auto& quantity = record.getItem("QUANTITY").get<std::string>(0);
const auto& data = RawString::strings( record.getItem("DATA").getData<RawString>() );
@ -132,7 +133,7 @@ namespace Opm {
if (action == UDQAction::ASSIGN) {
std::vector<std::string> selector(data.begin(), data.end() - 1);
double value = std::stod(data.back());
this->add_assign(quantity, selector, value);
this->add_assign(quantity, selector, value, report_step);
} else if (action == UDQAction::DEFINE)
this->add_define(quantity, data);
else
@ -144,13 +145,18 @@ namespace Opm {
return this->m_definitions.at(key);
}
UDQAction UDQConfig::action_type(const std::string& udq_key) const {
auto action_iter = this->input_index.find(udq_key);
return action_iter->second.action;
}
std::vector<UDQDefine> UDQConfig::definitions() const {
std::vector<UDQDefine> ret;
for (const auto& key : this->define_order)
ret.push_back(this->m_definitions.at(key));
for (const auto& key : this->define_order) {
if (this->action_type(key) == UDQAction::DEFINE)
ret.push_back(this->m_definitions.at(key));
}
return ret;
}
@ -159,7 +165,7 @@ namespace Opm {
std::vector<UDQDefine> filtered_defines;
for (const auto& key : this->define_order) {
const auto& udq_define = this->m_definitions.at(key);
if (udq_define.var_type() == var_type)
if (udq_define.var_type() == var_type && this->action_type(key) == UDQAction::DEFINE)
filtered_defines.push_back(udq_define);
}
return filtered_defines;
@ -210,16 +216,16 @@ namespace Opm {
std::vector<UDQAssign> UDQConfig::assignments(UDQVarType var_type) const {
std::vector<UDQAssign> filtered_defines;
std::vector<UDQAssign> filtered_assigns;
for (const auto& index_pair : this->input_index) {
if (index_pair.second.action == UDQAction::ASSIGN) {
const std::string& key = index_pair.first;
const auto& udq_define = this->m_assignments.at(key);
if (udq_define.var_type() == var_type)
filtered_defines.push_back(udq_define);
const std::string& key = index_pair.first;
const auto& assign_iter = this->m_assignments.find(key);
if (assign_iter != this->m_assignments.end()) {
if (assign_iter->second.var_type() == var_type)
filtered_assigns.push_back(assign_iter->second);
}
}
return filtered_defines;
return filtered_assigns;
}
@ -295,38 +301,44 @@ namespace Opm {
UDQContext context(func_table, st, udq_state);
for (const auto& assign : this->assignments(UDQVarType::WELL_VAR)) {
auto ws = assign.eval(st.wells());
context.update(assign.keyword(), ws);
st.update_udq(ws, undefined_value);
if (udq_state.assign(report_step, assign.keyword())) {
auto ws = assign.eval(st.wells());
context.update_assign(report_step, assign.keyword(), ws);
st.update_udq(ws, undefined_value);
}
}
for (const auto& def : this->definitions(UDQVarType::WELL_VAR)) {
auto ws = def.eval(context);
context.update(def.keyword(), ws);
context.update_define(def.keyword(), ws);
st.update_udq(ws, undefined_value);
}
for (const auto& assign : this->assignments(UDQVarType::GROUP_VAR)) {
auto ws = assign.eval(st.groups());
context.update(assign.keyword(), ws);
st.update_udq(ws, undefined_value);
if (udq_state.assign(report_step, assign.keyword())) {
auto ws = assign.eval(st.groups());
context.update_assign(report_step, assign.keyword(), ws);
st.update_udq(ws, undefined_value);
}
}
for (const auto& def : this->definitions(UDQVarType::GROUP_VAR)) {
auto ws = def.eval(context);
context.update(def.keyword(), ws);
context.update_define(def.keyword(), ws);
st.update_udq(ws, undefined_value);
}
for (const auto& assign : this->assignments(UDQVarType::FIELD_VAR)) {
auto ws = assign.eval();
context.update(assign.keyword(), ws);
st.update_udq(ws, undefined_value);
if (udq_state.assign(assign.report_step(), assign.keyword())) {
auto ws = assign.eval();
context.update_assign(report_step, assign.keyword(), ws);
st.update_udq(ws, undefined_value);
}
}
for (const auto& def : this->definitions(UDQVarType::FIELD_VAR)) {
auto field_udq = def.eval(context);
context.update(def.keyword(), field_udq);
context.update_define(def.keyword(), field_udq);
st.update_udq(field_udq, undefined_value);
}
}

View File

@ -62,9 +62,6 @@ bool is_udq(const std::string& key) {
this->add("TIME", 0.0);
}
void UDQContext::update(const std::string& keyword, const UDQSet& udq_result) {
this->udq_state.add(keyword, udq_result);
}
void UDQContext::add(const std::string& key, double value) {
this->values[key] = value;
@ -116,4 +113,12 @@ bool is_udq(const std::string& key) {
const UDQFunctionTable& UDQContext::function_table() const {
return this->udqft;
}
void UDQContext::update_assign(std::size_t report_step, const std::string& keyword, const UDQSet& udq_result) {
this->udq_state.add_assign(report_step, keyword, udq_result);
}
void UDQContext::update_define(const std::string& keyword, const UDQSet& udq_result) {
this->udq_state.add_define(keyword, udq_result);
}
}

View File

@ -254,7 +254,7 @@ UDQSet UDQDefine::eval(UDQContext& context) const {
std::string msg = "Invalid runtime type conversion detected when evaluating UDQ";
throw std::invalid_argument(msg);
}
context.update(this->keyword(), res);
context.update_define(this->keyword(), res);
if (res.var_type() == UDQVarType::SCALAR) {
/*

View File

@ -82,6 +82,15 @@ void UDQState::add(const std::string& udq_key, const UDQSet& result) {
res_iter->second = result;
}
void UDQState::add_define(const std::string& udq_key, const UDQSet& result) {
this->add(udq_key, result);
}
void UDQState::add_assign(std::size_t report_step, const std::string& udq_key, const UDQSet& result) {
this->assignments[udq_key] = report_step;
this->add(udq_key, result);
}
double UDQState::get(const std::string& key) const {
if (!is_udq(key))
throw std::logic_error("Key is not a UDQ variable:" + key);
@ -125,6 +134,16 @@ bool UDQState::operator==(const UDQState& other) const {
this->values == other.values;
}
bool UDQState::assign(std::size_t report_step, const std::string& udq_key) const {
auto assign_iter = this->assignments.find(udq_key);
if (assign_iter == this->assignments.end())
return true;
else
return report_step > assign_iter->second;
}
std::vector<char> UDQState::serialize() const {
Serializer ser;
ser.put(this->undefined_value);
@ -133,7 +152,8 @@ std::vector<char> UDQState::serialize() const {
ser.put( set_pair.first );
set_pair.second.serialize( ser );
}
return std::move(ser.buffer);
ser.put(this->assignments);
return ser.buffer;
}
@ -151,6 +171,7 @@ void UDQState::deserialize(const std::vector<char>& buffer) {
this->values.insert( std::make_pair(key, udq_set) );
}
}
this->assignments = ser.get<std::string, std::size_t>();
}
}

View File

@ -942,9 +942,9 @@ BOOST_AUTO_TEST_CASE(UDQ_SET_DIV) {
BOOST_AUTO_TEST_CASE(UDQASSIGN_TEST) {
UDQAssign as1("WUPR", {}, 1.0);
UDQAssign as2("WUPR", {"P*"}, 2.0);
UDQAssign as3("WUPR", {"P1"}, 4.0);
UDQAssign as1("WUPR", {}, 1.0, 1);
UDQAssign as2("WUPR", {"P*"}, 2.0, 2);
UDQAssign as3("WUPR", {"P1"}, 4.0, 3);
std::vector<std::string> ws1 = {"P1", "P2", "I1", "I2"};
auto res1 = as1.eval(ws1);
@ -1455,7 +1455,7 @@ BOOST_AUTO_TEST_CASE(UDQ_USAGE) {
BOOST_CHECK_EQUAL( usage.IUAD_size(), 0 );
UDAValue uda1("WUX");
conf.add_assign(uda1.get<std::string>(), {}, 100);
conf.add_assign(uda1.get<std::string>(), {}, 100, 0);
usage.update(conf, uda1, "W1", UDAControl::WCONPROD_ORAT);
BOOST_CHECK_EQUAL( usage.IUAD_size(), 1 );
@ -1842,12 +1842,12 @@ BOOST_AUTO_TEST_CASE(UDQSTATE) {
BOOST_CHECK_THROW(st.get("FUPR"), std::out_of_range);
auto fxpr = UDQSet::scalar("FXPR", 100);
BOOST_CHECK_THROW(st.add("FXPR", fxpr), std::logic_error);
BOOST_CHECK_THROW(st.add_define("FXPR", fxpr), std::logic_error);
BOOST_CHECK_THROW(st.get_well_var("OP1", "WUPR"), std::out_of_range);
auto fupr = UDQSet::scalar("FUPR", 100);
st.add("FUPR", fupr);
st.add_define("FUPR", fupr);
// This is not a well quantity
BOOST_CHECK_THROW(st.get_well_var("OP1", "FUPR"), std::logic_error);
@ -1856,7 +1856,7 @@ BOOST_AUTO_TEST_CASE(UDQSTATE) {
auto wupr = UDQSet::wells("WUPR", {"P1", "P2"});
wupr.assign("P1", 75);
st.add("WUPR", wupr);
st.add_define("WUPR", wupr);
BOOST_CHECK(st.has_well_var("P1", "WUPR"));
// We have a well P2 - but we have not assigned a value to it!
@ -1946,6 +1946,8 @@ DEFINE WUGASRA 750000 - WGLIR '*' /
BOOST_AUTO_TEST_CASE(UDQ_UNDEFINED) {
std::string deck_string = R"(
SCHEDULE
-- udq #2
UDQ
----XX xxxx xxx
@ -2129,15 +2131,13 @@ DEFINE FU_VAR91 GOPR TEST /
st.update_well_var("W1", "WGLIR", 1);
st.update_well_var("W2", "WGLIR", 2);
st.update_well_var("W3", "WGLIR", 3);
st.update_group_var("TEST", "GOPR", 1);
udq.eval(0, st, udq_state);
// The current testcase has some ordering & defined / undefined issues which
}
BOOST_AUTO_TEST_CASE(UDQ_KEY_ERROR) {
std::string deck_string = R"(
-- udq #2
@ -2156,3 +2156,85 @@ UDQ
BOOST_CHECK_THROW(udq.eval(0, st, udq_state), std::out_of_range);
}
BOOST_AUTO_TEST_CASE(UDQ_ASSIGN) {
std::string deck_string = R"(
-- udq #2
SCHEDULE
UDQ
ASSIGN FU_VAR1 5 /
DEFINE FU_VAR1 FU_VAR1 + 5 /
/
)";
auto schedule = make_schedule(deck_string);
const auto& udq = schedule.getUDQConfig(0);
auto undefined_value = udq.params().undefinedValue();
UDQState udq_state(undefined_value);
SummaryState st(std::chrono::system_clock::now());
udq.eval(0, st, udq_state);
BOOST_CHECK_EQUAL(st.get("FU_VAR1"), 10);
}
BOOST_AUTO_TEST_CASE(UDQ_ASSIGN_REASSIGN) {
std::string deck_string = R"(
-- udq #2
SCHEDULE
UDQ
ASSIGN FU_VAR1 0 /
DEFINE FU_VAR1 FU_VAR1 + 1 /
/
TSTEP
1 1 1 1 1 /
UDQ
ASSIGN FU_VAR1 0 /
DEFINE FU_VAR1 FU_VAR1 + 1 /
/
TSTEP
1 1 1 1 1 /
UDQ
ASSIGN FU_VAR1 0 /
/
TSTEP
1 1 1 1 1 /
)";
auto schedule = make_schedule(deck_string);
UDQState udq_state(0);
SummaryState st(std::chrono::system_clock::now());
// Counting: 1,2,3,4,5
for (std::size_t report_step = 0; report_step < 5; report_step++) {
const auto& udq = schedule.getUDQConfig(report_step);
udq.eval(report_step, st, udq_state);
auto fu_var1 = st.get("FU_VAR1");
BOOST_CHECK_EQUAL(fu_var1, report_step + 1);
}
// Reset to zero and count: 1,2,3,4,5
for (std::size_t report_step = 5; report_step < 10; report_step++) {
const auto& udq = schedule.getUDQConfig(report_step);
udq.eval(report_step, st, udq_state);
auto fu_var1 = st.get("FU_VAR1");
BOOST_CHECK_EQUAL(fu_var1, report_step - 4);
}
// Reset to zero and stay there.
for (std::size_t report_step = 10; report_step < 15; report_step++) {
const auto& udq = schedule.getUDQConfig(report_step);
udq.eval(report_step, st, udq_state);
auto fu_var1 = st.get("FU_VAR1");
BOOST_CHECK_EQUAL(fu_var1, 0);
}
}

View File

@ -87,32 +87,32 @@ Opm::UDQSet make_udq_set(const std::string& name, Opm::UDQVarType var_type, cons
{
auto state = Opm::UDQState{0};
state.add("WUOPRL", make_udq_set("WUOPRL",
Opm::UDQVarType::WELL_VAR,
{"PROD1", "PROD2", "WINJ1", "WINJ2"},
{210, 211, 212, 213}));
state.add_define("WUOPRL", make_udq_set("WUOPRL",
Opm::UDQVarType::WELL_VAR,
{"PROD1", "PROD2", "WINJ1", "WINJ2"},
{210, 211, 212, 213}));
state.add("WUOPRU", make_udq_set("WUOPRU",
Opm::UDQVarType::WELL_VAR,
{"PROD1", "PROD2", "WINJ1", "WINJ2"},
{220, 221, 222, 223}));
state.add_define("WUOPRU", make_udq_set("WUOPRU",
Opm::UDQVarType::WELL_VAR,
{"PROD1", "PROD2", "WINJ1", "WINJ2"},
{220, 221, 222, 223}));
state.add("WULPRL", make_udq_set("WULPRL",
Opm::UDQVarType::WELL_VAR,
{"PROD1", "PROD2", "WINJ1", "WINJ2"},
{230, 231, 232, 233}));
state.add_define("WULPRL", make_udq_set("WULPRL",
Opm::UDQVarType::WELL_VAR,
{"PROD1", "PROD2", "WINJ1", "WINJ2"},
{230, 231, 232, 233}));
state.add("WULPRU", make_udq_set("WULPRU",
Opm::UDQVarType::WELL_VAR,
{"PROD1", "PROD2", "WINJ1", "WINJ2"},
{160, 161, 162, 163}));
state.add_define("WULPRU", make_udq_set("WULPRU",
Opm::UDQVarType::WELL_VAR,
{"PROD1", "PROD2", "WINJ1", "WINJ2"},
{160, 161, 162, 163}));
state.add("GUOPRU", make_udq_set("GUOPRU",
Opm::UDQVarType::GROUP_VAR,
{"WGRP1", "WGRP2", "GRP1"},
{360, 361, 362}));
state.add_define("GUOPRU", make_udq_set("GUOPRU",
Opm::UDQVarType::GROUP_VAR,
{"WGRP1", "WGRP2", "GRP1"},
{360, 361, 362}));
state.add("FULPR", Opm::UDQSet::scalar("FULPR", 460));
state.add_define("FULPR", Opm::UDQSet::scalar("FULPR", 460));
return state;
}