Merge pull request #3375 from bska/eval-seg-udq

Compute Segment Level UDQ Values
This commit is contained in:
Markus Blatt
2023-10-06 15:35:23 +02:00
committed by GitHub
22 changed files with 1287 additions and 509 deletions

View File

@@ -128,7 +128,12 @@ void msim::run_step(WellTestState& wtest_state, UDQState& udq_state, data::Solut
/* inplace = */ {});
this->schedule.getUDQConfig(report_step - 1)
.eval(report_step, this->schedule, schedule.wellMatcher(report_step), this->st, udq_state);
.eval(report_step,
this->schedule,
this->schedule.wellMatcher(report_step),
this->schedule.segmentMatcherFactory(report_step),
this->st,
udq_state);
this->output(wtest_state,
udq_state,

View File

@@ -65,6 +65,7 @@ namespace Opm
class Python;
class RPTConfig;
class SCHEDULESection;
class SegmentMatcher;
struct SimulatorUpdate;
class SummaryState;
class TracerConfig;
@@ -237,6 +238,7 @@ namespace Opm
bool hasWell(const std::string& wellName, std::size_t timeStep) const;
WellMatcher wellMatcher(std::size_t report_step) const;
std::function<std::unique_ptr<SegmentMatcher>()> segmentMatcherFactory(std::size_t report_step) const;
std::vector<std::string> wellNames(const std::string& pattern, std::size_t timeStep, const std::vector<std::string>& matching_wells = {}) const;
std::vector<std::string> wellNames(const std::string& pattern) const;
std::vector<std::string> wellNames(std::size_t timeStep) const;

View File

@@ -91,6 +91,9 @@ private:
UDQSet eval_group_expression(const std::string& string_value,
const UDQContext& context) const;
UDQSet eval_segment_expression(const std::string& string_value,
const UDQContext& context) const;
UDQSet eval_scalar_function(const UDQVarType target_type,
const UDQContext& context) const;

View File

@@ -31,7 +31,9 @@
#include <opm/input/eclipse/EclipseState/Util/IOrderSet.hpp>
#include <cstddef>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
@@ -42,6 +44,7 @@ namespace Opm {
class DeckRecord;
class KeywordLocation;
class Schedule;
class SegmentMatcher;
class SummaryState;
class UDQState;
class WellMatcher;
@@ -57,6 +60,8 @@ namespace Opm {
class UDQConfig
{
public:
using SegmentMatcherFactory = std::function<std::unique_ptr<SegmentMatcher>()>;
UDQConfig() = default;
explicit UDQConfig(const UDQParams& params);
UDQConfig(const UDQParams& params, const RestartIO::RstState& rst_state);
@@ -66,18 +71,53 @@ namespace Opm {
const std::string& unit(const std::string& key) const;
bool has_unit(const std::string& keyword) const;
bool has_keyword(const std::string& keyword) const;
void add_record(const DeckRecord& record, const KeywordLocation& location, std::size_t report_step);
void add_unit(const std::string& keyword, const std::string& unit);
void add_update(const std::string& keyword, std::size_t report_step, const KeywordLocation& location, const std::vector<std::string>& data);
void add_assign(const std::string& quantity, const std::vector<std::string>& selector, double value, std::size_t report_step);
void add_assign(const std::string& quantity, const std::unordered_set<std::string>& selector, double value, std::size_t report_step);
void add_define(const std::string& quantity, const KeywordLocation& location, const std::vector<std::string>& expression, std::size_t report_step);
void add_record(SegmentMatcherFactory create_segment_matcher,
const DeckRecord& record,
const KeywordLocation& location,
std::size_t report_step);
void add_unit(const std::string& keyword,
const std::string& unit);
void add_update(const std::string& keyword,
std::size_t report_step,
const KeywordLocation& location,
const std::vector<std::string>& data);
void add_assign(const std::string& quantity,
SegmentMatcherFactory create_segment_matcher,
const std::vector<std::string>& selector,
double value,
std::size_t report_step);
void add_assign(const std::string& quantity,
const std::unordered_set<std::string>& selector,
double value,
std::size_t report_step);
void add_define(const std::string& quantity,
const KeywordLocation& location,
const std::vector<std::string>& expression,
std::size_t report_step);
void eval_assign(std::size_t report_step,
const Schedule& sched,
const WellMatcher& wm,
SegmentMatcherFactory create_segment_matcher,
SummaryState& st,
UDQState& udq_state) const;
void eval(std::size_t report_step,
const Schedule& sched,
const WellMatcher& wm,
SegmentMatcherFactory create_segment_matcher,
SummaryState& st,
UDQState& udq_state) const;
void eval_assign(std::size_t report_step, const Schedule& sched, const WellMatcher& wm, SummaryState& st, UDQState& udq_state) const;
void eval(std::size_t report_step, const Schedule& sched, const WellMatcher& wm, SummaryState& st, UDQState& udq_state) const;
const UDQDefine& define(const std::string& key) const;
const UDQAssign& assign(const std::string& key) const;
std::vector<UDQDefine> definitions() const;
std::vector<UDQDefine> definitions(UDQVarType var_type) const;
std::vector<UDQInput> input() const;
@@ -85,7 +125,7 @@ namespace Opm {
// The size() method will return the number of active DEFINE and ASSIGN
// statements; this will correspond to the length of the vector returned
// from input().
size_t size() const;
std::size_t size() const;
UDQInput operator[](const std::string& keyword) const;
UDQInput operator[](std::size_t insert_index) const;
@@ -107,18 +147,15 @@ namespace Opm {
serializer(units);
serializer(input_index);
serializer(type_count);
// The UDQFunction table is constant up to udq_params.
// So we can just construct a new instance here.
if (!serializer.isSerializing())
// The UDQFunction table is constant up to udq_params, so we can
// just construct a new instance here.
if (!serializer.isSerializing()) {
udqft = UDQFunctionTable(udq_params);
}
}
private:
void add_node(const std::string& quantity, UDQAction action);
UDQAction action_type(const std::string& udq_key) const;
void eval_assign(std::size_t report_step, const Schedule& sched, UDQState& udq_state, UDQContext& context) const;
void eval_define(std::size_t report_step, UDQState& udq_state, UDQContext& context) const;
UDQParams udq_params;
UDQFunctionTable udqft;
@@ -136,9 +173,31 @@ namespace Opm {
IOrderSet<std::string> define_order;
OrderedMap<UDQIndex> input_index;
std::map<UDQVarType, std::size_t> type_count;
void add_node(const std::string& quantity, UDQAction action);
UDQAction action_type(const std::string& udq_key) const;
void eval_assign(std::size_t report_step,
const Schedule& sched,
UDQState& udq_state,
UDQContext& context) const;
void eval_define(std::size_t report_step,
UDQState& udq_state,
UDQContext& context) const;
void add_named_assign(const std::string& quantity,
const std::vector<std::string>& selector,
double value,
std::size_t report_step);
void add_enumerated_assign(const std::string& quantity,
SegmentMatcherFactory create_segment_matcher,
const std::vector<std::string>& selector,
double value,
std::size_t report_step);
};
}
} // namespace Opm
#endif
#endif // UDQINPUT_HPP_

View File

@@ -17,49 +17,74 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UDQ_CONTEXT_HPP
#define UDQ_CONTEXT_HPP
#include <cstddef>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
#include <opm/input/eclipse/Schedule/UDQ/UDQParams.hpp>
namespace Opm {
class SegmentMatcher;
class SegmentSet;
class SummaryState;
class UDQFunctionTable;
class UDQSet;
class UDQState;
class WellMatcher;
class UDQContext {
} // namespace Opm
namespace Opm {
class UDQContext
{
public:
UDQContext(const UDQFunctionTable& udqft, const WellMatcher& wm, SummaryState& summary_state, UDQState& udq_state);
using SegmentMatcherFactory = std::function<std::unique_ptr<SegmentMatcher>()>;
UDQContext(const UDQFunctionTable& udqft,
const WellMatcher& wm,
SegmentMatcherFactory create_segment_matcher,
SummaryState& summary_state,
UDQState& udq_state);
std::optional<double> get(const std::string& key) const;
std::optional<double> get_well_var(const std::string& well, const std::string& var) const;
std::optional<double> get_group_var(const std::string& group, const std::string& var) const;
std::optional<double> get_segment_var(const std::string& well, const std::string& var, std::size_t segment) const;
void add(const std::string& key, double value);
void update_assign(std::size_t report_step, const std::string& keyword, const UDQSet& udq_result);
void update_define(std::size_t report_step, const std::string& keyword, const UDQSet& udq_result);
const UDQFunctionTable& function_table() const;
std::vector<std::string> wells() const;
std::vector<std::string> wells(const std::string& pattern) const;
std::vector<std::string> groups() const;
SegmentSet segments() const;
SegmentSet segments(const std::vector<std::string>& set_descriptor) const;
private:
const UDQFunctionTable& udqft;
const WellMatcher& well_matcher;
SegmentMatcherFactory create_segment_matcher;
mutable std::unique_ptr<SegmentMatcher> segment_matcher;
SummaryState& summary_state;
UDQState& udq_state;
//std::unordered_map<std::string, UDQSet> udq_results;
std::unordered_map<std::string, double> values;
void ensure_segment_matcher_exists() const;
};
}
} // namespace Opm
#endif
#endif // UDQ_CONTEXT_HPP

View File

@@ -116,6 +116,7 @@ private:
UDQSet scatter_scalar_value(UDQSet&& res, const UDQContext& context) const;
UDQSet scatter_scalar_well_value(const UDQContext& context, const std::optional<double>& value) const;
UDQSet scatter_scalar_group_value(const UDQContext& context, const std::optional<double>& value) const;
UDQSet scatter_scalar_segment_value(const UDQContext& context, const std::optional<double>& value) const;
};
} // Namespace Opm

View File

@@ -29,6 +29,10 @@
#include <unordered_map>
#include <vector>
namespace Opm {
class SegmentSet;
} // namespace Opm
namespace Opm {
class UDQScalar
@@ -197,6 +201,9 @@ public:
}
};
static std::vector<EnumeratedWellItems>
getSegmentItems(const SegmentSet& segmentSet);
/// Construct empty, named UDQ set of specific variable type
///
/// \param[in] name UDQ set name
@@ -304,6 +311,11 @@ public:
/// this UDQ set. Non-finite value leaves the UDQ set element
/// undefined.
static UDQSet field(const std::string& name, double scalar_value);
static UDQSet segments(const std::string& name,
const std::vector<EnumeratedWellItems>& segments);
static UDQSet segments(const std::string& name,
const std::vector<EnumeratedWellItems>& segments,
const double scalar_value);
/// Assign value to every element of the UDQ set
///

View File

@@ -29,8 +29,8 @@
namespace Opm { namespace EclIO {
struct lgr_info {
std::string name;
std::array<int, 3> ijk;
std::string name {};
std::array<int, 3> ijk {};
};
struct SummaryNode {
@@ -59,13 +59,13 @@ struct SummaryNode {
Undefined,
};
std::string keyword;
Category category;
Type type;
std::string wgname;
int number;
std::optional<std::string> fip_region;
std::optional<lgr_info> lgr;
std::string keyword {};
Category category { Category::Miscellaneous };
Type type { Type::Undefined };
std::string wgname {};
int number {};
std::optional<std::string> fip_region {};
std::optional<lgr_info> lgr {};
constexpr static int default_number { std::numeric_limits<int>::min() };

View File

@@ -1141,11 +1141,16 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
{
const auto& kw = keyword.name();
if (kw.size() > std::string::size_type{5}) {
// Easy check first--handles SUMMARY and SUMTHIN &c.
if ((kw == "SUMMARY") || (kw == "SUMTHIN")) {
return false;
}
if (kw[1] == 'U') {
// User-defined quantity at segment level. Unbounded set, so
// assume this is well defined.
return true;
}
const auto kw_whitelist = std::vector<const char*> {
"SOFR" , "SOFRF", "SOFRS", "SOFT", "SOFV", "SOHF", "SOVIS",
"SGFR" , "SGFRF", "SGFRS", "SGFT", "SGFV", "SGHF", "SGVIS",

View File

@@ -65,6 +65,7 @@
#include <opm/input/eclipse/Schedule/Group/GConSale.hpp>
#include <opm/input/eclipse/Schedule/Group/GroupEconProductionLimits.hpp>
#include <opm/input/eclipse/Schedule/Group/GuideRateConfig.hpp>
#include <opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp>
#include <opm/input/eclipse/Schedule/MSW/SICD.hpp>
#include <opm/input/eclipse/Schedule/MSW/Valve.hpp>
#include <opm/input/eclipse/Schedule/MSW/WellSegments.hpp>
@@ -1155,12 +1156,22 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno)
this->snapshots.back().events().addEvent(ScheduleEvents::TUNING_CHANGE);
}
void Schedule::handleUDQ(HandlerContext& handlerContext) {
void Schedule::handleUDQ(HandlerContext& handlerContext)
{
auto new_udq = this->snapshots.back().udq();
for (const auto& record : handlerContext.keyword)
new_udq.add_record(record, handlerContext.keyword.location(), handlerContext.currentStep);
this->snapshots.back().udq.update( std::move(new_udq) );
auto segment_matcher_factory = [this]()
{
return std::make_unique<SegmentMatcher>(this->snapshots.back());
};
for (const auto& record : handlerContext.keyword) {
new_udq.add_record(segment_matcher_factory, record,
handlerContext.keyword.location(),
handlerContext.currentStep);
}
this->snapshots.back().udq.update(std::move(new_udq));
}
void Schedule::handleVAPPARS(HandlerContext& handlerContext) {

View File

@@ -44,6 +44,7 @@
#include <opm/input/eclipse/Schedule/Group/GTNode.hpp>
#include <opm/input/eclipse/Schedule/Group/GuideRateConfig.hpp>
#include <opm/input/eclipse/Schedule/GasLiftOpt.hpp>
#include <opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp>
#include <opm/input/eclipse/Schedule/MSW/SICD.hpp>
#include <opm/input/eclipse/Schedule/MSW/Valve.hpp>
#include <opm/input/eclipse/Schedule/MSW/WellSegments.hpp>
@@ -1243,6 +1244,16 @@ File {} line {}.)", pattern, location.keyword, location.filename, location.linen
return WellMatcher(sched_state->well_order.get(), sched_state->wlist_manager.get());
}
std::function<std::unique_ptr<SegmentMatcher>()>
Schedule::segmentMatcherFactory(const std::size_t report_step) const
{
return {
[report_step, this]() -> std::unique_ptr<SegmentMatcher>
{
return std::make_unique<SegmentMatcher>((*this)[report_step]);
}
};
}
std::vector<std::string> Schedule::wellNames(const std::string& pattern) const {
return this->wellNames(pattern, this->size() - 1);

View File

@@ -266,6 +266,14 @@ namespace Opm
this->update_group_var(group, udq_set.name(), udq_value.value_or(undefined_value));
}
}
else if (var_type == UDQVarType::SEGMENT_VAR) {
for (const auto& udq_value : udq_set) {
this->update_segment_var(udq_value.wgname(),
udq_set.name(),
udq_value.number(),
udq_value.value().value_or(undefined_value));
}
}
else {
const auto& udq_var = udq_set[0].value();
this->update(udq_set.name(), udq_var.value_or(undefined_value));

View File

@@ -19,9 +19,11 @@
#include <opm/input/eclipse/Schedule/UDQ/UDQASTNode.hpp>
#include <opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQEnums.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQFunction.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQFunctionTable.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQSet.hpp>
#include <memory>
#include <set>
@@ -34,19 +36,32 @@
namespace {
bool is_udq(const std::string& key)
bool is_udq_blacklist(const std::string& keyword)
{
return (key.size() >= std::string::size_type{2})
&& (key[1] == 'U');
static const auto udq_blacklistkw = std::unordered_set<std::string> {
"SUMTHIN", "SUMMARY", "RUNSUM",
};
return udq_blacklistkw.find(keyword) != udq_blacklistkw.end();
}
bool is_udq(const std::string& keyword)
{
// Does 'keyword' match one of the patterns
// AU*, BU*, CU*, FU*, GU*, RU*, SU*, or WU*?
using sz_t = std::string::size_type;
return (keyword.size() > sz_t{1})
&& (keyword[1] == 'U')
&& ! is_udq_blacklist(keyword)
&& (keyword.find_first_of("WGFCRBSA") == sz_t{0});
}
Opm::UDQVarType init_type(const Opm::UDQTokenType token_type)
{
if (token_type == Opm::UDQTokenType::number) {
return Opm::UDQVarType::SCALAR;
}
if (Opm::UDQ::scalarFunc(token_type)) {
if ((token_type == Opm::UDQTokenType::number) ||
Opm::UDQ::scalarFunc(token_type))
{
return Opm::UDQVarType::SCALAR;
}
@@ -132,7 +147,6 @@ UDQASTNode::UDQASTNode(const UDQTokenType type_arg,
if ((this->var_type == UDQVarType::CONNECTION_VAR) ||
(this->var_type == UDQVarType::REGION_VAR) ||
(this->var_type == UDQVarType::SEGMENT_VAR) ||
(this->var_type == UDQVarType::AQUIFER_VAR) ||
(this->var_type == UDQVarType::BLOCK_VAR))
{
@@ -267,12 +281,13 @@ bool UDQASTNode::operator==(const UDQASTNode& data) const
void UDQASTNode::required_summary(std::unordered_set<std::string>& summary_keys) const
{
if (this->type == UDQTokenType::ecl_expr) {
if (std::holds_alternative<std::string>(this->value)) {
const auto& keyword = std::get<std::string>(this->value);
if (!is_udq(keyword)) {
summary_keys.insert(keyword);
}
if ((this->type == UDQTokenType::ecl_expr) &&
std::holds_alternative<std::string>(this->value))
{
if (const auto& keyword = std::get<std::string>(this->value);
!is_udq(keyword))
{
summary_keys.insert(keyword);
}
}
@@ -299,6 +314,10 @@ UDQASTNode::eval_expression(const UDQContext& context) const
return this->eval_group_expression(string_value, context);
}
if (data_type == UDQVarType::SEGMENT_VAR) {
return this->eval_segment_expression(string_value, context);
}
if (data_type == UDQVarType::FIELD_VAR) {
return UDQSet::scalar(string_value, context.get(string_value));
}
@@ -376,6 +395,54 @@ UDQASTNode::eval_group_expression(const std::string& string_value,
return res;
}
UDQSet
UDQASTNode::eval_segment_expression(const std::string& string_value,
const UDQContext& context) const
{
const auto all_msw_segments = UDQSet::getSegmentItems(context.segments());
if (this->selector.empty()) {
auto res = UDQSet::segments(string_value, all_msw_segments);
auto index = std::size_t{0};
for (const auto& ms_well : all_msw_segments) {
for (const auto& segment : ms_well.numbers) {
res.assign(index++, context.get_segment_var(ms_well.well, string_value, segment));
}
}
return res;
}
const auto selected_segments = context.segments(this->selector);
if (selected_segments.empty()) {
// No matching segments. Could be because the 'selector' only
// applies to MS wells that are no yet online, or because the
// segments don't yet exist.
return UDQSet::empty(string_value);
}
else if (selected_segments.isScalar()) {
// Selector matches a single segment in a single MS well.
const auto segSet = selected_segments.segments(0);
const auto well = std::string { segSet.well() };
return UDQSet::scalar(string_value, context.get_segment_var(well, string_value, *segSet.begin()));
}
// If we get here, the selector matches at least one segment in at least
// one MS well.
auto res = UDQSet::segments(string_value, all_msw_segments);
const auto numWells = selected_segments.numWells();
for (auto wellID = 0*numWells; wellID < numWells; ++wellID) {
const auto segSet = selected_segments.segments(wellID);
const auto well = std::string { segSet.well() };
for (const auto& segment : segSet) {
res.assign(well, segment, context.get_segment_var(well, string_value, segment));
}
}
return res;
}
UDQSet
UDQASTNode::eval_scalar_function(const UDQVarType target_type,
const UDQContext& context) const
@@ -429,6 +496,9 @@ UDQASTNode::eval_number(const UDQVarType target_type,
case UDQVarType::GROUP_VAR:
return UDQSet::groups(dummy_name, context.groups(), numeric_value);
case UDQVarType::SEGMENT_VAR:
return UDQSet::segments(dummy_name, UDQSet::getSegmentItems(context.segments()), numeric_value);
case UDQVarType::SCALAR:
return UDQSet::scalar(dummy_name, numeric_value);

View File

@@ -24,10 +24,12 @@
#include <opm/common/OpmLog/KeywordLocation.hpp>
#include <opm/common/utility/OpmInputError.hpp>
#include <opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQEnums.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQInput.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQSet.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQState.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/U.hpp> // UDQ
@@ -36,6 +38,7 @@
#include <algorithm>
#include <cstddef>
#include <functional>
#include <stdexcept>
#include <string>
#include <tuple>
@@ -55,6 +58,83 @@ namespace {
return s;
}
}
class EvalAssign
{
public:
static EvalAssign field()
{
return { []() {
return [](const auto& assign) { return assign.eval(); };
}};
}
static EvalAssign group(const std::size_t report_step,
const Opm::Schedule& sched)
{
return { [report_step, &sched]() {
return [groups = sched.groupNames(report_step)]
(const auto& assign)
{
return assign.eval(groups);
};
}};
}
static EvalAssign well(const std::size_t report_step,
const Opm::Schedule& sched)
{
return { [report_step, &sched]() {
return [wells = sched.wellNames(report_step)]
(const auto& assign)
{
return assign.eval(wells);
};
}};
}
static EvalAssign segment(const Opm::UDQContext& context)
{
return { [&context]() {
// Result set should be sized according to total number of
// segments.
const auto segSet = context.segments();
return [items = Opm::UDQSet::getSegmentItems(segSet)](const auto& assign)
{
return assign.eval(items);
};
}};
}
Opm::UDQSet operator()(const Opm::UDQAssign& assign) const
{
if (! this->eval_.has_value()) {
// First call to operator().
//
// Create evaluation function using whatever state was
// captured in the creation function, for instance a "const
// UDQContext&".
//
// Note: This is deferred initialisation. The create_()
// call could be rather expensive so we don't incur the cost
// of calling the function until we know we that we have to.
this->eval_ = this->create_();
}
// Evaluate type dependent UDQ ASSIGN statement.
return (*this->eval_)(assign);
}
private:
using Eval = std::function<Opm::UDQSet(const Opm::UDQAssign&)>;
using Create = std::function<Eval()>;
Create create_;
mutable std::optional<Eval> eval_;
EvalAssign(Create create) : create_{ std::move(create) } {}
};
} // Anonymous namespace
namespace Opm {
@@ -121,21 +201,23 @@ namespace Opm {
}
void UDQConfig::add_assign(const std::string& quantity,
SegmentMatcherFactory create_segment_matcher,
const std::vector<std::string>& selector,
const double value,
const std::size_t report_step)
{
this->add_node(quantity, UDQAction::ASSIGN);
auto assignment = this->m_assignments.find(quantity);
if (assignment == this->m_assignments.end()) {
this->m_assignments.emplace(std::piecewise_construct,
std::forward_as_tuple(quantity),
std::forward_as_tuple(quantity, selector,
value, report_step));
}
else {
assignment->second.add_record(selector, value, report_step);
switch (UDQ::varType(quantity)) {
case UDQVarType::SEGMENT_VAR:
this->add_enumerated_assign(quantity,
std::move(create_segment_matcher),
selector, value, report_step);
break;
default:
this->add_named_assign(quantity, selector, value, report_step);
break;
}
}
@@ -217,7 +299,8 @@ namespace Opm {
define.update_status(update_status, report_step);
}
void UDQConfig::add_record(const DeckRecord& record,
void UDQConfig::add_record(SegmentMatcherFactory create_segment_matcher,
const DeckRecord& record,
const KeywordLocation& location,
const std::size_t report_step)
{
@@ -236,7 +319,9 @@ namespace Opm {
else if (action == UDQAction::ASSIGN) {
const auto selector = std::vector<std::string>(data.begin(), data.end() - 1);
const auto value = std::stod(data.back());
this->add_assign(quantity, selector, value, report_step);
this->add_assign(quantity,
std::move(create_segment_matcher),
selector, value, report_step);
}
else if (action == UDQAction::DEFINE) {
this->add_define(quantity, location, data, report_step);
@@ -445,28 +530,31 @@ namespace Opm {
UDQState& udq_state,
UDQContext& context) const
{
const auto wells = sched.wellNames(report_step);
const auto groups = sched.groupNames(report_step);
const auto handlers = std::map<UDQVarType, EvalAssign> {
{ UDQVarType::FIELD_VAR , EvalAssign::field() },
{ UDQVarType::GROUP_VAR , EvalAssign::group(report_step, sched) },
{ UDQVarType::WELL_VAR , EvalAssign::well(report_step, sched) },
{ UDQVarType::SEGMENT_VAR, EvalAssign::segment(context) },
};
for (const auto& assign : this->assignments(UDQVarType::WELL_VAR)) {
if (udq_state.assign(assign.report_step(), assign.keyword())) {
auto ws = assign.eval(wells);
context.update_assign(report_step, assign.keyword(), ws);
for (const auto& index_pair : this->input_index) {
const auto& keyword = index_pair.first;
auto asgn_pos = this->m_assignments.find(keyword);
if ((asgn_pos == this->m_assignments.end()) ||
! udq_state.assign(asgn_pos->second.report_step(), keyword))
{
// No such ASSIGN or ASSIGN not active
continue;
}
}
for (const auto& assign : this->assignments(UDQVarType::GROUP_VAR)) {
if (udq_state.assign(assign.report_step(), assign.keyword())) {
auto ws = assign.eval(groups);
context.update_assign(report_step, assign.keyword(), ws);
auto handler = handlers.find(asgn_pos->second.var_type());
if (handler == handlers.end()) {
// Unhandled variable type.
continue;
}
}
for (const auto& assign : this->assignments(UDQVarType::FIELD_VAR)) {
if (udq_state.assign(assign.report_step(), assign.keyword())) {
auto ws = assign.eval();
context.update_assign(report_step, assign.keyword(), ws);
}
context.update_assign(report_step, keyword,
handler->second(asgn_pos->second));
}
}
@@ -483,6 +571,7 @@ namespace Opm {
select_var_type |= var_type_bit(UDQVarType::WELL_VAR);
select_var_type |= var_type_bit(UDQVarType::GROUP_VAR);
select_var_type |= var_type_bit(UDQVarType::FIELD_VAR);
select_var_type |= var_type_bit(UDQVarType::SEGMENT_VAR);
for (const auto& [keyword, index] : this->input_index) {
if (index.action != UDQAction::DEFINE) {
@@ -508,24 +597,30 @@ namespace Opm {
}
}
void UDQConfig::eval(const std::size_t report_step,
const Schedule& sched,
const WellMatcher& wm,
SummaryState& st,
UDQState& udq_state) const
void UDQConfig::eval(const std::size_t report_step,
const Schedule& sched,
const WellMatcher& wm,
SegmentMatcherFactory create_segment_matcher,
SummaryState& st,
UDQState& udq_state) const
{
UDQContext context(this->function_table(), wm, st, udq_state);
UDQContext context {
this->function_table(), wm, std::move(create_segment_matcher), st, udq_state
};
this->eval_assign(report_step, sched, udq_state, context);
this->eval_define(report_step, udq_state, context);
}
void UDQConfig::eval_assign(const std::size_t report_step,
const Schedule& sched,
const WellMatcher& wm,
SummaryState& st,
UDQState& udq_state) const
void UDQConfig::eval_assign(const std::size_t report_step,
const Schedule& sched,
const WellMatcher& wm,
SegmentMatcherFactory create_segment_matcher,
SummaryState& st,
UDQState& udq_state) const
{
UDQContext context(this->function_table(), wm, st, udq_state);
UDQContext context {
this->function_table(), wm, std::move(create_segment_matcher), st, udq_state
};
this->eval_assign(report_step, sched, udq_state, context);
}
@@ -536,4 +631,48 @@ namespace Opm {
}
}
void UDQConfig::add_named_assign(const std::string& quantity,
const std::vector<std::string>& selector,
const double value,
const std::size_t report_step)
{
auto [asgnPos, inserted] = this->m_assignments
.emplace(std::piecewise_construct,
std::forward_as_tuple(quantity),
std::forward_as_tuple(quantity, selector, value, report_step));
if (! inserted) {
asgnPos->second.add_record(selector, value, report_step);
}
}
void UDQConfig::add_enumerated_assign(const std::string& quantity,
SegmentMatcherFactory create_segment_matcher,
const std::vector<std::string>& selector,
const double value,
const std::size_t report_step)
{
auto segmentMatcher = create_segment_matcher();
auto setDescriptor = SegmentMatcher::SetDescriptor{};
if (! selector.empty()) {
setDescriptor.wellNames(selector.front());
}
if (selector.size() > 1) {
setDescriptor.segmentNumber(selector[1]);
}
auto items = UDQSet::
getSegmentItems(segmentMatcher->findSegments(setDescriptor));
auto [asgnPos, inserted] = this->m_assignments
.emplace(std::piecewise_construct,
std::forward_as_tuple(quantity),
std::forward_as_tuple(quantity, items, value, report_step));
if (! inserted) {
asgnPos->second.add_record(std::move(items), value, report_step);
}
}
} // namespace Opm

View File

@@ -16,45 +16,58 @@
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fmt/format.h>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQContext.hpp>
#include <opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQState.hpp>
#include <opm/input/eclipse/Schedule/Well/WellMatcher.hpp>
#include <opm/common/utility/TimeService.hpp>
namespace Opm {
#include <cassert>
#include <cstddef>
#include <functional>
#include <optional>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <fmt/format.h>
namespace {
bool is_udq(const std::string& key) {
if (key.size() < 2)
return false;
if (key[1] != 'U')
return false;
return true;
bool is_udq(const std::string& key)
{
return (key.size() >= std::string::size_type{2})
&& (key[1] == 'U');
}
}
} // Anonymous namespace
namespace Opm {
UDQContext::UDQContext(const UDQFunctionTable& udqft_arg, const WellMatcher& wm, SummaryState& summary_state_arg, UDQState& udq_state_arg) :
udqft(udqft_arg),
well_matcher(wm),
summary_state(summary_state_arg),
udq_state(udq_state_arg)
UDQContext::UDQContext(const UDQFunctionTable& udqft_arg,
const WellMatcher& wm,
SegmentMatcherFactory create_segment_matcher_arg,
SummaryState& summary_state_arg,
UDQState& udq_state_arg)
: udqft (udqft_arg)
, well_matcher (wm)
, create_segment_matcher(std::move(create_segment_matcher_arg))
, summary_state (summary_state_arg)
, udq_state (udq_state_arg)
{
for (const auto& pair : TimeService::eclipseMonthIndices())
this->add(pair.first, pair.second);
for (const auto& [month, index] : TimeService::eclipseMonthIndices()) {
this->add(month, index);
}
/*
Simulator performance keywords which are expected to be available for
UDQ keywords; probably better to guarantee that they are present in
the underlying summary state object.
*/
// Simulator performance keywords which are expected to be available
// for UDQ keywords; probably better to guarantee that they are
// present in the underlying summary state object.
this->add("MSUMLINS", 0.0);
this->add("MSUMNEWT", 0.0);
@@ -62,82 +75,169 @@ bool is_udq(const std::string& key) {
this->add("TCPU", 0.0);
}
void UDQContext::add(const std::string& key, double value) {
this->values[key] = value;
void UDQContext::add(const std::string& key, double value)
{
this->values.insert_or_assign(key, value);
}
std::optional<double> UDQContext::get(const std::string& key) const {
std::optional<double>
UDQContext::get(const std::string& key) const
{
if (is_udq(key)) {
if (this->udq_state.has(key))
if (this->udq_state.has(key)) {
return this->udq_state.get(key);
}
return std::nullopt;
}
const auto& pair_ptr = this->values.find(key);
if (pair_ptr != this->values.end())
auto pair_ptr = this->values.find(key);
if (pair_ptr != this->values.end()) {
return pair_ptr->second;
}
return this->summary_state.get(key);
}
std::optional<double> UDQContext::get_well_var(const std::string& well, const std::string& var) const {
std::optional<double>
UDQContext::get_well_var(const std::string& well,
const std::string& var) const
{
if (is_udq(var)) {
if (this->udq_state.has_well_var(well, var))
if (this->udq_state.has_well_var(well, var)) {
return this->udq_state.get_well_var(well, var);
}
return std::nullopt;
}
if (this->summary_state.has_well_var(var)) {
if (this->summary_state.has_well_var(well, var))
if (this->summary_state.has_well_var(well, var)) {
return this->summary_state.get_well_var(well, var);
else
return std::nullopt;
}
return std::nullopt;
}
throw std::logic_error(fmt::format("Summary well variable: {} not registered", var));
throw std::logic_error {
fmt::format("Summary well variable: {} not registered", var)
};
}
std::optional<double> UDQContext::get_group_var(const std::string& group, const std::string& var) const {
std::optional<double>
UDQContext::get_group_var(const std::string& group,
const std::string& var) const
{
if (is_udq(var)) {
if (this->udq_state.has_group_var(group, var))
if (this->udq_state.has_group_var(group, var)) {
return this->udq_state.get_group_var(group, var);
}
return std::nullopt;
}
if (this->summary_state.has_group_var(var)) {
if (this->summary_state.has_group_var(group, var))
if (this->summary_state.has_group_var(group, var)) {
return this->summary_state.get_group_var(group, var);
else
return std::nullopt;
}
return std::nullopt;
}
throw std::logic_error(fmt::format("Summary group variable: {} not registered", var));
throw std::logic_error {
fmt::format("Summary group variable: {} not registered", var)
};
}
std::vector<std::string> UDQContext::wells() const {
std::optional<double>
UDQContext::get_segment_var(const std::string& well,
const std::string& var,
std::size_t segment) const
{
if (is_udq(var)) {
if (this->udq_state.has_segment_var(well, var, segment)) {
return this->udq_state.get_segment_var(well, var, segment);
}
return std::nullopt;
}
if (this->summary_state.has_segment_var(well, var, segment)) {
return this->summary_state.get_segment_var(well, var, segment);
}
throw std::logic_error {
fmt::format("Segment summary variable {} not "
"registered for segment {} in well {}",
var, segment, well)
};
}
std::vector<std::string> UDQContext::wells() const
{
return this->well_matcher.wells();
}
std::vector<std::string> UDQContext::wells(const std::string& pattern) const {
std::vector<std::string> UDQContext::wells(const std::string& pattern) const
{
return this->well_matcher.wells(pattern);
}
std::vector<std::string> UDQContext::groups() const {
std::vector<std::string> UDQContext::groups() const
{
return this->summary_state.groups();
}
const UDQFunctionTable& UDQContext::function_table() const {
SegmentSet UDQContext::segments() const
{
// Empty descriptor matches all segments in all existing MS wells.
this->ensure_segment_matcher_exists();
return this->segment_matcher->findSegments(SegmentMatcher::SetDescriptor{});
}
SegmentSet UDQContext::segments(const std::vector<std::string>& set_descriptor) const
{
assert (! set_descriptor.empty() &&
"Internal error passing empty segment set "
"descriptor to filtered segment set query");
auto desc = SegmentMatcher::SetDescriptor{}
.wellNames(set_descriptor.front());
if (set_descriptor.size() > std::vector<std::string>::size_type{1}) {
desc.segmentNumber(set_descriptor[1]);
}
this->ensure_segment_matcher_exists();
return this->segment_matcher->findSegments(desc);
}
const UDQFunctionTable& UDQContext::function_table() const
{
return this->udqft;
}
void UDQContext::update_assign(std::size_t report_step, const std::string& keyword, const UDQSet& udq_result) {
void UDQContext::update_assign(const std::size_t report_step,
const std::string& keyword,
const UDQSet& udq_result)
{
this->udq_state.add_assign(report_step, keyword, udq_result);
this->summary_state.update_udq(udq_result, this->udq_state.undefined_value());
}
void UDQContext::update_define(std::size_t report_step, const std::string& keyword, const UDQSet& udq_result) {
void UDQContext::update_define(const std::size_t report_step,
const std::string& keyword,
const UDQSet& udq_result)
{
this->udq_state.add_define(report_step, keyword, udq_result);
this->summary_state.update_udq(udq_result, this->udq_state.undefined_value());
}
void UDQContext::ensure_segment_matcher_exists() const
{
if (this->segment_matcher == nullptr) {
this->segment_matcher = this->create_segment_matcher();
}
}
}

View File

@@ -22,6 +22,7 @@
#include <opm/common/OpmLog/OpmLog.hpp>
#include <opm/common/utility/String.hpp>
#include <opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQASTNode.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQEnums.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQToken.hpp>
@@ -439,6 +440,10 @@ UDQSet UDQDefine::scatter_scalar_value(UDQSet&& res, const UDQContext& context)
return this->scatter_scalar_group_value(context, res[0].value());
}
if (this->var_type() == UDQVarType::SEGMENT_VAR) {
return this->scatter_scalar_segment_value(context, res[0].value());
}
return std::move(res);
}
@@ -462,4 +467,14 @@ UDQSet UDQDefine::scatter_scalar_group_value(const UDQContext& contex
return UDQSet::groups(this->m_keyword, context.groups(), *value);
}
UDQSet UDQDefine::scatter_scalar_segment_value(const UDQContext& context,
const std::optional<double>& value) const
{
if (! value.has_value()) {
return UDQSet::segments(this->m_keyword, UDQSet::getSegmentItems(context.segments()));
}
return UDQSet::segments(this->m_keyword, UDQSet::getSegmentItems(context.segments()), *value);
}
} // namespace Opm

View File

@@ -19,6 +19,8 @@
#include <opm/input/eclipse/Schedule/UDQ/UDQSet.hpp>
#include <opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp>
#include <opm/common/utility/shmatch.hpp>
#include <algorithm>
@@ -270,6 +272,20 @@ UDQSet UDQSet::groups(const std::string& name,
return us;
}
UDQSet UDQSet::segments(const std::string& name,
const std::vector<EnumeratedWellItems>& segments)
{
return { name, UDQVarType::SEGMENT_VAR, segments };
}
UDQSet UDQSet::segments(const std::string& name,
const std::vector<EnumeratedWellItems>& segments,
const double scalar_value)
{
auto us = UDQSet::segments(name, segments);
us.assign(scalar_value);
return us;
}
bool UDQSet::has(const std::string& name) const
{
@@ -756,4 +772,20 @@ bool UDQSet::operator==(const UDQSet& other) const
&& (this->values == other.values);
}
std::vector<UDQSet::EnumeratedWellItems>
UDQSet::getSegmentItems(const SegmentSet& segSet)
{
const auto numWells = segSet.numWells();
auto items = std::vector<Opm::UDQSet::EnumeratedWellItems>(numWells);
for (auto wellID = 0*numWells; wellID < numWells; ++wellID) {
auto segRange = segSet.segments(wellID);
items[wellID].well = segRange.well();
items[wellID].numbers.assign(segRange.begin(), segRange.end());
}
return items;
}
} // namespace Opm

View File

@@ -39,6 +39,7 @@
"SWVIS"
],
"comment": "This list is incomplete",
"deck_name_regex": "SU.+",
"items": [
{
"name": "Well",

View File

@@ -38,6 +38,7 @@
#include <opm/input/eclipse/Schedule/GasLiftOpt.hpp>
#include <opm/input/eclipse/Schedule/Group/Group.hpp>
#include <opm/input/eclipse/Schedule/MSW/WellSegments.hpp>
#include <opm/input/eclipse/Schedule/MSW/Segment.hpp>
#include <opm/input/eclipse/Schedule/ScheduleState.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
@@ -4283,70 +4284,73 @@ void Opm::out::Summary::SummaryImplementation::write(const MiniStep& ms)
void
Opm::out::Summary::SummaryImplementation::
configureTimeVector(const EclipseState& es, const std::string& kw) {
configureTimeVector(const EclipseState& es, const std::string& kw)
{
const auto dfltwgname = std::string(":+:+:+:+");
const auto dfltnum = 0;
this->valueKeys_.push_back(kw);
if (kw == "TIME") {
const std::string& unit_string = es.getUnits().name(UnitSystem::measure::time);
auto eval = std::make_unique<Evaluator::Time>(kw);
valueUnits_.push_back(unit_string);
const auto* unit_string =
es.getUnits().name(UnitSystem::measure::time);
this->valueUnits_.push_back(unit_string);
this->outputParameters_
.makeParameter(kw, dfltwgname, dfltnum, unit_string, std::move(eval));
.makeParameter(kw, dfltwgname, dfltnum, unit_string,
std::make_unique<Evaluator::Time>(kw));
}
else if (kw == "DAY") {
auto eval = std::make_unique<Evaluator::Day>(kw);
valueUnits_.push_back("");
this->valueUnits_.push_back("");
this->outputParameters_
.makeParameter(kw, dfltwgname, dfltnum, "", std::move(eval));
.makeParameter(kw, dfltwgname, dfltnum, "",
std::make_unique<Evaluator::Day>(kw));
}
else if (kw == "MONTH" || kw == "MNTH") {
auto eval = std::make_unique<Evaluator::Month>(kw);
valueUnits_.push_back("");
else if ((kw == "MONTH") || (kw == "MNTH")) {
this->valueUnits_.push_back("");
this->outputParameters_
.makeParameter(kw, dfltwgname, dfltnum, "", std::move(eval));
.makeParameter(kw, dfltwgname, dfltnum, "",
std::make_unique<Evaluator::Month>(kw));
}
else if (kw == "YEAR") {
auto eval = std::make_unique<Evaluator::Year>(kw);
valueUnits_.push_back("");
this->valueUnits_.push_back("");
this->outputParameters_
.makeParameter(kw, dfltwgname, dfltnum, "", std::move(eval));
.makeParameter(kw, dfltwgname, dfltnum, "",
std::make_unique<Evaluator::Year>(kw));
}
else if (kw == "YEARS") {
auto eval = std::make_unique<Evaluator::Years>(kw);
valueUnits_.push_back("");
this->valueUnits_.push_back("");
this->outputParameters_
.makeParameter(kw, dfltwgname, dfltnum, kw, std::move(eval));
.makeParameter(kw, dfltwgname, dfltnum, kw,
std::make_unique<Evaluator::Years>(kw));
}
}
void
Opm::out::Summary::SummaryImplementation::
configureTimeVectors(const EclipseState& es, const SummaryConfig& sumcfg)
configureTimeVectors(const EclipseState& es,
const SummaryConfig& sumcfg)
{
this->configureTimeVector(es, "TIME");
this->configureTimeVector(es, "YEARS");
// TIME and YEARS are always available.
for (const auto* kw : { "TIME", "YEARS", }) {
this->configureTimeVector(es, kw);
}
if (sumcfg.hasKeyword("DAY"))
this->configureTimeVector(es, "DAY");
if (sumcfg.hasKeyword("MONTH"))
this->configureTimeVector(es, "MONTH");
if (sumcfg.hasKeyword("YEAR"))
this->configureTimeVector(es, "YEAR");
// DAY, MONTH, and YEAR only output if specifically requested.
for (const auto* kw : { "DAY", "MONTH", "YEAR", }) {
if (sumcfg.hasKeyword(kw)) {
this->configureTimeVector(es, kw);
}
}
}
void
@@ -4381,80 +4385,89 @@ configureSummaryInput(const SummaryConfig& sumcfg,
reportUnsupportedKeywords(std::move(unsuppkw));
}
/*
These nodes are added to the summary evaluation list because they are
requested by the UDQ system. In the case of well and group variables the code
will all nodes for all wells / groups - irrespective of what has been
requested in the UDQ code.
*/
// These nodes are added to the summary evaluation list because they are
// requested by the UDQ system. In the case of well and group variables the
// code will add nodes for every well/group in the model--irrespective of
// what has been requested in the UDQ code.
namespace {
std::vector<int>
unique_segment_numbers(const Opm::WellSegments& segments)
{
auto seg_num = std::vector<int>{};
seg_num.reserve(segments.size());
std::transform(segments.begin(), segments.end(),
std::back_inserter(seg_num),
[](const Opm::Segment& segment)
{
return segment.segmentNumber();
});
std::sort(seg_num.begin(), seg_num.end());
auto u = std::unique(seg_num.begin(), seg_num.end());
if (u != seg_num.end()) {
seg_num.erase(u, seg_num.end());
}
return seg_num;
}
template <typename... Args>
Opm::EclIO::SummaryNode make_node(Args&&... args)
{
return { std::forward<Args>(args)... };
}
std::vector<Opm::EclIO::SummaryNode>
make_default_nodes(const std::string& keyword,
make_default_nodes(const std::string& keyword,
const Opm::Schedule& sched)
{
if (Opm::TimeService::valid_month(keyword))
return {};
auto nodes = std::vector<Opm::EclIO::SummaryNode> {};
auto category = Opm::parseKeywordCategory(keyword);
auto type = Opm::parseKeywordType(keyword);
if (Opm::TimeService::valid_month(keyword)) {
return nodes;
}
const auto category = Opm::parseKeywordCategory(keyword);
const auto type = Opm::parseKeywordType(keyword);
switch (category) {
case Opm::EclIO::SummaryNode::Category::Field:
{
Opm::EclIO::SummaryNode node;
node.keyword = keyword;
node.category = category;
node.type = type;
nodes.push_back(node);
}
break;
case Opm::EclIO::SummaryNode::Category::Miscellaneous:
{
Opm::EclIO::SummaryNode node;
node.keyword = keyword;
node.category = category;
node.type = type;
nodes.push_back(node);
}
nodes.push_back(make_node(keyword, category, type));
break;
case Opm::EclIO::SummaryNode::Category::Well:
{
case Opm::EclIO::SummaryNode::Category::Well: {
for (const auto& well : sched.wellNames()) {
Opm::EclIO::SummaryNode node;
node.keyword = keyword;
node.category = category;
node.type = type;
node.wgname = well;
nodes.push_back(node);
nodes.push_back(make_node(keyword, category, type, well));
}
}
break;
break;
case Opm::EclIO::SummaryNode::Category::Group:
{
case Opm::EclIO::SummaryNode::Category::Group: {
for (const auto& group : sched.groupNames()) {
Opm::EclIO::SummaryNode node;
node.keyword = keyword;
node.category = category;
node.type = type;
node.wgname = group;
nodes.push_back(node);
nodes.push_back(make_node(keyword, category, type, group));
}
}
break;
break;
case Opm::EclIO::SummaryNode::Category::Segment: {
for (const auto& wname : sched.wellNames()) {
if (const auto& well = sched.getWellatEnd(wname); well.isMultiSegment()) {
for (const auto& seg_num : unique_segment_numbers(well.getSegments())) {
nodes.push_back(make_node(keyword, category, type, wname, seg_num));
}
}
}
}
break;
default:
throw std::logic_error {
fmt::format("Keyword category '{}' (e.g., summary keyword {}) is not supported in ACTIONX",
category, keyword)
fmt::format("Keyword category '{}' (e.g., summary keyword {}) "
"is not supported in ACTIONX", category, keyword)
};
}
@@ -4462,59 +4475,79 @@ namespace {
}
}
void Opm::out::Summary::SummaryImplementation::configureUDQ(const EclipseState& es,
const SummaryConfig& summary_config,
const Schedule& sched)
void
Opm::out::Summary::SummaryImplementation::
configureUDQ(const EclipseState& es,
const SummaryConfig& summary_config,
const Schedule& sched)
{
const std::unordered_set<std::string> time_vectors = {"TIME", "DAY", "MONTH", "YEAR", "YEARS", "MNTH"};
auto nodes = std::vector<Opm::EclIO::SummaryNode> {};
std::unordered_set<std::string> summary_keys;
for (const auto& [_, udq] : sched.unique<UDQConfig>()) {
(void)_;
udq.required_summary(summary_keys);
auto nodes = std::vector<Opm::EclIO::SummaryNode>{};
const auto time_vectors = std::unordered_set<std::string> {
"TIME", "DAY", "MONTH", "YEAR", "YEARS", "MNTH",
};
auto summary_keys = std::unordered_set<std::string>{};
for (const auto& unique_udqs : sched.unique<UDQConfig>()) {
unique_udqs.second.required_summary(summary_keys);
}
for (const auto& action : sched.back().actions.get())
for (const auto& action : sched.back().actions.get()) {
action.required_summary(summary_keys);
}
for (const auto& key : summary_keys) {
const auto& default_nodes = make_default_nodes(key, sched);
for (const auto& def_node : default_nodes)
for (const auto& def_node : make_default_nodes(key, sched)) {
nodes.push_back(def_node);
}
}
for (const auto& node: nodes) {
auto has_evaluator = [this](const auto& key)
{
return std::find(this->valueKeys_.begin(),
this->valueKeys_.end(), key)
!= this->valueKeys_.end();
};
for (const auto& node : nodes) {
// Handler already configured/requested through the normal
// SummaryConfig path.
if (summary_config.hasSummaryKey(node.unique_key()))
if (summary_config.hasSummaryKey(node.unique_key())) {
continue;
}
// Time related vectors are special cased in the valueKeys_ vector
// and must be checked explicitly.
if (time_vectors.count(node.keyword) > 0) {
if ((time_vectors.find(node.keyword) != time_vectors.end()) &&
! has_evaluator(node.keyword))
{
this->configureTimeVector(es, node.keyword);
continue;
}
// Handler already registered in the summary evaluator, in some
// other way.
if (std::find(this->valueKeys_.begin(), this->valueKeys_.end(), node.unique_key()) != this->valueKeys_.end())
// Handler already registered in the summary evaluator in some other
// way.
if (has_evaluator(node.unique_key())) {
continue;
}
auto fun_pos = funs.find(node.keyword);
if (fun_pos != funs.end()) {
this->extra_parameters.emplace(node.unique_key(), std::make_unique<Evaluator::FunctionRelation>(node, fun_pos->second));
this->extra_parameters
.emplace(node.unique_key(), std::make_unique<Evaluator::FunctionRelation>(node, fun_pos->second));
continue;
}
auto unit = single_values_units.find(node.keyword);
if (unit != single_values_units.end()) {
this->extra_parameters.emplace(node.unique_key(), std::make_unique<Evaluator::GlobalProcessValue>(node, unit->second));
this->extra_parameters
.emplace(node.unique_key(), std::make_unique<Evaluator::GlobalProcessValue>(node, unit->second));
continue;
}
if (node.is_user_defined())
if (node.is_user_defined()) {
continue;
}
throw std::logic_error {
fmt::format("Evaluation function for: {} not found ", node.keyword)

View File

@@ -30,7 +30,10 @@
#include <opm/input/eclipse/Python/Python.hpp>
#include <opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp>
#include <opm/input/eclipse/Schedule/MSW/WellSegments.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/ScheduleState.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQActive.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQAssign.hpp>
@@ -42,6 +45,7 @@
#include <opm/input/eclipse/Schedule/UDQ/UDQSet.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQState.hpp>
#include <opm/input/eclipse/Schedule/Well/NameOrder.hpp>
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
#include <opm/input/eclipse/Schedule/Well/WellMatcher.hpp>
#include <opm/input/eclipse/Utility/Typetools.hpp>
@@ -54,34 +58,126 @@
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Deck/UDAValue.hpp>
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <memory>
#include <limits>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
using namespace Opm;
namespace {
Schedule make_schedule(const std::string& input) {
Parser parser;
auto python = std::make_shared<Python>();
auto deck = parser.parseString(input);
Schedule make_schedule(const std::string& input)
{
auto deck = Parser{}.parseString(input);
if (deck.hasKeyword("DIMENS")) {
EclipseState es(deck);
return Schedule(deck, es, python);
} else {
return { deck, es, std::make_shared<Python>() };
}
else {
EclipseGrid grid(10,10,10);
TableManager table ( deck );
FieldPropsManager fp( deck, Phases{true, true, true}, grid, table);
Runspec runspec (deck);
return Schedule(deck, grid , fp, runspec, python);
TableManager table(deck);
FieldPropsManager fp(deck, Phases{true, true, true}, grid, table);
Runspec runspec(deck);
return { deck, grid, fp, runspec, std::make_shared<Python>() };
}
}
} // namespace anonymous
Opm::Segment makeSegment(const int segmentNumber)
{
return { segmentNumber, 1, 1, 1.0, 0.0, 0.5, 0.01, 0.25, 1.23, true, 0.0, 0.0 };
}
std::shared_ptr<Opm::WellSegments> makeSegments(const int numSegments)
{
auto segments = std::vector<Opm::Segment>{};
segments.reserve(numSegments);
for (auto segment = 0; segment < numSegments; ++segment) {
segments.push_back(makeSegment(segment + 1));
}
return std::make_shared<Opm::WellSegments>
(Opm::WellSegments::CompPressureDrop::HFA, segments);
}
Opm::Well makeProducerWell(const std::string& wname,
const std::size_t insert,
const int numSegments)
{
auto w = Opm::Well {
wname, "G", 0, insert, 1, 2, {},
Opm::WellType { true, Opm::Phase::OIL }, // Oil producer
Opm::Well::ProducerCMode::ORAT,
Opm::Connection::Order::INPUT,
Opm::UnitSystem::newMETRIC(),
-3.0e+20, // UDQ undefined
0.0, true, true, 0,
Opm::Well::GasInflowEquation::STD
};
if (numSegments > 0) {
w.updateSegments(makeSegments(numSegments));
}
return w;
}
Opm::Well makeInjectionWell(const std::string& wname,
const std::size_t insert,
const int numSegments)
{
auto w = Opm::Well {
wname, "G", 0, insert, 1, 2, {},
Opm::WellType { false, Opm::Phase::GAS }, // Gas injector
Opm::Well::ProducerCMode::ORAT,
Opm::Connection::Order::INPUT,
Opm::UnitSystem::newMETRIC(),
-3.0e+20, // UDQ undefined
0.0, true, true, 0,
Opm::Well::GasInflowEquation::STD
};
if (numSegments > 0) {
w.updateSegments(makeSegments(numSegments));
}
return w;
}
// Collection of wells
// OP-01: Producer, MSW, 20 segments (1 .. 20)
// OP-02: Producer, MSW, 5 segments (1 .. 5)
// OP-06: Producer, Standard well
// OPROD: Producer, MSW, 2 segments (1 .. 2)
//
// GI-01: Injector, MSW, 10 segments (1 .. 10)
// GI-08: Injector, Standard well
// I-45: Injector, MSW, 1 segment (1)
Opm::ScheduleState dynamicInputData()
{
auto block = Opm::ScheduleState { Opm::TimeService::now() };
block.wells.update(makeProducerWell("OP-01", 0, 20));
block.wells.update(makeProducerWell("OP-02", 1, 5));
block.wells.update(makeProducerWell("OP-06", 2, 0));
block.wells.update(makeProducerWell("OPROD", 3, 2));
block.wells.update(makeInjectionWell("GI-01", 4, 10));
block.wells.update(makeInjectionWell("GI-08", 5, 0));
block.wells.update(makeInjectionWell("I-45", 6, 1));
block.well_order.update(Opm::NameOrder {
"OP-01", "OP-02", "OP-06", "OPROD", "GI-01", "GI-08", "I-45",
});
return block;
}
} // Anonymous namespace
BOOST_AUTO_TEST_CASE(TYPE_COERCION) {
BOOST_CHECK( UDQVarType::SCALAR == UDQ::coerce(UDQVarType::SCALAR, UDQVarType::SCALAR) );
@@ -104,7 +200,8 @@ BOOST_AUTO_TEST_CASE(GROUP_VARIABLES)
UDQDefine def_group(udqp, "GUOPRL", 0, location, {"(", "5000", "-", "GOPR", "LOWER", "*", "0.13", "-", "GOPR", "UPPER", "*", "0.15", ")" , "*", "0.89"});
SummaryState st(TimeService::now());
UDQState udq_state(udqp.undefinedValue());
UDQContext context(udqft, {}, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, {}, segmentMatcherFactory, st, udq_state);
double gopr_lower = 1234;
double gopr_upper = 4321;
@@ -120,7 +217,39 @@ BOOST_AUTO_TEST_CASE(GROUP_VARIABLES)
BOOST_CHECK(!empty_value);
}
BOOST_AUTO_TEST_CASE(SINGLE_SEGMENT_VARIABLES)
{
const auto location = KeywordLocation{};
const auto udqp = UDQParams{};
const auto udqft = UDQFunctionTable{};
const auto def_seg = UDQDefine {
udqp, "SU_OFR", 0, location,
{ "(", "5000", "-", "SOFR", "'OP-*'", "3", "*", "0.13", ")" , "*", "0.89" }
};
auto st = SummaryState { TimeService::now() };
auto udq_state = UDQState { udqp.undefinedValue() };
auto segmentMatcherFactory = [sched_state = dynamicInputData()]()
{
return std::make_unique<SegmentMatcher>(sched_state);
};
auto context = UDQContext {
udqft, {}, segmentMatcherFactory, st, udq_state
};
const auto sofr_p1_3 = 1234.0;
const auto sofr_p2_3 = 4321.0;
st.update_segment_var("OP-01", "SOFR", 3, sofr_p1_3);
st.update_segment_var("OP-02", "SOFR", 3, sofr_p2_3);
const auto res_seg = def_seg.eval(context);
BOOST_CHECK_EQUAL(res_seg("OP-01", 3).get(), (5000.0 - sofr_p1_3*0.13) * 0.89);
BOOST_CHECK_EQUAL(res_seg("OP-02", 3).get(), (5000.0 - sofr_p2_3*0.13) * 0.89);
}
BOOST_AUTO_TEST_CASE(SUBTRACT)
{
@@ -133,7 +262,8 @@ BOOST_AUTO_TEST_CASE(SUBTRACT)
SummaryState st(TimeService::now());
UDQState udq_state(udqp.undefinedValue());
WellMatcher wm(NameOrder({"P1"}));
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_well_var("P1", "WOPR", 4);
auto res = def.eval(context);
@@ -156,7 +286,8 @@ BOOST_AUTO_TEST_CASE(TEST)
SummaryState st(TimeService::now());
UDQState udq_state(udqp.undefinedValue());
WellMatcher wm(NameOrder({"P1", "P2"}));
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_group_var("MAU", "GOPR", 4);
st.update_group_var("XXX", "GOPR", 5);
@@ -197,7 +328,8 @@ BOOST_AUTO_TEST_CASE(MIX_SCALAR) {
SummaryState st(TimeService::now());
UDQState udq_state(udqp.undefinedValue());
WellMatcher wm(NameOrder({"P1"}));
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_well_var("P1", "WOPR", 1);
@@ -219,7 +351,8 @@ BOOST_AUTO_TEST_CASE(UDQFieldSetTest) {
SummaryState st(TimeService::now());
UDQState udq_state(udqp.undefinedValue());
WellMatcher wm(NameOrder({"P1", "P2", "P3", "P4"}));
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_well_var("P1", "WOPR", 1.0);
st.update_well_var("P2", "WOPR", 2.0);
@@ -321,7 +454,8 @@ BOOST_AUTO_TEST_CASE(UDQ_GROUP_TEST) {
UDQDefine def_fopr(udqp, "FUOPR",0, location, {"SUM", "(", "GOPR", ")"});
SummaryState st(TimeService::now());
UDQState udq_state(udqp.undefinedValue());
UDQContext context(udqft, {}, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, {}, segmentMatcherFactory, st, udq_state);
st.update_group_var("G1", "GOPR", 1.0);
st.update_group_var("G2", "GOPR", 2.0);
@@ -343,7 +477,8 @@ BOOST_AUTO_TEST_CASE(UDQ_DEFINETEST) {
SummaryState st(TimeService::now());
UDQState udq_state(udqp.undefinedValue());
WellMatcher wm(NameOrder({"W1", "W2", "W3"}));
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_well_var("W1", "WBHP", 11);
st.update_well_var("W2", "WBHP", 2);
@@ -363,8 +498,8 @@ BOOST_AUTO_TEST_CASE(UDQ_DEFINETEST) {
SummaryState st(TimeService::now());
UDQState udq_state(udqp.undefinedValue());
WellMatcher wm(NameOrder({"I1", "I2", "P1", "P2"}));
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_well_var("P1", "WBHP", 1);
st.update_well_var("P2", "WBHP", 2);
@@ -382,7 +517,8 @@ BOOST_AUTO_TEST_CASE(UDQ_DEFINETEST) {
SummaryState st(TimeService::now());
UDQState udq_state(udqp.undefinedValue());
WellMatcher wm(NameOrder({"P1", "P2", "I1", "I2"}));
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_well_var("P1", "WBHP", 4);
st.update_well_var("P2", "WBHP", 3);
st.update_well_var("I1", "WBHP", 2);
@@ -579,15 +715,13 @@ ASSIGN WU2 8.0 /
BOOST_CHECK_EQUAL( w2["P2"].get(), 8.0 );
}
BOOST_AUTO_TEST_CASE(UDQ_CONTEXT) {
SummaryState st(TimeService::now());
UDQFunctionTable func_table;
UDQParams udqp;
UDQState udq_state(udqp.undefinedValue());
UDQContext ctx(func_table, {}, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext ctx(func_table, {}, segmentMatcherFactory, st, udq_state);
BOOST_CHECK_EQUAL(*ctx.get("JAN"), 1.0);
BOOST_CHECK_THROW(ctx.get("NO_SUCH_KEY"), std::out_of_range);
@@ -1044,7 +1178,8 @@ BOOST_AUTO_TEST_CASE(UDQ_POW_TEST) {
UDQState udq_state(udqp.undefinedValue());
NameOrder wo; wo.add("P1");
WellMatcher wm(wo);
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_well_var("P1", "WOPR", 1);
st.update_well_var("P1", "WWPR", 2);
@@ -1066,7 +1201,8 @@ BOOST_AUTO_TEST_CASE(UDQ_CMP_TEST) {
SummaryState st(TimeService::now());
UDQState udq_state(udqp.undefinedValue());
WellMatcher wm(NameOrder({"P1", "P2"}));
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_well_var("P1", "WOPR", 0);
st.update_well_var("P1", "WWPR", 10);
@@ -1097,7 +1233,8 @@ BOOST_AUTO_TEST_CASE(UDQ_SCALAR_SET) {
SummaryState st(TimeService::now());
UDQState udq_state(udqp.undefinedValue());
WellMatcher wm(NameOrder({"PA1", "PB2", "PC3", "PD4"}));
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_well_var("PA1", "WOPR", 1);
st.update_well_var("PB2", "WOPR", 2);
@@ -1168,7 +1305,8 @@ BOOST_AUTO_TEST_CASE(UDQ_SORTD_NAN) {
SummaryState st(TimeService::now());
UDQState udq_state(udqp.undefinedValue());
WellMatcher wm(NameOrder({"OP1", "OP2", "OP3", "OP4"}));
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_well_var("OP1", "WWIR", 1.0);
st.update_well_var("OP2", "WWIR", 2.0);
@@ -1213,7 +1351,8 @@ BOOST_AUTO_TEST_CASE(UDQ_SORTA) {
SummaryState st(TimeService::now());
UDQState udq_state(udqp.undefinedValue());
WellMatcher wm(NameOrder({"OPL01", "OPL02", "OPU01", "OPU02"}));
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_well_var("OPL01", "WWCT", 0.7);
st.update_well_var("OPL02", "WWCT", 0.8);
@@ -1242,7 +1381,8 @@ BOOST_AUTO_TEST_CASE(UDQ_BASIC_MATH_TEST) {
SummaryState st(TimeService::now());
UDQState udq_state(udqp.undefinedValue());
WellMatcher wm(NameOrder({"P1", "P2", "P3", "P4"}));
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_well_var("P1", "WOPR", 1);
st.update_well_var("P2", "WOPR", 2);
@@ -1305,7 +1445,8 @@ BOOST_AUTO_TEST_CASE(DECK_TEST) {
SummaryState st(TimeService::now());
UDQState udq_state(udqp.undefinedValue());
WellMatcher wm(NameOrder({"OP1", "OP2", "OP3"}));
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_well_var("OP1", "WOPR", 300);
st.update_well_var("OP2", "WOPR", 3000);
@@ -1360,7 +1501,8 @@ BOOST_AUTO_TEST_CASE(UDQ_PARSE_ERROR) {
UDQFunctionTable udqft(udqp);
UDQState udq_state(udqp.undefinedValue());
WellMatcher wm(NameOrder({"P1"}));
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_well_var("P1", "WBHP", 1);
auto res = def1.eval(context);
@@ -1387,7 +1529,8 @@ BOOST_AUTO_TEST_CASE(UDQ_TYPE_ERROR) {
UDQFunctionTable udqft(udqp);
UDQState udq_state(udqp.undefinedValue());
WellMatcher wm(NameOrder({"P1", "P2"}));
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_well_var("P1", "WBHP", 1);
st.update_well_var("P2", "WBHP", 2);
@@ -1547,7 +1690,8 @@ BOOST_AUTO_TEST_CASE(UDQ_USAGE) {
BOOST_CHECK_EQUAL( usage.iuad().size(), 0U );
UDAValue uda1("WUX");
conf.add_assign(uda1.get<std::string>(), std::vector<std::string>{}, 100, 0);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
conf.add_assign(uda1.get<std::string>(), segmentMatcherFactory, std::vector<std::string>{}, 100, 0);
const auto& iuad = usage.iuad();
usage.update(conf, uda1, "W1", UDAControl::WCONPROD_ORAT);
@@ -1843,7 +1987,8 @@ UDQ
SummaryState st(TimeService::now());
UDQFunctionTable udqft(udqp);
UDQState udq_state(udqp.undefinedValue());
UDQContext context(udqft, {}, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, {}, segmentMatcherFactory, st, udq_state);
auto res0 = def0.eval(context);
BOOST_CHECK_CLOSE( res0[0].get(), -0.00125*3, 1e-6);
@@ -1868,7 +2013,8 @@ UDQ
SummaryState st(TimeService::now());
UDQFunctionTable udqft(udqp);
UDQState udq_state(udqp.undefinedValue());
UDQContext context(udqft, {}, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, {}, segmentMatcherFactory, st, udq_state);
const double fwpr = 7;
const double fopr = 4;
const double fgpr = 7;
@@ -1911,7 +2057,8 @@ UDQ
UDQFunctionTable udqft(udqp);
UDQState udq_state(udqp.undefinedValue());
WellMatcher wm(NameOrder({"W1", "W2", "W3"}));
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_well_var("W1", "WOPR", 1);
st.update_well_var("W2", "WOPR", 2);
st.update_well_var("W3", "WOPR", 3);
@@ -1949,7 +2096,8 @@ UDQ
SummaryState st(TimeService::now());
auto undefined_value = udq.params().undefinedValue();
UDQState udq_state(undefined_value);
udq.eval(0, schedule, {}, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
udq.eval(0, schedule, {}, segmentMatcherFactory, st, udq_state);
BOOST_CHECK_EQUAL( st.get("FU_UADD"), 12); // 10 + 2
@@ -1975,7 +2123,8 @@ DEFINE FU_PAR2 FU_PAR3 /
auto undefined_value = udq.params().undefinedValue();
UDQState udq_state(undefined_value);
st.update("FMWPR", 100);
udq.eval(0, schedule, {}, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
udq.eval(0, schedule, {}, segmentMatcherFactory, st, udq_state);
BOOST_CHECK_EQUAL(st.get("FU_PAR2"), 100);
}
@@ -1993,7 +2142,8 @@ DEFINE FU_PAR3 FU_PAR2 + 1/
SummaryState st(TimeService::now());
auto undefined_value = udq.params().undefinedValue();
UDQState udq_state(undefined_value);
udq.eval(0, schedule, {}, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
udq.eval(0, schedule, {}, segmentMatcherFactory, st, udq_state);
BOOST_CHECK_EQUAL(st.get("FU_PAR2"), undefined_value);
BOOST_CHECK_EQUAL(st.get("FU_PAR3"), undefined_value);
@@ -2103,19 +2253,32 @@ DEFINE WUGASRA 750000 - WGLIR '*' /
NameOrder wo({"W1", "W2", "W3"});
WellMatcher wm(wo);
udq.eval(0, schedule, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
udq.eval(0, schedule, wm, segmentMatcherFactory, st, udq_state);
const auto required_keys = [&udq]()
{
std::unordered_set<std::string> required_keys;
udq.required_summary(required_keys);
BOOST_CHECK_EQUAL( required_keys.size(), 7);
BOOST_CHECK_EQUAL( required_keys.count("TIMESTEP"), 1);
BOOST_CHECK_EQUAL( required_keys.count("FMWPR"), 1);
BOOST_CHECK_EQUAL( required_keys.count("WGLIR"), 1);
BOOST_CHECK_EQUAL( required_keys.count("FOPR"), 1);
BOOST_CHECK_EQUAL( required_keys.count("FMWIN"), 1);
BOOST_CHECK_EQUAL( required_keys.count("FMWPA"), 1);
BOOST_CHECK_EQUAL( required_keys.count("FMWIA"), 1);
}
auto keys = std::unordered_set<std::string>{};
udq.required_summary(keys);
auto required = std::vector<std::string>{ keys.begin(), keys.end() };
std::sort(required.begin(), required.end());
return required;
}();
const auto expected_keys = std::vector<std::string> {
"FMWIA",
"FMWIN",
"FMWPA",
"FMWPR",
"FOPR",
"TIMESTEP",
"WGLIR",
};
BOOST_CHECK_EQUAL_COLLECTIONS(required_keys.begin(), required_keys.end(),
expected_keys.begin(), expected_keys.end());
}
BOOST_AUTO_TEST_CASE(UDQ_UNDEFINED) {
@@ -2307,7 +2470,8 @@ DEFINE FU_VAR91 GOPR TEST /
st.update_well_var("W3", "WGLIR", 3);
st.update_group_var("TEST", "GOPR", 1);
udq.eval(0, schedule, {}, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
udq.eval(0, schedule, {}, segmentMatcherFactory, st, udq_state);
}
BOOST_AUTO_TEST_CASE(UDQ_KEY_ERROR) {
@@ -2326,7 +2490,8 @@ UDQ
UDQState udq_state(undefined_value);
SummaryState st(TimeService::now());
BOOST_CHECK_THROW(udq.eval(0, schedule, {}, st, udq_state), std::exception);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
BOOST_CHECK_THROW(udq.eval(0, schedule, {}, segmentMatcherFactory, st, udq_state), std::exception);
}
BOOST_AUTO_TEST_CASE(UDQ_ASSIGN) {
@@ -2345,13 +2510,25 @@ UDQ
auto undefined_value = udq.params().undefinedValue();
UDQState udq_state(undefined_value);
SummaryState st(TimeService::now());
{
std::unordered_set<std::string> required_keys;
udq.required_summary(required_keys);
BOOST_CHECK(required_keys.empty());
}
udq.eval(0, schedule, {}, st, udq_state);
const auto required_keys = [&udq]()
{
auto keys = std::unordered_set<std::string>{};
udq.required_summary(keys);
auto required = std::vector<std::string>{ keys.begin(), keys.end() };
std::sort(required.begin(), required.end());
return required;
}();
const auto expected_keys = std::vector<std::string> {};
BOOST_CHECK_EQUAL_COLLECTIONS(required_keys.begin(), required_keys.end(),
expected_keys.begin(), expected_keys.end());
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
udq.eval(0, schedule, {}, segmentMatcherFactory, st, udq_state);
BOOST_CHECK_EQUAL(st.get("FU_VAR1"), 10);
}
@@ -2392,7 +2569,8 @@ TSTEP
// Counting: 1,2,3,4,5
for (std::size_t report_step = 0; report_step < 5; report_step++) {
const auto& udq = schedule.getUDQConfig(report_step);
udq.eval(report_step, schedule, schedule.wellMatcher(report_step), st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
udq.eval(report_step, schedule, schedule.wellMatcher(report_step), segmentMatcherFactory, st, udq_state);
auto fu_var1 = st.get("FU_VAR1");
BOOST_CHECK_EQUAL(fu_var1, report_step + 1);
}
@@ -2400,7 +2578,8 @@ TSTEP
// Reset to zero and count: 1,2,3,4,5
for (std::size_t report_step = 5; report_step < 10; report_step++) {
const auto& udq = schedule.getUDQConfig(report_step);
udq.eval(report_step, schedule, schedule.wellMatcher(report_step), st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
udq.eval(report_step, schedule, schedule.wellMatcher(report_step), segmentMatcherFactory, st, udq_state);
auto fu_var1 = st.get("FU_VAR1");
BOOST_CHECK_EQUAL(fu_var1, report_step - 4);
}
@@ -2408,7 +2587,8 @@ TSTEP
// Reset to zero and stay there.
for (std::size_t report_step = 10; report_step < 15; report_step++) {
const auto& udq = schedule.getUDQConfig(report_step);
udq.eval(report_step, schedule, schedule.wellMatcher(report_step),st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
udq.eval(report_step, schedule, schedule.wellMatcher(report_step), segmentMatcherFactory, st, udq_state);
auto fu_var1 = st.get("FU_VAR1");
BOOST_CHECK_EQUAL(fu_var1, 0);
}
@@ -2432,7 +2612,8 @@ BOOST_AUTO_TEST_CASE(UDQ_DIV_TEST) {
UDQDefine def_div(udqp, "FU",0, location, {"128", "/", "2", "/", "4", "/", "8"});
SummaryState st(TimeService::now());
UDQState udq_state(udqp.undefinedValue());
UDQContext context(udqft, {}, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, {}, segmentMatcherFactory, st, udq_state);
auto res_div = def_div.eval(context);
BOOST_CHECK_EQUAL( res_div[0].get() , 2.0);
@@ -2459,7 +2640,8 @@ UDQ
SummaryState st(TimeService::now());
const auto& udq = schedule.getUDQConfig(0);
udq.eval(0, schedule, {}, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
udq.eval(0, schedule, {}, segmentMatcherFactory, st, udq_state);
auto fu_var1 = st.get("FU_VAR1");
auto fu_var2 = st.get("FU_VAR2");
auto fu_var3 = st.get("FU_VAR3");
@@ -2504,7 +2686,8 @@ UDQ
st.update_well_var("P3", "WOPR", 3);
st.update_well_var("P4", "WOPR", 4);
udq.eval(0, schedule, schedule.wellMatcher(0), st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
udq.eval(0, schedule, schedule.wellMatcher(0), segmentMatcherFactory, st, udq_state);
auto fu_var1 = st.get("FU_VAR1");
auto fu_var2 = st.get("FU_VAR2");
auto fu_var3 = st.get("FU_VAR3");
@@ -2530,7 +2713,8 @@ UDQ
UDQState udq_state(0);
SummaryState st(TimeService::now());
const auto& udq = schedule.getUDQConfig(0);
udq.eval(0, schedule, schedule.wellMatcher(0), st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
udq.eval(0, schedule, schedule.wellMatcher(0), segmentMatcherFactory, st, udq_state);
auto fu_var1 = st.get("FU_VAR1");
auto fu_var2 = st.get("FU_VAR2");
@@ -2658,7 +2842,8 @@ UDQ
SummaryState st(TimeService::now());
UDQFunctionTable udqft;
WellMatcher wm(NameOrder({"W1", "W2", "W3"}));
UDQContext context(udqft, wm, st, udq_state);
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
UDQContext context(udqft, wm, segmentMatcherFactory, st, udq_state);
st.update_well_var("W1", "WBHP", 400);
st.update_well_var("W2", "WBHP", 300);
st.update_well_var("W3", "WBHP", 200);
@@ -2700,3 +2885,23 @@ BOOST_AUTO_TEST_CASE(UDQ_ASSIGN_RST) {
BOOST_CHECK_EQUAL(res["W2"].get(), 100);
BOOST_CHECK_EQUAL(res["W3"].defined(), false);
}
BOOST_AUTO_TEST_CASE(UDQ_ASSIGN_SEGMENT)
{
auto segmentMatcherFactory = [sched_state = dynamicInputData()]()
{
return std::make_unique<SegmentMatcher>(sched_state);
};
auto cfg = UDQConfig{};
cfg.add_assign("SUSPECT" , segmentMatcherFactory, {"OP-01"}, 17.29, 42);
cfg.add_assign("SUSPECT" , segmentMatcherFactory, {"OP-02", "3"}, 9.876e-5, 42);
cfg.add_assign("SUSPECT" , segmentMatcherFactory, {"OP-06", "1"}, 0.123, 42); // Not an MSW
cfg.add_assign("SUPER" , segmentMatcherFactory, {}, 2.71828, 42);
cfg.add_assign("SUCCINCT", segmentMatcherFactory, {"OP*", "2"}, 3.1415, 42);
{
const auto all = cfg.assignments();
BOOST_CHECK_EQUAL(all.size(), std::size_t{3}); // Three different SU* variables
}
}

View File

@@ -2,97 +2,113 @@
#include <boost/test/unit_test.hpp>
#include <opm/output/eclipse/AggregateUDQData.hpp>
#include <opm/output/eclipse/AggregateConnectionData.hpp>
#include <opm/output/eclipse/AggregateGroupData.hpp>
#include <opm/output/eclipse/AggregateWellData.hpp>
#include <opm/output/eclipse/AggregateConnectionData.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/output/eclipse/AggregateUDQData.hpp>
#include <opm/output/eclipse/WriteRestartHelpers.hpp>
#include <opm/output/eclipse/DoubHEAD.hpp>
#include <opm/output/eclipse/InteHEAD.hpp>
#include <opm/output/eclipse/VectorItems/intehead.hpp>
#include <opm/output/eclipse/DoubHEAD.hpp>
#include <opm/output/eclipse/WriteRestartHelpers.hpp>
#include <opm/output/data/Wells.hpp>
#include <opm/io/eclipse/ERst.hpp>
#include <opm/io/eclipse/OutputStream.hpp>
#include <opm/io/eclipse/RestartFileView.hpp>
#include <opm/io/eclipse/rst/state.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/Python/Python.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQInput.hpp>
#include <opm/input/eclipse/Schedule/Action/State.hpp>
#include <opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/ScheduleState.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQActive.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQConfig.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQParams.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQState.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQSet.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQEnums.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQInput.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQParams.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQSet.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQState.hpp>
#include <opm/input/eclipse/Schedule/Well/WellMatcher.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTestState.hpp>
#include <opm/input/eclipse/Units/UnitSystem.hpp>
#include <opm/input/eclipse/Units/Units.hpp>
#include <opm/common/utility/TimeService.hpp>
#include <opm/io/eclipse/ERst.hpp>
#include <opm/io/eclipse/RestartFileView.hpp>
#include <opm/io/eclipse/rst/state.hpp>
#include <opm/input/eclipse/Schedule/Action/State.hpp>
#include <opm/output/data/Wells.hpp>
#include <opm/io/eclipse/OutputStream.hpp>
#include <opm/common/utility/TimeService.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <algorithm>
#include <stdexcept>
#include <utility>
#include <cstddef>
#include <exception>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
#include "tests/WorkArea.hpp"
namespace {
Opm::Deck first_sim(std::string fname) {
Opm::Deck first_sim(const std::string& fname)
{
return Opm::Parser{}.parseFile(fname);
}
/*
Opm::UDQActive udq_active() {
int update_count = 0;
// construct record data for udq_active
Opm::UDQParams params;
Opm::UDQConfig conf(params);
Opm::UDQActive udq_act;
Opm::UDAValue uda1("WUOPRL");
update_count += udq_act.update(conf, uda1, "PROD1", Opm::UDAControl::WCONPROD_ORAT);
Opm::UDAValue uda2("WULPRL");
update_count += udq_act.update(conf, uda2, "PROD1", Opm::UDAControl::WCONPROD_LRAT);
Opm::UDAValue uda3("WUOPRU");
update_count += udq_act.update(conf, uda3, "PROD2", Opm::UDAControl::WCONPROD_ORAT);
Opm::UDAValue uda4("WULPRU");
update_count += udq_act.update(conf, uda4, "PROD2", Opm::UDAControl::WCONPROD_LRAT);
#if 0
Opm::UDQActive udq_active()
{
int update_count = 0;
// construct record data for udq_active
Opm::UDQParams params;
Opm::UDQConfig conf(params);
Opm::UDQActive udq_act;
Opm::UDAValue uda1("WUOPRL");
update_count += udq_act.update(conf, uda1, "PROD1", Opm::UDAControl::WCONPROD_ORAT);
for (std::size_t index=0; index < udq_act.IUAD_size(); index++)
{
const auto & record = udq_act[index];
auto ind = record.input_index;
auto udq_key = record.udq;
auto name = record.wgname;
auto ctrl_type = record.control;
}
return udq_act;
Opm::UDAValue uda2("WULPRL");
update_count += udq_act.update(conf, uda2, "PROD1", Opm::UDAControl::WCONPROD_LRAT);
Opm::UDAValue uda3("WUOPRU");
update_count += udq_act.update(conf, uda3, "PROD2", Opm::UDAControl::WCONPROD_ORAT);
Opm::UDAValue uda4("WULPRU");
update_count += udq_act.update(conf, uda4, "PROD2", Opm::UDAControl::WCONPROD_LRAT);
for (std::size_t index = 0; index < udq_act.IUAD_size(); ++index) {
const auto & record = udq_act[index];
auto ind = record.input_index;
auto udq_key = record.udq;
auto name = record.wgname;
auto ctrl_type = record.control;
}
return udq_act;
}
*/
}
#endif
Opm::UDQSet
make_udq_set(const std::string& name,
const Opm::UDQVarType var_type,
const std::vector<std::string>& wgnames,
const std::vector<double>& values)
{
Opm::UDQSet s(name, var_type, wgnames);
Opm::UDQSet make_udq_set(const std::string& name, Opm::UDQVarType var_type, const std::vector<std::string>& wgnames, const std::vector<double>& values) {
Opm::UDQSet s(name, var_type, wgnames);
for (std::size_t i=0; i < values.size(); i++)
s.assign(i , values[i]);
for (std::size_t i = 0; i < values.size(); ++i) {
s.assign(i, values[i]);
}
return s;
}
return s;
}
Opm::UDQState make_udq_state()
{
@@ -170,38 +186,42 @@ Opm::UDQSet make_udq_set(const std::string& name, Opm::UDQVarType var_type, cons
return state;
}
struct SimulationCase
{
explicit SimulationCase(const Opm::Deck& deck)
: es { deck }
, grid { deck }
, sched{ deck, es, std::make_shared<Opm::Python>() }
{}
//int main(int argc, char* argv[])
struct SimulationCase
{
explicit SimulationCase(const Opm::Deck& deck)
: es { deck }
, grid { deck }
, python { std::make_shared<Opm::Python>()}
, sched{ deck, es, python }
{}
// Order requirement: 'es' must be declared/initialised before 'sched'.
Opm::EclipseState es;
Opm::EclipseGrid grid;
Opm::Schedule sched;
Opm::Parser parser;
};
// Order requirement: 'es' must be declared/initialised before 'sched'.
Opm::EclipseState es;
Opm::EclipseGrid grid;
std::shared_ptr<Opm::Python> python;
Opm::Schedule sched;
Opm::Parser parser;
};
bool udq_contains(const std::vector<Opm::UDQActive::RstRecord>& records,
const Opm::UDAControl control,
const std::string& udq,
const std::string& wgname)
{
auto find_iter = std::find_if(records.begin(), records.end(),
[&control, &udq, &wgname](const Opm::UDQActive::RstRecord& record)
{
return (record.control == control)
&& (record.wgname == wgname)
&& (record.value.get<std::string>() == udq);
});
BOOST_AUTO_TEST_SUITE(Aggregate_UDQ)
return find_iter != records.end();
}
bool udq_contains(const std::vector<Opm::UDQActive::RstRecord>& records, Opm::UDAControl control, const std::string& udq, const std::string wgname) {
auto find_iter = std::find_if(records.begin(),
records.end(),
[&control, &udq, &wgname] (const Opm::UDQActive::RstRecord& record) {
return record.control == control &&
record.wgname == wgname &&
record.value.get<std::string>() == udq;
});
return find_iter != records.end();
}
// ---------------------------------------------------------------------------
BOOST_AUTO_TEST_SUITE(Aggregate_UDQ)
// test constructed UDQ restart data
BOOST_AUTO_TEST_CASE (Declared_UDQ_data)
@@ -219,11 +239,10 @@ BOOST_AUTO_TEST_CASE (Declared_UDQ_data)
// Report Step 1: 2008-10-10 --> 2011-01-20
const auto rptStep = std::size_t{1};
double secs_elapsed = 3.1536E07;
const auto ih = Opm::RestartIO::Helpers::
createInteHead(es, grid, sched, secs_elapsed,
rptStep, rptStep, rptStep-1);
rptStep, rptStep, rptStep - 1);
//set dummy value for next_step_size
const double next_step_size= 0.1;
@@ -234,10 +253,11 @@ BOOST_AUTO_TEST_CASE (Declared_UDQ_data)
const auto udqDims = Opm::RestartIO::Helpers::createUdqDims(sched, rptStep-1, ih);
auto udqData = Opm::RestartIO::Helpers::AggregateUDQData(udqDims);
udqData.captureDeclaredUDQData(sched, rptStep-1, udq_state, ih);
udqData.captureDeclaredUDQData(sched, rptStep - 1, udq_state, ih);
{
WorkArea work;
{
std::string outputDir = "./";
std::string baseName = "TEST_UDQRST";
@@ -309,7 +329,6 @@ BOOST_AUTO_TEST_CASE (Declared_UDQ_data)
BOOST_CHECK_EQUAL(dh[214], 1.0E-4);
}
{
/*
IUDQ
@@ -731,7 +750,6 @@ BOOST_AUTO_TEST_CASE (Declared_UDQ_data)
BOOST_CHECK_EQUAL(zUdn[start + 1].c_str(), "SM3/DAY "); // udq NO. 6
}
{
/*
ZUDL:
@@ -772,7 +790,6 @@ BOOST_AUTO_TEST_CASE (Declared_UDQ_data)
BOOST_CHECK_EQUAL(zUdl[start + 3].c_str(), " "); // udq NO. 1
}
{
/*
'DUDW ' 24 'DOUB'
@@ -816,7 +833,6 @@ BOOST_AUTO_TEST_CASE (Declared_UDQ_data)
BOOST_CHECK_EQUAL(dUdg[start + 4], -0.3E+21); // duDg NO. 1
}
{
/*
'DUDG ' 1 'DOUB'
@@ -841,12 +857,14 @@ BOOST_AUTO_TEST_CASE (Declared_UDQ_data)
BOOST_CHECK_EQUAL(rst_state.header.num_udq(), 44);
BOOST_CHECK_EQUAL(rst_state.udqs.size(), 44);
std::vector<std::pair<std::string, std::string>> expected = {{"WUOPRL", "SM3/DAY"},
{"WULPRL", "SM3/DAY"},
{"WUOPRU", "SM3/DAY"},
{"GUOPRU", "SM3/DAY"},
{"WULPRU", "SM3/DAY"},
{"FULPR", "SM3/DAY"}};
const auto expected = std::vector<std::pair<std::string, std::string>> {
{"WUOPRL", "SM3/DAY"},
{"WULPRL", "SM3/DAY"},
{"WUOPRU", "SM3/DAY"},
{"GUOPRU", "SM3/DAY"},
{"WULPRU", "SM3/DAY"},
{"FULPR" , "SM3/DAY"},
};
std::size_t iudq = 0;
for (const auto& [name, unit] : expected) {
@@ -855,7 +873,6 @@ BOOST_AUTO_TEST_CASE (Declared_UDQ_data)
iudq += 1;
}
const std::size_t report_step = 1;
const auto& udq_params = es.runspec().udqParams();
const auto& input_config = sched[report_step].udq();
@@ -863,12 +880,20 @@ BOOST_AUTO_TEST_CASE (Declared_UDQ_data)
BOOST_CHECK_EQUAL(input_config.size(), rst_config.size());
BOOST_CHECK_EQUAL(input_config.definitions().size(), rst_config.definitions().size());
const std::vector<std::string>& wells = {"PROD1", "PROD2", "WINJ1", "WINJ2"};
const auto wells = std::vector<std::string> {
"PROD1", "PROD2", "WINJ1", "WINJ2",
};
Opm::UDQState rst_udq_state(udq_params.undefinedValue());
Opm::UDQFunctionTable udqft(udq_params);
auto wm = Opm::WellMatcher(wells);
Opm::UDQContext input_context(udqft, wm, st, udq_state);
Opm::UDQContext rst_context(udqft, wm, st, rst_udq_state);
auto segmentMatcherFactory = []()
{
return std::make_unique<Opm::SegmentMatcher>(Opm::ScheduleState{});
};
Opm::UDQContext input_context(udqft, wm, segmentMatcherFactory, st, udq_state);
Opm::UDQContext rst_context(udqft, wm, segmentMatcherFactory, st, rst_udq_state);
rst_udq_state.load_rst(rst_state);
for (const auto& input_def : input_config.definitions()) {
@@ -888,9 +913,10 @@ BOOST_AUTO_TEST_CASE (Declared_UDQ_data)
BOOST_CHECK(input_eval == rst_eval);
}
const auto& uda_records = Opm::UDQActive::load_rst(
es.getUnits(), input_config, rst_state, sched.wellNames(report_step), sched.groupNames(report_step));
const auto uda_records =
Opm::UDQActive::load_rst(es.getUnits(), input_config, rst_state,
sched.wellNames(report_step),
sched.groupNames(report_step));
BOOST_CHECK_EQUAL(uda_records.size(), 4);
BOOST_CHECK(udq_contains(uda_records, Opm::UDAControl::WCONPROD_ORAT, "WUOPRU", "PROD1"));
@@ -916,7 +942,6 @@ BOOST_AUTO_TEST_CASE (Declared_UDQ_data_2)
auto rptStep = std::size_t{1};
auto simStep = rptStep - 1;
double secs_elapsed = 3.1536E07;
auto ih = Opm::RestartIO::Helpers::
createInteHead(es, grid, sched, secs_elapsed,
@@ -925,15 +950,14 @@ BOOST_AUTO_TEST_CASE (Declared_UDQ_data_2)
//set dummy value for next_step_size
double next_step_size= 0.1;
auto dh = Opm::RestartIO::Helpers::createDoubHead(es, sched, simStep, simStep+1,
secs_elapsed, next_step_size);
secs_elapsed, next_step_size);
auto lh = Opm::RestartIO::Helpers::createLogiHead(es);
auto udqDims = Opm::RestartIO::Helpers::createUdqDims(sched, simStep, ih);
auto udqData = Opm::RestartIO::Helpers::AggregateUDQData(udqDims);
auto udqData = Opm::RestartIO::Helpers::AggregateUDQData(udqDims);
udqData.captureDeclaredUDQData(sched, simStep, udq_state, ih);
{
const auto& iGph = udqData.getIGPH();
@@ -952,7 +976,7 @@ BOOST_AUTO_TEST_CASE (Declared_UDQ_data_2)
//set dummy value for next_step_size
next_step_size= 0.1;
dh = Opm::RestartIO::Helpers::createDoubHead(es, sched, simStep, simStep+1,
secs_elapsed, next_step_size);
secs_elapsed, next_step_size);
lh = Opm::RestartIO::Helpers::createLogiHead(es);
@@ -960,14 +984,12 @@ BOOST_AUTO_TEST_CASE (Declared_UDQ_data_2)
udqData = Opm::RestartIO::Helpers::AggregateUDQData(udqDims);
udqData.captureDeclaredUDQData(sched, simStep, udq_state, ih);
{
const auto& iGph = udqData.getIGPH();
auto start = 0*udqDims[1];
BOOST_CHECK_EQUAL(iGph[start + 0] , 2); // (2 - water injection)
}
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END() // Aggregate_UDQ

View File

@@ -22,26 +22,31 @@
#define BOOST_TEST_MODULE EclipseIO
#include <boost/test/unit_test.hpp>
#include <opm/output/data/Cells.hpp>
#include <opm/output/data/Groups.hpp>
#include <opm/output/data/Wells.hpp>
#include <opm/output/eclipse/AggregateAquiferData.hpp>
#include <opm/output/eclipse/EclipseIO.hpp>
#include <opm/output/eclipse/RestartIO.hpp>
#include <opm/output/eclipse/RestartValue.hpp>
#include <opm/output/data/Cells.hpp>
#include <opm/output/data/Wells.hpp>
#include <opm/output/data/Groups.hpp>
#include <opm/input/eclipse/Python/Python.hpp>
#include <opm/io/eclipse/ERst.hpp>
#include <opm/io/eclipse/EclIOdata.hpp>
#include <opm/io/eclipse/OutputStream.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/EclipseState/Tables/Eqldims.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/input/eclipse/EclipseState/IOConfig/IOConfig.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/Utility/Functional.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/EclipseState/Tables/Eqldims.hpp>
#include <opm/input/eclipse/Python/Python.hpp>
#include <opm/input/eclipse/Schedule/Action/State.hpp>
#include <opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/ScheduleState.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQConfig.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQEnums.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQState.hpp>
@@ -49,16 +54,17 @@
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
#include <opm/input/eclipse/Schedule/Well/WellMatcher.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTestState.hpp>
#include <opm/input/eclipse/Utility/Functional.hpp>
#include <opm/io/eclipse/OutputStream.hpp>
#include <opm/io/eclipse/EclIOdata.hpp>
#include <opm/io/eclipse/ERst.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/common/utility/TimeService.hpp>
#include <sstream>
#include <tuple>
#include <opm/common/utility/TimeService.hpp>
#include <tests/WorkArea.hpp>
using namespace Opm;
@@ -95,16 +101,14 @@ namespace {
{
return std::get<1>(vec);
}
}
} // Anonymous namespace
namespace Opm {
namespace data {
/*
* Some test specific equivalence definitions and pretty-printing. Not fit as a
* general purpose implementation, but does its job for testing and
* pretty-pringing for debugging purposes.
*/
// Some test specific equivalence definitions and pretty-printing. Not fit
// as a general purpose implementation, but does its job for testing and
// pretty-pringing for debugging purposes.
std::ostream& operator<<( std::ostream& stream, const Rates& r ) {
return stream << "{ "
@@ -141,7 +145,9 @@ std::ostream& operator<<( std::ostream& stream,
return stream;
}
}
} // namespace data
namespace {
data::GroupAndNetworkValues mkGroups() {
return {};
@@ -382,49 +388,54 @@ Opm::SummaryState sim_state(const Opm::Schedule& sched)
return state;
}
struct Setup {
struct Setup
{
Deck deck;
EclipseState es;
const EclipseGrid& grid;
std::shared_ptr<Python> python;
Schedule schedule;
SummaryConfig summary_config;
Setup( const char* path) :
deck( Parser().parseFile( path) ),
es( deck),
grid( es.getInputGrid( ) ),
python( std::make_shared<Python>() ),
schedule( deck, es, python ),
summary_config( deck, schedule, es.fieldProps(), es.aquifer() )
Setup(const char* path)
: deck ( Parser().parseFile( path) )
, es ( deck)
, grid ( es.getInputGrid( ) )
, schedule ( deck, es, std::make_shared<Python>() )
, summary_config( deck, schedule, es.fieldProps(), es.aquifer() )
{
auto& io_config = es.getIOConfig();
io_config.setEclCompatibleRST(false);
}
};
RestartValue first_sim(const Setup& setup, Action::State& action_state, SummaryState& st, UDQState& udq_state, bool write_double) {
RestartValue
first_sim(const Setup& setup,
Action::State& action_state,
SummaryState& st,
UDQState& udq_state,
bool write_double)
{
WellTestState wtest_state;
EclipseIO eclWriter( setup.es, setup.grid, setup.schedule, setup.summary_config);
auto num_cells = setup.grid.getNumActive( );
int report_step = 1;
auto start_time = setup.schedule.getStartTime();
auto first_step = setup.schedule.simTime(report_step);
auto sol = mkSolution( num_cells );
auto wells = mkWells();
auto groups = mkGroups();
const auto num_cells = setup.grid.getNumActive( );
const int report_step = 1;
const auto start_time = setup.schedule.getStartTime();
const auto first_step = setup.schedule.simTime(report_step);
const auto sol = mkSolution(num_cells);
const auto wells = mkWells();
const auto groups = mkGroups();
const auto& udq = setup.schedule.getUDQConfig(report_step);
RestartValue restart_value(sol, wells, groups, {});
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState{}); };
udq.eval(report_step,
setup.schedule,
setup.schedule.wellMatcher(report_step),
segmentMatcherFactory,
st, udq_state);
RestartValue restart_value(sol, wells, groups, {});
eclWriter.writeTimeStep( action_state,
wtest_state,
st,
@@ -438,32 +449,39 @@ RestartValue first_sim(const Setup& setup, Action::State& action_state, SummaryS
return restart_value;
}
RestartValue second_sim(const Setup& setup, Action::State& action_state, SummaryState& summary_state, const std::vector<RestartKey>& solution_keys) {
RestartValue
second_sim(const Setup& setup,
Action::State& action_state,
SummaryState& summary_state,
const std::vector<RestartKey>& solution_keys)
{
EclipseIO writer(setup.es, setup.grid, setup.schedule, setup.summary_config);
return writer.loadRestart( action_state, summary_state, solution_keys );
}
void compare( const RestartValue& fst,
const RestartValue& snd,
const std::vector<RestartKey>& solution_keys) {
void compare(const RestartValue& fst,
const RestartValue& snd,
const std::vector<RestartKey>& solution_keys)
{
for (const auto& value : solution_keys) {
double tol = 0.00001;
const std::string& key = value.key;
auto first = fst.solution.data( key ).begin();
auto second = snd.solution.data( key ).begin();
auto tol = 0.00001;
const auto& key = value.key;
if (key == "TEMP")
tol *= 10;
if (key == "TEMP") {
tol *= 10.0;
}
for( ; first != fst.solution.data( key).end(); ++first, ++second )
BOOST_CHECK_CLOSE( *first, *second, tol );
auto first = fst.solution.data(key).begin();
auto second = snd.solution.data(key).begin();
for (; first != fst.solution.data( key).end(); ++first, ++second) {
BOOST_CHECK_CLOSE(*first, *second, tol);
}
}
}
} // Anonymous namespace
BOOST_AUTO_TEST_CASE(EclipseReadWriteWellStateData) {
std::vector<RestartKey> keys {{"PRESSURE" , UnitSystem::measure::pressure},
@@ -488,7 +506,6 @@ BOOST_AUTO_TEST_CASE(EclipseReadWriteWellStateData) {
BOOST_CHECK_THROW( second_sim( restart_setup, action_state, st, {{"SOIL", UnitSystem::measure::pressure, true}}) , std::runtime_error );
}
BOOST_AUTO_TEST_CASE(ECL_FORMATTED) {
namespace OS = ::Opm::EclIO::OutputStream;
@@ -583,24 +600,25 @@ BOOST_AUTO_TEST_CASE(ECL_FORMATTED) {
}
}
namespace {
void compare_equal( const RestartValue& fst,
const RestartValue& snd ,
const std::vector<RestartKey>& keys) {
void compare_equal(const RestartValue& fst,
const RestartValue& snd,
const std::vector<RestartKey>& keys)
{
for (const auto& value : keys) {
const std::string& key = value.key;
auto first = fst.solution.data( key ).begin();
auto second = snd.solution.data( key ).begin();
for( ; first != fst.solution.data( key ).end(); ++first, ++second )
BOOST_CHECK_EQUAL( *first, *second);
for (; first != fst.solution.data(key).end(); ++first, ++second) {
BOOST_CHECK_EQUAL(*first, *second);
}
}
}
} // Anonymous namespace
BOOST_AUTO_TEST_CASE(EclipseReadWriteWellStateData_double) {
/*
Observe that the purpose of this test is to verify that with
@@ -628,7 +646,6 @@ BOOST_AUTO_TEST_CASE(EclipseReadWriteWellStateData_double) {
compare_equal( state1 , state2 , solution_keys);
}
BOOST_AUTO_TEST_CASE(WriteWrongSOlutionSize) {
namespace OS = ::Opm::EclIO::OutputStream;
@@ -668,7 +685,6 @@ BOOST_AUTO_TEST_CASE(WriteWrongSOlutionSize) {
}
}
BOOST_AUTO_TEST_CASE(ExtraData_KEYS) {
Setup setup("BASE_SIM.DATA");
auto num_cells = setup.grid.getNumActive( );
@@ -776,7 +792,6 @@ BOOST_AUTO_TEST_CASE(ExtraData_content) {
}
}
BOOST_AUTO_TEST_CASE(STORE_THPRES) {
namespace OS = ::Opm::EclIO::OutputStream;
@@ -886,8 +901,6 @@ BOOST_AUTO_TEST_CASE(STORE_THPRES) {
}
}
BOOST_AUTO_TEST_CASE(Restore_Cumulatives)
{
WorkArea wa{"test_Restart"};
@@ -1046,7 +1059,10 @@ BOOST_AUTO_TEST_CASE(Restore_Cumulatives)
BOOST_CHECK_CLOSE(rstSumState.get("FGITH"), 90123.45, 1.0e-10);
}
void init_st(SummaryState& st) {
namespace {
void init_st(SummaryState& st)
{
st.update_well_var("PROD1", "WOPR", 100);
st.update_well_var("PROD1", "WLPR", 100);
st.update_well_var("PROD2", "WOPR", 100);
@@ -1062,6 +1078,8 @@ void init_st(SummaryState& st) {
st.update("FLPR", 100);
}
} // Anonymous namespace
BOOST_AUTO_TEST_CASE(UDQ_RESTART) {
std::vector<RestartKey> keys {{"PRESSURE" , UnitSystem::measure::pressure},
{"SWAT" , UnitSystem::measure::identity},
@@ -1134,7 +1152,8 @@ BOOST_AUTO_TEST_CASE(UDQ_RESTART) {
BOOST_CHECK_EQUAL(st1.get(kw), st2.get(kw));
}
}
}
} // namespace Opm
namespace {