diff --git a/opm/simulators/flow/FlowGenericVanguard.cpp b/opm/simulators/flow/FlowGenericVanguard.cpp index 1f6e0af49..52ceaeeb8 100644 --- a/opm/simulators/flow/FlowGenericVanguard.cpp +++ b/opm/simulators/flow/FlowGenericVanguard.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include diff --git a/opm/simulators/utils/ParallelSerialization.cpp b/opm/simulators/utils/ParallelSerialization.cpp index 1d34cb387..4b64ffe26 100644 --- a/opm/simulators/utils/ParallelSerialization.cpp +++ b/opm/simulators/utils/ParallelSerialization.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include diff --git a/opm/simulators/utils/PartiallySupportedFlowKeywords.cpp b/opm/simulators/utils/PartiallySupportedFlowKeywords.cpp index 96b4595c8..644ea31a1 100644 --- a/opm/simulators/utils/PartiallySupportedFlowKeywords.cpp +++ b/opm/simulators/utils/PartiallySupportedFlowKeywords.cpp @@ -602,6 +602,14 @@ partiallySupported() {20,{true, allow_values {}, "GCONPROD(COMBRATE): linearly combined rate is not used and should be defaulted (1*)"}}, // LINEAR_COMBINED_TARGET }, }, + { + "GSATPROD", + { + {5,{true, allow_values {.0}, "GSATPROD(RESV): reservoir volume rate is not supported and should be defaulted (1*)"}}, // RESERVOIR_VOLUME_RATE + {6,{true, allow_values {.0}, "GSATPROD(GASLIFT): gaslift rate is not supported and should be defaulted (1*)"}}, // GASLIFT + {7,{false, allow_values {.0}, "GSATPROD(CALRATE): calorific rate is not used and should be defaulted (1*)"}}, // CALORIFIC_RATE + }, + }, { "GUIDERAT", { diff --git a/opm/simulators/utils/UnsupportedFlowKeywords.cpp b/opm/simulators/utils/UnsupportedFlowKeywords.cpp index d8afce549..da61bbd8b 100644 --- a/opm/simulators/utils/UnsupportedFlowKeywords.cpp +++ b/opm/simulators/utils/UnsupportedFlowKeywords.cpp @@ -253,7 +253,6 @@ const KeywordValidation::UnsupportedKeywords& unsupportedKeywords() {"GTADD", {true, std::nullopt}}, {"GTMULT", {true, std::nullopt}}, {"GUIDECAL", {true, std::nullopt}}, - {"GSATPROD", {true, std::nullopt}}, {"GUPFREQ", {true, std::nullopt}}, {"GWRTWCV", {true, std::nullopt}}, {"HALFTRAN", {true, std::nullopt}}, diff --git a/opm/simulators/wells/GlobalWellInfo.cpp b/opm/simulators/wells/GlobalWellInfo.cpp index 294a4d117..0509c7fa4 100644 --- a/opm/simulators/wells/GlobalWellInfo.cpp +++ b/opm/simulators/wells/GlobalWellInfo.cpp @@ -119,6 +119,12 @@ void GlobalWellInfo::clear() this->m_efficiency_scaling_factors.assign(this->name_map.size(), 1.0); } +template +bool GlobalWellInfo::isRank0() const +{ + return is_rank0_; +} + template Scalar GlobalWellInfo:: efficiency_scaling_factor(const std::string& wname) const diff --git a/opm/simulators/wells/GlobalWellInfo.hpp b/opm/simulators/wells/GlobalWellInfo.hpp index 03c9152cb..c941e2969 100644 --- a/opm/simulators/wells/GlobalWellInfo.hpp +++ b/opm/simulators/wells/GlobalWellInfo.hpp @@ -65,6 +65,7 @@ public: comm.sum( this->m_in_producing_group.data(), size); comm.sum( this->m_is_open.data(), size); comm.min( this->m_efficiency_scaling_factors.data(), size); + is_rank0_ = (comm.rank() == 0); } @@ -80,6 +81,7 @@ public: void update_efficiency_scaling_factor(std::size_t well_index, const Scalar efficiency_scaling_factor); Scalar efficiency_scaling_factor(const std::string& wname) const; void clear(); + bool isRank0() const; private: std::vector local_map; // local_index -> global_index @@ -89,6 +91,7 @@ private: std::vector m_in_producing_group; // global_index -> int/bool std::vector m_is_open; // global_index -> int/bool std::vector m_efficiency_scaling_factors; // global_index --> double + bool is_rank0_{true}; }; diff --git a/opm/simulators/wells/WellGroupHelpers.cpp b/opm/simulators/wells/WellGroupHelpers.cpp index e54bb5783..b0f20b846 100644 --- a/opm/simulators/wells/WellGroupHelpers.cpp +++ b/opm/simulators/wells/WellGroupHelpers.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -99,6 +100,14 @@ namespace Opm { rate += gefac * sumWellPhaseRates(res_rates, groupTmp, schedule, wellState, reportStepIdx, phasePos, injector); } + // only sum satelite production once + if (wellState.isRank0() && !injector) { + const auto rateComp = selectRateComponent(wellState.phaseUsage(), phasePos); + if (rateComp.has_value()) { + rate += satelliteProduction(schedule[reportStepIdx], group.groups(), *rateComp); + } + } + for (const std::string& wellName : group.wells()) { const auto& well_index = wellState.index(wellName); if (!well_index.has_value()) @@ -137,6 +146,42 @@ namespace Opm { return rate; } +template +Scalar WellGroupHelpers:: +satelliteProduction(const ScheduleState& sched, + const std::vector& groups, + const GSatProd::GSatProdGroup::Rate rateComp) +{ + auto gsatProdRate = Scalar{}; + const auto& gsatProd = sched.gsatprod(); + for (const auto& group : groups) { + if (! gsatProd.has(group)) { + continue; + } + gsatProdRate += gsatProd.get(group).rate[rateComp]; + } + return gsatProdRate; +} + +template +std::optional WellGroupHelpers:: +selectRateComponent(const PhaseUsage& pu, const int phasePos) +{ + using Rate = GSatProd::GSatProdGroup::Rate; + + for (const auto& [phase, rateComp] : std::array { + std::pair { BlackoilPhases::Aqua, Rate::Water }, + std::pair { BlackoilPhases::Liquid, Rate::Oil }, + std::pair { BlackoilPhases::Vapour, Rate::Gas } }) + { + if (pu.phase_used[phase] && (pu.phase_pos[phase] == phasePos)) { + return rateComp; + } + } + + return std::nullopt; +} + template void WellGroupHelpers:: setCmodeGroup(const Group& group, diff --git a/opm/simulators/wells/WellGroupHelpers.hpp b/opm/simulators/wells/WellGroupHelpers.hpp index 00db85713..62bd15b28 100644 --- a/opm/simulators/wells/WellGroupHelpers.hpp +++ b/opm/simulators/wells/WellGroupHelpers.hpp @@ -22,8 +22,10 @@ #define OPM_WELLGROUPHELPERS_HEADER_INCLUDED #include +#include #include #include +#include #include #include @@ -56,6 +58,13 @@ public: const int phasePos, const bool injector); + static Scalar satelliteProduction(const ScheduleState& sched, + const std::vector& groups, + const GSatProd::GSatProdGroup::Rate rateComp); + + static std::optional + selectRateComponent(const PhaseUsage& pu, const int phasePos); + static void setCmodeGroup(const Group& group, const Schedule& schedule, const SummaryState& summaryState, diff --git a/opm/simulators/wells/WellState.hpp b/opm/simulators/wells/WellState.hpp index 1d4f4bf85..e2a8edf7a 100644 --- a/opm/simulators/wells/WellState.hpp +++ b/opm/simulators/wells/WellState.hpp @@ -260,6 +260,10 @@ public: bool wellIsOwned(const std::string& wellName) const; + bool isRank0() const { + return this->global_well_info.value().isRank0(); + } + void updateStatus(int well_index, WellStatus status); void openWell(int well_index); diff --git a/tests/test_ParallelSerialization.cpp b/tests/test_ParallelSerialization.cpp index e7bd453e9..6d41c2a64 100644 --- a/tests/test_ParallelSerialization.cpp +++ b/tests/test_ParallelSerialization.cpp @@ -67,6 +67,7 @@ #include #include #include +#include #include #include #include @@ -239,6 +240,7 @@ TEST_FOR_TYPE(FoamConfig) TEST_FOR_TYPE(FoamData) TEST_FOR_TYPE(GConSale) TEST_FOR_TYPE(GConSump) +TEST_FOR_TYPE(GSatProd) TEST_FOR_TYPE(GridDims) TEST_FOR_TYPE(Group) TEST_FOR_TYPE_NAMED(Group::GroupInjectionProperties, GroupInjectionProperties)