Introduce a more general method of caching results
The ValueCache class is intended to standardize the method for implementing value caching in Cantera, and to reduce the need to introduce additional member variables to store cached variables and the thermodynamic state at which the cached values were computed. The initial usage is to replace the use of the m_tlast variable in IdealGasPhase as an example.
This commit is contained in:
parent
2b92ad5684
commit
7556b828f9
4 changed files with 162 additions and 3 deletions
125
include/cantera/base/ValueCache.h
Normal file
125
include/cantera/base/ValueCache.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* @file ValueCache.h
|
||||
*/
|
||||
|
||||
#ifndef CT_VALUECACHE_H
|
||||
#define CT_VALUECACHE_H
|
||||
|
||||
#include "ct_defs.h"
|
||||
#include <limits>
|
||||
|
||||
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 <class T>
|
||||
struct CachedValue {
|
||||
CachedValue() :
|
||||
state1(std::numeric_limits<double>::quiet_NaN()),
|
||||
state2(std::numeric_limits<double>::quiet_NaN()),
|
||||
stateNum(std::numeric_limits<int>::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<double>& CachedScalar;
|
||||
typedef CachedValue<vector_fp>& 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<int, CachedValue<double> > m_scalarCache;
|
||||
|
||||
//! Cached array values
|
||||
std::map<int, CachedValue<vector_fp> > m_arrayCache;
|
||||
|
||||
//! The last assigned id. Automatically incremented by the getId() method.
|
||||
static int m_last_id;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
30
src/base/ValueCache.cpp
Normal file
30
src/base/ValueCache.cpp
Normal file
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue