Add class GlobalWellInfo and use it from WellState

This commit is contained in:
Joakim Hove
2021-05-03 13:28:28 +02:00
parent 1329a73546
commit 8a78dc1c59
7 changed files with 239 additions and 82 deletions
+2
View File
@@ -43,6 +43,7 @@ list (APPEND MAIN_SOURCE_FILES
opm/simulators/utils/gatherDeferredLogger.cpp
opm/simulators/utils/ParallelFileMerger.cpp
opm/simulators/utils/ParallelRestart.cpp
opm/simulators/wells/GlobalWellInfo.cpp
opm/simulators/wells/GroupState.cpp
opm/simulators/wells/WGState.cpp
opm/simulators/wells/ALQState.cpp
@@ -261,6 +262,7 @@ list (APPEND PUBLIC_HEADER_FILES
opm/simulators/wells/TargetCalculator.hpp
opm/simulators/wells/WellConnectionAuxiliaryModule.hpp
opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp
opm/simulators/wells/GlobalWellInfo.hpp
opm/simulators/wells/GroupState.hpp
opm/simulators/wells/ALQState.hpp
opm/simulators/wells/WGState.hpp
+1 -3
View File
@@ -435,7 +435,6 @@ namespace Opm {
void initializeWellProdIndCalculators();
void initializeWellPerfData();
void initializeWellState(const int timeStepIdx,
const int globalNumWells,
const SummaryState& summaryState);
// create the well container
@@ -503,8 +502,7 @@ namespace Opm {
/// \brief Get the wells of our partition that are not shut.
/// \param timeStepIdx The index of the time step.
/// \param[out] globalNumWells the number of wells globally.
std::vector< Well > getLocalWells(const int timeStepIdx,
int& globalNumWells) const;
std::vector< Well > getLocalWells(const int timeStepId) const;
/// \brief Create the parallel well information
/// \param localWells The local wells from ECL schedule
@@ -229,10 +229,9 @@ namespace Opm {
template<typename TypeTag>
std::vector< Well >
BlackoilWellModel<TypeTag>::
getLocalWells(const int timeStepIdx, int& globalNumWells) const
getLocalWells(const int timeStepIdx) const
{
auto w = schedule().getWells(timeStepIdx);
globalNumWells = w.size();
w.erase(std::remove_if(w.begin(), w.end(), not_on_process_), w.end());
return w;
}
@@ -266,15 +265,14 @@ namespace Opm {
const Grid& grid = ebosSimulator_.vanguard().grid();
const auto& summaryState = ebosSimulator_.vanguard().summaryState();
int globalNumWells = 0;
// Make wells_ecl_ contain only this partition's wells.
wells_ecl_ = getLocalWells(timeStepIdx, globalNumWells);
wells_ecl_ = getLocalWells(timeStepIdx);
local_parallel_well_info_ = createLocalParallelWellInfo(wells_ecl_);
// The well state initialize bhp with the cell pressure in the top cell.
// We must therefore provide it with updated cell pressures
this->initializeWellPerfData();
this->initializeWellState(timeStepIdx, globalNumWells, summaryState);
this->initializeWellState(timeStepIdx, summaryState);
// Wells are active if they are active wells on at least
// one process.
@@ -632,9 +630,8 @@ namespace Opm {
// wells that have information written to the restart file.
const int report_step = std::max(eclState().getInitConfig().getRestartStep() - 1, 0);
const auto& summaryState = ebosSimulator_.vanguard().summaryState();
int globalNumWells = 0;
// wells_ecl_ should only contain wells on this processor.
wells_ecl_ = getLocalWells(report_step, globalNumWells);
wells_ecl_ = getLocalWells(report_step);
local_parallel_well_info_ = createLocalParallelWellInfo(wells_ecl_);
this->initializeWellProdIndCalculators();
@@ -645,7 +642,7 @@ namespace Opm {
const auto phaseUsage = phaseUsageFromDeck(eclState());
const size_t numCells = Opm::UgGridHelpers::numCells(grid());
const bool handle_ms_well = (param_.use_multisegment_well_ && anyMSWellOpenLocal());
this->wellState().resize(wells_ecl_, local_parallel_well_info_, schedule(), handle_ms_well, numCells, well_perf_data_, summaryState, globalNumWells); // Resize for restart step
this->wellState().resize(wells_ecl_, local_parallel_well_info_, schedule(), handle_ms_well, numCells, well_perf_data_, summaryState); // Resize for restart step
loadRestartData(restartValues.wells, restartValues.grp_nwrk, phaseUsage, handle_ms_well, this->wellState());
}
@@ -741,7 +738,6 @@ namespace Opm {
void
BlackoilWellModel<TypeTag>::
initializeWellState(const int timeStepIdx,
const int globalNumWells,
const SummaryState& summaryState)
{
std::vector<double> cellPressures(this->local_num_cells_, 0.0);
@@ -774,7 +770,7 @@ namespace Opm {
this->wellState().init(cellPressures, schedule(), wells_ecl_, local_parallel_well_info_, timeStepIdx,
&this->prevWellState(), well_perf_data_,
summaryState, globalNumWells);
summaryState);
}
@@ -1487,7 +1483,7 @@ namespace Opm {
// This builds some necessary lookup structures, so it must be called
// before we copy to well_state_nupcol_.
const auto& comm = ebosSimulator_.vanguard().grid().comm();
this->wellState().updateGlobalIsGrup(schedule(), reportStepIdx, comm);
this->wellState().updateGlobalIsGrup(comm);
if (iterationIdx < nupcol) {
this->updateNupcolWGState();
+90
View File
@@ -0,0 +1,90 @@
/*
Copyright 2021 Equinor
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdexcept>
#include <opm/simulators/wells/GlobalWellInfo.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
namespace Opm {
GlobalWellInfo::GlobalWellInfo(const Schedule& sched, std::size_t report_step, const std::vector<Well>& local_wells)
{
auto num_wells = sched.numWells(report_step);
this->m_in_injecting_group.resize(num_wells);
this->m_in_producing_group.resize(num_wells);
for (const auto& wname : sched.wellNames(report_step)) {
const auto& well = sched.getWell(wname, report_step);
auto global_well_index = well.seqIndex();
this->name_map.emplace( well.name(), global_well_index );
}
for (const auto& well : local_wells) {
this->local_map.push_back( well.seqIndex() );
this->is_injector.push_back( well.isInjector() );
}
}
bool GlobalWellInfo::in_injecting_group(const std::string& wname) const {
auto global_well_index = this->name_map.at(wname);
return this->m_in_injecting_group[global_well_index];
}
bool GlobalWellInfo::in_producing_group(const std::string& wname) const {
auto global_well_index = this->name_map.at(wname);
return this->m_in_producing_group[global_well_index];
}
void GlobalWellInfo::update_group(const std::vector<Well::Status>& well_status, const std::vector<Well::InjectorCMode>& injection_cmode, const std::vector<Well::ProducerCMode>& production_cmode) {
if (well_status.size() != this->local_map.size())
throw std::logic_error("Size mismatch");
this->m_in_injecting_group.assign(this->name_map.size(), 0);
this->m_in_producing_group.assign(this->name_map.size(), 0);
for (std::size_t well_index = 0; well_index < well_status.size(); well_index++) {
if (well_status[well_index] == Well::Status::OPEN) {
if (this->is_injector[well_index]) {
if (injection_cmode[well_index] == Well::InjectorCMode::GRUP)
this->m_in_injecting_group[this->local_map[well_index]] = 1;
} else {
if (production_cmode[well_index] == Well::ProducerCMode::GRUP)
this->m_in_producing_group[this->local_map[well_index]] = 1;
}
}
}
}
std::size_t GlobalWellInfo::well_index(const std::string& wname) const {
return this->name_map.at(wname);
}
const std::string& GlobalWellInfo::well_name(std::size_t well_index) const {
for (const auto& [name, index] : this->name_map) {
if (index == well_index)
return name;
}
throw std::logic_error("No well with index: " + std::to_string(well_index));
}
}
+89
View File
@@ -0,0 +1,89 @@
/*
Copyright 2021 Equinor
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_GLOBAL_WELL_INFO_HEADER_INCLUDED
#define OPM_GLOBAL_WELL_INFO_HEADER_INCLUDED
#include <cstddef>
#include <map>
#include <string>
#include <vector>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
namespace Opm {
class Schedule;
class Well;
/*
The context of the GlobalWellInfo class is the situation where the wells are
distributed among different processors. Most well processing only considers
the wells defined on the local process, but in some cases we need global
information about all the wells. This class maintains the following:
- Mapping between global well index and well name.
- Mapping between local well index and global index (only used internally in
class).
- Functionality to query well whether it is currently injecting or producing
under group control.
*/
class GlobalWellInfo {
public:
/*
Will sum the m_in_injecting_group and m_in_producing_group vectors across
all processes, so that all processes can query for an arbitrary well.
*/
template <typename Comm>
void communicate(const Comm& comm) {
auto size = this->m_in_injecting_group.size();
comm.sum( this->m_in_injecting_group.data(), size);
comm.sum( this->m_in_producing_group.data(), size);
};
GlobalWellInfo(const Schedule& sched, std::size_t report_step, const std::vector<Well>& local_wells);
bool in_producing_group(const std::string& wname) const;
bool in_injecting_group(const std::string& wname) const;
void update_group(const std::vector<Well::Status>& well_status, const std::vector<Well::InjectorCMode>& injection_cmode, const std::vector<Well::ProducerCMode>& production_cmode);
std::size_t well_index(const std::string& wname) const;
const std::string& well_name(std::size_t well_index) const;
private:
std::vector<std::size_t> local_map; // local_index -> global_index
std::vector<bool> is_injector; // local_index -> bool
std::map<std::string, std::size_t> name_map; // string -> global_index
std::vector<int> m_in_injecting_group; // global_index -> int/bool
std::vector<int> m_in_producing_group; // global_index -> int/bool
};
}
#endif
@@ -23,6 +23,7 @@
#include <opm/simulators/wells/WellState.hpp>
#include <opm/simulators/wells/ALQState.hpp>
#include <opm/simulators/wells/GlobalWellInfo.hpp>
#include <opm/core/props/BlackoilPhases.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
@@ -37,6 +38,7 @@
#include <iostream>
#include <map>
#include <numeric>
#include <optional>
#include <string>
#include <utility>
#include <vector>
@@ -85,19 +87,15 @@ namespace Opm
const int report_step,
const WellStateFullyImplicitBlackoil* prevState,
const std::vector<std::vector<PerforationData>>& well_perf_data,
const SummaryState& summary_state,
const int globalNumberOfWells)
const SummaryState& summary_state)
{
// call init on base class
BaseType :: init(cellPressures, wells_ecl, parallel_well_info, well_perf_data, summary_state);
this->global_well_info = std::make_optional<GlobalWellInfo>( schedule, report_step, wells_ecl );
for (const auto& winfo: parallel_well_info)
{
well_rates.insert({winfo->name(), std::make_pair(winfo->isOwner(), std::vector<double>())});
}
globalIsInjectionGrup_.assign(globalNumberOfWells,0);
globalIsProductionGrup_.assign(globalNumberOfWells,0);
wellNameToGlobalIdx_.clear();
const int nw = wells_ecl.size();
@@ -404,11 +402,10 @@ namespace Opm
const bool handle_ms_well,
const size_t numCells,
const std::vector<std::vector<PerforationData>>& well_perf_data,
const SummaryState& summary_state,
const int globalNumWells)
const SummaryState& summary_state)
{
const std::vector<double> tmp(numCells, 0.0); // <- UGLY HACK to pass the size
init(tmp, schedule, wells_ecl, parallel_well_info, 0, nullptr, well_perf_data, summary_state, globalNumWells);
init(tmp, schedule, wells_ecl, parallel_well_info, 0, nullptr, well_perf_data, summary_state);
if (handle_ms_well) {
initWellStateMSWell(wells_ecl, nullptr);
@@ -1077,52 +1074,18 @@ namespace Opm
}
template<class Comm>
void updateGlobalIsGrup(const Schedule& schedule, const int reportStepIdx, const Comm& comm)
void updateGlobalIsGrup(const Comm& comm)
{
std::fill(globalIsInjectionGrup_.begin(), globalIsInjectionGrup_.end(), 0);
std::fill(globalIsProductionGrup_.begin(), globalIsProductionGrup_.end(), 0);
const auto& end = wellMap().end();
for (const auto& well : schedule.getWells(reportStepIdx)) {
// Build global name->index map.
auto global_well_index = well.seqIndex();
wellNameToGlobalIdx_[well.name()] = global_well_index;
// For wells on this process...
const auto& it = wellMap().find( well.name());
if (it != end) {
// ... set the GRUP/not GRUP states.
const int well_index = it->second[0];
if (this->status_[well_index] == Well::Status::OPEN) {
if (well.isInjector()) {
globalIsInjectionGrup_[global_well_index] = (currentInjectionControls()[well_index] == Well::InjectorCMode::GRUP);
} else {
globalIsProductionGrup_[global_well_index] = (currentProductionControls()[well_index] == Well::ProducerCMode::GRUP);
}
}
}
}
comm.sum(globalIsInjectionGrup_.data(), globalIsInjectionGrup_.size());
comm.sum(globalIsProductionGrup_.data(), globalIsProductionGrup_.size());
this->global_well_info.value().update_group(this->status_, this->currentInjectionControls(), this->currentProductionControls());
this->global_well_info.value().communicate(comm);
}
bool isInjectionGrup(const std::string& name) const {
auto it = wellNameToGlobalIdx_.find(name);
if (it == wellNameToGlobalIdx_.end())
OPM_THROW(std::logic_error, "Could not find global injection group for well " << name);
return globalIsInjectionGrup_[it->second] != 0;
return this->global_well_info.value().in_injecting_group(name);
}
bool isProductionGrup(const std::string& name) const {
auto it = wellNameToGlobalIdx_.find(name);
if (it == wellNameToGlobalIdx_.end())
OPM_THROW(std::logic_error, "Could not find global production group for well " << name);
return globalIsProductionGrup_[it->second] != 0;
return this->global_well_info.value().in_producing_group(name);
}
double getALQ( const std::string& name) const
@@ -1169,23 +1132,11 @@ namespace Opm
}
int wellNameToGlobalIdx(const std::string &name) {
auto it = wellNameToGlobalIdx_.find(name);
if (it == wellNameToGlobalIdx_.end())
OPM_THROW(std::logic_error, "Could not find global well idx for well " << name);
return it->second;
return this->global_well_info.value().well_index(name);
}
std::string globalIdxToWellName(const int index) {
using Pair = std::pair<std::string, int>;
auto it = find_if(wellNameToGlobalIdx_.begin(), wellNameToGlobalIdx_.end(), [index](const Pair & p) {
return p.second == index;
});
if (it == wellNameToGlobalIdx_.end() )
{
OPM_THROW(std::logic_error, "Could not find well name for global idx " << index);
}
return it->first;
return this->global_well_info.value().well_name(index);
}
@@ -1200,10 +1151,10 @@ namespace Opm
std::vector<Opm::Well::InjectorCMode> current_injection_controls_;
std::vector<Well::ProducerCMode> current_production_controls_;
// size of global number of wells
std::vector<int> globalIsInjectionGrup_;
std::vector<int> globalIsProductionGrup_;
std::map<std::string, int> wellNameToGlobalIdx_;
// Use of std::optional<> here is a technical crutch, the
// WellStateFullyImplicitBlackoil class should be default constructible,
// whereas the GlobalWellInfo is not.
std::optional<GlobalWellInfo> global_well_info;
std::map<std::string, std::pair<bool, std::vector<double>>> well_rates;
ALQState alq_state;
+33 -2
View File
@@ -22,6 +22,7 @@
#define BOOST_TEST_MODULE WellStateFIBOTest
#include "MpiFixture.hpp"
#include <opm/simulators/wells/GlobalWellInfo.hpp>
#include <opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp>
#include <opm/parser/eclipse/Python/Python.hpp>
@@ -146,8 +147,7 @@ namespace {
state.init(cpress, setup.sched,
wells, ppinfos,
timeStep, nullptr, setup.well_perf_data, setup.st,
wells.size());
timeStep, nullptr, setup.well_perf_data, setup.st);
state.initWellStateMSWell(setup.sched.getWells(timeStep),
nullptr);
@@ -394,4 +394,35 @@ BOOST_AUTO_TEST_CASE(STOP_well)
BOOST_CHECK(p > 0);
}
// ---------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(GlobalWellInfo_TEST) {
const Setup setup{ "msw.data" };
std::vector<Opm::Well> local_wells = { setup.sched.getWell("PROD01", 1) };
Opm::GlobalWellInfo gwi(setup.sched, 1, local_wells);
BOOST_CHECK(!gwi.in_injecting_group("INJE01"));
BOOST_CHECK(!gwi.in_injecting_group("PROD01"));
BOOST_CHECK(!gwi.in_producing_group("INJE01"));
BOOST_CHECK(!gwi.in_producing_group("PROD01"));
BOOST_CHECK_EQUAL( gwi.well_name(0), "INJE01");
BOOST_CHECK_EQUAL( gwi.well_name(1), "PROD01");
BOOST_CHECK_EQUAL( gwi.well_index("PROD01"), 1);
BOOST_CHECK_THROW( gwi.update_group( {}, {}, {} ), std::exception);
gwi.update_group( {Opm::Well::Status::OPEN}, {Opm::Well::InjectorCMode::CMODE_UNDEFINED}, {Opm::Well::ProducerCMode::GRUP} );
BOOST_CHECK(!gwi.in_producing_group("INJE01"));
BOOST_CHECK(gwi.in_producing_group("PROD01"));
gwi.update_group( {Opm::Well::Status::OPEN}, {Opm::Well::InjectorCMode::CMODE_UNDEFINED}, {Opm::Well::ProducerCMode::NONE} );
BOOST_CHECK(!gwi.in_producing_group("INJE01"));
BOOST_CHECK(!gwi.in_producing_group("PROD01"));
}
BOOST_AUTO_TEST_SUITE_END()