Compute Segment Level UDQ Values

This commit adds support for calculating UDQs at the segment level,
i.e., UDQs named 'SU*'.  This necessitates an API change for the UDQ
context constructor and, transitively, every function that forms UDQ
context objects.  We pass a factory function that will create
segment matcher objects on demand, and provide a default
implementation of this factory function in the Schedule class.
This commit is contained in:
Bård Skaflestad 2023-01-19 13:20:52 +01:00
parent a8d3dff2a4
commit adab33ac60
17 changed files with 933 additions and 359 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,51 @@ 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(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 eval_assign(std::size_t report_step,
const Schedule& sched,
const WellMatcher& wm,
SegmentMatcherFactory segment_matcher_factory,
SummaryState& st,
UDQState& udq_state) const;
void eval(std::size_t report_step,
const Schedule& sched,
const WellMatcher& wm,
SegmentMatcherFactory segment_matcher_factory,
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 +123,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,17 +145,26 @@ 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;
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;
@ -137,8 +184,7 @@ namespace Opm {
OrderedMap<UDQIndex> input_index;
std::map<UDQVarType, std::size_t> type_count;
};
}
} // 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 segment_matcher_factory,
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 segment_matcher_factory;
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

@ -304,6 +304,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

@ -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,20 @@ 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>
{
const auto ix = (report_step < this->snapshots.size())
? report_step
: this->snapshots.size() - 1;
return std::make_unique<SegmentMatcher>(this->snapshots[ix]);
}
};
}
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,25 +36,54 @@
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;
}
return Opm::UDQVarType::NONE;
}
std::vector<Opm::UDQSet::EnumeratedWellItems>
make_segment_items(const Opm::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;
}
} // Anonymous namespace
namespace Opm {
@ -132,7 +163,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 +297,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 +330,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 +411,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 = make_segment_items(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 +512,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, make_segment_items(context.segments()), numeric_value);
case UDQVarType::SCALAR:
return UDQSet::scalar(dummy_name, numeric_value);

View File

@ -24,6 +24,7 @@
#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>
@ -483,6 +484,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 +510,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 segment_matcher_factory,
SummaryState& st,
UDQState& udq_state) const
{
UDQContext context(this->function_table(), wm, st, udq_state);
UDQContext context {
this->function_table(), wm, std::move(segment_matcher_factory), 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 segment_matcher_factory,
SummaryState& st,
UDQState& udq_state) const
{
UDQContext context(this->function_table(), wm, st, udq_state);
UDQContext context {
this->function_table(), wm, std::move(segment_matcher_factory), st, udq_state
};
this->eval_assign(report_step, sched, udq_state, context);
}

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 segment_matcher_factory_arg,
SummaryState& summary_state_arg,
UDQState& udq_state_arg)
: udqft (udqft_arg)
, well_matcher (wm)
, segment_matcher_factory(std::move(segment_matcher_factory_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->segment_matcher_factory();
}
}
}

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>
@ -217,6 +218,22 @@ bool dynamic_type_check(const Opm::UDQVarType lhs,
return true;
}
std::vector<Opm::UDQSet::EnumeratedWellItems>
make_segment_items(const Opm::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;
}
} // Anonymous namespace
namespace Opm {
@ -439,6 +456,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 +483,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, make_segment_items(context.segments()));
}
return UDQSet::segments(this->m_keyword, make_segment_items(context.segments()), *value);
}
} // namespace Opm

View File

@ -270,6 +270,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
{

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);
@ -1843,7 +1986,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 +2012,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 +2056,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 +2095,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 +2122,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 +2141,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 +2252,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 +2469,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 +2489,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 +2509,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 +2568,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 +2577,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 +2586,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 +2611,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 +2639,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 +2685,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 +2712,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 +2841,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);

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 {