turbx 1.0.2__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.
turbx/eas3.py ADDED
@@ -0,0 +1,420 @@
1
+ import io
2
+ import struct
3
+
4
+ import numpy as np
5
+
6
+ from .utils import even_print
7
+
8
+ # ======================================================================
9
+
10
+ class eas3:
11
+ '''
12
+ Interface class for EAS3 files (legacy binary NS3D output format)
13
+ - only binary read is supported, no writing
14
+ '''
15
+
16
+ def __init__(self, fname, **kwargs):
17
+ '''
18
+ initialize class instance
19
+ '''
20
+ self.fname = fname
21
+ self.verbose = kwargs.get('verbose',True)
22
+
23
+ if isinstance(fname, str):
24
+ self.f = open(fname,'rb')
25
+ elif isinstance(fname, io.BytesIO):
26
+ self.f = fname
27
+ else:
28
+ raise TypeError('fname should be type str or io.BytesIO')
29
+
30
+ self.udef = self.get_header()
31
+
32
+ def __enter__(self):
33
+ '''
34
+ for use with python 'with' statement
35
+ '''
36
+ #print('opening from enter() --> used with statement')
37
+ return self
38
+
39
+ def __exit__(self, exception_type, exception_value, exception_traceback):
40
+ '''
41
+ for use with python 'with' statement
42
+ '''
43
+ self.f.close()
44
+
45
+ if exception_type is not None:
46
+ print('\nsafely closed EAS3 due to exception')
47
+ print(72*'-')
48
+ print('exception type : '+str(exception_type))
49
+ if exception_value is not None:
50
+ print('exception_value : '+str(exception_value))
51
+ if exception_traceback is not None:
52
+ print('exception_traceback : '+str(exception_traceback))
53
+ if exception_type is not None:
54
+ print(72*'-')
55
+
56
+ def close(self):
57
+ '''
58
+ close() passthrough to handle from binary open()
59
+ '''
60
+ self.f.close()
61
+
62
+ def get_header(self, **kwargs):
63
+
64
+ ATTRLEN = kwargs.get('ATTRLEN',10)
65
+ UDEFLEN = kwargs.get('UDEFLEN',20)
66
+ ChangeGmode = kwargs.get('ChangeGmode',1)
67
+
68
+ ## Definitions
69
+ #EAS2=1; EAS3=2
70
+ IEEES=1; IEEED=2; IEEEQ=3
71
+ #EAS3_NO_ATTR=1
72
+ EAS3_ALL_ATTR=2
73
+ EAS3_NO_G=1
74
+ EAS3_X0DX_G=2
75
+ #EAS3_UDEF_G=3
76
+ EAS3_ALL_G=4
77
+ #EAS3_FULL_G=5
78
+ #EAS3_NO_UDEF=1
79
+ EAS3_ALL_UDEF=2
80
+ #EAS3_INT_UDEF=3
81
+
82
+ self.IEEES = IEEES
83
+ self.IEEED = IEEED
84
+ self.IEEEQ = IEEEQ
85
+
86
+ ## Identifier: 20 byte character
87
+ #identifier = self.f.read(20).strip()
88
+
89
+ ## File type: 8 byte integer
90
+ #file_type = struct.unpack('!q',self.f.read(8))[0]
91
+
92
+ ## Accuracy: 8 byte integer
93
+ accuracy = struct.unpack('!q',self.f.read(8))[0]
94
+ self.accuracy = accuracy
95
+
96
+ if (self.accuracy == self.IEEES):
97
+ self.dtype = np.float32
98
+ elif (self.accuracy == self.IEEED):
99
+ self.dtype = np.float64
100
+ elif (self.accuracy == self.IEEEQ):
101
+ self.dtype = np.float128
102
+ else:
103
+ raise ValueError('precision not identifiable')
104
+
105
+ ## Array sizes: each 8 byte integer
106
+ nzs = struct.unpack('!q',self.f.read(8))[0]
107
+ npar = struct.unpack('!q',self.f.read(8))[0]
108
+ ndim1 = struct.unpack('!q',self.f.read(8))[0]
109
+ ndim2 = struct.unpack('!q',self.f.read(8))[0]
110
+ ndim3 = struct.unpack('!q',self.f.read(8))[0]
111
+
112
+ self.ndim1 = ndim1
113
+ self.ndim2 = ndim2
114
+ self.ndim3 = ndim3
115
+ self.npar = npar
116
+ self.nzs = nzs
117
+
118
+ ## Attribute mode: 8 byte integer
119
+ attribute_mode = struct.unpack('!q',self.f.read(8))[0]
120
+
121
+ ## Geometry mode: each 8 byte integer
122
+ gmode_time = struct.unpack('!q',self.f.read(8))[0]
123
+ gmode_param = struct.unpack('!q',self.f.read(8))[0]
124
+ gmode_dim1 = struct.unpack('!q',self.f.read(8))[0]
125
+ gmode_dim2 = struct.unpack('!q',self.f.read(8))[0]
126
+ gmode_dim3 = struct.unpack('!q',self.f.read(8))[0]
127
+
128
+ ## Array sizes for geometry data: each 8 byte integer
129
+ size_time = struct.unpack('!q',self.f.read(8))[0]
130
+ size_param = struct.unpack('!q',self.f.read(8))[0]
131
+ size_dim1 = struct.unpack('!q',self.f.read(8))[0]
132
+ size_dim2 = struct.unpack('!q',self.f.read(8))[0]
133
+ size_dim3 = struct.unpack('!q',self.f.read(8))[0]
134
+
135
+ ## Specification of user defined data: 8 byte integer
136
+ udef = struct.unpack('!q',self.f.read(8))[0]
137
+
138
+ ## Array sizes for used defined data: each 8 byte integer
139
+ udef_char_size = struct.unpack('!q',self.f.read(8))[0]
140
+ udef_int_size = struct.unpack('!q',self.f.read(8))[0]
141
+ udef_real_size = struct.unpack('!q',self.f.read(8))[0]
142
+
143
+ ## Time step array: nzs x 8 byte
144
+ time_step = np.zeros(nzs,int)
145
+ for it in range(nzs):
146
+ time_step[it] = struct.unpack('!q',self.f.read(8))[0]
147
+
148
+ if attribute_mode==EAS3_ALL_ATTR:
149
+ ## Time step attributes
150
+ attr_time = [ self.f.read(ATTRLEN).decode('UTF-8').strip() ]
151
+ for it in range(1,nzs):
152
+ attr_time.append( self.f.read(ATTRLEN).decode('UTF-8').strip() )
153
+ ## Parameter attributes
154
+ attr_param = [ self.f.read(ATTRLEN).decode('UTF-8').strip() ]
155
+ for it in range(1,npar):
156
+ attr_param.append( self.f.read(ATTRLEN).decode('UTF-8').strip() )
157
+
158
+ # Spatial attributes
159
+ attr_dim1 = self.f.read(ATTRLEN).decode('UTF-8').strip()
160
+ attr_dim2 = self.f.read(ATTRLEN).decode('UTF-8').strip()
161
+ attr_dim3 = self.f.read(ATTRLEN).decode('UTF-8').strip()
162
+
163
+ ## If geometry mode > EAS3_NO_G for time
164
+ if gmode_time == EAS3_X0DX_G:
165
+ time_data = np.zeros(2)
166
+ for it in range(2):
167
+ time_data[it] = struct.unpack('!d',self.f.read(8))[0]
168
+ elif gmode_time == EAS3_ALL_G:
169
+ time_data = np.zeros(size_time)
170
+ for it in range(size_time):
171
+ time_data[it] = struct.unpack('!d',self.f.read(8))[0]
172
+ else:
173
+ time_data = np.zeros(1)
174
+
175
+ ## If geometry mode > EAS3_NO_G for parameters
176
+ if gmode_param > EAS3_NO_G:
177
+ param = np.zeros(size_param)
178
+ for it in range(size_param):
179
+ param[it] = struct.unpack('!d',self.f.read(8))[0]
180
+
181
+ ## If geometry mode > EAS3_NO_G for dimensions 1 to 3
182
+ dim1_data = np.zeros(size_dim1)
183
+ if gmode_dim1 > EAS3_NO_G:
184
+ for it in range(size_dim1):
185
+ dim1_data[it] = struct.unpack('!d',self.f.read(8))[0]
186
+ if abs(dim1_data[0]) < 1e-18: dim1_data[0] = 0.
187
+ dim2_data = np.zeros(size_dim2)
188
+ if gmode_dim2 > EAS3_NO_G:
189
+ for it in range(size_dim2):
190
+ dim2_data[it] = struct.unpack('!d',self.f.read(8))[0]
191
+ if abs(dim2_data[0]) < 1e-18: dim2_data[0] = 0.
192
+ dim3_data = np.zeros(size_dim3)
193
+ if gmode_dim3 > EAS3_NO_G:
194
+ for it in range(size_dim3):
195
+ dim3_data[it] = struct.unpack('!d',self.f.read(8))[0]
196
+ else:
197
+ dim3_data = None
198
+
199
+ ## If user-defined data is chosen
200
+ if udef==EAS3_ALL_UDEF:
201
+ udef_char = []
202
+ for it in range(udef_char_size):
203
+ udef_char.append(self.f.read(UDEFLEN).decode('UTF-8').strip())
204
+ udef_int = np.zeros(udef_int_size,int)
205
+ for it in range(udef_int_size):
206
+ udef_int[it] = struct.unpack('!q',self.f.read(8))[0]
207
+ udef_real = np.zeros(udef_real_size)
208
+ for it in range(udef_real_size):
209
+ udef_real[it] = struct.unpack('!d',self.f.read(8))[0]
210
+
211
+ ## Option: convert gmode=EAS3_X0DX_G to gmode=EAS3_ALL_G
212
+ if ChangeGmode==1:
213
+ if gmode_dim1==EAS3_X0DX_G:
214
+ dim1_data = np.linspace(dim1_data[0],dim1_data[0]+dim1_data[1]*(ndim1-1), ndim1)
215
+ gmode_dim1=EAS3_ALL_G
216
+ if gmode_dim2==EAS3_X0DX_G:
217
+ dim2_data = np.linspace(dim2_data[0],dim2_data[0]+dim2_data[1]*(ndim2-1), ndim2)
218
+ gmode_dim2=EAS3_ALL_G
219
+ if gmode_dim3==EAS3_X0DX_G:
220
+ dim3_data = np.linspace(dim3_data[0],dim3_data[0]+dim3_data[1]*(ndim3-1), ndim3)
221
+ gmode_dim3=EAS3_ALL_G
222
+ if gmode_time==EAS3_X0DX_G:
223
+ time_data = np.linspace(time_data[0],time_data[0]+time_data[1]*(nzs -1), nzs )
224
+ gmode_time=EAS3_ALL_G
225
+
226
+ # ===
227
+
228
+ self.attr_param = attr_param
229
+ self.scalars = attr_param
230
+ self.t = time_data
231
+ self.nt = self.t.size
232
+
233
+ if (attr_dim1=='x'):
234
+ self.x = dim1_data
235
+ elif (attr_dim1=='y'):
236
+ self.y = dim1_data
237
+ elif (attr_dim1=='z'):
238
+ self.z = dim1_data
239
+ else:
240
+ raise ValueError('attr_dim1 = %s not identifiable as any x,y,z'%attr_dim1)
241
+
242
+ if (attr_dim2=='x'):
243
+ self.x = dim2_data
244
+ elif (attr_dim2=='y'):
245
+ self.y = dim2_data
246
+ elif (attr_dim2=='z'):
247
+ self.z = dim2_data
248
+ else:
249
+ raise ValueError('attr_dim2 = %s not identifiable as any x,y,z'%attr_dim2)
250
+
251
+ if (attr_dim3=='x'):
252
+ self.x = dim3_data
253
+ elif (attr_dim3=='y'):
254
+ self.y = dim3_data
255
+ elif (attr_dim3=='z'):
256
+ self.z = dim3_data
257
+ else:
258
+ raise ValueError('attr_dim3 = %s not identifiable as any x,y,z'%attr_dim3)
259
+
260
+ # === transpose order to [x,y,z]
261
+
262
+ if all([(attr_dim1=='x'),(attr_dim2=='y'),(attr_dim3=='z')]):
263
+ self.axes_transpose_xyz = (0,1,2)
264
+ elif all([(attr_dim1=='y'),(attr_dim2=='x'),(attr_dim3=='z')]):
265
+ self.axes_transpose_xyz = (1,0,2)
266
+ elif all([(attr_dim1=='z'),(attr_dim2=='y'),(attr_dim3=='x')]):
267
+ self.axes_transpose_xyz = (2,1,0)
268
+ elif all([(attr_dim1=='x'),(attr_dim2=='z'),(attr_dim3=='y')]):
269
+ self.axes_transpose_xyz = (0,2,1)
270
+ elif all([(attr_dim1=='y'),(attr_dim2=='z'),(attr_dim3=='x')]):
271
+ self.axes_transpose_xyz = (2,0,1)
272
+ elif all([(attr_dim1=='z'),(attr_dim2=='x'),(attr_dim3=='y')]):
273
+ self.axes_transpose_xyz = (1,2,0)
274
+ else:
275
+ raise ValueError('could not determine axes transpose')
276
+
277
+ # ===
278
+
279
+ self.nx = self.x.shape[0]
280
+ self.ny = self.y.shape[0]
281
+ self.nz = self.z.shape[0]
282
+ self.ngp = self.nx*self.ny*self.nz
283
+
284
+ if self.verbose: print(72*'-')
285
+ if self.verbose: even_print('nx', '%i'%self.nx )
286
+ if self.verbose: even_print('ny', '%i'%self.ny )
287
+ if self.verbose: even_print('nz', '%i'%self.nz )
288
+ if self.verbose: even_print('ngp', '%i'%self.ngp )
289
+ if self.verbose: print(72*'-')
290
+
291
+ if self.verbose: even_print('x_min', '%0.2f'%self.x.min())
292
+ if self.verbose: even_print('x_max', '%0.2f'%self.x.max())
293
+ if self.verbose: even_print('dx begin : end', '%0.3E : %0.3E'%( (self.x[1]-self.x[0]), (self.x[-1]-self.x[-2]) ))
294
+ if self.verbose: even_print('y_min', '%0.2f'%self.y.min())
295
+ if self.verbose: even_print('y_max', '%0.2f'%self.y.max())
296
+ if self.verbose: even_print('dy begin : end', '%0.3E : %0.3E'%( (self.y[1]-self.y[0]), (self.y[-1]-self.y[-2]) ))
297
+ if self.verbose: even_print('z_min', '%0.2f'%self.z.min())
298
+ if self.verbose: even_print('z_max', '%0.2f'%self.z.max())
299
+ if self.verbose: even_print('dz begin : end', '%0.3E : %0.3E'%( (self.z[1]-self.z[0]), (self.z[-1]-self.z[-2]) ))
300
+ if self.verbose: print(72*'-'+'\n')
301
+
302
+ # ===
303
+
304
+ ## make a python dict from udef vectors
305
+ udef_dict = {}
306
+ for i in range(len(udef_char)):
307
+ if (udef_char[i]!=''):
308
+ if (udef_int[i]!=0):
309
+ udef_dict[udef_char[i]] = int(udef_int[i])
310
+ elif (udef_real[i]!=0.):
311
+ udef_dict[udef_char[i]] = float(udef_real[i])
312
+ else:
313
+ udef_dict[udef_char[i]] = 0.
314
+
315
+ if self.verbose:
316
+ print('udef from EAS3\n' + 72*'-')
317
+ for key in udef_dict:
318
+ if isinstance(udef_dict[key],float):
319
+ even_print(key, '%0.8f'%udef_dict[key])
320
+ elif isinstance(udef_dict[key],int):
321
+ even_print(key, '%i'%udef_dict[key])
322
+ else:
323
+ #print(type(udef_dict[key]))
324
+ raise TypeError('udef dict item not float or int')
325
+ print(72*'-'+'\n')
326
+
327
+ self.Ma = udef_dict['Ma']
328
+ self.Re = udef_dict['Re']
329
+ self.Pr = udef_dict['Pr']
330
+ self.kappa = udef_dict['kappa']
331
+ self.R = udef_dict['R']
332
+
333
+ ## get T_inf
334
+ if ('T_unend' in udef_dict):
335
+ self.T_inf = udef_dict['T_unend']
336
+ elif ('T_inf' in udef_dict):
337
+ self.T_inf = udef_dict['T_inf']
338
+ elif ('Tinf' in udef_dict):
339
+ self.T_inf = udef_dict['Tinf']
340
+ else:
341
+ raise ValueError('T_inf not found in udef')
342
+
343
+ ## get p_inf
344
+ if ('p_unend' in udef_dict):
345
+ self.p_inf = udef_dict['p_unend']
346
+ elif ('p_inf' in udef_dict):
347
+ self.p_inf = udef_dict['p_inf']
348
+ elif ('pinf' in udef_dict):
349
+ self.p_inf = udef_dict['pinf']
350
+ else:
351
+ raise ValueError('p_inf not found in udef')
352
+
353
+ ## !!! import what is called 'C_Suth' in NS3D as 'S_Suth' !!!
354
+ self.S_Suth = udef_dict['C_Suth'] ## [K] --> Sutherland temperature, usually 110.4 [K] for air
355
+
356
+ self.mu_Suth_ref = udef_dict['mu_Suth_ref'] ## usually 1.716e-05 [kg/(m·s)] --> μ of air at T_Suth_ref = 273.15 [K]
357
+ self.T_Suth_ref = udef_dict['T_Suth_ref'] ## usually 273.15 [K]
358
+
359
+ self.C_Suth = self.mu_Suth_ref/(self.T_Suth_ref**(3/2))*(self.T_Suth_ref + self.S_Suth) ## ~1.458e-6 [kg/(m·s·√K)]
360
+
361
+ ## derived values
362
+ self.rho_inf = self.p_inf/(self.R*self.T_inf) ## mass density [kg/m³]
363
+ self.mu_inf = self.mu_Suth_ref*(self.T_inf/self.T_Suth_ref)**(3/2) * ((self.T_Suth_ref+self.S_Suth)/(self.T_inf+self.S_Suth)) ## [Pa s] | [N s m^-2]
364
+ self.nu_inf = self.mu_inf / self.rho_inf ## kinematic viscosity [m²/s] --> momentum diffusivity
365
+
366
+ ## additional
367
+ if ('total_avg_time' in udef_dict):
368
+ self.total_avg_time = udef_dict['total_avg_time']
369
+ if ('total_avg_iter_count' in udef_dict):
370
+ self.total_avg_iter_count = udef_dict['total_avg_iter_count']
371
+ if ('nz' in udef_dict):
372
+ self.nz = udef_dict['nz']
373
+ if ('cp' in udef_dict):
374
+ self.cp = udef_dict['cp']
375
+ if ('cv' in udef_dict):
376
+ self.cv = udef_dict['cv']
377
+
378
+ # ===
379
+
380
+ if self.verbose: print(72*'-')
381
+ if self.verbose: even_print('Ma' , '%0.2f [-]' % self.Ma )
382
+ if self.verbose: even_print('Re' , '%0.1f [-]' % self.Re )
383
+ if self.verbose: even_print('Pr' , '%0.3f [-]' % self.Pr )
384
+ if self.verbose: even_print('T_inf' , '%0.3f [K]' % self.T_inf )
385
+ if self.verbose: even_print('p_inf' , '%0.1f [Pa]' % self.p_inf )
386
+ if self.verbose: even_print('kappa' , '%0.3f [-]' % self.kappa )
387
+ if self.verbose: even_print('R' , '%0.3f [J/(kg·K)]' % self.R )
388
+ if self.verbose: even_print('mu_Suth_ref' , '%0.6E [kg/(m·s)]' % self.mu_Suth_ref )
389
+ if self.verbose: even_print('T_Suth_ref' , '%0.2f [K]' % self.T_Suth_ref )
390
+ if self.verbose: even_print('C_Suth' , '%0.5e [kg/(m·s·√K)]' % self.C_Suth )
391
+ if self.verbose: even_print('S_Suth' , '%0.2f [K]' % self.S_Suth )
392
+ if self.verbose: print(72*'-')
393
+
394
+ self.a_inf = np.sqrt(self.kappa*self.R*self.T_inf)
395
+ self.U_inf = self.Ma*self.a_inf
396
+ self.cp = self.R*self.kappa/(self.kappa-1.)
397
+ self.cv = self.cp/self.kappa
398
+ self.recov_fac = self.Pr**(1/3)
399
+ self.Taw = self.T_inf + self.recov_fac*self.U_inf**2/(2*self.cp)
400
+ self.lchar = self.Re*self.nu_inf/self.U_inf
401
+
402
+ if self.verbose: even_print('rho_inf' , '%0.3f [kg/m³]' % self.rho_inf )
403
+ if self.verbose: even_print('mu_inf' , '%0.6E [kg/(m·s)]' % self.mu_inf )
404
+ if self.verbose: even_print('nu_inf' , '%0.6E [m²/s]' % self.nu_inf )
405
+ if self.verbose: even_print('a_inf' , '%0.6f [m/s]' % self.a_inf )
406
+ if self.verbose: even_print('U_inf' , '%0.6f [m/s]' % self.U_inf )
407
+ if self.verbose: even_print('cp' , '%0.3f [J/(kg·K)]' % self.cp )
408
+ if self.verbose: even_print('cv' , '%0.3f [J/(kg·K)]' % self.cv )
409
+ #if self.verbose: even_print('recovery factor' , '%0.6f [-]' % self.recov_fac )
410
+ #if self.verbose: even_print('Taw' , '%0.3f [K]' % self.Taw )
411
+ if self.verbose: even_print('lchar' , '%0.6E [m]' % self.lchar )
412
+ if self.verbose: print(72*'-'+'\n')
413
+
414
+ # ===
415
+
416
+ udef_char = [ 'Ma', 'Re', 'Pr', 'kappa', 'R', 'p_inf', 'T_inf', 'C_Suth', 'mu_Suth_ref', 'T_Suth_ref' ]
417
+ udef_real = [self.Ma , self.Re , self.Pr , self.kappa, self.R, self.p_inf, self.T_inf, self.C_Suth, self.mu_Suth_ref, self.T_Suth_ref ]
418
+ udef = dict(zip(udef_char, udef_real))
419
+
420
+ return udef