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,928 @@
1
+ from collections.abc import Iterable
2
+ from contextlib import contextmanager
3
+
4
+ from ..tecutil import _tecutil
5
+ from ..constant import *
6
+ from ..exception import *
7
+ from .. import layout, tecutil
8
+ from ..tecutil import IndexSet, Index, flatten_args, sv, optional
9
+
10
+
11
+ @tecutil.lock()
12
+ def execute_equation(equation, zones=None, i_range=None, j_range=None,
13
+ k_range=None, value_location=None, variable_data_type=None,
14
+ ignore_divide_by_zero=None):
15
+ """The execute_equation function operates on a data set within the
16
+ |Tecplot Engine| using FORTRAN-like equations.
17
+
18
+ Parameters:
19
+ equation (`str`): String containing the equation.
20
+ Multiple equations can be processed by separating each equation
21
+ with a newline. See Section 20 - 1 "Data Alteration through
22
+ Equations" in the |Tecplot User's Manual| for more information on
23
+ using equations. Iterable container of `Zone <data_access>` objects
24
+ to operate on. May be a list, set, tuple, or any iterable
25
+ container. If `None`, the equation will be applied to all zones.
26
+
27
+ .. note:: In the equation string, variable names should be enclosed
28
+ in curly braces. For example, '{X} = {X} + 1'
29
+
30
+ zones: (Iterable container of `Zone <data_access>` objects, optional):
31
+ Iterable container of `Zone <data_access>` objects to operate on.
32
+ May be a list, set, tuple, or any iterable container. If `None`,
33
+ the equation will be applied to all zones.
34
+ i_range (`tuple` of `integers <int>`, optional):
35
+ Tuple of integers for I: (min, max, step). If `None`, then the
36
+ equation will operate on the entire range.
37
+ Not used for finite element nodal data.
38
+ j_range (`tuple` of `integers <int>`, optional):
39
+ Tuple of integers for J: (min, max, step). If `None`, then the
40
+ equation will operate on the entire range.
41
+ Not used for finite element nodal data.
42
+ k_range (`tuple` of `integers <int>`, optional):
43
+ Tuple of integers for K: (min, max, step). If `None`, then the
44
+ equation will operate on the entire range.
45
+ Not used for finite element nodal data.
46
+ value_location (`ValueLocation`, optional):
47
+ Variable `ValueLocation` for the variable on the left hand side.
48
+ This is used only if this variable is being created for the first
49
+ time.
50
+ If `None`, |Tecplot Engine| will choose the location for you.
51
+ variable_data_type (`FieldDataType`, optional):
52
+ Data type for the variable on the left hand side.
53
+ This is used only if this variable is being created for the first
54
+ time.
55
+ If `None`, |Tecplot Engine| will choose the type for you.
56
+ ignore_divide_by_zero (`bool`, optional):
57
+ `bool` value which instructs |Tecplot Engine| to ignore
58
+ divide by zero errors. The result is clamped
59
+ such that 0/0 is clamped to zero and (+/-N)/0
60
+ where N != 0 clamps to +/-maximum value for the given type.
61
+
62
+ .. warning:: Zero-based Indexing
63
+
64
+ It is important to know that all indexing in |PyTecplot| scripts are
65
+ zero-based. This is a departure from the macro language which is
66
+ one-based. This is to keep with the expectations when working in the
67
+ python language. However, |PyTecplot| does not modify strings that are
68
+ passed to the |Tecplot Engine|. This means that one-based indexing
69
+ should be used when running macro commands from python or when using
70
+ `execute_equation() <tecplot.data.operate.execute_equation>`.
71
+
72
+ Add one to variable 'X' for zones 'Rectangular' and 'Circular' for every
73
+ data point:
74
+
75
+ >>> dataset = tecplot.active_frame().dataset
76
+ >>> execute_equation('{X} = {X} + 1', zones=[dataset.zone('Rectangular'),
77
+ >>> dataset.zone('Circular')])
78
+
79
+ Create a new, double precision variable called DIST:
80
+
81
+ >>> execute_equation('{DIST} = SQRT({X}**2 + {Y}**2)',
82
+ ... variable_data_type=FieldDataType.Double)
83
+
84
+ Set a variable called **P** to zero along the boundary of an IJ-ordered
85
+ zone:
86
+
87
+ >>> execute_equation('{P} = 0', i_range=(0, -1, 0), j_range=(0, -1, 0))
88
+
89
+ Using 1-based indexing in equations and 0-based indexing in parameters.
90
+ Zone 4 is subtracted from zone 3 and the result is placed in zone 2:
91
+
92
+ >>> execute_equation('{T} = {T}[3]-{T}[4]', zones=[ds.zone(1)])
93
+ """
94
+ if __debug__:
95
+ if not isinstance(equation, str):
96
+ raise TecplotTypeError('Equation must be a string')
97
+ elif len(equation) == 0:
98
+ raise TecplotValueError('Equation can not be empty')
99
+ if not isinstance(value_location, (ValueLocation, type(None))):
100
+ msg = 'value_location must be a ValueLocation'
101
+ raise TecplotTypeError(msg)
102
+ if not isinstance(variable_data_type, (FieldDataType, type(None))):
103
+ msg = 'variable_data_type must be a FieldDataType'
104
+ raise TecplotTypeError(msg)
105
+ if not isinstance(ignore_divide_by_zero, (bool, type(None))):
106
+ raise TecplotTypeError('ignore_divide_by_zero must be a bool')
107
+
108
+ # Check that all zones belong to the active dataset
109
+ # (which is currently the only dataset option available)
110
+ if zones:
111
+ def dataset_uid(zone):
112
+ '''return the dataset uid if zone is a Zone object.'''
113
+ try:
114
+ return zone.dataset.uid
115
+ except AttributeError:
116
+ return None
117
+
118
+ if isinstance(zones, Iterable):
119
+ # converting generator to list to allow
120
+ # iterating over it more than once
121
+ zones = list(zones)
122
+ parent_ids = map(dataset_uid, zones)
123
+ else:
124
+ parent_ids = {dataset_uid(zones)}
125
+ parent_ids = set(filter(None, parent_ids))
126
+
127
+ active_id = layout.active_frame().dataset.uid
128
+ if parent_ids and parent_ids != {active_id}:
129
+ msg = 'All zones must have the same parent dataset'
130
+ raise TecplotValueError(msg)
131
+
132
+ with tecutil.ArgList() as arglist:
133
+ arglist[sv.EQUATION] = equation
134
+ with optional(IndexSet, zones) as zoneset:
135
+ arglist[sv.ZONESET] = zoneset
136
+
137
+ for dim, rng in zip(['I', 'J', 'K'], [i_range, j_range, k_range]):
138
+ if rng is not None:
139
+ rng = tecutil.IndexRange(*rng)
140
+ if rng.min is not None:
141
+ arglist[dim + 'MIN'] = Index(rng.min)
142
+ if rng.max is not None:
143
+ arglist[dim + 'MAX'] = Index(rng.max)
144
+ if rng.step is not None:
145
+ step = int(rng.step)
146
+ if step > 0:
147
+ arglist[dim + 'SKIP'] = step
148
+ elif step < 0:
149
+ msg = 'Negative step not supported.'
150
+ raise TecplotLogicError(msg)
151
+
152
+ arglist[sv.VALUELOCATION] = value_location
153
+ arglist[sv.VARDATATYPE] = variable_data_type
154
+ arglist[sv.IGNOREDIVIDEBYZERO] = ignore_divide_by_zero
155
+
156
+ if not _tecutil.DataAlterX(arglist):
157
+ raise TecplotSystemError()
158
+
159
+
160
+ @contextmanager
161
+ def _interpolate_process_args(dest_zone, src_zones, variables, plot):
162
+ if plot is None:
163
+ plot = layout.active_frame().plot()
164
+ if isinstance(dest_zone, int):
165
+ dest_zone = plot.frame.dataset.zone(dest_zone)
166
+ if not isinstance(src_zones, (Iterable, type(None))):
167
+ src_zones = [src_zones]
168
+ if not isinstance(variables, (Iterable, type(None))):
169
+ variables = [variables]
170
+
171
+ if __debug__:
172
+ if plot.frame.dataset != dest_zone.dataset:
173
+ msg = 'Plot and destination zone do not share the same dataset.'
174
+ raise TecplotLogicError(msg)
175
+ if src_zones is not None:
176
+ def is_invalid_zone(z):
177
+ if isinstance(z, int):
178
+ return z >= plot.frame.dataset.num_zones
179
+ else:
180
+ return plot.frame.dataset != z.dataset
181
+ if any(map(is_invalid_zone, src_zones)):
182
+ msg = 'Source zones are not part of the same dataset.'
183
+ raise TecplotLogicError(msg)
184
+ if variables is not None:
185
+ def is_invalid_variable(v):
186
+ if isinstance(v, int):
187
+ return v >= plot.frame.dataset.num_variables
188
+ else:
189
+ return plot.frame.dataset != v.dataset
190
+ if any(map(is_invalid_variable, variables)):
191
+ msg = 'Variables are not part of the same dataset.'
192
+ raise TecplotLogicError(msg)
193
+
194
+ with plot.activated():
195
+ with optional(IndexSet, src_zones) as src:
196
+ with optional(IndexSet, variables) as varset:
197
+ dest = dest_zone.index + 1
198
+ yield src, dest, varset
199
+
200
+
201
+ @tecutil.lock()
202
+ def interpolate_linear(destination_zone, source_zones=None, variables=None,
203
+ fill_value=None, plot=None):
204
+ """Linear interpolation onto a destination zone.
205
+
206
+ Parameters:
207
+ destination_zone (`zone <data_access>` or `int`): The
208
+ destination zone (or zone index) for interpolation.
209
+ source_zones (`zones <data_access>` or `integers <int>`, optional):
210
+ Zones (or zone indices) used to obtain the field values for
211
+ interpolation. By default, all zones except the *destination_zone*
212
+ will be used. All source zones must be FE-Tetra, FE-Brick or be
213
+ IJK-ordered when doing linear interpolation in 3D.
214
+ variables (`variables <Variable>` or `integers <int>`, optional):
215
+ Variables (or variable indices) to interpolate. By default, all
216
+ variables except those assigned to the axes will be used and is in
217
+ general dependent on the active plot type of the frame.
218
+ fill_value (`float`, optional): Constant value to which all points
219
+ outside the data field are set. By default, the values outside
220
+ the field are preserved.
221
+ plot (:ref:`plot`, optional): The plot to use when interpolating which
222
+ determines the dimensionality and spatial variables. By default,
223
+ the active plot on the active frame will be used.
224
+
225
+ .. note:: Cartesian 2D and 3D plots only.
226
+
227
+ This interpolation method relies on the coordinates, :math:`(x, y)` for
228
+ 2D or :math:`(x, y, z)` for 3D, set for the active (or given) plot
229
+ which must be either Cartesian2D or Cartesian3D.
230
+
231
+ The following example loads a 2D dataset and uses interpolation to merge
232
+ information from two independent zones:
233
+
234
+ .. code-block:: python
235
+
236
+ import os
237
+ import numpy as np
238
+ import tecplot as tp
239
+ from tecplot.constant import *
240
+
241
+ # Use interpolation to merge information from two independent zones
242
+ examples_dir = tp.session.tecplot_examples_directory()
243
+ datafile = os.path.join(examples_dir, 'SimpleData', 'RainierElevation.plt')
244
+ dataset = tp.data.load_tecplot(datafile)
245
+ # Get list of source zones to use later
246
+ srczones = list(dataset.zones())
247
+
248
+ fr = tp.active_frame()
249
+ plot = fr.plot(PlotType.Cartesian2D)
250
+ plot.activate()
251
+ plot.show_contour = True
252
+ plot.show_edge = True
253
+
254
+ # Show two section of the plot independently
255
+ plot.contour(0).legend.show = False
256
+ plot.contour(1).legend.show = False
257
+ plot.contour(1).colormap_name = 'Diverging - Blue/Red'
258
+ for scrzone in srczones:
259
+ plot.fieldmap(scrzone).edge.line_thickness = 0.4
260
+ plot.fieldmap(0).contour.flood_contour_group = plot.contour(1)
261
+
262
+ # export image of original data
263
+ tp.export.save_png('interpolate_2d_source.png', 600, supersample=3)
264
+
265
+ # use the first zone as the source, and get the range of (x, y)
266
+ xvar = plot.axes.x_axis.variable
267
+ yvar = plot.axes.y_axis.variable
268
+ ymin, xmin = 99999,99999
269
+ ymax, xmax = -99999,-99999
270
+ for scrzone in srczones:
271
+ curxmin, curxmax = scrzone.values(xvar.index).minmax()
272
+ curymin, curymax = scrzone.values(yvar.index).minmax()
273
+ ymin = min(curymin,ymin)
274
+ ymax = max(curymax,ymax)
275
+ xmin = min(curxmin,xmin)
276
+ xmax = max(curxmax,xmax)
277
+
278
+ # create new zone with a coarse grid
279
+ # onto which we will interpolate from the source zone
280
+ xpoints = 40
281
+ ypoints = 40
282
+ newzone = dataset.add_ordered_zone('Interpolated', (xpoints, ypoints))
283
+
284
+ # setup the (x, y) positions of the new grid
285
+ xx = np.linspace(xmin, xmax, xpoints)
286
+ yy = np.linspace(ymin, ymax, ypoints)
287
+ YY, XX = np.meshgrid(yy, xx, indexing='ij')
288
+ newzone.values(xvar.index)[:] = XX.ravel()
289
+ newzone.values(yvar.index)[:] = YY.ravel()
290
+
291
+ # perform linear interpolation from the source to the new zone
292
+ tp.data.operate.interpolate_linear(newzone, source_zones=srczones)
293
+
294
+ # show the new zone's data, hide the source
295
+ plot.fieldmap(newzone).show = True
296
+ plot.fieldmap(newzone).contour.show = True
297
+ plot.fieldmap(newzone).contour.flood_contour_group = plot.contour(0)
298
+ plot.fieldmap(newzone).edge.show = True
299
+ plot.fieldmap(newzone).edge.line_thickness = .4
300
+ plot.fieldmap(newzone).edge.color = Color.Orange
301
+
302
+ for scrzone in srczones:
303
+ plot.fieldmap(scrzone).show = False
304
+
305
+ # export image of interpolated data
306
+ tp.export.save_png('interpolate_linear_2d_dest.png', 600, supersample=3)
307
+
308
+ .. figure:: /_static/images/interpolate_2d_source.png
309
+ :width: 300px
310
+ :figwidth: 300px
311
+
312
+ Source data.
313
+
314
+ .. figure:: /_static/images/interpolate_linear_2d_dest.png
315
+ :width: 300px
316
+ :figwidth: 300px
317
+
318
+ Interpolated data.
319
+
320
+ """
321
+ with _interpolate_process_args(destination_zone, source_zones, variables,
322
+ plot) as (src, dest, varset):
323
+ if fill_value is None:
324
+ interp_const = 0.0
325
+ interp_mode = LinearInterpMode.DontChange
326
+ else:
327
+ interp_const = float(fill_value)
328
+ interp_mode = LinearInterpMode.SetToConst
329
+ if not _tecutil.LinearInterpolate(src, dest, varset, interp_const,
330
+ interp_mode.value):
331
+ raise TecplotSystemError()
332
+
333
+
334
+ @tecutil.lock()
335
+ def interpolate_kriging(destination_zone, source_zones=None, variables=None,
336
+ krig_range=0.3, zero_value=0.0, drift=Drift.Linear,
337
+ point_selection=PtSelection.OctantN, num_points=8,
338
+ plot=None):
339
+ """Kriging interpolation onto a destination zone.
340
+
341
+ Parameters:
342
+ destination_zone (`zone <data_access>` or `int`): The
343
+ destination zone (or zone index) for interpolation.
344
+ source_zones (`zones <data_access>` or `integers <int>`, optional):
345
+ Zones (or zone indices) used to obtain the field values for
346
+ interpolation. By default, all zones except the *destination_zone*
347
+ will be used. All source zones must be FE-Tetra, FE-Brick or
348
+ IJK-ordered when doing kriging interpolation in 3D.
349
+ variables (`variables <Variable>` or `integers <int>`, optional):
350
+ Variables (or variable indices) to interpolate. By default, all
351
+ variables except those assigned to the axes will be used and is in
352
+ general dependent on the active plot type of the frame.
353
+ krig_range (`float`, optional): Distance beyond which source points
354
+ become insignificant. Must be between zero and one, inclusive.
355
+ (default: 0.3)
356
+ zero_value (`float`, optional): Semi-variance at each source data point
357
+ on a normalized scale from zero to one. (default: 0.0)
358
+ drift (`Drift`, optional): Overall trend for the data. Possible values:
359
+ `Drift.None_` no trend, `Drift.Linear` (default) linear trend,
360
+ `Drift.Quad` quadratic trend.
361
+ point_selection (`PtSelection`, optional): Method for determining which
362
+ source points to consider for each destination data point. Possible
363
+ values: `PtSelection.OctantN` (default) closest *num_points*
364
+ selected by coordinate-system octants, `PtSelection.NearestN`
365
+ closest *num_points* to the destination point, `PtSelection.All`
366
+ all points in the source zone.
367
+ num_points (`int`, optional): Number of source points to
368
+ consider for each destination data point if *point_selection* is
369
+ `PtSelection.OctantN` or `PtSelection.NearestN`. (default: 8)
370
+ plot (:ref:`plot`, optional): The plot to use when interpolating which
371
+ determines the dimensionality and spatial variables. By default,
372
+ the active plot on the active frame will be used.
373
+
374
+ .. note:: Cartesian 2D and 3D plots only.
375
+
376
+ This interpolation method relies on the coordinates, :math:`(x, y)` for
377
+ 2D or :math:`(x, y, z)` for 3D, set for the active (or given) plot
378
+ which must be either Cartesian2D or Cartesian3D.
379
+
380
+ The following example loads a 2D dataset and interpolates the first zone to
381
+ a new one with a larger grid spacing:
382
+
383
+ .. code-block:: python
384
+ :emphasize-lines: 58-59
385
+
386
+ import os
387
+ import numpy as np
388
+ import tecplot as tp
389
+ from tecplot.constant import *
390
+
391
+ # Use interpolation to merge information from two independent zones
392
+ examples_dir = tp.session.tecplot_examples_directory()
393
+ datafile = os.path.join(examples_dir, 'SimpleData',
394
+ 'RainierElevation.plt')
395
+ dataset = tp.data.load_tecplot(datafile)
396
+ # Get list of source zones to use later
397
+ srczones = list(dataset.zones())
398
+
399
+ fr = tp.active_frame()
400
+ plot = fr.plot(PlotType.Cartesian2D)
401
+ plot.activate()
402
+ plot.show_contour = True
403
+ plot.show_edge = True
404
+
405
+ # Show two section of the plot independently
406
+ plot.contour(0).legend.show = False
407
+ plot.contour(1).legend.show = False
408
+ plot.contour(1).colormap_name = 'Diverging - Blue/Red'
409
+ for scrzone in srczones:
410
+ plot.fieldmap(scrzone).edge.line_thickness = 0.4
411
+ plot.fieldmap(0).contour.flood_contour_group = plot.contour(1)
412
+
413
+ # export image of original data
414
+ tp.export.save_png('interpolate_2d_source.png', 600, supersample=3)
415
+
416
+ # use the first zone as the source, and get the range of (x, y)
417
+ xvar = plot.axes.x_axis.variable
418
+ yvar = plot.axes.y_axis.variable
419
+ ymin, xmin = 99999,99999
420
+ ymax, xmax = -99999,-99999
421
+ for scrzone in srczones:
422
+ curxmin, curxmax = scrzone.values(xvar.index).minmax()
423
+ curymin, curymax = scrzone.values(yvar.index).minmax()
424
+ ymin = min(curymin,ymin)
425
+ ymax = max(curymax,ymax)
426
+ xmin = min(curxmin,xmin)
427
+ xmax = max(curxmax,xmax)
428
+
429
+ # create new zone with a coarse grid
430
+ # onto which we will interpolate from the source zone
431
+ xpoints = 20
432
+ ypoints = 20
433
+ newzone = dataset.add_ordered_zone('Interpolated', (xpoints, ypoints))
434
+
435
+ # setup the (x, y) positions of the new grid
436
+ xx = np.linspace(xmin, xmax, xpoints)
437
+ yy = np.linspace(ymin, ymax, ypoints)
438
+ YY, XX = np.meshgrid(yy, xx, indexing='ij')
439
+ newzone.values(xvar.index)[:] = XX.ravel()
440
+ newzone.values(yvar.index)[:] = YY.ravel()
441
+
442
+ # perform linear interpolation from the source to the new zone
443
+ tp.data.operate.interpolate_kriging(newzone, source_zones=srczones,
444
+ drift=Drift.None_, num_points=1)
445
+
446
+ # show the new zone's data, hide the source
447
+ plot.fieldmap(newzone).show = True
448
+ plot.fieldmap(newzone).contour.show = True
449
+ plot.fieldmap(newzone).contour.flood_contour_group = plot.contour(0)
450
+ plot.fieldmap(newzone).edge.show = True
451
+ plot.fieldmap(newzone).edge.line_thickness = .4
452
+ plot.fieldmap(newzone).edge.color = Color.Orange
453
+
454
+ for scrzone in srczones:
455
+ plot.fieldmap(scrzone).show = False
456
+
457
+ # export image of interpolated data
458
+ tp.export.save_png('interpolate_krig_2d_dest.png', 600, supersample=3)
459
+
460
+ .. figure:: /_static/images/interpolate_2d_source.png
461
+ :width: 300px
462
+ :figwidth: 300px
463
+
464
+ Source data.
465
+
466
+ .. figure:: /_static/images/interpolate_krig_2d_dest.png
467
+ :width: 300px
468
+ :figwidth: 300px
469
+
470
+ Interpolated data.
471
+ """
472
+ with _interpolate_process_args(destination_zone, source_zones, variables,
473
+ plot) as (src, dest, varset):
474
+ if not _tecutil.Krig(
475
+ src, dest, varset, krig_range, zero_value, Drift(drift).value,
476
+ PtSelection(point_selection).value, num_points):
477
+ raise TecplotSystemError()
478
+
479
+
480
+ @tecutil.lock()
481
+ def interpolate_inverse_distance(destination_zone, source_zones=None,
482
+ variables=None, exponent=3.5, min_radius=0.0,
483
+ point_selection=PtSelection.OctantN,
484
+ num_points=8, plot=None):
485
+ """Inverse-Distance interpolation onto a destination zone.
486
+
487
+ Parameters:
488
+ destination_zone (`zone <data_access>` or `int`): The
489
+ destination zone (or zone index) for interpolation.
490
+ source_zones (`zones <data_access>` or `integers <int>`, optional):
491
+ Zones (or zone indices) used to obtain the field values for
492
+ interpolation. By default, all zones except the *destination_zone*
493
+ will be used. All source zones must be FE-Tetra, FE-Brick or be
494
+ IJK-ordered when doing linear interpolation in 3D.
495
+ variables (`variables <Variable>` or `integers <int>`, optional):
496
+ Variables (or variable indices) to interpolate. By default, all
497
+ variables except those assigned to the axes will be used and is in
498
+ general dependent on the active plot type of the frame.
499
+ exponent (`float`, optional): Exponent for the inverse-distance
500
+ weighting. (default: 3.5)
501
+ min_radius (`float`, optional): Minimum distance used for the
502
+ inverse-distance weighting. (default: 0.0)
503
+ point_selection (`PtSelection`, optional): Method for determining which
504
+ source points to consider for each destination data point. Possible
505
+ values: `PtSelection.OctantN` (default) closest *num_points*
506
+ selected by coordinate-system octants, `PtSelection.NearestN`
507
+ closest *num_points* to the destination point, `PtSelection.All`
508
+ all points in the source zone.
509
+ num_points (`int`, optional): Number of source points to
510
+ consider for each destination data point if *point_selection* is
511
+ `PtSelection.OctantN` or `PtSelection.NearestN`. (default: 8)
512
+ plot (:ref:`plot`, optional): The plot to use when interpolating which
513
+ determines the dimensionality and spatial variables. By default,
514
+ the active plot on the active frame will be used.
515
+
516
+ .. note:: Cartesian 2D and 3D plots only.
517
+
518
+ This interpolation method relies on the coordinates, :math:`(x, y)` for
519
+ 2D or :math:`(x, y, z)` for 3D, set for the active (or given) plot
520
+ which must be either Cartesian2D or Cartesian3D.
521
+
522
+ The following example loads a 2D dataset and interpolates the first zone to
523
+ a new one with a larger grid spacing:
524
+
525
+ .. code-block:: python
526
+ :emphasize-lines: 57
527
+
528
+ import os
529
+ import numpy as np
530
+ import tecplot as tp
531
+ from tecplot.constant import *
532
+
533
+ # Use interpolation to merge information from two independent zones
534
+ examples_dir = tp.session.tecplot_examples_directory()
535
+ datafile = os.path.join(examples_dir, 'SimpleData', 'RainierElevation.plt')
536
+ dataset = tp.data.load_tecplot(datafile)
537
+ # Get list of source zones to use later
538
+ srczones = list(dataset.zones())
539
+
540
+ fr = tp.active_frame()
541
+ plot = fr.plot(PlotType.Cartesian2D)
542
+ plot.activate()
543
+ plot.show_contour = True
544
+ plot.show_edge = True
545
+
546
+ # Show two section of the plot independently
547
+ plot.contour(0).legend.show = False
548
+ plot.contour(1).legend.show = False
549
+ plot.contour(1).colormap_name = 'Diverging - Blue/Red'
550
+ for scrzone in srczones:
551
+ plot.fieldmap(scrzone).edge.line_thickness = 0.4
552
+ plot.fieldmap(0).contour.flood_contour_group = plot.contour(1)
553
+
554
+ # export image of original data
555
+ tp.export.save_png('interpolate_2d_source.png', 600, supersample=3)
556
+
557
+ # use the first zone as the source, and get the range of (x, y)
558
+ xvar = plot.axes.x_axis.variable
559
+ yvar = plot.axes.y_axis.variable
560
+ ymin, xmin = 99999,99999
561
+ ymax, xmax = -99999,-99999
562
+ for scrzone in srczones:
563
+ curxmin, curxmax = scrzone.values(xvar.index).minmax()
564
+ curymin, curymax = scrzone.values(yvar.index).minmax()
565
+ ymin = min(curymin,ymin)
566
+ ymax = max(curymax,ymax)
567
+ xmin = min(curxmin,xmin)
568
+ xmax = max(curxmax,xmax)
569
+
570
+ # create new zone with a coarse grid
571
+ # onto which we will interpolate from the source zone
572
+ xpoints = 40
573
+ ypoints = 40
574
+ newzone = dataset.add_ordered_zone('Interpolated', (xpoints, ypoints))
575
+
576
+ # setup the (x, y) positions of the new grid
577
+ xx = np.linspace(xmin, xmax, xpoints)
578
+ yy = np.linspace(ymin, ymax, ypoints)
579
+ YY, XX = np.meshgrid(yy, xx, indexing='ij')
580
+ newzone.values(xvar.index)[:] = XX.ravel()
581
+ newzone.values(yvar.index)[:] = YY.ravel()
582
+
583
+ # perform linear interpolation from the source to the new zone
584
+ tp.data.operate.interpolate_inverse_distance(newzone, source_zones=srczones)
585
+
586
+ # show the new zone's data, hide the source
587
+ plot.fieldmap(newzone).show = True
588
+ plot.fieldmap(newzone).contour.show = True
589
+ plot.fieldmap(newzone).contour.flood_contour_group = plot.contour(0)
590
+ plot.fieldmap(newzone).edge.show = True
591
+ plot.fieldmap(newzone).edge.line_thickness = .4
592
+ plot.fieldmap(newzone).edge.color = Color.Orange
593
+
594
+ for scrzone in srczones:
595
+ plot.fieldmap(scrzone).show = False
596
+
597
+ # export image of interpolated data
598
+ tp.export.save_png('interpolate_invdst_2d_dest.png', 600, supersample=3)
599
+
600
+ .. figure:: /_static/images/interpolate_2d_source.png
601
+ :width: 300px
602
+ :figwidth: 300px
603
+
604
+ Source data.
605
+
606
+ .. figure:: /_static/images/interpolate_invdst_2d_dest.png
607
+ :width: 300px
608
+ :figwidth: 300px
609
+
610
+ Interpolated data.
611
+ """
612
+ with _interpolate_process_args(destination_zone, source_zones, variables,
613
+ plot) as (src, dest, varset):
614
+ if not _tecutil.InverseDistInterpolation(
615
+ src, dest, varset, exponent, min_radius,
616
+ PtSelection(point_selection).value, num_points):
617
+ raise TecplotSystemError()
618
+
619
+
620
+ @tecutil.lock()
621
+ def smooth(array, num_passes=1, weight=0.8,
622
+ boundary_condition=BoundaryCondition.Fixed, frame=None):
623
+ """Smooth a field data `Array` in the dataset.
624
+
625
+ Parameters:
626
+ array (`Array`): The field data to smooth.
627
+ num_passes (`int`, optional): The number of smoothing passes to
628
+ perform. (default: 1)
629
+ weight (`float`, optional): The relaxation factor for each pass of
630
+ smoothing. Must be a number between zero and one exclusively.
631
+ Higher numbers indicate a greater smoothing effect. (default: 0.8)
632
+ boundary_condition (`BoundaryCondition`, optional): The boundary
633
+ condition by which to smooth. Possible values are
634
+ `BoundaryCondition.Fixed` (default),
635
+ `BoundaryCondition.ZeroGradient` and `BoundaryCondition.Zero2nd`.
636
+ frame (`Frame <layout.Frame>`, optional): The `Frame <layout.Frame>` that specifies the spatial
637
+ variables to smooth over via the active plot. By default, the frame
638
+ associated with the input `Array` object will be used.
639
+
640
+ The data will be smoothed over the spatial variables set by the plot and is
641
+ dependent on the active plot type of the associated `Frame <layout.Frame>` (`Cartesian2D`
642
+ or `Cartesian3D`). Example usage::
643
+
644
+ >>> tp.data.operate.smooth(dataset.zone('Zone').values('Pressure'))
645
+ """
646
+ frame = frame or array.zone.dataset.frame
647
+ with frame.activated():
648
+ if not _tecutil.Smooth(array.zone.index + 1, array.variable.index + 1,
649
+ int(num_passes), float(weight),
650
+ BoundaryCondition(boundary_condition).value):
651
+ raise TecplotSystemError()
652
+
653
+
654
+ def _dataset(*objlists):
655
+ for objs in objlists:
656
+ if objs:
657
+ for obj in objs:
658
+ if obj and hasattr(obj, 'dataset'):
659
+ return obj.dataset
660
+ return layout.active_frame().dataset
661
+
662
+
663
+ @tecutil.lock()
664
+ def transform_polar_to_rectangular(r, theta, x=None, y=None,
665
+ angle_units=AngleUnits.Radians, zones=None):
666
+ """Transform all points from polar to rectangular coordinates.
667
+
668
+ Parameters:
669
+ r (`Variable`): The radial input variable.
670
+ theta (`Variable`): The angular input variable in **angle_units** from
671
+ the :math:`x`-axis.
672
+ x (`Variable`, optional): The rectangular output :math:`x` variable. By
673
+ default, a new variable will be created. Both **x** and **y** must
674
+ be specified together as either existing variables or as `None`.
675
+ y (`Variable`, optional): The rectangular output :math:`y` variable. By
676
+ default, a new variable will be created. Both **x** and **y** must
677
+ be specified together as either existing variables or as `None`.
678
+ angle_units (`AngleUnits`, optional): The units of the angular
679
+ variables. This may be either `AngleUnits.Radians` (default) or
680
+ `AngleUnits.Degrees`.
681
+ zones (`list` of `Zones <data_access>`, optional): Specific zones to
682
+ transform. By default, all zones are transformed.
683
+
684
+ This example will create two new variables in the dataset corresponding to
685
+ the :math:`x` and :math:`y` equivalents of the :math:`r` and :math:`theta`
686
+ variables::
687
+
688
+ >>> dataset = tp.active_frame().dataset
689
+ >>> r = dataset.variable('R (m)')
690
+ >>> theta = dataset.variable('Theta (rad)')
691
+ >>> tp.data.operate.transform_polar_to_rectangular(r, theta)
692
+ >>> x, y = dataset.variable(-2), dataset.variable(-1)
693
+
694
+ .. warning:: **Source variables must have the same location as the
695
+ destination variables.**
696
+
697
+ The variables involved with this transformation must all be either
698
+ nodal or cell-centered.
699
+ """
700
+ dataset = _dataset((r, theta, x, y), zones)
701
+ with tecutil.ArgList() as arglist:
702
+ arglist[sv.TRANSFORMATION] = Transform.PolarToRect
703
+ arglist[sv.RVAR] = dataset.variable(r).index
704
+ arglist[sv.THETAVAR] = dataset.variable(theta).index
705
+ if all(v is None for v in (x, y)):
706
+ arglist[sv.CREATENEWVARIABLES] = True
707
+ arglist[sv.XVAR] = 1
708
+ arglist[sv.YVAR] = 1
709
+ elif all(v is not None for v in (x, y)):
710
+ arglist[sv.CREATENEWVARIABLES] = False
711
+ arglist[sv.XVAR] = dataset.variable(x).index
712
+ arglist[sv.YVAR] = dataset.variable(y).index
713
+ else:
714
+ msg = 'All destination variables must be specified or None.'
715
+ raise TecplotLogicError(msg)
716
+ arglist[sv.ANGLESPEC] = AngleUnits(angle_units)
717
+ with tecutil.optional(tecutil.IndexSet, zones) as zones:
718
+ arglist[sv.ZONELIST] = zones
719
+ if not _tecutil.TransformCoordinatesX(arglist):
720
+ raise TecplotSystemError()
721
+
722
+
723
+ @tecutil.lock()
724
+ def transform_rectangular_to_polar(x, y, r=None, theta=None,
725
+ angle_units=AngleUnits.Radians, zones=None):
726
+ """Transform all points from rectangular to polar coordinates.
727
+
728
+ Parameters:
729
+ x (`Variable`): The rectangular input :math:`x` variable.
730
+ y (`Variable`): The rectangular input :math:`y` variable.
731
+ r (`Variable`, optional): The radial output :math:`r` variable. By
732
+ default, a new variable will be created. Both **r** and **theta**
733
+ must be specified together as either existing variables or as
734
+ `None`.
735
+ theta (`Variable`, optional): The angular output :math:`theta`
736
+ variable. By default, a new variable will be created. Both **r**
737
+ and **theta** must be specified together as either existing
738
+ variables or as `None`.
739
+ angle_units (`AngleUnits`, optional): The units of the angular
740
+ variables. This may be either `AngleUnits.Radians` (default) or
741
+ `AngleUnits.Degrees`.
742
+ zones (`list` of `Zones <data_access>`, optional): Specific zones to
743
+ transform. By default, all zones are transformed.
744
+
745
+ This example will create two new variables in the dataset corresponding to
746
+ the :math:`r` and :math:`theta` equivalents of the :math:`x` and :math:`y`
747
+ variables::
748
+
749
+ >>> dataset = tp.active_frame().dataset
750
+ >>> x = dataset.variable('X (m)')
751
+ >>> y = dataset.variable('Y (m)')
752
+ >>> tp.data.operate.transform_rectangular_to_polar(x, y)
753
+ >>> theta, r = dataset.variable(-2), dataset.variable(-1)
754
+
755
+ .. warning:: **Source variables must have the same location as the
756
+ destination variables.**
757
+
758
+ The variables involved with this transformation must all be either
759
+ nodal or cell-centered.
760
+ """
761
+ dataset = _dataset((x, y, r, theta), zones)
762
+ with tecutil.ArgList() as arglist:
763
+ arglist[sv.TRANSFORMATION] = Transform.RectToPolar
764
+ # (x, y) are switched in the SDK for this polar transformation
765
+ arglist[sv.XVAR] = dataset.variable(y).index
766
+ arglist[sv.YVAR] = dataset.variable(x).index
767
+ if all(v is None for v in (r, theta)):
768
+ arglist[sv.CREATENEWVARIABLES] = True
769
+ arglist[sv.RVAR] = 1
770
+ arglist[sv.THETAVAR] = 1
771
+ elif all(v is not None for v in (r, theta)):
772
+ arglist[sv.CREATENEWVARIABLES] = False
773
+ arglist[sv.RVAR] = dataset.variable(r).index
774
+ arglist[sv.THETAVAR] = dataset.variable(theta).index
775
+ else:
776
+ msg = 'All destination variables must be specified or None.'
777
+ raise TecplotLogicError(msg)
778
+ arglist[sv.ANGLESPEC] = AngleUnits(angle_units)
779
+ with tecutil.optional(tecutil.IndexSet, zones) as zones:
780
+ arglist[sv.ZONELIST] = zones
781
+ if not _tecutil.TransformCoordinatesX(arglist):
782
+ raise TecplotSystemError()
783
+
784
+
785
+ @tecutil.lock()
786
+ def transform_spherical_to_rectangular(r, theta, psi, x=None, y=None, z=None,
787
+ angle_units=AngleUnits.Radians, zones=None):
788
+ """Transform all points from spherical to rectangular coordinates.
789
+
790
+ Parameters:
791
+ r (`Variable`): The radial input variable.
792
+ theta (`Variable`): The angular input variable in **angle_units** from
793
+ the :math:`x`-axis.
794
+ psi (`Variable`): The angular input variable in **angle_units** from
795
+ the :math:`z`-axis.
796
+ x (`Variable`, optional): The rectangular output :math:`x` variable. By
797
+ default, a new variable will be created. All of **x**, **y** and
798
+ **z** must be specified together as either existing variables or as
799
+ `None`.
800
+ y (`Variable`, optional): The rectangular output :math:`y` variable. By
801
+ default, a new variable will be created. All of **x**, **y** and
802
+ **z** must be specified together as either existing variables or as
803
+ `None`.
804
+ z (`Variable`, optional): The rectangular output :math:`z` variable. By
805
+ default, a new variable will be created. All of **x**, **y** and
806
+ **z** must be specified together as either existing variables or as
807
+ `None`.
808
+ angle_units (`AngleUnits`, optional): The units of the angular
809
+ variables. This may be either `AngleUnits.Radians` (default) or
810
+ `AngleUnits.Degrees`.
811
+ zones (`list` of `Zones <data_access>`, optional): Specific zones to
812
+ transform. By default, all zones are transformed.
813
+
814
+ This example will create three new variables in the dataset corresponding
815
+ to the :math:`(x, y, z)` equivalents of the :math:`(r, theta, psi)`
816
+ variables::
817
+
818
+ >>> dataset = tp.active_frame().dataset
819
+ >>> r = dataset.variable('R (m)')
820
+ >>> theta = dataset.variable('Theta (rad)')
821
+ >>> psi = dataset.variable('Psi (rad)')
822
+ >>> tp.data.operate.transform_spherical_to_rectangular(r, theta, psi)
823
+ >>> x = dataset.variable(-3)
824
+ >>> y = dataset.variable(-2)
825
+ >>> z = dataset.variable(-1)
826
+
827
+ .. warning:: **Source variables must have the same location as the
828
+ destination variables.**
829
+
830
+ The variables involved with this transformation must all be either
831
+ nodal or cell-centered.
832
+ """
833
+ dataset = _dataset((r, theta, psi, x, y, z), zones)
834
+ with tecutil.ArgList() as arglist:
835
+ arglist[sv.TRANSFORMATION] = Transform.SphericalToRect
836
+ arglist[sv.RVAR] = dataset.variable(r).index
837
+ arglist[sv.THETAVAR] = dataset.variable(theta).index
838
+ arglist[sv.PSIVAR] = dataset.variable(psi).index
839
+ if all(v is None for v in (x, y, z)):
840
+ arglist[sv.CREATENEWVARIABLES] = True
841
+ arglist[sv.XVAR] = 1
842
+ arglist[sv.YVAR] = 1
843
+ arglist[sv.ZVAR] = 1
844
+ elif all(v is not None for v in (x, y, z)):
845
+ arglist[sv.CREATENEWVARIABLES] = False
846
+ arglist[sv.XVAR] = dataset.variable(x).index
847
+ arglist[sv.YVAR] = dataset.variable(y).index
848
+ arglist[sv.ZVAR] = dataset.variable(z).index
849
+ else:
850
+ msg = 'All destination variables must be specified or None.'
851
+ raise TecplotLogicError(msg)
852
+ arglist[sv.ANGLESPEC] = AngleUnits(angle_units)
853
+ with tecutil.optional(tecutil.IndexSet, zones) as zones:
854
+ arglist[sv.ZONELIST] = zones
855
+ if not _tecutil.TransformCoordinatesX(arglist):
856
+ raise TecplotSystemError()
857
+
858
+
859
+ @tecutil.lock()
860
+ def transform_rectangular_to_spherical(x, y, z, r=None, theta=None, psi=None,
861
+ angle_units=AngleUnits.Radians, zones=None):
862
+ """Transform all points from rectangular to spherical coordinates.
863
+
864
+ Parameters:
865
+ x (`Variable`): The rectangular input :math:`x` variable.
866
+ y (`Variable`): The rectangular input :math:`y` variable.
867
+ z (`Variable`): The rectangular input :math:`z` variable.
868
+ r (`Variable`, optional): The radial output :math:`r` variable. By
869
+ default, a new variable will be created. All of **r**, **theta**
870
+ and **psi** must be specified together as either existing variables
871
+ or as `None`.
872
+ theta (`Variable`, optional): The angular output :math:`theta` variable
873
+ from the :math:`x`-axis. By default, a new variable will be
874
+ created. All of **r**, **theta** and **psi** must be specified
875
+ together as either existing variables or as `None`.
876
+ psi (`Variable`, optional): The angular output :math:`psi` variable
877
+ from the :math:`z`-axis. By default, a new variable will be
878
+ created. All of **r**, **theta** and **psi** must be specified
879
+ together as either existing variables or as `None`.
880
+ angle_units (`AngleUnits`, optional): The units of the angular
881
+ variables. This may be either `AngleUnits.Radians` (default) or
882
+ `AngleUnits.Degrees`.
883
+ zones (`list` of `Zones <data_access>`, optional): Specific zones to
884
+ transform. By default, all zones are transformed.
885
+
886
+ This example will create three new variables in the dataset corresponding
887
+ to the :math:`(r, theta, psi)` equivalents of the :math:`(x, y, z)`
888
+ variables::
889
+
890
+ >>> dataset = tp.active_frame().dataset
891
+ >>> x = dataset.variable('X (m)')
892
+ >>> y = dataset.variable('Y (m)')
893
+ >>> z = dataset.variable('Z (m)')
894
+ >>> tp.data.operate.transform_rectangular_to_spherical(x, y, z)
895
+ >>> theta = dataset.variable(-3)
896
+ >>> r = dataset.variable(-2)
897
+ >>> psi = dataset.variable(-1)
898
+
899
+ .. warning:: **Source variables must have the same location as the
900
+ destination variables.**
901
+
902
+ The variables involved with this transformation must all be either
903
+ nodal or cell-centered.
904
+ """
905
+ dataset = _dataset((x, y, z, r, theta, psi), zones)
906
+ with tecutil.ArgList() as arglist:
907
+ arglist[sv.TRANSFORMATION] = Transform.RectToSpherical
908
+ arglist[sv.XVAR] = dataset.variable(x).index
909
+ arglist[sv.YVAR] = dataset.variable(y).index
910
+ arglist[sv.ZVAR] = dataset.variable(z).index
911
+ if all(v is None for v in (r, theta, psi)):
912
+ arglist[sv.CREATENEWVARIABLES] = True
913
+ arglist[sv.RVAR] = 1
914
+ arglist[sv.THETAVAR] = 1
915
+ arglist[sv.PSIVAR] = 1
916
+ elif all(v is not None for v in (r, theta, psi)):
917
+ arglist[sv.CREATENEWVARIABLES] = False
918
+ arglist[sv.RVAR] = dataset.variable(r).index
919
+ arglist[sv.THETAVAR] = dataset.variable(theta).index
920
+ arglist[sv.PSIVAR] = dataset.variable(psi).index
921
+ else:
922
+ msg = 'All destination variables must be specified or None.'
923
+ raise TecplotLogicError(msg)
924
+ arglist[sv.ANGLESPEC] = AngleUnits(angle_units)
925
+ with tecutil.optional(tecutil.IndexSet, zones) as zones:
926
+ arglist[sv.ZONELIST] = zones
927
+ if not _tecutil.TransformCoordinatesX(arglist):
928
+ raise TecplotSystemError()