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