tecio-python 0.1.1__tar.gz → 0.2.1__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.
Files changed (51) hide show
  1. {tecio_python-0.1.1/tecio_python.egg-info → tecio_python-0.2.1}/PKG-INFO +2 -2
  2. {tecio_python-0.1.1 → tecio_python-0.2.1}/README.md +1 -1
  3. {tecio_python-0.1.1 → tecio_python-0.2.1}/pyproject.toml +3 -2
  4. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/__init__.py +1 -2
  5. tecio_python-0.2.1/tecio/_meta.py +168 -0
  6. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/cli/tec2mat.py +46 -20
  7. tecio_python-0.2.1/tecio/cli/tecaux.py +700 -0
  8. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/cli/tecdump.py +50 -43
  9. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/cli/tecextract.py +51 -18
  10. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/cli/tecfix.py +25 -19
  11. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/cli/tecmerge.py +41 -19
  12. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/cli/teconvert.py +23 -16
  13. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/cli/tecscale.py +11 -8
  14. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/cli/tecslice.py +15 -9
  15. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/cli/tecstats.py +14 -14
  16. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/dat/_read.py +157 -29
  17. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/dat/_write.py +363 -77
  18. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/libtecio.py +111 -17
  19. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/plt/_read.py +207 -90
  20. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/plt/_write.py +379 -146
  21. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/szl/_read.py +75 -26
  22. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/szl/_write.py +553 -298
  23. {tecio_python-0.1.1 → tecio_python-0.2.1/tecio_python.egg-info}/PKG-INFO +2 -2
  24. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio_python.egg-info/SOURCES.txt +5 -8
  25. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio_python.egg-info/entry_points.txt +1 -0
  26. tecio_python-0.2.1/tests/test_cli.py +2016 -0
  27. tecio_python-0.2.1/tests/test_read.py +515 -0
  28. tecio_python-0.2.1/tests/test_write.py +1163 -0
  29. tecio_python-0.1.1/tests/test_cli.py +0 -1005
  30. tecio_python-0.1.1/tests/test_dat_read.py +0 -438
  31. tecio_python-0.1.1/tests/test_dat_write.py +0 -1020
  32. tecio_python-0.1.1/tests/test_plt_read.py +0 -265
  33. tecio_python-0.1.1/tests/test_plt_write.py +0 -634
  34. tecio_python-0.1.1/tests/test_szl_read.py +0 -262
  35. tecio_python-0.1.1/tests/test_szl_write.py +0 -816
  36. tecio_python-0.1.1/tests/test_tec2mat.py +0 -320
  37. {tecio_python-0.1.1 → tecio_python-0.2.1}/LICENSE +0 -0
  38. {tecio_python-0.1.1 → tecio_python-0.2.1}/NOTICE +0 -0
  39. {tecio_python-0.1.1 → tecio_python-0.2.1}/setup.cfg +0 -0
  40. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/_containers.py +0 -0
  41. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/_io.py +0 -0
  42. /tecio_python-0.1.1/tecio/utils.py → /tecio_python-0.2.1/tecio/_utils.py +0 -0
  43. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/cli/__init__.py +0 -0
  44. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/dat/__init__.py +0 -0
  45. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/plt/__init__.py +0 -0
  46. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio/szl/__init__.py +0 -0
  47. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio_python.egg-info/dependency_links.txt +0 -0
  48. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio_python.egg-info/requires.txt +0 -0
  49. {tecio_python-0.1.1 → tecio_python-0.2.1}/tecio_python.egg-info/top_level.txt +0 -0
  50. {tecio_python-0.1.1 → tecio_python-0.2.1}/tests/test_io.py +0 -0
  51. {tecio_python-0.1.1 → tecio_python-0.2.1}/tests/test_libtecio.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tecio-python
3
- Version: 0.1.1
3
+ Version: 0.2.1
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/
@@ -97,7 +97,7 @@ Reading a file:
97
97
 
98
98
  ```python
99
99
  with tecio.open("sine.szplt", "r") as tec:
100
- print(tec.variables) # ['x', 'y']
100
+ print(tec.variables) # ['x', 'y']
101
101
  x = tec.zone[0].variable[0].values
102
102
  y = tec.zone[0].variable[1].values
103
103
  # or
@@ -53,7 +53,7 @@ Reading a file:
53
53
 
54
54
  ```python
