Skip to content

Commit

Permalink
Merge pull request #353 from berndhahnebach/bhbdev058
Browse files Browse the repository at this point in the history
FEM: GMSH tool
  • Loading branch information
wwmayer committed Nov 28, 2016
2 parents b503d55 + 9154825 commit 1f6ed81
Show file tree
Hide file tree
Showing 22 changed files with 225 additions and 62 deletions.
119 changes: 105 additions & 14 deletions src/Mod/Fem/FemGmshTools.py
Expand Up @@ -48,31 +48,73 @@ def __init__(self, gmsh_mesh_obj, analysis=None):
# part to mesh
self.part_obj = self.mesh_obj.Part

# clmax, ElementSizeMax: float, 0.0 = 1e+22
self.clmax = Units.Quantity(self.mesh_obj.ElementSizeMax).Value
# clmax, CharacteristicLengthMax: float, 0.0 = 1e+22
self.clmax = Units.Quantity(self.mesh_obj.CharacteristicLengthMax).Value
if self.clmax == 0.0:
self.clmax = 1e+22

# clmin, ElementSizeMin: float
self.clmin = Units.Quantity(self.mesh_obj.ElementSizeMin).Value
# clmin, CharacteristicLengthMin: float
self.clmin = Units.Quantity(self.mesh_obj.CharacteristicLengthMin).Value

# order, ElementOrder: ['Auto', '1st', '2nd']
# order
# known_element_orders = ['Automatic', '1st', '2nd']
self.order = self.mesh_obj.ElementOrder
if self.order == '1st':
self.order = '1'
elif self.order == 'Auto' or self.order == '2nd':
elif self.order == 'Automatic' or self.order == '2nd':
self.order = '2'
else:
print('Error in order')

# dimension, ElementDimension: ['Auto', '1D', '2D', '3D']
# dimension
# known_element_dimensions = ['Automatic', '1D', '2D', '3D']
self.dimension = self.mesh_obj.ElementDimension

# Algorithm2D
# known_mesh_algorithm_2D = ['Automatic', 'MeshAdapt', 'Delaunay', 'Frontal', 'BAMG', 'DelQuad']
algo2D = self.mesh_obj.Algorithm2D
if algo2D == 'Automatic':
self.algorithm2D = '2'
elif algo2D == 'MeshAdapt':
self.algorithm2D = '1'
elif algo2D == 'Delaunay':
self.algorithm2D = '5'
elif algo2D == 'Frontal':
self.algorithm2D = '6'
elif algo2D == 'BAMG':
self.algorithm2D = '7'
elif algo2D == 'DelQuad':
self.algorithm2D = '8'
else:
self.algorithm2D = '2'

# Algorithm3D
# known_mesh_algorithm_3D = ['Automatic', 'Delaunay', 'New Delaunay', 'Frontal', 'Frontal Delaunay', 'Frontal Hex', 'MMG3D', 'R-tree']
algo3D = self.mesh_obj.Algorithm2D
if algo3D == 'Automatic':
self.algorithm3D = '1'
elif algo3D == 'Delaunay':
self.algorithm3D = '1'
elif algo3D == 'New Delaunay':
self.algorithm3D = '2'
elif algo3D == 'Frontal':
self.algorithm3D = '4'
elif algo3D == 'Frontal Delaunay':
self.algorithm3D = '5'
elif algo3D == 'Frontal Hex':
self.algorithm3D = '6'
elif algo3D == 'MMG3D':
self.algorithm3D = '7'
elif algo3D == 'R-tree':
self.algorithm3D = '9'
else:
self.algorithm3D = '1'

