Merge pull request #979 from joakim-hove/actionx-dynamic

Convert Schedule::Actions to a DynamicState<Actions>
This commit is contained in:
Atgeirr Flø Rasmussen
2019-09-04 14:25:06 +02:00
committed by GitHub
15 changed files with 331 additions and 24 deletions

View File

@@ -532,6 +532,7 @@ if(ENABLE_ECL_INPUT)
opm/parser/eclipse/EclipseState/Schedule/Action/Actdims.hpp
opm/parser/eclipse/EclipseState/Schedule/Action/Actions.hpp
opm/parser/eclipse/EclipseState/Schedule/Action/ActionX.hpp
opm/parser/eclipse/EclipseState/Schedule/Action/Condition.hpp
opm/parser/eclipse/EclipseState/Schedule/ArrayDimChecker.hpp
opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp
opm/parser/eclipse/EclipseState/Schedule/VFPInjTable.hpp

View File

@@ -57,7 +57,7 @@ void msim::run(Schedule& schedule, EclipseIO& io, bool report_only) {
void msim::post_step(Schedule& schedule, const SummaryState& st, data::Solution& /* sol */, data::Wells& /* well_data */, size_t report_step) const {
const auto& actions = schedule.actions();
const auto& actions = schedule.actions(report_step);
if (actions.empty())
return;

View File

@@ -43,6 +43,8 @@ namespace Opm {
void setLocation(const std::pair<const std::string&, std::size_t>& location);
const std::string& getFileName() const;
int getLineNumber() const;
std::pair<std::string, std::size_t> location() const;
size_t size() const;
void addRecord(DeckRecord&& record);

View File

@@ -13,7 +13,7 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
but eliminates the memory saving DynamicState is intended to enable. You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/

View File

@@ -28,6 +28,9 @@
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Action/ActionAST.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Action/ActionResult.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Action/Condition.hpp>
namespace Opm {
class DeckKeyword;
@@ -76,6 +79,13 @@ public:
std::vector<DeckKeyword>::const_iterator begin() const;
std::vector<DeckKeyword>::const_iterator end() const;
static bool valid_keyword(const std::string& keyword);
/*
The conditions() and keyword_strings() methods, and their underlying data
members are only present to support writing formatted restart files.
*/
const std::vector<Condition>& conditions() const;
std::vector<std::string> keyword_strings() const;
private:
std::string m_name;
size_t m_max_run = 0;
@@ -84,6 +94,7 @@ private:
std::vector<DeckKeyword> keywords;
Action::AST condition;
std::vector<Condition> m_conditions;
mutable size_t run_count = 0;
mutable std::time_t last_run = 0;
};

View File

@@ -23,7 +23,7 @@
#include <string>
#include <ctime>
#include <map>
#include <vector>
#include <opm/parser/eclipse/EclipseState/Schedule/Action/ActionX.hpp>
@@ -42,10 +42,14 @@ public:
bool empty() const;
void add(const ActionX& action);
bool ready(std::time_t sim_time) const;
Action::ActionX& at(const std::string& name);
const ActionX& get(const std::string& name) const;
const ActionX& get(std::size_t index) const;
std::vector<const ActionX *> pending(std::time_t sim_time) const;
std::vector<ActionX>::const_iterator begin() const;
std::vector<ActionX>::const_iterator end() const;
private:
std::map<std::string, ActionX> actions;
std::vector<ActionX> actions;
};
}
}

View File

