1 /**
2  *  @file ValueCache.h
3  */
4 
5 // This file is part of Cantera. See License.txt in the top-level directory or
6 // at https://cantera.org/license.txt for license and copyright information.
7 
8 #ifndef CT_VALUECACHE_H
9 #define CT_VALUECACHE_H
10 
11 #include "ct_defs.h"
12 #include <limits>
13 
14 namespace Cantera
15 {
16 
17 /*! A cached property value and the state at which it was evaluated
18  *
19  * This struct stores the value of some property evaluated at a particular
20  * thermodynamic state. The #value can be either a real scalar or an array,
21  * depending on the template parameter `T`. The exact meaning of #state1,
22  * #state2, and #stateNum is determined by the function using the cached value,
23  * which can check any combination of these variables before deciding whether
24  * to recompute the cached values.
25  *
26  * References to CachedValue objects are returned by the "get" methods of
27  * ValueCache, e.g. ValueCache::getScalar. Functions accessing cached values
28  * should use the typedefs CachedScalar and CachedArray. See ValueCache for
29  * details on how these classes should be used together.
30  */
31 template <class T>
32 struct CachedValue {
CachedValueCachedValue33     CachedValue() :
34         state1(std::numeric_limits<double>::quiet_NaN()),
35         state2(std::numeric_limits<double>::quiet_NaN()),
36         stateNum(std::numeric_limits<int>::min()),
37         value(T())
38     {
39     }
40 
41     //! Check whether the currently cached value is valid based on
42     //! a single state variable. If it is not valid it updates the stored
43     //! state to the new state in addition to returning false.
validateCachedValue44     bool validate(double state1New) {
45       if(state1 == state1New) {
46         return true;
47       } else {
48         state1 = state1New;
49       }
50       return false;
51     }
52 
53     //! Check whether the currently cached value is valid based on
54     //! state1 and state2. If it is not valid it updates the stored
55     //! state to the new state in addition to returning false.
validateCachedValue56     bool validate(double state1New, double state2New) {
57       if(state1 == state1New && state2 == state2New) {
58         return true;
59       } else {
60         state1 = state1New;
61         state2 = state2New;
62       }
63       return false;
64     }
65 
66     //! Check whether the currently cached value is valid based on
67     //! state1 and stateNum. If it is not valid it updates the stored
68     //! state to the new state in addition to returning false.
validateCachedValue69     bool validate(double state1New, int stateNumNew) {
70       if(state1 == state1New && stateNum == stateNumNew) {
71         return true;
72       } else {
73         state1 = state1New;
74         stateNum = stateNumNew;
75       }
76       return false;
77     }
78 
79     //! Check whether the currently cached value is valid based on
80     //! stateNum. If it is not valid it updates the stored
81     //! state to the new state in addition to returning false.
validateCachedValue82     bool validate(int stateNumNew) {
83       if(stateNum == stateNumNew) {
84         return true;
85       } else {
86         stateNum = stateNumNew;
87       }
88       return false;
89     }
90 
91     //! Check whether the currently cached value is valid based on
92     //! state1, state2, and stateNum. If it is not valid it updates the stored
93     //! state to the new state in addition to returning false.
validateCachedValue94     bool validate(double state1New, double state2New, int stateNumNew) {
95       if(state1 == state1New && state2 == state2New && stateNum == stateNumNew) {
96         return true;
97       } else {
98         state1 = state1New;
99         state2 = state2New;
100         stateNum = stateNumNew;
101       }
102       return false;
103     }
104 
105     //! Value of the first state variable for the state at which #value was
106     //! evaluated, e.g. temperature.
107     double state1;
108 
109     //! Value of the second state variable for the state at which #value was
110     //! evaluated, e.g. density or pressure.
111     double state2;
112 
113     //! A surrogate for the composition. For cached properties of Phase,
114     //! this should be set to Phase::stateMFNumber()
115     int stateNum;
116 
117     //! The value of the cached property
118     T value;
119 };
120 
121 typedef CachedValue<double>& CachedScalar;
122 typedef CachedValue<vector_fp>& CachedArray;
123 
124 /*! Storage for cached values
125  *
126  * Stores cached values of properties evaluated at a particular thermodynamic
127  * state. A class that needs cached values can have a ValueCache as a
128  * member variable.
129  *
130  * Each method in the class that implements caching behavior needs a unique id
131  * for its cached value. This id should be obtained by using the getId()
132  * function to initialize a static variable within the function.
133  *
134  * For cases where the property is a scalar or vector, the cached value can be
135  * stored in the CachedValue object. If the data type of the cached value is
136  * more complex, then it can be stored in the calling class, and the value
137  * attribute of the CachedScalar object can be ignored.
138  *
139  * An example use of class ValueCache:
140  * @code
141  * class Example {
142  *     ValueCache m_cache;
143  *     doublereal get_property(doublereal T, doublereal P) {
144  *         const static int cacheId = m_cache.getId();
145  *         CachedScalar cached = m_cache.getScalar(cacheId);
146  *         if (T != cached.state1 || P != cached.state2) {
147  *             cached.value = some_expensive_function(T,P);
148  *             cached.state1 = T;
149  *             cached.state2 = P;
150  *         }
151  *         return cached.value;
152  *     }
153  * };
154  * @endcode
155  */
156 class ValueCache
157 {
158 public:
159     //! Get a unique id for a cached value. Must be called exactly once for each
160     //! method that implements caching behavior.
161     int getId();
162 
163     //! Get a reference to a CachedValue object representing a scalar
164     //! (doublereal) with the given id.
getScalar(int id)165     CachedScalar getScalar(int id) {
166         return m_scalarCache[id];
167     }
168 
169     //! Get a reference to a CachedValue object representing an array (vector_fp)
170     //! with the given id.
getArray(int id)171     CachedArray getArray(int id) {
172         return m_arrayCache[id];
173     }
174 
175     //! Clear all cached values. This method should be called if the cached
176     //! values may be invalidated in a way that is not represented by the state
177     //! variables alone, such as a change to the constants defining a species
178     //! thermodynamics as a function of temperature.
179     void clear();
180 
181 protected:
182     //! Cached scalar values
183     std::map<int, CachedValue<double> > m_scalarCache;
184 
185     //! Cached array values
186     std::map<int, CachedValue<vector_fp> > m_arrayCache;
187 
188     //! The last assigned id. Automatically incremented by the getId() method.
189     static int m_last_id;
190 };
191 
192 }
193 
194 #endif
195