Maintain insert order in UDQINput

This commit is contained in:
Joakim Hove 2019-06-24 16:04:48 +02:00
parent 2fb64e21d9
commit 11fe1d6e03
6 changed files with 264 additions and 65 deletions

View File

@ -29,19 +29,30 @@
namespace Opm {
class UDQAssign{
public:
/*
If the same keyword is assigned several times the different assignment
records are assembled in one UDQAssign instance. This is an attempt to
support restart in a situation where a full UDQ ASSIGN statement can be
swapped with a UDQ DEFINE statement.
*/
struct AssignRecord {
std::vector<std::string> selector;
double value;
};
UDQAssign(const std::string& keyword, const std::vector<std::string>& selector, double value);
const std::string& keyword() const;
double value() const;
UDQVarType var_type() const;
const std::vector<std::string>& selector() const;
void add_record(const std::vector<std::string>& selector, double value);
UDQSet eval(const std::vector<std::string>& wells) const;
private:
std::string m_keyword;
UDQVarType m_var_type;
std::vector<std::string> m_selector;
double m_value;
std::vector<AssignRecord> records;
};
}

View File

@ -97,7 +97,8 @@ enum class UDQAction {
ASSIGN,
DEFINE,
UNITS,
UPDATE};
UPDATE
};

View File

@ -30,6 +30,7 @@
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQEnums.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQParams.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQFunctionTable.hpp>
#include <opm/parser/eclipse/EclipseState/Util/OrderedMap.hpp>
namespace Opm {
@ -46,20 +47,60 @@ namespace Opm {
void add_record(const DeckRecord& record);
void assign_unit(const std::string& keyword, const std::string& unit);
const std::vector<UDQDefine>& definitions() const;
std::vector<UDQDefine> definitions() const;
std::vector<UDQDefine> definitions(UDQVarType var_type) const;
const std::vector<UDQAssign>& assignments() const;
/*
The input_definitions() function is written to supply the information
needed when writing the restart file. The return value is a list of
pairs, where the first element in the pair is the index in the deck
for a particular UDQ keyword, and then the corresponding keyword.
Assume a deck keyword which looks like this:
UDQ
ASSIGN WUX 10 /
UNITS WUX 'BARSA' /
DEFINE WUPR SUM(WOPR) * 0.75 /
DEFINE FUCK MAX(WOPR) * 1.25 /
ASSIGN FUX 100 /
DEFINE BUPR ?? /
/
Then the return value from input_definitions() will be:
{{1, UDQDefine("WUPR")},
{2, UDQDefine("FUCK")},
{4, UDQDefine("BUPR")}
Where the the numerical index is the index in a fictious vector
consisting of only the ASSIGN and DEFINE keywords, in input order.
*/
std::vector<std::pair<size_t, UDQDefine>> input_definitions() const;
std::vector<UDQAssign> assignments() const;
std::vector<UDQAssign> assignments(UDQVarType var_type) const;
const UDQParams& params() const;
const UDQFunctionTable& function_table() const;
private:
UDQParams udq_params;
UDQFunctionTable udqft;
std::vector<UDQDefine> m_definitions;
std::vector<UDQAssign> m_assignments;
/*
The choices of datastructures are strongly motivated by the
constraints imposed by the Eclipse formatted restart files; for
writing restart files it is essential to keep meticolous control over
the ordering of the keywords. In this class the ordering is mainly
maintained by the input_index map which keeps track of the insert
order of each keyword, and whether the keyword is currently DEFINE'ed
or ASSIGN'ed.
*/
std::unordered_map<std::string, UDQDefine> m_definitions;
std::unordered_map<std::string, UDQAssign> m_assignments;
std::unordered_map<std::string, std::string> units;
std::unordered_set<std::string> keywords;
OrderedMap<std::string, std::pair<size_t, UDQAction>> input_index;
};
}

View File

@ -24,36 +24,35 @@ namespace Opm {
UDQAssign::UDQAssign(const std::string& keyword, const std::vector<std::string>& selector, double value) :
m_keyword(keyword),
m_var_type(UDQ::varType(keyword)),
m_selector(selector),
m_value(value)
{}
m_var_type(UDQ::varType(keyword))
{
this->add_record(selector, value);
}
void UDQAssign::add_record(const std::vector<std::string>& selector, double value) {
this->records.push_back({selector, value});
}
const std::string& UDQAssign::keyword() const {
return this->m_keyword;
}
const std::vector<std::string>& UDQAssign::selector() const {
return this->m_selector;
}
double UDQAssign::value() const {
return this->m_value;
}
UDQVarType UDQAssign::var_type() const {
return this->m_var_type;
}
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);
UDQSet ws = UDQSet::wells(this->m_keyword, wells);
if (this->m_selector.empty())
ws.assign(this->m_value);
else
ws.assign(this->m_selector[0], this->m_value);
for (const auto& record : this->records) {
const auto& selector = record.selector;
double value = record.value;
if (selector.empty())
ws.assign(value);
else
ws.assign(selector[0], value);
}
return ws;
}

View File

