Don't Copy Well Objects When Well Names are Sufficient

In particular, favour calling member functions on
ScheduleState::wells to retrieve references to individual well
objects given a name instead of using Schedule::getWells() or
Schedule::getWellsAtEnd().  While convenient, these functions
introduce needless copies of all well objects stored in the Schedule
object.  This commit instead, where possible, uses the somewhat less
convenient ScheduleState::well_order() accessor and then accesses
individual wells by name.

To this end, change BlackoilWellModelGeneric::not_on_process_ from a
function taking a well and returning a bool into a function taking a
well name and returning a bool.
This commit is contained in:
Bård Skaflestad committed 2025-06-19 09:27:57 +02:00
1 parent 9020fed6b1
commit fa45c41dd3
6 files changed
+92 -55

No files matched your search

@@ -41,6 +41,7 @@
#include <opm/input/eclipse/Schedule/RPTConfig.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Schedule/Well/NameOrder.hpp>
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
@@ -1182,7 +1183,10 @@ void GenericOutputBlackoilModule<FluidSystem>::
setupExtraBlockData(const std::size_t reportStepNum,
std::function<bool(int)> isCartIdxOnThisRank)
{
for (const auto& well : schedule_.getWells(reportStepNum - 1)) {
const auto& sched = this->schedule_[reportStepNum - 1];
for (const auto& wname : sched.well_order()) {
const auto& well = sched.wells.get(wname);
for (const auto& connection : well.getConnections()) {
if (isCartIdxOnThisRank(connection.global_index())) {
this->extraBlockData_.emplace(std::piecewise_construct,
+27 -19
View File
@@ -27,6 +27,7 @@
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Schedule/Well/NameOrder.hpp>
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
#include <opm/input/eclipse/Schedule/Well/WellEnums.hpp>
@@ -41,6 +42,7 @@
#include <algorithm>
#include <cmath>
#include <functional>
#include <iomanip>
#include <numeric>
#include <sstream>
@@ -202,8 +204,8 @@ cumulative(const std::size_t reportStepNum,
this->outputCumulativeReportRecord_(values, names, {});
}
for (const auto& wname : this->schedule_.wellNames(reportStepNum)) {
const auto& well = this->schedule_.getWell(wname, reportStepNum);
for (const auto& wname : this->schedule_[reportStepNum].well_order()) {
const auto& well = this->schedule_[reportStepNum].wells.get(wname);
std::vector<Scalar> values(WellCumDataType::numWCValues);
std::vector<std::string> names(WellCumDataType::numWCNames);
@@ -435,8 +437,8 @@ injection(const std::size_t reportStepNum,
this->outputInjectionReportRecord_(values, names, {});
}
for (const auto& wname : this->schedule_.wellNames(reportStepNum)) {
const auto& well = this->schedule_.getWell(wname, reportStepNum);
for (const auto& wname : this->schedule_[reportStepNum].well_order()) {
const auto& well = this->schedule_[reportStepNum].wells.get(wname);
// Ignore production wells for the injection report.
if (well.isProducer()) {
@@ -513,20 +515,26 @@ template<class Scalar>
void LogOutputHelper<Scalar>::
msw(const std::size_t reportStepNum) const
{
const auto& wells = this->schedule_.getWells(reportStepNum);
if (std::any_of(wells.begin(), wells.end(),
[](const auto& well) { return well.isMultiSegment(); }))
{
this->beginMSWReport_();
std::for_each(wells.begin(), wells.end(),
[this](const auto& well)
{
if (well.isMultiSegment()) {
this->outputMSWReportRecord_(well);
}
});
this->endMSWReport_();
const auto& sched = this->schedule_[reportStepNum];
auto msWells = std::vector<std::reference_wrapper<const Well>>{};
for (const auto& wname : sched.well_order()) {
if (const auto& well = sched.wells.get(wname); well.isMultiSegment()) {
msWells.push_back(std::cref(well));
}
}
if (msWells.empty()) {
return;
}
this->beginMSWReport_();
std::for_each(msWells.begin(), msWells.end(),
[this](const Well& well)
{ this->outputMSWReportRecord_(well); });
this->endMSWReport_();
}
@@ -567,8 +575,8 @@ production(const std::size_t reportStepNum,
this->outputProductionReportRecord_(values, names, {});
}
for (const auto& wname : this->schedule_.wellNames(reportStepNum)) {
const auto& well = this->schedule_.getWell(wname, reportStepNum);
for (const auto& wname : this->schedule_[reportStepNum].well_order()) {
const auto& well = this->schedule_[reportStepNum].wells.get(wname);
// Ignore injection wells for the production report.
if (well.isInjector()) {
+6 -3
View File
@@ -28,6 +28,7 @@
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/Schedule/RFTConfig.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/Well/NameOrder.hpp>
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
#include <opm/material/fluidsystems/BlackOilDefaultFluidSystemIndices.hpp>
@@ -85,7 +86,7 @@ addToWells(data::Wells& wellDatas,
gatherAndUpdateMap(gasConnectionSaturations_, comm);
}
for (const auto& wname : this->schedule_.wellNames(reportStepNum)) {
for (const auto& wname : this->schedule_[reportStepNum].well_order()) {
// Don't bother with wells not on this process.
if (!wellQuery_(wname)) {
continue;
@@ -144,13 +145,15 @@ allocate(const std::size_t reportStepNum)
return;
}
for (const auto& well: schedule_.getWells(reportStepNum)) {
for (const auto& wname : schedule_[reportStepNum].well_order()) {
// don't bother with wells not on this process
if (!wellQuery_(well.name())) {
if (!wellQuery_(wname)) {
continue;
}
const auto& well = schedule_[reportStepNum].wells.get(wname);
for (const auto& connection: well.getConnections()) {
const std::size_t index = connection.global_index();
@@ -44,6 +44,7 @@
#include <opm/input/eclipse/Schedule/Network/ExtNetwork.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/ScheduleTypes.hpp>
#include <opm/input/eclipse/Schedule/Well/NameOrder.hpp>
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTestConfig.hpp>
@@ -72,8 +73,9 @@
#include <algorithm>
#include <cassert>
#include <functional>
#include <stdexcept>
#include <iterator>
#include <sstream>
#include <stdexcept>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
@@ -108,18 +110,19 @@ BlackoilWellModelGeneric(Schedule& schedule,
{
const auto numProcs = comm_.size();
this->not_on_process_ = [this, numProcs](const Well& well) {
if (numProcs == decltype(numProcs){1})
this->not_on_process_ = [this, numProcs](const std::string& well) {
if (numProcs == decltype(numProcs){1}) {
return false;
}
// Recall: false indicates NOT active!
const auto value = std::make_pair(well.name(), true);
const auto value = std::make_pair(well, 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);
|| (*candidate != value);
};
const auto& node_pressures = eclState.getRestartNetworkPressures();
@@ -295,8 +298,21 @@ template<class Scalar>
std::vector<Well> BlackoilWellModelGeneric<Scalar>::
getLocalWells(const int timeStepIdx) const
{
auto w = schedule().getWells(timeStepIdx);
w.erase(std::remove_if(w.begin(), w.end(), not_on_process_), w.end());
auto w = std::vector<Well>{};
auto wnames = this->schedule().wellNames(timeStepIdx);
wnames.erase(std::remove_if(wnames.begin(), wnames.end(),
this->not_on_process_),
wnames.end());
w.reserve(wnames.size());
std::transform(wnames.begin(), wnames.end(),
std::back_inserter(w),
[&st = this->schedule()[timeStepIdx]]
(const std::string& wname)
{ return st.wells(wname); });
return w;
}
@@ -2002,50 +2018,55 @@ template<class Scalar>
std::vector<std::vector<int>> BlackoilWellModelGeneric<Scalar>::
getMaxWellConnections() const
{
std::vector<std::vector<int>> wells;
auto wellConnections = std::vector<std::vector<int>>{};
auto schedule_wells = schedule().getWellsatEnd();
schedule_wells.erase(std::remove_if(schedule_wells.begin(), schedule_wells.end(), not_on_process_), schedule_wells.end());
wells.reserve(schedule_wells.size());
auto schedule_wells = this->schedule().wellNames();
schedule_wells.erase(std::remove_if(schedule_wells.begin(),
schedule_wells.end(),
this->not_on_process_),
schedule_wells.end());
wellConnections.reserve(schedule_wells.size());
const auto possibleFutureConnections = schedule().getPossibleFutureConnections();
auto possibleFutureConnections = schedule().getPossibleFutureConnections();
#if HAVE_MPI
// Communicate Map to other processes, since it is only available on rank 0
Parallel::MpiSerializer ser(comm_);
ser.broadcast(Parallel::RootRank{0}, possibleFutureConnections);
#endif
// initialize the additional cell connections introduced by wells.
for (const auto& well : schedule_wells)
{
std::vector<int> compressed_well_perforations = this->getCellsForConnections(well);
const auto possibleFutureConnectionSetIt = possibleFutureConnections.find(well.name());
// initialize the additional cell connections introduced by wells.
for (const auto& well : schedule_wells) {
auto& compressed_well_perforations = wellConnections.emplace_back
(this->getCellsForConnections(this->schedule().back().wells(well)));
const auto possibleFutureConnectionSetIt = possibleFutureConnections.find(well);
if (possibleFutureConnectionSetIt != possibleFutureConnections.end()) {
for (const auto& global_index : possibleFutureConnectionSetIt->second) {
// THIS NEEDS TO BE UPDATED TO USE LGR
int compressed_idx = compressedIndexForInterior(global_index);
const int compressed_idx = compressedIndexForInterior(global_index);
if (compressed_idx >= 0) { // Ignore connections in inactive/remote cells.
compressed_well_perforations.push_back(compressed_idx);
}
}
}
// also include wells with no perforations in case
std::sort(compressed_well_perforations.begin(),
compressed_well_perforations.end());
wells.push_back(compressed_well_perforations);
}
return wells;
return wellConnections;
}
template<class Scalar>
int BlackoilWellModelGeneric<Scalar>::numLocalWellsEnd() const
{
auto w = schedule().getWellsatEnd();
w.erase(std::remove_if(w.begin(), w.end(), not_on_process_), w.end());
return w.size();
const auto& wnames = schedule().back().well_order().names();
return std::count_if(wnames.begin(), wnames.end(),
[this](const std::string& wname)
{ return ! this->not_on_process_(wname); });
}
template<class Scalar>
@@ -536,7 +536,7 @@ protected:
std::map<std::string, double> well_close_times_;
std::vector<ConnectionIndexMap> conn_idx_map_{};
std::function<bool(const Well&)> not_on_process_{};
std::function<bool(const std::string&)> not_on_process_{};
// a vector of all the wells.
std::vector<WellInterfaceGeneric<Scalar>*> well_container_generic_{};
+5 -4
View File
@@ -29,6 +29,7 @@
#include <opm/input/eclipse/Schedule/Group/Group.hpp>
#include <opm/input/eclipse/Schedule/Group/GuideRateConfig.hpp>
#include <opm/input/eclipse/Schedule/Network/ExtNetwork.hpp>
#include <opm/input/eclipse/Schedule/Well/NameOrder.hpp>
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
#include <opm/material/fluidsystems/BlackOilFluidSystem.hpp>
@@ -2289,12 +2290,12 @@ updateGuideRatesForWells(const Schedule& schedule,
GuideRate* guideRate)
{
OPM_TIMEFUNCTION();
for (const auto& well : schedule.getWells(reportStepIdx)) {
for (const auto& wname : schedule[reportStepIdx].well_order()) {
std::array<Scalar,3> potentials{};
auto& [oilpot, gaspot, waterpot] = potentials;
const auto& well_index = wellState.index(well.name());
if (well_index.has_value() && wellState.wellIsOwned(well_index.value(), well.name()))
const auto& well_index = wellState.index(wname);
if (well_index.has_value() && wellState.wellIsOwned(well_index.value(), wname))
{
// the well is found and owned
const auto& ws = wellState.well(well_index.value());
@@ -2313,7 +2314,7 @@ updateGuideRatesForWells(const Schedule& schedule,
oilpot = unit_system.from_si(UnitSystem::measure::liquid_surface_rate, oilpot);
waterpot = unit_system.from_si(UnitSystem::measure::liquid_surface_rate, waterpot);
gaspot = unit_system.from_si(UnitSystem::measure::gas_surface_rate, gaspot);
guideRate->compute(well.name(), reportStepIdx, simTime, oilpot, gaspot, waterpot);
guideRate->compute(wname, reportStepIdx, simTime, oilpot, gaspot, waterpot);
}
}