diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake
index fc74dd665..6b4502ad4 100644
--- a/CMakeLists_files.cmake
+++ b/CMakeLists_files.cmake
@@ -204,6 +204,7 @@ if(ENABLE_ECL_INPUT)
src/opm/input/eclipse/Schedule/Well/PAvg.cpp
src/opm/input/eclipse/Schedule/Well/PAvgCalculator.cpp
src/opm/input/eclipse/Schedule/Well/PAvgCalculatorCollection.cpp
+ src/opm/input/eclipse/Schedule/Well/PAvgDynamicSourceData.cpp
src/opm/input/eclipse/Schedule/Well/Well.cpp
src/opm/input/eclipse/Schedule/Well/WellConnections.cpp
src/opm/input/eclipse/Schedule/Well/WellMatcher.cpp
@@ -460,11 +461,12 @@ if(ENABLE_ECL_INPUT)
tests/test_RestartFileView.cpp
tests/test_EclIO.cpp
tests/test_EGrid.cpp
+ tests/test_EInit.cpp
tests/test_ERft.cpp
tests/test_ERst.cpp
tests/test_ESmry.cpp
- tests/test_EInit.cpp
tests/test_ExtESmry.cpp
+ tests/test_PAvgDynamicSourceData.cpp
tests/test_Serialization.cpp
tests/material/test_co2brinepvt.cpp
tests/material/test_eclblackoilfluidsystem.cpp
@@ -1212,6 +1214,7 @@ if(ENABLE_ECL_INPUT)
opm/input/eclipse/Schedule/Well/PAvg.hpp
opm/input/eclipse/Schedule/Well/PAvgCalculator.hpp
opm/input/eclipse/Schedule/Well/PAvgCalculatorCollection.hpp
+ opm/input/eclipse/Schedule/Well/PAvgDynamicSourceData.hpp
opm/input/eclipse/Schedule/Well/Well.hpp
opm/input/eclipse/Schedule/Well/WellEnums.hpp
opm/input/eclipse/Schedule/Well/WellInjectionControls.hpp
diff --git a/opm/input/eclipse/Schedule/Well/PAvgDynamicSourceData.hpp b/opm/input/eclipse/Schedule/Well/PAvgDynamicSourceData.hpp
new file mode 100644
index 000000000..481f68c93
--- /dev/null
+++ b/opm/input/eclipse/Schedule/Well/PAvgDynamicSourceData.hpp
@@ -0,0 +1,256 @@
+/*
+ Copyright 2023 Equinor ASA.
+
+ 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 .
+*/
+
+#ifndef PAVE_DYNAMIC_SOURCE_DATA_HPP
+#define PAVE_DYNAMIC_SOURCE_DATA_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Opm {
+
+/// Dynamic source data for block-average pressure calculations
+class PAvgDynamicSourceData
+{
+public:
+ /// Ad hoc implementation of fixed-width span/view of an underlying
+ /// contiguous range of elements.
+ ///
+ /// \tparam T Element type. Const or non-const as needed. Typically \c
+ /// double or \code const double \endcode.
+ template
+ class SourceDataSpan
+ {
+ private:
+ friend class PAvgDynamicSourceData;
+
+ public:
+ /// Supported items of dynamic data per source location
+ enum class Item
+ {
+ Pressure, //< Dynamic pressure value
+ MixtureDensity, //< Dynamic mixture density
+ PoreVol, //< Dynamic pore volume
+
+ // ----------------------------------------
+
+ Last_Do_Not_Use, //< Simplifies item count
+ };
+
+ using ElmT = std::remove_cv_t;
+
+ /// Read-only access to numerical value of specified item
+ ///
+ /// \param[in] i Item of dynamic source data.
+ /// \return Numerical value of specified item.
+ [[nodiscard]] constexpr ElmT operator[](const Item i) const
+ {
+ return this->begin_[this->index(i)];
+ }
+
+ /// Assign specified item
+ ///
+ /// Availble only if underlying range is non-const.
+ ///
+ /// \param[in] i Item of dynamic source data.
+ /// \param[in] value Numerical value of specified item.
+ /// \return \code *this \endcode to enable chaining.
+ template
+ constexpr std::enable_if_t, Ret>
+ set(const Item i, const ElmT value)
+ {
+ this->begin_[this->index(i)] = value;
+ return *this;
+ }
+
+ /// Assign all items
+ ///
+ /// Availble only if underlying range is non-const.
+ ///
+ /// \param[in] i Item of dynamic source data.
+ /// \param[in] value Numerical value of specified item.
+ /// \return \code *this \endcode to enable chaining.
+ template
+ constexpr std::enable_if_t, Ret>
+ operator=(const SourceDataSpan src)
+ {
+ std::copy_n(src.begin_, NumItems, this->begin_);
+ return *this;
+ }
+
+ private:
+ /// Number of data items per source location
+ static constexpr auto NumItems =
+ static_cast(Item::Last_Do_Not_Use);
+
+ /// Beginning of items associated to a single source location.
+ T* begin_{nullptr};
+
+ /// Constructor.
+ ///
+ /// \param[in] begin Beginning of items associated to a single
+ /// source location.
+ explicit SourceDataSpan(T* begin)
+ : begin_{begin}
+ {}
+
+ /// Translate item into linear index
+ ///
+ /// \param[in] i Item of dynamic source data
+ /// \return Linear index into span corresponding to specified item.
+ constexpr std::size_t index(const Item i) const
+ {
+ const auto ix = static_cast(i);
+ if (ix >= NumItems) {
+ throw std::invalid_argument {
+ "Index out of bounds"
+ };
+ }
+
+ return ix;
+ }
+ };
+
+ /// Constructor
+ ///
+ /// \param[in] sourceLocations Known locations, typically linearised
+ /// global call IDs, for which to enable collecting/reporting dynamic
+ /// source data.
+ explicit PAvgDynamicSourceData(const std::vector& sourceLocations);
+
+ /// Destructor
+ ///
+ /// Marked virtual because this type is intended for inheritance.
+ virtual ~PAvgDynamicSourceData() {}
+
+ /// Acquire read/write span of data items corresponding to a single
+ /// source location.
+ ///
+ /// Mostly intended for assigning values.
+ ///
+ /// \param[in] source Source location. Function will \c throw if \p
+ /// source is not one of the known locations registered in the object
+ /// constructor.
+ ///
+ /// \return Read/write span of data items.
+ [[nodiscard]] SourceDataSpan
+ operator[](const std::size_t source);
+
+ /// Acquire read-only span of data items corresponding to a single
+ /// source location.
+ ///
+ /// Intended for extracting previously assigned data items.
+ ///
+ /// \param[in] source Source location. Function will \c throw if \p
+ /// source is not one of the known locations registered in the object
+ /// constructor.
+ ///
+ /// \return Read-only span of data items.
+ [[nodiscard]] SourceDataSpan
+ operator[](const std::size_t source) const;
+
+protected:
+ /// Contiguous array of data items for all source locations.
+ ///
+ /// Intentionally accessible to derived classes for use in parallel
+ /// runs.
+ std::vector src_{};
+
+ /// Form mutable data span into non-default backing store.
+ ///
+ /// Mainly intended for constructing span objects in backing store for
+ /// local (on-rank) sources in parallel runs.
+ ///
+ /// \param[in] ix Logical element index into source term backing store.
+ ///
+ /// \param[in,out] src Source term backing store.
+ ///
+ /// \return Mutable view into \p src.
+ [[nodiscard]] SourceDataSpan
+ sourceTerm(const std::size_t ix, std::vector& src);
+
+ /// Reconstruct Source Data backing storage and internal mapping tables
+ ///
+ /// Effectively replaces the original object formed by the constructor.
+ /// Mainly intended for updating objects as new wells and/or new
+ /// reservoir connections are introduced.
+ ///
+ /// \param[in] sourceLocations Known locations, typically linearised
+ /// global call IDs, for which to enable collecting/reporting dynamic
+ /// source data.
+ void reconstruct(const std::vector& sourceLocations);
+
+ /// Provide number of span items using function syntax
+ ///
+ /// Marked 'protected' because derived classes might need this
+ /// information too.
+ ///
+ /// \return Number of span items.
+ static constexpr std::size_t numSpanItems() noexcept
+ {
+ return SourceDataSpan::NumItems;
+ }
+
+private:
+ /// Translate non-contiguous source locations to starting indices in \c
+ /// src_.
+ std::unordered_map::size_type> ix_{};
+
+ /// Form source location to index translation table.
+ ///
+ /// \param[in] sourceLocations Known locations, typically linearised
+ /// global call IDs, for which to enable collecting/reporting dynamic
+ /// source data.
+ ///
+ /// \return Whether or not table formation succeeded. Typical reasons
+ /// for failure is repeated source locations.
+ void buildLocationMapping(const std::vector& sourceLocations);
+
+ /// Translate source location to starting index in \c src_.
+ ///
+ /// \param[in] source Source location.
+ ///
+ /// \return Starting index. Nullopt if no index exists for \p source.
+ [[nodiscard]] std::optional::size_type>
+ index(const std::size_t source) const;
+
+ /// Translate element index into storage index.
+ ///
+ /// This is a customisation point to simplify usage in parallel contexts.
+ ///
+ /// Default implementation, identity mapping.
+ ///
+ /// \param[in] elemIndex Source element index.
+ ///
+ /// \return Storage (starting) index in \c src_.
+ [[nodiscard]] virtual std::vector::size_type
+ storageIndex(std::vector::size_type elemIndex) const
+ {
+ return elemIndex;
+ }
+};
+
+} // namespace Opm
+
+#endif // PAVE_DYNAMIC_SOURCE_DATA_HPP
diff --git a/src/opm/input/eclipse/Schedule/Well/PAvgDynamicSourceData.cpp b/src/opm/input/eclipse/Schedule/Well/PAvgDynamicSourceData.cpp
new file mode 100644
index 000000000..7d39893ee
--- /dev/null
+++ b/src/opm/input/eclipse/Schedule/Well/PAvgDynamicSourceData.cpp
@@ -0,0 +1,107 @@
+/*
+ Copyright 2023 Equinor ASA.
+
+ 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
+
+Opm::PAvgDynamicSourceData::PAvgDynamicSourceData(const std::vector& sourceLocations)
+ : src_(numSpanItems() * sourceLocations.size(), 0.0)
+{
+ this->buildLocationMapping(sourceLocations);
+}
+
+Opm::PAvgDynamicSourceData::SourceDataSpan
+Opm::PAvgDynamicSourceData::operator[](const std::size_t source)
+{
+ const auto i = this->index(source);
+ if (! i.has_value()) {
+ OPM_THROW_NOLOG(std::invalid_argument,
+ fmt::format("Dynamic source location "
+ "'{}' is not registered", source));
+ }
+
+ return SourceDataSpan{ &this->src_[*i] };
+}
+
+Opm::PAvgDynamicSourceData::SourceDataSpan
+Opm::PAvgDynamicSourceData::operator[](const std::size_t source) const
+{
+ const auto i = this->index(source);
+ if (! i.has_value()) {
+ OPM_THROW_NOLOG(std::invalid_argument,
+ fmt::format("Dynamic source location "
+ "'{}' is not registered", source));
+ }
+
+ return SourceDataSpan{ &this->src_[*i] };
+}
+
+Opm::PAvgDynamicSourceData::SourceDataSpan
+Opm::PAvgDynamicSourceData::sourceTerm(const std::size_t ix, std::vector& src)
+{
+ return SourceDataSpan { &src[ix*numSpanItems() + 0] };
+}
+
+void
+Opm::PAvgDynamicSourceData::
+reconstruct(const std::vector& sourceLocations)
+{
+ this->src_.assign(numSpanItems() * sourceLocations.size(), 0.0);
+
+ this->buildLocationMapping(sourceLocations);
+}
+
+void
+Opm::PAvgDynamicSourceData::
+buildLocationMapping(const std::vector& sourceLocations)
+{
+ this->ix_.clear();
+
+ auto ix = std::vector::size_type{0};
+ for (const auto& srcLoc : sourceLocations) {
+ auto elm = this->ix_.emplace(srcLoc, ix++);
+ if (! elm.second) {
+ OPM_THROW_NOLOG(std::invalid_argument,
+ fmt::format("Failed to set up internal mapping table, "
+ "single location {} entered multiple times.",
+ srcLoc));
+ }
+ }
+}
+
+std::optional::size_type>
+Opm::PAvgDynamicSourceData::index(const std::size_t source) const
+{
+ auto pos = this->ix_.find(source);
+ if (pos == this->ix_.end()) {
+ return {};
+ }
+
+ return numSpanItems() * this->storageIndex(pos->second);
+}
diff --git a/tests/test_PAvgDynamicSourceData.cpp b/tests/test_PAvgDynamicSourceData.cpp
new file mode 100644
index 000000000..f5547cd51
--- /dev/null
+++ b/tests/test_PAvgDynamicSourceData.cpp
@@ -0,0 +1,151 @@
+/*
+ Copyright (c) 2023 Equinor ASA
+
+ 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 .
+*/
+
+#define BOOST_TEST_MODULE test_PAvgDynamicSourceData
+
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+// ===========================================================================
+
+BOOST_AUTO_TEST_SUITE(Dynamic_Source_Data)
+
+namespace {
+ std::vector small()
+ {
+ return { 1, 2, 3, 5, };
+ }
+
+ std::vector repeated()
+ {
+ return { 1, 2, 3, 5, 3, };
+ }
+
+ Opm::PAvgDynamicSourceData smallResult()
+ {
+ using S = decltype(std::declval()[0]);
+ using I = typename S::Item;
+
+ auto src = Opm::PAvgDynamicSourceData { small() };
+
+ src[1].set(I::Pressure, 123.4).set(I::MixtureDensity, 121.2).set(I::PoreVol, 543.21);
+ src[2].set(I::Pressure, 12.34).set(I::MixtureDensity, 12.12).set(I::PoreVol, 54.321);
+ src[3].set(I::Pressure, 1.234).set(I::MixtureDensity, 1.212).set(I::PoreVol, 5.4321);
+ src[5].set(I::Pressure, 1234) .set(I::MixtureDensity, 1212.).set(I::PoreVol, 5432.1);
+
+ return src;
+ }
+} // Anonymous namespace
+
+BOOST_AUTO_TEST_CASE(Mutable)
+{
+ using S = decltype(std::declval()[0]);
+ using I = typename S::Item;
+
+ auto src = Opm::PAvgDynamicSourceData { small() };
+
+ src[1].set(I::Pressure, 123.4).set(I::MixtureDensity, 121.2).set(I::PoreVol, 543.21);
+
+ {
+ const auto s1 = src[1];
+ BOOST_CHECK_CLOSE(s1[I::Pressure], 123.4, 1.0e-10);
+ BOOST_CHECK_CLOSE(s1[I::MixtureDensity], 121.2, 1.0e-10);
+ BOOST_CHECK_CLOSE(s1[I::PoreVol], 543.21, 1.0e-10);
+ }
+
+ {
+ const auto s2 = src[2];
+ BOOST_CHECK_CLOSE(s2[I::Pressure], 0.0, 1.0e-10);
+ BOOST_CHECK_CLOSE(s2[I::MixtureDensity], 0.0, 1.0e-10);
+ BOOST_CHECK_CLOSE(s2[I::PoreVol], 0.0, 1.0e-10);
+ }
+
+ src[2].set(I::Pressure, 123.4).set(I::MixtureDensity, 121.2).set(I::PoreVol, 543.21);
+
+ {
+ const auto s2 = src[2];
+ BOOST_CHECK_CLOSE(s2[I::Pressure], 123.4, 1.0e-10);
+ BOOST_CHECK_CLOSE(s2[I::MixtureDensity], 121.2, 1.0e-10);
+ BOOST_CHECK_CLOSE(s2[I::PoreVol], 543.21, 1.0e-10);
+ }
+
+ BOOST_CHECK_THROW(src[4].set(I::Pressure, 0.5), std::invalid_argument);
+
+ src[5].set(I::Pressure, 123.4).set(I::MixtureDensity, 121.2).set(I::PoreVol, 543.21);
+
+ {
+ const auto s5 = src[5];
+ BOOST_CHECK_CLOSE(s5[I::Pressure], 123.4, 1.0e-10);
+ BOOST_CHECK_CLOSE(s5[I::MixtureDensity], 121.2, 1.0e-10);
+ BOOST_CHECK_CLOSE(s5[I::PoreVol], 543.21, 1.0e-10);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Immutable)
+{
+ const auto src = smallResult();
+
+ using S = decltype(src[0]);
+ using I = typename S::Item;
+
+ {
+ const auto s1 = src[1];
+ BOOST_CHECK_CLOSE(s1[I::Pressure], 123.4, 1.0e-10);
+ BOOST_CHECK_CLOSE(s1[I::MixtureDensity], 121.2, 1.0e-10);
+ BOOST_CHECK_CLOSE(s1[I::PoreVol], 543.21, 1.0e-10);
+ }
+
+ {
+ const auto s2 = src[2];
+ BOOST_CHECK_CLOSE(s2[I::Pressure], 12.34, 1.0e-10);
+ BOOST_CHECK_CLOSE(s2[I::MixtureDensity], 12.12, 1.0e-10);
+ BOOST_CHECK_CLOSE(s2[I::PoreVol], 54.321, 1.0e-10);
+ }
+
+ {
+ const auto s3 = src[3];
+ BOOST_CHECK_CLOSE(s3[I::Pressure], 1.234, 1.0e-10);
+ BOOST_CHECK_CLOSE(s3[I::MixtureDensity], 1.212, 1.0e-10);
+ BOOST_CHECK_CLOSE(s3[I::PoreVol], 5.4321, 1.0e-10);
+ }
+
+ {
+ const auto s5 = src[5];
+ BOOST_CHECK_CLOSE(s5[I::Pressure], 1234., 1.0e-10);
+ BOOST_CHECK_CLOSE(s5[I::MixtureDensity], 1212., 1.0e-10);
+ BOOST_CHECK_CLOSE(s5[I::PoreVol], 5432.1, 1.0e-10);
+ }
+
+ BOOST_CHECK_THROW(std::ignore = src[1729], std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(Repeated)
+{
+ BOOST_CHECK_THROW(std::ignore = Opm::PAvgDynamicSourceData{ repeated() },
+ std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Dynamic_Source_Data