@ -45,57 +45,105 @@ namespace Opm {
return this->udq_params;
}
void UDQInput::add_record(const DeckRecord& record) {
auto action = UDQ::actionType(record.getItem("ACTION").get<std::string>(0));
const auto& quantity = record.getItem("QUANTITY").get<std::string>(0);
const auto& data = record.getItem("DATA").getData<std::string>();
if (action == UDQAction::UPDATE)
throw std::invalid_argument("The UDQ action UPDATE is not yet implemented in opm/flow");
if (action == UDQAction::UNITS)
this->assign_unit( quantity, data[0] );
else if (action == UDQAction::ASSIGN) {
std::vector<std::string> selector(data.begin(), data.end() - 1);
double value = std::stod(data.back());
this->m_assignments.emplace_back( quantity, selector, value );
} else
this->m_definitions.emplace_back(this->udq_params, quantity, data);
this->keywords.insert(quantity);
else {
auto index_iter = this->input_index.find(quantity);
if (this->input_index.find(quantity) == this->input_index.end())
this->input_index[quantity] = std::make_pair(this->input_index.size(), action);
else
index_iter->second.second = action;
if (action == UDQAction::ASSIGN) {
std::vector<std::string> selector(data.begin(), data.end() - 1);
double value = std::stod(data.back());
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 )));
else
assignment->second.add_record(selector, value);
} else if (action == UDQAction::DEFINE)
this->m_definitions.insert( std::make_pair(quantity, UDQDefine(this->udq_params, quantity, data)));
else
throw std::runtime_error("Internal error - should not be here");
}
}
const std::vector<UDQDefine>& UDQInput::definitions() const {
return this->m_definitions;
std::vector<UDQDefine> UDQInput::definitions() const {
std::vector<UDQDefine> ret;
for (const auto& index_pair : this->input_index) {
if (index_pair.second.second == UDQAction::DEFINE) {
const std::string& key = index_pair.first;
ret.push_back(this->m_definitions.at(key));
}
}
return ret;
}
std::vector<UDQDefine> UDQInput::definitions(UDQVarType var_type) const {
std::vector<UDQDefine> filtered_defines;
std::copy_if(this->m_definitions.begin(),
this->m_definitions.end(),
std::back_inserter(filtered_defines),
[&var_type](const UDQDefine& def) { return def.var_type() == var_type; });
for (const auto& index_pair : this->input_index) {
if (index_pair.second.second == UDQAction::DEFINE) {
const std::string& key = index_pair.first;
const auto& udq_define = this->m_definitions.at(key);
if (udq_define.var_type() == var_type)
filtered_defines.push_back(udq_define);
}
}
return filtered_defines;
}
const std::vector<UDQAssign>& UDQInput::assignments() const {
return this->m_assignments;
std::vector<std::pair<size_t, UDQDefine>> UDQInput::input_definitions() const {
std::vector<std::pair<size_t, UDQDefine>> res;
for (const auto& index_pair : this->input_index) {
if (index_pair.second.second == UDQAction::DEFINE) {
const std::string& key = index_pair.first;
res.emplace_back(index_pair.second.first, this->m_definitions.at(key));
}
}
return res;
}
std::vector<UDQAssign> UDQInput::assignments() const {
std::vector<UDQAssign> ret;
for (const auto& index_pair : this->input_index) {
if (index_pair.second.second == UDQAction::ASSIGN) {
const std::string& key = index_pair.first;
ret.push_back(this->m_assignments.at(key));
}
}
return ret;
}
std::vector<UDQAssign> UDQInput::assignments(UDQVarType var_type) const {
std::vector<UDQAssign> filtered_assignments;
std::copy_if(this->m_assignments.begin(),
this->m_assignments.end(),
std::back_inserter(filtered_assignments),
[&var_type](const UDQAssign& assignment) { return assignment.var_type() == var_type; });
return filtered_assignments;
std::vector<UDQAssign> filtered_defines;
for (const auto& index_pair : this->input_index) {
if (index_pair.second.second == 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);
}
}
return filtered_defines;
}
const std::string& UDQInput::unit(const std::string& key) const {
const auto pair_ptr = this->units.find(key);
if (pair_ptr == this->units.end())
@ -121,10 +169,25 @@ namespace Opm {
return (this->units.count(keyword) > 0);
}
bool UDQInput::has_keyword(const std::string& keyword) const {
return (this->keywords.count(keyword) > 0);
if (this->m_assignments.count(keyword) > 0)
return true;
if (this->m_definitions.count(keyword) > 0)
return true;
/*
That a keyword is mentioned with UNITS is enough to consider it
as a keyword which is present.
*/
if (this->units.count(keyword) > 0)
return true;
return false;
}
const UDQFunctionTable& UDQInput::function_table() const {
return this->udqft;
}

View File

@ -48,6 +48,7 @@ Schedule make_schedule(const std::string& input) {
return Schedule(deck, grid , eclipseProperties, runspec);
}
BOOST_AUTO_TEST_CASE(MIX_SCALAR) {
UDQFunctionTable udqft;
UDQParams udqp;
@ -276,7 +277,6 @@ BOOST_AUTO_TEST_CASE(ENUM_CONVERSION) {
}
BOOST_AUTO_TEST_CASE(UDQ_KEWYORDS) {
const std::string input = R"(
RUNSPEC
@ -418,18 +418,18 @@ ASSIGN WU2 8.0 /
const auto& assignments = udq.assignments();
const auto& ass0 = assignments[0];
const auto& ass1 = assignments[1];
auto w1 = ass0.eval({"P1", "P2", "P12"});
auto w2 = ass1.eval({"P1", "P2", "P12"});
BOOST_CHECK_EQUAL(w1.name(), "WU1");
BOOST_CHECK_EQUAL(w2.name(), "WU2");
BOOST_CHECK_EQUAL( w1["P12"].value(), 4.0 );
BOOST_CHECK_EQUAL( w1["P1"].defined(), false );
BOOST_CHECK_EQUAL( w1["P2"].defined(), false );
BOOST_CHECK_EQUAL(ass0.keyword(), "WU1");
BOOST_CHECK_EQUAL(ass1.keyword(), "WU2");
BOOST_CHECK_EQUAL(ass0.value(), 4.0 );
BOOST_CHECK_EQUAL(ass1.value(), 8.0 );
std::vector<std::string> sel0 = {"P12"};
std::vector<std::string> sel1 = {};
BOOST_CHECK_EQUAL_COLLECTIONS(ass0.selector().begin(), ass0.selector().end(), sel0.begin(), sel0.end());
BOOST_CHECK_EQUAL_COLLECTIONS(ass1.selector().begin(), ass1.selector().end(), sel1.begin(), sel1.end());
BOOST_CHECK_EQUAL( w2["P12"].value(), 8.0 );
BOOST_CHECK_EQUAL( w2["P1"].value(), 8.0 );
BOOST_CHECK_EQUAL( w2["P2"].value(), 8.0 );
}
@ -1144,3 +1144,87 @@ BOOST_AUTO_TEST_CASE(UDA_VALUE_DIM) {
value0.set_dim( dim );
BOOST_CHECK_EQUAL( value0.get<double>(), 10);
}
BOOST_AUTO_TEST_CASE(UDQ_INPUT_BASIC) {
std::string deck_string = R"(
SCHEDULE
UDQ
ASSIGN WUBHP1 11 /
ASSIGN WUOPR 20 /
ASSIGN WUBHP2 P2 12 /
UNITS WUBHP 'BARSA' /
UNITS WUOPR 'SM3/DAY' /
DEFINE WUWCT WWPR / (WWPR + WOPR) /
UNITS WUWCT '1' /
DEFINE FUOPR SUM(WOPR) /
UNITS FUOPR 'SM3/DAY' /
UNITS FUXXX 'SM3/DAY' /
/
UDQ
ASSIGN WUBHPX P2 12 /
DEFINE FUOPRX SUM(WOPR) /
/
)";
auto schedule = make_schedule(deck_string);
const auto& udq = schedule.getUDQConfig(0);
const auto& def_input = udq.input_definitions();
const auto& def = udq.definitions();
BOOST_CHECK_EQUAL(def_input.size(), 3);
for (std::size_t i = 0; i < def_input.size(); i++)
BOOST_CHECK_EQUAL(def[i].input_string(), def_input[i].second.input_string());
BOOST_CHECK_EQUAL(def_input[0].first, 3);
BOOST_CHECK_EQUAL(def_input[1].first, 4);
BOOST_CHECK_EQUAL(def_input[2].first, 6);
BOOST_CHECK_EQUAL(def[0].keyword(), "WUWCT");
BOOST_CHECK_EQUAL(def[1].keyword(), "FUOPR");
BOOST_CHECK_EQUAL(def[2].keyword(), "FUOPRX");
BOOST_CHECK( udq.has_keyword("FUXXX") );
}
BOOST_AUTO_TEST_CASE(UDQ_INPUT_OVERWRITE) {
std::string deck_string = R"(
SCHEDULE
UDQ
ASSIGN WUBHP1 11 /
ASSIGN WUOPR 20 /
ASSIGN WUBHP2 P2 12 /
UNITS WUBHP 'BARSA' /
UNITS WUOPR 'SM3/DAY' /
DEFINE WUWCT WWPR / (WWPR + WOPR) /
UNITS WUWCT '1' /
DEFINE FUOPR SUM(WOPR) /
UNITS FUOPR 'SM3/DAY' /
/
UDQ
DEFINE WUBHP1 SUM(WOPR) /
/
)";
auto schedule = make_schedule(deck_string);
const auto& udq = schedule.getUDQConfig(0);
const auto& def_input = udq.input_definitions();
const auto& def = udq.definitions();
BOOST_CHECK_EQUAL(def_input.size(), 3);
for (std::size_t i = 0; i < def_input.size(); i++)
BOOST_CHECK_EQUAL(def[i].input_string(), def_input[i].second.input_string());
BOOST_CHECK_EQUAL(def_input[0].first, 0);
BOOST_CHECK_EQUAL(def_input[1].first, 3);
BOOST_CHECK_EQUAL(def_input[2].first, 4);
}