def create_mesh(self):
print("\nWe gone start GMSH FEM mesh run!")
print(' Part to mesh: Name --> ' + self.part_obj.Name + ', Label --> ' + self.part_obj.Label + ', ShapeType --> ' + self.part_obj.Shape.ShapeType)
print(' ElementSizeMax: ' + str(self.clmax))
print(' ElementSizeMin: ' + str(self.clmin))
print(' CharacteristicLengthMax: ' + str(self.clmax))
print(' CharacteristicLengthMin: ' + str(self.clmin))
print(' ElementOrder: ' + self.order)
self.get_dimension()
self.get_tmp_file_paths()
Expand All @@ -88,7 +130,7 @@ def get_dimension(self):
# Dimension
# GMSH uses the hightest availabe.
# A use case for not auto would be a surface (2D) mesh of a solid or other 3d shape
if self.dimension == 'Auto':
if self.dimension == 'Automatic':
shty = self.part_obj.Shape.ShapeType
if shty == 'Solid' or shty == 'CompSolid':
# print('Found: ' + shty)
Expand Down Expand Up @@ -181,6 +223,23 @@ def get_group_data(self):
print(self.group_elements)
else:
print(' NO group meshing.')
self.ele_length_map = self.mesh_obj.CharacteristicLengthMap
self.ele_node_map = {}
if self.ele_length_map:
import FemMeshTools
print(self.ele_length_map)
self.ele_node_map = {}
for e in self.ele_length_map:
if not e.startswith('Solid'):
# Face, Edge, Vertex
ele_shape = self.part_obj.Shape.getElement(e)
else:
# Solid
ele_shape_index = int(e.lstrip('Solid')) - 1
ele_shape = self.part_obj.Shape.Solids[ele_shape_index]
ele_vertexes = FemMeshTools.get_vertexes_by_element(self.part_obj.Shape, ele_shape)
self.ele_node_map[e] = ele_vertexes
print(self.ele_node_map)

def write_part_file(self):
self.part_obj.Shape.exportBrep(self.temp_file_geometry)
Expand All @@ -190,7 +249,7 @@ def write_geo(self):
geo.write('Merge "' + self.temp_file_geometry + '";\n')
geo.write("\n")
if self.analysis and self.group_elements:
print(' We gone have found elements to make mesh groups for!')
print(' We gone have found elements to make mesh groups for.')
geo.write("// group data\n")
# we use the element name of FreeCAD which starts with 1 (example: 'Face1'), same as GMSH
for group in self.group_elements:
Expand All @@ -214,12 +273,44 @@ def write_geo(self):
# print(ele_nr)
geo.write('Physical ' + physical_type + '("' + group + '") = {' + ele_nr + '};\n')
geo.write("\n")
if self.ele_length_map:
# we use the index FreeCAD which starts with 0, we need to add 1 for the index in GMSH
geo.write("// Characteristic Length according CharacteristicLengthMap\n")
for e in self.ele_length_map:
ele_nodes = (''.join((str(n + 1) + ', ') for n in self.ele_node_map[e])).rstrip(', ')
geo.write("// " + e + "\n")
geo.write("Characteristic Length { " + ele_nodes + " } = " + self.ele_length_map[e] + ";\n")
geo.write("\n")
geo.write("Mesh.CharacteristicLengthMax = " + str(self.clmax) + ";\n")
geo.write("Mesh.CharacteristicLengthMin = " + str(self.clmin) + ";\n")
geo.write("Mesh.ElementOrder = " + self.order + ";\n")
geo.write("//Mesh.HighOrderOptimize = 1;\n") # but does not really work, in GUI it does
geo.write("Mesh.Algorithm3D = 1;\n")
geo.write("Mesh.Algorithm = 2;\n")
geo.write("\n")
geo.write("//optimize the mesh\n")
# GMSH tetra optimizer
if hasattr(self.mesh_obj, 'OptimizeStd') and self.mesh_obj.OptimizeStd is True:
geo.write("Mesh.Optimize = 1;\n")
else:
geo.write("Mesh.Optimize = 0;\n")
# Netgen optimizer in GMSH
if hasattr(self.mesh_obj, 'OptimizeNetgen') and self.mesh_obj.OptimizeNetgen is True:
geo.write("Mesh.OptimizeNetgen = 1;\n")
else:
geo.write("Mesh.OptimizeNetgen = 0;\n")
# hight order mesh optimizing
if hasattr(self.mesh_obj, 'OptimizeNetgen') and self.mesh_obj.OptimizeNetgen is True:
geo.write("Mesh.HighOrderOptimize = 1; //probably needs more lines off adjustment in geo file\n")
else:
geo.write("Mesh.HighOrderOptimize = 0; //probably needs more lines off adjustment in geo file\n")
geo.write("\n")
if hasattr(self.mesh_obj, 'RecombineAll') and self.mesh_obj.RecombineAll is True:
geo.write("//recombine\n")
geo.write("Mesh.RecombineAll = 1;\n")
geo.write("\n")
geo.write("//mesh algorithm\n")
geo.write("Mesh.Algorithm = " + self.algorithm2D + ";\n")
geo.write("Mesh.Algorithm3D = " + self.algorithm3D + ";\n")
geo.write("\n")
geo.write("//more\n")
geo.write("Mesh " + self.dimension + ";\n")
geo.write("Mesh.Format = 2;\n") # unv
if self.analysis and self.group_elements:
Expand Down
43 changes: 43 additions & 0 deletions src/Mod/Fem/FemMeshTools.py
Expand Up @@ -1162,6 +1162,49 @@ def find_element_in_shape(aShape, anElement):
FreeCAD.Console.PrintError('Compound is not supported.\n')


