mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
This commit adds a new public member function SatfuncConsistencyChecks<>::collectFailures(root, comm) which aggregates consistency check violations from all ranks in the MPI communication object 'comm' onto rank 'root' of 'comm'. This amounts to summing the total number of violations from all ranks and potentially resampling the failure points for reporting purposes. To this end, extract the body of function processViolation() into a general helper which performs reservoir sampling and records point IDs and which uses a call-back function to populate the check values associated to a single failed check. Re-implement the original function in terms of this helper by wrapping exportCheckValues() in a lambda function. Extract similar helpers for numPoints() and anyFailedChecks(), and add a new helper function SatfuncConsistencyChecks<>::incorporateRankViolations() which brings sampled points from an MPI rank into the 'root's internal data structures. One caveat applies here. Our current approach to collecting check failures implies that calling member function reportFailures() is safe only on the 'root' process in a parallel run. On the other hand functions anyFailedChecks() and anyFailedCriticalChecks() are safe, and guaranteed to return the same answer, on all MPI ranks. On a final note, the internal helper functions are at present mostly implemented in terms of non-owning pointers. I intend to switch to using 'std::span<>' once we enable C++20 mode.
708 lines
24 KiB
C++
708 lines
24 KiB
C++
/*
|
|
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/simulators/utils/ParallelCommunication.hpp>
|
|
|
|
#include <opm/grid/common/CommunicationUtils.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>
|
|
void Opm::SatfuncConsistencyChecks<Scalar>::
|
|
collectFailures(const int root,
|
|
const Parallel::Communication& comm)
|
|
{
|
|
if (comm.size() == 1) {
|
|
// Not a parallel run. Violation structure complete without
|
|
// exchanging additional information, so nothing to do.
|
|
return;
|
|
}
|
|
|
|
for (auto& violation : this->violations_) {
|
|
this->collectFailures(root, comm, violation);
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
namespace {
|
|
bool anyFailedChecks(const std::vector<std::size_t>& count)
|
|
{
|
|
return std::any_of(count.begin(), count.end(),
|
|
[](const std::size_t n) { return n > 0; });
|
|
}
|
|
}
|
|
|
|
template <typename Scalar>
|
|
void Opm::SatfuncConsistencyChecks<Scalar>::
|
|
collectFailures(const int root,
|
|
const Parallel::Communication& comm,
|
|
ViolationSample& violation)
|
|
{
|
|
// Count total number of violations of each check across all ranks.
|
|
// This should be the final number emitted in reportFailures() on the
|
|
// root process.
|
|
auto totalCount = violation.count;
|
|
comm.sum(totalCount.data(), violation.count.size());
|
|
|
|
if (! ::anyFailedChecks(totalCount)) {
|
|
// No failed checks on any rank for this severity level.
|
|
//
|
|
// No additional work needed, since every rank will have zero
|
|
// failure counts for all checks.
|
|
return;
|
|
}
|
|
|
|
// CSR-like structures for the failure counts, sampled point IDs, and
|
|
// sampled check values from all ranks. One set of all-to-one messages
|
|
// for each quantity. If this stage becomes a bottleneck we must devise
|
|
// a better communication structure that reduces the number of messages.
|
|
const auto& [rankCount, startRankCount] =
|
|
gatherv(violation.count, comm, root);
|
|
|
|
const auto& [rankPointID, startRankPointID] =
|
|
gatherv(violation.pointID, comm, root);
|
|
|
|
const auto& [rankCheckValues, startRankCheckValues] =
|
|
gatherv(violation.checkValues, comm, root);
|
|
|
|
if (comm.rank() == root) {
|
|
// Re-initialise this violation sample to prepare for incorporating
|
|
// contributions from all MPI ranks--including the current rank.
|
|
violation.clear();
|
|
this->buildStructure(violation);
|
|
|
|
const auto numRanks = comm.size();
|
|
for (auto rank = 0*numRanks; rank < numRanks; ++rank) {
|
|
this->incorporateRankViolations
|
|
(rankCount.data() + startRankCount[rank],
|
|
rankPointID.data() + startRankPointID[rank],
|
|
rankCheckValues.data() + startRankCheckValues[rank],
|
|
violation);
|
|
}
|
|
}
|
|
|
|
// The final violation counts for reporting purposes should be the sum
|
|
// of the per-rank counts. This ensures that all ranks give the same
|
|
// answer to the anyFailedChecks() predicate, although the particular
|
|
// sample points will differ across the ranks.
|
|
violation.count.swap(totalCount);
|
|
|
|
// Ensure that all ranks are synchronised here before proceeding. We
|
|
// don't want to end up in a situation where the ranks have a different
|
|
// notion of what to send/receive.
|
|
comm.barrier();
|
|
}
|
|
|
|
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>
|
|
template <typename PopulateCheckValues>
|
|
void Opm::SatfuncConsistencyChecks<Scalar>::
|
|
processViolation(ViolationSample& violation,
|
|
const std::size_t checkIx,
|
|
const std::size_t pointID,
|
|
PopulateCheckValues&& populateCheckValues)
|
|
{
|
|
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[this->violationPointIDStart(checkIx) + sampleIx] = pointID;
|
|
|
|
auto* const checkValues = violation.checkValues.data()
|
|
+ this->violationValueStart(checkIx, sampleIx);
|
|
|
|
populateCheckValues(checkValues);
|
|
}
|
|
|
|
template <typename Scalar>
|
|
void Opm::SatfuncConsistencyChecks<Scalar>::
|
|
processViolation(const ViolationLevel level,
|
|
const std::size_t checkIx,
|
|
const std::size_t pointID)
|
|
{
|
|
this->processViolation(this->violations_[this->index(level)], checkIx, pointID,
|
|
[this, checkIx](Scalar* const exportedCheckValues)
|
|
{
|
|
this->battery_[checkIx]->exportCheckValues(exportedCheckValues);
|
|
});
|
|
}
|
|
|
|
template <typename Scalar>
|
|
void Opm::SatfuncConsistencyChecks<Scalar>::
|
|
incorporateRankViolations(const std::size_t* const count,
|
|
const std::size_t* const pointID,
|
|
const Scalar* const checkValues,
|
|
ViolationSample& violation)
|
|
{
|
|
this->checkLoop([this, count, pointID, checkValues, &violation]
|
|
(const Check* currentCheck,
|
|
const std::size_t checkIx)
|
|
{
|
|
if (count[checkIx] == 0) {
|
|
// No violations of this check on this rank. Nothing to do.
|
|
return;
|
|
}
|
|
|
|
const auto* const srcPointID = pointID
|
|
+ this->violationPointIDStart(checkIx);
|
|
|
|
const auto numCheckValues = currentCheck->numExportedCheckValues();
|
|
const auto numSrcSamples = this->numPoints(count[checkIx]);
|
|
|
|
for (auto srcSampleIx = 0*numSrcSamples; srcSampleIx < numSrcSamples; ++srcSampleIx) {
|
|
this->processViolation(violation, checkIx, srcPointID[srcSampleIx],
|
|
[numCheckValues,
|
|
srcCheckValues = checkValues + this->violationValueStart(checkIx, srcSampleIx)]
|
|
(Scalar* const destCheckValues)
|
|
{
|
|
std::copy_n(srcCheckValues, numCheckValues, destCheckValues);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
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 this->numPoints(violation.count[checkIx]);
|
|
}
|
|
|
|
template <typename Scalar>
|
|
std::size_t
|
|
Opm::SatfuncConsistencyChecks<Scalar>::
|
|
numPoints(const std::size_t violationCount) const
|
|
{
|
|
return std::min(this->numSamplePoints_, violationCount);
|
|
}
|
|
|
|
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>
|
|
std::vector<std::size_t>::size_type
|
|
Opm::SatfuncConsistencyChecks<Scalar>::
|
|
violationPointIDStart(const std::size_t checkIx) const
|
|
{
|
|
return checkIx * this->numSamplePoints_;
|
|
}
|
|
|
|
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
|
|
{
|
|
return ::anyFailedChecks(this->violations_[this->index(level)].count);
|
|
}
|
|
|
|
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>;
|