From cce9757fb9512ed9e84ca6aa477ab1e585cc621e Mon Sep 17 00:00:00 2001 From: German Date: Thu, 9 Dec 2021 00:25:45 +0100 Subject: [PATCH 1/7] Adding indexing --- ansys/mapdl/core/post.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ansys/mapdl/core/post.py b/ansys/mapdl/core/post.py index 4a2da96fc6..bbf42bf872 100644 --- a/ansys/mapdl/core/post.py +++ b/ansys/mapdl/core/post.py @@ -394,7 +394,7 @@ def element_values(self, item, comp="", option="AVG") -> np.ndarray: """ tmp_table = "__ETABLE__" self._mapdl.etable(tmp_table, item, comp, option, mute=True) - return self._mapdl._get_array("ELEM", 1, "ETAB", tmp_table) + return self._mapdl._get_array("ELEM", 1, "ETAB", tmp_table)[self.selected_elements] def plot_element_values( self, item, comp, option="AVG", show_elem_numbering=False, **kwargs @@ -905,7 +905,7 @@ def _plot_point_scalars(self, scalars, show_node_numbering=False, **kwargs): "exist within the result file." ) - surf = self._mapdl.mesh._surf + surf = self._mapdl.mesh._surf[self.selected_elements] # as ``disp`` returns the result for all nodes, we need all node numbers # and to index to the output node numbers From 867f40deceed1fa07e8a186e035dc31e01a4da58 Mon Sep 17 00:00:00 2001 From: German Date: Fri, 10 Dec 2021 13:17:57 +0100 Subject: [PATCH 2/7] Added `nodal_values` which is used heavily in the post_processing module instead of `_ndof_rst`. `nodal_values` returns only the values of the selected nodes. Improved `_plot_point_scalars` to account for when all the nodes are not selected. There has been some reorg in the functions order. --- ansys/mapdl/core/post.py | 1122 +++++++++++++++++++------------------- 1 file changed, 559 insertions(+), 563 deletions(-) diff --git a/ansys/mapdl/core/post.py b/ansys/mapdl/core/post.py index 7cc95c6d71..94f482f53e 100644 --- a/ansys/mapdl/core/post.py +++ b/ansys/mapdl/core/post.py @@ -289,7 +289,9 @@ def nodal_values(self, item, comp="") -> np.ndarray: """ # using _ndof_rst instead of get_array because it is wrapped to check the rst. - return self._ndof_rst(self, item, it1num=comp) + values = self._ndof_rst(item=item, it1num=comp) + mask = self.selected_nodes + return values[mask] def element_values(self, item, comp="", option="AVG") -> np.ndarray: """Compute the element-wise values for a given item and component. @@ -423,7 +425,7 @@ def element_values(self, item, comp="", option="AVG") -> np.ndarray: self._mapdl.etable(tmp_table, item, comp, option, mute=True) return self._mapdl._get_array("ELEM", 1, "ETAB", tmp_table)[self.selected_elements] - def plot_node_values( + def plot_nodal_values( self, item, comp, show_elem_numbering=False, **kwargs ): """Plot nodal values @@ -454,7 +456,7 @@ def plot_node_values( -------- Plot the contact status for the selected elements. - >>> mapdl.post_processing.plot_node_values( + >>> mapdl.post_processing.plot_nodal_values( ... "CONT", "STAT", scalar_bar_args={"title": "Contact status"} ... ) """ @@ -519,334 +521,245 @@ def plot_element_values( show_elem_numbering=show_elem_numbering, **kwargs ) - def element_displacement( - self, component="ALL", option="AVG" - ) -> np.ndarray: - """Return element displacement. - - One value per element. Either minimum, maximum, or average of - all nodes in each element. - - Equilvanent MAPDL commands: - - * ``ETABLE,VALUES,U,X`` - * ``PRETAB,VALUES`` or ``*VGET,TMP,ELEM,1,ETAB,VALUES`` - - Parameters - ---------- - component : str, optional - Structural displacement component to retrieve. Must be - ``"X"``, ``"Y"``, ``"Z"``, ``"ALL"`` or ``"NORM"``. Defaults to - ``"ALL"``. - option : str, optional - Option for storing element table data. One of the - following: - - * ``"MIN"`` : Store minimum element nodal value of the - specified item component. - * ``"MAX"`` : Store maximum element nodal value of the - specified item component. - * ``"AVG"`` : Store averaged element centroid value of the - specified item component (default). - - Examples - -------- - Return the average element displacement for all components. - - >>> arr = mapdl.post_processing.element_displacement("ALL") - >>> arr.shape - (2080, 3) - >>> arr - array([[ 1.07396154e-06, -9.03608033e-06, -5.17768108e-12], - [ 3.15631730e-06, -2.65527340e-05, 1.07714512e-11], - [ 5.12543515e-06, -4.31175194e-05, 2.19929719e-12], - ..., - [ 5.41204700e-06, -4.80335719e-05, 7.75819589e-11], - [ 3.33649806e-06, -2.96109417e-05, 1.44947535e-10], - [ 1.13836132e-06, -1.01038096e-05, 6.95566641e-11]]) + def _plot_point_scalars(self, scalars, show_node_numbering=False, **kwargs): + """Plot point scalars + Assumes scalars are from all nodes and not just the active surface. """ - check_elem_option(option) - component = component.upper() - check_comp(component, DISP_TYPE) - - if component in ["ALL", "NORM"]: - disp = np.vstack(( - self.element_values("U", "X", option), - self.element_values("U", "Y", option), - self.element_values("U", "Z", option) - )).T - if component == "NORM": - return np.linalg.norm(disp, axis=1) - return disp + if not scalars.size: + raise RuntimeError( + "Result unavailable. Either the result has not been loaded " + "with ``mapdl.set(step, sub_step)`` or the result does not " + "exist within the result file." + ) - return self.element_values("U", component, option) + sel = self.selected_nodes + all_scalars = np.zeros(sel.shape) + all_scalars[sel] = scalars - def plot_element_displacement( - self, component="NORM", option="AVG", show_elem_numbering=False, **kwargs - ) -> CameraPosition: - """Plot element displacement. + surf = self._mapdl.mesh._surf - Parameters - ---------- - component : str, optional - Structural displacement component to retrieve. Must be - ``"X"``, ``"Y"``, ``"Z"``, or ``"NORM"``. - option : str, optional - Option for storing element table data. One of the - following: + nods = surf["ansys_node_num"]-1 + all_scalars = all_scalars[nods] - * ``"MIN"`` : Store minimum element nodal value of the - specified item component. - * ``"MAX"`` : Store maximum element nodal value of the - specified item component. - * ``"AVG"`` : Store averaged element centroid value of the - specified item component (default). - show_element_numbering : bool, optional - Plot the element numbers of the elements. - **kwargs : dict, optional - Keyword arguments passed to :func:`general_plotter - `. + meshes = [ + { + "mesh": surf.copy(deep=False), # deep=False for ipyvtk-simple + "scalar_bar_args": {"title": kwargs.pop("stitle", "")}, + "scalars": all_scalars, + } + ] - Returns - ------- - pyvista.plotting.renderer.CameraPosition - Camera position from plotter. Can be reused as an input - parameter to use the same camera position for future - plots. Only returned when ``return_cpos`` is ``True``. + labels = [] + if show_node_numbering: + labels = [{"points": surf.points, "labels": surf["ansys_node_num"]}] - Examples - -------- - Plot the mean normalized element displacement for the first - result in the "X" direction. + return general_plotter(meshes, [], labels, **kwargs) - >>> mapdl.post1() - >>> mapdl.set(1, 1) - >>> mapdl.post_processing.plot_element_displacement( - ... "NORM", - ... option="AVG" - ... ) + def _plot_cell_scalars(self, scalars, show_elem_numbering=False, **kwargs): + """Plot cell scalars. + Assumes scalars are from all elements and not just the active surface. """ - if component.upper() == "ALL": - raise ValueError( - '"ALL" not allowed in this context. Select a ' - 'single displacement component (e.g. "X" or "NORM")' + if not scalars.size: + raise RuntimeError( + "Result unavailable. Either the result has not been loaded " + "with ``mapdl.set(step, sub_step)`` or the result does not " + "exist within the result file." ) - if component.upper() == "NORM": - disp = np.linalg.norm(self.element_displacement("ALL"), axis=1) + surf = self._mapdl.mesh._surf + + # as ``disp`` returns the result for all nodes, we need all node numbers + # and to index to the output node numbers + if hasattr(self._mapdl.mesh, "enum_all"): + enum = self._mapdl.mesh.enum else: - disp = self.element_displacement(component) - kwargs.setdefault("scalar_bar_args", {'title': f"{component} Element Displacement"}) - return self._plot_cell_scalars( - disp, show_elem_numbering=show_elem_numbering, **kwargs - ) + enum = self._all_enum - def element_stress(self, component, option='AVG') -> np.ndarray: - """Return element component or principal stress. + # it's possible that there are duplicated element numbers, + # therefore we need to get the unique values and a reverse index + uni, ridx = np.unique(surf["ansys_elem_num"], return_inverse=True) + mask = np.isin(enum, uni, assume_unique=True) - One value per element. Either minimum, maximum, or average of - all nodes in each element. + if scalars.size != mask.size: + scalars = scalars[self.selected_elements] + scalars = scalars[mask][ridx] - Equilvanent MAPDL commands: + meshes = [ + { + "mesh": surf.copy(deep=False), # deep=False for ipyvtk-simple + "scalar_bar_args": {"title": kwargs.pop("stitle", "")}, + "scalars": scalars, + } + ] - * ``ETABLE,VALUES,S,X`` - * ``PRETAB,VALUES`` or ``*VGET,TMP,ELEM,1,ETAB,VALUES`` + labels = [] + if show_elem_numbering: + labels = [{"points": surf.cell_centers().points, + "labels": surf["ansys_elem_num"]}] - Parameters - ---------- - component : str - Element stress to retrieve. One of the following: + return general_plotter(meshes, [], labels, **kwargs) - +---------------------+--------------------+ - | X, Y, Z, XY, YZ, XZ | Component stress. | - +---------------------+--------------------+ - | 1, 2, 3 | Principal stress. | - +---------------------+--------------------+ - | INT | Stress intensity. | - +---------------------+--------------------+ - | EQV | Equivalent stress | - +---------------------+--------------------+ + @property + @supress_logging + def _all_nnum(self): + self._mapdl.cm("__TMP_NODE__", "NODE") + self._mapdl.allsel() + nnum = self._mapdl.get_array("NODE", item1="NLIST") - option : str, optional - Option for storing element table data. One of the - following: + # rerun if encountered weird edge case of negative first index. + if nnum[0] == -1: + nnum = self._mapdl.get_array("NODE", item1="NLIST") + self._mapdl.cmsel("S", "__TMP_NODE__", "NODE") + return nnum.astype(np.int32, copy=False) - * ``"MIN"`` : Store minimum element nodal value of the - specified item component. - * ``"MAX"`` : Store maximum element nodal value of the - specified item component. - * ``"AVG"`` : Store averaged element centroid value of the - specified item component (default). + @property + @supress_logging + def _all_enum(self): + self._mapdl.cm("__TMP_ELEM__", "ELEM") + self._mapdl.allsel() + nnum = self._mapdl.get_array("ELEM", item1="ELIST") - Returns - ------- - numpy.ndarray - Numpy array of stresses. + # rerun if encountered weird edge case of negative first index. + if nnum[0] == -1: + enum = self._mapdl.get_array("ELEM", item1="ELIST") + self._mapdl.cmsel("S", "__TMP_ELEM__", "ELEM") + return enum.astype(np.int32, copy=False) - Examples - -------- - Return the average element component stress in the X direction. + @property + def _nsel(self): + """Return the MAPDL formatted selected nodes array. - >>> arr = mapdl.post_processing.element_stress("X") - >>> arr.shape - (2080, 3) - >>> arr - array([-0.29351357, -0.37027832, -0.37340827, ..., 0. , - 0. , 0. ]) + -1 for unselected + 0 for undefined + 1 for selected """ - component = elem_check_inputs(component, option, STRESS_TYPES) - return self.element_values("S", component, option) + return self._ndof_rst("NSEL").astype(np.int8) - def plot_element_stress( - self, - component, - option='AVG', - show_elem_numbering=False, - **kwargs - ) -> CameraPosition: - """Plot element component or principal stress. + @property + def selected_nodes(self) -> np.ndarray: + """Mask of the selected nodes. - One value per element. Either minimum, maximum, or average of - all nodes in each element. + Examples + -------- + >>> mapdl.post_processing.node_selection + array([False, False, False, ..., True, True, True]) - Parameters - ---------- - component : str - Element stress to retrieve. One of the following: + """ + return self._nsel == 1 - +---------------------+--------------------+ - | X, Y, Z, XY, YZ, XZ | Component stress. | - +---------------------+--------------------+ - | 1, 2, 3 | Principal stress. | - +---------------------+--------------------+ - | INT | Stress intensity. | - +---------------------+--------------------+ - | EQV | Equivalent stress | - +---------------------+--------------------+ + @property + def _esel(self): + """Return the MAPDL formatted selected elements array. - option : str, optional - Option for storing element table data. One of the - following: + -1 for unselected + 0 for undefined + 1 for selected - * ``"MIN"`` : Store minimum element nodal value of the - specified item component. - * ``"MAX"`` : Store maximum element nodal value of the - specified item component. - * ``"AVG"`` : Store averaged element centroid value of the - specified item component (default). + """ + return self._edof_rst("ESEL").astype(np.int8) - Returns - ------- - pyvista.plotting.renderer.CameraPosition - Camera position from plotter. Can be reused as an input - parameter to use the same camera position for future - plots. Only returned when ``return_cpos`` is ``True``. + @property + def selected_elements(self) -> np.ndarray: + """Mask of the selected elements. Examples -------- - Plot the average element component stress in the X direction. - - >>> mapdl.post_processing.plot_element_stress("X") + >>> mapdl.post_processing.selected_elements + array([False, False, False, ..., True, True, True]) """ - component = str(component).upper() - stress = self.element_stress(component, option=option) + return self._esel == 1 - if component in COMPONENT_STRESS_TYPE: - kwargs.setdefault("scalar_bar_args", {'title': f"{component} Component Element Stress"}) - elif component in ["1", "2", "3"]: - kwargs.setdefault("scalar_bar_args", {'title': f"{component} Principal Element Stress"}) - elif component == "INT": - kwargs.setdefault("scalar_bar_args", {'title': "Element Stress Intensity"}) - elif component == "EQV": - kwargs.setdefault("scalar_bar_args", {'title': "Element Equivalent Stress"}) + @check_result_loaded + def _ndof_rst(self, item, it1num="", item2=""): + """Nodal degree of freedom result using :func:`Mapdl.get_array() np.ndarray: - """Return element temperature. + """ + return self._mapdl.get_array("NODE", item1=item, it1num=it1num, item2=item2) - One value per element. Either minimum, maximum, or average of - all nodes in each element. + @check_result_loaded + def _edof_rst(self, item, it1num=""): + """Element degree of freedom result""" + return self._mapdl.get_array("ELEM", item1=item, it1num=it1num) - Equilvanent MAPDL commands: + @property + def nodal_temperature(self) -> np.ndarray: + """The nodal temperature of the current result. - * ``ETABLE,VALUES,TEMP`` - * ``PRETAB,VALUES`` or ``*VGET,TMP,ELEM,1,ETAB,VALUES`` + Equilvanent MAPDL command: + ``PRNSOL, TEMP`` - Parameters - ---------- - option : str, optional - Option for storing element table data. One of the - following: + Notes + ----- + The nodal results are averaged across all selected elements. + Not all nodes will contain valid results (e.g. midside nodes), + and those nodes will report a zero value. - * ``"MIN"`` : Store minimum element nodal value of the - specified item. - * ``"MAX"`` : Store maximum element nodal value of the - specified item. - * ``"AVG"`` : Store averaged element centroid value of the - specified item (default). + Elements that are not selected will not contribute to the + averaged nodal values, and if a node's attached elements are + all unselected, the element will report a zero value. Examples -------- - Return the average element temperature. - - >>> arr = mapdl.post_processing.element_temperature() - >>> arr.shape - (2080, 3) - >>> arr - array([20., 20., 20., ..., 20., 20., 20.]) + >>> mapdl.post_processing.temperature + array([0., 0., 0., ..., 0., 0., 0.]) """ - return self.element_values("TEMP", option=option) - - def plot_element_temperature( - self, - option='AVG', - show_elem_numbering=False, - **kwargs - ) -> CameraPosition: - """Plot element temperature. + return self.nodal_values("TEMP") - One value per element. Either minimum, maximum, or average of - all nodes in each element. + def plot_nodal_temperature(self, show_node_numbering=False, **kwargs): + """Plot nodal temperature of the current result. Parameters ---------- - option : str, optional - Option for storing element table data. One of the - following: + show_node_numbering : bool, optional + Plot the node numbers of surface nodes. - * ``"MIN"`` : Store minimum element nodal value of the - specified item component. - * ``"MAX"`` : Store maximum element nodal value of the - specified item component. - * ``"AVG"`` : Store averaged element centroid value of the - specified item component (default). + **kwargs : dict, optional + Keyword arguments passed to :func:`general_plotter + `. Returns ------- - pyvista.plotting.renderer.CameraPosition + list Camera position from plotter. Can be reused as an input parameter to use the same camera position for future - plots. Only returned when ``return_cpos`` is ``True``. + plots. Examples -------- - Plot the average element temperature. + Plot the nodal temperature for the second result - >>> arr = mapdl.post_processing.plot_element_temperature() + >>> mapdl.post1() + >>> mapdl.set(1, 2) + >>> mapdl.post_processing.temperature() - """ - scalars = self.element_temperature(option) + Plot off_screen and save a screenshot - return self._plot_cell_scalars( - scalars, show_elem_numbering=show_elem_numbering, **kwargs + >>> mapdl.post_processing.plot_nodal_temperature(off_screen=True, + ... savefig='temp_1_2.png') + + Subselect a single result type and plot those stress results. + + >>> mapdl.esel('S', 'TYPE', vmin=1) + >>> mapdl.post_processing.plot_nodal_temperature(smooth_shading=True) + """ + kwargs.setdefault("scalar_bar_args", {'title': "Nodal\nTemperature"}) + return self._plot_point_scalars( + self.nodal_temperature, show_node_numbering=show_node_numbering, **kwargs ) def nodal_displacement(self, component="NORM") -> np.ndarray: @@ -901,15 +814,15 @@ def nodal_displacement(self, component="NORM") -> np.ndarray: component = check_comp(component, DISP_TYPE) if component in ["NORM", "ALL"]: - x = self._ndof_rst("U", "X") - y = self._ndof_rst("U", "Y") - z = self._ndof_rst("U", "Z") + x = self.nodal_values("U", "X") + y = self.nodal_values("U", "Y") + z = self.nodal_values("U", "Z") disp = np.vstack((x, y, z)) if component == "NORM": return np.linalg.norm(disp, axis=0) return disp.T - return self._ndof_rst("U", component) + return self.nodal_values("U", component) def plot_nodal_displacement( self, component="NORM", show_node_numbering=False, **kwargs @@ -937,380 +850,463 @@ def plot_nodal_displacement( Examples -------- - Plot the normalized nodal displacement for the second result. + Plot the normalized nodal displacement for the second result. + + >>> mapdl.post1() + >>> mapdl.set(1, 2) + >>> mapdl.post_processing.plot_nodal_displacement('NORM', + ... smooth_shading=True) + + Plot the x displacement without smooth shading with individual + node numbering. + + >>> mapdl.post_processing.plot_nodal_displacement('X', + ... show_node_numbering=True) + """ + if isinstance(component, str): + if component.upper() == "ALL": + raise ValueError( + '"ALL" not allowed in this context. Select a ' + 'single displacement component (e.g. "X")' + ) + + disp = self.nodal_displacement(component) + kwargs.setdefault("scalar_bar_args", {'title': "%s Displacement" % component}) + return self._plot_point_scalars( + disp, show_node_numbering=show_node_numbering, **kwargs + ) + + def nodal_rotation(self, component="ALL") -> np.ndarray: + """Nodal X, Y, or Z structural rotation + + Equilvanent MAPDL commands: + + * ``PRNSOL, ROT, X`` + * ``PRNSOL, ROT, Y`` + * ``PRNSOL, ROT, Z`` + + Parameters + ---------- + component : str, optional + Structural rotational component to retrieve. Must be + ``'X'``, ``'Y'``, ``'Z'``, or ``'ALL'``. Defaults to ``'ALL'``. + + Returns + ------- + numpy.ndarray + Numpy array with nodal X, Y, Z, or all structural rotations. + + Notes + ----- + This command always returns all nodal rotations regardless of + if the nodes are selected or not. Use the + :attr:`selected_nodes ` mask to + get the currently selected nodes. + + Examples + -------- + Nodal rotation in all dimensions for current result. + + >>> mapdl.post1() + >>> mapdl.set(1, 1) + >>> mapdl.post_processing.nodal_rotation('ALL') + array([[0., 0., 0.], + [0., 0., 0.], + [0., 0., 0.], + ..., + [0., 0., 0.], + [0., 0., 0.], + [0., 0., 0.]]) + + Nodes corresponding to the nodal rotations. + + >>> mapdl.mesh.nnum_all + array([ 1, 2, 3, ..., 7215, 7216, 7217], dtype=int32) + + """ + component = check_comp(component, ROT_TYPE) + + if component == "ALL": + x = self.nodal_values("ROT", "X") + y = self.nodal_values("ROT", "Y") + z = self.nodal_values("ROT", "Z") + return np.vstack((x, y, z)).T + + return self.nodal_values("ROT", component) + + def plot_nodal_rotation(self, component, show_node_numbering=False, **kwargs): + """Plot nodal rotation. + + Parameters + ---------- + component : str + Structural rotation component to retrieve. Must be + ``'X'``, ``'Y'``, or ``'Z'``. + + show_node_numbering : bool, optional + Plot the node numbers of surface nodes. + + **kwargs : dict, optional + Keyword arguments passed to :func:`general_plotter + `. + + Returns + ------- + list + Camera position from plotter. Can be reused as an input + parameter to use the same camera position for future + plots. + + Examples + -------- + Plot the x rotation without smooth shading with individual + node numbering. >>> mapdl.post1() >>> mapdl.set(1, 2) - >>> mapdl.post_processing.plot_nodal_displacement('NORM', - ... smooth_shading=True) - - Plot the x displacement without smooth shading with individual - node numbering. - - >>> mapdl.post_processing.plot_nodal_displacement('X', - ... show_node_numbering=True) + >>> mapdl.post_processing.plot_nodal_rotation('X', show_node_numbering=True) """ if isinstance(component, str): if component.upper() == "ALL": raise ValueError( '"ALL" not allowed in this context. Select a ' - 'single displacement component (e.g. "X")' + 'single component (e.g. "X")' ) - disp = self.nodal_displacement(component) - kwargs.setdefault("scalar_bar_args", {'title': "%s Displacement" % component}) + disp = self.nodal_rotation(component) + kwargs.setdefault("scalar_bar_args", {'title': f"{component} Rotation"}) return self._plot_point_scalars( disp, show_node_numbering=show_node_numbering, **kwargs ) - def _plot_point_scalars(self, scalars, show_node_numbering=False, **kwargs): - """Plot point scalars - - Assumes scalars are from all nodes and not just the active surface. - """ - if not scalars.size: - raise RuntimeError( - "Result unavailable. Either the result has not been loaded " - "with ``mapdl.set(step, sub_step)`` or the result does not " - "exist within the result file." - ) + def element_displacement( + self, component="ALL", option="AVG" + ) -> np.ndarray: + """Return element displacement. - surf = self._mapdl.mesh._surf[self.selected_elements] + One value per element. Either minimum, maximum, or average of + all nodes in each element. - # as ``disp`` returns the result for all nodes, we need all node numbers - # and to index to the output node numbers - if hasattr(self._mapdl.mesh, "nnum_all"): - nnum = self._mapdl.mesh.nnum_all - else: - nnum = self._all_nnum + Equilvanent MAPDL commands: - mask = np.in1d(nnum, surf["ansys_node_num"]) - ridx = np.argsort(np.argsort(surf["ansys_node_num"])) - if scalars.size != mask.size: - scalars = scalars[self.selected_nodes] - scalars = scalars[mask][ridx] + * ``ETABLE,VALUES,U,X`` + * ``PRETAB,VALUES`` or ``*VGET,TMP,ELEM,1,ETAB,VALUES`` - meshes = [ - { - "mesh": surf.copy(deep=False), # deep=False for ipyvtk-simple - "scalar_bar_args": {"title": kwargs.pop("stitle", "")}, - "scalars": scalars, - } - ] + Parameters + ---------- + component : str, optional + Structural displacement component to retrieve. Must be + ``"X"``, ``"Y"``, ``"Z"``, ``"ALL"`` or ``"NORM"``. Defaults to + ``"ALL"``. + option : str, optional + Option for storing element table data. One of the + following: - labels = [] - if show_node_numbering: - labels = [{"points": surf.points, "labels": surf["ansys_node_num"]}] + * ``"MIN"`` : Store minimum element nodal value of the + specified item component. + * ``"MAX"`` : Store maximum element nodal value of the + specified item component. + * ``"AVG"`` : Store averaged element centroid value of the + specified item component (default). - return general_plotter(meshes, [], labels, **kwargs) + Examples + -------- + Return the average element displacement for all components. - def _plot_cell_scalars(self, scalars, show_elem_numbering=False, **kwargs): - """Plot cell scalars. + >>> arr = mapdl.post_processing.element_displacement("ALL") + >>> arr.shape + (2080, 3) + >>> arr + array([[ 1.07396154e-06, -9.03608033e-06, -5.17768108e-12], + [ 3.15631730e-06, -2.65527340e-05, 1.07714512e-11], + [ 5.12543515e-06, -4.31175194e-05, 2.19929719e-12], + ..., + [ 5.41204700e-06, -4.80335719e-05, 7.75819589e-11], + [ 3.33649806e-06, -2.96109417e-05, 1.44947535e-10], + [ 1.13836132e-06, -1.01038096e-05, 6.95566641e-11]]) - Assumes scalars are from all elements and not just the active surface. """ - if not scalars.size: - raise RuntimeError( - "Result unavailable. Either the result has not been loaded " - "with ``mapdl.set(step, sub_step)`` or the result does not " - "exist within the result file." - ) - - surf = self._mapdl.mesh._surf - - # as ``disp`` returns the result for all nodes, we need all node numbers - # and to index to the output node numbers - if hasattr(self._mapdl.mesh, "enum_all"): - enum = self._mapdl.mesh.enum - else: - enum = self._all_enum - - # it's possible that there are duplicated element numbers, - # therefore we need to get the unique values and a reverse index - uni, ridx = np.unique(surf["ansys_elem_num"], return_inverse=True) - mask = np.isin(enum, uni, assume_unique=True) - - if scalars.size != mask.size: - scalars = scalars[self.selected_elements] - scalars = scalars[mask][ridx] - - meshes = [ - { - "mesh": surf.copy(deep=False), # deep=False for ipyvtk-simple - "scalar_bar_args": {"title": kwargs.pop("stitle", "")}, - "scalars": scalars, - } - ] - - labels = [] - if show_elem_numbering: - labels = [{"points": surf.cell_centers().points, - "labels": surf["ansys_elem_num"]}] - - return general_plotter(meshes, [], labels, **kwargs) - - @property - @supress_logging - def _all_nnum(self): - self._mapdl.cm("__TMP_NODE__", "NODE") - self._mapdl.allsel() - nnum = self._mapdl.get_array("NODE", item1="NLIST") - - # rerun if encountered weird edge case of negative first index. - if nnum[0] == -1: - nnum = self._mapdl.get_array("NODE", item1="NLIST") - self._mapdl.cmsel("S", "__TMP_NODE__", "NODE") - return nnum.astype(np.int32, copy=False) + check_elem_option(option) + component = component.upper() + check_comp(component, DISP_TYPE) - @property - @supress_logging - def _all_enum(self): - self._mapdl.cm("__TMP_ELEM__", "ELEM") - self._mapdl.allsel() - nnum = self._mapdl.get_array("ELEM", item1="ELIST") + if component in ["ALL", "NORM"]: + disp = np.vstack(( + self.element_values("U", "X", option), + self.element_values("U", "Y", option), + self.element_values("U", "Z", option) + )).T + if component == "NORM": + return np.linalg.norm(disp, axis=1) + return disp - # rerun if encountered weird edge case of negative first index. - if nnum[0] == -1: - enum = self._mapdl.get_array("ELEM", item1="ELIST") - self._mapdl.cmsel("S", "__TMP_ELEM__", "ELEM") - return enum.astype(np.int32, copy=False) + return self.element_values("U", component, option) - @property - def _nsel(self): - """Return the MAPDL formatted selected nodes array. + def plot_element_displacement( + self, component="NORM", option="AVG", show_elem_numbering=False, **kwargs + ) -> CameraPosition: + """Plot element displacement. - -1 for unselected - 0 for undefined - 1 for selected + Parameters + ---------- + component : str, optional + Structural displacement component to retrieve. Must be + ``"X"``, ``"Y"``, ``"Z"``, or ``"NORM"``. + option : str, optional + Option for storing element table data. One of the + following: - """ - return self._ndof_rst("NSEL").astype(np.int8) + * ``"MIN"`` : Store minimum element nodal value of the + specified item component. + * ``"MAX"`` : Store maximum element nodal value of the + specified item component. + * ``"AVG"`` : Store averaged element centroid value of the + specified item component (default). + show_element_numbering : bool, optional + Plot the element numbers of the elements. + **kwargs : dict, optional + Keyword arguments passed to :func:`general_plotter + `. - @property - def selected_nodes(self) -> np.ndarray: - """Mask of the selected nodes. + Returns + ------- + pyvista.plotting.renderer.CameraPosition + Camera position from plotter. Can be reused as an input + parameter to use the same camera position for future + plots. Only returned when ``return_cpos`` is ``True``. Examples -------- - >>> mapdl.post_processing.node_selection - array([False, False, False, ..., True, True, True]) - - """ - return self._nsel == 1 - - @property - def _esel(self): - """Return the MAPDL formatted selected elements array. + Plot the mean normalized element displacement for the first + result in the "X" direction. - -1 for unselected - 0 for undefined - 1 for selected + >>> mapdl.post1() + >>> mapdl.set(1, 1) + >>> mapdl.post_processing.plot_element_displacement( + ... "NORM", + ... option="AVG" + ... ) """ - return self._edof_rst("ESEL").astype(np.int8) - - @property - def selected_elements(self) -> np.ndarray: - """Mask of the selected elements. + if component.upper() == "ALL": + raise ValueError( + '"ALL" not allowed in this context. Select a ' + 'single displacement component (e.g. "X" or "NORM")' + ) - Examples - -------- - >>> mapdl.post_processing.selected_elements - array([False, False, False, ..., True, True, True]) + if component.upper() == "NORM": + disp = np.linalg.norm(self.element_displacement("ALL"), axis=1) + else: + disp = self.element_displacement(component) + kwargs.setdefault("scalar_bar_args", {'title': f"{component} Element Displacement"}) + return self._plot_cell_scalars( + disp, show_elem_numbering=show_elem_numbering, **kwargs + ) - """ - return self._esel == 1 + def element_stress(self, component, option='AVG') -> np.ndarray: + """Return element component or principal stress. - def nodal_rotation(self, component="ALL") -> np.ndarray: - """Nodal X, Y, or Z structural rotation + One value per element. Either minimum, maximum, or average of + all nodes in each element. Equilvanent MAPDL commands: - * ``PRNSOL, ROT, X`` - * ``PRNSOL, ROT, Y`` - * ``PRNSOL, ROT, Z`` + * ``ETABLE,VALUES,S,X`` + * ``PRETAB,VALUES`` or ``*VGET,TMP,ELEM,1,ETAB,VALUES`` Parameters ---------- - component : str, optional - Structural rotational component to retrieve. Must be - ``'X'``, ``'Y'``, ``'Z'``, or ``'ALL'``. Defaults to ``'ALL'``. + component : str + Element stress to retrieve. One of the following: + + +---------------------+--------------------+ + | X, Y, Z, XY, YZ, XZ | Component stress. | + +---------------------+--------------------+ + | 1, 2, 3 | Principal stress. | + +---------------------+--------------------+ + | INT | Stress intensity. | + +---------------------+--------------------+ + | EQV | Equivalent stress | + +---------------------+--------------------+ + + option : str, optional + Option for storing element table data. One of the + following: + + * ``"MIN"`` : Store minimum element nodal value of the + specified item component. + * ``"MAX"`` : Store maximum element nodal value of the + specified item component. + * ``"AVG"`` : Store averaged element centroid value of the + specified item component (default). Returns ------- numpy.ndarray - Numpy array with nodal X, Y, Z, or all structural rotations. - - Notes - ----- - This command always returns all nodal rotations regardless of - if the nodes are selected or not. Use the - :attr:`selected_nodes ` mask to - get the currently selected nodes. + Numpy array of stresses. Examples -------- - Nodal rotation in all dimensions for current result. - - >>> mapdl.post1() - >>> mapdl.set(1, 1) - >>> mapdl.post_processing.nodal_rotation('ALL') - array([[0., 0., 0.], - [0., 0., 0.], - [0., 0., 0.], - ..., - [0., 0., 0.], - [0., 0., 0.], - [0., 0., 0.]]) - - Nodes corresponding to the nodal rotations. + Return the average element component stress in the X direction. - >>> mapdl.mesh.nnum_all - array([ 1, 2, 3, ..., 7215, 7216, 7217], dtype=int32) + >>> arr = mapdl.post_processing.element_stress("X") + >>> arr.shape + (2080, 3) + >>> arr + array([-0.29351357, -0.37027832, -0.37340827, ..., 0. , + 0. , 0. ]) """ - component = check_comp(component, ROT_TYPE) - - if component == "ALL": - x = self._ndof_rst("ROT", "X") - y = self._ndof_rst("ROT", "Y") - z = self._ndof_rst("ROT", "Z") - return np.vstack((x, y, z)).T + component = elem_check_inputs(component, option, STRESS_TYPES) + return self.element_values("S", component, option) - return self._ndof_rst("ROT", component) + def plot_element_stress( + self, + component, + option='AVG', + show_elem_numbering=False, + **kwargs + ) -> CameraPosition: + """Plot element component or principal stress. - def plot_nodal_rotation(self, component, show_node_numbering=False, **kwargs): - """Plot nodal rotation. + One value per element. Either minimum, maximum, or average of + all nodes in each element. Parameters ---------- component : str - Structural rotation component to retrieve. Must be - ``'X'``, ``'Y'``, or ``'Z'``. + Element stress to retrieve. One of the following: - show_node_numbering : bool, optional - Plot the node numbers of surface nodes. + +---------------------+--------------------+ + | X, Y, Z, XY, YZ, XZ | Component stress. | + +---------------------+--------------------+ + | 1, 2, 3 | Principal stress. | + +---------------------+--------------------+ + | INT | Stress intensity. | + +---------------------+--------------------+ + | EQV | Equivalent stress | + +---------------------+--------------------+ - **kwargs : dict, optional - Keyword arguments passed to :func:`general_plotter - `. + option : str, optional + Option for storing element table data. One of the + following: + + * ``"MIN"`` : Store minimum element nodal value of the + specified item component. + * ``"MAX"`` : Store maximum element nodal value of the + specified item component. + * ``"AVG"`` : Store averaged element centroid value of the + specified item component (default). Returns ------- - list + pyvista.plotting.renderer.CameraPosition Camera position from plotter. Can be reused as an input parameter to use the same camera position for future - plots. + plots. Only returned when ``return_cpos`` is ``True``. Examples -------- - Plot the x rotation without smooth shading with individual - node numbering. + Plot the average element component stress in the X direction. - >>> mapdl.post1() - >>> mapdl.set(1, 2) - >>> mapdl.post_processing.plot_nodal_rotation('X', show_node_numbering=True) - """ - if isinstance(component, str): - if component.upper() == "ALL": - raise ValueError( - '"ALL" not allowed in this context. Select a ' - 'single component (e.g. "X")' - ) + >>> mapdl.post_processing.plot_element_stress("X") - disp = self.nodal_rotation(component) - kwargs.setdefault("scalar_bar_args", {'title': f"{component} Rotation"}) - return self._plot_point_scalars( - disp, show_node_numbering=show_node_numbering, **kwargs - ) + """ + component = str(component).upper() + stress = self.element_stress(component, option=option) - @check_result_loaded - def _ndof_rst(self, item, it1num="", item2=""): - """Nodal degree of freedom result using :func:`Mapdl.get_array() np.ndarray: + """Return element temperature. - @check_result_loaded - def _edof_rst(self, item, it1num=""): - """Element degree of freedom result""" - return self._mapdl.get_array("ELEM", item1=item, it1num=it1num) + One value per element. Either minimum, maximum, or average of + all nodes in each element. - @property - def nodal_temperature(self) -> np.ndarray: - """The nodal temperature of the current result. + Equilvanent MAPDL commands: - Equilvanent MAPDL command: - ``PRNSOL, TEMP`` + * ``ETABLE,VALUES,TEMP`` + * ``PRETAB,VALUES`` or ``*VGET,TMP,ELEM,1,ETAB,VALUES`` - Notes - ----- - The nodal results are averaged across all selected elements. - Not all nodes will contain valid results (e.g. midside nodes), - and those nodes will report a zero value. + Parameters + ---------- + option : str, optional + Option for storing element table data. One of the + following: - Elements that are not selected will not contribute to the - averaged nodal values, and if a node's attached elements are - all unselected, the element will report a zero value. + * ``"MIN"`` : Store minimum element nodal value of the + specified item. + * ``"MAX"`` : Store maximum element nodal value of the + specified item. + * ``"AVG"`` : Store averaged element centroid value of the + specified item (default). Examples -------- - >>> mapdl.post_processing.temperature - array([0., 0., 0., ..., 0., 0., 0.]) + Return the average element temperature. + + >>> arr = mapdl.post_processing.element_temperature() + >>> arr.shape + (2080, 3) + >>> arr + array([20., 20., 20., ..., 20., 20., 20.]) """ - return self._ndof_rst("TEMP") + return self.element_values("TEMP", option=option) - def plot_nodal_temperature(self, show_node_numbering=False, **kwargs): - """Plot nodal temperature of the current result. + def plot_element_temperature( + self, + option='AVG', + show_elem_numbering=False, + **kwargs + ) -> CameraPosition: + """Plot element temperature. + + One value per element. Either minimum, maximum, or average of + all nodes in each element. Parameters ---------- - show_node_numbering : bool, optional - Plot the node numbers of surface nodes. + option : str, optional + Option for storing element table data. One of the + following: - **kwargs : dict, optional - Keyword arguments passed to :func:`general_plotter - `. + * ``"MIN"`` : Store minimum element nodal value of the + specified item component. + * ``"MAX"`` : Store maximum element nodal value of the + specified item component. + * ``"AVG"`` : Store averaged element centroid value of the + specified item component (default). Returns ------- - list + pyvista.plotting.renderer.CameraPosition Camera position from plotter. Can be reused as an input parameter to use the same camera position for future - plots. + plots. Only returned when ``return_cpos`` is ``True``. Examples -------- - Plot the nodal temperature for the second result - - >>> mapdl.post1() - >>> mapdl.set(1, 2) - >>> mapdl.post_processing.temperature() - - Plot off_screen and save a screenshot - - >>> mapdl.post_processing.plot_nodal_temperature(off_screen=True, - ... savefig='temp_1_2.png') + Plot the average element temperature. - Subselect a single result type and plot those stress results. + >>> arr = mapdl.post_processing.plot_element_temperature() - >>> mapdl.esel('S', 'TYPE', vmin=1) - >>> mapdl.post_processing.plot_nodal_temperature(smooth_shading=True) """ - kwargs.setdefault("scalar_bar_args", {'title': "Nodal\nTemperature"}) - return self._plot_point_scalars( - self.nodal_temperature, show_node_numbering=show_node_numbering, **kwargs + scalars = self.element_temperature(option) + + return self._plot_cell_scalars( + scalars, show_elem_numbering=show_elem_numbering, **kwargs ) @property @@ -1336,7 +1332,7 @@ def nodal_pressure(self) -> np.ndarray: array([0., 0., 0., ..., 0., 0., 0.]) """ - return self._ndof_rst("PRES") + return self.nodal_values("PRES") def plot_nodal_pressure(self, show_node_numbering=False, **kwargs): """Plot nodal pressure of the current result. @@ -1411,7 +1407,7 @@ def nodal_voltage(self) -> np.ndarray: >>> mapdl.post_processing.voltage array([0., 0., 0., ..., 0., 0., 0.]) """ - return self._ndof_rst("VOLT") + return self.nodal_values("VOLT") def plot_nodal_voltage(self, show_node_numbering=False, **kwargs): """Plot nodal voltage of the current result. @@ -1498,7 +1494,7 @@ def nodal_component_stress(self, component) -> np.ndarray: """ component = check_comp(component, COMPONENT_STRESS_TYPE) - return self._ndof_rst("S", component) + return self.nodal_values("S", component) def plot_nodal_component_stress( self, component, show_node_numbering=False, **kwargs @@ -1582,7 +1578,7 @@ def nodal_principal_stress(self, component) -> np.ndarray: if isinstance(component, int): component = str(component) component = check_comp(component, PRINCIPAL_TYPE) - return self._ndof_rst("S", component) + return self.nodal_values("S", component) def plot_nodal_principal_stress( self, component, show_node_numbering=False, **kwargs @@ -1648,7 +1644,7 @@ def nodal_stress_intensity(self) -> np.ndarray: 0. , 0. , 0. ]) """ - return self._ndof_rst("S", "INT") + return self.nodal_values("S", "INT") def plot_nodal_stress_intensity(self, show_node_numbering=False, **kwargs): """Plot the nodal stress intensity of the current result. @@ -1731,7 +1727,7 @@ def nodal_eqv_stress(self) -> np.ndarray: 0. , 0. , 0. ]) """ - return self._ndof_rst("S", "EQV") + return self.nodal_values("S", "EQV") def plot_nodal_eqv_stress(self, show_node_numbering=False, **kwargs): """Plot nodal equivalent stress of the current result. @@ -1822,7 +1818,7 @@ def nodal_total_component_strain(self, component) -> np.ndarray: if isinstance(component, int): component = str(component) component = check_comp(component, COMPONENT_STRESS_TYPE) - return self._ndof_rst("EPTO", component) + return self.nodal_values("EPTO", component) def plot_nodal_total_component_strain( self, component, show_node_numbering=False, **kwargs @@ -1910,7 +1906,7 @@ def nodal_total_principal_strain(self, component) -> np.ndarray: if isinstance(component, int): component = str(component) component = check_comp(component, PRINCIPAL_TYPE) - return self._ndof_rst("EPTO", component) + return self.nodal_values("EPTO", component) def plot_nodal_total_principal_strain( self, component, show_node_numbering=False, **kwargs @@ -1981,7 +1977,7 @@ def nodal_total_strain_intensity(self) -> np.ndarray: 0. , 0. , 0. ]) """ - return self._ndof_rst("EPEL", "INT") + return self.nodal_values("EPEL", "INT") def plot_nodal_total_strain_intensity(self, show_node_numbering=False, **kwargs): """Plot the total nodal strain intensity of the current result. @@ -2060,7 +2056,7 @@ def nodal_total_eqv_strain(self) -> np.ndarray: 0. , 0. , 0. ]) """ - return self._ndof_rst("EPTO", "EQV") + return self.nodal_values("EPTO", "EQV") def plot_nodal_total_eqv_strain(self, show_node_numbering=False, **kwargs): """Plot the total nodal equivalent strain of the current result. @@ -2143,7 +2139,7 @@ def nodal_elastic_component_strain(self, component) -> np.ndarray: if isinstance(component, int): component = str(component) component = check_comp(component, COMPONENT_STRESS_TYPE) - return self._ndof_rst("EPEL", component) + return self.nodal_values("EPEL", component) def plot_nodal_elastic_component_strain( self, component, show_node_numbering=False, **kwargs @@ -2226,7 +2222,7 @@ def nodal_elastic_principal_strain(self, component) -> np.ndarray: if isinstance(component, int): component = str(component) component = check_comp(component, PRINCIPAL_TYPE) - return self._ndof_rst("EPEL", component) + return self.nodal_values("EPEL", component) def plot_nodal_elastic_principal_strain( self, component, show_node_numbering=False, **kwargs @@ -2299,7 +2295,7 @@ def nodal_elastic_strain_intensity(self) -> np.ndarray: 0. , 0. , 0. ]) """ - return self._ndof_rst("EPEL", "INT") + return self.nodal_values("EPEL", "INT") def plot_nodal_elastic_strain_intensity(self, show_node_numbering=False, **kwargs): """Plot the elastic nodal strain intensity of the current result. @@ -2378,7 +2374,7 @@ def nodal_elastic_eqv_strain(self) -> np.ndarray: 0. , 0. , 0. ]) """ - return self._ndof_rst("EPEL", "EQV") + return self.nodal_values("EPEL", "EQV") def plot_nodal_elastic_eqv_strain(self, show_node_numbering=False, **kwargs): """Plot the elastic nodal equivalent strain of the current result. @@ -2466,7 +2462,7 @@ def nodal_plastic_component_strain(self, component) -> np.ndarray: if isinstance(component, int): component = str(component) component = check_comp(component, COMPONENT_STRESS_TYPE) - return self._ndof_rst("EPPL", component) + return self.nodal_values("EPPL", component) def plot_nodal_plastic_component_strain( self, component, show_node_numbering=False, **kwargs @@ -2543,7 +2539,7 @@ def nodal_plastic_principal_strain(self, component) -> np.ndarray: if isinstance(component, int): component = str(component) component = check_comp(component, PRINCIPAL_TYPE) - return self._ndof_rst("EPPL", component) + return self.nodal_values("EPPL", component) def plot_nodal_plastic_principal_strain( self, component, show_node_numbering=False, **kwargs @@ -2617,7 +2613,7 @@ def nodal_plastic_strain_intensity(self) -> np.ndarray: 0. , 0. , 0. ]) """ - return self._ndof_rst("EPPL", "INT") + return self.nodal_values("EPPL", "INT") def plot_nodal_plastic_strain_intensity(self, show_node_numbering=False, **kwargs): """Plot the plastic nodal strain intensity of the current result. @@ -2703,7 +2699,7 @@ def nodal_plastic_eqv_strain(self) -> np.ndarray: 0. , 0. , 0. ]) """ - return self._ndof_rst("EPPL", "EQV") + return self.nodal_values("EPPL", "EQV") def plot_nodal_plastic_eqv_strain(self, show_node_numbering=False, **kwargs): """Plot the plastic nodal equivalent strain of the current result. @@ -2792,7 +2788,7 @@ def nodal_thermal_component_strain(self, component) -> np.ndarray: if isinstance(component, int): component = str(component) component = check_comp(component, COMPONENT_STRESS_TYPE) - return self._ndof_rst("EPTH", component) + return self.nodal_values("EPTH", component) def plot_nodal_thermal_component_strain( self, component, show_node_numbering=False, **kwargs @@ -2875,7 +2871,7 @@ def nodal_thermal_principal_strain(self, component) -> np.ndarray: if isinstance(component, int): component = str(component) component = check_comp(component, PRINCIPAL_TYPE) - return self._ndof_rst("EPTH", component) + return self.nodal_values("EPTH", component) def plot_nodal_thermal_principal_strain( self, component, show_node_numbering=False, **kwargs @@ -2949,7 +2945,7 @@ def nodal_thermal_strain_intensity(self) -> np.ndarray: 0. , 0. , 0. ]) """ - return self._ndof_rst("EPTH", "INT") + return self.nodal_values("EPTH", "INT") def plot_nodal_thermal_strain_intensity(self, show_node_numbering=False, **kwargs): """Plot the thermal nodal strain intensity of the current result. @@ -3032,7 +3028,7 @@ def nodal_thermal_eqv_strain(self) -> np.ndarray: 0. , 0. , 0. ]) """ - return self._ndof_rst("EPTH", "EQV") + return self.nodal_values("EPTH", "EQV") def plot_nodal_thermal_eqv_strain(self, show_node_numbering=False, **kwargs): """Plot the thermal nodal equivalent strain of the current result. @@ -3108,7 +3104,7 @@ def nodal_contact_friction_stress(self) -> np.ndarray: 0. , 0. , 0. ]) """ - return self._ndof_rst("CONT", "SFRIC") + return self.nodal_values("CONT", "SFRIC") def plot_nodal_contact_friction_stress(self, show_node_numbering=False, **kwargs): """Plot the nodal contact friction stress of the current result. From 842db6c637bb9d5bb7c0794ef97e1f520f59cfd7 Mon Sep 17 00:00:00 2001 From: German Date: Fri, 10 Dec 2021 13:18:17 +0100 Subject: [PATCH 3/7] Added `nodal_values` which is used heavily in the post_processing module instead of `_ndof_rst`. `nodal_values` returns only the values of the selected nodes. Improved `_plot_point_scalars` to account for when all the nodes are not selected. There has been some reorg in the functions order. --- ansys/mapdl/core/post.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ansys/mapdl/core/post.py b/ansys/mapdl/core/post.py index 94f482f53e..4354c26f48 100644 --- a/ansys/mapdl/core/post.py +++ b/ansys/mapdl/core/post.py @@ -522,10 +522,7 @@ def plot_element_values( ) def _plot_point_scalars(self, scalars, show_node_numbering=False, **kwargs): - """Plot point scalars - - Assumes scalars are from all nodes and not just the active surface. - """ + """Plot point scalars""" if not scalars.size: raise RuntimeError( "Result unavailable. Either the result has not been loaded " From cb861b7a44f8600097672ba262027d8caf8a411e Mon Sep 17 00:00:00 2001 From: German Date: Fri, 10 Dec 2021 13:21:15 +0100 Subject: [PATCH 4/7] Added `nodal_values` which is used heavily in the post_processing module instead of `_ndof_rst`. `nodal_values` returns only the values of the selected nodes. Improved `_plot_point_scalars` to account for when all the nodes are not selected. There has been some reorg in the functions order. --- ansys/mapdl/core/post.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ansys/mapdl/core/post.py b/ansys/mapdl/core/post.py index 4354c26f48..44eab7e769 100644 --- a/ansys/mapdl/core/post.py +++ b/ansys/mapdl/core/post.py @@ -554,10 +554,7 @@ def _plot_point_scalars(self, scalars, show_node_numbering=False, **kwargs): return general_plotter(meshes, [], labels, **kwargs) def _plot_cell_scalars(self, scalars, show_elem_numbering=False, **kwargs): - """Plot cell scalars. - - Assumes scalars are from all elements and not just the active surface. - """ + """Plot cell scalars.""" if not scalars.size: raise RuntimeError( "Result unavailable. Either the result has not been loaded " From a8084053bd82f3a85d41968cf9f19d640914494b Mon Sep 17 00:00:00 2001 From: German Date: Fri, 10 Dec 2021 13:21:28 +0100 Subject: [PATCH 5/7] Added test units. --- tests/test_post.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test_post.py b/tests/test_post.py index d603484e69..7a6fe75e65 100644 --- a/tests/test_post.py +++ b/tests/test_post.py @@ -1040,6 +1040,21 @@ def test_plot_nodal_contact_friction_stress(mapdl, contact_solve): cpos = mapdl.post_processing.plot_nodal_contact_friction_stress(smooth_shading=True) assert isinstance(cpos, CameraPosition) + +def test_plot_uncomplete_element_selection(mapdl, contact_solve): + n_elements = mapdl.mesh.n_elem + mapdl.esel('s', 'elem', '', 1, n_elements//2) + cpos = mapdl.post_processing.plot_element_displacement(smooth_shading=True) + assert isinstance(cpos, CameraPosition) + + +def test_plot_uncomplete_nodal_selection(mapdl, contact_solve): + n_elements = mapdl.mesh.n_node + mapdl.nsel('s', 'node', '', 1, n_elements//2) + cpos = mapdl.post_processing.plot_element_displacement(smooth_shading=True) + assert isinstance(cpos, CameraPosition) + + ############################################################################### # @pytest.mark.parametrize('comp', COMPONENT_STRESS_TYPE) # def test_nodal_thermal_component_strain(mapdl, thermal_solve, comp): From 85c6cbd7e2453c421b84e5beebd996c78ced30b7 Mon Sep 17 00:00:00 2001 From: German Date: Fri, 10 Dec 2021 14:23:00 +0100 Subject: [PATCH 6/7] Fixing indexing problem. --- ansys/mapdl/core/post.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansys/mapdl/core/post.py b/ansys/mapdl/core/post.py index 44eab7e769..adee28de13 100644 --- a/ansys/mapdl/core/post.py +++ b/ansys/mapdl/core/post.py @@ -537,7 +537,7 @@ def _plot_point_scalars(self, scalars, show_node_numbering=False, **kwargs): surf = self._mapdl.mesh._surf nods = surf["ansys_node_num"]-1 - all_scalars = all_scalars[nods] + all_scalars = all_scalars[nods.astype(int)] meshes = [ { From 253238d0c5fa9dac2d3e8a2db9a7720f0fa78554 Mon Sep 17 00:00:00 2001 From: Alex Kaszynski Date: Sat, 11 Dec 2021 23:01:50 -0700 Subject: [PATCH 7/7] minor cleanup to selection --- ansys/mapdl/core/post.py | 27 ++++++++++++++++++++------- tests/test_post.py | 16 ++++++++-------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/ansys/mapdl/core/post.py b/ansys/mapdl/core/post.py index adee28de13..ef5e2c1dc5 100644 --- a/ansys/mapdl/core/post.py +++ b/ansys/mapdl/core/post.py @@ -530,14 +530,15 @@ def _plot_point_scalars(self, scalars, show_node_numbering=False, **kwargs): "exist within the result file." ) - sel = self.selected_nodes - all_scalars = np.zeros(sel.shape) - all_scalars[sel] = scalars + mask = self.selected_nodes + all_scalars = np.empty(mask.size) + all_scalars[mask] = scalars + # we can directly the node numbers as the array of selected + # nodes will be a mask sized to the highest node index - 1 surf = self._mapdl.mesh._surf - - nods = surf["ansys_node_num"]-1 - all_scalars = all_scalars[nods.astype(int)] + node_id = surf["ansys_node_num"].astype(np.int32) - 1 + all_scalars = all_scalars[node_id] meshes = [ { @@ -638,9 +639,16 @@ def selected_nodes(self) -> np.ndarray: Examples -------- - >>> mapdl.post_processing.node_selection + The mask of the selected nodes. + + >>> mapdl.post_processing.selected_nodes array([False, False, False, ..., True, True, True]) + If you want the node numbers of the selected nodes. + + >>> mapdl.post_processing.selected_nodes.nonzero()[0] + 1 + array([1, 2, 3, 4, 5, 6, 7, 8, 9]) + """ return self._nsel == 1 @@ -664,6 +672,11 @@ def selected_elements(self) -> np.ndarray: >>> mapdl.post_processing.selected_elements array([False, False, False, ..., True, True, True]) + If you want the element numbers of the selected elements. + + >>> mapdl.post_processing.selected_elements.nonzero()[0] + 1 + array([1, 2, 3, 4, 5, 6, 7, 8, 9]) + """ return self._esel == 1 diff --git a/tests/test_post.py b/tests/test_post.py index 7a6fe75e65..e0905b8b7e 100644 --- a/tests/test_post.py +++ b/tests/test_post.py @@ -53,6 +53,7 @@ def static_solve(mapdl): mapdl.esize(elemsize) mapdl.allsel("all") + breakpoint() mapdl.vsweep("ALL") mapdl.csys(1) mapdl.asel("s", "loc", "z", "", height - h_tip + 0.0001) @@ -207,6 +208,7 @@ def contact_solve(mapdl): mapdl.vsel("u", "volume", "", 1, 2) mapdl.mat(2) mapdl.esize(0.005) + mapdl.numstr("NODE", 1000) mapdl.vsweep("all") mapdl.allsel("all") @@ -1041,17 +1043,15 @@ def test_plot_nodal_contact_friction_stress(mapdl, contact_solve): assert isinstance(cpos, CameraPosition) -def test_plot_uncomplete_element_selection(mapdl, contact_solve): - n_elements = mapdl.mesh.n_elem - mapdl.esel('s', 'elem', '', 1, n_elements//2) - cpos = mapdl.post_processing.plot_element_displacement(smooth_shading=True) +def test_plot_incomplete_element_selection(mapdl, contact_solve): + mapdl.esel('S', 'ELEM', '', 1, mapdl.mesh.n_elem//2) + cpos = mapdl.post_processing.plot_element_displacement() assert isinstance(cpos, CameraPosition) -def test_plot_uncomplete_nodal_selection(mapdl, contact_solve): - n_elements = mapdl.mesh.n_node - mapdl.nsel('s', 'node', '', 1, n_elements//2) - cpos = mapdl.post_processing.plot_element_displacement(smooth_shading=True) +def test_plot_incomplete_nodal_selection(mapdl, contact_solve): + mapdl.nsel('S', 'NODE', '', 1, mapdl.mesh.n_node//2) + cpos = mapdl.post_processing.plot_nodal_displacement() assert isinstance(cpos, CameraPosition)