def get_vertexes_by_element(aShape, anElement):
# we gone extent the method find_element_in_shape and return the vertexes
# import Part
ele_vertexes = []
ele_st = anElement.ShapeType
if ele_st == 'Solid' or ele_st == 'CompSolid':
for index, solid in enumerate(aShape.Solids):
if is_same_geometry(solid, anElement):
for vele in aShape.Solids[index].Vertexes:
for i, v in enumerate(aShape.Vertexes):
if vele.isSame(v): # use isSame, because orientation could be different
ele_vertexes.append(i)
# print(' ' + str(sorted(ele_vertexes)))
return ele_vertexes
FreeCAD.Console.PrintError('Error, Solid ' + str(anElement) + ' not found in: ' + str(aShape) + '\n')
elif ele_st == 'Face' or ele_st == 'Shell':
for index, face in enumerate(aShape.Faces):
if is_same_geometry(face, anElement):
for vele in aShape.Faces[index].Vertexes:
for i, v in enumerate(aShape.Vertexes):
if vele.isSame(v): # use isSame, because orientation could be different
ele_vertexes.append(i)
# print(' ' + str(sorted(ele_vertexes)))
return ele_vertexes
elif ele_st == 'Edge' or ele_st == 'Wire':
for index, edge in enumerate(aShape.Edges):
if is_same_geometry(edge, anElement):
for vele in aShape.Edges[index].Vertexes:
for i, v in enumerate(aShape.Vertexes):
if vele.isSame(v): # use isSame, because orientation could be different
ele_vertexes.append(i)
# print(' ' + str(sorted(ele_vertexes)))
return ele_vertexes
elif ele_st == 'Vertex':
for index, vertex in enumerate(aShape.Vertexes):
if is_same_geometry(vertex, anElement):
ele_vertexes.append(index)
# print(' ' + str(sorted(ele_vertexes)))
return ele_vertexes
elif ele_st == 'Compound':
FreeCAD.Console.PrintError('Compound is not supported.\n')


def is_same_geometry(shape1, shape2):
# the vertexes and the CenterOfMass are compared
# it is a hack, but I do not know any better !
Expand Down
4 changes: 2 additions & 2 deletions src/Mod/Fem/Gui/Command.cpp
Expand Up @@ -975,7 +975,7 @@ CmdFemDefineNodesSet::CmdFemDefineNodesSet()
sToolTipText = QT_TR_NOOP("Create node set by Poly");
sWhatsThis = "Create node set by Poly";
sStatusTip = QT_TR_NOOP("Create node set by Poly");
sPixmap = "fem-fem-mesh-create-node-by-poly";
sPixmap = "fem-femmesh-create-node-by-poly";
}

void CmdFemDefineNodesSet::activated(int)
Expand Down Expand Up @@ -1032,7 +1032,7 @@ CmdFemCreateNodesSet::CmdFemCreateNodesSet()
sToolTipText = QT_TR_NOOP("Creates a FEM mesh nodes set");
sWhatsThis = "Fem_CreateNodesSet";
sStatusTip = sToolTipText;
sPixmap = "fem-fem-mesh-create-node-by-poly";
sPixmap = "fem-femmesh-create-node-by-poly";
}

