/* 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 . */ #include #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 #include #include #include #include #include #include #include BOOST_AUTO_TEST_SUITE(NoFailures) namespace { class Standard : public Opm::SatfuncConsistencyChecks::Check { public: void test(const Opm::EclEpsScalingPointsInfo&) 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 makePoints() { return {}; } } // Anonymous namespace BOOST_AUTO_TEST_CASE(All_Good) { auto checker = Opm::SatfuncConsistencyChecks{"Cell", 1}; checker.resetCheckSet(); checker.addCheck(std::make_unique()); 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::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::Check { public: void test(const Opm::EclEpsScalingPointsInfo&) 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::Check { public: void test(const Opm::EclEpsScalingPointsInfo&) 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 makePoints() { return {}; } } // Anonymous namespace BOOST_AUTO_TEST_CASE(Standard_Violation) { auto checker = Opm::SatfuncConsistencyChecks{"Cell", 1}; checker.resetCheckSet(); checker.addCheck(std::make_unique()); 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::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{"Cell", 1}; checker.resetCheckSet(); checker.addCheck(std::make_unique()); 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::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{"PVTNUM", 1}; checker.resetCheckSet(); checker.addCheck(std::make_unique()); checker.finaliseCheckSet(); checker.checkEndpoints(42, makePoints()); BOOST_CHECK_MESSAGE(! checker.anyFailedChecks(), "There must be no failed standard level checks"); BOOST_CHECK_MESSAGE(checker.anyFailedCriticalChecks(), "There must be at least one failed Critical check"); auto rpt = std::string{}; checker.reportFailures(Opm::SatfuncConsistencyChecks::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::Check { void test(const Opm::EclEpsScalingPointsInfo&) 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 makePoints() { return {}; } } // Anonymous namespace BOOST_AUTO_TEST_CASE(Standard) { auto checker = Opm::SatfuncConsistencyChecks{"Bucket", 1}; checker.resetCheckSet(); checker.addCheck(std::make_unique()); 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::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::Check { void test(const Opm::EclEpsScalingPointsInfo&) 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 makePoints() { return {}; } } // Anonymous namespace BOOST_AUTO_TEST_CASE(Standard) { auto checker = Opm::SatfuncConsistencyChecks{"Grid Block", 1}; checker.resetCheckSet(); checker.addCheck(std::make_unique()); 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::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{"Grid Block", 5}; checker.resetCheckSet(); checker.addCheck(std::make_unique()); 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::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::Check { void test(const Opm::EclEpsScalingPointsInfo&) 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::Check { void test(const Opm::EclEpsScalingPointsInfo&) 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::Check { void test(const Opm::EclEpsScalingPointsInfo&) 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::Check { void test(const Opm::EclEpsScalingPointsInfo&) 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 makePoints() { return {}; } } // Anonymous namespace BOOST_AUTO_TEST_CASE(Standard) { auto checker = Opm::SatfuncConsistencyChecks{"Grid Block", 1}; checker.resetCheckSet(); checker.addCheck(std::make_unique()); checker.addCheck(std::make_unique()); checker.addCheck(std::make_unique()); checker.addCheck(std::make_unique()); 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::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{"Grid Block", 5}; checker.resetCheckSet(); checker.addCheck(std::make_unique()); checker.addCheck(std::make_unique()); checker.addCheck(std::make_unique()); checker.addCheck(std::make_unique()); 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::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