pytecplot 1.5.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.
Files changed (97) hide show
  1. pytecplot-1.5.2.dist-info/LICENSE.html +77 -0
  2. pytecplot-1.5.2.dist-info/METADATA +508 -0
  3. pytecplot-1.5.2.dist-info/RECORD +97 -0
  4. pytecplot-1.5.2.dist-info/WHEEL +5 -0
  5. pytecplot-1.5.2.dist-info/top_level.txt +1 -0
  6. tecplot/__init__.py +94 -0
  7. tecplot/annotation/__init__.py +7 -0
  8. tecplot/annotation/annotation.py +223 -0
  9. tecplot/annotation/geometry.py +369 -0
  10. tecplot/annotation/georeference.py +33 -0
  11. tecplot/annotation/image.py +185 -0
  12. tecplot/annotation/iter.py +105 -0
  13. tecplot/annotation/polyline.py +381 -0
  14. tecplot/annotation/text.py +744 -0
  15. tecplot/constant.py +401 -0
  16. tecplot/data/__init__.py +34 -0
  17. tecplot/data/array.py +645 -0
  18. tecplot/data/create.py +1 -0
  19. tecplot/data/dataset.py +1809 -0
  20. tecplot/data/extract.py +544 -0
  21. tecplot/data/face_neighbors.py +494 -0
  22. tecplot/data/facemap.py +814 -0
  23. tecplot/data/load.py +2075 -0
  24. tecplot/data/nodemap.py +577 -0
  25. tecplot/data/operate.py +928 -0
  26. tecplot/data/query.py +334 -0
  27. tecplot/data/save.py +343 -0
  28. tecplot/data/variable.py +280 -0
  29. tecplot/data/zone.py +841 -0
  30. tecplot/exception.py +201 -0
  31. tecplot/export/__init__.py +13 -0
  32. tecplot/export/animation.py +521 -0
  33. tecplot/export/export_setup.py +318 -0
  34. tecplot/export/image.py +577 -0
  35. tecplot/export/print_setup.py +218 -0
  36. tecplot/export/time_animation.py +607 -0
  37. tecplot/extension/__init__.py +14 -0
  38. tecplot/extension/ipython.py +68 -0
  39. tecplot/layout/__init__.py +20 -0
  40. tecplot/layout/frame.py +1285 -0
  41. tecplot/layout/layout.py +488 -0
  42. tecplot/layout/page.py +541 -0
  43. tecplot/legend/__init__.py +4 -0
  44. tecplot/legend/contour_legend.py +516 -0
  45. tecplot/legend/legend.py +141 -0
  46. tecplot/legend/line_legend.py +66 -0
  47. tecplot/legend/rgb_coloring_legend.py +291 -0
  48. tecplot/legend/scatter_legend.py +136 -0
  49. tecplot/macro.py +339 -0
  50. tecplot/plot/__init__.py +62 -0
  51. tecplot/plot/axes.py +881 -0
  52. tecplot/plot/axis.py +1350 -0
  53. tecplot/plot/blanking.py +584 -0
  54. tecplot/plot/contour.py +1302 -0
  55. tecplot/plot/effects.py +162 -0
  56. tecplot/plot/fieldmap.py +2159 -0
  57. tecplot/plot/grid.py +758 -0
  58. tecplot/plot/isosurface.py +1167 -0
  59. tecplot/plot/labels.py +338 -0
  60. tecplot/plot/linemap.py +1905 -0
  61. tecplot/plot/linking.py +489 -0
  62. tecplot/plot/plot.py +1791 -0
  63. tecplot/plot/rgb_coloring.py +281 -0
  64. tecplot/plot/scatter.py +396 -0
  65. tecplot/plot/slice.py +1544 -0
  66. tecplot/plot/streamtrace.py +1949 -0
  67. tecplot/plot/symbol.py +567 -0
  68. tecplot/plot/ticks.py +666 -0
  69. tecplot/plot/title.py +380 -0
  70. tecplot/plot/vector.py +679 -0
  71. tecplot/plot/view.py +868 -0
  72. tecplot/session/__init__.py +112 -0
  73. tecplot/session/aux_data.py +276 -0
  74. tecplot/session/config.py +92 -0
  75. tecplot/session/session.py +425 -0
  76. tecplot/session/state_changed.py +225 -0
  77. tecplot/session/style.py +504 -0
  78. tecplot/tecutil/__init__.py +16 -0
  79. tecplot/tecutil/arglist.py +230 -0
  80. tecplot/tecutil/captured_output.py +177 -0
  81. tecplot/tecutil/constant.py +1312 -0
  82. tecplot/tecutil/index_set.py +118 -0
  83. tecplot/tecutil/lock.py +36 -0
  84. tecplot/tecutil/message_pb2.py +39 -0
  85. tecplot/tecutil/preamble.py +54 -0
  86. tecplot/tecutil/stringlist.py +114 -0
  87. tecplot/tecutil/sv.py +1662 -0
  88. tecplot/tecutil/tecutil.py +12402 -0
  89. tecplot/tecutil/tecutil_client.py +627 -0
  90. tecplot/tecutil/tecutil_connector.py +937 -0
  91. tecplot/tecutil/tecutil_rpc.py +6845 -0
  92. tecplot/tecutil/util.py +332 -0
  93. tecplot/text/__init__.py +3 -0
  94. tecplot/text/font.py +224 -0
  95. tecplot/text/label_format.py +374 -0
  96. tecplot/text/textbox.py +106 -0
  97. tecplot/version.py +41 -0
