diff --git a/include/cantera/base/ValueCache.h b/include/cantera/base/ValueCache.h new file mode 100644 index 000000000..e1f89b897 --- /dev/null +++ b/include/cantera/base/ValueCache.h @@ -0,0 +1,125 @@ +/** + * @file ValueCache.h + */ + +#ifndef CT_VALUECACHE_H +#define CT_VALUECACHE_H + +#include "ct_defs.h" +#include + +namespace Cantera +{ + +/*! A cached property value and the state at which it was evaluated + * + * This struct stores the value of some property evaluated at a particular + * thermodynamic state. The #value can be either a real scalar or an array, + * depending on the template parameter #T. The exact meaning of #state1, + * #state2, and #stateNum is determined by the function using the cached value, + * which can check any combination of these variables before deciding whether + * to recompute the cached values. + * + * References to CachedValue objects are returned by the "get" methods of + * ValueCache, e.g. ValueCache::getScalar. Functions accessing cached values + * should use the typedefs CachedScalar and CachedArray. See ValueCache for + * details on how these classes should be used together. + */ +template +struct CachedValue { + CachedValue() : + state1(std::numeric_limits::quiet_NaN()), + state2(std::numeric_limits::quiet_NaN()), + stateNum(std::numeric_limits::min()) + { + } + //! Value of the first state variable for the state at which #value was + //! evaluated, e.g. temperature. + double state1; + + //! Value of the second state variable for the state at which #value was + //! evaluated, e.g. density or pressure. + double state2; + + //! A surrogate for the composition. For cached properties of Phase, + //! this should be set to Phase::stateMFNumber() + int stateNum; + + //! The value of the cached property + T value; +}; + +typedef CachedValue& CachedScalar; +typedef CachedValue& CachedArray; + +/*! Storage for cached values + * + * Stores cached values of properties evaluated at a particular thermodynamic + * state. A class that needs cached values can have a ValueCache as a + * member variable. + * + * Each method in the class that implements caching behavior needs a unique id + * for its cached value. This id should be obtained by using the getId() + * function to initialize a static variable within the function. + * + * For cases where the property is a scalar or vector, the cached value can be + * stored in the CachedValue object. If the data type of the cached value is + * more complex, then it can be stored in the calling class, and the value + * attribute of the CachedScalar object can be ignored. + * + * An example use of class ValueCache: + * @code + * class Example { + * ValueCache m_cache; + * doublereal get_property(doublereal T, doublereal P) { + * const static int cacheId = m_cache.getId(); + * CachedScalar cached = m_cache.getScalar(cacheId); + * if (T != cached.state1 || P != cached.state2) { + * cached.value = some_expensive_function(T,P); + * cached.state1 = T; + * cached.state2 = P; + * } + * return cached.value; + * } + * }; + * @endcode + */ +class ValueCache +{ +public: + //! Get a unique id for a cached value. Must be called exactly once for each + //! method that implements caching behavior. + int getId(); + + //! Get a reference to a CachedValue object representing a scalar + //! (doublereal) with the given id. + CachedScalar getScalar(int id) { + return m_scalarCache[id]; + } + + //! Get a reference to a CachedValue object representing an array (vector_fp) + //! with the given id. + CachedArray getArray(int id) { + return m_arrayCache[id]; + } + + //! Clear all cached values. This method should be called if the cached + //! values may be invalidated in a way that is not represented by the state + //! variables alone, such as a change to the constants defining a species + //! thermodynamics as a function of temperature. + void clear(); + +protected: + //! Cached scalar values + std::map > m_scalarCache; + + //! Cached array values + std::map > m_arrayCache; + + //! The last assigned id. Automatically incremented by the getId() method. + static int m_last_id; +}; + +} + +#endif diff --git a/include/cantera/thermo/Phase.h b/include/cantera/thermo/Phase.h index 13918b899..dddfa3752 100644 --- a/include/cantera/thermo/Phase.h +++ b/include/cantera/thermo/Phase.h @@ -10,6 +10,7 @@ #include "cantera/base/vec_functions.h" #include "cantera/base/ctml.h" #include "cantera/thermo/Elements.h" +#include "cantera/base/ValueCache.h" namespace Cantera { @@ -733,6 +734,8 @@ protected: }; virtual void invalidateCachedDataOnStateChange(StateVariable changed_var) {} + mutable ValueCache m_cache; + //! Set the molecular weight of a single species to a given value //! @param k id of the species //! @param mw Molecular Weight (kg kmol-1) diff --git a/src/base/ValueCache.cpp b/src/base/ValueCache.cpp new file mode 100644 index 000000000..f071b2e77 --- /dev/null +++ b/src/base/ValueCache.cpp @@ -0,0 +1,30 @@ +/** + * @file ValueCache.cpp + */ + +#include "cantera/base/ValueCache.h" +#include "cantera/base/ct_thread.h" + +namespace +{ +Cantera::mutex_t id_mutex; +} + +namespace Cantera +{ + +int ValueCache::m_last_id = 0; + +int ValueCache::getId() +{ + ScopedLock lock(id_mutex); + return ++m_last_id; +} + +void ValueCache::clear() +{ + m_scalarCache.clear(); + m_arrayCache.clear(); +} + +} diff --git a/src/thermo/IdealGasPhase.cpp b/src/thermo/IdealGasPhase.cpp index 7d69c0197..576b2540a 100644 --- a/src/thermo/IdealGasPhase.cpp +++ b/src/thermo/IdealGasPhase.cpp @@ -383,20 +383,21 @@ void IdealGasPhase::setToEquilState(const doublereal* mu_RT) void IdealGasPhase::_updateThermo() const { + static const int cacheId = m_cache.getId(); + CachedScalar cached = m_cache.getScalar(cacheId); doublereal tnow = temperature(); // If the temperature has changed since the last time these // properties were computed, recompute them. - if (m_tlast != tnow) { + if (cached.state1 != tnow) { m_spthermo->update(tnow, &m_cp0_R[0], &m_h0_RT[0], &m_s0_R[0]); - m_tlast = tnow; + cached.state1 = tnow; // update the species Gibbs functions for (size_t k = 0; k < m_kk; k++) { m_g0_RT[k] = m_h0_RT[k] - m_s0_R[k]; } m_logc0 = log(m_p0 / (GasConstant * tnow)); - m_tlast = tnow; } } }