Merge pull request #656 from joakim-hove/udq-parse
UDQ: Complete well related UDQ's
This commit is contained in:
commit
fa77187871
@ -124,16 +124,18 @@ if(ENABLE_ECL_INPUT)
|
||||
src/opm/parser/eclipse/EclipseState/Tables/TableManager.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Tables/TableSchema.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Tables/Tables.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQASTNode.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQParams.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQParser.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQSet.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQWellSet.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQAssign.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQDefine.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQEnums.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQInput.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQContext.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQFunction.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQFunctionTable.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQExpression.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/VFPInjTable.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/VFPProdTable.cpp
|
||||
src/opm/parser/eclipse/Parser/ErrorGuard.cpp
|
||||
@ -519,6 +521,7 @@ if(ENABLE_ECL_INPUT)
|
||||
opm/parser/eclipse/EclipseState/checkDeck.hpp
|
||||
opm/parser/eclipse/EclipseState/Runspec.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQAssign.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQDefine.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQContext.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQInput.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQEnums.hpp
|
||||
@ -528,7 +531,6 @@ if(ENABLE_ECL_INPUT)
|
||||
opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQSet.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQFunction.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQFunctionTable.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQExpression.hpp
|
||||
opm/parser/eclipse/Deck/DeckItem.hpp
|
||||
opm/parser/eclipse/Deck/Deck.hpp
|
||||
opm/parser/eclipse/Deck/Section.hpp
|
||||
|
@ -21,18 +21,25 @@
|
||||
#ifndef UDQ_CONTEXT_HPP
|
||||
#define UDQ_CONTEXT_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
namespace Opm {
|
||||
class SummaryState;
|
||||
class UDQFunctionTable;
|
||||
|
||||
class UDQContext{
|
||||
public:
|
||||
explicit UDQContext(const SummaryState& summary_state);
|
||||
UDQContext(const UDQFunctionTable& udqft, const SummaryState& summary_state);
|
||||
double get(const std::string& key) const;
|
||||
double get_well_var(const std::string& well, const std::string& var) const;
|
||||
void add(const std::string& key, double value);
|
||||
const UDQFunctionTable& function_table() const;
|
||||
std::vector<std::string> wells() const;
|
||||
private:
|
||||
const UDQFunctionTable& udqft;
|
||||
const SummaryState& summary_state;
|
||||
std::unordered_map<std::string, double> values;
|
||||
};
|
||||
|
76
opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQDefine.hpp
Normal file
76
opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQDefine.hpp
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright 2018 Statoil 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 UDQ_DEFINE_HPP
|
||||
#define UDQ_DEFINE_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQEnums.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQWellSet.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQContext.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQFunctionTable.hpp>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
class UDQASTNode;
|
||||
class ParseContext;
|
||||
class ErrorGuard;
|
||||
|
||||
class UDQDefine{
|
||||
public:
|
||||
UDQDefine(const UDQParams& udq_params,
|
||||
const std::string& keyword,
|
||||
const std::vector<std::string>& deck_data);
|
||||
|
||||
UDQDefine(const UDQParams& udq_params,
|
||||
const std::string& keyword,
|
||||
const std::vector<std::string>& deck_data,
|
||||
const ParseContext& parseContext,
|
||||
ErrorGuard& errors);
|
||||
|
||||
template <typename T>
|
||||
UDQDefine(const UDQParams& udq_params,
|
||||
const std::string& keyword,
|
||||
const std::vector<std::string>& deck_data,
|
||||
const ParseContext& parseContext,
|
||||
T&& errors);
|
||||
|
||||
|
||||
UDQWellSet eval_wells(const UDQContext& context) const;
|
||||
UDQSet eval(const UDQContext& context) const;
|
||||
const std::string& keyword() const;
|
||||
/*
|
||||
Should not be internalized at all - and will go away; but temporarily needed for testing.
|
||||
*/
|
||||
std::vector<std::string> tokens;
|
||||
UDQVarType var_type() const;
|
||||
private:
|
||||
const UDQParams& udq_params; // Beacuse of the shared RNG stream this must be a reference.
|
||||
std::string m_keyword;
|
||||
std::shared_ptr<UDQASTNode> ast;
|
||||
UDQVarType m_var_type;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -35,7 +35,67 @@ enum class UDQVarType {
|
||||
};
|
||||
|
||||
|
||||
enum class UDQAction {ASSIGN, DEFINE, UNITS, UPDATE};
|
||||
|
||||
enum class UDQTokenType{
|
||||
error = 0,
|
||||
number = 1,
|
||||
open_paren = 2,
|
||||
close_paren = 3,
|
||||
ecl_expr = 7,
|
||||
//
|
||||
binary_op_add = 8,
|
||||
binary_op_sub = 9,
|
||||
binary_op_div = 10,
|
||||
binary_op_mul = 11,
|
||||
binary_op_pow = 12,
|
||||
binary_op_uadd = 13,
|
||||
binary_op_umul = 14,
|
||||
binary_op_umin = 15,
|
||||
binary_op_umax = 16,
|
||||
binary_cmp_eq = 17,
|
||||
binary_cmp_ne = 18,
|
||||
binary_cmp_le = 19,
|
||||
binary_cmp_ge = 20,
|
||||
binary_cmp_lt = 21,
|
||||
binary_cmp_gt = 22,
|
||||
//
|
||||
elemental_func_randn = 23,
|
||||
elemental_func_randu = 24,
|
||||
elemental_func_rrandn = 25,
|
||||
elemental_func_rrandu = 26,
|
||||
elemental_func_abs = 27,
|
||||
elemental_func_def = 28,
|
||||
elemental_func_exp = 29,
|
||||
elemental_func_idv = 30,
|
||||
elemental_func_ln = 31,
|
||||
elemental_func_log = 32,
|
||||
elemental_func_nint = 33,
|
||||
elemental_func_sorta = 34,
|
||||
elemental_func_sortd = 35,
|
||||
elemental_func_undef = 36,
|
||||
//
|
||||
scalar_func_sum = 37,
|
||||
scalar_func_avea = 38,
|
||||
scalar_func_aveg = 39,
|
||||
scalar_func_aveh = 40,
|
||||
scalar_func_max = 41,
|
||||
scalar_func_min = 42,
|
||||
scalar_func_norm1 = 43,
|
||||
scalar_func_norm2 = 44,
|
||||
scalar_func_normi = 45,
|
||||
scalar_func_prod = 46,
|
||||
//
|
||||
table_lookup = 47,
|
||||
//
|
||||
end = 100
|
||||
};
|
||||
|
||||
|
||||
enum class UDQAction {
|
||||
ASSIGN,
|
||||
DEFINE,
|
||||
UNITS,
|
||||
UPDATE};
|
||||
|
||||
|
||||
|
||||
@ -43,7 +103,11 @@ namespace UDQ {
|
||||
|
||||
UDQVarType varType(const std::string& keyword);
|
||||
UDQAction actionType(const std::string& action_string);
|
||||
|
||||
UDQTokenType funcType(const std::string& func_name);
|
||||
bool binaryFunc(UDQTokenType token_type);
|
||||
bool elementalUnaryFunc(UDQTokenType token_type);
|
||||
bool scalarFunc(UDQTokenType token_type);
|
||||
bool cmpFunc(UDQTokenType token_type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,17 +26,20 @@
|
||||
#include <random>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQSet.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQEnums.hpp>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
|
||||
class UDQFunction {
|
||||
public:
|
||||
explicit UDQFunction(const std::string& name);
|
||||
UDQFunction(const std::string& name);
|
||||
virtual ~UDQFunction() = default;
|
||||
const std::string& name() const;
|
||||
UDQTokenType type() const;
|
||||
private:
|
||||
std::string m_name;
|
||||
UDQTokenType func_type;
|
||||
};
|
||||
|
||||
class UDQScalarFunction : public UDQFunction {
|
||||
|
@ -30,7 +30,8 @@ namespace Opm {
|
||||
|
||||
class UDQFunctionTable {
|
||||
public:
|
||||
UDQFunctionTable(UDQParams& params);
|
||||
explicit UDQFunctionTable(const UDQParams& params);
|
||||
UDQFunctionTable();
|
||||
bool has_function(const std::string& name) const;
|
||||
const UDQFunction& get(const std::string& name) const;
|
||||
private:
|
||||
|
@ -25,26 +25,38 @@
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQExpression.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQDefine.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQAssign.hpp>
|
||||
#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>
|
||||
|
||||
|
||||
namespace Opm {
|
||||
|
||||
class DeckRecord;
|
||||
class Deck;
|
||||
|
||||
class UDQInput {
|
||||
public:
|
||||
void add_record(const DeckRecord& record);
|
||||
const std::vector<UDQExpression>& expressions() const noexcept;
|
||||
explicit UDQInput(const Deck& deck);
|
||||
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 assign_unit(const std::string& keyword, const std::string& unit);
|
||||
|
||||
const std::vector<UDQDefine>& definitions() const;
|
||||
std::vector<UDQDefine> definitions(UDQVarType var_type) const;
|
||||
|
||||
const std::vector<UDQAssign>& assignments() const;
|
||||
std::vector<UDQAssign> assignments(UDQVarType var_type) const;
|
||||
|
||||
const UDQFunctionTable& function_table() const;
|
||||
private:
|
||||
std::vector<UDQExpression> m_expressions;
|
||||
UDQParams udq_params;
|
||||
UDQFunctionTable udqft;
|
||||
std::vector<UDQDefine> m_definitions;
|
||||
std::vector<UDQAssign> m_assignments;
|
||||
std::unordered_map<std::string, std::string> units;
|
||||
std::unordered_set<std::string> keywords;
|
||||
|
@ -247,6 +247,7 @@ namespace Opm {
|
||||
*/
|
||||
const static std::string UNSUPPORTED_TERMINATE_IF_BHP;
|
||||
|
||||
const static std::string UDQ_PARSE_ERROR;
|
||||
|
||||
/*
|
||||
If the third item in the THPRES keyword is defaulted the
|
||||
|
@ -1122,11 +1122,12 @@ bool is_udq(const std::string& keyword) {
|
||||
void eval_udq(const Schedule& schedule, std::size_t sim_step, SummaryState& st)
|
||||
{
|
||||
const UDQInput& udq = schedule.getUDQConfig(sim_step);
|
||||
const auto& func_table = udq.function_table();
|
||||
UDQContext context(func_table, st);
|
||||
std::vector<std::string> wells;
|
||||
for (const auto* well : schedule.getWells())
|
||||
wells.push_back(well->name());
|
||||
|
||||
|
||||
for (const auto& assign : udq.assignments(UDQVarType::WELL_VAR)) {
|
||||
auto ws = assign.eval_wells(wells);
|
||||
for (const auto& well : wells) {
|
||||
@ -1135,9 +1136,16 @@ void eval_udq(const Schedule& schedule, std::size_t sim_step, SummaryState& st)
|
||||
st.add_well_var(well, ws.name(), udq_value.value());
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& def : udq.definitions(UDQVarType::WELL_VAR)) {
|
||||
auto ws = def.eval_wells(context);
|
||||
for (const auto& well : wells) {
|
||||
const auto& udq_value = ws[well];
|
||||
if (udq_value)
|
||||
st.add_well_var(well, def.keyword(), udq_value.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
namespace out {
|
||||
@ -1504,10 +1512,7 @@ void Summary::add_timestep( int report_step,
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
UDQContext udq_context(st);
|
||||
eval_udq(schedule, sim_step, st);
|
||||
}
|
||||
eval_udq(schedule, sim_step, st);
|
||||
{
|
||||
const ecl_sum_type * ecl_sum = this->ecl_sum.get();
|
||||
const ecl_smspec_type * smspec = ecl_sum_get_smspec(ecl_sum);
|
||||
|
@ -83,7 +83,7 @@ namespace Opm {
|
||||
m_runspec( runspec ),
|
||||
wtest_config(this->m_timeMap, std::make_shared<WellTestConfig>() ),
|
||||
wlist_manager( this->m_timeMap, std::make_shared<WListManager>()),
|
||||
udq_config(this->m_timeMap, std::make_shared<UDQInput>())
|
||||
udq_config(this->m_timeMap, std::make_shared<UDQInput>(deck))
|
||||
{
|
||||
m_controlModeWHISTCTL = WellProducer::CMODE_UNDEFINED;
|
||||
addGroup( "FIELD", 0 );
|
||||
@ -943,7 +943,6 @@ namespace Opm {
|
||||
void Schedule::handleUDQ(const DeckKeyword& keyword, size_t currentStep) {
|
||||
const auto& current = *this->udq_config.get(currentStep);
|
||||
std::shared_ptr<UDQInput> new_udq = std::make_shared<UDQInput>(current);
|
||||
|
||||
for (const auto& record : keyword)
|
||||
new_udq->add_record(record);
|
||||
|
||||
|
146
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQASTNode.cpp
Normal file
146
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQASTNode.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
#include <fnmatch.h>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQFunction.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQFunctionTable.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQEnums.hpp>
|
||||
|
||||
#include "UDQASTNode.hpp"
|
||||
|
||||
namespace Opm {
|
||||
|
||||
UDQASTNode::UDQASTNode() :
|
||||
type(UDQTokenType::error)
|
||||
{}
|
||||
|
||||
|
||||
UDQASTNode::UDQASTNode(UDQTokenType type) :
|
||||
type(type)
|
||||
{
|
||||
if (type == UDQTokenType::error)
|
||||
return;
|
||||
|
||||
if (type == UDQTokenType::end)
|
||||
return;
|
||||
|
||||
throw std::invalid_argument("The one argument constructor is only available for error and end");
|
||||
}
|
||||
|
||||
UDQASTNode::UDQASTNode(double scalar_value) :
|
||||
type(UDQTokenType::number),
|
||||
scalar_value(scalar_value)
|
||||
{}
|
||||
|
||||
|
||||
|
||||
|
||||
UDQASTNode::UDQASTNode(UDQTokenType type_arg,
|
||||
const std::string& func_name,
|
||||
const UDQASTNode& arg) :
|
||||
type(type_arg),
|
||||
string_value(func_name)
|
||||
{
|
||||
this->arglist.push_back(arg);
|
||||
}
|
||||
|
||||
|
||||
UDQASTNode::UDQASTNode(UDQTokenType type_arg,
|
||||
const std::string& func_name,
|
||||
const UDQASTNode& left,
|
||||
const UDQASTNode& right) :
|
||||
type(type_arg),
|
||||
string_value(func_name)
|
||||
{
|
||||
this->arglist.push_back(left);
|
||||
this->arglist.push_back(right);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UDQASTNode::UDQASTNode(UDQTokenType type_arg,
|
||||
const std::string& string_value,
|
||||
const std::vector<std::string>& selector) :
|
||||
type(type_arg),
|
||||
string_value(string_value),
|
||||
selector(selector)
|
||||
{
|
||||
if (type_arg == UDQTokenType::number)
|
||||
this->scalar_value = std::stod(string_value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
UDQWellSet UDQASTNode::eval_wells(const UDQContext& context) {
|
||||
const auto& wells = context.wells();
|
||||
if (this->type == UDQTokenType::ecl_expr) {
|
||||
auto res = UDQWellSet(this->string_value, wells);
|
||||
if (this->selector.size() > 0) {
|
||||
int fnmatch_flags = 0;
|
||||
const std::string& well_pattern = this->selector[0];
|
||||
if (well_pattern.find("*") == std::string::npos)
|
||||
throw std::invalid_argument("When evaluating a well UDQ you can not use fully qualified well variables");
|
||||
|
||||
for (const auto& well : wells) {
|
||||
if (fnmatch(well_pattern.c_str(), well.c_str(), fnmatch_flags) == 0)
|
||||
res.assign(well, context.get_well_var(well, this->string_value));
|
||||
}
|
||||
} else {
|
||||
for (const auto& well : wells)
|
||||
res.assign(well, context.get_well_var(well, this->string_value));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
if (UDQ::scalarFunc(this->type))
|
||||
throw std::invalid_argument("Can not invoke scalar function for well set");
|
||||
|
||||
|
||||
if (UDQ::elementalUnaryFunc(this->type)) {
|
||||
auto input_arg = this->arglist[0];
|
||||
auto func_arg = input_arg.eval_wells(context);
|
||||
|
||||
const auto& udqft = context.function_table();
|
||||
const UDQUnaryElementalFunction& func = dynamic_cast<const UDQUnaryElementalFunction&>(udqft.get(this->string_value));
|
||||
auto udq_set = func.eval(func_arg);
|
||||
|
||||
return UDQWellSet(this->string_value, wells, udq_set);
|
||||
}
|
||||
|
||||
if (UDQ::binaryFunc(this->type)) {
|
||||
auto left_arg = this->arglist[0].eval_wells(context);
|
||||
auto right_arg = this->arglist[1].eval_wells(context);
|
||||
|
||||
const auto& udqft = context.function_table();
|
||||
const UDQBinaryFunction& func = dynamic_cast<const UDQBinaryFunction&>(udqft.get(this->string_value));
|
||||
auto udq_set = func.eval(left_arg, right_arg);
|
||||
|
||||
return UDQWellSet(this->string_value, wells, udq_set);
|
||||
}
|
||||
|
||||
if (this->type == UDQTokenType::number)
|
||||
return UDQWellSet(this->string_value, wells, this->scalar_value);
|
||||
|
||||
throw std::invalid_argument("UDQASTNode::eval_wells: not implemented function type: " + std::to_string(static_cast<int>(this->type)));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
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 UDQASTNODE_HPP
|
||||
#define UDQASTNODE_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQWellSet.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQContext.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQEnums.hpp>
|
||||
|
||||
|
||||
|
||||
namespace Opm {
|
||||
|
||||
class UDQASTNode {
|
||||
public:
|
||||
UDQASTNode();
|
||||
|
||||
UDQASTNode(UDQTokenType type_arg);
|
||||
UDQASTNode(double scalar_value);
|
||||
UDQASTNode(UDQTokenType type_arg, const std::string& string_value, const std::vector<std::string>& selector);
|
||||
UDQASTNode(UDQTokenType type_arg, const std::string& func_name, const UDQASTNode& arg);
|
||||
UDQASTNode(UDQTokenType type_arg, const std::string& func_name, const UDQASTNode& left, const UDQASTNode& right);
|
||||
UDQWellSet eval_wells(const UDQContext& context);
|
||||
UDQTokenType type;
|
||||
|
||||
private:
|
||||
std::string string_value;
|
||||
double scalar_value;
|
||||
std::vector<std::string> selector;
|
||||
std::vector<UDQASTNode> arglist;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -47,7 +47,10 @@ UDQVarType UDQAssign::var_type() const {
|
||||
}
|
||||
|
||||
UDQWellSet UDQAssign::eval_wells(const std::vector<std::string>& wells) const {
|
||||
UDQWellSet ws(m_keyword, wells);
|
||||
if (this->m_var_type != UDQVarType::WELL_VAR)
|
||||
throw std::runtime_error("This is a bug - eval_wells called for UDQ variable which is not a well quantity.");
|
||||
|
||||
UDQWellSet ws(this->m_keyword, wells);
|
||||
|
||||
if (this->m_selector.empty())
|
||||
ws.assign(this->m_value);
|
||||
|
@ -24,7 +24,8 @@
|
||||
|
||||
namespace Opm {
|
||||
|
||||
UDQContext::UDQContext(const SummaryState& summary_state) :
|
||||
UDQContext::UDQContext(const UDQFunctionTable& udqft, const SummaryState& summary_state) :
|
||||
udqft(udqft),
|
||||
summary_state(summary_state)
|
||||
{
|
||||
for (const auto& pair : TimeMap::eclipseMonthIndices())
|
||||
@ -57,4 +58,17 @@ namespace Opm {
|
||||
|
||||
return pair_ptr->second;
|
||||
}
|
||||
|
||||
double UDQContext::get_well_var(const std::string& well, const std::string& var) const {
|
||||
return this->summary_state.get_well_var(well, var);
|
||||
}
|
||||
|
||||
std::vector<std::string> UDQContext::wells() const {
|
||||
return this->summary_state.wells();
|
||||
}
|
||||
|
||||
|
||||
const UDQFunctionTable& UDQContext::function_table() const {
|
||||
return this->udqft;
|
||||
}
|
||||
}
|
||||
|
151
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQDefine.cpp
Normal file
151
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQDefine.cpp
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
Copyright 2018 Statoil 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/>.
|
||||
*/
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
|
||||
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ErrorGuard.hpp>
|
||||
#include <opm/parser/eclipse/RawDeck/RawConsts.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQDefine.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQEnums.hpp>
|
||||
|
||||
#include "UDQParser.hpp"
|
||||
#include "UDQASTNode.hpp"
|
||||
|
||||
namespace Opm {
|
||||
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<std::string> quote_split(const std::string& item) {
|
||||
char quote_char = '\'';
|
||||
std::vector<std::string> items;
|
||||
std::size_t offset = 0;
|
||||
while (true) {
|
||||
auto quote_pos1 = item.find(quote_char, offset);
|
||||
if (quote_pos1 == std::string::npos) {
|
||||
items.push_back(item.substr(offset));
|
||||
break;
|
||||
}
|
||||
|
||||
auto quote_pos2 = item.find(quote_char, quote_pos1 + 1);
|
||||
if (quote_pos2 == std::string::npos)
|
||||
throw std::invalid_argument("Unbalanced quotes in: " + item);
|
||||
|
||||
if (quote_pos1 > offset)
|
||||
items.push_back(item.substr(offset, quote_pos1 - offset));
|
||||
items.push_back(item.substr(quote_pos1, 1 + quote_pos2 - quote_pos1));
|
||||
offset = quote_pos2 + 1;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
UDQDefine::UDQDefine(const UDQParams& udq_params,
|
||||
const std::string& keyword,
|
||||
const std::vector<std::string>& deck_data,
|
||||
const ParseContext& parseContext,
|
||||
T&& errors) :
|
||||
UDQDefine(udq_params, keyword, deck_data, parseContext, errors)
|
||||
{}
|
||||
|
||||
|
||||
UDQDefine::UDQDefine(const UDQParams& udq_params,
|
||||
const std::string& keyword,
|
||||
const std::vector<std::string>& deck_data) :
|
||||
UDQDefine(udq_params, keyword, deck_data, ParseContext(), ErrorGuard())
|
||||
{}
|
||||
|
||||
|
||||
UDQDefine::UDQDefine(const UDQParams& udq_params,
|
||||
const std::string& keyword,
|
||||
const std::vector<std::string>& deck_data,
|
||||
const ParseContext& parseContext,
|
||||
ErrorGuard& errors) :
|
||||
udq_params(udq_params),
|
||||
m_keyword(keyword),
|
||||
m_var_type(UDQ::varType(keyword))
|
||||
{
|
||||
std::vector<std::string> tokens;
|
||||
for (const std::string& deck_item : deck_data) {
|
||||
for (const std::string& item : quote_split(deck_item)) {
|
||||
if (RawConsts::is_quote()(item[0])) {
|
||||
tokens.push_back(item.substr(1, item.size() - 2));
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::vector<std::string> splitters = {"TU*[]", "(", ")", "[", "]", ",", "+", "-", "/", "*", "==", "!=", "^", ">=", "<=", ">", "<"};
|
||||
size_t offset = 0;
|
||||
size_t pos = 0;
|
||||
while (pos < item.size()) {
|
||||
size_t splitter_index = 0;
|
||||
while (splitter_index < splitters.size()) {
|
||||
const std::string& splitter = splitters[splitter_index];
|
||||
size_t find_pos = item.find(splitter, pos);
|
||||
if (find_pos == pos) {
|
||||
if (pos > offset)
|
||||
tokens.push_back(item.substr(offset, pos - offset));
|
||||
tokens.push_back(splitter);
|
||||
pos = find_pos + 1;
|
||||
offset = pos;
|
||||
break;
|
||||
}
|
||||
splitter_index++;
|
||||
}
|
||||
if (splitter_index == splitters.size())
|
||||
pos += 1;
|
||||
}
|
||||
if (pos > offset)
|
||||
tokens.push_back(item.substr(offset, pos - offset));
|
||||
|
||||
}
|
||||
}
|
||||
this->ast = std::make_shared<UDQASTNode>( UDQParser::parse(this->udq_params, tokens, parseContext, errors) );
|
||||
this->tokens = tokens;
|
||||
}
|
||||
|
||||
|
||||
UDQWellSet UDQDefine::eval_wells(const UDQContext& context) const {
|
||||
return this->ast->eval_wells(context);
|
||||
}
|
||||
|
||||
UDQSet UDQDefine::eval(const UDQContext& context) const {
|
||||
switch (this->m_var_type) {
|
||||
case UDQVarType::WELL_VAR:
|
||||
return this->eval_wells(context);
|
||||
default:
|
||||
throw std::invalid_argument("UDQ subtype: not supported");
|
||||
}
|
||||
}
|
||||
|
||||
UDQVarType UDQDefine::var_type() const {
|
||||
return this->m_var_type;
|
||||
}
|
||||
|
||||
|
||||
const std::string& UDQDefine::keyword() const {
|
||||
return this->m_keyword;
|
||||
}
|
||||
|
||||
}
|
@ -18,6 +18,9 @@
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQEnums.hpp>
|
||||
@ -25,6 +28,101 @@
|
||||
|
||||
namespace Opm {
|
||||
namespace UDQ {
|
||||
namespace {
|
||||
|
||||
const std::set<UDQTokenType> cmp_func = {UDQTokenType::binary_cmp_eq,
|
||||
UDQTokenType::binary_cmp_ne,
|
||||
UDQTokenType::binary_cmp_le,
|
||||
UDQTokenType::binary_cmp_ge,
|
||||
UDQTokenType::binary_cmp_lt,
|
||||
UDQTokenType::binary_cmp_gt};
|
||||
|
||||
const std::set<UDQTokenType> binary_func = {UDQTokenType::binary_op_add,
|
||||
UDQTokenType::binary_op_mul,
|
||||
UDQTokenType::binary_op_sub,
|
||||
UDQTokenType::binary_op_div,
|
||||
UDQTokenType::binary_op_pow,
|
||||
UDQTokenType::binary_op_uadd,
|
||||
UDQTokenType::binary_op_umul,
|
||||
UDQTokenType::binary_op_umin,
|
||||
UDQTokenType::binary_op_umax,
|
||||
UDQTokenType::binary_cmp_eq,
|
||||
UDQTokenType::binary_cmp_ne,
|
||||
UDQTokenType::binary_cmp_le,
|
||||
UDQTokenType::binary_cmp_ge,
|
||||
UDQTokenType::binary_cmp_lt,
|
||||
UDQTokenType::binary_cmp_gt};
|
||||
|
||||
|
||||
const std::set<UDQTokenType> scalar_func = {UDQTokenType::scalar_func_sum,
|
||||
UDQTokenType::scalar_func_avea,
|
||||
UDQTokenType::scalar_func_aveg,
|
||||
UDQTokenType::scalar_func_aveh,
|
||||
UDQTokenType::scalar_func_max,
|
||||
UDQTokenType::scalar_func_min,
|
||||
UDQTokenType::scalar_func_norm1,
|
||||
UDQTokenType::scalar_func_norm2,
|
||||
UDQTokenType::scalar_func_normi,
|
||||
UDQTokenType::scalar_func_prod};
|
||||
|
||||
|
||||
const std::set<UDQTokenType> unary_elemental_func = {UDQTokenType::elemental_func_randn,
|
||||
UDQTokenType::elemental_func_randu,
|
||||
UDQTokenType::elemental_func_rrandn,
|
||||
UDQTokenType::elemental_func_rrandu,
|
||||
UDQTokenType::elemental_func_abs,
|
||||
UDQTokenType::elemental_func_def,
|
||||
UDQTokenType::elemental_func_exp,
|
||||
UDQTokenType::elemental_func_idv,
|
||||
UDQTokenType::elemental_func_ln,
|
||||
UDQTokenType::elemental_func_log,
|
||||
UDQTokenType::elemental_func_nint,
|
||||
UDQTokenType::elemental_func_sorta,
|
||||
UDQTokenType::elemental_func_sortd,
|
||||
UDQTokenType::elemental_func_undef};
|
||||
|
||||
|
||||
const std::unordered_map<std::string, UDQTokenType> func_type = {{"+", UDQTokenType::binary_op_add},
|
||||
{"-", UDQTokenType::binary_op_sub},
|
||||
{"/", UDQTokenType::binary_op_div},
|
||||
{"DIV", UDQTokenType::binary_op_div},
|
||||
{"*", UDQTokenType::binary_op_mul},
|
||||
{"^", UDQTokenType::binary_op_pow},
|
||||
{"UADD", UDQTokenType::binary_op_uadd},
|
||||
{"UMUL", UDQTokenType::binary_op_umul},
|
||||
{"UMIN", UDQTokenType::binary_op_umin},
|
||||
{"UMAX", UDQTokenType::binary_op_umax},
|
||||
{"==", UDQTokenType::binary_cmp_eq},
|
||||
{"!=", UDQTokenType::binary_cmp_ne},
|
||||
{"<=", UDQTokenType::binary_cmp_le},
|
||||
{">=", UDQTokenType::binary_cmp_ge},
|
||||
{"<", UDQTokenType::binary_cmp_lt},
|
||||
{">", UDQTokenType::binary_cmp_gt},
|
||||
{"RANDN", UDQTokenType::elemental_func_randn},
|
||||
{"RANDU", UDQTokenType::elemental_func_randu},
|
||||
{"RRNDN", UDQTokenType::elemental_func_rrandn},
|
||||
{"RRNDU", UDQTokenType::elemental_func_rrandu},
|
||||
{"ABS", UDQTokenType::elemental_func_abs},
|
||||
{"DEF", UDQTokenType::elemental_func_def},
|
||||
{"EXP", UDQTokenType::elemental_func_exp},
|
||||
{"IDV", UDQTokenType::elemental_func_idv},
|
||||
{"LN", UDQTokenType::elemental_func_ln},
|
||||
{"LOG", UDQTokenType::elemental_func_log},
|
||||
{"NINT", UDQTokenType::elemental_func_nint},
|
||||
{"SORTA", UDQTokenType::elemental_func_sorta},
|
||||
{"SORTD", UDQTokenType::elemental_func_sortd},
|
||||
{"UNDEF", UDQTokenType::elemental_func_undef},
|
||||
{"SUM", UDQTokenType::scalar_func_sum},
|
||||
{"AVEA", UDQTokenType::scalar_func_avea},
|
||||
{"AVEG", UDQTokenType::scalar_func_aveg},
|
||||
{"AVEH", UDQTokenType::scalar_func_aveh},
|
||||
{"MAX", UDQTokenType::scalar_func_max},
|
||||
{"MIN", UDQTokenType::scalar_func_min},
|
||||
{"NORM1", UDQTokenType::scalar_func_norm1},
|
||||
{"NORM2", UDQTokenType::scalar_func_norm2},
|
||||
{"NORMI", UDQTokenType::scalar_func_normi},
|
||||
{"PROD", UDQTokenType::scalar_func_prod}};
|
||||
}
|
||||
|
||||
UDQVarType varType(const std::string& keyword) {
|
||||
if (keyword[1] != 'U')
|
||||
@ -71,5 +169,33 @@ UDQAction actionType(const std::string& action_string) {
|
||||
throw std::invalid_argument("Invalid action string " + action_string);
|
||||
}
|
||||
|
||||
|
||||
bool binaryFunc(UDQTokenType token_type) {
|
||||
return (binary_func.count(token_type) > 0);
|
||||
}
|
||||
|
||||
bool scalarFunc(UDQTokenType token_type) {
|
||||
return (scalar_func.count(token_type) > 0);
|
||||
}
|
||||
|
||||
bool elementalUnaryFunc(UDQTokenType token_type) {
|
||||
return (unary_elemental_func.count(token_type) > 0);
|
||||
}
|
||||
|
||||
bool cmpFunc(UDQTokenType token_type) {
|
||||
return (cmp_func.count(token_type) > 0);
|
||||
}
|
||||
|
||||
|
||||
UDQTokenType funcType(const std::string& func_name) {
|
||||
if (func_type.count(func_name) > 0)
|
||||
return func_type.at(func_name);
|
||||
|
||||
if (func_name.substr(0,2) == "TU") {
|
||||
return UDQTokenType::table_lookup;
|
||||
}
|
||||
|
||||
return UDQTokenType::error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,110 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 Statoil 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/>.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include <opm/parser/eclipse/RawDeck/RawConsts.hpp>
|
||||
#include <opm/parser/eclipse/Deck/DeckRecord.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQExpression.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQEnums.hpp>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace Opm {
|
||||
|
||||
/*
|
||||
The tokenizer algorithm has two problems with '*':
|
||||
|
||||
1. When used to specify a wildcard set - like 'P*' for all well starting
|
||||
with 'P' the tokenizer will interpret the '*' as a multiplication sign
|
||||
and split into independent tokens 'P' and '*'.
|
||||
|
||||
2. For items like '2*(1+WBHP)' the parsing code will expand the 2*
|
||||
operator to the repeated tokens : (1+WBHP), (1+WBHP)
|
||||
*/
|
||||
UDQExpression::UDQExpression(UDQAction action, const std::string& keyword_in, const std::vector<std::string>& input_data) :
|
||||
m_action(action),
|
||||
m_keyword(keyword_in),
|
||||
m_var_type(UDQ::varType(keyword_in))
|
||||
{
|
||||
for (const std::string& item : input_data) {
|
||||
if (RawConsts::is_quote()(item[0])) {
|
||||
this->data.push_back(item.substr(1, item.size() - 2));
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::vector<std::string> splitters = {"TU*[]", "(", ")", "[", "]", ",", "+", "-", "/", "*", "==", "!=", "^", ">=", "<=", ">", "<"};
|
||||
size_t offset = 0;
|
||||
size_t pos = 0;
|
||||
while (pos < item.size()) {
|
||||
size_t splitter_index = 0;
|
||||
while (splitter_index < splitters.size()) {
|
||||
const std::string& splitter = splitters[splitter_index];
|
||||
size_t find_pos = item.find(splitter, pos);
|
||||
if (find_pos == pos) {
|
||||
if (pos > offset)
|
||||
this->data.push_back(item.substr(offset, pos - offset));
|
||||
this->data.push_back(splitter);
|
||||
pos = find_pos + 1;
|
||||
offset = pos;
|
||||
break;
|
||||
}
|
||||
splitter_index++;
|
||||
}
|
||||
if (splitter_index == splitters.size())
|
||||
pos += 1;
|
||||
}
|
||||
if (pos > offset)
|
||||
this->data.push_back(item.substr(offset, pos - offset));
|
||||
}
|
||||
}
|
||||
|
||||
UDQExpression::UDQExpression(const DeckRecord& record) :
|
||||
UDQExpression(UDQ::actionType(record.getItem("ACTION").get<std::string>(0)),
|
||||
record.getItem("QUANTITY").get<std::string>(0),
|
||||
record.getItem("DATA").getData<std::string>())
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
const std::vector<std::string>& UDQExpression::tokens() const {
|
||||
return this->data;
|
||||
}
|
||||
|
||||
|
||||
UDQAction UDQExpression::action() const {
|
||||
return this->m_action;
|
||||
}
|
||||
|
||||
|
||||
const std::string& UDQExpression::keyword() const {
|
||||
return this->m_keyword;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -28,10 +28,15 @@
|
||||
namespace Opm {
|
||||
|
||||
UDQFunction::UDQFunction(const std::string& name) :
|
||||
m_name(name)
|
||||
m_name(name),
|
||||
func_type(UDQ::funcType(name))
|
||||
{
|
||||
}
|
||||
|
||||
UDQTokenType UDQFunction::type() const {
|
||||
return this->func_type;
|
||||
}
|
||||
|
||||
const std::string& UDQFunction::name() const {
|
||||
return this->m_name;
|
||||
}
|
||||
@ -279,7 +284,7 @@ namespace {
|
||||
UDQSet udq_sort(const UDQSet& arg, std::size_t defined_size, const std::function<bool(int,int)>& cmp) {
|
||||
auto result = arg;
|
||||
std::vector<int> index(defined_size);
|
||||
std::iota(index.begin(), index.end(), 0);
|
||||
std::iota(index.begin(), index.end(), 1);
|
||||
std::sort(index.begin(), index.end(), cmp);
|
||||
|
||||
std::size_t output_index = 0;
|
||||
@ -297,12 +302,12 @@ namespace {
|
||||
|
||||
UDQSet UDQUnaryElementalFunction::SORTA(const UDQSet& arg) {
|
||||
auto defined_values = arg.defined_values();
|
||||
return udq_sort(arg, defined_values.size(), [&defined_values](int a, int b){ return defined_values[a] < defined_values[b]; });
|
||||
return udq_sort(arg, defined_values.size(), [&defined_values](int a, int b){ return defined_values[a - 1] < defined_values[b - 1]; });
|
||||
}
|
||||
|
||||
UDQSet UDQUnaryElementalFunction::SORTD(const UDQSet& arg) {
|
||||
auto defined_values = arg.defined_values();
|
||||
return udq_sort(arg, defined_values.size(), [&defined_values](int a, int b){ return defined_values[a] > defined_values[b]; });
|
||||
return udq_sort(arg, defined_values.size(), [&defined_values](int a, int b){ return defined_values[a - 1] > defined_values[b - 1]; });
|
||||
}
|
||||
|
||||
UDQBinaryFunction::UDQBinaryFunction(const std::string& name, std::function<UDQSet(const UDQSet& lhs, const UDQSet& rhs)> f) :
|
||||
|
@ -25,7 +25,11 @@
|
||||
|
||||
namespace Opm {
|
||||
|
||||
UDQFunctionTable::UDQFunctionTable(UDQParams& params) :
|
||||
UDQFunctionTable::UDQFunctionTable() :
|
||||
UDQFunctionTable(UDQParams())
|
||||
{}
|
||||
|
||||
UDQFunctionTable::UDQFunctionTable(const UDQParams& params) :
|
||||
params(params)
|
||||
{
|
||||
// SCalar functions
|
||||
@ -73,7 +77,6 @@ UDQFunctionTable::UDQFunctionTable(UDQParams& params) :
|
||||
this->insert_function( std::make_shared<UDQBinaryFunction>(">=", ge) );
|
||||
this->insert_function( std::make_shared<UDQBinaryFunction>("<=", le) );
|
||||
|
||||
this->insert_function( std::make_shared<UDQBinaryFunction>("^", UDQBinaryFunction::POW ));
|
||||
this->insert_function( std::make_shared<UDQBinaryFunction>("^", UDQBinaryFunction::POW ));
|
||||
this->insert_function( std::make_shared<UDQBinaryFunction>("<", UDQBinaryFunction::LT ));
|
||||
this->insert_function( std::make_shared<UDQBinaryFunction>(">", UDQBinaryFunction::GT ));
|
||||
|
@ -35,6 +35,13 @@ namespace Opm {
|
||||
|
||||
}
|
||||
|
||||
UDQInput::UDQInput(const Deck& deck) :
|
||||
udq_params(deck),
|
||||
udqft(this->udq_params)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
void UDQInput::add_record(const DeckRecord& record) {
|
||||
auto action = UDQ::actionType(record.getItem("ACTION").get<std::string>(0));
|
||||
@ -48,14 +55,24 @@ namespace Opm {
|
||||
double value = std::stod(data.back());
|
||||
this->m_assignments.emplace_back( quantity, selector, value );
|
||||
} else
|
||||
this->m_expressions.emplace_back(action, quantity, data);
|
||||
|
||||
this->m_definitions.emplace_back(this->udq_params, quantity, data);
|
||||
this->keywords.insert(quantity);
|
||||
}
|
||||
|
||||
|
||||
const std::vector<UDQExpression>& UDQInput::expressions() const noexcept {
|
||||
return this->m_expressions;
|
||||
const std::vector<UDQDefine>& UDQInput::definitions() const {
|
||||
return this->m_definitions;
|
||||
}
|
||||
|
||||
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; });
|
||||
|
||||
return filtered_defines;
|
||||
}
|
||||
|
||||
|
||||
@ -105,4 +122,7 @@ namespace Opm {
|
||||
return (this->keywords.count(keyword) > 0);
|
||||
}
|
||||
|
||||
const UDQFunctionTable& UDQInput::function_table() const {
|
||||
return this->udqft;
|
||||
}
|
||||
}
|
||||
|
260
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQParser.cpp
Normal file
260
src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQParser.cpp
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
|
||||
#include "UDQParser.hpp"
|
||||
|
||||
namespace Opm {
|
||||
|
||||
UDQTokenType UDQParser::get_type(const std::string& arg) const {
|
||||
auto func_type = UDQ::funcType(arg);
|
||||
if (func_type == UDQTokenType::table_lookup)
|
||||
throw std::invalid_argument("Table lookup function TU*[] is not supported in UDQ");
|
||||
|
||||
if (func_type != UDQTokenType::error)
|
||||
return func_type;
|
||||
|
||||
if (arg == "(")
|
||||
return UDQTokenType::open_paren;
|
||||
|
||||
if (arg == ")")
|
||||
return UDQTokenType::close_paren;
|
||||
|
||||
{
|
||||
char * end_ptr;
|
||||
std::strtod(arg.c_str(), &end_ptr);
|
||||
if (std::strlen(end_ptr) == 0)
|
||||
return UDQTokenType::number;
|
||||
}
|
||||
|
||||
return UDQTokenType::ecl_expr;
|
||||
}
|
||||
|
||||
|
||||
std::size_t UDQParser::current_size() const {
|
||||
if (this->tokens.size() == static_cast<std::size_t>(this->current_pos))
|
||||
return 0;
|
||||
|
||||
if (this->current_pos < 0)
|
||||
return 1;
|
||||
|
||||
const std::string& first_arg = this->tokens[this->current_pos];
|
||||
if (this->get_type(first_arg) != UDQTokenType::ecl_expr)
|
||||
return 1;
|
||||
|
||||
std::size_t offset = this->current_pos;
|
||||
while (true) {
|
||||
const std::string& arg = this->tokens[offset];
|
||||
if (this->get_type(arg) != UDQTokenType::ecl_expr)
|
||||
break;
|
||||
|
||||
offset += 1;
|
||||
|
||||
if (offset == this->tokens.size())
|
||||
break;
|
||||
}
|
||||
|
||||
return offset - this->current_pos;
|
||||
}
|
||||
|
||||
|
||||
UDQParseNode UDQParser::next() {
|
||||
this->current_pos += this->current_size();
|
||||
return this->current();
|
||||
}
|
||||
|
||||
|
||||
UDQParseNode UDQParser::current() const {
|
||||
if (static_cast<size_t>(this->current_pos) == this->tokens.size())
|
||||
return UDQTokenType::end;
|
||||
|
||||
const std::string& arg = this->tokens[this->current_pos];
|
||||
auto type = this->get_type(arg);
|
||||
if (type != UDQTokenType::ecl_expr)
|
||||
return UDQParseNode(type, arg);
|
||||
|
||||
std::size_t selector_size = this->current_size() - 1;
|
||||
const auto * token_ptr = std::addressof(this->tokens[this->current_pos + 1]);
|
||||
std::vector<std::string> selector(token_ptr, token_ptr + selector_size);
|
||||
return UDQParseNode(type, arg, selector);
|
||||
}
|
||||
|
||||
|
||||
UDQASTNode UDQParser::parse_factor() {
|
||||
auto current = this->current();
|
||||
|
||||
if (current.type == UDQTokenType::open_paren) {
|
||||
this->next();
|
||||
auto inner_expr = this->parse_cmp();
|
||||
|
||||
current = this->current();
|
||||
if (current.type != UDQTokenType::close_paren)
|
||||
return UDQTokenType::error;
|
||||
|
||||
this->next();
|
||||
return inner_expr;
|
||||
}
|
||||
|
||||
if (UDQ::scalarFunc(current.type) || UDQ::elementalUnaryFunc(current.type)) {
|
||||
auto func_node = current;
|
||||
auto next = this->next();
|
||||
if (next.type == UDQTokenType::open_paren) {
|
||||
this->next();
|
||||
auto arg_expr = this->parse_cmp();
|
||||
|
||||
current = this->current();
|
||||
if (current.type != UDQTokenType::close_paren)
|
||||
return UDQTokenType::error;
|
||||
|
||||
this->next();
|
||||
return UDQASTNode(func_node.type, func_node.value, arg_expr);
|
||||
} else
|
||||
return UDQTokenType::error;
|
||||
}
|
||||
|
||||
UDQASTNode node(current.type, current.value, current.selector);
|
||||
this->next();
|
||||
return node;
|
||||
}
|
||||
|
||||
UDQASTNode UDQParser::parse_pow() {
|
||||
auto left = this->parse_factor();
|
||||
auto current = this->current();
|
||||
if (current.type == UDQTokenType::end)
|
||||
return left;
|
||||
|
||||
if (current.type == UDQTokenType::binary_op_pow) {
|
||||
auto func_node = current;
|
||||
this->next();
|
||||
auto right = this->parse_mul();
|
||||
if (right.type == UDQTokenType::end)
|
||||
return UDQASTNode(UDQTokenType::error);
|
||||
|
||||
return UDQASTNode(current.type, current.value, left, right);
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
|
||||
|
||||
UDQASTNode UDQParser::parse_mul() {
|
||||
auto left = this->parse_pow();
|
||||
auto current = this->current();
|
||||
if (current.type == UDQTokenType::end)
|
||||
return left;
|
||||
|
||||
if (current.type == UDQTokenType::binary_op_mul || current.type == UDQTokenType::binary_op_div) {
|
||||
auto func_node = current;
|
||||
this->next();
|
||||
auto right = this->parse_mul();
|
||||
if (right.type == UDQTokenType::end)
|
||||
return UDQASTNode(UDQTokenType::error);
|
||||
|
||||
return UDQASTNode(current.type, current.value, left, right);
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
|
||||
UDQASTNode UDQParser::parse_add() {
|
||||
auto left = this->parse_mul();
|
||||
auto current = this->current();
|
||||
if (current.type == UDQTokenType::end)
|
||||
return left;
|
||||
|
||||
if (current.type == UDQTokenType::binary_op_add || current.type == UDQTokenType::binary_op_sub) {
|
||||
auto func_node = current;
|
||||
auto next = this->next();
|
||||
auto right = this->parse_add();
|
||||
if (right.type == UDQTokenType::end)
|
||||
return UDQASTNode(UDQTokenType::error);
|
||||
|
||||
return UDQASTNode(current.type, current.value, left, right);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
A bit uncertain on the presedence of the comparison operators. In normal C the
|
||||
comparion operators bind weaker than addition, i.e. for the assignment:
|
||||
|
||||
auto cmp = a + b < c;
|
||||
|
||||
The sum (a+b) is evaluated and then compared with c, that is the order of
|
||||
presedence implemented here. But reading the eclipse UDQ manual one can get
|
||||
the imporession that the relation operators should bind "very strong", i.e.
|
||||
that (b < c) should be evaluated first, and then the result of the comparison
|
||||
added to a.
|
||||
*/
|
||||
|
||||
UDQASTNode UDQParser::parse_cmp() {
|
||||
auto left = this->parse_add();
|
||||
auto current = this->current();
|
||||
if (current.type == UDQTokenType::end)
|
||||
return left;
|
||||
|
||||
if (UDQ::cmpFunc(current.type)) {
|
||||
auto func_node = current;
|
||||
this->next();
|
||||
auto right = this->parse_cmp();
|
||||
if (right.type == UDQTokenType::end)
|
||||
return UDQASTNode(UDQTokenType::error);
|
||||
|
||||
return UDQASTNode(current.type, current.value, left, right);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UDQASTNode UDQParser::parse(const UDQParams& udq_params, const std::vector<std::string>& tokens, const ParseContext& parseContext, ErrorGuard& errors)
|
||||
{
|
||||
UDQParser parser(udq_params, tokens);
|
||||
parser.next();
|
||||
|
||||
auto tree = parser.parse_cmp();
|
||||
auto current = parser.current();
|
||||
|
||||
if (current.type != UDQTokenType::end || tree.type == UDQTokenType::error) {
|
||||
if (current.type != UDQTokenType::end) {
|
||||
size_t index = parser.current_pos;
|
||||
std::string msg = "Extra unhandled data starting with token[" + std::to_string(index) + "] = " + current.value;
|
||||
parseContext.handleError(ParseContext::UDQ_PARSE_ERROR, msg, errors);
|
||||
}
|
||||
|
||||
if (tree.type == UDQTokenType::error) {
|
||||
std::string msg = "Failed to parse UDQ expression";
|
||||
parseContext.handleError(ParseContext::UDQ_PARSE_ERROR, msg, errors);
|
||||
}
|
||||
return UDQASTNode( udq_params.undefinedValue() );
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
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 UDQPARSER_HPP
|
||||
#define UDQPARSER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQFunctionTable.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQParams.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQEnums.hpp>
|
||||
|
||||
#include "UDQASTNode.hpp"
|
||||
|
||||
namespace Opm {
|
||||
|
||||
class ParseContext;
|
||||
class ErrorGuard;
|
||||
|
||||
struct UDQParseNode {
|
||||
UDQParseNode(UDQTokenType type_arg, const std::string& value_arg, const std::vector<std::string>& selector) :
|
||||
type(type_arg),
|
||||
value(value_arg),
|
||||
selector(selector)
|
||||
{}
|
||||
|
||||
UDQParseNode(UDQTokenType type_arg, const std::string& value_arg) :
|
||||
UDQParseNode(type_arg, value_arg, {})
|
||||
{}
|
||||
|
||||
// Implicit converting constructor.
|
||||
UDQParseNode(UDQTokenType type_arg) : UDQParseNode(type_arg, "")
|
||||
{}
|
||||
|
||||
|
||||
UDQTokenType type;
|
||||
std::string value;
|
||||
std::vector<std::string> selector;
|
||||
};
|
||||
|
||||
|
||||
class UDQParser {
|
||||
public:
|
||||
static UDQASTNode parse(const UDQParams& udq_params, const std::vector<std::string>& tokens, const ParseContext& parseContext, ErrorGuard& errors);
|
||||
|
||||
private:
|
||||
UDQParser(const UDQParams& udq_params, const std::vector<std::string>& tokens) :
|
||||
udq_params(udq_params),
|
||||
udqft(UDQFunctionTable(udq_params)),
|
||||
tokens(tokens)
|
||||
{}
|
||||
|
||||
UDQASTNode parse_cmp();
|
||||
UDQASTNode parse_add();
|
||||
UDQASTNode parse_factor();
|
||||
UDQASTNode parse_mul();
|
||||
UDQASTNode parse_pow();
|
||||
|
||||
UDQParseNode current() const;
|
||||
UDQParseNode next();
|
||||
UDQTokenType get_type(const std::string& arg) const;
|
||||
std::size_t current_size() const;
|
||||
|
||||
const UDQParams& udq_params;
|
||||
UDQFunctionTable udqft;
|
||||
std::vector<std::string> tokens;
|
||||
ssize_t current_pos = -1;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -105,6 +105,8 @@ namespace Opm {
|
||||
|
||||
addKey(SIMULATOR_KEYWORD_NOT_SUPPORTED, InputError::WARN);
|
||||
addKey(SIMULATOR_KEYWORD_ITEM_NOT_SUPPORTED, InputError::WARN);
|
||||
|
||||
addKey(UDQ_PARSE_ERROR, InputError::THROW_EXCEPTION);
|
||||
}
|
||||
|
||||
void ParseContext::initEnv() {
|
||||
@ -334,4 +336,6 @@ namespace Opm {
|
||||
|
||||
const std::string ParseContext::SIMULATOR_KEYWORD_NOT_SUPPORTED = "SIMULATOR_KEYWORD_NOT_SUPPORTED";
|
||||
const std::string ParseContext::SIMULATOR_KEYWORD_ITEM_NOT_SUPPORTED = "SIMULATOR_KEYWORD_ITEM_NOT_SUPPORTED";
|
||||
|
||||
const std::string ParseContext::UDQ_PARSE_ERROR = "UDQ_PARSE_ERROR";
|
||||
}
|
||||
|
@ -354,6 +354,8 @@ WUBHP
|
||||
/
|
||||
WUOPR
|
||||
/
|
||||
WUWCT
|
||||
/
|
||||
SCHEDULE
|
||||
-- -------------------------------------------------------------------------
|
||||
RPTSCHED
|
||||
@ -370,6 +372,8 @@ UDQ
|
||||
ASSIGN WUBHP P4 14 /
|
||||
UNITS WUBHP 'BARSA' /
|
||||
UNITS WUOPR 'SM3/DAY' /
|
||||
DEFINE WUWCT WWPR / (WWPR + WOPR) /
|
||||
UNITS WUWCT '1' /
|
||||
/
|
||||
|
||||
|
||||
|
530
tests/msim/actionx2.include
Normal file
530
tests/msim/actionx2.include
Normal file
@ -0,0 +1,530 @@
|
||||
std::string actionx = R"(
|
||||
-- This reservoir simulation deck is made available under the Open Database
|
||||
-- License: http://opendatacommons.org/licenses/odbl/1.0/. Any rights in
|
||||
-- individual contents of the database are licensed under the Database Contents
|
||||
-- License: http://opendatacommons.org/licenses/dbcl/1.0/
|
||||
|
||||
-- Copyright (C) 2015 Statoil
|
||||
|
||||
-- This simulation is based on the data given in
|
||||
-- 'Comparison of Solutions to a Three-Dimensional
|
||||
-- Black-Oil Reservoir Simulation Problem' by Aziz S. Odeh,
|
||||
-- Journal of Petroleum Technology, January 1981
|
||||
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
------------------------ SPE1 - CASE 1 ------------------------------------
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
RUNSPEC
|
||||
-- -------------------------------------------------------------------------
|
||||
|
||||
TITLE
|
||||
SPE1 - CASE 1
|
||||
|
||||
DIMENS
|
||||
10 10 3 /
|
||||
|
||||
-- The number of equilibration regions is inferred from the EQLDIMS
|
||||
-- keyword.
|
||||
EQLDIMS
|
||||
/
|
||||
|
||||
-- The number of PVTW tables is inferred from the TABDIMS keyword;
|
||||
-- when no data is included in the keyword the default values are used.
|
||||
TABDIMS
|
||||
/
|
||||
|
||||
OIL
|
||||
GAS
|
||||
WATER
|
||||
DISGAS
|
||||
-- As seen from figure 4 in Odeh, GOR is increasing with time,
|
||||
-- which means that dissolved gas is present
|
||||
|
||||
FIELD
|
||||
|
||||
START
|
||||
1 'DEC' 2014 /
|
||||
|
||||
WELLDIMS
|
||||
-- Item 1: maximum number of wells in the model
|
||||
-- - there are two wells in the problem; injector and producer
|
||||
-- Item 2: maximum number of grid blocks connected to any one well
|
||||
-- - must be one as the wells are located at specific grid blocks
|
||||
-- Item 3: maximum number of groups in the model
|
||||
-- - we are dealing with only one 'group'
|
||||
-- Item 4: maximum number of wells in any one group
|
||||
-- - there must be two wells in a group as there are two wells in total
|
||||
2 1 1 2 /
|
||||
|
||||
UNIFOUT
|
||||
|
||||
UDQDIMS
|
||||
50 25 0 50 50 0 0 50 0 20 /
|
||||
|
||||
GRID
|
||||
|
||||
-- The INIT keyword is used to request an .INIT file. The .INIT file
|
||||
-- is written before the simulation actually starts, and contains grid
|
||||
-- properties and saturation tables as inferred from the input
|
||||
-- deck. There are no other keywords which can be used to configure
|
||||
-- exactly what is written to the .INIT file.
|
||||
INIT
|
||||
|
||||
|
||||
-- -------------------------------------------------------------------------
|
||||
NOECHO
|
||||
|
||||
DX
|
||||
-- There are in total 300 cells with length 1000ft in x-direction
|
||||
300*1000 /
|
||||
DY
|
||||
-- There are in total 300 cells with length 1000ft in y-direction
|
||||
300*1000 /
|
||||
DZ
|
||||
-- The layers are 20, 30 and 50 ft thick, in each layer there are 100 cells
|
||||
100*20 100*30 100*50 /
|
||||
|
||||
TOPS
|
||||
-- The depth of the top of each grid block
|
||||
100*8325 /
|
||||
|
||||
PORO
|
||||
-- Constant porosity of 0.3 throughout all 300 grid cells
|
||||
300*0.3 /
|
||||
|
||||
PERMX
|
||||
-- The layers have perm. 500mD, 50mD and 200mD, respectively.
|
||||
100*500 100*50 100*200 /
|
||||
|
||||
PERMY
|
||||
-- Equal to PERMX
|
||||
100*500 100*50 100*200 /
|
||||
|
||||
PERMZ
|
||||
-- Cannot find perm. in z-direction in Odeh's paper
|
||||
-- For the time being, we will assume PERMZ equal to PERMX and PERMY:
|
||||
100*500 100*50 100*200 /
|
||||
ECHO
|
||||
|
||||
PROPS
|
||||
-- -------------------------------------------------------------------------
|
||||
|
||||
PVTW
|
||||
-- Item 1: pressure reference (psia)
|
||||
-- Item 2: water FVF (rb per bbl or rb per stb)
|
||||
-- Item 3: water compressibility (psi^{-1})
|
||||
-- Item 4: water viscosity (cp)
|
||||
-- Item 5: water 'viscosibility' (psi^{-1})
|
||||
|
||||
-- Using values from Norne:
|
||||
-- In METRIC units:
|
||||
-- 277.0 1.038 4.67E-5 0.318 0.0 /
|
||||
-- In FIELD units:
|
||||
4017.55 1.038 3.22E-6 0.318 0.0 /
|
||||
|
||||
ROCK
|
||||
-- Item 1: reference pressure (psia)
|
||||
-- Item 2: rock compressibility (psi^{-1})
|
||||
|
||||
-- Using values from table 1 in Odeh:
|
||||
14.7 3E-6 /
|
||||
|
||||
SWOF
|
||||
-- Column 1: water saturation
|
||||
-- - this has been set to (almost) equally spaced values from 0.12 to 1
|
||||
-- Column 2: water relative permeability
|
||||
-- - generated from the Corey-type approx. formula
|
||||
-- the coeffisient is set to 10e-5, S_{orw}=0 and S_{wi}=0.12
|
||||
-- Column 3: oil relative permeability when only oil and water are present
|
||||
-- - we will use the same values as in column 3 in SGOF.
|
||||
-- This is not really correct, but since only the first
|
||||
-- two values are of importance, this does not really matter
|
||||
-- Column 4: water-oil capillary pressure (psi)
|
||||
|
||||
0.12 0 1 0
|
||||
0.18 4.64876033057851E-008 1 0
|
||||
0.24 0.000000186 0.997 0
|
||||
0.3 4.18388429752066E-007 0.98 0
|
||||
0.36 7.43801652892562E-007 0.7 0
|
||||
0.42 1.16219008264463E-006 0.35 0
|
||||
0.48 1.67355371900826E-006 0.2 0
|
||||
0.54 2.27789256198347E-006 0.09 0
|
||||
0.6 2.97520661157025E-006 0.021 0
|
||||
0.66 3.7654958677686E-006 0.01 0
|
||||
0.72 4.64876033057851E-006 0.001 0
|
||||
0.78 0.000005625 0.0001 0
|
||||
0.84 6.69421487603306E-006 0 0
|
||||
0.91 8.05914256198347E-006 0 0
|
||||
1 0.00001 0 0 /
|
||||
|
||||
|
||||
SGOF
|
||||
-- Column 1: gas saturation
|
||||
-- Column 2: gas relative permeability
|
||||
-- Column 3: oil relative permeability when oil, gas and connate water are present
|
||||
-- Column 4: oil-gas capillary pressure (psi)
|
||||
-- - stated to be zero in Odeh's paper
|
||||
|
||||
-- Values in column 1-3 are taken from table 3 in Odeh's paper:
|
||||
0 0 1 0
|
||||
0.001 0 1 0
|
||||
0.02 0 0.997 0
|
||||
0.05 0.005 0.980 0
|
||||
0.12 0.025 0.700 0
|
||||
0.2 0.075 0.350 0
|
||||
0.25 0.125 0.200 0
|
||||
0.3 0.190 0.090 0
|
||||
0.4 0.410 0.021 0
|
||||
0.45 0.60 0.010 0
|
||||
0.5 0.72 0.001 0
|
||||
0.6 0.87 0.0001 0
|
||||
0.7 0.94 0.000 0
|
||||
0.85 0.98 0.000 0
|
||||
0.88 0.984 0.000 0 /
|
||||
--1.00 1.0 0.000 0 /
|
||||
-- Warning from Eclipse: first sat. value in SWOF + last sat. value in SGOF
|
||||
-- must not be greater than 1, but Eclipse still runs
|
||||
-- Flow needs the sum to be excactly 1 so I added a row with gas sat. = 0.88
|
||||
-- The corresponding krg value was estimated by assuming linear rel. between
|
||||
-- gas sat. and krw. between gas sat. 0.85 and 1.00 (the last two values given)
|
||||
|
||||
DENSITY
|
||||
-- Density (lb per ft³) at surface cond. of
|
||||
-- oil, water and gas, respectively (in that order)
|
||||
|
||||
-- Using values from Norne:
|
||||
-- In METRIC units:
|
||||
-- 859.5 1033.0 0.854 /
|
||||
-- In FIELD units:
|
||||
53.66 64.49 0.0533 /
|
||||
|
||||
PVDG
|
||||
-- Column 1: gas phase pressure (psia)
|
||||
-- Column 2: gas formation volume factor (rb per Mscf)
|
||||
-- - in Odeh's paper the units are said to be given in rb per bbl,
|
||||
-- but this is assumed to be a mistake: FVF-values in Odeh's paper
|
||||
-- are given in rb per scf, not rb per bbl. This will be in
|
||||
-- agreement with conventions
|
||||
-- Column 3: gas viscosity (cP)
|
||||
|
||||
-- Using values from lower right table in Odeh's table 2:
|
||||
14.700 166.666 0.008000
|
||||
264.70 12.0930 0.009600
|
||||
514.70 6.27400 0.011200
|
||||
1014.7 3.19700 0.014000
|
||||
2014.7 1.61400 0.018900
|
||||
2514.7 1.29400 0.020800
|
||||
3014.7 1.08000 0.022800
|
||||
4014.7 0.81100 0.026800
|
||||
5014.7 0.64900 0.030900
|
||||
9014.7 0.38600 0.047000 /
|
||||
|
||||
PVTO
|
||||
-- Column 1: dissolved gas-oil ratio (Mscf per stb)
|
||||
-- Column 2: bubble point pressure (psia)
|
||||
-- Column 3: oil FVF for saturated oil (rb per stb)
|
||||
-- Column 4: oil viscosity for saturated oil (cP)
|
||||
|
||||
-- Use values from top left table in Odeh's table 2:
|
||||
0.0010 14.7 1.0620 1.0400 /
|
||||
0.0905 264.7 1.1500 0.9750 /
|
||||
0.1800 514.7 1.2070 0.9100 /
|
||||
0.3710 1014.7 1.2950 0.8300 /
|
||||
0.6360 2014.7 1.4350 0.6950 /
|
||||
0.7750 2514.7 1.5000 0.6410 /
|
||||
0.9300 3014.7 1.5650 0.5940 /
|
||||
1.2700 4014.7 1.6950 0.5100
|
||||
9014.7 1.5790 0.7400 /
|
||||
1.6180 5014.7 1.8270 0.4490
|
||||
9014.7 1.7370 0.6310 /
|
||||
-- It is required to enter data for undersaturated oil for the highest GOR
|
||||
-- (i.e. the last row) in the PVTO table.
|
||||
-- In order to fulfill this requirement, values for oil FVF and viscosity
|
||||
-- at 9014.7psia and GOR=1.618 for undersaturated oil have been approximated:
|
||||
-- It has been assumed that there is a linear relation between the GOR
|
||||
-- and the FVF when keeping the pressure constant at 9014.7psia.
|
||||
-- From Odeh we know that (at 9014.7psia) the FVF is 2.357 at GOR=2.984
|
||||
-- for saturated oil and that the FVF is 1.579 at GOR=1.27 for undersaturated oil,
|
||||
-- so it is possible to use the assumption described above.
|
||||
-- An equivalent approximation for the viscosity has been used.
|
||||
/
|
||||
|
||||
SOLUTION
|
||||
-- -------------------------------------------------------------------------
|
||||
|
||||
EQUIL
|
||||
-- Item 1: datum depth (ft)
|
||||
-- Item 2: pressure at datum depth (psia)
|
||||
-- - Odeh's table 1 says that initial reservoir pressure is
|
||||
-- 4800 psi at 8400ft, which explains choice of item 1 and 2
|
||||
-- Item 3: depth of water-oil contact (ft)
|
||||
-- - chosen to be directly under the reservoir
|
||||
-- Item 4: oil-water capillary pressure at the water oil contact (psi)
|
||||
-- - given to be 0 in Odeh's paper
|
||||
-- Item 5: depth of gas-oil contact (ft)
|
||||
-- - chosen to be directly above the reservoir
|
||||
-- Item 6: gas-oil capillary pressure at gas-oil contact (psi)
|
||||
-- - given to be 0 in Odeh's paper
|
||||
-- Item 7: RSVD-table
|
||||
-- Item 8: RVVD-table
|
||||
-- Item 9: Set to 0 as this is the only value supported by OPM
|
||||
|
||||
-- Item #: 1 2 3 4 5 6 7 8 9
|
||||
8400 4800 8450 0 8300 0 1 0 0 /
|
||||
|
||||
RSVD
|
||||
-- Dissolved GOR is initially constant with depth through the reservoir.
|
||||
-- The reason is that the initial reservoir pressure given is higher
|
||||
---than the bubble point presssure of 4014.7psia, meaning that there is no
|
||||
-- free gas initially present.
|
||||
8300 1.270
|
||||
8450 1.270 /
|
||||
|
||||
SUMMARY
|
||||
-- -------------------------------------------------------------------------
|
||||
|
||||
FOPR
|
||||
|
||||
WGOR
|
||||
/
|
||||
WOPR
|
||||
/
|
||||
WWPR
|
||||
/
|
||||
WWCT
|
||||
/
|
||||
|
||||
|
||||
FGOR
|
||||
|
||||
-- 2a) Pressures of the cell where the injector and producer are located
|
||||
BPR
|
||||
1 1 1 /
|
||||
10 10 3 /
|
||||
/
|
||||
|
||||
-- 2b) Gas saturation at grid points given in Odeh's paper
|
||||
BGSAT
|
||||
1 1 1 /
|
||||
1 1 2 /
|
||||
1 1 3 /
|
||||
10 1 1 /
|
||||
10 1 2 /
|
||||
10 1 3 /
|
||||
10 10 1 /
|
||||
10 10 2 /
|
||||
10 10 3 /
|
||||
/
|
||||
|
||||
-- In order to compare Eclipse with Flow:
|
||||
WBHP
|
||||
/
|
||||
|
||||
WGIR
|
||||
'INJ'
|
||||
/
|
||||
|
||||
WGIT
|
||||
'INJ'
|
||||
/
|
||||
|
||||
WGPR
|
||||
/
|
||||
|
||||
WGPT
|
||||
/
|
||||
|
||||
WOPR
|
||||
/
|
||||
|
||||
WOPT
|
||||
/
|
||||
|
||||
WWIR
|
||||
/
|
||||
WWIT
|
||||
/
|
||||
WWPR
|
||||
/
|
||||
WWPT
|
||||
/
|
||||
SCHEDULE
|
||||
-- -------------------------------------------------------------------------
|
||||
RPTSCHED
|
||||
'PRES' 'SGAS' 'RS' 'WELLS' 'WELSPECS' /
|
||||
|
||||
RPTRST
|
||||
'BASIC=1' /
|
||||
|
||||
UDQ
|
||||
DEFINE WUPR3 SORTA(WOPR 'P*') /
|
||||
/
|
||||
|
||||
|
||||
|
||||
|
||||
-- If no resolution (i.e. case 1), the two following lines must be added:
|
||||
DRSDT
|
||||
0 /
|
||||
-- if DRSDT is set to 0, GOR cannot rise and free gas does not
|
||||
-- dissolve in undersaturated oil -> constant bubble point pressure
|
||||
|
||||
WELSPECS
|
||||
-- Item #: 1 2 3 4 5 6
|
||||
'P1' 'G1' 3 3 8400 'OIL' /
|
||||
'P2' 'G1' 4 4 8400 'OIL' /
|
||||
'P3' 'G1' 5 5 8400 'OIL' /
|
||||
'P4' 'G1' 6 6 8400 'OIL' /
|
||||
'INJ' 'G1' 1 1 8335 'GAS' /
|
||||
/
|
||||
-- Coordinates in item 3-4 are retrieved from Odeh's figure 1 and 2
|
||||
-- Note that the depth at the midpoint of the well grid blocks
|
||||
-- has been used as reference depth for bottom hole pressure in item 5
|
||||
|
||||
COMPDAT
|
||||
-- Item #: 1 2 3 4 5 6 7 8 9
|
||||
'P1' 3 3 3 3 'OPEN' 1* 1* 0.5 /
|
||||
'P2' 4 4 3 3 'OPEN' 1* 1* 0.5 /
|
||||
'P3' 5 5 3 3 'OPEN' 1* 1* 0.5 /
|
||||
'P4' 6 6 3 3 'OPEN' 1* 1* 0.5 /
|
||||
'INJ' 1 1 1 1 'OPEN' 1* 1* 0.5 /
|
||||
/
|
||||
-- Coordinates in item 2-5 are retreived from Odeh's figure 1 and 2
|
||||
-- Item 9 is the well bore internal diameter,
|
||||
-- the radius is given to be 0.25ft in Odeh's paper
|
||||
|
||||
|
||||
WCONPROD
|
||||
-- Item #:1 2 3 4 5 9
|
||||
'P1' 'OPEN' 'ORAT' 5000 4* 1000 /
|
||||
'P2' 'OPEN' 'ORAT' 5000 4* 1000 /
|
||||
'P3' 'OPEN' 'ORAT' 5000 4* 1000 /
|
||||
'P4' 'OPEN' 'ORAT' 5000 4* 1000 /
|
||||
/
|
||||
|
||||
-- It is stated in Odeh's paper that the maximum oil prod. rate
|
||||
-- is 20 000stb per day which explains the choice of value in item 4.
|
||||
-- The items > 4 are defaulted with the exception of item 9,
|
||||
-- the BHP lower limit, which is given to be 1000psia in Odeh's paper
|
||||
|
||||
WCONINJE
|
||||
-- Item #:1 2 3 4 5 6 7
|
||||
'INJ' 'GAS' 'OPEN' 'RATE' 100000 1* 9014 /
|
||||
/
|
||||
-- Stated in Odeh that gas inj. rate (item 5) is 100MMscf per day
|
||||
-- BHP upper limit (item 7) should not be exceeding the highest
|
||||
-- pressure in the PVT table=9014.7psia (default is 100 000psia)
|
||||
|
||||
DATES
|
||||
1 'JAN' 2015 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'FEB' 2015 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'MAR' 2015 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'APR' 2015 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'MAI' 2015 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'JUN' 2015 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'JUL' 2015 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'AUG' 2015 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'SEP' 2015 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'OCT' 2015 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'NOV' 2015 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'DEC' 2015 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'JAN' 2016 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'FEB' 2016 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'MAR' 2016 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'APR' 2016 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'MAI' 2016 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'JUN' 2016 /
|
||||
/
|
||||
|
||||
ACTIONX
|
||||
'SHUT_WELL' 100000 /
|
||||
WUPR3 'P*' = 1 /
|
||||
/
|
||||
|
||||
WELOPEN
|
||||
'?' 'SHUT' /
|
||||
/
|
||||
|
||||
ENDACTIO
|
||||
|
||||
|
||||
DATES
|
||||
1 'JUL' 2016 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'AUG' 2016 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'SEP' 2016 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'OCT' 2016 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'NOV' 2016 /
|
||||
/
|
||||
|
||||
DATES
|
||||
1 'DEC' 2016 /
|
||||
/
|
||||
|
||||
|
||||
END
|
||||
)";
|
@ -72,6 +72,12 @@ double prod_opr(const EclipseState& es, const Schedule& sched, const data::Solu
|
||||
return -units.to_si(UnitSystem::measure::rate, oil_rate);
|
||||
}
|
||||
|
||||
double prod_opr_low(const EclipseState& es, const Schedule& sched, const data::Solution& sol, size_t report_step, double seconds_elapsed) {
|
||||
const auto& units = es.getUnits();
|
||||
double oil_rate = 0.5;
|
||||
return -units.to_si(UnitSystem::measure::rate, oil_rate);
|
||||
}
|
||||
|
||||
double prod_wpr_P1(const EclipseState& es, const Schedule& sched, const data::Solution& sol, size_t report_step, double seconds_elapsed) {
|
||||
const auto& units = es.getUnits();
|
||||
double water_rate = 0.0;
|
||||
@ -102,6 +108,42 @@ double prod_wpr_P4(const EclipseState& es, const Schedule& sched, const data::S
|
||||
return -units.to_si(UnitSystem::measure::rate, water_rate);
|
||||
}
|
||||
|
||||
/*
|
||||
The deck tested here has a UDQ DEFINE statement which sorts the wells after
|
||||
oil production rate, and then subsequently closes the well with lowest OPR
|
||||
with a ACTIONX keyword.
|
||||
*/
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQ_SORTA_EXAMPLE) {
|
||||
#include "actionx2.include"
|
||||
|
||||
test_data td( actionx );
|
||||
msim sim(td.state);
|
||||
{
|
||||
test_work_area_type * work_area = test_work_area_alloc("test_msim");
|
||||
EclipseIO io(td.state, td.state.getInputGrid(), td.schedule, td.summary_config);
|
||||
|
||||
sim.well_rate("P1", data::Rates::opt::oil, prod_opr);
|
||||
sim.well_rate("P2", data::Rates::opt::oil, prod_opr);
|
||||
sim.well_rate("P3", data::Rates::opt::oil, prod_opr);
|
||||
sim.well_rate("P4", data::Rates::opt::oil, prod_opr_low);
|
||||
|
||||
sim.run(td.schedule, io);
|
||||
{
|
||||
const auto& w1 = td.schedule.getWell("P1");
|
||||
const auto& w4 = td.schedule.getWell("P4");
|
||||
std::size_t last_step = td.schedule.size() - 1;
|
||||
|
||||
BOOST_CHECK_EQUAL(w1->getStatus(1), WellCommon::StatusEnum::OPEN );
|
||||
BOOST_CHECK_EQUAL(w4->getStatus(1), WellCommon::StatusEnum::OPEN );
|
||||
BOOST_CHECK_EQUAL(w1->getStatus(last_step), WellCommon::StatusEnum::OPEN );
|
||||
BOOST_CHECK_EQUAL(w4->getStatus(last_step), WellCommon::StatusEnum::SHUT );
|
||||
}
|
||||
|
||||
test_work_area_free(work_area);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(WELL_CLOSE_EXAMPLE) {
|
||||
#include "actionx1.include"
|
||||
@ -204,3 +246,43 @@ BOOST_AUTO_TEST_CASE(UDQ_ASSIGN) {
|
||||
test_work_area_free(work_area);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQ_WUWCT) {
|
||||
#include "actionx1.include"
|
||||
|
||||
test_data td( actionx1 );
|
||||
msim sim(td.state);
|
||||
{
|
||||
test_work_area_type * work_area = test_work_area_alloc("test_msim");
|
||||
EclipseIO io(td.state, td.state.getInputGrid(), td.schedule, td.summary_config);
|
||||
|
||||
sim.well_rate("P1", data::Rates::opt::oil, prod_opr);
|
||||
sim.well_rate("P2", data::Rates::opt::oil, prod_opr);
|
||||
sim.well_rate("P3", data::Rates::opt::oil, prod_opr);
|
||||
sim.well_rate("P4", data::Rates::opt::oil, prod_opr_low);
|
||||
|
||||
sim.well_rate("P1", data::Rates::opt::wat, prod_wpr_P1);
|
||||
sim.well_rate("P2", data::Rates::opt::wat, prod_wpr_P2);
|
||||
sim.well_rate("P3", data::Rates::opt::wat, prod_wpr_P3);
|
||||
sim.well_rate("P4", data::Rates::opt::wat, prod_wpr_P4);
|
||||
|
||||
sim.run(td.schedule, io);
|
||||
|
||||
const auto& base_name = td.state.getIOConfig().getBaseName();
|
||||
ecl_sum_type * ecl_sum = ecl_sum_fread_alloc_case( base_name.c_str(), ":");
|
||||
for (const auto& well : {"P1", "P2", "P3", "P4"}) {
|
||||
std::string wwct_key = std::string("WWCT:") + well;
|
||||
std::string wuwct_key = std::string("WUWCT:") + well;
|
||||
|
||||
BOOST_CHECK( ecl_sum_has_general_var(ecl_sum, wwct_key.c_str()));
|
||||
BOOST_CHECK( ecl_sum_has_general_var(ecl_sum, wuwct_key.c_str()));
|
||||
|
||||
for (int step = 0; step < ecl_sum_get_data_length(ecl_sum); step++)
|
||||
BOOST_CHECK_EQUAL( ecl_sum_get_general_var(ecl_sum, step, wwct_key.c_str()),
|
||||
ecl_sum_get_general_var(ecl_sum, step, wuwct_key.c_str()));
|
||||
|
||||
}
|
||||
|
||||
test_work_area_free(work_area);
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,6 @@
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQInput.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQSet.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQWellSet.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQExpression.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQContext.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQAssign.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQFunction.hpp>
|
||||
@ -49,6 +48,28 @@ Schedule make_schedule(const std::string& input) {
|
||||
return Schedule(deck, grid , eclipseProperties, runspec);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(MIX_SCALAR) {
|
||||
UDQFunctionTable udqft;
|
||||
UDQParams udqp;
|
||||
UDQDefine def_add(udqp, "WU", {"WOPR", "+", "1"});
|
||||
SummaryState st;
|
||||
UDQContext context(udqft, st);
|
||||
|
||||
st.add_well_var("P1", "WOPR", 1);
|
||||
|
||||
auto res_add = def_add.eval_wells(context);
|
||||
BOOST_CHECK_EQUAL( res_add["P1"].value() , 2);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQ_TABLE_EXCEPTION) {
|
||||
UDQParams udqp;
|
||||
BOOST_CHECK_THROW(UDQDefine(udqp, "WU", {"TUPRICE[WOPR]"}), std::invalid_argument);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQWellSetTest) {
|
||||
std::vector<std::string> wells = {"P1", "P2", "I1", "I2"};
|
||||
UDQWellSet ws("NAME", wells);
|
||||
@ -91,6 +112,85 @@ BOOST_AUTO_TEST_CASE(UDQWellSetTest) {
|
||||
BOOST_CHECK_EQUAL(ws3[wells[i]].value(), i*1.0);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQ_DEFINETEST) {
|
||||
UDQParams udqp;
|
||||
UDQFunctionTable udqft(udqp);
|
||||
{
|
||||
UDQDefine def(udqp, "WUBHP", {"WBHP"});
|
||||
SummaryState st;
|
||||
UDQContext context(udqft, st);
|
||||
|
||||
st.add_well_var("W1", "WBHP", 11);
|
||||
st.add_well_var("W2", "WBHP", 2);
|
||||
st.add_well_var("W3", "WBHP", 3);
|
||||
auto res = def.eval_wells(context);
|
||||
BOOST_CHECK_EQUAL(res.size(), 3);
|
||||
BOOST_CHECK_EQUAL( res["W1"].value(), 11 );
|
||||
BOOST_CHECK_EQUAL( res["W2"].value(), 2 );
|
||||
BOOST_CHECK_EQUAL( res["W3"].value(), 3 );
|
||||
}
|
||||
{
|
||||
UDQDefine def(udqp, "WUBHP", {"WBHP" , "'P*'"});
|
||||
SummaryState st;
|
||||
UDQContext context(udqft, st);
|
||||
|
||||
|
||||
st.add_well_var("P1", "WBHP", 1);
|
||||
st.add_well_var("P2", "WBHP", 2);
|
||||
st.add_well_var("I1", "WBHP", 1);
|
||||
st.add_well_var("I2", "WBHP", 2);
|
||||
auto res = def.eval_wells(context);
|
||||
BOOST_CHECK_EQUAL(res.size(), 4);
|
||||
BOOST_CHECK_EQUAL( res["P1"].value(), 1 );
|
||||
BOOST_CHECK_EQUAL( res["P2"].value(), 2 );
|
||||
BOOST_CHECK_EQUAL( res["I1"].defined(), false);
|
||||
BOOST_CHECK_EQUAL( res["I1"].defined(), false);
|
||||
}
|
||||
{
|
||||
UDQDefine def(udqp, "WUBHP", {"WBHP" , "'P1'"});
|
||||
SummaryState st;
|
||||
UDQContext context(udqft, st);
|
||||
|
||||
|
||||
st.add_well_var("P1", "WBHP", 1);
|
||||
BOOST_CHECK_THROW( def.eval_wells( context ), std::invalid_argument);
|
||||
}
|
||||
|
||||
{
|
||||
UDQDefine def(udqp, "WUBHP", {"NINT" , "(", "WBHP", ")"});
|
||||
SummaryState st;
|
||||
UDQContext context(udqft, st);
|
||||
st.add_well_var("P1", "WBHP", 4);
|
||||
st.add_well_var("P2", "WBHP", 3);
|
||||
st.add_well_var("I1", "WBHP", 2);
|
||||
st.add_well_var("I2", "WBHP", 1);
|
||||
|
||||
auto res = def.eval_wells(context);
|
||||
BOOST_CHECK_EQUAL( res["P1"].value(), 4 );
|
||||
BOOST_CHECK_EQUAL( res["P2"].value(), 3 );
|
||||
BOOST_CHECK_EQUAL( res["I1"].value(), 2 );
|
||||
BOOST_CHECK_EQUAL( res["I2"].value(), 1 );
|
||||
}
|
||||
|
||||
// This should eventually fail because the SUM() function evaluates to
|
||||
// scalar context and that is not appropriate for the WUBHP variable which
|
||||
// should evaluate to a full well set.
|
||||
{
|
||||
UDQDefine def(udqp, "WUBHP", {"SUM" , "(", "WBHP", ")"});
|
||||
SummaryState st;
|
||||
UDQContext context(udqft, st);
|
||||
|
||||
|
||||
st.add_well_var("P1", "WBHP", 1);
|
||||
BOOST_CHECK_THROW( def.eval_wells( context ), std::invalid_argument);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(KEYWORDS) {
|
||||
const std::string input = R"(
|
||||
RUNSPEC
|
||||
@ -133,6 +233,11 @@ BOOST_AUTO_TEST_CASE(ENUM_CONVERSION) {
|
||||
BOOST_CHECK(UDQ::varType("RUBHP") == UDQVarType::REGION_VAR);
|
||||
BOOST_CHECK(UDQ::varType("AUBHP") == UDQVarType::AQUIFER_VAR);
|
||||
BOOST_CHECK(UDQ::varType("SUBHP") == UDQVarType::SEGMENT_VAR);
|
||||
|
||||
BOOST_REQUIRE_THROW( UDQ::actionType("INVALID_ACTION"), std::invalid_argument);
|
||||
BOOST_CHECK(UDQ::actionType("DEFINE") == UDQAction::DEFINE );
|
||||
BOOST_CHECK(UDQ::actionType("UNITS") == UDQAction::UNITS );
|
||||
BOOST_CHECK(UDQ::actionType("ASSIGN") == UDQAction::ASSIGN );
|
||||
}
|
||||
|
||||
|
||||
@ -154,6 +259,7 @@ UDQ
|
||||
UNITS WUBHP 'BARSA' /
|
||||
DEFINE FUOPR AVEG(WOPR) + 1/
|
||||
ASSIGN WUXUNIT 0.0 /
|
||||
DEFINE FUOPR AVEG(WOPR)/
|
||||
/
|
||||
|
||||
DATES
|
||||
@ -162,14 +268,14 @@ DATES
|
||||
|
||||
UDQ
|
||||
ASSIGN WUBHP 0.0 /
|
||||
DEFINE FUOPR AVEG(WOPR) + 1/
|
||||
DEFINE FUOPR AVEG(WOPR)/
|
||||
UNITS WUBHP 'BARSA' / -- Repeating the same unit multiple times is superfluous but OK
|
||||
/
|
||||
)";
|
||||
|
||||
auto schedule = make_schedule(input);
|
||||
const auto& udq = schedule.getUDQConfig(0);
|
||||
BOOST_CHECK_EQUAL(1, udq.expressions().size());
|
||||
BOOST_CHECK_EQUAL(2, udq.assignments().size());
|
||||
|
||||
BOOST_CHECK_THROW( udq.unit("NO_SUCH_KEY"), std::invalid_argument );
|
||||
BOOST_CHECK_EQUAL( udq.unit("WUBHP"), "BARSA");
|
||||
@ -233,49 +339,6 @@ UDQ
|
||||
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQ_KEYWORD) {
|
||||
// Invalid action
|
||||
BOOST_REQUIRE_THROW( UDQ::actionType("INVALID_ACTION"), std::invalid_argument);
|
||||
|
||||
// Invalid keyword
|
||||
BOOST_REQUIRE_THROW( UDQExpression(UDQAction::ASSIGN, "INVALID_KEYWORD", {}), std::invalid_argument);
|
||||
|
||||
BOOST_CHECK_NO_THROW(UDQExpression(UDQAction::ASSIGN ,"WUBHP", {"1"}));
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQ_DEFINE_DATA) {
|
||||
const std::string input = R"(
|
||||
RUNSPEC
|
||||
|
||||
UDQDIMS
|
||||
10* 'Y'/
|
||||
|
||||
UDQPARAM
|
||||
3* 0.25 /
|
||||
|
||||
SCHEDULE
|
||||
|
||||
UDQ
|
||||
DEFINE CUMW1 P12 10 12 1 (4.0 + 6*(4 - 2)) /
|
||||
DEFINE WUMW1 WBHP 'P*1*' UMAX WBHP 'P*4*' /
|
||||
/
|
||||
|
||||
|
||||
)";
|
||||
const auto schedule = make_schedule(input);
|
||||
const auto& udq = schedule.getUDQConfig(0);
|
||||
const auto& records = udq.expressions();
|
||||
const auto& rec0 = records[0];
|
||||
const auto& rec1 = records[1];
|
||||
const std::vector<std::string> exp0 = {"P12", "10", "12", "1", "(", "4.0", "+", "6", "*", "(", "4", "-", "2", ")", ")"};
|
||||
const std::vector<std::string> exp1 = {"WBHP", "P*1*", "UMAX", "WBHP" , "P*4*"};
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(rec0.tokens().begin(), rec0.tokens().end(), exp0.begin(), exp0.end());
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(rec1.tokens().begin(), rec1.tokens().end(), exp1.begin(), exp1.end());
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQ_DEFINE_WITH_SLASH) {
|
||||
const std::string input = R"(
|
||||
UDQ
|
||||
@ -339,7 +402,8 @@ ASSIGN WU2 8.0 /
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQ_CONTEXT) {
|
||||
SummaryState st;
|
||||
UDQContext ctx(st);
|
||||
UDQFunctionTable func_table;
|
||||
UDQContext ctx(func_table, st);
|
||||
BOOST_CHECK_EQUAL(ctx.get("JAN"), 1.0);
|
||||
|
||||
BOOST_REQUIRE_THROW(ctx.get("NO_SUCH_KEY"), std::out_of_range);
|
||||
@ -407,8 +471,7 @@ BOOST_AUTO_TEST_CASE(UDQ_SET) {
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQ_FUNCTION_TABLE) {
|
||||
UDQParams params;
|
||||
UDQFunctionTable udqft(params);
|
||||
UDQFunctionTable udqft;
|
||||
BOOST_CHECK(udqft.has_function("SUM"));
|
||||
BOOST_CHECK(!udqft.has_function("NO_SUCH_FUNCTION"));
|
||||
UDQSet arg("NAME", 5);
|
||||
@ -472,8 +535,7 @@ BOOST_AUTO_TEST_CASE(UDQ_FUNCTION_TABLE) {
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CMP_FUNCTIONS) {
|
||||
UDQParams params;
|
||||
UDQFunctionTable udqft(params);
|
||||
UDQFunctionTable udqft;
|
||||
UDQSet arg1("NAME", 5);
|
||||
UDQSet arg2("NAME", 5);
|
||||
UDQSet arg3("NAME", 3);
|
||||
@ -559,16 +621,14 @@ BOOST_AUTO_TEST_CASE(CMP_FUNCTIONS) {
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(BAD_CAST) {
|
||||
UDQParams params;
|
||||
UDQFunctionTable udqft(params);
|
||||
UDQFunctionTable udqft;
|
||||
|
||||
BOOST_CHECK_THROW( dynamic_cast<const UDQUnaryElementalFunction&>(udqft.get("==")), std::bad_cast);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ELEMENTAL_UNARY_FUNCTIONS) {
|
||||
UDQParams params;
|
||||
UDQFunctionTable udqft(params);
|
||||
UDQFunctionTable udqft;
|
||||
UDQSet arg("NAME", 5);
|
||||
arg.assign(0,1);
|
||||
arg.assign(2,2);
|
||||
@ -656,28 +716,27 @@ BOOST_AUTO_TEST_CASE(ELEMENTAL_UNARY_FUNCTIONS) {
|
||||
const auto& func = dynamic_cast<const UDQUnaryElementalFunction&>(udqft.get("SORTA"));
|
||||
auto result = func.eval(arg);
|
||||
|
||||
BOOST_CHECK_EQUAL( result[0].value(), 0);
|
||||
BOOST_CHECK_EQUAL( result[0].value(), 1);
|
||||
BOOST_CHECK( !result[1] );
|
||||
BOOST_CHECK_EQUAL( result[2].value(), 1);
|
||||
BOOST_CHECK_EQUAL( result[2].value(), 2);
|
||||
BOOST_CHECK( !result[3] );
|
||||
BOOST_CHECK_EQUAL( result[4].value(), 2);
|
||||
BOOST_CHECK_EQUAL( result[4].value(), 3);
|
||||
}
|
||||
{
|
||||
const auto& func = dynamic_cast<const UDQUnaryElementalFunction&>(udqft.get("SORTD"));
|
||||
auto result = func.eval(arg);
|
||||
|
||||
BOOST_CHECK_EQUAL( result[0].value(), 2);
|
||||
BOOST_CHECK_EQUAL( result[0].value(), 3);
|
||||
BOOST_CHECK( !result[1] );
|
||||
BOOST_CHECK_EQUAL( result[2].value(), 1);
|
||||
BOOST_CHECK_EQUAL( result[2].value(), 2);
|
||||
BOOST_CHECK( !result[3] );
|
||||
BOOST_CHECK_EQUAL( result[4].value(), 0);
|
||||
BOOST_CHECK_EQUAL( result[4].value(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UNION_FUNCTIONS) {
|
||||
UDQParams params;
|
||||
UDQFunctionTable udqft(params);
|
||||
UDQFunctionTable udqft;
|
||||
UDQSet arg1("NAME", 5);
|
||||
UDQSet arg2("NAME", 5);
|
||||
|
||||
@ -721,7 +780,6 @@ BOOST_AUTO_TEST_CASE(UDQ_SET_DIV) {
|
||||
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQASSIGN_TEST) {
|
||||
UDQAssign as1("WUPR", {}, 1.0);
|
||||
UDQAssign as2("WUPR", {"P*"}, 2.0);
|
||||
@ -745,3 +803,154 @@ BOOST_AUTO_TEST_CASE(UDQASSIGN_TEST) {
|
||||
BOOST_CHECK(!res3["I1"].defined());
|
||||
BOOST_CHECK(!res3["I2"].defined());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQ_POW_TEST) {
|
||||
UDQFunctionTable udqft;
|
||||
UDQParams udqp;
|
||||
UDQDefine def_pow1(udqp, "WU", {"WOPR", "+", "WWPR", "*", "WGOR", "^", "WWIR"});
|
||||
UDQDefine def_pow2(udqp, "WU", {"(", "WOPR", "+", "WWPR", ")", "^", "(", "WOPR", "+" , "WGOR", "*", "WWIR", "-", "WOPT", ")"});
|
||||
SummaryState st;
|
||||
UDQContext context(udqft, st);
|
||||
|
||||
st.add_well_var("P1", "WOPR", 1);
|
||||
st.add_well_var("P1", "WWPR", 2);
|
||||
st.add_well_var("P1", "WGOR", 3);
|
||||
st.add_well_var("P1", "WWIR", 4);
|
||||
st.add_well_var("P1", "WOPT", 7);
|
||||
|
||||
auto res_pow1 = def_pow1.eval_wells(context);
|
||||
auto res_pow2 = def_pow2.eval_wells(context);
|
||||
BOOST_CHECK_EQUAL( res_pow1["P1"].value() , 1 + 2 * std::pow(3,4));
|
||||
BOOST_CHECK_EQUAL( res_pow2["P1"].value() , std::pow(1 + 2, 1 + 3*4 - 7));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQ_CMP_TEST) {
|
||||
UDQFunctionTable udqft;
|
||||
UDQParams udqp;
|
||||
UDQDefine def_cmp(udqp, "WU", {"WOPR", ">", "WWPR", "+", "WGOR", "*", "WWIR"});
|
||||
SummaryState st;
|
||||
UDQContext context(udqft, st);
|
||||
|
||||
st.add_well_var("P1", "WOPR", 0);
|
||||
st.add_well_var("P1", "WWPR", 10);
|
||||
st.add_well_var("P1", "WGOR", -3);
|
||||
st.add_well_var("P1", "WWIR", 4);
|
||||
|
||||
st.add_well_var("P2", "WOPR", 0);
|
||||
st.add_well_var("P2", "WWPR", -2);
|
||||
st.add_well_var("P2", "WGOR", 4);
|
||||
st.add_well_var("P2", "WWIR", 1);
|
||||
|
||||
auto res_cmp = def_cmp.eval_wells(context);
|
||||
BOOST_CHECK_EQUAL( res_cmp["P1"].value() , 1.0);
|
||||
BOOST_CHECK_EQUAL( res_cmp["P2"].value() , 0.0);
|
||||
}
|
||||
|
||||
/*BOOST_AUTO_TEST_CASE(UDQPARSE_ERROR) {
|
||||
setUDQFunctionTable udqft;
|
||||
UDQDefine def1(udqft, "WUBHP", {"WWCT", "+"});
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQ_BASIC_MATH_TEST) {
|
||||
UDQParams udqp;
|
||||
UDQFunctionTable udqft;
|
||||
UDQDefine def_add(udqp, "WU2OPR", {"WOPR", "+", "WOPR"});
|
||||
UDQDefine def_sub(udqp, "WU2OPR", {"WOPR", "-", "WOPR"});
|
||||
UDQDefine def_mul(udqp, "WU2OPR", {"WOPR", "*", "WOPR"});
|
||||
UDQDefine def_div(udqp, "WU2OPR", {"WOPR", "/", "WOPR"});
|
||||
UDQDefine def_muladd(udqp , "WUX", {"WOPR", "+", "WOPR", "*", "WOPR"});
|
||||
UDQDefine def_wuwct(udqp , "WUWCT", {"WWPR", "/", "(", "WOPR", "+", "WWPR", ")"});
|
||||
SummaryState st;
|
||||
UDQContext context(udqft, st);
|
||||
|
||||
st.add_well_var("P1", "WOPR", 1);
|
||||
st.add_well_var("P2", "WOPR", 2);
|
||||
st.add_well_var("P3", "WOPR", 3);
|
||||
st.add_well_var("P4", "WOPR", 4);
|
||||
|
||||
st.add_well_var("P1", "WWPR", 1);
|
||||
st.add_well_var("P2", "WWPR", 2);
|
||||
st.add_well_var("P3", "WWPR", 3);
|
||||
st.add_well_var("P4", "WWPR", 4);
|
||||
|
||||
auto res_add = def_add.eval_wells(context);
|
||||
BOOST_CHECK_EQUAL( res_add.size(), 4);
|
||||
BOOST_CHECK_EQUAL( res_add["P1"].value(), 2);
|
||||
BOOST_CHECK_EQUAL( res_add["P2"].value(), 4);
|
||||
BOOST_CHECK_EQUAL( res_add["P3"].value(), 6);
|
||||
BOOST_CHECK_EQUAL( res_add["P4"].value(), 8);
|
||||
|
||||
auto res_sub = def_sub.eval_wells(context);
|
||||
BOOST_CHECK_EQUAL( res_sub.size(), 4);
|
||||
BOOST_CHECK_EQUAL( res_sub["P1"].value(), 0);
|
||||
BOOST_CHECK_EQUAL( res_sub["P2"].value(), 0);
|
||||
BOOST_CHECK_EQUAL( res_sub["P3"].value(), 0);
|
||||
BOOST_CHECK_EQUAL( res_sub["P4"].value(), 0);
|
||||
|
||||
auto res_div = def_div.eval_wells(context);
|
||||
BOOST_CHECK_EQUAL( res_div.size(), 4);
|
||||
BOOST_CHECK_EQUAL( res_div["P1"].value(), 1);
|
||||
BOOST_CHECK_EQUAL( res_div["P2"].value(), 1);
|
||||
BOOST_CHECK_EQUAL( res_div["P3"].value(), 1);
|
||||
BOOST_CHECK_EQUAL( res_div["P4"].value(), 1);
|
||||
|
||||
auto res_mul = def_mul.eval_wells(context);
|
||||
BOOST_CHECK_EQUAL( res_mul.size(), 4);
|
||||
BOOST_CHECK_EQUAL( res_mul["P1"].value(), 1);
|
||||
BOOST_CHECK_EQUAL( res_mul["P2"].value(), 4);
|
||||
BOOST_CHECK_EQUAL( res_mul["P3"].value(), 9);
|
||||
BOOST_CHECK_EQUAL( res_mul["P4"].value(),16);
|
||||
|
||||
auto res_muladd = def_muladd.eval_wells(context);
|
||||
BOOST_CHECK_EQUAL( res_muladd.size(), 4);
|
||||
BOOST_CHECK_EQUAL( res_muladd["P1"].value(), 1 + 1);
|
||||
BOOST_CHECK_EQUAL( res_muladd["P2"].value(), 4 + 2);
|
||||
BOOST_CHECK_EQUAL( res_muladd["P3"].value(), 9 + 3);
|
||||
BOOST_CHECK_EQUAL( res_muladd["P4"].value(),16 + 4);
|
||||
|
||||
auto res_wuwct= def_wuwct.eval_wells(context);
|
||||
BOOST_CHECK_EQUAL( res_wuwct.size(), 4);
|
||||
BOOST_CHECK_EQUAL( res_wuwct["P1"].value(),0.50);
|
||||
BOOST_CHECK_EQUAL( res_wuwct["P2"].value(),0.50);
|
||||
BOOST_CHECK_EQUAL( res_wuwct["P3"].value(),0.50);
|
||||
BOOST_CHECK_EQUAL( res_wuwct["P4"].value(),0.50);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQPARSE_TEST1) {
|
||||
UDQParams udqp;
|
||||
UDQDefine def1(udqp, "WUBHP", {"1/(WWCT", "'W1*')"});
|
||||
std::vector<std::string> tokens1 = {"1", "/", "(", "WWCT", "W1*", ")"};
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(tokens1.begin(), tokens1.end(),
|
||||
def1.tokens.begin(), def1.tokens.end());
|
||||
|
||||
|
||||
UDQDefine def2(udqp, "WUBHP", {"2*(1", "+" , "WBHP)"});
|
||||
std::vector<std::string> tokens2 = {"2", "*", "(", "1", "+", "WBHP", ")"};
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(tokens2.begin(), tokens2.end(),
|
||||
def2.tokens.begin(), def2.tokens.end());
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQPARSE_PARSECONTEXT) {
|
||||
UDQParams udqp;
|
||||
ParseContext parseContext;
|
||||
ErrorGuard errors;
|
||||
std::vector<std::string> tokens = {"WBHP", "+"};
|
||||
parseContext.update(ParseContext::UDQ_PARSE_ERROR, InputError::IGNORE);
|
||||
{
|
||||
UDQDefine def1(udqp, "WUBHP", tokens, parseContext, errors);
|
||||
SummaryState st;
|
||||
UDQContext context(UDQFunctionTable(udqp), st);
|
||||
st.add_well_var("P1", "WOPR", 1);
|
||||
printf("Have returned with def1 \n");
|
||||
|
||||
auto res = def1.eval_wells(context);
|
||||
BOOST_CHECK_EQUAL(res["P1"].value(), udqp.undefinedValue());
|
||||
}
|
||||
|
||||
parseContext.update(ParseContext::UDQ_PARSE_ERROR, InputError::THROW_EXCEPTION);
|
||||
BOOST_CHECK_THROW( UDQDefine(udqp, "WUBHP", tokens, parseContext, errors), std::invalid_argument);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user