tecio-python 0.3.2__py3-none-any.whl → 0.4.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.
- tecio/__init__.py +29 -3
- tecio/_dat_read.py +296 -81
- tecio/_dat_write.py +206 -146
- tecio/_io.py +338 -73
- tecio/_meta.py +6 -6
- tecio/_plt_legacy.py +400 -0
- tecio/_plt_read.py +180 -41
- tecio/_plt_write.py +195 -125
- tecio/_reader.py +82 -37
- tecio/_szl_read.py +68 -61
- tecio/_szl_write.py +249 -188
- tecio/_writer.py +380 -134
- tecio/cli/tec2mat.py +21 -11
- tecio/cli/tecaux.py +77 -93
- tecio/cli/tecdump.py +1 -1
- tecio/cli/tecextract.py +68 -86
- tecio/cli/tecfix.py +45 -64
- tecio/cli/tecmerge.py +343 -66
- tecio/cli/teconvert.py +22 -37
- tecio/cli/tecscale.py +185 -109
- tecio/cli/tecslice.py +75 -100
- tecio/cli/tecstats.py +41 -41
- tecio_python-0.4.0.data/data/share/plugins/matlab/contourfe.m +111 -0
- tecio_python-0.4.0.data/data/share/plugins/matlab/tecmat2struct.m +137 -0
- {tecio_python-0.3.2.data/data/share/tecio → tecio_python-0.4.0.data/data/share}/plugins/paraview/TecplotTecioReader.py +9 -0
- tecio_python-0.4.0.data/data/share/plugins/paraview/TecplotTecioWriter.py +651 -0
- {tecio_python-0.3.2.dist-info → tecio_python-0.4.0.dist-info}/METADATA +1 -1
- tecio_python-0.4.0.dist-info/RECORD +38 -0
- {tecio_python-0.3.2.dist-info → tecio_python-0.4.0.dist-info}/WHEEL +1 -1
- tecio_python-0.3.2.dist-info/RECORD +0 -34
- {tecio_python-0.3.2.dist-info → tecio_python-0.4.0.dist-info}/entry_points.txt +0 -0
- {tecio_python-0.3.2.dist-info → tecio_python-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {tecio_python-0.3.2.dist-info → tecio_python-0.4.0.dist-info}/licenses/NOTICE +0 -0
- {tecio_python-0.3.2.dist-info → tecio_python-0.4.0.dist-info}/top_level.txt +0 -0
tecio/_io.py
CHANGED
|
@@ -29,27 +29,32 @@ Note:
|
|
|
29
29
|
approach because PLT and SZL are sequential binary formats with no in-place editing
|
|
30
30
|
capability.
|
|
31
31
|
|
|
32
|
+
Note:
|
|
33
|
+
Legacy PLT files (v75, v107, v111) are read-only, so ``"a"`` and ``"a+"`` raise
|
|
34
|
+
:exc:`ValueError` for them.
|
|
35
|
+
|
|
32
36
|
Note:
|
|
33
37
|
FEPOLYGON and FEPOLYHEDRON zones are not copied during append operations because the
|
|
34
38
|
``Write`` API does not yet expose a poly-zone writer. A :exc:`NotImplementedError`
|
|
35
39
|
is raised if such zones are encountered in the source file.
|
|
36
40
|
"""
|
|
37
41
|
|
|
42
|
+
# ruff: noqa: A001 N806
|
|
43
|
+
|
|
38
44
|
from __future__ import annotations
|
|
39
45
|
|
|
40
46
|
import os
|
|
41
47
|
import tempfile
|
|
42
48
|
from pathlib import Path
|
|
43
|
-
from typing import Any, Literal, overload
|
|
49
|
+
from typing import Any, Literal, NamedTuple, overload
|
|
44
50
|
|
|
45
|
-
import numpy as np
|
|
46
51
|
import numpy.typing as npt
|
|
47
52
|
|
|
48
53
|
from ._constants import FileType, ValueLocation, ZoneType
|
|
49
54
|
from ._containers import ZoneList
|
|
50
|
-
from ._dat_read import TecplotDatReader
|
|
55
|
+
from ._dat_read import TecplotDatReader, _peek_dat_metadata
|
|
51
56
|
from ._dat_write import TecplotDatWriter
|
|
52
|
-
from ._plt_read import TecplotPltReader
|
|
57
|
+
from ._plt_read import TecplotPltReader, _is_legacy_plt
|
|
53
58
|
from ._plt_write import TecplotPltWriter
|
|
54
59
|
from ._reader import (
|
|
55
60
|
TecplotAuxDataReader,
|
|
@@ -88,6 +93,20 @@ _HANDLERS: dict[str, dict[str, Any]] = {
|
|
|
88
93
|
"a": None,
|
|
89
94
|
"a+": None,
|
|
90
95
|
},
|
|
96
|
+
".bbin": {
|
|
97
|
+
"r": TecplotPltReader,
|
|
98
|
+
"w": TecplotPltWriter,
|
|
99
|
+
"x": None,
|
|
100
|
+
"a": None,
|
|
101
|
+
"a+": None,
|
|
102
|
+
},
|
|
103
|
+
".bbin8": {
|
|
104
|
+
"r": TecplotPltReader,
|
|
105
|
+
"w": TecplotPltWriter,
|
|
106
|
+
"x": None,
|
|
107
|
+
"a": None,
|
|
108
|
+
"a+": None,
|
|
109
|
+
},
|
|
91
110
|
".dat": {
|
|
92
111
|
"r": TecplotDatReader,
|
|
93
112
|
"w": TecplotDatWriter,
|
|
@@ -104,6 +123,9 @@ _HANDLERS: dict[str, dict[str, Any]] = {
|
|
|
104
123
|
},
|
|
105
124
|
}
|
|
106
125
|
|
|
126
|
+
# Extensions handled by the PLT reader, which may hold a legacy (read-only) version.
|
|
127
|
+
_PLT_EXTS: frozenset[str] = frozenset({".plt", ".bin", ".bbin", ".bbin8"})
|
|
128
|
+
|
|
107
129
|
# FE zone types that the Write API supports for zone copying.
|
|
108
130
|
_FE_SIMPLE: frozenset[ZoneType] = frozenset({
|
|
109
131
|
ZoneType.FELINESEG,
|
|
@@ -156,62 +178,43 @@ def _copy_zones(reader: TecplotReader, writer: TecplotWriter) -> None:
|
|
|
156
178
|
"copying is not supported by the Write API yet."
|
|
157
179
|
)
|
|
158
180
|
|
|
159
|
-
# Collect per-variable metadata and data arrays
|
|
181
|
+
# Collect per-variable metadata and data arrays
|
|
160
182
|
data: list[npt.NDArray] = []
|
|
161
183
|
value_locations: list[ValueLocation] = []
|
|
162
|
-
passive_vars:
|
|
163
|
-
var_sharing:
|
|
184
|
+
passive_vars: set[str] = set()
|
|
185
|
+
var_sharing: dict[str, int] = {}
|
|
164
186
|
|
|
165
187
|
for var in zone.variables:
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if var.is_passive() or sv is not None:
|
|
173
|
-
# No array needed; append a placeholder so indices stay aligned.
|
|
174
|
-
data.append(np.array([], dtype=np.float32))
|
|
188
|
+
sv = var.shared_zone # None, or the 0-based source-zone index shared from
|
|
189
|
+
if var.is_passive():
|
|
190
|
+
passive_vars.add(var.name)
|
|
191
|
+
elif sv is not None:
|
|
192
|
+
var_sharing[var.name] = sv
|
|
175
193
|
else:
|
|
176
194
|
data.append(var.values) # ty: ignore[invalid-argument-type]
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
value_locations, passive_vars, var_sharing, strict=False
|
|
189
|
-
)
|
|
190
|
-
if not is_p and sv == 0
|
|
191
|
-
]
|
|
192
|
-
|
|
193
|
-
common_kw: dict[str, Any] = dict(
|
|
194
|
-
title=zone.title,
|
|
195
|
-
value_locations=active_locs,
|
|
196
|
-
passive_vars=passive_vars,
|
|
197
|
-
var_sharing=var_sharing,
|
|
198
|
-
solution_time=zone.solution_time,
|
|
199
|
-
strand_id=zone.strand_id,
|
|
200
|
-
aux=dict(zone.auxdata.items()) or None,
|
|
201
|
-
)
|
|
195
|
+
value_locations.append(var.value_location)
|
|
196
|
+
|
|
197
|
+
common_kw: dict[str, Any] = {
|
|
198
|
+
"title": zone.title,
|
|
199
|
+
"value_locations": value_locations,
|
|
200
|
+
"passive_vars": passive_vars,
|
|
201
|
+
"var_sharing": var_sharing,
|
|
202
|
+
"solution_time": zone.solution_time,
|
|
203
|
+
"strand_id": zone.strand_id,
|
|
204
|
+
"aux": dict(zone.auxdata.items()) or None,
|
|
205
|
+
}
|
|
202
206
|
|
|
203
207
|
if isinstance(zone, TecplotOrderedZoneReader):
|
|
204
|
-
writer.write_ordered_zone(data=
|
|
208
|
+
writer.write_ordered_zone(data=data, **common_kw)
|
|
205
209
|
elif isinstance(zone, TecplotFEZoneReader):
|
|
206
210
|
# Forward connectivity sharing: when shared, pass ``con_sharing`` and omit
|
|
207
211
|
# the node map so the writer derives the node/cell counts from the source
|
|
208
212
|
# zone; otherwise write this zone's own node map.
|
|
209
|
-
|
|
210
|
-
con_sharing = cv if cv is not None else 0
|
|
213
|
+
con_sharing = zone.shared_connectivity # None, or 0-based source-zone index
|
|
211
214
|
writer.write_fe_zone(
|
|
212
215
|
zone_type=zone.zone_type,
|
|
213
|
-
data=
|
|
214
|
-
node_map=None if con_sharing else zone.node_map,
|
|
216
|
+
data=data,
|
|
217
|
+
node_map=None if con_sharing is not None else zone.node_map,
|
|
215
218
|
con_sharing=con_sharing,
|
|
216
219
|
**common_kw,
|
|
217
220
|
)
|
|
@@ -301,14 +304,15 @@ class AppendWrite:
|
|
|
301
304
|
value_locations (Sequence[ValueLocation] | None): Per-variable
|
|
302
305
|
:class:`~tecio.libtecio.ValueLocation` for active variables. Defaults
|
|
303
306
|
to all :attr:`~tecio.libtecio.ValueLocation.NODAL`.
|
|
304
|
-
passive_vars (
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
var_sharing (
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
duplicating large
|
|
307
|
+
passive_vars (list | tuple | set | None): Variables to mark passive: a list,
|
|
308
|
+
tuple, or set of variable names, or of 0-based indices. Defaults to all
|
|
309
|
+
active.
|
|
310
|
+
var_sharing (dict[str | int, int] | None): Variables to share from another
|
|
311
|
+
zone: a ``{variable: source zone}`` mapping (name or 0-based index to
|
|
312
|
+
0-based zone index, counting all zones including those copied from the
|
|
313
|
+
original file). Defaults to no sharing. Sharing grid coordinates from
|
|
314
|
+
zone 0 is a common pattern for transient data to avoid duplicating large
|
|
315
|
+
arrays.
|
|
312
316
|
solution_time (float): Solution time for transient data. Defaults to
|
|
313
317
|
``0.0``.
|
|
314
318
|
strand_id (int): Strand ID grouping related time steps. Zones with the same
|
|
@@ -317,13 +321,11 @@ class AppendWrite:
|
|
|
317
321
|
pairs. Defaults to ``None``.
|
|
318
322
|
|
|
319
323
|
Example:
|
|
320
|
-
Append a time step, sharing the grid from zone
|
|
324
|
+
Append a time step, sharing the grid from zone 0:
|
|
321
325
|
|
|
322
|
-
>>> n = len(tec.variables) # e.g. ["x", "y", "pressure"]
|
|
323
326
|
>>> tec.write_ordered_zone(
|
|
324
327
|
... data=[p_new], # only the non-shared variable
|
|
325
|
-
...
|
|
326
|
-
... var_sharing=[1, 1, 0], # x and y shared from zone 1
|
|
328
|
+
... var_sharing={"x": 0, "y": 0},
|
|
327
329
|
... solution_time=5.0,
|
|
328
330
|
... strand_id=1,
|
|
329
331
|
... )
|
|
@@ -350,19 +352,22 @@ class AppendWrite:
|
|
|
350
352
|
must have length ``num_nodes``; CELL_CENTERED arrays must have length
|
|
351
353
|
``num_cells``. Both are inferred from ``node_map``.
|
|
352
354
|
node_map (array-like | None): Integer array of shape ``(num_cells,
|
|
353
|
-
nodes_per_cell)`` with
|
|
354
|
-
node_map.max()``. The 32- or 64-bit write path is chosen
|
|
355
|
+
nodes_per_cell)`` with 0-based node indices. ``num_nodes =
|
|
356
|
+
node_map.max() + 1``. The 32- or 64-bit write path is chosen
|
|
357
|
+
automatically.
|
|
355
358
|
title (str | None): Zone title. Defaults to ``"FE_Zone_{n}"``.
|
|
356
359
|
value_locations (Sequence[ValueLocation] | None): Per-variable value
|
|
357
360
|
location for active variables. Defaults to all
|
|
358
361
|
:attr:`~tecio.libtecio.ValueLocation.NODAL`.
|
|
359
|
-
passive_vars (
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
362
|
+
passive_vars (list | tuple | set | None): Variables to mark passive: a list,
|
|
363
|
+
tuple, or set of variable names, or of 0-based indices. Defaults to all
|
|
364
|
+
active.
|
|
365
|
+
var_sharing (dict[str | int, int] | None): Variables to share from another
|
|
366
|
+
zone: a ``{variable: source zone}`` mapping (name or 0-based index to
|
|
367
|
+
0-based zone index). Defaults to no sharing.
|
|
368
|
+
con_sharing (int | None): Zone index to share connectivity from (0-based).
|
|
369
|
+
``None`` = write connectivity directly. Cannot be used with GLOBAL
|
|
370
|
+
face-neighbor modes. Defaults to ``None``.
|
|
366
371
|
face_neighbors (array-like | None): Face-neighbor connectivity. When
|
|
367
372
|
provided, ``num_face_cons`` is set automatically.
|
|
368
373
|
face_nbr_mode (FaceNeighborMode | None): Face-neighbor mode. Defaults to
|
|
@@ -402,7 +407,7 @@ class AppendWrite:
|
|
|
402
407
|
|
|
403
408
|
@property
|
|
404
409
|
def current_zone(self) -> int:
|
|
405
|
-
"""
|
|
410
|
+
"""0-based index of the most recently written zone, -1 before any write."""
|
|
406
411
|
return self._writer.current_zone
|
|
407
412
|
|
|
408
413
|
# -- Close and finalise ------------------------------------------------------------
|
|
@@ -506,16 +511,16 @@ class AppendReadWrite(AppendWrite):
|
|
|
506
511
|
return self._reader.num_auxdata_items
|
|
507
512
|
|
|
508
513
|
@property
|
|
509
|
-
def var_auxdata(self) -> list[TecplotAuxDataReader
|
|
514
|
+
def var_auxdata(self) -> list[TecplotAuxDataReader]:
|
|
510
515
|
"""Per-variable auxiliary data list from the *original* file."""
|
|
511
516
|
return self._reader.var_auxdata
|
|
512
517
|
|
|
513
518
|
def get_var_auxdata(self, var_index: int) -> TecplotAuxDataReader:
|
|
514
|
-
"""Return variable aux data for *var_index* (
|
|
519
|
+
"""Return variable aux data for *var_index* (0-based)."""
|
|
515
520
|
return self._reader.get_var_auxdata(var_index)
|
|
516
521
|
|
|
517
522
|
def get_zone_auxdata(self, zone_index: int) -> TecplotAuxDataReader:
|
|
518
|
-
"""Return zone aux data for *zone_index* (
|
|
523
|
+
"""Return zone aux data for *zone_index* (0-based)."""
|
|
519
524
|
return self._reader.get_zone_auxdata(zone_index)
|
|
520
525
|
|
|
521
526
|
|
|
@@ -546,6 +551,8 @@ def _open_append(
|
|
|
546
551
|
|
|
547
552
|
Raises:
|
|
548
553
|
FileNotFoundError: If *path* does not exist (nothing to append to).
|
|
554
|
+
ValueError: If *path* is a legacy PLT file (v75, v107, or v111), which is
|
|
555
|
+
read-only.
|
|
549
556
|
"""
|
|
550
557
|
src = Path(path)
|
|
551
558
|
if not src.exists():
|
|
@@ -553,6 +560,11 @@ def _open_append(
|
|
|
553
560
|
f"Cannot append to '{src}': file does not exist. "
|
|
554
561
|
"Use mode='w' to create a new file."
|
|
555
562
|
)
|
|
563
|
+
if ext in _PLT_EXTS and _is_legacy_plt(src):
|
|
564
|
+
raise ValueError(
|
|
565
|
+
f"Cannot append to '{src}': legacy PLT versions (v75, v107, v111) are "
|
|
566
|
+
"read-only. Open with mode='r', or convert to a current format first."
|
|
567
|
+
)
|
|
556
568
|
|
|
557
569
|
ReadCls = _reader_for_ext(ext)
|
|
558
570
|
WriteCls = _writer_for_ext(ext)
|
|
@@ -690,7 +702,7 @@ def open(
|
|
|
690
702
|
path (str | os.PathLike): File path. Extension determines the format handler:
|
|
691
703
|
|
|
692
704
|
- ``.szplt`` → :mod:`tecio.szl`
|
|
693
|
-
- ``.plt`` / ``.bin`` → :mod:`tecio.plt`
|
|
705
|
+
- ``.plt`` / ``.bin`` / ``.bbin`` / ``.bbin8`` → :mod:`tecio.plt`
|
|
694
706
|
- ``.dat`` / ``.tec`` → :mod:`tecio.dat`
|
|
695
707
|
|
|
696
708
|
mode (str): Access mode — mirrors Python's built-in :func:`open`:
|
|
@@ -706,6 +718,9 @@ def open(
|
|
|
706
718
|
- ``'a+'`` -- Same as ``'a'`` but the returned handle also exposes the full
|
|
707
719
|
read API populated from the original file.
|
|
708
720
|
|
|
721
|
+
Legacy PLT files (v75, v107, v111) can only be opened with ``'r'``;
|
|
722
|
+
``'a'`` and ``'a+'`` raise :exc:`ValueError` for them.
|
|
723
|
+
|
|
709
724
|
**kwargs: Forwarded to the underlying handler constructor. The accepted keyword
|
|
710
725
|
arguments differ by mode — see the tables below.
|
|
711
726
|
|
|
@@ -755,7 +770,8 @@ def open(
|
|
|
755
770
|
- ``'a+'`` → :class:`~tecio.AppendReadWrite`
|
|
756
771
|
|
|
757
772
|
Raises:
|
|
758
|
-
ValueError: Unsupported file extension
|
|
773
|
+
ValueError: Unsupported file extension, unrecognised mode, or ``'a'``/``'a+'``
|
|
774
|
+
on a legacy PLT file (v75, v107, v111).
|
|
759
775
|
FileNotFoundError: ``'r'``, ``'a'``, or ``'a+'`` on a missing file.
|
|
760
776
|
FileExistsError: ``'x'`` on an existing file.
|
|
761
777
|
NotImplementedError: Append mode on a file containing FEPOLYGON or FEPOLYHEDRON
|
|
@@ -811,3 +827,252 @@ def open(
|
|
|
811
827
|
raise ValueError(
|
|
812
828
|
f"Unrecognised mode '{mode}'. Supported modes: 'r', 'w', 'x', 'a', 'a+'"
|
|
813
829
|
)
|
|
830
|
+
|
|
831
|
+
|
|
832
|
+
# --------------------------------------------------------------------------------------
|
|
833
|
+
# Lightweight metadata queries
|
|
834
|
+
# --------------------------------------------------------------------------------------
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
def _validated_ext(path: str | os.PathLike) -> str:
|
|
838
|
+
"""Return *path*'s lowercased extension, or raise if unsupported.
|
|
839
|
+
|
|
840
|
+
Example:
|
|
841
|
+
>>> _validated_ext("flow.szplt")
|
|
842
|
+
'.szplt'
|
|
843
|
+
"""
|
|
844
|
+
ext = Path(path).suffix.lower()
|
|
845
|
+
if ext not in _HANDLERS:
|
|
846
|
+
raise ValueError(
|
|
847
|
+
f"Unsupported file extension: '{ext}'. Supported: {sorted(_HANDLERS)}"
|
|
848
|
+
)
|
|
849
|
+
return ext
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
class ZoneSummary(NamedTuple):
|
|
853
|
+
"""Per-zone metadata from a lightweight :func:`peek` query.
|
|
854
|
+
|
|
855
|
+
``num_nodes``/``num_elements`` are ``None`` only for a DAT ordered zone whose header
|
|
856
|
+
omits I/J/K entirely (rare; relies on the reader inferring the point count from the
|
|
857
|
+
data itself, which a lightweight query never does). Every other case, and every
|
|
858
|
+
SZL/PLT zone, always has both.
|
|
859
|
+
"""
|
|
860
|
+
|
|
861
|
+
title: str
|
|
862
|
+
zone_type: ZoneType
|
|
863
|
+
num_nodes: int | None
|
|
864
|
+
num_elements: int | None
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
class FileSummary(NamedTuple):
|
|
868
|
+
"""Whole-file metadata from :func:`peek`, with no variable data touched."""
|
|
869
|
+
|
|
870
|
+
title: str
|
|
871
|
+
file_type: FileType
|
|
872
|
+
variable_names: list[str]
|
|
873
|
+
zones: list[ZoneSummary]
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
def get_variable_list(path: str | os.PathLike) -> list[str]:
|
|
877
|
+
"""Return a file's variable names without reading any variable data.
|
|
878
|
+
|
|
879
|
+
For SZL and PLT this is already what a normal :func:`open` does, both formats
|
|
880
|
+
resolve dataset-level metadata (including variable names) up front without touching
|
|
881
|
+
data arrays, so this is a thin convenience wrapper for those two. DAT is different:
|
|
882
|
+
opening it normally always reads and numerically converts every value in the file,
|
|
883
|
+
so this function instead scans just the file's header text, several orders of
|
|
884
|
+
magnitude cheaper for a file with large data blocks.
|
|
885
|
+
|
|
886
|
+
Args:
|
|
887
|
+
path: Path to a ``.szplt``, ``.plt``/``.bin``, or ``.dat``/``.tec`` file.
|
|
888
|
+
|
|
889
|
+
Returns:
|
|
890
|
+
Variable names, in file order.
|
|
891
|
+
|
|
892
|
+
Raises:
|
|
893
|
+
FileNotFoundError: If *path* does not exist.
|
|
894
|
+
ValueError: If the file extension is not recognized.
|
|
895
|
+
|
|
896
|
+
Example:
|
|
897
|
+
>>> get_variable_list("flow.szplt")
|
|
898
|
+
['x', 'y', 'z', 'pressure']
|
|
899
|
+
"""
|
|
900
|
+
ext = _validated_ext(path)
|
|
901
|
+
if ext in (".dat", ".tec"):
|
|
902
|
+
return _peek_dat_metadata(path).variable_names
|
|
903
|
+
with _HANDLERS[ext]["r"](str(path)) as r:
|
|
904
|
+
return list(r.variables)
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
def get_zone_list(path: str | os.PathLike) -> list[str]:
|
|
908
|
+
"""Return a file's zone titles without reading any variable data.
|
|
909
|
+
|
|
910
|
+
Same rationale as :func:`get_variable_list`: already cheap for SZL and PLT
|
|
911
|
+
(constructing a zone reader only resolves scalar metadata, never variable data),
|
|
912
|
+
so this is a thin wrapper for those; for DAT, uses a dedicated scan that skips
|
|
913
|
+
every data and connectivity block entirely.
|
|
914
|
+
|
|
915
|
+
Args:
|
|
916
|
+
path: Path to a ``.szplt``, ``.plt``/``.bin``, or ``.dat``/``.tec`` file.
|
|
917
|
+
|
|
918
|
+
Returns:
|
|
919
|
+
Zone titles, in file order. An unnamed zone contributes an empty string, not
|
|
920
|
+
a placeholder like ``"Zone 1"``.
|
|
921
|
+
|
|
922
|
+
Raises:
|
|
923
|
+
FileNotFoundError: If *path* does not exist.
|
|
924
|
+
ValueError: If the file extension is not recognized.
|
|
925
|
+
|
|
926
|
+
Example:
|
|
927
|
+
>>> get_zone_list("flow.szplt")
|
|
928
|
+
['FluidVolume', 'WingSurface']
|
|
929
|
+
"""
|
|
930
|
+
ext = _validated_ext(path)
|
|
931
|
+
if ext in (".dat", ".tec"):
|
|
932
|
+
return [z.title for z in _peek_dat_metadata(path).zones]
|
|
933
|
+
with _HANDLERS[ext]["r"](str(path)) as r:
|
|
934
|
+
return [zone.title for zone in r.zones]
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
def get_num_zones(path: str | os.PathLike) -> int:
|
|
938
|
+
"""Return a file's zone count without reading any variable data.
|
|
939
|
+
|
|
940
|
+
Cheaper still than :func:`get_zone_list`: for SZL, a single scalar C-library
|
|
941
|
+
call with no zone objects constructed at all; for PLT and DAT, a count against
|
|
942
|
+
already-parsed/scanned header metadata.
|
|
943
|
+
|
|
944
|
+
Args:
|
|
945
|
+
path: Path to a ``.szplt``, ``.plt``/``.bin``, or ``.dat``/``.tec`` file.
|
|
946
|
+
|
|
947
|
+
Returns:
|
|
948
|
+
Number of zones in the file.
|
|
949
|
+
|
|
950
|
+
Raises:
|
|
951
|
+
FileNotFoundError: If *path* does not exist.
|
|
952
|
+
ValueError: If the file extension is not recognized.
|
|
953
|
+
|
|
954
|
+
Example:
|
|
955
|
+
>>> get_num_zones("flow.szplt")
|
|
956
|
+
2
|
|
957
|
+
"""
|
|
958
|
+
ext = _validated_ext(path)
|
|
959
|
+
if ext in (".dat", ".tec"):
|
|
960
|
+
return len(_peek_dat_metadata(path).zones)
|
|
961
|
+
with _HANDLERS[ext]["r"](str(path)) as r:
|
|
962
|
+
return r.num_zones
|
|
963
|
+
|
|
964
|
+
|
|
965
|
+
def get_num_variables(path: str | os.PathLike) -> int:
|
|
966
|
+
"""Return a file's variable count without reading any variable data.
|
|
967
|
+
|
|
968
|
+
Cheaper still than :func:`get_variable_list`: for SZL, a single scalar
|
|
969
|
+
C-library call.
|
|
970
|
+
|
|
971
|
+
Args:
|
|
972
|
+
path: Path to a ``.szplt``, ``.plt``/``.bin``, or ``.dat``/``.tec`` file.
|
|
973
|
+
|
|
974
|
+
Returns:
|
|
975
|
+
Number of variables in the dataset.
|
|
976
|
+
|
|
977
|
+
Raises:
|
|
978
|
+
FileNotFoundError: If *path* does not exist.
|
|
979
|
+
ValueError: If the file extension is not recognized.
|
|
980
|
+
|
|
981
|
+
Example:
|
|
982
|
+
>>> get_num_variables("flow.szplt")
|
|
983
|
+
4
|
|
984
|
+
"""
|
|
985
|
+
ext = _validated_ext(path)
|
|
986
|
+
if ext in (".dat", ".tec"):
|
|
987
|
+
return len(_peek_dat_metadata(path).variable_names)
|
|
988
|
+
with _HANDLERS[ext]["r"](str(path)) as r:
|
|
989
|
+
return r.num_vars
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
def get_title(path: str | os.PathLike) -> str:
|
|
993
|
+
"""Return a file's dataset title without reading any variable data.
|
|
994
|
+
|
|
995
|
+
Args:
|
|
996
|
+
path: Path to a ``.szplt``, ``.plt``/``.bin``, or ``.dat``/``.tec`` file.
|
|
997
|
+
|
|
998
|
+
Returns:
|
|
999
|
+
Dataset title string.
|
|
1000
|
+
|
|
1001
|
+
Raises:
|
|
1002
|
+
FileNotFoundError: If *path* does not exist.
|
|
1003
|
+
ValueError: If the file extension is not recognized.
|
|
1004
|
+
|
|
1005
|
+
Example:
|
|
1006
|
+
>>> get_title("flow.szplt")
|
|
1007
|
+
'Onera M6 wing'
|
|
1008
|
+
"""
|
|
1009
|
+
ext = _validated_ext(path)
|
|
1010
|
+
if ext in (".dat", ".tec"):
|
|
1011
|
+
return _peek_dat_metadata(path).title
|
|
1012
|
+
with _HANDLERS[ext]["r"](str(path)) as r:
|
|
1013
|
+
return r.title
|
|
1014
|
+
|
|
1015
|
+
|
|
1016
|
+
def get_file_type(path: str | os.PathLike) -> FileType:
|
|
1017
|
+
"""Return a file's :class:`~tecio.FileType` without reading any variable data.
|
|
1018
|
+
|
|
1019
|
+
Args:
|
|
1020
|
+
path: Path to a ``.szplt``, ``.plt``/``.bin``, or ``.dat``/``.tec`` file.
|
|
1021
|
+
|
|
1022
|
+
Returns:
|
|
1023
|
+
The file type (``FULL``, ``GRID``, or ``SOLUTION``).
|
|
1024
|
+
|
|
1025
|
+
Raises:
|
|
1026
|
+
FileNotFoundError: If *path* does not exist.
|
|
1027
|
+
ValueError: If the file extension is not recognized.
|
|
1028
|
+
|
|
1029
|
+
Example:
|
|
1030
|
+
>>> get_file_type("flow.szplt")
|
|
1031
|
+
<FileType.FULL: 0>
|
|
1032
|
+
"""
|
|
1033
|
+
ext = _validated_ext(path)
|
|
1034
|
+
if ext in (".dat", ".tec"):
|
|
1035
|
+
return _peek_dat_metadata(path).file_type
|
|
1036
|
+
with _HANDLERS[ext]["r"](str(path)) as r:
|
|
1037
|
+
return r.file_type
|
|
1038
|
+
|
|
1039
|
+
|
|
1040
|
+
def peek(path: str | os.PathLike) -> FileSummary:
|
|
1041
|
+
"""Return a file's title, file type, variable names, and per-zone summaries.
|
|
1042
|
+
|
|
1043
|
+
A single combined query covering everything :func:`get_variable_list`,
|
|
1044
|
+
:func:`get_zone_list`, :func:`get_title`, and :func:`get_file_type` return, plus
|
|
1045
|
+
each zone's type and size. For DAT, calling several of those functions
|
|
1046
|
+
separately on the same file means scanning it that many times over, this
|
|
1047
|
+
function scans once no matter how much of the summary is needed.
|
|
1048
|
+
|
|
1049
|
+
Args:
|
|
1050
|
+
path: Path to a ``.szplt``, ``.plt``/``.bin``, or ``.dat``/``.tec`` file.
|
|
1051
|
+
|
|
1052
|
+
Returns:
|
|
1053
|
+
Whole-file and per-zone metadata. See :class:`FileSummary` and
|
|
1054
|
+
:class:`ZoneSummary`.
|
|
1055
|
+
|
|
1056
|
+
Raises:
|
|
1057
|
+
FileNotFoundError: If *path* does not exist.
|
|
1058
|
+
ValueError: If the file extension is not recognized.
|
|
1059
|
+
|
|
1060
|
+
Example:
|
|
1061
|
+
>>> summary = peek("flow.szplt")
|
|
1062
|
+
>>> [z.title for z in summary.zones]
|
|
1063
|
+
['FluidVolume', 'WingSurface']
|
|
1064
|
+
"""
|
|
1065
|
+
ext = _validated_ext(path)
|
|
1066
|
+
if ext in (".dat", ".tec"):
|
|
1067
|
+
meta = _peek_dat_metadata(path)
|
|
1068
|
+
zones = [
|
|
1069
|
+
ZoneSummary(z.title, z.zone_type, z.num_nodes, z.num_elements)
|
|
1070
|
+
for z in meta.zones
|
|
1071
|
+
]
|
|
1072
|
+
return FileSummary(meta.title, meta.file_type, meta.variable_names, zones)
|
|
1073
|
+
with _HANDLERS[ext]["r"](str(path)) as r:
|
|
1074
|
+
zones = [
|
|
1075
|
+
ZoneSummary(zone.title, zone.zone_type, zone.num_nodes, zone.num_elements)
|
|
1076
|
+
for zone in r.zones
|
|
1077
|
+
]
|
|
1078
|
+
return FileSummary(r.title, r.file_type, list(r.variables), zones)
|
tecio/_meta.py
CHANGED
|
@@ -56,7 +56,7 @@ class ZoneMeta:
|
|
|
56
56
|
variable-type arrays of a ``TECZNE142`` zone header.
|
|
57
57
|
|
|
58
58
|
Attributes:
|
|
59
|
-
index:
|
|
59
|
+
index: 0-based index of this zone within the dataset.
|
|
60
60
|
title: Zone title.
|
|
61
61
|
zone_type: The zone's :class:`~tecio.libtecio.ZoneType`.
|
|
62
62
|
solution_time: Solution time (``0.0`` for static zones).
|
|
@@ -72,8 +72,8 @@ class ZoneMeta:
|
|
|
72
72
|
else ``None`` if it has none.
|
|
73
73
|
value_locations: Per-variable value location, full dataset length.
|
|
74
74
|
passive_vars: Per-variable passive flags, full dataset length.
|
|
75
|
-
shared_vars: Per-variable share-from zone index
|
|
76
|
-
shared
|
|
75
|
+
shared_vars: Per-variable share-from zone index, or ``None`` for not
|
|
76
|
+
shared, full dataset length.
|
|
77
77
|
data_types: Per-variable data type, full dataset length.
|
|
78
78
|
|
|
79
79
|
"""
|
|
@@ -93,7 +93,7 @@ class ZoneMeta:
|
|
|
93
93
|
# Per-variable descriptors (length == dataset variable count).
|
|
94
94
|
value_locations: tuple[ValueLocation, ...] = ()
|
|
95
95
|
passive_vars: tuple[bool, ...] = ()
|
|
96
|
-
shared_vars: tuple[int, ...] = ()
|
|
96
|
+
shared_vars: tuple[int | None, ...] = ()
|
|
97
97
|
data_types: tuple[DataType, ...] = ()
|
|
98
98
|
|
|
99
99
|
@property
|
|
@@ -133,7 +133,7 @@ class WriterMeta:
|
|
|
133
133
|
header has been written (lazy-open).
|
|
134
134
|
num_dataset_aux_items: Count of dataset-level aux items written.
|
|
135
135
|
num_var_aux_items: Total variable-level aux items written.
|
|
136
|
-
zones: Mapping of
|
|
136
|
+
zones: Mapping of 0-based zone index to :class:`ZoneMeta`,
|
|
137
137
|
in write order.
|
|
138
138
|
"""
|
|
139
139
|
|
|
@@ -173,7 +173,7 @@ class WriterMeta:
|
|
|
173
173
|
self.num_var_aux_items += count
|
|
174
174
|
|
|
175
175
|
def record_zone(self, zone: ZoneMeta) -> None:
|
|
176
|
-
"""Register a fully written zone by its
|
|
176
|
+
"""Register a fully written zone by its 0-based index."""
|
|
177
177
|
self.zones[zone.index] = zone
|
|
178
178
|
|
|
179
179
|
# -- Retrieval helpers ------------------------------------------------------------
|