opm-simulators/tests/test_SatfuncConsistencyChecks.cpp

815 lines
26 KiB
C++
Raw Normal View History

Add New Platform for Saturation Function Consistency Checks The intention is that this will ultimately replace the existing RelpermDiagnostics component which does not really work in parallel and which does not report enough context to help diagnose underlying issues. For now, though, we just add the shell of a new set of checks and hook that up to the build. Class SatfuncConsistencyChecks<Scalar> manages a configurable set of consistency checks, the implementations of which must publicly derive from SatfuncConsistencyChecks<Scalar>::Check. Client code will configure a set of checks by first calling SatfuncConsistencyChecks<Scalar>::resetCheckSet() then register individual checks by calling SatfuncConsistencyChecks<Scalar>::addCheck() and finally build requisite internal structures by calling SatfuncConsistencyChecks<Scalar>::finaliseCheckSet() Client code will then run the checks by calling SatfuncConsistencyChecks<Scalar>::checkEndpoints() typically in a loop. Class SatfuncConsistencyChecks<Scalar> will count consistency check failures and attribute these to each individual check as needed. We also maintain separate counts for "Standard" and "Critical" failures. The former will typically generate warnings while the latter will typically cause the simulation run to stop. Individual checks get to decide which check is "Critical", and client code gets to decide how to respond to "Critical" failures. Member function SatfuncConsistencyChecks<Scalar>::reportFailures() will generate a textual report of the known set of consistency check failures at a give severity level. As an internal implementation detail, SatfuncConsistencyChecks uses "reservoir sampling" (https://en.wikipedia.org/wiki/Reservoir_sampling) to track details about individual failed checks. We maintain at most a fixed number of individual points (constructor argument).
2024-06-20 11:30:50 -05:00
/*
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());
Add MPI Support to Saturation Function Consistency Checks This commit adds a new public member function SatfuncConsistencyChecks<>::collectFailures(root, comm) which aggregates consistency check violations from all ranks in the MPI communication object 'comm' onto rank 'root' of 'comm'. This amounts to summing the total number of violations from all ranks and potentially resampling the failure points for reporting purposes. To this end, extract the body of function processViolation() into a general helper which performs reservoir sampling and records point IDs and which uses a call-back function to populate the check values associated to a single failed check. Re-implement the original function in terms of this helper by wrapping exportCheckValues() in a lambda function. Extract similar helpers for numPoints() and anyFailedChecks(), and add a new helper function SatfuncConsistencyChecks<>::incorporateRankViolations() which brings sampled points from an MPI rank into the 'root's internal data structures. One caveat applies here. Our current approach to collecting check failures implies that calling member function reportFailures() is safe only on the 'root' process in a parallel run. On the other hand functions anyFailedChecks() and anyFailedCriticalChecks() are safe, and guaranteed to return the same answer, on all MPI ranks. On a final note, the internal helper functions are at present mostly implemented in terms of non-owning pointers. I intend to switch to using 'std::span<>' once we enable C++20 mode.
2024-06-25 11:57:09 -05:00
BOOST_CHECK_MESSAGE(! checker.anyFailedChecks(),
"There must be no failed standard level checks");
Add New Platform for Saturation Function Consistency Checks The intention is that this will ultimately replace the existing RelpermDiagnostics component which does not really work in parallel and which does not report enough context to help diagnose underlying issues. For now, though, we just add the shell of a new set of checks and hook that up to the build. Class SatfuncConsistencyChecks<Scalar> manages a configurable set of consistency checks, the implementations of which must publicly derive from SatfuncConsistencyChecks<Scalar>::Check. Client code will configure a set of checks by first calling SatfuncConsistencyChecks<Scalar>::resetCheckSet() then register individual checks by calling SatfuncConsistencyChecks<Scalar>::addCheck() and finally build requisite internal structures by calling SatfuncConsistencyChecks<Scalar>::finaliseCheckSet() Client code will then run the checks by calling SatfuncConsistencyChecks<Scalar>::checkEndpoints() typically in a loop. Class SatfuncConsistencyChecks<Scalar> will count consistency check failures and attribute these to each individual check as needed. We also maintain separate counts for "Standard" and "Critical" failures. The former will typically generate warnings while the latter will typically cause the simulation run to stop. Individual checks get to decide which check is "Critical", and client code gets to decide how to respond to "Critical" failures. Member function SatfuncConsistencyChecks<Scalar>::reportFailures() will generate a textual report of the known set of consistency check failures at a give severity level. As an internal implementation detail, SatfuncConsistencyChecks uses "reservoir sampling" (https://en.wikipedia.org/wiki/Reservoir_sampling) to track details about individual failed checks. We maintain at most a fixed number of individual points (constructor argument).
2024-06-20 11:30:50 -05:00
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