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,638 @@
1
+ import os
2
+ import sys
3
+ import timeit
4
+ from pathlib import Path, PurePosixPath
5
+
6
+ import h5py
7
+ import numpy as np
8
+ import scipy as sp
9
+ from scipy.signal import csd
10
+ from tqdm import tqdm
11
+
12
+ from .h5 import h5_print_contents
13
+ from .utils import even_print, format_time_string
14
+
15
+ # ======================================================================
16
+
17
+ def _calc_turb_cospectrum_wall(self, **kwargs):
18
+ '''
19
+ Calculate FFT cospectrum in [z,t] at every [x]
20
+ - designed for analyzing unsteady, pre-computed wall quantities ([y] plane)
21
+ '''
22
+
23
+ if (self.rank==0):
24
+ verbose = True
25
+ else:
26
+ verbose = False
27
+
28
+ if verbose: print('\n'+'spd.calc_turb_cospectrum_wall()'+'\n'+72*'-')
29
+ t_start_func = timeit.default_timer()
30
+
31
+ if not self.usingmpi:
32
+ raise NotImplementedError('function is not implemented for non-MPI usage')
33
+
34
+ h5py_is_mpi_build = h5py.h5.get_config().mpi
35
+ if not h5py_is_mpi_build:
36
+ if verbose: print('h5py was not compiled for parallel usage! exiting.')
37
+ sys.exit(1)
38
+
39
+ ri = kwargs.get('ri',1)
40
+ rj = kwargs.get('rj',1)
41
+ rt = kwargs.get('rt',1)
42
+
43
+ fn_h5_out = kwargs.get('fn_h5_out',None) ## filename for output HDF5 (.h5) file
44
+ overlap_fac_nom = kwargs.get('overlap_fac_nom',0.50) ## nominal windows overlap factor
45
+ n_win = kwargs.get('n_win',8) ## number of segment windows for [t] PSD calc
46
+
47
+ ## check data distribution (only distribute data in [i]/[x])
48
+ if (rj!=1):
49
+ raise AssertionError('rj!=1')
50
+ if (rt!=1):
51
+ raise AssertionError('rt!=1')
52
+
53
+ if not isinstance(ri,int) or (ri<1):
54
+ raise ValueError('ri should be a positive non-zero int')
55
+
56
+ if (ri*rj*rt != self.n_ranks):
57
+ raise AssertionError('ri*rj*rt != self.n_ranks')
58
+ if (ri>self.ni):
59
+ raise AssertionError('ri>self.ni')
60
+ if (self.ni%ri!=0):
61
+ raise AssertionError('ni currently needs to be divisible by the n ranks')
62
+
63
+ ## distribute data over [i]/[x]
64
+ ril_ = np.array_split(np.arange(self.ni,dtype=np.int64),min(ri,self.ni))
65
+ ril = [[b[0],b[-1]+1] for b in ril_ ]
66
+ ri1,ri2 = ril[self.rank]
67
+ nir = ri2 - ri1
68
+
69
+ ## output filename : HDF5 (.h5)
70
+ if (fn_h5_out is None): ## automatically determine name
71
+ fname_path = os.path.dirname(self.fname)
72
+ fname_base = os.path.basename(self.fname)
73
+ fname_root, fname_ext = os.path.splitext(fname_base)
74
+ #fname_root = re.findall(r'io\S+_mpi_[0-9]+', fname_root)[0]
75
+ fn_h5_out_base = fname_root+'_fft.h5'
76
+ fn_h5_out = str(PurePosixPath(fname_path, fn_h5_out_base))
77
+ if (Path(fn_h5_out).suffix != '.h5'):
78
+ raise ValueError(f"fn_h5_out='{str(fn_h5_out)}' must end in .h5")
79
+ if os.path.isfile(fn_h5_out):
80
+ if (fn_h5_out == self.fname):
81
+ raise ValueError(f"fn_h5_out='{str(fn_h5_out)}' cannot be same as input filename.")
82
+
83
+ if verbose: even_print( 'fn_h5' , self.fname )
84
+ if verbose: even_print( 'fn_h5_out' , fn_h5_out )
85
+ if verbose: print(72*'-')
86
+ self.comm.Barrier()
87
+
88
+ # ===
89
+
90
+ ## the data dictionary to be written to .h5 later
91
+ data = {}
92
+
93
+ ## infile
94
+ fsize = os.path.getsize(self.fname)/1024**3
95
+ if verbose: even_print(os.path.basename(self.fname),'%0.1f [GB]'%fsize)
96
+ if verbose: even_print('ni',f'{self.ni:d}')
97
+ if verbose: even_print('nj',f'{self.nj:d}')
98
+ if verbose: even_print('nt',f'{self.nt:d}')
99
+ if verbose: even_print('n_pts',f'{self.n_pts/1e6:0.1f} [M]')
100
+ if verbose: even_print('n_ranks',f'{self.n_ranks:d}')
101
+ if verbose: print(72*'-')
102
+
103
+ ## 0D freestream scalars
104
+ #lchar = self.lchar ; data['lchar'] = self.lchar
105
+ U_inf = self.U_inf ; data['U_inf'] = self.U_inf
106
+ rho_inf = self.rho_inf ; data['rho_inf'] = self.rho_inf
107
+ T_inf = self.T_inf ; data['T_inf'] = self.T_inf
108
+ #mu_inf = self.mu_inf ; data['mu_inf'] = self.mu_inf
109
+ data['p_inf'] = self.p_inf
110
+ #data['M_inf'] = self.M_inf
111
+ data['Ma'] = self.Ma
112
+ data['Pr'] = self.Pr
113
+
114
+ ## read in 1D coordinate arrays & re-dimensionalize
115
+ x = np.copy( self['dims/x'][()] * self.lchar )
116
+ z = np.copy( self['dims/z'][()] * self.lchar )
117
+ t = np.copy( self['dims/t'][()] * self.tchar )
118
+ data['x'] = x
119
+ data['z'] = z
120
+ data['t'] = t
121
+
122
+ nx = self.ni
123
+ ni = self.ni
124
+ nz = self.nj
125
+ nj = self.nj
126
+ data['ni'] = ni
127
+ data['nx'] = nx
128
+ data['nj'] = nj
129
+ data['nz'] = nz
130
+
131
+ nt = self.nt
132
+ data['nt'] = nt
133
+
134
+ ## assert constant Δz
135
+ dz0 = np.diff(z)[0]
136
+ if not np.all(np.isclose(np.diff(z), dz0, rtol=1e-6)):
137
+ raise NotImplementedError('Δz not constant')
138
+
139
+ ## get Δt, dimensional [s]
140
+ if hasattr(self,'dt'):
141
+ dt = self.dt * self.tchar
142
+ np.testing.assert_allclose(dt, t[1]-t[0], rtol=1e-12, atol=1e-12)
143
+ else:
144
+ dt = t[1] - t[0] ## already dimensional
145
+
146
+ if hasattr(self,'duration'):
147
+ t_meas = self.duration * self.tchar
148
+ np.testing.assert_allclose(t_meas, t.max()-t.min(), rtol=1e-12, atol=1e-12)
149
+ else:
150
+ t_meas = t[-1] - t[0] ## already dimensional
151
+ self.duration = t_meas / self.tchar ## non-dimensionalize for attribute
152
+
153
+ zrange = z.max() - z.min()
154
+
155
+ data['t'] = t
156
+ data['dt'] = dt
157
+ data['dz'] = dz0
158
+ data['zrange'] = zrange
159
+
160
+ if verbose: even_print( 'Δt/tchar' , f'{dt/self.tchar:0.8f}' )
161
+ if verbose: even_print( 'Δt' , f'{dt:0.3e} [s]' )
162
+ if verbose: even_print( 'duration/tchar' , f'{self.duration:0.1f}' )
163
+ if verbose: even_print( 'duration' , f'{self.duration*self.tchar:0.3e} [s]' )
164
+ if verbose: print(72*'-')
165
+
166
+ ## report
167
+ if verbose:
168
+ even_print('Δt' , f'{dt :0.5e} [s]' )
169
+ even_print('t_meas' , f'{t_meas:0.5e} [s]' )
170
+ even_print('Δz' , f'{dz0 :0.5e} [m]' )
171
+ even_print('zrange' , f'{zrange:0.5e} [m]' )
172
+ print(72*'-')
173
+
174
+ ## establish [t] windowing & get frequency
175
+ nperseg = nt // n_win
176
+ noverlap = int(round(nperseg*overlap_fac_nom))
177
+ overlap_fac = noverlap / nperseg
178
+ fs = 1./dt ## dimensional [1/s]
179
+
180
+ ## get [freq] vector
181
+ freq,_ = csd(
182
+ np.zeros((nt,),dtype=np.float64),
183
+ np.zeros((nt,),dtype=np.float64),
184
+ fs=fs,
185
+ nperseg=nperseg,
186
+ noverlap=noverlap,
187
+ window='hann',
188
+ detrend='constant',
189
+ scaling='density',
190
+ return_onesided=True,
191
+ )
192
+
193
+ nf = freq.shape[0]
194
+ df = np.diff(freq)[0]
195
+
196
+ data['nperseg'] = nperseg
197
+ data['noverlap'] = noverlap
198
+ data['freq'] = freq
199
+ data['df'] = df
200
+ data['nf'] = nf
201
+
202
+ if verbose:
203
+ even_print('overlap_fac (nominal)' , f'{overlap_fac_nom:0.5f}' )
204
+ even_print('n_win' , f'{n_win:d}' )
205
+ even_print('nperseg' , f'{nperseg:d}' )
206
+ even_print('noverlap' , f'{noverlap:d}' )
207
+ even_print('overlap_fac' , f'{overlap_fac:0.5f}' )
208
+ print(72*'-')
209
+
210
+ if verbose:
211
+ even_print('freq min',f'{freq.min():0.1f} [Hz]')
212
+ even_print('freq max',f'{freq.max():0.1f} [Hz]')
213
+ even_print('df',f'{df:0.1f} [Hz]')
214
+ even_print('nf',f'{nf:d}')
215
+ print(72*'-')
216
+
217
+ ## spatial [z] wavenumber (kz) vector
218
+ kz_full = sp.fft.fftfreq(n=nz, d=dz0) * ( 2 * np.pi )
219
+ kzp = np.where(kz_full>0) ## indices of positive values
220
+ kz = np.copy(kz_full[kzp])
221
+ dkz = kz[1] - kz[0]
222
+ nkz = kz.shape[0]
223
+
224
+ ## wavenumber vector should be size nz//2-1
225
+ if (nkz!=nz//2-1):
226
+ raise ValueError
227
+
228
+ data['kz'] = kz
229
+ data['dkz'] = dkz
230
+ data['nkz'] = nkz
231
+
232
+ if verbose:
233
+ even_print('kz min',f'{kz.min():0.1f} [1/m]')
234
+ even_print('kz max',f'{kz.max():0.1f} [1/m]')
235
+ even_print('dkz',f'{dkz:0.1f} [1/m]')
236
+ even_print('nkz',f'{nkz:d}')
237
+ print(72*'-')
238
+
239
+ ## wavelength λz = (2·π)/kz
240
+ lz = np.copy( 2 * np.pi / kz )
241
+ data['lz'] = lz
242
+
243
+ # ==============================================================
244
+ # prepare buffers, etc.
245
+ # ==============================================================
246
+
247
+ do_density_weighting = False ## deactivated for now... would need implementation
248
+
249
+ ## cospectrum pairs
250
+ ## [ str:var1, str:var2 ]
251
+ fft_combis = [
252
+
253
+ [ 'u_tau' , 'u_tau' ], ## [ uτ , uτ ] --> FFT[ uτ′ , uτ′ ]
254
+ [ 'v_tau' , 'v_tau' ], ## [ vτ , vτ ] --> FFT[ vτ′ , vτ′ ]
255
+ [ 'w_tau' , 'w_tau' ], ## [ wτ , wτ ] --> FFT[ wτ′ , wτ′ ]
256
+
257
+ #[ 'u_tau' , 'v_tau' ], ## [ uτ , vτ ] --> FFT[ uτ′ , vτ′ ]
258
+ #[ 'u_tau' , 'w_tau' ], ## [ uτ , wτ ] --> FFT[ uτ′ , wτ′ ]
259
+ #[ 'v_tau' , 'w_tau' ], ## [ vτ , wτ ] --> FFT[ vτ′ , wτ′ ]
260
+
261
+ [ 'tau_uy' , 'tau_uy' ], ## [ τuy , τuy ] --> FFT[ τuy′ , τuy′ ]
262
+ [ 'tau_vy' , 'tau_vy' ], ## [ τvy , τvy ] --> FFT[ τvy′ , τvy′ ]
263
+ [ 'tau_wy' , 'tau_wy' ], ## [ τwy , τwy ] --> FFT[ τwy′ , τwy′ ]
264
+
265
+ #[ 'tau_uy' , 'tau_vy' ], ## [ τuy , τvy ] --> FFT[ τuy′ , τvy′ ]
266
+ #[ 'tau_uy' , 'tau_wy' ], ## [ τuy , τwy ] --> FFT[ τuy′ , τwy′ ]
267
+ #[ 'tau_vy' , 'tau_wy' ], ## [ τvy , τwy ] --> FFT[ τvy′ , τwy′ ]
268
+
269
+ [ 'p' , 'p' ], ## [p,p] --> FFT[ p′ , p′ ]
270
+ [ 'T' , 'T' ], ## [T,T] --> FFT[ T′ , T′ ]
271
+ [ 'rho' , 'rho' ], ## [ρ,ρ] --> FFT[ ρ′ , ρ′ ]
272
+
273
+ [ 'u_tau' , 'p' ], ## [uτ,p] --> FFT[ uτ′ , p′ ]
274
+ [ 'tau_uy' , 'p' ], ## [τuy,p] --> FFT[ τuy′ , p′ ]
275
+
276
+ ]
277
+
278
+ ## generate FFT cospectrum scalar names
279
+ scalars = []
280
+ for fft_combi in fft_combis:
281
+ #s1,s2,do_density_weighting = fft_combi
282
+ s1,s2 = fft_combi
283
+ if do_density_weighting:
284
+ raise NotImplementedError
285
+ else:
286
+ scalars.append(f"{s1.replace('_','')}I_{s2.replace('_','')}I")
287
+
288
+ ## generate AVG scalar names
289
+ scalars_Re_avg = []
290
+ #scalars_Fv_avg = []
291
+ for fft_combi in fft_combis:
292
+ #s1,s2,do_density_weighting = fft_combi
293
+ s1,s2 = fft_combi
294
+ if do_density_weighting and ('rho' not in scalars_Re_avg):
295
+ scalars_Re_avg.append('rho')
296
+ if do_density_weighting:
297
+ #if (s1 not in scalars_Fv_avg):
298
+ # scalars_Fv_avg.append(s1)
299
+ #if (s2 not in scalars_Fv_avg):
300
+ # scalars_Fv_avg.append(s2)
301
+ raise NotImplementedError
302
+ else:
303
+ if (s1 not in scalars_Re_avg):
304
+ scalars_Re_avg.append(s1)
305
+ if (s2 not in scalars_Re_avg):
306
+ scalars_Re_avg.append(s2)
307
+
308
+ ## numpy formatted arrays: buffers for PSD & other data (rank-local)
309
+ Ekz = np.zeros(shape=(nir,nkz ) , dtype={'names':scalars , 'formats':[ np.dtype(np.complex128) for s in scalars ]})
310
+ Ef = np.zeros(shape=(nir,nf ) , dtype={'names':scalars , 'formats':[ np.dtype(np.complex128) for s in scalars ]})
311
+ covariance = np.zeros(shape=(nir, ) , dtype={'names':scalars , 'formats':[ np.dtype(np.float64) for s in scalars ]})
312
+ avg_Re = np.zeros(shape=(nir, ) , dtype={'names':scalars_Re_avg , 'formats':[ np.dtype(np.float64) for s in scalars_Re_avg ]})
313
+ #avg_Fv = np.zeros(shape=(nir, ) , dtype={'names':scalars_Fv_avg , 'formats':[ np.dtype(np.float64) for s in scalars_Fv_avg ]})
314
+
315
+ if verbose:
316
+ even_print('n turb spectrum scalar combinations' , '%i'%(len(fft_combis),))
317
+ print(72*'-')
318
+
319
+ ## window for [z] -- rectangular because [z] is assumed periodic already
320
+ window_z = np.ones(nz,dtype=np.float64)
321
+ mean_sq_win_z = np.mean(window_z**2)
322
+ if verbose:
323
+ even_print('mean(window_z**2)', '%0.5f'%(mean_sq_win_z,))
324
+
325
+ # ==============================================================
326
+ # main loop
327
+ # ==============================================================
328
+
329
+ if verbose:
330
+ progress_bar = tqdm(
331
+ total=len(fft_combis)*nir,
332
+ ncols=100,
333
+ desc='fft',
334
+ leave=True,
335
+ file=sys.stdout,
336
+ mininterval=0.1,
337
+ smoothing=0.,
338
+ #bar_format="\033[B{l_bar}{bar}| {n}/{total} [{percentage:.1f}%] {elapsed}/{remaining}\033[A\n\b",
339
+ bar_format="{l_bar}{bar}| {n}/{total} [{percentage:.1f}%] {elapsed}/{remaining}",
340
+ ascii="░█",
341
+ colour='#FF6600',
342
+ )
343
+
344
+ for cci,cc in enumerate(fft_combis): ## fft pairs
345
+
346
+ if verbose: tqdm.write(72*'-')
347
+
348
+ scalar_L, scalar_R = cc
349
+
350
+ msg = f'FFT[{scalar_L}′,{scalar_R}′]'
351
+ if verbose:
352
+ tqdm.write(even_print('computing',msg,s=True,))
353
+
354
+ dset_L = self[f'data/{scalar_L}']
355
+ dset_R = self[f'data/{scalar_R}']
356
+ data_gb_1x = self.n_ranks * 1 * self.nj * self.nt * dset_L.dtype.itemsize / 1024**3
357
+
358
+ scalar = scalars[cci]
359
+
360
+ ## assert scalar name
361
+ scalar_ = f"{scalar_L.replace('_','')}I_{scalar_R.replace('_','')}I"
362
+ if (scalar != scalar_):
363
+ raise ValueError
364
+
365
+ ## assert scalar name
366
+ if do_density_weighting:
367
+ #if (f'r{scalar_L}II_r{scalar_R}II' != scalar ):
368
+ # raise ValueError
369
+ raise NotImplementedError
370
+ else:
371
+ if (f"{scalar_L.replace('_','')}I_{scalar_R.replace('_','')}I" != scalar ):
372
+ raise ValueError
373
+
374
+ ## [x] loop (rank-local)
375
+ for ii in range(ri1,ri2):
376
+
377
+ iii = ii - ri1
378
+
379
+ data_L = np.zeros( (nz,nt) , dtype=np.float64 )
380
+ data_R = np.zeros( (nz,nt) , dtype=np.float64 )
381
+
382
+ self.comm.Barrier()
383
+ t_start = timeit.default_timer()
384
+
385
+ ## read data L
386
+ n_scalars_read = 1 ## initialize
387
+ scalar_str = scalar_L ## initialize
388
+ with dset_L.collective:
389
+ data_L[:,:] = np.copy( dset_L[ii,:,:] ).astype(np.float64)
390
+ self.comm.Barrier()
391
+
392
+ ## read data R (if != data L)
393
+ if (scalar_L==scalar_R):
394
+ data_R[:,:] = np.copy( data_L )
395
+ else:
396
+ n_scalars_read += 1
397
+ scalar_str += f',{scalar_R}'
398
+ with dset_R.collective:
399
+ data_R[:,:] = np.copy( dset_R[ii,:,:] ).astype(np.float64)
400
+ self.comm.Barrier()
401
+
402
+ self.comm.Barrier()
403
+ t_delta = timeit.default_timer() - t_start
404
+ data_gb = data_gb_1x * n_scalars_read
405
+
406
+ if verbose:
407
+ tqdm.write(even_print(f'read: {scalar_str}', f'{data_gb:0.3f} [GB] {t_delta:0.3f} [s] {data_gb/t_delta:0.3f} [GB/s]', s=True))
408
+
409
+ ## data_L and data_R shape should be (nz,nt)
410
+ if ( data_L.shape != (nz,nt) ) or ( data_R.shape != (nz,nt) ):
411
+ print(f'rank {self.rank:d}: shape violation')
412
+ self.comm.Abort(1)
413
+
414
+ # === redimensionalize
415
+
416
+ if scalar_L in ['tau_uy','tau_vy','tau_wy',]:
417
+ data_L *= rho_inf * U_inf**2
418
+ elif scalar_L in ['u_tau','v_tau','w_tau',]:
419
+ data_L *= U_inf
420
+ elif scalar_L in ['p',]:
421
+ data_L *= rho_inf * U_inf**2
422
+ elif scalar_L in ['T',]:
423
+ data_L *= T_inf
424
+ elif scalar_L in ['rho',]:
425
+ data_L *= rho_inf
426
+ else:
427
+ raise ValueError
428
+
429
+ if scalar_R in ['tau_uy','tau_vy','tau_wy',]:
430
+ data_R *= rho_inf * U_inf**2
431
+ elif scalar_R in ['u_tau','v_tau','w_tau',]:
432
+ data_R *= U_inf
433
+ elif scalar_R in ['p',]:
434
+ data_R *= rho_inf * U_inf**2
435
+ elif scalar_R in ['T',]:
436
+ data_R *= T_inf
437
+ elif scalar_R in ['rho',]:
438
+ data_R *= rho_inf
439
+ else:
440
+ raise ValueError
441
+
442
+ ## data_L and data_R shape should be (nz,nt)
443
+ if ( data_L.shape != (nz,nt) ) or ( data_R.shape != (nz,nt) ):
444
+ print(f'rank {self.rank:d}: shape violation')
445
+ self.comm.Abort(1)
446
+
447
+ # === compute mean-removed data
448
+
449
+ ## avg(□) or avg(ρ·□)/avg(ρ) in [t]
450
+ if do_density_weighting:
451
+ #rho_avg = np.mean( rho , axis=-1, dtype=np.float64, keepdims=True)
452
+ #data_L_avg = np.mean( rho*data_L , axis=-1, dtype=np.float64, keepdims=True)
453
+ #data_L_avg /= rho_avg
454
+ #data_R_avg = np.mean( rho*data_R , axis=-1, dtype=np.float64, keepdims=True)
455
+ #data_R_avg /= rho_avg
456
+ raise NotImplementedError
457
+ else:
458
+ data_L_avg = np.mean( data_L , axis=-1, dtype=np.float64, keepdims=True) ## (nz,1)
459
+ data_R_avg = np.mean( data_R , axis=-1, dtype=np.float64, keepdims=True) ## (nz,1)
460
+
461
+ ## data_L_avg and data_R_avg shape should be (nz,1)
462
+ if ( data_L_avg.shape != (nz,1) ) or ( data_R_avg.shape != (nz,1) ):
463
+ print(f'rank {self.rank:d}: shape violation')
464
+ self.comm.Abort(1)
465
+
466
+ ## Reynolds prime □′ or Favre prime □″ --> shape (nz,nt)
467
+ data_L -= data_L_avg
468
+ data_R -= data_R_avg
469
+
470
+ ## data_L and data_R shape should be (nz,nt)
471
+ if ( data_L.shape != (nz,nt) ) or ( data_R.shape != (nz,nt) ):
472
+ print(f'rank {self.rank:d}: shape violation')
473
+ self.comm.Abort(1)
474
+
475
+ ## assert stationarity / definition averaging
476
+ ## avg(□′)==0 or avg(ρ·□″)==0
477
+ if do_density_weighting:
478
+ #a_ = np.mean(rho*data_L, axis=-1, dtype=np.float64, keepdims=True)
479
+ #b_ = np.mean(rho*data_R, axis=-1, dtype=np.float64, keepdims=True)
480
+ raise NotImplementedError
481
+ else:
482
+ a_ = np.mean(data_L, axis=-1, dtype=np.float64, keepdims=True) ## average in [t] --> (nz,1)
483
+ b_ = np.mean(data_R, axis=-1, dtype=np.float64, keepdims=True)
484
+ if not np.allclose( a_, np.zeros_like(a_), atol=1e-6 ) or not np.allclose( b_, np.zeros_like(b_), atol=1e-6 ):
485
+ print(f'rank {self.rank:d}: avg(□′)!=0 or avg(ρ·□″)!=0')
486
+ self.comm.Abort(1)
487
+
488
+ ## covariance: <□′·□′> OR <ρ□″·ρ□″> --> note that this is NOT the typical Favre <ρ·□″□″>
489
+ if do_density_weighting:
490
+ #covariance_ = np.mean( rho*data_L * rho*data_R , axis=-1 , dtype=np.float64, keepdims=True)
491
+ raise NotImplementedError
492
+ else:
493
+ covariance_ = np.mean( data_L*data_R , axis=-1 , dtype=np.float64, keepdims=True) ## average in [t] --> (nz,1)
494
+
495
+ if ( covariance_.shape != (nz,1) ):
496
+ print(f'rank {self.rank:d}: shape violation')
497
+ self.comm.Abort(1)
498
+
499
+ ## write this chunk/scalar's covariance to covariance buffer
500
+ ## avg over [z,t] --> np.float64()
501
+ covariance[scalar][iii] = np.mean( covariance_ , axis=(0,1) , dtype=np.float64)
502
+
503
+ ## write (rank-local) 1D [x] average
504
+ if do_density_weighting:
505
+ #avg_Fv[scalar_L][iii] = np.mean( data_L_avg , axis=(0,1) , dtype=np.float64) )
506
+ #avg_Fv[scalar_R][iii] = np.mean( data_R_avg , axis=(0,1) , dtype=np.float64) )
507
+ #avg_Re['rho'][iii] = np.mean( rho_avg , axis=(0,1) , dtype=np.float64) )
508
+ raise ValueError
509
+ else:
510
+ avg_Re[scalar_L][iii] = np.mean( data_L_avg , axis=(0,1) , dtype=np.float64)
511
+ avg_Re[scalar_R][iii] = np.mean( data_R_avg , axis=(0,1) , dtype=np.float64)
512
+
513
+ # ===============================================================================
514
+ # At this point you have 4D [x,y,z,t] □′ data
515
+ # ===============================================================================
516
+
517
+ ## do [z] FFT for every [t]
518
+ Ekz_buf = np.zeros((nt,nkz), dtype=np.complex128)
519
+ for ti in range(nt):
520
+ uL = np.copy( data_L[:,ti] )
521
+ uR = np.copy( data_R[:,ti] )
522
+
523
+ ## One-sided amplitude spectra
524
+ A1 = sp.fft.fft( uL * window_z )[kzp] / nz
525
+ A2 = sp.fft.fft( uR * window_z )[kzp] / nz
526
+
527
+ Ekz_buf[ti,:] = 2. * A1 * np.conj(A2) / ( dkz * mean_sq_win_z )
528
+
529
+ Ekz[scalar][iii,:] = np.mean(Ekz_buf, axis=0) ## mean across [t] --> (nkz,)
530
+
531
+ ## do [t] FFT for every [z]
532
+ Ef_buf = np.zeros((nz,nf), dtype=np.complex128)
533
+ #for zi in range(self.nj):
534
+ for zi in range(nz):
535
+ uL = np.copy( data_L[zi,:] )
536
+ uR = np.copy( data_R[zi,:] )
537
+
538
+ ## One-sided complex cross-spectral density in [f] (using Welch's method)
539
+ _,P = csd(
540
+ uL,uR,
541
+ fs=fs,
542
+ nperseg=nperseg,
543
+ noverlap=noverlap,
544
+ window='hann',
545
+ detrend='constant',
546
+ scaling='density',
547
+ return_onesided=True,
548
+ )
549
+
550
+ Ef_buf[zi,:] = P
551
+
552
+ Ef[scalar][iii,:] = np.mean(Ef_buf, axis=0) ## mean across [z] --> (nf,)
553
+
554
+ self.comm.Barrier() ## [x] loop ('ii' within this rank's range)
555
+ if verbose: progress_bar.update()
556
+
557
+ #break ## DEBUG
558
+
559
+ self.comm.Barrier()
560
+ if verbose:
561
+ progress_bar.close()
562
+ print(72*'-')
563
+
564
+ # ==============================================================
565
+ # write HDF5 (.h5) file
566
+ # ==============================================================
567
+
568
+ ## overwrite outfile!
569
+ ## open on rank 0 and write attributes, dimensions, etc.
570
+ if (self.rank==0):
571
+ with h5py.File(fn_h5_out, 'w') as hfw:
572
+
573
+ ## write floats,ints as top-level attributes
574
+ for key,val in data.items():
575
+ if isinstance(data[key], (int,np.int32,np.int64)):
576
+ hfw.attrs[key] = val
577
+ elif isinstance(data[key], (float,np.float32,np.float64)):
578
+ hfw.attrs[key] = val
579
+ elif isinstance(data[key], np.ndarray):
580
+ pass
581
+ else:
582
+ print(f'key {key} is type {str(type(data[key]))}')
583
+ self.comm.Abort(1)
584
+
585
+ ## write numpy arrays
586
+ hfw.create_dataset( 'dims/x' , data=x ) ## [m]
587
+ hfw.create_dataset( 'dims/z' , data=z ) ## [m]
588
+ hfw.create_dataset( 'dims/t' , data=t ) ## [s]
589
+ hfw.create_dataset( 'dims/freq' , data=freq ) ## [1/s] | [Hz]
590
+ hfw.create_dataset( 'dims/kz' , data=kz ) ## [1/m]
591
+ hfw.create_dataset( 'dims/lz' , data=lz ) ## [m]
592
+
593
+ ## initialize datasets : covariance,Ekz,Ef
594
+ for scalar in scalars:
595
+ hfw.create_dataset( f'covariance/{scalar}' , shape=(nx,) , dtype=np.float64 , chunks=None , data=np.full((nx,),0.,np.float64) )
596
+ hfw.create_dataset( f'Ekz/{scalar}' , shape=(nx,nkz) , dtype=np.complex128 , chunks=(1,nkz) , data=np.full((nx,nkz),0.,np.complex128) )
597
+ hfw.create_dataset( f'Ef/{scalar}' , shape=(nx,nf) , dtype=np.complex128 , chunks=(1,nf) , data=np.full((nx,nf),0.,np.complex128) )
598
+
599
+ ## initialize datasets : 1D [x] mean
600
+ for scalar in avg_Re.dtype.names:
601
+ hfw.create_dataset( f'avg/Re/{scalar}', shape=(nx,), dtype=np.float64, chunks=None, data=np.full((nx,),0.,np.float64) )
602
+
603
+ self.comm.Barrier()
604
+
605
+ ## re-open in parallel for data writes
606
+ with h5py.File(fn_h5_out, 'a', driver='mpio', comm=self.comm) as hfw:
607
+
608
+ ## collectively write covariance,Ekz,Ef
609
+ for scalar in scalars:
610
+ dset = hfw[f'covariance/{scalar}']
611
+ with dset.collective:
612
+ dset[ri1:ri2] = covariance[scalar][:]
613
+ dset = hfw[f'Ekz/{scalar}']
614
+ with dset.collective:
615
+ dset[ri1:ri2,:] = Ekz[scalar][:,:]
616
+ dset = hfw[f'Ef/{scalar}']
617
+ with dset.collective:
618
+ dset[ri1:ri2,:] = Ef[scalar][:,:]
619
+
620
+ ## collectively write 1D [y] avgs
621
+ for scalar in avg_Re.dtype.names:
622
+ dset = hfw[f'avg/Re/{scalar}']
623
+ with dset.collective:
624
+ dset[ri1:ri2] = avg_Re[scalar][:]
625
+
626
+ ## report file contents
627
+ self.comm.Barrier()
628
+ if (self.rank==0):
629
+ even_print( os.path.basename(fn_h5_out) , f'{(os.path.getsize(fn_h5_out)/1024**2):0.1f} [MB]' )
630
+ print(72*'-')
631
+ with h5py.File(fn_h5_out,'r') as hfr:
632
+ h5_print_contents(hfr)
633
+ self.comm.Barrier()
634
+
635
+ if verbose: print(72*'-')
636
+ if verbose: print('total time : spd.calc_turb_cospectrum_wall() : %s'%format_time_string((timeit.default_timer() - t_start_func)))
637
+ if verbose: print(72*'-')
638
+ return