Fem: New implementation of FemMesh Netgen object
This commit is contained in:
@@ -165,6 +165,7 @@ SET(FemMesh_SRCS
|
||||
femmesh/gmshtools.py
|
||||
femmesh/meshsetsgetter.py
|
||||
femmesh/meshtools.py
|
||||
femmesh/netgentools.py
|
||||
)
|
||||
|
||||
SET(FemObjects_SRCS
|
||||
@@ -194,6 +195,7 @@ SET(FemObjects_SRCS
|
||||
femobjects/mesh_boundarylayer.py
|
||||
femobjects/mesh_gmsh.py
|
||||
femobjects/mesh_group.py
|
||||
femobjects/mesh_netgen.py
|
||||
femobjects/mesh_region.py
|
||||
femobjects/mesh_result.py
|
||||
femobjects/result_mechanical.py
|
||||
@@ -592,6 +594,7 @@ SET(FemGuiTaskPanels_SRCS
|
||||
femtaskpanels/task_mesh_gmsh.py
|
||||
femtaskpanels/task_mesh_group.py
|
||||
femtaskpanels/task_mesh_region.py
|
||||
femtaskpanels/task_mesh_netgen.py
|
||||
femtaskpanels/task_result_mechanical.py
|
||||
femtaskpanels/task_solver_ccxtools.py
|
||||
)
|
||||
@@ -636,6 +639,7 @@ SET(FemGuiViewProvider_SRCS
|
||||
femviewprovider/view_mesh_boundarylayer.py
|
||||
femviewprovider/view_mesh_gmsh.py
|
||||
femviewprovider/view_mesh_group.py
|
||||
femviewprovider/view_mesh_netgen.py
|
||||
femviewprovider/view_mesh_region.py
|
||||
femviewprovider/view_mesh_result.py
|
||||
femviewprovider/view_result_mechanical.py
|
||||
|
||||
@@ -415,6 +415,7 @@ SET(FemGuiPythonUI_SRCS
|
||||
Resources/ui/MeshGmsh.ui
|
||||
Resources/ui/MeshGroup.ui
|
||||
Resources/ui/MeshGroupXDMFExport.ui
|
||||
Resources/ui/MeshNetgen.ui
|
||||
Resources/ui/MeshRegion.ui
|
||||
Resources/ui/ResultHints.ui
|
||||
Resources/ui/ResultShow.ui
|
||||
|
||||
277
src/Mod/Fem/Gui/Resources/ui/MeshNetgen.ui
Normal file
277
src/Mod/Fem/Gui/Resources/ui/MeshNetgen.ui
Normal file
@@ -0,0 +1,277 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>NetgenMesh</class>
|
||||
<widget class="QWidget" name="NetgenMesh">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>475</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>FEM Mesh by Netgen</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gpb_mesh_params">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>1677215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Mesh Parameters</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_1">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="lbl_fineness">
|
||||
<property name="text">
|
||||
<string>Fineness:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="cb_fineness"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="l_max">
|
||||
<property name="text">
|
||||
<string>Maximal Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="qsb_max_size">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">mm</string>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeft|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="keyboardTracking">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1000000000000000000000.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>1000.000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="l_min">
|
||||
<property name="text">
|
||||
<string>Minimal Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="qsb_min_size">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">mm</string>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeft|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="keyboardTracking">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1000000000000000000000.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="ckb_second_order">
|
||||
<property name="text">
|
||||
<string>Second Order</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Growth Rate:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QDoubleSpinBox" name="dsb_growth_rate">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Curvature Safety:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QDoubleSpinBox" name="dsb_curvature_safety">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Segments Per Edge:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QDoubleSpinBox" name="dsb_seg_per_edge">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb03_run_netgen">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>1677215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Netgen</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="1" column="1">
|
||||
<layout class="QGridLayout" name="gl_actions">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTextEdit" name="te_output">
|
||||
<property name="lineWrapMode">
|
||||
<enum>QTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="l_time">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Time:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QPushButton" name="pb_get_netgen_version">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Netgen version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::QuantitySpinBox</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>Gui/QuantitySpinBox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
297
src/Mod/Fem/femmesh/netgentools.py
Normal file
297
src/Mod/Fem/femmesh/netgentools.py
Normal file
@@ -0,0 +1,297 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2024 Mario Passaglia <mpassaglia[at]cbc.uba.ar> *
|
||||
# * *
|
||||
# * This file is part of FreeCAD. *
|
||||
# * *
|
||||
# * FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
# * under the terms of the GNU Lesser General Public License as *
|
||||
# * published by the Free Software Foundation, either version 2.1 of the *
|
||||
# * License, or (at your option) any later version. *
|
||||
# * *
|
||||
# * FreeCAD is distributed in the hope that it will be useful, but *
|
||||
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
# * Lesser General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Lesser General Public *
|
||||
# * License along with FreeCAD. If not, see *
|
||||
# * <https://www.gnu.org/licenses/>. *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "Tools for the work with Netgen mesher"
|
||||
__author__ = "Mario Passaglia"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
import numpy as np
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import FreeCAD
|
||||
import Fem
|
||||
|
||||
try:
|
||||
from netgen import occ, config as ng_config
|
||||
except ModuleNotFoundError:
|
||||
FreeCAD.Console.PrintError("To use FemMesh Netgen objects, install the Netgen Python bindings")
|
||||
|
||||
|
||||
class NetgenTools:
|
||||
|
||||
# to change order of nodes from netgen to smesh
|
||||
order_edge = {
|
||||
2: [0, 1, 2], # seg2
|
||||
3: [0, 1, 2], # seg3
|
||||
}
|
||||
order_face = {
|
||||
3: [0, 1, 2, 3, 4, 5, 6, 7], # tria3
|
||||
6: [0, 1, 2, 5, 3, 4, 6, 7], # tria6
|
||||
4: [0, 1, 2, 3, 4, 5, 6, 7], # quad4
|
||||
8: [0, 1, 2, 3, 4, 7, 5, 6], # quad8
|
||||
}
|
||||
order_volume = {
|
||||
4: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], # tetra4
|
||||
10: [0, 1, 2, 3, 4, 7, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], # tetra10
|
||||
8: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], # hexa8
|
||||
20: [0, 1, 2, 3, 4, 5, 6, 7, 8, 11, 9, 10, 12, 15, 13, 14, 16, 17, 18, 19], # hexa20
|
||||
5: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], # pyra5
|
||||
13: [0, 1, 2, 3, 4, 5, 8, 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], # pyra13
|
||||
6: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], # penta6
|
||||
15: [0, 1, 2, 3, 4, 5, 6, 8, 7, 12, 14, 13, 9, 10, 11, 15, 16, 17, 18, 19], # penta15
|
||||
}
|
||||
|
||||
name = "Netgen"
|
||||
|
||||
def __init__(self, obj):
|
||||
self.obj = obj
|
||||
self.fem_mesh = None
|
||||
self.process = None
|
||||
self.tmpdir = ""
|
||||
|
||||
def write_geom(self):
|
||||
if not self.tmpdir:
|
||||
self.tmpdir = tempfile.mkdtemp(prefix="fem_")
|
||||
|
||||
sh = self.obj.Shape.getPropertyOfGeometry()
|
||||
self.brep_file = self.tmpdir + "/shape.brep"
|
||||
self.result_file = self.tmpdir + "/result.npy"
|
||||
sh.exportBrep(self.brep_file)
|
||||
|
||||
code = """
|
||||
from femmesh.netgentools import NetgenTools
|
||||
|
||||
NetgenTools.run_netgen(**{params})
|
||||
"""
|
||||
|
||||
def compute(self):
|
||||
self.write_geom()
|
||||
mesh_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,
|
||||
}
|
||||
|
||||
code_str = self.code.format(params=mesh_params)
|
||||
|
||||
cmd_list = [
|
||||
sys.executable,
|
||||
"-c",
|
||||
code_str,
|
||||
]
|
||||
self.process = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
out, err = self.process.communicate()
|
||||
if self.process.returncode != 0:
|
||||
raise RuntimeError(err.decode("utf-8"))
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def run_netgen(brep_file, threads, heal, fineness, params, second_order, result_file):
|
||||
import pyngcore as ngcore
|
||||
from netgen import meshing
|
||||
|
||||
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)
|
||||
|
||||
if second_order:
|
||||
mesh.SecondOrder()
|
||||
|
||||
coords = mesh.Coordinates()
|
||||
|
||||
edges = mesh.Elements1D().NumPy()
|
||||
faces = mesh.Elements2D().NumPy()
|
||||
volumes = mesh.Elements3D().NumPy()
|
||||
|
||||
nod_edges = edges["nodes"]
|
||||
nod_faces = faces["nodes"]
|
||||
nod_volumes = volumes["nodes"]
|
||||
|
||||
np_edges = (nod_edges != 0).sum(axis=1).tolist()
|
||||
np_faces = faces["np"].tolist()
|
||||
np_volumes = volumes["np"].tolist()
|
||||
|
||||
# set smesh node order
|
||||
for i in range(faces.size):
|
||||
nod_faces[i] = nod_faces[i][NetgenTools.order_face[np_faces[i]]]
|
||||
|
||||
for i in range(volumes.size):
|
||||
nod_volumes[i] = nod_volumes[i][NetgenTools.order_volume[np_volumes[i]]]
|
||||
|
||||
flat_edges = nod_edges[nod_edges != 0].tolist()
|
||||
flat_faces = nod_faces[nod_faces != 0].tolist()
|
||||
flat_volumes = nod_volumes[nod_volumes != 0].tolist()
|
||||
|
||||
result = {
|
||||
"coords": coords,
|
||||
"Edges": [flat_edges, np_edges],
|
||||
"Faces": [flat_faces, np_faces],
|
||||
"Volumes": [flat_volumes, np_volumes],
|
||||
}
|
||||
|
||||
# create groups
|
||||
nb_edges = edges.size
|
||||
nb_faces = faces.size
|
||||
nb_volumes = volumes.size
|
||||
|
||||
idx_edges = edges["index"]
|
||||
idx_faces = faces["index"]
|
||||
idx_volumes = volumes["index"]
|
||||
|
||||
groups = {"Edges": [], "Faces": [], "Solids": []}
|
||||
for i in np.unique(idx_edges):
|
||||
edge_i = (np.nonzero(idx_edges == i)[0] + 1).tolist()
|
||||
groups["Edges"].append([i, edge_i])
|
||||
for i in np.unique(idx_faces):
|
||||
face_i = (np.nonzero(idx_faces == i)[0] + nb_edges + 1).tolist()
|
||||
groups["Faces"].append([i, face_i])
|
||||
|
||||
for i in np.unique(idx_volumes):
|
||||
volume_i = (np.nonzero(idx_volumes == i)[0] + nb_edges + nb_faces + 1).tolist()
|
||||
groups["Solids"].append([i, volume_i])
|
||||
|
||||
np.save(result_file, [result, groups])
|
||||
|
||||
def fem_mesh_from_result(self):
|
||||
fem_mesh = Fem.FemMesh()
|
||||
|
||||
# load Netgen result
|
||||
netgen_result, groups = np.load(self.result_file, allow_pickle=True)
|
||||
|
||||
for node in netgen_result["coords"]:
|
||||
fem_mesh.addNode(*node)
|
||||
|
||||
fem_mesh.addEdgeList(*netgen_result["Edges"])
|
||||
fem_mesh.addFaceList(*netgen_result["Faces"])
|
||||
fem_mesh.addVolumeList(*netgen_result["Volumes"])
|
||||
|
||||
for g in groups["Edges"]:
|
||||
grp_id = fem_mesh.addGroup("Edge" + str(g[0]), "Edge")
|
||||
fem_mesh.addGroupElements(grp_id, g[1])
|
||||
|
||||
for g in groups["Faces"]:
|
||||
grp_id = fem_mesh.addGroup("Face" + str(g[0]), "Face")
|
||||
fem_mesh.addGroupElements(grp_id, g[1])
|
||||
|
||||
for g in groups["Solids"]:
|
||||
grp_id = fem_mesh.addGroup("Solid" + str(g[0]), "Volume")
|
||||
fem_mesh.addGroupElements(grp_id, g[1])
|
||||
|
||||
return fem_mesh
|
||||
|
||||
def update_properties(self):
|
||||
self.obj.FemMesh = self.fem_mesh_from_result()
|
||||
|
||||
def get_meshing_parameters(self):
|
||||
params = {
|
||||
"optimize3d": self.obj.Optimize3d,
|
||||
"optimize2d": self.obj.Optimize2d,
|
||||
"optsteps3d": self.obj.OptimizationSteps3d,
|
||||
"optsteps2d": self.obj.OptimizationSteps2d,
|
||||
"opterrpow": self.obj.OptimizationErrorPower,
|
||||
"blockfill": self.obj.BlockFill,
|
||||
"filldist": self.obj.FillDistance.Value,
|
||||
"safety": self.obj.Safety,
|
||||
"relinnersafety": self.obj.RelinnerSafety,
|
||||
"uselocalh": self.obj.UseLocalH,
|
||||
"grading": self.obj.GrowthRate,
|
||||
"delaunay": self.obj.Delaunay,
|
||||
"delaunay2d": self.obj.Delaunay2d,
|
||||
"maxh": self.obj.MaxSize.Value,
|
||||
"minh": self.obj.MinSize.Value,
|
||||
"startinsurface": self.obj.StartInSurface,
|
||||
"checkoverlap": self.obj.CheckOverlap,
|
||||
"checkoverlappingboundary": self.obj.CheckOverlappingBoundary,
|
||||
"checkchartboundary": self.obj.CheckChartBoundary,
|
||||
"curvaturesafety": self.obj.CurvatureSafety,
|
||||
"segmentsperedge": self.obj.SegmentsPerEdge,
|
||||
"elsizeweight": self.obj.ElementSizeWeight,
|
||||
"parthread": self.obj.ParallelMeshing,
|
||||
"perfstepsstart": self.obj.StartStep,
|
||||
"perfstepsend": self.obj.EndStep,
|
||||
"giveuptol2d": self.obj.GiveUpTolerance2d,
|
||||
"giveuptol": self.obj.GiveUpTolerance,
|
||||
"giveuptolopenquads": self.obj.GiveUpToleranceOpenQuads,
|
||||
"maxoutersteps": self.obj.MaxOuterSteps,
|
||||
"starshapeclass": self.obj.StarShapeClass,
|
||||
"baseelnp": self.obj.BaseElementNp,
|
||||
"sloppy": self.obj.Sloppy,
|
||||
"badellimit": self.obj.BadElementLimit,
|
||||
"check_impossible": self.obj.CheckImpossible,
|
||||
"only3D_domain_nr": self.obj.Only3dDomainNr,
|
||||
"secondorder": self.obj.SecondOrder,
|
||||
"elementorder": self.obj.ElementOrder,
|
||||
"quad_dominated": self.obj.QuadDominated,
|
||||
"try_hexes": self.obj.TryHexes,
|
||||
"inverttets": self.obj.InvertTets,
|
||||
"inverttrigs": self.obj.InvertTrigs,
|
||||
"autozrefine": self.obj.AutoZRefine,
|
||||
"parallel_meshing": self.obj.ParallelMeshing,
|
||||
"nthreads": self.obj.Threads,
|
||||
"closeedgefac": self.obj.CloseEdgeFactor,
|
||||
}
|
||||
|
||||
return params
|
||||
|
||||
@staticmethod
|
||||
def version():
|
||||
result = "{}: {}\n" + "{}: {}\n" + "{}: {}\n" + "{}: {}"
|
||||
return result.format(
|
||||
"Netgen",
|
||||
ng_config.version,
|
||||
"Python",
|
||||
ng_config.PYTHON_VERSION,
|
||||
"OpenCASCADE",
|
||||
occ.occ_version,
|
||||
"Use MPI",
|
||||
ng_config.USE_MPI,
|
||||
)
|
||||
|
||||
def __del__(self):
|
||||
if self.tmpdir:
|
||||
shutil.rmtree(self.tmpdir)
|
||||
486
src/Mod/Fem/femobjects/mesh_netgen.py
Normal file
486
src/Mod/Fem/femobjects/mesh_netgen.py
Normal file
@@ -0,0 +1,486 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2024 Mario Passaglia <mpassaglia[at]cbc.uba.ar> *
|
||||
# * *
|
||||
# * This file is part of FreeCAD. *
|
||||
# * *
|
||||
# * FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
# * under the terms of the GNU Lesser General Public License as *
|
||||
# * published by the Free Software Foundation, either version 2.1 of the *
|
||||
# * License, or (at your option) any later version. *
|
||||
# * *
|
||||
# * FreeCAD is distributed in the hope that it will be useful, but *
|
||||
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
# * Lesser General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Lesser General Public *
|
||||
# * License along with FreeCAD. If not, see *
|
||||
# * <https://www.gnu.org/licenses/>. *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD FEM mesh netgen document object"
|
||||
__author__ = "Mario Passaglia"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package mesh_netgen
|
||||
# \ingroup FEM
|
||||
# \brief mesh gmsh object
|
||||
|
||||
from FreeCAD import Base
|
||||
from . import base_fempythonobject
|
||||
|
||||
_PropHelper = base_fempythonobject._PropHelper
|
||||
|
||||
|
||||
class MeshNetgen(base_fempythonobject.BaseFemPythonObject):
|
||||
"""
|
||||
A Fem::FemMeshShapeBaseObject python type, add Netgen specific properties
|
||||
"""
|
||||
|
||||
Type = "Fem::FemMeshNetgen"
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
|
||||
for prop in self._get_properties():
|
||||
prop.add_to_object(obj)
|
||||
|
||||
def _get_properties(self):
|
||||
prop = []
|
||||
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyString",
|
||||
name="Optimize3d",
|
||||
group="Mesh Parameters",
|
||||
doc="3d optimization strategy.\n"
|
||||
+ "m: move nodes, M: move nodes, cheap functional\n"
|
||||
+ "s: swap faces, c: combine elements, d: divide elements,\n"
|
||||
+ "D: divide and join opposite edges, remove element,\n"
|
||||
+ "p: plot, no pause, P: plot, Pause,\n"
|
||||
+ "h: Histogramm, no pause, H: Histogramm, pause",
|
||||
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",
|
||||
name="OptimizationSteps3d",
|
||||
group="Mesh Parameters",
|
||||
doc="Number of 3d optimization steps",
|
||||
value=3,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyString",
|
||||
name="Optimize2d",
|
||||
group="Mesh Parameters",
|
||||
doc="2d optimization strategy.\n"
|
||||
+ "s: swap opt 6 lines/node, S: swap optimal elements,\n"
|
||||
+ "m: move nodes, p: plot, no pause\n"
|
||||
+ "P: plot, pause, c: combine",
|
||||
value="smcmSmcmSmcm",
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="OptimizationSteps2d",
|
||||
group="Mesh Parameters",
|
||||
doc="Number of 2d optimization steps",
|
||||
value=3,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="OptimizationErrorPower",
|
||||
group="Mesh Parameters",
|
||||
doc="Power of error to approximate max error optimization",
|
||||
value=2.0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="BlockFill",
|
||||
group="Mesh Parameters",
|
||||
doc="Do block filling",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyLength",
|
||||
name="FillDistance",
|
||||
group="Mesh Parameters",
|
||||
doc="Block filling up to distance",
|
||||
value=0.1,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="Safety",
|
||||
group="Mesh Parameters",
|
||||
doc="Radius of local environment (times h)",
|
||||
value=5.0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="RelinnerSafety",
|
||||
group="Mesh Parameters",
|
||||
doc="Radius of active environment (times h)",
|
||||
value=3.0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="LocalH",
|
||||
group="Mesh Parameters",
|
||||
doc="Use local h",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="UseLocalH",
|
||||
group="Mesh Parameters",
|
||||
doc="Use local H",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="GrowthRate",
|
||||
group="Mesh Parameters",
|
||||
doc="Grading for local h",
|
||||
value=0.3,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="Delaunay",
|
||||
group="Mesh Parameters",
|
||||
doc="Use Delaunay for 3d meshing",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="Delaunay2d",
|
||||
group="Mesh Parameters",
|
||||
doc="Use Delaunay for 2d meshing",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyLength",
|
||||
name="MaxSize",
|
||||
group="Mesh Parameters",
|
||||
doc="Maximal mesh size",
|
||||
value="1000 mm",
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyLength",
|
||||
name="MinSize",
|
||||
group="Mesh Parameters",
|
||||
doc="Minimal mesh size",
|
||||
value="0 mm",
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="CloseEdgeFactor",
|
||||
group="Mesh Parameters",
|
||||
doc="Factor to restrict meshing based on close edges",
|
||||
value=2.0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="StartInSurface",
|
||||
group="Mesh Parameters",
|
||||
doc="Start surface meshing from everywhere in surface",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="CheckOverlap",
|
||||
group="Mesh Parameters",
|
||||
doc="Check overlapping surfaces",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="CheckOverlappingBoundary",
|
||||
group="Mesh Parameters",
|
||||
doc="Check overlapping surface mesh before volume meshing",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="CheckChartBoundary",
|
||||
group="Mesh Parameters",
|
||||
doc="Check chart boundary",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="CurvatureSafety",
|
||||
group="Mesh Parameters",
|
||||
doc="Safety factor for curvatures (elements per radius)",
|
||||
value=2.0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="SegmentsPerEdge",
|
||||
group="Mesh Parameters",
|
||||
doc="Minimal number of segments per edge",
|
||||
value=2.0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="ElementSizeWeight",
|
||||
group="Mesh Parameters",
|
||||
doc="Weight of element size respect to element shape",
|
||||
value=0.2,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="StartStep",
|
||||
group="Mesh Parameters",
|
||||
doc="Start at step",
|
||||
value=0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="EndStep",
|
||||
group="Mesh Parameters",
|
||||
doc="EndStep",
|
||||
value=6,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="GiveUpTolerance2d",
|
||||
group="Mesh Parameters",
|
||||
doc="Give up quality class, 2d meshing",
|
||||
value=200,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="GiveUpTolerance",
|
||||
group="Mesh Parameters",
|
||||
doc="Give up quality class, 3d meshing",
|
||||
value=10,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="GiveUpToleranceOpenQuads",
|
||||
group="Mesh Parameters",
|
||||
doc="Give up quality class, for closing open quads, greather than 100 for free pyramids",
|
||||
value=15,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="MaxOuterSteps",
|
||||
group="Mesh Parameters",
|
||||
doc="Maximal outer steps",
|
||||
value=10,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="StarShapeClass",
|
||||
group="Mesh Parameters",
|
||||
doc="Class starting star-shape filling",
|
||||
value=5,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="BaseElementNp",
|
||||
group="Mesh Parameters",
|
||||
doc="If non-zero, baseelement must have BaseElementlNp points",
|
||||
value=0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="Sloppy",
|
||||
group="Mesh Parameters",
|
||||
doc="Quality tolerances are handled less careful",
|
||||
value=10,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="BadElementLimit",
|
||||
group="Mesh Parameters",
|
||||
doc="Limit for max element angle (150-180)",
|
||||
value=175,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="CheckImpossible",
|
||||
group="Mesh Parameters",
|
||||
doc="",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="Only3dDomainNr",
|
||||
group="Mesh Parameters",
|
||||
doc="",
|
||||
value=0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="SecondOrder",
|
||||
group="Mesh Parameters",
|
||||
doc="Second order element meshing",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="ElementOrder",
|
||||
group="Mesh Parameters",
|
||||
doc="High order element curvature",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="QuadDominated",
|
||||
group="Mesh Parameters",
|
||||
doc="Quad-dominated surface meshing",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="TryHexes",
|
||||
group="Mesh Parameters",
|
||||
doc="Try hexahedral elements",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="InvertTets",
|
||||
group="Mesh Parameters",
|
||||
doc="",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="InvertTrigs",
|
||||
group="Mesh Parameters",
|
||||
doc="",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="AutoZRefine",
|
||||
group="Mesh Parameters",
|
||||
doc="Automatic Z refine",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="ParallelMeshing",
|
||||
group="Mesh Parameters",
|
||||
doc="Use parallel meshing",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="Threads",
|
||||
group="Mesh Parameters",
|
||||
doc="Number of threads for parallel meshing",
|
||||
value=4,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="HealShape",
|
||||
group="Mesh Parameters",
|
||||
doc="Heal shape before meshing",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
|
||||
return prop
|
||||
164
src/Mod/Fem/femtaskpanels/task_mesh_netgen.py
Normal file
164
src/Mod/Fem/femtaskpanels/task_mesh_netgen.py
Normal file
@@ -0,0 +1,164 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2024 Mario Passaglia <mpassaglia[at]cbc.uba.ar> *
|
||||
# * *
|
||||
# * This file is part of FreeCAD. *
|
||||
# * *
|
||||
# * FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
# * under the terms of the GNU Lesser General Public License as *
|
||||
# * published by the Free Software Foundation, either version 2.1 of the *
|
||||
# * License, or (at your option) any later version. *
|
||||
# * *
|
||||
# * FreeCAD is distributed in the hope that it will be useful, but *
|
||||
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
# * Lesser General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Lesser General Public *
|
||||
# * License along with FreeCAD. If not, see *
|
||||
# * <https://www.gnu.org/licenses/>. *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD FEM mesh netgen task panel for mesh netgen object"
|
||||
__author__ = "Mario Passaglia"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package task_mesh_netgen
|
||||
# \ingroup FEM
|
||||
# \brief task panel for mesh netgen object
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
from femmesh import netgentools
|
||||
|
||||
from . import base_femmeshtaskpanel
|
||||
|
||||
|
||||
class _TaskPanel(base_femmeshtaskpanel._BaseMeshTaskPanel):
|
||||
"""
|
||||
The TaskPanel for editing References property of
|
||||
MeshNetgen objects and creation of new FEM mesh
|
||||
"""
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self.form = FreeCADGui.PySideUic.loadUi(
|
||||
FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/MeshNetgen.ui"
|
||||
)
|
||||
|
||||
self.tool = netgentools.NetgenTools(obj)
|
||||
|
||||
QtCore.QObject.connect(
|
||||
self.form.qsb_max_size,
|
||||
QtCore.SIGNAL("valueChanged(Base::Quantity)"),
|
||||
self.max_size_changed,
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self.form.qsb_min_size,
|
||||
QtCore.SIGNAL("valueChanged(Base::Quantity)"),
|
||||
self.min_size_changed,
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self.form.dsb_seg_per_edge,
|
||||
QtCore.SIGNAL("valueChanged(double)"),
|
||||
self.seg_per_edge_changed,
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self.form.dsb_curvature_safety,
|
||||
QtCore.SIGNAL("valueChanged(double)"),
|
||||
self.curvature_safety_changed,
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self.form.dsb_growth_rate,
|
||||
QtCore.SIGNAL("valueChanged(double)"),
|
||||
self.growth_rate_changed,
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self.form.ckb_second_order, QtCore.SIGNAL("toggled(bool)"), self.second_order_changed
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self.form.cb_fineness,
|
||||
QtCore.SIGNAL("currentIndexChanged(int)"),
|
||||
self.fineness_changed,
|
||||
)
|
||||
QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.update_timer_text)
|
||||
QtCore.QObject.connect(
|
||||
self.form.pb_get_netgen_version, QtCore.SIGNAL("clicked()"), self.get_version
|
||||
)
|
||||
|
||||
self.get_mesh_params()
|
||||
self.set_widgets()
|
||||
|
||||
def get_mesh_params(self):
|
||||
self.min_size = self.obj.MinSize
|
||||
self.max_size = self.obj.MaxSize
|
||||
self.fineness = self.obj.Fineness
|
||||
self.growth_rate = self.obj.GrowthRate
|
||||
self.curvature_safety = self.obj.CurvatureSafety
|
||||
self.seg_per_edge = self.obj.SegmentsPerEdge
|
||||
self.second_order = self.obj.SecondOrder
|
||||
|
||||
def set_mesh_params(self):
|
||||
self.obj.MinSize = self.min_size
|
||||
self.obj.MaxSize = self.max_size
|
||||
self.obj.Fineness = self.fineness
|
||||
self.obj.GrowthRate = self.growth_rate
|
||||
self.obj.CurvatureSafety = self.curvature_safety
|
||||
self.obj.SegmentsPerEdge = self.seg_per_edge
|
||||
self.obj.SecondOrder = self.second_order
|
||||
|
||||
def set_widgets(self):
|
||||
"fills the widgets"
|
||||
self.form.qsb_max_size.setProperty("value", self.max_size)
|
||||
FreeCADGui.ExpressionBinding(self.form.qsb_max_size).bind(self.obj, "MaxSize")
|
||||
|
||||
self.form.qsb_min_size.setProperty("value", self.min_size)
|
||||
FreeCADGui.ExpressionBinding(self.form.qsb_min_size).bind(self.obj, "MinSize")
|
||||
|
||||
self.fineness_enum = self.obj.getEnumerationsOfProperty("Fineness")
|
||||
index = self.fineness_enum.index(self.fineness)
|
||||
self.form.cb_fineness.addItems(self.fineness_enum)
|
||||
self.form.cb_fineness.setCurrentIndex(index)
|
||||
self.form.dsb_growth_rate.setValue(self.growth_rate)
|
||||
self.form.dsb_curvature_safety.setValue(self.curvature_safety)
|
||||
self.form.dsb_seg_per_edge.setValue(self.seg_per_edge)
|
||||
|
||||
self.form.ckb_second_order.setChecked(self.second_order)
|
||||
|
||||
def max_size_changed(self, base_quantity_value):
|
||||
self.max_size = base_quantity_value
|
||||
|
||||
def min_size_changed(self, base_quantity_value):
|
||||
self.min_size = base_quantity_value
|
||||
|
||||
def seg_per_edge_changed(self, value):
|
||||
self.seg_per_edge = value
|
||||
|
||||
def curvature_safety_changed(self, value):
|
||||
self.curvature_safety = value
|
||||
|
||||
def growth_rate_changed(self, value):
|
||||
self.growth_rate = value
|
||||
|
||||
def fineness_changed(self, index):
|
||||
self.fineness = self.fineness_enum[index]
|
||||
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)
|
||||
else:
|
||||
self.form.qsb_min_size.setEnabled(False)
|
||||
self.form.qsb_max_size.setEnabled(False)
|
||||
self.form.dsb_seg_per_edge.setEnabled(False)
|
||||
self.form.dsb_growth_rate.setEnabled(False)
|
||||
self.form.dsb_curvature_safety.setEnabled(False)
|
||||
|
||||
def second_order_changed(self, bool_value):
|
||||
self.second_order = bool_value
|
||||
158
src/Mod/Fem/femviewprovider/view_mesh_netgen.py
Normal file
158
src/Mod/Fem/femviewprovider/view_mesh_netgen.py
Normal file
@@ -0,0 +1,158 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2024 Mario Passaglia <mpassaglia[at]cbc.uba.ar> *
|
||||
# * *
|
||||
# * This file is part of FreeCAD. *
|
||||
# * *
|
||||
# * FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
# * under the terms of the GNU Lesser General Public License as *
|
||||
# * published by the Free Software Foundation, either version 2.1 of the *
|
||||
# * License, or (at your option) any later version. *
|
||||
# * *
|
||||
# * FreeCAD is distributed in the hope that it will be useful, but *
|
||||
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
# * Lesser General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Lesser General Public *
|
||||
# * License along with FreeCAD. If not, see *
|
||||
# * <https://www.gnu.org/licenses/>. *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD FEM mesh netgen ViewProvider for the document object"
|
||||
__author__ = "Mario Passaglia"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package view_mesh_netgen
|
||||
# \ingroup FEM
|
||||
# \brief view provider for mesh netgen object
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
import FemGui
|
||||
from PySide import QtGui
|
||||
from femtaskpanels import task_mesh_netgen
|
||||
from femtools.femutils import is_of_type
|
||||
from femviewprovider import view_base_femobject
|
||||
|
||||
|
||||
class VPMeshNetgen(view_base_femobject.VPBaseFemObject):
|
||||
"""
|
||||
A View Provider for the MeshNetgen object
|
||||
"""
|
||||
|
||||
def __init__(self, vobj):
|
||||
vobj.Proxy = self
|
||||
|
||||
def getIcon(self):
|
||||
return ":/icons/FEM_MeshNetgenFromShape.svg"
|
||||
|
||||
def setEdit(self, vobj, mode):
|
||||
# hide all FEM meshes and VTK FemPost* objects
|
||||
for obj in vobj.Object.Document.Objects:
|
||||
if obj.isDerivedFrom("Fem::FemMeshObject") or obj.isDerivedFrom("Fem::FemPostObject"):
|
||||
obj.ViewObject.hide()
|
||||
# show the mesh we like to edit
|
||||
self.ViewObject.show()
|
||||
# show task panel
|
||||
taskd = task_mesh_netgen._TaskPanel(self.Object)
|
||||
FreeCADGui.Control.showDialog(taskd)
|
||||
return True
|
||||
|
||||
def doubleClicked(self, vobj):
|
||||
# Group meshing is only active on active analysis
|
||||
# we should make sure the analysis the mesh belongs too is active
|
||||
gui_doc = FreeCADGui.getDocument(vobj.Object.Document)
|
||||
if not gui_doc.getInEdit():
|
||||
# may be go the other way around and just activate the
|
||||
# analysis the user has doubleClicked on ?!
|
||||
# not a fast one, we need to iterate over all member of all
|
||||
# analysis to know to which analysis the object belongs too!!!
|
||||
# first check if there is an analysis in the active document
|
||||
found_an_analysis = False
|
||||
for o in gui_doc.Document.Objects:
|
||||
if o.isDerivedFrom("Fem::FemAnalysisPython"):
|
||||
found_an_analysis = True
|
||||
break
|
||||
if found_an_analysis:
|
||||
if FemGui.getActiveAnalysis() is not None:
|
||||
if FemGui.getActiveAnalysis().Document is FreeCAD.ActiveDocument:
|
||||
if self.Object in FemGui.getActiveAnalysis().Group:
|
||||
if not gui_doc.getInEdit():
|
||||
gui_doc.setEdit(vobj.Object.Name)
|
||||
else:
|
||||
FreeCAD.Console.PrintError(
|
||||
"Activate the analysis this Netgen FEM "
|
||||
"mesh object belongs too!\n"
|
||||
)
|
||||
else:
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"Netgen FEM mesh object does not belong to the active analysis.\n"
|
||||
)
|
||||
found_mesh_analysis = False
|
||||
for o in gui_doc.Document.Objects:
|
||||
if o.isDerivedFrom("Fem::FemAnalysisPython"):
|
||||
for m in o.Group:
|
||||
if m == self.Object:
|
||||
found_mesh_analysis = True
|
||||
FemGui.setActiveAnalysis(o)
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"The analysis the Netgen FEM mesh object "
|
||||
"belongs to was found and activated: {}\n".format(
|
||||
o.Name
|
||||
)
|
||||
)
|
||||
gui_doc.setEdit(vobj.Object.Name)
|
||||
break
|
||||
if not found_mesh_analysis:
|
||||
FreeCAD.Console.PrintLog(
|
||||
"Netgen FEM mesh object does not belong to an analysis. "
|
||||
"Analysis group meshing can not be used.\n"
|
||||
)
|
||||
gui_doc.setEdit(vobj.Object.Name)
|
||||
else:
|
||||
FreeCAD.Console.PrintError("Active analysis is not in active document.\n")
|
||||
else:
|
||||
FreeCAD.Console.PrintLog(
|
||||
"No active analysis in active document, "
|
||||
"we are going to have a look if the Netgen FEM mesh object "
|
||||
"belongs to a non active analysis.\n"
|
||||
)
|
||||
found_mesh_analysis = False
|
||||
for o in gui_doc.Document.Objects:
|
||||
if o.isDerivedFrom("Fem::FemAnalysisPython"):
|
||||
for m in o.Group:
|
||||
if m == self.Object:
|
||||
found_mesh_analysis = True
|
||||
FemGui.setActiveAnalysis(o)
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"The analysis the Netgen FEM mesh object "
|
||||
"belongs to was found and activated: {}\n".format(o.Name)
|
||||
)
|
||||
gui_doc.setEdit(vobj.Object.Name)
|
||||
break
|
||||
if not found_mesh_analysis:
|
||||
FreeCAD.Console.PrintLog(
|
||||
"Netgen FEM mesh object does not belong to an analysis. "
|
||||
"Analysis group meshing can not be used.\n"
|
||||
)
|
||||
gui_doc.setEdit(vobj.Object.Name)
|
||||
else:
|
||||
FreeCAD.Console.PrintLog("No analysis in the active document.\n")
|
||||
gui_doc.setEdit(vobj.Object.Name)
|
||||
else:
|
||||
from PySide.QtGui import QMessageBox
|
||||
|
||||
message = "Active Task Dialog found! Please close this one before opening a new one!"
|
||||
QMessageBox.critical(None, "Error in tree view", message)
|
||||
FreeCAD.Console.PrintError(message + "\n")
|
||||
return True
|
||||
|
||||
def dumps(self):
|
||||
return None
|
||||
|
||||
def loads(self, state):
|
||||
return None
|
||||
Reference in New Issue
Block a user