From 5638e062546f490d7fcdc7b232ee78af578c94c5 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 18 Feb 2022 21:53:52 -0600 Subject: [PATCH 1/8] Update Copyright information --- License.txt | 2 +- doc/sphinx/conf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/License.txt b/License.txt index 6e3c0940f7..bdca2cd929 100644 --- a/License.txt +++ b/License.txt @@ -6,7 +6,7 @@ Copyright (c) 2009 Sandia Corporation. Under the terms of Contract AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. -Copyright (c) 2011-2021, Cantera Developers. +Copyright (c) 2011-2022, Cantera Developers. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/doc/sphinx/conf.py b/doc/sphinx/conf.py index e0503d6ae4..2b1f0171d7 100644 --- a/doc/sphinx/conf.py +++ b/doc/sphinx/conf.py @@ -116,7 +116,7 @@ def escape_splats(app, what, name, obj, options, lines): # General information about the project. project = 'Cantera' -copyright = '2001-2021, Cantera Developers' +copyright = "2001-2022, Cantera Developers" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From 104628f0c643bc83b5813dbaf2b86cd81d24415c Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 19 Feb 2022 14:16:37 -0600 Subject: [PATCH 2/8] [Base] Enable generic warning messages --- include/cantera/base/global.h | 49 +++++++++++++++++++++++++++++------ src/base/global.cpp | 7 ++--- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/include/cantera/base/global.h b/include/cantera/base/global.h index 4293714fa3..927167f2b1 100644 --- a/include/cantera/base/global.h +++ b/include/cantera/base/global.h @@ -192,18 +192,35 @@ void writelogendl(); void writeline(char repeat, size_t count, bool endl_after=true, bool endl_before=false); -//! @copydoc Application::warn_deprecated -void warn_deprecated(const std::string& method, const std::string& extra=""); +//! helper function passing deprecation warning to global handler +void _warn_deprecated(const std::string& method, const std::string& extra=""); //! @copydoc Application::suppress_deprecation_warnings void suppress_deprecation_warnings(); -//! helper function passing user warning to global handler -void _warn_user(const std::string& method, const std::string& extra); +//! helper function passing generic warning to global handler +void _warn(const std::string& warning, + const std::string& method, const std::string& extra); +//! Print a generic warning raised from *method*. @see Application::warn +/*! + * @param warning type of warning; @see Logger::warn + * @param method method name + * @param msg Python-style format string with the following arguments + * @param args arguments for the format string + */ +template +void warn(const std::string& warning, const std::string& method, + const std::string& msg, const Args&... args) { + if (sizeof...(args) == 0) { + _warn(warning, method, msg); + } else { + _warn(warning, method, fmt::format(msg, args...)); + } +} + +//! Print a user warning raised from *method*. /*! - * Print a user warning raised from *method*. - * * @param method method name * @param msg Python-style format string with the following arguments * @param args arguments for the format string @@ -212,9 +229,25 @@ template void warn_user(const std::string& method, const std::string& msg, const Args&... args) { if (sizeof...(args) == 0) { - _warn_user(method, msg); + _warn("User", method, msg); + } else { + _warn("User", method, fmt::format(msg, args...)); + } +} + +//! Print a deprecation warning raised from *method*. @see Application::warn_deprecated +/*! + * @param method method name + * @param msg Python-style format string with the following arguments + * @param args arguments for the format string + */ +template +void warn_deprecated(const std::string& method, const std::string& msg, + const Args&... args) { + if (sizeof...(args) == 0) { + _warn_deprecated(method, msg); } else { - _warn_user(method, fmt::format(msg, args...)); + _warn_deprecated(method, fmt::format(msg, args...)); } } diff --git a/src/base/global.cpp b/src/base/global.cpp index 87cf410849..a644617ccb 100644 --- a/src/base/global.cpp +++ b/src/base/global.cpp @@ -55,14 +55,15 @@ void writeline(char repeat, size_t count, bool endl_after, bool endl_before) } } -void warn_deprecated(const std::string& method, const std::string& extra) +void _warn_deprecated(const std::string& method, const std::string& extra) { app()->warn_deprecated(method, extra); } -void _warn_user(const std::string& method, const std::string& extra) +void _warn(const std::string& warning, + const std::string& method, const std::string& extra) { - app()->warn_user(method, extra); + app()->warn(warning, method, extra); } void suppress_deprecation_warnings() From 9eabfdb668cdd86ef04354362b7c881b8bfda136 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 18 Feb 2022 20:13:16 -0600 Subject: [PATCH 3/8] [Base] Add dedicated loggers for warnings Raise warnings as native Python warnings --- include/cantera/base/global.h | 6 +++--- include/cantera/base/logger.h | 16 ++++++++++++++++ include/cantera/cython/wrappers.h | 31 +++++++++++++++++++++++++++++-- src/base/application.cpp | 23 +++++++++++++++++------ src/base/application.h | 30 +++++++++++++++++++++++++++--- 5 files changed, 92 insertions(+), 14 deletions(-) diff --git a/include/cantera/base/global.h b/include/cantera/base/global.h index 927167f2b1..6049e18ba5 100644 --- a/include/cantera/base/global.h +++ b/include/cantera/base/global.h @@ -219,7 +219,7 @@ void warn(const std::string& warning, const std::string& method, } } -//! Print a user warning raised from *method*. +//! Print a user warning raised from *method* as `CanteraWarning`. /*! * @param method method name * @param msg Python-style format string with the following arguments @@ -229,9 +229,9 @@ template void warn_user(const std::string& method, const std::string& msg, const Args&... args) { if (sizeof...(args) == 0) { - _warn("User", method, msg); + _warn("Cantera", method, msg); } else { - _warn("User", method, fmt::format(msg, args...)); + _warn("Cantera", method, fmt::format(msg, args...)); } } diff --git a/include/cantera/base/logger.h b/include/cantera/base/logger.h index 89bdac63c0..8c6a66736d 100644 --- a/include/cantera/base/logger.h +++ b/include/cantera/base/logger.h @@ -67,6 +67,22 @@ class Logger std::cout << std::endl; } + //! Write a warning message. + /*! + * The default behavior is to write to the logging output. + * @param warning String specifying type of warning; @see Logger::warn + * @param msg String message to be written to cout + */ + virtual void warn(const std::string& warning, const std::string& msg) { + + std::clog << warning << "Warning: " << msg; + } + + //! Write an end of line character to the warning and flush output. + virtual void warnendl() { + std::clog << std::endl; + } + //! Write an error message and quit. /*! * The default behavior is to write to the standard error stream, and then diff --git a/include/cantera/cython/wrappers.h b/include/cantera/cython/wrappers.h index c9b7a245ef..228ba34eed 100644 --- a/include/cantera/cython/wrappers.h +++ b/include/cantera/cython/wrappers.h @@ -9,6 +9,23 @@ #include "Python.h" +// Warning types supported by the Python C-API. +// See https://docs.python.org/3/c-api/exceptions.html#issuing-warnings +std::map mapped_PyWarnings = { + {"", PyExc_Warning}, + {"Bytes", PyExc_BytesWarning}, + {"Cantera", PyExc_UserWarning}, // pre-existing warning + {"Deprecation", PyExc_DeprecationWarning}, + {"Future", PyExc_FutureWarning}, + {"Import", PyExc_ImportWarning}, + {"PendingDeprecation", PyExc_PendingDeprecationWarning}, + {"Resource", PyExc_ResourceWarning}, + {"Runtime", PyExc_RuntimeWarning}, + {"Syntax", PyExc_SyntaxWarning}, + {"Unicode", PyExc_UnicodeWarning}, + {"User", PyExc_UserWarning} +}; + // Wrappers for preprocessor defines std::string get_cantera_version() { @@ -37,9 +54,19 @@ class PythonLogger : public Cantera::Logger std::cout.flush(); } + virtual void warn(const std::string& warning, const std::string& msg) { + if (mapped_PyWarnings.find(warning) != mapped_PyWarnings.end()) { + PyErr_WarnEx(mapped_PyWarnings[warning], msg.c_str(), 1); + } else { + // issue generic warning + PyErr_WarnEx(PyExc_Warning, msg.c_str(), 1); + } + } + + virtual void warnendl() {} + virtual void error(const std::string& msg) { - std::string err = "raise Exception('''"+msg+"''')"; - PyRun_SimpleString(err.c_str()); + PyErr_SetString(PyExc_RuntimeError, msg.c_str()); } }; diff --git a/src/base/application.cpp b/src/base/application.cpp index 427f6f90c8..eb49fb7455 100644 --- a/src/base/application.cpp +++ b/src/base/application.cpp @@ -97,6 +97,16 @@ void Application::Messages::writelogendl() logwriter->writeendl(); } +void Application::Messages::warnlog(const std::string& warning, const std::string& msg) +{ + logwriter->warn(warning, msg); +} + +void Application::Messages::warnlogendl() +{ + logwriter->warnendl(); +} + //! Mutex for access to string messages static std::mutex msg_mutex; @@ -177,20 +187,21 @@ void Application::warn_deprecated(const std::string& method, return; } warnings.insert(method); - writelog(fmt::format("DeprecationWarning: {}: {}", method, extra)); - writelogendl(); + warnlog("Deprecation", fmt::format("{}: {}", method, extra)); + warnlogendl(); } -void Application::warn_user(const std::string& method, - const std::string& extra) +void Application::warn(const std::string& warning, + const std::string& method, + const std::string& extra) { if (m_fatal_warnings) { throw CanteraError(method, extra); } else if (m_suppress_warnings) { return; } - writelog(fmt::format("CanteraWarning: {}: {}", method, extra)); - writelogendl(); + warnlog(warning, fmt::format("{}: {}", method, extra)); + warnlogendl(); } void Application::thread_complete() diff --git a/src/base/application.h b/src/base/application.h index 31ea365b0c..652998ec96 100644 --- a/src/base/application.h +++ b/src/base/application.h @@ -137,6 +137,17 @@ class Application //! Write an end of line character to the screen and flush output void writelogendl(); + //! Write a warning message to the screen. + /*! + * @param warning String specifying type of warning; @see Logger::warn + * @param msg String to be written to the screen + * @ingroup textlogs + */ + void warnlog(const std::string& warning, const std::string& msg); + + //! Write an end of line character to the screen and flush output + void warnlogendl(); + //! Install a logger. /*! * Called by the language interfaces to install an appropriate logger. @@ -331,6 +342,16 @@ class Application pMessenger->writelogendl(); } + //! @copydoc Messages::warnlog + void warnlog(const std::string& warning, const std::string& msg) { + pMessenger->warnlog(warning, msg); + } + + //! Write an endl to the screen and flush output + void warnlogendl() { + pMessenger->warnlogendl(); + } + //! Print a warning indicating that *method* is deprecated. Additional //! information (removal version, alternatives) can be specified in //! *extra*. Deprecation warnings are printed once per method per @@ -350,9 +371,12 @@ class Application m_fatal_deprecation_warnings = true; } - //! Print a user warning arising during usage of *method*. Additional - //! information can be specified in *extra*. - void warn_user(const std::string& method, const std::string& extra=""); + //! Generate a general purpose warning; repeated warnings are not suppressed + //! @param warning Warning type; @see Logger::warn + //! @param method Name of method triggering the warning + //! @param extra Additional information printed for the warning + void warn(const std::string& warning, + const std::string& method, const std::string& extra=""); //! Globally disable printing of (user) warnings. Used primarily to //! prevent certain tests from failing. From 957ee1ac51ca6b425d7806dfc2326cedc55c5592 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 19 Feb 2022 07:45:50 -0600 Subject: [PATCH 4/8] [Samples] Suppress DeprecationWarning in Python example --- .../cython/cantera/examples/kinetics/custom_reactions.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/interfaces/cython/cantera/examples/kinetics/custom_reactions.py b/interfaces/cython/cantera/examples/kinetics/custom_reactions.py index 3d39a3dcb9..a8054fc3c4 100644 --- a/interfaces/cython/cantera/examples/kinetics/custom_reactions.py +++ b/interfaces/cython/cantera/examples/kinetics/custom_reactions.py @@ -9,6 +9,7 @@ from timeit import default_timer import numpy as np from math import exp +import warnings import cantera as ct @@ -29,9 +30,12 @@ gas1 = ct.Solution(thermo='ideal-gas', kinetics='gas', species=species, reactions=custom_reactions) -# old framework - use xml input +# old framework - use xml input (to be removed after Cantera 2.6) -gas2 = ct.Solution(mech.replace('.yaml', '.xml')) +with warnings.catch_warnings(): + # suppress warning: XML input is deprecated + warnings.simplefilter("ignore") + gas2 = ct.Solution(mech.replace(".yaml", ".xml")) # construct test case - simulate ignition From 61e9f3a55686a97da45fc280479a8b6f63710f82 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 22 Feb 2022 10:52:58 -0600 Subject: [PATCH 5/8] [Python] Remove C++ warning infrastructure --- interfaces/cython/cantera/_cantera.pxd | 3 --- interfaces/cython/cantera/utils.pyx | 9 --------- 2 files changed, 12 deletions(-) diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 04ba9e836b..cd86b63ebc 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -173,9 +173,6 @@ cdef extern from "cantera/base/global.h" namespace "Cantera": cdef XML_Node* CxxGetXmlFile "Cantera::get_XML_File" (string) except +translate_exception cdef XML_Node* CxxGetXmlFromString "Cantera::get_XML_from_string" (string) except +translate_exception cdef void Cxx_make_deprecation_warnings_fatal "Cantera::make_deprecation_warnings_fatal" () - cdef void Cxx_make_warnings_fatal "Cantera::make_warnings_fatal" () - cdef void Cxx_suppress_warnings "Cantera::suppress_warnings" () - cdef cbool Cxx_warnings_suppressed "Cantera::warnings_suppressed" () cdef void Cxx_suppress_deprecation_warnings "Cantera::suppress_deprecation_warnings" () cdef void Cxx_suppress_thermo_warnings "Cantera::suppress_thermo_warnings" (cbool) cdef void Cxx_use_legacy_rate_constants "Cantera::use_legacy_rate_constants" (cbool) diff --git a/interfaces/cython/cantera/utils.pyx b/interfaces/cython/cantera/utils.pyx index 0298d3f472..1b77b0aba5 100644 --- a/interfaces/cython/cantera/utils.pyx +++ b/interfaces/cython/cantera/utils.pyx @@ -70,15 +70,6 @@ def make_deprecation_warnings_fatal(): message='.*Cantera.*') # for warnings in Cython code Cxx_make_deprecation_warnings_fatal() -def suppress_warnings(): # for warnings in C++ code - Cxx_suppress_warnings() - -def warnings_suppressed(): # for warnings in C++ code - return Cxx_warnings_suppressed() - -def make_warnings_fatal():# for warnings in Cython code - Cxx_make_warnings_fatal() - def suppress_deprecation_warnings(): warnings.filterwarnings('ignore', category=DeprecationWarning, module='cantera') # for warnings in Python code From dee1d8ae0ee83d98fd30f36d0648099e2eb9bd2d Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 22 Feb 2022 11:20:31 -0600 Subject: [PATCH 6/8] Implement review suggestions --- include/cantera/base/global.h | 32 +++++++++++++++---------------- include/cantera/base/logger.h | 8 +------- include/cantera/cython/wrappers.h | 2 -- src/base/application.cpp | 7 ------- src/base/application.h | 8 -------- 5 files changed, 17 insertions(+), 40 deletions(-) diff --git a/include/cantera/base/global.h b/include/cantera/base/global.h index 6049e18ba5..5e8da9b66a 100644 --- a/include/cantera/base/global.h +++ b/include/cantera/base/global.h @@ -195,6 +195,22 @@ void writeline(char repeat, size_t count, //! helper function passing deprecation warning to global handler void _warn_deprecated(const std::string& method, const std::string& extra=""); +//! Print a deprecation warning raised from *method*. @see Application::warn_deprecated +/*! + * @param method method name + * @param msg Python-style format string with the following arguments + * @param args arguments for the format string + */ +template +void warn_deprecated(const std::string& method, const std::string& msg, + const Args&... args) { + if (sizeof...(args) == 0) { + _warn_deprecated(method, msg); + } else { + _warn_deprecated(method, fmt::format(msg, args...)); + } +} + //! @copydoc Application::suppress_deprecation_warnings void suppress_deprecation_warnings(); @@ -235,22 +251,6 @@ void warn_user(const std::string& method, const std::string& msg, } } -//! Print a deprecation warning raised from *method*. @see Application::warn_deprecated -/*! - * @param method method name - * @param msg Python-style format string with the following arguments - * @param args arguments for the format string - */ -template -void warn_deprecated(const std::string& method, const std::string& msg, - const Args&... args) { - if (sizeof...(args) == 0) { - _warn_deprecated(method, msg); - } else { - _warn_deprecated(method, fmt::format(msg, args...)); - } -} - //! @copydoc Application::make_deprecation_warnings_fatal void make_deprecation_warnings_fatal(); diff --git a/include/cantera/base/logger.h b/include/cantera/base/logger.h index 8c6a66736d..3be3f1e0d9 100644 --- a/include/cantera/base/logger.h +++ b/include/cantera/base/logger.h @@ -74,13 +74,7 @@ class Logger * @param msg String message to be written to cout */ virtual void warn(const std::string& warning, const std::string& msg) { - - std::clog << warning << "Warning: " << msg; - } - - //! Write an end of line character to the warning and flush output. - virtual void warnendl() { - std::clog << std::endl; + std::clog << warning << "Warning: " << msg << std::endl; } //! Write an error message and quit. diff --git a/include/cantera/cython/wrappers.h b/include/cantera/cython/wrappers.h index 228ba34eed..c669aea407 100644 --- a/include/cantera/cython/wrappers.h +++ b/include/cantera/cython/wrappers.h @@ -63,8 +63,6 @@ class PythonLogger : public Cantera::Logger } } - virtual void warnendl() {} - virtual void error(const std::string& msg) { PyErr_SetString(PyExc_RuntimeError, msg.c_str()); } diff --git a/src/base/application.cpp b/src/base/application.cpp index eb49fb7455..eae08703e6 100644 --- a/src/base/application.cpp +++ b/src/base/application.cpp @@ -102,11 +102,6 @@ void Application::Messages::warnlog(const std::string& warning, const std::strin logwriter->warn(warning, msg); } -void Application::Messages::warnlogendl() -{ - logwriter->warnendl(); -} - //! Mutex for access to string messages static std::mutex msg_mutex; @@ -188,7 +183,6 @@ void Application::warn_deprecated(const std::string& method, } warnings.insert(method); warnlog("Deprecation", fmt::format("{}: {}", method, extra)); - warnlogendl(); } void Application::warn(const std::string& warning, @@ -201,7 +195,6 @@ void Application::warn(const std::string& warning, return; } warnlog(warning, fmt::format("{}: {}", method, extra)); - warnlogendl(); } void Application::thread_complete() diff --git a/src/base/application.h b/src/base/application.h index 652998ec96..1403571afd 100644 --- a/src/base/application.h +++ b/src/base/application.h @@ -145,9 +145,6 @@ class Application */ void warnlog(const std::string& warning, const std::string& msg); - //! Write an end of line character to the screen and flush output - void warnlogendl(); - //! Install a logger. /*! * Called by the language interfaces to install an appropriate logger. @@ -347,11 +344,6 @@ class Application pMessenger->warnlog(warning, msg); } - //! Write an endl to the screen and flush output - void warnlogendl() { - pMessenger->warnlogendl(); - } - //! Print a warning indicating that *method* is deprecated. Additional //! information (removal version, alternatives) can be specified in //! *extra*. Deprecation warnings are printed once per method per From 9786293708c3c7c970fa1b71dc3153c6642153ba Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 19 Feb 2022 16:41:27 -0600 Subject: [PATCH 7/8] [UnitTests] Replace C++ warning suppression by native Python handling --- .../cython/cantera/test/test_jacobian.py | 26 +++++++++++-------- .../cython/cantera/test/test_kinetics.py | 8 +++--- .../cython/cantera/test/test_reaction.py | 25 +++++++++++------- interfaces/cython/cantera/test/utilities.py | 9 +++++++ 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/interfaces/cython/cantera/test/test_jacobian.py b/interfaces/cython/cantera/test/test_jacobian.py index 68dd54d448..48fe851fdc 100644 --- a/interfaces/cython/cantera/test/test_jacobian.py +++ b/interfaces/cython/cantera/test/test_jacobian.py @@ -1,4 +1,5 @@ import numpy as np +import pytest import cantera as ct from . import utilities @@ -478,17 +479,20 @@ def setUpClass(cls): # species: [AR, O, H2, H, OH, O2, H2O, H2O2, HO2] cls.gas.X = [0.1, 3e-4, 5e-5, 6e-6, 3e-3, 0.6, 0.25, 1e-6, 2e-5] cls.gas.TP = 2000, 5 * ct.one_atm + super().setUpClass() - # suppress user warning (e.g. temperature derivative of Blowers-Masel) - cls.warnings_suppressed = ct.warnings_suppressed() - ct.suppress_warnings() + @utilities.has_temperature_derivative_warnings + def test_forward_rop_ddT(self): + super().test_forward_rop_ddT() - super().setUpClass() + @utilities.has_temperature_derivative_warnings + def test_reverse_rop_ddT(self): + super().test_reverse_rop_ddT() + + @utilities.has_temperature_derivative_warnings + def test_net_rop_ddT(self): + super().test_net_rop_ddT() - @classmethod - def tearDownClass(cls): - if not cls.warnings_suppressed: - ct.make_warnings_fatal() class TestPlog(FromScratchCases, utilities.CanteraTest): # Plog reaction @@ -510,15 +514,15 @@ class TestBlowersMasel(FromScratchCases, utilities.CanteraTest): equation = "H2 + O <=> H + OH" rate_type = "Blowers-Masel" - @utilities.unittest.skip("change of reaction enthalpy is not considered") + @pytest.mark.xfail(reason="Change of reaction enthalpy is not considered") def test_forward_rop_ddT(self): super().test_forward_rop_ddT() - @utilities.unittest.skip("change of reaction enthalpy is not considered") + @pytest.mark.xfail(reason="Change of reaction enthalpy is not considered") def test_reverse_rop_ddT(self): super().test_reverse_rop_ddT() - @utilities.unittest.skip("change of reaction enthalpy is not considered") + @pytest.mark.xfail(reason="Change of reaction enthalpy is not considered") def test_net_rop_ddT(self): super().test_net_rop_ddT() diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 1a07d6a045..7e4172620a 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -2,6 +2,7 @@ import re import itertools import pkg_resources +import pytest import cantera as ct from . import utilities @@ -476,14 +477,11 @@ def test_sticking_coeff_err(self): "at T = 5000.0", "at T = 10000.0", "Sticking coefficient is greater than 1 for reaction", - "CanteraError thrown by InterfaceReaction::validate:", + "InterfaceReaction::validate:", ) - if not ct.debug_mode_enabled(): - err_msg += ("CanteraError thrown by BlowersMaselInterfaceReaction::validate:",) - ct.make_warnings_fatal() for err in err_msg: - with self.assertRaisesRegex(ct.CanteraError, err): + with pytest.warns(UserWarning, match=err): gas = ct.Solution('sticking_coeff_check.yaml') surface = ct.Interface('sticking_coeff_check.yaml', 'Pt_surf', [gas]) diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 138eb7e026..e83e18f469 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -134,15 +134,6 @@ def setUpClass(cls): ct.use_legacy_rate_constants(False) cls.gas = ct.Solution("kineticsfromscratch.yaml") - # suppress user warning (e.g. temperature derivative of Blowers-Masel) - cls.warnings_suppressed = ct.warnings_suppressed() - ct.suppress_warnings() - - @classmethod - def tearDownClass(cls): - if not cls.warnings_suppressed: - ct.make_warnings_fatal() - def setUp(self): self.gas.X = "H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5, H2O2:1e-7" self.gas.TP = 900, 2 * ct.one_atm @@ -223,6 +214,7 @@ def test_with_units(self): with self.assertRaisesRegex(Exception, "not supported"): ct.ReactionRate.from_yaml(yaml) + @utilities.has_temperature_derivative_warnings def test_derivative_ddT(self): # check temperature derivative against numerical derivative deltaT = self.gas.derivative_settings["rtol-delta"] @@ -281,6 +273,7 @@ def test_standalone(self): with self.assertRaisesRegex(Exception, "not supported"): ct.ReactionRate.from_yaml(yaml) + @utilities.has_temperature_derivative_warnings def test_derivative_ddT_exact(self): # check exact derivative against analytical and numerical derivatives rate = self.from_parts() @@ -336,7 +329,7 @@ def test_negative_A(self): rate.allow_negative_pre_exponential_factor = True self.assertTrue(rate.allow_negative_pre_exponential_factor) - @pytest.mark.skip("Change of reaction enthalpy is not considered") + @pytest.mark.xfail(reason="Change of reaction enthalpy is not considered") def test_derivative_ddT(self): super().test_derivative_ddT() @@ -421,6 +414,7 @@ def test_data(self): for n in self._n_data: rate.falloff_coeffs = np.random.rand(n) + @utilities.has_temperature_derivative_warnings def test_derivative_ddT(self): pert = self.gas.derivative_settings["rtol-delta"] deltaT = self.gas.T * pert @@ -499,6 +493,17 @@ class TestTroeRate(FalloffRateTests, utilities.CanteraTest): """ _n_data = [3, 4] + def test_unexpected_parameter(self): + yaml = """ + type: falloff + low-P-rate-constant: {A: 2.3e+12, b: -0.9, Ea: -7112800.0} + high-P-rate-constant: {A: 7.4e+10, b: -0.37, Ea: 0.0} + Troe: {A: 0.7346, T3: 94.0, T1: 1756.0, T2: 0.} + """ + + with pytest.warns(UserWarning, match="Unexpected parameter value T2=0"): + ct.ReactionRate.from_yaml(yaml) + class TestSriRate(FalloffRateTests, utilities.CanteraTest): # test SRI rate expressions diff --git a/interfaces/cython/cantera/test/utilities.py b/interfaces/cython/cantera/test/utilities.py index cb5bc8b066..055902f57a 100644 --- a/interfaces/cython/cantera/test/utilities.py +++ b/interfaces/cython/cantera/test/utilities.py @@ -4,6 +4,7 @@ import tempfile import unittest from pathlib import Path, PurePath +import pytest try: from ruamel import yaml @@ -27,6 +28,14 @@ def wrapper(*args, **kwargs): return wrapper +def has_temperature_derivative_warnings(test): + def wrapper(*args, **kwargs): + with pytest.warns(UserWarning, match="ddTScaledFromStruct"): + # test warning raised for BlowersMasel and TwoTempPlasma derivatives + test(*args, **kwargs) + + return wrapper + def load_yaml(yml_file): # Load YAML data from file using the "safe" loading option. try: From 4a6c3727664dc7f395c13767bd4122296f1f24e2 Mon Sep 17 00:00:00 2001 From: Bryan Weber Date: Thu, 3 Mar 2022 10:21:23 -0500 Subject: [PATCH 8/8] [Test] Use Pytest fixtures for a few simple cases These functions are much easier to read than the previous wrapper versions. --- .../cython/cantera/test/test_convert.py | 12 ++++----- .../cython/cantera/test/test_jacobian.py | 7 ++--- .../cython/cantera/test/test_kinetics.py | 19 +++++++------- interfaces/cython/cantera/test/test_onedim.py | 8 +++--- .../cython/cantera/test/test_reaction.py | 7 ++--- interfaces/cython/cantera/test/test_thermo.py | 26 ++++++++++--------- .../cython/cantera/test/test_transport.py | 6 ++++- interfaces/cython/cantera/test/utilities.py | 26 +++++++++---------- 8 files changed, 59 insertions(+), 52 deletions(-) diff --git a/interfaces/cython/cantera/test/test_convert.py b/interfaces/cython/cantera/test/test_convert.py index e55b58c4d6..6f1b4de6e1 100644 --- a/interfaces/cython/cantera/test/test_convert.py +++ b/interfaces/cython/cantera/test/test_convert.py @@ -2,8 +2,10 @@ from pathlib import Path import logging import io +import pytest from . import utilities +from .utilities import allow_deprecated import cantera as ct from cantera import ck2cti, ck2yaml, cti2yaml, ctml2yaml @@ -534,8 +536,10 @@ def test_error_for_big_element_number(self): with self.assertRaisesRegex(self.InputError, 'Element amounts can have no more than 3 digits.'): self.convert('big_element_num_err.inp') + +@pytest.mark.usefixtures("allow_deprecated") class CtmlConverterTest(utilities.CanteraTest): - @utilities.allow_deprecated + def test_sofc(self): gas_a, anode_bulk, oxide_a = ct.import_phases( 'sofc.cti', @@ -546,14 +550,12 @@ def test_sofc(self): self.assertNear(oxide_a.density_mole, 17.6) @utilities.slow_test - @utilities.allow_deprecated def test_diamond(self): gas, solid = ct.import_phases('diamond.cti', ['gas','diamond']) face = ct.Interface('diamond.cti', 'diamond_100', [gas, solid]) self.assertNear(face.site_density, 3e-8) - @utilities.allow_deprecated def test_invalid(self): try: gas = ct.Solution('invalid.cti') @@ -562,13 +564,11 @@ def test_invalid(self): self.assertIn('already contains', err.args[0]) - @utilities.allow_deprecated def test_noninteger_atomicity(self): gas = ct.Solution('noninteger-atomicity.cti') self.assertNear(gas.molecular_weights[gas.species_index('CnHm')], 10.65*gas.atomic_weight('C') + 21.8*gas.atomic_weight('H')) - @utilities.allow_deprecated def test_reaction_orders(self): gas = ct.Solution('reaction-orders.cti') self.assertEqual(gas.n_reactions, 1) @@ -578,7 +578,6 @@ def test_reaction_orders(self): self.assertTrue(R.allow_negative_orders) self.assertNear(R.orders.get('H2'), -0.25) - @utilities.allow_deprecated def test_long_source_input(self): """ Here we are testing if passing a very long string will result in a @@ -593,7 +592,6 @@ def test_long_source_input(self): self.assertEqual(gas.n_reactions, gas2.n_reactions) - @utilities.allow_deprecated def test_short_source_input(self): """ Here we are testing if passing a short string will result in a Solution diff --git a/interfaces/cython/cantera/test/test_jacobian.py b/interfaces/cython/cantera/test/test_jacobian.py index 48fe851fdc..6b15e62704 100644 --- a/interfaces/cython/cantera/test/test_jacobian.py +++ b/interfaces/cython/cantera/test/test_jacobian.py @@ -3,6 +3,7 @@ import cantera as ct from . import utilities +from .utilities import has_temperature_derivative_warnings class RateExpressionTests: @@ -481,15 +482,15 @@ def setUpClass(cls): cls.gas.TP = 2000, 5 * ct.one_atm super().setUpClass() - @utilities.has_temperature_derivative_warnings + @pytest.mark.usefixtures("has_temperature_derivative_warnings") def test_forward_rop_ddT(self): super().test_forward_rop_ddT() - @utilities.has_temperature_derivative_warnings + @pytest.mark.usefixtures("has_temperature_derivative_warnings") def test_reverse_rop_ddT(self): super().test_reverse_rop_ddT() - @utilities.has_temperature_derivative_warnings + @pytest.mark.usefixtures("has_temperature_derivative_warnings") def test_net_rop_ddT(self): super().test_net_rop_ddT() diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 7e4172620a..7abefca887 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -6,6 +6,7 @@ import cantera as ct from . import utilities +from .utilities import allow_deprecated # avoid explicit dependence of cantera on scipy try: @@ -948,14 +949,14 @@ def test_nonreacting_species(self): class TestReaction(utilities.CanteraTest): @classmethod - def setUpClass(self): + def setUpClass(cls): utilities.CanteraTest.setUpClass() - self.gas = ct.Solution('h2o2.yaml', transport_model=None) - self.gas.X = 'H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5' - self.gas.TP = 900, 2*ct.one_atm - self.species = ct.Species.list_from_file("h2o2.yaml") + cls.gas = ct.Solution('h2o2.yaml', transport_model=None) + cls.gas.X = 'H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5' + cls.gas.TP = 900, 2*ct.one_atm + cls.species = ct.Species.list_from_file("h2o2.yaml") - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_fromCti(self): r = ct.Reaction.fromCti('''three_body_reaction('2 O + M <=> O2 + M', [1.200000e+11, -1.0, 0.0], efficiencies='AR:0.83 H2:2.4 H2O:15.4')''') @@ -969,7 +970,7 @@ def test_fromCti(self): self.assertIn('O2', r) self.assertNotIn('H2O', r) - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_fromXml(self): import xml.etree.ElementTree as ET root = ET.parse(self.cantera_data_path / "h2o2.xml").getroot() @@ -1005,7 +1006,7 @@ def test_listFromFile(self): eq2 = [r.equation for r in self.gas.reactions()] self.assertEqual(eq1, eq2) - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_listFromCti(self): gas = ct.Solution("h2o2.xml", transport_model=None) R = ct.Reaction.listFromCti((self.cantera_data_path / "h2o2.cti").read_text()) @@ -1013,7 +1014,7 @@ def test_listFromCti(self): eq2 = [r.equation for r in gas.reactions()] self.assertEqual(eq1, eq2) - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_listFromXml(self): gas = ct.Solution("h2o2.xml", transport_model=None) R = ct.Reaction.listFromXml((self.cantera_data_path / "h2o2.xml").read_text()) diff --git a/interfaces/cython/cantera/test/test_onedim.py b/interfaces/cython/cantera/test/test_onedim.py index 4533fd1607..42a7b20e98 100644 --- a/interfaces/cython/cantera/test/test_onedim.py +++ b/interfaces/cython/cantera/test/test_onedim.py @@ -1,6 +1,8 @@ import cantera as ct from . import utilities import numpy as np +from .utilities import allow_deprecated +import pytest from cantera.composite import _h5py @@ -503,7 +505,7 @@ def test_prune(self): # TODO: check that the solution is actually correct (i.e. that the # residual satisfies the error tolerances) on the new grid. - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_save_restore_xml(self): reactants = 'H2:1.1, O2:1, AR:5' p = 2 * ct.one_atm @@ -647,7 +649,7 @@ def test_array_properties(self): getattr(self.sim, attr) @utilities.slow_test - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_save_restore_add_species_xml(self): reactants = 'H2:1.1, O2:1, AR:5' p = 2 * ct.one_atm @@ -713,7 +715,7 @@ def test_save_restore_add_species_yaml(self): self.assertArrayNear(Y1[k1], Y2[k2]) @utilities.slow_test - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_save_restore_remove_species_xml(self): reactants = 'H2:1.1, O2:1, AR:5' p = 2 * ct.one_atm diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index e83e18f469..c63c74f013 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -5,6 +5,7 @@ import cantera as ct import numpy as np from . import utilities +from .utilities import has_temperature_derivative_warnings import pytest @@ -214,7 +215,7 @@ def test_with_units(self): with self.assertRaisesRegex(Exception, "not supported"): ct.ReactionRate.from_yaml(yaml) - @utilities.has_temperature_derivative_warnings + @pytest.mark.usefixtures("has_temperature_derivative_warnings") def test_derivative_ddT(self): # check temperature derivative against numerical derivative deltaT = self.gas.derivative_settings["rtol-delta"] @@ -273,7 +274,7 @@ def test_standalone(self): with self.assertRaisesRegex(Exception, "not supported"): ct.ReactionRate.from_yaml(yaml) - @utilities.has_temperature_derivative_warnings + @pytest.mark.usefixtures("has_temperature_derivative_warnings") def test_derivative_ddT_exact(self): # check exact derivative against analytical and numerical derivatives rate = self.from_parts() @@ -414,7 +415,7 @@ def test_data(self): for n in self._n_data: rate.falloff_coeffs = np.random.rand(n) - @utilities.has_temperature_derivative_warnings + @pytest.mark.usefixtures("has_temperature_derivative_warnings") def test_derivative_ddT(self): pert = self.gas.derivative_settings["rtol-delta"] deltaT = self.gas.T * pert diff --git a/interfaces/cython/cantera/test/test_thermo.py b/interfaces/cython/cantera/test/test_thermo.py index 13ca265624..ee30a66e50 100644 --- a/interfaces/cython/cantera/test/test_thermo.py +++ b/interfaces/cython/cantera/test/test_thermo.py @@ -4,6 +4,8 @@ import cantera as ct from . import utilities +from .utilities import allow_deprecated +import pytest class TestThermoPhase(utilities.CanteraTest): @@ -1136,7 +1138,7 @@ def check(self, gas, phase, T, P, nSpec, nElem): self.assertEqual(gas.n_species, nSpec) self.assertEqual(gas.n_elements, nElem) - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_import_phase_cti(self): gas1 = ct.Solution('air-no-reactions.cti', 'air') self.check(gas1, 'air', 300, 101325, 8, 3) @@ -1144,13 +1146,13 @@ def test_import_phase_cti(self): gas2 = ct.Solution('air-no-reactions.cti', 'notair') self.check(gas2, 'notair', 900, 5*101325, 7, 2) - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_import_phase_cti2(self): # This should import the first phase, i.e. 'air' gas = ct.Solution('air-no-reactions.cti') self.check(gas, 'air', 300, 101325, 8, 3) - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_import_phase_xml(self): gas1 = ct.Solution('air-no-reactions.xml', 'air') self.check(gas1, 'air', 300, 101325, 8, 3) @@ -1158,7 +1160,7 @@ def test_import_phase_xml(self): gas2 = ct.Solution('air-no-reactions.xml', 'notair') self.check(gas2, 'notair', 900, 5*101325, 7, 2) - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_import_phase_cti_text(self): cti_def = """ ideal_gas(name='spam', elements='O H', @@ -1169,7 +1171,7 @@ def test_import_phase_cti_text(self): gas = ct.Solution(source=cti_def) self.check(gas, 'spam', 350, 2e6, 8, 2) - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_import_phase_xml_text(self): xml_def = """ @@ -1206,7 +1208,7 @@ def test_import_from_species(self): self.assertNear(gas1.T, gas2.T) self.assertArrayNear(gas1.X, gas2.X) - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_checkReactionBalance(self): with self.assertRaisesRegex(ct.CanteraError, 'reaction is unbalanced'): ct.Solution('h2o2_unbalancedReaction.xml') @@ -1262,7 +1264,7 @@ def test_name_accessor(self): s = self.gas.species(name) self.assertEqual(s.name, name) - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_fromCti(self): h2_cti = """ species( @@ -1291,7 +1293,7 @@ def test_fromCti(self): self.assertEqual(s1.composition, s2.composition) self.assertEqual(s1.thermo.cp(350), s2.thermo.cp(350)) - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_fromXml(self): import xml.etree.ElementTree as ET root = ET.parse(self.cantera_data_path / "h2o2.xml").getroot() @@ -1305,12 +1307,12 @@ def test_fromXml(self): self.assertEqual(s1.composition, s2.composition) self.assertEqual(s1.thermo.cp(350), s2.thermo.cp(350)) - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_listFromFile_cti(self): S = ct.Species.listFromFile('h2o2.cti') self.assertEqual(S[3].name, self.gas.species_name(3)) - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_listFromFile_xml(self): S = ct.Species.listFromFile('h2o2.xml') self.assertEqual(S[3].name, self.gas.species_name(3)) @@ -1319,7 +1321,7 @@ def test_listfromFile_yaml(self): S = ct.Species.list_from_file("h2o2.yaml") self.assertEqual({sp.name for sp in S}, set(self.gas.species_names)) - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_listFromCti(self): S = ct.Species.listFromCti((self.cantera_data_path / "h2o2.cti").read_text()) self.assertEqual(S[3].name, self.gas.species_name(3)) @@ -1368,7 +1370,7 @@ def test_from_dict(self): self.assertEqual(species.composition, {'H': 2, 'O': 1}) self.assertNear(species.thermo.h(300), 100) - @utilities.allow_deprecated + @pytest.mark.usefixtures("allow_deprecated") def test_listFromXml(self): S = ct.Species.listFromXml((self.cantera_data_path / "h2o2.xml").read_text()) self.assertEqual(S[4].name, self.gas.species_name(4)) diff --git a/interfaces/cython/cantera/test/test_transport.py b/interfaces/cython/cantera/test/test_transport.py index 02df771797..55e0afdaa9 100644 --- a/interfaces/cython/cantera/test/test_transport.py +++ b/interfaces/cython/cantera/test/test_transport.py @@ -2,7 +2,9 @@ import cantera as ct from . import utilities +from .utilities import allow_deprecated import copy +import pytest class TestTransport(utilities.CanteraTest): @@ -205,8 +207,10 @@ def test_transport_polynomial_fits_diffusion(self): self.assertEqual(D12, D12new) self.assertEqual(D23, D23new) + +@pytest.mark.usefixtures("allow_deprecated") class TestIonTransport(utilities.CanteraTest): - @utilities.allow_deprecated + def setUp(self): self.p = ct.one_atm self.T = 2237 diff --git a/interfaces/cython/cantera/test/utilities.py b/interfaces/cython/cantera/test/utilities.py index 055902f57a..cfa9dbdc30 100644 --- a/interfaces/cython/cantera/test/utilities.py +++ b/interfaces/cython/cantera/test/utilities.py @@ -18,23 +18,20 @@ TEST_DATA_PATH = Path(__file__).parent / "data" CANTERA_DATA_PATH = Path(__file__).parents[1] / "data" -def allow_deprecated(test): - def wrapper(*args, **kwargs): - cantera.suppress_deprecation_warnings() - try: - test(*args, **kwargs) - finally: - cantera.make_deprecation_warnings_fatal() - return wrapper +@pytest.fixture +def allow_deprecated(): + cantera.suppress_deprecation_warnings() + yield + cantera.make_deprecation_warnings_fatal() -def has_temperature_derivative_warnings(test): - def wrapper(*args, **kwargs): - with pytest.warns(UserWarning, match="ddTScaledFromStruct"): - # test warning raised for BlowersMasel and TwoTempPlasma derivatives - test(*args, **kwargs) - return wrapper +@pytest.fixture +def has_temperature_derivative_warnings(): + with pytest.warns(UserWarning, match="ddTScaledFromStruct"): + # test warning raised for BlowersMasel and TwoTempPlasma derivatives + yield + def load_yaml(yml_file): # Load YAML data from file using the "safe" loading option. @@ -48,6 +45,7 @@ def load_yaml(yml_file): # ruamel.yaml versions (prior to 0.17.0). return yaml.safe_load(stream) + class CanteraTest(unittest.TestCase): @classmethod def setUpClass(cls):