diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake
index fd665acab..1b93ab6b1 100644
--- a/CMakeLists_files.cmake
+++ b/CMakeLists_files.cmake
@@ -131,6 +131,7 @@ if(ENABLE_ECL_INPUT)
src/opm/input/eclipse/Schedule/MSW/icd.cpp
src/opm/input/eclipse/Schedule/MSW/Compsegs.cpp
src/opm/input/eclipse/Schedule/MSW/Segment.cpp
+ src/opm/input/eclipse/Schedule/MSW/SegmentMatcher.cpp
src/opm/input/eclipse/Schedule/MSW/WellSegments.cpp
src/opm/input/eclipse/Schedule/MSW/AICD.cpp
src/opm/input/eclipse/Schedule/MSW/SICD.cpp
@@ -374,6 +375,7 @@ list (APPEND TEST_SOURCE_FILES
tests/test_param.cpp
tests/test_pengrobinson.cpp
tests/test_RootFinders.cpp
+ tests/test_SegmentMatcher.cpp
tests/test_sparsevector.cpp
tests/test_spline.cpp
tests/test_tabulation.cpp
@@ -1099,7 +1101,7 @@ if(ENABLE_ECL_INPUT)
opm/input/eclipse/Schedule/OilVaporizationProperties.hpp
opm/input/eclipse/Schedule/MSW/icd.hpp
opm/input/eclipse/Schedule/MSW/Segment.hpp
- opm/input/eclipse/Schedule/MSW/Segment.hpp
+ opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp
opm/input/eclipse/Schedule/MSW/WellSegments.hpp
opm/input/eclipse/Schedule/MSW/AICD.hpp
opm/input/eclipse/Schedule/MSW/SICD.hpp
diff --git a/opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp b/opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp
new file mode 100644
index 000000000..4d1fe8f0b
--- /dev/null
+++ b/opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp
@@ -0,0 +1,354 @@
+/*
+ Copyright 2022 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 SEGMENT_MATCHER_HPP
+#define SEGMENT_MATCHER_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/// \file Facility for Identifying Specific Well Segments Matching a UDQ Segment Set.
+
+namespace Opm {
+ class ScheduleState;
+} // namespace Opm
+
+namespace Opm {
+
+/// Encapsulation of Matching Process for MSW Segment Sets
+///
+/// Primary use case is determining the set of MSW segments used to define
+/// segment level UDQs. Typical segment quantities in this context are
+///
+/// SOFR - Oil flow rate in all segments in all MS wells
+/// SOFR 'P' - Oil flow rate in all segments of MS well 'P'
+/// SOFR 'P' 1 - Oil flow rate in segment 1 of MS well 'P'
+/// SOFR 'P*' - Oil flow rate in all segments of all MS wells whose name
+/// begins with 'P'
+/// SOFR 'P*' 27 - Oil flow rate in segment 27 of all MS wells whose name
+/// begins with 'P'
+/// SOFR '*' 27 - Oil flow rate in segment 27 of all MS wells
+/// SOFR '*' '*' - Oil flow rate in all segments of all MS wells
+///
+/// The user initiates the matching process by constructing a SetDescriptor
+/// object, filling in the known pieces of information--well name patterns
+/// and segment numbers--if applicable. A SetDescriptor with no well name
+/// or segment number will match all segments in all MS wells.
+///
+/// The matching process, \code SegmentMatcher::findSegments() \endcode,
+/// forms a \c SegmentSet object which holds a list of matching MS wells and
+/// their associate/corresponding matching segment numbers.
+class SegmentMatcher
+{
+public:
+ /// Result Set From Matching Process
+ class SegmentSet
+ {
+ public:
+ /// Demarcation of Start/End of Segment Range for Single MS Well
+ using WellSegmentRangeIterator = std::vector::const_iterator;
+
+ /// Segment Range for Single MS Well.
+ class WellSegmentRange
+ {
+ public:
+ /// Start of Range
+ WellSegmentRangeIterator begin() const { return this->begin_; }
+
+ /// End of Range
+ WellSegmentRangeIterator end() const { return this->end_; }
+
+ /// Name of well to which this segment range is attached
+ std::string_view well() const { return this->well_; }
+
+ friend class SegmentSet;
+
+ private:
+ /// Start of Range
+ WellSegmentRangeIterator begin_{};
+
+ /// End of Range
+ WellSegmentRangeIterator end_{};
+
+ /// Name of well to which this segment range is attached
+ std::string_view well_{};
+
+ /// Default Constructor.
+ ///
+ /// Empty range.
+ ///
+ /// For use by SegmentSet only.
+ WellSegmentRange() = default;
+
+ /// Non-Empty Range
+ ///
+ /// For use by SegmentSet only.
+ ///
+ /// \param[in] begin Start of range.
+ /// \param[in] end End of range.
+ /// \param[in] well Name of well to which this segment range is
+ /// attached
+ WellSegmentRange(WellSegmentRangeIterator begin,
+ WellSegmentRangeIterator end,
+ std::string_view well)
+ : begin_{ begin }
+ , end_ { end }
+ , well_ { well }
+ {}
+ };
+
+ /// Default Constructor.
+ SegmentSet();
+
+ /// Predicate for whether or not segment set is empty.
+ ///
+ /// \return Whether or not segment set is empty.
+ bool empty() const
+ {
+ return this->segments_.empty();
+ }
+
+ /// Predicate for whether or not segment set applies to a single
+ /// segment in a single MS well.
+ ///
+ /// \return Whether or not segment set is a single segment in a
+ /// single MS well. Useful to distinguish whether or not this
+ /// segment set generates a scalar UDQ or a UDQ set in the context
+ /// of a segment level UDQ.
+ bool isScalar() const
+ {
+ return this->segments_.size() == std::vector::size_type{1};
+ }
+
+ /// Retrieve list of (MS) well names covered by this result set.
+ ///
+ /// \return List MS well names covered by this result set.
+ std::vector wells() const;
+
+ /// Retrieve number of (MS) wells covered by this result set.
+ ///
+ /// \return Number of MS wells covered by this result set.
+ std::size_t numWells() const
+ {
+ return this->wells_.size();
+ }
+
+ /// Retrive result set's segments for single MS well.
+ ///
+ /// \param[in] well Named MS well. Should usually be one of the
+ /// items in the return value from \code wells() \endcode.
+ ///
+ /// \return range of \c well's segments matching the input request.
+ /// Empty unless \p well is one of the return values from \code
+ /// wells() \endcode.
+ WellSegmentRange segments(std::string_view well) const;
+
+ /// Retrive result set's segments for single MS well.
+ ///
+ /// \param[in] well Named MS well. Should usually be one of the
+ /// items in the return value from \code wells() \endcode.
+ ///
+ /// \return range of \c well's segments matching the input request.
+ /// Empty unless \p well is one of the return values from \code
+ /// wells() \endcode.
+ WellSegmentRange segments(const std::size_t well) const;
+
+ friend class SegmentMatcher;
+
+ private:
+ /// List of MS wells covered by this result set.
+ std::vector wells_{};
+
+ /// Name-to-index lookup table.
+ ///
+ /// User, i.e., the SegmentMatcher, must call \code
+ /// establishNameLookupIndex() \endcode to prepare the lookup table.
+ std::vector::size_type> wellNameIndex_{};
+
+ /// CSR start pointers for MS wells' segments.
+ std::vector::size_type> segmentStart_{};
+
+ /// All segments covered by this result set. Structured by \c
+ /// segmentStart_.
+ std::vector segments_{};
+
+ /// Build well-name to well number lookup index.
+ ///
+ /// For use by SegmentMatcher only.
+ void establishNameLookupIndex();
+
+ /// Add non-empty range of segments for single MS well to result set.
+ ///
+ /// For use by SegmentMatcher only.
+ ///
+ /// \param[in] well Name of MS well.
+ ///
+ /// \param[in] segments List of segment numbers matching input
+ /// request for \p well.
+ void addWellSegments(const std::string& well,
+ const std::vector& segments);
+ };
+
+ /// Description of Particular Segment Set
+ ///
+ /// User specified.
+ class SetDescriptor
+ {
+ public:
+ /// Default Constructor
+ SetDescriptor() = default;
+
+ /// Assign request's segment number.
+ ///
+ /// Non-positive matches all segments.
+ ///
+ /// \param[in] segNum Requests's segment number.
+ ///
+ /// \return \code *this \endcode.
+ SetDescriptor& segmentNumber(const int segNum);
+
+ /// Assign request's segment number.
+ ///
+ /// String version. Supports both quoted and unquoted strings.
+ /// Wildcard ('*') and string representation of a negative number
+ /// (e.g., '-1'), match all segments.
+ ///
+ /// \param[in] segNum Requests's segment number. Must be a text
+ /// representation of an integer or one of the recognized options
+ /// for matching all segments. Throws exception otherwise.
+ ///
+ /// \return \code *this \endcode.
+ SetDescriptor& segmentNumber(std::string_view segNum);
+
+ /// Retrive request's segment number
+ ///
+ /// \return Segment number. Unset if request matches all segments.
+ const std::optional& segmentNumber() const
+ {
+ return this->segmentNumber_;
+ }
+
+ /// Assign request's well set.
+ ///
+ /// \param[in] wellNamePattern Pattern for selecting specific MS
+ /// wells. Used in normal matching, except well lists are not
+ /// supported.
+ SetDescriptor& wellNames(std::string_view wellNamePattern);
+
+ /// Retrive request's well name pattern.
+ ///
+ /// \return Well name pattern. Unset if request matches all
+ /// multi-segmented wells.
+ const std::optional& wellNames() const
+ {
+ return this->wellNamePattern_;
+ }
+
+ private:
+ /// Request's well name or well name pattern. Unset if request
+ /// applies to all MS wells.
+ std::optional wellNamePattern_{};
+
+ /// Request's segment number. Unset if request applies to all
+ /// segments of pertinent well set.
+ std::optional segmentNumber_{};
+ };
+
+ /// Default constructor
+ ///
+ /// Disabled.
+ SegmentMatcher() = delete;
+
+ /// Constructor
+ ///
+ /// \param[in] mswInputData Input's notion of existing wells, both
+ /// regular and multi-segmented. Expected to be the state of a
+ /// Schedule block at a particular report step.
+ explicit SegmentMatcher(const ScheduleState& mswInputData);
+
+ /// Copy constructor
+ ///
+ /// Disabled.
+ ///
+ /// \param[in] rhs Source object.
+ SegmentMatcher(const SegmentMatcher& rhs) = delete;
+
+ /// Move constructor
+ ///
+ /// \param[in,out] rhs Source object. Left in empty state upon return.
+ SegmentMatcher(SegmentMatcher&& rhs);
+
+ /// Assignment operator
+ ///
+ /// Disabled.
+ ///
+ /// \param[in] rhs Source object.
+ ///
+ /// \return \code *this \endcode.
+ SegmentMatcher& operator=(const SegmentMatcher& rhs) = delete;
+
+ /// Move-assignment operator
+ ///
+ /// \param[in,out] rhs Source object. Left in empty state upon return.
+ ///
+ /// \return \code *this \endcode.
+ SegmentMatcher& operator=(SegmentMatcher&& rhs);
+
+ /// Destructor
+ ///
+ /// Needed to implement "pimpl" idiom.
+ ~SegmentMatcher();
+
+ /// Determine set of MS wells and corresponding segments matching an
+ /// input set description.
+ ///
+ /// Set is typically derived from description of user defined quantities
+ /// at the segment level, e.g.,
+ ///
+ /// \code
+ /// UDQ
+ /// DEFINE SU-LPR1 SOFR OP01 + SWPR OP01 /
+ /// /
+ /// \endcode
+ ///
+ /// which represents the surface level liquid production rate for all
+ /// segments in the multi-segmented well OP01.
+ ///
+ /// \param[in] segments Segment set selection description.
+ ///
+ /// \return Set of MS wells and corresponding segments matching input
+ /// set description.
+ SegmentSet findSegments(const SetDescriptor& segments) const;
+
+private:
+ /// Implementation class.
+ class Impl;
+
+ /// Pointer to implementation.
+ std::unique_ptr pImpl_{};
+};
+
+} // namespace Opm
+
+#endif // SEGMENT_MATCHER_HPP
diff --git a/src/opm/input/eclipse/Schedule/MSW/SegmentMatcher.cpp b/src/opm/input/eclipse/Schedule/MSW/SegmentMatcher.cpp
new file mode 100644
index 000000000..5ed51b983
--- /dev/null
+++ b/src/opm/input/eclipse/Schedule/MSW/SegmentMatcher.cpp
@@ -0,0 +1,405 @@
+/*
+ Copyright 2022 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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+class Opm::SegmentMatcher::Impl
+{
+public:
+ explicit Impl(const ScheduleState& mswInputData)
+ : mswInputData_{ std::cref(mswInputData) }
+ {}
+
+ template
+ SegmentSet findSegments(const SetDescriptor& request,
+ AddWellSegments addSegments) const;
+
+private:
+ std::reference_wrapper mswInputData_;
+
+ std::vector
+ candidateWells(const std::optional& wellNamePattern) const;
+
+ std::vector
+ candidateWells(const std::string& wellNamePattern) const;
+
+ std::vector candidateWells() const;
+
+ std::vector
+ candidateWells(const std::vector& allWells) const;
+
+ std::vector
+ matchingSegments(const std::string& well,
+ const std::optional& segmentNumber) const;
+
+ std::vector
+ matchingSegments(const std::string& well,
+ const int segmentNumber) const;
+
+ std::vector matchingSegments(const std::string& well) const;
+};
+
+template
+Opm::SegmentMatcher::SegmentSet
+Opm::SegmentMatcher::Impl::findSegments(const SetDescriptor& request,
+ AddWellSegments addSegments) const
+{
+ auto segSet = SegmentSet{};
+
+ for (const auto& well : this->candidateWells(request.wellNames())) {
+ if (const auto& segments = this->matchingSegments(well, request.segmentNumber());
+ ! segments.empty())
+ {
+ addSegments(well, segments, segSet);
+ }
+ }
+
+ return segSet;
+}
+
+std::vector
+Opm::SegmentMatcher::Impl::
+candidateWells(const std::optional& wellNamePattern) const
+{
+ return wellNamePattern.has_value()
+ ? this->candidateWells(*wellNamePattern)
+ : this->candidateWells();
+}
+
+std::vector
+Opm::SegmentMatcher::Impl::
+candidateWells(const std::string& wellNamePattern) const
+{
+ // Consider all MS wells matching 'wellNamePattern'.
+ return this->candidateWells(WellMatcher {
+ this->mswInputData_.get().well_order()
+ }.wells(wellNamePattern));
+}
+
+std::vector Opm::SegmentMatcher::Impl::candidateWells() const
+{
+ // No specific wellname pattern => all MS wells match.
+ return this->candidateWells(this->mswInputData_.get().well_order().names());
+}
+
+std::vector
+Opm::SegmentMatcher::Impl::
+candidateWells(const std::vector& allWells) const
+{
+ auto candidates = std::vector{};
+ candidates.reserve(allWells.size());
+
+ std::copy_if(allWells.begin(), allWells.end(), std::back_inserter(candidates),
+ [this](const std::string& well)
+ {
+ return this->mswInputData_.get().wells(well).isMultiSegment();
+ });
+
+ return candidates;
+}
+
+std::vector
+Opm::SegmentMatcher::Impl::
+matchingSegments(const std::string& well,
+ const std::optional& segmentNumber) const
+{
+ return segmentNumber.has_value()
+ ? this->matchingSegments(well, *segmentNumber)
+ : this->matchingSegments(well);
+}
+
+std::vector
+Opm::SegmentMatcher::Impl::
+matchingSegments(const std::string& wellname,
+ const int segmentNumber) const
+{
+ const auto& well = this->mswInputData_.get().wells(wellname);
+ assert (well.isMultiSegment());
+
+ if (const auto ix = well.getSegments().segmentNumberToIndex(segmentNumber);
+ ix < 0)
+ {
+ // SegmentNumber not among well's segments. Empty result set.
+ return {};
+ }
+
+ // SegmentNumber is among well's segments. Result set is exactly that segment.
+ return { segmentNumber };
+}
+
+std::vector
+Opm::SegmentMatcher::Impl::matchingSegments(const std::string& wellname) const
+{
+ // No specific segment number => All segments match.
+ auto segments = std::vector{};
+
+ const auto& well = this->mswInputData_.get().wells(wellname);
+ assert (well.isMultiSegment());
+
+ const auto& segSet = well.getSegments();
+
+ segments.reserve(segSet.size());
+ std::transform(segSet.begin(), segSet.end(), std::back_inserter(segments),
+ [](const Segment& segment) { return segment.segmentNumber(); });
+
+ return segments;
+}
+
+// ===========================================================================
+// Public Interface Below Separator
+// ===========================================================================
+
+namespace {
+ std::string_view dequote(std::string_view s)
+ {
+ auto b = s.find_first_of("'");
+ if (b == std::string_view::npos) {
+ return s;
+ }
+
+ auto x = s.substr(b + 1);
+ auto e = x.find_first_of("'");
+ if (e == std::string_view::npos) {
+ throw std::invalid_argument {
+ fmt::format("Invalid quoted string |{}|", s)
+ };
+ }
+
+ return x.substr(0, e);
+ }
+
+ bool is_asterisk(std::string_view s)
+ {
+ const auto ast = std::regex { R"(\s*\*\s*)" };
+ return std::regex_match(s.begin(), s.end(), ast);
+ }
+} // Anonymous namespace
+
+Opm::SegmentMatcher::SetDescriptor&
+Opm::SegmentMatcher::SetDescriptor::segmentNumber(const int segNum)
+{
+ if (segNum <= 0) {
+ // No specific segment number
+ this->segmentNumber_.reset();
+ }
+ else {
+ this->segmentNumber_ = segNum;
+ }
+
+ return *this;
+}
+
+Opm::SegmentMatcher::SetDescriptor&
+Opm::SegmentMatcher::SetDescriptor::segmentNumber(std::string_view segNum0)
+{
+ auto segNum = dequote(segNum0);
+
+ if (segNum.empty()) {
+ // Not specified
+ return this->segmentNumber(0);
+ }
+
+ auto result = 0;
+ auto [ptr, ec] { std::from_chars(segNum.data(), segNum.data() + segNum.size(), result) };
+
+ if ((ec == std::errc{}) && (ptr == segNum.data() + segNum.size())) {
+ // Segment number is "123", or "'-1'", or something similar.
+ return this->segmentNumber(result);
+ }
+ else if ((ec == std::errc::invalid_argument) && is_asterisk(segNum)) {
+ // Segment number is '*'. Treat as all segments.
+ return this->segmentNumber(0);
+ }
+ else {
+ // Segment number is some unrecognized number string other than '*'.
+ throw std::invalid_argument {
+ fmt::format("Invalid segment number string |{}|", segNum0)
+ };
+ }
+}
+
+Opm::SegmentMatcher::SetDescriptor&
+Opm::SegmentMatcher::SetDescriptor::wellNames(std::string_view wellNamePattern)
+{
+ if (wellNamePattern.empty()) {
+ // Match all MS wells.
+ this->wellNamePattern_.reset();
+ }
+ else {
+ // Match only those MS wells whose names match 'wellNamePattern'.
+ this->wellNamePattern_ = wellNamePattern;
+ }
+
+ return *this;
+}
+
+// ---------------------------------------------------------------------------
+
+Opm::SegmentMatcher::SegmentSet::SegmentSet()
+{
+ this->segmentStart_.push_back(this->segments_.size());
+}
+
+std::vector
+Opm::SegmentMatcher::SegmentSet::wells() const
+{
+ auto wellset = std::vector{};
+ wellset.reserve(this->wells_.size());
+
+ auto ix = std::vector::size_type>::size_type{0};
+ for (const auto& well : this->wells_) {
+ if (this->segmentStart_[ix] != this->segmentStart_[ix + 1]) {
+ wellset.emplace_back(well);
+ }
+
+ ix += 1;
+ }
+
+ return wellset;
+}
+
+Opm::SegmentMatcher::SegmentSet::WellSegmentRange
+Opm::SegmentMatcher::SegmentSet::segments(std::string_view well) const
+{
+ using Ix = std::vector::size_type;
+
+ // Get 'well's insertion index from list sorted alphabetically on well
+ // names. Client must call establishNameLookupIndex() prior to calling
+ // segments(string_view).
+
+ auto ixPos =
+ std::lower_bound(this->wellNameIndex_.begin(), this->wellNameIndex_.end(),
+ well, [this](const Ix i, std::string_view wellname)
+ {
+ return this->wells_[i] < wellname;
+ });
+
+ if ((ixPos == this->wellNameIndex_.end()) || (this->wells_[*ixPos] != well)) {
+ return this->segments(this->numWells());
+ }
+ else {
+ return this->segments(*ixPos);
+ }
+}
+
+Opm::SegmentMatcher::SegmentSet::WellSegmentRange
+Opm::SegmentMatcher::SegmentSet::segments(const std::size_t well) const
+{
+ if (well >= this->numWells()) {
+ return {};
+ }
+
+ const auto first = this->segmentStart_[well + 0];
+ const auto last = this->segmentStart_[well + 1];
+
+ return {
+ this->segments_.begin() + first,
+ this->segments_.begin() + last,
+ this->wells_[well]
+ };
+}
+
+void Opm::SegmentMatcher::SegmentSet::establishNameLookupIndex()
+{
+ using Ix = std::vector::size_type;
+
+ // Sort well insertion/order indices alphabetically on well names.
+ // Enables using std::lobwer_bound() in segments(well).
+
+ this->wellNameIndex_.resize(this->wells_.size());
+ std::iota(this->wellNameIndex_.begin(), this->wellNameIndex_.end(), Ix{0});
+ std::sort(this->wellNameIndex_.begin(), this->wellNameIndex_.end(),
+ [this](const Ix i1, const Ix i2)
+ {
+ return this->wells_[i1] < this->wells_[i2];
+ });
+}
+
+void Opm::SegmentMatcher::SegmentSet::addWellSegments(const std::string& well,
+ const std::vector& segments)
+{
+ assert (! segments.empty());
+
+ this->wells_.push_back(well);
+
+ // Note that segmentStart_.push_back() must be after segments_.insert()
+ // in order to maintain CSR invariant.
+ this->segments_.insert(this->segments_.end(), segments.begin(), segments.end());
+ this->segmentStart_.push_back(this->segments_.size());
+}
+
+// ---------------------------------------------------------------------------
+
+Opm::SegmentMatcher::SegmentMatcher(const ScheduleState& mswInputData)
+ : pImpl_{ new Impl { mswInputData } }
+{}
+
+Opm::SegmentMatcher::SegmentMatcher(SegmentMatcher&& rhs)
+ : pImpl_{ std::move(rhs.pImpl_) }
+{}
+
+Opm::SegmentMatcher&
+Opm::SegmentMatcher::operator=(SegmentMatcher&& rhs)
+{
+ this->pImpl_ = std::move(rhs.pImpl_);
+ return *this;
+}
+
+Opm::SegmentMatcher::~SegmentMatcher() = default;
+
+Opm::SegmentMatcher::SegmentSet
+Opm::SegmentMatcher::findSegments(const SetDescriptor& segments) const
+{
+ auto segSet = this->pImpl_->
+ findSegments(segments,
+ [](const std::string& well,
+ const std::vector& well_segments,
+ SegmentSet& seg_set)
+ {
+ seg_set.addWellSegments(well, well_segments);
+ });
+
+ segSet.establishNameLookupIndex();
+
+ return segSet;
+}
diff --git a/tests/test_SegmentMatcher.cpp b/tests/test_SegmentMatcher.cpp
new file mode 100644
index 000000000..b07952622
--- /dev/null
+++ b/tests/test_SegmentMatcher.cpp
@@ -0,0 +1,1592 @@
+/*
+ Copyright 2022 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 Segment_Matcher
+
+#include
+
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+BOOST_AUTO_TEST_SUITE(Set_Descriptor)
+
+BOOST_AUTO_TEST_CASE(Default)
+{
+ const auto request = Opm::SegmentMatcher::SetDescriptor{};
+
+ BOOST_CHECK_MESSAGE(! request.segmentNumber().has_value(),
+ "Defaulted SetDescriptor must NOT "
+ "have a specific segment number");
+
+ BOOST_CHECK_MESSAGE(! request.wellNames().has_value(),
+ "Defaulted SetDescriptor must NOT "
+ "have a specific well name pattern");
+}
+
+BOOST_AUTO_TEST_SUITE(Segment_Number)
+
+BOOST_AUTO_TEST_SUITE(Integer_Overload)
+
+BOOST_AUTO_TEST_CASE(Specific)
+{
+ auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber(123);
+
+ BOOST_REQUIRE_MESSAGE(request.segmentNumber().has_value(),
+ "Assigned SetDescriptor must "
+ "have a specific segment number");
+
+ BOOST_CHECK_EQUAL(request.segmentNumber().value(), 123);
+
+ request.segmentNumber(1729);
+ BOOST_CHECK_EQUAL(request.segmentNumber().value(), 1729);
+}
+
+BOOST_AUTO_TEST_CASE(NonPositive)
+{
+ auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber(0);
+
+ BOOST_CHECK_MESSAGE(! request.segmentNumber().has_value(),
+ "Zero segment number must NOT "
+ "have a specifc segment number "
+ "in the final descriptor");
+
+ request.segmentNumber(-1);
+
+ BOOST_CHECK_MESSAGE(! request.segmentNumber().has_value(),
+ "Negative segment number must NOT "
+ "have a specifc segment number "
+ "in the final descriptor");
+}
+
+BOOST_AUTO_TEST_CASE(Positve_To_Negative)
+{
+ auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber(11);
+
+ BOOST_REQUIRE_MESSAGE(request.segmentNumber().has_value(),
+ "Assigned SetDescriptor must "
+ "have a specific segment number");
+
+ BOOST_CHECK_EQUAL(request.segmentNumber().value(), 11);
+
+ request.segmentNumber(-1);
+
+ BOOST_CHECK_MESSAGE(! request.segmentNumber().has_value(),
+ "Negative segment number must NOT "
+ "have a specifc segment number "
+ "in the final descriptor");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Integer_Overload
+
+// ---------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE(StringView_Overload)
+
+BOOST_AUTO_TEST_CASE(Specific)
+{
+ using namespace std::literals;
+
+ auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber("123"sv);
+
+ BOOST_REQUIRE_MESSAGE(request.segmentNumber().has_value(),
+ "Assigned SetDescriptor must "
+ "have a specific segment number");
+
+ BOOST_CHECK_EQUAL(request.segmentNumber().value(), 123);
+
+ request.segmentNumber("'1729'"sv);
+ BOOST_CHECK_EQUAL(request.segmentNumber().value(), 1729);
+}
+
+BOOST_AUTO_TEST_CASE(NonPositive)
+{
+ using namespace std::literals;
+
+ auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber("0"sv);
+
+ BOOST_CHECK_MESSAGE(! request.segmentNumber().has_value(),
+ "Zero segment number must NOT "
+ "have a specifc segment number "
+ "in the final descriptor");
+
+ request.segmentNumber("'-1'");
+
+ BOOST_CHECK_MESSAGE(! request.segmentNumber().has_value(),
+ "Negative segment number must NOT "
+ "have a specifc segment number "
+ "in the final descriptor");
+}
+
+BOOST_AUTO_TEST_CASE(Asterisk)
+{
+ using namespace std::literals;
+
+ auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber("*"sv);
+
+ BOOST_CHECK_MESSAGE(! request.segmentNumber().has_value(),
+ "Defaulted segment number must NOT "
+ "have a specifc segment number "
+ "in the final descriptor");
+}
+
+BOOST_AUTO_TEST_CASE(Positve_To_Negative)
+{
+ using namespace std::literals;
+
+ auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber("'11'"sv);
+
+ BOOST_REQUIRE_MESSAGE(request.segmentNumber().has_value(),
+ "Assigned SetDescriptor must "
+ "have a specific segment number");
+
+ BOOST_CHECK_EQUAL(request.segmentNumber().value(), 11);
+
+ request.segmentNumber("-1"sv);
+
+ BOOST_CHECK_MESSAGE(! request.segmentNumber().has_value(),
+ "Negative segment number must NOT "
+ "have a specifc segment number "
+ "in the final descriptor");
+}
+
+BOOST_AUTO_TEST_CASE(Invalid)
+{
+ using namespace std::literals;
+
+ BOOST_CHECK_THROW(const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber("'1*'"sv), std::invalid_argument);
+
+ BOOST_CHECK_THROW(const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber("'123;'"sv), std::invalid_argument);
+
+ BOOST_CHECK_THROW(const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber("x"sv), std::invalid_argument);
+
+ BOOST_CHECK_THROW(const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber("-123-"sv), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(Leading_And_Trailing_Blanks)
+{
+ using namespace std::literals;
+
+ BOOST_CHECK_THROW(const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber(" 123 "sv), std::invalid_argument);
+
+ BOOST_CHECK_THROW(const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber("' 1729'"sv), std::invalid_argument);
+
+ BOOST_CHECK_THROW(const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber("'27 '"sv), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // StringView_Overload
+
+BOOST_AUTO_TEST_SUITE_END() // Segment_Number
+
+// ---------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE(Well_Name_Pattern)
+
+BOOST_AUTO_TEST_CASE(Single_Well)
+{
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP01");
+
+ BOOST_REQUIRE_MESSAGE(request.wellNames().has_value(),
+ "Assigned SetDescriptor must "
+ "have a specific well name");
+
+ BOOST_CHECK_EQUAL(request.wellNames().value(), "OP01");
+}
+
+BOOST_AUTO_TEST_CASE(Well_Pattern)
+{
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP01*");
+
+ BOOST_REQUIRE_MESSAGE(request.wellNames().has_value(),
+ "Assigned SetDescriptor must "
+ "have a specific well name");
+
+ BOOST_CHECK_EQUAL(request.wellNames().value(), "OP01*");
+}
+
+BOOST_AUTO_TEST_CASE(Asterisk)
+{
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("*");
+
+ BOOST_CHECK_MESSAGE(request.wellNames().has_value(),
+ "Assigned SetDescriptor must "
+ "have a specific well name");
+
+ BOOST_CHECK_EQUAL(request.wellNames().value(), "*");
+}
+
+BOOST_AUTO_TEST_CASE(Invalid_Characters_Unchecked)
+{
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("Ab+C;E^F/");
+
+ BOOST_CHECK_MESSAGE(request.wellNames().has_value(),
+ "Assigned SetDescriptor must "
+ "have a specific well name");
+
+ BOOST_CHECK_EQUAL(request.wellNames().value(), "Ab+C;E^F/");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Well_Name_Pattern
+
+BOOST_AUTO_TEST_SUITE_END() // Set_Descriptor
+
+// ===========================================================================
+
+BOOST_AUTO_TEST_SUITE(Matcher)
+
+namespace {
+ Opm::Segment makeSegment(const int segmentNumber)
+ {
+ return { segmentNumber, 1, 1, 1.0, 0.0, 0.5, 0.01, 0.25, 1.23, true, 0.0, 0.0 };
+ }
+
+ std::shared_ptr makeSegments(const int numSegments)
+ {
+ auto segments = std::vector{};
+ segments.reserve(numSegments);
+
+ for (auto segment = 0; segment < numSegments; ++segment) {
+ segments.push_back(makeSegment(segment + 1));
+ }
+
+ return std::make_shared
+ (Opm::WellSegments::CompPressureDrop::HFA, segments);
+ }
+
+ Opm::Well makeProducerWell(const std::string& wname,
+ const std::size_t insert,
+ const int numSegments)
+ {
+ auto w = Opm::Well {
+ wname, "G", 0, insert, 1, 2, {},
+ Opm::WellType { true, Opm::Phase::OIL }, // Oil producer
+ Opm::Well::ProducerCMode::ORAT,
+ Opm::Connection::Order::INPUT,
+ Opm::UnitSystem::newMETRIC(),
+ -3.0e+20, // UDQ undefined
+ 0.0, true, true, 0,
+ Opm::Well::GasInflowEquation::STD
+ };
+
+ if (numSegments > 0) {
+ w.updateSegments(makeSegments(numSegments));
+ }
+
+ return w;
+ }
+
+ Opm::Well makeInjectionWell(const std::string& wname,
+ const std::size_t insert,
+ const int numSegments)
+ {
+ auto w = Opm::Well {
+ wname, "G", 0, insert, 1, 2, {},
+ Opm::WellType { false, Opm::Phase::GAS }, // Gas injector
+ Opm::Well::ProducerCMode::ORAT,
+ Opm::Connection::Order::INPUT,
+ Opm::UnitSystem::newMETRIC(),
+ -3.0e+20, // UDQ undefined
+ 0.0, true, true, 0,
+ Opm::Well::GasInflowEquation::STD
+ };
+
+ if (numSegments > 0) {
+ w.updateSegments(makeSegments(numSegments));
+ }
+
+ return w;
+ }
+
+ // Collection of wells
+ // OP-01: Producer, MSW, 20 segments (1 .. 20)
+ // OP-02: Producer, MSW, 5 segments (1 .. 5)
+ // OP-06: Producer, Standard well
+ // OPROD: Producer, MSW, 2 segments (1 .. 2)
+ //
+ // GI-01: Injector, MSW, 10 segments (1 .. 10)
+ // GI-08: Injector, Standard well
+ // I-45: Injector, MSW, 1 segment (1)
+ Opm::ScheduleState dynamicInputData()
+ {
+ auto block = Opm::ScheduleState { Opm::TimeService::now() };
+
+ block.wells.update(makeProducerWell("OP-01", 0, 20));
+ block.wells.update(makeProducerWell("OP-02", 1, 5));
+ block.wells.update(makeProducerWell("OP-06", 2, 0));
+ block.wells.update(makeProducerWell("OPROD", 3, 2));
+ block.wells.update(makeInjectionWell("GI-01", 4, 10));
+ block.wells.update(makeInjectionWell("GI-08", 5, 0));
+ block.wells.update(makeInjectionWell("I-45", 6, 1));
+
+ block.well_order.update(Opm::NameOrder {
+ "OP-01", "OP-02", "OP-06", "OPROD", "GI-01", "GI-08", "I-45",
+ });
+
+ return block;
+ }
+}
+
+BOOST_AUTO_TEST_SUITE(Indexed_Lookup)
+
+BOOST_AUTO_TEST_CASE(Single_Well_Single_Segment)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP-01").segmentNumber(17);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE( segSet.isScalar(), "Resulting segment set must be scalar");
+
+ BOOST_CHECK_EQUAL(segSet.numWells(), std::size_t{1});
+
+ const auto expectWells = std::vector { "OP-01" };
+ const auto expectSeg = std::vector { 17 };
+ const auto segments = segSet.segments(0);
+
+ BOOST_CHECK_EQUAL(segments.well(), expectWells[0]);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP-02").segmentNumber("5");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE( segSet.isScalar(), "Resulting segment set must be scalar");
+
+ BOOST_CHECK_EQUAL(segSet.numWells(), std::size_t{1});
+
+ const auto expectWells = std::vector { "OP-02" };
+ const auto expectSeg = std::vector { 5 };
+ const auto segments = segSet.segments(0);
+
+ BOOST_CHECK_EQUAL(segments.well(), expectWells[0]);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Single_Well_All_Segments)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP-01");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ BOOST_CHECK_EQUAL(segSet.numWells(), std::size_t{1});
+
+ const auto expectWells = std::vector { "OP-01" };
+ const auto expectSeg = std::vector {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ };
+ const auto segments = segSet.segments(0);
+
+ BOOST_CHECK_EQUAL(segments.well(), expectWells[0]);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP-02").segmentNumber("*");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ BOOST_CHECK_EQUAL(segSet.numWells(), std::size_t{1});
+
+ const auto expectWells = std::vector { "OP-02" };
+ const auto expectSeg = std::vector { 1, 2, 3, 4, 5, };
+ const auto segments = segSet.segments(0);
+
+ BOOST_CHECK_EQUAL(segments.well(), expectWells[0]);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("GI-01").segmentNumber("'-1'");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must be scalar");
+
+ const auto expectWells = std::vector { "GI-01" };
+ const auto expectSeg = std::vector {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ };
+ const auto segments = segSet.segments(0);
+
+ BOOST_CHECK_EQUAL(segments.well(), expectWells[0]);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Single_Well_Missing_Segments)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP-01").segmentNumber(42);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(segSet.empty(), "Resulting segment set must be empty");
+ }
+
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP-02").segmentNumber("'6'");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(segSet.empty(), "Resulting segment set must be empty");
+ }
+}
+
+BOOST_AUTO_TEST_CASE(All_Wells_Single_Segment)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ // Case 1: Unset wellNames(), filtered down to MS wells
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber(1);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "OP-02", "OPROD", "GI-01", "I-45",
+ };
+
+ BOOST_CHECK_EQUAL(segSet.numWells(), expectWells.size());
+
+ for (auto well = 0*segSet.numWells(); well < segSet.numWells(); ++well) {
+ const auto expectSeg = std::vector { 1 };
+ const auto segments = segSet.segments(well);
+
+ BOOST_CHECK_EQUAL(segments.well(), expectWells[well]);
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+
+ // Case 2: Pattern matching all wells, filtered down to MS wells
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("*").segmentNumber("'1'");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "OP-02", "OPROD", "GI-01", "I-45",
+ };
+
+ BOOST_CHECK_EQUAL(segSet.numWells(), expectWells.size());
+
+ for (auto well = 0*segSet.numWells(); well < segSet.numWells(); ++well) {
+ const auto expectSeg = std::vector { 1 };
+ const auto segments = segSet.segments(well);
+
+ BOOST_CHECK_EQUAL(segments.well(), expectWells[well]);
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(All_Wells_Single_Segment_Scalar)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber(14);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE( segSet.isScalar(), "Resulting segment set must be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01",
+ };
+
+ BOOST_CHECK_EQUAL(segSet.numWells(), expectWells.size());
+
+ for (auto well = 0*segSet.numWells(); well < segSet.numWells(); ++well) {
+ const auto expectSeg = std::vector { 14 };
+ const auto segments = segSet.segments(well);
+
+ BOOST_CHECK_EQUAL(segments.well(), expectWells[well]);
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(All_Wells_Partially_Missing_Single_Segment)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ // Case 1: Unset wellNames(), filtered down to MS wells whose segment
+ // set contains segment 2.
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber(2);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "OP-02", "OPROD", "GI-01"
+ };
+
+ BOOST_CHECK_EQUAL(segSet.numWells(), expectWells.size());
+
+ for (auto well = 0*segSet.numWells(); well < segSet.numWells(); ++well) {
+ const auto expectSeg = std::vector { 2 };
+ const auto segments = segSet.segments(well);
+
+ BOOST_CHECK_EQUAL(segments.well(), expectWells[well]);
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+
+ // Case 2: Pattern matching all wells, filtered down to MS wells whose
+ // segment set contains segment 7.
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("*").segmentNumber("'7'");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "GI-01",
+ };
+
+ BOOST_CHECK_EQUAL(segSet.numWells(), expectWells.size());
+
+ for (auto well = 0*segSet.numWells(); well < segSet.numWells(); ++well) {
+ const auto expectSeg = std::vector { 7 };
+ const auto segments = segSet.segments(well);
+
+ BOOST_CHECK_EQUAL(segments.well(), expectWells[well]);
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(All_Wells_All_Segments)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ // Case 1: Unset wellNames(), unset segmentNumber(), filtered down to
+ // all segments in all MS wells.
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{};
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "OP-02", "OPROD", "GI-01", "I-45",
+ };
+
+ BOOST_CHECK_EQUAL(segSet.numWells(), expectWells.size());
+
+ // OP-01
+ {
+ const auto expectSeg = std::vector {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ };
+ const auto segments = segSet.segments(0);
+ BOOST_CHECK_EQUAL(segments.well(), "OP-01");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // OP-02
+ {
+ const auto expectSeg = std::vector { 1, 2, 3, 4, 5, };
+ const auto segments = segSet.segments(1);
+ BOOST_CHECK_EQUAL(segments.well(), "OP-02");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // OPROD
+ {
+ const auto expectSeg = std::vector { 1, 2, };
+ const auto segments = segSet.segments(2);
+ BOOST_CHECK_EQUAL(segments.well(), "OPROD");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // GI-01
+ {
+ const auto expectSeg = std::vector {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ };
+ const auto segments = segSet.segments(3);
+ BOOST_CHECK_EQUAL(segments.well(), "GI-01");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // I-45
+ {
+ const auto expectSeg = std::vector { 1 };
+ const auto segments = segSet.segments(4);
+ BOOST_CHECK_EQUAL(segments.well(), "I-45");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+
+ // Case 2: Pattern matching all wells, unset segmentNumber(), filtered
+ // down to all segments in all MS wells..
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("*");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "OP-02", "OPROD", "GI-01", "I-45",
+ };
+
+ BOOST_CHECK_EQUAL(segSet.numWells(), expectWells.size());
+
+ // OP-01
+ {
+ const auto expectSeg = std::vector {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ };
+ const auto segments = segSet.segments(0);
+ BOOST_CHECK_EQUAL(segments.well(), "OP-01");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // OP-02
+ {
+ const auto expectSeg = std::vector { 1, 2, 3, 4, 5, };
+ const auto segments = segSet.segments(1);
+ BOOST_CHECK_EQUAL(segments.well(), "OP-02");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // OPROD
+ {
+ const auto expectSeg = std::vector { 1, 2, };
+ const auto segments = segSet.segments(2);
+ BOOST_CHECK_EQUAL(segments.well(), "OPROD");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // GI-01
+ {
+ const auto expectSeg = std::vector {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ };
+ const auto segments = segSet.segments(3);
+ BOOST_CHECK_EQUAL(segments.well(), "GI-01");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // I-45
+ {
+ const auto expectSeg = std::vector { 1 };
+ const auto segments = segSet.segments(4);
+ BOOST_CHECK_EQUAL(segments.well(), "I-45");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Select_Wells_Single_Segment)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ // Case 1: Selected wells, specific segmentNumber(), filtered down to
+ // those MS wells which match the pattern and which have that segment.
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP-*").segmentNumber(3);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "OP-02",
+ };
+
+ BOOST_CHECK_EQUAL(segSet.numWells(), expectWells.size());
+
+ for (auto well = 0*segSet.numWells(); well < segSet.numWells(); ++well) {
+ const auto expectSeg = std::vector { 3 };
+ const auto segments = segSet.segments(well);
+
+ BOOST_CHECK_EQUAL(segments.well(), expectWells[well]);
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+
+ // Case 2: Selected wells, specific segmentNumber(), filtered down to
+ // those MS wells which match the pattern and which have that segment.
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("I*").segmentNumber(1);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE( segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "I-45",
+ };
+
+ BOOST_CHECK_EQUAL(segSet.numWells(), expectWells.size());
+
+ for (auto well = 0*segSet.numWells(); well < segSet.numWells(); ++well) {
+ const auto expectSeg = std::vector { 1 };
+ const auto segments = segSet.segments(well);
+
+ BOOST_CHECK_EQUAL(segments.well(), expectWells[well]);
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Select_Wells_Partially_Missing_Segment)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ // Selected wells, specific segmentNumber(), filtered down to those MS
+ // wells which match the pattern and which have that segment.
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP*").segmentNumber(3);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "OP-02",
+ };
+
+ BOOST_CHECK_EQUAL(segSet.numWells(), expectWells.size());
+
+ for (auto well = 0*segSet.numWells(); well < segSet.numWells(); ++well) {
+ const auto expectSeg = std::vector { 3 };
+ const auto segments = segSet.segments(well);
+
+ BOOST_CHECK_EQUAL(segments.well(), expectWells[well]);
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Select_Wells_All_Segments)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ // Producer wells, unset segmentNumber(), filtered down to all segments
+ // in those MS producer wells which match the well name pattern.
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP*");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "OP-02", "OPROD",
+ };
+
+ BOOST_CHECK_EQUAL(segSet.numWells(), expectWells.size());
+
+ // OP-01
+ {
+ const auto expectSeg = std::vector {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ };
+ const auto segments = segSet.segments(0);
+ BOOST_CHECK_EQUAL(segments.well(), "OP-01");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // OP-02
+ {
+ const auto expectSeg = std::vector { 1, 2, 3, 4, 5, };
+ const auto segments = segSet.segments(1);
+ BOOST_CHECK_EQUAL(segments.well(), "OP-02");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // OPROD
+ {
+ const auto expectSeg = std::vector { 1, 2, };
+ const auto segments = segSet.segments(2);
+ BOOST_CHECK_EQUAL(segments.well(), "OPROD");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+
+ // Selected producer wells, defaulted segmentNumber(), filtered down to
+ // all segments in those MS producer wells which match the well name
+ // pattern.
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OPR*").segmentNumber("'-1'");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OPROD",
+ };
+
+ BOOST_CHECK_EQUAL(segSet.numWells(), expectWells.size());
+
+ // OPROD
+ {
+ const auto expectSeg = std::vector { 1, 2, };
+ const auto segments = segSet.segments(0);
+ BOOST_CHECK_EQUAL(segments.well(), "OPROD");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Select_Wells_Missing_Segments)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP*").segmentNumber(42);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(segSet.empty(), "Resulting segment set must be empty");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Indexed_Lookup
+
+// ---------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE(WellName_Lookup)
+
+BOOST_AUTO_TEST_CASE(Single_Well_Single_Segment)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP-01").segmentNumber(17);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE( segSet.isScalar(), "Resulting segment set must be scalar");
+
+ const auto expectWells = std::vector { "OP-01" };
+ const auto wells = segSet.wells();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wells .begin(), wells .end(),
+ expectWells.begin(), expectWells.end());
+
+ const auto expectSeg = std::vector { 17 };
+ const auto segments = segSet.segments(wells[0]);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP-02").segmentNumber("5");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE( segSet.isScalar(), "Resulting segment set must be scalar");
+
+ const auto expectWells = std::vector { "OP-02" };
+ const auto wells = segSet.wells();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wells .begin(), wells .end(),
+ expectWells.begin(), expectWells.end());
+
+ const auto expectSeg = std::vector { 5 };
+ const auto segments = segSet.segments(wells[0]);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Single_Well_All_Segments)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP-01");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector { "OP-01" };
+ const auto wells = segSet.wells();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wells .begin(), wells .end(),
+ expectWells.begin(), expectWells.end());
+
+ const auto expectSeg = std::vector {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ };
+ const auto segments = segSet.segments(wells[0]);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP-02").segmentNumber("*");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector { "OP-02" };
+ const auto wells = segSet.wells();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wells .begin(), wells .end(),
+ expectWells.begin(), expectWells.end());
+
+ const auto expectSeg = std::vector { 1, 2, 3, 4, 5, };
+ const auto segments = segSet.segments(wells[0]);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("GI-01").segmentNumber("'-1'");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must be scalar");
+
+ const auto expectWells = std::vector { "GI-01" };
+ const auto wells = segSet.wells();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wells .begin(), wells .end(),
+ expectWells.begin(), expectWells.end());
+
+ const auto expectSeg = std::vector {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ };
+ const auto segments = segSet.segments(wells[0]);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(All_Wells_Single_Segment)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ // Case 1: Unset wellNames(), filtered down to MS wells
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber(1);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "OP-02", "OPROD", "GI-01", "I-45",
+ };
+ const auto wells = segSet.wells();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wells .begin(), wells .end(),
+ expectWells.begin(), expectWells.end());
+
+ for (const auto& well : wells) {
+ const auto expectSeg = std::vector { 1 };
+ const auto segments = segSet.segments(well);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+
+ // Case 2: Pattern matching all wells, filtered down to MS wells
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("*").segmentNumber("'1'");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "OP-02", "OPROD", "GI-01", "I-45",
+ };
+ const auto wells = segSet.wells();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wells .begin(), wells .end(),
+ expectWells.begin(), expectWells.end());
+
+ for (const auto& well : wells) {
+ const auto expectSeg = std::vector { 1 };
+ const auto segments = segSet.segments(well);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(All_Wells_Single_Segment_Scalar)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber(14);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE( segSet.isScalar(), "Resulting segment set must be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01",
+ };
+ const auto wells = segSet.wells();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wells .begin(), wells .end(),
+ expectWells.begin(), expectWells.end());
+
+ for (const auto& well : wells) {
+ const auto expectSeg = std::vector { 14 };
+ const auto segments = segSet.segments(well);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(All_Wells_Partially_Missing_Single_Segment)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ // Case 1: Unset wellNames(), filtered down to MS wells whose segment
+ // set contains segment 2.
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber(2);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "OP-02", "OPROD", "GI-01"
+ };
+ const auto wells = segSet.wells();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wells .begin(), wells .end(),
+ expectWells.begin(), expectWells.end());
+
+ for (const auto& well : wells) {
+ const auto expectSeg = std::vector { 2 };
+ const auto segments = segSet.segments(well);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+
+ // Case 2: Pattern matching all wells, filtered down to MS wells whose
+ // segment set contains segment 7.
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("*").segmentNumber("'7'");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "GI-01",
+ };
+ const auto wells = segSet.wells();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wells .begin(), wells .end(),
+ expectWells.begin(), expectWells.end());
+
+ for (const auto& well : wells) {
+ const auto expectSeg = std::vector { 7 };
+ const auto segments = segSet.segments(well);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(All_Wells_All_Segments)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ // Case 1: Unset wellNames(), unset segmentNumber(), filtered down to
+ // all segments in all MS wells.
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{};
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "OP-02", "OPROD", "GI-01", "I-45",
+ };
+ const auto wells = segSet.wells();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wells .begin(), wells .end(),
+ expectWells.begin(), expectWells.end());
+
+ // OP-01
+ {
+ const auto expectSeg = std::vector {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ };
+ const auto segments = segSet.segments("OP-01");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // OP-02
+ {
+ const auto expectSeg = std::vector { 1, 2, 3, 4, 5, };
+ const auto segments = segSet.segments("OP-02");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // OPROD
+ {
+ const auto expectSeg = std::vector { 1, 2, };
+ const auto segments = segSet.segments("OPROD");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // GI-01
+ {
+ const auto expectSeg = std::vector {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ };
+ const auto segments = segSet.segments("GI-01");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // I-45
+ {
+ const auto expectSeg = std::vector { 1 };
+ const auto segments = segSet.segments("I-45");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+
+ // Case 2: Pattern matching all wells, unset segmentNumber(), filtered
+ // down to all segments in all MS wells..
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("*");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "OP-02", "OPROD", "GI-01", "I-45",
+ };
+ const auto wells = segSet.wells();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wells .begin(), wells .end(),
+ expectWells.begin(), expectWells.end());
+
+ // OP-01
+ {
+ const auto expectSeg = std::vector {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ };
+ const auto segments = segSet.segments("OP-01");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // OP-02
+ {
+ const auto expectSeg = std::vector { 1, 2, 3, 4, 5, };
+ const auto segments = segSet.segments("OP-02");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // OPROD
+ {
+ const auto expectSeg = std::vector { 1, 2, };
+ const auto segments = segSet.segments("OPROD");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // GI-01
+ {
+ const auto expectSeg = std::vector {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ };
+ const auto segments = segSet.segments("GI-01");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // I-45
+ {
+ const auto expectSeg = std::vector { 1 };
+ const auto segments = segSet.segments("I-45");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(All_Wells_Missing_Segments)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .segmentNumber(42);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(segSet.empty(), "Resulting segment set must be empty");
+}
+
+BOOST_AUTO_TEST_CASE(Select_Wells_Single_Segment)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ // Case 1: Selected wells, specific segmentNumber(), filtered down to
+ // those MS wells which match the pattern and which have that segment.
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP-*").segmentNumber(3);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "OP-02",
+ };
+ const auto wells = segSet.wells();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wells .begin(), wells .end(),
+ expectWells.begin(), expectWells.end());
+
+ for (const auto& well : wells) {
+ const auto expectSeg = std::vector { 3 };
+ const auto segments = segSet.segments(well);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+
+ // Case 2: Selected wells, specific segmentNumber(), filtered down to
+ // those MS wells which match the pattern and which have that segment.
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("I*").segmentNumber(1);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE( segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "I-45",
+ };
+ const auto wells = segSet.wells();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wells .begin(), wells .end(),
+ expectWells.begin(), expectWells.end());
+
+ for (const auto& well : wells) {
+ const auto expectSeg = std::vector { 1 };
+ const auto segments = segSet.segments(well);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Select_Wells_Partially_Missing_Segment)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ // Selected wells, specific segmentNumber(), filtered down to those MS
+ // wells which match the pattern and which have that segment.
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP*").segmentNumber(3);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "OP-02",
+ };
+ const auto wells = segSet.wells();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wells .begin(), wells .end(),
+ expectWells.begin(), expectWells.end());
+
+ for (const auto& well : wells) {
+ const auto expectSeg = std::vector { 3 };
+ const auto segments = segSet.segments(well);
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Select_Wells_All_Segments)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ // Producer wells, unset segmentNumber(), filtered down to all segments
+ // in those MS producer wells which match the well name pattern.
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OP*");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OP-01", "OP-02", "OPROD",
+ };
+ const auto wells = segSet.wells();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wells .begin(), wells .end(),
+ expectWells.begin(), expectWells.end());
+
+ // OP-01
+ {
+ const auto expectSeg = std::vector {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ };
+ const auto segments = segSet.segments("OP-01");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // OP-02
+ {
+ const auto expectSeg = std::vector { 1, 2, 3, 4, 5, };
+ const auto segments = segSet.segments("OP-02");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+
+ // OPROD
+ {
+ const auto expectSeg = std::vector { 1, 2, };
+ const auto segments = segSet.segments("OPROD");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+
+ // Selected producer wells, defaulted segmentNumber(), filtered down to
+ // all segments in those MS producer wells which match the well name
+ // pattern.
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OPR*").segmentNumber("'-1'");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(! segSet.empty(), "Resulting segment set must not be empty");
+ BOOST_CHECK_MESSAGE(! segSet.isScalar(), "Resulting segment set must not be scalar");
+
+ const auto expectWells = std::vector {
+ "OPROD",
+ };
+ const auto wells = segSet.wells();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wells .begin(), wells .end(),
+ expectWells.begin(), expectWells.end());
+
+ // OPROD
+ {
+ const auto expectSeg = std::vector { 1, 2, };
+ const auto segments = segSet.segments("OPROD");
+ BOOST_CHECK_EQUAL_COLLECTIONS(segments .begin(), segments .end(),
+ expectSeg.begin(), expectSeg.end());
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Missing_Wells_Specific_Segments)
+{
+ // Note: Lifetime of input data must exceed that of matcher object
+ const auto mswInputData = dynamicInputData();
+ const auto matcher = Opm::SegmentMatcher { mswInputData };
+
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("Hello").segmentNumber(2);
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(segSet.empty(), "Resulting segment set must be empty");
+ }
+
+ {
+ const auto request = Opm::SegmentMatcher::SetDescriptor{}
+ .wellNames("OIL*").segmentNumber("11");
+
+ const auto segSet = matcher.findSegments(request);
+ BOOST_CHECK_MESSAGE(segSet.empty(), "Resulting segment set must be empty");
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // WellName_Lookup
+
+BOOST_AUTO_TEST_SUITE_END() // Matcher