diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake
index 01ace7fcf..dfa8b97b7 100644
--- a/CMakeLists_files.cmake
+++ b/CMakeLists_files.cmake
@@ -194,6 +194,7 @@ list (APPEND MAIN_SOURCE_FILES
if (HAVE_ECL_INPUT)
list (APPEND MAIN_SOURCE_FILES
+ opm/simulators/utils/satfunc/GasPhaseConsistencyChecks.cpp
opm/simulators/utils/satfunc/OilPhaseConsistencyChecks.cpp
opm/simulators/utils/satfunc/PhaseCheckBase.cpp
opm/simulators/utils/satfunc/SatfuncConsistencyChecks.cpp
@@ -380,6 +381,7 @@ list (APPEND TEST_SOURCE_FILES
if (HAVE_ECL_INPUT)
list(APPEND TEST_SOURCE_FILES
tests/test_nonnc.cpp
+ tests/test_GasSatfuncConsistencyChecks.cpp
tests/test_OilSatfuncConsistencyChecks.cpp
tests/test_SatfuncConsistencyChecks.cpp
tests/test_SatfuncConsistencyChecks_parallel.cpp
@@ -986,6 +988,7 @@ endif()
if (HAVE_ECL_INPUT)
list (APPEND PUBLIC_HEADER_FILES
+ opm/simulators/utils/satfunc/GasPhaseConsistencyChecks.hpp
opm/simulators/utils/satfunc/OilPhaseConsistencyChecks.hpp
opm/simulators/utils/satfunc/PhaseCheckBase.hpp
opm/simulators/utils/satfunc/SatfuncConsistencyChecks.hpp
diff --git a/opm/simulators/utils/satfunc/GasPhaseConsistencyChecks.cpp b/opm/simulators/utils/satfunc/GasPhaseConsistencyChecks.cpp
new file mode 100644
index 000000000..d2961bda9
--- /dev/null
+++ b/opm/simulators/utils/satfunc/GasPhaseConsistencyChecks.cpp
@@ -0,0 +1,122 @@
+/*
+ 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
+
+#include
+
+#include
+
+#include
+
+// ---------------------------------------------------------------------------
+
+template
+void Opm::Satfunc::PhaseChecks::Gas::SGmin::
+testImpl(const EclEpsScalingPointsInfo& endPoints)
+{
+ this->sgl_ = endPoints.Sgl;
+
+ if (! std::isfinite(this->sgl_)) {
+ this->setViolated();
+ this->setCritical();
+
+ return;
+ }
+
+ const auto low = this->sgl_ < Scalar{0};
+ const auto high = ! (this->sgl_ < Scalar{1});
+
+ if (low || high) {
+ this->setViolated();
+ this->setCritical();
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+template
+void Opm::Satfunc::PhaseChecks::Gas::SGmax::
+testImpl(const EclEpsScalingPointsInfo& endPoints)
+{
+ this->sgu_ = endPoints.Sgu;
+
+ if (! std::isfinite(this->sgu_)) {
+ this->setViolated();
+ this->setCritical();
+
+ return;
+ }
+
+ const auto low = this->sgu_ < Scalar{0};
+ const auto high = ! (this->sgu_ < Scalar{1});
+
+ if (low || high) {
+ this->setViolated();
+ this->setCritical();
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+template
+void Opm::Satfunc::PhaseChecks::Gas::SGcr::
+testImpl(const EclEpsScalingPointsInfo& endPoints)
+{
+ this->sgl_ = endPoints.Sgl;
+ this->sgcr_ = endPoints.Sgcr;
+ this->sgu_ = endPoints.Sgu;
+
+ if (! std::isfinite(this->sgl_) ||
+ ! std::isfinite(this->sgcr_) ||
+ ! std::isfinite(this->sgu_))
+ {
+ this->setViolated();
+ this->setCritical();
+
+ return;
+ }
+
+ const auto low = this->sgcr_ < this->sgl_;
+ const auto high = ! (this->sgcr_ < this->sgu_);
+
+ if (low || high) {
+ this->setViolated();
+ this->setCritical();
+ }
+}
+
+// ===========================================================================
+// Explicit Specialisations of Individual Check Templates
+//
+// No other code below this separator
+// ===========================================================================
+
+template class Opm::Satfunc::PhaseChecks::Gas::SGmin;
+template class Opm::Satfunc::PhaseChecks::Gas::SGmin;
+
+// ---------------------------------------------------------------------------
+
+template class Opm::Satfunc::PhaseChecks::Gas::SGmax;
+template class Opm::Satfunc::PhaseChecks::Gas::SGmax;
+
+// ---------------------------------------------------------------------------
+
+template class Opm::Satfunc::PhaseChecks::Gas::SGcr;
+template class Opm::Satfunc::PhaseChecks::Gas::SGcr;
diff --git a/opm/simulators/utils/satfunc/GasPhaseConsistencyChecks.hpp b/opm/simulators/utils/satfunc/GasPhaseConsistencyChecks.hpp
new file mode 100644
index 000000000..d34f1f444
--- /dev/null
+++ b/opm/simulators/utils/satfunc/GasPhaseConsistencyChecks.hpp
@@ -0,0 +1,205 @@
+/*
+ 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 .
+*/
+
+#ifndef GAS_PHASE_CONSISTENCY_CHECKS_HPP_INCLUDED
+#define GAS_PHASE_CONSISTENCY_CHECKS_HPP_INCLUDED
+
+#include
+#include
+
+#include
+#include
+
+namespace Opm::Satfunc::PhaseChecks::Gas {
+
+ /// Verify that minimum gas saturation is in valid range
+ ///
+ /// \tparam Scalar Element type. Typically \c float or \c double.
+ template
+ class SGmin : public PhaseCheckBase
+ {
+ public:
+ /// Number of \c Scalar values involved in the check.
+ std::size_t numExportedCheckValues() const override { return 1; };
+
+ /// Get a linearised copy of the \c Scalar values involved in the check.
+ ///
+ /// \param[in,out] exportedCheckValues Pointer to contiguous
+ /// sequence of at least numExportedCheckValues() \c Scalars.
+ void exportCheckValues(Scalar* exportedCheckValues) const override
+ {
+ exportedCheckValues[0] = this->sgl_;
+ }
+
+ /// Descriptive textual summary of this check.
+ std::string description() const override
+ {
+ return { "Non-negative minimum gas saturation" };
+ }
+
+ /// Textual representation of the consistency condition.
+ std::string condition() const override
+ {
+ return { "0 <= SGL < 1" };
+ }
+
+ /// Retrieve names of the exported check values.
+ ///
+ /// \param[in,out] headers Pointer to contiguous sequence of at
+ /// least numExportedCheckValues() strings.
+ void columnNames(std::string* headers) const override
+ {
+ headers[0] = "SGL";
+ }
+
+ private:
+ /// Minimum gas saturation.
+ Scalar sgl_;
+
+ /// Run 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.
+ void testImpl(const EclEpsScalingPointsInfo& endPoints) override;
+ };
+
+ /// Verify that maximum gas saturation is in valid range.
+ ///
+ /// \tparam Scalar Element type. Typically \c float or \c double.
+ template
+ class SGmax : public PhaseCheckBase
+ {
+ public:
+ /// Number of \c Scalar values involved in the check.
+ std::size_t numExportedCheckValues() const override { return 1; };
+
+ /// Get a linearised copy of the \c Scalar values involved in the check.
+ ///
+ /// \param[in,out] exportedCheckValues Pointer to contiguous
+ /// sequence of at least numExportedCheckValues() \c Scalars.
+ void exportCheckValues(Scalar* exportedCheckValues) const override
+ {
+ exportedCheckValues[0] = this->sgu_;
+ }
+
+ /// Descriptive textual summary of this check.
+ std::string description() const override
+ {
+ return { "Non-negative maximum gas saturation strictly less than one" };
+ }
+
+ /// Textual representation of the consistency condition.
+ std::string condition() const override
+ {
+ return { "0 <= SGU < 1" };
+ }
+
+ /// Retrieve names of the exported check values.
+ ///
+ /// \param[in,out] headers Pointer to contiguous sequence of at
+ /// least numExportedCheckValues() strings.
+ void columnNames(std::string* headers) const override
+ {
+ headers[0] = "SGU";
+ }
+
+ private:
+ /// Maximum gas saturation.
+ Scalar sgu_;
+
+ /// Run 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.
+ void testImpl(const EclEpsScalingPointsInfo& endPoints) override;
+ };
+
+ /// Verify that critical gas saturation is in valid range.
+ ///
+ /// \tparam Scalar Element type. Typically \c float or \c double.
+ template
+ class SGcr : public PhaseCheckBase
+ {
+ public:
+ /// Number of \c Scalar values involved in the check.
+ std::size_t numExportedCheckValues() const override { return 3; };
+
+ /// Get a linearised copy of the \c Scalar values involved in the check.
+ ///
+ /// \param[in,out] exportedCheckValues Pointer to contiguous
+ /// sequence of at least numExportedCheckValues() \c Scalars.
+ void exportCheckValues(Scalar* exportedCheckValues) const override
+ {
+ exportedCheckValues[0] = this->sgl_;
+ exportedCheckValues[1] = this->sgcr_;
+ exportedCheckValues[2] = this->sgu_;
+ }
+
+ /// Descriptive textual summary of this check.
+ std::string description() const override
+ {
+ return { "Mobile gas saturation" };
+ }
+
+ /// Textual representation of the consistency condition.
+ std::string condition() const override
+ {
+ return { "SGL <= SGCR < SGU" };
+ }
+
+ /// Retrieve names of the exported check values.
+ ///
+ /// \param[in,out] headers Pointer to contiguous sequence of at
+ /// least numExportedCheckValues() strings.
+ void columnNames(std::string* headers) const override
+ {
+ headers[0] = "SGL";
+ headers[1] = "SGCR";
+ headers[2] = "SGU";
+ }
+
+ private:
+ /// Minimum gas saturation.
+ Scalar sgl_;
+
+ /// Critical gas saturation.
+ Scalar sgcr_;
+
+ /// Maximum gas saturation.
+ Scalar sgu_;
+
+ /// Run 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.
+ void testImpl(const EclEpsScalingPointsInfo& endPoints) override;
+ };
+
+} // namespace Opm::Satfunc::PhaseChecks::Gas
+
+#endif // GAS_PHASE_CONSISTENCY_CHECKS_HPP_INCLUDED
diff --git a/tests/test_GasSatfuncConsistencyChecks.cpp b/tests/test_GasSatfuncConsistencyChecks.cpp
new file mode 100644
index 000000000..0eb039956
--- /dev/null
+++ b/tests/test_GasSatfuncConsistencyChecks.cpp
@@ -0,0 +1,739 @@
+/*
+ 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 TestGasPhaseConsistencyChecks
+
+#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
+
+// ###########################################################################
+
+namespace Checks = Opm::Satfunc::PhaseChecks::Gas;
+
+// ===========================================================================
+
+BOOST_AUTO_TEST_SUITE(Sg_min)
+
+BOOST_AUTO_TEST_CASE(All_Good)
+{
+ auto check = Checks::SGmin{};
+
+ BOOST_CHECK_EQUAL(check.numExportedCheckValues(), std::size_t{1});
+
+ {
+ auto column = std::string{};
+ check.columnNames(&column);
+
+ BOOST_CHECK_EQUAL(column, "SGL");
+ }
+
+ {
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgl = 0.125f; // >= 0 && < 1
+
+ check.test(endPoints);
+ }
+
+ {
+ auto value = -0.1f;
+ check.exportCheckValues(&value);
+
+ BOOST_CHECK_CLOSE(value, 0.125f, 1.0e-6f);
+ }
+
+ BOOST_CHECK_MESSAGE(! check.isViolated(), "Test must not be violated");
+ BOOST_CHECK_MESSAGE(! check.isCritical(), "Test must not be violated at critical level");
+}
+
+BOOST_AUTO_TEST_CASE(Non_Finite)
+{
+ // NaN
+ if constexpr (std::numeric_limits::has_quiet_NaN) {
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgl = std::numeric_limits::quiet_NaN();
+
+ auto check = Checks::SGmin{};
+ check.test(endPoints);
+
+ auto value = -0.1f;
+ check.exportCheckValues(&value);
+
+ BOOST_CHECK_MESSAGE(std::isnan(value), "Sgl value must be NaN");
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+
+ // Inf
+ if constexpr (std::numeric_limits::has_infinity) {
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgl = std::numeric_limits::infinity();
+
+ auto check = Checks::SGmin{};
+ check.test(endPoints);
+
+ auto value = -0.1f;
+ check.exportCheckValues(&value);
+
+ BOOST_CHECK_MESSAGE(std::isinf(value), "Sgl value must be Inf");
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Negative)
+{
+ auto check = Checks::SGmin{};
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgl = -0.01; // < 0
+
+ check.test(endPoints);
+
+ {
+ auto value = 1.0;
+ check.exportCheckValues(&value);
+
+ BOOST_CHECK_CLOSE(value, -0.01, 1.0e-8);
+ }
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Test must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Test must be violated at critical level");
+}
+
+BOOST_AUTO_TEST_CASE(Is_One)
+{
+ auto check = Checks::SGmin{};
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgl = 1.0; // >= 1
+
+ check.test(endPoints);
+
+ {
+ auto value = 0.0;
+ check.exportCheckValues(&value);
+
+ BOOST_CHECK_CLOSE(value, 1.0, 1.0e-8);
+ }
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Test must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Test must be violated at critical level");
+}
+
+BOOST_AUTO_TEST_CASE(Exceeds_One)
+{
+ auto check = Checks::SGmin{};
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgl = 1.2; // >= 1
+
+ check.test(endPoints);
+
+ {
+ auto value = 0.0;
+ check.exportCheckValues(&value);
+
+ BOOST_CHECK_CLOSE(value, 1.2, 1.0e-8);
+ }
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Test must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Test must be violated at critical level");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Sg_min
+
+// ---------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE(Sg_max)
+
+BOOST_AUTO_TEST_CASE(All_Good)
+{
+ auto check = Checks::SGmax{};
+
+ BOOST_CHECK_EQUAL(check.numExportedCheckValues(), std::size_t{1});
+
+ {
+ auto column = std::string{};
+ check.columnNames(&column);
+
+ BOOST_CHECK_EQUAL(column, "SGU");
+ }
+
+ {
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgu = 0.125f; // >= 0 && < 1
+
+ check.test(endPoints);
+ }
+
+ {
+ auto value = -0.1f;
+ check.exportCheckValues(&value);
+
+ BOOST_CHECK_CLOSE(value, 0.125f, 1.0e-6f);
+ }
+
+ BOOST_CHECK_MESSAGE(! check.isViolated(), "Test must not be violated");
+ BOOST_CHECK_MESSAGE(! check.isCritical(), "Test must not be violated at critical level");
+}
+
+BOOST_AUTO_TEST_CASE(Non_Finite)
+{
+ // NaN
+ if constexpr (std::numeric_limits::has_quiet_NaN) {
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgu = std::numeric_limits::quiet_NaN();
+
+ auto check = Checks::SGmax{};
+ check.test(endPoints);
+
+ auto value = -0.1f;
+ check.exportCheckValues(&value);
+
+ BOOST_CHECK_MESSAGE(std::isnan(value), "Sgu value must be NaN");
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+
+ // Inf
+ if constexpr (std::numeric_limits::has_infinity) {
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgu = std::numeric_limits::infinity();
+
+ auto check = Checks::SGmax{};
+ check.test(endPoints);
+
+ auto value = -0.1f;
+ check.exportCheckValues(&value);
+
+ BOOST_CHECK_MESSAGE(std::isinf(value), "Sgu value must be Inf");
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Negative)
+{
+ auto check = Checks::SGmax{};
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgu = -0.01; // < 0
+
+ check.test(endPoints);
+
+ {
+ auto value = 1.0;
+ check.exportCheckValues(&value);
+
+ BOOST_CHECK_CLOSE(value, -0.01, 1.0e-8);
+ }
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Test must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Test must be violated at critical level");
+}
+
+BOOST_AUTO_TEST_CASE(Is_One)
+{
+ auto check = Checks::SGmax{};
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgu = 1.0; // >= 1
+
+ check.test(endPoints);
+
+ {
+ auto value = 0.0;
+ check.exportCheckValues(&value);
+
+ BOOST_CHECK_CLOSE(value, 1.0, 1.0e-8);
+ }
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Test must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Test must be violated at critical level");
+}
+
+BOOST_AUTO_TEST_CASE(Exceeds_One)
+{
+ auto check = Checks::SGmax{};
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgu = 1.2; // >= 1
+
+ check.test(endPoints);
+
+ {
+ auto value = 0.0;
+ check.exportCheckValues(&value);
+
+ BOOST_CHECK_CLOSE(value, 1.2, 1.0e-8);
+ }
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Test must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Test must be violated at critical level");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Sg_max
+
+// ---------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE(Sg_cr)
+
+BOOST_AUTO_TEST_CASE(All_Good)
+{
+ auto check = Checks::SGcr{};
+
+ BOOST_CHECK_EQUAL(check.numExportedCheckValues(), std::size_t{3});
+
+ {
+ auto columns = std::vector(3);
+ check.columnNames(columns.data());
+
+ BOOST_CHECK_EQUAL(columns[0], "SGL");
+ BOOST_CHECK_EQUAL(columns[1], "SGCR");
+ BOOST_CHECK_EQUAL(columns[2], "SGU");
+ }
+
+ {
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgl = 0.09;
+ endPoints.Sgcr = 0.12;
+ endPoints.Sgu = 0.9;
+
+ check.test(endPoints);
+ }
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_CLOSE(values[0], 0.09, 1.0e-8);
+ BOOST_CHECK_CLOSE(values[1], 0.12, 1.0e-8);
+ BOOST_CHECK_CLOSE(values[2], 0.90, 1.0e-8);
+ }
+
+ BOOST_CHECK_MESSAGE(! check.isViolated(), "Test must not be violated");
+ BOOST_CHECK_MESSAGE(! check.isCritical(), "Test must not be violated at critical level");
+}
+
+BOOST_AUTO_TEST_CASE(All_Good_Sgcr_Same_As_Sgl)
+{
+ auto check = Checks::SGcr{};
+
+ {
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgl = 0.01;
+ endPoints.Sgcr = 0.01;
+ endPoints.Sgu = 0.9;
+
+ check.test(endPoints);
+ }
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_CLOSE(values[0], 0.01, 1.0e-8);
+ BOOST_CHECK_CLOSE(values[1], 0.01, 1.0e-8);
+ BOOST_CHECK_CLOSE(values[2], 0.90, 1.0e-8);
+ }
+
+ BOOST_CHECK_MESSAGE(! check.isViolated(), "Test must not be violated");
+ BOOST_CHECK_MESSAGE(! check.isCritical(), "Test must not be violated at critical level");
+}
+
+BOOST_AUTO_TEST_CASE(Non_Finite)
+{
+ // NaN
+ if constexpr (std::numeric_limits::has_quiet_NaN) {
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgl = std::numeric_limits::quiet_NaN();
+ endPoints.Sgcr = 0.125f;
+ endPoints.Sgu = 0.75f;
+
+ auto check = Checks::SGcr{};
+ check.test(endPoints);
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_MESSAGE(std::isnan(values[0]), "Sgl value must be NaN");
+ BOOST_CHECK_CLOSE (values[1], 0.125f, 1.0e-6f);
+ BOOST_CHECK_CLOSE (values[2], 0.75f, 1.0e-6f);
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+
+ endPoints.Sgl = 0.01f;
+ endPoints.Sgcr = std::numeric_limits::quiet_NaN();
+ endPoints.Sgu = 0.75;
+ check.test(endPoints);
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_CLOSE (values[0], 0.01f, 1.0e-6f);
+ BOOST_CHECK_MESSAGE(std::isnan(values[1]), "Sgcr value must be NaN");
+ BOOST_CHECK_CLOSE (values[2], 0.75f, 1.0e-6f);
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+
+ endPoints.Sgl = 0.01f;
+ endPoints.Sgcr = 0.125f;
+ endPoints.Sgu = std::numeric_limits::quiet_NaN();
+
+ check.test(endPoints);
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_CLOSE (values[0], 0.01f, 1.0e-6f);
+ BOOST_CHECK_CLOSE (values[1], 0.125f, 1.0e-6f);
+ BOOST_CHECK_MESSAGE(std::isnan(values[2]), "Sgu value must be NaN");
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+
+ endPoints.Sgl = std::numeric_limits::quiet_NaN();
+ endPoints.Sgcr = std::numeric_limits::quiet_NaN();
+ endPoints.Sgu = 0.75f;
+
+ check.test(endPoints);
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_MESSAGE(std::isnan(values[0]), "Sgl value must be NaN");
+ BOOST_CHECK_MESSAGE(std::isnan(values[1]), "Sgcr value must be NaN");
+ BOOST_CHECK_CLOSE (values[2], 0.75f, 1.0e-6f);
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+
+ endPoints.Sgl = std::numeric_limits::quiet_NaN();
+ endPoints.Sgcr = 0.125f;
+ endPoints.Sgu = std::numeric_limits::quiet_NaN();;
+
+ check.test(endPoints);
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_MESSAGE(std::isnan(values[0]), "Sgl value must be NaN");
+ BOOST_CHECK_CLOSE (values[1], 0.125f, 1.0e-6f);
+ BOOST_CHECK_MESSAGE(std::isnan(values[2]), "Sgu value must be NaN");
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+
+ endPoints.Sgl = 0.01f;
+ endPoints.Sgcr = std::numeric_limits::quiet_NaN();
+ endPoints.Sgu = std::numeric_limits::quiet_NaN();;
+
+ check.test(endPoints);
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_CLOSE (values[0], 0.01f, 1.0e-6f);
+ BOOST_CHECK_MESSAGE(std::isnan(values[1]), "Sgcr value must be NaN");
+ BOOST_CHECK_MESSAGE(std::isnan(values[2]), "Sgu value must be NaN");
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+
+ endPoints.Sgl = std::numeric_limits::quiet_NaN();
+ endPoints.Sgcr = std::numeric_limits::quiet_NaN();
+ endPoints.Sgu = std::numeric_limits::quiet_NaN();;
+
+ check.test(endPoints);
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_MESSAGE(std::isnan(values[0]), "Sgcr value must be NaN");
+ BOOST_CHECK_MESSAGE(std::isnan(values[1]), "Sgcr value must be NaN");
+ BOOST_CHECK_MESSAGE(std::isnan(values[2]), "Sgu value must be NaN");
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+ }
+
+ // Inf
+ if constexpr (std::numeric_limits::has_infinity) {
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgl = std::numeric_limits::infinity();
+ endPoints.Sgcr = 0.125f;
+ endPoints.Sgu = 0.75f;
+
+ auto check = Checks::SGcr{};
+ check.test(endPoints);
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_MESSAGE(std::isinf(values[0]), "Sgl value must be Inf");
+ BOOST_CHECK_CLOSE (values[1], 0.125f, 1.0e-6f);
+ BOOST_CHECK_CLOSE (values[2], 0.75f, 1.0e-6f);
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+
+ endPoints.Sgl = 0.01f;
+ endPoints.Sgcr = std::numeric_limits::infinity();
+ endPoints.Sgu = 0.75;
+ check.test(endPoints);
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_CLOSE (values[0], 0.01f, 1.0e-6f);
+ BOOST_CHECK_MESSAGE(std::isinf(values[1]), "Sgcr value must be Inf");
+ BOOST_CHECK_CLOSE (values[2], 0.75f, 1.0e-6f);
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+
+ endPoints.Sgl = 0.01f;
+ endPoints.Sgcr = 0.125f;
+ endPoints.Sgu = std::numeric_limits::infinity();
+
+ check.test(endPoints);
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_CLOSE (values[0], 0.01f, 1.0e-6f);
+ BOOST_CHECK_CLOSE (values[1], 0.125f, 1.0e-6f);
+ BOOST_CHECK_MESSAGE(std::isinf(values[2]), "Sgu value must be Inf");
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+
+ endPoints.Sgl = std::numeric_limits::infinity();
+ endPoints.Sgcr = std::numeric_limits::infinity();
+ endPoints.Sgu = 0.75f;
+
+ check.test(endPoints);
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_MESSAGE(std::isinf(values[0]), "Sgl value must be Inf");
+ BOOST_CHECK_MESSAGE(std::isinf(values[1]), "Sgcr value must be Inf");
+ BOOST_CHECK_CLOSE (values[2], 0.75f, 1.0e-6f);
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+
+ endPoints.Sgl = std::numeric_limits::infinity();
+ endPoints.Sgcr = 0.125f;
+ endPoints.Sgu = std::numeric_limits::infinity();;
+
+ check.test(endPoints);
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_MESSAGE(std::isinf(values[0]), "Sgl value must be Inf");
+ BOOST_CHECK_CLOSE (values[1], 0.125f, 1.0e-6f);
+ BOOST_CHECK_MESSAGE(std::isinf(values[2]), "Sgu value must be Inf");
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+
+ endPoints.Sgl = 0.01f;
+ endPoints.Sgcr = std::numeric_limits::infinity();
+ endPoints.Sgu = std::numeric_limits::infinity();;
+
+ check.test(endPoints);
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_CLOSE (values[0], 0.01f, 1.0e-6f);
+ BOOST_CHECK_MESSAGE(std::isinf(values[1]), "Sgcr value must be Inf");
+ BOOST_CHECK_MESSAGE(std::isinf(values[2]), "Sgu value must be Inf");
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+
+ endPoints.Sgl = std::numeric_limits::infinity();
+ endPoints.Sgcr = std::numeric_limits::infinity();
+ endPoints.Sgu = std::numeric_limits::infinity();;
+
+ check.test(endPoints);
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_MESSAGE(std::isinf(values[0]), "Sgcr value must be Inf");
+ BOOST_CHECK_MESSAGE(std::isinf(values[1]), "Sgcr value must be Inf");
+ BOOST_CHECK_MESSAGE(std::isinf(values[2]), "Sgu value must be Inf");
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Check must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Check must be violated at critical level");
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Sgcr_TooSmall)
+{
+ auto check = Checks::SGcr{};
+
+ {
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgl = 0.15;
+ endPoints.Sgcr = 0.125;
+ endPoints.Sgu = 0.9;
+
+ check.test(endPoints);
+ }
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_CLOSE(values[0], 0.15, 1.0e-8);
+ BOOST_CHECK_CLOSE(values[1], 0.125, 1.0e-8);
+ BOOST_CHECK_CLOSE(values[2], 0.9, 1.0e-8);
+ }
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Test must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Test must not be violated at critical level");
+}
+
+BOOST_AUTO_TEST_CASE(Sgcr_TooLarge)
+{
+ auto check = Checks::SGcr{};
+
+ {
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgl = 0.15;
+ endPoints.Sgcr = 0.65;
+ endPoints.Sgu = 0.6;
+
+ check.test(endPoints);
+ }
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_CLOSE(values[0], 0.15, 1.0e-8);
+ BOOST_CHECK_CLOSE(values[1], 0.65, 1.0e-8);
+ BOOST_CHECK_CLOSE(values[2], 0.6, 1.0e-8);
+ }
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Test must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Test must not be violated at critical level");
+}
+
+BOOST_AUTO_TEST_CASE(Sgcr_Same_As_Sgu)
+{
+ auto check = Checks::SGcr{};
+
+ {
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgl = 0.15;
+ endPoints.Sgcr = 0.65;
+ endPoints.Sgu = 0.65;
+
+ check.test(endPoints);
+ }
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_CLOSE(values[0], 0.15, 1.0e-8);
+ BOOST_CHECK_CLOSE(values[1], 0.65, 1.0e-8);
+ BOOST_CHECK_CLOSE(values[2], 0.65, 1.0e-8);
+ }
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Test must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Test must not be violated at critical level");
+}
+
+BOOST_AUTO_TEST_CASE(Sgu_TooSmall)
+{
+ auto check = Checks::SGcr{};
+
+ {
+ auto endPoints = Opm::EclEpsScalingPointsInfo{};
+ endPoints.Sgl = 0.15;
+ endPoints.Sgcr = 0.15;
+ endPoints.Sgu = 0.10;
+
+ check.test(endPoints);
+ }
+
+ {
+ auto values = std::vector(3);
+ check.exportCheckValues(values.data());
+
+ BOOST_CHECK_CLOSE(values[0], 0.15, 1.0e-8);
+ BOOST_CHECK_CLOSE(values[1], 0.15, 1.0e-8);
+ BOOST_CHECK_CLOSE(values[2], 0.10, 1.0e-8);
+ }
+
+ BOOST_CHECK_MESSAGE(check.isViolated(), "Test must be violated");
+ BOOST_CHECK_MESSAGE(check.isCritical(), "Test must not be violated at critical level");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // So_min