microstructure-tpw 0.3.0__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.
- microstructure_tpw-0.3.0.dist-info/METADATA +234 -0
- microstructure_tpw-0.3.0.dist-info/RECORD +119 -0
- microstructure_tpw-0.3.0.dist-info/WHEEL +5 -0
- microstructure_tpw-0.3.0.dist-info/entry_points.txt +7 -0
- microstructure_tpw-0.3.0.dist-info/licenses/LICENSE +674 -0
- microstructure_tpw-0.3.0.dist-info/top_level.txt +1 -0
- odas_tpw/__init__.py +18 -0
- odas_tpw/chi/__init__.py +48 -0
- odas_tpw/chi/batchelor.py +189 -0
- odas_tpw/chi/chi.py +857 -0
- odas_tpw/chi/fp07.py +649 -0
- odas_tpw/chi/l2_chi.py +153 -0
- odas_tpw/chi/l3_chi.py +388 -0
- odas_tpw/chi/l4_chi.py +391 -0
- odas_tpw/config_base.py +423 -0
- odas_tpw/perturb/__init__.py +18 -0
- odas_tpw/perturb/adcp.py +274 -0
- odas_tpw/perturb/atomic_io.py +47 -0
- odas_tpw/perturb/binning.py +761 -0
- odas_tpw/perturb/cli.py +591 -0
- odas_tpw/perturb/combo.py +346 -0
- odas_tpw/perturb/config.py +1046 -0
- odas_tpw/perturb/ctd.py +424 -0
- odas_tpw/perturb/diag/__init__.py +12 -0
- odas_tpw/perturb/diag/_common.py +140 -0
- odas_tpw/perturb/diag/chi.py +42 -0
- odas_tpw/perturb/diag/cli.py +67 -0
- odas_tpw/perturb/diag/data.py +662 -0
- odas_tpw/perturb/diag/epsilon.py +45 -0
- odas_tpw/perturb/diag/inspector.py +342 -0
- odas_tpw/perturb/diag/mixing.py +94 -0
- odas_tpw/perturb/diag/render.py +543 -0
- odas_tpw/perturb/discover.py +40 -0
- odas_tpw/perturb/fp07_cal.py +354 -0
- odas_tpw/perturb/gps.py +458 -0
- odas_tpw/perturb/hotel.py +572 -0
- odas_tpw/perturb/logging_setup.py +181 -0
- odas_tpw/perturb/merge.py +320 -0
- odas_tpw/perturb/mk_sections.py +310 -0
- odas_tpw/perturb/netcdf_schema.py +520 -0
- odas_tpw/perturb/pipeline.py +2654 -0
- odas_tpw/perturb/plot/__init__.py +15 -0
- odas_tpw/perturb/plot/cli.py +107 -0
- odas_tpw/perturb/plot/diagnostics.py +195 -0
- odas_tpw/perturb/plot/eps_chi.py +524 -0
- odas_tpw/perturb/plot/figure.py +679 -0
- odas_tpw/perturb/plot/gamma_scaling.py +1097 -0
- odas_tpw/perturb/plot/grid.py +112 -0
- odas_tpw/perturb/plot/layout.py +361 -0
- odas_tpw/perturb/plot/overview.py +547 -0
- odas_tpw/perturb/plot/presets/eps-chi.yaml +16 -0
- odas_tpw/perturb/plot/presets/profiles.yaml +32 -0
- odas_tpw/perturb/plot/presets/scalar.yaml +21 -0
- odas_tpw/perturb/plot/profiles.py +652 -0
- odas_tpw/perturb/plot/scalar.py +391 -0
- odas_tpw/perturb/plot/sections.py +506 -0
- odas_tpw/perturb/plot/xaxis.py +396 -0
- odas_tpw/perturb/qc_gate.py +177 -0
- odas_tpw/perturb/qc_rules.py +373 -0
- odas_tpw/perturb/resolve.py +306 -0
- odas_tpw/perturb/seawater.py +76 -0
- odas_tpw/perturb/speed.py +14 -0
- odas_tpw/perturb/trim.py +313 -0
- odas_tpw/processing/__init__.py +62 -0
- odas_tpw/processing/bottom.py +215 -0
- odas_tpw/processing/chi_combine.py +300 -0
- odas_tpw/processing/ct_align.py +172 -0
- odas_tpw/processing/epsilon_combine.py +210 -0
- odas_tpw/processing/mixing.py +630 -0
- odas_tpw/processing/thorpe.py +458 -0
- odas_tpw/processing/top_trim.py +274 -0
- odas_tpw/pyturb/__init__.py +3 -0
- odas_tpw/pyturb/__main__.py +5 -0
- odas_tpw/pyturb/_compat.py +204 -0
- odas_tpw/pyturb/_profind.py +124 -0
- odas_tpw/pyturb/bin.py +113 -0
- odas_tpw/pyturb/cli.py +175 -0
- odas_tpw/pyturb/eps.py +353 -0
- odas_tpw/pyturb/merge.py +101 -0
- odas_tpw/pyturb/p2nc.py +76 -0
- odas_tpw/rsi/__init__.py +31 -0
- odas_tpw/rsi/adapter.py +314 -0
- odas_tpw/rsi/bench.py +904 -0
- odas_tpw/rsi/binning.py +193 -0
- odas_tpw/rsi/channels.py +376 -0
- odas_tpw/rsi/chi_io.py +980 -0
- odas_tpw/rsi/cli.py +1411 -0
- odas_tpw/rsi/combine.py +120 -0
- odas_tpw/rsi/config.py +126 -0
- odas_tpw/rsi/config_patch.py +790 -0
- odas_tpw/rsi/convert.py +651 -0
- odas_tpw/rsi/deconvolve.py +119 -0
- odas_tpw/rsi/diss_look.py +343 -0
- odas_tpw/rsi/dissipation.py +669 -0
- odas_tpw/rsi/helpers.py +527 -0
- odas_tpw/rsi/mixing_look.py +509 -0
- odas_tpw/rsi/p_file.py +771 -0
- odas_tpw/rsi/pipeline.py +924 -0
- odas_tpw/rsi/profile.py +624 -0
- odas_tpw/rsi/quick_look.py +758 -0
- odas_tpw/rsi/sensor_inventory.py +684 -0
- odas_tpw/rsi/shear_noise.py +128 -0
- odas_tpw/rsi/speed.py +221 -0
- odas_tpw/rsi/vehicle.py +74 -0
- odas_tpw/rsi/viewer_base.py +1071 -0
- odas_tpw/rsi/window.py +459 -0
- odas_tpw/scor160/__init__.py +36 -0
- odas_tpw/scor160/cli.py +242 -0
- odas_tpw/scor160/compare.py +484 -0
- odas_tpw/scor160/despike.py +227 -0
- odas_tpw/scor160/goodman.py +288 -0
- odas_tpw/scor160/io.py +690 -0
- odas_tpw/scor160/l2.py +311 -0
- odas_tpw/scor160/l3.py +279 -0
- odas_tpw/scor160/l4.py +850 -0
- odas_tpw/scor160/nasmyth.py +208 -0
- odas_tpw/scor160/ocean.py +287 -0
- odas_tpw/scor160/profile.py +231 -0
- odas_tpw/scor160/spectral.py +482 -0
|
@@ -0,0 +1,758 @@
|
|
|
1
|
+
# Mar-2026, Claude and Pat Welch, pat@mousebrains.com
|
|
2
|
+
"""Interactive quick-look viewer for Rockland Scientific profiler data.
|
|
3
|
+
|
|
4
|
+
Port of quick_look.m from the ODAS MATLAB library.
|
|
5
|
+
Provides a multi-panel figure with Prev/Next buttons to step through profiles.
|
|
6
|
+
|
|
7
|
+
Layout (2 rows x 4 columns):
|
|
8
|
+
Row 0 (profile panels, pressure y-axis linked):
|
|
9
|
+
Overview | epsilon profile | chi profile | Shear probes
|
|
10
|
+
Row 1 (spectra + detail):
|
|
11
|
+
T1, T2, JAC_T | epsilon spectra | chi spectra | Fall rate
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import warnings
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
17
|
+
|
|
18
|
+
from odas_tpw.rsi.p_file import PFile
|
|
19
|
+
from odas_tpw.rsi.viewer_base import (
|
|
20
|
+
ProfileViewer,
|
|
21
|
+
_interp_slow_to_fast,
|
|
22
|
+
compute_depth_spectra,
|
|
23
|
+
select_mid_window,
|
|
24
|
+
)
|
|
25
|
+
from odas_tpw.rsi.window import compute_chi_window, compute_eps_window
|
|
26
|
+
from odas_tpw.scor160.nasmyth import nasmyth
|
|
27
|
+
from odas_tpw.scor160.ocean import visc35
|
|
28
|
+
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
# ql-specific free functions
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _compute_chi_spectra(
|
|
35
|
+
therm_data,
|
|
36
|
+
diff_gains,
|
|
37
|
+
P_fast,
|
|
38
|
+
T_slow,
|
|
39
|
+
speed_fast,
|
|
40
|
+
fs_fast,
|
|
41
|
+
sel,
|
|
42
|
+
fft_length,
|
|
43
|
+
f_AA,
|
|
44
|
+
epsilons,
|
|
45
|
+
foms=None,
|
|
46
|
+
spectrum_model="kraichnan",
|
|
47
|
+
):
|
|
48
|
+
"""Compute temperature gradient spectra and all three chi methods for one profile segment.
|
|
49
|
+
|
|
50
|
+
Uses shared ``compute_chi_window`` from ``window.py`` for Method 1 (chi
|
|
51
|
+
from epsilon) and Method 2 Iterative (Peterson & Fer 2014). Method 2 MLE
|
|
52
|
+
uses ``_mle_fit_kB`` directly with the correct gradient spectrum from the
|
|
53
|
+
shared function.
|
|
54
|
+
|
|
55
|
+
Selects a single diss_length window closest to the midpoint pressure,
|
|
56
|
+
matching diss_look and MATLAB tpw_plot_spectra.
|
|
57
|
+
|
|
58
|
+
Returns (K, F, obs_spectra, methods_results, noise_K, mean_speed, nu).
|
|
59
|
+
|
|
60
|
+
methods_results is a dict keyed by method name ("M1", "M2-MLE", "M2-Iter"),
|
|
61
|
+
each mapping to a list of per-probe (batch_spec, chi_val, kB_val) tuples.
|
|
62
|
+
"""
|
|
63
|
+
from odas_tpw.chi.chi import _mle_fit_kB
|
|
64
|
+
from odas_tpw.chi.fp07 import fp07_tau, fp07_transfer
|
|
65
|
+
from odas_tpw.chi.fp07 import gradT_noise as _gradT_noise
|
|
66
|
+
from odas_tpw.scor160.ocean import kappa_T as _kappa_T_TSP
|
|
67
|
+
|
|
68
|
+
w_sel = select_mid_window(P_fast, sel, fft_length, diss_length=None)
|
|
69
|
+
|
|
70
|
+
# Per-window mean temperature (was the whole-cast mean -> ~14% visc bias).
|
|
71
|
+
mean_T = float(np.mean(_interp_slow_to_fast(T_slow, len(P_fast))[w_sel]))
|
|
72
|
+
# T-dependent thermal diffusivity (S=35/P=0 reference for this diagnostic).
|
|
73
|
+
kappa_T_ql = float(_kappa_T_TSP(mean_T))
|
|
74
|
+
mean_speed = float(np.mean(np.abs(speed_fast[w_sel])))
|
|
75
|
+
if mean_speed < 0.01:
|
|
76
|
+
mean_speed = 0.01
|
|
77
|
+
nu = float(visc35(mean_T))
|
|
78
|
+
|
|
79
|
+
n_therm = len(therm_data)
|
|
80
|
+
n_freq = fft_length // 2 + 1
|
|
81
|
+
f_AA_chi = 0.9 * f_AA # 10% margin, matching MATLAB get_chi
|
|
82
|
+
|
|
83
|
+
therm_segs = [therm_data[ci][1][w_sel] for ci in range(n_therm)]
|
|
84
|
+
eps_arr = np.array(epsilons) if epsilons else None
|
|
85
|
+
fom_arr = np.array(foms) if foms else None
|
|
86
|
+
|
|
87
|
+
# Method 1: chi from epsilon (FOM-filtered mean of shear probes)
|
|
88
|
+
cr_m1 = compute_chi_window(
|
|
89
|
+
therm_segs,
|
|
90
|
+
diff_gains,
|
|
91
|
+
mean_speed,
|
|
92
|
+
mean_T,
|
|
93
|
+
nu,
|
|
94
|
+
fs_fast,
|
|
95
|
+
fft_length,
|
|
96
|
+
f_AA_chi,
|
|
97
|
+
spectrum_model=spectrum_model,
|
|
98
|
+
epsilon=eps_arr,
|
|
99
|
+
fom=fom_arr,
|
|
100
|
+
method=1,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Method 2 Iterative
|
|
104
|
+
cr_m2_iter = compute_chi_window(
|
|
105
|
+
therm_segs,
|
|
106
|
+
diff_gains,
|
|
107
|
+
mean_speed,
|
|
108
|
+
mean_T,
|
|
109
|
+
nu,
|
|
110
|
+
fs_fast,
|
|
111
|
+
fft_length,
|
|
112
|
+
f_AA_chi,
|
|
113
|
+
spectrum_model=spectrum_model,
|
|
114
|
+
method=2,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
K = cr_m1.K
|
|
118
|
+
F = cr_m1.F
|
|
119
|
+
H2 = cr_m1.H2
|
|
120
|
+
obs_spectra = cr_m1.grad_specs
|
|
121
|
+
noise_K = cr_m1.noise_K
|
|
122
|
+
K_AA_chi = f_AA_chi / mean_speed
|
|
123
|
+
|
|
124
|
+
nan_result = (np.full(n_freq, np.nan), np.nan, np.nan)
|
|
125
|
+
methods_results: dict[str, list] = {"M1": [], "M2-MLE": [], "M2-Iter": []}
|
|
126
|
+
|
|
127
|
+
for ci in range(n_therm):
|
|
128
|
+
# M1
|
|
129
|
+
if np.isfinite(cr_m1.chi[ci]) and cr_m1.chi[ci] > 0:
|
|
130
|
+
methods_results["M1"].append(
|
|
131
|
+
(cr_m1.model_specs[ci], float(cr_m1.chi[ci]), float(cr_m1.kB[ci]))
|
|
132
|
+
)
|
|
133
|
+
else:
|
|
134
|
+
methods_results["M1"].append(nan_result)
|
|
135
|
+
|
|
136
|
+
# M2-Iter
|
|
137
|
+
if np.isfinite(cr_m2_iter.chi[ci]) and cr_m2_iter.chi[ci] > 0:
|
|
138
|
+
methods_results["M2-Iter"].append(
|
|
139
|
+
(cr_m2_iter.model_specs[ci], float(cr_m2_iter.chi[ci]), float(cr_m2_iter.kB[ci]))
|
|
140
|
+
)
|
|
141
|
+
else:
|
|
142
|
+
methods_results["M2-Iter"].append(nan_result)
|
|
143
|
+
|
|
144
|
+
# M2-MLE: call _mle_fit_kB directly with gradient spectrum from shared code
|
|
145
|
+
grad = obs_spectra[ci] if ci < len(obs_spectra) else np.full(n_freq, np.nan)
|
|
146
|
+
dg_i = diff_gains[ci] if ci < len(diff_gains) else 0.94
|
|
147
|
+
valid = np.isfinite(grad) & (grad > 0) & (K > 0) & (K_AA_chi >= K)
|
|
148
|
+
if np.sum(valid) >= 3:
|
|
149
|
+
chi_obs = max(6 * kappa_T_ql * np.trapezoid(grad[valid], K[valid]), 1e-10)
|
|
150
|
+
# Pre-compute noise/H2 for _mle_fit_kB
|
|
151
|
+
_tau0 = float(fp07_tau(mean_speed))
|
|
152
|
+
_noise_K_i, _ = _gradT_noise(F, mean_T, mean_speed, fs=fs_fast, diff_gain=dg_i)
|
|
153
|
+
kB_best, chi_val, _, _, spec_raw, _, _, _ = _mle_fit_kB(
|
|
154
|
+
grad,
|
|
155
|
+
K,
|
|
156
|
+
chi_obs,
|
|
157
|
+
nu,
|
|
158
|
+
_noise_K_i,
|
|
159
|
+
H2,
|
|
160
|
+
_tau0,
|
|
161
|
+
fp07_transfer,
|
|
162
|
+
f_AA_chi,
|
|
163
|
+
mean_speed,
|
|
164
|
+
spectrum_model,
|
|
165
|
+
kappa_T_ql,
|
|
166
|
+
)
|
|
167
|
+
spec_mle = spec_raw * H2 if np.isfinite(chi_val) else np.full(n_freq, np.nan)
|
|
168
|
+
methods_results["M2-MLE"].append((spec_mle, chi_val, kB_best))
|
|
169
|
+
else:
|
|
170
|
+
methods_results["M2-MLE"].append(nan_result)
|
|
171
|
+
|
|
172
|
+
return K, F, obs_spectra, methods_results, noise_K, mean_speed, nu
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _compute_windowed_eps_chi(
|
|
176
|
+
shear_data,
|
|
177
|
+
accel_data,
|
|
178
|
+
therm_data,
|
|
179
|
+
diff_gains,
|
|
180
|
+
P_fast,
|
|
181
|
+
T_slow,
|
|
182
|
+
speed_fast,
|
|
183
|
+
fs_fast,
|
|
184
|
+
sel,
|
|
185
|
+
fft_length,
|
|
186
|
+
f_AA,
|
|
187
|
+
do_goodman,
|
|
188
|
+
chi_method=1,
|
|
189
|
+
spectrum_model="kraichnan",
|
|
190
|
+
diss_length=None,
|
|
191
|
+
):
|
|
192
|
+
"""Compute windowed epsilon and chi estimates for a profile segment.
|
|
193
|
+
|
|
194
|
+
Returns (P_windows, eps_array, chi_array).
|
|
195
|
+
eps_array shape: (n_shear, n_windows)
|
|
196
|
+
chi_array shape: (n_therm, n_windows)
|
|
197
|
+
|
|
198
|
+
Parameters
|
|
199
|
+
----------
|
|
200
|
+
chi_method : int
|
|
201
|
+
1 = chi from known epsilon (Method 1), 2 = iterative fit (Method 2).
|
|
202
|
+
spectrum_model : str
|
|
203
|
+
Theoretical spectrum: 'batchelor' or 'kraichnan'.
|
|
204
|
+
diss_length : int or None
|
|
205
|
+
Dissipation window length. Default: 4 * fft_length.
|
|
206
|
+
"""
|
|
207
|
+
if diss_length is None:
|
|
208
|
+
diss_length = 4 * fft_length
|
|
209
|
+
overlap = diss_length // 2
|
|
210
|
+
step = diss_length - overlap
|
|
211
|
+
|
|
212
|
+
seg_start = sel.start
|
|
213
|
+
seg_end = sel.stop
|
|
214
|
+
N = seg_end - seg_start
|
|
215
|
+
|
|
216
|
+
n_shear = len(shear_data)
|
|
217
|
+
n_therm = len(therm_data)
|
|
218
|
+
n_windows = max(0, 1 + (N - diss_length) // step)
|
|
219
|
+
if n_windows == 0:
|
|
220
|
+
return np.array([]), np.empty((n_shear, 0)), np.empty((n_therm, 0))
|
|
221
|
+
|
|
222
|
+
P_windows = np.full(n_windows, np.nan)
|
|
223
|
+
eps_arr = np.full((n_shear, n_windows), np.nan)
|
|
224
|
+
chi_arr = np.full((n_therm, n_windows), np.nan)
|
|
225
|
+
|
|
226
|
+
# Full-length fast temperature: the window loop slices T_fast with ABSOLUTE
|
|
227
|
+
# fast indices (s = seg_start + idx*step), exactly as it slices the
|
|
228
|
+
# full-length P_fast/speed_fast/shear arrays. Sizing T_fast to the segment
|
|
229
|
+
# length N instead made every window past index N empty -> mean_T NaN ->
|
|
230
|
+
# all-NaN eps/chi for every profile after the first, and compressed the
|
|
231
|
+
# whole-record temperature onto profile 0's segment (biased viscosity).
|
|
232
|
+
# Regression from PR #75 M-7; matches viewer_base._interp_slow_to_fast use
|
|
233
|
+
# (audit r1-13).
|
|
234
|
+
T_fast = _interp_slow_to_fast(T_slow, len(P_fast))
|
|
235
|
+
f_AA_chi = 0.9 * f_AA # 10% margin, matching MATLAB get_chi
|
|
236
|
+
|
|
237
|
+
for idx in range(n_windows):
|
|
238
|
+
s = seg_start + idx * step
|
|
239
|
+
e = s + diss_length
|
|
240
|
+
if e > seg_end:
|
|
241
|
+
break
|
|
242
|
+
w_sel = slice(s, e)
|
|
243
|
+
# Per-window mean temperature (was the whole-cast mean -> ~14% bias).
|
|
244
|
+
mean_T = float(np.mean(T_fast[w_sel]))
|
|
245
|
+
P_windows[idx] = float(np.mean(P_fast[w_sel]))
|
|
246
|
+
|
|
247
|
+
# --- Epsilon ---
|
|
248
|
+
er = None
|
|
249
|
+
if n_shear > 0:
|
|
250
|
+
sh_seg = np.column_stack([d[w_sel] for _, d in shear_data])
|
|
251
|
+
ac_seg = np.column_stack([d[w_sel] for _, d in accel_data]) if accel_data else None
|
|
252
|
+
er = compute_eps_window(
|
|
253
|
+
sh_seg,
|
|
254
|
+
ac_seg,
|
|
255
|
+
speed_fast[w_sel],
|
|
256
|
+
P_fast[w_sel],
|
|
257
|
+
mean_T,
|
|
258
|
+
fs_fast,
|
|
259
|
+
fft_length,
|
|
260
|
+
f_AA,
|
|
261
|
+
do_goodman,
|
|
262
|
+
)
|
|
263
|
+
eps_arr[:, idx] = er.epsilon
|
|
264
|
+
|
|
265
|
+
# --- Chi ---
|
|
266
|
+
if n_therm > 0 and er is not None:
|
|
267
|
+
therm_segs = [therm_data[ci][1][w_sel] for ci in range(n_therm)]
|
|
268
|
+
method = 1 if chi_method == 1 else 2
|
|
269
|
+
eps_for_chi = er.epsilon if chi_method == 1 else None
|
|
270
|
+
cr = compute_chi_window(
|
|
271
|
+
therm_segs,
|
|
272
|
+
diff_gains,
|
|
273
|
+
er.W,
|
|
274
|
+
mean_T,
|
|
275
|
+
er.nu,
|
|
276
|
+
fs_fast,
|
|
277
|
+
fft_length,
|
|
278
|
+
f_AA_chi,
|
|
279
|
+
spectrum_model=spectrum_model,
|
|
280
|
+
epsilon=eps_for_chi,
|
|
281
|
+
method=method,
|
|
282
|
+
)
|
|
283
|
+
chi_arr[:, idx] = cr.chi
|
|
284
|
+
|
|
285
|
+
return P_windows, eps_arr, chi_arr
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
# ---------------------------------------------------------------------------
|
|
289
|
+
# QuickLookViewer
|
|
290
|
+
# ---------------------------------------------------------------------------
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class QuickLookViewer(ProfileViewer):
|
|
294
|
+
"""Interactive multi-panel profile viewer with Prev/Next navigation."""
|
|
295
|
+
|
|
296
|
+
def __init__(self, pf, chi_method=1, spectrum_model="kraichnan", **kwargs):
|
|
297
|
+
super().__init__(pf, **kwargs)
|
|
298
|
+
self.chi_method = chi_method
|
|
299
|
+
self.spectrum_model = spectrum_model
|
|
300
|
+
|
|
301
|
+
def _setup_axes(self):
|
|
302
|
+
# Link pressure y-axes: row 0 all cols + row 1 cols 0, 3
|
|
303
|
+
p_ref = self.axes[0, 0]
|
|
304
|
+
for col in range(1, 4):
|
|
305
|
+
self.axes[0, col].sharey(p_ref)
|
|
306
|
+
self.axes[1, 0].sharey(p_ref)
|
|
307
|
+
self.axes[1, 3].sharey(p_ref)
|
|
308
|
+
p_ref.invert_yaxis()
|
|
309
|
+
|
|
310
|
+
# Link wavenumber x-axes: epsilon spectra (1,1) and chi spectra (1,2)
|
|
311
|
+
self.axes[1, 2].sharex(self.axes[1, 1])
|
|
312
|
+
|
|
313
|
+
def _draw(self):
|
|
314
|
+
"""Draw all panels for the current profile."""
|
|
315
|
+
s_slow, e_slow = self.profiles[self.profile_idx]
|
|
316
|
+
sel_fast = self._slow_to_fast_slice(s_slow, e_slow)
|
|
317
|
+
self._spec_bin_width = self._spec_bin_dbar(s_slow, e_slow)
|
|
318
|
+
sel_spec = self._spec_fast_slice(sel_fast)
|
|
319
|
+
|
|
320
|
+
with warnings.catch_warnings():
|
|
321
|
+
warnings.simplefilter("ignore", UserWarning)
|
|
322
|
+
for ax in self.axes.flat:
|
|
323
|
+
ax.clear()
|
|
324
|
+
# Re-invert pressure y-axes (clear resets inversion)
|
|
325
|
+
self.axes[0, 0].invert_yaxis()
|
|
326
|
+
|
|
327
|
+
# Pre-compute depth spectra (dict format from viewer_base)
|
|
328
|
+
n_spec = sel_spec.stop - sel_spec.start
|
|
329
|
+
self._cached_spec = None
|
|
330
|
+
if self.shear and n_spec >= self.diss_length:
|
|
331
|
+
self._cached_spec = compute_depth_spectra(
|
|
332
|
+
self.shear,
|
|
333
|
+
self.accel,
|
|
334
|
+
self.therm_fast,
|
|
335
|
+
self.diff_gains,
|
|
336
|
+
self.P_fast,
|
|
337
|
+
self.T,
|
|
338
|
+
self.speed_fast,
|
|
339
|
+
self.fs_fast,
|
|
340
|
+
sel_spec,
|
|
341
|
+
self.fft_length,
|
|
342
|
+
self.f_AA,
|
|
343
|
+
self.goodman,
|
|
344
|
+
diss_length=self.diss_length,
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
# Pre-compute windowed eps/chi for profile panels
|
|
348
|
+
n_fast = sel_fast.stop - sel_fast.start
|
|
349
|
+
self._cached_windowed = None
|
|
350
|
+
if n_fast >= self.diss_length:
|
|
351
|
+
P_win, eps_arr, chi_arr = _compute_windowed_eps_chi(
|
|
352
|
+
self.shear,
|
|
353
|
+
self.accel,
|
|
354
|
+
self.therm_fast,
|
|
355
|
+
self.diff_gains,
|
|
356
|
+
self.P_fast,
|
|
357
|
+
self.T,
|
|
358
|
+
self.speed_fast,
|
|
359
|
+
self.fs_fast,
|
|
360
|
+
sel_fast,
|
|
361
|
+
self.fft_length,
|
|
362
|
+
self.f_AA,
|
|
363
|
+
self.goodman,
|
|
364
|
+
chi_method=self.chi_method,
|
|
365
|
+
spectrum_model=self.spectrum_model,
|
|
366
|
+
diss_length=self.diss_length,
|
|
367
|
+
)
|
|
368
|
+
if len(P_win) > 0:
|
|
369
|
+
self._cached_windowed = (P_win, eps_arr, chi_arr)
|
|
370
|
+
|
|
371
|
+
# Row 0: profile panels
|
|
372
|
+
self._draw_overview(self.axes[0, 0], s_slow, e_slow)
|
|
373
|
+
P_win = self._cached_windowed[0] if self._cached_windowed else np.array([])
|
|
374
|
+
eps_arr = (
|
|
375
|
+
self._cached_windowed[1] if self._cached_windowed else np.empty((len(self.shear), 0))
|
|
376
|
+
)
|
|
377
|
+
self._draw_eps_profile(self.axes[0, 1], P_win, eps_arr)
|
|
378
|
+
self._draw_chi_profile()
|
|
379
|
+
self._draw_shear(self.axes[0, 3], sel_fast)
|
|
380
|
+
|
|
381
|
+
# Row 1: spectra and detail panels
|
|
382
|
+
self._draw_temps(self.axes[1, 0], s_slow, e_slow)
|
|
383
|
+
self._draw_spectra(sel_spec)
|
|
384
|
+
self._draw_chi_spectra(sel_spec)
|
|
385
|
+
self._draw_fall_rate(self.axes[1, 3], s_slow, e_slow)
|
|
386
|
+
|
|
387
|
+
# Finish (green band + title)
|
|
388
|
+
pressure_axes = [self.axes[0, c] for c in range(4)] + [self.axes[1, 0], self.axes[1, 3]]
|
|
389
|
+
self._finish_draw(s_slow, e_slow, pressure_axes)
|
|
390
|
+
|
|
391
|
+
# ------------------------------------------------------------------
|
|
392
|
+
# ql-specific panels
|
|
393
|
+
# ------------------------------------------------------------------
|
|
394
|
+
|
|
395
|
+
def _draw_chi_profile(self):
|
|
396
|
+
"""Panel (0,2): Chi from each thermistor vs pressure."""
|
|
397
|
+
ax = self.axes[0, 2]
|
|
398
|
+
|
|
399
|
+
if self._cached_windowed is None:
|
|
400
|
+
ax.text(0.5, 0.5, "No data", transform=ax.transAxes, ha="center", va="center")
|
|
401
|
+
return
|
|
402
|
+
|
|
403
|
+
P_win, _, chi_arr = self._cached_windowed
|
|
404
|
+
colors_chi = ["C3", "C4"]
|
|
405
|
+
has_data = False
|
|
406
|
+
for i, (name, _) in enumerate(self.therm_fast):
|
|
407
|
+
c = colors_chi[i % len(colors_chi)]
|
|
408
|
+
valid = np.isfinite(chi_arr[i]) & (chi_arr[i] > 0)
|
|
409
|
+
if np.any(valid):
|
|
410
|
+
ax.plot(
|
|
411
|
+
chi_arr[i, valid],
|
|
412
|
+
P_win[valid],
|
|
413
|
+
f"{c}s-",
|
|
414
|
+
markersize=3,
|
|
415
|
+
linewidth=0.8,
|
|
416
|
+
label=name,
|
|
417
|
+
)
|
|
418
|
+
has_data = True
|
|
419
|
+
if has_data:
|
|
420
|
+
ax.set_xscale("log")
|
|
421
|
+
ax.set_xlabel("\u03c7 [K\u00b2/s]")
|
|
422
|
+
ax.set_ylabel("Pressure [dbar]")
|
|
423
|
+
ax.legend(fontsize=6, loc="lower left")
|
|
424
|
+
# chi_method=2 runs compute_chi_window(method=2) -> _iterative_fit,
|
|
425
|
+
# i.e. M2-Iter (NOT the MLE variant).
|
|
426
|
+
selected = {1: "M1", 2: "M2-Iter"}.get(self.chi_method, "M1")
|
|
427
|
+
ax.set_title(f"\u03c7 profile ({selected}, {self.spectrum_model})", fontsize=9)
|
|
428
|
+
ax.grid(True, alpha=0.3, which="both")
|
|
429
|
+
else:
|
|
430
|
+
ax.text(0.5, 0.5, "No valid \u03c7", transform=ax.transAxes, ha="center", va="center")
|
|
431
|
+
|
|
432
|
+
def _draw_spectra(self, sel_spec):
|
|
433
|
+
"""Panel (1,1): Epsilon spectra with Nasmyth + K_max."""
|
|
434
|
+
ax = self.axes[1, 1]
|
|
435
|
+
|
|
436
|
+
if self._cached_spec is None:
|
|
437
|
+
ax.text(
|
|
438
|
+
0.5,
|
|
439
|
+
0.5,
|
|
440
|
+
"Insufficient data for spectra",
|
|
441
|
+
transform=ax.transAxes,
|
|
442
|
+
ha="center",
|
|
443
|
+
va="center",
|
|
444
|
+
)
|
|
445
|
+
return
|
|
446
|
+
|
|
447
|
+
r = self._cached_spec
|
|
448
|
+
K = r["K"]
|
|
449
|
+
if len(K) == 0:
|
|
450
|
+
ax.text(0.5, 0.5, "No spectra", transform=ax.transAxes, ha="center", va="center")
|
|
451
|
+
return
|
|
452
|
+
|
|
453
|
+
colors = ["C0", "C1", "C4", "C5"]
|
|
454
|
+
for i, (name, _) in enumerate(self.shear):
|
|
455
|
+
if i >= len(r["shear_specs"]):
|
|
456
|
+
break
|
|
457
|
+
c = colors[i % len(colors)]
|
|
458
|
+
ax.loglog(K, r["shear_specs"][i], c, linewidth=0.8, label=name)
|
|
459
|
+
eps_i = r["epsilons"][i]
|
|
460
|
+
meth_i = r["methods"][i] if i < len(r["methods"]) else 0
|
|
461
|
+
chosen = "ISR" if meth_i == 1 else "var"
|
|
462
|
+
e_isr = r["epsilons_isr"][i] if i < len(r.get("epsilons_isr", [])) else np.nan
|
|
463
|
+
e_var = r["epsilons_var"][i] if i < len(r.get("epsilons_var", [])) else np.nan
|
|
464
|
+
if np.isfinite(eps_i):
|
|
465
|
+
isr_mark = "*" if chosen == "ISR" else ""
|
|
466
|
+
var_mark = "*" if chosen == "var" else ""
|
|
467
|
+
label_parts = []
|
|
468
|
+
if np.isfinite(e_isr):
|
|
469
|
+
label_parts.append(f"ISR={e_isr:.1e}{isr_mark}")
|
|
470
|
+
if np.isfinite(e_var):
|
|
471
|
+
label_parts.append(f"var={e_var:.1e}{var_mark}")
|
|
472
|
+
ax.loglog(
|
|
473
|
+
K,
|
|
474
|
+
r["nasmyth_specs"][i],
|
|
475
|
+
c,
|
|
476
|
+
linewidth=0.75,
|
|
477
|
+
linestyle="--",
|
|
478
|
+
alpha=0.9,
|
|
479
|
+
label=f"Nas {', '.join(label_parts)}",
|
|
480
|
+
)
|
|
481
|
+
k_max_i = r["K_maxes"][i]
|
|
482
|
+
if np.isfinite(k_max_i):
|
|
483
|
+
ax.axvline(
|
|
484
|
+
k_max_i,
|
|
485
|
+
color=c,
|
|
486
|
+
linestyle="-.",
|
|
487
|
+
linewidth=0.8,
|
|
488
|
+
alpha=0.6,
|
|
489
|
+
label=f"K_max={k_max_i:.0f} cpm",
|
|
490
|
+
)
|
|
491
|
+
# Filled inverted triangle at K_max on the observed spectrum
|
|
492
|
+
k_idx = np.argmin(np.abs(K - k_max_i))
|
|
493
|
+
ax.plot(
|
|
494
|
+
k_max_i,
|
|
495
|
+
r["shear_specs"][i][k_idx],
|
|
496
|
+
marker="v",
|
|
497
|
+
color=c,
|
|
498
|
+
markersize=8,
|
|
499
|
+
zorder=5,
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
nu = r["nu"]
|
|
503
|
+
for exp in [-9, -8, -7, -6, -5]:
|
|
504
|
+
eps_ref = 10.0**exp
|
|
505
|
+
nas_ref = nasmyth(eps_ref, nu, K)
|
|
506
|
+
ax.loglog(K, nas_ref, "0.8", linewidth=0.3)
|
|
507
|
+
y_val = nas_ref[len(K) // 2]
|
|
508
|
+
if np.isfinite(y_val) and y_val > 0:
|
|
509
|
+
ax.text(K[len(K) // 2], y_val, f"{exp}", fontsize=6, color="0.5", va="bottom")
|
|
510
|
+
|
|
511
|
+
K_AA = self.f_AA / r["W"]
|
|
512
|
+
# Marks the physical anti-aliasing frequency f_AA; the spectral fits
|
|
513
|
+
# stop slightly lower at 0.9*f_AA, so label it to avoid reading the line
|
|
514
|
+
# as the fit cutoff (#13).
|
|
515
|
+
ax.axvline(
|
|
516
|
+
K_AA, color="0.5", linestyle=":", linewidth=0.5, alpha=0.5, label="f_AA"
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
P_lo = float(self.P_fast[sel_spec.start])
|
|
520
|
+
P_hi = float(self.P_fast[min(sel_spec.stop - 1, len(self.P_fast) - 1)])
|
|
521
|
+
ax.set_xlabel("Wavenumber [cpm]")
|
|
522
|
+
ax.set_ylabel("\u03a6(k) [s\u207b\u00b2 cpm\u207b\u00b9]")
|
|
523
|
+
ax.set_xlim(0.5, 300)
|
|
524
|
+
ax.set_ylim(1e-9, 1e0)
|
|
525
|
+
ax.legend(fontsize=5, loc="best", ncol=2)
|
|
526
|
+
ax.set_title(
|
|
527
|
+
f"\u03b5 spectra speed={r['W']:.2f} m/s\nP={P_lo:.1f}\u2013{P_hi:.1f} dbar",
|
|
528
|
+
fontsize=9,
|
|
529
|
+
)
|
|
530
|
+
ax.grid(True, alpha=0.3, which="both")
|
|
531
|
+
|
|
532
|
+
def _draw_chi_spectra(self, sel_spec):
|
|
533
|
+
"""Panel (1,2): Chi spectra + all three chi methods."""
|
|
534
|
+
ax = self.axes[1, 2]
|
|
535
|
+
self._cached_chi = None
|
|
536
|
+
|
|
537
|
+
if not self.therm_fast:
|
|
538
|
+
ax.text(
|
|
539
|
+
0.5,
|
|
540
|
+
0.5,
|
|
541
|
+
"No thermistor gradient channels",
|
|
542
|
+
transform=ax.transAxes,
|
|
543
|
+
ha="center",
|
|
544
|
+
va="center",
|
|
545
|
+
)
|
|
546
|
+
return
|
|
547
|
+
|
|
548
|
+
n_fast = sel_spec.stop - sel_spec.start
|
|
549
|
+
if n_fast < self.diss_length:
|
|
550
|
+
ax.text(
|
|
551
|
+
0.5,
|
|
552
|
+
0.5,
|
|
553
|
+
"Insufficient data for chi spectra",
|
|
554
|
+
transform=ax.transAxes,
|
|
555
|
+
ha="center",
|
|
556
|
+
va="center",
|
|
557
|
+
)
|
|
558
|
+
return
|
|
559
|
+
|
|
560
|
+
eps_for_chi = self._cached_spec["epsilons"] if self._cached_spec else []
|
|
561
|
+
# FM (MAD-based) is the statistic the 1.15 QC limit is defined for
|
|
562
|
+
fom_for_chi = self._cached_spec.get("FMs", []) if self._cached_spec else []
|
|
563
|
+
|
|
564
|
+
self._cached_chi = _compute_chi_spectra(
|
|
565
|
+
self.therm_fast,
|
|
566
|
+
self.diff_gains,
|
|
567
|
+
self.P_fast,
|
|
568
|
+
self.T,
|
|
569
|
+
self.speed_fast,
|
|
570
|
+
self.fs_fast,
|
|
571
|
+
sel_spec,
|
|
572
|
+
self.fft_length,
|
|
573
|
+
self.f_AA,
|
|
574
|
+
eps_for_chi,
|
|
575
|
+
foms=fom_for_chi,
|
|
576
|
+
spectrum_model=self.spectrum_model,
|
|
577
|
+
)
|
|
578
|
+
K, _F, obs_spectra, methods_results, noise_K, mean_speed, _nu = self._cached_chi
|
|
579
|
+
|
|
580
|
+
# Line styles for each method; selected method drawn thicker. chi_method=2
|
|
581
|
+
# is the iterative fit (M2-Iter) — the same curve the chi-profile panel
|
|
582
|
+
# plots — so highlight it, not the separate M2-MLE curve.
|
|
583
|
+
selected_method = {1: "M1", 2: "M2-Iter"}.get(self.chi_method, "M1")
|
|
584
|
+
method_styles = [
|
|
585
|
+
("M1", "--", 0.9),
|
|
586
|
+
("M2-MLE", "-.", 0.9),
|
|
587
|
+
("M2-Iter", ":", 0.9),
|
|
588
|
+
]
|
|
589
|
+
colors = ["C3", "C1", "C4", "C5"]
|
|
590
|
+
n_probes = len(self.therm_fast)
|
|
591
|
+
|
|
592
|
+
# Plot all lines (no labels yet -- legend order controlled manually)
|
|
593
|
+
obs_lines: list[tuple | None] = []
|
|
594
|
+
method_lines: dict[str, list[tuple | None]] = {m: [] for m, _, _ in method_styles}
|
|
595
|
+
for i, (name, _) in enumerate(self.therm_fast):
|
|
596
|
+
c = colors[i % len(colors)]
|
|
597
|
+
valid = np.isfinite(obs_spectra[i]) & (obs_spectra[i] > 0)
|
|
598
|
+
if np.any(valid):
|
|
599
|
+
(line,) = ax.loglog(K[valid], obs_spectra[i][valid], c, linewidth=0.8)
|
|
600
|
+
obs_lines.append((line, name))
|
|
601
|
+
else:
|
|
602
|
+
obs_lines.append(None)
|
|
603
|
+
|
|
604
|
+
for method_name, ls, alpha in method_styles:
|
|
605
|
+
batch_spec, chi_val, _kB_val = methods_results[method_name][i]
|
|
606
|
+
valid_b = np.isfinite(batch_spec) & (batch_spec > 0)
|
|
607
|
+
lw = 1.5 if method_name == selected_method else 0.75
|
|
608
|
+
if np.any(valid_b):
|
|
609
|
+
chi_str = f"{chi_val:.1e}" if np.isfinite(chi_val) else "N/A"
|
|
610
|
+
label = f"{method_name} \u03c7={chi_str}"
|
|
611
|
+
if method_name == selected_method:
|
|
612
|
+
label = f"*{label}"
|
|
613
|
+
(line,) = ax.loglog(
|
|
614
|
+
K[valid_b],
|
|
615
|
+
batch_spec[valid_b],
|
|
616
|
+
c,
|
|
617
|
+
linewidth=lw,
|
|
618
|
+
linestyle=ls,
|
|
619
|
+
alpha=alpha,
|
|
620
|
+
)
|
|
621
|
+
method_lines[method_name].append((line, label))
|
|
622
|
+
else:
|
|
623
|
+
method_lines[method_name].append(None)
|
|
624
|
+
|
|
625
|
+
valid_n = np.isfinite(noise_K) & (noise_K > 0) & (K > 0)
|
|
626
|
+
noise_line = None
|
|
627
|
+
if np.any(valid_n):
|
|
628
|
+
(nl,) = ax.loglog(K[valid_n], noise_K[valid_n], "0.5", linewidth=0.8, linestyle=":")
|
|
629
|
+
noise_line = (nl, "Noise")
|
|
630
|
+
|
|
631
|
+
K_AA = self.f_AA / mean_speed
|
|
632
|
+
# Marks the physical anti-aliasing frequency f_AA; the spectral fits
|
|
633
|
+
# stop slightly lower at 0.9*f_AA, so label it to avoid reading the line
|
|
634
|
+
# as the fit cutoff (#13).
|
|
635
|
+
ax.axvline(
|
|
636
|
+
K_AA, color="0.5", linestyle=":", linewidth=0.5, alpha=0.5, label="f_AA"
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
# Build legend: left column = probe 0, right column = probe 1.
|
|
640
|
+
# matplotlib ncol fills column-major: entries 0..N/2-1 go in left col,
|
|
641
|
+
# N/2..N-1 in right col. So we build per-column lists then concatenate.
|
|
642
|
+
rows = [obs_lines] + [method_lines[m] for m, _, _ in method_styles]
|
|
643
|
+
columns: list[list] = [[] for _ in range(n_probes)]
|
|
644
|
+
for ci in range(n_probes):
|
|
645
|
+
for row in rows:
|
|
646
|
+
if ci < len(row) and row[ci] is not None:
|
|
647
|
+
columns[ci].append(row[ci])
|
|
648
|
+
else:
|
|
649
|
+
(ph,) = ax.plot([], [], alpha=0)
|
|
650
|
+
columns[ci].append((ph, ""))
|
|
651
|
+
# Noise in the last row of each column
|
|
652
|
+
if ci == 0 and noise_line is not None:
|
|
653
|
+
columns[ci].append(noise_line)
|
|
654
|
+
else:
|
|
655
|
+
(ph,) = ax.plot([], [], alpha=0)
|
|
656
|
+
columns[ci].append((ph, ""))
|
|
657
|
+
|
|
658
|
+
# Pad columns to equal length
|
|
659
|
+
max_len = max(len(c) for c in columns)
|
|
660
|
+
for col_list in columns:
|
|
661
|
+
while len(col_list) < max_len:
|
|
662
|
+
(ph,) = ax.plot([], [], alpha=0)
|
|
663
|
+
col_list.append((ph, ""))
|
|
664
|
+
|
|
665
|
+
handles, labels = [], []
|
|
666
|
+
for col_list in columns:
|
|
667
|
+
for h, lbl in col_list:
|
|
668
|
+
handles.append(h)
|
|
669
|
+
labels.append(lbl)
|
|
670
|
+
|
|
671
|
+
ax.set_xlabel("Wavenumber [cpm]")
|
|
672
|
+
ax.set_ylabel("\u03a6_T(k) [(K/m)\u00b2 cpm\u207b\u00b9]")
|
|
673
|
+
ax.set_xlim(0.5, 300)
|
|
674
|
+
ax.set_ylim(1e-11, None)
|
|
675
|
+
ax.legend(handles, labels, fontsize=5, loc="best", ncol=n_probes)
|
|
676
|
+
ax.set_title(
|
|
677
|
+
f"\u03c7 spectra fit={selected_method} model={self.spectrum_model}\n"
|
|
678
|
+
f"speed={mean_speed:.2f} m/s",
|
|
679
|
+
fontsize=9,
|
|
680
|
+
)
|
|
681
|
+
ax.grid(True, alpha=0.3, which="both")
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
# ---------------------------------------------------------------------------
|
|
685
|
+
# Entry point
|
|
686
|
+
# ---------------------------------------------------------------------------
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
def quick_look(
|
|
690
|
+
source,
|
|
691
|
+
fft_length=1024,
|
|
692
|
+
f_AA=98.0,
|
|
693
|
+
goodman=True,
|
|
694
|
+
direction="auto",
|
|
695
|
+
P_min=0.5,
|
|
696
|
+
W_min=0.3,
|
|
697
|
+
min_duration=7.0,
|
|
698
|
+
spec_P_range=None,
|
|
699
|
+
chi_method=1,
|
|
700
|
+
spectrum_model="kraichnan",
|
|
701
|
+
diss_length=None,
|
|
702
|
+
vehicle=None,
|
|
703
|
+
) -> QuickLookViewer:
|
|
704
|
+
"""Open an interactive quick-look viewer for a .p file.
|
|
705
|
+
|
|
706
|
+
Parameters
|
|
707
|
+
----------
|
|
708
|
+
source : str or Path
|
|
709
|
+
Path to a Rockland .p file.
|
|
710
|
+
fft_length : int
|
|
711
|
+
FFT segment length for spectral estimation (default: 1024).
|
|
712
|
+
f_AA : float
|
|
713
|
+
Anti-aliasing filter cutoff [Hz].
|
|
714
|
+
goodman : bool
|
|
715
|
+
Apply Goodman coherent noise removal to shear spectra.
|
|
716
|
+
direction : str
|
|
717
|
+
Profile direction: 'up' or 'down'.
|
|
718
|
+
P_min : float
|
|
719
|
+
Minimum pressure for profile detection [dbar].
|
|
720
|
+
W_min : float
|
|
721
|
+
Minimum fall rate for profile detection [dbar/s].
|
|
722
|
+
min_duration : float
|
|
723
|
+
Minimum profile duration [s].
|
|
724
|
+
spec_P_range : tuple of (float, float) or None
|
|
725
|
+
Pressure range (P_min, P_max) in dbar for spectral calculations.
|
|
726
|
+
If None, uses the full profile depth range.
|
|
727
|
+
chi_method : int
|
|
728
|
+
Chi method for windowed profile estimates: 1 = from known epsilon,
|
|
729
|
+
2 = MLE Batchelor fit. All three methods are always shown on the
|
|
730
|
+
chi spectra panel.
|
|
731
|
+
spectrum_model : str
|
|
732
|
+
Theoretical spectrum model: 'batchelor' or 'kraichnan'.
|
|
733
|
+
diss_length : int or None
|
|
734
|
+
Dissipation window length [samples]. Default: 4 * fft_length.
|
|
735
|
+
|
|
736
|
+
Returns
|
|
737
|
+
-------
|
|
738
|
+
QuickLookViewer
|
|
739
|
+
The viewer instance (useful for scripting).
|
|
740
|
+
"""
|
|
741
|
+
pf = PFile(source)
|
|
742
|
+
viewer = QuickLookViewer(
|
|
743
|
+
pf,
|
|
744
|
+
fft_length=fft_length,
|
|
745
|
+
f_AA=f_AA,
|
|
746
|
+
goodman=goodman,
|
|
747
|
+
direction=direction,
|
|
748
|
+
P_min=P_min,
|
|
749
|
+
W_min=W_min,
|
|
750
|
+
min_duration=min_duration,
|
|
751
|
+
spec_P_range=spec_P_range,
|
|
752
|
+
chi_method=chi_method,
|
|
753
|
+
spectrum_model=spectrum_model,
|
|
754
|
+
diss_length=diss_length,
|
|
755
|
+
vehicle=vehicle,
|
|
756
|
+
)
|
|
757
|
+
viewer.show()
|
|
758
|
+
return viewer
|