@@ -0,0 +1,66 @@
/*
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACTIONX_CONDITION_HPP
#define ACTIONX_CONDITION_HPP
#include <string>
namespace Opm {
namespace Action {
class Condition {
public:
enum class Logical {
AND,
OR,
END
};
enum class Comparator {
EQUAL,
GREATER,
LESS,
INVALID
};
void add_token(const std::string& token) {
if (!this->expression.empty())
this->expression += " ";
this->expression += token;
}
Condition(const std::string& quant) :
quantity(quant)
{}
std::string expression;
std::string quantity;
Logical logic = Logical::END;
Comparator cmp = Comparator::INVALID;
};
}
}
#endif

View File

@@ -183,7 +183,7 @@ namespace Opm
const WellTestConfig& wtestConfig(size_t timestep) const;
const WListManager& getWListManager(size_t timeStep) const;
const UDQConfig& getUDQConfig(size_t timeStep) const;
const Action::Actions& actions() const;
const Action::Actions& actions(std::size_t timeStep) const;
void evalAction(const SummaryState& summary_state, size_t timeStep);
GTNode groupTree(std::size_t report_step) const;
@@ -235,9 +235,9 @@ namespace Opm
DynamicState<std::shared_ptr<UDQActive>> udq_active;
DynamicState<std::shared_ptr<GuideRateModel>> guide_rate_model;
DynamicState<WellProducer::ControlModeEnum> global_whistctl_mode;
DynamicState<std::shared_ptr<Action::Actions>> m_actions;
RFTConfig rft_config;
Action::Actions m_actions;
std::map<std::string,Events> well_events;
DynamicState<int> m_nupcol;
@@ -250,6 +250,7 @@ namespace Opm
void addWellToGroup( const std::string& group_name, const std::string& well_name , size_t timeStep);
void iterateScheduleSection(const ParseContext& parseContext , ErrorGuard& errors, const SCHEDULESection& , const EclipseGrid& grid,
const Eclipse3DProperties& eclipseProperties);
void addACTIONX(const Action::ActionX& action, std::size_t currentStep);
void addGroupToGroup( const std::string& parent_group, const std::string& child_group, size_t timeStep);
void addGroupToGroup( const std::string& parent_group, const Group2& child_group, size_t timeStep);
void addGroup(const std::string& groupName , size_t timeStep, const UnitSystem& unit_system);

View File

@@ -60,6 +60,10 @@ namespace Opm {
return m_lineNumber;
}
std::pair<std::string, std::size_t> DeckKeyword::location() const {
return std::make_pair( this->getFileName(), this->getLineNumber() );
}
void DeckKeyword::setDataKeyword(bool isDataKeyword_) {
m_isDataKeyword = isDataKeyword_;
}

View File

@@ -31,7 +31,7 @@ Parser::Parser(const std::vector<std::string>& tokens_arg) :
{}
TokenType Parser::get_type(const std::string& arg) const {
TokenType Parser::get_type(const std::string& arg) {
std::string lower_arg = arg;
std::for_each(lower_arg.begin(),
lower_arg.end(),

View File

@@ -50,11 +50,11 @@ struct ParseNode {
class Parser {
public:
static Action::ASTNode parse(const std::vector<std::string>& tokens);
static TokenType get_type(const std::string& arg);
private:
explicit Parser(const std::vector<std::string>& tokens);
TokenType get_type(const std::string& arg) const;
ParseNode current() const;
ParseNode next();
size_t pos() const;

View File

@@ -17,12 +17,14 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sstream>
#include <unordered_set>
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Action/ActionX.hpp>
#include "ActionValue.hpp"
#include "ActionParser.hpp"
namespace Opm {
namespace Action {
@@ -57,8 +59,39 @@ ActionX::ActionX(const DeckKeyword& kw, std::time_t start_time) :
std::vector<std::string> tokens;
for (size_t record_index = 1; record_index < kw.size(); record_index++) {
const auto& record = kw.getRecord(record_index);
for (const auto& token : record.getItem("CONDITION").getData<std::string>())
const auto& cond_tokens = record.getItem("CONDITION").getData<std::string>();
Condition cond(cond_tokens[0]);
for (const auto& token : cond_tokens) {
tokens.push_back(token);
cond.add_token(token);
{
auto token_type = Parser::get_type(token);
if (token_type == TokenType::op_eq)
cond.cmp = Condition::Comparator::EQUAL;
if (token_type == TokenType::op_gt)
cond.cmp = Condition::Comparator::GREATER;
if (token_type == TokenType::op_lt)
cond.cmp = Condition::Comparator::LESS;
}
}
{
auto token_type = Parser::get_type(cond_tokens.back());
if (token_type == TokenType::op_and)
cond.logic = Condition::Logical::AND;
else if (token_type == TokenType::op_or)
cond.logic = Condition::Logical::OR;
}
if (cond.cmp == Condition::Comparator::INVALID) {
const auto& location = kw.location();
throw std::invalid_argument("Could not determine comparison type for ACTIONX keyword at " + location.first + ":" + std::to_string(location.second));
}
this->m_conditions.push_back(std::move(cond));
}
this->condition = Action::AST(tokens);
}
@@ -110,5 +143,39 @@ std::vector<DeckKeyword>::const_iterator ActionX::end() const {
return this->keywords.end();
}
std::vector<std::string> ActionX::keyword_strings() const {
std::vector<std::string> keyword_strings;
std::string keyword_string;
{
std::stringstream ss;
for (const auto& kw : this->keywords)
ss << kw;
keyword_string = ss.str();
}
std::size_t offset = 0;
while (true) {
auto eol_pos = keyword_string.find('\n', offset);
if (eol_pos == std::string::npos)
break;
if (eol_pos > offset)
keyword_strings.push_back(keyword_string.substr(offset, eol_pos - offset));
offset = eol_pos + 1;
}
keyword_strings.push_back("ENDACTIO");
return keyword_strings;
}
const std::vector<Condition>& ActionX::conditions() const {
return this->m_conditions;
}
}
}

View File

@@ -16,6 +16,7 @@
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <opm/parser/eclipse/EclipseState/Schedule/Action/Actions.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Action/ActionX.hpp>
@@ -34,21 +35,34 @@ bool Actions::empty() const {
void Actions::add(const ActionX& action) {
auto iter = this->actions.find(action.name());
if (iter != this->actions.end())
this->actions.erase(iter);
this->actions.insert(std::pair<std::string,ActionX>(action.name(), action));
printf("Adding: %s\n", action.name().c_str());
auto iter = std::find_if( this->actions.begin(), this->actions.end(), [&action](const ActionX& arg) { return arg.name() == action.name(); });
if (iter == this->actions.end()) {
printf("push_back\n");
this->actions.push_back(action);
} else {
printf("update\n");
*iter = action;
}
}
ActionX& Actions::at(const std::string& name) {
return this->actions.at(name);
const ActionX& Actions::get(const std::string& name) const {
const auto iter = std::find_if( this->actions.begin(), this->actions.end(), [&name](const ActionX& action) { return action.name() == name; });
if (iter == this->actions.end())
throw std::range_error("No such action: " + name);
return *iter;
}
const ActionX& Actions::get(std::size_t index) const {
return this->actions[index];
}
bool Actions::ready(std::time_t sim_time) const {
for (const auto& pair : this->actions) {
if (pair.second.ready(sim_time))
for (const auto& action : this->actions) {
if (action.ready(sim_time))
return true;
}
return false;
@@ -57,13 +71,20 @@ bool Actions::ready(std::time_t sim_time) const {
std::vector<const ActionX *> Actions::pending(std::time_t sim_time) const {
std::vector<const ActionX *> action_vector;
for (auto& pair : this->actions) {
auto& action = pair.second;
for (const auto& action : this->actions) {
if (action.ready(sim_time))
action_vector.push_back( &action );
}
return action_vector;
}
std::vector<ActionX>::const_iterator Actions::begin() const {
return this->actions.begin();
}
std::vector<ActionX>::const_iterator Actions::end() const {
return this->actions.end();
}
}
}

View File

@@ -123,6 +123,7 @@ namespace {
udq_active(this->m_timeMap, std::make_shared<UDQActive>()),
guide_rate_model(this->m_timeMap, std::make_shared<GuideRateModel>()),
global_whistctl_mode(this->m_timeMap, WellProducer::CMODE_UNDEFINED),
m_actions(this->m_timeMap, std::make_shared<Action::Actions>()),
rft_config(this->m_timeMap),
m_nupcol(this->m_timeMap, 3)
{
@@ -429,7 +430,7 @@ namespace {
parseContext.handleError( ParseContext::ACTIONX_ILLEGAL_KEYWORD, msg, errors);
}
}
this->m_actions.add(action);
this->addACTIONX(action, currentStep);
} else
this->handleKeyword(currentStep, section, keywordIdx, keyword, parseContext, errors, grid, eclipseProperties, unit_system, rftProperties);
@@ -454,6 +455,13 @@ namespace {
}
void Schedule::addACTIONX(const Action::ActionX& action, std::size_t currentStep) {
auto new_actions = std::make_shared<Action::Actions>( this->actions(currentStep) );
new_actions->add(action);
this->m_actions.update(currentStep, new_actions);
}
void Schedule::checkUnhandledKeywords(const SCHEDULESection& /*section*/) const
{
}
@@ -2560,8 +2568,9 @@ void Schedule::handleGRUPTREE( const DeckKeyword& keyword, size_t currentStep, c
}
const Action::Actions& Schedule::actions() const {
return this->m_actions;
const Action::Actions& Schedule::actions(std::size_t timeStep) const {
const auto& ptr = this->m_actions.get(timeStep);
return *ptr;
}

View File

@@ -595,3 +595,124 @@ BOOST_AUTO_TEST_CASE(TestFieldAND) {
BOOST_CHECK_EQUAL(wells[0], "OP3");
}
}
BOOST_AUTO_TEST_CASE(SCAN2) {
const auto deck_string = std::string{ R"(
SCHEDULE
TSTEP
10 /
ACTIONX
'B' /
WWCT 'OPX' > 0.75 AND / -- The spaces will/should be normalized in Condition::expression()
FPR < 100 /
/
WELSPECS
'W1' 'OP' 1 1 3.33 'OIL' 7*/
/
ENDACTIO
TSTEP
10 /
ACTIONX
'A' /
WOPR 'OPX' = 1000 /
/
ENDACTIO
ACTIONX
'B' /
FWCT < 0.50 /
/
ENDACTIO
TSTEP
10 /
)"};
Opm::Parser parser;
auto deck = parser.parseString(deck_string);
EclipseGrid grid1(10,10,10);
TableManager table ( deck );
Eclipse3DProperties eclipseProperties ( deck , table, grid1);
Runspec runspec (deck);
Schedule sched(deck, grid1, eclipseProperties, runspec);
const auto& actions0 = sched.actions(0);
BOOST_CHECK_EQUAL(actions0.size(), 0);
const auto& actions1 = sched.actions(1);
BOOST_CHECK_EQUAL(actions1.size(), 1);
const auto& act1 = actions1.get("B");
const auto& strings = act1.keyword_strings();
BOOST_CHECK_EQUAL(strings.size(), 4);
BOOST_CHECK_EQUAL(strings.back(), "ENDACTIO");
std::string rdeck_string = "";
for (std::size_t i = 0; i < strings.size(); i++)
rdeck_string += strings[i] + "\n";
auto deck2 = parser.parseString(rdeck_string);
BOOST_CHECK(deck2.getKeyword("WELSPECS") == deck.getKeyword("WELSPECS"));
const auto& conditions = act1.conditions();
BOOST_CHECK_EQUAL(conditions.size() , 2);
const auto& cond0 = conditions[0];
BOOST_CHECK_EQUAL(cond0.expression, "WWCT 'OPX' > 0.75 AND");
BOOST_CHECK_EQUAL(cond0.quantity, "WWCT");
BOOST_CHECK(cond0.cmp == Action::Condition::Comparator::GREATER);
BOOST_CHECK(cond0.logic == Action::Condition::Logical::AND);
const auto& cond1 = conditions[1];
BOOST_CHECK_EQUAL(cond1.expression, "FPR < 100");
BOOST_CHECK_EQUAL(cond1.quantity, "FPR");
BOOST_CHECK(cond1.cmp == Action::Condition::Comparator::LESS);
BOOST_CHECK(cond1.logic == Action::Condition::Logical::END);
/*****************************************************************/
const auto& actions2 = sched.actions(2);
BOOST_CHECK_EQUAL(actions2.size(), 2);
const auto& actB = actions2.get("B");
const auto& condB = actB.conditions();
BOOST_CHECK_EQUAL(condB.size() , 1);
BOOST_CHECK_EQUAL(condB[0].expression, "FWCT < 0.50");
BOOST_CHECK_EQUAL(condB[0].quantity, "FWCT");
BOOST_CHECK(condB[0].cmp == Action::Condition::Comparator::LESS);
BOOST_CHECK(condB[0].logic == Action::Condition::Logical::END);
const auto& actA = actions2.get("A");
const auto& condA = actA.conditions();
BOOST_CHECK_EQUAL(condA.size() , 1);
BOOST_CHECK_EQUAL(condA[0].expression, "WOPR 'OPX' = 1000");
BOOST_CHECK_EQUAL(condA[0].quantity, "WOPR");
BOOST_CHECK(condA[0].cmp == Action::Condition::Comparator::EQUAL);
BOOST_CHECK(condA[0].logic == Action::Condition::Logical::END);
std::size_t index = 0;
for (const auto& act : actions2) {
if (index == 0)
BOOST_CHECK_EQUAL("B", act.name());
if (index == 1)
BOOST_CHECK_EQUAL("A", act.name());
index++;
}
}