tecio-python 0.2.0__py3-none-any.whl → 0.2.2__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/_containers.py +2 -2
- tecio/_io.py +46 -38
- tecio/_meta.py +4 -4
- tecio/cli/__init__.py +3 -3
- tecio/cli/tec2mat.py +21 -21
- tecio/cli/tecaux.py +971 -0
- tecio/cli/tecdump.py +4 -4
- tecio/cli/tecextract.py +6 -6
- tecio/cli/tecfix.py +4 -4
- tecio/cli/tecmerge.py +12 -12
- tecio/cli/teconvert.py +3 -3
- tecio/cli/tecscale.py +7 -7
- tecio/cli/tecslice.py +12 -12
- tecio/cli/tecstats.py +4 -4
- tecio/dat/__init__.py +1 -1
- tecio/dat/_read.py +33 -21
- tecio/dat/_write.py +22 -22
- tecio/libtecio.py +73 -16
- tecio/plt/_read.py +21 -21
- tecio/plt/_write.py +35 -35
- tecio/szl/_read.py +3 -3
- tecio/szl/_write.py +50 -28
- {tecio_python-0.2.0.dist-info → tecio_python-0.2.2.dist-info}/METADATA +1 -1
- tecio_python-0.2.2.dist-info/RECORD +33 -0
- {tecio_python-0.2.0.dist-info → tecio_python-0.2.2.dist-info}/entry_points.txt +1 -0
- {tecio_python-0.2.0.dist-info → tecio_python-0.2.2.dist-info}/licenses/NOTICE +2 -2
- tecio_python-0.2.0.dist-info/RECORD +0 -32
- {tecio_python-0.2.0.dist-info → tecio_python-0.2.2.dist-info}/WHEEL +0 -0
- {tecio_python-0.2.0.dist-info → tecio_python-0.2.2.dist-info}/licenses/LICENSE +0 -0
- {tecio_python-0.2.0.dist-info → tecio_python-0.2.2.dist-info}/top_level.txt +0 -0
tecio/cli/tecdump.py
CHANGED
|
@@ -2,10 +2,10 @@ r"""Dump the full contents of a Tecplot data file to standard output.
|
|
|
2
2
|
|
|
3
3
|
Inspecting the contents of a binary Tecplot file (variable names, zone metadata,
|
|
4
4
|
auxiliary data, and raw nodal or element values) ordinarily requires either opening the
|
|
5
|
-
file in Tecplot itself or writing a dedicated script against the TecIO API.
|
|
5
|
+
file in Tecplot itself or writing a dedicated script against the TecIO API. For a quick
|
|
6
6
|
sanity check during a simulation or post-processing workflow, ``tecdump`` serializes
|
|
7
7
|
every record in the file to plain text, providing a complete and human-readable
|
|
8
|
-
representation of the file contents directly in the terminal.
|
|
8
|
+
representation of the file contents directly in the terminal. The output is structured
|
|
9
9
|
hierarchically: file header and auxiliary data first, followed by per-zone headers and,
|
|
10
10
|
optionally, the underlying variable and connectivity arrays.
|
|
11
11
|
|
|
@@ -27,7 +27,7 @@ arrays can be suppressed entirely to focus on structural metadata.
|
|
|
27
27
|
|
|
28
28
|
:Options:
|
|
29
29
|
``--ignore-zones``
|
|
30
|
-
Print only the file-level header and auxiliary data, then exit.
|
|
30
|
+
Print only the file-level header and auxiliary data, then exit. Zone records
|
|
31
31
|
and variable arrays are suppressed entirely. Useful for confirming file-level
|
|
32
32
|
metadata such as solver, strand IDs, or global auxiliary entries without the
|
|
33
33
|
overhead of parsing zone data.
|
|
@@ -47,7 +47,7 @@ arrays can be suppressed entirely to focus on structural metadata.
|
|
|
47
47
|
|
|
48
48
|
``-maxvals INT``
|
|
49
49
|
Maximum number of values to print from any variable or connectivity array before
|
|
50
|
-
truncating with a summary line.
|
|
50
|
+
truncating with a summary line. Defaults to a small number suitable for a quick
|
|
51
51
|
preview; set to a large value (e.g. ``1000000``) to print an array in full.
|
|
52
52
|
|
|
53
53
|
:Returns:
|
tecio/cli/tecextract.py
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
r"""Extract a subset of zones and/or variables from a Tecplot data file.
|
|
2
2
|
|
|
3
3
|
Large Tecplot files produced by CFD solvers frequently contain many zones and variables,
|
|
4
|
-
only a fraction of which are relevant to a given analysis.
|
|
4
|
+
only a fraction of which are relevant to a given analysis. Loading the full file into
|
|
5
5
|
Tecplot or writing a dedicated extraction script is impractical when the goal is simply
|
|
6
6
|
to isolate a surface zone or a handful of flow variables for downstream processing.
|
|
7
7
|
``tecextract`` addresses this by writing a new Tecplot file containing only the
|
|
8
8
|
requested subset, with the output format determined by the file extension of the output
|
|
9
|
-
path.
|
|
9
|
+
path. This makes it straightforward to reduce file size, change format, or prepare a
|
|
10
10
|
subset for use with another tool in a single command.
|
|
11
11
|
|
|
12
12
|
:Usage:
|
|
@@ -158,7 +158,7 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
158
158
|
metavar="LIST",
|
|
159
159
|
help=(
|
|
160
160
|
"Comma-separated list of 1-based zone indices to extract "
|
|
161
|
-
"(e.g. -zones 1,3,5).
|
|
161
|
+
"(e.g. -zones 1,3,5). Default is all zones."
|
|
162
162
|
),
|
|
163
163
|
)
|
|
164
164
|
parser.add_argument(
|
|
@@ -168,7 +168,7 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
168
168
|
metavar="LIST",
|
|
169
169
|
help=(
|
|
170
170
|
"Comma-separated list of 1-based variable indices to extract "
|
|
171
|
-
"(e.g. -variables 1,2,5).
|
|
171
|
+
"(e.g. -variables 1,2,5). Default is all variables."
|
|
172
172
|
),
|
|
173
173
|
)
|
|
174
174
|
parser.add_argument(
|
|
@@ -178,8 +178,8 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
178
178
|
default=None,
|
|
179
179
|
metavar="PATH",
|
|
180
180
|
help=(
|
|
181
|
-
"Output file path.
|
|
182
|
-
"(.szplt, .plt, .dat).
|
|
181
|
+
"Output file path. The extension controls the output format "
|
|
182
|
+
"(.szplt, .plt, .dat). Defaults to <stem>_extract<ext> in "
|
|
183
183
|
"the same directory as the input."
|
|
184
184
|
),
|
|
185
185
|
)
|
tecio/cli/tecfix.py
CHANGED
|
@@ -156,7 +156,7 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
156
156
|
default=None,
|
|
157
157
|
metavar="PATH",
|
|
158
158
|
help=(
|
|
159
|
-
"Explicit output file path.
|
|
159
|
+
"Explicit output file path. Defaults to <stem>_fixed<ext> "
|
|
160
160
|
"in the same directory as the input file."
|
|
161
161
|
),
|
|
162
162
|
)
|
|
@@ -196,7 +196,7 @@ def _classify_array(arr: npt.NDArray) -> str | None:
|
|
|
196
196
|
|
|
197
197
|
"""
|
|
198
198
|
# Some readers (e.g. DAT) may return an empty or None array for passive
|
|
199
|
-
# or otherwise unavailable variables.
|
|
199
|
+
# or otherwise unavailable variables. Treat these as clean -> there is
|
|
200
200
|
# nothing to inspect and numpy operations like isnan would error or
|
|
201
201
|
# return misleading results on a zero-size array.
|
|
202
202
|
if arr is None or arr.size == 0:
|
|
@@ -235,7 +235,7 @@ def _process_zone(
|
|
|
235
235
|
|
|
236
236
|
For each variable:
|
|
237
237
|
- If the variable is already passive or shared, it is forwarded exactly
|
|
238
|
-
as-is.
|
|
238
|
+
as-is. Sharing references are 1-based zone indices that are identical
|
|
239
239
|
in the output file because tecfix writes every zone in order without
|
|
240
240
|
skipping any.
|
|
241
241
|
- If the variable is a floating-point type and contains NaN or Inf it is
|
|
@@ -478,7 +478,7 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
478
478
|
} or None
|
|
479
479
|
|
|
480
480
|
# Sanitize solution_time: if it is NaN the zone metadata
|
|
481
|
-
# is corrupt.
|
|
481
|
+
# is corrupt. Reset both solution_time and strand_id to 0
|
|
482
482
|
# so Tecplot treats the zone as stationary and the writer
|
|
483
483
|
# does not propagate the bad value downstream.
|
|
484
484
|
sol_time = zone.solution_time
|
tecio/cli/tecmerge.py
CHANGED
|
@@ -3,8 +3,8 @@ r"""Merge zones from multiple Tecplot data files into a single output file.
|
|
|
3
3
|
Post-processing workflows commonly produce results distributed across multiple files.
|
|
4
4
|
``tecmerge`` collects all zones from an arbitrary number of input files into a single
|
|
5
5
|
output, reconciling variable lists across sources by taking their union and writing any
|
|
6
|
-
variable absent from a given source as passive.
|
|
7
|
-
or via a quoted glob pattern and may be any mix of supported formats.
|
|
6
|
+
variable absent from a given source as passive. Input files may be specified explicitly
|
|
7
|
+
or via a quoted glob pattern and may be any mix of supported formats. When merging
|
|
8
8
|
time-step sequences, solution times and strand IDs can be assigned automatically from a
|
|
9
9
|
start time and either a fixed interval or an end time.
|
|
10
10
|
|
|
@@ -44,7 +44,7 @@ start time and either a fixed interval or an end time.
|
|
|
44
44
|
Solution time of the first input file. Used with ``--assign-time-strands``.
|
|
45
45
|
|
|
46
46
|
``-delta VALUE``
|
|
47
|
-
Constant time increment between successive input files.
|
|
47
|
+
Constant time increment between successive input files. Mutually exclusive with
|
|
48
48
|
``-end``.
|
|
49
49
|
|
|
50
50
|
``-end VALUE``
|
|
@@ -136,8 +136,8 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
136
136
|
nargs="+",
|
|
137
137
|
metavar="FILE",
|
|
138
138
|
help=(
|
|
139
|
-
"Input Tecplot files.
|
|
140
|
-
"pattern to prevent shell expansion).
|
|
139
|
+
"Input Tecplot files. Glob patterns are expanded (quote the "
|
|
140
|
+
"pattern to prevent shell expansion). Files are merged in the "
|
|
141
141
|
"order given / matched."
|
|
142
142
|
),
|
|
143
143
|
)
|
|
@@ -150,7 +150,7 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
150
150
|
required=True,
|
|
151
151
|
metavar="PATH",
|
|
152
152
|
help=(
|
|
153
|
-
"Output file path.
|
|
153
|
+
"Output file path. The extension controls the output format "
|
|
154
154
|
"(.szplt, .plt, .dat)."
|
|
155
155
|
),
|
|
156
156
|
)
|
|
@@ -166,7 +166,7 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
166
166
|
default=None,
|
|
167
167
|
metavar="STRING",
|
|
168
168
|
help=(
|
|
169
|
-
"Dataset title for the output file.
|
|
169
|
+
"Dataset title for the output file. Defaults to the title of "
|
|
170
170
|
"the first input file."
|
|
171
171
|
),
|
|
172
172
|
)
|
|
@@ -180,7 +180,7 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
180
180
|
dest="assign_ts",
|
|
181
181
|
help=(
|
|
182
182
|
"Assign evenly-spaced solution times and a strand ID to all "
|
|
183
|
-
"zones.
|
|
183
|
+
"zones. Each input file corresponds to one time step. "
|
|
184
184
|
"Requires -start and one of -delta or -end."
|
|
185
185
|
),
|
|
186
186
|
)
|
|
@@ -192,7 +192,7 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
192
192
|
help="Solution time of the first input file.",
|
|
193
193
|
)
|
|
194
194
|
# -delta and -end are mutually exclusive -- either specifies the spacing,
|
|
195
|
-
# the other specifies the endpoint.
|
|
195
|
+
# the other specifies the endpoint. Providing both over-constrains the
|
|
196
196
|
# problem and is therefore disallowed at the parser level.
|
|
197
197
|
step_group = ts.add_mutually_exclusive_group()
|
|
198
198
|
step_group.add_argument(
|
|
@@ -208,7 +208,7 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
208
208
|
default=None,
|
|
209
209
|
metavar="VALUE",
|
|
210
210
|
help=(
|
|
211
|
-
"Solution time of the last input file.
|
|
211
|
+
"Solution time of the last input file. The step is computed as "
|
|
212
212
|
"(end - start) / (N - 1) where N is the number of input files."
|
|
213
213
|
),
|
|
214
214
|
)
|
|
@@ -217,7 +217,7 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
217
217
|
type=int,
|
|
218
218
|
default=1,
|
|
219
219
|
metavar="ID",
|
|
220
|
-
help="Strand ID to assign to all zones.
|
|
220
|
+
help="Strand ID to assign to all zones. Default: 1.",
|
|
221
221
|
)
|
|
222
222
|
|
|
223
223
|
return parser.parse_args(argv)
|
|
@@ -310,7 +310,7 @@ def _write_zone(
|
|
|
310
310
|
) -> None:
|
|
311
311
|
"""Write one zone to *writer* using the reconciled variable list.
|
|
312
312
|
|
|
313
|
-
Variables present in *zone* are copied verbatim.
|
|
313
|
+
Variables present in *zone* are copied verbatim. Variables absent from
|
|
314
314
|
*zone* (``None`` entries in *local_index_map*) are written as passive.
|
|
315
315
|
|
|
316
316
|
Args:
|
tecio/cli/teconvert.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
r"""Convert between Tecplot data file formats.
|
|
2
2
|
|
|
3
3
|
The three Tecplot file formats, SZL binary (``.szplt``), PLT binary (``.plt``), and
|
|
4
|
-
ASCII DAT (``.dat``), are not always interchangeable in practice.
|
|
4
|
+
ASCII DAT (``.dat``), are not always interchangeable in practice. Tecplot itself
|
|
5
5
|
stores all data internally in binary form and provides license-free command-line
|
|
6
6
|
utilities (``preplot``, ``tec360 -convert``) for converting from lower-level formats
|
|
7
7
|
upward to SZL, but no supported path exists for the reverse without a active license.
|
|
@@ -182,7 +182,7 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
182
182
|
default=None,
|
|
183
183
|
metavar="PATH",
|
|
184
184
|
help=(
|
|
185
|
-
"Explicit output file path.
|
|
185
|
+
"Explicit output file path. Defaults to the input file stem with "
|
|
186
186
|
"the new extension in the same directory as the input."
|
|
187
187
|
),
|
|
188
188
|
)
|
|
@@ -198,7 +198,7 @@ def _copy_zones(reader: Any, writer: Any) -> None:
|
|
|
198
198
|
"""Stream all zones from *reader* into the open *writer*.
|
|
199
199
|
|
|
200
200
|
Each zone is reproduced at its original data type and value location.
|
|
201
|
-
Connectivity (node maps) is copied verbatim for FE zones.
|
|
201
|
+
Connectivity (node maps) is copied verbatim for FE zones. Zone-level
|
|
202
202
|
auxiliary data is forwarded as well.
|
|
203
203
|
|
|
204
204
|
Args:
|
tecio/cli/tecscale.py
CHANGED
|
@@ -3,7 +3,7 @@ r"""Scale and/or offset variable arrays in a Tecplot data file.
|
|
|
3
3
|
CFD solvers and experimental data sources frequently produce output in differing unit
|
|
4
4
|
systems, and converting between them (e.g.pressure in Pa to kPa, temperature in Kelvin
|
|
5
5
|
to Celsius or lengths in meters to feet) is a routine step before visualisation or
|
|
6
|
-
comparison.
|
|
6
|
+
comparison. Performing these transformations would otherwise require loading the file
|
|
7
7
|
in Tecplot or writing a dedicated script. ``tecscale`` applies the linear transformation
|
|
8
8
|
|
|
9
9
|
.. math::
|
|
@@ -11,7 +11,7 @@ in Tecplot or writing a dedicated script. ``tecscale`` applies the linear transf
|
|
|
11
11
|
v' = v \cdot s + b
|
|
12
12
|
|
|
13
13
|
to every value in a selected variable array, where :math:`s` is the scale factor and
|
|
14
|
-
:math:`b` is the additive offset.
|
|
14
|
+
:math:`b` is the additive offset. The transformation can be restricted to a single zone
|
|
15
15
|
and the output format is controlled by the output file extension, allowing unit
|
|
16
16
|
conversion and format conversion to be performed in a single step.
|
|
17
17
|
|
|
@@ -144,14 +144,14 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
144
144
|
type=float,
|
|
145
145
|
default=1.0,
|
|
146
146
|
metavar="FLOAT",
|
|
147
|
-
help="Multiplicative scale factor.
|
|
147
|
+
help="Multiplicative scale factor. Default: 1.0.",
|
|
148
148
|
)
|
|
149
149
|
parser.add_argument(
|
|
150
150
|
"-offset",
|
|
151
151
|
type=float,
|
|
152
152
|
default=0.0,
|
|
153
153
|
metavar="FLOAT",
|
|
154
|
-
help="Additive offset applied after scaling.
|
|
154
|
+
help="Additive offset applied after scaling. Default: 0.0.",
|
|
155
155
|
)
|
|
156
156
|
parser.add_argument(
|
|
157
157
|
"-zone",
|
|
@@ -159,7 +159,7 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
159
159
|
default=None,
|
|
160
160
|
metavar="INDEX",
|
|
161
161
|
help=(
|
|
162
|
-
"1-based zone index to apply the transformation to.
|
|
162
|
+
"1-based zone index to apply the transformation to. Default is all zones."
|
|
163
163
|
),
|
|
164
164
|
)
|
|
165
165
|
parser.add_argument(
|
|
@@ -169,7 +169,7 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
169
169
|
default=None,
|
|
170
170
|
metavar="PATH",
|
|
171
171
|
help=(
|
|
172
|
-
"Output file path.
|
|
172
|
+
"Output file path. The extension controls the output format. "
|
|
173
173
|
"Defaults to <stem>_scaled<ext> in the same directory."
|
|
174
174
|
),
|
|
175
175
|
)
|
|
@@ -225,7 +225,7 @@ def _resolve_variable(spec: str, var_names: list[str]) -> int | None:
|
|
|
225
225
|
return i
|
|
226
226
|
|
|
227
227
|
print(
|
|
228
|
-
f"Error: variable '{spec}' not found.
|
|
228
|
+
f"Error: variable '{spec}' not found. Available: {var_names}",
|
|
229
229
|
file=sys.stderr,
|
|
230
230
|
)
|
|
231
231
|
return None
|
tecio/cli/tecslice.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
r"""Slice a Tecplot data file along IJK indices and/or solution time.
|
|
2
2
|
|
|
3
3
|
Structured CFD datasets frequently contain more spatial resolution or time steps than
|
|
4
|
-
are necessary for a given analysis.
|
|
4
|
+
are necessary for a given analysis. Reducing the data to a relevant subset (like every
|
|
5
5
|
other grid point, a specific index range along one axis, or a window of solution times)
|
|
6
6
|
would otherwise require loading the full file in Tecplot or writing a dedicated script.
|
|
7
7
|
``tecslice`` applies these reductions directly to the file using a compact
|
|
@@ -212,7 +212,7 @@ def _parse_ijk_slice(value: str) -> IjkSliceSpec:
|
|
|
212
212
|
)
|
|
213
213
|
except ValueError as exc:
|
|
214
214
|
raise argparse.ArgumentTypeError(
|
|
215
|
-
f"Invalid IJK slice {value!r}: {exc}.
|
|
215
|
+
f"Invalid IJK slice {value!r}: {exc}. All components must be integers."
|
|
216
216
|
) from exc
|
|
217
217
|
|
|
218
218
|
|
|
@@ -313,7 +313,7 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
313
313
|
required=True,
|
|
314
314
|
metavar="PATH",
|
|
315
315
|
help=(
|
|
316
|
-
"Output file path.
|
|
316
|
+
"Output file path. The extension controls the output format "
|
|
317
317
|
"(.szplt, .plt, .dat)."
|
|
318
318
|
),
|
|
319
319
|
)
|
|
@@ -408,10 +408,10 @@ def _ijk_spec_to_slice(spec: IjkSliceSpec | None) -> slice:
|
|
|
408
408
|
# 1-based inclusive end -> Python exclusive stop.
|
|
409
409
|
#
|
|
410
410
|
# Positive step: 1-based end N -> py_stop N.
|
|
411
|
-
# arr[:N] (0-based) gives elements 0..N-1 = 1-based 1..N.
|
|
411
|
+
# arr[:N] (0-based) gives elements 0..N-1 = 1-based 1..N. Correct.
|
|
412
412
|
#
|
|
413
413
|
# Negative step: 1-based end N -> py_stop N-1.
|
|
414
|
-
# arr[s:N-1:-1] includes 0-based index N-1 = 1-based N.
|
|
414
|
+
# arr[s:N-1:-1] includes 0-based index N-1 = 1-based N. Correct.
|
|
415
415
|
# Special case: 1-based end=1 -> 0-based index 0 -> py_stop=None
|
|
416
416
|
# (arr[s:0:-1] would miss index 0; arr[s:None:-1] includes it).
|
|
417
417
|
if spec.end is not None:
|
|
@@ -445,7 +445,7 @@ def _build_time_filter(
|
|
|
445
445
|
) -> set[int]:
|
|
446
446
|
"""Return the set of 0-based zone indices to include in the output.
|
|
447
447
|
|
|
448
|
-
Zones with strand ID == 0 are always included.
|
|
448
|
+
Zones with strand ID == 0 are always included. For each eligible
|
|
449
449
|
strand the zones are sorted by solution time, the ``[start, end]``
|
|
450
450
|
window is applied (inclusive), then the ``skip`` stride is applied to
|
|
451
451
|
the windowed list.
|
|
@@ -514,7 +514,7 @@ def _build_protected_set(
|
|
|
514
514
|
|
|
515
515
|
A zone is "protected" if it is excluded by the time filter but is
|
|
516
516
|
referenced as a variable-sharing source by at least one zone that will
|
|
517
|
-
be written.
|
|
517
|
+
be written. Protected zones are written with all non-shared variables
|
|
518
518
|
set passive and their strand ID forced to 0 so they act as inert grid
|
|
519
519
|
anchors without contributing to the time series.
|
|
520
520
|
|
|
@@ -554,7 +554,7 @@ def _collect_zone_arrays(
|
|
|
554
554
|
|
|
555
555
|
Returns:
|
|
556
556
|
``(data, locs, passive_vars, var_sharing)`` where each list has
|
|
557
|
-
length ``num_vars``.
|
|
557
|
+
length ``num_vars``. Passive and shared entries have empty arrays.
|
|
558
558
|
|
|
559
559
|
"""
|
|
560
560
|
data: list[np.ndarray] = []
|
|
@@ -653,9 +653,9 @@ def _write_zone_protected(
|
|
|
653
653
|
"""Write a zone as a protected grid anchor.
|
|
654
654
|
|
|
655
655
|
All variables that are not already passive or shared are set passive so
|
|
656
|
-
that no solution data is carried into the output.
|
|
656
|
+
that no solution data is carried into the output. The strand ID is
|
|
657
657
|
forced to 0 so the zone is treated as stationary and does not appear in
|
|
658
|
-
any time-series animation.
|
|
658
|
+
any time-series animation. Sharing references are preserved verbatim so
|
|
659
659
|
that zones which share coordinates from this zone continue to work.
|
|
660
660
|
|
|
661
661
|
Args:
|
|
@@ -754,7 +754,7 @@ def _slice_and_write_ordered(
|
|
|
754
754
|
|
|
755
755
|
if var.value_location == ValueLocation.CELL_CENTERED:
|
|
756
756
|
# Cell array has shape (I-1, J-1, K-1).
|
|
757
|
-
# Cell c sits between nodes c and c+1.
|
|
757
|
+
# Cell c sits between nodes c and c+1. For a nodal selection
|
|
758
758
|
# with stride s, the fully-bounded cells are exactly the
|
|
759
759
|
# lower-node indices of each consecutive selected-node pair:
|
|
760
760
|
# positive step -> selected_nodes[:-1] (each is the lower node)
|
|
@@ -837,7 +837,7 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
837
837
|
|
|
838
838
|
if not do_ijk and not do_time:
|
|
839
839
|
print(
|
|
840
|
-
"Warning: no slice flags provided.
|
|
840
|
+
"Warning: no slice flags provided. Output will be a verbatim copy.",
|
|
841
841
|
file=sys.stderr,
|
|
842
842
|
)
|
|
843
843
|
|
tecio/cli/tecstats.py
CHANGED
|
@@ -26,7 +26,7 @@ corrective operation.
|
|
|
26
26
|
are reported.
|
|
27
27
|
|
|
28
28
|
``-variable INDEX``
|
|
29
|
-
Restrict output to the variable at the given one-based index.
|
|
29
|
+
Restrict output to the variable at the given one-based index. If omitted, all
|
|
30
30
|
variables are reported.
|
|
31
31
|
|
|
32
32
|
``-csv``
|
|
@@ -154,14 +154,14 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
154
154
|
type=int,
|
|
155
155
|
default=None,
|
|
156
156
|
metavar="INDEX",
|
|
157
|
-
help="1-based zone index to report.
|
|
157
|
+
help="1-based zone index to report. Default is all zones.",
|
|
158
158
|
)
|
|
159
159
|
parser.add_argument(
|
|
160
160
|
"-variable",
|
|
161
161
|
type=int,
|
|
162
162
|
default=None,
|
|
163
163
|
metavar="INDEX",
|
|
164
|
-
help="1-based variable index to report.
|
|
164
|
+
help="1-based variable index to report. Default is all variables.",
|
|
165
165
|
)
|
|
166
166
|
parser.add_argument(
|
|
167
167
|
"-csv",
|
|
@@ -169,7 +169,7 @@ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
|
169
169
|
default=False,
|
|
170
170
|
dest="write_csv",
|
|
171
171
|
help=(
|
|
172
|
-
"Write statistics to a CSV file.
|
|
172
|
+
"Write statistics to a CSV file. The filename is derived "
|
|
173
173
|
"automatically from the input file stem with a _stats suffix "
|
|
174
174
|
"always appended, preceded by optional _zone_N and _var_N "
|
|
175
175
|
"segments when -zone or -variable are active "
|
tecio/dat/__init__.py
CHANGED
|
@@ -6,7 +6,7 @@ files, but are useful for small datasets, debugging, and interoperability
|
|
|
6
6
|
with other tools.
|
|
7
7
|
|
|
8
8
|
This module provides :class:`Read` for parsing and :class:`Write` for
|
|
9
|
-
producing Tecplot 360 ASCII data files (``.dat`` / ``.tec``).
|
|
9
|
+
producing Tecplot 360 ASCII data files (``.dat`` / ``.tec``). Both
|
|
10
10
|
classes mirror the interfaces of :class:`szl.Read` / :class:`szl.Write`
|
|
11
11
|
and :class:`plt.Read` / :class:`plt.Write` so that downstream code can
|
|
12
12
|
switch between file formats by changing only the file extension passed to
|
tecio/dat/_read.py
CHANGED
|
@@ -3,7 +3,7 @@ r"""Tecplot ASCII DAT file reader API.
|
|
|
3
3
|
Supported ``DATAPACKING`` modes:
|
|
4
4
|
* ``BLOCK`` — one contiguous value block per variable (Tecplot default).
|
|
5
5
|
* ``POINT`` — one row of all variable values per node, followed by a separate
|
|
6
|
-
row-per-cell section for any cell-centred variables.
|
|
6
|
+
row-per-cell section for any cell-centred variables. This is the format most
|
|
7
7
|
commonly produced by third-party exporters and tools that treat the file like a
|
|
8
8
|
CSV with a header.
|
|
9
9
|
"""
|
|
@@ -213,9 +213,9 @@ def _next_token_is_value(text: str, i: int) -> bool:
|
|
|
213
213
|
"""Return ``True`` if the token at *i* is a value, not the next key.
|
|
214
214
|
|
|
215
215
|
Supports the legacy whitespace-separated ``KEY VALUE`` form in zone headers
|
|
216
|
-
(e.g. ``STRANDID 2``).
|
|
216
|
+
(e.g. ``STRANDID 2``). A token is treated as the *next key* — and therefore not a
|
|
217
217
|
value for the preceding key — when it is immediately followed (after optional
|
|
218
|
-
whitespace) by ``=``.
|
|
218
|
+
whitespace) by ``=``. A quoted token is always a value.
|
|
219
219
|
|
|
220
220
|
Example:
|
|
221
221
|
>>> _next_token_is_value("STRANDID 2", 9)
|
|
@@ -270,7 +270,7 @@ def _kv_split(text: str) -> dict[str, str]:
|
|
|
270
270
|
i += 1
|
|
271
271
|
continue
|
|
272
272
|
|
|
273
|
-
# Determine the separator between key and value.
|
|
273
|
+
# Determine the separator between key and value. The modern form is
|
|
274
274
|
# ``KEY = VALUE``; some legacy files (and ``preplot``) also accept a bare
|
|
275
275
|
# whitespace separator, e.g. ``STRANDID 2``.
|
|
276
276
|
while i < n and text[i] in " \t":
|
|
@@ -362,7 +362,7 @@ def _parse_legacy_element_type(text: str) -> ZoneType:
|
|
|
362
362
|
"""Parse a legacy ``ET=`` (element type) value into a :class:`ZoneType`.
|
|
363
363
|
|
|
364
364
|
Recognises the canonical Tecplot element names (``TRIANGLE``,
|
|
365
|
-
``QUADRILATERAL``, ``TETRAHEDRON``, ``BRICK``, ``LINESEG``).
|
|
365
|
+
``QUADRILATERAL``, ``TETRAHEDRON``, ``BRICK``, ``LINESEG``). A prefix-based
|
|
366
366
|
fallback provides a little tolerance for the minor spelling variants some
|
|
367
367
|
third-party exporters emit, keeping the reader as permissive as ``preplot``.
|
|
368
368
|
|
|
@@ -473,7 +473,7 @@ def _parse_varsharelist(text: str) -> dict[int, int]:
|
|
|
473
473
|
"([1-3,5]=2)" → {0: 2, 1: 2, 2: 2, 4: 2}
|
|
474
474
|
|
|
475
475
|
Note that the bracketed group may contain either a single integer or a
|
|
476
|
-
hyphen-separated range.
|
|
476
|
+
hyphen-separated range. Mixed forms such as ``[1-3,5]`` are also produced by some
|
|
477
477
|
writers.
|
|
478
478
|
|
|
479
479
|
Args:
|
|
@@ -751,7 +751,7 @@ class ReadVariable:
|
|
|
751
751
|
"""Return :class:`DataType` inferred from the stored NumPy dtype.
|
|
752
752
|
|
|
753
753
|
A shared variable reports the source zone's actual dtype, since its data array
|
|
754
|
-
is resolved from that zone (see :attr:`shared_zone`).
|
|
754
|
+
is resolved from that zone (see :attr:`shared_zone`). Only a passive variable
|
|
755
755
|
(which has no data array anywhere in the file) returns :attr:`DataType.FLOAT` as
|
|
756
756
|
a placeholder.
|
|
757
757
|
"""
|
|
@@ -846,7 +846,7 @@ class ReadZone:
|
|
|
846
846
|
Attributes:
|
|
847
847
|
datapacking: :class:`~tecio.libtecio.DataPacking` member reflecting the
|
|
848
848
|
``DATAPACKING`` keyword found in the zone header (``BLOCK`` or
|
|
849
|
-
``POINT``).
|
|
849
|
+
``POINT``). The data arrays are identical either way; this attribute
|
|
850
850
|
records how the values were laid out on disk.
|
|
851
851
|
shared_connectivity: 1-based source zone index if this zone's connectivity is
|
|
852
852
|
shared via ``CONNECTIVITYSHAREZONE``, else ``None``. :attr:`node_map`
|
|
@@ -921,7 +921,7 @@ class ReadZone:
|
|
|
921
921
|
def nodes_per_cell(self) -> int:
|
|
922
922
|
"""Nodes per cell based on zone type.
|
|
923
923
|
|
|
924
|
-
Uses the module-level ``_NODES_PER_ELEM`` table for simple FE types.
|
|
924
|
+
Uses the module-level ``_NODES_PER_ELEM`` table for simple FE types. For
|
|
925
925
|
ORDERED zones the count is inferred from the number of active dimensions (1-D →
|
|
926
926
|
2, 2-D → 4, 3-D → 8).
|
|
927
927
|
|
|
@@ -954,7 +954,7 @@ class ReadZone:
|
|
|
954
954
|
) -> npt.NDArray | None | tuple[npt.NDArray | None, ...]:
|
|
955
955
|
"""Return variable data array(s) for this zone.
|
|
956
956
|
|
|
957
|
-
A single key (0-based index or exact name) returns one array.
|
|
957
|
+
A single key (0-based index or exact name) returns one array. A list of exact
|
|
958
958
|
names returns a tuple of arrays in the order given, suitable for unpacking::
|
|
959
959
|
|
|
960
960
|
p = zone.get_array("p")
|
|
@@ -986,7 +986,7 @@ class ReadZone:
|
|
|
986
986
|
class Read:
|
|
987
987
|
"""Read a Tecplot ASCII DAT file into memory.
|
|
988
988
|
|
|
989
|
-
The entire file is parsed on construction.
|
|
989
|
+
The entire file is parsed on construction. All data is then available through the
|
|
990
990
|
same attributes and methods as :class:`szl.Read`.
|
|
991
991
|
|
|
992
992
|
Example:
|
|
@@ -1011,8 +1011,12 @@ class Read:
|
|
|
1011
1011
|
self._zones: list[ReadZone] = []
|
|
1012
1012
|
self._zone_list: ZoneList[ReadZone] | None = None
|
|
1013
1013
|
self._auxdata: ReadAuxData = ReadAuxData()
|
|
1014
|
-
# Index 0 is a None placeholder so that 1-based indexing works directly
|
|
1014
|
+
# Index 0 is a None placeholder so that 1-based indexing works directly
|
|
1015
1015
|
self._var_auxdata: list[ReadAuxData | None] = [None]
|
|
1016
|
+
# Raw VARAUXDATA lines seen before the first zone, buffered by
|
|
1017
|
+
# _parse_file_header() and applied once _var_auxdata is allocated with the
|
|
1018
|
+
# correct length (num_vars isn't known until the header finishes parsing)
|
|
1019
|
+
self._deferred_var_aux_lines: list[str] = []
|
|
1016
1020
|
self._parse()
|
|
1017
1021
|
|
|
1018
1022
|
def __repr__(self) -> str:
|
|
@@ -1045,7 +1049,7 @@ class Read:
|
|
|
1045
1049
|
"""Context manager protocol for read-only file interfaces.
|
|
1046
1050
|
|
|
1047
1051
|
Read classes hold no open file handle between accesses, so there is nothing to
|
|
1048
|
-
release on exit.
|
|
1052
|
+
release on exit. Provided for API consistency with the Write classes and to
|
|
1049
1053
|
support the ``with tecio.open(...) as r:`` pattern.
|
|
1050
1054
|
"""
|
|
1051
1055
|
pass
|
|
@@ -1148,6 +1152,12 @@ class Read:
|
|
|
1148
1152
|
# Build per-variable aux data slots now that num_vars is known.
|
|
1149
1153
|
self._var_auxdata = [None] + [ReadAuxData() for _ in range(self.num_vars)]
|
|
1150
1154
|
|
|
1155
|
+
# Now that _var_auxdata exists, apply any VARAUXDATA lines that appeared before
|
|
1156
|
+
# the first zone
|
|
1157
|
+
for raw in self._deferred_var_aux_lines:
|
|
1158
|
+
_apply_varauxdata(raw, self._var_auxdata)
|
|
1159
|
+
self._deferred_var_aux_lines.clear()
|
|
1160
|
+
|
|
1151
1161
|
while tokens.has_more():
|
|
1152
1162
|
line = tokens.peek_stripped()
|
|
1153
1163
|
upper = line.upper()
|
|
@@ -1229,9 +1239,11 @@ class Read:
|
|
|
1229
1239
|
if name:
|
|
1230
1240
|
self._auxdata._data[name] = value
|
|
1231
1241
|
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1242
|
+
elif upper_key.startswith("VARAUXDATA"):
|
|
1243
|
+
# Can't process this without num_vars, and therefore _var_auxdata, isn't
|
|
1244
|
+
# known until this header finishes parsing. Buffer the raw line;
|
|
1245
|
+
# _parse() applies these once _var_auxdata is allocated.
|
|
1246
|
+
self._deferred_var_aux_lines.append(line)
|
|
1235
1247
|
|
|
1236
1248
|
def _parse_zone(self, tokens: _LineBuffer) -> None:
|
|
1237
1249
|
"""Parse one ZONE block (header + data blocks + connectivity).
|
|
@@ -1288,7 +1300,7 @@ class Read:
|
|
|
1288
1300
|
# (legacy). ``ET`` (element type) only ever appears on finite-element zones —
|
|
1289
1301
|
# ordered/structured data has no elements — so it merely names the element
|
|
1290
1302
|
# *shape* once a zone is already known to be FE, and is ignored on ordered
|
|
1291
|
-
# zones.
|
|
1303
|
+
# zones. Modern keywords win when present.
|
|
1292
1304
|
legacy_is_fe: bool | None = None
|
|
1293
1305
|
legacy_packing: DataPacking | None = None
|
|
1294
1306
|
if "F" in kv:
|
|
@@ -1298,11 +1310,11 @@ class Read:
|
|
|
1298
1310
|
zt_raw = kv["ZONETYPE"].rstrip(",").strip().lower()
|
|
1299
1311
|
zone_type = _STR_TO_ZONETYPE.get(zt_raw, ZoneType.ORDERED)
|
|
1300
1312
|
elif legacy_is_fe is False:
|
|
1301
|
-
# F=POINT/BLOCK: ordered zone.
|
|
1313
|
+
# F=POINT/BLOCK: ordered zone. A stray ET (if any) does not apply.
|
|
1302
1314
|
zone_type = ZoneType.ORDERED
|
|
1303
1315
|
elif legacy_is_fe or "ET" in kv:
|
|
1304
1316
|
# Finite-element zone: F=FEPOINT/FEBLOCK, or an ET keyword with no F
|
|
1305
|
-
# (some exporters omit F).
|
|
1317
|
+
# (some exporters omit F). The element shape comes from ET, which is
|
|
1306
1318
|
# then required.
|
|
1307
1319
|
if "ET" not in kv:
|
|
1308
1320
|
raise ValueError(
|
|
@@ -1572,9 +1584,9 @@ class Read:
|
|
|
1572
1584
|
|
|
1573
1585
|
The spec writes two interleaved sections:
|
|
1574
1586
|
|
|
1575
|
-
* **Nodal section** — ``num_nodes`` rows, one per node.
|
|
1587
|
+
* **Nodal section** — ``num_nodes`` rows, one per node. Each row contains
|
|
1576
1588
|
the values of all active nodal variables in dataset order.
|
|
1577
|
-
* **Cell-centered section** — ``num_cells`` rows, one per element.
|
|
1589
|
+
* **Cell-centered section** — ``num_cells`` rows, one per element. Each row
|
|
1578
1590
|
contains the values of all active CC variables in dataset order.
|
|
1579
1591
|
|
|
1580
1592
|
When all variables are nodal the cell-centered section is empty and is skipped
|