mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
Merge pull request #5438 from bska/sfunc-consistency-checks
Add New Platform for Saturation Function Consistency Checks
This commit is contained in:
commit
e9591e0a3c
@ -183,6 +183,11 @@ list (APPEND MAIN_SOURCE_FILES
|
||||
opm/simulators/wells/WGState.cpp
|
||||
)
|
||||
|
||||
if (HAVE_ECL_INPUT)
|
||||
list (APPEND MAIN_SOURCE_FILES
|
||||
opm/simulators/utils/satfunc/SatfuncConsistencyChecks.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if (Damaris_FOUND AND MPI_FOUND AND USE_DAMARIS_LIB)
|
||||
list (APPEND MAIN_SOURCE_FILES
|
||||
@ -333,7 +338,10 @@ list (APPEND TEST_SOURCE_FILES
|
||||
)
|
||||
|
||||
if (HAVE_ECL_INPUT)
|
||||
list(APPEND TEST_SOURCE_FILES tests/test_nonnc.cpp)
|
||||
list(APPEND TEST_SOURCE_FILES
|
||||
tests/test_nonnc.cpp
|
||||
tests/test_SatfuncConsistencyChecks.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(MPI_FOUND)
|
||||
@ -675,6 +683,12 @@ if (USE_BDA_BRIDGE)
|
||||
)
|
||||
endif()
|
||||
|
||||
if (HAVE_ECL_INPUT)
|
||||
list (APPEND PUBLIC_HEADER_FILES
|
||||
opm/simulators/utils/satfunc/SatfuncConsistencyChecks.hpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if (Damaris_FOUND AND MPI_FOUND AND USE_DAMARIS_LIB)
|
||||
list (APPEND PUBLIC_HEADER_FILES
|
||||
opm/simulators/utils/DamarisKeywords.hpp
|
||||
|
560
opm/simulators/utils/satfunc/SatfuncConsistencyChecks.cpp
Normal file
560
opm/simulators/utils/satfunc/SatfuncConsistencyChecks.cpp
Normal file
@ -0,0 +1,560 @@
|
||||
/*
|
||||
Copyright 2024 Equinor AS
|
||||
|
||||
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 <config.h>
|
||||
|
||||
#include <opm/simulators/utils/satfunc/SatfuncConsistencyChecks.hpp>
|
||||
|
||||
#include <opm/material/fluidmatrixinteractions/EclEpsScalingPoints.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
// ===========================================================================
|
||||
// Public member functions for SatfuncConsistencyChecks Template
|
||||
// ===========================================================================
|
||||
|
||||
template <typename Scalar>
|
||||
Opm::SatfuncConsistencyChecks<Scalar>::
|
||||
SatfuncConsistencyChecks(std::string_view pointName,
|
||||
const std::size_t numSamplePoints)
|
||||
: pointName_ { pointName }
|
||||
, numSamplePoints_ { numSamplePoints }
|
||||
, formatPointID_ { [](const std::size_t i) { return fmt::format("{}", i); } }
|
||||
{}
|
||||
|
||||
template <typename Scalar>
|
||||
Opm::SatfuncConsistencyChecks<Scalar>::
|
||||
SatfuncConsistencyChecks(SatfuncConsistencyChecks&& rhs)
|
||||
: pointName_ { std::move(rhs.pointName_) }
|
||||
, numSamplePoints_ { rhs.numSamplePoints_ }
|
||||
, formatPointID_ { std::move(rhs.formatPointID_) }
|
||||
, startCheckValues_ { std::move(rhs.startCheckValues_) }
|
||||
, violations_ { std::move(rhs.violations_) }
|
||||
, battery_ { std::move(rhs.battery_) }
|
||||
{}
|
||||
|
||||
template <typename Scalar>
|
||||
Opm::SatfuncConsistencyChecks<Scalar>&
|
||||
Opm::SatfuncConsistencyChecks<Scalar>::operator=(SatfuncConsistencyChecks&& rhs)
|
||||
{
|
||||
this->pointName_ = std::move(rhs.pointName_);
|
||||
this->numSamplePoints_ = rhs.numSamplePoints_;
|
||||
this->formatPointID_ = std::move(rhs.formatPointID_);
|
||||
this->startCheckValues_ = std::move(rhs.startCheckValues_);
|
||||
this->violations_ = std::move(rhs.violations_);
|
||||
this->battery_ = std::move(rhs.battery_);
|
||||
|
||||
this->urbg_.reset();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
void Opm::SatfuncConsistencyChecks<Scalar>::resetCheckSet()
|
||||
{
|
||||
this->startCheckValues_.clear();
|
||||
this->startCheckValues_.push_back(0);
|
||||
|
||||
for (auto& violation : this->violations_) {
|
||||
violation.clear();
|
||||
}
|
||||
|
||||
this->battery_.clear();
|
||||
this->urbg_.reset();
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
void Opm::SatfuncConsistencyChecks<Scalar>::addCheck(std::unique_ptr<Check> check)
|
||||
{
|
||||
this->battery_.push_back(std::move(check));
|
||||
|
||||
const auto numCheckValues = this->battery_.back()->numExportedCheckValues();
|
||||
this->startCheckValues_.push_back(this->numSamplePoints_ * numCheckValues);
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
void Opm::SatfuncConsistencyChecks<Scalar>::finaliseCheckSet()
|
||||
{
|
||||
std::partial_sum(this->startCheckValues_.begin(),
|
||||
this->startCheckValues_.end(),
|
||||
this->startCheckValues_.begin());
|
||||
|
||||
for (auto& violation : this->violations_) {
|
||||
this->buildStructure(violation);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
void Opm::SatfuncConsistencyChecks<Scalar>::
|
||||
checkEndpoints(const std::size_t pointID,
|
||||
const EclEpsScalingPointsInfo<Scalar>& endPoints)
|
||||
{
|
||||
this->checkLoop([pointID, &endPoints, this]
|
||||
(Check* currentCheck, const std::size_t checkIx)
|
||||
{
|
||||
currentCheck->test(endPoints);
|
||||
|
||||
if (! currentCheck->isViolated()) {
|
||||
// Check holds for this set of end-points. Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
// If we get here then the check does not hold for this set of
|
||||
// end-points. Process the violation at the prescribed level of
|
||||
// attention. Critical violations typically end the run whereas
|
||||
// a standard level violation typically generates warnings only.
|
||||
|
||||
const auto level = currentCheck->isCritical()
|
||||
? ViolationLevel::Critical
|
||||
: ViolationLevel::Standard;
|
||||
|
||||
this->processViolation(level, checkIx, pointID);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
bool Opm::SatfuncConsistencyChecks<Scalar>::anyFailedChecks() const
|
||||
{
|
||||
return this->anyFailedChecks(ViolationLevel::Standard);
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
bool Opm::SatfuncConsistencyChecks<Scalar>::anyFailedCriticalChecks() const
|
||||
{
|
||||
return this->anyFailedChecks(ViolationLevel::Critical);
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
void Opm::SatfuncConsistencyChecks<Scalar>::
|
||||
reportFailures(const ViolationLevel level,
|
||||
const ReportRecordOutput& emitReportRecord) const
|
||||
{
|
||||
this->checkLoop([this,
|
||||
&emitReportRecord,
|
||||
nValueChar = fmt::formatted_size("{:> 8.6e}", 1.0),
|
||||
&violation = this->violations_[this->index(level)]]
|
||||
(const Check* currentCheck, const std::size_t checkIx)
|
||||
{
|
||||
if (violation.count[checkIx] == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->writeReportHeader(currentCheck,
|
||||
violation.count[checkIx],
|
||||
emitReportRecord);
|
||||
|
||||
this->writeTabulatedReportSample(nValueChar,
|
||||
currentCheck,
|
||||
violation,
|
||||
checkIx,
|
||||
emitReportRecord);
|
||||
});
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Private member functions for SatfuncConsistencyChecks Template
|
||||
// ===========================================================================
|
||||
|
||||
template <typename Scalar>
|
||||
void Opm::SatfuncConsistencyChecks<Scalar>::ViolationSample::clear()
|
||||
{
|
||||
this->count.clear();
|
||||
this->pointID.clear();
|
||||
this->checkValues.clear();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void
|
||||
Opm::SatfuncConsistencyChecks<Scalar>::
|
||||
buildStructure(ViolationSample& violation)
|
||||
{
|
||||
violation.count.assign(this->battery_.size(), 0);
|
||||
violation.pointID.resize(this->battery_.size() * this->numSamplePoints_,
|
||||
static_cast<std::size_t>(0xdeadc0deUL));
|
||||
violation.checkValues.resize(this->startCheckValues_.back());
|
||||
|
||||
if constexpr (std::numeric_limits<Scalar>::has_quiet_NaN) {
|
||||
std::fill(violation.checkValues.begin(),
|
||||
violation.checkValues.end(),
|
||||
std::numeric_limits<Scalar>::quiet_NaN());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
void Opm::SatfuncConsistencyChecks<Scalar>::
|
||||
processViolation(const ViolationLevel level,
|
||||
const std::size_t checkIx,
|
||||
const std::size_t pointID)
|
||||
{
|
||||
auto& violation = this->violations_[this->index(level)];
|
||||
|
||||
const auto nViol = ++violation.count[checkIx];
|
||||
|
||||
// Special case handling for number of violations not exceeding number
|
||||
// of sample points. Needed in order to guarantee that the full table
|
||||
// is populated before starting the random replacement stage.
|
||||
const auto sampleIx = (nViol <= this->numSamplePoints_)
|
||||
? (nViol - 1)
|
||||
: this->getSampleIndex(nViol);
|
||||
|
||||
if (sampleIx >= this->numSamplePoints_) {
|
||||
// Reservoir sampling algorithm
|
||||
// (https://en.wikipedia.org/wiki/Reservoir_sampling) says that this
|
||||
// particular set of end-points should *not* be included in the
|
||||
// reported violations. No more work needed in this case.
|
||||
return;
|
||||
}
|
||||
|
||||
// If we get here, then this set of end-points should be included in the
|
||||
// reported violations. Record the pointID and the corresponding check
|
||||
// values in their appropriate locations.
|
||||
|
||||
violation.pointID[checkIx*this->numSamplePoints_ + sampleIx] = pointID;
|
||||
|
||||
auto* exportedCheckValues = violation.checkValues.data()
|
||||
+ this->violationValueStart(checkIx, sampleIx);
|
||||
|
||||
this->battery_[checkIx]->exportCheckValues(exportedCheckValues);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<std::string::size_type>
|
||||
computeFieldWidths(const std::vector<std::string>& columnHeaders,
|
||||
const std::string::size_type minColWidth)
|
||||
{
|
||||
auto fieldWidths = std::vector<std::size_t>(columnHeaders.size());
|
||||
|
||||
std::transform(columnHeaders.begin(), columnHeaders.end(),
|
||||
fieldWidths.begin(),
|
||||
[minColWidth](const std::string& header)
|
||||
{ return std::max(minColWidth, header.size()); });
|
||||
|
||||
return fieldWidths;
|
||||
}
|
||||
|
||||
std::string
|
||||
createTableSeparator(const std::string::size_type fwPointID,
|
||||
const std::vector<std::string::size_type>& fieldWidths)
|
||||
{
|
||||
using namespace fmt::literals;
|
||||
|
||||
// Note: "+2" for one blank space on each side of the string value.
|
||||
auto separator = fmt::format("+{name:-<{width}}",
|
||||
"name"_a = "",
|
||||
"width"_a = fwPointID + 2);
|
||||
|
||||
for (const auto& fieldWidth : fieldWidths) {
|
||||
separator += fmt::format("+{name:-<{width}}",
|
||||
"name"_a = "",
|
||||
"width"_a = fieldWidth + 2);
|
||||
}
|
||||
|
||||
separator += '+';
|
||||
|
||||
return separator;
|
||||
}
|
||||
|
||||
template <typename EmitRecord>
|
||||
void writeTableHeader(const std::string_view::size_type fwPointID,
|
||||
std::string_view pointName,
|
||||
const std::vector<std::string::size_type>& fieldWidths,
|
||||
const std::vector<std::string>& columnHeaders,
|
||||
EmitRecord&& emitRecord)
|
||||
{
|
||||
using namespace fmt::literals;
|
||||
|
||||
auto tableHeader = fmt::format("| {name:<{width}} ",
|
||||
"name"_a = pointName,
|
||||
"width"_a = fwPointID);
|
||||
|
||||
for (auto colIx = 0*columnHeaders.size(); colIx < columnHeaders.size(); ++colIx) {
|
||||
tableHeader += fmt::format("| {name:<{width}} ",
|
||||
"name"_a = columnHeaders[colIx],
|
||||
"width"_a = fieldWidths[colIx]);
|
||||
}
|
||||
|
||||
emitRecord(tableHeader + '|');
|
||||
}
|
||||
|
||||
template <typename Scalar, typename EmitRecord>
|
||||
void writeTableRecord(const std::string_view::size_type fwPointID,
|
||||
std::string_view pointID,
|
||||
const std::vector<std::string::size_type>& fieldWidths,
|
||||
const Scalar* checkValues,
|
||||
EmitRecord&& emitRecord)
|
||||
{
|
||||
using namespace fmt::literals;
|
||||
|
||||
auto record = fmt::format("| {pointID:<{width}} ",
|
||||
"width"_a = fwPointID,
|
||||
"pointID"_a = pointID);
|
||||
|
||||
for (auto colIx = 0*fieldWidths.size(); colIx < fieldWidths.size(); ++colIx) {
|
||||
record += fmt::format("| {checkValue:>{width}.6e} ",
|
||||
"width"_a = fieldWidths[colIx],
|
||||
"checkValue"_a = checkValues[colIx]);
|
||||
}
|
||||
|
||||
emitRecord(record + '|');
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
template <typename Scalar>
|
||||
void Opm::SatfuncConsistencyChecks<Scalar>::
|
||||
writeReportHeader(const Check* currentCheck,
|
||||
const std::size_t violationCount,
|
||||
const ReportRecordOutput& emitReportRecord) const
|
||||
{
|
||||
const auto* sampleMsg = (violationCount > this->numSamplePoints_)
|
||||
? "Sample Violations"
|
||||
: "List of Violations";
|
||||
|
||||
emitReportRecord(fmt::format("Consistency Problem:\n"
|
||||
" {}\n"
|
||||
" {}\n"
|
||||
" Total Violations: {}\n\n"
|
||||
"{}",
|
||||
currentCheck->description(),
|
||||
currentCheck->condition(),
|
||||
violationCount, sampleMsg));
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
void Opm::SatfuncConsistencyChecks<Scalar>::
|
||||
writeTabulatedReportSample(const std::size_t nValueChar,
|
||||
const Check* currentCheck,
|
||||
const ViolationSample& violation,
|
||||
const std::size_t checkIx,
|
||||
const ReportRecordOutput& emitReportRecord) const
|
||||
{
|
||||
const auto formattedPointIDs = this->formatPointIDs(violation, checkIx);
|
||||
const auto fieldWidthPointID =
|
||||
std::max(formattedPointIDs.second, this->pointName_.size());
|
||||
|
||||
const auto columnHeaders = this->collectColumnHeaders(currentCheck);
|
||||
const auto fieldWidths = computeFieldWidths(columnHeaders, nValueChar);
|
||||
|
||||
const auto separator = createTableSeparator(fieldWidthPointID, fieldWidths);
|
||||
|
||||
// Output separator to start table output.
|
||||
emitReportRecord(separator);
|
||||
|
||||
// Output column headers.
|
||||
writeTableHeader(fieldWidthPointID, this->pointName_,
|
||||
fieldWidths, columnHeaders,
|
||||
emitReportRecord);
|
||||
|
||||
// Output separator to start table value output.
|
||||
emitReportRecord(separator);
|
||||
|
||||
// Emit sampled check violations in order sorted on the pointID.
|
||||
for (const auto& i : this->sortedPointIndices(violation, checkIx)) {
|
||||
const auto* checkValues = violation.checkValues.data()
|
||||
+ this->violationValueStart(checkIx, i);
|
||||
|
||||
writeTableRecord(fieldWidthPointID, formattedPointIDs.first[i],
|
||||
fieldWidths, checkValues,
|
||||
emitReportRecord);
|
||||
}
|
||||
|
||||
// Output separator to end table output.
|
||||
//
|
||||
// Note: We emit two blank lines after final separator in order to
|
||||
// generate some vertical space for the case of multiple failing checks.
|
||||
emitReportRecord(fmt::format("{}\n\n", separator));
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
std::pair<std::vector<std::string>, std::string::size_type>
|
||||
Opm::SatfuncConsistencyChecks<Scalar>::
|
||||
formatPointIDs(const ViolationSample& violation,
|
||||
const std::size_t checkIx) const
|
||||
{
|
||||
auto formattedPointIDs = std::pair
|
||||
<std::vector<std::string>,
|
||||
std::string::size_type>
|
||||
{
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(),
|
||||
std::forward_as_tuple(std::string::size_type{0})
|
||||
};
|
||||
|
||||
const auto nPoints = this->numPoints(violation, checkIx);
|
||||
|
||||
formattedPointIDs.first.reserve(nPoints);
|
||||
|
||||
const auto* pointIDs = violation.pointID.data()
|
||||
+ (checkIx * this->numSamplePoints_);
|
||||
|
||||
for (auto point = 0*nPoints; point < nPoints; ++point) {
|
||||
formattedPointIDs.first.push_back
|
||||
(this->formatPointID_(pointIDs[point]));
|
||||
|
||||
formattedPointIDs.second =
|
||||
std::max(formattedPointIDs.second,
|
||||
formattedPointIDs.first.back().size());
|
||||
}
|
||||
|
||||
return formattedPointIDs;
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
std::vector<std::string>
|
||||
Opm::SatfuncConsistencyChecks<Scalar>::
|
||||
collectColumnHeaders(const Check* currentCheck) const
|
||||
{
|
||||
auto headers = std::vector<std::string>
|
||||
(currentCheck->numExportedCheckValues());
|
||||
|
||||
currentCheck->columnNames(headers.data());
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
std::vector<std::size_t>
|
||||
Opm::SatfuncConsistencyChecks<Scalar>::
|
||||
sortedPointIndices(const ViolationSample& violation,
|
||||
const std::size_t checkIx) const
|
||||
{
|
||||
auto sortedIdxs = std::vector<std::size_t>
|
||||
(this->numPoints(violation, checkIx));
|
||||
|
||||
std::iota(sortedIdxs.begin(), sortedIdxs.end(), std::size_t{0});
|
||||
|
||||
std::sort(sortedIdxs.begin(), sortedIdxs.end(),
|
||||
[pointIDs = violation.pointID.data() + (checkIx * this->numSamplePoints_)]
|
||||
(const std::size_t i1, const std::size_t i2)
|
||||
{
|
||||
return pointIDs[i1] < pointIDs[i2];
|
||||
});
|
||||
|
||||
return sortedIdxs;
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
std::size_t
|
||||
Opm::SatfuncConsistencyChecks<Scalar>::
|
||||
numPoints(const ViolationSample& violation,
|
||||
const std::size_t checkIx) const
|
||||
{
|
||||
return std::min(this->numSamplePoints_,
|
||||
violation.count[checkIx]);
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
std::size_t
|
||||
Opm::SatfuncConsistencyChecks<Scalar>::
|
||||
getSampleIndex(const std::size_t sampleSize)
|
||||
{
|
||||
assert (sampleSize > 0);
|
||||
|
||||
this->ensureRandomBitGeneratorIsInitialised();
|
||||
|
||||
return std::uniform_int_distribution<std::size_t>
|
||||
{ 0, sampleSize - 1 }(*this->urbg_);
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
void Opm::SatfuncConsistencyChecks<Scalar>::ensureRandomBitGeneratorIsInitialised()
|
||||
{
|
||||
if (this->urbg_ != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto k = static_cast<std::size_t>
|
||||
(std::log2(RandomBitGenerator::modulus) / 32) + 1;
|
||||
|
||||
auto state = std::vector<typename RandomBitGenerator::result_type>(k + 3);
|
||||
|
||||
std::random_device rd{};
|
||||
std::generate(state.begin(), state.end(), std::ref(rd));
|
||||
|
||||
std::seed_seq seeds(state.begin(), state.end());
|
||||
this->urbg_ = std::make_unique<RandomBitGenerator>(seeds);
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
typename std::vector<Scalar>::size_type
|
||||
Opm::SatfuncConsistencyChecks<Scalar>::
|
||||
violationValueStart(const std::size_t checkIx,
|
||||
const std::size_t sampleIx) const
|
||||
{
|
||||
return this->startCheckValues_[checkIx]
|
||||
+ (sampleIx * this->battery_[checkIx]->numExportedCheckValues());
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
bool
|
||||
Opm::SatfuncConsistencyChecks<Scalar>::
|
||||
anyFailedChecks(const ViolationLevel level) const
|
||||
{
|
||||
const auto& violation = this->violations_[this->index(level)];
|
||||
|
||||
return std::any_of(violation.count.begin(), violation.count.end(),
|
||||
[](const std::size_t n) { return n > 0; });
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
template <typename Body>
|
||||
void Opm::SatfuncConsistencyChecks<Scalar>::checkLoop(Body&& body)
|
||||
{
|
||||
const auto numChecks = this->battery_.size();
|
||||
|
||||
for (auto checkIx = 0*numChecks; checkIx < numChecks; ++checkIx) {
|
||||
body(this->battery_[checkIx].get(), checkIx);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
template <typename Body>
|
||||
void Opm::SatfuncConsistencyChecks<Scalar>::checkLoop(Body&& body) const
|
||||
{
|
||||
const auto numChecks = this->battery_.size();
|
||||
|
||||
for (auto checkIx = 0*numChecks; checkIx < numChecks; ++checkIx) {
|
||||
body(this->battery_[checkIx].get(), checkIx);
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Explicit Specialisations of SatfuncConsistencyChecks Template
|
||||
//
|
||||
// No other code below this separator
|
||||
// ===========================================================================
|
||||
|
||||
template class Opm::SatfuncConsistencyChecks<float>;
|
||||
template class Opm::SatfuncConsistencyChecks<double>;
|
505
opm/simulators/utils/satfunc/SatfuncConsistencyChecks.hpp
Normal file
505
opm/simulators/utils/satfunc/SatfuncConsistencyChecks.hpp
Normal file
@ -0,0 +1,505 @@
|
||||
/*
|
||||
Copyright 2024 Equinor AS
|
||||
|
||||
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 OPM_SATFUNC_CONSISTENCY_CHECK_MODULE_HPP
|
||||
#define OPM_SATFUNC_CONSISTENCY_CHECK_MODULE_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm {
|
||||
template <typename Scalar>
|
||||
struct EclEpsScalingPointsInfo;
|
||||
} // namespace Opm
|
||||
|
||||
namespace Opm {
|
||||
|
||||
/// Platform for running sets of consistency checks against collection
|
||||
/// of saturation function end-points
|
||||
///
|
||||
/// \tparam Scalar Element type. Typically \c float or \c double.
|
||||
template <typename Scalar>
|
||||
class SatfuncConsistencyChecks
|
||||
{
|
||||
public:
|
||||
/// Call-back interface for an individual check.
|
||||
///
|
||||
/// Specific checks are expected to inherit from this base class.
|
||||
class Check
|
||||
{
|
||||
public:
|
||||
/// Run specific check against a set of saturation function end-points.
|
||||
///
|
||||
/// \param[in] endPoints Set of saturation function end-points.
|
||||
/// Might for instance be the scaled end-points of the
|
||||
/// drainage functions in a single grid block or the unscaled
|
||||
/// end-points of the tabulated saturation functions in a
|
||||
/// single saturation region.
|
||||
virtual void test(const EclEpsScalingPointsInfo<Scalar>& endPoints) = 0;
|
||||
|
||||
/// Whether or not last set of end-points violated this particular check.
|
||||
virtual bool isViolated() const = 0;
|
||||
|
||||
/// Whether or not this check is critical to the simulator's
|
||||
/// ability to run the case.
|
||||
///
|
||||
/// Violating critical checks should typically stop the run.
|
||||
virtual bool isCritical() const = 0;
|
||||
|
||||
/// Number of \c Scalar values involved in the check.
|
||||
virtual std::size_t numExportedCheckValues() const = 0;
|
||||
|
||||
/// Get a linearised copy of the \c Sclar values involved in the check.
|
||||
///
|
||||
/// \param[in,out] exportedCheckValues Pointer to contiguous
|
||||
/// sequence of at least numExportedCheckValues() \c Scalars.
|
||||
/// It is the responsibility of exportCheckValues() to
|
||||
/// populate this sequence with sensible values.
|
||||
virtual void exportCheckValues(Scalar* exportedCheckValues) const = 0;
|
||||
|
||||
/// Descriptive textual summary of this check.
|
||||
///
|
||||
/// Might for instance be something along the lines of
|
||||
/// Oil-phase scaled end-points
|
||||
virtual std::string description() const = 0;
|
||||
|
||||
/// Textual representation of the consistency condition.
|
||||
virtual std::string condition() const = 0;
|
||||
|
||||
/// Retrieve names of the exported check values.
|
||||
///
|
||||
/// Order should match that of exportCheckValues().
|
||||
///
|
||||
/// \param[in,out] headers Pointer to contiguous sequence of at
|
||||
/// least numExportedCheckValues() strings. It is the
|
||||
/// responsibility of columnNames() to populate this sequence
|
||||
/// with sensible values.
|
||||
virtual void columnNames(std::string* headers) const = 0;
|
||||
};
|
||||
|
||||
/// Severity level for consistency condition violation.
|
||||
enum class ViolationLevel : std::size_t {
|
||||
/// Consistency condition violated, but we're able to continue
|
||||
/// the run.
|
||||
Standard,
|
||||
|
||||
/// Consistency condition violated and we're not able to
|
||||
/// continue the run.
|
||||
Critical,
|
||||
|
||||
/// Implementation helper. Must be last enumerator.
|
||||
NumLevels,
|
||||
};
|
||||
|
||||
/// Call-back function type for outputting a single record of a
|
||||
/// consistency condition violation report.
|
||||
using ReportRecordOutput = std::function<void(std::string_view)>;
|
||||
|
||||
/// Call-back function type for formatting a numeric end-point ID.
|
||||
using PointIDFormatCallback = std::function<std::string(std::size_t)>;
|
||||
|
||||
/// Constructor
|
||||
///
|
||||
/// \param[in] pointName Name/category of the points in this set of
|
||||
/// checks. Might for instance be "Grid block" or "Saturation
|
||||
/// region". Will be used as a column header.
|
||||
///
|
||||
/// \param[in] numSamplePoints Upper bound on the number of
|
||||
/// end-point check violations to preserve for reporting
|
||||
/// purposes. Should normally be a small number like 5 or 10.
|
||||
explicit SatfuncConsistencyChecks(std::string_view pointName,
|
||||
const std::size_t numSamplePoints);
|
||||
|
||||
/// Destructor.
|
||||
~SatfuncConsistencyChecks() = default;
|
||||
|
||||
/// Deleted copy constructor
|
||||
SatfuncConsistencyChecks(const SatfuncConsistencyChecks& rhs) = delete;
|
||||
|
||||
/// Move-constructor.
|
||||
///
|
||||
/// \param[in,out] rhs Source object. Left in a "valid but
|
||||
/// unspecified" state on exit.
|
||||
SatfuncConsistencyChecks(SatfuncConsistencyChecks&& rhs);
|
||||
|
||||
/// Deleted assignment operator.
|
||||
SatfuncConsistencyChecks&
|
||||
operator=(const SatfuncConsistencyChecks& rhs) = delete;
|
||||
|
||||
/// Move-assignment operator.
|
||||
///
|
||||
/// \param[in,out] rhs Source object. Left in a "valid but
|
||||
/// unspecified" state on exit.
|
||||
///
|
||||
/// \return \code *this \endcode.
|
||||
SatfuncConsistencyChecks& operator=(SatfuncConsistencyChecks&& rhs);
|
||||
|
||||
/// Replace formatting function for end-point IDs.
|
||||
///
|
||||
/// The default formatting function is just the identity (\code
|
||||
/// std::to_string() \endcode) which is useful for testing, but
|
||||
/// which will for instance not capture Cartesian structure.
|
||||
///
|
||||
/// \param[in] formatPointID Call-back function type for formatting
|
||||
/// a numeric end-point ID.
|
||||
///
|
||||
/// \return \code *this \endcode
|
||||
SatfuncConsistencyChecks&
|
||||
setPointIDFormatCallback(const PointIDFormatCallback& formatPointID)
|
||||
{
|
||||
this->formatPointID_ = formatPointID;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Clear current set of end-point checks.
|
||||
void resetCheckSet();
|
||||
|
||||
/// Add specific check to in-progress check set.
|
||||
///
|
||||
/// \param[in] check Particular end-point check.
|
||||
void addCheck(std::unique_ptr<Check> check);
|
||||
|
||||
/// Commit current set of checks and build requisite internal
|
||||
/// support structures.
|
||||
void finaliseCheckSet();
|
||||
|
||||
/// Run current set of checks against a specific set of end-points.
|
||||
///
|
||||
/// \param[in] pointID Numeric identifier for this particular set of
|
||||
/// end-points. Typically a saturation region or a cell ID.
|
||||
///
|
||||
/// \param[in] endPoints Set of saturation function end-points.
|
||||
/// Might for instance be the scaled end-points of the drainage
|
||||
/// functions in a single grid block or the unscaled end-points
|
||||
/// of the tabulated saturation functions in a single saturation
|
||||
/// region. Will be passed directly on to \code Check::test()
|
||||
/// \endcode for each check in the current set.
|
||||
void checkEndpoints(const std::size_t pointID,
|
||||
const EclEpsScalingPointsInfo<Scalar>& endPoints);
|
||||
|
||||
/// Whether or not any checks failed at the \c Standard level.
|
||||
bool anyFailedChecks() const;
|
||||
|
||||
/// Whether or not any checks failed at the \c Critical level.
|
||||
bool anyFailedCriticalChecks() const;
|
||||
|
||||
/// Generate textual summary output of all failed consistency checks
|
||||
/// at specific level.
|
||||
///
|
||||
/// Reports only those conditions/checks for which there is at least
|
||||
/// one violation.
|
||||
///
|
||||
/// \param[in] level Report's severity level.
|
||||
///
|
||||
/// \param[in] emitReportRecord Call-back function for outputting a
|
||||
/// single record/line of a violation report. Typically a
|
||||
/// wrapper of \code OpmLog::info() \endcode. It is the
|
||||
/// responsibility of emitReportRecord() to properly display the
|
||||
/// text lines to end users.
|
||||
void reportFailures(const ViolationLevel level,
|
||||
const ReportRecordOutput& emitReportRecord) const;
|
||||
|
||||
private:
|
||||
/// Uniform random bit generator for "reservoir sampling".
|
||||
///
|
||||
/// We don't need mt19937 for this problem. The linear congruential
|
||||
/// engine has a smaller internal state than mt19937 and that aspect
|
||||
/// is more important here.
|
||||
using RandomBitGenerator = std::minstd_rand;
|
||||
|
||||
/// Sample of consistency check violations at single severity level.
|
||||
struct ViolationSample
|
||||
{
|
||||
/// Number of consistency check violations.
|
||||
///
|
||||
/// Size equal to number of consistency checks.
|
||||
std::vector<std::size_t> count{};
|
||||
|
||||
/// Sample of point IDs for violated consistency checks.
|
||||
///
|
||||
/// \c numSamplePoints_ allocated for each consistency check.
|
||||
/// Number of valid entries for check \c i is minimum of \c
|
||||
/// numSamplePoints_ and \code count[i] \endcode.
|
||||
std::vector<std::size_t> pointID{};
|
||||
|
||||
/// Scalar values for each sampled point.
|
||||
///
|
||||
/// \c numSamplePoints_ allocated for each individual check, and
|
||||
/// the number of values per check determined by \code
|
||||
/// Check::numExportedCheckValues() \endcode. Number of valid
|
||||
/// entries for check \c i is minimum of \c numSamplePoints_ and
|
||||
/// \code count[i] \endcode.
|
||||
std::vector<Scalar> checkValues{};
|
||||
|
||||
/// Clear contents of all data members.
|
||||
void clear();
|
||||
};
|
||||
|
||||
/// Collection of consistency check violations.
|
||||
///
|
||||
/// One set of violations for each severity level.
|
||||
using ViolationCollection = std::array
|
||||
<ViolationSample, static_cast<std::size_t>(ViolationLevel::NumLevels)>;
|
||||
|
||||
/// Name/category of the points in this set of checks.
|
||||
///
|
||||
/// Set by constructor. Might for instance be "Grid block" or
|
||||
/// "Saturation region". Will be used as a column header.
|
||||
std::string pointName_{};
|
||||
|
||||
/// Maximum number of points retained for reporting purposes.
|
||||
std::size_t numSamplePoints_;
|
||||
|
||||
/// Formatting function for end-point IDs.
|
||||
///
|
||||
/// The default formatting function is just the identity (\code
|
||||
/// std::to_string() \endcode) which is useful for testing, but
|
||||
/// which will for instance not capture Cartesian structure.
|
||||
PointIDFormatCallback formatPointID_{};
|
||||
|
||||
/// Start offsets into \code ViolationSample::checkValues \endcode
|
||||
/// for each individual check.
|
||||
std::vector<typename std::vector<Scalar>::size_type> startCheckValues_{};
|
||||
|
||||
/// Collection of sampled consistency check violations.
|
||||
///
|
||||
/// One collection for each severity level.
|
||||
ViolationCollection violations_{};
|
||||
|
||||
/// Collection of checks to run against the saturation function
|
||||
/// end-points.
|
||||
std::vector<std::unique_ptr<Check>> battery_{};
|
||||
|
||||
/// Random bit generator for point sampling.
|
||||
///
|
||||
/// Represented as a pointer in order to avoid allocation and
|
||||
/// initialisation when the facility is not needed. This is useful
|
||||
/// when none of the consistency checks are violated which in turn
|
||||
/// is a common case in production runs.
|
||||
std::unique_ptr<RandomBitGenerator> urbg_{};
|
||||
|
||||
/// Allocate and initialise backing storage for a single set of
|
||||
/// sampled consistency check violations.
|
||||
///
|
||||
/// \param[out] violation On return, zeroed or otherwise initialised
|
||||
/// violation sample of proper size.
|
||||
void buildStructure(ViolationSample& violation);
|
||||
|
||||
/// Internalise a single violation into internal data structures.
|
||||
///
|
||||
/// Counts the violation and uses "reservoir sampling"
|
||||
/// (https://en.wikipedia.org/wiki/Reservoir_sampling) to determine
|
||||
/// whether or not to include the specific point into the reporting
|
||||
/// sample.
|
||||
///
|
||||
/// \param[in] level Violation severity level.
|
||||
///
|
||||
/// \param[in] checkIx Numerical check index in the range
|
||||
/// [0..battery_.size()).
|
||||
///
|
||||
/// \param[in] pointID Numeric identifier for this particular set of
|
||||
/// end-points. Typically a saturation region or a cell ID.
|
||||
void processViolation(const ViolationLevel level,
|
||||
const std::size_t checkIx,
|
||||
const std::size_t pointID);
|
||||
|
||||
/// Generate random index in the sample size.
|
||||
///
|
||||
/// \param[in] sampleSize Total number of violations of a particular
|
||||
/// check.
|
||||
///
|
||||
/// \return Uniformly distributed random integer in the range
|
||||
/// 0..sampleSize-1, inclusive.
|
||||
std::size_t getSampleIndex(const std::size_t sampleSize);
|
||||
|
||||
/// Ensure that random bit generator exists and is properly
|
||||
/// initialised.
|
||||
void ensureRandomBitGeneratorIsInitialised();
|
||||
|
||||
/// Compute start offset into ViolationSample::checkValues for
|
||||
/// particular check and sample index.
|
||||
///
|
||||
/// \param[in] checkIx Numerical check index in the range
|
||||
/// [0..battery_.size()).
|
||||
///
|
||||
/// \param[in] sampleIx Numerical index in the range
|
||||
/// [0..min(count[checkIx], numSamplePoints_)).
|
||||
///
|
||||
/// \return Start offset into ViolationSample::checkValues.
|
||||
typename std::vector<Scalar>::size_type
|
||||
violationValueStart(const std::size_t checkIx,
|
||||
const std::size_t sampleIx) const;
|
||||
|
||||
/// Emit violation report header.
|
||||
///
|
||||
/// Includes such information as the \code Check::description()
|
||||
/// \endcode, the \code Check::condition() \endcode and the total
|
||||
/// number of violations of this specific check.
|
||||
///
|
||||
/// \param[in] currentCheck Current check object. Needed for the
|
||||
/// description and condition values.
|
||||
///
|
||||
/// \param[in] violationCount Total number of condition violations.
|
||||
///
|
||||
/// \param[in] emitReportRecord Call-back function for outputting a
|
||||
/// single record/line of a violation report.
|
||||
void writeReportHeader(const Check* currentCheck,
|
||||
const std::size_t violationCount,
|
||||
const ReportRecordOutput& emitReportRecord) const;
|
||||
|
||||
/// Emit tabulated sample of check violations.
|
||||
///
|
||||
/// \param[in] nValueChar Minimum number of characters (field width)
|
||||
/// needed to display a single floating-point number ("checkValues").
|
||||
///
|
||||
/// \param[in] currentCheck Current check object. Needed for the
|
||||
/// column headers in the tabulated output.
|
||||
///
|
||||
/// \param[in] violation Sample of consistency check violations.
|
||||
///
|
||||
/// \param[in] checkIx Numerical check index in the range
|
||||
/// [0..battery_.size()).
|
||||
///
|
||||
/// \param[in] emitReportRecord Call-back function for outputting a
|
||||
/// single record/line of a violation report.
|
||||
void writeTabulatedReportSample(const std::size_t nValueChar,
|
||||
const Check* currentCheck,
|
||||
const ViolationSample& violation,
|
||||
const std::size_t checkIx,
|
||||
const ReportRecordOutput& emitReportRecord) const;
|
||||
|
||||
/// Generate textual representation of all sampled point IDs for a
|
||||
/// single consistency check.
|
||||
///
|
||||
/// \param[in] violation Sample of consistency check violations.
|
||||
///
|
||||
/// \param[in] checkIx Numerical check index in the range
|
||||
/// [0..battery_.size()).
|
||||
///
|
||||
/// \return Sequence of formatted point IDs and the minimum number
|
||||
/// of characters needed to represent each one of these.
|
||||
std::pair<std::vector<std::string>, std::string::size_type>
|
||||
formatPointIDs(const ViolationSample& violation,
|
||||
const std::size_t checkIx) const;
|
||||
|
||||
/// Generate sequence of table column headers for a single
|
||||
/// consistency check.
|
||||
///
|
||||
/// \param[in] currentCheck Current check object. Needed for the
|
||||
/// \code Check::columnNames() \endcode.
|
||||
///
|
||||
/// \return Copy of the check's column names.
|
||||
std::vector<std::string>
|
||||
collectColumnHeaders(const Check* currentCheck) const;
|
||||
|
||||
/// Generate sequence of sample indices, sorted ascendingly on the
|
||||
/// corresponding point ID.
|
||||
///
|
||||
/// \param[in] currentCheck Current check object. Needed for the
|
||||
/// \code Check::columnNames() \endcode.
|
||||
///
|
||||
/// \param[in] checkIx Numerical check index in the range
|
||||
/// [0..battery_.size()).
|
||||
///
|
||||
/// \return Ascendingly sorted sample indices.
|
||||
std::vector<std::size_t>
|
||||
sortedPointIndices(const ViolationSample& violation,
|
||||
const std::size_t checkIx) const;
|
||||
|
||||
/// Compute number of sample points for a single check's violations.
|
||||
///
|
||||
/// Effectively the minimum of the number of violations of that
|
||||
/// check and the maximum number of sample points (\code
|
||||
/// this->numSamplePoints_ \endcode).
|
||||
///
|
||||
/// \param[in] violation Sample of consistency check violations.
|
||||
///
|
||||
/// \param[in] checkIx Numerical check index in the range
|
||||
/// [0..battery_.size()).
|
||||
///
|
||||
/// \return Number of active sample points.
|
||||
std::size_t numPoints(const ViolationSample& violation,
|
||||
const std::size_t checkIx) const;
|
||||
|
||||
/// Whether or not any checks failed at specified severity level.
|
||||
///
|
||||
/// \param[in] level Violation severity level.
|
||||
///
|
||||
/// \return Whether or not any checks failed at severity level \p
|
||||
/// level.
|
||||
bool anyFailedChecks(const ViolationLevel level) const;
|
||||
|
||||
/// Convert severity level into collection index.
|
||||
///
|
||||
/// \param[in] level Violation severity level.
|
||||
///
|
||||
/// \return Corresponding index into \code this->violations_
|
||||
/// \endcode.
|
||||
auto index(const ViolationLevel level) const
|
||||
{
|
||||
return static_cast<typename ViolationCollection::size_type>(level);
|
||||
}
|
||||
|
||||
/// Run block of code for each registered consistency check.
|
||||
///
|
||||
/// Mutable version.
|
||||
///
|
||||
/// \tparam Body Call-back function type encapsulating block of code
|
||||
/// to run for each registered consistency check. Expected to be a
|
||||
/// callable type with a function call operator of the form
|
||||
/// \code
|
||||
/// void Body::operator()(Check* checkPtr, std::size_t i)
|
||||
/// \endcode
|
||||
/// in which the \c checkPtr points to a (mutable) \c Check object
|
||||
/// and \c i is the sequence index in which the particular check was
|
||||
/// registered in a call to addCheck().
|
||||
///
|
||||
/// \param[in,out] body Block of code to run for each registered
|
||||
/// consistency check.
|
||||
template <typename Body>
|
||||
void checkLoop(Body&& body);
|
||||
|
||||
/// Run block of code for each registered consistency check.
|
||||
///
|
||||
/// Immutable version.
|
||||
///
|
||||
/// \tparam Body Call-back function type encapsulating block of code
|
||||
/// to run for each registered consistency check. Expected to be a
|
||||
/// callable type with a function call operator of the form
|
||||
/// \code
|
||||
/// void Body::operator()(const Check* checkPtr, std::size_t i) const
|
||||
/// \endcode
|
||||
/// in which the \c checkPtr points to an immutable \c Check object
|
||||
/// and \c i is the sequence index in which the particular check was
|
||||
/// registered in a call to addCheck().
|
||||
///
|
||||
/// \param[in] body Block of code to run for each registered
|
||||
/// consistency check.
|
||||
template <typename Body>
|
||||
void checkLoop(Body&& body) const;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_SATFUNC_CONSISTENCY_CHECK_MODULE_HPP
|
811
tests/test_SatfuncConsistencyChecks.cpp
Normal file
811
tests/test_SatfuncConsistencyChecks.cpp
Normal file
@ -0,0 +1,811 @@
|
||||
/*
|
||||
Copyright 2024 Equinor AS
|
||||
|
||||
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 <config.h>
|
||||
|
||||
#define BOOST_TEST_MODULE TestSatfuncConsistencyChecks
|
||||
|
||||
#ifndef HAVE_MPI
|
||||
// Suppress GCC diagnostics of the form
|
||||
//
|
||||
// warning: "HAVE_MPI" is not defined, evaluates to 0
|
||||
//
|
||||
// when compiling with "-Wundef".
|
||||
#define HAVE_MPI 0
|
||||
#endif // HAVE_MPI
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <opm/simulators/utils/satfunc/SatfuncConsistencyChecks.hpp>
|
||||
|
||||
#include <opm/material/fluidmatrixinteractions/EclEpsScalingPoints.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(NoFailures)
|
||||
|
||||
namespace {
|
||||
class Standard : public Opm::SatfuncConsistencyChecks<double>::Check
|
||||
{
|
||||
public:
|
||||
void test(const Opm::EclEpsScalingPointsInfo<double>&) override {}
|
||||
bool isViolated() const override { return false; }
|
||||
bool isCritical() const override { return false; }
|
||||
std::size_t numExportedCheckValues() const override { return 1; }
|
||||
|
||||
void exportCheckValues(double* exportedCheckValues) const override
|
||||
{
|
||||
*exportedCheckValues = 17.29;
|
||||
}
|
||||
|
||||
std::string description() const override
|
||||
{
|
||||
return "Water Phase End-Point";
|
||||
}
|
||||
|
||||
std::string condition() const override
|
||||
{
|
||||
return "0 <= SWL < 1";
|
||||
}
|
||||
|
||||
void columnNames(std::string* headers) const override
|
||||
{
|
||||
*headers = "SWL";
|
||||
}
|
||||
};
|
||||
|
||||
Opm::EclEpsScalingPointsInfo<double> makePoints() { return {}; }
|
||||
} // Anonymous namespace
|
||||
|
||||
BOOST_AUTO_TEST_CASE(All_Good)
|
||||
{
|
||||
auto checker = Opm::SatfuncConsistencyChecks<double>{"Cell", 1};
|
||||
|
||||
checker.resetCheckSet();
|
||||
checker.addCheck(std::make_unique<Standard>());
|
||||
checker.finaliseCheckSet();
|
||||
|
||||
checker.checkEndpoints(1234, makePoints());
|
||||
|
||||
BOOST_CHECK_MESSAGE(! checker.anyFailedChecks(),
|
||||
"There must be no failed checks");
|
||||
|
||||
BOOST_CHECK_MESSAGE(! checker.anyFailedCriticalChecks(),
|
||||
"There must be no failed critical checks");
|
||||
|
||||
auto rpt = std::string{};
|
||||
checker.reportFailures(Opm::SatfuncConsistencyChecks<double>::ViolationLevel::Standard,
|
||||
[&rpt](std::string_view record)
|
||||
{
|
||||
rpt += fmt::format("{}\n", record);
|
||||
});
|
||||
|
||||
BOOST_CHECK_MESSAGE(rpt.empty(), "There must be no output from reportFailures()");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(Single_Exported_Value)
|
||||
|
||||
namespace {
|
||||
class StandardViolation : public Opm::SatfuncConsistencyChecks<double>::Check
|
||||
{
|
||||
public:
|
||||
void test(const Opm::EclEpsScalingPointsInfo<double>&) override {}
|
||||
bool isViolated() const override { return true; }
|
||||
bool isCritical() const override { return false; }
|
||||
std::size_t numExportedCheckValues() const override { return 1; }
|
||||
|
||||
void exportCheckValues(double* exportedCheckValues) const override
|
||||
{
|
||||
*exportedCheckValues = 17.29;
|
||||
}
|
||||
|
||||
std::string description() const override
|
||||
{
|
||||
return "Water Phase End-Point";
|
||||
}
|
||||
|
||||
std::string condition() const override
|
||||
{
|
||||
return "0 <= SWL < 1";
|
||||
}
|
||||
|
||||
void columnNames(std::string* headers) const override
|
||||
{
|
||||
*headers = "SWL";
|
||||
}
|
||||
};
|
||||
|
||||
class CriticalViolation : public Opm::SatfuncConsistencyChecks<double>::Check
|
||||
{
|
||||
public:
|
||||
void test(const Opm::EclEpsScalingPointsInfo<double>&) override {}
|
||||
bool isViolated() const override { return true; }
|
||||
bool isCritical() const override { return true; }
|
||||
std::size_t numExportedCheckValues() const override { return 1; }
|
||||
|
||||
void exportCheckValues(double* exportedCheckValues) const override
|
||||
{
|
||||
*exportedCheckValues = 314.15926;
|
||||
}
|
||||
|
||||
std::string description() const override
|
||||
{
|
||||
return "Minimum Pressure";
|
||||
}
|
||||
|
||||
std::string condition() const override
|
||||
{
|
||||
return "PRESS > 350";
|
||||
}
|
||||
|
||||
void columnNames(std::string* headers) const override
|
||||
{
|
||||
*headers = "PRESS";
|
||||
}
|
||||
};
|
||||
|
||||
Opm::EclEpsScalingPointsInfo<double> makePoints() { return {}; }
|
||||
} // Anonymous namespace
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Standard_Violation)
|
||||
{
|
||||
auto checker = Opm::SatfuncConsistencyChecks<double>{"Cell", 1};
|
||||
|
||||
checker.resetCheckSet();
|
||||
checker.addCheck(std::make_unique<StandardViolation>());
|
||||
checker.finaliseCheckSet();
|
||||
|
||||
checker.checkEndpoints(1234, makePoints());
|
||||
|
||||
BOOST_CHECK_MESSAGE(checker.anyFailedChecks(),
|
||||
"There must be at least one failed check");
|
||||
|
||||
auto rpt = std::string{};
|
||||
checker.reportFailures(Opm::SatfuncConsistencyChecks<double>::ViolationLevel::Standard,
|
||||
[&rpt](std::string_view record)
|
||||
{
|
||||
rpt += fmt::format("{}\n", record);
|
||||
});
|
||||
|
||||
BOOST_CHECK_EQUAL(rpt, R"(Consistency Problem:
|
||||
Water Phase End-Point
|
||||
0 <= SWL < 1
|
||||
Total Violations: 1
|
||||
|
||||
List of Violations
|
||||
+------+---------------+
|
||||
| Cell | SWL |
|
||||
+------+---------------+
|
||||
| 1234 | 1.729000e+01 |
|
||||
+------+---------------+
|
||||
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Standard_Violation_ReportIJK)
|
||||
{
|
||||
auto checker = Opm::SatfuncConsistencyChecks<double>{"Cell", 1};
|
||||
|
||||
checker.resetCheckSet();
|
||||
checker.addCheck(std::make_unique<StandardViolation>());
|
||||
checker.finaliseCheckSet();
|
||||
|
||||
checker.setPointIDFormatCallback([](const std::size_t)
|
||||
{
|
||||
return std::string { "(11, 22, 33)" };
|
||||
});
|
||||
|
||||
checker.checkEndpoints(1234, makePoints());
|
||||
|
||||
BOOST_CHECK_MESSAGE(checker.anyFailedChecks(),
|
||||
"There must be at least one failed check");
|
||||
|
||||
auto rpt = std::string{};
|
||||
checker.reportFailures(Opm::SatfuncConsistencyChecks<double>::ViolationLevel::Standard,
|
||||
[&rpt](std::string_view record)
|
||||
{
|
||||
rpt += fmt::format("{}\n", record);
|
||||
});
|
||||
|
||||
BOOST_CHECK_EQUAL(rpt, R"(Consistency Problem:
|
||||
Water Phase End-Point
|
||||
0 <= SWL < 1
|
||||
Total Violations: 1
|
||||
|
||||
List of Violations
|
||||
+--------------+---------------+
|
||||
| Cell | SWL |
|
||||
+--------------+---------------+
|
||||
| (11, 22, 33) | 1.729000e+01 |
|
||||
+--------------+---------------+
|
||||
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Critical_Violation)
|
||||
{
|
||||
auto checker = Opm::SatfuncConsistencyChecks<double>{"PVTNUM", 1};
|
||||
|
||||
checker.resetCheckSet();
|
||||
checker.addCheck(std::make_unique<CriticalViolation>());
|
||||
checker.finaliseCheckSet();
|
||||
|
||||
checker.checkEndpoints(42, makePoints());
|
||||
|
||||
BOOST_CHECK_MESSAGE(checker.anyFailedCriticalChecks(),
|
||||
"There must be at least one failed Critical check");
|
||||
|
||||
auto rpt = std::string{};
|
||||
checker.reportFailures(Opm::SatfuncConsistencyChecks<double>::ViolationLevel::Critical,
|
||||
[&rpt](std::string_view record)
|
||||
{
|
||||
rpt += fmt::format("{}\n", record);
|
||||
});
|
||||
|
||||
BOOST_CHECK_EQUAL(rpt, R"(Consistency Problem:
|
||||
Minimum Pressure
|
||||
PRESS > 350
|
||||
Total Violations: 1
|
||||
|
||||
List of Violations
|
||||
+--------+---------------+
|
||||
| PVTNUM | PRESS |
|
||||
+--------+---------------+
|
||||
| 42 | 3.141593e+02 |
|
||||
+--------+---------------+
|
||||
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // Single_Exported_Value
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(Two_Exported_Values)
|
||||
|
||||
namespace {
|
||||
class Violation : public Opm::SatfuncConsistencyChecks<float>::Check
|
||||
{
|
||||
void test(const Opm::EclEpsScalingPointsInfo<float>&) override {}
|
||||
bool isViolated() const override { return true; }
|
||||
bool isCritical() const override { return false; }
|
||||
std::size_t numExportedCheckValues() const override { return 2; }
|
||||
|
||||
void exportCheckValues(float* exportedCheckValues) const override
|
||||
{
|
||||
exportedCheckValues[0] = 1.6f;
|
||||
exportedCheckValues[1] = 1.6f + 0.5f;
|
||||
}
|
||||
|
||||
std::string description() const override
|
||||
{
|
||||
return "Sum";
|
||||
}
|
||||
|
||||
std::string condition() const override
|
||||
{
|
||||
return "a + 1/2 < 2";
|
||||
}
|
||||
|
||||
void columnNames(std::string* headers) const override
|
||||
{
|
||||
headers[0] = "a";
|
||||
headers[1] = "a + 1/2";
|
||||
}
|
||||
};
|
||||
|
||||
Opm::EclEpsScalingPointsInfo<float> makePoints() { return {}; }
|
||||
} // Anonymous namespace
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Standard)
|
||||
{
|
||||
auto checker = Opm::SatfuncConsistencyChecks<float>{"Bucket", 1};
|
||||
|
||||
checker.resetCheckSet();
|
||||
checker.addCheck(std::make_unique<Violation>());
|
||||
checker.finaliseCheckSet();
|
||||
|
||||
checker.checkEndpoints(1234, makePoints());
|
||||
|
||||
BOOST_CHECK_MESSAGE(checker.anyFailedChecks(),
|
||||
"There must be at least one failed check");
|
||||
|
||||
auto rpt = std::string{};
|
||||
checker.reportFailures(Opm::SatfuncConsistencyChecks<float>::ViolationLevel::Standard,
|
||||
[&rpt](std::string_view record)
|
||||
{
|
||||
rpt += fmt::format("{}\n", record);
|
||||
});
|
||||
|
||||
BOOST_CHECK_EQUAL(rpt, R"(Consistency Problem:
|
||||
Sum
|
||||
a + 1/2 < 2
|
||||
Total Violations: 1
|
||||
|
||||
List of Violations
|
||||
+--------+---------------+---------------+
|
||||
| Bucket | a | a + 1/2 |
|
||||
+--------+---------------+---------------+
|
||||
| 1234 | 1.600000e+00 | 2.100000e+00 |
|
||||
+--------+---------------+---------------+
|
||||
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // Two_Exported_Values
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(Five_Exported_Values)
|
||||
|
||||
namespace {
|
||||
class Violation : public Opm::SatfuncConsistencyChecks<float>::Check
|
||||
{
|
||||
void test(const Opm::EclEpsScalingPointsInfo<float>&) override {}
|
||||
bool isViolated() const override { return true; }
|
||||
bool isCritical() const override { return false; }
|
||||
std::size_t numExportedCheckValues() const override { return 5; }
|
||||
|
||||
void exportCheckValues(float* exportedCheckValues) const override
|
||||
{
|
||||
exportedCheckValues[0] = 0.1f;
|
||||
exportedCheckValues[1] = 0.7f;
|
||||
exportedCheckValues[2] = 0.3f;
|
||||
exportedCheckValues[3] = 0.2f;
|
||||
exportedCheckValues[4] = 0.88f;
|
||||
}
|
||||
|
||||
std::string description() const override
|
||||
{
|
||||
return "Water Phase End-Point Displacing Saturation";
|
||||
}
|
||||
|
||||
std::string condition() const override
|
||||
{
|
||||
return "SWCR < 1-SOWCR-SGL < SWU";
|
||||
}
|
||||
|
||||
void columnNames(std::string* headers) const override
|
||||
{
|
||||
headers[0] = "SGL";
|
||||
headers[1] = "SOWCR";
|
||||
headers[2] = "SWCR";
|
||||
headers[3] = "1-SOWCR-SGL";
|
||||
headers[4] = "SWU";
|
||||
}
|
||||
};
|
||||
|
||||
Opm::EclEpsScalingPointsInfo<float> makePoints() { return {}; }
|
||||
} // Anonymous namespace
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Standard)
|
||||
{
|
||||
auto checker = Opm::SatfuncConsistencyChecks<float>{"Grid Block", 1};
|
||||
|
||||
checker.resetCheckSet();
|
||||
checker.addCheck(std::make_unique<Violation>());
|
||||
checker.finaliseCheckSet();
|
||||
|
||||
checker.setPointIDFormatCallback([](const std::size_t)
|
||||
{
|
||||
return std::string { "(121, 323, 42)" };
|
||||
});
|
||||
|
||||
checker.checkEndpoints(1234, makePoints());
|
||||
|
||||
BOOST_CHECK_MESSAGE(checker.anyFailedChecks(),
|
||||
"There must be at least one failed check");
|
||||
|
||||
auto rpt = std::string{};
|
||||
checker.reportFailures(Opm::SatfuncConsistencyChecks<float>::ViolationLevel::Standard,
|
||||
[&rpt](std::string_view record)
|
||||
{
|
||||
rpt += fmt::format("{}\n", record);
|
||||
});
|
||||
|
||||
BOOST_CHECK_EQUAL(rpt, R"(Consistency Problem:
|
||||
Water Phase End-Point Displacing Saturation
|
||||
SWCR < 1-SOWCR-SGL < SWU
|
||||
Total Violations: 1
|
||||
|
||||
List of Violations
|
||||
+----------------+---------------+---------------+---------------+---------------+---------------+
|
||||
| Grid Block | SGL | SOWCR | SWCR | 1-SOWCR-SGL | SWU |
|
||||
+----------------+---------------+---------------+---------------+---------------+---------------+
|
||||
| (121, 323, 42) | 1.000000e-01 | 7.000000e-01 | 3.000000e-01 | 2.000000e-01 | 8.800000e-01 |
|
||||
+----------------+---------------+---------------+---------------+---------------+---------------+
|
||||
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Standard_Multiple_Failing_Points)
|
||||
{
|
||||
auto checker = Opm::SatfuncConsistencyChecks<float>{"Grid Block", 5};
|
||||
|
||||
checker.resetCheckSet();
|
||||
checker.addCheck(std::make_unique<Violation>());
|
||||
checker.finaliseCheckSet();
|
||||
|
||||
checker.checkEndpoints( 1234, makePoints());
|
||||
checker.checkEndpoints( 1729, makePoints());
|
||||
checker.checkEndpoints( 1618, makePoints());
|
||||
checker.checkEndpoints(31415, makePoints());
|
||||
|
||||
BOOST_CHECK_MESSAGE(checker.anyFailedChecks(),
|
||||
"There must be at least one failed check");
|
||||
|
||||
auto rpt = std::string{};
|
||||
checker.reportFailures(Opm::SatfuncConsistencyChecks<float>::ViolationLevel::Standard,
|
||||
[&rpt](std::string_view record)
|
||||
{
|
||||
rpt += fmt::format("{}\n", record);
|
||||
});
|
||||
|
||||
// Note that grid blocks are reported in sorted order rather in the
|
||||
// order of insertion.
|
||||
BOOST_CHECK_EQUAL(rpt, R"(Consistency Problem:
|
||||
Water Phase End-Point Displacing Saturation
|
||||
SWCR < 1-SOWCR-SGL < SWU
|
||||
Total Violations: 4
|
||||
|
||||
List of Violations
|
||||
+------------+---------------+---------------+---------------+---------------+---------------+
|
||||
| Grid Block | SGL | SOWCR | SWCR | 1-SOWCR-SGL | SWU |
|
||||
+------------+---------------+---------------+---------------+---------------+---------------+
|
||||
| 1234 | 1.000000e-01 | 7.000000e-01 | 3.000000e-01 | 2.000000e-01 | 8.800000e-01 |
|
||||
| 1618 | 1.000000e-01 | 7.000000e-01 | 3.000000e-01 | 2.000000e-01 | 8.800000e-01 |
|
||||
| 1729 | 1.000000e-01 | 7.000000e-01 | 3.000000e-01 | 2.000000e-01 | 8.800000e-01 |
|
||||
| 31415 | 1.000000e-01 | 7.000000e-01 | 3.000000e-01 | 2.000000e-01 | 8.800000e-01 |
|
||||
+------------+---------------+---------------+---------------+---------------+---------------+
|
||||
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // Five_Exported_Values
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(Multiple_Failing_Tests)
|
||||
|
||||
namespace {
|
||||
class MonotoneWater : public Opm::SatfuncConsistencyChecks<float>::Check
|
||||
{
|
||||
void test(const Opm::EclEpsScalingPointsInfo<float>&) override {}
|
||||
bool isViolated() const override { return true; }
|
||||
bool isCritical() const override { return false; }
|
||||
std::size_t numExportedCheckValues() const override { return 3; }
|
||||
|
||||
void exportCheckValues(float* exportedCheckValues) const override
|
||||
{
|
||||
exportedCheckValues[0] = 0.1f;
|
||||
exportedCheckValues[1] = 0.3f;
|
||||
exportedCheckValues[2] = 0.3f;
|
||||
}
|
||||
|
||||
std::string description() const override
|
||||
{
|
||||
return "Water Phase End-Point Monotonicity";
|
||||
}
|
||||
|
||||
std::string condition() const override
|
||||
{
|
||||
return "SWL <= SWCR < SWU";
|
||||
}
|
||||
|
||||
void columnNames(std::string* headers) const override
|
||||
{
|
||||
headers[0] = "SWL";
|
||||
headers[1] = "SWCR";
|
||||
headers[2] = "SWU";
|
||||
}
|
||||
};
|
||||
|
||||
class MonotoneGas : public Opm::SatfuncConsistencyChecks<float>::Check
|
||||
{
|
||||
void test(const Opm::EclEpsScalingPointsInfo<float>&) override {}
|
||||
bool isViolated() const override { return true; }
|
||||
bool isCritical() const override { return false; }
|
||||
std::size_t numExportedCheckValues() const override { return 3; }
|
||||
|
||||
void exportCheckValues(float* exportedCheckValues) const override
|
||||
{
|
||||
exportedCheckValues[0] = 0.0f;
|
||||
exportedCheckValues[1] = -0.1f;
|
||||
exportedCheckValues[2] = 0.8f;
|
||||
}
|
||||
|
||||
std::string description() const override
|
||||
{
|
||||
return "Gas Phase End-Point Monotonicity";
|
||||
}
|
||||
|
||||
std::string condition() const override
|
||||
{
|
||||
return "SGL <= SGCR < SGU";
|
||||
}
|
||||
|
||||
void columnNames(std::string* headers) const override
|
||||
{
|
||||
headers[0] = "SGL";
|
||||
headers[1] = "SGCR";
|
||||
headers[2] = "SGU";
|
||||
}
|
||||
};
|
||||
|
||||
class NonNegOSAT_OW : public Opm::SatfuncConsistencyChecks<float>::Check
|
||||
{
|
||||
void test(const Opm::EclEpsScalingPointsInfo<float>&) override {}
|
||||
bool isViolated() const override { return true; }
|
||||
bool isCritical() const override { return false; }
|
||||
std::size_t numExportedCheckValues() const override { return 3; }
|
||||
|
||||
void exportCheckValues(float* exportedCheckValues) const override
|
||||
{
|
||||
exportedCheckValues[0] = 0.1f;
|
||||
exportedCheckValues[1] = 1.0f;
|
||||
exportedCheckValues[2] = 1.1f;
|
||||
}
|
||||
|
||||
std::string description() const override
|
||||
{
|
||||
return "Oil Phase Non-Negative Saturation (O/W)";
|
||||
}
|
||||
|
||||
std::string condition() const override
|
||||
{
|
||||
return "SGL + SWU <= 1";
|
||||
}
|
||||
|
||||
void columnNames(std::string* headers) const override
|
||||
{
|
||||
headers[0] = "SGL";
|
||||
headers[1] = "SWU";
|
||||
headers[2] = "SGL + SWU";
|
||||
}
|
||||
};
|
||||
|
||||
class NonNegOSAT_GO : public Opm::SatfuncConsistencyChecks<float>::Check
|
||||
{
|
||||
void test(const Opm::EclEpsScalingPointsInfo<float>&) override {}
|
||||
bool isViolated() const override { return true; }
|
||||
bool isCritical() const override { return false; }
|
||||
std::size_t numExportedCheckValues() const override { return 3; }
|
||||
|
||||
void exportCheckValues(float* exportedCheckValues) const override
|
||||
{
|
||||
exportedCheckValues[0] = 0.25f;
|
||||
exportedCheckValues[1] = 0.8f;
|
||||
exportedCheckValues[2] = 1.05f;
|
||||
}
|
||||
|
||||
std::string description() const override
|
||||
{
|
||||
return "Oil Phase Non-Negative Saturation (G/O)";
|
||||
}
|
||||
|
||||
std::string condition() const override
|
||||
{
|
||||
return "SWL + SGU <= 1";
|
||||
}
|
||||
|
||||
void columnNames(std::string* headers) const override
|
||||
{
|
||||
headers[0] = "SWL";
|
||||
headers[1] = "SGU";
|
||||
headers[2] = "SWL + SGU";
|
||||
}
|
||||
};
|
||||
|
||||
Opm::EclEpsScalingPointsInfo<float> makePoints() { return {}; }
|
||||
} // Anonymous namespace
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Standard)
|
||||
{
|
||||
auto checker = Opm::SatfuncConsistencyChecks<float>{"Grid Block", 1};
|
||||
|
||||
checker.resetCheckSet();
|
||||
checker.addCheck(std::make_unique<MonotoneGas>());
|
||||
checker.addCheck(std::make_unique<NonNegOSAT_GO>());
|
||||
checker.addCheck(std::make_unique<MonotoneWater>());
|
||||
checker.addCheck(std::make_unique<NonNegOSAT_OW>());
|
||||
checker.finaliseCheckSet();
|
||||
|
||||
checker.setPointIDFormatCallback([](const std::size_t)
|
||||
{
|
||||
return std::string { "(121, 323, 42)" };
|
||||
});
|
||||
|
||||
checker.checkEndpoints(1234, makePoints());
|
||||
|
||||
BOOST_CHECK_MESSAGE(checker.anyFailedChecks(),
|
||||
"There must be at least one failed check");
|
||||
|
||||
auto rpt = std::string{};
|
||||
checker.reportFailures(Opm::SatfuncConsistencyChecks<float>::ViolationLevel::Standard,
|
||||
[&rpt](std::string_view record)
|
||||
{
|
||||
rpt += fmt::format("{}\n", record);
|
||||
});
|
||||
|
||||
BOOST_CHECK_EQUAL(rpt, R"(Consistency Problem:
|
||||
Gas Phase End-Point Monotonicity
|
||||
SGL <= SGCR < SGU
|
||||
Total Violations: 1
|
||||
|
||||
List of Violations
|
||||
+----------------+---------------+---------------+---------------+
|
||||
| Grid Block | SGL | SGCR | SGU |
|
||||
+----------------+---------------+---------------+---------------+
|
||||
| (121, 323, 42) | 0.000000e+00 | -1.000000e-01 | 8.000000e-01 |
|
||||
+----------------+---------------+---------------+---------------+
|
||||
|
||||
|
||||
Consistency Problem:
|
||||
Oil Phase Non-Negative Saturation (G/O)
|
||||
SWL + SGU <= 1
|
||||
Total Violations: 1
|
||||
|
||||
List of Violations
|
||||
+----------------+---------------+---------------+---------------+
|
||||
| Grid Block | SWL | SGU | SWL + SGU |
|
||||
+----------------+---------------+---------------+---------------+
|
||||
| (121, 323, 42) | 2.500000e-01 | 8.000000e-01 | 1.050000e+00 |
|
||||
+----------------+---------------+---------------+---------------+
|
||||
|
||||
|
||||
Consistency Problem:
|
||||
Water Phase End-Point Monotonicity
|
||||
SWL <= SWCR < SWU
|
||||
Total Violations: 1
|
||||
|
||||
List of Violations
|
||||
+----------------+---------------+---------------+---------------+
|
||||
| Grid Block | SWL | SWCR | SWU |
|
||||
+----------------+---------------+---------------+---------------+
|
||||
| (121, 323, 42) | 1.000000e-01 | 3.000000e-01 | 3.000000e-01 |
|
||||
+----------------+---------------+---------------+---------------+
|
||||
|
||||
|
||||
Consistency Problem:
|
||||
Oil Phase Non-Negative Saturation (O/W)
|
||||
SGL + SWU <= 1
|
||||
Total Violations: 1
|
||||
|
||||
List of Violations
|
||||
+----------------+---------------+---------------+---------------+
|
||||
| Grid Block | SGL | SWU | SGL + SWU |
|
||||
+----------------+---------------+---------------+---------------+
|
||||
| (121, 323, 42) | 1.000000e-01 | 1.000000e+00 | 1.100000e+00 |
|
||||
+----------------+---------------+---------------+---------------+
|
||||
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Standard_Multiple_Failing_Points)
|
||||
{
|
||||
auto checker = Opm::SatfuncConsistencyChecks<float>{"Grid Block", 5};
|
||||
|
||||
checker.resetCheckSet();
|
||||
checker.addCheck(std::make_unique<MonotoneGas>());
|
||||
checker.addCheck(std::make_unique<NonNegOSAT_GO>());
|
||||
checker.addCheck(std::make_unique<MonotoneWater>());
|
||||
checker.addCheck(std::make_unique<NonNegOSAT_OW>());
|
||||
checker.finaliseCheckSet();
|
||||
|
||||
checker.checkEndpoints( 1234, makePoints());
|
||||
checker.checkEndpoints( 1729, makePoints());
|
||||
checker.checkEndpoints( 1618, makePoints());
|
||||
checker.checkEndpoints(31415, makePoints());
|
||||
|
||||
BOOST_CHECK_MESSAGE(checker.anyFailedChecks(),
|
||||
"There must be at least one failed check");
|
||||
|
||||
auto rpt = std::string{};
|
||||
checker.reportFailures(Opm::SatfuncConsistencyChecks<float>::ViolationLevel::Standard,
|
||||
[&rpt](std::string_view record)
|
||||
{
|
||||
rpt += fmt::format("{}\n", record);
|
||||
});
|
||||
|
||||
// Note that grid blocks are reported in sorted order rather in the
|
||||
// order of insertion.
|
||||
BOOST_CHECK_EQUAL(rpt, R"(Consistency Problem:
|
||||
Gas Phase End-Point Monotonicity
|
||||
SGL <= SGCR < SGU
|
||||
Total Violations: 4
|
||||
|
||||
List of Violations
|
||||
+------------+---------------+---------------+---------------+
|
||||
| Grid Block | SGL | SGCR | SGU |
|
||||
+------------+---------------+---------------+---------------+
|
||||
| 1234 | 0.000000e+00 | -1.000000e-01 | 8.000000e-01 |
|
||||
| 1618 | 0.000000e+00 | -1.000000e-01 | 8.000000e-01 |
|
||||
| 1729 | 0.000000e+00 | -1.000000e-01 | 8.000000e-01 |
|
||||
| 31415 | 0.000000e+00 | -1.000000e-01 | 8.000000e-01 |
|
||||
+------------+---------------+---------------+---------------+
|
||||
|
||||
|
||||
Consistency Problem:
|
||||
Oil Phase Non-Negative Saturation (G/O)
|
||||
SWL + SGU <= 1
|
||||
Total Violations: 4
|
||||
|
||||
List of Violations
|
||||
+------------+---------------+---------------+---------------+
|
||||
| Grid Block | SWL | SGU | SWL + SGU |
|
||||
+------------+---------------+---------------+---------------+
|
||||
| 1234 | 2.500000e-01 | 8.000000e-01 | 1.050000e+00 |
|
||||
| 1618 | 2.500000e-01 | 8.000000e-01 | 1.050000e+00 |
|
||||
| 1729 | 2.500000e-01 | 8.000000e-01 | 1.050000e+00 |
|
||||
| 31415 | 2.500000e-01 | 8.000000e-01 | 1.050000e+00 |
|
||||
+------------+---------------+---------------+---------------+
|
||||
|
||||
|
||||
Consistency Problem:
|
||||
Water Phase End-Point Monotonicity
|
||||
SWL <= SWCR < SWU
|
||||
Total Violations: 4
|
||||
|
||||
List of Violations
|
||||
+------------+---------------+---------------+---------------+
|
||||
| Grid Block | SWL | SWCR | SWU |
|
||||
+------------+---------------+---------------+---------------+
|
||||
| 1234 | 1.000000e-01 | 3.000000e-01 | 3.000000e-01 |
|
||||
| 1618 | 1.000000e-01 | 3.000000e-01 | 3.000000e-01 |
|
||||
| 1729 | 1.000000e-01 | 3.000000e-01 | 3.000000e-01 |
|
||||
| 31415 | 1.000000e-01 | 3.000000e-01 | 3.000000e-01 |
|
||||
+------------+---------------+---------------+---------------+
|
||||
|
||||
|
||||
Consistency Problem:
|
||||
Oil Phase Non-Negative Saturation (O/W)
|
||||
SGL + SWU <= 1
|
||||
Total Violations: 4
|
||||
|
||||
List of Violations
|
||||
+------------+---------------+---------------+---------------+
|
||||
| Grid Block | SGL | SWU | SGL + SWU |
|
||||
+------------+---------------+---------------+---------------+
|
||||
| 1234 | 1.000000e-01 | 1.000000e+00 | 1.100000e+00 |
|
||||
| 1618 | 1.000000e-01 | 1.000000e+00 | 1.100000e+00 |
|
||||
| 1729 | 1.000000e-01 | 1.000000e+00 | 1.100000e+00 |
|
||||
| 31415 | 1.000000e-01 | 1.000000e+00 | 1.100000e+00 |
|
||||
+------------+---------------+---------------+---------------+
|
||||
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // Multiple_Failing_Tests
|
Loading…
Reference in New Issue
Block a user