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