From 9c246faefab49baeaa67087310ac181bb4a291d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Tue, 3 Sep 2024 15:30:22 +0200 Subject: [PATCH] Add Gas Phase Saturation Function Consistency Checks This commit introduces a set of consistency checks for the gas phase saturation functions. These plug into the framework introduced in commit c3939c544 (PR #5438). We implement the following three checks - 0 <= SGL < 1 - 0 <= SGU < 1 - SGL <= SGCR < SGU which collectively enable a non-negative oil saturation in the two phase gas/oil system. --- CMakeLists_files.cmake | 3 + .../satfunc/GasPhaseConsistencyChecks.cpp | 122 +++ .../satfunc/GasPhaseConsistencyChecks.hpp | 205 +++++ tests/test_GasSatfuncConsistencyChecks.cpp | 739 ++++++++++++++++++ 4 files changed, 1069 insertions(+) create mode 100644 opm/simulators/utils/satfunc/GasPhaseConsistencyChecks.cpp create mode 100644 opm/simulators/utils/satfunc/GasPhaseConsistencyChecks.hpp create mode 100644 tests/test_GasSatfuncConsistencyChecks.cpp 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