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/__init__.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from .blasius import Blasius_solution
|
|
2
|
+
from .composite_profile import composite_profile_CMN2009
|
|
3
|
+
from .confidence_interval import calc_var_bmbc, confidence_interval_unbiased
|
|
4
|
+
from .eas3 import eas3
|
|
5
|
+
from .eas4 import eas4
|
|
6
|
+
from .fig_ax_constructor import fig_ax_grid
|
|
7
|
+
from .freestream_parameters import freestream_parameters
|
|
8
|
+
from .gradient import fd_coeff_calculator, gradient
|
|
9
|
+
from .h5 import h5_print_contents
|
|
10
|
+
from .mvp import (
|
|
11
|
+
asymptotic_H12_Retheta,
|
|
12
|
+
asymptotic_H32_Retheta,
|
|
13
|
+
comp_transform_GFM,
|
|
14
|
+
comp_transform_TL,
|
|
15
|
+
comp_transform_VD,
|
|
16
|
+
comp_transform_VIPL,
|
|
17
|
+
)
|
|
18
|
+
from .rgd import rgd
|
|
19
|
+
from .set_mpl_env import set_mpl_env
|
|
20
|
+
from .spd import spd
|
|
21
|
+
from .utils import even_print, format_nbytes, format_time_string, step
|
|
22
|
+
from .ztmd import ztmd
|
|
23
|
+
from .ztmd_loader import ztmd_loader
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
'Blasius_solution',
|
|
27
|
+
'asymptotic_H12_Retheta',
|
|
28
|
+
'asymptotic_H32_Retheta',
|
|
29
|
+
'calc_var_bmbc',
|
|
30
|
+
'comp_transform_GFM',
|
|
31
|
+
'comp_transform_TL',
|
|
32
|
+
'comp_transform_VD',
|
|
33
|
+
'comp_transform_VIPL',
|
|
34
|
+
'composite_profile_CMN2009',
|
|
35
|
+
'confidence_interval_unbiased',
|
|
36
|
+
'eas3',
|
|
37
|
+
'eas4',
|
|
38
|
+
'even_print',
|
|
39
|
+
'fd_coeff_calculator',
|
|
40
|
+
'fig_ax_grid',
|
|
41
|
+
'format_nbytes',
|
|
42
|
+
'format_time_string',
|
|
43
|
+
'freestream_parameters',
|
|
44
|
+
'gradient',
|
|
45
|
+
'h5_print_contents',
|
|
46
|
+
'rgd',
|
|
47
|
+
'set_mpl_env',
|
|
48
|
+
'spd',
|
|
49
|
+
'step',
|
|
50
|
+
'ztmd',
|
|
51
|
+
'ztmd_loader',
|
|
52
|
+
]
|
turbx/bl.py
ADDED
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import scipy as sp
|
|
3
|
+
from scipy.integrate import cumulative_trapezoid, trapezoid
|
|
4
|
+
from scipy.interpolate import interp1d
|
|
5
|
+
from tqdm import tqdm
|
|
6
|
+
|
|
7
|
+
from .gradient import gradient
|
|
8
|
+
|
|
9
|
+
# ======================================================================
|
|
10
|
+
|
|
11
|
+
def _generic_bl_integral(y, integrand, y_edge=None, j_edge=None, **kwargs):
|
|
12
|
+
'''
|
|
13
|
+
Compute a definite boundary-layer integral from y=0 to 'edge'
|
|
14
|
+
-----
|
|
15
|
+
The integration limit is given by j_edge (on-grid) or y_edge
|
|
16
|
+
(off-grid, using interpolation of the cumulative integral)
|
|
17
|
+
'''
|
|
18
|
+
|
|
19
|
+
interp_kind = kwargs.get('interp_kind','cubic')
|
|
20
|
+
|
|
21
|
+
## Check coordinate [y] and integrand
|
|
22
|
+
if y.ndim != 1 or integrand.ndim != 1:
|
|
23
|
+
raise ValueError
|
|
24
|
+
if y.shape[0] != integrand.shape[0]:
|
|
25
|
+
raise ValueError
|
|
26
|
+
if not np.all(np.diff(y) > 0): ## check (+) monotonicity
|
|
27
|
+
raise ValueError("y must be monotonically increasing")
|
|
28
|
+
|
|
29
|
+
## Assert y[0]~0.
|
|
30
|
+
if y[0] < -1e-4:
|
|
31
|
+
raise ValueError("y[0] < 0")
|
|
32
|
+
if not np.isclose(y[0], 0., atol=1e-4):
|
|
33
|
+
raise ValueError("y[0] not sufficiently close to 0")
|
|
34
|
+
|
|
35
|
+
## Check distance 'y_edge' and/or index 'j_edge'
|
|
36
|
+
if j_edge is not None and not isinstance(j_edge,(int,np.integer)):
|
|
37
|
+
raise ValueError("j_edge must be int")
|
|
38
|
+
if y_edge is not None:
|
|
39
|
+
y_edge = float(y_edge)
|
|
40
|
+
if (y_edge is not None) and (j_edge is not None):
|
|
41
|
+
if not np.isclose(y[j_edge], y_edge, rtol=1e-4):
|
|
42
|
+
raise ValueError("y_edge and j_edge inconsistent")
|
|
43
|
+
if y_edge is None and j_edge is None: ## No edge defined; integrate full profile
|
|
44
|
+
j_edge = -1
|
|
45
|
+
|
|
46
|
+
if j_edge is not None: ## Index-defined 'edge'
|
|
47
|
+
integral_val = trapezoid(
|
|
48
|
+
integrand[:j_edge+1],
|
|
49
|
+
x=y[:j_edge+1],
|
|
50
|
+
)
|
|
51
|
+
else: ## 'edge' distance supplied, perform 1D interpolation
|
|
52
|
+
integral_cum = cumulative_trapezoid(
|
|
53
|
+
integrand,
|
|
54
|
+
x=y,
|
|
55
|
+
initial=0.0,
|
|
56
|
+
)
|
|
57
|
+
integral_val = interp1d(
|
|
58
|
+
y,
|
|
59
|
+
integral_cum,
|
|
60
|
+
kind=interp_kind,
|
|
61
|
+
bounds_error=True,
|
|
62
|
+
)(y_edge)
|
|
63
|
+
|
|
64
|
+
return integral_val
|
|
65
|
+
|
|
66
|
+
def calc_d1(y, u, rho=None, y_edge=None, j_edge=None, **kwargs):
|
|
67
|
+
'''
|
|
68
|
+
Displacement (mass-flux deficit) thickness δ1 = δ*
|
|
69
|
+
-----
|
|
70
|
+
δ1 = ∫ ( 1 - ρu / (ρe·ue) ) dy
|
|
71
|
+
'''
|
|
72
|
+
interp_kind = kwargs.get('interp_kind','cubic')
|
|
73
|
+
|
|
74
|
+
if y_edge is None and j_edge is None:
|
|
75
|
+
j_edge = -1 ## integrate over complete profile
|
|
76
|
+
|
|
77
|
+
if rho is None:
|
|
78
|
+
rho = np.ones_like(u)
|
|
79
|
+
|
|
80
|
+
if j_edge is None:
|
|
81
|
+
u_e = interp1d(y, u, kind=interp_kind, bounds_error=True)(y_edge)
|
|
82
|
+
rho_e = interp1d(y, rho, kind=interp_kind, bounds_error=True)(y_edge)
|
|
83
|
+
else:
|
|
84
|
+
u_e = u[j_edge]
|
|
85
|
+
rho_e = rho[j_edge]
|
|
86
|
+
|
|
87
|
+
integrand = 1. - (rho*u)/(rho_e*u_e) ## δ1
|
|
88
|
+
|
|
89
|
+
return _generic_bl_integral(
|
|
90
|
+
y,
|
|
91
|
+
integrand,
|
|
92
|
+
y_edge=y_edge,
|
|
93
|
+
j_edge=j_edge,
|
|
94
|
+
interp_kind=interp_kind,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def calc_d2(y, u, rho=None, y_edge=None, j_edge=None, **kwargs):
|
|
98
|
+
'''
|
|
99
|
+
Momentum deficit thickness δ2 = θ
|
|
100
|
+
-----
|
|
101
|
+
δ2 = ∫ ( ρu / (ρe·ue) ) ( 1 - u / ue ) dy
|
|
102
|
+
'''
|
|
103
|
+
interp_kind = kwargs.get('interp_kind','cubic')
|
|
104
|
+
|
|
105
|
+
if y_edge is None and j_edge is None:
|
|
106
|
+
j_edge = -1 ## integrate over complete profile
|
|
107
|
+
|
|
108
|
+
if rho is None:
|
|
109
|
+
rho = np.ones_like(u)
|
|
110
|
+
|
|
111
|
+
if j_edge is None:
|
|
112
|
+
u_e = interp1d(y, u, kind=interp_kind, bounds_error=True)(y_edge)
|
|
113
|
+
rho_e = interp1d(y, rho, kind=interp_kind, bounds_error=True)(y_edge)
|
|
114
|
+
else:
|
|
115
|
+
u_e = u[j_edge]
|
|
116
|
+
rho_e = rho[j_edge]
|
|
117
|
+
|
|
118
|
+
integrand = (rho*u)/(rho_e*u_e) * (1. - u/u_e) ## δ2
|
|
119
|
+
|
|
120
|
+
return _generic_bl_integral(
|
|
121
|
+
y,
|
|
122
|
+
integrand,
|
|
123
|
+
y_edge=y_edge,
|
|
124
|
+
j_edge=j_edge,
|
|
125
|
+
interp_kind=interp_kind,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
def calc_d3(y, u, rho=None, y_edge=None, j_edge=None, **kwargs):
|
|
129
|
+
'''
|
|
130
|
+
Kinetic energy deficit thickness δ3
|
|
131
|
+
-----
|
|
132
|
+
δ3 = ∫ ( ρu / (ρe·ue) ) ( 1 - (u / ue)^2 ) dy
|
|
133
|
+
'''
|
|
134
|
+
interp_kind = kwargs.get('interp_kind','cubic')
|
|
135
|
+
|
|
136
|
+
if y_edge is None and j_edge is None:
|
|
137
|
+
j_edge = -1 ## integrate over complete profile
|
|
138
|
+
|
|
139
|
+
if rho is None:
|
|
140
|
+
rho = np.ones_like(u)
|
|
141
|
+
|
|
142
|
+
if j_edge is None:
|
|
143
|
+
u_e = interp1d(y, u, kind=interp_kind, bounds_error=True)(y_edge)
|
|
144
|
+
rho_e = interp1d(y, rho, kind=interp_kind, bounds_error=True)(y_edge)
|
|
145
|
+
else:
|
|
146
|
+
u_e = u[j_edge]
|
|
147
|
+
rho_e = rho[j_edge]
|
|
148
|
+
|
|
149
|
+
integrand = (rho*u)/(rho_e*u_e) * (1. - (u/u_e)**2) ## δ3
|
|
150
|
+
|
|
151
|
+
return _generic_bl_integral(
|
|
152
|
+
y,
|
|
153
|
+
integrand,
|
|
154
|
+
y_edge=y_edge,
|
|
155
|
+
j_edge=j_edge,
|
|
156
|
+
interp_kind=interp_kind,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
def calc_dRC(y, u, u_tau, rho=None, rho_wall=None, y_edge=None, j_edge=None, **kwargs):
|
|
160
|
+
'''
|
|
161
|
+
Rotta-Clauser Δ = ∫W+dy = ∫(ρe+ue+ - ρ+u+) dy
|
|
162
|
+
-----
|
|
163
|
+
W = (ρe·ue - ρ·u)/(ρe·ue) = 1 - (ρu/(ρe·ue))
|
|
164
|
+
W+ = (ρe·ue - ρ·u)/(ρw·uτ) = ρe+ue+ - ρ+u+
|
|
165
|
+
'''
|
|
166
|
+
interp_kind = kwargs.get('interp_kind','cubic')
|
|
167
|
+
|
|
168
|
+
if y_edge is None and j_edge is None:
|
|
169
|
+
j_edge = -1 ## integrate over complete profile
|
|
170
|
+
|
|
171
|
+
if rho is None:
|
|
172
|
+
if rho_wall is not None:
|
|
173
|
+
raise ValueError("rho_wall provided but rho is None")
|
|
174
|
+
rho = np.ones_like(u)
|
|
175
|
+
|
|
176
|
+
if rho_wall is None:
|
|
177
|
+
rho_wall = rho[0]
|
|
178
|
+
else:
|
|
179
|
+
if not np.isclose(rho[0], rho_wall, rtol=1e-4):
|
|
180
|
+
raise ValueError("rho_wall inconsistent with rho[0]")
|
|
181
|
+
|
|
182
|
+
if j_edge is None:
|
|
183
|
+
u_e = interp1d(y, u, bounds_error=True)(y_edge)
|
|
184
|
+
rho_e = interp1d(y, rho, bounds_error=True)(y_edge)
|
|
185
|
+
else:
|
|
186
|
+
u_e = u[j_edge]
|
|
187
|
+
rho_e = rho[j_edge]
|
|
188
|
+
|
|
189
|
+
integrand = (rho_e*u_e - rho*u) / (rho_wall*u_tau) ## W+
|
|
190
|
+
|
|
191
|
+
## Returned value is units of input [y]
|
|
192
|
+
return _generic_bl_integral(
|
|
193
|
+
y,
|
|
194
|
+
integrand,
|
|
195
|
+
y_edge=y_edge,
|
|
196
|
+
j_edge=j_edge,
|
|
197
|
+
interp_kind=interp_kind,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# ======================================================================
|
|
201
|
+
|
|
202
|
+
def calc_profile_edge_1d(y, u=None, ddy_u=None, **kwargs):
|
|
203
|
+
'''
|
|
204
|
+
Determine the edge location of a 1D boundary-layer profile
|
|
205
|
+
|
|
206
|
+
The edge is defined as the first location where |du+/dy+|<ϵ
|
|
207
|
+
|
|
208
|
+
Parameters
|
|
209
|
+
----------
|
|
210
|
+
y : np.ndarray
|
|
211
|
+
1D wall-normal coordinate (units arbitrary)
|
|
212
|
+
u : np.ndarray, optional
|
|
213
|
+
Profile used to compute du/dy internally
|
|
214
|
+
ddy_u : np.ndarray, optional
|
|
215
|
+
Precomputed du/dy. Exactly one of `u` or `ddy_u` must be provided
|
|
216
|
+
|
|
217
|
+
Keyword Arguments
|
|
218
|
+
-----------------
|
|
219
|
+
epsilon : float, default=5e-5
|
|
220
|
+
Threshold ϵ for |du+/dy+|
|
|
221
|
+
acc : int, default=6
|
|
222
|
+
Accuracy order for numerical differentiation
|
|
223
|
+
edge_stencil : str, default='half'
|
|
224
|
+
Gradient stencil near boundaries
|
|
225
|
+
interp_kind : {'linear','cubic'}, default='cubic'
|
|
226
|
+
Interpolation method when `ongrid=False`
|
|
227
|
+
ongrid : bool, default=True
|
|
228
|
+
Snap to first grid point if True; otherwise interpolate
|
|
229
|
+
|
|
230
|
+
Returns
|
|
231
|
+
-------
|
|
232
|
+
y_edge : float
|
|
233
|
+
Wall-normal location of the boundary-layer edge
|
|
234
|
+
|
|
235
|
+
Notes
|
|
236
|
+
-----
|
|
237
|
+
- Assumes |du+/dy+| at y=0 is O(1)
|
|
238
|
+
- ϵ is adaptively increased if no crossing is found
|
|
239
|
+
- Deprecated kwargs (`lchar`, `deriv`) are rejected
|
|
240
|
+
'''
|
|
241
|
+
|
|
242
|
+
epsilon = kwargs.get('epsilon',5e-5)
|
|
243
|
+
acc = kwargs.get('acc',6)
|
|
244
|
+
edge_stencil = kwargs.get('edge_stencil','half')
|
|
245
|
+
interp_kind = kwargs.get('interp_kind','cubic')
|
|
246
|
+
ongrid = kwargs.get('ongrid',True) ## Snap to grid point
|
|
247
|
+
|
|
248
|
+
## These options were deprecated -- check that they are not passed
|
|
249
|
+
lchar = kwargs.get('lchar',None)
|
|
250
|
+
if (lchar is not None):
|
|
251
|
+
raise ValueError('lchar was deprecated as kwarg for turbx.calc_profile_edge_1d()')
|
|
252
|
+
deriv = kwargs.get('deriv',None)
|
|
253
|
+
if (deriv is not None):
|
|
254
|
+
raise ValueError('deriv was deprecated as kwarg for turbx.calc_profile_edge_1d()')
|
|
255
|
+
|
|
256
|
+
# do_debug_plot = kwargs.get('do_debug_plot',False) ## show a debug plot for tuning ϵ
|
|
257
|
+
|
|
258
|
+
## Checks
|
|
259
|
+
if not isinstance(y, np.ndarray):
|
|
260
|
+
raise ValueError('y should be a numpy array')
|
|
261
|
+
if (y.ndim!=1):
|
|
262
|
+
raise ValueError
|
|
263
|
+
|
|
264
|
+
## Make sure exactly one of u OR ddy_u was passed
|
|
265
|
+
if (u is None) and (ddy_u is None):
|
|
266
|
+
raise ValueError('pass one of either u or ddy_u')
|
|
267
|
+
if (u is not None) and (ddy_u is not None):
|
|
268
|
+
raise ValueError('either pass u or ddy_u (not both)')
|
|
269
|
+
|
|
270
|
+
## Make sure dims of passed u | ddy_u are correct
|
|
271
|
+
if (u is not None):
|
|
272
|
+
if not isinstance(u, np.ndarray):
|
|
273
|
+
raise ValueError
|
|
274
|
+
if (u.ndim!=1):
|
|
275
|
+
raise ValueError
|
|
276
|
+
if (u.shape[0]!=y.shape[0]):
|
|
277
|
+
raise ValueError
|
|
278
|
+
if (ddy_u is not None):
|
|
279
|
+
if not isinstance(ddy_u, np.ndarray):
|
|
280
|
+
raise ValueError
|
|
281
|
+
if (ddy_u.ndim!=1):
|
|
282
|
+
raise ValueError
|
|
283
|
+
if (ddy_u.shape[0]!=y.shape[0]):
|
|
284
|
+
raise ValueError
|
|
285
|
+
|
|
286
|
+
if not any([(interp_kind=='linear'),(interp_kind=='cubic')]):
|
|
287
|
+
raise ValueError("'interp_kind' should be one of: 'linear','cubic'")
|
|
288
|
+
|
|
289
|
+
ny = y.shape[0]
|
|
290
|
+
|
|
291
|
+
## ddy_u was NOT passed, need to calculate du/dy here
|
|
292
|
+
if (ddy_u is None):
|
|
293
|
+
ddy_u = gradient(u, y, axis=0, d=1, acc=acc, edge_stencil=edge_stencil)
|
|
294
|
+
|
|
295
|
+
## Confirm that |d□/dy|_w is ~O(1)
|
|
296
|
+
np.testing.assert_allclose( ddy_u[0], 1., rtol=0.05) ## 5% rtol
|
|
297
|
+
|
|
298
|
+
## Static ϵ
|
|
299
|
+
# ## get [y] of first intersection
|
|
300
|
+
# jtop = ny-1
|
|
301
|
+
# for j in range(ny):
|
|
302
|
+
# if ( np.abs(ddy_u[j]) < epsilon ): ## |d□/dy|<ϵ
|
|
303
|
+
# jtop = j
|
|
304
|
+
# break
|
|
305
|
+
|
|
306
|
+
## Variable ϵ
|
|
307
|
+
## Get [y] of first intersection
|
|
308
|
+
keep_going = True
|
|
309
|
+
while keep_going:
|
|
310
|
+
jtop = ny-1
|
|
311
|
+
for j in range(ny):
|
|
312
|
+
if ( np.abs(ddy_u[j]) < epsilon ): ## |d□/dy|<ϵ
|
|
313
|
+
jtop = j
|
|
314
|
+
break
|
|
315
|
+
if (jtop == ny-1): ## Got to end, recalibrate & keep going
|
|
316
|
+
epsilon *= 1.05
|
|
317
|
+
tqdm.write(f'recalibrating: ϵ={epsilon:0.3e}')
|
|
318
|
+
else:
|
|
319
|
+
keep_going = False
|
|
320
|
+
|
|
321
|
+
if (jtop == ny-1):
|
|
322
|
+
raise ValueError('jtop == ny-1')
|
|
323
|
+
if (jtop<3):
|
|
324
|
+
raise ValueError('jtop<3')
|
|
325
|
+
|
|
326
|
+
if ongrid: ## Grid-snapped, i.e. first point satisfying |d□/dy|<ϵ
|
|
327
|
+
y_edge = float( y[jtop] )
|
|
328
|
+
|
|
329
|
+
else: ## Find interpolated |d□/dy|==ϵ
|
|
330
|
+
|
|
331
|
+
#intrp_func = interp1d(y, ddy_u, kind=interp_kind, bounds_error=True) ## deprecated!
|
|
332
|
+
|
|
333
|
+
if (interp_kind=='cubic'):
|
|
334
|
+
|
|
335
|
+
#intrp_func = sp.interpolate.CubicSpline(y, ddy_u, axis=0, bc_type='natural', extrapolate=False)
|
|
336
|
+
intrp_func = sp.interpolate.PchipInterpolator(y, ddy_u, axis=0, extrapolate=False)
|
|
337
|
+
#intrp_func = sp.interpolate.Akima1DInterpolator(y, ddy_u, axis=0, method='akima', extrapolate=False)
|
|
338
|
+
|
|
339
|
+
elif (interp_kind=='linear'):
|
|
340
|
+
raise NotImplementedError
|
|
341
|
+
## need to use np.interp() --> no longer retuns a function! needs to be wrapped.
|
|
342
|
+
else:
|
|
343
|
+
raise NotImplementedError
|
|
344
|
+
|
|
345
|
+
def __f_opt_edge_locator(y_test, intrp_func, epsilon):
|
|
346
|
+
ddy_u_test = intrp_func(y_test)
|
|
347
|
+
root = np.abs( ddy_u_test ) - epsilon
|
|
348
|
+
return root
|
|
349
|
+
|
|
350
|
+
sol = sp.optimize.least_squares(
|
|
351
|
+
fun=__f_opt_edge_locator,
|
|
352
|
+
args=(intrp_func,epsilon,),
|
|
353
|
+
x0 = y[0] + 0.99*(y[jtop]-y[0]),
|
|
354
|
+
#xtol=1e-15,
|
|
355
|
+
#ftol=1e-15,
|
|
356
|
+
#gtol=1e-15,
|
|
357
|
+
method='dogbox',
|
|
358
|
+
#method='trf',
|
|
359
|
+
#bounds=(y[jtop-2], y[jtop]),
|
|
360
|
+
bounds=(y[0], y[jtop]),
|
|
361
|
+
)
|
|
362
|
+
if not sol.success:
|
|
363
|
+
raise ValueError
|
|
364
|
+
if ( sol.x.shape[0] != 1 ):
|
|
365
|
+
raise ValueError
|
|
366
|
+
|
|
367
|
+
y_edge = float(sol.x[0])
|
|
368
|
+
|
|
369
|
+
if ( y_edge > y[jtop] ):
|
|
370
|
+
raise ValueError
|
|
371
|
+
|
|
372
|
+
# if do_debug_plot:
|
|
373
|
+
# plt.close('all')
|
|
374
|
+
# fig1 = plt.figure(figsize=(2*(16/9),2), dpi=300)
|
|
375
|
+
# ax1 = plt.gca()
|
|
376
|
+
# ax1.set_xscale('log', base=10)
|
|
377
|
+
# ax1.plot(
|
|
378
|
+
# ddy_u,
|
|
379
|
+
# y,
|
|
380
|
+
# lw=0.8,
|
|
381
|
+
# )
|
|
382
|
+
# #ax1.set_xlim(1e-30, 1e1)
|
|
383
|
+
# ax1.axhline(y=y_edge, linestyle='dashed', c='gray', zorder=1, lw=0.5)
|
|
384
|
+
# ax1.axvline(x=epsilon, linestyle='dashed', c='gray', zorder=1, lw=0.5)
|
|
385
|
+
# fig1.tight_layout(pad=0.25)
|
|
386
|
+
# fig1.tight_layout(pad=0.25)
|
|
387
|
+
# plt.show()
|
|
388
|
+
|
|
389
|
+
return y_edge
|
|
390
|
+
|
|
391
|
+
def calc_d99_1d(y, u, y_edge, u_edge=None, **kwargs):
|
|
392
|
+
'''
|
|
393
|
+
determine δ99 location of 1D profile
|
|
394
|
+
- [y] is a 1D coordinate vector
|
|
395
|
+
- [u] is some profile variable (streamwise velocity, pseudovelocity, etc.)
|
|
396
|
+
- [y_edge] is the edge location
|
|
397
|
+
- [u_edge] (optional) is the variable value at the edge location
|
|
398
|
+
'''
|
|
399
|
+
|
|
400
|
+
rtol = kwargs.get('rtol',1e-3) ## rtol for asserting u[y_edge]==u_edge
|
|
401
|
+
interp_kind = kwargs.get('interp_kind','cubic')
|
|
402
|
+
d95 = kwargs.get('d95',False) ## δ95 instead of δ99
|
|
403
|
+
|
|
404
|
+
if (y.ndim!=1):
|
|
405
|
+
raise ValueError
|
|
406
|
+
if (u.ndim!=1):
|
|
407
|
+
raise ValueError
|
|
408
|
+
if (u.shape[0]!=y.shape[0]):
|
|
409
|
+
raise ValueError
|
|
410
|
+
if not isinstance(y_edge, (float,np.floating)):
|
|
411
|
+
raise ValueError
|
|
412
|
+
if (u_edge is not None):
|
|
413
|
+
if not isinstance(u_edge, (float,np.floating)):
|
|
414
|
+
raise ValueError
|
|
415
|
+
|
|
416
|
+
if (y_edge>y.max()):
|
|
417
|
+
raise ValueError
|
|
418
|
+
if (y_edge<y.min()):
|
|
419
|
+
raise ValueError
|
|
420
|
+
|
|
421
|
+
ny = y.shape[0]
|
|
422
|
+
|
|
423
|
+
j_edge = np.abs(y-y_edge).argmin()
|
|
424
|
+
if ( y[j_edge] < y_edge ):
|
|
425
|
+
j_edge += 1
|
|
426
|
+
je = min(j_edge,ny)
|
|
427
|
+
je += 1 ## for numpy [:n]
|
|
428
|
+
|
|
429
|
+
intrp_func = interp1d(x=y[:je], y=u[:je], kind=interp_kind, bounds_error=True)
|
|
430
|
+
|
|
431
|
+
if (u_edge is not None): ## assert passed u_edge is == f(y_edge)
|
|
432
|
+
np.testing.assert_allclose(u_edge, intrp_func(y_edge), rtol=rtol)
|
|
433
|
+
else:
|
|
434
|
+
u_edge = intrp_func(y_edge)
|
|
435
|
+
|
|
436
|
+
def __f_opt_d99_locator(y_test, intrp_func, u_edge):
|
|
437
|
+
root = np.abs( 0.99*u_edge - intrp_func(y_test) )
|
|
438
|
+
return root
|
|
439
|
+
|
|
440
|
+
def __f_opt_d95_locator(y_test, intrp_func, u_edge):
|
|
441
|
+
root = np.abs( 0.95*u_edge - intrp_func(y_test) )
|
|
442
|
+
return root
|
|
443
|
+
|
|
444
|
+
if d95:
|
|
445
|
+
__f_opt = __f_opt_d95_locator
|
|
446
|
+
else:
|
|
447
|
+
__f_opt = __f_opt_d99_locator
|
|
448
|
+
|
|
449
|
+
sol = sp.optimize.least_squares(
|
|
450
|
+
fun=__f_opt,
|
|
451
|
+
args=(intrp_func,u_edge),
|
|
452
|
+
x0=0.1*y_edge,
|
|
453
|
+
xtol=1e-15,
|
|
454
|
+
ftol=1e-15,
|
|
455
|
+
gtol=1e-15,
|
|
456
|
+
method='dogbox',
|
|
457
|
+
bounds=(y.min(), y_edge),
|
|
458
|
+
)
|
|
459
|
+
if not sol.success:
|
|
460
|
+
raise ValueError
|
|
461
|
+
if ( sol.x.shape[0] != 1 ):
|
|
462
|
+
raise ValueError
|
|
463
|
+
|
|
464
|
+
delta = float(sol.x[0]) ## δ99 or δ95
|
|
465
|
+
|
|
466
|
+
if ( delta > y_edge ):
|
|
467
|
+
raise ValueError
|
|
468
|
+
|
|
469
|
+
return delta
|
|
470
|
+
|
|
471
|
+
def calc_bl_integral_quantities_1d(
|
|
472
|
+
y,
|
|
473
|
+
u,
|
|
474
|
+
rho,
|
|
475
|
+
u_tau,
|
|
476
|
+
d99,
|
|
477
|
+
y_edge,
|
|
478
|
+
rho_edge,
|
|
479
|
+
nu_edge,
|
|
480
|
+
u_edge,
|
|
481
|
+
rho_wall,
|
|
482
|
+
nu_wall,
|
|
483
|
+
**kwargs,
|
|
484
|
+
):
|
|
485
|
+
'''
|
|
486
|
+
For 1D profile, get [δ1=δ*, δ2=θ, Reθ, Reτ] etc.
|
|
487
|
+
'''
|
|
488
|
+
|
|
489
|
+
rtol = kwargs.get('rtol',1e-6)
|
|
490
|
+
interp_kind = kwargs.get('interp_kind','cubic')
|
|
491
|
+
|
|
492
|
+
## Checks
|
|
493
|
+
if y.ndim != 1:
|
|
494
|
+
raise ValueError
|
|
495
|
+
if u.ndim != 1 or u.shape[0] != y.shape[0]:
|
|
496
|
+
raise ValueError
|
|
497
|
+
if rho.ndim != 1 or rho.shape[0] != y.shape[0]:
|
|
498
|
+
raise ValueError
|
|
499
|
+
|
|
500
|
+
for val in (u_tau, d99, y_edge, rho_edge, nu_edge, u_edge, rho_wall, nu_wall):
|
|
501
|
+
if not isinstance(val, (float, np.floating)):
|
|
502
|
+
raise ValueError
|
|
503
|
+
|
|
504
|
+
if (y_edge > y.max()) or (y_edge < y.min()):
|
|
505
|
+
raise ValueError
|
|
506
|
+
|
|
507
|
+
## Consistency checks for edge quantities
|
|
508
|
+
rho_edge_ = interp1d(y, rho, kind=interp_kind, bounds_error=True)(y_edge)
|
|
509
|
+
np.testing.assert_allclose(rho_edge, rho_edge_, rtol=rtol)
|
|
510
|
+
|
|
511
|
+
u_edge_ = interp1d(y, u, kind=interp_kind, bounds_error=True)(y_edge)
|
|
512
|
+
np.testing.assert_allclose(u_edge, u_edge_, rtol=rtol)
|
|
513
|
+
|
|
514
|
+
# === Compressible integrals (root names)
|
|
515
|
+
|
|
516
|
+
d1 = calc_d1(
|
|
517
|
+
y, u,
|
|
518
|
+
rho=rho,
|
|
519
|
+
y_edge=y_edge,
|
|
520
|
+
interp_kind=interp_kind,
|
|
521
|
+
)
|
|
522
|
+
dstar = d1
|
|
523
|
+
|
|
524
|
+
d2 = calc_d2(
|
|
525
|
+
y, u,
|
|
526
|
+
rho=rho,
|
|
527
|
+
y_edge=y_edge,
|
|
528
|
+
interp_kind=interp_kind,
|
|
529
|
+
)
|
|
530
|
+
theta = d2
|
|
531
|
+
|
|
532
|
+
d3 = calc_d3(
|
|
533
|
+
y, u,
|
|
534
|
+
rho=rho,
|
|
535
|
+
y_edge=y_edge,
|
|
536
|
+
interp_kind=interp_kind,
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
dRC = calc_dRC(
|
|
540
|
+
y, u,
|
|
541
|
+
u_tau=u_tau,
|
|
542
|
+
rho=rho,
|
|
543
|
+
rho_wall=rho_wall,
|
|
544
|
+
y_edge=y_edge,
|
|
545
|
+
interp_kind=interp_kind,
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
# === Kinetic (u-only) integrals
|
|
549
|
+
|
|
550
|
+
d1_k = calc_d1(
|
|
551
|
+
y, u,
|
|
552
|
+
rho=None,
|
|
553
|
+
y_edge=y_edge,
|
|
554
|
+
interp_kind=interp_kind,
|
|
555
|
+
)
|
|
556
|
+
dstar_k = d1_k
|
|
557
|
+
|
|
558
|
+
d2_k = calc_d2(
|
|
559
|
+
y, u,
|
|
560
|
+
rho=None,
|
|
561
|
+
y_edge=y_edge,
|
|
562
|
+
interp_kind=interp_kind,
|
|
563
|
+
)
|
|
564
|
+
theta_k = d2_k
|
|
565
|
+
|
|
566
|
+
d3_k = calc_d3(
|
|
567
|
+
y, u,
|
|
568
|
+
rho=None,
|
|
569
|
+
y_edge=y_edge,
|
|
570
|
+
interp_kind=interp_kind,
|
|
571
|
+
)
|
|
572
|
+
|
|
573
|
+
dRC_k = calc_dRC(
|
|
574
|
+
y, u,
|
|
575
|
+
u_tau=u_tau,
|
|
576
|
+
rho=None,
|
|
577
|
+
rho_wall=None,
|
|
578
|
+
y_edge=y_edge,
|
|
579
|
+
interp_kind=interp_kind,
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
## Shape factors
|
|
583
|
+
H12 = d1 / d2
|
|
584
|
+
H12_k = d1_k / d2_k
|
|
585
|
+
H32 = d3 / d2
|
|
586
|
+
H32_k = d3_k / d2_k
|
|
587
|
+
|
|
588
|
+
## Reynolds numbers
|
|
589
|
+
Re_tau = d99 * u_tau / nu_wall
|
|
590
|
+
Re_theta = theta * u_edge / nu_edge
|
|
591
|
+
Re_d99 = d99 * u_edge / nu_edge
|
|
592
|
+
|
|
593
|
+
dd = {
|
|
594
|
+
'd1': d1,
|
|
595
|
+
'd1_k': d1_k,
|
|
596
|
+
'dstar': dstar,
|
|
597
|
+
'dstar_k': dstar_k,
|
|
598
|
+
|
|
599
|
+
'd2': d2,
|
|
600
|
+
'd2_k': d2_k,
|
|
601
|
+
'theta': theta,
|
|
602
|
+
'theta_k': theta_k,
|
|
603
|
+
|
|
604
|
+
'd3': d3,
|
|
605
|
+
'd3_k': d3_k,
|
|
606
|
+
|
|
607
|
+
'dRC': dRC,
|
|
608
|
+
'dRC_k': dRC_k,
|
|
609
|
+
|
|
610
|
+
'H12': H12,
|
|
611
|
+
'H12_k': H12_k,
|
|
612
|
+
'H32': H32,
|
|
613
|
+
'H32_k': H32_k,
|
|
614
|
+
|
|
615
|
+
'Re_tau': Re_tau,
|
|
616
|
+
'Re_theta': Re_theta,
|
|
617
|
+
'Re_d99': Re_d99,
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
return dd
|