Add Facility For Identifying Specific Well Segments
The primary use case is identifying UDQ sets when the model defines UDQs at the segment level (SU*). The call site will construct a SegmentMatcher object backed by a specific ScheduleState, and then segment-level UDQs will match against this to determine the correct UDQ set on which to define the quantity.
This commit is contained in:
parent
c8daba28ad
commit
1a1ba1f7a1
@ -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
|
||||
|
354
opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp
Normal file
354
opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SEGMENT_MATCHER_HPP
|
||||
#define SEGMENT_MATCHER_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
/// \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<int>::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<int>::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<std::string_view> 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<std::string> wells_{};
|
||||
|
||||
/// Name-to-index lookup table.
|
||||
///
|
||||
/// User, i.e., the SegmentMatcher, must call \code
|
||||
/// establishNameLookupIndex() \endcode to prepare the lookup table.
|
||||
std::vector<std::vector<std::string>::size_type> wellNameIndex_{};
|
||||
|
||||
/// CSR start pointers for MS wells' segments.
|
||||
std::vector<std::vector<int>::size_type> segmentStart_{};
|
||||
|
||||
/// All segments covered by this result set. Structured by \c
|
||||
/// segmentStart_.
|
||||
std::vector<int> 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<int>& 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<int>& 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<std::string>& wellNames() const
|
||||
{
|
||||
return this->wellNamePattern_;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Request's well name or well name pattern. Unset if request
|
||||
/// applies to all MS wells.
|
||||
std::optional<std::string> wellNamePattern_{};
|
||||
|
||||
/// Request's segment number. Unset if request applies to all
|
||||
/// segments of pertinent well set.
|
||||
std::optional<int> 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<Impl> pImpl_{};
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // SEGMENT_MATCHER_HPP
|
405
src/opm/input/eclipse/Schedule/MSW/SegmentMatcher.cpp
Normal file
405
src/opm/input/eclipse/Schedule/MSW/SegmentMatcher.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <opm/input/eclipse/Schedule/MSW/SegmentMatcher.hpp>
|
||||
|
||||
#include <opm/input/eclipse/Schedule/MSW/Segment.hpp>
|
||||
#include <opm/input/eclipse/Schedule/MSW/WellSegments.hpp>
|
||||
#include <opm/input/eclipse/Schedule/ScheduleState.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Well/WellMatcher.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <charconv>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <regex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
class Opm::SegmentMatcher::Impl
|
||||
{
|
||||
public:
|
||||
explicit Impl(const ScheduleState& mswInputData)
|
||||
: mswInputData_{ std::cref(mswInputData) }
|
||||
{}
|
||||
|
||||
template <class AddWellSegments>
|
||||
SegmentSet findSegments(const SetDescriptor& request,
|
||||
AddWellSegments addSegments) const;
|
||||
|
||||
private:
|
||||
std::reference_wrapper<const ScheduleState> mswInputData_;
|
||||
|
||||
std::vector<std::string>
|
||||
candidateWells(const std::optional<std::string>& wellNamePattern) const;
|
||||
|
||||
std::vector<std::string>
|
||||
candidateWells(const std::string& wellNamePattern) const;
|
||||
|
||||
std::vector<std::string> candidateWells() const;
|
||||
|
||||
std::vector<std::string>
|
||||
candidateWells(const std::vector<std::string>& allWells) const;
|
||||
|
||||
std::vector<int>
|
||||
matchingSegments(const std::string& well,
|
||||
const std::optional<int>& segmentNumber) const;
|
||||
|
||||
std::vector<int>
|
||||
matchingSegments(const std::string& well,
|
||||
const int segmentNumber) const;
|
||||
|
||||
std::vector<int> matchingSegments(const std::string& well) const;
|
||||
};
|
||||
|
||||
template <class AddWellSegments>
|
||||
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<std::string>
|
||||
Opm::SegmentMatcher::Impl::
|
||||
candidateWells(const std::optional<std::string>& wellNamePattern) const
|
||||
{
|
||||
return wellNamePattern.has_value()
|
||||
? this->candidateWells(*wellNamePattern)
|
||||
: this->candidateWells();
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
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<std::string> Opm::SegmentMatcher::Impl::candidateWells() const
|
||||
{
|
||||
// No specific wellname pattern => all MS wells match.
|
||||
return this->candidateWells(this->mswInputData_.get().well_order().names());
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
Opm::SegmentMatcher::Impl::
|
||||
candidateWells(const std::vector<std::string>& allWells) const
|
||||
{
|
||||
auto candidates = std::vector<std::string>{};
|
||||
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<int>
|
||||
Opm::SegmentMatcher::Impl::
|
||||
matchingSegments(const std::string& well,
|
||||
const std::optional<int>& segmentNumber) const
|
||||
{
|
||||
return segmentNumber.has_value()
|
||||
? this->matchingSegments(well, *segmentNumber)
|
||||
: this->matchingSegments(well);
|
||||
}
|
||||
|
||||
std::vector<int>
|
||||
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<int>
|
||||
Opm::SegmentMatcher::Impl::matchingSegments(const std::string& wellname) const
|
||||
{
|
||||
// No specific segment number => All segments match.
|
||||
auto segments = std::vector<int>{};
|
||||
|
||||
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<std::string_view>
|
||||
Opm::SegmentMatcher::SegmentSet::wells() const
|
||||
{
|
||||
auto wellset = std::vector<std::string_view>{};
|
||||
wellset.reserve(this->wells_.size());
|
||||
|
||||
auto ix = std::vector<std::vector<int>::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<std::string>::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<std::string>::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<int>& 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<int>& well_segments,
|
||||
SegmentSet& seg_set)
|
||||
{
|
||||
seg_set.addWellSegments(well, well_segments);
|
||||
});
|
||||
|
||||
segSet.establishNameLookupIndex();
|
||||
|
||||
return segSet;
|
||||
}
|
1592
tests/test_SegmentMatcher.cpp
Normal file
1592
tests/test_SegmentMatcher.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user