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.
Files changed (34) hide show
  1. tecio/__init__.py +29 -3
  2. tecio/_dat_read.py +296 -81
  3. tecio/_dat_write.py +206 -146
  4. tecio/_io.py +338 -73
  5. tecio/_meta.py +6 -6
  6. tecio/_plt_legacy.py +400 -0
  7. tecio/_plt_read.py +180 -41
  8. tecio/_plt_write.py +195 -125
  9. tecio/_reader.py +82 -37
  10. tecio/_szl_read.py +68 -61
  11. tecio/_szl_write.py +249 -188
  12. tecio/_writer.py +380 -134
  13. tecio/cli/tec2mat.py +21 -11
  14. tecio/cli/tecaux.py +77 -93
  15. tecio/cli/tecdump.py +1 -1
  16. tecio/cli/tecextract.py +68 -86
  17. tecio/cli/tecfix.py +45 -64
  18. tecio/cli/tecmerge.py +343 -66
  19. tecio/cli/teconvert.py +22 -37
  20. tecio/cli/tecscale.py +185 -109
  21. tecio/cli/tecslice.py +75 -100
  22. tecio/cli/tecstats.py +41 -41
  23. tecio_python-0.4.0.data/data/share/plugins/matlab/contourfe.m +111 -0
  24. tecio_python-0.4.0.data/data/share/plugins/matlab/tecmat2struct.m +137 -0
  25. {tecio_python-0.3.2.data/data/share/tecio → tecio_python-0.4.0.data/data/share}/plugins/paraview/TecplotTecioReader.py +9 -0
  26. tecio_python-0.4.0.data/data/share/plugins/paraview/TecplotTecioWriter.py +651 -0
  27. {tecio_python-0.3.2.dist-info → tecio_python-0.4.0.dist-info}/METADATA +1 -1
  28. tecio_python-0.4.0.dist-info/RECORD +38 -0
  29. {tecio_python-0.3.2.dist-info → tecio_python-0.4.0.dist-info}/WHEEL +1 -1
  30. tecio_python-0.3.2.dist-info/RECORD +0 -34
  31. {tecio_python-0.3.2.dist-info → tecio_python-0.4.0.dist-info}/entry_points.txt +0 -0
  32. {tecio_python-0.3.2.dist-info → tecio_python-0.4.0.dist-info}/licenses/LICENSE +0 -0
  33. {tecio_python-0.3.2.dist-info → tecio_python-0.4.0.dist-info}/licenses/NOTICE +0 -0
  34. {tecio_python-0.3.2.dist-info → tecio_python-0.4.0.dist-info}/top_level.txt +0 -0
tecio/_dat_write.py CHANGED
@@ -32,9 +32,12 @@ from ._constants import (
32
32
  )
33
33
  from ._meta import ZoneMeta
