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:
@@ -183,6 +183,11 @@ list (APPEND MAIN_SOURCE_FILES
|
|||||||
opm/simulators/wells/WGState.cpp
|
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)
|
if (Damaris_FOUND AND MPI_FOUND AND USE_DAMARIS_LIB)
|
||||||
list (APPEND MAIN_SOURCE_FILES
|
list (APPEND MAIN_SOURCE_FILES
|
||||||
@@ -333,7 +338,10 @@ list (APPEND TEST_SOURCE_FILES
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (HAVE_ECL_INPUT)
|
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()
|
endif()
|
||||||
|
|
||||||
if(MPI_FOUND)
|
if(MPI_FOUND)
|
||||||
@@ -675,6 +683,12 @@ if (USE_BDA_BRIDGE)
|
|||||||
)
|
)
|
||||||
endif()
|
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)
|
if (Damaris_FOUND AND MPI_FOUND AND USE_DAMARIS_LIB)
|
||||||
list (APPEND PUBLIC_HEADER_FILES
|
list (APPEND PUBLIC_HEADER_FILES
|
||||||
opm/simulators/utils/DamarisKeywords.hpp
|
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
|
||||||
Reference in New Issue
Block a user