55
55
  with tecio.open("sine.szplt", "r") as tec:
56
- print(tec.variables) # ['x', 'y']
56
+ print(tec.variables) # ['x', 'y']
57
57
  x = tec.zone[0].variable[0].values
58
58
  y = tec.zone[0].variable[1].values
59
59
  # or
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tecio-python"
7
- version = "0.1.1"
7
+ version = "0.2.1"
8
8
  description = "Python interface for reading and writing Tecplot data files"
9
9
 
10
10
  readme = "README.md"
@@ -37,6 +37,8 @@ Issues = "https://github.com/meersman/tecio/issues"
37
37
  Changelog = "https://github.com/meersman/tecio/blob/main/docs/changelog.md"
38
38
 
39
39
  [project.scripts]
40
+ tec2mat = "tecio.cli.tec2mat:main"
41
+ tecaux = "tecio.cli.tecaux:main"
40
42
  teconvert = "tecio.cli.teconvert:main"
41
43
  tecdump = "tecio.cli.tecdump:main"
42
44
  tecextract = "tecio.cli.tecextract:main"
@@ -45,7 +47,6 @@ tecmerge = "tecio.cli.tecmerge:main"
45
47
  tecscale = "tecio.cli.tecscale:main"
46
48
  tecslice = "tecio.cli.tecslice:main"
47
49
  tecstats = "tecio.cli.tecstats:main"
48
- tec2mat = "tecio.cli.tec2mat:main"
49
50
 
50
51
  [project.optional-dependencies]
