Skip to content

Commit 50874be

Browse files
authored
fix/2d quad tri (#21)
* fix tri to linear map and scoping bug * fix example to match API * conditionally add IDs for the mesh
1 parent 9f9d2aa commit 50874be

File tree

5 files changed

+66
-34
lines changed

5 files changed

+66
-34
lines changed

ansys/dpf/core/meshed_region.py

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -183,15 +183,19 @@ def __str__(self):
183183
# self._message = skin.get_output(0, types.meshed_region)
184184
# return MeshedRegion(self._channel, skin, self._model, name)
185185

186-
def _as_vtk(self, as_linear=True):
186+
def _as_vtk(self, as_linear=True, include_ids=False):
187187
"""Convert DPF mesh to a pyvista unstructured grid"""
188188
from ansys.dpf.core.vtk_helper import dpf_mesh_to_vtk
189189
nodes = self.nodes.coordinates_field.data
190190
etypes = self.elements.element_types_field.data
191191
conn = self.elements.connectivities_field.data
192192
grid = dpf_mesh_to_vtk(nodes, etypes, conn, as_linear)
193-
grid['node_ids'] = self.nodes.scoping.ids
194-
grid['element_ids'] = self.elements.scoping.ids
193+
194+
# consider adding this when scoping request is faster
195+
if include_ids:
196+
grid['node_ids'] = self.nodes.scoping.ids
197+
grid['element_ids'] = self.elements.scoping.ids
198+
195199
return grid
196200

197201
@property
@@ -290,7 +294,8 @@ def plot(self, field_or_fields_container=None, notebook=None,
290294
off_screen, show_axes, **kwargs)
291295

292296
# otherwise, simply plot self
293-
return pl.plot_mesh(notebook)
297+
kwargs['notebook'] = notebook
298+
return pl.plot_mesh(**kwargs)
294299

295300

296301
class Node:
@@ -642,13 +647,13 @@ def mapping_id_to_index(self):
642647
self._mapping_id_to_index = self._build_mapping_id_to_index()
643648
return self._mapping_id_to_index
644649

645-
def map_scoping(self, scope):
650+
def map_scoping(self, external_scope):
646651
"""Return the indices to map the scoping of these elements to
647652
the scoping of a field.
648653
649654
Parameters
650655
----------
651-
scope : scoping.Scoping
656+
external_scope : scoping.Scoping
652657
Scoping to map to.
653658
654659
Returns
@@ -657,6 +662,9 @@ def map_scoping(self, scope):
657662
List of indices to map from the external scope to the
658663
scoping of these nodes.
659664
665+
mask : numpy.ndarray
666+
Members of the external scope that are in the node scoping.
667+
660668
Examples
661669
--------
662670
Return the indices that map a field to a nodes collection.
@@ -667,9 +675,10 @@ def map_scoping(self, scope):
667675
>>> nodes = model.metadata.meshed_region.nodes
668676
>>> disp = model.results.displacements()
669677
>>> field = disp.outputs.field_containers()[0]
670-
>>> ind = nodes.map_scoping(field.scoping)
671-
>>> ind
678+
>>> ind, mask = nodes.map_scoping(field.scoping)
679+
>>> ind, mask
672680
array([ 508, 509, 909, ..., 3472, 3471, 3469])
681+
array([True, True, True, ..., True, True, True])
673682
674683
These indices can then be used to remap ``nodes.coordinates`` to
675684
match the order of the field data. That way the field data matches the
@@ -678,10 +687,12 @@ def map_scoping(self, scope):
678687
>>> mapped_nodes = nodes.coordinates[ind]
679688
680689
"""
681-
if scope.location in ['Elemental', 'NodalElemental']:
690+
if external_scope.location in ['Elemental', 'NodalElemental']:
682691
raise ValueError('Input scope location must be "Nodal"')
683-
return np.array(list(map(self.mapping_id_to_index.get, scope.ids)))
684-
692+
arr = np.array(list(map(self.mapping_id_to_index.get, external_scope.ids)))
693+
mask = arr != None
694+
ind = arr[mask].astype(np.int)
695+
return ind, mask
685696

686697
class Elements():
687698
"""Elements belonging to a ``meshed_region``.
@@ -899,13 +910,13 @@ def mapping_id_to_index(self) -> dict:
899910
self._mapping_id_to_index = self._build_mapping_id_to_index()
900911
return self._mapping_id_to_index
901912

902-
def map_scoping(self, scope):
913+
def map_scoping(self, external_scope):
903914
"""Return the indices to map the scoping of these elements to
904915
the scoping of a field.
905916
906917
Parameters
907918
----------
908-
scope : scoping.Scoping
919+
external_scope : scoping.Scoping
909920
Scoping to map to.
910921
911922
Returns
@@ -914,6 +925,9 @@ def map_scoping(self, scope):
914925
List of indices to map from the external scope to the
915926
scoping of these elements.
916927
928+
mask : numpy.ndarray
929+
Members of the external scope that are in the element scoping.
930+
917931
Examples
918932
--------
919933
Return the indices that map a field to an elements collection.
@@ -924,7 +938,7 @@ def map_scoping(self, scope):
924938
>>> elements = model.metadata.meshed_region.elements
925939
>>> vol = model.results.volume()
926940
>>> field = vol.outputs.field_containers()[0]
927-
>>> ind = elements.map_scoping(field.scoping)
941+
>>> ind, mask = elements.map_scoping(field.scoping)
928942
>>> ind
929943
[66039
930944
11284,
@@ -942,6 +956,9 @@ def map_scoping(self, scope):
942956
>>> mapped_data = field.data[ind]
943957
944958
"""
945-
if scope.location in ['Nodal', 'NodalElemental']:
946-
raise ValueError('Input scope location must be "Elemental"')
947-
return np.array(list(map(self.mapping_id_to_index.get, scope.ids)))
959+
if external_scope.location in ['Nodal', 'NodalElemental']:
960+
raise ValueError('Input scope location must be "Nodal"')
961+
arr = np.array(list(map(self.mapping_id_to_index.get, external_scope.ids)))
962+
mask = arr != None
963+
ind = arr[mask].astype(np.int)
964+
return ind, mask

ansys/dpf/core/plotter.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class Plotter:
1919
def __init__(self, mesh):
2020
self._mesh = mesh
2121

22-
def plot_mesh(self, notebook=None):
22+
def plot_mesh(self, **kwargs):
2323
"""Plot the mesh using pyvista.
2424
2525
Parameters
@@ -30,26 +30,33 @@ def plot_mesh(self, notebook=None):
3030
external to the notebook with an interactive window. When
3131
``True``, always plot within a notebook.
3232
33+
**kwargs : optional
34+
Additional keyword arguments for the plotter. See
35+
``help(pyvista.plot)`` for additional keyword arguments.
3336
"""
34-
return self._mesh.grid.plot(notebook=notebook)
35-
37+
kwargs.setdefault('color', 'w')
38+
kwargs.setdefault('show_edges', True)
39+
return self._mesh.grid.plot(**kwargs)
40+
3641
def plot_chart(self, fields_container):
37-
"""Plot the minimum/maximum result values over time
38-
if the time_freq_support contains several time_steps
39-
(for example: transient analysis)
42+
"""Plot the minimum/maximum result values over time.
43+
44+
This is a valid method if the time_freq_support contains
45+
several time_steps (for example, a transient analysis)
4046
4147
Parameters
4248
----------
43-
field_container
44-
dpf.core.FieldsContainer that must contains a result for each time step of the time_freq_support.
49+
field_container : dpf.core.FieldsContainer
50+
A fields container that must contains a result for each
51+
time step of the time_freq_support.
4552
4653
Examples
4754
--------
4855
>>> from ansys.dpf import core
4956
>>> model = core.Model('file.rst')
5057
>>> stress = model.results.stress()
5158
>>> scoping = core.Scoping()
52-
>>> scoping.ids = list(range(1, len(model.metadata.time_freq_support.frequencies) + 1))
59+
>>> scoping.ids = range(1, len(model.metadata.time_freq_support.frequencies) + 1)
5360
>>> stress.inputs.time_scoping.connect(scoping)
5461
>>> fc = stress.outputs.fields_container()
5562
>>> plotter = core.plotter.Plotter(model.metadata.meshed_region)
@@ -65,8 +72,8 @@ def plot_chart(self, fields_container):
6572
minmaxOp.inputs.connect(normOp.outputs)
6673
fieldMin = minmaxOp.outputs.field_min()
6774
fieldMax = minmaxOp.outputs.field_max()
68-
pyplot.plot(time_field.data,fieldMax.data,'r',label='Maximum')
69-
pyplot.plot(time_field.data,fieldMin.data,'b',label='Minimum')
75+
pyplot.plot(time_field.data, fieldMax.data, 'r', label='Maximum')
76+
pyplot.plot(time_field.data, fieldMin.data, 'b', label='Minimum')
7077
unit = tfq.frequencies.unit
7178
if unit == "Hz":
7279
pyplot.xlabel("frequencies (Hz)")
@@ -76,7 +83,7 @@ def plot_chart(self, fields_container):
7683
pyplot.xlabel(unit)
7784
substr = fields_container[0].name.split("_")
7885
pyplot.ylabel(substr[0] + fieldMin.unit)
79-
pyplot.title( substr[0] + ": min/max values over time")
86+
pyplot.title(substr[0] + ": min/max values over time")
8087
return pyplot.legend()
8188

8289
def plot_contour(self, field_or_fields_container, notebook=None,
@@ -181,8 +188,8 @@ def plot_contour(self, field_or_fields_container, notebook=None,
181188
overall_data = np.full(len(mesh_location), np.nan)
182189

183190
for field in fields_container:
184-
ind = mesh_location.map_scoping(field.scoping)
185-
overall_data[ind] = field.data
191+
ind, mask = mesh_location.map_scoping(field.scoping)
192+
overall_data[ind] = field.data[mask]
186193

187194
# create the plotter and add the meshes
188195
plotter = pv.Plotter(notebook=notebook, off_screen=off_screen)
@@ -233,4 +240,3 @@ def _plot_contour_using_vtk_file(self, fields_container, notebook=None):
233240
plotter.add_mesh(grid, scalars=val, stitle=field_name, show_edges=True)
234241
plotter.add_axes()
235242
plotter.show()
236-

ansys/dpf/core/vtk_helper.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,15 @@ def compute_offset():
180180
cells[cell_pos + 7] = cells[cell_pos + 3]
181181
cells[cell_pos + 8] = cells[cell_pos + 4]
182182

183+
anstri6_mask = etypes == 4 # kAnsTri6 = 4
184+
if np.any(anstri6_mask):
185+
if offset is None:
186+
offset = compute_offset()
187+
cell_pos = offset[anstri6_mask]
188+
cells[cell_pos + 4] = cells[cell_pos + 1]
189+
cells[cell_pos + 5] = cells[cell_pos + 2]
190+
cells[cell_pos + 6] = cells[cell_pos + 3]
191+
183192
else:
184193
vtk_cell_type = VTK_MAPPING[etypes]
185194

examples/01-static-transient/00-basic_transient.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@
154154
# to match the order of nodes in the mesh.
155155

156156
nodes = meshed_region.nodes
157-
ind = nodes.map_scoping(field.scoping)
157+
ind, mask = nodes.map_scoping(field.scoping)
158158

159159
# show that the order of the remapped node scoping matches the field scoping
160160
print('Scoping matches:', np.allclose(np.array(nodes.scoping.ids)[ind],

tests/test_meshregion.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def test_get_mesh_from_model(simple_bar_model):
2929

3030
def test_vtk_grid_from_model(simple_bar_model):
3131
mesh = simple_bar_model.metadata.meshed_region
32-
grid = mesh.grid
32+
grid = mesh._as_vtk(include_ids=True)
3333
assert np.allclose(grid['element_ids'], mesh.elements.scoping.ids)
3434
assert np.allclose(grid['node_ids'], mesh.nodes.scoping.ids)
3535
assert all(grid.celltypes == vtk.VTK_HEXAHEDRON)

0 commit comments

Comments
 (0)