diff --git a/doc/source/api/unit_testing.rst b/doc/source/api/unit_testing.rst index a8a2bb33d5..ce8f6817e4 100644 --- a/doc/source/api/unit_testing.rst +++ b/doc/source/api/unit_testing.rst @@ -160,7 +160,7 @@ Here are some examples of how you use ``pytest``: def test_dunder_methods_keys(mapdl, basic_components): - assert ["MYCOMP1", "MYCOMP2"] == list(mapdl.components.list()) + assert ["MYCOMP1", "MYCOMP2"] == list(mapdl.components.names()) def test_dunder_methods_types(mapdl, basic_components): diff --git a/src/ansys/mapdl/core/component.py b/src/ansys/mapdl/core/component.py index 2ff7d353a0..ef67f93458 100644 --- a/src/ansys/mapdl/core/component.py +++ b/src/ansys/mapdl/core/component.py @@ -441,7 +441,8 @@ def __iter__(self): """ yield from self._comp.keys() - def list(self): + @property + def names(self): """ Return a tuple that contains the components. @@ -453,6 +454,7 @@ def list(self): """ return tuple(self._comp.keys()) + @property def types(self): """ Return the types of the components. @@ -476,3 +478,24 @@ def items(self): """ return self._comp.items() + + def select(self, names: Union[str, list[str], tuple[str]], mute=False) -> None: + """Select Select components given their names + + Select components given their names. + + Parameters + ---------- + names : Union[str, list[str], tuple[str]] + Name(s) of the components + mute : bool, optional + Whether to mute the `/CMSEL` command output or not, by default False. + """ + if isinstance(names, str): + names = [names] + + for i, each_name in enumerate(names): + if i == 0: + self._mapdl.cmsel("S", each_name, mute=mute) + else: + self._mapdl.cmsel("A", each_name, mute=mute) diff --git a/src/ansys/mapdl/core/mapdl.py b/src/ansys/mapdl/core/mapdl.py index a34959f80e..a7362c2721 100644 --- a/src/ansys/mapdl/core/mapdl.py +++ b/src/ansys/mapdl/core/mapdl.py @@ -648,6 +648,17 @@ def force_output(self): """ return self._force_output(self) + @property + def save_selection(self): + """Save selection + + Save the current selection (nodes, elements, keypoints, lines, areas, + volumes and components) before entering in the context manager, and + when exit returns to that selection. + + """ + return self._save_selection(self) + @property def solution(self) -> "Solution": """Solution parameters of MAPDL. @@ -816,6 +827,50 @@ def __exit__(self, *args): self._parent()._flush_stored() self._parent()._store_commands = False + class _save_selection: + """Save the selection and returns to it when exiting""" + + def __init__(self, parent): + self._parent = weakref.ref(parent) + self.selection_sets = [] + self.selection_sets_comps = [] + + def __enter__(self): + self._parent()._log.debug("Entering saving selection context") + + selection_set_name = random_string(10) + self.selection_sets.append(selection_set_name) + self.selection_sets_comps.append(self._parent().components.names) + + mapdl = self._parent() + + prev_ier = mapdl.ignore_errors + mapdl.ignore_errors = True + for entity in ["kp", "lines", "area", "volu", "node", "elem"]: + mapdl.cm(f"_{selection_set_name}_{entity}_", f"{entity}", mute=True) + mapdl.ignore_errors = prev_ier + + def __exit__(self, *args): + self._parent()._log.debug("Exiting saving selection context") + last_selection_name = self.selection_sets.pop() + last_selection_cmps = self.selection_sets_comps.pop() + + mapdl = self._parent() + + # probably this is redundant + prev_ier = mapdl.ignore_errors + mapdl.ignore_errors = True + for entity in ["kp", "lines", "area", "volu", "node", "elem"]: + cmp_name = f"_{last_selection_name}_{entity}_" + mapdl.cmsel("s", cmp_name, f"{entity}", mute=True) + mapdl.cmdele(cmp_name) + + mapdl.ignore_errors = prev_ier + + # mute to avoid getting issues when the component wasn't created in + # first place because there was no entities. + self._parent().components.select(last_selection_cmps, mute=True) + class _chain_commands: """Store MAPDL commands and send one chained command.""" @@ -1718,45 +1773,39 @@ def vplot( ) return general_plotter([], [], [], **kwargs) - cm_name_area = "__tmp_area2__" - cm_name_volu = "__tmp_volu2__" - self.cm(cm_name_area, "AREA", mute=True) - self.cm(cm_name_volu, "VOLU", mute=True) - - volumes = self.geometry.vnum - meshes = [] - points = [] - labels = [] - - return_plotter = kwargs.pop("return_plotter", False) - color_areas = True - - for each_volu in volumes: - self.vsel("S", vmin=each_volu) - self.aslv("S", mute=True) # select areas attached to active volumes - - pl = self.aplot( - vtk=True, - color_areas=color_areas, - quality=quality, - show_area_numbering=show_area_numbering, - show_line_numbering=show_line_numbering, - show_lines=show_lines, - return_plotter=True, - **kwargs, - ) - - meshes_ = get_meshes_from_plotter(pl) + # Storing entities selection + with self.save_selection: + volumes = self.geometry.vnum + meshes = [] + points = [] + labels = [] + + return_plotter = kwargs.pop("return_plotter", False) + color_areas = True + + for each_volu in volumes: + self.vsel("S", vmin=each_volu) + self.aslv("S", mute=True) # select areas attached to active volumes + + pl = self.aplot( + vtk=True, + color_areas=color_areas, + quality=quality, + show_area_numbering=show_area_numbering, + show_line_numbering=show_line_numbering, + show_lines=show_lines, + return_plotter=True, + **kwargs, + ) - for each_mesh in meshes_: - each_mesh.cell_data["entity_num"] = int(each_volu) + meshes_ = get_meshes_from_plotter(pl) - meshes.extend(meshes_) + for each_mesh in meshes_: + each_mesh.cell_data["entity_num"] = int(each_volu) - meshes = [{"mesh": meshes}] + meshes.extend(meshes_) - self.cmsel("S", cm_name_area, "AREA", mute=True) - self.cmsel("S", cm_name_volu, "VOLU", mute=True) + meshes = [{"mesh": meshes}] return general_plotter( meshes, points, labels, return_plotter=return_plotter, **kwargs @@ -1973,11 +2022,10 @@ def aplot( if show_lines or show_line_numbering: kwargs.setdefault("line_width", 2) # subselect lines belonging to the current areas - self.cm("__area__", "AREA", mute=True) - self.lsla("S", mute=True) - lines = self.geometry.get_lines() - self.cmsel("S", "__area__", "AREA", mute=True) + with self.save_selection: + self.lsla("S", mute=True) + lines = self.geometry.get_lines() if show_lines: meshes.append( @@ -4933,6 +4981,7 @@ def cmplot(self, label: str = "", entity: str = "", keyword: str = "", **kwargs) if entity[:4] not in ["NODE", "ELEM", "KP", "LINE", "AREA", "VOLU"]: raise ValueError(f"The entity '{entity}' is not allowed.") + cmps_names = self.components.names self.cm("__tmp_cm__", entity=entity) if label == "ALL": self.cmsel("ALL", entity=entity) @@ -4954,4 +5003,5 @@ def cmplot(self, label: str = "", entity: str = "", keyword: str = "", **kwargs) # returning to previous selection self.cmsel("s", "__tmp_cm__", entity=entity) + self.components.select(cmps_names) return output diff --git a/src/ansys/mapdl/core/mapdl_geometry.py b/src/ansys/mapdl/core/mapdl_geometry.py index a70b3cfef7..193a907344 100644 --- a/src/ansys/mapdl/core/mapdl_geometry.py +++ b/src/ansys/mapdl/core/mapdl_geometry.py @@ -1,5 +1,4 @@ """Module to support MAPDL CAD geometry""" -import contextlib from functools import wraps import re from typing import TYPE_CHECKING, Any, Iterable, List, Optional, Sequence, Tuple, Union @@ -9,7 +8,7 @@ import pyvista as pv from ansys.mapdl.core import _HAS_PYVISTA, Mapdl -from ansys.mapdl.core.errors import ComponentNoData, VersionError +from ansys.mapdl.core.errors import VersionError if _HAS_PYVISTA: import pyvista as pv @@ -655,75 +654,67 @@ def generate_surface( ninc : int, optional Steps to between amin and amax. """ - # store initially selected areas and elements - with contextlib.suppress(ComponentNoData): - # avoiding empty components exceptions - with self._mapdl.non_interactive: - self._mapdl.cm("__tmp_elem__", "ELEM") - self._mapdl.cm("__tmp_area__", "AREA") - - orig_anum = self.anum - - # reselect from existing selection to mimic APDL behavior - if amin or amax: - if amax is None: - amax = amin - - if amin is None: # amax is non-zero - amin = 1 - - if ninc is None: - ninc = "" - - self._mapdl.asel("R", "AREA", vmin=amin, vmax=amax, vinc=ninc) - - # duplicate areas to avoid affecting existing areas - a_num = int(self._mapdl.get(entity="AREA", item1="NUM", it1num="MAXD")) - self._mapdl.numstr("AREA", a_num, mute=True) - self._mapdl.agen(2, "ALL", noelem=1, mute=True) - a_max = int(self._mapdl.get(entity="AREA", item1="NUM", it1num="MAXD")) - - self._mapdl.asel("S", "AREA", vmin=a_num + 1, vmax=a_max, mute=True) - # necessary to reset element/area meshing association - self._mapdl.aatt(mute=True) - - # create a temporary etype - etype_max = int(self._mapdl.get(entity="ETYP", item1="NUM", it1num="MAX")) - etype_old = self._mapdl.parameters.type - etype_tmp = etype_max + 1 - - old_routine = self._mapdl.parameters.routine - - self._mapdl.et(etype_tmp, "MESH200", 6, mute=True) - self._mapdl.shpp("off", mute=True) - self._mapdl.smrtsize(density, mute=True) - self._mapdl.type(etype_tmp, mute=True) - - if old_routine != "PREP7": - self._mapdl.prep7(mute=True) - - # Mesh and get the number of elements per area - resp = self._mapdl.amesh("all") - groups = get_elements_per_area(resp) - - self._mapdl.esla("S") - grid = self._mapdl.mesh._grid.linear_copy() - pd = pv.PolyData(grid.points, grid.cells, n_faces=grid.n_cells) - - # pd['ansys_node_num'] = grid['ansys_node_num'] - # pd['vtkOriginalPointIds'] = grid['vtkOriginalPointIds'] - # pd.clean(inplace=True) # OPTIONAL - - # delete all temporary meshes and clean up settings - self._mapdl.aclear("ALL", mute=True) - self._mapdl.adele("ALL", kswp=1, mute=True) - self._mapdl.numstr("AREA", 1, mute=True) - self._mapdl.type(etype_old, mute=True) - self._mapdl.etdele(etype_tmp, mute=True) - self._mapdl.shpp("ON", mute=True) - self._mapdl.smrtsize("OFF", mute=True) - self._mapdl.cmsel("S", "__tmp_area__", "AREA", mute=True) - self._mapdl.cmsel("S", "__tmp_elem__", "ELEM", mute=True) + with self._mapdl.save_selection: + orig_anum = self.anum + + # reselect from existing selection to mimic APDL behavior + if amin or amax: + if amax is None: + amax = amin + + if amin is None: # amax is non-zero + amin = 1 + + if ninc is None: + ninc = "" + + self._mapdl.asel("R", "AREA", vmin=amin, vmax=amax, vinc=ninc) + + # duplicate areas to avoid affecting existing areas + a_num = int(self._mapdl.get(entity="AREA", item1="NUM", it1num="MAXD")) + self._mapdl.numstr("AREA", a_num, mute=True) + self._mapdl.agen(2, "ALL", noelem=1, mute=True) + a_max = int(self._mapdl.get(entity="AREA", item1="NUM", it1num="MAXD")) + + self._mapdl.asel("S", "AREA", vmin=a_num + 1, vmax=a_max, mute=True) + # necessary to reset element/area meshing association + self._mapdl.aatt(mute=True) + + # create a temporary etype + etype_max = int(self._mapdl.get(entity="ETYP", item1="NUM", it1num="MAX")) + etype_old = self._mapdl.parameters.type + etype_tmp = etype_max + 1 + + old_routine = self._mapdl.parameters.routine + + self._mapdl.et(etype_tmp, "MESH200", 6, mute=True) + self._mapdl.shpp("off", mute=True) + self._mapdl.smrtsize(density, mute=True) + self._mapdl.type(etype_tmp, mute=True) + + if old_routine != "PREP7": + self._mapdl.prep7(mute=True) + + # Mesh and get the number of elements per area + resp = self._mapdl.amesh("all") + groups = get_elements_per_area(resp) + + self._mapdl.esla("S") + grid = self._mapdl.mesh._grid.linear_copy() + pd = pv.PolyData(grid.points, grid.cells, n_faces=grid.n_cells) + + # pd['ansys_node_num'] = grid['ansys_node_num'] + # pd['vtkOriginalPointIds'] = grid['vtkOriginalPointIds'] + # pd.clean(inplace=True) # OPTIONAL + + # delete all temporary meshes and clean up settings + self._mapdl.aclear("ALL", mute=True) + self._mapdl.adele("ALL", kswp=1, mute=True) + self._mapdl.numstr("AREA", 1, mute=True) + self._mapdl.type(etype_old, mute=True) + self._mapdl.etdele(etype_tmp, mute=True) + self._mapdl.shpp("ON", mute=True) + self._mapdl.smrtsize("OFF", mute=True) # store the area number used for each element entity_num = np.empty(grid.n_cells, dtype=np.int32) @@ -866,21 +857,13 @@ def vnum(self) -> NDArray[np.int32]: def _load_lines(self) -> List[pv.PolyData]: """Load lines from MAPDL using IGES""" # ignore volumes - self._mapdl.cm("__tmp_volu__", "VOLU", mute=True) - self._mapdl.cm("__tmp_line__", "LINE", mute=True) - self._mapdl.cm("__tmp_area__", "AREA", mute=True) - self._mapdl.cm("__tmp_keyp__", "KP", mute=True) - self._mapdl.ksel("ALL", mute=True) - self._mapdl.lsel("ALL", mute=True) - self._mapdl.asel("ALL", mute=True) - self._mapdl.vsel("NONE", mute=True) - - iges = self._load_iges() - - self._mapdl.cmsel("S", "__tmp_volu__", "VOLU", mute=True) - self._mapdl.cmsel("S", "__tmp_area__", "AREA", mute=True) - self._mapdl.cmsel("S", "__tmp_line__", "LINE", mute=True) - self._mapdl.cmsel("S", "__tmp_keyp__", "KP", mute=True) + with self._mapdl.save_selection: + self._mapdl.ksel("ALL", mute=True) + self._mapdl.lsel("ALL", mute=True) + self._mapdl.asel("ALL", mute=True) + self._mapdl.vsel("NONE", mute=True) + + iges = self._load_iges() selected_lnum = self.lnum lines = [] @@ -914,18 +897,11 @@ def _load_lines(self) -> List[pv.PolyData]: def _load_keypoints(self) -> Tuple[NDArray, NDArray]: """Load keypoints from MAPDL using IGES.""" # write only keypoints - self._mapdl.cm("__tmp_volu__", "VOLU", mute=True) - self._mapdl.cm("__tmp_area__", "AREA", mute=True) - self._mapdl.cm("__tmp_line__", "LINE", mute=True) - self._mapdl.vsel("NONE", mute=True) - self._mapdl.asel("NONE", mute=True) - self._mapdl.lsel("NONE", mute=True) - - iges = self._load_iges() - - self._mapdl.cmsel("S", "__tmp_volu__", "VOLU", mute=True) - self._mapdl.cmsel("S", "__tmp_area__", "AREA", mute=True) - self._mapdl.cmsel("S", "__tmp_line__", "LINE", mute=True) + with self._mapdl.save_selection: + self._mapdl.vsel("NONE", mute=True) + self._mapdl.asel("NONE", mute=True) + self._mapdl.lsel("NONE", mute=True) + iges = self._load_iges() keypoints = [] kp_num = [] @@ -1329,22 +1305,15 @@ def get_volumes( return surf # Cache current selection - with contextlib.suppress(ComponentNoData): - # avoiding empty components exceptions - self._mapdl.cm("__temp_volu__", "volu") - self._mapdl.cm("__temp_area__", "area") - - area_num = surf["entity_num"].astype(int) - - for each_volu in self.vnum: - self._mapdl.vsel("S", vmin=each_volu) - self._mapdl.aslv("S") - unstruct = surf.extract_cells(np.in1d(area_num, self.anum)) - unstruct.entity_num = int(each_volu) - volumes_.append(unstruct) - - self._mapdl.cmsel("S", "__temp_volu__") - self._mapdl.cmsel("S", "__temp_area__") + with self._mapdl.save_selection: + area_num = surf["entity_num"].astype(int) + + for each_volu in self.vnum: + self._mapdl.vsel("S", vmin=each_volu) + self._mapdl.aslv("S") + unstruct = surf.extract_cells(np.in1d(area_num, self.anum)) + unstruct.entity_num = int(each_volu) + volumes_.append(unstruct) return volumes_ diff --git a/src/ansys/mapdl/core/mesh_grpc.py b/src/ansys/mapdl/core/mesh_grpc.py index f29849a62b..7f1705005f 100644 --- a/src/ansys/mapdl/core/mesh_grpc.py +++ b/src/ansys/mapdl/core/mesh_grpc.py @@ -134,33 +134,33 @@ def _update_cache(self): """ self.logger.debug("Updating cache") # elements must have their underlying nodes selected to avoid - # VTK segfault - self._mapdl.cm(TMP_NODE_CM, "NODE", mute=True) - self._mapdl.nsle("S", mute=True) + # VTK segfaul + with self._mapdl.save_selection: + self._mapdl.nsle("S", mute=True) - # not thread safe - self._update_cache_elem() + # not thread safe + self._update_cache_elem() - threads = [ - self._update_cache_element_desc(), - self._update_cache_nnum(), - self._update_node_coord(), - ] + threads = [ + self._update_cache_element_desc(), + self._update_cache_nnum(), + self._update_node_coord(), + ] - for thread in threads: - thread.join() + for thread in threads: + thread.join() - # must occur after read - self._ignore_cache_reset = True + # must occur after read + self._ignore_cache_reset = True + + # somehow requesting path seems to help windows avoid an + # outright segfault prior to running CMSEL + if os.name == "nt": + _ = self._mapdl.path - # somehow requesting path seems to help windows avoid an - # outright segfault prior to running CMSEL - if os.name == "nt": - _ = self._mapdl.path + # TODO: flaky + time.sleep(0.05) - # TODO: flaky - time.sleep(0.05) - self._mapdl.cmsel("S", TMP_NODE_CM, "NODE", mute=True) self._ignore_cache_reset = False @threaded @@ -391,16 +391,15 @@ def nnum_all(self) -> np.ndarray: array([ 1, 2, 3, ..., 19998, 19999, 20000]) """ self._ignore_cache_reset = True - self._mapdl.cm(TMP_NODE_CM, "NODE", mute=True) - self._mapdl.nsel("all", mute=True) + with self._mapdl.save_selection: + self._mapdl.nsel("all", mute=True) - nnum = self._mapdl.get_array("NODE", item1="NLIST") - nnum = nnum.astype(np.int32) - if nnum.size == 1: - if nnum[0] == 0: - nnum = np.empty(0, np.int32) + nnum = self._mapdl.get_array("NODE", item1="NLIST") + nnum = nnum.astype(np.int32) + if nnum.size == 1: + if nnum[0] == 0: + nnum = np.empty(0, np.int32) - self._mapdl.cmsel("S", TMP_NODE_CM, "NODE", mute=True) self._ignore_cache_reset = False return nnum @@ -415,18 +414,16 @@ def enum_all(self) -> np.ndarray: array([ 1, 2, 3, ..., 19998, 19999, 20000]) """ self._ignore_cache_reset = True - self._mapdl.cm("__ELEM__", "ELEM", mute=True) - self._mapdl.esel("all", mute=True) + with self._mapdl.save_selection: + self._mapdl.esel("all", mute=True) - enum = self._mapdl.get_array("ELEM", item1="ELIST") - enum = enum.astype(np.int32) - if enum.size == 1: - if enum[0] == 0: - enum = np.empty(0, np.int32) + enum = self._mapdl.get_array("ELEM", item1="ELIST") + enum = enum.astype(np.int32) + if enum.size == 1: + if enum[0] == 0: + enum = np.empty(0, np.int32) - self._mapdl.cmsel("S", "__ELEM__", "ELEM", mute=True) self._ignore_cache_reset = False - return enum @property diff --git a/src/ansys/mapdl/core/post.py b/src/ansys/mapdl/core/post.py index 1a4c95f295..c802f32ba7 100644 --- a/src/ansys/mapdl/core/post.py +++ b/src/ansys/mapdl/core/post.py @@ -670,27 +670,27 @@ def _plot_cell_scalars(self, scalars, show_elem_numbering=False, **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: + with self._mapdl.save_selection: + self._mapdl.allsel() nnum = self._mapdl.get_array("NODE", item1="NLIST") - self._mapdl.cmsel("S", "__TMP_NODE__", "NODE") + + # rerun if encountered weird edge case of negative first index. + if nnum[0] == -1: + nnum = self._mapdl.get_array("NODE", item1="NLIST") + return nnum.astype(np.int32, copy=False) @property @supress_logging def _all_enum(self): - self._mapdl.cm("__TMP_ELEM__", "ELEM") - self._mapdl.allsel() - enum = self._mapdl.get_array("ELEM", item1="ELIST") - - # rerun if encountered weird edge case of negative first index. - if enum[0] == -1: + with self._mapdl.save_selection: + self._mapdl.allsel() enum = self._mapdl.get_array("ELEM", item1="ELIST") - self._mapdl.cmsel("S", "__TMP_ELEM__", "ELEM") + + # rerun if encountered weird edge case of negative first index. + if enum[0] == -1: + enum = self._mapdl.get_array("ELEM", item1="ELIST") + return enum.astype(np.int32, copy=False) @property diff --git a/tests/.image_cache/components_selection_keep_between_plots.png b/tests/.image_cache/components_selection_keep_between_plots.png new file mode 100644 index 0000000000..0a8be97034 Binary files /dev/null and b/tests/.image_cache/components_selection_keep_between_plots.png differ diff --git a/tests/test_component.py b/tests/test_component.py index 4e99122596..83ae0d40c1 100644 --- a/tests/test_component.py +++ b/tests/test_component.py @@ -264,11 +264,11 @@ def test_dunder_methods_iter(mapdl, basic_components): def test_dunder_methods_keys(mapdl, basic_components): - assert ["MYCOMP1", "MYCOMP2"] == list(mapdl.components.list()) + assert ["MYCOMP1", "MYCOMP2"] == list(mapdl.components.names) def test_dunder_methods_types(mapdl, basic_components): - assert ["NODE", "KP"] == list(mapdl.components.types()) + assert ["NODE", "KP"] == list(mapdl.components.types) def test_dunder_methods_items(mapdl, basic_components): diff --git a/tests/test_mapdl.py b/tests/test_mapdl.py index 283fc70487..b99490ad48 100644 --- a/tests/test_mapdl.py +++ b/tests/test_mapdl.py @@ -2004,3 +2004,112 @@ def test_check_parameter_names(mapdl): mapdl.check_parameter_names = False mapdl.parameters["_dummy"] = 1 mapdl.check_parameter_names = True # returning to default + + +def test_components_selection_keep_between_plots(mapdl, cube_solve): + mapdl.cm("mycm", "volu") + assert "MYCM" in mapdl.cmlist() + assert "mycm" in mapdl.components + + mapdl.vplot() + + assert "MYCM" in mapdl.cmlist() + assert "mycm" in mapdl.components + + +def test_saving_selection_context(mapdl, cube_solve): + mapdl.allsel() + + for i in range(1, 4): + mapdl.nsel("s", "", "", i) + mapdl.cm(f"nod_selection_{i}", "node") + + for i in range(1, 4): + mapdl.esel("s", "", "", i) + mapdl.cm(f"elem_selection_{i}", "elem") + + mapdl.cmsel("s", "nod_selection_1") + assert "nod_selection_1".upper() in mapdl.cmlist() + assert "nod_selection_1" in mapdl.components + + mapdl.cmsel("a", "elem_selection_1") + assert "elem_selection_1".upper() in mapdl.cmlist() + assert "elem_selection_1" in mapdl.components + assert "nod_selection_1".upper() in mapdl.cmlist() + assert "nod_selection_1" in mapdl.components + + with mapdl.save_selection: + mapdl.cmsel("a", "nod_selection_2") + assert "nod_selection_2".upper() in mapdl.cmlist() + assert "nod_selection_2" in mapdl.components + + mapdl.cmsel("a", "elem_selection_2") + assert "nod_selection_2".upper() in mapdl.cmlist() + assert "nod_selection_2" in mapdl.components + assert "elem_selection_2".upper() in mapdl.cmlist() + assert "elem_selection_2" in mapdl.components + + assert "elem_selection_1".upper() in mapdl.cmlist() + assert "elem_selection_1" in mapdl.components + assert "nod_selection_1".upper() in mapdl.cmlist() + assert "nod_selection_1" in mapdl.components + + mapdl.nsel("s", "", "", 4) + mapdl.cm("nod_selection_4", "node") + + with mapdl.save_selection: + mapdl.cmsel("s", "nod_selection_3") + assert "nod_selection_3".upper() in mapdl.cmlist() + assert "nod_selection_3" in mapdl.components + + mapdl.cmsel("a", "elem_selection_3") + assert "elem_selection_3".upper() in mapdl.cmlist() + assert "elem_selection_3" in mapdl.components + assert "nod_selection_3".upper() in mapdl.cmlist() + assert "nod_selection_3" in mapdl.components + + # Erased because the previous cmsel("s") + assert "nod_selection_4".upper() not in mapdl.cmlist() + assert "nod_selection_4" not in mapdl.components + + assert "nod_selection_2".upper() not in mapdl.cmlist() + assert "nod_selection_2" not in mapdl.components + assert "elem_selection_2".upper() not in mapdl.cmlist() + assert "elem_selection_2" not in mapdl.components + + assert "elem_selection_1".upper() not in mapdl.cmlist() + assert "elem_selection_1" not in mapdl.components + assert "nod_selection_1".upper() not in mapdl.cmlist() + assert "nod_selection_1" not in mapdl.components + + # Checking correctly exiting contexts + assert "nod_selection_2".upper() in mapdl.cmlist() + assert "nod_selection_2" in mapdl.components + assert "elem_selection_2".upper() in mapdl.cmlist() + assert "elem_selection_2" in mapdl.components + + assert "elem_selection_3".upper() not in mapdl.cmlist() + assert "elem_selection_3" not in mapdl.components + assert "nod_selection_3".upper() not in mapdl.cmlist() + assert "nod_selection_3" not in mapdl.components + + assert "nod_selection_4".upper() in mapdl.cmlist() + assert "nod_selection_4" in mapdl.components + + assert "elem_selection_1".upper() in mapdl.cmlist() + assert "elem_selection_1" in mapdl.components + assert "nod_selection_1".upper() in mapdl.cmlist() + assert "nod_selection_1" in mapdl.components + + assert "nod_selection_2".upper() not in mapdl.cmlist() + assert "nod_selection_2" not in mapdl.components + assert "nod_selection_2".upper() not in mapdl.cmlist() + assert "nod_selection_2" not in mapdl.components + + assert "elem_selection_3".upper() not in mapdl.cmlist() + assert "elem_selection_3" not in mapdl.components + assert "nod_selection_3".upper() not in mapdl.cmlist() + assert "nod_selection_3" not in mapdl.components + + assert "nod_selection_4".upper() not in mapdl.cmlist() + assert "nod_selection_4" not in mapdl.components