From ba72cc20e31a2c541cdaa0cbffe19798be8022ca Mon Sep 17 00:00:00 2001 From: marioalexis Date: Fri, 11 Oct 2024 11:58:48 -0300 Subject: [PATCH] Fem: Netgen meshing parameters improvements --- src/Mod/Fem/femcommands/commands.py | 1 + src/Mod/Fem/femmesh/netgentools.py | 72 ++++++++--- src/Mod/Fem/femobjects/mesh_netgen.py | 122 +++++++++++++++--- src/Mod/Fem/femtaskpanels/task_mesh_netgen.py | 32 ++++- 4 files changed, 187 insertions(+), 40 deletions(-) diff --git a/src/Mod/Fem/femcommands/commands.py b/src/Mod/Fem/femcommands/commands.py index 78085ce8b4..f558529987 100644 --- a/src/Mod/Fem/femcommands/commands.py +++ b/src/Mod/Fem/femcommands/commands.py @@ -837,6 +837,7 @@ class _MeshNetgenFromShape(CommandManager): ) ) FreeCADGui.doCommand("FreeCAD.ActiveDocument.ActiveObject.Fineness = 'Moderate'") + FreeCADGui.doCommand("FreeCAD.ActiveDocument.ActiveObject.EndStep = 'OptimizeVolume'") # Netgen mesh object could be added without an active analysis # but if there is an active analysis move it in there import FemGui diff --git a/src/Mod/Fem/femmesh/netgentools.py b/src/Mod/Fem/femmesh/netgentools.py index c0a7857a95..c4ef9bbd0b 100644 --- a/src/Mod/Fem/femmesh/netgentools.py +++ b/src/Mod/Fem/femmesh/netgentools.py @@ -35,7 +35,8 @@ import FreeCAD import Fem try: - from netgen import occ, config as ng_config + from netgen import occ, meshing, config as ng_config + import pyngcore as ngcore except ModuleNotFoundError: FreeCAD.Console.PrintError("To use FemMesh Netgen objects, install the Netgen Python bindings") @@ -64,6 +65,15 @@ class NetgenTools: 15: [0, 1, 2, 3, 4, 5, 6, 8, 7, 12, 14, 13, 9, 10, 11, 15, 16, 17, 18, 19], # penta15 } + meshing_step = { + "AnalizeGeometry": 1, # MESHCONST_ANALYSE + "MeshEdges": 2, # MESHCONST_MESHEDGES + "MeshSurface": 3, # MESHCONST_MESHSURFACE + "OptimizeSurface": 4, # MESHCONST_OPTSURFACE + "MeshVolume": 5, # MESHCONST_MESHVOLUME + "OptimizeVolume": 6, # MESHCONST_OPTVOLUME + } + name = "Netgen" def __init__(self, obj): @@ -97,7 +107,6 @@ NetgenTools.run_netgen(**{params}) "brep_file": self.brep_file, "threads": self.obj.Threads, "heal": self.obj.HealShape, - "fineness": self.obj.Fineness, "params": self.get_meshing_parameters(), "second_order": self.obj.SecondOrder, "result_file": self.result_file, @@ -118,30 +127,15 @@ NetgenTools.run_netgen(**{params}) return True @staticmethod - def run_netgen(brep_file, threads, heal, fineness, params, second_order, result_file): - import pyngcore as ngcore - from netgen import meshing + def run_netgen(brep_file, threads, heal, params, second_order, result_file): geom = occ.OCCGeometry(brep_file) ngcore.SetNumThreads(threads) - if fineness == "UserDefined": - mp = meshing.MeshingParameters(**params) - elif fineness == "VeryCoarse": - mp = meshing.meshsize.very_coarse - elif fineness == "Coarse": - mp = meshing.meshsize.coarse - elif fineness == "Moderate": - mp = meshing.meshsize.moderate - elif fineness == "Fine": - mp = meshing.meshsize.fine - elif fineness == "VeryFine": - mp = meshing.meshsize.very_fine - with ngcore.TaskManager(): if heal: geom.Heal() - mesh = geom.GenerateMesh(mp=mp) + mesh = geom.GenerateMesh(mp=meshing.MeshingParameters(**params)) if second_order: mesh.SecondOrder() @@ -256,8 +250,8 @@ NetgenTools.run_netgen(**{params}) "segmentsperedge": self.obj.SegmentsPerEdge, "elsizeweight": self.obj.ElementSizeWeight, "parthread": self.obj.ParallelMeshing, - "perfstepsstart": self.obj.StartStep, - "perfstepsend": self.obj.EndStep, + "perfstepsstart": NetgenTools.meshing_step[self.obj.StartStep], + "perfstepsend": NetgenTools.meshing_step[self.obj.EndStep], "giveuptol2d": self.obj.GiveUpTolerance2d, "giveuptol": self.obj.GiveUpTolerance, "giveuptolopenquads": self.obj.GiveUpToleranceOpenQuads, @@ -280,6 +274,42 @@ NetgenTools.run_netgen(**{params}) "closeedgefac": self.obj.CloseEdgeFactor, } + # set specific parameters by fineness + if self.obj.Fineness == "VeryCoarse": + params["curvaturesafety"] = 1 + params["segmentsperedge"] = 0.3 + params["grading"] = 0.7 + params["closeedgefac"] = 0.5 + params["optsteps3d"] = 5 + + elif self.obj.Fineness == "Coarse": + params["curvaturesafety"] = 1.5 + params["segmentsperedge"] = 0.5 + params["grading"] = 0.5 + params["closeedgefac"] = 1 + params["optsteps3d"] = 5 + + elif self.obj.Fineness == "Moderate": + params["curvaturesafety"] = 2 + params["segmentsperedge"] = 1 + params["grading"] = 0.3 + params["closeedgefac"] = 2 + params["optsteps3d"] = 5 + + elif self.obj.Fineness == "Fine": + params["curvaturesafety"] = 3 + params["segmentsperedge"] = 2 + params["grading"] = 0.2 + params["closeedgefac"] = 3.5 + params["optsteps3d"] = 5 + + elif self.obj.Fineness == "VeryFine": + params["curvaturesafety"] = 5 + params["segmentsperedge"] = 3 + params["grading"] = 0.1 + params["closeedgefac"] = 5 + params["optsteps3d"] = 5 + return params @staticmethod diff --git a/src/Mod/Fem/femobjects/mesh_netgen.py b/src/Mod/Fem/femobjects/mesh_netgen.py index c04482b978..3b023b384f 100644 --- a/src/Mod/Fem/femobjects/mesh_netgen.py +++ b/src/Mod/Fem/femobjects/mesh_netgen.py @@ -65,15 +65,6 @@ class MeshNetgen(base_fempythonobject.BaseFemPythonObject): value="cmdDmustm", ) ) - prop.append( - _PropHelper( - type="App::PropertyEnumeration", - name="Fineness", - group="Mesh Parameters", - doc="Fineness", - value=["VeryCoarse", "Coarse", "Moderate", "Fine", "VeryFine", "UserDefined"], - ) - ) prop.append( _PropHelper( type="App::PropertyInteger", @@ -284,22 +275,33 @@ class MeshNetgen(base_fempythonobject.BaseFemPythonObject): value=0.2, ) ) + + # Netgen meshing steps + meshing_step = [ + "AnalizeGeometry", + "MeshEdges", + "MeshSurface", + "OptimizeSurface", + "MeshVolume", + "OptimizeVolume", + ] + prop.append( _PropHelper( - type="App::PropertyInteger", + type="App::PropertyEnumeration", name="StartStep", group="Mesh Parameters", - doc="Start at step", - value=0, + doc="First step", + value=meshing_step, ) ) prop.append( _PropHelper( - type="App::PropertyInteger", + type="App::PropertyEnumeration", name="EndStep", group="Mesh Parameters", - doc="EndStep", - value=6, + doc="Last step", + value=meshing_step, ) ) prop.append( @@ -482,5 +484,95 @@ class MeshNetgen(base_fempythonobject.BaseFemPythonObject): value=False, ) ) + prop.append( + _PropHelper( + type="App::PropertyEnumeration", + name="Fineness", + group="Mesh Parameters", + doc="Mesh granularity.\n" + + "If differs from UserDefined, uses specific values\n" + + "for CurvatureSafety, SegmentsPerEdge, GrowthRate,\n" + + "CloseEdgeFactor and OptimizationSteps3d", + value=["VeryCoarse", "Coarse", "Moderate", "Fine", "VeryFine", "UserDefined"], + ) + ) return prop + + def onChanged(self, obj, prop): + if prop == "Fineness": + if obj.Fineness != "UserDefined": + p = self.get_predef_fineness_params(obj.Fineness) + obj.CurvatureSafety = p["curvaturesafety"] + obj.SegmentsPerEdge = p["segmentsperedge"] + obj.GrowthRate = p["grading"] + obj.CloseEdgeFactor = p["closeedgefac"] + obj.OptimizationSteps3d = p["optsteps3d"] + + obj.setPropertyStatus("CurvatureSafety", "ReadOnly") + obj.setPropertyStatus("SegmentsPerEdge", "ReadOnly") + obj.setPropertyStatus("GrowthRate", "ReadOnly") + obj.setPropertyStatus("CloseEdgeFactor", "ReadOnly") + obj.setPropertyStatus("OptimizationSteps3d", "ReadOnly") + else: + obj.setPropertyStatus("CurvatureSafety", "-ReadOnly") + obj.setPropertyStatus("SegmentsPerEdge", "-ReadOnly") + obj.setPropertyStatus("GrowthRate", "-ReadOnly") + obj.setPropertyStatus("CloseEdgeFactor", "-ReadOnly") + obj.setPropertyStatus("OptimizationSteps3d", "-ReadOnly") + + def onDocumentRestored(self, obj): + # update old project with new properties + for prop in self._get_properties(): + try: + obj.getPropertyByName(prop.name) + except Base.PropertyError: + prop.add_to_object(obj) + + # for StartStep and EndStep set enumeration index from old integer value + if prop.name == "StartStep" or prop.name == "EndStep": + prop.handle_change_type( + obj, "App::PropertyInteger", lambda x: 0 if x <= 1 else 5 if x >= 6 else x - 1 + ) + + def get_predef_fineness_params(self, fineness): + # set specific parameters by fineness + params = {} + if fineness == "VeryCoarse": + params["curvaturesafety"] = 1 + params["segmentsperedge"] = 0.3 + params["grading"] = 0.7 + params["closeedgefac"] = 0.5 + params["optsteps3d"] = 5 + elif fineness == "Coarse": + params["curvaturesafety"] = 1.5 + params["segmentsperedge"] = 0.5 + params["grading"] = 0.5 + params["closeedgefac"] = 1 + params["optsteps3d"] = 5 + elif fineness == "Moderate": + params["curvaturesafety"] = 2 + params["segmentsperedge"] = 1 + params["grading"] = 0.3 + params["closeedgefac"] = 2 + params["optsteps3d"] = 5 + elif fineness == "Fine": + params["curvaturesafety"] = 3 + params["segmentsperedge"] = 2 + params["grading"] = 0.2 + params["closeedgefac"] = 3.5 + params["optsteps3d"] = 5 + elif fineness == "VeryFine": + params["curvaturesafety"] = 5 + params["segmentsperedge"] = 3 + params["grading"] = 0.1 + params["closeedgefac"] = 5 + params["optsteps3d"] = 5 + elif fineness == "UserDefined": + params["curvaturesafety"] = 2 + params["segmentsperedge"] = 2 + params["grading"] = 0.3 + params["closeedgefac"] = 2 + params["optsteps3d"] = 3 + + return params diff --git a/src/Mod/Fem/femtaskpanels/task_mesh_netgen.py b/src/Mod/Fem/femtaskpanels/task_mesh_netgen.py index 56354d7d0a..3d9a3b03d1 100644 --- a/src/Mod/Fem/femtaskpanels/task_mesh_netgen.py +++ b/src/Mod/Fem/femtaskpanels/task_mesh_netgen.py @@ -102,6 +102,7 @@ class _TaskPanel(base_femmeshtaskpanel._BaseMeshTaskPanel): self.curvature_safety = self.obj.CurvatureSafety self.seg_per_edge = self.obj.SegmentsPerEdge self.second_order = self.obj.SecondOrder + self.user_p = self.get_user_fineness_params(self.obj) def set_mesh_params(self): self.obj.MinSize = self.min_size @@ -155,18 +156,41 @@ class _TaskPanel(base_femmeshtaskpanel._BaseMeshTaskPanel): def fineness_changed(self, index): self.fineness = self.fineness_enum[index] + self.obj.Fineness = self.fineness if self.fineness == "UserDefined": - self.form.qsb_min_size.setEnabled(True) - self.form.qsb_max_size.setEnabled(True) self.form.dsb_seg_per_edge.setEnabled(True) self.form.dsb_growth_rate.setEnabled(True) self.form.dsb_curvature_safety.setEnabled(True) + self.form.dsb_seg_per_edge.setProperty("value", self.user_p["segmentsperedge"]) + self.form.dsb_growth_rate.setProperty("value", self.user_p["grading"]) + self.form.dsb_curvature_safety.setProperty("value", self.user_p["curvaturesafety"]) + # set properties + self.obj.SegmentsPerEdge = self.user_p["segmentsperedge"] + self.obj.GrowthRate = self.user_p["grading"] + self.obj.CurvatureSafety = self.user_p["curvaturesafety"] + self.obj.OptimizationSteps3d = self.user_p["optsteps3d"] + self.obj.CloseEdgeFactor = self.user_p["closeedgefac"] else: - self.form.qsb_min_size.setEnabled(False) - self.form.qsb_max_size.setEnabled(False) + p = self.obj.Proxy.get_predef_fineness_params(self.fineness) self.form.dsb_seg_per_edge.setEnabled(False) self.form.dsb_growth_rate.setEnabled(False) self.form.dsb_curvature_safety.setEnabled(False) + self.form.dsb_growth_rate.setProperty("value", p["grading"]) + self.form.dsb_seg_per_edge.setProperty("value", p["segmentsperedge"]) + self.form.dsb_curvature_safety.setProperty("value", p["curvaturesafety"]) def second_order_changed(self, bool_value): self.second_order = bool_value + + def get_user_fineness_params(self, obj): + # parameters affected by the fineness value. If initial fineness is "UserDefined" + # use current values otherwise use predefined values + p = obj.Proxy.get_predef_fineness_params("UserDefined") + if obj.Fineness == "UserDefined": + p["curvaturesafety"] = obj.CurvatureSafety + p["grading"] = obj.GrowthRate + p["segmentsperedge"] = obj.SegmentsPerEdge + p["optsteps3d"] = obj.OptimizationSteps3d + p["closeedgefac"] = obj.CloseEdgeFactor + + return p