goPEST 0.0.11__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
@@ -0,0 +1,192 @@
1
+ from mulgrids import *
2
+ from t2data import *
3
+
4
+
5
+
6
+ def normalname_match(name_wc, name):
7
+ """ return true if the name_wc (wild cast * supported) match name """
8
+ is_match = True
9
+ for i in xrange(5):
10
+ if name_wc[i] != '*':
11
+ if name_wc[i] != name[i]:
12
+ is_match = False
13
+ break
14
+ return is_match
15
+
16
+ def toughname_match(name_wc, name):
17
+ """ return true if the name_wc (wild cast * supported) match name """
18
+ from mulgrids import unfix_blockname
19
+ c_name_wc = unfix_blockname(name_wc)
20
+ c_name = unfix_blockname(name)
21
+
22
+ is_match = True
23
+ for i in xrange(5):
24
+ if c_name_wc[i] != '*':
25
+ if c_name_wc[i] != c_name[i]:
26
+ is_match = False
27
+ break
28
+ return is_match
29
+
30
+ # GENER purpose check
31
+
32
+ def is_upflow(gener,geo,convention='*** 1'):
33
+ b = gener.block
34
+ w = gener.name
35
+ checks = [
36
+ normalname_match(convention,w),
37
+ geo.layer[geo.layer_name(b)].name == geo.layerlist[-1].name,
38
+ gener.type == 'MASS'
39
+ ]
40
+ return all(checks)
41
+
42
+ def is_heat(gener,geo,convention='*** 2'):
43
+ b = gener.block
44
+ w = gener.name
45
+ checks = [
46
+ normalname_match(convention,w),
47
+ geo.layer[geo.layer_name(b)].name == geo.layerlist[-1].name,
48
+ gener.type == 'HEAT'
49
+ ]
50
+ return all(checks)
51
+
52
+ def is_upflow_rech(gener,geo,convention='*** 3'):
53
+ b = gener.block
54
+ w = gener.name
55
+ checks = [
56
+ normalname_match(convention,w),
57
+ geo.layer[geo.layer_name(b)].name == geo.layerlist[-1].name,
58
+ gener.type == 'RECH'
59
+ ]
60
+ return all(checks)
61
+
62
+ def is_side_rech(gener,geo,convention='*****'):
63
+ b = gener.block
64
+ w = gener.name
65
+ checks = [
66
+ normalname_match(convention,w),
67
+ b == w,
68
+ gener.type == 'RECH'
69
+ ]
70
+ return all(checks)
71
+
72
+ def is_rain(gener,geo,convention='*****'):
73
+ b = gener.block
74
+ w = gener.name
75
+ checks = [
76
+ normalname_match(convention,w),
77
+ geo.layer_name(b) == geo.layerlist[geo.num_layers - geo.column[geo.column_name(b)].num_layers].name,
78
+ gener.type == 'MASS'
79
+ ]
80
+ return all(checks)
81
+
82
+ def is_spring(gener,geo,convention='SP***'):
83
+ b = gener.block
84
+ w = gener.name
85
+ checks = [
86
+ normalname_match(convention,w),
87
+ gener.type in ['DELG','MASS'],
88
+ ]
89
+ return all(checks)
90
+
91
+ def check_geners(geners, checks):
92
+ """ geners, a list of t2generators. checks, a list of functions that
93
+ accepts generator as argument and return True/False, names of functions
94
+ will be used as dict key when returning counts of each function when
95
+ returning True. A list of partial functions can be in place the list of
96
+ functions. """
97
+
98
+ try:
99
+ fnames = [f.__name__ for f in checks]
100
+ except AttributeError:
101
+ fnames = [pf.func.__name__ for pf in checks]
102
+
103
+ # set all counter to zero, dict with func names as key
104
+ count = dict([(n,0) for n in fnames])
105
+ count['UNKNOWN'] = 0
106
+
107
+ for i,g in enumerate(geners):
108
+ match = []
109
+ for f,t in zip(checks,fnames):
110
+ if f(g):
111
+ count[t] += 1
112
+ match.append(t)
113
+ if len(match) > 1:
114
+ print('WARNING: %s match types: %s' % (g.name,','.join(match)))
115
+ if len(match) == 0:
116
+ count['UNKNOWN'] += 1
117
+ print('WARNING: %s doe not match any type.' % g.name)
118
+
119
+ return count
120
+
121
+ def wai_area_polygon():
122
+ pline = [
123
+ np.array([ 2768202.24117734 , 6270490.54421648]),
124
+ np.array([ 2776545.06074986 , 6276563.022725 ]),
125
+ np.array([ 2777310.95894013 , 6277137.4463677 ]),
126
+ np.array([ 2778049.5036236 , 6277766.57702399]),
127
+ np.array([ 2780784.85430311 , 6280173.68562197]),
128
+ np.array([ 2781988.4086021 , 6281377.23992095]),
129
+ np.array([ 2783629.61900981 , 6294807.81175738]),
130
+ np.array([ 2772524.09525097 , 6296312.25463111]),
131
+ np.array([ 2764454.8107464 , 6291662.15847593]),
132
+ np.array([ 2762567.41877753 , 6281213.11888018]),
133
+ np.array([ 2764865.11334833 , 6272842.94580086]),
134
+ np.array([ 2767436.34298707 , 6270955.553832 ])]
135
+ return pline
136
+
137
+ def ro_area_polygon():
138
+ pline = [
139
+ np.array([ 2783739.03303699 , 6294671.0442234 ]),
140
+ np.array([ 2781742.22704094 , 6281623.42148211]),
141
+ np.array([ 2785216.12240393 , 6279462.49444529]),
142
+ np.array([ 2787267.63541357 , 6279626.61548606]),
143
+ np.array([ 2788416.48269896 , 6279134.25236375]),
144
+ np.array([ 2795911.34356084 , 6279927.50406081]),
145
+ np.array([ 2790440.64220181 , 6292263.93562543]),
146
+ np.array([ 2784586.99174764 , 6294534.27668943])]
147
+ return pline
148
+
149
+ def th_area_polygon():
150
+ pline = [
151
+ np.array([ 2788334.42217858 , 6279298.37340452]),
152
+ np.array([ 2795938.69706763 , 6280118.97860838]),
153
+ np.array([ 2797579.90747534 , 6272131.75462419]),
154
+ np.array([ 2794188.07263274 , 6265129.25688463]),
155
+ np.array([ 2789784.15803872 , 6262776.85530024]),
156
+ np.array([ 2778842.75532066 , 6260479.16072945]),
157
+ np.array([ 2771566.72251314 , 6264992.48935065]),
158
+ np.array([ 2768421.0692317 , 6269970.82758737]),
159
+ np.array([ 2778131.56414398 , 6277848.63754438]),
160
+ np.array([ 2781632.81301376 , 6280966.93731903]),
161
+ np.array([ 2784942.58733598 , 6279407.7874317 ]),
162
+ np.array([ 2786747.91878446 , 6279653.96899286]),
163
+ np.array([ 2788115.59412422 , 6279353.08041811])]
164
+ return pline
165
+
166
+ if __name__ == '__main__':
167
+
168
+ geo = mulgrid('gwai41458_09.dat')
169
+ nsdat = t2data('wai41458ns_464.dat')
170
+ prdat = t2data('wai41458pr_464.dat')
171
+
172
+ # check these possibilities
173
+ from functools import partial
174
+ g_types = [
175
+ partial(is_rain,geo=geo,convention='*** 0'),
176
+ partial(is_heat,geo=geo,convention='*** 2'),
177
+ partial(is_upflow,geo=geo,convention='*** 1'),
178
+ partial(is_upflow_rech,geo=geo,convention='*** 3'),
179
+ partial(is_side_rech,geo=geo,convention='*****')
180
+ ]
181
+
182
+ print('+++++', nsdat.filename)
183
+ count = check_geners(nsdat.generatorlist, g_types)
184
+ for t in sorted(count.keys()):
185
+ print('%20s = %i' % (t,count[t]))
186
+ print('Total # of generators is ', len(nsdat.generatorlist))
187
+
188
+ print('+++++', prdat.filename)
189
+ count = check_geners(prdat.generatorlist, g_types)
190
+ for t in sorted(count.keys()):
191
+ print('%20s = %i' % (t,count[t]))
192
+ print('Total # of generators is ', len(prdat.generatorlist))
@@ -0,0 +1,376 @@
1
+ """
2
+ Copyright 2013, 2014 University of Auckland.
3
+
4
+ This file is part of TIM (Tim Isn't Mulgraph).
5
+
6
+ TIM is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ TIM is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU General Public License for more details.
15
+
16
+ You should have received a copy of the GNU General Public License
17
+ along with TIM. If not, see <http://www.gnu.org/licenses/>.
18
+ """
19
+
20
+ """
21
+ Wrap AUTOUGH2's hdf5 output as t2listing
22
+ """
23
+
24
+ import h5py
25
+ import numpy as np
26
+
27
+ from t2listing import *
28
+ # from t2listing import listingtable
29
+ from mulgrids import fix_blockname, unfix_blockname
30
+
31
+ from pprint import pprint as pp
32
+ import unittest
33
+
34
+ class h5table(listingtable):
35
+ """ Class emulating the listingtable class in PyTOUGH.
36
+
37
+ Class for table in listing file, with values addressable by index (0-based)
38
+ or row name, and column name: e.g. table[i] returns the ith row (as a
39
+ dictionary), table[rowname] returns the row with the specified name, and
40
+ table[colname] returns the column with the specified name.
41
+
42
+ !!! IMPORTANT !!!
43
+ .index needs to be set whenever listing object changed time index
44
+ """
45
+ def __init__(self, cols, rows, h5_table,
46
+ num_keys = 1, allow_reverse_keys = False,
47
+ index = 0):
48
+ """ The row_format parameter is a dictionary with three keys,
49
+ 'key','index' and 'values'. These contain the positions, in each row of
50
+ the table, of the start of the keys, index and data fields. The
51
+ row_line parameter is a list containing, for each row of the table, the
52
+ number of lines before it in the listing file, from the start of the
53
+ table. This is needed for TOUGH2_MP listing files, in which the rows
54
+ are not in index order and can also be duplicated.
55
+
56
+ h5_table should be the table within the h5 file.
57
+ """
58
+ self.column_name = cols
59
+ self.row_name = rows
60
+ self.num_keys = num_keys
61
+ self.allow_reverse_keys = allow_reverse_keys
62
+ self._col = dict([(c,i) for i,c in enumerate(cols)])
63
+ self._row = dict([(r,i) for i,r in enumerate(rows)])
64
+ self._h5_table = h5_table
65
+ self._index = index # time index
66
+
67
+ def __repr__(self):
68
+ # h5 table lst._h5['element'][time index, eleme index, field index]
69
+ return repr(self.column_name) + '\n' + repr(self._h5_table[self._index, :, :])
70
+
71
+ def __getitem__(self, key):
72
+ if isinstance(key, int):
73
+ return dict(zip(['key'] + self.column_name, [self.row_name[key]] +
74
+ list(self._h5_table[self._index, key, :])))
75
+ else:
76
+ if key in self.column_name:
77
+ return self._h5_table[self._index, :, self._col[key]]
78
+ elif key in self.row_name:
79
+ rowindex = self._row[key]
80
+ return dict(zip(['key'] + self.column_name,
81
+ [self.row_name[rowindex]] +
82
+ list(self._h5_table[self._index, rowindex, :])))
83
+ elif len(key) > 1 and self.allow_reverse_keys:
84
+ revkey = key[::-1] # try reversed key for multi-key tables
85
+ if revkey in self.row_name:
86
+ rowindex = self._row[revkey]
87
+ return dict(zip(['key'] + self.column_name,
88
+ [self.row_name[rowindex][::-1]] +
89
+ list(-self._h5_table[self._index, rowindex, :])))
90
+ else: return None
91
+
92
+ def __add__(self, other):
93
+ raise NotImplementedError
94
+ """Adds two listing tables together."""
95
+ if self.column_name == other.column_name and self.row_name == other.row_name:
96
+ from copy import copy
97
+ result = listingtable(copy(self.column_name), copy(self.row_name), num_keys = self.num_keys,
98
+ allow_reverse_keys = self.allow_reverse_keys)
99
+ result._data = self._data + other._data
100
+ return result
101
+ else: raise Exception("Incompatible tables: can't be added together.")
102
+
103
+ def __sub__(self, other):
104
+ raise NotImplementedError
105
+ """Subtracts one listing table from another."""
106
+ if self.column_name == other.column_name and self.row_name == other.row_name:
107
+ from copy import copy
108
+ result = listingtable(copy(self.column_name), copy(self.row_name), num_keys = self.num_keys,
109
+ allow_reverse_keys = self.allow_reverse_keys)
110
+ result._data = self._data - other._data
111
+ return result
112
+ else: raise Exception("Incompatible tables: can't be subtracted.")
113
+
114
+
115
+ class t2listingh5(object):
116
+ def __init__(self, filename):
117
+ """
118
+ """
119
+ self._table = {}
120
+ self._h5 = h5py.File(filename, 'r')
121
+ self.filename = filename
122
+ self.setup()
123
+ self.simulator = 'AUTOUGH2_H5'
124
+
125
+ def close(self):
126
+ self._h5.close()
127
+
128
+ def setup(self):
129
+ self.fulltimes = self._h5['fulltimes']['TIME']
130
+ self.num_fulltimes = len(self.fulltimes)
131
+ self._index = 0 # this is the internal one
132
+ ### element table
133
+ if 'element' in self._h5:
134
+ cols = [x.decode('utf-8') for x in self._h5['element_fields']]
135
+ blocks = [fix_blockname(x.decode('utf-8')) for x in self._h5['element_names']]
136
+ table = h5table(cols, blocks, self._h5['element'], num_keys=1)
137
+ self._table['element'] = table
138
+ ### connection table
139
+ if 'connection' in self._h5:
140
+ cols = [x.decode('utf-8') for x in self._h5['connection_fields'][:]]
141
+ b1 = [fix_blockname(x.decode('utf-8')) for x in self._h5['connection_names1'][:]]
142
+ b2 = [fix_blockname(x.decode('utf-8')) for x in self._h5['connection_names2'][:]]
143
+ table = h5table(cols, list(zip(b1,b2)), self._h5['connection'], num_keys=2,
144
+ allow_reverse_keys=True)
145
+ self._table['connection'] = table
146
+ ### generation table
147
+ if 'generation' in self._h5:
148
+ cols = [x.decode('utf-8') for x in self._h5['generation_fields'][:]]
149
+ blocks = [fix_blockname(x.decode('utf-8')) for x in self._h5['generation_eleme'][:]]
150
+ geners = [fix_blockname(x.decode('utf-8')) for x in self._h5['generation_names'][:]]
151
+ table = h5table(cols, list(zip(blocks,geners)), self._h5['generation'], num_keys=1)
152
+ self._table['generation'] = table
153
+ # makes tables in self._table accessible as attributes
154
+ for key,table in self._table.items():
155
+ setattr(self, key, table)
156
+ # have to be get first table ready
157
+ self.index = 0
158
+
159
+ def history(self, selection, short=False, start_datetime=None):
160
+ """
161
+ short is not used at the moment
162
+ """
163
+ if isinstance(selection, tuple):
164
+ selection = [selection]
165
+ results = []
166
+ for tbl,b,cname in selection:
167
+ table_name, bi, fieldi = self.selection_index(tbl, b, cname)
168
+ if bi < 0:
169
+ bi = len(self.block_name_index) + bi
170
+ ### important to convert cell index
171
+ ys = self._h5[table_name][:,bi,fieldi]
172
+ results.append((self.fulltimes, ys))
173
+ if len(results) == 1: results = results[0]
174
+ return results
175
+
176
+ def selection_index(self, tbl, b, field):
177
+ dname = {
178
+ 'e': 'element',
179
+ 'c': 'connection',
180
+ 'g': 'generation',
181
+ }
182
+ def eleme_index(b):
183
+ if isinstance(b, str):
184
+ bi = self.block_name_index[b]
185
+ elif isinstance(b, int):
186
+ bi = b
187
+ else:
188
+ raise Exception('.history() block must be an int or str: %s (%s)' % (str(b),str(type(b))))
189
+ return bi
190
+ def conne_index(b):
191
+ if isinstance(b, tuple):
192
+ bi = self.connection_name_index[(str(b[0]), str(b[1]))]
193
+ elif isinstance(b, int):
194
+ bi = b
195
+ else:
196
+ raise Exception('.history() conne must be an int or (str,str): %s (%s)' % (str(b),str(type(b))))
197
+ return bi
198
+ def gener_index(b):
199
+ if isinstance(b, tuple):
200
+ bi = self.generation_name_index[(str(b[0]), str(b[1]))]
201
+ elif isinstance(b, int):
202
+ bi = b
203
+ else:
204
+ raise Exception('.history() gener must be an int or (str,str): %s (%s)' % (str(b),str(type(b))))
205
+ return bi
206
+ iname = {
207
+ 'e': eleme_index,
208
+ 'c': conne_index,
209
+ 'g': gener_index,
210
+ }
211
+ if not hasattr(self, 'field_index'):
212
+ self.field_index = {}
213
+ for n,nn in dname.items():
214
+ for i,ff in enumerate(self._h5[nn + '_fields']):
215
+ self.field_index[(n,ff)] = i
216
+ return dname[tbl], iname[tbl](b), self.field_index[(tbl,field)]
217
+
218
+ @property
219
+ def block_name_index(self):
220
+ if not hasattr(self, '_block_name_index'):
221
+ self._block_name_index = {}
222
+ # self._block_name_index.update({str(e):i for i,e in enumerate(self._h5['element_names'])})
223
+ self._block_name_index.update({fix_blockname(str(e)):i for i,e in enumerate(self._h5['element_names'])})
224
+ return self._block_name_index
225
+
226
+ @property
227
+ def connection_name_index(self):
228
+ if not hasattr(self, '_connection_name_index'):
229
+ a = self._h5['connection_names1']
230
+ b = self._h5['connection_names2']
231
+ self._connection_name_index = {}
232
+ self._connection_name_index.update({(fix_blockname(str(x[0])),fix_blockname(str(x[1]))):i for i,x in enumerate(zip(a,b))})
233
+ return self._connection_name_index
234
+
235
+ @property
236
+ def generation_name_index(self):
237
+ if not hasattr(self, '_generation_name_index'):
238
+ a = self._h5['generation_eleme']
239
+ b = self._h5['generation_names']
240
+ self._generation_name_index = {}
241
+ # self._generation_name_index.update({(str(x[0]),str(x[1])):i for i,x in enumerate(zip(a,b))})
242
+ # self._generation_name_index.update({(fix_blockname(str(x[0])),(str(x[1]))):i for i,x in enumerate(zip(a,b))})
243
+ # self._generation_name_index.update({((str(x[0])),fix_blockname(str(x[1]))):i for i,x in enumerate(zip(a,b))})
244
+ self._generation_name_index.update({(fix_blockname(str(x[0])),fix_blockname(str(x[1]))):i for i,x in enumerate(zip(a,b))})
245
+ return self._generation_name_index
246
+
247
+
248
+ def read_tables(self):
249
+ """ copy values from h5 into listingtables, with slicing """
250
+ if 'element' in self.table_names:
251
+ self.element._index = self.index
252
+ # for i,cname in enumerate(self.element.column_name):
253
+ # self.element._data[:,i] = self._h5['element'][self._index, :, i]
254
+ if 'connection' in self.table_names:
255
+ self.connection._index = self.index
256
+ # for i,cname in enumerate(self.connection.column_name):
257
+ # self.connection._data[:,i] = self._h5['connection'][self._index, :, i]
258
+ if 'generation' in self.table_names:
259
+ self.generation._index = self.index
260
+ # for i,cname in enumerate(self.generation.column_name):
261
+ # self.generation._data[:,i] = self._h5['generation'][self._index, :, i]
262
+
263
+ def get_index(self): return self._index
264
+ def set_index(self, i):
265
+ self._index = i
266
+ if self._index < 0: self._index += self.num_fulltimes
267
+ self.read_tables()
268
+ index = property(get_index, set_index)
269
+
270
+ def first(self): self.index = 0
271
+ def last(self): self.index = -1
272
+ def next(self):
273
+ """Find and read next set of results; returns false if at end of listing"""
274
+ more = self.index < self.num_fulltimes - 1
275
+ if more: self.index += 1
276
+ return more
277
+ def prev(self):
278
+ """Find and read previous set of results; returns false if at start of listing"""
279
+ more = self.index > 0
280
+ if more: self.index -= 1
281
+ return more
282
+
283
+ def get_table_names(self):
284
+ return sorted(self._table.keys())
285
+ table_names = property(get_table_names)
286
+
287
+ def get_time(self): return self.fulltimes[self.index]
288
+ def set_time(self, t):
289
+ if t < self.fulltimes[0]: self.index = 0
290
+ elif t > self.fulltimes[-1]: self.index = -1
291
+ else:
292
+ dt = np.abs(self.fulltimes - t)
293
+ self.index = np.argmin(dt)
294
+ time = property(get_time, set_time)
295
+
296
+
297
+ class test_fivespot(unittest.TestCase):
298
+ def setUp(self):
299
+ self.lst_h = t2listingh5('fivespot.h5')
300
+ self.lst_t= t2listing('expected.listing')
301
+
302
+ def test_match_tables(self):
303
+ # check row and column names
304
+ def check_names(tbl):
305
+ tbl_h = getattr(self.lst_h, tbl)
306
+ tbl_t = getattr(self.lst_t, tbl)
307
+ self.assertEqual(tbl_h.row_name, tbl_t.row_name)
308
+ for i,field in enumerate(tbl_h.column_name):
309
+ if tbl_t.column_name[i] in field:
310
+ match = True
311
+ else:
312
+ match = False
313
+ self.assertEqual(match, True, '%s: column name mismatch' % tbl)
314
+ for tbl in ['element', 'connection', 'generation']:
315
+ check_names(tbl)
316
+ # check table values, after change index also
317
+ def check_tables():
318
+ rtol = 1.0e-5 # roughly 4~5 significant digits from text listing file
319
+ for field in ['Temperature', 'Pressure', 'Vapour saturation']:
320
+ np.testing.assert_allclose(self.lst_h.element[field],
321
+ self.lst_t.element[field], rtol=rtol)
322
+ for field in ['Mass flow', 'Enthalpy', 'Heat flow']:
323
+ np.testing.assert_allclose(self.lst_h.connection[field],
324
+ self.lst_t.connection[field], rtol=rtol)
325
+ for field in ['Generation rate', 'Enthalpy']:
326
+ np.testing.assert_allclose(self.lst_h.generation[field],
327
+ self.lst_t.generation[field], rtol=rtol)
328
+ check_tables()
329
+ self.lst_h.last(); self.lst_t.last()
330
+ check_tables()
331
+ self.lst_h.first(); self.lst_t.first()
332
+ check_tables()
333
+
334
+ # check table with element index
335
+ def check_table_by_index(i):
336
+ tbl_h = self.lst_h.element[i]
337
+ tbl_t = self.lst_t.element[i]
338
+ for k in tbl_h.keys():
339
+ if k == 'key':
340
+ self.assertEqual(tbl_h[k], tbl_t[k])
341
+ else:
342
+ np.testing.assert_approx_equal(tbl_h[k], tbl_t[k], significant=5)
343
+ for i in range(len(self.lst_h.element.row_name)):
344
+ check_table_by_index(i)
345
+
346
+ # check table with element name
347
+ def check_table_by_name(b):
348
+ tbl_h = self.lst_h.element[b]
349
+ tbl_t = self.lst_t.element[b]
350
+ for k in tbl_h.keys():
351
+ if k == 'key':
352
+ self.assertEqual(tbl_h[k], tbl_t[k])
353
+ else:
354
+ np.testing.assert_approx_equal(tbl_h[k], tbl_t[k], significant=5)
355
+ for b in self.lst_h.element.row_name:
356
+ check_table_by_index(b)
357
+
358
+ def test_match_history(self):
359
+ self.assertEqual(self.lst_h.num_fulltimes, self.lst_t.num_fulltimes)
360
+ np.testing.assert_allclose(self.lst_h.fulltimes, self.lst_t.fulltimes)
361
+ rtol = 1.0e-5
362
+ # seems allfield names are identical with fivespot's EOS
363
+ for sel in [('e', 'AA106', 'Pressure'),
364
+ ('e', 'AA 66', 'Temperature'),
365
+ ('c', ('AA 66', 'AA 67'), 'Mass flow'),
366
+ ('g', ('AA 11', 'PRO 1'), 'Generation rate'),
367
+ ('g', ('AA 11', 'PRO 1'), 'Enthalpy'),
368
+ ]:
369
+ xs_h, ys_h = self.lst_h.history(sel)
370
+ xs_t, ys_t = self.lst_t.history(sel)
371
+ np.testing.assert_allclose(xs_h, xs_t, rtol=rtol)
372
+ np.testing.assert_allclose(ys_h, ys_t, rtol=rtol)
373
+
374
+
375
+ if __name__ == '__main__':
376
+ unittest.main(verbosity=2)