51
52
  dev = [
@@ -11,7 +11,7 @@ try:
11
11
  except metadata.PackageNotFoundError:
12
12
  __version__ = "0.0.0"
13
13
 
14
- from . import cli, dat, libtecio, plt, szl, utils
14
+ from . import cli, dat, libtecio, plt, szl
15
15
  from ._containers import VariableList, ZoneList
16
16
  from ._io import AppendReadWrite, AppendWrite, open
17
17
 
@@ -28,7 +28,6 @@ __all__ = [
28
28
  "dat",
29
29
  "plt",
30
30
  "szl",
31
- "utils",
32
31
  "cli",
33
32
  "AppendWrite",
34
33
  "AppendReadWrite",
@@ -0,0 +1,168 @@
1
+ r"""Structural metadata records shared by the Tecplot file writers.
2
+
3
+ The writer for each output format -- SZL (``.szplt``), PLT (``.plt``), and ASCII DAT
4
+ (``.dat``) -- keeps a running, in-memory description of what it has committed to disk:
5
+ the dataset header, auxiliary-data counts, and one record per zone. Defining that
6
+ description once here lets all three writers share a single representation instead of
7
+ each maintaining ad-hoc bookkeeping.
8
+
9
+ The records mirror the file- and zone-level fields of the Tecplot data format
10
+ (``TECINI142`` for the file header and ``TECZNE142`` for each zone): dataset title, file
11
+ type, variable names, and the per-variable passive, value-location, and share-from
12
+ arrays, together with per-zone dimensions and aux-item counts.
13
+
14
+ Design notes:
15
+ * Only lightweight descriptors are stored -- shapes, enums, and small integers --
16
+ never the variable data arrays, so the record stays cheap in memory even for files
17
+ with many zones.
18
+ * ``slots=True`` removes the per-instance ``__dict__``, and immutable tuple fields
19
+ keep the per-variable lists compact. A :class:`ZoneMeta` is a write-once snapshot
20
+ and is therefore ``frozen``; :class:`WriterMeta` is mutable because it grows as
21
+ zones are written.
22
+ * Enum types are imported only under :data:`typing.TYPE_CHECKING`. With ``from
23
+ __future__ import annotations`` the annotations are never evaluated at runtime,
24
+ which keeps this module free of any import cycle with :mod:`tecio.libtecio`.
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ from dataclasses import dataclass, field
30
+ from typing import TYPE_CHECKING
31
+
32
+ if TYPE_CHECKING:
33
+ from .libtecio import DataType, FileType, ValueLocation, ZoneType
34
+
35
+
36
+ # =====================================================================================
37
+ # Zone-level record
38
+ # =====================================================================================
39
+
40
+
41
+ @dataclass(slots=True, frozen=True)
42
+ class ZoneMeta:
43
+ """Lightweight, write-once description of a single written zone.
44
+
45
+ Per-variable fields (:attr:`value_locations`, :attr:`passive_vars`,
46
+ :attr:`shared_vars`, :attr:`data_types`) span every dataset variable and correspond
47
+ to the ``ValueLocation``, ``PassiveVarList``, ``ShareVarFromZone``, and
48
+ variable-type arrays of a ``TECZNE142`` zone header.
49
+
50
+ Attributes:
51
+ index: 1-based zone index returned by the C library.
52
+ title: Zone title.
53
+ zone_type: The zone's :class:`~tecio.libtecio.ZoneType`.
54
+ solution_time: Solution time (``0.0`` for static zones).
55
+ strand_id: Strand ID (``0`` for static zones).
56
+ num_aux_items: Number of zone-level auxiliary items written.
57
+ dimensions: Nodal ``(imax, jmax, kmax)`` for ORDERED zones, else ``None``.
58
+ num_nodes: Node count for FE zones, else ``None``.
59
+ num_elements: Element count for FE zones, else ``None``.
60
+ value_locations: Per-variable value location, full dataset length.
61
+ passive_vars: Per-variable passive flags, full dataset length.
62
+ shared_vars: Per-variable share-from zone index (1-based; ``0`` for not
63
+ shared), full dataset length.
64
+ data_types: Per-variable data type, full dataset length.
65
+
66
+ """
67
+
68
+ index: int
69
+ title: str
70
+ zone_type: ZoneType
71
+ solution_time: float = 0.0
72
+ strand_id: int = 0
73
+ num_aux_items: int = 0
74
+ # Ordered zones carry IJK dimensions; FE zones carry node/element counts.
75
+ dimensions: tuple[int, int, int] | None = None
76
+ num_nodes: int | None = None
77
+ num_elements: int | None = None
78
+ # Per-variable descriptors (length == dataset variable count).
79
+ value_locations: tuple[ValueLocation, ...] = ()
80
+ passive_vars: tuple[bool, ...] = ()
81
+ shared_vars: tuple[int, ...] = ()
82
+ data_types: tuple[DataType, ...] = ()
83
+
84
+ @property
85
+ def nodal_shape(self) -> tuple[int, int, int] | None:
86
+ """Nodal ``(imax, jmax, kmax)`` for ORDERED zones, else ``None``."""
87
+ return self.dimensions
88
+
89
+ @property
90
+ def cell_shape(self) -> tuple[int, ...] | None:
91
+ """Cell-centred ``(imax-1, jmax-1, kmax-1)`` (floored at 1) or ``None``."""
92
+ if self.dimensions is None:
93
+ return None
94
+ return tuple(max(n - 1, 1) for n in self.dimensions)
95
+
96
+
97
+ # =====================================================================================
98
+ # Dataset-level record
99
+ # =====================================================================================
100
+
101
+
102
+ @dataclass(slots=True)
103
+ class WriterMeta:
104
+ """Running record of everything a writer has committed to a file.
105
+
106
+ Populated incrementally as the file header, auxiliary data, and zones are
107
+ written, so it always reflects the current on-disk state. It is the single
108
+ source of truth for cross-zone validation (for example, resolving the shape
109
+ of a shared variable) and is suitable for summarising the writer state.
110
+
111
+ Attributes:
112
+ path: Output file path.
113
+ title: Dataset title.
114
+ file_type: :class:`~tecio.libtecio.FileType` of the output.
115
+ file_format: Format tag, e.g. ``"szplt"``, ``"plt"``, or
116
+ ``"dat"``.
117
+ variables: Variable name list, or ``None`` before the file
118
+ header has been written (lazy-open).
119
+ num_dataset_aux_items: Count of dataset-level aux items written.
120
+ num_var_aux_items: Total variable-level aux items written.
121
+ zones: Mapping of 1-based zone index to :class:`ZoneMeta`,
122
+ in write order.
123
+ """
124
+
125
+ path: str
126
+ title: str
127
+ file_type: FileType
128
+ file_format: str
129
+ variables: list[str] | None = None
130
+ num_dataset_aux_items: int = 0
131
+ num_var_aux_items: int = 0
132
+ zones: dict[int, ZoneMeta] = field(default_factory=dict)
133
+
134
+ # -- Derived quantities -----------------------------------------------------------
135
+
136
+ @property
137
+ def num_vars(self) -> int:
138
+ """Number of dataset variables, or ``0`` before the header is written."""
139
+ return len(self.variables) if self.variables is not None else 0
140
+
141
+ @property
142
+ def num_zones(self) -> int:
143
+ """Number of zones written so far."""
144
+ return len(self.zones)
145
+
146
+ # -- Update methods (called by the writer as it commits data) ---------------------
147
+
148
+ def set_variables(self, names: list[str]) -> None:
149
+ """Record the dataset variable names once the header is written."""
150
+ self.variables = names
151
+
152
+ def note_dataset_aux(self, count: int) -> None:
153
+ """Accumulate the number of dataset-level aux items written."""
154
+ self.num_dataset_aux_items += count
155
+
156
+ def note_var_aux(self, count: int) -> None:
157
+ """Accumulate the number of variable-level aux items written."""
158
+ self.num_var_aux_items += count
159
+
160
+ def record_zone(self, zone: ZoneMeta) -> None:
161
+ """Register a fully written zone by its 1-based index."""
162
+ self.zones[zone.index] = zone
163
+
164
+ # -- Retrieval helpers ------------------------------------------------------------
165
+
166
+ def zone(self, index: int) -> ZoneMeta | None:
167
+ """Return the :class:`ZoneMeta` for *index*, or ``None`` if unknown."""
168
+ return self.zones.get(index)
@@ -69,7 +69,10 @@ Output structure:
69
69
  .var_locations cell 'NODAL' | 'CELL_CENTERED' | ''
70
70
  .var_dtypes cell 'FLOAT' | 'DOUBLE' | 'INT32' | ...
71
71
  .var_shared_from array 1-based source zone, or 0 if not shared
72
- .node_map array (num_elements x nodes_per_cell), FE only
72
+ .node_map array (num_elements x nodes_per_cell), FE only,
73
+ omitted if connectivity is shared
74
+ .node_map_shared_from double 1-based source zone, FE only, present only
75
+ if this zone shares its connectivity
73
76
 
74
77
  Variable arrays are stored at their on-disk NumPy dtype, so single/double/integer
75
78
  precision is preserved. The real variable names are kept only in ``info.var_names``
@@ -79,7 +82,9 @@ Output structure:
79
82
  Passive and shared variables carry no data: their ``var_<k>`` field is an empty
80
83
  matrix ``[]``. A shared variable is therefore never duplicated on disk -- the data
81
84
  lives in its source zone and ``var_shared_from`` records where, so the MATLAB user
82
- can dereference it (see the examples below).
85
+ can dereference it (see the examples below). Shared FE connectivity follows the
86
+ same convention: a zone sharing its node map has no ``node_map`` field at all, only
87
+ ``node_map_shared_from``.
83
88
 
84
89
  Examples:
85
90
  Convert an SZL file to ``flow.mat``::
@@ -119,6 +124,17 @@ Examples:
119
124
  end
120
125
  end
121
126
 
127
+ Shared FE connectivity resolves the same way, via ``node_map_shared_from``::
128
+
129
+ function m = tecnodemap(d, zoneIdx)
130
+ z = d.(sprintf('zone_%d', zoneIdx));
131
+ if isfield(z, 'node_map')
132
+ m = z.node_map;
133
+ else
134
+ m = d.(sprintf('zone_%d', z.node_map_shared_from)).node_map;
135
+ end
136
+ end
137
+
122
138
  See Also:
123
139
  * :mod:`tecio.cli.teconvert`: Convert between Tecplot file formats (``.szplt``,
124
140
  ``.plt``, ``.dat``) without leaving the Tecplot ecosystem.
@@ -153,9 +169,9 @@ import numpy as np
153
169
  from .. import open as tecio_open
154
170
  from ..libtecio import ZoneType
155
171
 
156
- # ---------------------------------------------------------------------------
172
+ # --------------------------------------------------------------------------------------
157
173
  # Constants
158
- # ---------------------------------------------------------------------------
174
+ # --------------------------------------------------------------------------------------
159
175
 
160
176
  #: Zone types whose connectivity is face-based and cannot be read; their
161
177
  #: node map is omitted from the output.
@@ -165,17 +181,18 @@ _FE_POLY: frozenset[ZoneType] = frozenset({
165
181
  })
166
182
 
167
183
 
168
- # ---------------------------------------------------------------------------
184
+ # --------------------------------------------------------------------------------------
169
185
  # Argument parsing
170
- # ---------------------------------------------------------------------------
186
+ # --------------------------------------------------------------------------------------
171
187
 
172
188
 
173
189
  def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
174
190
  parser = argparse.ArgumentParser(
175
191
  prog="tec2mat",
176
192
  description=(
177
- "Convert a Tecplot file to a MATLAB .mat file. Each input file maps to "
178
- "one output file, with every zone stored as a named struct."
193
+ # -|-------------------|---------------------------------------------|
194
+ "Convert a Tecplot file to a MATLAB .mat file. Each input file maps\n"
195
+ "to one output file, with every zone stored as a named struct."
179
196
  ),
180
197
  epilog=(
181
198
  "Example usage:\n"
@@ -236,9 +253,9 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
236
253
  return parser.parse_args(argv)
237
254
 
238
255
 
239
- # ---------------------------------------------------------------------------
256
+ # --------------------------------------------------------------------------------------
240
257
  # Conversion helpers
241
- # ---------------------------------------------------------------------------
258
+ # --------------------------------------------------------------------------------------
242
259
 
243
260
 
244
261
  def _build_info_dict(reader: Any) -> dict[str, Any]:
@@ -360,19 +377,28 @@ def _zone_to_dict(zone: Any, num_vars: int) -> dict[str, Any]:
360
377
  d["var_dtypes"] = np.array(dtypes, dtype=object)
361
378
  d["var_shared_from"] = shared_from
362
379
 
363
- # Connectivity for simple FE zones only. Ordered zones have no node map,
364
- # and poly zones expose none through the readers.
380
+ # Connectivity for simple FE zones only. Ordered zones have no node map, and poly
381
+ # zones expose none through the readers.
382
+ #
383
+ # A zone that shares connectivity is handled the same way as a shared variable
384
+ # above: rather than duplicating the (potentially large) node map into every zone's
385
+ # struct, store the 1-based source zone number so the MATLAB user can dereference
386
+ # zone_<src>.node_map themselves.
365
387
  if zone.zone_type != ZoneType.ORDERED and zone.zone_type not in _FE_POLY:
366
- node_map = zone.node_map
367
- if node_map is not None:
368
- d["node_map"] = node_map
388
+ con_src = zone.shared_connectivity
389
+ if con_src is not None:
390
+ d["node_map_shared_from"] = np.int32(con_src)
391
+ else:
392
+ node_map = zone.node_map
393
+ if node_map is not None:
394
+ d["node_map"] = node_map
369
395
 
370
396
  return d
371
397
 
372
398
 
373
- # ---------------------------------------------------------------------------
399
+ # --------------------------------------------------------------------------------------
374
400
  # Main entry point
375
- # ---------------------------------------------------------------------------
401
+ # --------------------------------------------------------------------------------------
376
402
 
377
403
 
378
404
  def main(argv: Sequence[str] | None = None) -> int:
@@ -384,9 +410,9 @@ def main(argv: Sequence[str] | None = None) -> int:
384
410
  """
385
411
  args = _parse_args(argv)
386
412
 
387
- # SciPy is an optional dependency, imported lazily so that merely importing
388
- # this module (e.g. to call main() from a script) does not require SciPy
389
- # unless the tool is actually run.
413
+ # SciPy is an optional dependency, imported lazily so that merely importing this
414
+ # module (e.g. to call main() from a script) does not require SciPy unless the tool
415
+ # is actually run.
390
416
  try:
391
417
  from scipy import io as scipy_io
392
418
  except ImportError: