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.
@@ -0,0 +1,1193 @@
1
+ import os
2
+ import re
3
+ import sys
4
+ import timeit
5
+ from pathlib import PurePosixPath
6
+
7
+ import h5py
8
+ import numpy as np
9
+ import psutil
10
+ from mpi4py import MPI
11
+ from tqdm import tqdm
12
+
13
+ #from .rgd import rgd ## no!
14
+ from .gradient import gradient
15
+ from .h5 import h5_chunk_sizer, h5_print_contents
16
+ from .utils import even_print, format_time_string
17
+
18
+ # ======================================================================
19
+
20
+ def _calc_turb_budget_xpln(self, **kwargs):
21
+ '''
22
+ calculate budget of turbulent kinetic energy (k)
23
+ -----
24
+ - dimensional [SI]
25
+ - designed for analyzing unsteady, thin planes in [x]
26
+ -----
27
+ SI units of terms are [kg/(m·s³)] or [kg m^-1 s^-3]
28
+ normalize with
29
+ * ν / u**4 / ρ --> *[ kg^-1 m s^3]
30
+ or
31
+ / ( u**4 * ρ / ν ) --> /[kg m^-1 s^-3]
32
+ -----
33
+ Pirozzoli Grasso Gatski (2004)
34
+ Direct numerical simulation and analysis of a spatially evolving supersonic turbulent boundary layer at M=2.25
35
+ https://doi.org/10.1063/1.1637604
36
+ -----
37
+ Gaurini Moser Shariff Wray (2000)
38
+ Direct numerical simulation of a supersonic turbulent boundary layer at Mach 2.5
39
+ https://doi.org/10.1017/S0022112000008466
40
+ '''
41
+
42
+ rgd_meta = type(self) ## workaround for using rgd()
43
+
44
+ if (self.rank==0):
45
+ verbose = True
46
+ else:
47
+ verbose = False
48
+
49
+ if verbose: print('\n'+'rgd.calc_turb_budget_xpln()'+'\n'+72*'-')
50
+ t_start_func = timeit.default_timer()
51
+
52
+ if (self.fsubtype!='unsteady'):
53
+ raise RuntimeError(f"file subtype must be 'unsteady' but is '{str(self.fsubtype)}'")
54
+
55
+ if not self.usingmpi:
56
+ raise ValueError('rgd.calc_turb_budget_xpln() currently only works in MPI mode')
57
+
58
+ rx = kwargs.get('rx',1)
59
+ ry = kwargs.get('ry',1)
60
+ rz = kwargs.get('rz',1)
61
+ rt = kwargs.get('rt',1)
62
+
63
+ ## number of subdivisions per rank [t] range
64
+ ct = kwargs.get( 'ct' , int((self.nt//self.n_ranks)//4) )
65
+ if not isinstance(ct,int) or (ct<1):
66
+ raise TypeError('ct should be a positive non-zero int')
67
+
68
+ # st = kwargs.get('st',1)
69
+ # if not isinstance(st,int) or (st<1):
70
+ # raise TypeError('st should be a positive non-zero int')
71
+ # if (self.nt%st!=0):
72
+ # raise ValueError('nt not divisible by st')
73
+
74
+ force = kwargs.get('force',False)
75
+
76
+ fn_h5_mean = kwargs.get('fn_h5_mean',None)
77
+ fn_h5_out = kwargs.get('fn_h5_out',None)
78
+
79
+ save_unsteady = kwargs.get('save_unsteady',False)
80
+ fn_h5_out_unsteady = kwargs.get('fn_h5_out_unsteady',None)
81
+
82
+ acc = kwargs.get('acc', 6)
83
+ edge_stencil = kwargs.get('edge_stencil', 'half')
84
+
85
+ chunk_kb = kwargs.get('chunk_kb',4*1024) ## h5 chunk size: default 4 [MB]
86
+ chunk_constraint = kwargs.get('chunk_constraint',(1,None,None,None)) ## the 'constraint' parameter for sizing h5 chunks
87
+ chunk_base = kwargs.get('chunk_base',2)
88
+
89
+ ## only distribute data in [t]
90
+ if (rx!=1):
91
+ raise AssertionError('rx!=1')
92
+ if (ry!=1):
93
+ raise AssertionError('ry!=1')
94
+ if (rz!=1):
95
+ raise AssertionError('rz!=1')
96
+
97
+ if not isinstance(rt,int) or (rt<1):
98
+ raise ValueError('rt should be a positive non-zero int')
99
+
100
+ if (rx*ry*rz*rt != self.n_ranks):
101
+ raise AssertionError('rx*ry*rz*rt != self.n_ranks')
102
+ if (rx>self.nx):
103
+ raise AssertionError('rx>self.nx')
104
+ if (ry>self.ny):
105
+ raise AssertionError('ry>self.ny')
106
+ if (rz>self.nz):
107
+ raise AssertionError('rz>self.nz')
108
+ if (rt>self.nt):
109
+ raise AssertionError('rt>self.nt')
110
+
111
+ # ===
112
+
113
+ rtl_ = np.array_split(np.arange(self.nt,dtype=np.int64),rt)
114
+ rtl = [[b[0],b[-1]+1] for b in rtl_ ]
115
+ rt1,rt2 = rtl[self.rank]
116
+ ntr = rt2 - rt1
117
+
118
+ if (ntr<1):
119
+ print(f'rank {self.rank:d} ntr < 1')
120
+ self.comm.Abort(1)
121
+ if (ntr<ct):
122
+ print(f'rank {self.rank:d} ntr < ct')
123
+ self.comm.Abort(1)
124
+
125
+ ## [t] sub chunk range --> ctl = list of ranges in rt1:rt2
126
+ ctl_ = np.array_split( np.arange(rt1,rt2) , ct )
127
+ ctl = [[b[0],b[-1]+1] for b in ctl_ ]
128
+
129
+ ## check that no sub ranges are <=1
130
+ for a_ in [ ctl_[1]-ctl_[0] for ctl_ in ctl ]:
131
+ if (a_ <= 1):
132
+ #raise ValueError
133
+ print(f'rank {self.rank:d} has [t] a chunk subrange <= 1')
134
+ self.comm.Abort(1)
135
+
136
+ ## the average sub [t] chunk size on this rank
137
+ #avg_ntc = np.mean( [ ctl_[1]-ctl_[0] for ctl_ in ctl ] )
138
+ #min_ntc = min( [ ctl_[1]-ctl_[0] for ctl_ in ctl ] )
139
+ #max_ntc = max( [ ctl_[1]-ctl_[0] for ctl_ in ctl ] )
140
+
141
+ ## get all ntcs
142
+ my_ntcs = [ ctl_[1]-ctl_[0] for ctl_ in ctl ]
143
+ G = self.comm.gather([ self.rank , my_ntcs ], root=0)
144
+ G = self.comm.bcast(G, root=0)
145
+
146
+ allntcs = []
147
+ for G_ in G:
148
+ allntcs += G_[1]
149
+ allntcs = np.array( allntcs , dtype=np.int64 )
150
+
151
+ avg_ntc = np.mean( allntcs , dtype=np.float64 )
152
+ min_ntc = allntcs.min()
153
+ max_ntc = allntcs.max()
154
+
155
+ if 1: ## check that [t] sub-chunk ranges are correct
156
+
157
+ mytimeindices = []
158
+ for ctl_ in ctl:
159
+ ct1, ct2 = ctl_
160
+ mytimeindices += [ ti_ for ti_ in self.ti[ct1:ct2] ]
161
+
162
+ G = self.comm.gather([ self.rank , mytimeindices ], root=0)
163
+ G = self.comm.bcast(G, root=0)
164
+
165
+ alltimeindices = []
166
+ for G_ in G:
167
+ alltimeindices += G_[1]
168
+ alltimeindices = np.array( sorted(alltimeindices), dtype=np.int64 )
169
+
170
+ if not np.array_equal( alltimeindices , self.ti ):
171
+ raise AssertionError
172
+ if not np.array_equal( alltimeindices , np.arange(self.nt, dtype=np.int64) ):
173
+ raise AssertionError
174
+
175
+ self.comm.Barrier()
176
+
177
+ ## mean file name (for reading)
178
+ if (fn_h5_mean is None):
179
+ fname_path = os.path.dirname(self.fname)
180
+ fname_base = os.path.basename(self.fname)
181
+ fname_root, fname_ext = os.path.splitext(fname_base)
182
+ fname_mean_h5_base = fname_root+'_mean.h5'
183
+ #fn_h5_mean = os.path.join(fname_path, fname_mean_h5_base)
184
+ fn_h5_mean = str(PurePosixPath(fname_path, fname_mean_h5_base))
185
+ #fn_h5_mean = Path(fname_path, fname_mean_h5_base)
186
+
187
+ if not os.path.isfile(fn_h5_mean):
188
+ raise FileNotFoundError('%s not found!'%fn_h5_mean)
189
+
190
+ ## turb_budget .h5 file name (for writing) --> AVERAGED
191
+ if (fn_h5_out is None):
192
+ fname_path = os.path.dirname(self.fname)
193
+ fname_base = os.path.basename(self.fname)
194
+ fname_root, fname_ext = os.path.splitext(fname_base)
195
+ fname_root = re.findall(r'io\S+_mpi_[0-9]+', fname_root)[0]
196
+ fn_h5_out_base = fname_root+'_turb_budget.h5'
197
+ fn_h5_out = str(PurePosixPath(fname_path, fn_h5_out_base))
198
+
199
+ if os.path.isfile(fn_h5_out) and (force is False):
200
+ raise ValueError(f'{fn_h5_out} already present & force=False')
201
+
202
+ ## turb_budget .h5 file name (for writing) --> UNSTEADY
203
+ if save_unsteady:
204
+ if (fn_h5_out_unsteady is None):
205
+ fname_path = os.path.dirname(self.fname)
206
+ fname_base = os.path.basename(self.fname)
207
+ fname_root, fname_ext = os.path.splitext(fname_base)
208
+ fname_root = re.findall(r'io\S+_mpi_[0-9]+', fname_root)[0]
209
+ fn_h5_out_unsteady_base = fname_root+'_turb_budget_unsteady.h5'
210
+ fn_h5_out_unsteady = str(PurePosixPath(fname_path, fn_h5_out_unsteady_base))
211
+
212
+ if os.path.isfile(fn_h5_out_unsteady) and (force is False):
213
+ raise ValueError(f'{fn_h5_out_unsteady} already present & force=False')
214
+
215
+ if verbose: even_print('fn_h5' , self.fname )
216
+ if verbose: even_print('fn_h5_mean' , fn_h5_mean )
217
+ if verbose: even_print('fn_h5_out' , fn_h5_out )
218
+ if verbose: print(72*'-')
219
+ if verbose: even_print('save unsteady' , str(save_unsteady) )
220
+ if verbose and save_unsteady:
221
+ even_print('unsteady file' , fn_h5_out_unsteady )
222
+ if verbose: print(72*'-')
223
+
224
+ # ===
225
+
226
+ if verbose: even_print('nx' , f'{self.nx:d}' )
227
+ if verbose: even_print('ny' , f'{self.ny:d}' )
228
+ if verbose: even_print('nz' , f'{self.nz:d}' )
229
+ if verbose: even_print('nt' , f'{self.nt:d}' )
230
+ if verbose: print(72*'-')
231
+ if verbose: even_print('rt' , f'{rt:d}' )
232
+ if verbose: even_print('ct' , f'{ct:d}' )
233
+ #if verbose: even_print('st' , f'{st:d}' )
234
+ if verbose: even_print('ntr' , f'{ntr:d}' )
235
+ if verbose: even_print('avg [t] chunk nt' , f'{avg_ntc:0.6f}' )
236
+ if verbose: even_print('min [t] chunk nt' , f'{min_ntc:d}' )
237
+ if verbose: even_print('max [t] chunk nt' , f'{max_ntc:d}' )
238
+ if verbose: print(72*'-')
239
+
240
+ # === init outfiles
241
+
242
+ #if verbose: print(72*'*')
243
+ if verbose: print(fn_h5_out)
244
+ if verbose: print(len(fn_h5_out)*'-')
245
+
246
+ ## initialize file: turbulent kinetic energy budget, AVERAGED
247
+ #with rgd(fn_h5_out, 'w', force=force, driver='mpio', comm=self.comm) as f1:
248
+ with rgd_meta(fn_h5_out, 'w', force=force, driver='mpio', comm=self.comm) as f1:
249
+
250
+ f1.init_from_rgd(self.fname, t_info=False)
251
+
252
+ ## set some top-level attributes
253
+ #f1.attrs['duration_avg'] = duration_avg ## duration of mean
254
+ f1.attrs['duration_avg'] = self.t[-1] - self.t[0]
255
+ #f1_mean.attrs['duration_avg'] = self.duration
256
+ f1.attrs['dt'] = self.t[1] - self.t[0]
257
+ #f1.attrs['fclass'] = 'rgd'
258
+ f1.attrs['fsubtype'] = 'mean'
259
+
260
+ shape = (1,f1.nz,f1.ny,f1.nx)
261
+ chunks = h5_chunk_sizer(nxi=shape, constraint=chunk_constraint, size_kb=chunk_kb, base=chunk_base, itemsize=8)
262
+
263
+ data_gb = 8 * 1 * f1.nz*f1.ny*f1.nx / 1024**3
264
+
265
+ for dss in ['production','dissipation','transport','diffusion','p_dilatation','p_diffusion']:
266
+
267
+ if verbose:
268
+ even_print('initializing data/%s'%(dss,),'%0.1f [GB]'%(data_gb,))
269
+
270
+ dset = f1.create_dataset(
271
+ f'data/{dss}',
272
+ shape=shape,
273
+ dtype=np.float64,
274
+ chunks=chunks,
275
+ #data=np.full(shape,0.,np.float64),
276
+ )
277
+
278
+ chunk_kb_ = np.prod(dset.chunks) * dset.dtype.itemsize / 1024. ## actual
279
+ if verbose:
280
+ even_print('chunk shape (t,z,y,x)','%s'%str(dset.chunks))
281
+ even_print('chunk size','%i [KB]'%int(round(chunk_kb_)))
282
+
283
+ # === replace dims/t array --> take last time of series
284
+ if ('dims/t' in f1):
285
+ del f1['dims/t']
286
+ f1.create_dataset('dims/t', data=np.array([self.t[-1]],dtype=np.float64), )
287
+
288
+ if hasattr(f1, 'duration_avg'):
289
+ if verbose: even_print('duration_avg', '%0.2f'%f1.duration_avg)
290
+
291
+ if verbose: print(72*'-')
292
+
293
+ ## initialize file: turbulent kinetic energy budget, UNSTEADY
294
+ if save_unsteady:
295
+
296
+ if verbose: print(fn_h5_out_unsteady)
297
+ if verbose: print(len(str(fn_h5_out_unsteady))*'-')
298
+
299
+ #with rgd(fn_h5_out_unsteady, 'w', force=force, driver='mpio', comm=self.comm) as f1:
300
+ with rgd_meta(fn_h5_out_unsteady, 'w', force=force, driver='mpio', comm=self.comm) as f1:
301
+
302
+ f1.init_from_rgd(self.fname, t_info=True)
303
+
304
+ ## set some top-level attributes
305
+ #f1.attrs['duration_avg'] = duration_avg ## duration of mean
306
+ f1.attrs['duration_avg'] = self.t[-1] - self.t[0]
307
+ #f1_mean.attrs['duration_avg'] = self.duration
308
+ f1.attrs['dt'] = self.t[1] - self.t[0]
309
+ #f1.attrs['fclass'] = 'rgd'
310
+ f1.attrs['fsubtype'] = 'unsteady'
311
+
312
+ shape = (self.nt,f1.nz,f1.ny,f1.nx)
313
+ #chunks = h5_chunk_sizer(nxi=shape, constraint=chunk_constraint, size_kb=chunk_kb, base=chunk_base, itemsize=4)
314
+ chunks = self['data/u'].chunks ## use chunk pattern of this file
315
+
316
+ data_gb = 4*self.nt*f1.nz*f1.ny*f1.nx / 1024**3
317
+
318
+ for dss in ['production','dissipation','transport','diffusion']: #,'p_dilatation','p_diffusion']:
319
+
320
+ if verbose:
321
+ even_print('initializing data/%s'%(dss,),'%0.1f [GB]'%(data_gb,))
322
+
323
+ dset = f1.create_dataset(
324
+ f'data/{dss}',
325
+ shape=shape,
326
+ dtype=np.float32,
327
+ chunks=chunks,
328
+ #data=np.full(shape,0.,np.float32),
329
+ )
330
+
331
+ chunk_kb_ = np.prod(dset.chunks) * dset.dtype.itemsize / 1024. ## actual
332
+ if verbose:
333
+ even_print('chunk shape (t,z,y,x)','%s'%str(dset.chunks))
334
+ even_print('chunk size','%i [KB]'%int(round(chunk_kb_)))
335
+
336
+ if verbose: print(72*'-')
337
+
338
+ self.comm.Barrier()
339
+
340
+ # ===
341
+
342
+ ## OPEN the mean file... make sure to close later!
343
+ #hf_mean = rgd(fn_h5_mean, 'r', driver='mpio', comm=self.comm)
344
+ hf_mean = rgd_meta(fn_h5_mean, 'r', driver='mpio', comm=self.comm)
345
+
346
+ ## verify contents of the mean file
347
+ np.testing.assert_allclose( hf_mean.x , self.x , atol=1e-12, rtol=1e-12 )
348
+ np.testing.assert_allclose( hf_mean.y , self.y , atol=1e-12, rtol=1e-12 )
349
+ np.testing.assert_allclose( hf_mean.z , self.z , atol=1e-12, rtol=1e-12 )
350
+ np.testing.assert_allclose( hf_mean.lchar , self.lchar , atol=1e-12, rtol=1e-12 )
351
+ np.testing.assert_allclose( hf_mean.U_inf , self.U_inf , atol=1e-12, rtol=1e-12 )
352
+ np.testing.assert_allclose( hf_mean.T_inf , self.T_inf , atol=1e-12, rtol=1e-12 )
353
+ np.testing.assert_allclose( hf_mean.rho_inf , self.rho_inf , atol=1e-12, rtol=1e-12 )
354
+ if (hf_mean.fsubtype!='mean'):
355
+ raise ValueError
356
+
357
+ # ===
358
+
359
+ data = {} ## dict to be output to .dat file
360
+
361
+ if ('data_dim' not in hf_mean):
362
+ raise ValueError('group data_dim not present')
363
+
364
+ ## put all data from 'data_dim' into the dictionary data which will be pickled at the end
365
+ for dsn in hf_mean['data_dim'].keys():
366
+ d_ = np.copy( hf_mean[f'data_dim/{dsn}'][()] )
367
+ if (d_.ndim == 0):
368
+ d_ = float(d_)
369
+ data[dsn] = d_
370
+
371
+ ## 1D
372
+ #rho_avg = np.copy( hf_mean['data_dim/rho'][()] )
373
+ #u_avg = np.copy( hf_mean['data_dim/u'][()] )
374
+ #v_avg = np.copy( hf_mean['data_dim/v'][()] )
375
+ #w_avg = np.copy( hf_mean['data_dim/w'][()] )
376
+ #T_avg = np.copy( hf_mean['data_dim/T'][()] )
377
+ #p_avg = np.copy( hf_mean['data_dim/p'][()] )
378
+
379
+ ## 0D
380
+ u_tau = float( hf_mean['data_dim/u_tau'][()] )
381
+ nu_wall = float( hf_mean['data_dim/nu_wall'][()] )
382
+ rho_wall = float( hf_mean['data_dim/rho_wall'][()] )
383
+ #T_wall = float( hf_mean['data_dim/T_wall'][()] )
384
+ d99 = float( hf_mean['data_dim/d99'][()] )
385
+ u_99 = float( hf_mean['data_dim/u_99'][()] )
386
+ Re_tau = float( hf_mean['data_dim/Re_tau'][()] )
387
+ Re_theta = float( hf_mean['data_dim/Re_theta'][()] )
388
+ #sc_u_in = float( hf_mean['data_dim/sc_u_in'][()] )
389
+ sc_l_in = float( hf_mean['data_dim/sc_l_in'][()] )
390
+ sc_t_in = float( hf_mean['data_dim/sc_t_in'][()] )
391
+ #sc_u_out = float( hf_mean['data_dim/sc_u_out'][()] )
392
+ #sc_l_out = float( hf_mean['data_dim/sc_l_out'][()] )
393
+ #sc_t_out = float( hf_mean['data_dim/sc_t_out'][()] )
394
+
395
+ ## 0D scalars
396
+ lchar = self.lchar ; data['lchar'] = lchar
397
+ U_inf = self.U_inf ; data['U_inf'] = U_inf
398
+ rho_inf = self.rho_inf ; data['rho_inf'] = rho_inf
399
+ T_inf = self.T_inf ; data['T_inf'] = T_inf
400
+
401
+ #data['M_inf'] = self.M_inf
402
+ data['Ma'] = self.Ma
403
+ data['Pr'] = self.Pr
404
+
405
+ ## read in 1D coordinate arrays & re-dimensionalize
406
+ x = np.copy( self['dims/x'][()] * self.lchar )
407
+ y = np.copy( self['dims/y'][()] * self.lchar )
408
+ z = np.copy( self['dims/z'][()] * self.lchar )
409
+ t = np.copy( self['dims/t'][()] * self.tchar )
410
+
411
+ ## dimensional [s]
412
+ dt = self.dt * self.tchar
413
+ np.testing.assert_allclose(dt, t[1]-t[0], rtol=1e-14, atol=1e-14)
414
+
415
+ t_meas = self.duration * self.tchar
416
+ np.testing.assert_allclose(t_meas, t.max()-t.min(), rtol=1e-14, atol=1e-14)
417
+
418
+ t_eddy = t_meas / ( d99 / u_tau )
419
+
420
+ ## check if constant Δz (calculate Δz+ later)
421
+ dz0 = np.diff(z)[0]
422
+ if not np.all(np.isclose(np.diff(z), dz0, rtol=1e-7)):
423
+ raise NotImplementedError
424
+ np.testing.assert_allclose(dz0, z[1]-z[0], rtol=1e-14, atol=1e-14)
425
+
426
+ zrange = z.max() - z.min()
427
+ np.testing.assert_allclose(zrange, z[-1]-z[0], rtol=1e-14, atol=1e-14)
428
+
429
+ nx = self.nx ; data['nx'] = nx
430
+ ny = self.ny ; data['ny'] = ny
431
+ nz = self.nz ; data['nz'] = nz
432
+ nt = self.nt ; data['nt'] = nt
433
+
434
+ ## add data to dict that gets output
435
+ data['x'] = x
436
+ data['y'] = y
437
+ data['z'] = z
438
+ data['t'] = t
439
+ data['t_meas'] = t_meas
440
+ data['dt'] = dt
441
+ data['dz0'] = dz0
442
+ data['zrange'] = zrange
443
+
444
+ if verbose: even_print('Δt/tchar','%0.8f'%(dt/self.tchar))
445
+ if verbose: even_print('Δt','%0.3e [s]'%(dt,))
446
+ if verbose: even_print('duration/tchar','%0.1f'%(self.duration,))
447
+ if verbose: even_print('duration','%0.3e [s]'%(self.duration*self.tchar,))
448
+ if verbose: print(72*'-')
449
+
450
+ ## report
451
+ if verbose:
452
+ even_print('dt' , f'{dt :0.5e} [s]' )
453
+ even_print('t_meas' , f'{t_meas :0.5e} [s]' )
454
+ even_print('dz0' , f'{dz0 :0.5e} [m]' )
455
+ even_print('zrange' , f'{zrange :0.5e} [m]' )
456
+ print(72*'-')
457
+
458
+ ## report
459
+ if verbose:
460
+ even_print( 'Reτ' , f'{Re_tau:0.1f}' )
461
+ even_print( 'Reθ' , f'{Re_theta:0.1f}' )
462
+ even_print( 'δ99' , f'{d99:0.5e} [m]' )
463
+ even_print( 'δν=(ν_wall/u_τ)' , f'{sc_l_in:0.5e} [m]' )
464
+ even_print( 'U_inf' , f'{self.U_inf:0.3f} [m/s]' )
465
+ even_print( 'uτ' , f'{u_tau:0.3f} [m/s]' )
466
+ even_print( 'ν_wall' , f'{nu_wall:0.5e} [m²/s]' )
467
+ even_print( 'ρ_wall' , f'{rho_wall:0.6f} [kg/m³]' )
468
+ even_print( 'Δz+' , f'{dz0/sc_l_in:0.3f}' )
469
+ even_print( 'zrange/δ99' , f'{zrange/d99:0.3f}' )
470
+ even_print( 'Δt+' , f'{dt/sc_t_in:0.3f}' )
471
+ print(72*'-')
472
+
473
+ ## report
474
+ if verbose:
475
+ even_print('t_meas/(δ99/u_τ) = t_eddy' , '%0.2f'%t_eddy)
476
+ even_print('t_meas/(δ99/u99)' , '%0.2f'%(t_meas/(d99/u_99)))
477
+ even_print('t_meas/(20·δ99/u99)' , '%0.2f'%(t_meas/(20*d99/u_99)))
478
+ print(72*'-')
479
+
480
+ # ===
481
+
482
+ ## copy AVG [u,v,w] into memory... DIMENSIONAL
483
+ u_re = np.copy( U_inf * hf_mean['data/u'][0,:,:,:].T ).astype(np.float64)
484
+ v_re = np.copy( U_inf * hf_mean['data/v'][0,:,:,:].T ).astype(np.float64)
485
+ w_re = np.copy( U_inf * hf_mean['data/w'][0,:,:,:].T ).astype(np.float64)
486
+ u_fv = np.copy( U_inf * hf_mean['data/u_fv'][0,:,:,:].T ).astype(np.float64)
487
+ v_fv = np.copy( U_inf * hf_mean['data/v_fv'][0,:,:,:].T ).astype(np.float64)
488
+ w_fv = np.copy( U_inf * hf_mean['data/w_fv'][0,:,:,:].T ).astype(np.float64)
489
+
490
+ p_re = np.copy( rho_inf * U_inf**2 * hf_mean['data/p'][0,:,:,:].T ).astype(np.float64)
491
+
492
+ ## get Reynolds avg strain tensor elements
493
+ #dudx_re = gradient( u=u_re , x=x , axis=0 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
494
+ #dudy_re = gradient( u=u_re , x=y , axis=1 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
495
+ #dudz_re = gradient( u=u_re , x=z , axis=2 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
496
+ #dvdx_re = gradient( u=v_re , x=x , axis=0 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
497
+ #dvdy_re = gradient( u=v_re , x=y , axis=1 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
498
+ #dvdz_re = gradient( u=v_re , x=z , axis=2 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
499
+ #dwdx_re = gradient( u=w_re , x=x , axis=0 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
500
+ #dwdy_re = gradient( u=w_re , x=y , axis=1 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
501
+ #dwdz_re = gradient( u=w_re , x=z , axis=2 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
502
+
503
+ ## get Favre avg strain tensor elements
504
+ dudx_fv = gradient( u=u_fv , x=x , axis=0 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
505
+ dudy_fv = gradient( u=u_fv , x=y , axis=1 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
506
+ dudz_fv = gradient( u=u_fv , x=z , axis=2 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
507
+ dvdx_fv = gradient( u=v_fv , x=x , axis=0 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
508
+ dvdy_fv = gradient( u=v_fv , x=y , axis=1 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
509
+ dvdz_fv = gradient( u=v_fv , x=z , axis=2 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
510
+ dwdx_fv = gradient( u=w_fv , x=x , axis=0 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
511
+ dwdy_fv = gradient( u=w_fv , x=y , axis=1 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
512
+ dwdz_fv = gradient( u=w_fv , x=z , axis=2 , acc=acc , edge_stencil=edge_stencil )[:,:,:,np.newaxis]
513
+
514
+ ## accumulators for per-timestep sum, at end gets multiplied by (1/nt) to get average
515
+ unsteady_production_sum = np.zeros((nx,ny,nz,1), dtype=np.float64, order='C')
516
+ unsteady_dissipation_sum = np.zeros((nx,ny,nz,1), dtype=np.float64, order='C')
517
+ unsteady_transport_sum = np.zeros((nx,ny,nz,1), dtype=np.float64, order='C')
518
+ unsteady_diffusion_sum = np.zeros((nx,ny,nz,1), dtype=np.float64, order='C')
519
+ unsteady_p_diffusion_sum = np.zeros((nx,ny,nz,1), dtype=np.float64, order='C')
520
+ unsteady_p_dilatation_sum = np.zeros((nx,ny,nz,1), dtype=np.float64, order='C')
521
+
522
+ # ==============================================================
523
+ # check memory
524
+ # ==============================================================
525
+
526
+ hostname = MPI.Get_processor_name()
527
+ mem_free_gb = psutil.virtual_memory().free / 1024**3
528
+ G = self.comm.gather([ self.rank , hostname , mem_free_gb ], root=0)
529
+ G = self.comm.bcast(G, root=0)
530
+
531
+ ## multiple ranks per node
532
+ host_mem = {}
533
+ for rank, host, mem in G:
534
+ if (host not in host_mem) or (mem < host_mem[host]):
535
+ host_mem[host] = mem
536
+ total_free = sum(host_mem.values())
537
+
538
+ if verbose:
539
+ for key,value in host_mem.items():
540
+ even_print(f'RAM free {key}', f'{int(np.floor(value)):d} [GB]')
541
+ print(72*'-')
542
+ even_print('RAM free (local,min)', f'{int(np.floor(min(host_mem.values()))):d} [GB]')
543
+ even_print('RAM free (global)', f'{int(np.floor(total_free)):d} [GB]')
544
+
545
+ shape_read = ( nx, ny, nz, max_ntc ) ## local
546
+ if verbose: even_print('read shape (local,max)', f'[{nx:d},{ny:d},{nz:d},{max_ntc:d}]')
547
+ data_gb = np.dtype(np.float64).itemsize * np.prod(shape_read) / 1024**3
548
+ if verbose: even_print('read size (local)', f'{data_gb:0.2f} [GB]')
549
+ if verbose: even_print('read size (global)', f'{int(np.ceil(data_gb*rt)):d} [GB]')
550
+
551
+ fac = 65
552
+ if verbose: even_print(f'read size (global) ×{fac:d}', f'{int(np.ceil(data_gb*rt*fac)):d} [GB]')
553
+ ram_usage_est = data_gb*rt*fac/total_free
554
+ if verbose: even_print('RAM usage estimate', f'{100*ram_usage_est:0.1f} [%]')
555
+
556
+ self.comm.Barrier()
557
+ if (ram_usage_est>0.90):
558
+ print('RAM consumption might be too high. exiting.')
559
+ self.comm.Abort(1)
560
+
561
+ if verbose:
562
+ print(72*'-')
563
+
564
+ # ==============================================================
565
+ # main loop
566
+ # ==============================================================
567
+
568
+ if verbose:
569
+ progress_bar = tqdm(
570
+ total=ct,
571
+ ncols=100,
572
+ desc='turb budget',
573
+ leave=True,
574
+ file=sys.stdout,
575
+ mininterval=0.1,
576
+ smoothing=0.,
577
+ #bar_format="\033[B{l_bar}{bar}| {n}/{total} [{percentage:.1f}%] {elapsed}/{remaining}\033[A\n\b",
578
+ bar_format="{l_bar}{bar}| {n}/{total} [{percentage:.1f}%] {elapsed}/{remaining}",
579
+ ascii="░█",
580
+ colour='#FF6600',
581
+ )
582
+
583
+ ct_counter=0
584
+ for ctl_ in ctl:
585
+ ct_counter += 1
586
+ ct1, ct2 = ctl_
587
+ ntc = ct2 - ct1
588
+
589
+ G = self.comm.gather([ self.rank , ntc ], root=0)
590
+ G = self.comm.bcast(G, root=0)
591
+ ntc_global = sum( [ g[1] for g in G ] )
592
+
593
+ if (ct>1):
594
+ if verbose:
595
+ mesg = f'[t] sub chunk {ct_counter:d}/{ct:d}'
596
+ tqdm.write( mesg )
597
+ tqdm.write( '-'*len(mesg) )
598
+ tqdm.write( even_print('nt in chunk', f'{ntc_global:d}', s=True) )
599
+
600
+ # === read unsteady data
601
+
602
+ ss1 = ['u','v','w','T','rho','p']
603
+ ss2 = ['uI','vI','wI', 'uII','vII','wII', 'pI'] ## 'TI','rhoI'
604
+
605
+ ## data buffer
606
+ formats = [ np.float64 for s in ss1+ss2 ]
607
+ shape = (nx,ny,nz,ntc)
608
+ dd = np.zeros(shape=shape, dtype={'names':ss1+ss2, 'formats':formats}, order='C')
609
+
610
+ ## read DIMLESS u,v,w,T,rho,p
611
+ for ss in ss1:
612
+ dset = self['data/%s'%ss]
613
+ self.comm.Barrier()
614
+ t_start = timeit.default_timer()
615
+ with dset.collective:
616
+ dd[ss] = dset[ct1:ct2,:,:,:].T
617
+ self.comm.Barrier()
618
+ t_delta = timeit.default_timer() - t_start
619
+ data_gb = dset.dtype.itemsize * nx*ny*nz * ntc_global / 1024**3
620
+ if verbose:
621
+ tqdm.write( even_print('read: %s'%ss, '%0.3f [GB] %0.3f [s] %0.3f [GB/s]'%(data_gb,t_delta,(data_gb/t_delta)), s=True) )
622
+
623
+ ## re-dimensionalize unsteady data
624
+ dd['u'][:,:,:,:] *= U_inf
625
+ dd['v'][:,:,:,:] *= U_inf
626
+ dd['w'][:,:,:,:] *= U_inf
627
+ dd['T'][:,:,:,:] *= T_inf
628
+ dd['rho'][:,:,:,:] *= rho_inf
629
+ dd['p'][:,:,:,:] *= ( rho_inf * U_inf**2 )
630
+
631
+ ## calculate DIMENSIONAL primes
632
+ dd['uI'][:,:,:,:] = dd['u'] - u_re[:,:,:,np.newaxis]
633
+ dd['vI'][:,:,:,:] = dd['v'] - v_re[:,:,:,np.newaxis]
634
+ dd['wI'][:,:,:,:] = dd['w'] - w_re[:,:,:,np.newaxis]
635
+ dd['uII'][:,:,:,:] = dd['u'] - u_fv[:,:,:,np.newaxis]
636
+ dd['vII'][:,:,:,:] = dd['v'] - v_fv[:,:,:,np.newaxis]
637
+ dd['wII'][:,:,:,:] = dd['w'] - w_fv[:,:,:,np.newaxis]
638
+ dd['pI'][:,:,:,:] = dd['p'] - p_re[:,:,:,np.newaxis]
639
+
640
+ if verbose: tqdm.write(72*'-')
641
+
642
+ data_gb = dd.nbytes / 1024**3
643
+ if verbose:
644
+ tqdm.write(even_print('unsteady data (rank)', f'{data_gb:0.1f} [GB]', s=True))
645
+
646
+ # === make VIEWS of the numpy structured array for convenience
647
+
648
+ #u = dd['u'][:,:,:,:]
649
+ #v = dd['v'][:,:,:,:]
650
+ #w = dd['w'][:,:,:,:]
651
+ T = dd['T'][:,:,:,:]
652
+ #p = dd['p'][:,:,:,:]
653
+ rho = dd['rho'][:,:,:,:]
654
+
655
+ uI = dd['uI'][:,:,:,:]
656
+ vI = dd['vI'][:,:,:,:]
657
+ wI = dd['wI'][:,:,:,:]
658
+ uII = dd['uII'][:,:,:,:]
659
+ vII = dd['vII'][:,:,:,:]
660
+ wII = dd['wII'][:,:,:,:]
661
+ pI = dd['pI'][:,:,:,:]
662
+
663
+ mu = np.copy( self.C_Suth * T**(3/2) / (T + self.S_Suth) )
664
+ #nu = np.copy( mu / rho )
665
+
666
+ # dd = None ; del dd
667
+
668
+ self.comm.Barrier()
669
+ mem_free_gb = psutil.virtual_memory().free/1024**3
670
+ if verbose:
671
+ tqdm.write(even_print('mem free', f'{mem_free_gb:0.1f} [GB]', s=True))
672
+
673
+ # === get gradients
674
+
675
+ t_start = timeit.default_timer() ## rank 0 only
676
+
677
+ #dudx = gradient(u, x, acc=acc, edge_stencil=edge_stencil, axis=0)
678
+ #dudy = gradient(u, y, acc=acc, edge_stencil=edge_stencil, axis=1)
679
+ #dudz = gradient(u, z, acc=acc, edge_stencil=edge_stencil, axis=2)
680
+ #dvdx = gradient(v, x, acc=acc, edge_stencil=edge_stencil, axis=0)
681
+ #dvdy = gradient(v, y, acc=acc, edge_stencil=edge_stencil, axis=1)
682
+ #dvdz = gradient(v, z, acc=acc, edge_stencil=edge_stencil, axis=2)
683
+ #dwdx = gradient(w, x, acc=acc, edge_stencil=edge_stencil, axis=0)
684
+ #dwdy = gradient(w, y, acc=acc, edge_stencil=edge_stencil, axis=1)
685
+ #dwdz = gradient(w, z, acc=acc, edge_stencil=edge_stencil, axis=2)
686
+
687
+ duIdx = gradient(uI, x, acc=acc, edge_stencil=edge_stencil, axis=0)
688
+ duIdy = gradient(uI, y, acc=acc, edge_stencil=edge_stencil, axis=1)
689
+ duIdz = gradient(uI, z, acc=acc, edge_stencil=edge_stencil, axis=2)
690
+ dvIdx = gradient(vI, x, acc=acc, edge_stencil=edge_stencil, axis=0)
691
+ dvIdy = gradient(vI, y, acc=acc, edge_stencil=edge_stencil, axis=1)
692
+ dvIdz = gradient(vI, z, acc=acc, edge_stencil=edge_stencil, axis=2)
693
+ dwIdx = gradient(wI, x, acc=acc, edge_stencil=edge_stencil, axis=0)
694
+ dwIdy = gradient(wI, y, acc=acc, edge_stencil=edge_stencil, axis=1)
695
+ dwIdz = gradient(wI, z, acc=acc, edge_stencil=edge_stencil, axis=2)
696
+
697
+ duIIdx = gradient(uII, x, acc=acc, edge_stencil=edge_stencil, axis=0)
698
+ duIIdy = gradient(uII, y, acc=acc, edge_stencil=edge_stencil, axis=1)
699
+ duIIdz = gradient(uII, z, acc=acc, edge_stencil=edge_stencil, axis=2)
700
+ dvIIdx = gradient(vII, x, acc=acc, edge_stencil=edge_stencil, axis=0)
701
+ dvIIdy = gradient(vII, y, acc=acc, edge_stencil=edge_stencil, axis=1)
702
+ dvIIdz = gradient(vII, z, acc=acc, edge_stencil=edge_stencil, axis=2)
703
+ dwIIdx = gradient(wII, x, acc=acc, edge_stencil=edge_stencil, axis=0)
704
+ dwIIdy = gradient(wII, y, acc=acc, edge_stencil=edge_stencil, axis=1)
705
+ dwIIdz = gradient(wII, z, acc=acc, edge_stencil=edge_stencil, axis=2)
706
+
707
+ t_delta = timeit.default_timer() - t_start ## rank 0 only
708
+
709
+ if verbose:
710
+ tqdm.write(even_print('get gradients', format_time_string(t_delta), s=True))
711
+
712
+ self.comm.Barrier()
713
+ mem_free_gb = psutil.virtual_memory().free/1024**3
714
+ if verbose:
715
+ tqdm.write(even_print('mem free', f'{mem_free_gb:0.1f} [GB]', s=True))
716
+
717
+ # === stack velocities into vectors and mean-removed strains into tensors
718
+
719
+ t_start = timeit.default_timer()
720
+
721
+ uI_i = np.stack(( uI , vI , wI ), axis=-1)
722
+ uII_i = np.stack(( uII , vII , wII ), axis=-1)
723
+
724
+ duIdx_ij = np.stack((np.stack((duIdx, duIdy, duIdz), axis=4),
725
+ np.stack((dvIdx, dvIdy, dvIdz), axis=4),
726
+ np.stack((dwIdx, dwIdy, dwIdz), axis=4)), axis=5)
727
+
728
+ duIIdx_ij = np.stack((np.stack((duIIdx, duIIdy, duIIdz), axis=4),
729
+ np.stack((dvIIdx, dvIIdy, dvIIdz), axis=4),
730
+ np.stack((dwIIdx, dwIIdy, dwIIdz), axis=4)), axis=5)
731
+
732
+ t_delta = timeit.default_timer() - t_start
733
+ if verbose:
734
+ tqdm.write(even_print('tensor stacking',format_time_string(t_delta), s=True))
735
+
736
+ self.comm.Barrier()
737
+ mem_free_gb = psutil.virtual_memory().free/1024**3
738
+ if verbose:
739
+ tqdm.write(even_print('mem free', f'{mem_free_gb:0.1f} [GB]', s=True))
740
+
741
+ # === production : P
742
+ if True:
743
+
744
+ if verbose: tqdm.write(72*'-')
745
+ t_start = timeit.default_timer()
746
+
747
+ r_uII_uII = rho*uII*uII
748
+ r_uII_vII = rho*uII*vII
749
+ r_uII_wII = rho*uII*wII
750
+
751
+ r_vII_uII = rho*vII*uII
752
+ r_vII_vII = rho*vII*vII
753
+ r_vII_wII = rho*vII*wII
754
+
755
+ r_wII_uII = rho*wII*uII
756
+ r_wII_vII = rho*wII*vII
757
+ r_wII_wII = rho*wII*wII
758
+
759
+ ## unsteady_production_ = - ( r_uII_uII * dudx_fv + r_uII_vII * dudy_fv + r_uII_wII * dudz_fv \
760
+ ## + r_uII_vII * dvdx_fv + r_vII_vII * dvdy_fv + r_vII_wII * dvdz_fv \
761
+ ## + r_uII_wII * dwdx_fv + r_vII_wII * dwdy_fv + r_wII_wII * dwdz_fv )
762
+
763
+ r_uIIuII_ij = np.stack((np.stack((r_uII_uII, r_uII_vII, r_uII_wII), axis=4),
764
+ np.stack((r_vII_uII, r_vII_vII, r_vII_wII), axis=4),
765
+ np.stack((r_wII_uII, r_wII_vII, r_wII_wII), axis=4)), axis=5)
766
+
767
+ dudx_fv_ij = np.stack((np.stack((dudx_fv, dudy_fv, dudz_fv), axis=4),
768
+ np.stack((dvdx_fv, dvdy_fv, dvdz_fv), axis=4),
769
+ np.stack((dwdx_fv, dwdy_fv, dwdz_fv), axis=4)), axis=5)
770
+
771
+ unsteady_production = -1*np.einsum('xyztij,xyztij->xyzt', r_uIIuII_ij, dudx_fv_ij)
772
+
773
+ ## np.testing.assert_allclose(unsteady_production, unsteady_production_, atol=20000)
774
+ ## print('check passed : np.einsum()')
775
+
776
+ t_delta = timeit.default_timer() - t_start
777
+ if verbose:
778
+ tqdm.write(even_print('calc production', format_time_string(t_delta), s=True))
779
+
780
+ self.comm.Barrier()
781
+
782
+ if save_unsteady: ## write 4D unsteady production
783
+
784
+ ## technically this is an estimate
785
+ data_gb = self.n_ranks * np.prod(unsteady_production.shape) * unsteady_production.dtype.itemsize / 1024**3
786
+
787
+ #with rgd(fn_h5_out_unsteady, 'a', driver='mpio', comm=self.comm) as f1:
788
+ with rgd_meta(fn_h5_out_unsteady, 'a', driver='mpio', comm=self.comm) as f1:
789
+ dset = f1['data/production']
790
+ self.comm.Barrier()
791
+ t_start = timeit.default_timer()
792
+ with dset.collective:
793
+ dset[ct1:ct2,:,:,:] = unsteady_production.T
794
+ self.comm.Barrier()
795
+ t_delta = timeit.default_timer() - t_start
796
+ if verbose:
797
+ tqdm.write(even_print('write: production', '%0.2f [GB] %0.2f [s] %0.3f [GB/s]'%(data_gb,t_delta,(data_gb/t_delta)), s=True))
798
+
799
+ ## combine sums across ranks for this [t] chunk
800
+ unsteady_production_sum_i = np.sum(unsteady_production, axis=3, keepdims=True, dtype=np.float64)
801
+ unsteady_production_sum_buf = np.zeros((nx,ny,nz,1), dtype=np.float64, order='C')
802
+ self.comm.Reduce(
803
+ [unsteady_production_sum_i, MPI.DOUBLE],
804
+ [unsteady_production_sum_buf, MPI.DOUBLE],
805
+ op=MPI.SUM,
806
+ root=0,
807
+ )
808
+
809
+ ## add aggregated sum (across ranks) to the total accumulator
810
+ unsteady_production_sum += unsteady_production_sum_buf
811
+
812
+ # === release mem
813
+ r_uII_uII = None; del r_uII_uII
814
+ r_uII_vII = None; del r_uII_vII
815
+ r_uII_wII = None; del r_uII_wII
816
+ r_vII_uII = None; del r_vII_uII
817
+ r_vII_vII = None; del r_vII_vII
818
+ r_vII_wII = None; del r_vII_wII
819
+ r_wII_uII = None; del r_wII_uII
820
+ r_wII_vII = None; del r_wII_vII
821
+ r_wII_wII = None; del r_wII_wII
822
+ r_uIIuII_ij = None; del r_uIIuII_ij
823
+ dudx_fv_ij = None; del dudx_fv_ij
824
+ unsteady_production = None; del unsteady_production
825
+
826
+ self.comm.Barrier()
827
+ mem_free_gb = psutil.virtual_memory().free/1024**3
828
+ if verbose:
829
+ tqdm.write(even_print('mem free', f'{mem_free_gb:0.1f} [GB]', s=True))
830
+
831
+ # === dissipation : ε
832
+ if True:
833
+
834
+ if verbose: tqdm.write(72*'-')
835
+ t_start = timeit.default_timer()
836
+
837
+ duIIdx_ij_duIdx_ij = np.einsum('xyztij,xyztij->xyzt', duIIdx_ij, duIdx_ij)
838
+ duIIdx_ij_duIdx_ji = np.einsum('xyztij,xyztji->xyzt', duIIdx_ij, duIdx_ij)
839
+
840
+ ## # === from pp_turbulent_budget.F90
841
+ ## unsteady_dissipation_ = mu * ( (duIdx + duIdx) * duIIdx + (duIdy + dvIdx) * duIIdy + (duIdz + dwIdx) * duIIdz \
842
+ ## + (dvIdx + duIdy) * dvIIdx + (dvIdy + dvIdy) * dvIIdy + (dvIdz + dwIdy) * dvIIdz \
843
+ ## + (dwIdx + duIdz) * dwIIdx + (dwIdy + dvIdz) * dwIIdy + (dwIdz + dwIdz) * dwIIdz )
844
+
845
+ unsteady_dissipation = mu*(duIIdx_ij_duIdx_ij + duIIdx_ij_duIdx_ji)
846
+
847
+ ## np.testing.assert_allclose(unsteady_dissipation, unsteady_dissipation_, rtol=1e-4)
848
+ ## print('check passed : np.einsum() : dissipation')
849
+
850
+ t_delta = timeit.default_timer() - t_start
851
+ if verbose:
852
+ tqdm.write(even_print('calc dissipation', format_time_string(t_delta),s=True))
853
+
854
+ self.comm.Barrier()
855
+
856
+ if save_unsteady: ## write 4D unsteady dissipation
857
+
858
+ ## technically this is an estimate
859
+ data_gb = self.n_ranks * np.prod(unsteady_dissipation.shape) * unsteady_dissipation.dtype.itemsize / 1024**3
860
+
861
+ #with rgd(fn_h5_out_unsteady, 'a', driver='mpio', comm=self.comm) as f1:
862
+ with rgd_meta(fn_h5_out_unsteady, 'a', driver='mpio', comm=self.comm) as f1:
863
+ dset = f1['data/dissipation']
864
+ self.comm.Barrier()
865
+ t_start = timeit.default_timer()
866
+ with dset.collective:
867
+ dset[ct1:ct2,:,:,:] = unsteady_dissipation.T
868
+ self.comm.Barrier()
869
+ t_delta = timeit.default_timer() - t_start
870
+ if verbose:
871
+ tqdm.write(even_print('write: dissipation', '%0.2f [GB] %0.2f [s] %0.3f [GB/s]'%(data_gb,t_delta,(data_gb/t_delta)), s=True))
872
+
873
+ ## combine sums across ranks for this [t] chunk
874
+ unsteady_dissipation_sum_i = np.sum(unsteady_dissipation, axis=3, keepdims=True, dtype=np.float64)
875
+ unsteady_dissipation_sum_buf = np.zeros((nx,ny,nz,1), dtype=np.float64, order='C')
876
+ self.comm.Reduce(
877
+ [unsteady_dissipation_sum_i, MPI.DOUBLE],
878
+ [unsteady_dissipation_sum_buf, MPI.DOUBLE],
879
+ op=MPI.SUM,
880
+ root=0,
881
+ )
882
+
883
+ ## add aggregated sum (across ranks) to the total accumulator
884
+ unsteady_dissipation_sum += unsteady_dissipation_sum_buf
885
+
886
+ ## release mem
887
+ duIIdx_ij_duIdx_ij = None; del duIIdx_ij_duIdx_ij
888
+ duIIdx_ij_duIdx_ji = None; del duIIdx_ij_duIdx_ji
889
+ unsteady_dissipation = None; del unsteady_dissipation
890
+
891
+ self.comm.Barrier()
892
+ mem_free_gb = psutil.virtual_memory().free/1024**3
893
+ if verbose:
894
+ tqdm.write(even_print('mem free', f'{mem_free_gb:0.1f} [GB]', s=True))
895
+
896
+ # === transport : T
897
+ if True:
898
+
899
+ if verbose: tqdm.write(72*'-')
900
+ t_start = timeit.default_timer()
901
+
902
+ ## triple correlation
903
+ tc = np.einsum('xyzt,xyzti,xyzti,xyztj->xyztj', rho, uII_i, uII_i, uII_i)
904
+
905
+ #tc_ddx = np.gradient(tc, x, axis=0, edge_order=2)
906
+ #tc_ddy = np.gradient(tc, y, axis=1, edge_order=2)
907
+ #tc_ddz = np.gradient(tc, z, axis=2, edge_order=2)
908
+
909
+ tc_ddx = gradient(tc, x, axis=0, acc=acc, edge_stencil=edge_stencil)
910
+ tc_ddy = gradient(tc, y, axis=1, acc=acc, edge_stencil=edge_stencil)
911
+ tc_ddz = gradient(tc, z, axis=2, acc=acc, edge_stencil=edge_stencil)
912
+
913
+ unsteady_transport = -0.5*(tc_ddx[:,:,:,:,0] + tc_ddy[:,:,:,:,1] + tc_ddz[:,:,:,:,2])
914
+
915
+ t_delta = timeit.default_timer() - t_start
916
+ if verbose:
917
+ tqdm.write(even_print('calc transport', format_time_string(t_delta),s=True))
918
+
919
+ self.comm.Barrier()
920
+
921
+ if save_unsteady: ## write 4D unsteady transport
922
+
923
+ ## technically this is an estimate
924
+ data_gb = self.n_ranks * np.prod(unsteady_transport.shape) * unsteady_transport.dtype.itemsize / 1024**3
925
+
926
+ #with rgd(fn_h5_out_unsteady, 'a', driver='mpio', comm=self.comm) as f1:
927
+ with rgd_meta(fn_h5_out_unsteady, 'a', driver='mpio', comm=self.comm) as f1:
928
+ dset = f1['data/transport']
929
+ self.comm.Barrier()
930
+ t_start = timeit.default_timer()
931
+ with dset.collective:
932
+ dset[ct1:ct2,:,:,:] = unsteady_transport.T
933
+ self.comm.Barrier()
934
+ t_delta = timeit.default_timer() - t_start
935
+ if verbose:
936
+ tqdm.write(even_print('write: transport', '%0.2f [GB] %0.2f [s] %0.3f [GB/s]'%(data_gb,t_delta,(data_gb/t_delta)), s=True))
937
+
938
+ ## combine sums across ranks for this [t] chunk
939
+ unsteady_transport_sum_i = np.sum(unsteady_transport, axis=3, keepdims=True, dtype=np.float64)
940
+ unsteady_transport_sum_buf = np.zeros((nx,ny,nz,1), dtype=np.float64, order='C')
941
+ self.comm.Reduce(
942
+ [unsteady_transport_sum_i, MPI.DOUBLE],
943
+ [unsteady_transport_sum_buf, MPI.DOUBLE],
944
+ op=MPI.SUM,
945
+ root=0,
946
+ )
947
+
948
+ ## add aggregated sum (across ranks) to the total accumulator
949
+ unsteady_transport_sum += unsteady_transport_sum_buf
950
+
951
+ ## release mem
952
+ tc = None; del tc
953
+ tc_ddx = None; del tc_ddx
954
+ tc_ddy = None; del tc_ddy
955
+ tc_ddz = None; del tc_ddz
956
+ unsteady_transport = None; del unsteady_transport
957
+
958
+ self.comm.Barrier()
959
+ mem_free_gb = psutil.virtual_memory().free/1024**3
960
+ if verbose:
961
+ tqdm.write(even_print('mem free', f'{mem_free_gb:0.1f} [GB]', s=True))
962
+
963
+ # === viscous diffusion : D
964
+ if True:
965
+
966
+ if verbose: tqdm.write(72*'-')
967
+ t_start = timeit.default_timer()
968
+
969
+ omega_ij = duIdx_ij + np.transpose(duIdx_ij, axes=(0,1,2,3,5,4))
970
+
971
+ if False:
972
+
973
+ omega_ij_2 = np.stack((np.stack(((duIdx+duIdx), (dvIdx+duIdy), (dwIdx+duIdz)), axis=4),
974
+ np.stack(((duIdy+dvIdx), (dvIdy+dvIdy), (dwIdy+dvIdz)), axis=4),
975
+ np.stack(((duIdz+dwIdx), (dvIdz+dwIdy), (dwIdz+dwIdz)), axis=4)), axis=5)
976
+
977
+ np.testing.assert_allclose(omega_ij, omega_ij_2, rtol=1e-8)
978
+
979
+ if verbose:
980
+ print('check passed : omega_ij')
981
+
982
+ A = np.einsum('xyzt,xyzti,xyztij->xyztj', mu, uI_i, omega_ij)
983
+
984
+ A_ddx = gradient(A[:,:,:,:,0], x, axis=0, acc=acc, edge_stencil=edge_stencil)
985
+ A_ddy = gradient(A[:,:,:,:,1], y, axis=1, acc=acc, edge_stencil=edge_stencil)
986
+ A_ddz = gradient(A[:,:,:,:,2], z, axis=2, acc=acc, edge_stencil=edge_stencil)
987
+
988
+ unsteady_diffusion = A_ddx + A_ddy + A_ddz
989
+
990
+ t_delta = timeit.default_timer() - t_start
991
+ if verbose:
992
+ tqdm.write(even_print('calc diffusion', format_time_string(t_delta),s=True))
993
+
994
+ self.comm.Barrier()
995
+
996
+ if save_unsteady: ## write 4D unsteady diffusion
997
+
998
+ ## technically this is an estimate
999
+ data_gb = self.n_ranks * np.prod(unsteady_diffusion.shape) * unsteady_diffusion.dtype.itemsize / 1024**3
1000
+
1001
+ #with rgd(fn_h5_out_unsteady, 'a', driver='mpio', comm=self.comm) as f1:
1002
+ with rgd_meta(fn_h5_out_unsteady, 'a', driver='mpio', comm=self.comm) as f1:
1003
+ dset = f1['data/diffusion']
1004
+ self.comm.Barrier()
1005
+ t_start = timeit.default_timer()
1006
+ with dset.collective:
1007
+ dset[ct1:ct2,:,:,:] = unsteady_diffusion.T
1008
+ self.comm.Barrier()
1009
+ t_delta = timeit.default_timer() - t_start
1010
+ if verbose:
1011
+ tqdm.write(even_print('write: diffusion', '%0.2f [GB] %0.2f [s] %0.3f [GB/s]'%(data_gb,t_delta,(data_gb/t_delta)), s=True))
1012
+
1013
+ ## combine sums across ranks for this [t] chunk
1014
+ unsteady_diffusion_sum_i = np.sum(unsteady_diffusion, axis=3, keepdims=True, dtype=np.float64)
1015
+ unsteady_diffusion_sum_buf = np.zeros((nx,ny,nz,1), dtype=np.float64, order='C')
1016
+ self.comm.Reduce(
1017
+ [unsteady_diffusion_sum_i, MPI.DOUBLE],
1018
+ [unsteady_diffusion_sum_buf, MPI.DOUBLE],
1019
+ op=MPI.SUM,
1020
+ root=0,
1021
+ )
1022
+
1023
+ ## add aggregated sum (across ranks) to the total accumulator
1024
+ unsteady_diffusion_sum += unsteady_diffusion_sum_buf
1025
+
1026
+ ## release mem
1027
+ omega_ij = None; del omega_ij
1028
+ A = None; del A
1029
+ A_ddx = None; del A_ddx
1030
+ A_ddy = None; del A_ddy
1031
+ A_ddz = None; del A_ddz
1032
+ unsteady_diffusion = None; del unsteady_diffusion
1033
+
1034
+ self.comm.Barrier()
1035
+ mem_free_gb = psutil.virtual_memory().free/1024**3
1036
+ if verbose:
1037
+ tqdm.write(even_print('mem free', f'{mem_free_gb:0.1f} [GB]', s=True))
1038
+
1039
+ # === pressure diffusion
1040
+ if True:
1041
+
1042
+ if verbose: tqdm.write(72*'-')
1043
+ t_start = timeit.default_timer()
1044
+
1045
+ A = np.einsum('xyzti,xyzt->xyzti', uII_i, pI)
1046
+
1047
+ #A_ddx = np.gradient(A[:,:,:,:,0], x, axis=0, edge_order=2)
1048
+ #A_ddy = np.gradient(A[:,:,:,:,1], y, axis=1, edge_order=2)
1049
+ #A_ddz = np.gradient(A[:,:,:,:,2], z, axis=2, edge_order=2)
1050
+
1051
+ A_ddx = gradient(A[:,:,:,:,0], x, axis=0, acc=acc, edge_stencil=edge_stencil)
1052
+ A_ddy = gradient(A[:,:,:,:,1], y, axis=1, acc=acc, edge_stencil=edge_stencil)
1053
+ A_ddz = gradient(A[:,:,:,:,2], z, axis=2, acc=acc, edge_stencil=edge_stencil)
1054
+
1055
+ unsteady_p_diffusion = A_ddx + A_ddy + A_ddz
1056
+
1057
+ t_delta = timeit.default_timer() - t_start
1058
+ if verbose:
1059
+ tqdm.write(even_print('calc p_diffusion', format_time_string(t_delta),s=True))
1060
+
1061
+ self.comm.Barrier()
1062
+
1063
+ ## combine sums across ranks for this [t] chunk
1064
+ unsteady_p_diffusion_sum_i = np.sum(unsteady_p_diffusion, axis=3, keepdims=True, dtype=np.float64)
1065
+ unsteady_p_diffusion_sum_buf = np.zeros((nx,ny,nz,1), dtype=np.float64, order='C')
1066
+ self.comm.Reduce(
1067
+ [unsteady_p_diffusion_sum_i, MPI.DOUBLE],
1068
+ [unsteady_p_diffusion_sum_buf, MPI.DOUBLE],
1069
+ op=MPI.SUM,
1070
+ root=0,
1071
+ )
1072
+
1073
+ ## add aggregated sum (across ranks) to the total accumulator
1074
+ unsteady_p_diffusion_sum += unsteady_p_diffusion_sum_buf
1075
+
1076
+ # === release mem
1077
+ A = None; del A
1078
+ A_ddx = None; del A_ddx
1079
+ A_ddy = None; del A_ddy
1080
+ A_ddz = None; del A_ddz
1081
+ unsteady_p_diffusion = None; del unsteady_p_diffusion
1082
+
1083
+ self.comm.Barrier()
1084
+ mem_free_gb = psutil.virtual_memory().free/1024**3
1085
+ if verbose:
1086
+ tqdm.write(even_print('mem free', f'{mem_free_gb:0.1f} [GB]', s=True))
1087
+
1088
+ # === pressure dilatation
1089
+ if True:
1090
+
1091
+ if verbose: tqdm.write(72*'-')
1092
+ t_start = timeit.default_timer()
1093
+
1094
+ # A = np.einsum('xyzt,xyzti->xyzti', pI, uII_i)
1095
+ # A_ddx = np.gradient(A[:,:,:,:,0], x, axis=0, edge_order=2)
1096
+ # A_ddy = np.gradient(A[:,:,:,:,1], y, axis=1, edge_order=2)
1097
+ # A_ddz = np.gradient(A[:,:,:,:,2], z, axis=2, edge_order=2)
1098
+ # unsteady_p_dilatation = A_ddx + A_ddy + A_ddz
1099
+
1100
+ unsteady_p_dilatation = pI * ( duIIdx + dvIIdy + dwIIdz )
1101
+
1102
+ t_delta = timeit.default_timer() - t_start
1103
+ if verbose:
1104
+ tqdm.write(even_print('calc p_dilatation', format_time_string(t_delta),s=True))
1105
+
1106
+ self.comm.Barrier()
1107
+
1108
+ ## combine sums across ranks for this [t] chunk
1109
+ unsteady_p_dilatation_sum_i = np.sum(unsteady_p_dilatation, axis=3, keepdims=True, dtype=np.float64)
1110
+ unsteady_p_dilatation_sum_buf = np.zeros((nx,ny,nz,1), dtype=np.float64, order='C')
1111
+ self.comm.Reduce(
1112
+ [unsteady_p_dilatation_sum_i, MPI.DOUBLE],
1113
+ [unsteady_p_dilatation_sum_buf, MPI.DOUBLE],
1114
+ op=MPI.SUM,
1115
+ root=0,
1116
+ )
1117
+
1118
+ ## add aggregated sum (across ranks) to the total accumulator
1119
+ unsteady_p_dilatation_sum += unsteady_p_dilatation_sum_buf
1120
+
1121
+ ## release mem
1122
+ unsteady_p_dilatation = None
1123
+ del unsteady_p_dilatation
1124
+
1125
+ self.comm.Barrier()
1126
+ mem_free_gb = psutil.virtual_memory().free/1024**3
1127
+ if verbose:
1128
+ tqdm.write(even_print('mem free', f'{mem_free_gb:0.1f} [GB]', s=True))
1129
+
1130
+ if verbose:
1131
+ progress_bar.update()
1132
+ tqdm.write(72*'-')
1133
+
1134
+ #break ## debug
1135
+
1136
+ if verbose: progress_bar.close()
1137
+ if verbose: print(72*'-')
1138
+
1139
+ ## CLOSE the files that were opened
1140
+ hf_mean.close()
1141
+
1142
+ # ==============================================================
1143
+ # multiply accumulators by (1/n) to get [t] avg
1144
+ # ==============================================================
1145
+
1146
+ if (self.rank==0):
1147
+ production = np.copy( ((1/nt) * unsteady_production_sum ) )
1148
+ dissipation = np.copy( ((1/nt) * unsteady_dissipation_sum ) )
1149
+ transport = np.copy( ((1/nt) * unsteady_transport_sum ) )
1150
+ diffusion = np.copy( ((1/nt) * unsteady_diffusion_sum ) )
1151
+ p_diffusion = np.copy( ((1/nt) * unsteady_p_diffusion_sum ) )
1152
+ p_dilatation = np.copy( ((1/nt) * unsteady_p_dilatation_sum ) )
1153
+ self.comm.Barrier()
1154
+
1155
+ # === write to HDF5
1156
+
1157
+ if self.rank==0:
1158
+ #with rgd(fn_h5_out, 'a') as f1:
1159
+ with rgd_meta(fn_h5_out, 'a') as f1:
1160
+ f1['data/production'][:,:,:,:] = production.T
1161
+ f1['data/dissipation'][:,:,:,:] = dissipation.T
1162
+ f1['data/transport'][:,:,:,:] = transport.T
1163
+ f1['data/diffusion'][:,:,:,:] = diffusion.T
1164
+ f1['data/p_diffusion'][:,:,:,:] = p_diffusion.T
1165
+ f1['data/p_dilatation'][:,:,:,:] = p_dilatation.T
1166
+ self.comm.Barrier()
1167
+
1168
+ ## report file contents
1169
+ if (self.rank==0):
1170
+ with h5py.File(fn_h5_out,'r') as hfr:
1171
+ h5_print_contents(hfr)
1172
+ self.comm.Barrier()
1173
+
1174
+ if verbose: print(72*'-')
1175
+ if verbose: print('total time : rgd.calc_turb_budget() : %s'%format_time_string((timeit.default_timer() - t_start_func)))
1176
+ if verbose: print(72*'-')
1177
+
1178
+ ## make .xdmf
1179
+ if self.rank==0:
1180
+ #with rgd(fn_h5_out, 'r') as f1:
1181
+ with rgd_meta(fn_h5_out, 'r') as f1:
1182
+ f1.make_xdmf()
1183
+ self.comm.Barrier()
1184
+
1185
+ ## make .xdmf
1186
+ if save_unsteady:
1187
+ if self.rank==0:
1188
+ #with rgd(fn_h5_out_unsteady, 'r') as f1:
1189
+ with rgd_meta(fn_h5_out_unsteady, 'r') as f1:
1190
+ f1.make_xdmf()
1191
+ self.comm.Barrier()
1192
+
1193
+ return