diff --git a/src/ansys/aedt/core/visualization/post/post_common_3d.py b/src/ansys/aedt/core/visualization/post/post_common_3d.py index 736d76d3b03..398a1fce4cf 100644 --- a/src/ansys/aedt/core/visualization/post/post_common_3d.py +++ b/src/ansys/aedt/core/visualization/post/post_common_3d.py @@ -207,6 +207,7 @@ def _get_intrinsic(self, setup): @pyaedt_function_handler(list_objs="assignment") def _get_volume_objects(self, assignment): + obj_list = [] if self._app.solution_type not in ["HFSS3DLayout", "HFSS 3D Layout Design"]: obj_list = [] editor = self._app._odesign.SetActiveEditor("3D Modeler") @@ -1340,36 +1341,53 @@ def create_fieldplot_line_traces( ) @pyaedt_function_handler() - def _get_3dl_layers_nets(self, layers, nets, setup): + def _get_all_3dl_layers_nets(self, setup): + try: + get_ids = self._odesign.GetGeometryIdsForAllNetLayerCombinations(setup) + except: # pragma no cover + get_ids = [] + k = 0 + get_ids_dict = {} + key = "" + list_to_add = [] + while k < len(get_ids): + if get_ids[k].startswith("PlotGeomInfo"): + if key: + get_ids_dict[key] = list_to_add + key = get_ids[k].replace("PlotGeomInfo for ", "").replace(" (net/layer combination):", "") + list_to_add = [] + else: + try: + list_to_add.append(int(get_ids[k])) + except ValueError: + pass + k = k + 1 + return get_ids_dict + + @pyaedt_function_handler() + def _get_3dl_layers_nets(self, layers, nets, setup, include_dielectrics): lst_faces = [] new_layers = [] + ids_dict = self._get_all_3dl_layers_nets(setup) if not layers: - new_layers.extend([f"{i}" for i in self._app.modeler.edb.stackup.dielectric_layers.keys()]) + if include_dielectrics: + new_layers.extend([f"{i}" for i in self._app.modeler.edb.stackup.dielectric_layers.keys()]) for layer in self._app.modeler.edb.stackup.signal_layers.keys(): if not nets: nets = list(self._app.modeler.edb.nets.nets.keys()) for el in nets: - try: - get_ids = self._odesign.GetGeometryIdsForNetLayerCombination(el, layer, setup) - except: - get_ids = [] - if isinstance(get_ids, (tuple, list)) and len(get_ids) > 2: - lst_faces.extend([int(i) for i in get_ids[2:]]) - + if f"{el}/{layer}" in ids_dict: + lst_faces.extend(ids_dict[f"{el}/{layer}"]) else: for layer in layers: - if layer in self._app.modeler.edb.stackup.dielectric_layers: + if layer in self._app.modeler.edb.stackup.dielectric_layers and include_dielectrics: new_layers.append(f"{layer}") elif layer in self._app.modeler.edb.stackup.signal_layers: if not nets: nets = list(self._app.modeler.edb.nets.nets.keys()) for el in nets: - try: - get_ids = self._odesign.GetGeometryIdsForNetLayerCombination(el, layer, setup) - except: - get_ids = [] - if isinstance(get_ids, (tuple, list)) and len(get_ids) > 2: - lst_faces.extend([int(i) for i in get_ids[2:]]) + if f"{el}/{layer}" in ids_dict: + lst_faces.extend(ids_dict[f"{el}/{layer}"]) return lst_faces, new_layers @pyaedt_function_handler() @@ -1395,9 +1413,9 @@ def create_fieldplot_layers( ): # type: (list, str, str, list, bool, dict, str) -> FieldPlot """Create a field plot of stacked layer plot. - This plot is valid from AEDT 2023 R2 and later in HFSS 3D Layout. + This plot is valid from AEDT 2023 R2 and later in HFSS 3D Layout. Nets can be used as a filter. + Dielectrics will be included into the plot. It works when a layout components in 3d modeler is used. - In order to plot on signal layers use the method ``create_fieldplot_layers_nets``. Parameters ---------- @@ -1414,6 +1432,9 @@ def create_fieldplot_layers( use in the export or ``LastAdaptive``. nets : list, optional List of nets to filter the field plot. Optional. + plot_on_surface : bool, optional + Whether if the plot has to be on surfaces or inside the objects. + It is applicable only to layout components. Default is ``True``. intrinsics : dict, str, optional Intrinsic variables required to compute the field before the export. These are typically: frequency, time and phase. @@ -1453,7 +1474,7 @@ def create_fieldplot_layers( return self.field_plots[name] if self._app.design_type in ["HFSS 3D Layout Design"]: - lst_faces, new_layers = self._get_3dl_layers_nets(layers, nets, setup) + lst_faces, new_layers = self._get_3dl_layers_nets(layers, nets, setup, include_dielectrics=True) if new_layers: plt = self._create_fieldplot( new_layers, quantity, setup, intrinsics, "ObjList", name, create_plot=False @@ -1484,12 +1505,89 @@ def create_fieldplot_layers( return self._create_fieldplot(dielectrics, quantity, setup, intrinsics, "ObjList", name) return False + @pyaedt_function_handler() + def create_fieldplot_nets( + self, nets, quantity, setup=None, layers=None, plot_on_surface=True, intrinsics=None, name=None + ): + # type: (list, str, str, list, bool, dict, str) -> FieldPlot + """Create a field plot of stacked layer plot based on a net selections. Layers can be used as a filter. + Dielectrics will be excluded from the plot. + This plot is valid from AEDT 2023 R2 and later in HFSS 3D Layout. + It works when a layout components in 3d modeler is used. + + Parameters + ---------- + nets : list, optional + List of nets to filter the field plot. + quantity : str + Name of the quantity to plot. + setup : str, optional + Name of the setup. The default is ``None``, in which case the ``nominal_adaptive`` + setup is used. Make sure to build a setup string in the form of + ``"SetupName : SetupSweep"``, where ``SetupSweep`` is the sweep name to + use in the export or ``LastAdaptive``. + layers : list, optional + List of layers to plot. For example: + ``["Layer1","Layer2"]``. If empty list is provided + all layers are considered. + intrinsics : dict, str, optional + Intrinsic variables required to compute the field before the export. + These are typically: frequency, time and phase. + It can be provided either as a dictionary or as a string. + If it is a dictionary, keys depend on the solution type and can be expressed in lower or camel case as: + + - ``"Freq"`` or ``"Frequency"``. + - ``"Time"``. + - ``"Phase"``. + + If it is a string, it can either be ``"Freq"`` or ``"Time"`` depending on the solution type. + The default is ``None`` in which case the intrinsics value is automatically computed based on the setup. + plot_on_surface : bool, optional + Whether if the plot has to be on surfaces or inside the objects. + It is applicable only to layout components. Default is ``True``. + name : str, optional + Name of the field plot to create. + + Returns + ------- + :class:``ansys.aedt.core.modules.solutions.FieldPlot`` or bool + Plot object. + + References + ---------- + >>> oModule.CreateFieldPlot + """ + intrinsics = self._app._check_intrinsics(intrinsics, setup=setup) + if not setup: + setup = self._app.existing_analysis_sweeps[0] + if nets is None: + nets = [] + if not ( + "APhi" in self.post_solution_type and settings.aedt_version >= "2023.2" + ) and not self._app.design_type in ["HFSS", "HFSS 3D Layout Design"]: + self.logger.error("This method requires AEDT 2023 R2 and Maxwell 3D Transient APhi Formulation.") + return False + if name and name in list(self.field_plots.keys()): + self.logger.info(f"Plot {name} exists. returning the object.") + return self.field_plots[name] + + if self._app.design_type in ["HFSS 3D Layout Design"]: + lst_faces, new_layers = self._get_3dl_layers_nets(layers, nets, setup, include_dielectrics=False) + return self._create_fieldplot(lst_faces, quantity, setup, intrinsics, "FacesList", name) + else: + _, new_layers = self._get_3d_layers_nets(layers, nets) + if plot_on_surface: + plot_type = "LayerNetsExtFace" + else: + plot_type = "LayerNets" + return self._create_fieldplot(new_layers, quantity, setup, intrinsics, plot_type, name) + @pyaedt_function_handler(quantity_name="quantity", setup_name="setup") def create_fieldplot_layers_nets( self, layers_nets, quantity, setup=None, intrinsics=None, plot_on_surface=True, plot_name=None ): # type: (list, str, str, dict, bool, str) -> FieldPlot - """Create a field plot of stacked layer plot. + """Create a field plot of stacked layer plot on specified matrix of layers and nets. This plot is valid from AEDT 2023 R2 and later in HFSS 3D Layout and any modeler where a layout component is used. @@ -1520,7 +1618,8 @@ def create_fieldplot_layers_nets( If it is a string, it can either be ``"Freq"`` or ``"Time"`` depending on the solution type. The default is ``None`` in which case the intrinsics value is automatically computed based on the setup. plot_on_surface : bool, optional - Whether the plot is to be on the surface or volume of traces. + Whether if the plot has to be on surfaces or inside the objects. + It is applicable only to layout components. Default is ``True``. plot_name : str, optional Name of the field plot to create. @@ -1548,7 +1647,17 @@ def create_fieldplot_layers_nets( if not setup: setup = self._app.existing_analysis_sweeps[0] lst = [] + if len(layers_nets) == 0: + + dicts_in = self._get_all_3dl_layers_nets(setup) + for _, v in dicts_in.items(): + lst.extend(v) for layer in layers_nets: + if len(layer) == 1: + dicts_in = self._get_all_3dl_layers_nets(setup) + for v, i in dicts_in.items(): + if v.split("/")[1] == layer[0] or v.split("/")[0] == layer[0]: + lst.extend(i) for el in layer[1:]: el = "" if el == "no-net" else el try: diff --git a/tests/system/general/test_41_3dlayout_modeler.py b/tests/system/general/test_41_3dlayout_modeler.py index e4ec1129816..d05fafd4e00 100644 --- a/tests/system/general/test_41_3dlayout_modeler.py +++ b/tests/system/general/test_41_3dlayout_modeler.py @@ -837,6 +837,17 @@ def test_42_post_processing_3d_layout(self, add_app): assert pl1 assert pl1.export_image_from_aedtplt(tempfile.gettempdir()) + pl2 = test.post.create_fieldplot_nets( + ["V3P3_S5"], + "Mag_E", + layers=["LYR_1"], + intrinsics={"Freq": "1GHz"}, + name="Test_Layers2", + ) + + assert pl2 + assert pl2.export_image_from_aedtplt(tempfile.gettempdir()) + self.aedtapp.close_project(test.project_name) @pytest.mark.skipif(is_linux, reason="Bug on linux")