From 67d3abc486bd7a34ca3defe230b4e45412992443 Mon Sep 17 00:00:00 2001 From: Samuel Lopez <85613111+Samuelopez-ansys@users.noreply.github.com> Date: Thu, 12 Dec 2024 20:01:44 +0100 Subject: [PATCH] FEAT: Assign initial mesh method (#5556) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sébastien Morais <146729917+SMoraisAnsys@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/ansys/aedt/core/modules/mesh.py | 183 ++++++++++++++++++---- tests/system/general/test_20_HFSS.py | 6 +- tests/system/general/test_27_Maxwell2D.py | 13 +- tests/system/general/test_28_Maxwell3D.py | 9 +- 4 files changed, 174 insertions(+), 37 deletions(-) diff --git a/src/ansys/aedt/core/modules/mesh.py b/src/ansys/aedt/core/modules/mesh.py index 22e9101116c..19c54c21dca 100644 --- a/src/ansys/aedt/core/modules/mesh.py +++ b/src/ansys/aedt/core/modules/mesh.py @@ -786,43 +786,51 @@ def assign_model_resolution(self, assignment, defeature_length=None, name=None): self.meshoperations.append(mop) return mop - @pyaedt_function_handler() + @pyaedt_function_handler( + usedynamicsurface="dynamic_surface", + useflexmesh="flex_mesh", + applycurvilinear="curvilinear", + usefallback="fallback", + usephi="phi", + automodelresolution="auto_model_resolution", + modelresolutionlength="model_resolution_length", + ) def assign_initial_mesh_from_slider( self, level=5, method="Auto", - usedynamicsurface=True, - useflexmesh=False, - applycurvilinear=False, - usefallback=True, - usephi=True, - automodelresolution=True, - modelresolutionlength="0.0001mm", + dynamic_surface=True, + flex_mesh=False, + curvilinear=False, + fallback=True, + phi=True, + auto_model_resolution=True, + model_resolution_length="0.0001mm", ): """Assign a surface mesh level to an object. Parameters ---------- level : int, optional - Level of the surface mesh. Options are ``1`` through ``10``. The default is ``5.`` + Level of the surface mesh. Options are ``1`` through ``10``. The default is ``5``. method : str, optional Meshing method. Options are ``"Auto"``, ``"AnsoftTAU"``, and ``"AnsoftClassic"`` The default is ``"Auto"``. - usedynamicsurface : bool, optional - Whether to use a dynamic surface. The default is ``True``. - useflexmesh : bool, optional - Whether to use a flexible mesh. The default is ``False``. - applycurvilinear : bool, optional + dynamic_surface : bool, optional + Whether to use dynamic surface resolution. The default is ``True``. + flex_mesh : bool, optional + Whether to use flexible mesh for TAU volume mesh. The default is ``False``. + curvilinear : bool, optional Whether to apply curvilinear elements. The default is ``False``. - usefallback : bool, optional + fallback : bool, optional Whether to retain as a fallback. The default is ``True``. - usephi : bool, optional + phi : bool, optional Whether to use the Phi mesher for layered geometry. The default is ``True``. - automodelresolution : bool, optional + auto_model_resolution : bool, optional Whether to automatically calculate the resolution length based on each object's effective thickness. The default is ``True``. - modelresolutionlength : float, optional + model_resolution_length : float, optional Resolution thickness with units if ``automodelresolution=False``. The default ``"0.0001mm"``. @@ -836,17 +844,16 @@ def assign_initial_mesh_from_slider( >>> oModule.InitialMeshSettings """ - if self._app.design_type == "2D Extractor" or self._app.design_type == "Maxwell 2D": + if self._app.design_type in ["2D Extractor", "Maxwell 2D"]: mesh_methods = ["Auto", "AnsoftClassic"] else: mesh_methods = ["Auto", "AnsoftTAU", "AnsoftClassic"] if method not in mesh_methods: - raise RuntimeError(f"Invalid mesh method {method}") # pragma: no cover + raise ValueError(f"Invalid mesh method {method}") - modelres = ["NAME:GlobalModelRes", "UseAutoLength:=", automodelresolution] - if not automodelresolution: - modelres.append("DefeatureLength:=") - modelres.append(modelresolutionlength) + modelres = ["NAME:GlobalModelRes", "UseAutoLength:=", auto_model_resolution] + if not auto_model_resolution: + modelres += ["DefeatureLength:=", model_resolution_length] surface_appr = [ "NAME:GlobalSurfApproximation", "CurvedSurfaceApproxChoice:=", @@ -854,28 +861,140 @@ def assign_initial_mesh_from_slider( "SliderMeshSettings:=", level, ] - if self._app.design_type == "2D Extractor" or self._app.design_type == "Maxwell 2D": + + if self._app.design_type in ["2D Extractor", "Maxwell 2D"]: + args = ["NAME:MeshSettings", surface_appr, modelres, "MeshMethod:=", method] + else: + args = [ + "NAME:MeshSettings", + surface_appr, + ["NAME:GlobalCurvilinear", "Apply:=", curvilinear], + modelres, + "MeshMethod:=", + method, + "UseLegacyFaceterForTauVolumeMesh:=", + False, + "DynamicSurfaceResolution:=", + dynamic_surface, + "UseFlexMeshingForTAUvolumeMesh:=", + flex_mesh, + ] + if self._app.design_type == "HFSS": + args += ["UseAlternativeMeshMethodsAsFallBack:=", fallback, "AllowPhiForLayeredGeometry:=", phi] + self.omeshmodule.InitialMeshSettings(args) + return True + + @pyaedt_function_handler() + def assign_initial_mesh( + self, + method="Auto", + surface_deviation=None, + normal_deviation=None, + aspect_ratio=None, + flex_mesh=False, + curvilinear=False, + fallback=True, + phi=True, + auto_model_resolution=True, + model_resolution_length="0.0001mm", + ): + """Assign a surface mesh level to an object. + + Parameters + ---------- + method : str, optional + Meshing method. Options are ``"Auto"``, ``"AnsoftTAU"``, and ``"AnsoftClassic"`` + The default is ``"Auto"``. + surface_deviation : float or str, optional + Surface deviation. + The default is ``None``, in which case the default value is assigned. + normal_deviation : float or str, optional + Normal deviation. + The default is ``None``, in which case the default value is assigned. + aspect_ratio : float or str, optional + Aspect ratio. + The default is ``None``, in which case the default value is assigned. + flex_mesh : bool, optional + Whether to use flexible mesh for TAU volume mesh. The default is ``False``. + curvilinear : bool, optional + Whether to apply curvilinear elements. The default is ``False``. + fallback : bool, optional + Whether to retain as a fallback. The default is ``True``. + phi : bool, optional + Whether to use the Phi mesher for layered geometry. + The default is ``True``. + auto_model_resolution : bool, optional + Whether to automatically calculate the resolution length + based on each object's effective thickness. The default is ``True``. + model_resolution_length : float or str, optional + Resolution thickness with units if ``automodelresolution=False``. + The default ``"0.0001mm"``. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + References + ---------- + + >>> oModule.InitialMeshSettings + """ + if self._app.design_type in ["2D Extractor", "Maxwell 2D"]: + mesh_methods = ["Auto", "AnsoftClassic"] + else: + mesh_methods = ["Auto", "AnsoftTAU", "AnsoftClassic"] + if method not in mesh_methods: + raise ValueError(f"Invalid mesh method {method}") + + modelres = ["NAME:GlobalModelRes", "UseAutoLength:=", auto_model_resolution] + if not auto_model_resolution: + modelres.append("DefeatureLength:=") + modelres.append(model_resolution_length) + + surface_appr = [ + "NAME:GlobalSurfApproximation", + "CurvedSurfaceApproxChoice:=", + "ManualSettings", + "SurfDevChoice:=", + ] + + if not surface_deviation: + surface_appr.append(0) + else: + surface_appr += [2, "SurfDev:=", surface_deviation] + + surface_appr.append("NormalDevChoice:=") + if not normal_deviation: + surface_appr.append(1) + else: + surface_appr += [2, "NormalDev:=", normal_deviation] + + surface_appr.append("AspectRatioChoice:=") + if not aspect_ratio: + surface_appr.append(1) + else: + surface_appr += [2, "AspectRatio:=", aspect_ratio] + + if self._app.design_type in ["2D Extractor", "Maxwell 2D"]: args = ["NAME:MeshSettings", surface_appr, modelres, "MeshMethod:=", method] else: args = [ "NAME:MeshSettings", surface_appr, - ["NAME:GlobalCurvilinear", "Apply:=", applycurvilinear], + ["NAME:GlobalCurvilinear", "Apply:=", curvilinear], modelres, "MeshMethod:=", method, "UseLegacyFaceterForTauVolumeMesh:=", False, "DynamicSurfaceResolution:=", - usedynamicsurface, + False, "UseFlexMeshingForTAUvolumeMesh:=", - useflexmesh, + flex_mesh, ] if self._app.design_type == "HFSS": - args.append("UseAlternativeMeshMethodsAsFallBack:=") - args.append(usefallback) - args.append("AllowPhiForLayeredGeometry:=") - args.append(usephi) + args += ["UseAlternativeMeshMethodsAsFallBack:=", fallback, "AllowPhiForLayeredGeometry:=", phi] self.omeshmodule.InitialMeshSettings(args) return True diff --git a/tests/system/general/test_20_HFSS.py b/tests/system/general/test_20_HFSS.py index 9df72ef1b5c..e4df99e6860 100644 --- a/tests/system/general/test_20_HFSS.py +++ b/tests/system/general/test_20_HFSS.py @@ -902,9 +902,13 @@ def test_24_create_curvilinear(self): mesh.delete() assert len(self.aedtapp.mesh.meshoperations) == 2 - def test_30_assign_initial_mesh(self): + def test_30a_assign_initial_mesh(self): assert self.aedtapp.mesh.assign_initial_mesh_from_slider(6) + def test_03b_assign_initial_mesh(self): + assert self.aedtapp.mesh.assign_initial_mesh() + assert self.aedtapp.mesh.assign_initial_mesh(normal_deviation="25deg", surface_deviation=0.2, aspect_ratio=20) + def test_30a_add_mesh_link(self): design_name = self.aedtapp.design_name nominal_adaptive = self.aedtapp.nominal_adaptive diff --git a/tests/system/general/test_27_Maxwell2D.py b/tests/system/general/test_27_Maxwell2D.py index 18163164eda..eaa8c67e51b 100644 --- a/tests/system/general/test_27_Maxwell2D.py +++ b/tests/system/general/test_27_Maxwell2D.py @@ -87,9 +87,20 @@ def init(self, aedtapp, m2d_ctrl_prg, m2d_field_export, m2d_circuit, local_scrat self.m2d_circuit = m2d_circuit self.local_scratch = local_scratch - def test_03_assign_initial_mesh_from_slider(self): + def test_03a_assign_initial_mesh_from_slider(self): assert self.aedtapp.mesh.assign_initial_mesh_from_slider(4) + def test_03a_assign_initial_mesh_from_slider_failure_due_to_wrong_method(self): + with pytest.raises(ValueError): + self.aedtapp.mesh.assign_initial_mesh_from_slider(method="dummy") + + def test_03b_assign_initial_mesh(self): + assert self.aedtapp.mesh.assign_initial_mesh(surface_deviation="2mm") + + def test_03b_assign_initial_mesh_failure_due_to_wrong_method(self): + with pytest.raises(ValueError): + self.aedtapp.mesh.assign_initial_mesh(method="dummy") + def test_04_create_winding(self): bounds = self.aedtapp.assign_winding(assignment=["Coil"], current=20e-3) assert bounds diff --git a/tests/system/general/test_28_Maxwell3D.py b/tests/system/general/test_28_Maxwell3D.py index 9a61868f337..78eb079aab1 100644 --- a/tests/system/general/test_28_Maxwell3D.py +++ b/tests/system/general/test_28_Maxwell3D.py @@ -301,15 +301,18 @@ def test_24_create_curvilinear(self): def test_24_create_edge_cut(self): assert self.aedtapp.mesh.assign_edge_cut(["Coil"]) - def test_24_density_control(self): + def test_24a_density_control(self): assert self.aedtapp.mesh.assign_density_control(["Coil"], maximum_element_length="2mm", layers_number="3") - def test_24_density_control(self): + def test_24b_density_control(self): assert self.aedtapp.mesh.assign_rotational_layer(["Coil"]) - def test_25_assign_initial_mesh(self): + def test_25a_assign_initial_mesh(self): assert self.aedtapp.mesh.assign_initial_mesh_from_slider(4) + def test_25b_assign_initial_mesh(self): + assert self.aedtapp.mesh.assign_initial_mesh(surface_deviation="2mm") + @pytest.mark.skipif(is_linux, reason="Crashing on Linux") def test_26_create_udp(self): my_udpPairs = []