@@ -0,0 +1,1809 @@
1
+ import collections
2
+ import ctypes
3
+ import fnmatch
4
+ import itertools as it
5
+ import logging
6
+ import re
7
+ import warnings
8
+
9
+ from collections.abc import Iterable
10
+ from functools import reduce
11
+ from keyword import iskeyword
12
+ from textwrap import dedent, TextWrapper
13
+
14
+ from ..tecutil import _tecutil, _tecutil_connector
15
+ from ..constant import *
16
+ from ..exception import *
17
+ from .. import layout, macro, session, tecutil, version
18
+ from ..tecutil import (IndexSet, ListWrapper, StringList,
19
+ flatten_args, lock, lock_attributes, sv)
20
+ from .variable import Variable
21
+ from .zone import ClassicFEZone, OrderedZone, PolyFEZone, Zone
22
+
23
+ log = logging.getLogger(__name__)
24
+
25
+
26
+ @lock_attributes
27
+ class SolutionTimeClustering(object):
28
+ """Settings for controlling solution time clustering of solution times.
29
+
30
+ By default, a dataset's zones solution times are organized into time steps
31
+ with the assumption that they are distributed linearly, and are grouped
32
+ together if they are within the following solution time tolerance::
33
+
34
+ (tolerance_factor)*(max_time - min_time) + absolute_tolerance
35
+
36
+ Where by default the tolerance_factor is 1.0e-6 and the absolute_tolerance
37
+ is 0.0.
38
+ """
39
+ def __init__(self, dataset):
40
+ self.dataset = dataset
41
+
42
+ def __str__(self):
43
+ """Brief string representation.
44
+
45
+ Returns:
46
+ `str`: Brief representation of this `SolutionTimeClustering`,
47
+ showing the options controlling how solution times are grouped into
48
+ time steps.
49
+
50
+ Example::
51
+
52
+ >>> dataset = frame.dataset
53
+ >>> print(dataset.solution_time_clustering)
54
+ SolutionTimeClustering
55
+ time_scaling: TimeScaling.Linear
56
+ absolute_tolerance: 0.0
57
+ tolerance_factor: 1.0e-6
58
+ """
59
+ fmt = dedent('''\
60
+ SolutionTimeClustering:
61
+ time_scaling: {}
62
+ absolute_tolerance: {}
63
+ tolerance_factor: {}''')
64
+ return fmt.format(self.time_scaling, self.absolute_tolerance,
65
+ self.tolerance_factor)
66
+
67
+ @property
68
+ @lock()
69
+ def time_scaling(self):
70
+ """`TimeScaling`: Scaling used to cluster zones by their solution times.
71
+
72
+ Identifies if the dataset's zone solution times are distributed linearly
73
+ or logarithmically. (Default: `TimeScaling.Linear`)
74
+
75
+ Possible values: `TimeScaling.Linear`, `TimeScaling.Logarithmic`.
76
+
77
+ Example usage::
78
+
79
+ >>> from tecplot.constant import TimeScaling
80
+ >>> dataset.solution_time_clustering.time_scaling = \\
81
+ ... TimeScaling.Logarithmic
82
+
83
+ .. versionadded:: 2021.2
84
+ Solution time clustering requires Tecplot 360 2021 R2 or later.
85
+ """
86
+ return _tecutil.SolutionTimeGetTimeScaling(self.dataset.uid)
87
+
88
+ @time_scaling.setter
89
+ @lock()
90
+ def time_scaling(self, time_scaling):
91
+ value = TimeScaling(time_scaling).value
92
+ if not _tecutil.SolutionTimeSetTimeScaling(self.dataset.uid, value):
93
+ raise TecplotSystemError()
94
+
95
+ @property
96
+ @lock()
97
+ def absolute_tolerance(self):
98
+ """`float`: Absolute tolerance used to cluster zones by their solution times.
99
+
100
+ Absolute tolerance applied to each solution time when clustering with
101
+ nearby times. (Default: 0.0)
102
+
103
+ Example usage::
104
+
105
+ >>> dataset.solution_time_clustering.absolute_tolerance = 0.0025
106
+
107
+ .. versionadded:: 2021.2
108
+ Solution time clustering requires Tecplot 360 2021 R2 or later.
109
+ """
110
+ return _tecutil.SolutionTimeGetAbsoluteTolerance(self.dataset.uid)
111
+
112
+ @absolute_tolerance.setter
113
+ @lock()
114
+ def absolute_tolerance(self, absolute_tolerance):
115
+ uid = self.dataset.uid
116
+ value = float(absolute_tolerance)
117
+ if not _tecutil.SolutionTimeSetAbsoluteTolerance(uid, value):
118
+ raise TecplotSystemError()
119
+
120
+ @property
121
+ @lock()
122
+ def tolerance_factor(self):
123
+ """`float`: Tolerance factor used to cluster zones by solution times.
124
+
125
+ Tolerance factor, which is multiplied by the overall solution time
126
+ delta and applied to each solution time when clustering nearby times.
127
+ (Default: 1.0e-6)
128
+
129
+ Example usage::
130
+
131
+ >>> dataset.solution_time_clustering.tolerance_factor = 1.0e-4
132
+
133
+ .. versionadded:: 2021.2
134
+ Solution time clustering requires Tecplot 360 2021 R2 or later.
135
+ """
136
+ return _tecutil.SolutionTimeGetToleranceFactor(self.dataset.uid)
137
+
138
+ @tolerance_factor.setter
139
+ @lock()
140
+ def tolerance_factor(self, tolerance_factor):
141
+ uid = self.dataset.uid
142
+ value = float(tolerance_factor)
143
+ if not _tecutil.SolutionTimeSetToleranceFactor(uid, value):
144
+ raise TecplotSystemError()
145
+
146
+
147
+ @lock_attributes
148
+ class Dataset(object):
149
+ """Table of `Arrays <Array>` identified by `Zone <data_access>` and `Variable`.
150
+
151
+ This is the primary data container within the Tecplot Engine. A `Dataset <tecplot.data.Dataset>`
152
+ can be shared among several `Frames <layout.Frame>`, though any particular
153
+ `Dataset <tecplot.data.Dataset>` object will have a handle to at least one of them. Any
154
+ modification of a shared `Dataset <tecplot.data.Dataset>` will be reflected in all `Frames
155
+ <layout.Frame>` that use it.
156
+
157
+ Though a `Dataset <tecplot.data.Dataset>` is usually attached to a `Frame <layout.Frame>` and the plot style
158
+ associated with that, it can be thought of as independent from any style or
159
+ plotting representation. Each `Dataset <tecplot.data.Dataset>` consists of a list of `Variables
160
+ <Variable>` which are used by one or more of a list of `Zones
161
+ <data_access>`. The `Variable` determines the data type, while the `Zone
162
+ <data_access>` determines the layout such as shape and ordered vs
163
+ unordered.
164
+
165
+ The actual data are found at the intersection of a `Zone <data_access>` and
166
+ `Variable` and the resulting object is an `Array`. The data array can be
167
+ obtained using either path::
168
+
169
+ >>> # These two lines obtain the same object "x"
170
+ >>> x = dataset.zone('My Zone').values('X')
171
+ >>> x = dataset.variable('X').values('My Zone')
172
+
173
+ A `Dataset <tecplot.data.Dataset>` is the object returned by most data-loading operations in
174
+ |PyTecplot|::
175
+
176
+ >>> dataset = tecplot.data.load_tecplot('my_data.plt')
177
+
178
+ Under `Dataset <tecplot.data.Dataset>`, there are a number methods to create and delete `Zones
179
+ <data_access>` and `variables <Variable>`.
180
+ """
181
+ def __init__(self, uid, frame):
182
+ self.uid = uid
183
+ self.frame = frame
184
+
185
+ def __repr__(self):
186
+ """Executable string representation.
187
+
188
+ Returns:
189
+ `str`: Internal representation of this `Dataset <tecplot.data.Dataset>`.
190
+
191
+ The string returned can be executed to generate a
192
+ clone of this `Dataset <tecplot.data.Dataset>` object::
193
+
194
+ >>> dataset = frame.dataset
195
+ >>> print(repr(dataset))
196
+ Dataset(uid=21, frame=Frame(uid=11, page=Page(uid=1)))
197
+ >>> exec('dataset_clone = '+repr(dataset))
198
+ >>> dataset_clone
199
+ Dataset(uid=21, frame=Frame(uid=11, page=Page(uid=1)))
200
+ >>> dataset == dataset_clone
201
+ True
202
+ """
203
+ return 'Dataset(uid={uid}, frame={frame})'.format(
204
+ uid=self.uid, frame=repr(self.frame))
205
+
206
+ def __str__(self):
207
+ """Brief string representation.
208
+
209
+ Returns:
210
+ `str`: Brief representation of this `Dataset <tecplot.data.Dataset>`, showing
211
+ `Zones <data_access>` and `Variables <Variable>`.
212
+
213
+ Example::
214
+
215
+ >>> dataset = frame.dataset
216
+ >>> print(dataset)
217
+ Dataset: 'My Dataset'
218
+ Zones: 'Rectangular zone'
219
+ Variables: 'x', 'y', 'z'
220
+ """
221
+ fmt = dedent('''\
222
+ Dataset: '{}'
223
+ Zones: {}
224
+ Variables: {}''')
225
+ maxwidth = 79
226
+ wrapper = ListWrapper(initial_width=maxwidth - len(' Zones: '),
227
+ subsequent_indent=' ',
228
+ subsequent_width=maxwidth)
229
+ zones = wrapper.fill(self.zone_names)
230
+ wrapper.initial_width = maxwidth - len(' Variables: ')
231
+ variables = wrapper.fill(self.variable_names)
232
+ return fmt.format(self.title, zones, variables)
233
+
234
+ def __eq__(self, other):
235
+ """Checks for equality in the |Tecplot Engine|.
236
+
237
+ Returns:
238
+ `bool`: `True` if the unique ID numbers are the same for both
239
+ `Datasets <tecplot.data.Dataset>`.
240
+
241
+ This can be useful for determining if two `Frames <layout.Frame>`
242
+ are holding on to the same `Dataset <tecplot.data.Dataset>`::
243
+
244
+ >>> frame1.dataset == frame2.dataset
245
+ True
246
+ """
247
+ return self.uid == other.uid
248
+
249
+ def __ne__(self, other):
250
+ return not self.__eq__(other)
251
+
252
+ def __contains__(self, obj):
253
+ if obj.dataset == self:
254
+ if isinstance(obj, Variable) and obj == self.variable(obj.index):
255
+ return True
256
+ elif isinstance(obj, Zone) and obj == self.zone(obj.index):
257
+ return True
258
+ return False
259
+
260
+ @property
261
+ def aux_data(self):
262
+ """Auxiliary data for this dataset.
263
+
264
+ Returns:
265
+ `AuxData`
266
+
267
+ This is the auxiliary data attached to the dataset. Such data is
268
+ written to the layout file by default and can be retrieved later.
269
+ Example usage::
270
+
271
+ >>> frame = tp.active_frame()
272
+ >>> aux = frame.dataset.aux_data
273
+ >>> aux['Result'] = '3.14159'
274
+ >>> print(aux['Result'])
275
+ 3.14159
276
+ """
277
+ return session.AuxData(self.frame, AuxDataObjectType.Dataset)
278
+
279
+ @property
280
+ def title(self):
281
+ """`str`: Title of this `Dataset <tecplot.data.Dataset>`.
282
+
283
+ Example usage::
284
+
285
+ >>> dataset.title = 'My Data'
286
+
287
+ .. versionchanged:: 2017.3 of Tecplot 360
288
+ The dataset title property requires Tecplot 360 2017 R3 or later.
289
+ """
290
+ success, title, _, _ = _tecutil.DataSetGetInfoByUniqueID(self.uid)
291
+ if not success:
292
+ raise TecplotSystemError()
293
+ return title
294
+
295
+ @title.setter
296
+ @lock()
297
+ def title(self, title):
298
+ if not _tecutil.DataSetSetTitleByUniqueID(self.uid, title):
299
+ raise TecplotSystemError()
300
+
301
+ @property
302
+ def solution_time_clustering(self):
303
+ """`SolutionTimeClustering`: Solution time clustering options.
304
+
305
+ Example usage::
306
+
307
+ >>> from tecplot.constant import TimeScaling
308
+ >>> dataset.solution_time_clustering.time_scaling = \\
309
+ ... TimeScaling.Logarithmic
310
+
311
+ .. versionadded:: 2021.2
312
+ Solution time clustering requires Tecplot 360 2021 R2 or later.
313
+ """
314
+ if __debug__:
315
+ reqver = (2021, 2)
316
+ if version.sdk_version_info < reqver:
317
+ msg = 'Solution time clustering options require 2021 R2 or later.'
318
+ raise TecplotOutOfDateEngineError(reqver, msg)
319
+ return SolutionTimeClustering(self)
320
+
321
+ @property
322
+ def num_zones(self):
323
+ """`int`: Number of `Zones <data_access>` in this `Dataset <tecplot.data.Dataset>`.
324
+
325
+ This count includes disabled zones which were skipped when loading the
326
+ data. Example usage::
327
+
328
+ >>> for i in range(dataset.num_zones):
329
+ ... zone = dataset.zone(i)
330
+ """
331
+ return _tecutil.DataSetGetNumZonesByUniqueID(self.uid)
332
+
333
+ def zone(self, pattern):
334
+ """Returns `Zone <data_access>` by index or string pattern.
335
+
336
+ Parameters:
337
+ pattern (`int`, `str` or `re.Pattern <re.compile>`): Zero-based
338
+ index, case-insensitive `glob-style pattern string
339
+ <fnmatch.fnmatch>` or a compiled `regex pattern instance
340
+ <re.compile>` used to match the zones by name. A negative index
341
+ is interpreted as counting from the end of the available zones.
342
+
343
+ Returns:
344
+ `OrderedZone`, `ClassicFEZone` or `PolyFEZone` depending on the
345
+ zone type, `None` no matching `zone <data_access>` name was found.
346
+
347
+ Raises:
348
+ `TecplotIndexError`
349
+
350
+ .. note::
351
+
352
+ A `Dataset <tecplot.data.Dataset>` can contain `zones <data_access>` with identical names
353
+ and only the first match found is returned. This is not guaranteed
354
+ to be deterministic and care should be taken to have only
355
+ `zones <data_access>` with unique names when this feature is used.
356
+
357
+ The `Zone.name <OrderedZone.name>` attribute is used to match the
358
+ *pattern* to the desired `Zone <data_access>` though this is not
359
+ necessarily unique::
360
+
361
+ >>> ds = frame.dataset
362
+ >>> print(ds)
363
+ Dataset:
364
+ Zones: ['Rectangular zone']
365
+ Variables: ['x', 'y', 'z']
366
+ >>> rectzone = ds.zone('Rectangular zone')
367
+ >>> rectzone == ds.zone(0)
368
+ True
369
+
370
+ .. warning:: **Zone and variable ordering may change between releases**
371
+
372
+ Due to possible changes in data loaders or data formats over time,
373
+ the ordering of zones and variables may be different between
374
+ versions of Tecplot 360. Therefore it is recommended to always
375
+ reference zones and variables **by name** instead of by index.
376
+ """
377
+ if isinstance(pattern, Zone):
378
+ return pattern
379
+ _dispatch = {
380
+ ZoneType.Ordered: OrderedZone,
381
+ ZoneType.FELineSeg: ClassicFEZone,
382
+ ZoneType.FETriangle: ClassicFEZone,
383
+ ZoneType.FEQuad: ClassicFEZone,
384
+ ZoneType.FETetra: ClassicFEZone,
385
+ ZoneType.FEBrick: ClassicFEZone,
386
+ ZoneType.FEPolygon: PolyFEZone,
387
+ ZoneType.FEPolyhedron: PolyFEZone}
388
+ pattern_type = getattr(re, 'Pattern', getattr(re, '_pattern_type', None))
389
+ if isinstance(pattern, (str, pattern_type)):
390
+ try:
391
+ return next(self.zones(pattern))
392
+ except StopIteration:
393
+ pass
394
+ else:
395
+ if pattern < 0:
396
+ pattern += self.num_zones
397
+ try:
398
+ if __debug__:
399
+ # ensure zone is enabled
400
+ if not _tecutil.ZoneIsEnabledByDataSetID(self.uid, pattern + 1):
401
+ msg = 'zone {} is not enabled'.format(pattern)
402
+ raise TecplotIndexError(msg)
403
+ uid = _tecutil.ZoneGetUniqueIDByDataSetID(self.uid,
404
+ pattern + 1)
405
+ ztype = _tecutil.ZoneGetTypeByDataSetID(self.uid, pattern + 1)
406
+ return _dispatch[ztype](uid, self)
407
+ except (TecplotSystemError, TecplotLogicError) as e:
408
+ raise TecplotIndexError(str(e))
409
+
410
+ @property
411
+ def _zone_indices(self):
412
+ """Zero-based indices of all enabled zones in the dataset."""
413
+ success, ptr = _tecutil.ZoneGetEnabledByDataSetID(self.uid)
414
+ if not success:
415
+ raise TecplotSystemError()
416
+ indices = ctypes.cast(ptr, IndexSet)
417
+ try:
418
+ return list(indices)
419
+ finally:
420
+ indices.dealloc()
421
+
422
+ @property
423
+ def _zone_uids(self):
424
+ """Unique IDs for all enabled zones in the dataset."""
425
+ try:
426
+ success, nuids, uids_ptr = _tecutil.ZoneGetUniqueIDsByDataSetID(self.uid)
427
+ if not success:
428
+ raise TecplotSystemError()
429
+ try:
430
+ return uids_ptr[:nuids]
431
+ finally:
432
+ if not _tecutil_connector.connected:
433
+ _tecutil.ArrayDealloc(uids_ptr)
434
+ except (AttributeError, TecplotSystemError) as err:
435
+ try:
436
+ return [_tecutil.ZoneGetUniqueIDByDataSetID(self.uid, zone_index + 1)
437
+ for zone_index in self._zone_indices]
438
+ except:
439
+ raise err
440
+
441
+ @property
442
+ def _zone_types(self):
443
+ """`ZoneType`s of all enabled zones in the dataset."""
444
+ try:
445
+ success, ntypes, types_ptr = _tecutil.ZoneGetTypesByDataSetID(self.uid)
446
+ if not success:
447
+ raise TecplotSystemError()
448
+ try:
449
+ return [ZoneType(types_ptr[i]) for i in range(ntypes)]
450
+ finally:
451
+ if not _tecutil_connector.connected:
452
+ _tecutil.ArrayDealloc(types_ptr)
453
+ except (AttributeError, TecplotSystemError) as err:
454
+ try:
455
+ return [_tecutil.ZoneGetTypeByDataSetID(self.uid, zone_index + 1)
456
+ for zone_index in self._zone_indices]
457
+ except:
458
+ try:
459
+ with self.frame.activated():
460
+ return [_tecutil.ZoneGetType(zone_index + 1)
461
+ for zone_index in self._zone_indices]
462
+ except:
463
+ raise err
464
+
465
+ @property
466
+ def zone_names(self):
467
+ """A `list` of names for all zones in the dataset.
468
+
469
+ .. warning:: **Newlines in string identifiers may affect performance.**
470
+
471
+ When iterating over many items by name, such as must be done when
472
+ fetching an item via pattern matching, PyTecplot will optimize the
473
+ search only if there are no newline characters in the searched
474
+ items. Iterating over strings that contain newlines will be slower
475
+ and therefore, it is best to avoid using newlines in string
476
+ identifiers or names of objects such as `Zones <data_access>` or
477
+ `Variables <Variable>`.
478
+
479
+ Example usage::
480
+
481
+ >>> print(dataset.zone_names)
482
+ ['Zone A', 'Zone B', 'Zone C']
483
+ """
484
+ try:
485
+ success, ptr = _tecutil.ZoneGetNamesByDataSetID(self.uid)
486
+ if not success:
487
+ raise TecplotSystemError()
488
+ names = ctypes.cast(ptr, StringList)
489
+ try:
490
+ return list(names)
491
+ finally:
492
+ names.dealloc()
493
+ except (AttributeError, TecplotSystemError) as err:
494
+ try:
495
+ success, ptr = _tecutil.ZoneGetEnabledNamesByDataSetID(self.uid)
496
+ if not success:
497
+ raise TecplotSystemError()
498
+ names = ctypes.cast(ptr, StringList)
499
+ try:
500
+ return list(names)
501
+ finally:
502
+ names.dealloc()
503
+ except:
504
+ try:
505
+ return [z.name for z in self.zones()]
506
+ except:
507
+ raise err
508
+
509
+ def zones(self, pattern=None):
510
+ """Yields all `Zones <data_access>` matching a *pattern*.
511
+
512
+ Parameters:
513
+ pattern (`str` or `re.Pattern <re.compile>`, optional):
514
+ Case-insensitive `glob-style pattern string <fnmatch.fnmatch>`
515
+ or a compiled `regex pattern instance <re.compile>` used to
516
+ match zone names.
517
+
518
+ Returns:
519
+ Generator of `OrderedZones <OrderedZone>`, `ClassicFEZones
520
+ <ClassicFEZone>` or `PolyFEZones <PolyFEZone>` depending on the
521
+ zone types. All `zones <data_access>` if *pattern* is not
522
+ specified.
523
+
524
+ If **pattern** is a string, this will be interpreted as a
525
+ case-insensitive `glob-style pattern <fnmatch.fnmatch>`. Example using
526
+ glob which will match all zones starting with "A" or "a"::
527
+
528
+ >>> for zone in dataset.zones('A*'):
529
+ ... x_array = zone.variable('X')
530
+
531
+ Alternatively, a regular-expression may be compiled using `re.compile`
532
+ which will be used directly. This example shows a case-sensitive regex
533
+ search::
534
+
535
+ >>> import re
536
+ >>> pattern = re.compile('[A-Z]*')
537
+ >>> for zone in dataset.zones(pattern):
538
+ ... x_array = zone.variable('X')
539
+
540
+ Use list comprehension to construct a list of all zones with 'Wing'
541
+ in the zone name. In contrast to the `Dataset.zones()` method, this
542
+ is case-sensitive::
543
+
544
+ >>> wing_zones = [Z for Z in dataset.zones() if 'Wing' in Z.name]
545
+
546
+ .. warning:: **Zone and variable ordering may change between releases**
547
+
548
+ Due to possible changes in data loaders or data formats over time,
549
+ the ordering of zones and variables may be different between
550
+ versions of Tecplot 360. Therefore it is recommended to always
551
+ reference zones and variables **by name** instead of by index.
552
+ """
553
+ _dispatch = {
554
+ ZoneType.Ordered: OrderedZone,
555
+ ZoneType.FELineSeg: ClassicFEZone,
556
+ ZoneType.FETriangle: ClassicFEZone,
557
+ ZoneType.FEQuad: ClassicFEZone,
558
+ ZoneType.FETetra: ClassicFEZone,
559
+ ZoneType.FEBrick: ClassicFEZone,
560
+ ZoneType.FEPolygon: PolyFEZone,
561
+ ZoneType.FEPolyhedron: PolyFEZone}
562
+
563
+ if pattern:
564
+ pattern_type = getattr(re, 'Pattern', getattr(re, '_pattern_type', None))
565
+ if not isinstance(pattern, pattern_type):
566
+ regex = re.compile(fnmatch.translate(pattern), re.IGNORECASE)
567
+ else:
568
+ regex = pattern
569
+
570
+ if __debug__:
571
+ found = False
572
+ for uid, typ, name in zip(self._zone_uids, self._zone_types, self.zone_names):
573
+ if regex.match(name):
574
+ if __debug__:
575
+ found = True
576
+ yield _dispatch[typ](uid, self)
577
+
578
+ if __debug__:
579
+ if (
580
+ not found and
581
+ isinstance(pattern, str) and
582
+ any(c in pattern for c in '*?[]')
583
+ ):
584
+ msg = 'no zones found matching: "{}"'.format(pattern)
585
+ warning = TecplotPatternMatchWarning(pattern, msg, 'glob')
586
+ warnings.warn(warning)
587
+ else:
588
+ for uid, typ in zip(self._zone_uids, self._zone_types):
589
+ yield _dispatch[typ](uid, self)
590
+
591
+ @property
592
+ def num_variables(self):
593
+ """`int`: Number of `Variables <Variable>` in this `Dataset <tecplot.data.Dataset>`.
594
+
595
+ This count includes disabled variables which were skipped when the data
596
+ was loaded. Example usage::
597
+
598
+ >>> for i in range(dataset.num_variables):
599
+ ... variable = dataset.variable(i)
600
+ """
601
+ return _tecutil.DataSetGetNumVarsByUniqueID(self.uid)
602
+
603
+ def variable(self, pattern):
604
+ """Returns the `Variable` by index or string pattern.
605
+
606
+ Parameters:
607
+ pattern (`int`, `str` or `re.Pattern <re.compile>`): Zero-based
608
+ index, case-insensitive `glob-style pattern string
609
+ <fnmatch.fnmatch>` or a compiled `regex pattern instance
610
+ <re.compile>` used to match the variable by name. A negative
611
+ index is interpreted as counting from the end of the available
612
+ variable.
613
+
614
+ Returns:
615
+ `Variable` or `None` if no matching `Variable` name was found.
616
+
617
+ Raises:
618
+ `TecplotIndexError`
619
+
620
+ .. note::
621
+
622
+ A `Dataset <tecplot.data.Dataset>` can contain `variables <Variable>` with identical names
623
+ and only the first match found is returned. This is not guaranteed
624
+ to be deterministic and care should be taken to have only
625
+ `variables <Variable>` with unique names when this feature is used.
626
+
627
+ The `Variable.name` attribute is used to match the *pattern* to the
628
+ desired `Variable` though this is not necessarily unique::
629
+
630
+ >>> ds = frame.dataset
631
+ >>> print(ds)
632
+ Dataset:
633
+ Zones: ['Rectangular zone']
634
+ Variables: ['x', 'y', 'z']
635
+ >>> x = ds.variable('x')
636
+ >>> x == ds.variable(0)
637
+ True
638
+
639
+ .. warning:: **Zone and variable ordering may change between releases**
640
+
641
+ Due to possible changes in data loaders or data formats over time,
642
+ the ordering of zones and variables may be different between
643
+ versions of Tecplot 360. Therefore it is recommended to always
644
+ reference zones and variables **by name** instead of by index.
645
+ """
646
+ if isinstance(pattern, Variable):
647
+ return pattern
648
+ pattern_type = getattr(re, 'Pattern', getattr(re, '_pattern_type', None))
649
+ if isinstance(pattern, (str, pattern_type)):
650
+ try:
651
+ return next(self.variables(pattern))
652
+ except StopIteration:
653
+ pass
654
+ else:
655
+ if pattern < 0:
656
+ pattern += self.num_variables
657
+ if 0 <= pattern < self.num_variables:
658
+ if __debug__:
659
+ if not _tecutil.VarIsEnabledByDataSetID(self.uid, pattern + 1):
660
+ msg = 'variable {} is not enabled'.format(pattern)
661
+ raise TecplotIndexError(msg)
662
+ return Variable(_tecutil.VarGetUniqueIDByDataSetID(
663
+ self.uid, pattern + 1), self)
664
+ raise TecplotIndexError
665
+
666
+ @property
667
+ def _variable_indices(self):
668
+ """Yields zero-based indices for all enabled variables in the dataset."""
669
+ success, ptr = _tecutil.VarGetEnabledByDataSetID(self.uid)
670
+ if not success:
671
+ raise TecplotSystemError()
672
+ indices = ctypes.cast(ptr, IndexSet)
673
+ try:
674
+ for i in indices:
675
+ yield i
676
+ finally:
677
+ indices.dealloc()
678
+
679
+ @property
680
+ def _variable_uids(self):
681
+ """`list` of Unique IDs for all enabled variables in the dataset."""
682
+ try:
683
+ success, nuids, uids_ptr = _tecutil.VarGetUniqueIDsByDataSetID(self.uid)
684
+ if not success:
685
+ raise TecplotSystemError()
686
+ try:
687
+ return uids_ptr[:nuids]
688
+ finally:
689
+ if not _tecutil_connector.connected:
690
+ _tecutil.ArrayDealloc(uids_ptr)
691
+ except (AttributeError, TecplotSystemError) as err:
692
+ try:
693
+ return [_tecutil.VarGetUniqueIDByDataSetID(self.uid, variable_index + 1)
694
+ for variable_index in self._variable_indices]
695
+ except:
696
+ raise err
697
+
698
+ @property
699
+ def variable_names(self):
700
+ """A `list` of names for all variables in the dataset.
701
+
702
+ .. warning:: **Newlines in string identifiers may affect performance.**
703
+
704
+ When iterating over many items by name, such as must be done when
705
+ fetching an item via pattern matching, PyTecplot will optimize the
706
+ search only if there are no newline characters in the searched
707
+ items. Iterating over strings that contain newlines will be slower
708
+ and therefore, it is best to avoid using newlines in string
709
+ identifiers or names of objects such as `Zones <data_access>` or
710
+ `Variables <Variable>`.
711
+
712
+ Example usage::
713
+
714
+ >>> print(dataset.variable_names)
715
+ ['x', 'y', 'z', 's']
716
+ """
717
+ try:
718
+ success, ptr = _tecutil.VarGetNamesByDataSetID(self.uid)
719
+ if not success:
720
+ raise TecplotSystemError()
721
+ names = ctypes.cast(ptr, StringList)
722
+ try:
723
+ return list(names)
724
+ finally:
725
+ names.dealloc()
726
+ except (AttributeError, TecplotSystemError) as err:
727
+ try:
728
+ success, ptr = _tecutil.VarGetEnabledNamesByDataSetID(self.uid)
729
+ if not success:
730
+ raise TecplotSystemError()
731
+ names = ctypes.cast(ptr, StringList)
732
+ try:
733
+ return list(names)
734
+ finally:
735
+ names.dealloc()
736
+ except:
737
+ try:
738
+ return [z.name for z in self.variables()]
739
+ except:
740
+ raise err
741
+
742
+ def variables(self, pattern=None):
743
+ """Yields all `Variables <Variable>` matching a *pattern*.
744
+
745
+ Parameters:
746
+ pattern (`str` or `re.Pattern <re.compile>`, optional):
747
+ Case-insensitive `glob-style pattern string <fnmatch.fnmatch>`
748
+ or a compiled `regex pattern instance <re.compile>` used to
749
+ match variable names.
750
+
751
+ Returns:
752
+ Generator of `Variables <Variable>`. All `Variables <Variable>` if
753
+ *pattern* is not specified.
754
+
755
+ Example using case-insensitive glob-style matching::
756
+
757
+ >>> for variable in dataset.variables('A*'):
758
+ ... array = variable.values('My Zone')
759
+
760
+ Example using (case-sensitive) regex::
761
+
762
+ >>> import re
763
+ >>> for variable in dataset.variables(re.compile(r'A.*')):
764
+ ... array = variable.values('My Zone')
765
+
766
+ .. warning:: **Zone and variable ordering may change between releases**
767
+
768
+ Due to possible changes in data loaders or data formats over time,
769
+ the ordering of zones and variables may be different between
770
+ versions of Tecplot 360. Therefore it is recommended to always
771
+ reference zones and variables **by name** instead of by index.
772
+ """
773
+ if pattern:
774
+ pattern_type = getattr(re, 'Pattern', getattr(re, '_pattern_type', None))
775
+ if not isinstance(pattern, pattern_type):
776
+ regex = re.compile(fnmatch.translate(pattern), re.IGNORECASE)
777
+ else:
778
+ regex = pattern
779
+
780
+ if __debug__:
781
+ found = False
782
+ for uid, name in zip(self._variable_uids, self.variable_names):
783
+ if regex.match(name):
784
+ if __debug__:
785
+ found = True
786
+ yield Variable(uid, self)
787
+
788
+ if __debug__:
789
+ if (
790
+ not found and
791
+ isinstance(pattern, str) and
792
+ any(c in pattern for c in '*?[]')
793
+ ):
794
+ msg = 'no variables found matching: "{}"'.format(pattern)
795
+ warning = TecplotPatternMatchWarning(pattern, msg)
796
+ warnings.warn(warning)
797
+ else:
798
+ for uid in self._variable_uids:
799
+ yield Variable(uid, self)
800
+
801
+ @property
802
+ def VariablesNamedTuple(self):
803
+ r"""A `collections.namedtuple` object using variable names.
804
+
805
+ The variable names are transformed to be unique, valid identifiers
806
+ suitable for use as the key-list for a `collections.namedtuple`.
807
+ This means that all invalid characters such as spaces and dashes
808
+ are converted to underscores, Python keywords are appended by an
809
+ underscore, leading numbers or empty names are prepended with a "v"
810
+ and duplicate variable names are indexed starting with zero, padded
811
+ left with zeros variable names duplicated more than nine times. The
812
+ following table gives some specific examples:
813
+
814
+ ==================== ===========================
815
+ Variable names Resulting namedtuple fields
816
+ ==================== ===========================
817
+ ``'x', 'y'`` ``'x', 'y'``
818
+ ``'x', 'x'`` ``'x0', 'x1'``
819
+ ``'X', 'Y=f(X)'`` ``'X', 'Y_f_X_'``
820
+ ``'x 2', '_', '_'`` ``'x_2', 'v0', 'v1'``
821
+ ``'def', 'if'`` ``'def_', 'if_'``
822
+ ``'1', '2', '3'`` ``'v1', 'v2', 'v3'``
823
+ ==================== ===========================
824
+
825
+ This example shows how one can use this n-tuple type with the
826
+ result from a call to `tecplot.data.query.probe_at_position`:
827
+
828
+ .. code-block:: python
829
+
830
+ from os import path
831
+ import tecplot as tp
832
+
833
+ examples_dir = tp.session.tecplot_examples_directory()
834
+ datafile = path.join(examples_dir,'SimpleData','DownDraft.plt')
835
+ dataset = tp.data.load_tecplot(datafile)
836
+ result = tp.data.query.probe_at_position(0,0.1,0.3)
837
+ data = dataset.VariablesNamedTuple(*result.data)
838
+
839
+ # prints: (RHO, E) = (1.17, 252930.37)
840
+ msg = '(RHO, E) = ({:.2f}, {:.2f})'
841
+ print(msg.format(data.RHO, data.E))
842
+ """
843
+ # First we clean up variable names
844
+ # and keep a count of each name used
845
+ names = []
846
+ count = {}
847
+ for vname in self.variable_names:
848
+ # sub invalid characters with underscores
849
+ name = re.sub(r'\W|^(?=\d)', r'_', vname)
850
+ # remove leading underscores
851
+ name = re.sub(r'^_+', r'', name)
852
+ # prepend leading number with 'v'
853
+ name = re.sub(r'^(\d)', r'v\1', name)
854
+ # append keywords with underscore
855
+ if iskeyword(name):
856
+ name = name + '_'
857
+ # force non-empty variable names using 'v'
858
+ if name == '':
859
+ name = 'v'
860
+ # add name to list
861
+ names.append(name)
862
+ # keep track of names to identify duplicates
863
+ if name not in count:
864
+ count[name] = 0
865
+ count[name] += 1
866
+
867
+ # append index, with padded zeros
868
+ numbered = count.copy()
869
+ for i,name in enumerate(names):
870
+ if count[name] > 1:
871
+ names[i] = '{0}{1:0{2}d}'.format(name,
872
+ count[name] - numbered[name], len(str(count[name] - 1)))
873
+ numbered[name] -= 1
874
+
875
+ return collections.namedtuple('DatasetVariables', names)
876
+
877
+ @lock()
878
+ def copy_zones(self, *zones, **kwargs):
879
+ """Copies `Zones <data_access>` within this `Dataset <tecplot.data.Dataset>`.
880
+
881
+ Parameters:
882
+ *zones (`Zone <data_access>`, optional): Specific `Zones
883
+ <data_access>` to copy. All zones will be copied if none are
884
+ supplied.
885
+ shared_variables (`bool` or `list` of `Variables <Variable>`):
886
+ Share all variables between the original and generated zones if
887
+ `True` or the list of Variables to be shared. Variable sharing
888
+ allows you to lower the use of physical memory (RAM). When
889
+ sharing a variable the memory used by the source zone is shared
890
+ with the copied zone. Alterations to the variable in one zone
891
+ will affect the other. See also `Dataset.branch_variables()`.
892
+ Default: `False`.
893
+ i_range (`tuple` of `integers <int>`, optional):
894
+ Range (min, max, step) along the ``i`` dimension for ordered
895
+ data. Min and max are zero-based indicies where max is
896
+ inclusive. If step causes max to be skipped, max will be
897
+ included. If `None` (default), the entire range will be copied.
898
+ j_range (`tuple` of `integers <int>`, optional):
899
+ Range (min, max, step) along the ``j`` dimension for ordered
900
+ data. Min and max are zero-based indicies where max is
901
+ inclusive. If step causes max to be skipped, max will be
902
+ included. If `None` (default), the entire range will be copied.
903
+ k_range (`tuple` of `integers <int>`, optional):
904
+ Range (min, max, step) along the ``k`` dimension for ordered
905
+ data. Min and max are zero-based indicies where max is
906
+ inclusive. If step causes max to be skipped, max will be
907
+ included. If `None` (default), the entire range will be copied.
908
+
909
+ Returns:
910
+ `list` of the newly created `Zones <data_access>`.
911
+
912
+ .. note:: **Performance considerations for data manipulations.**
913
+
914
+ When performing many data-manipulation operations including adding
915
+ zones, adding variables, modifying field data or connectivity, and
916
+ especially in connected mode, it is recommended to do this all with
917
+ the `tecplot.session.suspend()`. This will prevent the Tecplot
918
+ engine from trying to "keep up" with the changes. Tecplot will be
919
+ notified of all changes made upon exit of this context. This may
920
+ result in significant performance gains for long operations.
921
+
922
+ .. code-block:: python
923
+
924
+ import tecplot as tp
925
+
926
+ ds = tp.active_page().add_frame().create_dataset('D', ['x','y','z'])
927
+ z = ds.add_ordered_zone('Z1', (3,3,3))
928
+ ds.copy_zones(z, i_range=(1, -1, 2), share_variables=True)
929
+ """
930
+ share_variables = kwargs.pop('share_variables', False)
931
+ i_range = kwargs.pop('i_range', None)
932
+ j_range = kwargs.pop('j_range', None)
933
+ k_range = kwargs.pop('k_range', None)
934
+
935
+ if not share_variables:
936
+ branch_variables = list(self.variables())
937
+ elif isinstance(share_variables, Iterable):
938
+ for idx, var in enumerate(share_variables):
939
+ if not isinstance(var, Variable):
940
+ share_variables[idx]= self.variable(var)
941
+
942
+ branch_variables = [v for v in self.variables()
943
+ if v not in share_variables]
944
+ else:
945
+ branch_variables = []
946
+
947
+ num_zones = self.num_zones
948
+
949
+ with tecutil.ArgList() as arglist:
950
+ if not zones:
951
+ zones = self.zones()
952
+ with IndexSet(*zones) as zoneset:
953
+ arglist[sv.SOURCEZONES] = zoneset
954
+
955
+ for dim, rng in zip(['I', 'J', 'K'], [i_range, j_range, k_range]):
956
+ if rng is not None:
957
+ rng = tecutil.IndexRange(*rng)
958
+ if rng.min is not None:
959
+ arglist[dim + 'MIN'] = tecutil.Index(rng.min)
960
+ if rng.max is not None:
961
+ arglist[dim + 'MAX'] = tecutil.Index(rng.max)
962
+ if rng.step is not None:
963
+ step = int(rng.step)
964
+ if step > 0:
965
+ arglist[dim + 'SKIP'] = step
966
+ elif step < 0:
967
+ msg = 'Negative step not supported.'
968
+ raise TecplotLogicError(msg)
969
+ _tecutil.ZoneCopyX(arglist)
970
+
971
+ new_zones = [self.zone(i) for i in range(num_zones, self.num_zones)]
972
+
973
+ for zone in new_zones:
974
+ for var in branch_variables:
975
+ self.branch_variables(zone, var, True)
976
+
977
+ return new_zones
978
+
979
+ def mirror_zones(self, mirror_variables, *zones):
980
+ """Mirrors `Zones <data_access>` within this `Dataset <tecplot.data.Dataset>`.
981
+
982
+ Each mirror zone has a name of the form "Mirror of zone *sourcezone*",
983
+ where *sourcezone* is the number of the zone from which the mirrored
984
+ zone was created. The variables in the newly created zones are shared
985
+ with their corresponding source zones, except for variables to be
986
+ mirrored as specified.
987
+
988
+ Parameters:
989
+ mirror_variables (`Variable` or `list` of `Variables <Variable>`):
990
+ Variables in the new zone to be multiplied by :math:`-1` after
991
+ the zone is copied. the variables may be `Variable` objects,
992
+ `str` names or `int` indices.
993
+ *zones (`Zones <data_access>`, optional): Specific `Zones
994
+ <data_access>` to mirror. All zones will be mirrored if none
995
+ are supplied. The values may also be `str` names or `int`
996
+ indices.
997
+
998
+ Returns:
999
+ Generator of mirrored zones.
1000
+
1001
+ This example show how to mirror all zones across the xy-plane in 3D::
1002
+
1003
+ >>> mirrored_zones = dataset.mirror_zones('Z')
1004
+ """
1005
+ if (
1006
+ isinstance(mirror_variables, str) or
1007
+ not isinstance(mirror_variables, Iterable)
1008
+ ):
1009
+ mirror_variables = [mirror_variables]
1010
+
1011
+ mvars = (v if isinstance(v, Variable) else self.variable(v)
1012
+ for v in mirror_variables)
1013
+ mvars = ','.join([str(v.index + 1) for v in mvars])
1014
+
1015
+ if not zones:
1016
+ mzones = '1-{}'.format(self.num_zones)
1017
+ else:
1018
+ mzones = (z if isinstance(z, Zone) else self.zone(z)
1019
+ for z in tecutil.flatten_args(*zones))
1020
+ mzones = ','.join([str(z.index + 1) for z in mzones])
1021
+
1022
+ num_zones = self.num_zones
1023
+ cmd = '$!CreateMirrorZones SourceZones = [{0}] MirrorVars = [{1}]'
1024
+ macro.execute_command(cmd.format(mzones, mvars))
1025
+ return (self.zone(i) for i in range(num_zones, self.num_zones))
1026
+
1027
+ @lock()
1028
+ def add_variable(self, name, dtypes=None, locations=None):
1029
+ """Add a single `Variable` to the active `Dataset <tecplot.data.Dataset>`.
1030
+
1031
+ Parameters:
1032
+ name (`str`): The name of the new `Variable`. This does not
1033
+ have to be unique.
1034
+ dtypes (`FieldDataType` or `list` of `FieldDataType`, optional):
1035
+ Data types of this `Variable` for each `Zone <data_access>` in
1036
+ the currently active `Dataset <tecplot.data.Dataset>`. Options are:
1037
+ `FieldDataType.Float`, `Double <FieldDataType.Double>`,
1038
+ `Int32`, `Int16`, `Byte` and `Bit`. If a single value, this
1039
+ will be duplicated for all `Zones <data_access>`. (default:
1040
+ `None`)
1041
+ locations (`ValueLocation` or `list` of `ValueLocation`, optional):
1042
+ Point locations of this `Variable` for each
1043
+ `Zone <data_access>` in the currently active `Dataset <tecplot.data.Dataset>`. Options
1044
+ are: `Nodal` and `CellCentered`. If a single value, this will
1045
+ be duplicated for all `Zones <data_access>`. (default: `None`)
1046
+
1047
+ Returns:
1048
+ `Variable`
1049
+
1050
+ .. note:: **Performance considerations for data manipulations.**
1051
+
1052
+ When performing many data-manipulation operations including adding
1053
+ zones, adding variables, modifying field data or connectivity, and
1054
+ especially in connected mode, it is recommended to do this all with
1055
+ the `tecplot.session.suspend()`. This will prevent the Tecplot
1056
+ engine from trying to "keep up" with the changes. Tecplot will be
1057
+ notified of all changes made upon exit of this context. This may
1058
+ result in significant performance gains for long operations.
1059
+
1060
+ The added `Variable` will be available for use in each `Zone
1061
+ <data_access>` of the dataset. This method should be used in
1062
+ conjunction with other data creation methods such as
1063
+ `Dataset.add_zone`:
1064
+
1065
+ .. code-block:: python
1066
+ :emphasize-lines: 7-8
1067
+
1068
+ import math
1069
+ import tecplot as tp
1070
+ from tecplot.constant import PlotType
1071
+
1072
+ # Setup Tecplot dataset
1073
+ dataset = tp.active_frame().create_dataset('Data')
1074
+ dataset.add_variable('x')
1075
+ dataset.add_variable('s')
1076
+ zone = dataset.add_ordered_zone('Zone', 100)
1077
+
1078
+ # Fill the dataset
1079
+ x = [0.1 * i for i in range(100)]
1080
+ zone.values('x')[:] = x
1081
+ zone.values('s')[:] = [math.sin(i) for i in x]
1082
+
1083
+ # Set plot type to XYLine
1084
+ tp.active_frame().plot(PlotType.XYLine).activate()
1085
+
1086
+ tp.export.save_png('add_variables.png', 600, supersample=3)
1087
+
1088
+ .. figure:: /_static/images/add_variables.png
1089
+ :width: 300px
1090
+ :figwidth: 300px
1091
+ """
1092
+ assert len(name) <= 128, 'Variable names are limited to 128 characters'
1093
+ with self.frame.activated():
1094
+ dataset = self.frame.dataset
1095
+ new_variable_index = dataset.num_variables
1096
+ with tecutil.ArgList(NAME=name) as arglist:
1097
+ if dtypes is not None:
1098
+ if not hasattr(dtypes, '__iter__'):
1099
+ dtypes = [dtypes] * dataset.num_zones
1100
+ arglist['VARDATATYPE'] = dtypes
1101
+ if locations is not None:
1102
+ if not hasattr(locations, '__iter__'):
1103
+ locations = [locations] * dataset.num_zones
1104
+ arglist['VALUELOCATION'] = locations
1105
+
1106
+ if __debug__:
1107
+ msg = 'new variable: ' + name
1108
+ for k, v in arglist.items():
1109
+ msg += '\n {} = {}'.format(k, v)
1110
+ log.debug(msg)
1111
+ if not _tecutil.DataSetAddVarX(arglist):
1112
+ raise TecplotSystemError()
1113
+ return dataset.variable(new_variable_index)
1114
+
1115
+ @lock()
1116
+ def add_zone(self, zone_type, name, shape, dtypes=None, locations=None,
1117
+ face_neighbor_mode=None, parent_zone=None, solution_time=None,
1118
+ strand_id=None, index=None):
1119
+ """Add a single `Zone <data_access>` to this `Dataset <tecplot.data.Dataset>`.
1120
+
1121
+ Parameters:
1122
+ zone_type (`ZoneType`): The type of `Zone <data_access>` to be
1123
+ created. Possible values are: `Ordered`, `FETriangle`,
1124
+ `FEQuad`, `FETetra`, `FEBrick`, `FELineSeg`, `FEPolyhedron` and
1125
+ `FEPolygon`.
1126
+ name (`str`): Name of the new `Zone <data_access>`. This
1127
+ does not have to be unique.
1128
+ shape (`int` or `list` of `integers <int>`): Specifies
1129
+ the length and dimension (up to three) of the new `Zone
1130
+ <data_access>`. A 1D `Zone <data_access>` is assumed if a
1131
+ single `int` is given. This is **(i, j, k)** for ordered `Zones
1132
+ <data_access>`, **(num_points, num_elements)** for
1133
+ finite-element `Zones <data_access>` and **(num_points,
1134
+ num_elements, num_faces)** for polytope `Zones <data_access>`
1135
+ where the number of faces is known.
1136
+ dtypes (`FieldDataType`, `list` of `FieldDataType`, optional): Data
1137
+ types of this `Zone <data_access>` for each `Variable` in the
1138
+ currently active `Dataset <tecplot.data.Dataset>`. Options are: `Float
1139
+ <FieldDataType.Float>`, `Double <FieldDataType.Double>`,
1140
+ `Int32`, `Int16`, `Byte` and `Bit`. If a single value, this
1141
+ will be duplicated for all `Variables <Variable>`. If `None`
1142
+ then the type of the first `Variable`, defaulting to
1143
+ `FieldDataType.Float`, is used for all. (default: `None`)
1144
+ locations (`ValueLocation`, `list` of `ValueLocation`, optional):
1145
+ Point locations of this `Zone <data_access>` for each
1146
+ `Variable` in the currently active `Dataset <tecplot.data.Dataset>`. Options are:
1147
+ `Nodal` and `CellCentered`. If a single value, this will be
1148
+ duplicated for all `Variables <Variable>`. If `None` then the
1149
+ type of the first `Variable`, defaulting to `Nodal`, is used
1150
+ for all. (default: `None`)
1151
+ face_neighbor_mode (`FaceNeighborMode`, optional): Specifies the
1152
+ face-neighbor mode for this zone. Options are:
1153
+ `FaceNeighborMode.LocalOneToOne` (default),
1154
+ `FaceNeighborMode.LocalOneToMany`,
1155
+ `FaceNeighborMode.GlobalOneToOne` or
1156
+ `FaceNeighborMode.GlobalOneToMany`.
1157
+ parent_zone (`Zone <data_access>`, optional): A parent `Zone
1158
+ <data_access>` to be used when generating surface-restricted
1159
+ streamtraces.
1160
+ solution_time (`float`, optional): Solution time for this zone.
1161
+ (default: 0)
1162
+ strand_id (`int`, optional): Associate this new `Zone
1163
+ <data_access>` with a particular strand.
1164
+ index (`int`, optional): Number of the zone to add or
1165
+ replace. If omitted or set to `None`, the new zone will be
1166
+ appended to the dataset. This value can be set to the number of
1167
+ a zone that already exists thereby replacing the existing zone.
1168
+ (default: `None`)
1169
+
1170
+ Returns:
1171
+ `Zone <data_access>`
1172
+
1173
+ .. warning:: **Setting connectivity in connected mode.**
1174
+
1175
+ When connected to a running instance of Tecplot 360 using the
1176
+ TecUtil Server, care must be taken to ensure that the GUI does not
1177
+ try to render the data between the creation of the zone and the
1178
+ setting of the connectivity, through the `Facemap` or `Nodemap`
1179
+ objects. This can be achieved by setting the plot type of the
1180
+ frame(s) holding on to the dataset to `PlotType.Sketch` before
1181
+ creating the zone and only going to `PlotType.Cartesian3D` after
1182
+ the connectivity is set. Tecplot 360 may get into a bad state,
1183
+ corrupting loaded data, if it attempts to render (especially
1184
+ polytope) data without connectivity.
1185
+
1186
+ .. note:: **Performance considerations for data manipulations.**
1187
+
1188
+ When performing many data-manipulation operations including adding
1189
+ zones, adding variables, modifying field data or connectivity, and
1190
+ especially in connected mode, it is recommended to do this all with
1191
+ the `tecplot.session.suspend()`. This will prevent the Tecplot
1192
+ engine from trying to "keep up" with the changes. Tecplot will be
1193
+ notified of all changes made upon exit of this context. This may
1194
+ result in significant performance gains for long operations.
1195
+
1196
+ The added `Zone <data_access>` will be able to use all `Variables
1197
+ <Variable>` defined in the dataset. This method should be used in
1198
+ conjunction with other data creation methods such as
1199
+ `Frame.create_dataset`. Example usage::
1200
+
1201
+ >>> from tecplot.constant import ZoneType
1202
+ >>> zone = dataset.add_zone(ZoneType.Ordered, 'Zone', (10, 10, 10))
1203
+
1204
+ .. note::
1205
+
1206
+ The relationship and meaning of this method's parameters change
1207
+ depending on the type of zone being created. Therefore, it is
1208
+ recommended to use the more specific zone creation methods:
1209
+
1210
+ * `Dataset.add_ordered_zone`
1211
+ * `Dataset.add_fe_zone`
1212
+ * `Dataset.add_poly_zone`
1213
+ """
1214
+ if __debug__:
1215
+ if self.num_variables == 0:
1216
+ errmsg = dedent('''\
1217
+ Can not create a zone on a dataset with no variables.
1218
+ Add at least one variable to this dataset before
1219
+ creating any zones.''')
1220
+ raise TecplotLogicError(errmsg)
1221
+
1222
+ with self.frame.activated():
1223
+ dataset = self.frame.dataset
1224
+ new_zone_index = dataset.num_zones if index is None else index
1225
+ with tecutil.ArgList(ZONETYPE=zone_type, NAME=name) as arglist:
1226
+ # convert shape to (imax, jmax, kmax)
1227
+ if not hasattr(shape, '__iter__'):
1228
+ shape = [shape]
1229
+ for k, v in zip([sv.IMAX, sv.JMAX, sv.KMAX], shape):
1230
+ arglist[k] = v
1231
+
1232
+ # expand data types and locations to length of num_variables
1233
+ for key, val in zip([sv.VARDATATYPE, sv.VALUELOCATION],
1234
+ [dtypes, locations]):
1235
+ if val is not None:
1236
+ if not hasattr(val, '__iter__'):
1237
+ val = [val] * dataset.num_variables
1238
+ arglist[key] = val
1239
+
1240
+ if solution_time is not None:
1241
+ arglist[sv.SOLUTIONTIME] = float(solution_time)
1242
+
1243
+ if strand_id is not None:
1244
+ arglist[sv.STRANDID] = int(strand_id)
1245
+
1246
+ if parent_zone is not None:
1247
+ arglist[sv.PARENTZONE] = parent_zone.index + 1
1248
+
1249
+ if face_neighbor_mode is not None:
1250
+ arglist[sv.FACENEIGHBORMODE] = FaceNeighborMode(
1251
+ face_neighbor_mode)
1252
+
1253
+ if index is not None:
1254
+ arglist[sv.ZONE] = index + 1
1255
+
1256
+ if __debug__:
1257
+ shp = '({})'.format(','.join(str(s) for s in shape))
1258
+ msg = 'new dataset shape: ' + shp
1259
+ for k, v in arglist.items():
1260
+ msg += '\n {} = {}'.format(k, v)
1261
+ log.debug(msg)
1262
+
1263
+ if not _tecutil.DataSetAddZoneX(arglist):
1264
+ raise TecplotSystemError()
1265
+
1266
+ new_zone = dataset.zone(new_zone_index)
1267
+ session.zone_added(new_zone)
1268
+ return new_zone
1269
+
1270
+ def add_ordered_zone(self, name, shape, **kwargs):
1271
+ """Add a single ordered `Zone <data_access>` to this `Dataset <tecplot.data.Dataset>`.
1272
+
1273
+ Parameters:
1274
+ name (`str`): Name of the new `Zone <data_access>`. This
1275
+ does not have to be unique.
1276
+ shape (`int` or `list` of `integers <int>`): Specifies
1277
+ the length and dimension **(i, j, k)** of the new
1278
+ `Zone <data_access>`. A 1D `Zone <data_access>` is assumed if a
1279
+ single `int` is given.
1280
+ **kwargs: These arguments are passed to `Dataset.add_zone`.
1281
+
1282
+ .. seealso:: `Dataset.add_zone`
1283
+
1284
+ Keyword arguments are passed to the parent zone creation method
1285
+ `Dataset.add_zone`.
1286
+
1287
+ .. note:: **Performance considerations for data manipulations.**
1288
+
1289
+ When performing many data-manipulation operations including adding
1290
+ zones, adding variables, modifying field data or connectivity, and
1291
+ especially in connected mode, it is recommended to do this all with
1292
+ the `tecplot.session.suspend()`. This will prevent the Tecplot
1293
+ engine from trying to "keep up" with the changes. Tecplot will be
1294
+ notified of all changes made upon exit of this context. This may
1295
+ result in significant performance gains for long operations.
1296
+
1297
+ This example creates a 10x10x10 ordered zone of double-precision
1298
+ floating-point numbers::
1299
+
1300
+ >>> from tecplot.constant import FieldDataType
1301
+ >>> my_zone = dataset.add_zone('My Zone', (10, 10, 10),
1302
+ ... dtype=FieldDataType.Double)
1303
+
1304
+ Here is a full example:
1305
+
1306
+ .. code-block:: python
1307
+ :emphasize-lines: 12,17,22
1308
+
1309
+ import numpy as np
1310
+ import tecplot as tp
1311
+ from tecplot.constant import PlotType, Color
1312
+
1313
+ # Generate data
1314
+ x = np.linspace(-4, 4, 100)
1315
+
1316
+ # Setup Tecplot dataset
1317
+ dataset = tp.active_frame().create_dataset('Data', ['x', 'y'])
1318
+
1319
+ # Create a zone
1320
+ zone = dataset.add_ordered_zone('sin(x)', len(x))
1321
+ zone.values('x')[:] = x
1322
+ zone.values('y')[:] = np.sin(x)
1323
+
1324
+ # Create another zone
1325
+ zone = dataset.add_ordered_zone('cos(x)', len(x))
1326
+ zone.values('x')[:] = x
1327
+ zone.values('y')[:] = np.cos(x)
1328
+
1329
+ # And one more zone
1330
+ zone = dataset.add_ordered_zone('tan(x)', len(x))
1331
+ zone.values('x')[:] = x
1332
+ zone.values('y')[:] = np.tan(x)
1333
+
1334
+ # Set plot type to XYLine
1335
+ plot = tp.active_frame().plot(PlotType.XYLine)
1336
+ plot.activate()
1337
+
1338
+ # Show all linemaps and make the lines a bit thicker
1339
+ for lmap in plot.linemaps():
1340
+ lmap.show = True
1341
+ lmap.line.line_thickness = 0.6
1342
+
1343
+ plot.legend.show = True
1344
+
1345
+ tp.export.save_png('add_ordered_zones.png', 600, supersample=3)
1346
+
1347
+ .. figure:: /_static/images/add_ordered_zones.png
1348
+ :width: 300px
1349
+ :figwidth: 300px
1350
+ """
1351
+ return self.add_zone(ZoneType.Ordered, name, shape, **kwargs)
1352
+
1353
+ def add_fe_zone(self, zone_type, name, num_points, num_elements, **kwargs):
1354
+ r"""Add a single finite-element `Zone <data_access>` to this `Dataset <tecplot.data.Dataset>`.
1355
+
1356
+ Parameters:
1357
+ zone_type (`ZoneType`): The type of `Zone <data_access>` to be
1358
+ created. Possible values are: `FETriangle`, `FEQuad`,
1359
+ `FETetra`, `FEBrick` and `FELineSeg`.
1360
+ name (`str`): Name of the new `Zone <data_access>`. This
1361
+ does not have to be unique.
1362
+ num_points (`int`): Number of points (nodes) in this
1363
+ zone.
1364
+ num_elements (`int`): Number of elements in this zone.
1365
+ The nodemap will have the shape (num_points, num_elements).
1366
+ **kwargs: These arguments are passed to `Dataset.add_zone`.
1367
+
1368
+ .. seealso:: `Dataset.add_zone`
1369
+
1370
+ Keyword arguments are passed to the parent zone creation method
1371
+ `Dataset.add_zone`.
1372
+
1373
+ .. warning:: **Setting connectivity in connected mode.**
1374
+
1375
+ When connected to a running instance of Tecplot 360 using the
1376
+ TecUtil Server, care must be taken to ensure that the GUI does not
1377
+ try to render the data between the creation of the zone and the
1378
+ setting of the connectivity, through the `Facemap` or `Nodemap`
1379
+ objects. This can be achieved by setting the plot type of the
1380
+ frame(s) holding on to the dataset to `PlotType.Sketch` before
1381
+ creating the zone and only going to `PlotType.Cartesian3D` after
1382
+ the connectivity is set. Tecplot 360 may get into a bad state,
1383
+ corrupting loaded data, if it attempts to render (especially
1384
+ polytope) data without connectivity.
1385
+
1386
+ .. note:: **Performance considerations for data manipulations.**
1387
+
1388
+ When performing many data-manipulation operations including adding
1389
+ zones, adding variables, modifying field data or connectivity, and
1390
+ especially in connected mode, it is recommended to do this all with
1391
+ the `tecplot.session.suspend()`. This will prevent the Tecplot
1392
+ engine from trying to "keep up" with the changes. Tecplot will be
1393
+ notified of all changes made upon exit of this context. This may
1394
+ result in significant performance gains for long operations.
1395
+
1396
+ The number of points (also known as nodes) per finite-element is
1397
+ determined from the ``zone_type`` parameter. The follow table shows the
1398
+ number of points per element for the available zone types along with
1399
+ the resulting shape of the nodemap based on the number of points
1400
+ specified (:math:`N`):
1401
+
1402
+ ============== ============== ========================
1403
+ Zone Type Points/Element Nodemap Shape
1404
+ ============== ============== ========================
1405
+ ``FELineSeg`` 2 (:math:`N`, :math:`2 N`)
1406
+ ``FETriangle`` 3 (:math:`N`, :math:`3 N`)
1407
+ ``FEQuad`` 4 (:math:`N`, :math:`4 N`)
1408
+ ``FETetra`` 4 (:math:`N`, :math:`4 N`)
1409
+ ``FEBrick`` 8 (:math:`N`, :math:`8 N`)
1410
+ ============== ============== ========================
1411
+
1412
+ For more details, see the "working with datasets" examples shipped with
1413
+ PyTecplot in the Tecplot 360 distribution.
1414
+ """
1415
+ assert zone_type in [ZoneType.FETriangle, ZoneType.FEQuad,
1416
+ ZoneType.FETetra, ZoneType.FEBrick,
1417
+ ZoneType.FELineSeg]
1418
+ return self.add_zone(zone_type, name, (num_points, num_elements),
1419
+ **kwargs)
1420
+
1421
+ def add_poly_zone(self, zone_type, name, num_points, num_elements,
1422
+ num_faces, **kwargs):
1423
+ """Add a single polygonal `Zone <data_access>` to this `Dataset <tecplot.data.Dataset>`.
1424
+
1425
+ Parameters:
1426
+ zone_type (`ZoneType`): The type of `Zone <data_access>` to be
1427
+ created. Possible values are: `FEPolyhedron` and `FEPolygon`.
1428
+ name (`str`): Name of the new `Zone <data_access>`. This
1429
+ does not have to be unique.
1430
+ num_points (`int`): Number of points in this zone.
1431
+ num_elements (`int`): Number of elements in this zone.
1432
+ num_faces (`int`): Number of faces in this zone.
1433
+ **kwargs: These arguments are passed to `Dataset.add_zone`.
1434
+
1435
+ .. seealso:: `Dataset.add_zone`
1436
+
1437
+ Keyword arguments are passed to the parent zone creation method
1438
+ `Dataset.add_zone`.
1439
+
1440
+ .. warning:: **Setting connectivity in connected mode.**
1441
+
1442
+ When connected to a running instance of Tecplot 360 using the
1443
+ TecUtil Server, care must be taken to ensure that the GUI does not
1444
+ try to render the data between the creation of the zone and the
1445
+ setting of the connectivity, through the `Facemap` or `Nodemap`
1446
+ objects. This can be achieved by setting the plot type of the
1447
+ frame(s) holding on to the dataset to `PlotType.Sketch` before
1448
+ creating the zone and only going to `PlotType.Cartesian3D` after
1449
+ the connectivity is set. Tecplot 360 may get into a bad state,
1450
+ corrupting loaded data, if it attempts to render (especially
1451
+ polytope) data without connectivity.
1452
+
1453
+ .. note:: **Performance considerations for data manipulations.**
1454
+
1455
+ When performing many data-manipulation operations including adding
1456
+ zones, adding variables, modifying field data or connectivity, and
1457
+ especially in connected mode, it is recommended to do this all with
1458
+ the `tecplot.session.suspend()`. This will prevent the Tecplot
1459
+ engine from trying to "keep up" with the changes. Tecplot will be
1460
+ notified of all changes made upon exit of this context. This may
1461
+ result in significant performance gains for long operations.
1462
+
1463
+ .. note:: The **num_faces** is the number of *unique faces*.
1464
+
1465
+ The number of unique faces, given an element map can be obtained
1466
+ using the following function for polygon data::
1467
+
1468
+ def num_unique_faces(elementmap):
1469
+ return len(set( tuple(sorted([e[i], e[(i+1)%len(e)]]))
1470
+ for e in elementmap for i in range(len(e)) ))
1471
+
1472
+ This function creates a unique set of node pairs (edges around
1473
+ the polygons) and counts them. For polyhedron data, the following
1474
+ can be used::
1475
+
1476
+ def num_unique_faces(elementmap):
1477
+ return len(set( tuple(sorted(f)) for e in elementmap
1478
+ for f in e ))
1479
+
1480
+ which merely counts the number of unique faces defined in the
1481
+ element map.
1482
+
1483
+ For more details, see the "working with datasets" examples shipped with
1484
+ PyTecplot in the Tecplot 360 distribution.
1485
+ """
1486
+ assert zone_type in [ZoneType.FEPolyhedron, ZoneType.FEPolygon]
1487
+ return self.add_zone(zone_type, name, (num_points, num_elements,
1488
+ num_faces), **kwargs)
1489
+
1490
+ @lock()
1491
+ def delete_variables(self, *variables):
1492
+ """Remove `Variables <Variable>` from this `Dataset <tecplot.data.Dataset>`.
1493
+
1494
+ Parameters:
1495
+ *variables (`Variable` or index `int`): Variables to
1496
+ remove from this dataset.
1497
+
1498
+ .. code-block:: python
1499
+ :emphasize-lines: 3
1500
+
1501
+ >>> print(dataset.variable_names)
1502
+ ['X','Y','Z']
1503
+ >>> dataset.delete_variables(dataset.variable('Z'))
1504
+ >>> print(dataset.variable_names)
1505
+ ['X','Y']
1506
+
1507
+ .. Warning::
1508
+ Deleting `Variables <Variable>` invalidates iterators referencing
1509
+ them in the containing `Dataset <tecplot.data.Dataset>` such as those obtained from
1510
+ `Dataset.variables()`. It is recommended to create a list of the
1511
+ `Variables <Variable>` you want to delete and to pass that into a
1512
+ single call to `Dataset.delete_variables()`
1513
+
1514
+ Notes:
1515
+ Multiple `Variables <Variable>` can be deleted at once, though the
1516
+ last `Variable` can not be deleted. The following example deletes
1517
+ all but the first `Variable` in the `Dataset <tecplot.data.Dataset>` (usually ``X``)::
1518
+
1519
+ >>> # Try to delete all variables:
1520
+ >>> dataset.delete_variables(dataset.variables())
1521
+ >>> # Dataset requires at least one variable to
1522
+ >>> # exist, so it leaves the first one:
1523
+ >>> print(dataset.variable_names)
1524
+ ['X']
1525
+ """
1526
+ variables = flatten_args(*variables)
1527
+ with self.frame.activated():
1528
+ with IndexSet(*variables) as vlist:
1529
+ _tecutil.DataSetDeleteVar(vlist)
1530
+
1531
+ @lock()
1532
+ def delete_zones(self, *zones):
1533
+ """Remove `Zones <data_access>` from this `Dataset <tecplot.data.Dataset>`.
1534
+
1535
+ Parameters:
1536
+ *zones (`Zones <data_access>` or index `integers <int>`): Zones to
1537
+ remove from this dataset.
1538
+
1539
+ .. code-block:: python
1540
+ :emphasize-lines: 3
1541
+
1542
+ >>> print(dataset.zone_names)
1543
+ ['Zone 1', 'Zone 2']
1544
+ >>> dataset.delete_zones(dataset.zone('Zone 2'))
1545
+ >>> print(dataset.zone_names)
1546
+ ['Zone 1']
1547
+
1548
+ .. Warning::
1549
+ Deleting `Zones <data_access>` invalidates iterators referencing
1550
+ them in the containing `Dataset <tecplot.data.Dataset>` such as those obtained from
1551
+ `Dataset.zones()`. It is recommended to create a list of the `Zones
1552
+ <data_access>` you want to delete and to pass that into a single
1553
+ call to `Dataset.delete_zones()`
1554
+
1555
+ Notes:
1556
+ Multiple `Zones <data_access>` can be deleted at once, though the
1557
+ last `Zone <data_access>` can not be deleted. The following example
1558
+ deletes all but the first `Zone <data_access>` in the `Dataset <tecplot.data.Dataset>`::
1559
+
1560
+ >>> dataset.delete_zones(dataset.zones())
1561
+ """
1562
+ zones = flatten_args(*zones)
1563
+ with self.frame.activated():
1564
+ with IndexSet(*zones) as zlist:
1565
+ _tecutil.DataSetDeleteZone(zlist)
1566
+
1567
+ @property
1568
+ def num_solution_times(self):
1569
+ """`int` (read-only): Number of solution times in the dataset.
1570
+
1571
+ This property is read-only. Solution times are grouped according to the
1572
+ current solution time clustering settings (see
1573
+ `Dataset.solution_time_clustering`). Example usage::
1574
+
1575
+ >>> print(dataset.num_solution_times)
1576
+ 10
1577
+
1578
+ .. versionadded:: 2017.3
1579
+ Solution time manipulation requires Tecplot 360 2017 R3 or later.
1580
+ """
1581
+ res = _tecutil.SolutionTimeGetNumTimeStepsByDataSetID(self.uid)
1582
+ success, value = res
1583
+ if not success:
1584
+ raise TecplotSystemError()
1585
+ return value
1586
+
1587
+ @property
1588
+ def solution_times(self):
1589
+ """`list` of `floats <float>`: Solution times in the dataset.
1590
+
1591
+ This property is read-only. Solution times are grouped according to the
1592
+ current solution time clustering settings (see
1593
+ `Dataset.solution_time_clustering`). Example usage::
1594
+
1595
+ >>> print(dataset.solution_times)
1596
+ [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
1597
+
1598
+ .. versionadded:: 2017.3
1599
+ Solution time manipulation requires Tecplot 360 2017 R3 or later.
1600
+ """
1601
+ res = _tecutil.SolutionTimeGetSolutionTimesByDataSetID(self.uid)
1602
+ success, ntimes, times = res
1603
+ if not success:
1604
+ raise TecplotSystemError()
1605
+ ret = list(times[:ntimes])
1606
+ if not _tecutil_connector.connected:
1607
+ _tecutil.ArrayDealloc(times)
1608
+ return ret
1609
+
1610
+ @lock()
1611
+ def branch_variables(self, zones, variables, copy_data=True):
1612
+ """Breaks data sharing between zones.
1613
+
1614
+ Parameters:
1615
+ zones (`list` of `Zones <data_access>`): Zones to be branched.
1616
+ variables (`list` of `Variables <Variable>`): Variables to be
1617
+ branched.
1618
+ copy_data (`bool`, optional): Allocate space for the branched
1619
+ values and copy the data. If `False`, the new variables will
1620
+ be passive. (default: `True`)
1621
+
1622
+ .. seealso:: `Dataset.share_variables()`
1623
+
1624
+ Variable sharing allows you to lower the use of physical memory (RAM).
1625
+ When sharing a variable the memory used by the source zone is shared
1626
+ with the copied zone. Alterations to the variable in one zone will
1627
+ affect the other. Example usage::
1628
+
1629
+ >>> z = dataset.zone('My Zone')
1630
+ >>> zcopy = z.copy(share_variables=True)
1631
+ >>> print([zn.index for zn in z.values(0).shared_zones])
1632
+ [0,1]
1633
+ >>> dataset.branch_variables(zcopy,dataset.variable(0))
1634
+ >>> print([zn.index for zn in z.values(0).shared_zones])
1635
+ []
1636
+ >>> print([zn.index for zn in z.values(1).shared_zones])
1637
+ [0,1]
1638
+ """
1639
+ if not isinstance(zones, Iterable):
1640
+ zones = [zones]
1641
+ if not isinstance(variables, Iterable):
1642
+ variables = [variables]
1643
+
1644
+ for var in variables:
1645
+ varidx = getattr(var, 'index', self.variable(var).index)
1646
+ for zone in zones:
1647
+ zoneidx = getattr(zone, 'index', self.zone(zone).index)
1648
+ if not _tecutil.DataValueBranchShared(zoneidx + 1, varidx + 1,
1649
+ copy_data):
1650
+ raise TecplotSystemError()
1651
+
1652
+ @lock()
1653
+ def share_variables(self, source_zone, destination_zones, variables):
1654
+ """Share field data between zones.
1655
+
1656
+ This method links the underlying data `arrays <Array>` of the
1657
+ destination `zones <data_access>` to the data of the source `zone
1658
+ <data_access>`. Modifying the array data of one zone will affect all
1659
+ others in this group.
1660
+
1661
+ Parameters:
1662
+ source_zone (`Zone <data_access>`): Zone which provides data to be
1663
+ shared.
1664
+ destination_zones (`list` of `Zones <data_access>`): Zones
1665
+ where data will be overwritten.
1666
+ variables (`list` of `Variables <Variable>`): Variables to be
1667
+ shared.
1668
+
1669
+ .. seealso:: `Dataset.branch_variables()`
1670
+
1671
+ Example usage::
1672
+
1673
+ >>> z = dataset.zone('My Zone')
1674
+ >>> zcopy = z.copy(share_variables=False)
1675
+ >>> print([zn.index for zn in z.values(0).shared_zones])
1676
+ []
1677
+ >>> dataset.share_variables(zcopy,[z],[dataset.variable(0)])
1678
+ >>> print([zn.index for zn in z.values(0).shared_zones])
1679
+ [0,1]
1680
+ >>> print([zn.index for zn in z.values(1).shared_zones])
1681
+ []
1682
+ """
1683
+ if not isinstance(destination_zones, Iterable):
1684
+ destination_zones = [destination_zones]
1685
+ if not isinstance(variables, Iterable):
1686
+ variables = [variables]
1687
+
1688
+ for var in variables:
1689
+ varidx = getattr(var, 'index', self.variable(var).index)
1690
+ for zone in destination_zones:
1691
+ zoneidx = getattr(zone, 'index', self.zone(zone).index)
1692
+ source_zoneidx = getattr(source_zone, 'index',
1693
+ self.zone(source_zone).index)
1694
+ if not _tecutil.DataValueIsSharingOk(source_zoneidx+1,
1695
+ zoneidx+1, varidx+1):
1696
+ raise TecplotSystemError()
1697
+ _tecutil.DataValueShare(source_zoneidx+1, zoneidx+1, varidx+1)
1698
+
1699
+ @lock()
1700
+ def branch_connectivity(self, zones):
1701
+ """Breaks connectivity sharing between zones.
1702
+
1703
+ Parameters:
1704
+ zones (`list` of `Zones <data_access>`): Zones to be branched.
1705
+
1706
+ .. seealso:: `Dataset.share_connectivity()`
1707
+
1708
+ Example usage::
1709
+
1710
+ >>> z = dataset.zone('My Zone')
1711
+ >>> zcopy = z.copy()
1712
+ >>> print([zn.index for zn in z.shared_connectivity])
1713
+ [0,1]
1714
+ >>> dataset.branch_connectivity(zcopy)
1715
+ >>> print([zn.index for zn in z.shared_connectivity])
1716
+ []
1717
+ """
1718
+ if not isinstance(zones, Iterable):
1719
+ zones = [zones]
1720
+
1721
+ for zone in zones:
1722
+
1723
+ try:
1724
+ zoneidx = getattr(zone, 'index')
1725
+ except:
1726
+ zoneidx = self.zone(zone).index
1727
+ if not _tecutil.DataConnectBranchShared(zoneidx + 1):
1728
+ raise TecplotSystemError()
1729
+
1730
+ @lock()
1731
+ def share_connectivity(self, source_zone, destination_zones):
1732
+ """Share connectivity between zones.
1733
+
1734
+ This method links the connectivity (`nodemap` or `facemap`) of the
1735
+ destination `zones <data_access>` to the connectivity of the source
1736
+ zone. Modifying the connectivity of one zone will affect all others in
1737
+ this group.
1738
+
1739
+ Parameters:
1740
+ source_zone (`Zone <data_access>`): Zone which provides data to be
1741
+ shared.
1742
+ destination_zones (`list` of `Zones <data_access>`): Zones where
1743
+ connectivity list will be overwritten.
1744
+
1745
+ .. seealso:: `Dataset.branch_connectivity()`
1746
+
1747
+ Example usage::
1748
+
1749
+ >>> z = dataset.zone('My Zone')
1750
+ >>> zcopy = z.copy()
1751
+ >>> print([zn.index for zn in z.shared_connectivity])
1752
+ [0,1]
1753
+ >>> dataset.branch_connectivity(zcopy)
1754
+ >>> print([zn.index for zn in z.shared_connectivity])
1755
+ []
1756
+ >>> dataset.share_connectivity(z,zcopy)
1757
+ >>> print([zn.index for zn in z.shared_connectivity])
1758
+ [0,1]
1759
+ """
1760
+ if not isinstance(destination_zones, Iterable):
1761
+ destination_zones = [destination_zones]
1762
+
1763
+ def _index(obj):
1764
+ i = getattr(obj, 'index', None)
1765
+ if i is None:
1766
+ i = self.zone(obj).index
1767
+ return i
1768
+
1769
+ for zone in destination_zones:
1770
+ idst = _index(zone)
1771
+ isrc = _index(source_zone)
1772
+ if not _tecutil.DataConnectIsSharingOk(isrc + 1, idst + 1):
1773
+ raise TecplotSystemError()
1774
+ _tecutil.DataConnectShare(isrc + 1, idst + 1)
1775
+
1776
+
1777
+ @property
1778
+ @lock()
1779
+ def dataset(frame):
1780
+ """`Dataset <tecplot.data.Dataset>` attached to this `Frame <layout.Frame>`.
1781
+
1782
+ Returns:
1783
+ `Dataset <tecplot.data.Dataset>`: The object holding onto the data associated with this
1784
+ `Frame <layout.Frame>`.
1785
+
1786
+ If no `Dataset <tecplot.data.Dataset>` has been created for this `Frame <layout.Frame>`, a new one is created
1787
+ and returned::
1788
+
1789
+ >>> dataset = frame.dataset
1790
+ """
1791
+ if not frame.has_dataset:
1792
+ frame.create_dataset(frame.name + ' Dataset')
1793
+ dataset_uid = _tecutil.FrameGetDataSetUniqueIDByFrameID(frame.uid)
1794
+ return Dataset(dataset_uid, frame)
1795
+
1796
+
1797
+ @property
1798
+ def has_dataset(frame):
1799
+ """`bool`: Checks to see if the `Frame <layout.Frame>` as an attached `Dataset <tecplot.data.Dataset>`
1800
+
1801
+ Example usage::
1802
+
1803
+ >>> if not frame.has_dataset:
1804
+ ... dataset = frame.create_dataset('Dataset', ['x','y','z','p'])
1805
+ """
1806
+ return _tecutil.DataSetIsAvailableForFrame(frame.uid)
1807
+
1808
+ layout.Frame.dataset = dataset
1809
+ layout.Frame.has_dataset = has_dataset