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/__init__.py +52 -0
- turbx/bl.py +620 -0
- turbx/blasius.py +64 -0
- turbx/cli.py +19 -0
- turbx/composite_profile.py +243 -0
- turbx/confidence_interval.py +64 -0
- turbx/eas3.py +420 -0
- turbx/eas4.py +567 -0
- turbx/fig_ax_constructor.py +52 -0
- turbx/freestream_parameters.py +268 -0
- turbx/gradient.py +391 -0
- turbx/grid_metric.py +272 -0
- turbx/h5.py +236 -0
- turbx/mvp.py +385 -0
- turbx/rgd.py +2693 -0
- turbx/rgd_mean.py +523 -0
- turbx/rgd_testing.py +354 -0
- turbx/rgd_xpln_ccor.py +701 -0
- turbx/rgd_xpln_coh.py +992 -0
- turbx/rgd_xpln_mean_dim.py +336 -0
- turbx/rgd_xpln_spectrum.py +940 -0
- turbx/rgd_xpln_stats.py +738 -0
- turbx/rgd_xpln_turb_budget.py +1193 -0
- turbx/set_mpl_env.py +85 -0
- turbx/signal.py +277 -0
- turbx/spd.py +1206 -0
- turbx/spd_wall_ccor.py +629 -0
- turbx/spd_wall_ci.py +406 -0
- turbx/spd_wall_import.py +676 -0
- turbx/spd_wall_spectrum.py +638 -0
- turbx/spd_wall_stats.py +618 -0
- turbx/utils.py +84 -0
- turbx/ztmd.py +2224 -0
- turbx/ztmd_analysis.py +2337 -0
- turbx/ztmd_loader.py +56 -0
- turbx-1.0.2.dist-info/LICENSE +21 -0
- turbx-1.0.2.dist-info/METADATA +120 -0
- turbx-1.0.2.dist-info/RECORD +41 -0
- turbx-1.0.2.dist-info/WHEEL +5 -0
- turbx-1.0.2.dist-info/entry_points.txt +2 -0
- turbx-1.0.2.dist-info/top_level.txt +1 -0
turbx/grid_metric.py
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import timeit
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from tqdm import tqdm
|
|
5
|
+
|
|
6
|
+
from .gradient import gradient
|
|
7
|
+
from .utils import even_print
|
|
8
|
+
|
|
9
|
+
# ======================================================================
|
|
10
|
+
|
|
11
|
+
def get_metric_tensor_2d(x2d, y2d, acc=2, edge_stencil='full', **kwargs):
|
|
12
|
+
'''
|
|
13
|
+
Compute the grid metric tensor (inverse of grid Jacobian) for a 2D grid
|
|
14
|
+
-----
|
|
15
|
+
Computational Fluid Mechanics and Heat Transfer (2012) Pletcher, Tannehill, Anderson
|
|
16
|
+
p.266-270, 335-337, 652
|
|
17
|
+
'''
|
|
18
|
+
|
|
19
|
+
verbose = kwargs.get('verbose',False)
|
|
20
|
+
no_warn = kwargs.get('no_warn',False)
|
|
21
|
+
|
|
22
|
+
if not isinstance(x2d, np.ndarray):
|
|
23
|
+
raise ValueError('x2d should be of type np.ndarray')
|
|
24
|
+
if not isinstance(y2d, np.ndarray):
|
|
25
|
+
raise ValueError('y2d should be of type np.ndarray')
|
|
26
|
+
|
|
27
|
+
if (x2d.ndim!=2):
|
|
28
|
+
raise ValueError('x2d should have ndim=2 (xy)')
|
|
29
|
+
if (y2d.ndim!=2):
|
|
30
|
+
raise ValueError('y2d should have ndim=2 (xy)')
|
|
31
|
+
|
|
32
|
+
if not (x2d.shape==y2d.shape):
|
|
33
|
+
raise ValueError('x2d.shape!=y2d.shape')
|
|
34
|
+
|
|
35
|
+
nx,ny = x2d.shape
|
|
36
|
+
|
|
37
|
+
## the 'computational' grid (unit Cartesian)
|
|
38
|
+
## --> [x_comp,y_comp]= [ξ,η] = [q1,q2]
|
|
39
|
+
#x_comp = np.arange(nx, dtype=np.float64)
|
|
40
|
+
#y_comp = np.arange(ny, dtype=np.float64)
|
|
41
|
+
x_comp = 1.
|
|
42
|
+
y_comp = 1.
|
|
43
|
+
|
|
44
|
+
# === get Jacobian :: ∂(x,y)/∂(q1,q2)
|
|
45
|
+
|
|
46
|
+
t_start = timeit.default_timer()
|
|
47
|
+
|
|
48
|
+
dxdx = gradient(x2d, x_comp, axis=0, d=1, acc=acc, edge_stencil=edge_stencil, no_warn=no_warn)
|
|
49
|
+
dydx = gradient(y2d, x_comp, axis=0, d=1, acc=acc, edge_stencil=edge_stencil, no_warn=no_warn)
|
|
50
|
+
dxdy = gradient(x2d, y_comp, axis=1, d=1, acc=acc, edge_stencil=edge_stencil, no_warn=no_warn)
|
|
51
|
+
dydy = gradient(y2d, y_comp, axis=1, d=1, acc=acc, edge_stencil=edge_stencil, no_warn=no_warn)
|
|
52
|
+
|
|
53
|
+
J = np.stack((np.stack((dxdx, dydx), axis=2),
|
|
54
|
+
np.stack((dxdy, dydy), axis=2)), axis=3)
|
|
55
|
+
|
|
56
|
+
t_delta = timeit.default_timer() - t_start
|
|
57
|
+
if verbose: tqdm.write( even_print('get J','%0.3f [s]'%(t_delta,), s=True) )
|
|
58
|
+
|
|
59
|
+
# === get metric tensor M = J^-1 = ∂(q1,q2)/∂(x,y) = ∂(ξ,η)/∂(x,y)
|
|
60
|
+
|
|
61
|
+
if False: ## method 1
|
|
62
|
+
|
|
63
|
+
t_start = timeit.default_timer()
|
|
64
|
+
|
|
65
|
+
M = np.linalg.inv(J)
|
|
66
|
+
|
|
67
|
+
# M_bak = np.copy(M)
|
|
68
|
+
# M = np.zeros((nx,ny,2,2),dtype=np.float64)
|
|
69
|
+
# for i in range(nx):
|
|
70
|
+
# for j in range(ny):
|
|
71
|
+
# M[i,j,:,:] = sp.linalg.inv( J[i,j,:,:] )
|
|
72
|
+
# np.testing.assert_allclose(M_bak, M, atol=1e-12, rtol=1e-12)
|
|
73
|
+
# print('check passed')
|
|
74
|
+
|
|
75
|
+
t_delta = timeit.default_timer() - t_start
|
|
76
|
+
if verbose: tqdm.write( even_print('get M','%0.3f [s]'%(t_delta,), s=True) )
|
|
77
|
+
|
|
78
|
+
if True: ## method 2
|
|
79
|
+
|
|
80
|
+
if ('M' in locals()):
|
|
81
|
+
M_bak = np.copy(M)
|
|
82
|
+
M = None; del M
|
|
83
|
+
|
|
84
|
+
t_start = timeit.default_timer()
|
|
85
|
+
|
|
86
|
+
## Jacobian determinant
|
|
87
|
+
Jac_det = dxdx*dydy - dydx*dxdy
|
|
88
|
+
|
|
89
|
+
# Jac_det_bak = np.copy(Jac_det)
|
|
90
|
+
# Jac_det = None; del Jac_det
|
|
91
|
+
# Jac_det = np.linalg.det(J)
|
|
92
|
+
# np.testing.assert_allclose(Jac_det, Jac_det_bak, atol=1e-14, rtol=1e-14)
|
|
93
|
+
# print('check passed')
|
|
94
|
+
|
|
95
|
+
M = np.zeros((nx,ny,2,2), dtype=np.float64)
|
|
96
|
+
M[:,:,0,0] = +dydy / Jac_det ## ξ_x
|
|
97
|
+
M[:,:,0,1] = -dxdy / Jac_det ## ξ_y
|
|
98
|
+
M[:,:,1,0] = -dydx / Jac_det ## η_x
|
|
99
|
+
M[:,:,1,1] = +dxdx / Jac_det ## η_y
|
|
100
|
+
|
|
101
|
+
t_delta = timeit.default_timer() - t_start
|
|
102
|
+
if verbose: tqdm.write( even_print('get M','%0.3f [s]'%(t_delta,), s=True) )
|
|
103
|
+
|
|
104
|
+
if ('M_bak' in locals()):
|
|
105
|
+
np.testing.assert_allclose(M[:,:,0,0], M_bak[:,:,0,0], atol=1e-14, rtol=1e-14)
|
|
106
|
+
print('check passed: ξ_x')
|
|
107
|
+
np.testing.assert_allclose(M[:,:,0,1], M_bak[:,:,0,1], atol=1e-14, rtol=1e-14)
|
|
108
|
+
print('check passed: ξ_y')
|
|
109
|
+
np.testing.assert_allclose(M[:,:,1,0], M_bak[:,:,1,0], atol=1e-14, rtol=1e-14)
|
|
110
|
+
print('check passed: η_x')
|
|
111
|
+
np.testing.assert_allclose(M[:,:,1,1], M_bak[:,:,1,1], atol=1e-14, rtol=1e-14)
|
|
112
|
+
print('check passed: η_y')
|
|
113
|
+
np.testing.assert_allclose(M, M_bak, atol=1e-14, rtol=1e-14)
|
|
114
|
+
print('check passed: M')
|
|
115
|
+
|
|
116
|
+
return M
|
|
117
|
+
|
|
118
|
+
def get_metric_tensor_3d(x3d, y3d, z3d, acc=2, edge_stencil='full', **kwargs):
|
|
119
|
+
'''
|
|
120
|
+
Compute the grid metric tensor (inverse of grid Jacobian) for a 3D grid
|
|
121
|
+
-----
|
|
122
|
+
Computational Fluid Mechanics and Heat Transfer (2012) Pletcher, Tannehill, Anderson
|
|
123
|
+
p.266-270, 335-337, 652
|
|
124
|
+
'''
|
|
125
|
+
|
|
126
|
+
verbose = kwargs.get('verbose',False)
|
|
127
|
+
no_warn = kwargs.get('no_warn',False)
|
|
128
|
+
|
|
129
|
+
if not isinstance(x3d, np.ndarray):
|
|
130
|
+
raise ValueError('x3d should be of type np.ndarray')
|
|
131
|
+
if not isinstance(y3d, np.ndarray):
|
|
132
|
+
raise ValueError('y3d should be of type np.ndarray')
|
|
133
|
+
if not isinstance(z3d, np.ndarray):
|
|
134
|
+
raise ValueError('z3d should be of type np.ndarray')
|
|
135
|
+
|
|
136
|
+
if (x3d.ndim!=3):
|
|
137
|
+
raise ValueError('x3d should have ndim=3 (xyz)')
|
|
138
|
+
if (y3d.ndim!=3):
|
|
139
|
+
raise ValueError('y3d should have ndim=3 (xyz)')
|
|
140
|
+
if (z3d.ndim!=3):
|
|
141
|
+
raise ValueError('z3d should have ndim=3 (xyz)')
|
|
142
|
+
|
|
143
|
+
if not (x3d.shape==y3d.shape):
|
|
144
|
+
raise ValueError('x3d.shape!=y3d.shape')
|
|
145
|
+
if not (y3d.shape==z3d.shape):
|
|
146
|
+
raise ValueError('y3d.shape!=z3d.shape')
|
|
147
|
+
|
|
148
|
+
nx,ny,nz = x3d.shape
|
|
149
|
+
|
|
150
|
+
## the 'computational' grid (unit Cartesian)
|
|
151
|
+
## --> [x_comp,y_comp,z_comp ]= [ξ,η,ζ] = [q1,q2,q3]
|
|
152
|
+
#x_comp = np.arange(nx, dtype=np.float64)
|
|
153
|
+
#y_comp = np.arange(ny, dtype=np.float64)
|
|
154
|
+
#z_comp = np.arange(nz, dtype=np.float64)
|
|
155
|
+
x_comp = 1.
|
|
156
|
+
y_comp = 1.
|
|
157
|
+
z_comp = 1.
|
|
158
|
+
|
|
159
|
+
# === get Jacobian :: ∂(x,y,z)/∂(q1,q2,q3)
|
|
160
|
+
|
|
161
|
+
t_start = timeit.default_timer()
|
|
162
|
+
|
|
163
|
+
dxdx = gradient(x3d, x_comp, axis=0, d=1, acc=acc, edge_stencil=edge_stencil, no_warn=no_warn)
|
|
164
|
+
dydx = gradient(y3d, x_comp, axis=0, d=1, acc=acc, edge_stencil=edge_stencil, no_warn=no_warn)
|
|
165
|
+
dzdx = gradient(z3d, x_comp, axis=0, d=1, acc=acc, edge_stencil=edge_stencil, no_warn=no_warn)
|
|
166
|
+
|
|
167
|
+
dxdy = gradient(x3d, y_comp, axis=1, d=1, acc=acc, edge_stencil=edge_stencil, no_warn=no_warn)
|
|
168
|
+
dydy = gradient(y3d, y_comp, axis=1, d=1, acc=acc, edge_stencil=edge_stencil, no_warn=no_warn)
|
|
169
|
+
dzdy = gradient(z3d, y_comp, axis=1, d=1, acc=acc, edge_stencil=edge_stencil, no_warn=no_warn)
|
|
170
|
+
|
|
171
|
+
dxdz = gradient(x3d, z_comp, axis=2, d=1, acc=acc, edge_stencil=edge_stencil, no_warn=no_warn)
|
|
172
|
+
dydz = gradient(y3d, z_comp, axis=2, d=1, acc=acc, edge_stencil=edge_stencil, no_warn=no_warn)
|
|
173
|
+
dzdz = gradient(z3d, z_comp, axis=2, d=1, acc=acc, edge_stencil=edge_stencil, no_warn=no_warn)
|
|
174
|
+
|
|
175
|
+
J = np.stack((np.stack((dxdx, dydx, dzdx), axis=3),
|
|
176
|
+
np.stack((dxdy, dydy, dzdy), axis=3),
|
|
177
|
+
np.stack((dxdz, dydz, dzdz), axis=3)), axis=4)
|
|
178
|
+
|
|
179
|
+
t_delta = timeit.default_timer() - t_start
|
|
180
|
+
if verbose: tqdm.write( even_print('get J','%0.3f [s]'%(t_delta,), s=True) )
|
|
181
|
+
|
|
182
|
+
# === get metric tensor M = J^-1 = ∂(q1,q2,q3)/∂(x,y,z) = ∂(ξ,η,ζ)/∂(x,y,z)
|
|
183
|
+
|
|
184
|
+
if False: ## method 1
|
|
185
|
+
|
|
186
|
+
t_start = timeit.default_timer()
|
|
187
|
+
|
|
188
|
+
M = np.linalg.inv(J)
|
|
189
|
+
|
|
190
|
+
# M_bak = np.copy(M)
|
|
191
|
+
# for i in range(nx):
|
|
192
|
+
# for j in range(ny):
|
|
193
|
+
# for k in range(nz):
|
|
194
|
+
# M[i,j,k,:,:] = sp.linalg.inv( J[i,j,k,:,:] )
|
|
195
|
+
# np.testing.assert_allclose(M_bak, M, atol=1e-12, rtol=1e-12)
|
|
196
|
+
# print('check passed')
|
|
197
|
+
|
|
198
|
+
t_delta = timeit.default_timer() - t_start
|
|
199
|
+
if verbose: tqdm.write( even_print('get M','%0.3f [s]'%(t_delta,), s=True) )
|
|
200
|
+
|
|
201
|
+
if True: ## method 2
|
|
202
|
+
|
|
203
|
+
if ('M' in locals()):
|
|
204
|
+
M_bak = np.copy(M)
|
|
205
|
+
M = None; del M
|
|
206
|
+
|
|
207
|
+
t_start = timeit.default_timer()
|
|
208
|
+
|
|
209
|
+
a = J[:,:,:,0,0]
|
|
210
|
+
b = J[:,:,:,0,1]
|
|
211
|
+
c = J[:,:,:,0,2]
|
|
212
|
+
d = J[:,:,:,1,0]
|
|
213
|
+
e = J[:,:,:,1,1]
|
|
214
|
+
f = J[:,:,:,1,2]
|
|
215
|
+
g = J[:,:,:,2,0]
|
|
216
|
+
h = J[:,:,:,2,1]
|
|
217
|
+
i = J[:,:,:,2,2]
|
|
218
|
+
|
|
219
|
+
# a = J[:,:,:,0,0]
|
|
220
|
+
# b = J[:,:,:,1,0]
|
|
221
|
+
# c = J[:,:,:,2,0]
|
|
222
|
+
# d = J[:,:,:,0,1]
|
|
223
|
+
# e = J[:,:,:,1,1]
|
|
224
|
+
# f = J[:,:,:,2,1]
|
|
225
|
+
# g = J[:,:,:,0,2]
|
|
226
|
+
# h = J[:,:,:,1,2]
|
|
227
|
+
# i = J[:,:,:,2,2]
|
|
228
|
+
|
|
229
|
+
Jac_det = ( + a*e*i
|
|
230
|
+
+ b*f*g
|
|
231
|
+
+ c*d*h
|
|
232
|
+
- c*e*g
|
|
233
|
+
- b*d*i
|
|
234
|
+
- a*f*h )
|
|
235
|
+
|
|
236
|
+
M = np.zeros((nx,ny,nz,3,3), dtype=np.float64)
|
|
237
|
+
M[:,:,:,0,0] = +( dydy * dzdz - dydz * dzdy ) / Jac_det ## ξ_x
|
|
238
|
+
M[:,:,:,0,1] = -( dxdy * dzdz - dxdz * dzdy ) / Jac_det ## ξ_y
|
|
239
|
+
M[:,:,:,0,2] = +( dxdy * dydz - dxdz * dydy ) / Jac_det ## ξ_z
|
|
240
|
+
M[:,:,:,1,0] = -( dydx * dzdz - dydz * dzdx ) / Jac_det ## η_x
|
|
241
|
+
M[:,:,:,1,1] = +( dxdx * dzdz - dxdz * dzdx ) / Jac_det ## η_y
|
|
242
|
+
M[:,:,:,1,2] = -( dxdx * dydz - dxdz * dydx ) / Jac_det ## η_z
|
|
243
|
+
M[:,:,:,2,0] = +( dydx * dzdy - dydy * dzdx ) / Jac_det ## ζ_x
|
|
244
|
+
M[:,:,:,2,1] = -( dxdx * dzdy - dxdy * dzdx ) / Jac_det ## ζ_y
|
|
245
|
+
M[:,:,:,2,2] = +( dxdx * dydy - dxdy * dydx ) / Jac_det ## ζ_z
|
|
246
|
+
|
|
247
|
+
t_delta = timeit.default_timer() - t_start
|
|
248
|
+
if verbose: tqdm.write( even_print('get M','%0.3f [s]'%(t_delta,), s=True) )
|
|
249
|
+
|
|
250
|
+
if ('M_bak' in locals()):
|
|
251
|
+
np.testing.assert_allclose(M[:,:,:,0,0], M_bak[:,:,:,0,0], atol=1e-14, rtol=1e-14)
|
|
252
|
+
print('check passed: ξ_x')
|
|
253
|
+
np.testing.assert_allclose(M[:,:,:,0,1], M_bak[:,:,:,0,1], atol=1e-14, rtol=1e-14)
|
|
254
|
+
print('check passed: ξ_y')
|
|
255
|
+
np.testing.assert_allclose(M[:,:,:,0,2], M_bak[:,:,:,0,2], atol=1e-14, rtol=1e-14)
|
|
256
|
+
print('check passed: ξ_z')
|
|
257
|
+
np.testing.assert_allclose(M[:,:,:,1,0], M_bak[:,:,:,1,0], atol=1e-14, rtol=1e-14)
|
|
258
|
+
print('check passed: η_x')
|
|
259
|
+
np.testing.assert_allclose(M[:,:,:,1,1], M_bak[:,:,:,1,1], atol=1e-14, rtol=1e-14)
|
|
260
|
+
print('check passed: η_y')
|
|
261
|
+
np.testing.assert_allclose(M[:,:,:,1,2], M_bak[:,:,:,1,2], atol=1e-14, rtol=1e-14)
|
|
262
|
+
print('check passed: η_z')
|
|
263
|
+
np.testing.assert_allclose(M[:,:,:,2,0], M_bak[:,:,:,2,0], atol=1e-14, rtol=1e-14)
|
|
264
|
+
print('check passed: ζ_x')
|
|
265
|
+
np.testing.assert_allclose(M[:,:,:,2,1], M_bak[:,:,:,2,1], atol=1e-14, rtol=1e-14)
|
|
266
|
+
print('check passed: ζ_y')
|
|
267
|
+
np.testing.assert_allclose(M[:,:,:,2,2], M_bak[:,:,:,2,2], atol=1e-14, rtol=1e-14)
|
|
268
|
+
print('check passed: ζ_z')
|
|
269
|
+
np.testing.assert_allclose(M, M_bak, atol=1e-14, rtol=1e-14)
|
|
270
|
+
print('check passed: M')
|
|
271
|
+
|
|
272
|
+
return M
|
turbx/h5.py
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
import h5py
|
|
4
|
+
import numpy as np
|
|
5
|
+
from tqdm import tqdm
|
|
6
|
+
|
|
7
|
+
# ======================================================================
|
|
8
|
+
|
|
9
|
+
def h5_chunk_sizer(nxi, **kwargs):
|
|
10
|
+
'''
|
|
11
|
+
Solve for HDF5 dataset chunk size.
|
|
12
|
+
|
|
13
|
+
Parameters:
|
|
14
|
+
----------
|
|
15
|
+
nxi : iterable
|
|
16
|
+
The shape of the full HDF5 dataset.
|
|
17
|
+
constraint : iterable
|
|
18
|
+
Per-axis constraint. Each element can be:
|
|
19
|
+
- None → flexible
|
|
20
|
+
- int (>0) → fixed chunk size
|
|
21
|
+
- 'full' or -1 → chunk size equals axis size
|
|
22
|
+
- ('max', int) → chunk size must be ≤ int
|
|
23
|
+
'''
|
|
24
|
+
|
|
25
|
+
size_kb = kwargs.get('size_kb' , 2*1024 ) ## target chunk size in [KB] --> default = 2 [MB]
|
|
26
|
+
itemsize = kwargs.get('itemsize' , 4 ) ## dtype.itemsize --> default single precision i.e. 4 [B]
|
|
27
|
+
constraint = kwargs.get('constraint' , None ) ## iterable of nxi constraints --> int,None,'full'/-1
|
|
28
|
+
base = kwargs.get('base' , 2 ) ## axis chunk size = ceil[size/(<int>*base)] where <int> is incremented
|
|
29
|
+
|
|
30
|
+
## if no constraint given, all axes are fully flexible
|
|
31
|
+
if constraint is None:
|
|
32
|
+
constraint = [ None for i in range(len(nxi)) ]
|
|
33
|
+
|
|
34
|
+
## check inputs
|
|
35
|
+
if not hasattr(constraint, '__iter__') or len(nxi) != len(constraint):
|
|
36
|
+
raise ValueError('nxi and constraint must be iterable and the same length')
|
|
37
|
+
if not isinstance(base,int):
|
|
38
|
+
raise TypeError('base must be an integer')
|
|
39
|
+
if (base<1):
|
|
40
|
+
raise TypeError('base must be an integer')
|
|
41
|
+
|
|
42
|
+
# === increment divisor on largest axis, with divisor=<int>*base
|
|
43
|
+
|
|
44
|
+
nxi = list(nxi)
|
|
45
|
+
div = [ 1 for i in range(len(nxi)) ] ## divisor vector, initialize with int ones
|
|
46
|
+
|
|
47
|
+
## list of axes indices which are 'flexible' ... this is updated dynamically in loop
|
|
48
|
+
i_flexible = [ i for i,c in enumerate(constraint) if c is None or isinstance(c,tuple) ]
|
|
49
|
+
|
|
50
|
+
while True:
|
|
51
|
+
|
|
52
|
+
div_last = list(div) ## make a copy
|
|
53
|
+
#print(f'div_last = {str(tuple(div_last))}')
|
|
54
|
+
|
|
55
|
+
chunks = []
|
|
56
|
+
for i in range(len(nxi)):
|
|
57
|
+
|
|
58
|
+
dim = nxi[i]
|
|
59
|
+
|
|
60
|
+
if (constraint[i] is None):
|
|
61
|
+
C = max( int(np.floor(dim/div[i])) , 1 ) ## divide by divisor
|
|
62
|
+
elif (constraint[i] == 'full') or (constraint[i] == -1):
|
|
63
|
+
C = dim ## chunk axis shape is == dset axis shape
|
|
64
|
+
elif isinstance(constraint[i], int) and (constraint[i]>0):
|
|
65
|
+
C = constraint[i] ## chunk axis shape is just the constraint
|
|
66
|
+
elif isinstance(constraint[i], tuple) and (constraint[i][0]=='max') and isinstance(constraint[i][1],int):
|
|
67
|
+
max_val = constraint[i][1]
|
|
68
|
+
C = min( max( int(np.floor(dim/div[i])) , 1 ) , max_val )
|
|
69
|
+
else:
|
|
70
|
+
raise ValueError(f'problem with constraint[{i:d}] = {str(constraint[i])}')
|
|
71
|
+
chunks.append(C)
|
|
72
|
+
|
|
73
|
+
#print(f'chunks = {str(tuple(chunks))}')
|
|
74
|
+
|
|
75
|
+
## recalculate i_flexible
|
|
76
|
+
i_flexible = []
|
|
77
|
+
for i,c in enumerate(constraint):
|
|
78
|
+
if chunks[i] == 1: ## already at min, is not flexible
|
|
79
|
+
continue
|
|
80
|
+
if c is None:
|
|
81
|
+
i_flexible.append(i)
|
|
82
|
+
elif isinstance(c,tuple) and c[0]=='max':
|
|
83
|
+
if chunks[i] > 1:
|
|
84
|
+
i_flexible.append(i)
|
|
85
|
+
|
|
86
|
+
#print(f'i_flexible = {str(i_flexible)}')
|
|
87
|
+
|
|
88
|
+
## there are no flexible axes --> exit loop
|
|
89
|
+
if len(i_flexible)==0:
|
|
90
|
+
break
|
|
91
|
+
|
|
92
|
+
## the current size of a chunk
|
|
93
|
+
chunk_size_kb = np.prod(chunks)*itemsize / 1024.
|
|
94
|
+
#print(f'chunk size {chunk_size_kb:0.1f} [KB] / {np.prod(chunks)*itemsize:d} [B]')
|
|
95
|
+
|
|
96
|
+
if ( chunk_size_kb <= size_kb ): ## if chunk size is < target, then break
|
|
97
|
+
break
|
|
98
|
+
else: ## otherwise, increase the divisor of the greatest 'flexible' axis
|
|
99
|
+
|
|
100
|
+
## get index of (flexible) axis with greatest size
|
|
101
|
+
aa = [ i for i,c in enumerate(chunks) if (i in i_flexible) ]
|
|
102
|
+
bb = [ c for i,c in enumerate(chunks) if (i in i_flexible) ]
|
|
103
|
+
i_gt = aa[np.argmax(bb)]
|
|
104
|
+
|
|
105
|
+
## update divisor
|
|
106
|
+
div[i_gt] *= base
|
|
107
|
+
|
|
108
|
+
#print(f'div = {str(tuple(div))}')
|
|
109
|
+
#print('---')
|
|
110
|
+
|
|
111
|
+
## check if in infinite loop (divisor not being updated)
|
|
112
|
+
if (div_last is not None) and (div == div_last):
|
|
113
|
+
raise ValueError(f'invalid parameters for h5_chunk_sizer() : constraint={str(constraint)}, size_kb={size_kb:d}, base={base:d}')
|
|
114
|
+
|
|
115
|
+
return tuple(chunks)
|
|
116
|
+
|
|
117
|
+
def h5_visititems_print_attrs(name, obj):
|
|
118
|
+
'''
|
|
119
|
+
callable for input to h5py.Group.visititems() to print names & attributes
|
|
120
|
+
'''
|
|
121
|
+
n_slashes = name.count('/')
|
|
122
|
+
shift = n_slashes*2*' '
|
|
123
|
+
item_name = name.split('/')[-1]
|
|
124
|
+
|
|
125
|
+
if isinstance(obj,h5py._hl.dataset.Dataset):
|
|
126
|
+
print(shift + item_name + ' --> shape=%s, dtype=%s'%( str(obj.shape), str(obj.dtype) ) )
|
|
127
|
+
else:
|
|
128
|
+
print(shift + item_name)
|
|
129
|
+
|
|
130
|
+
## print attributes
|
|
131
|
+
for key, val in obj.attrs.items():
|
|
132
|
+
try:
|
|
133
|
+
print(shift + 2*' ' + f'{key} = {str(val)} --> dtype={str(val.dtype)}')
|
|
134
|
+
except AttributeError:
|
|
135
|
+
print(shift + 2*' ' + f'{key} = {str(val)} --> type={str(type(val).__name__)}')
|
|
136
|
+
|
|
137
|
+
def h5_print_contents(h5filehandle):
|
|
138
|
+
'''
|
|
139
|
+
Print file-level attributes and recursively print names & attributes of all groups and datasets.
|
|
140
|
+
'''
|
|
141
|
+
|
|
142
|
+
## file-level attributes
|
|
143
|
+
for key, val in h5filehandle.attrs.items():
|
|
144
|
+
try:
|
|
145
|
+
print(f'{key} = {str(val)} --> dtype={str(val.dtype)}')
|
|
146
|
+
except AttributeError:
|
|
147
|
+
print(f'{key} = {str(val)} --> type={str(type(val).__name__)}')
|
|
148
|
+
|
|
149
|
+
def visitor(name, obj):
|
|
150
|
+
n_slashes = name.count('/')
|
|
151
|
+
shift = n_slashes * 2 * ' '
|
|
152
|
+
item_name = name.split('/')[-1]
|
|
153
|
+
|
|
154
|
+
if isinstance(obj, h5py._hl.dataset.Dataset):
|
|
155
|
+
print(shift + item_name + ' --> shape=%s, dtype=%s' % (str(obj.shape), str(obj.dtype)))
|
|
156
|
+
else:
|
|
157
|
+
print(shift + item_name)
|
|
158
|
+
|
|
159
|
+
for key, val in obj.attrs.items():
|
|
160
|
+
try:
|
|
161
|
+
print(shift + 2 * ' ' + f'{key} = {str(val)} --> dtype={str(val.dtype)}')
|
|
162
|
+
except AttributeError:
|
|
163
|
+
print(shift + 2 * ' ' + f'{key} = {str(val)} --> type={str(type(val).__name__)}')
|
|
164
|
+
|
|
165
|
+
# Use the visitor function with visititems
|
|
166
|
+
h5filehandle.visititems(visitor)
|
|
167
|
+
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
class h5_visit_container:
|
|
171
|
+
'''
|
|
172
|
+
callable for input to h5py.Group.visit() which stores dataset/group names
|
|
173
|
+
'''
|
|
174
|
+
def __init__(self):
|
|
175
|
+
self.names = []
|
|
176
|
+
def __call__(self, name):
|
|
177
|
+
if (name not in self.names):
|
|
178
|
+
self.names.append(name)
|
|
179
|
+
|
|
180
|
+
def h5_ds_force_allocate_chunks(ds, verbose=False, quick=False):
|
|
181
|
+
'''
|
|
182
|
+
Force allocation of all chunks in an ND dataset by writing real data
|
|
183
|
+
'''
|
|
184
|
+
if not isinstance(ds, h5py.Dataset):
|
|
185
|
+
raise TypeError('ds must be a h5py.Dataset object')
|
|
186
|
+
|
|
187
|
+
shape = ds.shape
|
|
188
|
+
dtype = ds.dtype
|
|
189
|
+
chunk_shape = ds.chunks
|
|
190
|
+
rng = np.random.default_rng(seed=1)
|
|
191
|
+
|
|
192
|
+
## for contiguous datasets, fill the entire array
|
|
193
|
+
if chunk_shape is None:
|
|
194
|
+
#ds[...] = np.zeros(shape, dtype=dtype) ## might lead to optimizations under the hood
|
|
195
|
+
#ds[...] = rng.uniform(-1,+1,size=shape).astype(dtype)
|
|
196
|
+
ds[...] = rng.random(size=shape, dtype=dtype)
|
|
197
|
+
return
|
|
198
|
+
|
|
199
|
+
## info needed for iterating through chunks
|
|
200
|
+
chunk_starts = [range(0, dim, cdim) for dim, cdim in zip(shape, chunk_shape)]
|
|
201
|
+
chunk_grid_shape = [len(r) for r in chunk_starts]
|
|
202
|
+
total_chunks = np.prod(chunk_grid_shape)
|
|
203
|
+
|
|
204
|
+
if verbose:
|
|
205
|
+
progress_bar = tqdm(
|
|
206
|
+
total=total_chunks,
|
|
207
|
+
ncols=100,
|
|
208
|
+
desc='allocate chunks',
|
|
209
|
+
leave=True,
|
|
210
|
+
file=sys.stdout,
|
|
211
|
+
mininterval=0.1,
|
|
212
|
+
smoothing=0.,
|
|
213
|
+
#bar_format="\033[B{l_bar}{bar}| {n}/{total} [{percentage:.1f}%] {elapsed}/{remaining}\033[A\n\b",
|
|
214
|
+
bar_format="{l_bar}{bar}| {n}/{total} [{percentage:.1f}%] {elapsed}/{remaining}",
|
|
215
|
+
ascii="░█",
|
|
216
|
+
colour='#FF6600',
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
for chunk_idx in np.ndindex(*chunk_grid_shape):
|
|
220
|
+
starts = [r[i] for r, i in zip(chunk_starts, chunk_idx)]
|
|
221
|
+
|
|
222
|
+
if quick: ## just write a single element to allocate the chunk
|
|
223
|
+
ds[tuple(starts)] = 0
|
|
224
|
+
else:
|
|
225
|
+
slices = tuple(
|
|
226
|
+
slice(start, min(start + size, dim))
|
|
227
|
+
for start, size, dim in zip(starts, chunk_shape, shape)
|
|
228
|
+
)
|
|
229
|
+
actual_shape = tuple(slc.stop - slc.start for slc in slices)
|
|
230
|
+
#ds[slices] = np.zeros(actual_shape, dtype=dtype) ## might lead to optimizations under the hood
|
|
231
|
+
#ds[slices] = rng.uniform(-1,+1,size=actual_shape).astype(dtype)
|
|
232
|
+
ds[slices] = rng.random(size=actual_shape, dtype=dtype)
|
|
233
|
+
|
|
234
|
+
if verbose: progress_bar.update()
|
|
235
|
+
if verbose: progress_bar.close()
|
|
236
|
+
return
|