/*
Copyright 2016 SINTEF ICT, Applied Mathematics.
Copyright 2016 - 2017 Statoil ASA.
Copyright 2017 Dr. Blatt - HPC-Simulation-Software & Services
Copyright 2016 - 2018 IRIS AS
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace {
Opm::data::GuideRateValue::Item
guideRateRestartItem(const Opm::GuideRateModel::Target target)
{
using Item = Opm::data::GuideRateValue::Item;
using Target = Opm::GuideRateModel::Target;
static const auto items = std::unordered_map {
{ Target::OIL, Item::Oil },
{ Target::GAS, Item::Gas },
{ Target::WAT, Item::Water },
{ Target::RES, Item::ResV },
};
auto i = items.find(target);
return (i == items.end()) ? Item::NumItems : i->second;
}
Opm::GuideRate::GuideRateValue
makeGuideRateValue(const Opm::data::GuideRateValue& restart,
const Opm::GuideRateModel::Target target)
{
const auto item = guideRateRestartItem(target);
if (! restart.has(item)) {
return {};
}
return { 0.0, restart.get(item), target };
}
struct RetrieveWellGuideRate
{
RetrieveWellGuideRate() = default;
explicit RetrieveWellGuideRate(const Opm::GuideRate& guideRate,
const std::string& wgname);
explicit RetrieveWellGuideRate(const Opm::GuideRate& guideRate,
const Opm::Group& group);
bool prod { false };
bool inj_water { false };
bool inj_gas { false };
};
RetrieveWellGuideRate
operator||(RetrieveWellGuideRate lhs, const RetrieveWellGuideRate& rhs)
{
lhs.prod = lhs.prod || rhs.prod;
lhs.inj_water = lhs.inj_water || rhs.inj_water;
lhs.inj_gas = lhs.inj_gas || rhs.inj_gas;
return lhs;
}
RetrieveWellGuideRate::RetrieveWellGuideRate(const Opm::GuideRate& guideRate,
const std::string& wgname)
: prod { guideRate.has(wgname) }
, inj_water { guideRate.has(wgname, Opm::Phase::WATER) }
, inj_gas { guideRate.has(wgname, Opm::Phase::GAS) }
{}
RetrieveWellGuideRate::RetrieveWellGuideRate(const Opm::GuideRate& guideRate,
const Opm::Group& group)
: RetrieveWellGuideRate{ guideRate, group.name() }
{
if (group.isProductionGroup()) {
this->prod = true;
}
if (group.isInjectionGroup()) {
this->inj_water = this->inj_water || group.hasInjectionControl(Opm::Phase::WATER);
this->inj_gas = this->inj_gas || group.hasInjectionControl(Opm::Phase::GAS);
}
}
class GroupTreeWalker
{
public:
using GroupOp = std::function;
using WellOp = std::function;
explicit GroupTreeWalker(const Opm::Schedule& sched,
const int reportStepIdx)
: sched_ (sched)
, reportStepIdx_(reportStepIdx)
{}
GroupTreeWalker& groupOp(GroupOp visit)
{
this->visitGroup_ = std::move(visit);
return *this;
}
GroupTreeWalker& wellOp(WellOp visit)
{
this->visitWell_ = std::move(visit);
return *this;
}
void clear()
{
this->visitGroup_ = GroupOp{};
this->visitWell_ = WellOp{};
}
void traversePreOrder();
void traversePostOrder();
private:
using NodeOp = void (GroupTreeWalker::*)(std::string_view) const;
std::reference_wrapper sched_;
int reportStepIdx_;
GroupOp visitGroup_{};
WellOp visitWell_{};
std::stack> dfsGroupStack_{};
std::unordered_set dfsGroupDiscovered_{};
NodeOp postDiscover_{nullptr};
NodeOp preFinish_{nullptr};
void traverse();
void startWalk();
void discover(std::string_view group);
void finish(std::string_view group);
bool isSeen(std::string_view group) const;
std::size_t insertIndex(std::string_view group) const;
void visitGroup(std::string_view group) const;
void visitWell(std::string_view well) const;
const Opm::Group& getGroup(std::string_view group) const;
const Opm::Well& getWell(std::string_view well) const;
};
void GroupTreeWalker::traversePreOrder()
{
this->preFinish_ = nullptr;
this->postDiscover_ = &GroupTreeWalker::visitGroup;
this->traverse();
}
void GroupTreeWalker::traversePostOrder()
{
this->preFinish_ = &GroupTreeWalker::visitGroup;
this->postDiscover_ = nullptr;
this->traverse();
}
void GroupTreeWalker::traverse()
{
this->startWalk();
while (! this->dfsGroupStack_.empty()) {
const auto gname = this->dfsGroupStack_.top();
if (this->isSeen(gname)) {
if (this->preFinish_ != nullptr) {
(this->*preFinish_)(gname);
}
this->finish(gname);
continue;
}
this->discover(gname);
if (this->postDiscover_ != nullptr) {
(this->*postDiscover_)(gname);
}
const auto& group = this->getGroup(gname);
if (! group.wellgroup()) { // Node group. Register child groups.
for (const auto& child : group.groups()) {
if (! this->isSeen(child)) {
this->dfsGroupStack_.push(child);
}
}
}
else { // Group is a well group--visit its wells.
for (const auto& well : group.wells()) {
this->visitWell(well);
}
}
}
}
void GroupTreeWalker::startWalk()
{
this->dfsGroupDiscovered_.clear();
while (! this->dfsGroupStack_.empty()) {
this->dfsGroupStack_.pop();
}
this->dfsGroupStack_.push("FIELD");
}
void GroupTreeWalker::discover(std::string_view group)
{
this->dfsGroupDiscovered_.insert(this->insertIndex(group));
}
void GroupTreeWalker::finish(std::string_view group)
{
if (this->dfsGroupStack_.top() != group) {
throw std::invalid_argument {
fmt::format("Internal Error: Expected group '{}', but got '{}'",
group, this->dfsGroupStack_.top())
};
}
this->dfsGroupStack_.pop();
}
bool GroupTreeWalker::isSeen(std::string_view group) const
{
return this->dfsGroupDiscovered_.find(this->insertIndex(group))
!= this->dfsGroupDiscovered_.end();
}
std::size_t GroupTreeWalker::insertIndex(std::string_view group) const
{
return this->getGroup(group).insert_index();
}
void GroupTreeWalker::visitGroup(std::string_view group) const
{
if (! this->visitGroup_) {
return;
}
this->visitGroup_(this->getGroup(group));
}
void GroupTreeWalker::visitWell(std::string_view well) const
{
if (! this->visitWell_) {
return;
}
this->visitWell_(this->getWell(well));
}
const Opm::Group& GroupTreeWalker::getGroup(std::string_view group) const
{
return this->sched_.get().getGroup({group.data(), group.size()}, this->reportStepIdx_);
}
const Opm::Well& GroupTreeWalker::getWell(std::string_view well) const
{
return this->sched_.get().getWell({well.data(), well.size()}, this->reportStepIdx_);
}
} // Anonymous
namespace Opm {
BlackoilWellModelGeneric::
BlackoilWellModelGeneric(Schedule& schedule,
const SummaryState& summaryState,
const EclipseState& eclState,
const PhaseUsage& phase_usage,
const Parallel::Communication& comm)
: schedule_(schedule)
, summaryState_(summaryState)
, eclState_(eclState)
, comm_(comm)
, phase_usage_(phase_usage)
, guideRate_(schedule)
, active_wgstate_(phase_usage)
, last_valid_wgstate_(phase_usage)
, nupcol_wgstate_(phase_usage)
{
const auto numProcs = comm_.size();
this->not_on_process_ = [this, numProcs](const Well& well) {
if (numProcs == decltype(numProcs){1})
return false;
// Recall: false indicates NOT active!
const auto value = std::make_pair(well.name(), true);
auto candidate = std::lower_bound(this->parallel_well_info_.begin(),
this->parallel_well_info_.end(),
value);
return (candidate == this->parallel_well_info_.end())
|| (*candidate != value);
};
}
int
BlackoilWellModelGeneric::
numLocalWells() const
{
return wells_ecl_.size();
}
int
BlackoilWellModelGeneric::
numPhases() const
{
return phase_usage_.num_phases;
}
bool
BlackoilWellModelGeneric::
hasWell(const std::string& wname)
{
return std::any_of(this->wells_ecl_.begin(), this->wells_ecl_.end(),
[&wname](const Well& well)
{
return well.name() == wname;
});
}
bool
BlackoilWellModelGeneric::
wellsActive() const
{
return wells_active_;
}
bool
BlackoilWellModelGeneric::
localWellsActive() const
{
return numLocalWells() > 0;
}
bool
BlackoilWellModelGeneric::
anyMSWellOpenLocal() const
{
for (const auto& well : wells_ecl_) {
if (well.isMultiSegment()) {
return true;
}
}
return false;
}
const Well&
BlackoilWellModelGeneric::
getWellEcl(const std::string& well_name) const
{
// finding the iterator of the well in wells_ecl
auto well_ecl = std::find_if(wells_ecl_.begin(),
wells_ecl_.end(),
[&well_name](const Well& elem)->bool {
return elem.name() == well_name;
});
assert(well_ecl != wells_ecl_.end());
return *well_ecl;
}
void
BlackoilWellModelGeneric::
loadRestartConnectionData(const std::vector& phs,
const data::Well& rst_well,
const std::vector& old_perf_data,
SingleWellState& ws)
{
auto& perf_data = ws.perf_data;
auto perf_pressure = perf_data.pressure.begin();
auto perf_rates = perf_data.rates.begin();
auto perf_phase_rates = perf_data.phase_rates.begin();
for (const auto& pd : old_perf_data) {
const auto& rst_connection = rst_well.connections[pd.ecl_index];
*perf_pressure = rst_connection.pressure; ++perf_pressure;
*perf_rates = rst_connection.reservoir_rate; ++perf_rates;
for (const auto& phase : phs) {
*perf_phase_rates = rst_connection.rates.get(phase);
++perf_phase_rates;
}
}
}
void
BlackoilWellModelGeneric::
loadRestartSegmentData(const std::string& well_name,
const std::vector& phs,
const data::Well& rst_well,
SingleWellState& ws)
{
const auto& segment_set = this->getWellEcl(well_name).getSegments();
const auto& rst_segments = rst_well.segments;
// \Note: Eventually we need to handle the situations that some segments are shut
assert(0u + segment_set.size() == rst_segments.size());
const auto np = phs.size();
const auto pres_idx = data::SegmentPressures::Value::Pressure;
auto& segments = ws.segments;
auto& segment_pressure = segments.pressure;
auto& segment_rates = segments.rates;
for (const auto& [segNum, rst_segment] : rst_segments) {
const int segment_index = segment_set.segmentNumberToIndex(segNum);
// Recovering segment rates and pressure from the restart values
segment_pressure[segment_index] = rst_segment.pressures[pres_idx];
const auto& rst_segment_rates = rst_segment.rates;
for (auto p = 0*np; p < np; ++p) {
segment_rates[segment_index*np + p] = rst_segment_rates.get(phs[p]);
}
}
}
void
BlackoilWellModelGeneric::
loadRestartWellData(const std::string& well_name,
const bool handle_ms_well,
const std::vector& phs,
const data::Well& rst_well,
const std::vector& old_perf_data,
SingleWellState& ws)
{
const auto np = phs.size();
ws.bhp = rst_well.bhp;
ws.thp = rst_well.thp;
ws.temperature = rst_well.temperature;
if (rst_well.current_control.isProducer) {
ws.production_cmode = rst_well.current_control.prod;
}
else {
ws.injection_cmode = rst_well.current_control.inj;
}
for (auto i = 0*np; i < np; ++i) {
assert( rst_well.rates.has( phs[ i ] ) );
ws.surface_rates[i] = rst_well.rates.get(phs[i]);
}
this->loadRestartConnectionData(phs, rst_well, old_perf_data, ws);
if (handle_ms_well && !rst_well.segments.empty()) {
this->loadRestartSegmentData(well_name, phs, rst_well, ws);
}
}
void
BlackoilWellModelGeneric::
loadRestartGroupData(const std::string& group,
const data::GroupData& value)
{
using GPMode = Group::ProductionCMode;
using GIMode = Group::InjectionCMode;
const auto cpc = value.currentControl.currentProdConstraint;
const auto cgi = value.currentControl.currentGasInjectionConstraint;
const auto cwi = value.currentControl.currentWaterInjectionConstraint;
auto& grpState = this->groupState();
if (cpc != GPMode::NONE) {
grpState.production_control(group, cpc);
}
if (cgi != GIMode::NONE) {
grpState.injection_control(group, Phase::GAS, cgi);
}
if (cwi != GIMode::NONE) {
grpState.injection_control(group, Phase::WATER, cwi);
}
}
void
BlackoilWellModelGeneric::
loadRestartGuideRates(const int report_step,
const GuideRateModel::Target target,
const data::Wells& rst_wells)
{
for (const auto& [well_name, rst_well] : rst_wells) {
if (! this->hasWell(well_name) || this->getWellEcl(well_name).isInjector()) {
continue;
}
this->guideRate_.init_grvalue_SI(report_step, well_name,
makeGuideRateValue(rst_well.guide_rates, target));
}
}
void
BlackoilWellModelGeneric::
loadRestartGuideRates(const int report_step,
const GuideRateConfig& config,
const std::map& rst_groups)
{
const auto target = config.model().target();
for (const auto& [group_name, rst_group] : rst_groups) {
if (! config.has_production_group(group_name)) {
continue;
}
const auto& group = config.production_group(group_name);
if ((group.guide_rate > 0.0) || (group.target != Group::GuideRateProdTarget::FORM)) {
continue;
}
this->guideRate_.init_grvalue_SI(report_step, group_name,
makeGuideRateValue(rst_group.guideRates.production, target));
}
}
void
BlackoilWellModelGeneric::
loadRestartData(const data::Wells& rst_wells,
const data::GroupAndNetworkValues& grpNwrkValues,
const PhaseUsage& phases,
const bool handle_ms_well,
WellState& well_state)
{
using rt = data::Rates::opt;
const auto np = phases.num_phases;
std::vector