From be64ef3ef6f10c1131a24185fa8049910fceb528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Tue, 3 Sep 2024 16:35:50 +0200 Subject: [PATCH] Add Water Phase Saturation Function Consistency Checks This commit introduces a set of consistency checks for the water phase saturation functions. These plug into the framework introduced in commit c3939c544 (PR #5438). We implement the following three checks - 0 <= SWL < 1 - 0 < SWU <= 1 - SWL <= SWCR < SWU which collectively enable a non-negative oil saturation in the two phase oil/water system. --- CMakeLists_files.cmake | 3 + .../satfunc/WaterPhaseConsistencyChecks.cpp | 128 +++ .../satfunc/WaterPhaseConsistencyChecks.hpp | 205 +++++ tests/test_WaterSatfuncConsistencyChecks.cpp | 758 ++++++++++++++++++ 4 files changed, 1094 insertions(+) create mode 100644 opm/simulators/utils/satfunc/WaterPhaseConsistencyChecks.cpp create mode 100644 opm/simulators/utils/satfunc/WaterPhaseConsistencyChecks.hpp create mode 100644 tests/test_WaterSatfuncConsistencyChecks.cpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 2bc206e8e..09148a4dc 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -202,6 +202,7 @@ if (HAVE_ECL_INPUT) opm/simulators/utils/satfunc/OilPhaseConsistencyChecks.cpp opm/simulators/utils/satfunc/PhaseCheckBase.cpp opm/simulators/utils/satfunc/SatfuncConsistencyChecks.cpp + opm/simulators/utils/satfunc/WaterPhaseConsistencyChecks.cpp ) endif() @@ -389,6 +390,7 @@ if (HAVE_ECL_INPUT) tests/test_OilSatfuncConsistencyChecks.cpp tests/test_SatfuncConsistencyChecks.cpp tests/test_SatfuncConsistencyChecks_parallel.cpp + tests/test_WaterSatfuncConsistencyChecks.cpp ) endif() @@ -996,6 +998,7 @@ if (HAVE_ECL_INPUT) opm/simulators/utils/satfunc/OilPhaseConsistencyChecks.hpp opm/simulators/utils/satfunc/PhaseCheckBase.hpp opm/simulators/utils/satfunc/SatfuncConsistencyChecks.hpp + opm/simulators/utils/satfunc/WaterPhaseConsistencyChecks.hpp ) endif() diff --git a/opm/simulators/utils/satfunc/WaterPhaseConsistencyChecks.cpp b/opm/simulators/utils/satfunc/WaterPhaseConsistencyChecks.cpp new file mode 100644 index 000000000..9ae7e94b6 --- /dev/null +++ b/opm/simulators/utils/satfunc/WaterPhaseConsistencyChecks.cpp @@ -0,0 +1,128 @@ +/* + 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::Water::SWmin:: +testImpl(const EclEpsScalingPointsInfo& endPoints) +{ + // 0 <= SWL < 1 + + this->swl_ = endPoints.Swl; + + if (! std::isfinite(this->swl_)) { + this->setViolated(); + this->setCritical(); + + return; + } + + const auto low = this->swl_ < Scalar{0}; + const auto high = ! (this->swl_ < Scalar{1}); + + if (low || high) { + this->setViolated(); + this->setCritical(); + } +} + +// --------------------------------------------------------------------------- + +template +void Opm::Satfunc::PhaseChecks::Water::SWmax:: +testImpl(const EclEpsScalingPointsInfo& endPoints) +{ + // 0 < SWU <= 1 + + this->swu_ = endPoints.Swu; + + if (! std::isfinite(this->swu_)) { + this->setViolated(); + this->setCritical(); + + return; + } + + const auto low = ! (this->swu_ > Scalar{0}); + const auto high = this->swu_ > Scalar{1}; + + if (low || high) { + this->setViolated(); + this->setCritical(); + } +} + +// --------------------------------------------------------------------------- + +template +void Opm::Satfunc::PhaseChecks::Water::SWcr:: +testImpl(const EclEpsScalingPointsInfo& endPoints) +{ + // SWL <= SWCR < SWU + + this->swl_ = endPoints.Swl; + this->swcr_ = endPoints.Swcr; + this->swu_ = endPoints.Swu; + + if (! std::isfinite(this->swl_) || + ! std::isfinite(this->swcr_) || + ! std::isfinite(this->swu_)) + { + this->setViolated(); + this->setCritical(); + + return; + } + + const auto low = this->swcr_ < this->swl_; + const auto high = ! (this->swcr_ < this->swu_); + + if (low || high) { + this->setViolated(); + this->setCritical(); + } +} + +// =========================================================================== +// Explicit Specialisations of Individual Check Templates +// +// No other code below this separator +// =========================================================================== + +template class Opm::Satfunc::PhaseChecks::Water::SWmin; +template class Opm::Satfunc::PhaseChecks::Water::SWmin; + +// --------------------------------------------------------------------------- + +template class Opm::Satfunc::PhaseChecks::Water::SWmax; +template class Opm::Satfunc::PhaseChecks::Water::SWmax; + +// --------------------------------------------------------------------------- + +template class Opm::Satfunc::PhaseChecks::Water::SWcr; +template class Opm::Satfunc::PhaseChecks::Water::SWcr; diff --git a/opm/simulators/utils/satfunc/WaterPhaseConsistencyChecks.hpp b/opm/simulators/utils/satfunc/WaterPhaseConsistencyChecks.hpp new file mode 100644 index 000000000..aab6db022 --- /dev/null +++ b/opm/simulators/utils/satfunc/WaterPhaseConsistencyChecks.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 WATER_PHASE_CONSISTENCY_CHECKS_HPP_INCLUDED +#define WATER_PHASE_CONSISTENCY_CHECKS_HPP_INCLUDED + +#include +#include + +#include +#include + +namespace Opm::Satfunc::PhaseChecks::Water { + + /// Verify that minimum gas saturation is in valid range + /// + /// \tparam Scalar Element type. Typically \c float or \c double. + template + class SWmin : 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->swl_; + } + + /// Descriptive textual summary of this check. + std::string description() const override + { + return { "Non-negative minimum water saturation" }; + } + + /// Textual representation of the consistency condition. + std::string condition() const override + { + return { "0 <= SWL < 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] = "SWL"; + } + + private: + /// Minimum (connate) water saturation. + Scalar swl_; + + /// 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 SWmax : 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->swu_; + } + + /// Descriptive textual summary of this check. + std::string description() const override + { + return { "Positive maximum water saturation" }; + } + + /// Textual representation of the consistency condition. + std::string condition() const override + { + return { "0 < SWU <= 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] = "SWU"; + } + + private: + /// Maximum water saturation. + Scalar swu_; + + /// 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 SWcr : 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->swl_; + exportedCheckValues[1] = this->swcr_; + exportedCheckValues[2] = this->swu_; + } + + /// Descriptive textual summary of this check. + std::string description() const override + { + return { "Mobile water saturation" }; + } + + /// Textual representation of the consistency condition. + std::string condition() const override + { + return { "SWL <= SWCR < SWU" }; + } + + /// 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] = "SWL"; + headers[1] = "SWCR"; + headers[2] = "SWU"; + } + + private: + /// Minimum (connate) water saturation. + Scalar swl_; + + /// Critical water saturation. + Scalar swcr_; + + /// Maximum water saturation. + Scalar swu_; + + /// 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::Water + +#endif // WATER_PHASE_CONSISTENCY_CHECKS_HPP_INCLUDED diff --git a/tests/test_WaterSatfuncConsistencyChecks.cpp b/tests/test_WaterSatfuncConsistencyChecks.cpp new file mode 100644 index 000000000..449b3dd22 --- /dev/null +++ b/tests/test_WaterSatfuncConsistencyChecks.cpp @@ -0,0 +1,758 @@ +/* + 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 TestWaterPhaseConsistencyChecks + +#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::Water; + +// =========================================================================== + +BOOST_AUTO_TEST_SUITE(Sw_min) + +BOOST_AUTO_TEST_CASE(All_Good) +{ + auto check = Checks::SWmin{}; + + BOOST_CHECK_EQUAL(check.numExportedCheckValues(), std::size_t{1}); + + { + auto column = std::string{}; + check.columnNames(&column); + + BOOST_CHECK_EQUAL(column, "SWL"); + } + + { + auto endPoints = Opm::EclEpsScalingPointsInfo{}; + endPoints.Swl = 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.Swl = std::numeric_limits::quiet_NaN(); + + auto check = Checks::SWmin{}; + check.test(endPoints); + + auto value = -0.1f; + check.exportCheckValues(&value); + + BOOST_CHECK_MESSAGE(std::isnan(value), "Swl 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.Swl = std::numeric_limits::infinity(); + + auto check = Checks::SWmin{}; + check.test(endPoints); + + auto value = -0.1f; + check.exportCheckValues(&value); + + BOOST_CHECK_MESSAGE(std::isinf(value), "Swl 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::SWmin{}; + auto endPoints = Opm::EclEpsScalingPointsInfo{}; + endPoints.Swl = -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::SWmin{}; + auto endPoints = Opm::EclEpsScalingPointsInfo{}; + endPoints.Swl = 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::SWmin{}; + auto endPoints = Opm::EclEpsScalingPointsInfo{}; + endPoints.Swl = 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() // Sw_min + +// --------------------------------------------------------------------------- + +BOOST_AUTO_TEST_SUITE(Sw_max) + +BOOST_AUTO_TEST_CASE(All_Good) +{ + auto check = Checks::SWmax{}; + + BOOST_CHECK_EQUAL(check.numExportedCheckValues(), std::size_t{1}); + + { + auto column = std::string{}; + check.columnNames(&column); + + BOOST_CHECK_EQUAL(column, "SWU"); + } + + { + auto endPoints = Opm::EclEpsScalingPointsInfo{}; + endPoints.Swu = 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(All_Good_Swu_Is_One) +{ + auto check = Checks::SWmax{}; + auto endPoints = Opm::EclEpsScalingPointsInfo{}; + endPoints.Swu = 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(Non_Finite) +{ + // NaN + if constexpr (std::numeric_limits::has_quiet_NaN) { + auto endPoints = Opm::EclEpsScalingPointsInfo{}; + endPoints.Swu = std::numeric_limits::quiet_NaN(); + + auto check = Checks::SWmax{}; + check.test(endPoints); + + auto value = -0.1f; + check.exportCheckValues(&value); + + BOOST_CHECK_MESSAGE(std::isnan(value), "Swu 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.Swu = std::numeric_limits::infinity(); + + auto check = Checks::SWmax{}; + check.test(endPoints); + + auto value = -0.1f; + check.exportCheckValues(&value); + + BOOST_CHECK_MESSAGE(std::isinf(value), "Swu 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::SWmax{}; + auto endPoints = Opm::EclEpsScalingPointsInfo{}; + endPoints.Swu = -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(Zero) +{ + auto check = Checks::SWmax{}; + auto endPoints = Opm::EclEpsScalingPointsInfo{}; + endPoints.Swu = 0.0; // <= 0 + + check.test(endPoints); + + { + auto value = 1.0; + check.exportCheckValues(&value); + + BOOST_CHECK_CLOSE(value, 0.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::SWmax{}; + auto endPoints = Opm::EclEpsScalingPointsInfo{}; + endPoints.Swu = 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() // Sw_max + +// --------------------------------------------------------------------------- + +BOOST_AUTO_TEST_SUITE(Sw_cr) + +BOOST_AUTO_TEST_CASE(All_Good) +{ + auto check = Checks::SWcr{}; + + BOOST_CHECK_EQUAL(check.numExportedCheckValues(), std::size_t{3}); + + { + auto columns = std::vector(3); + check.columnNames(columns.data()); + + BOOST_CHECK_EQUAL(columns[0], "SWL"); + BOOST_CHECK_EQUAL(columns[1], "SWCR"); + BOOST_CHECK_EQUAL(columns[2], "SWU"); + } + + { + auto endPoints = Opm::EclEpsScalingPointsInfo{}; + endPoints.Swl = 0.09; + endPoints.Swcr = 0.12; + endPoints.Swu = 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_Swcr_Same_As_Swl) +{ + auto check = Checks::SWcr{}; + + { + auto endPoints = Opm::EclEpsScalingPointsInfo{}; + endPoints.Swl = 0.01; + endPoints.Swcr = 0.01; + endPoints.Swu = 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.Swl = std::numeric_limits::quiet_NaN(); + endPoints.Swcr = 0.125f; + endPoints.Swu = 0.75f; + + auto check = Checks::SWcr{}; + check.test(endPoints); + + { + auto values = std::vector(3); + check.exportCheckValues(values.data()); + + BOOST_CHECK_MESSAGE(std::isnan(values[0]), "Swl 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.Swl = 0.01f; + endPoints.Swcr = std::numeric_limits::quiet_NaN(); + endPoints.Swu = 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]), "Swcr 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.Swl = 0.01f; + endPoints.Swcr = 0.125f; + endPoints.Swu = 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]), "Swu 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.Swl = std::numeric_limits::quiet_NaN(); + endPoints.Swcr = std::numeric_limits::quiet_NaN(); + endPoints.Swu = 0.75f; + + check.test(endPoints); + + { + auto values = std::vector(3); + check.exportCheckValues(values.data()); + + BOOST_CHECK_MESSAGE(std::isnan(values[0]), "Swl value must be NaN"); + BOOST_CHECK_MESSAGE(std::isnan(values[1]), "Swcr 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.Swl = std::numeric_limits::quiet_NaN(); + endPoints.Swcr = 0.125f; + endPoints.Swu = std::numeric_limits::quiet_NaN();; + + check.test(endPoints); + + { + auto values = std::vector(3); + check.exportCheckValues(values.data()); + + BOOST_CHECK_MESSAGE(std::isnan(values[0]), "Swl value must be NaN"); + BOOST_CHECK_CLOSE (values[1], 0.125f, 1.0e-6f); + BOOST_CHECK_MESSAGE(std::isnan(values[2]), "Swu 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.Swl = 0.01f; + endPoints.Swcr = std::numeric_limits::quiet_NaN(); + endPoints.Swu = 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]), "Swcr value must be NaN"); + BOOST_CHECK_MESSAGE(std::isnan(values[2]), "Swu 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.Swl = std::numeric_limits::quiet_NaN(); + endPoints.Swcr = std::numeric_limits::quiet_NaN(); + endPoints.Swu = std::numeric_limits::quiet_NaN();; + + check.test(endPoints); + + { + auto values = std::vector(3); + check.exportCheckValues(values.data()); + + BOOST_CHECK_MESSAGE(std::isnan(values[0]), "Swcr value must be NaN"); + BOOST_CHECK_MESSAGE(std::isnan(values[1]), "Swcr value must be NaN"); + BOOST_CHECK_MESSAGE(std::isnan(values[2]), "Swu 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.Swl = std::numeric_limits::infinity(); + endPoints.Swcr = 0.125f; + endPoints.Swu = 0.75f; + + auto check = Checks::SWcr{}; + check.test(endPoints); + + { + auto values = std::vector(3); + check.exportCheckValues(values.data()); + + BOOST_CHECK_MESSAGE(std::isinf(values[0]), "Swl 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.Swl = 0.01f; + endPoints.Swcr = std::numeric_limits::infinity(); + endPoints.Swu = 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]), "Swcr 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.Swl = 0.01f; + endPoints.Swcr = 0.125f; + endPoints.Swu = 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]), "Swu 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.Swl = std::numeric_limits::infinity(); + endPoints.Swcr = std::numeric_limits::infinity(); + endPoints.Swu = 0.75f; + + check.test(endPoints); + + { + auto values = std::vector(3); + check.exportCheckValues(values.data()); + + BOOST_CHECK_MESSAGE(std::isinf(values[0]), "Swl value must be Inf"); + BOOST_CHECK_MESSAGE(std::isinf(values[1]), "Swcr 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.Swl = std::numeric_limits::infinity(); + endPoints.Swcr = 0.125f; + endPoints.Swu = std::numeric_limits::infinity();; + + check.test(endPoints); + + { + auto values = std::vector(3); + check.exportCheckValues(values.data()); + + BOOST_CHECK_MESSAGE(std::isinf(values[0]), "Swl value must be Inf"); + BOOST_CHECK_CLOSE (values[1], 0.125f, 1.0e-6f); + BOOST_CHECK_MESSAGE(std::isinf(values[2]), "Swu 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.Swl = 0.01f; + endPoints.Swcr = std::numeric_limits::infinity(); + endPoints.Swu = 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]), "Swcr value must be Inf"); + BOOST_CHECK_MESSAGE(std::isinf(values[2]), "Swu 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.Swl = std::numeric_limits::infinity(); + endPoints.Swcr = std::numeric_limits::infinity(); + endPoints.Swu = std::numeric_limits::infinity();; + + check.test(endPoints); + + { + auto values = std::vector(3); + check.exportCheckValues(values.data()); + + BOOST_CHECK_MESSAGE(std::isinf(values[0]), "Swcr value must be Inf"); + BOOST_CHECK_MESSAGE(std::isinf(values[1]), "Swcr value must be Inf"); + BOOST_CHECK_MESSAGE(std::isinf(values[2]), "Swu 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(Swcr_TooSmall) +{ + auto check = Checks::SWcr{}; + + { + auto endPoints = Opm::EclEpsScalingPointsInfo{}; + endPoints.Swl = 0.15; + endPoints.Swcr = 0.125; + endPoints.Swu = 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(Swcr_TooLarge) +{ + auto check = Checks::SWcr{}; + + { + auto endPoints = Opm::EclEpsScalingPointsInfo{}; + endPoints.Swl = 0.15; + endPoints.Swcr = 0.65; + endPoints.Swu = 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(Swcr_Same_As_Swu) +{ + auto check = Checks::SWcr{}; + + { + auto endPoints = Opm::EclEpsScalingPointsInfo{}; + endPoints.Swl = 0.15; + endPoints.Swcr = 0.65; + endPoints.Swu = 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(Swu_TooSmall) +{ + auto check = Checks::SWcr{}; + + { + auto endPoints = Opm::EclEpsScalingPointsInfo{}; + endPoints.Swl = 0.15; + endPoints.Swcr = 0.15; + endPoints.Swu = 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