Skip to content

Commit 5f86b94

Browse files
rokamoto26pyansys-ci-botkoubaa
authored
feat: enhance facet extraction functions to return element_ids and pa… (#882)
Co-authored-by: pyansys-ci-bot <[email protected]> Co-authored-by: Mohamed Koubaa <[email protected]>
1 parent 9429a9a commit 5f86b94

File tree

3 files changed

+139
-38
lines changed

3 files changed

+139
-38
lines changed

doc/changelog/882.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Enhance facet extraction functions to return element_ids and pa…

src/ansys/dyna/core/lib/deck_plotter.py

Lines changed: 118 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -224,25 +224,26 @@ def extract_shell_facets(shells: pd.DataFrame, mapping):
224224
"""
225225

226226
if len(shells) == 0:
227-
return []
228-
229-
# extract only the node information
230-
# could keep in the future to separate the parts or elements
231-
shells = shells.drop(columns=["eid", "pid"])
227+
return np.empty((0), dtype=int), np.empty((0), dtype=int), np.empty((0), dtype=int)
232228

229+
# extract the node information, element_ids and part_ids
233230
facet_with_prefix = []
231+
eid = []
232+
pid = []
234233

235234
idx = 0
236235
for row in shells.itertuples(index=False):
237-
array = shell_facet_array(row)
236+
array = shell_facet_array(row[2:])
238237
facet_with_prefix.append(array)
238+
eid.append(row[0])
239+
pid.append(row[1])
239240
idx += 1
240241

242+
# Convert list to np.ndarray
241243
flat_facets = np.concatenate(facet_with_prefix, axis=0)
242-
243244
flat_facets_indexed = map_facet_nid_to_index(flat_facets, mapping)
244245

245-
return flat_facets_indexed
246+
return flat_facets_indexed, np.array(eid), np.array(pid)
246247

247248

248249
def extract_lines(beams: pd.DataFrame, mapping: typing.Dict[int, int]) -> np.ndarray:
@@ -264,43 +265,63 @@ def extract_lines(beams: pd.DataFrame, mapping: typing.Dict[int, int]) -> np.nda
264265
"""
265266
# dont need to do this if there is no beams
266267
if len(beams) == 0:
267-
return np.empty((0), dtype=int)
268-
269-
# extract only the node information
270-
# could keep in the future to separate the parts or elements
271-
beams = beams[["n1", "n2"]]
268+
return np.empty((0), dtype=int), np.empty((0), dtype=int), np.empty((0), dtype=int)
272269

270+
# extract the node information, element_ids and part_ids
273271
line_with_prefix = []
272+
eid = []
273+
pid = []
274274

275275
for row in beams.itertuples(index=False):
276-
line_with_prefix.append(line_array(row))
276+
line_with_prefix.append(line_array(row[2:]))
277+
eid.append(row[0])
278+
pid.append(row[1])
277279

280+
# Convert list to np.ndarray
278281
flat_lines = np.concatenate(line_with_prefix, axis=0)
279-
280282
flat_lines_indexed = map_facet_nid_to_index(flat_lines, mapping)
281283

282-
return flat_lines_indexed
284+
return flat_lines_indexed, np.array(eid), np.array(pid)
283285

284286

285287
def extract_solids(solids: pd.DataFrame, mapping: typing.Dict[int, int]):
286288
if len(solids) == 0:
287-
return []
289+
return {}
288290

289-
solids = solids.drop(columns=["eid", "pid"])
291+
solid_with_prefix = {
292+
8: [[], [], []], # Hexa
293+
6: [[], [], []], # Wedge
294+
5: [[], [], []], # Pyramid
295+
4: [[], [], []], # Tetra
296+
}
290297

291-
idx = 0
298+
# extract the node information, element_ids and part_ids
299+
for row in solids.itertuples(index=False):
300+
arr = np.array(row[2:10])
301+
temp_array, indices = np.unique(arr, return_index=True)
302+
key = len(temp_array)
292303

293-
solid_with_prefix = []
304+
sorted_unique_indices = np.sort(indices)
305+
temp_array = arr[sorted_unique_indices]
294306

295-
for row in solids.itertuples(index=False):
296-
solid_with_prefix.append(solid_array(row))
297-
idx += 1
307+
if key == 6: # convert node numbering to PyVista Style for Wedge
308+
temp_array = temp_array[np.array((0, 1, 4, 3, 2, 5))]
309+
310+
solid_with_prefix[key][0].append([key, *temp_array]) # connectivity
311+
solid_with_prefix[key][1].append(row[0]) # element_ids
312+
solid_with_prefix[key][2].append(row[1]) # part_ids
298313

299-
flat_solids = np.concatenate(solid_with_prefix, axis=0)
314+
# Convert list to np.ndarray
315+
for key, value in solid_with_prefix.items():
316+
if value[0]:
317+
flat_solids = np.concatenate(value[0], axis=0)
300318

301-
flat_solids_indexed = map_facet_nid_to_index(flat_solids, mapping)
319+
value[0] = map_facet_nid_to_index(flat_solids, mapping) # connectivity
302320

303-
return flat_solids_indexed
321+
value[1] = np.array(value[1]) # element_ids
322+
value[2] = np.array(value[2]) # part_ids
323+
324+
return solid_with_prefix
304325

305326

306327
def get_pyvista():
@@ -318,9 +339,14 @@ def get_polydata(deck: Deck, cwd=None):
318339
pv = get_pyvista()
319340

320341
# check kwargs for cwd. future more arguments to plot
321-
flat_deck = deck.expand(cwd)
322-
nodes_df, element_dict = merge_keywords(flat_deck)
342+
# flatten deck
343+
if cwd is not None:
344+
flat_deck = deck.expand(cwd=cwd, recurse=True)
345+
else:
346+
flat_deck = deck.expand(recurse=True)
323347

348+
# get dataframes for each element types
349+
nodes_df, element_dict = merge_keywords(flat_deck)
324350
shells_df = element_dict["SHELL"]
325351
beams_df = element_dict["BEAM"]
326352
solids_df = element_dict["SOLID"]
@@ -332,16 +358,73 @@ def get_polydata(deck: Deck, cwd=None):
332358

333359
mapping = get_nid_to_index_mapping(nodes_df)
334360

335-
facets = extract_shell_facets(shells_df, mapping)
336-
lines = extract_lines(beams_df, mapping)
337-
solids = extract_solids(solids_df, mapping)
338-
plot_data = pv.PolyData(nodes_list, [*facets, *solids])
339-
if len(lines) > 0:
340-
plot_data.lines = lines
361+
# get the node information, element_ids and part_ids
362+
facets, shell_eids, shell_pids = extract_shell_facets(shells_df, mapping)
363+
lines, line_eids, line_pids = extract_lines(beams_df, mapping)
364+
solids_info = extract_solids(solids_df, mapping)
365+
366+
# celltype_dict for beam and shell
367+
celltype_dict = {
368+
pv.CellType.LINE: lines.reshape([-1, 3])[:, 1:],
369+
pv.CellType.QUAD: facets.reshape([-1, 5])[:, 1:],
370+
}
371+
372+
# dict of cell types for node counts
373+
solid_celltype = {
374+
4: pv.CellType.TETRA,
375+
5: pv.CellType.PYRAMID,
376+
6: pv.CellType.WEDGE,
377+
8: pv.CellType.HEXAHEDRON,
378+
}
379+
380+
# Update celltype_dict with solid elements
381+
solids_pids = np.empty((0), dtype=int)
382+
solids_eids = np.empty((0), dtype=int)
383+
384+
for n_points, elements in solids_info.items():
385+
if len(elements[0]) == 0:
386+
continue
387+
388+
temp_solids, temp_solids_eids, temp_solids_pids = elements
389+
390+
celltype_dict[solid_celltype[n_points]] = temp_solids.reshape([-1, n_points + 1])[:, 1:]
391+
392+
# Update part_ids and element_ids info for solid elements
393+
if len(solids_pids) != 0:
394+
solids_pids = np.concatenate((solids_pids, temp_solids_pids), axis=0)
395+
else:
396+
solids_pids = temp_solids_pids
397+
398+
if len(solids_pids) != 0:
399+
solids_eids = np.concatenate((solids_eids, temp_solids_eids), axis=0)
400+
else:
401+
solids_eids = temp_solids_eids
402+
403+
# Create UnstructuredGrid
404+
plot_data = pv.UnstructuredGrid(celltype_dict, nodes_list)
405+
406+
# Mapping part_ids and element_ids
407+
plot_data.cell_data["part_ids"] = np.concatenate((line_pids, shell_pids, solids_pids), axis=0)
408+
plot_data.cell_data["element_ids"] = np.concatenate((line_eids, shell_eids, solids_eids), axis=0)
409+
341410
return plot_data
342411

343412

344413
def plot_deck(deck, **args):
345414
"""Plot the deck."""
415+
416+
# import this lazily (otherwise this adds over a second to the import time of pyDyna)
417+
pv = get_pyvista()
418+
346419
plot_data = get_polydata(deck, args.pop("cwd", ""))
347-
return plot_data.plot(**args)
420+
421+
# set default color if both color and scalars are not specified
422+
color = args.pop("color", None)
423+
scalars = args.pop("scalars", None)
424+
425+
if scalars is not None:
426+
return plot_data.plot(scalars=scalars, **args) # User specified scalars
427+
elif color is not None:
428+
return plot_data.plot(color=color, **args) # User specified color
429+
else:
430+
return plot_data.plot(color=pv.global_theme.color, **args) # Default color

tests/test_plotter.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,29 @@ def test_extract_shell_facets():
116116
}
117117
)
118118
numpy.testing.assert_allclose(
119-
extract_shell_facets(test_1_pddf, {1: 1, 2: 2, 3: 3, 4: 4}),
119+
extract_shell_facets(test_1_pddf, {1: 1, 2: 2, 3: 3, 4: 4})[0],
120120
[3, 1, 2, 3, 4, 2, 3, 4, 1],
121121
)
122122
numpy.testing.assert_allclose(
123-
extract_shell_facets(test_1_pddf, {1: 2, 2: 3, 3: 4, 4: 5}),
124-
[3, 2, 3, 4, 4, 3, 4, 5, 2],
123+
extract_shell_facets(test_1_pddf, {1: 1, 2: 2, 3: 3, 4: 4})[1],
124+
np.array([1,3]),
125+
)
126+
numpy.testing.assert_allclose(
127+
extract_shell_facets(test_1_pddf, {1: 1, 2: 2, 3: 3, 4: 4})[2],
128+
np.array([1,3]),
129+
)
130+
131+
numpy.testing.assert_allclose(
132+
extract_shell_facets(test_1_pddf, {1: 2, 2: 3, 3: 4, 4: 5})[0],
133+
[3, 2, 3, 4, 4, 3, 4, 5, 2],
134+
)
135+
numpy.testing.assert_allclose(
136+
extract_shell_facets(test_1_pddf, {1: 2, 2: 3, 3: 4, 4: 5})[1],
137+
np.array([1,3]),
138+
)
139+
numpy.testing.assert_allclose(
140+
extract_shell_facets(test_1_pddf, {1: 2, 2: 3, 3: 4, 4: 5})[2],
141+
np.array([1,3]),
125142
)
126143

127144

0 commit comments

Comments
 (0)