Merge pull request #4694 from bska/wbp-compute-values

Hook New WBPn Calculation Up to Well Model
This commit is contained in:
Bård Skaflestad 2023-07-11 11:25:28 +02:00 committed by GitHub
commit aa988a88a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 457 additions and 35 deletions

View File

@ -210,7 +210,7 @@ public:
simulator_.vanguard().setupTime();
const auto localWellData = simulator_.problem().wellModel().wellData();
const auto localWBP = data::WellBlockAveragePressures{};
const auto localWBP = simulator_.problem().wellModel().wellBlockAveragePressures();
const auto localGroupAndNetworkData = simulator_.problem().wellModel()
.groupAndNetworkData(reportStepNum);

View File

@ -38,35 +38,39 @@
#include <unordered_map>
#include <vector>
#include <opm/input/eclipse/Schedule/Group/Group.hpp>
#include <opm/input/eclipse/Schedule/Group/GuideRate.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTestState.hpp>
#include <opm/input/eclipse/Schedule/Group/GuideRate.hpp>
#include <opm/input/eclipse/Schedule/Group/Group.hpp>
#include <opm/simulators/timestepping/SimulatorReport.hpp>
#include <opm/simulators/flow/countGlobalCells.hpp>
#include <opm/simulators/flow/SubDomain.hpp>
#include <opm/simulators/wells/BlackoilWellModelGeneric.hpp>
#include <opm/simulators/wells/BlackoilWellModelGuideRates.hpp>
#include <opm/simulators/wells/GasLiftGroupInfo.hpp>
#include <opm/simulators/wells/GasLiftSingleWell.hpp>
#include <opm/simulators/wells/GasLiftWellState.hpp>
#include <opm/simulators/wells/GasLiftSingleWellGeneric.hpp>
#include <opm/simulators/wells/GasLiftStage2.hpp>
#include <opm/simulators/wells/GasLiftGroupInfo.hpp>
#include <opm/simulators/wells/GasLiftWellState.hpp>
#include <opm/simulators/wells/MultisegmentWell.hpp>
#include <opm/simulators/wells/ParallelWBPCalculation.hpp>
#include <opm/simulators/wells/ParallelWellInfo.hpp>
#include <opm/simulators/wells/PerforationData.hpp>
#include <opm/simulators/wells/VFPInjProperties.hpp>
#include <opm/simulators/wells/VFPProdProperties.hpp>
#include <opm/simulators/wells/WellState.hpp>
#include <opm/simulators/wells/WGState.hpp>
#include <opm/simulators/wells/RateConverter.hpp>
#include <opm/simulators/wells/RegionAverageCalculator.hpp>
#include <opm/simulators/wells/WellInterface.hpp>
#include <opm/simulators/wells/StandardWell.hpp>
#include <opm/simulators/wells/MultisegmentWell.hpp>
#include <opm/simulators/wells/VFPInjProperties.hpp>
#include <opm/simulators/wells/VFPProdProperties.hpp>
#include <opm/simulators/wells/WGState.hpp>
#include <opm/simulators/wells/WellGroupHelpers.hpp>
#include <opm/simulators/wells/WellInterface.hpp>
#include <opm/simulators/wells/WellProdIndexCalculator.hpp>
#include <opm/simulators/wells/ParallelWellInfo.hpp>
#include <opm/simulators/wells/WellState.hpp>
#include <opm/simulators/timestepping/SimulatorReport.hpp>
#include <opm/simulators/timestepping/gatherConvergenceReport.hpp>
#include <dune/common/fmatrix.hh>
#include <dune/istl/bcrsmatrix.hh>
#include <dune/istl/matrixmatrix.hh>
@ -266,6 +270,11 @@ namespace Opm {
return wsrpt;
}
data::WellBlockAveragePressures wellBlockAveragePressures() const
{
return this->computeWellBlockAveragePressures();
}
// subtract Binv(D)rw from r;
void apply( BVector& r) const;
@ -387,6 +396,13 @@ namespace Opm {
std::unique_ptr<RateConverterType> rateConverter_{};
std::map<std::string, std::unique_ptr<AverageRegionalPressureType>> regionalAveragePressureCalculator_{};
struct WBPCalcID
{
std::optional<typename std::vector<WellInterfacePtr>::size_type> openWellIdx_{};
std::size_t wbpCalcIdx_{};
};
std::vector<WBPCalcID> wbpCalcMap_{};
SimulatorReportSingle last_report_{};
@ -447,6 +463,16 @@ namespace Opm {
// setting the well_solutions_ based on well_state.
void updatePrimaryVariables(DeferredLogger& deferred_logger);
void initializeWBPCalculationService();
data::WellBlockAveragePressures
computeWellBlockAveragePressures() const;
ParallelWBPCalculation::EvaluatorFactory
makeWellSourceEvaluatorFactory(const std::vector<Well>::size_type wellIdx) const;
void registerOpenWellsForWBPCalculation();
void updateAverageFormationFactor();
void computePotentials(const std::size_t widx,

View File

@ -49,6 +49,7 @@
#include <opm/simulators/wells/BlackoilWellModelGuideRates.hpp>
#include <opm/simulators/wells/BlackoilWellModelRestart.hpp>
#include <opm/simulators/wells/GasLiftStage2.hpp>
#include <opm/simulators/wells/ParallelWBPCalculation.hpp>
#include <opm/simulators/wells/VFPProperties.hpp>
#include <opm/simulators/wells/WellFilterCake.hpp>
#include <opm/simulators/wells/WellGroupHelpers.hpp>
@ -81,6 +82,7 @@ BlackoilWellModelGeneric(Schedule& schedule,
, eclState_(eclState)
, comm_(comm)
, phase_usage_(phase_usage)
, wbpCalculationService_ { eclState.gridDims(), comm_ }
, guideRate_(schedule)
, active_wgstate_(phase_usage)
, last_valid_wgstate_(phase_usage)
@ -287,57 +289,89 @@ BlackoilWellModelGeneric::
initializeWellPerfData()
{
well_perf_data_.resize(wells_ecl_.size());
this->conn_idx_map_.clear();
this->conn_idx_map_.reserve(wells_ecl_.size());
int well_index = 0;
for (const auto& well : wells_ecl_) {
int connection_index = 0;
// INVALID_ECL_INDEX marks no above perf available
int connection_index_above = ParallelWellInfo::INVALID_ECL_INDEX;
well_perf_data_[well_index].clear();
well_perf_data_[well_index].reserve(well.getConnections().size());
CheckDistributedWellConnections checker(well, local_parallel_well_info_[well_index].get());
auto& connIdxMap = this->conn_idx_map_
.emplace_back(well.getConnections().size());
CheckDistributedWellConnections checker {
well, this->local_parallel_well_info_[well_index].get()
};
bool hasFirstConnection = false;
bool firstOpenConnection = true;
auto& parallelWellInfo = this->local_parallel_well_info_[well_index].get();
parallelWellInfo.beginReset();
for (const auto& connection : well.getConnections()) {
const int active_index = compressedIndexForInterior(connection.global_index());
if (connection.state() == Connection::State::OPEN) {
const auto active_index =
this->compressedIndexForInterior(connection.global_index());
const auto connIsOpen =
connection.state() == Connection::State::OPEN;
if (active_index >= 0) {
connIdxMap.addActiveConnection(connection_index, connIsOpen);
}
if ((connIsOpen && (active_index >= 0)) || !connIsOpen) {
checker.connectionFound(connection_index);
}
if (connIsOpen) {
if (active_index >= 0) {
if (firstOpenConnection)
{
if (firstOpenConnection) {
hasFirstConnection = true;
}
checker.connectionFound(connection_index);
PerforationData pd;
auto pd = PerforationData{};
pd.cell_index = active_index;
pd.connection_transmissibility_factor = connection.CF();
pd.satnum_id = connection.satTableId();
pd.ecl_index = connection_index;
well_perf_data_[well_index].push_back(pd);
parallelWellInfo.pushBackEclIndex(connection_index_above,
connection_index);
}
firstOpenConnection = false;
// Next time this index is the one above as each open connection is
// is stored somehwere.
// Next time this index is the one above as each open
// connection is stored somewhere.
connection_index_above = connection_index;
} else {
checker.connectionFound(connection_index);
if (connection.state() != Connection::State::SHUT) {
OPM_THROW(std::runtime_error,
"Connection state: " +
Connection::State2String(connection.state()) +
" not handled");
}
}
// Note: we rely on the connections being filtered! I.e. there are only connections
// to active cells in the global grid.
else if (connection.state() != Connection::State::SHUT) {
OPM_THROW(std::runtime_error,
fmt::format("Connection state '{}' not handled",
Connection::State2String(connection.state())));
}
// Note: we rely on the connections being filtered! I.e., there
// are only connections to active cells in the global grid.
++connection_index;
}
parallelWellInfo.endReset();
checker.checkAllConnectionsFound();
parallelWellInfo.communicateFirstPerforation(hasFirstConnection);
++well_index;
}
}

View File

@ -25,20 +25,28 @@
#include <opm/output/data/GuideRateValue.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTestState.hpp>
#include <opm/input/eclipse/Schedule/Group/GuideRate.hpp>
#include <opm/input/eclipse/Schedule/Well/PAvg.hpp>
#include <opm/input/eclipse/Schedule/Well/PAvgCalculator.hpp>
#include <opm/input/eclipse/Schedule/Well/PAvgCalculatorCollection.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTestState.hpp>
#include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp>
#include <opm/simulators/wells/ParallelPAvgDynamicSourceData.hpp>
#include <opm/simulators/wells/ParallelWBPCalculation.hpp>
#include <opm/simulators/wells/PerforationData.hpp>
#include <opm/simulators/wells/WellFilterCake.hpp>
#include <opm/simulators/wells/WellProdIndexCalculator.hpp>
#include <opm/simulators/wells/WGState.hpp>
#include <cstddef>
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <vector>
@ -419,6 +427,94 @@ protected:
std::vector<Well> wells_ecl_;
std::vector<std::vector<PerforationData>> well_perf_data_;
/// Connection index mappings
class ConnectionIndexMap
{
public:
/// Constructor.
///
/// \param[in] numConns Total number of well connections, both open
/// and closed/shut. Typically \code WellConnections::size() \endcode.
explicit ConnectionIndexMap(const std::size_t numConns)
: local_(numConns, -1)
{
this->global_.reserve(numConns);
this->open_.reserve(numConns);
}
/// Enumerate/map new active connection.
///
/// \param[in] connIdx Global well connection index. Must be an
/// integer in the range 0..numConns-1.
///
/// \param[in] connIsOpen Whether or not the connection is
/// open/flowing.
void addActiveConnection(const int connIdx,
const bool connIsOpen)
{
this->local_[connIdx] =
static_cast<int>(this->global_.size());
this->global_.push_back(connIdx);
const auto open_conn_idx = connIsOpen
? this->num_open_conns_++
: -1;
this->open_.push_back(open_conn_idx);
}
/// Get local connection IDs/indices of every existing well
/// connection.
///
/// Negative value (-1) for connections that don't intersect the
/// current rank.
const std::vector<int>& local() const
{
return this->local_;
}
/// Get global connection ID of local (on-rank) connection.
///
/// \param[in] connIdx Local connection index.
///
/// \return Global connection ID of \p connIdx.
int global(const int connIdx) const
{
return this->global_[connIdx];
}
/// Get open connection ID of local (on-rank) connection.
///
/// \param[in] connIdx Local connection index.
///
/// \return Open connection ID of \p connIdx. Integer in the range
/// 0..#open connections - 1 if the connection is open or negative
/// value (-1) otherwise.
int open(const int connIdx) const
{
return this->open_[connIdx];
}
private:
/// Local connection IDs/indices of every existing well connection.
/// Negative value (-1) for connections that don't intersect the
/// current rank.
std::vector<int> local_{};
/// Global connection index of each on-rank reservoir connection.
/// Reverse/transpose mapping of \c local_.
std::vector<int> global_{};
/// Open connection index of each on-rank reservoir connection.
std::vector<int> open_{};
/// Number of open connections on this rank.
int num_open_conns_{0};
};
std::vector<ConnectionIndexMap> conn_idx_map_{};
std::function<bool(const Well&)> not_on_process_{};
// a vector of all the wells.
@ -430,6 +526,7 @@ protected:
std::vector<std::reference_wrapper<ParallelWellInfo>> local_parallel_well_info_;
std::vector<WellProdIndexCalculator> prod_index_calc_;
mutable ParallelWBPCalculation wbpCalculationService_;
std::vector<int> pvt_region_idx_;

View File

@ -27,8 +27,11 @@
#include <opm/input/eclipse/Units/UnitSystem.hpp>
#include <opm/input/eclipse/Schedule/Network/Balance.hpp>
#include <opm/input/eclipse/Schedule/Network/ExtNetwork.hpp>
#include <opm/input/eclipse/Schedule/Well/PAvgDynamicSourceData.hpp>
#include <opm/simulators/wells/BlackoilWellModelConstraints.hpp>
#include <opm/simulators/wells/ParallelPAvgDynamicSourceData.hpp>
#include <opm/simulators/wells/ParallelWBPCalculation.hpp>
#include <opm/simulators/wells/VFPProperties.hpp>
#include <opm/simulators/utils/MPIPacker.hpp>
#include <opm/simulators/linalg/bda/WellContributions.hpp>
@ -73,6 +76,48 @@ namespace Opm {
this->alternative_well_rate_init_ =
EWOMS_GET_PARAM(TypeTag, bool, AlternativeWellRateInit);
this->wbpCalculationService_
.localCellIndex([this](const std::size_t globalIndex)
{ return this->compressedIndexForInterior(globalIndex); })
.evalCellSource([this](const int localCell,
PAvgDynamicSourceData::SourceDataSpan<double> sourceTerms)
{
using Item = PAvgDynamicSourceData::SourceDataSpan<double>::Item;
const auto* intQuants = this->ebosSimulator_.model()
.cachedIntensiveQuantities(localCell, /*timeIndex = */0);
const auto& fs = intQuants->fluidState();
sourceTerms.set(Item::PoreVol, intQuants->porosity().value() *
this->ebosSimulator_.model().dofTotalVolume(localCell));
constexpr auto io = FluidSystem::oilPhaseIdx;
constexpr auto ig = FluidSystem::gasPhaseIdx;
constexpr auto iw = FluidSystem::waterPhaseIdx;
// Ideally, these would be 'constexpr'.
const auto haveOil = FluidSystem::phaseIsActive(io);
const auto haveGas = FluidSystem::phaseIsActive(ig);
const auto haveWat = FluidSystem::phaseIsActive(iw);
auto weightedPhaseDensity = [&fs](const auto ip)
{
return fs.saturation(ip).value() * fs.density(ip).value();
};
if (haveOil) { sourceTerms.set(Item::Pressure, fs.pressure(io).value()); }
else if (haveGas) { sourceTerms.set(Item::Pressure, fs.pressure(ig).value()); }
else { sourceTerms.set(Item::Pressure, fs.pressure(iw).value()); }
// Strictly speaking, assumes SUM(s[p]) == 1.
auto rho = 0.0;
if (haveOil) { rho += weightedPhaseDensity(io); }
if (haveGas) { rho += weightedPhaseDensity(ig); }
if (haveWat) { rho += weightedPhaseDensity(iw); }
sourceTerms.set(Item::MixtureDensity, rho);
});
}
template<typename TypeTag>
@ -209,6 +254,7 @@ namespace Opm {
// We must therefore provide it with updated cell pressures
this->initializeWellPerfData();
this->initializeWellState(timeStepIdx, summaryState);
this->initializeWBPCalculationService();
// handling MS well related
if (param_.use_multisegment_well_&& anyMSWellOpenLocal()) { // if we use MultisegmentWell model
@ -230,11 +276,16 @@ namespace Opm {
}
{
const auto& sched_state = this->schedule()[timeStepIdx];
// update VFP properties
const auto& sched_state = this->schedule()[timeStepIdx];
vfp_properties_ = std::make_unique<VFPProperties>(sched_state.vfpinj(),
sched_state.vfpprod(),
this->wellState());
}
{
const auto& sched_state = this->schedule()[timeStepIdx];
this->initializeWellProdIndCalculators();
if (sched_state.events().hasEvent(ScheduleEvents::Events::WELL_PRODUCTIVITY_INDEX)) {
this->runWellPIScaling(timeStepIdx, local_deferredLogger);
@ -799,6 +850,8 @@ namespace Opm {
}
}
}
this->registerOpenWellsForWBPCalculation();
}
@ -1801,6 +1854,154 @@ namespace Opm {
template <typename TypeTag>
void
BlackoilWellModel<TypeTag>::
initializeWBPCalculationService()
{
this->wbpCalcMap_.clear();
this->wbpCalcMap_.resize(this->wells_ecl_.size());
this->registerOpenWellsForWBPCalculation();
auto wellID = std::size_t{0};
for (const auto& well : this->wells_ecl_) {
this->wbpCalcMap_[wellID].wbpCalcIdx_ = this->wbpCalculationService_
.createCalculator(well,
this->local_parallel_well_info_[wellID],
this->conn_idx_map_[wellID].local(),
this->makeWellSourceEvaluatorFactory(wellID));
++wellID;
}
this->wbpCalculationService_.defineCommunication();
}
template <typename TypeTag>
data::WellBlockAveragePressures
BlackoilWellModel<TypeTag>::
computeWellBlockAveragePressures() const
{
auto wbpResult = data::WellBlockAveragePressures{};
using Calculated = PAvgCalculator::Result::WBPMode;
using Output = data::WellBlockAvgPress::Quantity;
this->wbpCalculationService_.collectDynamicValues();
const auto numWells = this->wells_ecl_.size();
for (auto wellID = 0*numWells; wellID < numWells; ++wellID) {
const auto calcIdx = this->wbpCalcMap_[wellID].wbpCalcIdx_;
const auto& well = this->wells_ecl_[wellID];
if (! well.hasRefDepth()) {
// Can't perform depth correction without at least a
// fall-back datum depth.
continue;
}
this->wbpCalculationService_
.inferBlockAveragePressures(calcIdx, well.pavg(),
this->gravity_,
well.getWPaveRefDepth());
const auto& result = this->wbpCalculationService_
.averagePressures(calcIdx);
auto& reported = wbpResult.values[well.name()];
reported[Output::WBP] = result.value(Calculated::WBP);
reported[Output::WBP4] = result.value(Calculated::WBP4);
reported[Output::WBP5] = result.value(Calculated::WBP5);
reported[Output::WBP9] = result.value(Calculated::WBP9);
}
return wbpResult;
}
template <typename TypeTag>
ParallelWBPCalculation::EvaluatorFactory
BlackoilWellModel<TypeTag>::
makeWellSourceEvaluatorFactory(const std::vector<Well>::size_type wellIdx) const
{
using Span = PAvgDynamicSourceData::SourceDataSpan<double>;
using Item = typename Span::Item;
return [wellIdx, this]() -> ParallelWBPCalculation::Evaluator
{
if (! this->wbpCalcMap_[wellIdx].openWellIdx_.has_value()) {
// Well is stopped/shut. Return evaluator for stopped wells.
return []([[maybe_unused]] const int connIdx, Span sourceTerm)
{
// Well/connection is stopped/shut. Set all items to
// zero.
sourceTerm
.set(Item::Pressure , 0.0)
.set(Item::PoreVol , 0.0)
.set(Item::MixtureDensity, 0.0);
};
}
// Well is open. Return an evaluator for open wells/open connections.
return [this, wellPtr = this->well_container_[*this->wbpCalcMap_[wellIdx].openWellIdx_].get()]
(const int connIdx, Span sourceTerm)
{
// Note: The only item which actually matters for the WBP
// calculation at the well reservoir connection level is the
// mixture density. Set other items to zero.
const auto& connIdxMap =
this->conn_idx_map_[wellPtr->indexOfWell()];
const auto rho = wellPtr->
connectionDensity(connIdxMap.global(connIdx),
connIdxMap.open(connIdx));
sourceTerm
.set(Item::Pressure , 0.0)
.set(Item::PoreVol , 0.0)
.set(Item::MixtureDensity, rho);
};
};
}
template <typename TypeTag>
void
BlackoilWellModel<TypeTag>::
registerOpenWellsForWBPCalculation()
{
assert (this->wbpCalcMap_.size() == this->wells_ecl_.size());
for (auto& wbpCalc : this->wbpCalcMap_) {
wbpCalc.openWellIdx_.reset();
}
auto openWellIdx = typename std::vector<WellInterfacePtr>::size_type{0};
for (const auto* openWell : this->well_container_generic_) {
this->wbpCalcMap_[openWell->indexOfWell()].openWellIdx_ = openWellIdx++;
}
}
template<typename TypeTag>
void
BlackoilWellModel<TypeTag>::

View File

@ -137,6 +137,9 @@ namespace Opm
WellState& well_state,
DeferredLogger& deferred_logger) const override;
double connectionDensity(const int globalConnIdx,
const int openConnIdx) const override;
void addWellContributions(SparseMatrixAdapter& jacobian) const override;
void addWellPressureEquations(PressureMatrix& mat,

View File

@ -21,8 +21,12 @@
#include <opm/common/Exceptions.hpp>
#include <opm/common/OpmLog/OpmLog.hpp>
#include <opm/input/eclipse/Schedule/MSW/Segment.hpp>
#include <opm/input/eclipse/Schedule/MSW/Valve.hpp>
#include <opm/input/eclipse/Schedule/MSW/WellSegments.hpp>
#include <opm/input/eclipse/Schedule/Well/Connection.hpp>
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
#include <opm/input/eclipse/Units/Units.hpp>
#include <opm/material/densead/EvaluationFormat.hpp>
@ -703,6 +707,28 @@ namespace Opm
template<typename TypeTag>
double
MultisegmentWell<TypeTag>::
connectionDensity(const int globalConnIdx,
[[maybe_unused]] const int openConnIdx) const
{
// Simple approximation: Mixture density at reservoir connection is
// mixture density at connection's segment.
const auto segNum = this->wellEcl()
.getConnections()[globalConnIdx].segment();
const auto segIdx = this->wellEcl()
.getSegments().segmentNumberToIndex(segNum);
return this->segments_.density(segIdx).value();
}
template<typename TypeTag>
void
MultisegmentWell<TypeTag>::

View File

@ -185,6 +185,9 @@ namespace Opm
WellState& well_state,
DeferredLogger& deferred_logger) const override;
virtual double connectionDensity(const int globalConnIdx,
const int openConnIdx) const override;
virtual void addWellContributions(SparseMatrixAdapter& mat) const override;
virtual void addWellPressureEquations(PressureMatrix& mat,

View File

@ -73,7 +73,19 @@ public:
//! \brief Returns density for first perforation.
Scalar rho() const
{
return this->perf_densities_.empty() ? 0.0 : perf_densities_[0];
return this->rho(0);
}
//! \brief Returns density for specific perforation/connection.
//!
//! \param[in] i Connection index
//!
//! \return Mixture density at connection \p i.
Scalar rho(const typename std::vector<Scalar>::size_type i) const
{
return (i < this->perf_densities_.size())
? this->perf_densities_[i]
: 0.0;
}
//! \brief Returns pressure drop for a given perforation.

View File

@ -1570,6 +1570,23 @@ namespace Opm
template<typename TypeTag>
double
StandardWell<TypeTag>::
connectionDensity([[maybe_unused]] const int globalConnIdx,
const int openConnIdx) const
{
return (openConnIdx < 0)
? 0.0
: this->connections_.rho(openConnIdx);
}
template<typename TypeTag>
void
StandardWell<TypeTag>::

View File

@ -254,6 +254,9 @@ public:
WellState& well_state,
DeferredLogger& deferred_logger) const = 0;
virtual double connectionDensity(const int globalConnIdx,
const int openConnIdx) const = 0;
/// \brief Wether the Jacobian will also have well contributions in it.
virtual bool jacobianContainsWellContributions() const
{