tecio-python 0.2.3__tar.gz → 0.2.4__tar.gz
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_python-0.2.3/tecio_python.egg-info → tecio_python-0.2.4}/PKG-INFO +1 -1
- {tecio_python-0.2.3 → tecio_python-0.2.4}/pyproject.toml +1 -1
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/__init__.py +1 -1
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/dat/_read.py +260 -30
- {tecio_python-0.2.3 → tecio_python-0.2.4/tecio_python.egg-info}/PKG-INFO +1 -1
- {tecio_python-0.2.3 → tecio_python-0.2.4}/LICENSE +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/NOTICE +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/README.md +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/setup.cfg +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/_containers.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/_io.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/_meta.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/_utils.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/__init__.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tec2mat.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tecaux.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tecdump.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tecextract.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tecfix.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tecmerge.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/teconvert.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tecscale.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tecslice.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tecstats.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/dat/__init__.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/dat/_write.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/libtecio.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/plt/__init__.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/plt/_read.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/plt/_write.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/plugins/paraview/TecplotTecioReader.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/szl/__init__.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/szl/_read.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/szl/_write.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio_python.egg-info/SOURCES.txt +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio_python.egg-info/dependency_links.txt +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio_python.egg-info/entry_points.txt +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio_python.egg-info/requires.txt +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio_python.egg-info/top_level.txt +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tests/test_cli.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tests/test_io.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tests/test_libtecio.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tests/test_read.py +0 -0
- {tecio_python-0.2.3 → tecio_python-0.2.4}/tests/test_write.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tecio-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Python interface for reading and writing Tecplot data files
|
|
5
5
|
Project-URL: Homepage, https://github.com/meersman/tecio
|
|
6
6
|
Project-URL: Documentation, https://meersman.github.io/tecio/
|
|
@@ -9,7 +9,7 @@ from importlib import metadata
|
|
|
9
9
|
try:
|
|
10
10
|
__version__ = metadata.version("tecio")
|
|
11
11
|
except metadata.PackageNotFoundError:
|
|
12
|
-
__version__ = "0.2.
|
|
12
|
+
__version__ = "0.2.4"
|
|
13
13
|
|
|
14
14
|
from . import cli, dat, libtecio, plt, szl
|
|
15
15
|
from ._containers import VariableList, ZoneList
|
|
@@ -137,6 +137,10 @@ _STR_TO_DATATYPE: dict[str, DataType] = {
|
|
|
137
137
|
#: Values per line for Write data blocks.
|
|
138
138
|
_VALUES_PER_LINE: int = 5
|
|
139
139
|
|
|
140
|
+
# Safety cap on the number of grow-and-reparse iterations the vectorized block readers
|
|
141
|
+
# will attempt before giving up and falling back to the tolerant token-by-token parser
|
|
142
|
+
_MAX_FAST_BLOCK_ITERATIONS: int = 64
|
|
143
|
+
|
|
140
144
|
|
|
141
145
|
# ======================================================================================
|
|
142
146
|
# Shared internal helpers
|
|
@@ -174,6 +178,42 @@ def _strip_comment(line: str) -> str:
|
|
|
174
178
|
return line[:idx].rstrip() if idx >= 0 else line.rstrip()
|
|
175
179
|
|
|
176
180
|
|
|
181
|
+
def _is_float_block_boundary(line: str) -> bool:
|
|
182
|
+
r"""Return ``True`` if a stripped *line* ends a float data block early.
|
|
183
|
+
|
|
184
|
+
Shared by :meth:`Read._read_float_block_slow` (line-by-line) and
|
|
185
|
+
:meth:`Read._read_float_block_fast` (bulk, on parse failure) so the two readers
|
|
186
|
+
agree on exactly where a block stops.
|
|
187
|
+
|
|
188
|
+
Example:
|
|
189
|
+
>>> _is_float_block_boundary("ZONE T=\\"next\\"")
|
|
190
|
+
True
|
|
191
|
+
"""
|
|
192
|
+
upper = line.lstrip().upper()
|
|
193
|
+
if not upper:
|
|
194
|
+
return False
|
|
195
|
+
if upper.split("=")[0].split()[0] == "ZONE":
|
|
196
|
+
return True
|
|
197
|
+
return (
|
|
198
|
+
upper.startswith(("DATASETAUXDATA", "VARAUXDATA"))
|
|
199
|
+
or upper.startswith("TITLE")
|
|
200
|
+
and "=" in upper
|
|
201
|
+
or upper.startswith("VARIABLES")
|
|
202
|
+
and "=" in upper
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _is_int_block_boundary(line: str) -> bool:
|
|
207
|
+
r"""Return ``True`` if a stripped *line* ends an int (connectivity) block.
|
|
208
|
+
|
|
209
|
+
Example:
|
|
210
|
+
>>> _is_int_block_boundary("DATASETAUXDATA foo=\\"bar\\"")
|
|
211
|
+
True
|
|
212
|
+
"""
|
|
213
|
+
upper = line.lstrip().upper()
|
|
214
|
+
return upper.startswith(("ZONE", "DATASETAUXDATA"))
|
|
215
|
+
|
|
216
|
+
|
|
177
217
|
def _infer_data_type(arr: npt.NDArray) -> DataType:
|
|
178
218
|
"""Return the most appropriate :class:`DataType` for *arr*'s dtype.
|
|
179
219
|
|
|
@@ -569,38 +609,99 @@ def _apply_varauxdata(line: str, var_auxdata_list: list) -> None:
|
|
|
569
609
|
class _LineBuffer:
|
|
570
610
|
"""Peekable iterator over stripped, comment-free text lines.
|
|
571
611
|
|
|
612
|
+
Also exposes a small *raw* interface (:meth:`take_raw`, :meth:`position`,
|
|
613
|
+
:meth:`seek`) used only by the vectorized numeric-block readers
|
|
614
|
+
(:meth:`Read._read_float_block_fast`/:meth:`Read._read_int_block_fast`). Those
|
|
615
|
+
readers bypass per-line comment-stripping for speed and instead detect the rare line
|
|
616
|
+
that *isn't* plain numeric data (a comment, a header keyword) by letting NumPy's
|
|
617
|
+
parser fail on it, then falling back to this class's ordinary stripped-line
|
|
618
|
+
interface. Keeping both interfaces on one object means the fast path can hand back
|
|
619
|
+
an exact resume position for that fallback via :meth:`position`/:meth:`seek`.
|
|
620
|
+
|
|
572
621
|
Example:
|
|
573
622
|
>>> buf = _LineBuffer(lines)
|
|
574
623
|
"""
|
|
575
624
|
|
|
625
|
+
__slots__ = ("_lines", "_peeked", "_peeked_pos", "_pos")
|
|
626
|
+
|
|
576
627
|
def __init__(self, lines: list[str]) -> None:
|
|
577
628
|
self._lines: list[str] = lines
|
|
578
629
|
self._pos: int = 0
|
|
630
|
+
# One-line lookahead cache so a peek_stripped() immediately followed by
|
|
631
|
+
# next_stripped() strips comments/whitespace only once instead of twice.
|
|
632
|
+
self._peeked: str | None = None
|
|
633
|
+
self._peeked_pos: int = -1
|
|
579
634
|
|
|
580
635
|
def has_more(self) -> bool:
|
|
581
636
|
"""Return ``True`` if there are unconsumed lines."""
|
|
582
|
-
return self._pos < len(self._lines)
|
|
637
|
+
return self._peeked is not None or self._pos < len(self._lines)
|
|
583
638
|
|
|
584
639
|
def peek_stripped(self) -> str:
|
|
585
640
|
"""Return the next non-blank stripped line without consuming it."""
|
|
641
|
+
if self._peeked is not None:
|
|
642
|
+
return self._peeked
|
|
586
643
|
pos = self._pos
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
stripped =
|
|
644
|
+
n = len(self._lines)
|
|
645
|
+
while pos < n:
|
|
646
|
+
stripped = _strip_comment(self._lines[pos]).strip()
|
|
647
|
+
pos += 1
|
|
590
648
|
if stripped:
|
|
649
|
+
self._peeked = stripped
|
|
650
|
+
self._peeked_pos = pos
|
|
591
651
|
return stripped
|
|
592
|
-
|
|
652
|
+
self._peeked = ""
|
|
653
|
+
self._peeked_pos = pos
|
|
593
654
|
return ""
|
|
594
655
|
|
|
595
656
|
def next_stripped(self) -> str:
|
|
596
657
|
"""Consume and return the next non-blank stripped line."""
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
return
|
|
658
|
+
if self._peeked is None:
|
|
659
|
+
self.peek_stripped()
|
|
660
|
+
value = self._peeked or ""
|
|
661
|
+
self._pos = self._peeked_pos
|
|
662
|
+
self._peeked = None
|
|
663
|
+
self._peeked_pos = -1
|
|
664
|
+
return value
|
|
665
|
+
|
|
666
|
+
def take_raw(self, count: int) -> list[str]:
|
|
667
|
+
"""Consume and return up to *count* **unprocessed** source lines.
|
|
668
|
+
|
|
669
|
+
Unlike :meth:`next_stripped`, this does not strip comments, skip blank lines, or
|
|
670
|
+
strip whitespace -- it is a raw slice of the underlying line list, advancing the
|
|
671
|
+
position by exactly how many lines were returned (fewer than *count* at end of
|
|
672
|
+
file). Any pending :meth:`peek_stripped` lookahead is discarded first, since
|
|
673
|
+
this call always resumes from the true unconsumed position.
|
|
674
|
+
|
|
675
|
+
Intended only for the numeric fast-path readers, which feed the result straight
|
|
676
|
+
to a whitespace-tolerant NumPy parser and don't need per-line preprocessing.
|
|
677
|
+
|
|
678
|
+
Example:
|
|
679
|
+
>>> lines = buf.take_raw(64)
|
|
680
|
+
"""
|
|
681
|
+
self._peeked = None
|
|
682
|
+
self._peeked_pos = -1
|
|
683
|
+
end = min(self._pos + count, len(self._lines))
|
|
684
|
+
out = self._lines[self._pos : end]
|
|
685
|
+
self._pos = end
|
|
686
|
+
return out
|
|
687
|
+
|
|
688
|
+
def position(self) -> int:
|
|
689
|
+
"""Return an opaque marker for the current position.
|
|
690
|
+
|
|
691
|
+
Example:
|
|
692
|
+
>>> marker = buf.position()
|
|
693
|
+
"""
|
|
694
|
+
return self._pos
|
|
695
|
+
|
|
696
|
+
def seek(self, marker: int) -> None:
|
|
697
|
+
"""Restore the position to a marker previously returned by :meth:`position`.
|
|
698
|
+
|
|
699
|
+
Example:
|
|
700
|
+
>>> buf.seek(marker)
|
|
701
|
+
"""
|
|
702
|
+
self._pos = marker
|
|
703
|
+
self._peeked = None
|
|
704
|
+
self._peeked_pos = -1
|
|
604
705
|
|
|
605
706
|
|
|
606
707
|
# ======================================================================================
|
|
@@ -865,7 +966,7 @@ class ReadZone:
|
|
|
865
966
|
zone_index: int,
|
|
866
967
|
title: str,
|
|
867
968
|
zone_type: ZoneType,
|
|
868
|
-
I: int, # noqa
|
|
969
|
+
I: int, # noqa E741
|
|
869
970
|
J: int,
|
|
870
971
|
K: int,
|
|
871
972
|
solution_time: float,
|
|
@@ -1058,7 +1159,6 @@ class Read:
|
|
|
1058
1159
|
release on exit. Provided for API consistency with the Write classes and to
|
|
1059
1160
|
support the ``with tecio.open(...) as r:`` pattern.
|
|
1060
1161
|
"""
|
|
1061
|
-
pass
|
|
1062
1162
|
|
|
1063
1163
|
# -- Properties --------------------------------------------------------------------
|
|
1064
1164
|
|
|
@@ -1270,9 +1370,7 @@ class Read:
|
|
|
1270
1370
|
# Stop at a new zone, top-level keyword, or the first data line.
|
|
1271
1371
|
if nxt_upper.split("=")[0].split()[0] == "ZONE":
|
|
1272
1372
|
break
|
|
1273
|
-
if nxt_upper.startswith("DATASETAUXDATA")
|
|
1274
|
-
"VARAUXDATA"
|
|
1275
|
-
):
|
|
1373
|
+
if nxt_upper.startswith(("DATASETAUXDATA", "VARAUXDATA")):
|
|
1276
1374
|
break
|
|
1277
1375
|
first_ch = nxt.lstrip()[0] if nxt.lstrip() else ""
|
|
1278
1376
|
# A data line begins with a numeric token (covers leading-dot values
|
|
@@ -1338,7 +1436,7 @@ class Read:
|
|
|
1338
1436
|
)
|
|
1339
1437
|
|
|
1340
1438
|
if zone_type == ZoneType.ORDERED:
|
|
1341
|
-
I = int(kv.get("I", "1") or "1") # noqa
|
|
1439
|
+
I = int(kv.get("I", "1") or "1") # noqa E741
|
|
1342
1440
|
J = int(kv.get("J", "1") or "1")
|
|
1343
1441
|
K = int(kv.get("K", "1") or "1")
|
|
1344
1442
|
num_nodes = I * J * K
|
|
@@ -1348,7 +1446,7 @@ class Read:
|
|
|
1348
1446
|
# spellings for the node and element counts.
|
|
1349
1447
|
num_nodes = int(kv.get("NODES", kv.get("N", "0")) or "0")
|
|
1350
1448
|
num_cells = int(kv.get("ELEMENTS", kv.get("E", "0")) or "0")
|
|
1351
|
-
I, J, K = num_nodes, num_cells, 0 # noqa
|
|
1449
|
+
I, J, K = num_nodes, num_cells, 0 # noqa E741
|
|
1352
1450
|
|
|
1353
1451
|
# Packing: DATAPACKING wins, then legacy F, else BLOCK (Tecplot default).
|
|
1354
1452
|
if "DATAPACKING" in kv:
|
|
@@ -1509,26 +1607,98 @@ class Read:
|
|
|
1509
1607
|
"""Read *n_values* floats from *tokens* into an intermediate float64 array.
|
|
1510
1608
|
|
|
1511
1609
|
This is a pure text-parsing primitive. Callers cast the result to each
|
|
1512
|
-
variable's real dtype
|
|
1610
|
+
variable's real dtype afterward, once individual per-variable arrays have been
|
|
1513
1611
|
split out.
|
|
1514
1612
|
|
|
1613
|
+
Dispatches to :meth:`_read_float_block_fast`, a vectorized NumPy parser that
|
|
1614
|
+
handles the overwhelming majority of (well-formed) input in a handful of C-level
|
|
1615
|
+
calls regardless of block size, and falls back to :meth:`_read_float_block_slow`
|
|
1616
|
+
-- a tolerant, token-by-token parser -- only if the fast path can't make sense
|
|
1617
|
+
of what it read (a stray comment or non-numeric token inside the block, or a
|
|
1618
|
+
boundary reached before *n_values* values were collected). The fallback re-reads
|
|
1619
|
+
from *tokens* at the position the fast path started from, so the two never
|
|
1620
|
+
disagree about what was consumed.
|
|
1621
|
+
|
|
1515
1622
|
See Also:
|
|
1516
1623
|
:meth:`_read_block_var_data`/:meth:`_read_point_var_data`.
|
|
1517
1624
|
|
|
1518
1625
|
Examples:
|
|
1519
1626
|
>>> arr = Read._read_float_block(tokens, 100)
|
|
1520
1627
|
"""
|
|
1628
|
+
if n_values <= 0:
|
|
1629
|
+
return np.empty(0, dtype=np.float64)
|
|
1630
|
+
|
|
1631
|
+
marker = tokens.position()
|
|
1632
|
+
try:
|
|
1633
|
+
arr = Read._read_float_block_fast(tokens, n_values)
|
|
1634
|
+
except ValueError:
|
|
1635
|
+
arr = None
|
|
1636
|
+
if arr is not None:
|
|
1637
|
+
return arr
|
|
1638
|
+
|
|
1639
|
+
tokens.seek(marker)
|
|
1640
|
+
return Read._read_float_block_slow(tokens, n_values)
|
|
1641
|
+
|
|
1642
|
+
@staticmethod
|
|
1643
|
+
def _read_float_block_fast(
|
|
1644
|
+
tokens: _LineBuffer, n_values: int
|
|
1645
|
+
) -> npt.NDArray | None:
|
|
1646
|
+
"""Vectorized fast path for :meth:`_read_float_block`.
|
|
1647
|
+
|
|
1648
|
+
Reads raw (unprocessed) lines in a handful of growing bulk grabs (sized from a
|
|
1649
|
+
one-line width probe, so a multi-million-value block costs a small constant
|
|
1650
|
+
number of Python-level calls). Passing ``sep`` here is the (fully supported)
|
|
1651
|
+
text-parsing mode of that function, not its deprecated binary-decoding mode.
|
|
1652
|
+
|
|
1653
|
+
Returns ``None`` (never a short array) if a block boundary is hit before
|
|
1654
|
+
*n_values* values are collected, so the caller can unambiguously fall back to
|
|
1655
|
+
the slow, boundary-aware parser. A ``ValueError`` from ``numpy.fromstring`` is
|
|
1656
|
+
allowed to propagate to the caller for the same reason.
|
|
1657
|
+
|
|
1658
|
+
Example:
|
|
1659
|
+
>>> arr = Read._read_float_block_fast(tokens, 100)
|
|
1660
|
+
|
|
1661
|
+
"""
|
|
1662
|
+
pieces: list[npt.NDArray] = []
|
|
1663
|
+
have = 0
|
|
1664
|
+
per_line: int | None = None
|
|
1665
|
+
for _ in range(_MAX_FAST_BLOCK_ITERATIONS):
|
|
1666
|
+
remaining = n_values - have
|
|
1667
|
+
if remaining <= 0:
|
|
1668
|
+
break
|
|
1669
|
+
# First grab is a single-line probe to measure the block's (typically
|
|
1670
|
+
# constant) values-per-line width
|
|
1671
|
+
n_lines = 1 if per_line is None else -(-remaining // per_line)
|
|
1672
|
+
batch = tokens.take_raw(n_lines)
|
|
1673
|
+
if not batch:
|
|
1674
|
+
return None # ran out of input before reaching n_values
|
|
1675
|
+
piece = np.fromstring("".join(batch), dtype=np.float64, sep=" ")
|
|
1676
|
+
if per_line is None:
|
|
1677
|
+
per_line = max(piece.size, 1)
|
|
1678
|
+
pieces.append(piece)
|
|
1679
|
+
have += piece.size
|
|
1680
|
+
else:
|
|
1681
|
+
return None # didn't converge; let the slow path sort it out
|
|
1682
|
+
arr = pieces[0] if len(pieces) == 1 else np.concatenate(pieces)
|
|
1683
|
+
return arr[:n_values] if arr.size >= n_values else None
|
|
1684
|
+
|
|
1685
|
+
@staticmethod
|
|
1686
|
+
def _read_float_block_slow(tokens: _LineBuffer, n_values: int) -> npt.NDArray:
|
|
1687
|
+
"""Tolerant, token-by-token fallback for :meth:`_read_float_block`.
|
|
1688
|
+
|
|
1689
|
+
Reads one stripped line at a time, converting tokens one at a time and silently
|
|
1690
|
+
skipping any that aren't valid floats, stopping at a block boundary (see
|
|
1691
|
+
:func:`_is_float_block_boundary`) even if *n_values* hasn't been reached
|
|
1692
|
+
yet. Slow by design, but guarantees a result either way.
|
|
1693
|
+
|
|
1694
|
+
Example:
|
|
1695
|
+
>>> arr = Read._read_float_block_slow(tokens, 100)
|
|
1696
|
+
|
|
1697
|
+
"""
|
|
1521
1698
|
values: list[float] = []
|
|
1522
1699
|
while len(values) < n_values and tokens.has_more():
|
|
1523
1700
|
nxt = tokens.peek_stripped()
|
|
1524
|
-
|
|
1525
|
-
if (
|
|
1526
|
-
upper.split("=")[0].split()[0] == "ZONE"
|
|
1527
|
-
or upper.startswith("DATASETAUXDATA")
|
|
1528
|
-
or upper.startswith("VARAUXDATA")
|
|
1529
|
-
or (upper.startswith("TITLE") and "=" in upper)
|
|
1530
|
-
or (upper.startswith("VARIABLES") and "=" in upper)
|
|
1531
|
-
):
|
|
1701
|
+
if _is_float_block_boundary(nxt):
|
|
1532
1702
|
break
|
|
1533
1703
|
line = tokens.next_stripped()
|
|
1534
1704
|
for tok in line.split():
|
|
@@ -1559,6 +1729,10 @@ class Read:
|
|
|
1559
1729
|
to that variable's actual declared dtype (from ``DT=``, or the spec's documented
|
|
1560
1730
|
SINGLE default) right here, where each variable's array is finalized.
|
|
1561
1731
|
|
|
1732
|
+
Reading one variable at a time (rather than all of them in a single combined
|
|
1733
|
+
call) is deliberate: :meth:`_read_float_block`'s vectorized fast path already
|
|
1734
|
+
costs only a handful of C-level calls per block regardless of size.
|
|
1735
|
+
|
|
1562
1736
|
Note:
|
|
1563
1737
|
Passive and shared variables contribute a ``None`` placeholder to the
|
|
1564
1738
|
returned list. The caller resolves shared variables against the zones parsed
|
|
@@ -1661,14 +1835,70 @@ class Read:
|
|
|
1661
1835
|
def _read_int_block(tokens: _LineBuffer, n_values: int) -> npt.NDArray:
|
|
1662
1836
|
"""Read exactly *n_values* integers from *tokens* into an int64 array.
|
|
1663
1837
|
|
|
1838
|
+
Used for connectivity (node-map) data, which can run to tens of millions of
|
|
1839
|
+
entries for large volume meshes. Follows the same vectorized-fast-path /
|
|
1840
|
+
tolerant-slow-path split as :meth:`_read_float_block`; see that method's
|
|
1841
|
+
docstring for the rationale.
|
|
1842
|
+
|
|
1664
1843
|
Examples:
|
|
1665
1844
|
>>> arr = Read._read_int_block(tokens, 24)
|
|
1666
1845
|
"""
|
|
1846
|
+
if n_values <= 0:
|
|
1847
|
+
return np.empty(0, dtype=np.int64)
|
|
1848
|
+
|
|
1849
|
+
marker = tokens.position()
|
|
1850
|
+
try:
|
|
1851
|
+
arr = Read._read_int_block_fast(tokens, n_values)
|
|
1852
|
+
except ValueError:
|
|
1853
|
+
arr = None
|
|
1854
|
+
if arr is not None:
|
|
1855
|
+
return arr
|
|
1856
|
+
|
|
1857
|
+
tokens.seek(marker)
|
|
1858
|
+
return Read._read_int_block_slow(tokens, n_values)
|
|
1859
|
+
|
|
1860
|
+
@staticmethod
|
|
1861
|
+
def _read_int_block_fast(tokens: _LineBuffer, n_values: int) -> npt.NDArray | None:
|
|
1862
|
+
"""Vectorized fast path for :meth:`_read_int_block`.
|
|
1863
|
+
|
|
1864
|
+
See :meth:`_read_float_block_fast`, which this mirrors exactly except for the
|
|
1865
|
+
target dtype.
|
|
1866
|
+
|
|
1867
|
+
Example:
|
|
1868
|
+
>>> arr = Read._read_int_block_fast(tokens, 24)
|
|
1869
|
+
"""
|
|
1870
|
+
pieces: list[npt.NDArray] = []
|
|
1871
|
+
have = 0
|
|
1872
|
+
per_line: int | None = None
|
|
1873
|
+
for _ in range(_MAX_FAST_BLOCK_ITERATIONS):
|
|
1874
|
+
remaining = n_values - have
|
|
1875
|
+
if remaining <= 0:
|
|
1876
|
+
break
|
|
1877
|
+
n_lines = 1 if per_line is None else -(-remaining // per_line)
|
|
1878
|
+
batch = tokens.take_raw(n_lines)
|
|
1879
|
+
if not batch:
|
|
1880
|
+
return None
|
|
1881
|
+
piece = np.fromstring("".join(batch), dtype=np.int64, sep=" ")
|
|
1882
|
+
if per_line is None:
|
|
1883
|
+
per_line = max(piece.size, 1)
|
|
1884
|
+
pieces.append(piece)
|
|
1885
|
+
have += piece.size
|
|
1886
|
+
else:
|
|
1887
|
+
return None
|
|
1888
|
+
arr = pieces[0] if len(pieces) == 1 else np.concatenate(pieces)
|
|
1889
|
+
return arr[:n_values] if arr.size >= n_values else None
|
|
1890
|
+
|
|
1891
|
+
@staticmethod
|
|
1892
|
+
def _read_int_block_slow(tokens: _LineBuffer, n_values: int) -> npt.NDArray:
|
|
1893
|
+
"""Tolerant, token-by-token fallback for :meth:`_read_int_block`.
|
|
1894
|
+
|
|
1895
|
+
Example:
|
|
1896
|
+
>>> arr = Read._read_int_block_slow(tokens, 24)
|
|
1897
|
+
"""
|
|
1667
1898
|
values: list[int] = []
|
|
1668
1899
|
while len(values) < n_values and tokens.has_more():
|
|
1669
1900
|
nxt = tokens.peek_stripped()
|
|
1670
|
-
|
|
1671
|
-
if upper.startswith("ZONE") or upper.startswith("DATASETAUXDATA"):
|
|
1901
|
+
if _is_int_block_boundary(nxt):
|
|
1672
1902
|
break
|
|
1673
1903
|
line = tokens.next_stripped()
|
|
1674
1904
|
for tok in line.split():
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tecio-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Python interface for reading and writing Tecplot data files
|
|
5
5
|
Project-URL: Homepage, https://github.com/meersman/tecio
|
|
6
6
|
Project-URL: Documentation, https://meersman.github.io/tecio/
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|