34
34
  from ._writer import (
35
+ PassiveVarsSpec,
35
36
  PreparedFEZone,
36
37
  PreparedOrderedZone,
37
38
  TecplotWriter,
39
+ VarSharingSpec,
40
+ convert_face_connections_to_one_based,
38
41
  normalize_precision,
39
42
  prepare_fe_zone,
40
43
  prepare_ordered_zone,
@@ -403,8 +406,8 @@ class TecplotDatWriter(TecplotWriter):
403
406
  title: str | None = None,
404
407
  variables: list[str] | None = None,
405
408
  value_locations: Sequence[ValueLocation] | None = None,
406
- passive_vars: Sequence[bool | int] | None = None,
407
- var_sharing: Sequence[int] | None = None,
409
+ passive_vars: PassiveVarsSpec | None = None,
410
+ var_sharing: VarSharingSpec | None = None,
408
411
  solution_time: float = 0.0,
409
412
  strand_id: int = 0,
410
413
  aux: dict[str, Any] | None = None,
@@ -415,39 +418,62 @@ class TecplotDatWriter(TecplotWriter):
415
418
  Zone dimensions are inferred from the shape of the first NODAL array.
416
419
 
417
420
  Args:
418
- data: One NumPy array per dataset variable. Array shape is used
419
- to infer ``imax``, ``jmax``, and ``kmax``; Fortran
420
- (column-major) order is assumed. Pass ``None`` to write a
421
- zone header only.
422
- title: Zone title. Defaults to ``"IJK_Zone_{current_zone + 1}"``.
423
- variables: Variable name list. Required on the first call when the
424
- file has not been opened yet (lazy-open path); ignored once
425
- the file is already initialised. Default to ``[V1, V2, V3,
426
- ...]`` if not provided in open or zone call.
427
- value_locations: Per-variable :class:`~libtecio.ValueLocation`. Defaults to
428
- all ``NODAL``.
429
- passive_vars: Per-variable passive flags. Defaults to all active
430
- (``False``).
431
- var_sharing: Per-variable share-from zone index (1-based). Defaults to
432
- no sharing (all zeros).
433
- solution_time: Solution time for transient data. Use ``0.0`` for static
434
- zones. Default to ``0.0`` if not defined.
435
- strand_id: Strand ID for transient data. Use ``0`` for static
436
- zones. Default to ``0`` (static) if not defined.
437
- aux: Zone-level auxiliary data as ``{name: value}`` string
438
- pairs.
439
- datapacking: Must be :attr:`~tecio.libtecio.DataPacking.BLOCK` (the
440
- default). :attr:`~tecio.libtecio.DataPacking.POINT` is an
441
- ASCII-only layout and is not supported by the SZL binary
442
- format. Defined only for parity with ASCII writer.
421
+ data (Sequence[npt.ArrayLike]):
422
+ One NumPy array per active (non-passive, non-shared) dataset variable,
423
+ in dataset order. Array shape is used to infer ``imax``, ``jmax``, and
424
+ ``kmax``; Fortran (column-major) order is assumed. Pass ``None`` to
425
+ write a zone header only.
426
+ title (str):
427
+ Zone title. Defaults to ``"IJK_Zone_{index}"``, this zone's 0-based
428
+ index.
429
+ variables (list[str]):
430
+ Variable name list. Required on the first call when the file has not
431
+ been opened yet (lazy-open path); ignored once the file is already
432
+ initialised. Default to ``[V1, V2, V3, ...]`` if not provided in open or
433
+ zone call.
434
+ value_locations (Sequence[ValueLocation]):
435
+ Per-active-variable :class:`~tecio.ValueLocation`. Defaults to all
436
+ ``NODAL``.
437
+ passive_vars (list | tuple | set | None):
438
+ Variables to mark passive: a list, tuple, or set of variable names, or
439
+ of 0-based indices, e.g. ``{"x"}`` or ``[3]``. Defaults to all active
440
+ (``passive_vars=None``).
441
+ var_sharing (Mapping[int | str, int]):
442
+ Variables to share from another zone: a ``{variable: source zone}``
443
+ mapping, name or 0-based index to 0-based zone index, e.g. ``{"x": 0,
444
+ "y": 0}``. Defaults to no sharing.
445
+ solution_time (float):
446
+ Solution time for transient data. Default to ``0.0`` if not defined.
447
+ strand_id (int):
448
+ Strand ID for transient data. Use ``0`` for static zones. Default to
449
+ ``0`` (static) if not defined.
450
+ aux (dict[str, str | float | int]):
451
+ Zone-level auxiliary data as ``{name: value}`` string pairs.
452
+ datapacking (DataPacking | str):
453
+ In :attr:`~tecio.DataPacking.POINT` format, the values for all variables
454
+ are given for the first point, then the second point, and so on. In
455
+ :attr:`~tecio.DataPacking.BLOCK` format, all of the values for the first
456
+ variable are given in a block, then all of the values for the second
457
+ variable, then all of the values for the third, and so
458
+ forth. :attr:`~tecio.DataPacking.BLOCK` format must be used for
459
+ cell-centered data and polyhedral zones
460
+ (:attr:`~tecio.ZoneType.FEPOLYGON`/:attr:`~tecio.ZoneType.FEPOLYHEDRAL`),
461
+ as well as for all binary data. `POINT` and `BLOCK` keyword strings also
462
+ accepted. Defaults to :attr:`~tecio.DataPacking.BLOCK`.
443
463
 
444
464
  Raises:
445
- NotImplementedError: If *datapacking* is
446
- :attr:`~tecio.libtecio.DataPacking.POINT`.
447
- ValueError: If I/O operation attempted on closed or None file
448
- handle.
449
- RuntimeError: If attempting to write variable aux before variables
450
- are defined.
465
+ TypeError:
466
+ If *passive_vars*/*var_sharing* isn't a list/tuple/set/mapping as
467
+ appropriate, or mixes variable names and indices.
468
+ ValueError:
469
+ If I/O operation attempted on closed or None file handle, or a variable
470
+ is named in both *passive_vars* and *var_sharing*.
471
+ RuntimeError:
472
+ If attempting to write variable aux before variables are defined.
473
+ KeyError:
474
+ A *passive_vars*/*var_sharing* name doesn't match any dataset variable.
475
+ IndexError:
476
+ A *passive_vars*/*var_sharing* index is out of range.
451
477
  """
452
478
  if isinstance(datapacking, str):
453
479
  try:
@@ -458,15 +484,16 @@ class TecplotDatWriter(TecplotWriter):
458
484
  "use DataPacking.BLOCK, DataPacking.POINT, or their "
459
485
  "string equivalents."
460
486
  ) from None
487
+ new_zone_index = self.current_zone + 1
461
488
  if title is None:
462
- title = f"IJK_Zone_{self.current_zone + 1}"
489
+ title = f"IJK_Zone_{new_zone_index}"
463
490
  if variables is None:
464
491
  variables = [f"V{i}" for i in range(1, len(data) + 1)]
465
492
 
466
493
  # Open the file if lazily loaded and flush buffered aux data
467
494
  if not self._opened:
468
495
  self._open(variables)
469
- if self.current_zone == 0:
496
+ if self.current_zone == -1:
470
497
  self.flush_aux()
471
498
 
472
499
  # Convert input data to NumPy arrays
@@ -480,7 +507,7 @@ class TecplotDatWriter(TecplotWriter):
480
507
  if value_locations is None:
481
508
  value_locations = [ValueLocation.NODAL] * len(arrays)
482
509
 
483
- prepared: PreparedOrderedZone = prepare_ordered_zone(
510
+ prepared: PreparedOrderedZone | None = prepare_ordered_zone(
484
511
  arrays,
485
512
  variable_types,
486
513
  value_locations=value_locations,
@@ -490,8 +517,10 @@ class TecplotDatWriter(TecplotWriter):
490
517
  meta=self._meta,
491
518
  on_error=self._handle_zone_error,
492
519
  )
493
- passive_vars = prepared.passive_vars
494
- var_sharing = prepared.var_sharing
520
+ if prepared is None:
521
+ return
522
+ passive_flags = prepared.passive_vars
523
+ share_idx = prepared.var_sharing
495
524
  imax, jmax, kmax = prepared.imax, prepared.jmax, prepared.kmax
496
525
  value_locations_global = prepared.value_locations_global
497
526
  variable_types_global = prepared.variable_types_global
@@ -506,8 +535,8 @@ class TecplotDatWriter(TecplotWriter):
506
535
  kmax=kmax,
507
536
  solution_time=solution_time,
508
537
  strand_id=strand_id,
509
- passive_vars=passive_vars,
510
- var_sharing=var_sharing,
538
+ passive_vars=passive_flags,
539
+ var_sharing=share_idx,
511
540
  value_locations_global=value_locations_global,
512
541
  variable_types_global=variable_types_global,
513
542
  aux=aux,
@@ -539,7 +568,7 @@ class TecplotDatWriter(TecplotWriter):
539
568
  _stage_float_array(buf, cast, self._float_fmt)
540
569
 
541
570
  self._check_handle().write(buf.getvalue())
542
- self.current_zone += 1
571
+ self.current_zone = new_zone_index
543
572
 
544
573
  # Finally set zone metadata after successfully completing TecIO calls
545
574
  self._meta.record_zone(
@@ -552,8 +581,8 @@ class TecplotDatWriter(TecplotWriter):
552
581
  num_aux_items=len(aux) if aux else 0,
553
582
  dimensions=(imax, jmax, kmax),
554
583
  value_locations=tuple(value_locations_global),
555
- passive_vars=tuple(bool(p) for p in passive_vars),
556
- shared_vars=tuple(int(s) for s in var_sharing),
584
+ passive_vars=tuple(bool(p) for p in passive_flags),
585
+ shared_vars=tuple(share_idx),
557
586
  data_types=tuple(variable_types_global),
558
587
  )
559
588
  )
@@ -564,13 +593,13 @@ class TecplotDatWriter(TecplotWriter):
564
593
  self,
565
594
  data: Sequence[npt.ArrayLike],
566
595
  zone_type: ZoneType,
567
- *,
568
596
  node_map: npt.ArrayLike | None = None,
597
+ *,
569
598
  title: str | None = None,
570
599
  variables: list[str] | None = None,
571
600
  value_locations: Sequence[ValueLocation] | None = None,
572
- passive_vars: Sequence[bool | int] | None = None,
573
- var_sharing: Sequence[int] | None = None,
601
+ passive_vars: PassiveVarsSpec | None = None,
602
+ var_sharing: VarSharingSpec | None = None,
574
603
  con_sharing: int | None = None,
575
604
  face_neighbors: npt.ArrayLike | None = None,
576
605
  face_neighbor_mode: FaceNeighborMode | None = None,
@@ -589,85 +618,104 @@ class TecplotDatWriter(TecplotWriter):
589
618
  ``FEPOLYGON`` and ``FEPOLYHEDRON`` raise :exc:`NotImplementedError`.
590
619
 
591
620
  Args:
592
- data: Sequence of 1-D arrays, one per dataset variable. NODAL
593
- arrays must have length ``num_nodes``; CELL_CENTERED arrays
594
- must have length ``num_cells``. ``num_nodes`` and
595
- ``num_cells`` are inferred from ``node_map`` (or from the
596
- ``con_sharing`` source zone when ``node_map`` is omitted).
597
- zone_type: FE zone type from the ZoneType enum. Must be one of the
598
- types in ``_FE_SIMPLE``.
599
- node_map: Integer array of shape ``(num_cells, nodes_per_cell)``
600
- containing 1-based node indices. 32- or 64-bit write is
601
- chosen automatically based on the maximum index value.
602
- Required unless ``con_sharing`` is set, in which case the
603
- connectivity -- and the node/cell counts derived from it --
604
- are inherited from the source zone instead.
605
- title: Zone title string. Defaults to ``"FE_Zone_{current_zone +
606
- 1}"`` if not provided.
607
- variables: Variable name list. Required only when the file has not
608
- been opened yet (lazy-open path). Ignored on subsequent
609
- zones once the file is already initialised. Default to
610
- ``[V1, V2, V3, ...]`` if not provided in open or zone call.
611
- value_locations: Per-variable ValueLocation. Defaults to all NODAL.
612
- passive_vars: Per-variable passive flags. Defaults to all active
613
- (False).
614
- var_sharing: Per-variable share from zone index. Defaults to no
615
- sharing. Cross-checked against ``node_map`` /
616
- ``con_sharing`` for a consistent node/cell count.
617
- con_sharing: Optional zone index that the connectivity is shared from.
618
- ``None`` or ``0`` indicates no sharing (this zone owns its
619
- connectivity). The first zone in a dataset must own its
620
- connectivity. Connectivity cannot be shared when face
621
- neighbor mode is set to global. Connectivity cannot be
622
- shared between cell-based and face-based finite element
623
- zones.
624
- face_neighbors: Optional face-neighbor connectivity, always a
625
- flat array (whatever shape you pass is
626
- flattened before writing). The number of
627
- values per connection depends on
628
- ``face_neighbor_mode``, a fixed 3 or 4 for the
629
- one-to-one modes, a variable, self-describing
630
- count per record for the "many" modes. The
631
- connection count is always derived from this
632
- array, never a separate parameter.
633
- face_neighbor_mode: None (the default) means no face-neighbor
634
- data, matching the read side. If
635
- ``face_neighbors`` is given without this,
636
- treated as LOCAL_ONE_TO_ONE. Given *without*
637
- ``face_neighbors``, raises, that combination is
638
- almost certainly a mistake.
639
- face_neighbors_complete: Whether ``face_neighbors`` is the
640
- entire adjacency picture (``True``), Tecplot
641
- shouldn't auto-detect anything further, or a
642
- supplement to auto-detected conformal adjacency
643
- (``False``). ``None`` (the default) omits the
644
- ``FEFACENEIGHBORSCOMPLETE`` keyword entirely,
645
- deferring to Tecplot's own default. ASCII-only:
646
- neither the classic nor new C API can express
647
- this, so it has no equivalent on
648
- :class:`~tecio.TecplotSzlWriter` or
649
- :class:`~tecio.TecplotPltWriter`.
650
- solution_time: Solution time for transient data (0.0 = static).
651
- strand_id: Strand ID for transient data (0 = static).
652
- aux: Zone-level auxiliary data as ``{name: value}`` strings.
653
- datapacking: Must be :attr:`~tecio.libtecio.DataPacking.BLOCK` (the
654
- default). :attr:`~tecio.libtecio.DataPacking.POINT` is an
655
- ASCII-only layout and is not supported by the SZL binary
656
- format. Defined only for parity with ASCII writer.
621
+ data (Sequence[npt.ArrayLike]):
622
+ Sequence of 1-D arrays, one per active (non-passive, non-shared) dataset
623
+ variable, in dataset order. NODAL arrays must have length ``num_nodes``;
624
+ CELL_CENTERED arrays must have length ``num_cells``. ``num_nodes`` and
625
+ ``num_cells`` are inferred from ``node_map`` (or from the
626
+ ``con_sharing`` source zone when ``node_map`` is omitted).
627
+ zone_type (ZoneType):
628
+ FE zone type from the ZoneType enum. Must be one of the types in
629
+ ``_FE_SIMPLE``.
630
+ node_map (npt.ArrayLike):
631
+ Integer array of shape ``(num_cells, nodes_per_cell)`` containing
632
+ 0-based node indices. Required unless ``con_sharing`` is set, in which
633
+ case the connectivity are inherited from the source zone instead.
634
+ title (str):
635
+ Zone title string. Defaults to ``"FE_Zone_{index}"``, this zone's
636
+ 0-based index.
637
+ variables (list[str]):
638
+ Variable name list. Required only when the file has not been opened yet
639
+ (lazy-open path). Ignored on subsequent zones once the file is already
640
+ initialised. Default to ``[V1, V2, V3, ...]`` if not provided in open or
641
+ zone call.
642
+ value_locations (Sequence[ValueLocation]):
643
+ Per-active-variable :class:`~tecio.ValueLocation`. Defaults to all
644
+ ``NODAL``.
645
+ passive_vars (list | tuple | set | None):
646
+ Variables to mark passive: a list, tuple, or set of variable names, or
647
+ of 0-based indices, e.g. ``{"x"}`` or ``[3]``. Defaults to all active
648
+ (``passive_vars=None``).
649
+ var_sharing (Mapping[int | str, int]):
650
+ Variables to share from another zone: a ``{variable: source zone}``
651
+ mapping, name or 0-based index to 0-based zone index. Defaults to no
652
+ sharing (None or an empty mapping). Cross-checked against ``node_map`` /
653
+ ``con_sharing`` for a consistent node/cell count.
654
+ con_sharing (int):
655
+ Optional zone index that the connectivity is shared from. ``None``
656
+ indicates no sharing (this zone owns its connectivity). The first
657
+ zone in a dataset must own its connectivity. Connectivity cannot be
658
+ shared when face neighbor mode is set to global. Connectivity cannot be
659
+ shared between cell-based and face-based finite element zones.
660
+ face_neighbors (npt.ArrayLike):
661
+ Optional face-neighbor connectivity, always a flat array (whatever shape
662
+ you pass is flattened before writing). The number of values per
663
+ connection depends on ``face_neighbor_mode``, a fixed 3 or 4 for the
664
+ one-to-one modes, a variable, self-describing count per record for the
665
+ "many" modes. The connection count is always derived from this array,
666
+ never a separate parameter.
667
+ face_neighbor_mode (FaceNeighborMode):
668
+ Default to None meaning no face-neighbor data. If ``face_neighbors`` is
669
+ supplied without defining a face neighbor mode, it is treated as
670
+ LOCAL_ONE_TO_ONE. Cannot be defined without also supplying a
671
+ ``face_neighbors`` array.
672
+ face_neighbors_complete (bool):
673
+ Whether ``face_neighbors`` is the entire adjacency picture (``True``),
674
+ Tecplot shouldn't auto-detect anything further, or a supplement to
675
+ auto-detected conformal adjacency (``False``). ``None`` (the default)
676
+ omits the ``FEFACENEIGHBORSCOMPLETE`` keyword entirely, deferring to the
677
+ Tecplot default. ASCII-only: neither the classic nor new C API can
678
+ express this, so it has no equivalent on
679
+ :class:`~tecio.TecplotSzlWriter` or :class:`~tecio.TecplotPltWriter`.
680
+ solution_time (float):
681
+ Solution time for transient data. Default to ``0.0`` if not defined.
682
+ strand_id (int):
683
+ Strand ID for transient data. Use ``0`` for static zones. Default to
684
+ ``0`` (static) if not defined.
685
+ aux (dict[str, str | float | int]):
686
+ Zone-level auxiliary data as ``{name: value}`` string pairs.
687
+ datapacking (DataPacking | str):
688
+ In :attr:`~tecio.DataPacking.POINT` format, the values for all variables
689
+ are given for the first point, then the second point, and so on. In
690
+ :attr:`~tecio.DataPacking.BLOCK` format, all of the values for the first
691
+ variable are given in a block, then all of the values for the second
692
+ variable, then all of the values for the third, and so
693
+ forth. :attr:`~tecio.DataPacking.BLOCK` format must be used for
694
+ cell-centered data and polyhedral zones
695
+ (:attr:`~tecio.ZoneType.FEPOLYGON`/:attr:`~tecio.ZoneType.FEPOLYHEDRAL`),
696
+ as well as for all binary data. `POINT` and `BLOCK` keyword strings also
697
+ accepted. Defaults to :attr:`~tecio.DataPacking.BLOCK`.
657
698
 
658
699
  Raises:
659
- NotImplementedError: For FEPOLYGON, FEPOLYHEDRON, or if *datapacking*
660
- is :attr:`~tecio.libtecio.DataPacking.POINT`.
661
- ValueError: On variable count or array length mismatch; if
662
- ``node_map`` is omitted without ``con_sharing``; or if
663
- ``var_sharing``/``con_sharing`` reference a zone with
664
- no recorded node/cell count, or one whose count
665
- disagrees with this zone's.
666
- ValueError: If I/O operation attempted on closed or None file
667
- handle.
668
- RuntimeError: If attempting to write variable aux before variables
669
- are defined.
670
-
700
+ NotImplementedError:
701
+ For FEPOLYGON or FEPOLYHEDRON.
702
+ TypeError:
703
+ If *passive_vars*/*var_sharing* isn't a list/tuple/set/mapping as
704
+ appropriate, or mixes variable names and indices.
705
+ ValueError:
706
+ On variable count or array length mismatch; if ``node_map`` is omitted
707
+ without ``con_sharing``; if ``var_sharing``/``con_sharing`` reference a
708
+ zone with no recorded node/cell count, or one whose count disagrees with
709
+ this zone's; or a variable named in both *passive_vars* and
710
+ *var_sharing*.
711
+ ValueError:
712
+ If I/O operation attempted on closed or None file handle.
713
+ RuntimeError:
714
+ If attempting to write variable aux before variables are defined.
715
+ KeyError:
716
+ A *passive_vars*/*var_sharing* name doesn't match any dataset variable.
717
+ IndexError:
718
+ A *passive_vars*/*var_sharing* index is out of range.
671
719
  """
672
720
  if isinstance(datapacking, str):
673
721
  try:
@@ -687,15 +735,16 @@ class TecplotDatWriter(TecplotWriter):
687
735
  f"Zone type {zone_type!r} is not supported by write_fe_zone."
688
736
  )
689
737
 
738
+ new_zone_index = self.current_zone + 1
690
739
  if title is None:
691
- title = f"FE_Zone_{self.current_zone + 1}"
740
+ title = f"FE_Zone_{new_zone_index}"
692
741
  if variables is None:
693
742
  variables = [f"V{i}" for i in range(1, len(data) + 1)]
694
743
 
695
744
  # Open the file if lazily loaded and flush buffered aux data
696
745
  if not self._opened:
697
746
  self._open(variables)
698
- if self.current_zone == 0:
747
+ if self.current_zone == -1:
699
748
  self.flush_aux()
700
749
 
701
750
  variable_types = [
@@ -709,7 +758,7 @@ class TecplotDatWriter(TecplotWriter):
709
758
  if value_locations is None:
710
759
  value_locations = [ValueLocation.NODAL] * len(data)
711
760
 
712
- prepared: PreparedFEZone = prepare_fe_zone(
761
+ prepared: PreparedFEZone | None = prepare_fe_zone(
713
762
  arrays,
714
763
  variable_types,
715
764
  zone_type,
@@ -724,8 +773,10 @@ class TecplotDatWriter(TecplotWriter):
724
773
  meta=self._meta,
725
774
  on_error=self._handle_zone_error,
726
775
  )
727
- passive_vars = prepared.passive_vars
728
- var_sharing = prepared.var_sharing
776
+ if prepared is None:
777
+ return
778
+ passive_flags = prepared.passive_vars
779
+ share_idx = prepared.var_sharing
729
780
  con_sharing = prepared.con_sharing
730
781
  num_nodes = prepared.num_nodes
731
782
  num_cells = prepared.num_cells
@@ -750,8 +801,8 @@ class TecplotDatWriter(TecplotWriter):
750
801
  num_elements=num_cells,
751
802
  solution_time=solution_time,
752
803
  strand_id=strand_id,
753
- passive_vars=passive_vars,
754
- var_sharing=var_sharing,
804
+ passive_vars=passive_flags,
805
+ var_sharing=share_idx,
755
806
  value_locations_global=value_locations_global,
756
807
  variable_types_global=variable_types_global,
757
808
  con_sharing=con_sharing,
@@ -786,19 +837,26 @@ class TecplotDatWriter(TecplotWriter):
786
837
  cast = np.asarray(arr, dtype=_DT_TO_DTYPE[dt]).ravel()
787
838
  _stage_float_array(buf, cast, self._float_fmt)
788
839
 
789
- # Write connectivity (if not shared)
790
- if not con_sharing:
840
+ # Write connectivity (if not shared). Node numbers are 0-based in Python;
841
+ # ASCII text always uses 1-based node numbers.
842
+ if con_sharing is None:
791
843
  assert node_map is not None
792
- conn = np.asarray(node_map, dtype=np.intp).reshape(
793
- num_cells, _NODES_PER_ELEM[zone_type]
844
+ conn = (
845
+ np.asarray(node_map, dtype=np.intp).reshape(
846
+ num_cells, _NODES_PER_ELEM[zone_type]
847
+ )
848
+ + 1
794
849
  )
795
850
  for row in conn:
796
851
  _stage_connectivity_row(buf, row)
797
852
 
798
- # Write face-neighbor connections (if provided)
853
+ # Write face-neighbor connections (if provided). Cell and remote-zone
854
+ # references are 0-based in Python; ASCII text is always fully 1-based.
799
855
  if face_neighbors_arr is not None:
800
856
  assert face_neighbor_mode is not None # narrowed above
801
- flat = face_neighbors_arr
857
+ flat = convert_face_connections_to_one_based(
858
+ face_neighbors_arr, face_neighbor_mode
859
+ )
802
860
  one_to_one = (
803
861
  FaceNeighborMode.LOCAL_ONE_TO_ONE,
804
862
  FaceNeighborMode.GLOBAL_ONE_TO_ONE,
@@ -822,7 +880,7 @@ class TecplotDatWriter(TecplotWriter):
822
880
  i += record_len
823
881
 
824
882
  self._check_handle().write(buf.getvalue())
825
- self.current_zone += 1
883
+ self.current_zone = new_zone_index
826
884
 
827
885
  # Finally set zone metadata after successfully completing TecIO calls
828
886
  self._meta.record_zone(
@@ -836,8 +894,8 @@ class TecplotDatWriter(TecplotWriter):
836
894
  num_nodes=num_nodes,
837
895
  num_elements=num_cells,
838
896
  value_locations=tuple(value_locations_global),
839
- passive_vars=tuple(bool(p) for p in passive_vars),
840
- shared_vars=tuple(int(s) for s in var_sharing),
897
+ passive_vars=tuple(bool(p) for p in passive_flags),
898
+ shared_vars=tuple(share_idx),
841
899
  data_types=tuple(variable_types_global),
842
900
  face_neighbor_mode=(
843
901
  face_neighbor_mode if face_neighbors_arr is not None else None
@@ -850,7 +908,7 @@ class TecplotDatWriter(TecplotWriter):
850
908
 
851
909
  def _handle_zone_error(self) -> None:
852
910
  """Delete the output file if no zone has been committed yet."""
853
- if self.current_zone == 0:
911
+ if self.current_zone == -1:
854
912
  if self._fp is not None:
855
913
  self._fp.close()
856
914
  self._fp = None
@@ -880,10 +938,10 @@ class TecplotDatWriter(TecplotWriter):
880
938
  solution_time: float = 0.0,
881
939
  strand_id: int = 0,
882
940
  passive_vars: Sequence[bool | int] | None = None,
883
- var_sharing: Sequence[int] | None = None,
941
+ var_sharing: Sequence[int | None] | None = None,
884
942
  value_locations_global: list[ValueLocation] | None = None,
885
943
  variable_types_global: list[DataType] | None = None,
886
- con_sharing: int = 0,
944
+ con_sharing: int | None = None,
887
945
  aux: dict[str, Any] | None = None,
888
946
  datapacking: DataPacking = DataPacking.BLOCK,
889
947
  num_face_connections: int = 0,
@@ -940,13 +998,15 @@ class TecplotDatWriter(TecplotWriter):
940
998
  if pidx:
941
999
  buf.write(f"{_INDENT}PASSIVEVARLIST=[{','.join(pidx)}]\n")
942
1000
 
943
- if var_sharing and any(var_sharing):
944
- entries = [f"[{i + 1}]={z}" for i, z in enumerate(var_sharing) if z]
1001
+ if var_sharing and any(z is not None for z in var_sharing):
1002
+ entries = [
1003
+ f"[{i + 1}]={z + 1}" for i, z in enumerate(var_sharing) if z is not None
1004
+ ]
945
1005
  if entries:
946
1006
  buf.write(f"{_INDENT}VARSHARELIST=({','.join(entries)})\n")
947
1007
 
948
- if con_sharing:
949
- buf.write(f"{_INDENT}CONNECTIVITYSHAREZONE={con_sharing}\n")
1008
+ if con_sharing is not None:
1009
+ buf.write(f"{_INDENT}CONNECTIVITYSHAREZONE={con_sharing + 1}\n")
950
1010
 
951
1011
  if aux:
952
1012
  for name, value in aux.items():