void CmdFemCreateNodesSet::activated(int)
Expand Down
8 changes: 4 additions & 4 deletions src/Mod/Fem/Gui/Resources/Fem.qrc
Expand Up @@ -25,10 +25,10 @@
<file>icons/fem-control-solver.svg</file>
<file>icons/fem-cylinder.svg</file>
<file>icons/fem-data.png</file>
<file>icons/fem-fem-mesh-create-node-by-poly.svg</file>
<file>icons/fem-fem-mesh-from-shape.svg</file>
<file>icons/fem-fem-mesh-gmsh-from-shape.svg</file>
<file>icons/fem-fem-mesh-netgen-from-shape.svg</file>
<file>icons/fem-femmesh-create-node-by-poly.svg</file>
<file>icons/fem-femmesh-from-shape.svg</file>
<file>icons/fem-femmesh-gmsh-from-shape.svg</file>
<file>icons/fem-femmesh-netgen-from-shape.svg</file>
<file>icons/fem-femmesh-to-mesh.svg</file>
<file>icons/fem-frequency-analysis.svg</file>
<file>icons/fem-inp-editor.svg</file>
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/Mod/Fem/Gui/TaskAnalysisInfo.cpp
Expand Up @@ -41,7 +41,7 @@ using namespace Gui;


TaskAnalysisInfo::TaskAnalysisInfo(Fem::FemAnalysis *pcObject,QWidget *parent)
: TaskBox(Gui::BitmapFactory().pixmap("fem-fem-mesh-create-node-by-poly"),
: TaskBox(Gui::BitmapFactory().pixmap("fem-femmesh-create-node-by-poly"),
tr("Nodes set"),
true,
parent),
Expand Down
2 changes: 1 addition & 1 deletion src/Mod/Fem/Gui/TaskCreateNodeSet.cpp
Expand Up @@ -59,7 +59,7 @@ using namespace Gui;


TaskCreateNodeSet::TaskCreateNodeSet(Fem::FemSetNodesObject *pcObject,QWidget *parent)
: TaskBox(Gui::BitmapFactory().pixmap("fem-fem-mesh-create-node-by-poly"),
: TaskBox(Gui::BitmapFactory().pixmap("fem-femmesh-create-node-by-poly"),
tr("Nodes set"),
true,
parent),
Expand Down
2 changes: 1 addition & 1 deletion src/Mod/Fem/Gui/TaskDriver.cpp
Expand Up @@ -47,7 +47,7 @@ using namespace Gui;


TaskDriver::TaskDriver(Fem::FemAnalysis *pcObject,QWidget *parent)
: TaskBox(Gui::BitmapFactory().pixmap("fem-fem-mesh-create-node-by-poly"),
: TaskBox(Gui::BitmapFactory().pixmap("fem-femmesh-create-node-by-poly"),
tr("Nodes set"),
true,
parent),
Expand Down
2 changes: 1 addition & 1 deletion src/Mod/Fem/Gui/TaskObjectName.cpp
Expand Up @@ -44,7 +44,7 @@ using namespace FemGui;
using namespace Gui;

TaskObjectName::TaskObjectName(App::DocumentObject *pcObject,QWidget *parent)
: TaskBox(Gui::BitmapFactory().pixmap("fem-fem-mesh-create-node-by-poly"),
: TaskBox(Gui::BitmapFactory().pixmap("fem-femmesh-create-node-by-poly"),
tr("TaskObjectName"),
true,
parent),
Expand Down
12 changes: 6 additions & 6 deletions src/Mod/Fem/Gui/TaskPostBoxes.cpp
Expand Up @@ -171,7 +171,7 @@ void TaskPostBox::updateEnumerationList(App::PropertyEnumeration& prop, QComboBo
//###########################################################################################################

TaskPostDisplay::TaskPostDisplay(Gui::ViewProviderDocumentObject* view, QWidget *parent)
: TaskPostBox(view, Gui::BitmapFactory().pixmap("fem-fem-mesh-create-node-by-poly"), tr("Display options"), parent)
: TaskPostBox(view, Gui::BitmapFactory().pixmap("fem-femmesh-create-node-by-poly"), tr("Display options"), parent)
{
//we need a separate container widget to add all controls to
proxy = new QWidget(this);
Expand Down Expand Up @@ -221,7 +221,7 @@ void TaskPostDisplay::applyPythonCode() {

//############################################################################################

TaskPostFunction::TaskPostFunction(ViewProviderDocumentObject* view, QWidget* parent): TaskPostBox(view, Gui::BitmapFactory().pixmap("fem-fem-mesh-create-node-by-poly"), tr("Implicit function"), parent) {
TaskPostFunction::TaskPostFunction(ViewProviderDocumentObject* view, QWidget* parent): TaskPostBox(view, Gui::BitmapFactory().pixmap("fem-femmesh-create-node-by-poly"), tr("Implicit function"), parent) {

assert(view->isDerivedFrom(ViewProviderFemPostFunction::getClassTypeId()));

Expand All @@ -244,7 +244,7 @@ void TaskPostFunction::applyPythonCode() {
//############################################################################################

TaskPostClip::TaskPostClip(ViewProviderDocumentObject* view, App::PropertyLink* function, QWidget* parent)
: TaskPostBox(view,Gui::BitmapFactory().pixmap("fem-fem-mesh-create-node-by-poly"), tr("Choose implicit function"), parent) {
: TaskPostBox(view,Gui::BitmapFactory().pixmap("fem-femmesh-create-node-by-poly"), tr("Choose implicit function"), parent) {

assert(view->isDerivedFrom(ViewProviderFemPostClip::getClassTypeId()));
assert(function);
Expand Down Expand Up @@ -363,7 +363,7 @@ void TaskPostClip::on_InsideOut_toggled(bool val) {
//############################################################################################

TaskPostScalarClip::TaskPostScalarClip(ViewProviderDocumentObject* view, QWidget* parent) :
TaskPostBox(view, Gui::BitmapFactory().pixmap("fem-fem-mesh-create-node-by-poly"), tr("Clip options"), parent) {
TaskPostBox(view, Gui::BitmapFactory().pixmap("fem-femmesh-create-node-by-poly"), tr("Clip options"), parent) {

assert(view->isDerivedFrom(ViewProviderFemPostScalarClip::getClassTypeId()));

Expand Down Expand Up @@ -455,7 +455,7 @@ void TaskPostScalarClip::on_InsideOut_toggled(bool val) {
//############################################################################################

TaskPostWarpVector::TaskPostWarpVector(ViewProviderDocumentObject* view, QWidget* parent) :
TaskPostBox(view, Gui::BitmapFactory().pixmap("fem-fem-mesh-create-node-by-poly"), tr("Clip options"), parent) {
TaskPostBox(view, Gui::BitmapFactory().pixmap("fem-femmesh-create-node-by-poly"), tr("Clip options"), parent) {

assert(view->isDerivedFrom(ViewProviderFemPostWarpVector::getClassTypeId()));

Expand Down Expand Up @@ -540,7 +540,7 @@ void TaskPostWarpVector::on_Min_valueChanged(double v) {
//############################################################################################

TaskPostCut::TaskPostCut(ViewProviderDocumentObject* view, App::PropertyLink* function, QWidget* parent)
: TaskPostBox(view,Gui::BitmapFactory().pixmap("fem-fem-mesh-create-node-by-poly"), tr("Choose implicit function"), parent) {
: TaskPostBox(view,Gui::BitmapFactory().pixmap("fem-femmesh-create-node-by-poly"), tr("Choose implicit function"), parent) {

assert(view->isDerivedFrom(ViewProviderFemPostCut::getClassTypeId()));
assert(function);
Expand Down
2 changes: 1 addition & 1 deletion src/Mod/Fem/Gui/TaskTetParameter.cpp
Expand Up @@ -48,7 +48,7 @@ using namespace Gui;


TaskTetParameter::TaskTetParameter(Fem::FemMeshShapeNetgenObject *pcObject,QWidget *parent)
: TaskBox(Gui::BitmapFactory().pixmap("fem-fem-mesh-create-node-by-poly"),
: TaskBox(Gui::BitmapFactory().pixmap("fem-femmesh-create-node-by-poly"),
tr("Tet Parameter"),
true,
parent),
Expand Down
2 changes: 1 addition & 1 deletion src/Mod/Fem/Gui/ViewProviderFemMesh.cpp
Expand Up @@ -183,7 +183,7 @@ App::PropertyFloatConstraint::Constraints ViewProviderFemMesh::floatRange = {1.0

ViewProviderFemMesh::ViewProviderFemMesh()
{
sPixmap = "fem-fem-mesh-from-shape";
sPixmap = "fem-femmesh-from-shape";

ADD_PROPERTY(PointColor,(App::Color(0.7f,0.7f,0.7f)));
ADD_PROPERTY(PointSize,(5.0f));
Expand Down

0 comments on commit 1f6ed81

Please sign in to comment.