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,577 @@
1
+ import itertools as it
2
+ import logging
3
+ import textwrap
4
+
5
+ from contextlib import contextmanager
6
+ from ctypes import addressof, cast, c_int32, c_int64, c_void_p, POINTER
7
+
8
+ from ..tecutil import _tecutil, _tecutil_connector
9
+ from ..constant import *
10
+ from ..exception import *
11
+ from .. import session
12
+ from ..tecutil import array_to_str, lock, lock_attributes, filled_slice
13
+
14
+ log = logging.getLogger(__name__)
15
+
16
+
17
+ class Elementmap(c_void_p):
18
+ """Nodemap reverse look-up."""
19
+ def __init__(self, zone):
20
+ self.zone = zone
21
+ super().__init__(self._native_reference())
22
+
23
+ @lock()
24
+ def _native_reference(self):
25
+ with self.zone.dataset.frame.activated():
26
+ return _tecutil.DataNodeToElemMapGetReadableRef(self.zone.index + 1)
27
+
28
+ def num_elements(self, node):
29
+ return _tecutil.DataNodeToElemMapGetNumElems(self, node + 1)
30
+
31
+ def element(self, node, offset):
32
+ return _tecutil.DataNodeToElemMapGetElem(self, node + 1, offset + 1) - 1
33
+
34
+
35
+ @lock_attributes
36
+ class Nodemap(c_void_p):
37
+ r"""Connectivity list definition and control for classic FE zones.
38
+
39
+ A nodemap holds the connectivity between nodes and elements for classic
40
+ finite-element zones. It is nominally a two-dimensionaly array of shape
41
+ :math:`(N_e, N_{npe})` where :math:`N_e` is the number of elements and
42
+ :math:`N_{npe}` is the number of nodes per element. The nodemap interface
43
+ has flat-array access through the `Nodemap.array` property as well as
44
+ reverse look-up with `Nodemap.num_elements()` and `Nodemap.element()`.
45
+
46
+ The nodemap behaves mostly like a two-dimensional array and can be treated
47
+ as such::
48
+
49
+ >>> nodemap = dataset.zone('My Zone').nodemap
50
+ >>> print('nodes in the first element:', nodemap[0])
51
+ nodes in the first element: [0, 1, 2, 3]
52
+ >>> print(nodemap[:3])
53
+ [[0, 1, 2, 3], [2, 3, 4, 5], [4, 5, 6, 7]]
54
+ >>> nodemap[0] = [6, 7, 8, 9]
55
+ >>> print(nodemap[0])
56
+ [6, 7, 8, 9]
57
+
58
+ Just for clarity, the nodemap indexing is by element first, then offset
59
+ within that element::
60
+
61
+ >>> element = 6
62
+ >>> offset = 2
63
+ >>> node = nodemap[element][offset]
64
+ >>> print(node)
65
+ 21
66
+
67
+ Setting node indices must be done for an entire element because getting
68
+ values out of the nodemap and into Python always creates a copy. For
69
+ example, **this will not work**::
70
+
71
+ >>> nodemap = dataset.zone('My Zone').nodemap
72
+ >>> # Trying to set the 3rd node of the element 10
73
+ >>> nodemap[10][2] = 5 # Error: nodemap[10] returns a copy
74
+
75
+ To modify a single node in a nodemap, it is neccessary to do a round trip
76
+ like so::
77
+
78
+ nodemap = dataset.zone('My Zone').nodemap
79
+ >>> nodes = nodemap[10]
80
+ >>> nodes[2] = 5
81
+ >>> nodemap[10] = nodes # OK: setting whole element at a time
82
+ >>> print(nodemap[10])
83
+ [20, 21, 5, 22]
84
+
85
+ The following script creates a quad of two triangles from scratch using the
86
+ PyTecplot low-level data creation interface. The general steps are:
87
+
88
+ 1. Setup the data
89
+ 2. Create the tecplot dataset and variables
90
+ 3. Create the zone
91
+ 4. Set the node locations and connectivity lists
92
+ 5. Set the (scalar) data
93
+ 6. Write out data file
94
+ 7. Adjust plot style and export image
95
+
96
+ The data created looks like this:
97
+
98
+ .. code-block:: none
99
+
100
+ Node positions (x,y,z):
101
+
102
+ (1,1,1)
103
+ 3
104
+ / \
105
+ / \
106
+ (0,1,.5) 2-----1 (1,0,.5)
107
+ \ /
108
+ \ /
109
+ 0
110
+ (0,0,0)
111
+
112
+ Breaking up the two triangular elements, the faces look like this. Notice the
113
+ first element (index: 0) is on the bottom:
114
+
115
+ .. code-block:: none
116
+
117
+ Element 1 Faces:
118
+ *
119
+ (nodes 3-2) 1 / \ 0 (nodes 1-3)
120
+ / \
121
+ *-----*
122
+ 2
123
+ (nodes 2-1)
124
+
125
+ Element 0 Faces:
126
+ (nodes 1-2)
127
+ 1
128
+ *-----*
129
+ \ /
130
+ (nodes 2-0) 2 \ / 0 (nodes 0-1)
131
+ *
132
+
133
+ The nodes are created as a list of :math:`(x, y, z)` positions::
134
+
135
+ [(x0, y0, z0), (x1, y1, z1)...]
136
+
137
+ which are transposed to lists of :math:`x`, :math:`y` and :math:`z`-positions::
138
+
139
+ [(x0, x1, x2...), (y0, y1, y2...)...]
140
+
141
+ and passed to the :math:`(x, y, z)` arrays. The nodemap, or connectivity list,
142
+ is given as an array of dimensions :math:`(N, D)` where :math:`N` is the number
143
+ of elements and :math:`D` is the number of nodes per element. The order of the
144
+ node locations determines the indices used when specifying the connectivity
145
+ list. The Nodemap can be set individually and separately or all at once as
146
+ shown here:
147
+
148
+ .. code-block:: python
149
+ :emphasize-lines: 39
150
+
151
+ import tecplot as tp
152
+ from tecplot.constant import *
153
+
154
+ # Triangle 0
155
+ nodes0 = (
156
+ (0, 0, 0 ),
157
+ (1, 0, 0.5),
158
+ (0, 1, 0.5))
159
+ scalar_data0 = (0, 1, 2)
160
+ conn0 = ((0, 1, 2),)
161
+ neighbors0 = ((None, 0, None),)
162
+ neighbor_zones0 = ((None, 1, None),)
163
+
164
+ # Triangle 1
165
+ nodes1 = (
166
+ (1, 0, 0.5),
167
+ (0, 1, 0.5),
168
+ (1, 1, 1 ))
169
+ scalar_data1 = (1, 2, 3)
170
+ conn1 = ((0, 1, 2),)
171
+ neighbors1 = ((0, None, None),)
172
+ neighbor_zones1 = ((0, None, None),)
173
+
174
+ # Create the dataset and zones
175
+ ds = tp.active_frame().create_dataset('Data', ['x','y','z','s'])
176
+ z0 = ds.add_fe_zone(ZoneType.FETriangle,
177
+ name='FE Triangle Float (3,1) Nodal 0',
178
+ num_points=len(nodes0), num_elements=len(conn0),
179
+ face_neighbor_mode=FaceNeighborMode.GlobalOneToOne)
180
+ z1 = ds.add_fe_zone(ZoneType.FETriangle,
181
+ name='FE Triangle Float (3,1) Nodal 1',
182
+ num_points=len(nodes1), num_elements=len(conn1),
183
+ face_neighbor_mode=FaceNeighborMode.GlobalOneToOne)
184
+
185
+ # Fill in and connect first triangle
186
+ z0.values('x')[:] = [n[0] for n in nodes0]
187
+ z0.values('y')[:] = [n[1] for n in nodes0]
188
+ z0.values('z')[:] = [n[2] for n in nodes0]
189
+ z0.nodemap[:] = conn0
190
+ z0.values('s')[:] = scalar_data0
191
+
192
+ # Fill in and connect second triangle
193
+ z1.values('x')[:] = [n[0] for n in nodes1]
194
+ z1.values('y')[:] = [n[1] for n in nodes1]
195
+ z1.values('z')[:] = [n[2] for n in nodes1]
196
+ z1.nodemap[:] = conn1
197
+ z1.values('s')[:] = scalar_data1
198
+
199
+ # Set face neighbors
200
+ z0.face_neighbors.set_neighbors(neighbors0, neighbor_zones0, obscures=True)
201
+ z1.face_neighbors.set_neighbors(neighbors1, neighbor_zones1, obscures=True)
202
+
203
+
204
+ ### Setup a view of the data
205
+ plot = tp.active_frame().plot(PlotType.Cartesian3D)
206
+ plot.activate()
207
+
208
+ plot.contour(0).colormap_name = 'Sequential - Yellow/Green/Blue'
209
+ plot.contour(0).colormap_filter.distribution = ColorMapDistribution.Continuous
210
+
211
+ for ax in plot.axes:
212
+ ax.show = True
213
+
214
+ plot.show_mesh = False
215
+ plot.show_contour = True
216
+ plot.show_edge = True
217
+ plot.use_translucency = True
218
+
219
+ # View parameters obtained interactively from Tecplot 360
220
+ plot.view.distance = 10
221
+ plot.view.width = 2
222
+ plot.view.psi = 80
223
+ plot.view.theta = 30
224
+ plot.view.alpha = 0
225
+ plot.view.position = (-4.2, -8.0, 2.3)
226
+
227
+ fmaps = plot.fieldmaps()
228
+ fmaps.surfaces.surfaces_to_plot = SurfacesToPlot.All
229
+ fmaps.effects.surface_translucency = 40
230
+
231
+ # Turning on mesh, we can see all the individual triangles
232
+ plot.show_mesh = True
233
+ fmaps.mesh.line_pattern = LinePattern.Dashed
234
+
235
+ plot.contour(0).levels.reset_to_nice()
236
+ tp.export.save_png('fe_triangles1.png', 600, supersample=3)
237
+
238
+ .. figure:: /_static/images/fe_triangles1.png
239
+ :width: 300px
240
+ :figwidth: 300px
241
+ """
242
+ def __init__(self, zone):
243
+ self.zone = zone
244
+ super().__init__(self._native_reference())
245
+
246
+ @lock()
247
+ def _native_reference(self, writable=False):
248
+ _dispatch = {
249
+ True: _tecutil.DataNodeGetWritableRef,
250
+ False: _tecutil.DataNodeGetReadableRef}
251
+ with self.zone.dataset.frame.activated():
252
+ return _dispatch[writable](self.zone.index + 1)
253
+
254
+ @lock()
255
+ def _raw_pointer(self, writable=False):
256
+ if _tecutil_connector.connected:
257
+ msg = 'raw pointer access only available in batch-mode'
258
+ raise TecplotLogicError(msg)
259
+ _dispatch = {
260
+ True: {
261
+ c_int32: _tecutil.DataNodeGetWritableRawPtrByRef,
262
+ c_int64: _tecutil.DataNodeGetWritableRawPtrByRef64},
263
+ False: {
264
+ c_int32: _tecutil.DataNodeGetReadableRawPtrByRef,
265
+ c_int64: _tecutil.DataNodeGetReadableRawPtrByRef64}
266
+ }
267
+ return _dispatch[writable][self.c_type](self)
268
+
269
+ def _raw_array(self, writable=False):
270
+ ptr = self._raw_pointer(writable)
271
+ return cast(ptr, POINTER(self.c_type * self.size)).contents
272
+
273
+ @property
274
+ def array(self):
275
+ r"""`list`-like array: Flattened array accessor for this nodemap.
276
+
277
+ The nodemap is normally dimensioned by :math:`(N_e, N_{npe})` where
278
+ :math:`N_e` is the number of elements and :math:`N_{npe}` is the number
279
+ of nodes per element. This property represents a flattened view into
280
+ the array which is of length :math:`N_e \times N_{npe}`. This may be
281
+ more convenient than flattening the array in your script using a
282
+ looping construct.
283
+
284
+ Standard Python list slicing works for both fetching values and
285
+ assignments. Example usage::
286
+
287
+ >>> nmap = dataset.zone('My Zone').nodemap
288
+ >>> nmap.array[:] = mydata
289
+ >>> print(nmap.array[:10])
290
+ [1, 10, 8, 0, 5, 18, 6, 12, 18, 11]
291
+ """
292
+ return NodemapArray(self.zone)
293
+
294
+ @lock()
295
+ def alloc(self):
296
+ """Allocates the internal space needed to store the Nodemap.
297
+
298
+ This method is used in conjunction with deferred nodemap creation and
299
+ is not needed with load-on-demand or normal zone creation methods.
300
+ """
301
+ with self.zone.dataset.frame.activated():
302
+ if not _tecutil.DataNodeAlloc(self.zone.index + 1):
303
+ raise TecplotSystemError()
304
+
305
+ def __eq__(self, other):
306
+ self_addr = addressof(cast(self, POINTER(c_int64)).contents)
307
+ other_addr = addressof(cast(other, POINTER(c_int64)).contents)
308
+ return self_addr == other_addr
309
+
310
+ def __ne__(self, other):
311
+ return not (self == other)
312
+
313
+ def __len__(self):
314
+ return self.zone.num_elements
315
+
316
+ def __iter__(self):
317
+ self._current_index = -1
318
+ self._current_length = len(self)
319
+ return self
320
+
321
+ def __next__(self):
322
+ self._current_index = self._current_index + 1
323
+ if self._current_index < self._current_length:
324
+ return self.__getitem__(self._current_index)
325
+ else:
326
+ del self._current_index
327
+ del self._current_length
328
+ raise StopIteration
329
+
330
+ def next(self): # if sys.version_info < (3,)
331
+ return self.__next__()
332
+
333
+ @contextmanager
334
+ def assignment(self):
335
+ """Context manager for assigning to the nodemap.
336
+
337
+ When setting values to the nodemap, a `state change` is emitted to the
338
+ engine after every statement. This can degrade performance if in the
339
+ script the nodemap is being set many times. This context provides a way
340
+ to suspend the state change notification until all assignments have
341
+ been completed. In the following example, the state change is emitted
342
+ only after the ``nodemap.assignment()`` context exits::
343
+
344
+ >>> nodemap = dataset.zone('My Zone').nodemap
345
+ >>> with nodemap.assignment():
346
+ ... for elem, nodes in enumerate(mydata):
347
+ ... nodemap[elem] = nodes
348
+ """
349
+ yield
350
+ session.connectivity_altered(self.zone)
351
+
352
+ @property
353
+ def shape(self):
354
+ r"""`tuple` of `integers <int>`: Shape of the nodemap array.
355
+
356
+ .. note:: This property is read-only.
357
+
358
+ This is defined by the zone type and is equal to :math:`(N_e, N_{npe})`
359
+ where :math:`N_e` is the number of elements and :math:`N_{npe}` is the
360
+ number of nodes per element. Example usage::
361
+
362
+ >>> print(dataset.zone(0).nodemap.shape)
363
+ (1024, 4)
364
+ """
365
+ return (self.zone.num_elements, self.num_points_per_element)
366
+
367
+ @property
368
+ def size(self):
369
+ r"""`int`: Total number of nodes stored in the nodemap array.
370
+
371
+ .. note:: This property is read-only.
372
+
373
+ This is defined by the zone type and is equal to :math:`N_e \times
374
+ N_{npe}` where :math:`N_e` is the number of elements and
375
+ :math:`N_{npe}` is the number of nodes per element. Example usage::
376
+
377
+ >>> print(dataset.zone(0).nodemap.shape)
378
+ (1024, 4)
379
+ >>> print(dataset.zone(0).nodemap.size)
380
+ 4096
381
+ """
382
+ return self.zone.num_elements * self.num_points_per_element
383
+
384
+ @property
385
+ def c_type(self):
386
+ """`ctypes.c_int32` or `ctypes.c_int64`: The underlying data type for this nodemap.
387
+
388
+ .. note:: This property is read-only.
389
+
390
+ This is the `ctypes` integer type used by the Tecplot Engine to store
391
+ the nodemap data. This is used internally and is not normally needed
392
+ for simple nodemap access.
393
+ """
394
+ _ctypes = {
395
+ OffsetDataType.OffsetDataType_32Bit: c_int32,
396
+ OffsetDataType.OffsetDataType_64Bit: c_int64}
397
+ data_type = _tecutil.DataNodeGetRawItemType(self)
398
+ return _ctypes[data_type]
399
+
400
+ @property
401
+ def num_points_per_element(self):
402
+ r"""`int`: Points per element for classic finite-element zones.
403
+
404
+ .. note:: This property is read-only.
405
+
406
+ The number of points (also known as nodes) per finite-element is
407
+ determined from the ``zone_type`` parameter. The following table shows
408
+ the number of points per element for the available zone types along
409
+ with the resulting shape of the nodemap based on the number of points
410
+ specified (:math:`N`):
411
+
412
+ ============== ============== ================
413
+ Zone Type Points/Element Nodemap Shape
414
+ ============== ============== ================
415
+ ``FELineSeg`` 2 :math:`(N, 2 N)`
416
+ ``FETriangle`` 3 :math:`(N, 3 N)`
417
+ ``FEQuad`` 4 :math:`(N, 4 N)`
418
+ ``FETetra`` 4 :math:`(N, 4 N)`
419
+ ``FEBrick`` 8 :math:`(N, 8 N)`
420
+ ============== ============== ================
421
+
422
+ Example usage::
423
+
424
+ >>> zone = dataset.zone('My Zone')
425
+ >>> print(zone.zone_type)
426
+ ZoneType.FETriangle
427
+ >>> print(zone.nodemap.num_points_per_element)
428
+ 3
429
+ """
430
+ return _tecutil.DataNodeGetNodesPerElem(self)
431
+
432
+ def __getitem__(self, i):
433
+ """Access the nodemap by elements.
434
+
435
+ Parameters:
436
+ i (`int` or `slice`): Element(s) to fetch from the
437
+ nodemap.
438
+ """
439
+ ppe = self.num_points_per_element
440
+ if isinstance(i, slice):
441
+ s = filled_slice(i, len(self))
442
+ nelems = s.stop - s.start
443
+ start = (s.start * ppe)
444
+ stop = (s.stop * ppe)
445
+ arr = self.array[start:stop]
446
+ elems = list(range(nelems))[::s.step]
447
+ return [arr[e * ppe:(e + 1) * ppe] for e in elems]
448
+ else:
449
+ return self.array[i * ppe:(i + 1) * ppe]
450
+
451
+ def __setitem__(self, i, nodes):
452
+ """Modify the nodemap by elements.
453
+
454
+ Parameters:
455
+ i (`int` or `slice`): Element(s) to modify in the
456
+ nodemap.
457
+ """
458
+ ppe = self.num_points_per_element
459
+ if isinstance(i, slice):
460
+ s = filled_slice(i, len(self))
461
+ if s.step == 1:
462
+ a = s.start * ppe
463
+ b = s.stop * ppe
464
+ self.array[a:b] = list(it.chain.from_iterable(nodes))
465
+ else:
466
+ with self.assignment():
467
+ for i, n in zip(range(s.start, s.stop, s.step), nodes):
468
+ self[i] = n
469
+ else:
470
+ self.array[i * ppe:(i + 1) * ppe] = nodes
471
+
472
+ def num_elements(self, node):
473
+ """The number of elements that use a given node.
474
+
475
+ Parameters:
476
+ node: (`int`): Zero-based index of a node.
477
+
478
+ Returns:
479
+ `int` - The number of elements that use this node.
480
+
481
+ Example usage::
482
+
483
+ >>> nodemap = dataset.zone('My Zone').nodemap
484
+ >>> nodemap.num_elements(3)
485
+ 8
486
+ """
487
+ return Elementmap(self.zone).num_elements(node)
488
+
489
+ def element(self, node, offset):
490
+ """The element containing a given node.
491
+
492
+ Parameters:
493
+ node (`int`): Zero-based index of a node.
494
+ offset (`int`): Zero-based index of the element that uses
495
+ the given node.
496
+
497
+ Returns:
498
+ `int` - Zero-based index of the element.
499
+
500
+ Example usage::
501
+
502
+ >>> nodemap = dataset.zone('My Zone').nodemap
503
+ >>> print(nodemap.element(3, 7))
504
+ 324
505
+ """
506
+ return Elementmap(self.zone).element(node, offset)
507
+
508
+
509
+ class NodemapArray(Nodemap):
510
+ def __len__(self):
511
+ return self.size
512
+
513
+ @property
514
+ def shape(self):
515
+ return (len(self),)
516
+
517
+ @lock()
518
+ def __getitem__(self, i):
519
+ """Access the underlying nodemap as 1D array."""
520
+ if isinstance(i, slice):
521
+ s = filled_slice(i, len(self))
522
+ n = s.stop - s.start
523
+ arr = (self.c_type * n)()
524
+ _tecutil.DataNodeArrayGetByRef(self, s.start + 1, n, arr)
525
+ for i in range(len(arr)):
526
+ arr[i] -= 1
527
+ return arr[::s.step]
528
+ else:
529
+ elem, node = divmod(i, self.num_points_per_element)
530
+ return _tecutil.DataNodeGetByRef(self, elem + 1, node + 1) - 1
531
+
532
+ @lock()
533
+ def __setitem__(self, i, nodes):
534
+ """Modify the underlying nodemap as 1D array."""
535
+ _dispatch = {
536
+ c_int32: _tecutil.DataNodeArraySetByRef,
537
+ c_int64: _tecutil.DataNodeArraySetByRef64}
538
+ data_ctype = self.c_type
539
+ ref = self._native_reference(writable=True)
540
+ with self.assignment():
541
+ if isinstance(i, slice):
542
+ s = filled_slice(i, len(self))
543
+ if s.step == 1:
544
+ n = s.stop - s.start
545
+ try:
546
+ import numpy as np
547
+ nparr = np.asarray(nodes, dtype=data_ctype) + 1
548
+ ptarr = nparr.ctypes.data_as(POINTER(data_ctype))
549
+ arr = (data_ctype * n).from_address(addressof(ptarr.contents))
550
+ except ImportError:
551
+ msg = textwrap.dedent('''\
552
+ Falling back to using basic Python for data operations.
553
+ If installed, PyTecplot will make use of Numpy where
554
+ appropriate for significant perfomance gains.
555
+ ''')
556
+ log.warning(msg)
557
+ arr = (data_ctype * n)(*[nd + 1 for nd in nodes])
558
+
559
+ if __debug__:
560
+ if min(arr) < 1 or self.zone.num_points < max(arr):
561
+ raise TecplotIndexError
562
+ log.debug(textwrap.dedent('''\
563
+ Nodemap Assignment:
564
+ offset: {}
565
+ array({}): {}''').format(s.start + 1, n,
566
+ array_to_str(arr)))
567
+ _dispatch[data_ctype](ref, s.start + 1, n, arr)
568
+ else:
569
+ ppe = self.num_points_per_element
570
+ for i, n in zip(range(s.start, s.stop, s.step), nodes):
571
+ elem, node = divmod(i, ppe)
572
+ _tecutil.DataNodeSetByRef(ref, elem + 1, node + 1,
573
+ n + 1)
574
+ else:
575
+ ppe = self.num_points_per_element
576
+ elem, node = divmod(i, ppe)
577
+ _tecutil.DataNodeSetByRef(ref, elem + 1, node + 1, nodes + 1)