Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reducing pymapdl-reader dependency on the mesh module #1299

Merged
merged 40 commits into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7a953e1
Replacing default value for chunk size.
germa89 Jul 25, 2022
d773c97
First attemp
germa89 Jul 25, 2022
dc45712
Merge branch 'main' into feat/replacing-mesh
germa89 Jul 25, 2022
3279887
Merge branch 'main' into feat/replacing-mesh
germa89 Jul 27, 2022
9662cae
Initial commit.
germa89 Jul 27, 2022
1d42302
Adding dpf to test requirements.
germa89 Aug 23, 2022
c3784e6
Missing import
germa89 Aug 23, 2022
18e1006
Renaming class.
germa89 Aug 24, 2022
2f43419
Merge branch 'main' into feat/replacing-mesh
germa89 Aug 24, 2022
7a08c35
Moving contact solve fixture to conftest
germa89 Aug 24, 2022
4aa9e8a
fixing some coverage.
germa89 Aug 24, 2022
b3f4870
Adding unit tests
germa89 Aug 24, 2022
37d5887
removing extra file
germa89 Aug 24, 2022
f330e77
Removing dpf
germa89 Aug 24, 2022
f459e0d
Fixing tests
germa89 Aug 24, 2022
3f2a6a5
Adding test to not implemented methods.
germa89 Aug 24, 2022
cc01ec2
Removing needs from unit tests in CICD
germa89 Aug 24, 2022
af559b3
Merge branch 'ci/make-unit-test-independent-' into feat/replacing-mesh
germa89 Aug 24, 2022
da50d25
Fixing unit tests and removing overwritting of ``node_angles``
germa89 Aug 24, 2022
9b1f1a7
Undoing the node_angles
germa89 Aug 24, 2022
c1e26da
Merging both classes and changing folder name.
germa89 Aug 25, 2022
2ab7a9b
emptying the mesh file.
germa89 Aug 25, 2022
b8e44cb
emptying the mesh file.
germa89 Aug 25, 2022
3415677
Big refactoring.
germa89 Aug 25, 2022
57235a0
Adding unit tests.
germa89 Aug 25, 2022
06c2222
Fixing tests
germa89 Aug 25, 2022
35e25ed
Improving tests.
germa89 Aug 25, 2022
e4a0194
Improving coverage
germa89 Aug 25, 2022
69f246e
Calling parse_vtk from mesh folder (centralizing)
germa89 Aug 25, 2022
a925e29
Adding mesh.py
germa89 Aug 25, 2022
4856654
Removing duplicated function
germa89 Aug 25, 2022
d9f9efc
Trying to fix the docs.
germa89 Aug 26, 2022
dccc09f
Fixing tech demo example
germa89 Aug 29, 2022
70fe5aa
Merge branch 'main' into feat/replacing-mesh
germa89 Aug 29, 2022
4db3b14
Fixing mesh plotting in tech demo
germa89 Aug 29, 2022
8ce0f94
Ading more clean options to make file
germa89 Aug 29, 2022
a6d8fbb
Reset cache
germa89 Aug 29, 2022
fd5efa2
Merge branch 'main' into feat/replacing-mesh
germa89 Aug 29, 2022
14287b7
fixing cache.
germa89 Aug 29, 2022
983eca9
Removing useless if condition.
germa89 Aug 30, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ env:
# You should go up in number, if you go down (or repeat a previous value)
# you might end up reusing a previous cache if it haven't been deleted already.
# It applies 7 days retention policy by default.
RESET_PIP_CACHE: 2
RESET_EXAMPLES_CACHE: 2
RESET_DOC_BUILD_CACHE: 2
RESET_AUTOSUMMARY_CACHE: 2
RESET_PIP_CACHE: 4
RESET_EXAMPLES_CACHE: 4
RESET_DOC_BUILD_CACHE: 4
RESET_AUTOSUMMARY_CACHE: 4
PACKAGE_NAME: PyMAPDL

concurrency:
Expand Down
12 changes: 12 additions & 0 deletions doc/make.bat
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ set BUILDDIR=build

if "%1" == "" goto help
if "%1" == "clean" goto clean
if "%1" == "clean-all" goto clean-all
if "%1" == "clean-examples" goto clean-examples

%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
Expand All @@ -34,6 +36,16 @@ rmdir /s /q %BUILDDIR% > /NUL 2>&1
for /d /r %SOURCEDIR% %%d in (_autosummary) do @if exist "%%d" rmdir /s /q "%%d"
goto end

:clean-all
rmdir /s /q %BUILDDIR% > /NUL 2>&1
rmdir /s /q source\examples\gallery_examples > /NUL 2>&1
for /d /r %SOURCEDIR% %%d in (_autosummary) do @if exist "%%d" rmdir /s /q "%%d"
goto end

:clean-examples
rmdir /s /q source\examples\gallery_examples > /NUL 2>&1
goto end

:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -569,8 +569,9 @@ The following contact settings are used for the ``CONTA174`` elements:
mapdl.esel('s', 'mat', '', 2)
mapdl.esel("r", "ename", "", elem)
esurf = mapdl.mesh._grid.linear_copy().extract_surface().clean()
pl.add_mesh(esurf, show_edges=True, show_scalar_bar=False,
style='surface', color=color)
if mapdl.mesh.n_elem != 1:
pl.add_mesh(esurf, show_edges=True, show_scalar_bar=False,
style='surface', color=color)
pl.show()

**Figure 28.5: Rigid Surface Constrained.**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@

mapdl.run("/SOLU")
mapdl.antype("STATIC")
mapdl.nsel("ALL")
mapdl.allsel()
mapdl.solve()
mapdl.finish()
mapdl.finish(mute=True)

###############################################################################
# Post-Processing
Expand Down
5 changes: 4 additions & 1 deletion src/ansys/mapdl/core/mapdl_grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1634,7 +1634,7 @@ def download(
self,
files,
target_dir=None,
chunk_size=DEFAULT_CHUNKSIZE,
chunk_size=None,
progress_bar=None,
recursive=False,
): # pragma: no cover
Expand Down Expand Up @@ -1696,6 +1696,9 @@ def download(
>>> mapdl.download_project()

"""
if chunk_size is None:
chunk_size = DEFAULT_CHUNKSIZE

if chunk_size > 4 * 1024 * 1024: # 4MB
raise ValueError(
f"Chunk sizes bigger than 4 MB can generate unstable behaviour in PyMAPDL. "
Expand Down
Empty file.
246 changes: 246 additions & 0 deletions src/ansys/mapdl/core/mesh/mesh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
"""Module for common class between Archive, and result mesh."""
from ansys.mapdl.reader import _reader, _relaxmidside
from ansys.mapdl.reader.elements import ETYPE_MAP
from ansys.mapdl.reader.misc import unique_rows
import numpy as np
import pyvista as pv
from pyvista._vtk import VTK9

INVALID_ALLOWABLE_TYPES = TypeError(
"`allowable_types` must be an array " "of ANSYS element types from 1 and 300"
)

# map MESH200 elements to a pymapdl_reader/VTK element type (see elements.py)
MESH200_MAP = {
0: 2, # line
1: 2, # line
2: 2, # line
3: 2, # line
4: 3, # triangle
5: 3, # triangle
6: 3, # quadrilateral
7: 3, # quadrilateral
8: 5, # tetrahedron with 4 nodes
9: 5, # tetrahedron with 10 nodes
10: 4, # hex with 8 nodes
11: 4,
} # hex with 8 nodes

SHAPE_MAP = { # from ELIST definition
0: "",
1: "LINE",
2: "PARA",
3: "ARC ",
4: "CARC",
5: "",
6: "TRIA",
7: "QUAD",
8: "TRI6",
9: "QUA8",
10: "POIN",
11: "CIRC",
12: "",
13: "",
14: "CYLI",
15: "CONE",
16: "SPHE",
17: "",
18: "",
19: "PILO",
}
# element type to VTK conversion function call map
# 0: skip
# 1: Point
# 2: Line (linear or quadratic)
# 3: Shell
# 4: 3D Solid (Hexahedral, wedge, pyramid, tetrahedral)
# 5: Tetrahedral
# 6: Line (always linear)
TARGE170_MAP = {
"TRI": 3, # 3-Node Triangle
"QUAD": 3, # 4-Node Quadrilateral
"CYLI": 0, # Not supported (NS) # Cylinder
"CONE": 0, # NS # Cone
"TRI6": 3, # 6-Node triangle
"SPHE": 0, # NS # Sphere
"PILO": 1, # Pilot Node
"QUAD8": 3, # 8-Node Quadrilateral
"LINE": 2, # Line
"PARA": 2, # Parabola
"POINT": 1, # Point
}


def _parse_vtk(
mesh,
allowable_types=None,
force_linear=False,
null_unallowed=False,
fix_midside=True,
additional_checking=False,
):
"""Convert raw ANSYS nodes and elements to a VTK UnstructuredGrid

Parameters
----------
fix_midside : bool, optional
Adds additional midside nodes when ``True``. When
``False``, missing ANSYS cells will simply point to the
first node.

"""
if not mesh._has_nodes or not mesh._has_elements:
# warnings.warn('Missing nodes or elements. Unable to parse to vtk')
return

etype_map = ETYPE_MAP
if allowable_types is not None:
try:
allowable_types = np.asarray(allowable_types)
except:
raise INVALID_ALLOWABLE_TYPES

if not issubclass(allowable_types.dtype.type, np.integer):
raise TypeError("Element types must be an integer array-like")

if allowable_types.min() < 1 or allowable_types.max() > 300:
raise INVALID_ALLOWABLE_TYPES

etype_map = np.zeros_like(ETYPE_MAP)
etype_map[allowable_types] = ETYPE_MAP[allowable_types]

# ANSYS element type to VTK map
type_ref = np.empty(2 << 16, np.int32) # 131072
type_ref[mesh._ekey[:, 0]] = etype_map[mesh._ekey[:, 1]]

if allowable_types is None or 200 in allowable_types:
for etype_ind, etype in mesh._ekey:

# MESH200
if etype == 200 and etype_ind in mesh.key_option:
# keyoption 1 contains various cell types
# map them to the corresponding type (see elements.py)
mapped = MESH200_MAP[mesh.key_option[etype_ind][0][1]]
type_ref[etype_ind] = mapped

# TARGE170 specifics
if etype == 170:
# edge case where missing element within the tshape_key
if etype_ind not in mesh.tshape_key: # pragma: no cover
continue
tshape_num = mesh.tshape_key[etype_ind]
if tshape_num >= 19: # weird bug when 'PILO' can be 99 instead of 19.
tshape_num = 19
tshape_label = SHAPE_MAP[tshape_num]
type_ref[etype_ind] = TARGE170_MAP.get(tshape_label, 0)

offset, celltypes, cells = _reader.ans_vtk_convert(
mesh._elem, mesh._elem_off, type_ref, mesh.nnum, True
) # for reset_midside

nodes, angles, nnum = mesh.nodes, mesh.node_angles, mesh.nnum

# fix missing midside
if np.any(cells == -1):
if fix_midside:
nodes, angles, nnum = fix_missing_midside(
cells, nodes, celltypes, offset, angles, nnum
)
else:
cells[cells == -1] = 0

if additional_checking:
cells[cells < 0] = 0
# cells[cells >= nodes.shape[0]] = 0 # fails when n_nodes < 20

if VTK9:
grid = pv.UnstructuredGrid(cells, celltypes, nodes, deep=True)
else:
grid = pv.UnstructuredGrid(offset, cells, celltypes, nodes, deep=True)

# Store original ANSYS element and node information
grid.point_data["ansys_node_num"] = nnum
grid.cell_data["ansys_elem_num"] = mesh.enum
grid.cell_data["ansys_real_constant"] = mesh.elem_real_constant
grid.cell_data["ansys_material_type"] = mesh.material_type
grid.cell_data["ansys_etype"] = mesh._ans_etype
grid.cell_data["ansys_elem_type_num"] = mesh.etype

# add components
# Add element components to unstructured grid
for key, item in mesh.element_components.items():
mask = np.in1d(mesh.enum, item, assume_unique=True)
grid.cell_data[key] = mask

# Add node components to unstructured grid
for key, item in mesh.node_components.items():
mask = np.in1d(nnum, item, assume_unique=True)
grid.point_data[key] = mask

# store node angles
if angles is not None:
if angles.shape[1] == 3:
grid.point_data["angles"] = angles

if not null_unallowed:
grid = grid.extract_cells(grid.celltypes != 0)

if force_linear:
# only run if the grid has points or cells
if grid.n_points:
grid = grid.linear_copy()

# map over element types
# Add tracker for original node numbering
ind = np.arange(grid.n_points)
grid.point_data["origid"] = ind
grid.point_data["VTKorigID"] = ind
return grid


def fix_missing_midside(cells, nodes, celltypes, offset, angles, nnum):
"""Adds missing midside nodes to cells.

ANSYS sometimes does not add midside nodes, and this is denoted in
the element array with a ``0``. When translated to VTK, this is
saved as a ``-1``. If this is not corrected, VTK will segfault.

This function creates missing midside nodes for the quadratic
elements.
"""
# Check for missing midside nodes
mask = cells == -1
nnodes = nodes.shape[0]

nextra = mask.sum()
cells[mask] = np.arange(nnodes, nnodes + nextra)

nodes_new = np.empty((nnodes + nextra, 3))
nodes_new[:nnodes] = nodes
nodes_new[nnodes:] = 0 # otherwise, segfault disaster

# Set new midside nodes directly between their edge nodes
temp_nodes = nodes_new.copy()
_relaxmidside.reset_midside(cells, celltypes, offset, temp_nodes)

# merge midside nodes
unique_nodes, idx_a, idx_b = unique_rows(temp_nodes[nnodes:])

# rewrite node numbers
cells[mask] = idx_b + nnodes
nextra = idx_a.shape[0] # extra unique nodes
nodes_new = nodes_new[: nnodes + nextra]
nodes_new[nnodes:] = unique_nodes

if angles is not None:
new_angles = np.empty((nnodes + nextra, 3))
new_angles[:nnodes] = angles
new_angles[nnodes:] = 0
else:
new_angles = None

# Add extra node numbers
nnum_new = np.empty(nnodes + nextra)
nnum_new[:nnodes] = nnum
nnum_new[nnodes:] = -1
return nodes_new, new_angles, nnum_new
Loading