golem-toolkit 0.1.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.
- golem_toolkit/__init__.py +5 -0
- golem_toolkit/hello_package/__init__.py +0 -0
- golem_toolkit/hello_package/hello.py +2 -0
- golem_toolkit/loading/DAS_systems.py +645 -0
- golem_toolkit/loading/__init__.py +22 -0
- golem_toolkit/loading/basic.py +433 -0
- golem_toolkit/loading/test.py +45 -0
- golem_toolkit/probes/__init__.py +27 -0
- golem_toolkit/probes/_signal.py +58 -0
- golem_toolkit/probes/iv_calibration.py +160 -0
- golem_toolkit/probes/iv_processing.py +190 -0
- golem_toolkit/probes/tektronix.py +123 -0
- golem_toolkit/py.typed +0 -0
- golem_toolkit-0.1.0.dist-info/METADATA +111 -0
- golem_toolkit-0.1.0.dist-info/RECORD +17 -0
- golem_toolkit-0.1.0.dist-info/WHEEL +4 -0
- golem_toolkit-0.1.0.dist-info/licenses/LICENSE +21 -0
|
File without changes
|
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
# import pandas as pd
|
|
3
|
+
import xarray as xr
|
|
4
|
+
import matplotlib.pyplot as plt
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import hashlib, json, tempfile, os, contextlib, re
|
|
7
|
+
|
|
8
|
+
from golem_toolkit.loading import basic as load
|
|
9
|
+
|
|
10
|
+
def _fingerprint_for_cache(key: dict) -> str:
|
|
11
|
+
"""Stable short fingerprint for any JSON-serializable object."""
|
|
12
|
+
return hashlib.sha1(
|
|
13
|
+
json.dumps(key, sort_keys=True, default=str).encode()
|
|
14
|
+
).hexdigest()[:10]
|
|
15
|
+
|
|
16
|
+
def cache_file_for(
|
|
17
|
+
shot_no,
|
|
18
|
+
data_url,
|
|
19
|
+
das_settings,
|
|
20
|
+
n_channels,
|
|
21
|
+
names=None,
|
|
22
|
+
skiprows=0,
|
|
23
|
+
time_units="s",
|
|
24
|
+
cache_dir="DATA_CACHE",
|
|
25
|
+
):
|
|
26
|
+
"""Return the exact Path where this call would cache the dataset."""
|
|
27
|
+
if names is None:
|
|
28
|
+
names = ["t"] + [f"CH{i}" for i in range(1, n_channels+1)]
|
|
29
|
+
key = {
|
|
30
|
+
"shot_no": shot_no,
|
|
31
|
+
"url": data_url,
|
|
32
|
+
"settings": das_settings,
|
|
33
|
+
"n_channels": n_channels,
|
|
34
|
+
"names": names,
|
|
35
|
+
"skiprows": skiprows,
|
|
36
|
+
"time_units": time_units,
|
|
37
|
+
"version": 1,
|
|
38
|
+
}
|
|
39
|
+
fp = _fingerprint_for_cache(key)
|
|
40
|
+
return Path(cache_dir) / f"DAS_{shot_no}_{fp}.nc"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def list_cache(cache_dir="DATA_CACHE", shot_no=None):
|
|
44
|
+
"""
|
|
45
|
+
List cached .nc files. If shot_no is given, only list caches for that shot.
|
|
46
|
+
Returns a list of Paths sorted by modified time (newest first).
|
|
47
|
+
"""
|
|
48
|
+
cdir = Path(cache_dir)
|
|
49
|
+
if not cdir.exists():
|
|
50
|
+
return []
|
|
51
|
+
pattern = f"DAS_{shot_no}_" if shot_no is not None else "DAS_"
|
|
52
|
+
files = [p for p in cdir.glob("*.nc") if p.name.startswith(pattern)]
|
|
53
|
+
return sorted(files, key=lambda p: p.stat().st_mtime, reverse=True)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def clear_cache(cache_dir="DATA_CACHE", shot_no=None, keep_latest=False, verbose=1):
|
|
57
|
+
"""
|
|
58
|
+
Delete cache files.
|
|
59
|
+
- If shot_no is None: delete all caches in cache_dir.
|
|
60
|
+
- If shot_no is set: delete only that shot's caches.
|
|
61
|
+
- If keep_latest=True: for each shot, keep the newest file and delete the rest.
|
|
62
|
+
|
|
63
|
+
Returns (deleted_paths: list[Path], kept_paths: list[Path])
|
|
64
|
+
"""
|
|
65
|
+
cdir = Path(cache_dir)
|
|
66
|
+
if not cdir.exists():
|
|
67
|
+
return ([], [])
|
|
68
|
+
|
|
69
|
+
if shot_no is None:
|
|
70
|
+
# group by shot inferred from filename DAS_<shot>_<fp>.nc
|
|
71
|
+
groups = {}
|
|
72
|
+
for p in cdir.glob("DAS_*_*.nc"):
|
|
73
|
+
m = re.match(r"DAS_(.+?)_[0-9a-f]{10}\.nc$", p.name)
|
|
74
|
+
if not m:
|
|
75
|
+
continue
|
|
76
|
+
sh = m.group(1)
|
|
77
|
+
groups.setdefault(sh, []).append(p)
|
|
78
|
+
else:
|
|
79
|
+
groups = {str(shot_no): list_cache(cache_dir, shot_no=shot_no)}
|
|
80
|
+
|
|
81
|
+
deleted, kept = [], []
|
|
82
|
+
for sh, files in groups.items():
|
|
83
|
+
files_sorted = sorted(files, key=lambda p: p.stat().st_mtime, reverse=True)
|
|
84
|
+
if keep_latest and files_sorted:
|
|
85
|
+
kept.append(files_sorted[0])
|
|
86
|
+
to_delete = files_sorted[1:]
|
|
87
|
+
else:
|
|
88
|
+
to_delete = files_sorted
|
|
89
|
+
|
|
90
|
+
for p in to_delete:
|
|
91
|
+
try:
|
|
92
|
+
p.unlink()
|
|
93
|
+
deleted.append(p)
|
|
94
|
+
if verbose:
|
|
95
|
+
print(f"[clear_cache] Deleted {p}")
|
|
96
|
+
except Exception as e:
|
|
97
|
+
if verbose:
|
|
98
|
+
print(f"[clear_cache] Could not delete {p} ({e})")
|
|
99
|
+
|
|
100
|
+
if verbose and kept:
|
|
101
|
+
for p in kept:
|
|
102
|
+
print(f"[clear_cache] Kept latest for shot: {p}")
|
|
103
|
+
|
|
104
|
+
return (deleted, kept)
|
|
105
|
+
|
|
106
|
+
def load_DAS(
|
|
107
|
+
shot_no,
|
|
108
|
+
data_url,
|
|
109
|
+
das_settings,
|
|
110
|
+
n_channels,
|
|
111
|
+
names=None,
|
|
112
|
+
skiprows=0,
|
|
113
|
+
time_units="s",
|
|
114
|
+
verbose=0,
|
|
115
|
+
*,
|
|
116
|
+
cache=False,
|
|
117
|
+
cache_dir="DATA_CACHE",
|
|
118
|
+
force_refresh=False,
|
|
119
|
+
):
|
|
120
|
+
"""Load DAS data into an xarray.Dataset with optional caching."""
|
|
121
|
+
|
|
122
|
+
# ---------- Cache setup ----------
|
|
123
|
+
if names is None:
|
|
124
|
+
names = ["t"] + [f"CH{i}" for i in range(1, n_channels+1)]
|
|
125
|
+
|
|
126
|
+
if cache:
|
|
127
|
+
key = {
|
|
128
|
+
"shot_no": shot_no,
|
|
129
|
+
"url": data_url,
|
|
130
|
+
"settings": das_settings,
|
|
131
|
+
"n_channels": n_channels,
|
|
132
|
+
"names": names,
|
|
133
|
+
"skiprows": skiprows,
|
|
134
|
+
"time_units": time_units,
|
|
135
|
+
"version": 1,
|
|
136
|
+
}
|
|
137
|
+
fingerprint = _fingerprint_for_cache(key)
|
|
138
|
+
|
|
139
|
+
cache_dir = Path(cache_dir)
|
|
140
|
+
cache_dir.mkdir(exist_ok=True)
|
|
141
|
+
cache_file = cache_dir / f"DAS_{shot_no}_{fingerprint}.nc"
|
|
142
|
+
if verbose > 1:
|
|
143
|
+
print(f"[load_DAS] cache_file = {cache_file}")
|
|
144
|
+
|
|
145
|
+
# ---------- Try cache ----------
|
|
146
|
+
if cache and cache_file.exists() and not force_refresh:
|
|
147
|
+
if verbose:
|
|
148
|
+
print(f"[load_DAS] Loading from cache {cache_file}")
|
|
149
|
+
try:
|
|
150
|
+
return xr.load_dataset(cache_file)
|
|
151
|
+
except Exception as e:
|
|
152
|
+
if verbose:
|
|
153
|
+
print(f"[load_DAS] Cache read failed ({e}); rebuilding...")
|
|
154
|
+
|
|
155
|
+
# ---------- Load fresh data ----------
|
|
156
|
+
pd_data = load.load_array(data_url, names=names, index_col="t", verbose=verbose, skiprows=skiprows)
|
|
157
|
+
if pd_data is None:
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
data = pd_data.to_xarray()
|
|
161
|
+
DAS = xr.Dataset()
|
|
162
|
+
|
|
163
|
+
for channel, settings in das_settings.items():
|
|
164
|
+
DAS[settings["var_name"]] = data[channel] * settings["scaling_factor"]
|
|
165
|
+
DAS[settings["var_name"]].attrs = settings["attrs"]
|
|
166
|
+
|
|
167
|
+
DAS.attrs = {"shot_no": shot_no}
|
|
168
|
+
|
|
169
|
+
factor = 1 if time_units == "s" else 1e3
|
|
170
|
+
DAS = DAS.rename({"index": "t"}) if "index" in DAS.dims else DAS
|
|
171
|
+
DAS = DAS.assign_coords(t=DAS.t * factor)
|
|
172
|
+
DAS.t.attrs["units"] = time_units
|
|
173
|
+
|
|
174
|
+
# ---------- Save to cache ----------
|
|
175
|
+
if cache:
|
|
176
|
+
if verbose:
|
|
177
|
+
print(f"[load_DAS] Saving to cache {cache_file}")
|
|
178
|
+
fd, tmp_name = tempfile.mkstemp(suffix=".nc", dir=str(cache_dir))
|
|
179
|
+
os.close(fd)
|
|
180
|
+
tmp_path = Path(tmp_name)
|
|
181
|
+
try:
|
|
182
|
+
DAS.to_netcdf(tmp_path)
|
|
183
|
+
tmp_path.replace(cache_file) # atomic replace
|
|
184
|
+
except Exception:
|
|
185
|
+
with contextlib.suppress(Exception):
|
|
186
|
+
print(f"[load_DAS] Saving to cache failed!")
|
|
187
|
+
tmp_path.unlink()
|
|
188
|
+
raise
|
|
189
|
+
|
|
190
|
+
return DAS
|
|
191
|
+
|
|
192
|
+
def plot_DAS(DAS_dataset, DAS_name=None, figsize=None, filename=None, show=True):
|
|
193
|
+
if DAS_dataset is None:
|
|
194
|
+
raise ValueError("DAS_dataset is None.")
|
|
195
|
+
|
|
196
|
+
# Normalize supported inputs to (name, DataArray) pairs.
|
|
197
|
+
if isinstance(DAS_dataset, xr.DataArray):
|
|
198
|
+
items = [(DAS_dataset.name or "signal", DAS_dataset)]
|
|
199
|
+
attrs = getattr(DAS_dataset, "attrs", {})
|
|
200
|
+
elif isinstance(DAS_dataset, xr.Dataset):
|
|
201
|
+
items = list(DAS_dataset.data_vars.items())
|
|
202
|
+
attrs = getattr(DAS_dataset, "attrs", {})
|
|
203
|
+
elif hasattr(DAS_dataset, "items"):
|
|
204
|
+
items = list(DAS_dataset.items())
|
|
205
|
+
attrs = {}
|
|
206
|
+
else:
|
|
207
|
+
raise TypeError(
|
|
208
|
+
"DAS_dataset must be xarray Dataset/DataArray or dict-like of DataArrays."
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
if len(items) == 0:
|
|
212
|
+
raise ValueError("DAS_dataset has no data variables to plot.")
|
|
213
|
+
|
|
214
|
+
set_figsize = figsize if figsize else (8, max(1, len(items)) * 3)
|
|
215
|
+
fig, ax = plt.subplots(
|
|
216
|
+
len(items), 1,
|
|
217
|
+
figsize=set_figsize,
|
|
218
|
+
dpi=300,
|
|
219
|
+
tight_layout=True,
|
|
220
|
+
sharex=True,
|
|
221
|
+
squeeze=False,
|
|
222
|
+
)
|
|
223
|
+
axes = ax.ravel()
|
|
224
|
+
|
|
225
|
+
shot_no = attrs.get("shot_no")
|
|
226
|
+
supt = f"#{shot_no}" if shot_no is not None else "DAS signals"
|
|
227
|
+
if DAS_name:
|
|
228
|
+
supt += f" (DAS = {DAS_name})"
|
|
229
|
+
fig.suptitle(supt)
|
|
230
|
+
|
|
231
|
+
for axi, (var, da) in zip(axes, items):
|
|
232
|
+
if da is None:
|
|
233
|
+
axi.text(0.5, 0.5, f"{var}: no data", ha="center", va="center")
|
|
234
|
+
axi.set_axis_off()
|
|
235
|
+
continue
|
|
236
|
+
|
|
237
|
+
if isinstance(da, xr.DataArray):
|
|
238
|
+
# Use native xarray plotting when coordinate context exists.
|
|
239
|
+
if da.ndim == 0:
|
|
240
|
+
axi.text(0.5, 0.5, f"{var}: scalar = {da.item()}", ha="center", va="center")
|
|
241
|
+
axi.set_axis_off()
|
|
242
|
+
continue
|
|
243
|
+
if da.ndim > 1:
|
|
244
|
+
da = da.squeeze(drop=True)
|
|
245
|
+
if da.ndim > 1:
|
|
246
|
+
da = da.isel({d: 0 for d in da.dims[1:]})
|
|
247
|
+
|
|
248
|
+
if "t" in da.coords:
|
|
249
|
+
da.plot(ax=axi, x="t")
|
|
250
|
+
else:
|
|
251
|
+
da.plot(ax=axi)
|
|
252
|
+
probe_name = da.attrs.get("probe", var)
|
|
253
|
+
else:
|
|
254
|
+
# Last-resort fallback for plain array-like objects.
|
|
255
|
+
axi.plot(np.asarray(da))
|
|
256
|
+
probe_name = var
|
|
257
|
+
|
|
258
|
+
axi.set_title(f"probe {probe_name}")
|
|
259
|
+
axi.grid(True)
|
|
260
|
+
|
|
261
|
+
if filename:
|
|
262
|
+
fig.savefig(filename)
|
|
263
|
+
|
|
264
|
+
if show:
|
|
265
|
+
plt.show()
|
|
266
|
+
else:
|
|
267
|
+
plt.close(fig)
|
|
268
|
+
|
|
269
|
+
class REDPITAYA:
|
|
270
|
+
def __init__(self, das_settings, DAS_name="REDPITAYA", data_url_template=None):
|
|
271
|
+
self.DAS_name = DAS_name
|
|
272
|
+
|
|
273
|
+
# Default template if not provided
|
|
274
|
+
if data_url_template is None:
|
|
275
|
+
data_url_template = (
|
|
276
|
+
"http://golem.fjfi.cvut.cz/shotdir/{shot_no}/Devices/ITs/SignalLab-a/data.csv"
|
|
277
|
+
)
|
|
278
|
+
self.data_url_template = data_url_template
|
|
279
|
+
|
|
280
|
+
self.n_channels = 2 # number of DAS channels
|
|
281
|
+
self.skiprows = 1
|
|
282
|
+
self.das_settings = das_settings
|
|
283
|
+
|
|
284
|
+
def get_data_url(self, shot_no):
|
|
285
|
+
return self.data_url_template.format(shot_no=shot_no)
|
|
286
|
+
|
|
287
|
+
def load_data(self, shot_no, **load_DAS_kwargs):
|
|
288
|
+
data_url = self.get_data_url(shot_no)
|
|
289
|
+
self.data = load_DAS(shot_no,
|
|
290
|
+
data_url,
|
|
291
|
+
self.das_settings,
|
|
292
|
+
self.n_channels,
|
|
293
|
+
skiprows=self.skiprows,
|
|
294
|
+
**load_DAS_kwargs)
|
|
295
|
+
|
|
296
|
+
# return self.data
|
|
297
|
+
|
|
298
|
+
def plot(self, **kwargs):
|
|
299
|
+
try:
|
|
300
|
+
data = getattr(self, "data")
|
|
301
|
+
except Exception as e:
|
|
302
|
+
print(f"The data has not been loaded yet! Error: {e}")
|
|
303
|
+
plot_DAS(self.data, self.DAS_name, **kwargs)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
_TEK64_ALL_CHANNELS = ["CH1", "CH2", "CH3", "CH4"]
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def load_tek64_csv(
|
|
310
|
+
path: str | Path,
|
|
311
|
+
*,
|
|
312
|
+
channels: list[str] | None = None,
|
|
313
|
+
skiprows: int = 12,
|
|
314
|
+
time_units: str = "s",
|
|
315
|
+
verbose: int = 0,
|
|
316
|
+
**load_DAS_kwargs,
|
|
317
|
+
) -> xr.Dataset:
|
|
318
|
+
"""Load a Tektronix MSO64 CSV file into an xarray.Dataset (raw volts, coord ``t``).
|
|
319
|
+
|
|
320
|
+
Uses :func:`load_DAS` / :class:`TEK64` conventions (``load_array`` with fixed
|
|
321
|
+
column names). Default ``skiprows=12`` skips the 11-line Tek metadata block **and**
|
|
322
|
+
the ``TIME,CH1,...`` header row — same as :attr:`TEK64.skiprows`.
|
|
323
|
+
|
|
324
|
+
The file always has four scope channels; all are read, then ``channels`` selects the
|
|
325
|
+
subset returned (default all four).
|
|
326
|
+
"""
|
|
327
|
+
path = Path(path)
|
|
328
|
+
if channels is None:
|
|
329
|
+
channels = list(_TEK64_ALL_CHANNELS)
|
|
330
|
+
|
|
331
|
+
das_settings = {
|
|
332
|
+
ch: {"var_name": ch, "scaling_factor": 1.0, "attrs": {"units": "V"}}
|
|
333
|
+
for ch in _TEK64_ALL_CHANNELS
|
|
334
|
+
}
|
|
335
|
+
names = ["t"] + _TEK64_ALL_CHANNELS
|
|
336
|
+
ds = load_DAS(
|
|
337
|
+
"local",
|
|
338
|
+
str(path),
|
|
339
|
+
das_settings,
|
|
340
|
+
n_channels=4,
|
|
341
|
+
names=names,
|
|
342
|
+
skiprows=skiprows,
|
|
343
|
+
time_units=time_units,
|
|
344
|
+
verbose=verbose,
|
|
345
|
+
cache=False,
|
|
346
|
+
**load_DAS_kwargs,
|
|
347
|
+
)
|
|
348
|
+
if ds is None:
|
|
349
|
+
raise OSError(f"Failed to load Tektronix CSV: {path}")
|
|
350
|
+
ds.attrs["source_file"] = str(path)
|
|
351
|
+
return ds[channels]
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
class TEK64:
|
|
355
|
+
def __init__(self, das_settings, DAS_name="TEK64", data_url_template=None):
|
|
356
|
+
self.DAS_name = DAS_name
|
|
357
|
+
|
|
358
|
+
# Default template if not provided
|
|
359
|
+
if data_url_template is None:
|
|
360
|
+
data_url_template = (
|
|
361
|
+
"http://golem.fjfi.cvut.cz/shotdir/{shot_no}/Diagnostics/LangBallPenProbe/DAS_raw_data_dir/TektrMSO64_ALL.csv"
|
|
362
|
+
)
|
|
363
|
+
self.data_url_template = data_url_template
|
|
364
|
+
|
|
365
|
+
self.n_channels = 4 # number of DAS channels
|
|
366
|
+
self.skiprows = 12
|
|
367
|
+
self.das_settings = das_settings
|
|
368
|
+
|
|
369
|
+
def get_data_url(self, shot_no):
|
|
370
|
+
return self.data_url_template.format(shot_no=shot_no)
|
|
371
|
+
|
|
372
|
+
def load_data(self, shot_no, **load_DAS_kwargs):
|
|
373
|
+
data_url = self.get_data_url(shot_no)
|
|
374
|
+
self.data = load_DAS(
|
|
375
|
+
shot_no,
|
|
376
|
+
data_url,
|
|
377
|
+
self.das_settings,
|
|
378
|
+
self.n_channels,
|
|
379
|
+
skiprows=self.skiprows,
|
|
380
|
+
**load_DAS_kwargs,
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
def load_from_path(
|
|
384
|
+
self,
|
|
385
|
+
path: str | Path,
|
|
386
|
+
*,
|
|
387
|
+
shot_no: str | int = "local",
|
|
388
|
+
channels: list[str] | None = None,
|
|
389
|
+
skiprows: int | None = None,
|
|
390
|
+
time_units: str = "s",
|
|
391
|
+
verbose: int = 0,
|
|
392
|
+
**load_DAS_kwargs,
|
|
393
|
+
) -> xr.Dataset:
|
|
394
|
+
"""Load a local Tektronix CSV with :func:`load_DAS` and this device's ``das_settings``."""
|
|
395
|
+
path = Path(path)
|
|
396
|
+
if channels is None:
|
|
397
|
+
channels = list(self.das_settings.keys())
|
|
398
|
+
names = ["t"] + list(channels)
|
|
399
|
+
self.data = load_DAS(
|
|
400
|
+
shot_no,
|
|
401
|
+
str(path),
|
|
402
|
+
self.das_settings,
|
|
403
|
+
self.n_channels,
|
|
404
|
+
names=names,
|
|
405
|
+
skiprows=self.skiprows if skiprows is None else skiprows,
|
|
406
|
+
time_units=time_units,
|
|
407
|
+
verbose=verbose,
|
|
408
|
+
cache=False,
|
|
409
|
+
**load_DAS_kwargs,
|
|
410
|
+
)
|
|
411
|
+
if self.data is None:
|
|
412
|
+
raise OSError(f"Failed to load Tektronix CSV: {path}")
|
|
413
|
+
self.data.attrs["source_file"] = str(path)
|
|
414
|
+
return self.data
|
|
415
|
+
|
|
416
|
+
def plot(self, **kwargs):
|
|
417
|
+
try:
|
|
418
|
+
data = getattr(self, "data")
|
|
419
|
+
except Exception as e:
|
|
420
|
+
print(f"The data has not been loaded yet! \nError: {e}")
|
|
421
|
+
|
|
422
|
+
plot_DAS(self.data, self.DAS_name, **kwargs)
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
class PAPOUCH:
|
|
426
|
+
def __init__(self, das_settings, DAS_name="PAPOUCH", data_url_template=None, channel_files=None):
|
|
427
|
+
self.DAS_name = DAS_name
|
|
428
|
+
|
|
429
|
+
if data_url_template is None:
|
|
430
|
+
data_url_template = (
|
|
431
|
+
"http://golem.fjfi.cvut.cz/shots/{shot_no}/Diagnostics/Diamagnetism/"
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
if channel_files is None:
|
|
435
|
+
channel_files = {
|
|
436
|
+
"CH1": "U_loop.csv",
|
|
437
|
+
"CH2": "U_rog-coil-out.csv",
|
|
438
|
+
"CH3": "U_rog-coil-in.csv",
|
|
439
|
+
"CH4": "U_diam-in.csv",
|
|
440
|
+
"CH5": "U_diam-out.csv",
|
|
441
|
+
"CH6": "U_tor-hfs.csv",
|
|
442
|
+
"CH7": "U_tor-lfs.csv",
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
self.data_url_template = data_url_template
|
|
446
|
+
self.n_channels = 7 # number of DAS channels
|
|
447
|
+
self.skiprows = 0
|
|
448
|
+
self.das_settings = das_settings
|
|
449
|
+
self.channel_files = channel_files
|
|
450
|
+
|
|
451
|
+
def get_data_url(self, shot_no):
|
|
452
|
+
return self.data_url_template.format(shot_no=shot_no)
|
|
453
|
+
|
|
454
|
+
def load_data(
|
|
455
|
+
self,
|
|
456
|
+
shot_no,
|
|
457
|
+
time_units="s",
|
|
458
|
+
verbose=0,
|
|
459
|
+
cache=False,
|
|
460
|
+
cache_dir="DATA_CACHE",
|
|
461
|
+
force_refresh=False,
|
|
462
|
+
):
|
|
463
|
+
# ---------- Cache setup ----------
|
|
464
|
+
if cache:
|
|
465
|
+
key = {
|
|
466
|
+
"shot_no": shot_no,
|
|
467
|
+
"type": "PAPOUCH",
|
|
468
|
+
"data_url_template": self.data_url_template,
|
|
469
|
+
"channel_files": self.channel_files,
|
|
470
|
+
"das_settings": self.das_settings,
|
|
471
|
+
"time_units": time_units,
|
|
472
|
+
"version": 1,
|
|
473
|
+
}
|
|
474
|
+
fingerprint = _fingerprint_for_cache(key)
|
|
475
|
+
|
|
476
|
+
cache_dir = Path(cache_dir)
|
|
477
|
+
cache_dir.mkdir(exist_ok=True)
|
|
478
|
+
cache_file = cache_dir / f"DAS_{shot_no}_{fingerprint}.nc"
|
|
479
|
+
if verbose > 1:
|
|
480
|
+
print(f"[PAPOUCH.load_data] cache_file = {cache_file}")
|
|
481
|
+
|
|
482
|
+
# ---------- Try cache ----------
|
|
483
|
+
if cache and cache_file.exists() and not force_refresh:
|
|
484
|
+
if verbose:
|
|
485
|
+
print(f"[PAPOUCH.load_data] Loading from cache {cache_file}")
|
|
486
|
+
try:
|
|
487
|
+
self.data = xr.load_dataset(cache_file)
|
|
488
|
+
return self.data
|
|
489
|
+
except Exception as e:
|
|
490
|
+
if verbose:
|
|
491
|
+
print(f"[PAPOUCH.load_data] Cache read failed ({e}); rebuilding...")
|
|
492
|
+
|
|
493
|
+
# ---------- Load fresh data ----------
|
|
494
|
+
data_vars = {}
|
|
495
|
+
base_url = self.get_data_url(shot_no)
|
|
496
|
+
|
|
497
|
+
for channel, settings in self.das_settings.items():
|
|
498
|
+
filename = self.channel_files.get(channel)
|
|
499
|
+
if filename is None:
|
|
500
|
+
if verbose > 0:
|
|
501
|
+
print(f"Warning: Missing filename mapping for {channel}, skipping.")
|
|
502
|
+
continue
|
|
503
|
+
url = base_url + filename
|
|
504
|
+
try:
|
|
505
|
+
pd_data = load.load_array(
|
|
506
|
+
url,
|
|
507
|
+
names=["t", channel],
|
|
508
|
+
index_col=0,
|
|
509
|
+
verbose=verbose,
|
|
510
|
+
)
|
|
511
|
+
if pd_data is None:
|
|
512
|
+
if verbose > 0:
|
|
513
|
+
print(f"Warning: Empty data for {channel} at {url}")
|
|
514
|
+
continue
|
|
515
|
+
da = pd_data.to_xarray()[channel] * settings["scaling_factor"]
|
|
516
|
+
da = da.rename(settings["var_name"])
|
|
517
|
+
da.attrs = dict(settings["attrs"])
|
|
518
|
+
da.attrs["source"] = url
|
|
519
|
+
data_vars[settings["var_name"]] = da
|
|
520
|
+
except Exception as e:
|
|
521
|
+
if verbose > 0:
|
|
522
|
+
print(f"Warning: Could not load {channel} for shot {shot_no}: {e}")
|
|
523
|
+
|
|
524
|
+
if not data_vars:
|
|
525
|
+
raise ValueError(f"No data could be loaded for shot {shot_no}")
|
|
526
|
+
|
|
527
|
+
self.data = xr.Dataset(data_vars)
|
|
528
|
+
self.data.attrs["shot_no"] = shot_no
|
|
529
|
+
self.data.attrs["source"] = "PAPOUCH DAS"
|
|
530
|
+
factor = 1 if time_units == "s" else 1e3
|
|
531
|
+
self.data = self.data.rename({"index": "t"}) if "index" in self.data.dims else self.data
|
|
532
|
+
self.data = self.data.assign_coords(t=self.data.t * factor)
|
|
533
|
+
self.data.t.attrs["units"] = time_units
|
|
534
|
+
|
|
535
|
+
# ---------- Save to cache ----------
|
|
536
|
+
if cache:
|
|
537
|
+
if verbose:
|
|
538
|
+
print(f"[PAPOUCH.load_data] Saving to cache {cache_file}")
|
|
539
|
+
fd, tmp_name = tempfile.mkstemp(suffix=".nc", dir=str(cache_dir))
|
|
540
|
+
os.close(fd)
|
|
541
|
+
tmp_path = Path(tmp_name)
|
|
542
|
+
try:
|
|
543
|
+
self.data.to_netcdf(tmp_path)
|
|
544
|
+
tmp_path.replace(cache_file) # atomic replace
|
|
545
|
+
except Exception:
|
|
546
|
+
with contextlib.suppress(Exception):
|
|
547
|
+
print("[PAPOUCH.load_data] Saving to cache failed!")
|
|
548
|
+
tmp_path.unlink()
|
|
549
|
+
raise
|
|
550
|
+
|
|
551
|
+
return self.data
|
|
552
|
+
|
|
553
|
+
def plot(self, **kwargs):
|
|
554
|
+
try:
|
|
555
|
+
data = getattr(self, "data")
|
|
556
|
+
except Exception as e:
|
|
557
|
+
print(f"The data has not been loaded yet! \nError: {e}")
|
|
558
|
+
|
|
559
|
+
plot_DAS(self.data, self.DAS_name, **kwargs)
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
if __name__ == "__main__":
|
|
563
|
+
|
|
564
|
+
I_measurement_resistor = 47
|
|
565
|
+
U_divider_factor = 42
|
|
566
|
+
|
|
567
|
+
tek_settings = {
|
|
568
|
+
"CH2": {"var_name": "Ufl",
|
|
569
|
+
"scaling_factor": U_divider_factor,
|
|
570
|
+
"attrs": {"units": "V",
|
|
571
|
+
"probe": "LP"},
|
|
572
|
+
},
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
redp_settings = {
|
|
577
|
+
"CH1": {"var_name": "I",
|
|
578
|
+
"scaling_factor": 1/I_measurement_resistor*1e3,
|
|
579
|
+
"attrs": {"units": "mA",
|
|
580
|
+
"probe": "BPP"},
|
|
581
|
+
},
|
|
582
|
+
|
|
583
|
+
"CH2": {"var_name": "U",
|
|
584
|
+
"scaling_factor": U_divider_factor,
|
|
585
|
+
"attrs": {"units": "V",
|
|
586
|
+
"probe": "BPP"},
|
|
587
|
+
},
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
papouch_settings = {
|
|
591
|
+
"CH1": {"var_name": "U_loop",
|
|
592
|
+
"scaling_factor": 1,
|
|
593
|
+
"attrs": {"units": "V",
|
|
594
|
+
"probe": "U_loop"},
|
|
595
|
+
},
|
|
596
|
+
"CH2": {"var_name": "U_rog_coil_out",
|
|
597
|
+
"scaling_factor": 1,
|
|
598
|
+
"attrs": {"units": "V",
|
|
599
|
+
"probe": "Outer Rogcoil"},
|
|
600
|
+
},
|
|
601
|
+
"CH3": {"var_name": "U_rog_coil_in",
|
|
602
|
+
"scaling_factor": 1,
|
|
603
|
+
"attrs": {"units": "V",
|
|
604
|
+
"probe": "DIA-RING"},
|
|
605
|
+
},
|
|
606
|
+
"CH4": {"var_name": "U_diam_in",
|
|
607
|
+
"scaling_factor": 1,
|
|
608
|
+
"attrs": {"units": "V",
|
|
609
|
+
"probe": "DIA-RING"},
|
|
610
|
+
},
|
|
611
|
+
"CH5": {"var_name": "U_diam_out",
|
|
612
|
+
"scaling_factor": 1,
|
|
613
|
+
"attrs": {"units": "V",
|
|
614
|
+
"probe": "DIA-RING"},
|
|
615
|
+
},
|
|
616
|
+
"CH6": {"var_name": "U_tor_hfs",
|
|
617
|
+
"scaling_factor": 1,
|
|
618
|
+
"attrs": {"units": "V",
|
|
619
|
+
"probe": "DIA-RING"},
|
|
620
|
+
},
|
|
621
|
+
"CH7": {"var_name": "U_tor_lfs",
|
|
622
|
+
"scaling_factor": 1,
|
|
623
|
+
"attrs": {"units": "V",
|
|
624
|
+
"probe": "DIA-RING"},
|
|
625
|
+
},
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
calib_shot = 52261 # kolem toho
|
|
630
|
+
time_units = "ms"
|
|
631
|
+
|
|
632
|
+
# RP = REDPITAYA(redp_settings)
|
|
633
|
+
TEK = TEK64(tek_settings)
|
|
634
|
+
# PAP = PAPOUCH(papouch_settings)
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
# RP.load_data(calib_shot, time_units=time_units, verbose=1)
|
|
638
|
+
TEK.load_data(calib_shot, time_units=time_units, verbose=1)
|
|
639
|
+
# PAP.load_data(51672, time_units=time_units, verbose=1)
|
|
640
|
+
|
|
641
|
+
# RP.plot()
|
|
642
|
+
TEK.plot()
|
|
643
|
+
# PAP.plot()
|
|
644
|
+
|
|
645
|
+
# print(PAP.data)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from .basic import (
|
|
2
|
+
create_spectrogram_da,
|
|
3
|
+
create_stft_da,
|
|
4
|
+
load_array,
|
|
5
|
+
load_parameter,
|
|
6
|
+
remove_initial_offset,
|
|
7
|
+
smoothen_dataarray,
|
|
8
|
+
)
|
|
9
|
+
from .DAS_systems import TEK64, load_DAS, load_tek64_csv
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"TEK64",
|
|
13
|
+
"create_spectrogram_da",
|
|
14
|
+
"create_stft_da",
|
|
15
|
+
"load_DAS",
|
|
16
|
+
"load_array",
|
|
17
|
+
"load_parameter",
|
|
18
|
+
"load_tek64_csv",
|
|
19
|
+
"remove_initial_offset",
|
|
20
|
+
"smoothen_dataarray",
|
|
21
|
+
]
|
|
22
|
+
|