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.
- pytecplot-1.5.2.dist-info/LICENSE.html +77 -0
- pytecplot-1.5.2.dist-info/METADATA +508 -0
- pytecplot-1.5.2.dist-info/RECORD +97 -0
- pytecplot-1.5.2.dist-info/WHEEL +5 -0
- pytecplot-1.5.2.dist-info/top_level.txt +1 -0
- tecplot/__init__.py +94 -0
- tecplot/annotation/__init__.py +7 -0
- tecplot/annotation/annotation.py +223 -0
- tecplot/annotation/geometry.py +369 -0
- tecplot/annotation/georeference.py +33 -0
- tecplot/annotation/image.py +185 -0
- tecplot/annotation/iter.py +105 -0
- tecplot/annotation/polyline.py +381 -0
- tecplot/annotation/text.py +744 -0
- tecplot/constant.py +401 -0
- tecplot/data/__init__.py +34 -0
- tecplot/data/array.py +645 -0
- tecplot/data/create.py +1 -0
- tecplot/data/dataset.py +1809 -0
- tecplot/data/extract.py +544 -0
- tecplot/data/face_neighbors.py +494 -0
- tecplot/data/facemap.py +814 -0
- tecplot/data/load.py +2075 -0
- tecplot/data/nodemap.py +577 -0
- tecplot/data/operate.py +928 -0
- tecplot/data/query.py +334 -0
- tecplot/data/save.py +343 -0
- tecplot/data/variable.py +280 -0
- tecplot/data/zone.py +841 -0
- tecplot/exception.py +201 -0
- tecplot/export/__init__.py +13 -0
- tecplot/export/animation.py +521 -0
- tecplot/export/export_setup.py +318 -0
- tecplot/export/image.py +577 -0
- tecplot/export/print_setup.py +218 -0
- tecplot/export/time_animation.py +607 -0
- tecplot/extension/__init__.py +14 -0
- tecplot/extension/ipython.py +68 -0
- tecplot/layout/__init__.py +20 -0
- tecplot/layout/frame.py +1285 -0
- tecplot/layout/layout.py +488 -0
- tecplot/layout/page.py +541 -0
- tecplot/legend/__init__.py +4 -0
- tecplot/legend/contour_legend.py +516 -0
- tecplot/legend/legend.py +141 -0
- tecplot/legend/line_legend.py +66 -0
- tecplot/legend/rgb_coloring_legend.py +291 -0
- tecplot/legend/scatter_legend.py +136 -0
- tecplot/macro.py +339 -0
- tecplot/plot/__init__.py +62 -0
- tecplot/plot/axes.py +881 -0
- tecplot/plot/axis.py +1350 -0
- tecplot/plot/blanking.py +584 -0
- tecplot/plot/contour.py +1302 -0
- tecplot/plot/effects.py +162 -0
- tecplot/plot/fieldmap.py +2159 -0
- tecplot/plot/grid.py +758 -0
- tecplot/plot/isosurface.py +1167 -0
- tecplot/plot/labels.py +338 -0
- tecplot/plot/linemap.py +1905 -0
- tecplot/plot/linking.py +489 -0
- tecplot/plot/plot.py +1791 -0
- tecplot/plot/rgb_coloring.py +281 -0
- tecplot/plot/scatter.py +396 -0
- tecplot/plot/slice.py +1544 -0
- tecplot/plot/streamtrace.py +1949 -0
- tecplot/plot/symbol.py +567 -0
- tecplot/plot/ticks.py +666 -0
- tecplot/plot/title.py +380 -0
- tecplot/plot/vector.py +679 -0
- tecplot/plot/view.py +868 -0
- tecplot/session/__init__.py +112 -0
- tecplot/session/aux_data.py +276 -0
- tecplot/session/config.py +92 -0
- tecplot/session/session.py +425 -0
- tecplot/session/state_changed.py +225 -0
- tecplot/session/style.py +504 -0
- tecplot/tecutil/__init__.py +16 -0
- tecplot/tecutil/arglist.py +230 -0
- tecplot/tecutil/captured_output.py +177 -0
- tecplot/tecutil/constant.py +1312 -0
- tecplot/tecutil/index_set.py +118 -0
- tecplot/tecutil/lock.py +36 -0
- tecplot/tecutil/message_pb2.py +39 -0
- tecplot/tecutil/preamble.py +54 -0
- tecplot/tecutil/stringlist.py +114 -0
- tecplot/tecutil/sv.py +1662 -0
- tecplot/tecutil/tecutil.py +12402 -0
- tecplot/tecutil/tecutil_client.py +627 -0
- tecplot/tecutil/tecutil_connector.py +937 -0
- tecplot/tecutil/tecutil_rpc.py +6845 -0
- tecplot/tecutil/util.py +332 -0
- tecplot/text/__init__.py +3 -0
- tecplot/text/font.py +224 -0
- tecplot/text/label_format.py +374 -0
- tecplot/text/textbox.py +106 -0
- tecplot/version.py +41 -0
tecplot/data/nodemap.py
ADDED
|
@@ -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)
|