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/facemap.py
ADDED
|
@@ -0,0 +1,814 @@
|
|
|
1
|
+
import itertools as it
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from collections import deque, namedtuple
|
|
5
|
+
from contextlib import contextmanager
|
|
6
|
+
from ctypes import c_int32, c_int64, c_void_p, POINTER, addressof, cast
|
|
7
|
+
from textwrap import dedent
|
|
8
|
+
|
|
9
|
+
from ..tecutil import _tecutil
|
|
10
|
+
from ..constant import *
|
|
11
|
+
from ..exception import *
|
|
12
|
+
from .. import session, tecutil, version
|
|
13
|
+
|
|
14
|
+
log = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@tecutil.lock_attributes
|
|
18
|
+
class Elementmap(c_void_p):
|
|
19
|
+
"""Facemap reverse look-up."""
|
|
20
|
+
def __init__(self, zone):
|
|
21
|
+
self.zone = zone
|
|
22
|
+
super().__init__(self._native_reference())
|
|
23
|
+
|
|
24
|
+
@tecutil.lock()
|
|
25
|
+
def _native_reference(self):
|
|
26
|
+
with self.zone.dataset.frame.activated():
|
|
27
|
+
return _tecutil.DataElemGetReadableRef(self.zone.index + 1)
|
|
28
|
+
|
|
29
|
+
def num_faces(self, element):
|
|
30
|
+
return _tecutil.DataElemGetNumFaces(self, element + 1)
|
|
31
|
+
|
|
32
|
+
def face(self, element, offset):
|
|
33
|
+
return _tecutil.DataElemGetFace(self, element + 1, offset + 1) - 1
|
|
34
|
+
|
|
35
|
+
def faces(self, element):
|
|
36
|
+
return [self.face(element, f) for f in range(self.num_faces(element))]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@tecutil.lock_attributes
|
|
40
|
+
class Facemap(c_void_p):
|
|
41
|
+
r"""Connectivity list definition and control.
|
|
42
|
+
|
|
43
|
+
A facemap holds the connectivity for a polytopal finite-element zone. This
|
|
44
|
+
includes node-to-element and element-to-element connections. The following
|
|
45
|
+
script creates a quad of two triangles from scratch using the PyTecplot
|
|
46
|
+
low-level data creation interface. The data created looks like this:
|
|
47
|
+
|
|
48
|
+
.. code-block:: none
|
|
49
|
+
|
|
50
|
+
Node positions (x,y,z):
|
|
51
|
+
|
|
52
|
+
(1,1,1)
|
|
53
|
+
3
|
|
54
|
+
/ \
|
|
55
|
+
/ \
|
|
56
|
+
(0,1,.5) 2-----1 (1,0,.5)
|
|
57
|
+
\ /
|
|
58
|
+
\ /
|
|
59
|
+
0
|
|
60
|
+
(0,0,0)
|
|
61
|
+
|
|
62
|
+
Element indices are used when identifying the left and right of each face,
|
|
63
|
+
where :math:`-1` is used to indicate no element:
|
|
64
|
+
|
|
65
|
+
.. code-block:: none
|
|
66
|
+
|
|
67
|
+
*
|
|
68
|
+
-1 / \ -1
|
|
69
|
+
/ 1 \
|
|
70
|
+
*-----*
|
|
71
|
+
\ 0 /
|
|
72
|
+
-1 \ / -1
|
|
73
|
+
*
|
|
74
|
+
|
|
75
|
+
.. code-block:: python
|
|
76
|
+
:emphasize-lines: 28
|
|
77
|
+
|
|
78
|
+
import tecplot as tp
|
|
79
|
+
from tecplot.constant import *
|
|
80
|
+
|
|
81
|
+
nodes = ((0, 0, 0 ),
|
|
82
|
+
(1, 0, 0.5),
|
|
83
|
+
(0, 1, 0.5),
|
|
84
|
+
(1, 1, 1 ))
|
|
85
|
+
faces = ((0, 1),
|
|
86
|
+
(1, 2),
|
|
87
|
+
(2, 0),
|
|
88
|
+
(1, 3),
|
|
89
|
+
(3, 2))
|
|
90
|
+
elements = (( 0, 0, 0, 1, 1), # elements to the left of each face
|
|
91
|
+
(-1, 1, -1, -1, -1)) # elements to the right of each face
|
|
92
|
+
num_elements = 2
|
|
93
|
+
scalar_data = (0, 1, 2, 3)
|
|
94
|
+
|
|
95
|
+
ds = tp.active_frame().create_dataset('Data', ['x','y','z','s'])
|
|
96
|
+
z = ds.add_poly_zone(ZoneType.FEPolygon,
|
|
97
|
+
name='FE Polygon Float (4,2,5) Nodal',
|
|
98
|
+
num_points=len(nodes),
|
|
99
|
+
num_elements=num_elements,
|
|
100
|
+
num_faces=len(faces))
|
|
101
|
+
|
|
102
|
+
z.values('x')[:] = [n[0] for n in nodes]
|
|
103
|
+
z.values('y')[:] = [n[1] for n in nodes]
|
|
104
|
+
z.values('z')[:] = [n[2] for n in nodes]
|
|
105
|
+
z.facemap.set_mapping(faces, elements)
|
|
106
|
+
z.values('s')[:] = scalar_data
|
|
107
|
+
|
|
108
|
+
### setup a view of the data
|
|
109
|
+
plot = tp.active_frame().plot(PlotType.Cartesian3D)
|
|
110
|
+
plot.activate()
|
|
111
|
+
|
|
112
|
+
cont = plot.contour(0)
|
|
113
|
+
cont.colormap_name = 'Sequential - Yellow/Green/Blue'
|
|
114
|
+
cont.colormap_filter.distribution = ColorMapDistribution.Continuous
|
|
115
|
+
|
|
116
|
+
for ax in plot.axes:
|
|
117
|
+
ax.show = True
|
|
118
|
+
|
|
119
|
+
plot.show_mesh = False
|
|
120
|
+
plot.show_contour = True
|
|
121
|
+
plot.show_edge = True
|
|
122
|
+
plot.use_translucency = True
|
|
123
|
+
|
|
124
|
+
fmap = plot.fieldmap(z)
|
|
125
|
+
fmap.surfaces.surfaces_to_plot = SurfacesToPlot.All
|
|
126
|
+
fmap.effects.surface_translucency = 40
|
|
127
|
+
|
|
128
|
+
# View parameters obtained interactively from Tecplot 360
|
|
129
|
+
plot.view.distance = 10
|
|
130
|
+
plot.view.width = 2
|
|
131
|
+
plot.view.psi = 80
|
|
132
|
+
plot.view.theta = 30
|
|
133
|
+
plot.view.alpha = 0
|
|
134
|
+
plot.view.position = (-4.2, -8.0, 2.3)
|
|
135
|
+
|
|
136
|
+
# Turning on mesh, we can see all the individual triangles
|
|
137
|
+
plot.show_mesh = True
|
|
138
|
+
plot.fieldmap(z).mesh.line_pattern = LinePattern.Dashed
|
|
139
|
+
|
|
140
|
+
cont.levels.reset_to_nice()
|
|
141
|
+
tp.export.save_png('polygons1.png', 600, supersample=3)
|
|
142
|
+
|
|
143
|
+
.. figure:: /_static/images/polygons1.png
|
|
144
|
+
:width: 300px
|
|
145
|
+
:figwidth: 300px
|
|
146
|
+
|
|
147
|
+
Two triangle polygons showing edge and mesh lines.
|
|
148
|
+
"""
|
|
149
|
+
def __init__(self, zone):
|
|
150
|
+
self.zone = zone
|
|
151
|
+
if self.zone.num_faces > 0:
|
|
152
|
+
try:
|
|
153
|
+
ref = self._native_reference()
|
|
154
|
+
except:
|
|
155
|
+
ref = None
|
|
156
|
+
else:
|
|
157
|
+
ref = None
|
|
158
|
+
super().__init__(ref)
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def _has_data_backing(self):
|
|
162
|
+
return bool(self)
|
|
163
|
+
|
|
164
|
+
@tecutil.lock()
|
|
165
|
+
def _native_reference(self, writable=False):
|
|
166
|
+
try:
|
|
167
|
+
_dispatch = {
|
|
168
|
+
True: _tecutil.DataFaceMapGetWritableRef,
|
|
169
|
+
False: _tecutil.DataFaceMapGetReadableRef}
|
|
170
|
+
with self.zone.dataset.frame.activated():
|
|
171
|
+
return _dispatch[writable](self.zone.index + 1)
|
|
172
|
+
except TecplotLogicError:
|
|
173
|
+
raise TecplotLogicError(
|
|
174
|
+
'A facemap must first be created with Facemap.alloc()')
|
|
175
|
+
|
|
176
|
+
@tecutil.lock()
|
|
177
|
+
def alloc(self, face_nodes, boundary_faces=0, boundary_connections=0):
|
|
178
|
+
"""Allocate space for the facemap.
|
|
179
|
+
|
|
180
|
+
Parameters:
|
|
181
|
+
face_nodes (`int`): Total number of nodes for all faces. This is
|
|
182
|
+
not the number of unique nodes but the total number. For
|
|
183
|
+
example if a facemap defines two triangle polygons that share a
|
|
184
|
+
common face, ``faces`` would be 5 and ``face_nodes`` would be
|
|
185
|
+
6, not 4.
|
|
186
|
+
boundary_faces (`int`, optional): Total number of boundary faces.
|
|
187
|
+
(default: 0)
|
|
188
|
+
boundary_connections (`int`, optional): Total number of boundary
|
|
189
|
+
face elements or boundary face element/zone pairs. (default: 0)
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
`Facemap`
|
|
193
|
+
|
|
194
|
+
This is called when using the `Facemap.set_mapping()` method which is
|
|
195
|
+
the preferred method for filling the connectivity of polytope zones. If
|
|
196
|
+
the zone does not already have space allocated for a facemap and if you
|
|
197
|
+
wish to use the `Facemap.set_nodes()` and `Facemap.set_elements()`
|
|
198
|
+
methods to fill in the connectivity, then this must be called first::
|
|
199
|
+
|
|
200
|
+
>>> facemap = zone.facemap.alloc(400, 25, 50)
|
|
201
|
+
|
|
202
|
+
.. note:: Tecplot version 2017.2 or later.
|
|
203
|
+
|
|
204
|
+
Setting the boundary faces and boundary connections using PyTecplot
|
|
205
|
+
requires Tecplot version 2017.2 or later.
|
|
206
|
+
"""
|
|
207
|
+
if not _tecutil.DataFaceMapAlloc(self.zone.index + 1,
|
|
208
|
+
self.zone.num_faces, face_nodes,
|
|
209
|
+
boundary_faces, boundary_connections):
|
|
210
|
+
raise TecplotSystemError()
|
|
211
|
+
c_void_p.__init__(self, self._native_reference())
|
|
212
|
+
|
|
213
|
+
def __eq__(self, other):
|
|
214
|
+
if not (self._has_data_backing and other._has_data_backing):
|
|
215
|
+
return False
|
|
216
|
+
self_addr = addressof(cast(self, POINTER(c_int64)).contents)
|
|
217
|
+
other_addr = addressof(cast(other, POINTER(c_int64)).contents)
|
|
218
|
+
return self_addr == other_addr
|
|
219
|
+
|
|
220
|
+
def __ne__(self, other):
|
|
221
|
+
return not (self == other)
|
|
222
|
+
|
|
223
|
+
def __len__(self):
|
|
224
|
+
return self.num_faces()
|
|
225
|
+
|
|
226
|
+
def num_faces(self, element=None):
|
|
227
|
+
"""The number of faces of an element in this `Facemap`.
|
|
228
|
+
|
|
229
|
+
Parameters:
|
|
230
|
+
element (`int`, optional): Zero-based index of an
|
|
231
|
+
element. If no element is given, the total number of faces
|
|
232
|
+
in the map are returned.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
`int`
|
|
236
|
+
|
|
237
|
+
Example usage::
|
|
238
|
+
|
|
239
|
+
>>> print(zone.facemap.num_faces())
|
|
240
|
+
1048576
|
|
241
|
+
"""
|
|
242
|
+
if element is None:
|
|
243
|
+
if not self._has_data_backing:
|
|
244
|
+
return self.zone.num_faces
|
|
245
|
+
return _tecutil.DataFaceMapGetNFaces(self)
|
|
246
|
+
else:
|
|
247
|
+
return Elementmap(self.zone).num_faces(element)
|
|
248
|
+
|
|
249
|
+
@property
|
|
250
|
+
def num_unique_nodes(self):
|
|
251
|
+
"""`int`: The number of unique nodes in this `Facemap`.
|
|
252
|
+
|
|
253
|
+
.. note:: This property is read-only.
|
|
254
|
+
|
|
255
|
+
Example usage::
|
|
256
|
+
|
|
257
|
+
>>> print(zone.facemap.num_unique_nodes)
|
|
258
|
+
4194304
|
|
259
|
+
"""
|
|
260
|
+
if not self._has_data_backing:
|
|
261
|
+
return self.zone.num_points
|
|
262
|
+
return _tecutil.DataFaceMapGetNumNodes(self)
|
|
263
|
+
|
|
264
|
+
def num_nodes(self, face=None, element=None):
|
|
265
|
+
"""The number nodes for a given face in this `Facemap`.
|
|
266
|
+
|
|
267
|
+
Parameters:
|
|
268
|
+
face (`int`, required if no *element* is given): The
|
|
269
|
+
zero-based index of the face either within the given *element*
|
|
270
|
+
or globally. If no face is given, the number of nodes on the
|
|
271
|
+
given *element* are returned.
|
|
272
|
+
element (`int`, required if no face is given): The
|
|
273
|
+
zero-based index of an element. If no element is given, then
|
|
274
|
+
*face* is globally indexed from zero over the whole zone.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
`int` The number of nodes.
|
|
278
|
+
|
|
279
|
+
Example usage::
|
|
280
|
+
|
|
281
|
+
>>> print(zone.facemap.num_nodes(face=1))
|
|
282
|
+
4
|
|
283
|
+
"""
|
|
284
|
+
if element is None:
|
|
285
|
+
if face is None:
|
|
286
|
+
msg = 'face or element must be specified'
|
|
287
|
+
raise TecplotValueError(msg)
|
|
288
|
+
# While the following sum() is technically correct, it is
|
|
289
|
+
# extremely slow as currently, there is no way to query
|
|
290
|
+
# the total number of nodes in the connectivity list directly.
|
|
291
|
+
#
|
|
292
|
+
# return sum(self.num_nodes(i) for i in range(self.num_faces()))
|
|
293
|
+
else:
|
|
294
|
+
if self.zone.zone_type == ZoneType.FEPolygon:
|
|
295
|
+
return 2
|
|
296
|
+
else:
|
|
297
|
+
return _tecutil.DataFaceMapGetNFaceNodes(self, face + 1)
|
|
298
|
+
else:
|
|
299
|
+
emap = Elementmap(self.zone)
|
|
300
|
+
if face is None:
|
|
301
|
+
return sum(self.num_nodes(f) for f in emap.faces(element))
|
|
302
|
+
else:
|
|
303
|
+
return self.num_nodes(emap.face(element, face))
|
|
304
|
+
|
|
305
|
+
def num_boundary_connections(self, face=None, element=None):
|
|
306
|
+
"""The number of boundary connections for a given face.
|
|
307
|
+
|
|
308
|
+
Parameters:
|
|
309
|
+
face (`int`, required if no element is given): The
|
|
310
|
+
zero-based index of the face.
|
|
311
|
+
element (`int`, optional): Zero-based index of an
|
|
312
|
+
element. If given, *face* will be locally indexed within this
|
|
313
|
+
element, otherwise *face* is globally indexed over the whole
|
|
314
|
+
zone.
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
`int` The number of boundary connections.
|
|
318
|
+
|
|
319
|
+
Example usage::
|
|
320
|
+
|
|
321
|
+
>>> print(zone.facemap.num_boundary_connections(1))
|
|
322
|
+
1
|
|
323
|
+
"""
|
|
324
|
+
if face is None:
|
|
325
|
+
if element is None:
|
|
326
|
+
msg = 'face or element must be specified'
|
|
327
|
+
raise TecplotValueError(msg)
|
|
328
|
+
# While the following sum() is technically correct using
|
|
329
|
+
# the face indices here, it is extremely slow as currently,
|
|
330
|
+
# there is no way to query the total number of boundary
|
|
331
|
+
# connections directly.
|
|
332
|
+
#
|
|
333
|
+
# faces = range(self.num_faces())
|
|
334
|
+
else:
|
|
335
|
+
emap = Elementmap(self.zone)
|
|
336
|
+
faces = emap.faces(element)
|
|
337
|
+
return sum(self.num_boundary_connections(f) for f in faces)
|
|
338
|
+
elif element is not None:
|
|
339
|
+
face = self.face(element, face)
|
|
340
|
+
return _tecutil.DataFaceMapGetNBndryConns(self, face + 1)
|
|
341
|
+
|
|
342
|
+
_BoundaryConnection = namedtuple('BoundaryConnection', ['element', 'zone'])
|
|
343
|
+
|
|
344
|
+
def boundary_connection(self, face, offset, element=None):
|
|
345
|
+
"""The connected element and zone along a boundary face.
|
|
346
|
+
|
|
347
|
+
Parameters:
|
|
348
|
+
face (`int`): The zero-based index of the face.
|
|
349
|
+
offset (`int`): The zero-based index of the node being
|
|
350
|
+
requested.
|
|
351
|
+
element (`int`, optional): Zero-based index of an
|
|
352
|
+
element. If given, *face* will be locally indexed within this
|
|
353
|
+
element, otherwise *face* is globally indexed over the whole
|
|
354
|
+
zone.
|
|
355
|
+
|
|
356
|
+
Returns:
|
|
357
|
+
`namedtuple <collections.namedtuple>`: ``(element, zone)``:
|
|
358
|
+
``element``
|
|
359
|
+
The zero-based index of the neighboring element.
|
|
360
|
+
``zone``
|
|
361
|
+
The zone holding the neighboring element.
|
|
362
|
+
|
|
363
|
+
Example usage::
|
|
364
|
+
|
|
365
|
+
>>> bconn = zone.facemap.boundary_connection(0, 2)
|
|
366
|
+
>>> print(bconn.element)
|
|
367
|
+
128
|
|
368
|
+
>>> print(bconn.zone.index)
|
|
369
|
+
2
|
|
370
|
+
"""
|
|
371
|
+
if element is not None:
|
|
372
|
+
face = self.face(element, face)
|
|
373
|
+
elem, zone = _tecutil.DataFaceMapGetBndryConn(self, face + 1,
|
|
374
|
+
offset + 1)
|
|
375
|
+
return Facemap._BoundaryConnection(elem - 1,
|
|
376
|
+
self.zone.dataset.zone(zone - 1))
|
|
377
|
+
|
|
378
|
+
def node(self, face, offset, element=None):
|
|
379
|
+
"""The node index along a specific face.
|
|
380
|
+
|
|
381
|
+
Parameters:
|
|
382
|
+
face (`int`): The zero-based index of the face.
|
|
383
|
+
offset (`int`): The zero-based index of the node being
|
|
384
|
+
requested.
|
|
385
|
+
element (`int`, optional): Zero-based index of an
|
|
386
|
+
element. If given, *face* will be locally indexed within this
|
|
387
|
+
element, otherwise *face* is globally indexed over the whole
|
|
388
|
+
zone.
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
`int` The node at the specified location.
|
|
392
|
+
|
|
393
|
+
Example usage::
|
|
394
|
+
|
|
395
|
+
>>> print(zone.facemap.face_node(0, 2))
|
|
396
|
+
128
|
|
397
|
+
"""
|
|
398
|
+
if element is not None:
|
|
399
|
+
face = self.face(element, face)
|
|
400
|
+
return _tecutil.DataFaceMapGetFaceNode(self, face + 1, offset + 1) - 1
|
|
401
|
+
|
|
402
|
+
def left_element(self, face, element=None):
|
|
403
|
+
"""The element to the left of a specific face.
|
|
404
|
+
|
|
405
|
+
Parameters:
|
|
406
|
+
face (`int`): The zero-based index of the face.
|
|
407
|
+
element (`int`, optional): Zero-based index of an
|
|
408
|
+
element. If given, *face* will be locally indexed within this
|
|
409
|
+
element, otherwise *face* is globally indexed over the whole
|
|
410
|
+
zone.
|
|
411
|
+
|
|
412
|
+
Returns:
|
|
413
|
+
`int`
|
|
414
|
+
|
|
415
|
+
A negative number indicates there is no element to the left of this
|
|
416
|
+
face. Example usage::
|
|
417
|
+
|
|
418
|
+
>>> print(zone.facemap.left_element(0))
|
|
419
|
+
128
|
|
420
|
+
"""
|
|
421
|
+
if element is not None:
|
|
422
|
+
face = self.face(element, face)
|
|
423
|
+
return _tecutil.DataFaceMapGetLeftElem(self, face + 1) - 1
|
|
424
|
+
|
|
425
|
+
def right_element(self, face, element=None):
|
|
426
|
+
"""The element to the right of a specific face.
|
|
427
|
+
|
|
428
|
+
Parameters:
|
|
429
|
+
face (`int`): The zero-based index of the face.
|
|
430
|
+
element (`int`, optional): Zero-based index of an
|
|
431
|
+
element. If given, *face* will be locally indexed within this
|
|
432
|
+
element, otherwise *face* is globally indexed over the whole
|
|
433
|
+
zone.
|
|
434
|
+
|
|
435
|
+
Returns:
|
|
436
|
+
`int`
|
|
437
|
+
|
|
438
|
+
A negative number indicates there is no element to the right of this
|
|
439
|
+
face. Example usage::
|
|
440
|
+
|
|
441
|
+
>>> print(zone.facemap.right_element(0))
|
|
442
|
+
-1
|
|
443
|
+
"""
|
|
444
|
+
if element is not None:
|
|
445
|
+
face = self.face(element, face)
|
|
446
|
+
return _tecutil.DataFaceMapGetRightElem(self, face + 1) - 1
|
|
447
|
+
|
|
448
|
+
def face(self, element, offset):
|
|
449
|
+
"""Face index on a specific element.
|
|
450
|
+
|
|
451
|
+
Parameters:
|
|
452
|
+
element (`int`): The zero-based index of the element.
|
|
453
|
+
offset (`int`): The zero-based index of the face being
|
|
454
|
+
requested.
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
`int` Zero-based index of the face at the specified
|
|
458
|
+
location.
|
|
459
|
+
|
|
460
|
+
Example usage::
|
|
461
|
+
|
|
462
|
+
>>> print(zone.facemap.face(0, 2))
|
|
463
|
+
128
|
|
464
|
+
"""
|
|
465
|
+
return Elementmap(self.zone).face(element, offset)
|
|
466
|
+
|
|
467
|
+
@property
|
|
468
|
+
def node_c_type(self):
|
|
469
|
+
"""The data type of the node indices.
|
|
470
|
+
|
|
471
|
+
Possible values: `ctypes.c_int32`, `ctypes.c_int64`
|
|
472
|
+
|
|
473
|
+
.. note:: This property is read-only.
|
|
474
|
+
"""
|
|
475
|
+
_ctypes = {
|
|
476
|
+
OffsetDataType.OffsetDataType_32Bit: c_int32,
|
|
477
|
+
OffsetDataType.OffsetDataType_64Bit: c_int64}
|
|
478
|
+
if self._has_data_backing:
|
|
479
|
+
data_type = _tecutil.DataFaceMapGetNodeRawItemType(self)
|
|
480
|
+
return _ctypes[data_type]
|
|
481
|
+
|
|
482
|
+
@property
|
|
483
|
+
def element_c_type(self):
|
|
484
|
+
"""The data type of the element indices.
|
|
485
|
+
|
|
486
|
+
Possible values: `ctypes.c_int32`, `ctypes.c_int64`
|
|
487
|
+
|
|
488
|
+
.. note:: This property is read-only.
|
|
489
|
+
"""
|
|
490
|
+
_ctypes = {
|
|
491
|
+
OffsetDataType.OffsetDataType_32Bit: c_int32,
|
|
492
|
+
OffsetDataType.OffsetDataType_64Bit: c_int64}
|
|
493
|
+
if self._has_data_backing:
|
|
494
|
+
ref = self._native_reference(writable=True)
|
|
495
|
+
data_type = _tecutil.DataFaceMapGetElementRawItemType(ref)
|
|
496
|
+
return _ctypes[data_type]
|
|
497
|
+
|
|
498
|
+
def set_nodes(self, facemap):
|
|
499
|
+
"""Sets the polytope connectivity.
|
|
500
|
+
|
|
501
|
+
Parameters:
|
|
502
|
+
facemap (2D array of zero-based `integers <int>`): The `list` of
|
|
503
|
+
`lists <list>` which need not all be the same length, defining
|
|
504
|
+
the individual elements by their nodes.
|
|
505
|
+
|
|
506
|
+
The facemap must first be allocated with `Facemap.alloc()` and must be
|
|
507
|
+
called from within a `Facemap.assignment()` context and should be
|
|
508
|
+
followed by a call to `Facemap.set_elements()` to complete the
|
|
509
|
+
connectivity map information needed for rendering. It is recomended to
|
|
510
|
+
use the `Facemap.set_mapping()` which does all the required
|
|
511
|
+
book keeping.
|
|
512
|
+
"""
|
|
513
|
+
_dispatch = {
|
|
514
|
+
c_int32: _tecutil.DataFaceMapAssignNodes,
|
|
515
|
+
c_int64: _tecutil.DataFaceMapAssignNodes64}
|
|
516
|
+
data_type = self.node_c_type
|
|
517
|
+
nfaces = len(facemap)
|
|
518
|
+
nnodes = [len(n) for n in facemap]
|
|
519
|
+
nfacenodes = sum(nnodes)
|
|
520
|
+
if self.zone.zone_type is ZoneType.FEPolygon:
|
|
521
|
+
faces = None
|
|
522
|
+
else:
|
|
523
|
+
faces = (c_int32 * nfaces)(*nnodes)
|
|
524
|
+
nodes = (data_type * nfacenodes)(*[n + 1 for n in it.chain(*facemap)])
|
|
525
|
+
|
|
526
|
+
if __debug__:
|
|
527
|
+
if min(nodes) < 1 or self.num_unique_nodes < max(nodes):
|
|
528
|
+
raise TecplotIndexError
|
|
529
|
+
log.debug(dedent('''\
|
|
530
|
+
Facemap set nodes:
|
|
531
|
+
faces({}): {}
|
|
532
|
+
nodes: {}''').format(nfaces, tecutil.array_to_str(faces),
|
|
533
|
+
tecutil.array_to_str(nodes)))
|
|
534
|
+
|
|
535
|
+
ref = self._native_reference(writable=True)
|
|
536
|
+
_dispatch[data_type](ref, nfaces, faces, nodes)
|
|
537
|
+
|
|
538
|
+
def set_elements(self, left_elements, right_elements):
|
|
539
|
+
"""Sets the polytope connectivity.
|
|
540
|
+
|
|
541
|
+
Parameters:
|
|
542
|
+
left_elements (array of zero-based `integers <int>`): This is an
|
|
543
|
+
array of the elements to the left of each face in the facemap
|
|
544
|
+
and must be the same length as the number of faces.
|
|
545
|
+
right_elements (array of zero-based `integers <int>`): Same as
|
|
546
|
+
*left_elements* for the right side of each face.
|
|
547
|
+
|
|
548
|
+
The facemap must first be allocated with `Facemap.alloc()` and must be
|
|
549
|
+
called from within a `Facemap.assignment()` context and should follow a
|
|
550
|
+
call to `Facemap.set_nodes()` to complete the connectivity map
|
|
551
|
+
information needed for rendering. Using `Facemap.set_mapping()` is
|
|
552
|
+
recommended, which does all the required book keeping.
|
|
553
|
+
"""
|
|
554
|
+
_dispatch = {
|
|
555
|
+
c_int32: _tecutil.DataFaceMapAssignElems,
|
|
556
|
+
c_int64: _tecutil.DataFaceMapAssignElems64}
|
|
557
|
+
data_type = self.element_c_type
|
|
558
|
+
nelements = len(left_elements)
|
|
559
|
+
left = (data_type * nelements)(*[e + 1 for e in left_elements])
|
|
560
|
+
right = (data_type * nelements)(*[e + 1 for e in right_elements])
|
|
561
|
+
|
|
562
|
+
if __debug__:
|
|
563
|
+
if self.zone.num_elements < max(left):
|
|
564
|
+
raise TecplotIndexError
|
|
565
|
+
if self.zone.num_elements < max(right):
|
|
566
|
+
raise TecplotIndexError
|
|
567
|
+
log.debug(dedent('''\
|
|
568
|
+
Facemap set elements:
|
|
569
|
+
left({}): {}
|
|
570
|
+
right({}): {}'''
|
|
571
|
+
).format(len(left), tecutil.array_to_str(left),
|
|
572
|
+
len(right), tecutil.array_to_str(right)))
|
|
573
|
+
|
|
574
|
+
ref = self._native_reference(writable=True)
|
|
575
|
+
_dispatch[data_type](ref, nelements, left, right)
|
|
576
|
+
|
|
577
|
+
def set_boundary_connections(self, elements, zones):
|
|
578
|
+
"""Set the boundary connections.
|
|
579
|
+
|
|
580
|
+
Parameters:
|
|
581
|
+
elements (2D array of `integers <int>`): Zero-based indices of the
|
|
582
|
+
connected elements. This is a "ragged" array of dimension
|
|
583
|
+
:math:`(N,E_i)` where :math:`N` is the number of boundary faces
|
|
584
|
+
and :math:`E_i` is the number of boundary connected elements
|
|
585
|
+
for the :math:`i^{th}` face.
|
|
586
|
+
zones (2D array of `integers <int>`): Zero-based indices of the
|
|
587
|
+
zones for each entry given in *elements*. This must be the same
|
|
588
|
+
shape as *elements*.
|
|
589
|
+
|
|
590
|
+
The facemap must first be allocated with `Facemap.alloc()` and must be
|
|
591
|
+
called from within a `Facemap.assignment()` context, and should follow
|
|
592
|
+
calls to `Facemap.set_nodes()` and `Facemap.set_elements()` to complete
|
|
593
|
+
the connectivity map information needed for rendering. Using
|
|
594
|
+
`Facemap.set_mapping()` is recommended, which does all the required
|
|
595
|
+
book keeping.
|
|
596
|
+
"""
|
|
597
|
+
_dispatch = {
|
|
598
|
+
c_int32: _tecutil.DataFaceMapAssignBConns,
|
|
599
|
+
c_int64: _tecutil.DataFaceMapAssignBConns64}
|
|
600
|
+
data_type = self.node_c_type
|
|
601
|
+
nbfaces = len(elements)
|
|
602
|
+
nbconns = (c_int32 * nbfaces)(*[len(n) for n in elements])
|
|
603
|
+
nbelems = sum(nbconns)
|
|
604
|
+
|
|
605
|
+
def _flatten_add_one(arr):
|
|
606
|
+
def _f(x):
|
|
607
|
+
return x + 1 if x is not None else 0
|
|
608
|
+
return list(map(_f, it.chain(*arr)))
|
|
609
|
+
|
|
610
|
+
def _flatten_add_one_index(arr):
|
|
611
|
+
def _f(x):
|
|
612
|
+
return getattr(x, 'index', x) + 1 if x is not None else 0
|
|
613
|
+
return list(map(_f, it.chain(*arr)))
|
|
614
|
+
|
|
615
|
+
belems = (data_type * nbelems)(*_flatten_add_one(elements))
|
|
616
|
+
bzones = (c_int32 * nbelems)(*_flatten_add_one_index(zones))
|
|
617
|
+
|
|
618
|
+
if __debug__:
|
|
619
|
+
if min(belems) < 1 or self.zone.num_elements < max(belems):
|
|
620
|
+
raise TecplotIndexError
|
|
621
|
+
if min(bzones) < 1 or self.zone.dataset.num_zones < max(bzones):
|
|
622
|
+
raise TecplotIndexError
|
|
623
|
+
log.debug(dedent('''\
|
|
624
|
+
Facemap set boundary connections:
|
|
625
|
+
num boundary faces: {}
|
|
626
|
+
num boundary conns: {}
|
|
627
|
+
boundary elems: {}
|
|
628
|
+
boundary zones: {}'''.format(nbfaces,
|
|
629
|
+
tecutil.array_to_str(nbconns),
|
|
630
|
+
tecutil.array_to_str(belems),
|
|
631
|
+
tecutil.array_to_str(bzones))))
|
|
632
|
+
|
|
633
|
+
if version.sdk_version_info >= (2021, 2):
|
|
634
|
+
# The TecUtil API 2021.2+ requires boundary zone unique IDs.
|
|
635
|
+
bzone_uids = (c_int64 * nbelems)()
|
|
636
|
+
_tecutil.DataSetGetUniqueIDsForZones(bzones, nbelems, bzone_uids)
|
|
637
|
+
bzones = bzone_uids
|
|
638
|
+
|
|
639
|
+
ref = self._native_reference(writable=True)
|
|
640
|
+
_dispatch[data_type](ref, nbfaces, nbconns, belems, bzones)
|
|
641
|
+
|
|
642
|
+
def set_mapping(self, facemap, elements, boundary_elements=None,
|
|
643
|
+
boundary_zones=None):
|
|
644
|
+
"""Set the node and element connectivity for this polytope zone.
|
|
645
|
+
|
|
646
|
+
Parameters:
|
|
647
|
+
facemap (2D array of zero-based `integers <int>`): The `list` of
|
|
648
|
+
`lists <list>` which need not all be the same length, defining
|
|
649
|
+
the individual elements by their nodes.
|
|
650
|
+
elements (2D array of zero-based `integers <int>`): This is a
|
|
651
|
+
:math:`(2,N)` array where :math:`N` is the number of faces and
|
|
652
|
+
the items are a list of the elements to the left and right of
|
|
653
|
+
the face respectively.
|
|
654
|
+
boundary_elements (2D array of `integers <int>`, optional):
|
|
655
|
+
Zero-based indices of the connected elements. This is a
|
|
656
|
+
"ragged" array of dimension :math:`(N, E_i)` where :math:`N` is
|
|
657
|
+
the number of boundary faces and :math:`E_i` is the number of
|
|
658
|
+
boundary connected elements for the :math:`i^{th}` face.
|
|
659
|
+
boundary_zones (2D array of `integers <int>`, optional): Zero-based
|
|
660
|
+
indices of the zones for each entry given in *elements*. This
|
|
661
|
+
must be the same shape as *elements*.
|
|
662
|
+
|
|
663
|
+
See the code example for the `Facemap` class object for details.
|
|
664
|
+
|
|
665
|
+
.. note:: **boundary_elements** and **boundary_zones**
|
|
666
|
+
|
|
667
|
+
The two parameters, **boundary_elements** and **boundary_zones**,
|
|
668
|
+
must be supplied together or not at all.
|
|
669
|
+
"""
|
|
670
|
+
nfacenodes = sum(len(n) for n in facemap)
|
|
671
|
+
if boundary_elements is not None:
|
|
672
|
+
nbfaces = len(boundary_elements)
|
|
673
|
+
nbelems = sum(len(n) for n in boundary_elements)
|
|
674
|
+
else:
|
|
675
|
+
nbfaces = 0
|
|
676
|
+
nbelems = 0
|
|
677
|
+
|
|
678
|
+
if __debug__:
|
|
679
|
+
log.debug(dedent('''\
|
|
680
|
+
Facemap alloc:
|
|
681
|
+
num face nodes: {}
|
|
682
|
+
num boundary faces: {}
|
|
683
|
+
num boundary elements: {}'''.format(nfacenodes, nbfaces,
|
|
684
|
+
nbelems)))
|
|
685
|
+
|
|
686
|
+
self.alloc(nfacenodes, nbfaces, nbelems)
|
|
687
|
+
with self.assignment():
|
|
688
|
+
self.set_nodes(facemap)
|
|
689
|
+
self.set_elements(*elements)
|
|
690
|
+
if boundary_elements is not None:
|
|
691
|
+
self.set_boundary_connections(boundary_elements, boundary_zones)
|
|
692
|
+
|
|
693
|
+
@contextmanager
|
|
694
|
+
def assignment(self):
|
|
695
|
+
"""Context manager for assigning facemap connections.
|
|
696
|
+
|
|
697
|
+
This context ensures the proper book keeping is done when setting the
|
|
698
|
+
connectivity list and must be used with `Facemap.set_nodes()`,
|
|
699
|
+
`Facemap.set_elements()` and `Facemap.set_boundary_connections()`,
|
|
700
|
+
which are used to define the connectivity of the zone.
|
|
701
|
+
"""
|
|
702
|
+
if hasattr(self, '_in_assignment'):
|
|
703
|
+
yield
|
|
704
|
+
else:
|
|
705
|
+
with tecutil.lock():
|
|
706
|
+
ref = self._native_reference(writable=True)
|
|
707
|
+
_tecutil.DataFaceMapBeginAssign(ref)
|
|
708
|
+
try:
|
|
709
|
+
self._in_assignment = True
|
|
710
|
+
yield
|
|
711
|
+
finally:
|
|
712
|
+
try:
|
|
713
|
+
if not _tecutil.DataFaceMapEndAssign(ref):
|
|
714
|
+
raise TecplotSystemError()
|
|
715
|
+
finally:
|
|
716
|
+
del self._in_assignment
|
|
717
|
+
|
|
718
|
+
def set_elementmap(self, elementmap):
|
|
719
|
+
"""Define connectivity per element.
|
|
720
|
+
|
|
721
|
+
Parameters:
|
|
722
|
+
elementmap (`integers <int>`): Zero-based indices of the nodes that
|
|
723
|
+
make up each face of each element. For polygons, the map is a
|
|
724
|
+
list of elements, each made of up a list of nodes. For
|
|
725
|
+
polyhedrons, this is a list of elements, made up a list of
|
|
726
|
+
faces, each made up of a list of nodes.
|
|
727
|
+
|
|
728
|
+
.. warning::
|
|
729
|
+
|
|
730
|
+
This method is mutually exclusive with the `Facemap.set_mapping()`
|
|
731
|
+
and `Facemap.assignment()`, `Facemap.set_nodes()`,
|
|
732
|
+
`Facemap.set_elements()` and `Facemap.set_boundary_connections()`
|
|
733
|
+
family of methods. The size of the underlying arrays are calculated
|
|
734
|
+
based on the elementmap given and a call to `Facemap.alloc()` is
|
|
735
|
+
made which will override any previous allocation.
|
|
736
|
+
|
|
737
|
+
This may be a more convenient way to describe the connectivity of a
|
|
738
|
+
polytope zone, however it does not support boundary face connections to
|
|
739
|
+
other zones (see `Facemap.set_mapping()`).
|
|
740
|
+
|
|
741
|
+
Here is an example of an elementmap for two triangles (polygons)::
|
|
742
|
+
|
|
743
|
+
nodes = ((0, 0, 0 ),
|
|
744
|
+
(1, 0, 0.5),
|
|
745
|
+
(0, 1, 0.5),
|
|
746
|
+
(1, 1, 1 ))
|
|
747
|
+
elementmap = ((0, 1, 2), # polygon 0, 3 faces
|
|
748
|
+
(1, 3, 2)) # polygon 1, 3 faces
|
|
749
|
+
|
|
750
|
+
This is an example of an elementmap for two tetrahedrons
|
|
751
|
+
(polyhedrons)::
|
|
752
|
+
|
|
753
|
+
nodes = ((0, 0, 0),
|
|
754
|
+
(1, 1, 0),
|
|
755
|
+
(1, 0, 1),
|
|
756
|
+
(0, 1, 1),
|
|
757
|
+
(0, 0, 1))
|
|
758
|
+
elementmap = (((0, 1, 2), # polyhedron 0, 4 faces
|
|
759
|
+
(0, 1, 3),
|
|
760
|
+
(1, 3, 2),
|
|
761
|
+
(0, 2, 3)),
|
|
762
|
+
((0, 2, 3), # polyhedron 1, 4 faces
|
|
763
|
+
(2, 3, 4),
|
|
764
|
+
(0, 2, 4),
|
|
765
|
+
(0, 4, 3)))
|
|
766
|
+
|
|
767
|
+
"""
|
|
768
|
+
_dispatch = {
|
|
769
|
+
c_int32: _tecutil.DataFaceMapAssignElemToNodeMap,
|
|
770
|
+
c_int64: _tecutil.DataFaceMapAssignElemToNodeMap64}
|
|
771
|
+
data_type = self.node_c_type
|
|
772
|
+
if data_type is None:
|
|
773
|
+
data_type = c_int32
|
|
774
|
+
|
|
775
|
+
nelems = len(elementmap)
|
|
776
|
+
|
|
777
|
+
faces = [len(e) for e in elementmap]
|
|
778
|
+
faces = (c_int32 * len(faces))(*faces)
|
|
779
|
+
|
|
780
|
+
if self.zone.zone_type == ZoneType.FEPolygon:
|
|
781
|
+
nodes = None
|
|
782
|
+
elemmap = [n + 1 for face in elementmap for n in face]
|
|
783
|
+
else: # if self.zone.zone_type == ZoneType.FEPolyhedron:
|
|
784
|
+
nodes = [len(face) for elem in elementmap for face in elem]
|
|
785
|
+
nodes = (c_int32 * len(nodes))(*nodes)
|
|
786
|
+
elemmap = [n + 1 for elem in elementmap
|
|
787
|
+
for face in elem
|
|
788
|
+
for n in face]
|
|
789
|
+
|
|
790
|
+
elemmap = (data_type * len(elemmap))(*elemmap)
|
|
791
|
+
|
|
792
|
+
if __debug__:
|
|
793
|
+
if len(elemmap):
|
|
794
|
+
if min(elemmap) < 1 or self.num_unique_nodes < max(elemmap):
|
|
795
|
+
raise TecplotIndexError
|
|
796
|
+
log.debug(dedent('''\
|
|
797
|
+
FaceMapAssign:
|
|
798
|
+
nelems: {}
|
|
799
|
+
faces: {}
|
|
800
|
+
nodes: {}
|
|
801
|
+
elemmap: {}''').format(nelems, tecutil.array_to_str(faces),
|
|
802
|
+
tecutil.array_to_str(nodes),
|
|
803
|
+
tecutil.array_to_str(elemmap)))
|
|
804
|
+
|
|
805
|
+
# reset facemap before we fill it with the nodemap
|
|
806
|
+
if self.zone.zone_type == ZoneType.FEPolygon:
|
|
807
|
+
self.alloc(int(self.zone.num_faces * self.num_nodes(0)))
|
|
808
|
+
else:
|
|
809
|
+
self.alloc(0)
|
|
810
|
+
|
|
811
|
+
with tecutil.lock():
|
|
812
|
+
ref = self._native_reference(writable=True)
|
|
813
|
+
if not _dispatch[data_type](ref, nelems, faces, nodes, elemmap):
|
|
814
|
+
raise TecplotSystemError()
|