FEM: added object FemShellThickness and FemBeamSection to enable shell and beam analysises

This commit is contained in:
Bernd Hahnebach 2015-09-17 06:59:31 +02:00 committed by Yorik van Havre
parent de1bdefc7c
commit 705c339f21
15 changed files with 886 additions and 27 deletions

View File

@ -72,6 +72,10 @@ SET(FemScripts_SRCS
ccxFrdReader.py
ccxInpWriter.py
TestFem.py
FemShellThickness.py
FemShellThickness.ui
FemBeamSection.py
FemBeamSection.ui
FemTools.py
MechanicalAnalysis.ui
MechanicalAnalysis.py

View File

@ -14,7 +14,11 @@ INSTALL(
ccxInpWriter.py
FemTools.py
TestFem.py
FemBeamSection.py
FemBeamSection.ui
FemExample.py
FemShellThickness.py
FemShellThickness.ui
MechanicalAnalysis.py
MechanicalMaterial.py
MechanicalMaterial.ui

View File

@ -0,0 +1,227 @@
#***************************************************************************
#* *
#* Copyright (c) 2015 - Bernd Hahnebach <bernd@bimstatik.org> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* This program 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 Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
import FreeCAD
if FreeCAD.GuiUp:
import FreeCADGui
import FemGui
from PySide import QtGui
from PySide import QtCore
from pivy import coin
__title__ = "FemBeamSection"
__author__ = "Bernd Hahnebach"
__url__ = "http://www.freecadweb.org"
def makeFemBeamSection(width=20.0, height=20.0, name="BeamSection"):
'''makeFemBeamSection([width], [height], [name]): creates an beamsection object to define a cross section'''
obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", name)
_FemBeamSection(obj)
obj.Width = width
obj.Height = height
if FreeCAD.GuiUp:
_ViewProviderFemBeamSection(obj.ViewObject)
return obj
class _CommandFemBeamSection:
"The Fem_BeamSection command definition"
def GetResources(self):
return {'Pixmap': 'fem-beam-section',
'MenuText': QtCore.QT_TRANSLATE_NOOP("Fem_BeamSection", "FEM Beam Cross Section Definition ..."),
'Accel': "C, B",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Fem_BeamSection", "Creates a FEM Beam Cross Section")}
def Activated(self):
FreeCAD.ActiveDocument.openTransaction("Create FemBeamSection")
FreeCADGui.addModule("FemBeamSection")
FreeCADGui.doCommand("FemBeamSection.makeFemBeamSection()")
FreeCADGui.doCommand("App.activeDocument()." + FemGui.getActiveAnalysis().Name + ".Member = App.activeDocument()." + FemGui.getActiveAnalysis().Name + ".Member + [App.ActiveDocument.ActiveObject]")
def IsActive(self):
True if FemGui.getActiveAnalysis() else False
class _FemBeamSection:
"The FemBeamSection object"
def __init__(self, obj):
obj.addProperty("App::PropertyLength", "Width", "BeamSection", "set width of the beam elements")
obj.addProperty("App::PropertyLength", "Height", "BeamSection", "set height of the beam elements")
obj.addProperty("App::PropertyLinkSubList", "References", "BeamSection", "List of beam section shapes")
obj.Proxy = self
self.Type = "FemBeamSection"
def execute(self, obj):
return
class _ViewProviderFemBeamSection:
"A View Provider for the FemBeamSection object"
def __init__(self, vobj):
vobj.Proxy = self
def getIcon(self):
return ":/icons/fem-beam-section.svg"
def attach(self, vobj):
self.ViewObject = vobj
self.Object = vobj.Object
self.standard = coin.SoGroup()
vobj.addDisplayMode(self.standard, "Standard")
def getDisplayModes(self, obj):
return ["Standard"]
def getDefaultDisplayMode(self):
return "Standard"
def updateData(self, obj, prop):
return
def onChanged(self, vobj, prop):
return
def setEdit(self, vobj, mode=0):
taskd = _FemBeamSectionTaskPanel(self.Object)
taskd.obj = vobj.Object
#taskd.update() When is this needed ?
FreeCADGui.Control.showDialog(taskd)
return True
def unsetEdit(self, vobj, mode=0):
FreeCADGui.Control.closeDialog()
return
def doubleClicked(self, vobj):
self.setEdit(vobj)
def __getstate__(self):
return None
def __setstate__(self, state):
return None
class _FemBeamSectionTaskPanel:
'''The TaskPanel for editing References property of FemBeamSection objects'''
def __init__(self, obj):
FreeCADGui.Selection.clearSelection()
self.sel_server = None
self.obj = obj
self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Fem/FemBeamSection.ui")
QtCore.QObject.connect(self.form.pushButton_Reference, QtCore.SIGNAL("clicked()"), self.add_references)
self.form.list_References.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.form.list_References.connect(self.form.list_References, QtCore.SIGNAL("customContextMenuRequested(QPoint)"), self.references_list_right_clicked)
self.previous_references = self.obj.References
self.references = self.obj.References
self.rebuild_list_References()
def accept(self):
if self.sel_server:
FreeCADGui.Selection.removeObserver(self.sel_server)
self.obj.References = self.references
FreeCADGui.ActiveDocument.resetEdit()
FreeCAD.ActiveDocument.recompute()
return True
def reject(self):
if self.sel_server:
FreeCADGui.Selection.removeObserver(self.sel_server)
FreeCADGui.ActiveDocument.resetEdit()
return True
def references_list_right_clicked(self, QPos):
self.form.contextMenu = QtGui.QMenu()
menu_item = self.form.contextMenu.addAction("Remove Reference")
if not self.references:
menu_item.setDisabled(True)
self.form.connect(menu_item, QtCore.SIGNAL("triggered()"), self.remove_reference)
parentPosition = self.form.list_References.mapToGlobal(QtCore.QPoint(0, 0))
self.form.contextMenu.move(parentPosition + QPos)
self.form.contextMenu.show()
def remove_reference(self):
if not self.references:
return
currentItemName = str(self.form.list_References.currentItem().text())
for ref in self.references:
refname_to_compare_listentry = ref[0].Name + '-->' + ref[1]
if refname_to_compare_listentry == currentItemName:
self.references.remove(ref)
self.rebuild_list_References()
def add_references(self):
'''Called if Button add_reference is triggered'''
# in constraints EditTaskPanel the selection is active as soon as the taskpanel is open
# here the addReference button EditTaskPanel has to be triggered to start selection mode
FreeCADGui.Selection.clearSelection()
# start SelectionObserver and parse the function to add the References to the widget
self.sel_server = ReferenceShapeSelectionObserver(self.selectionParser)
def selectionParser(self, selsub):
sel = selsub[0]
sub = selsub[1]
# print 'selection: ', sel.Shape.ShapeType, ' ', sel.Name, ' ', sub
if hasattr(sel, "Shape"):
elt = sel.Shape.getElement(sub)
if elt.ShapeType == 'Edge':
if selsub not in self.references:
self.references.append(selsub)
self.rebuild_list_References()
else:
print sel.Name, '-->', sub, ' is already in reference list!'
else:
print 'Selection has no shape!'
def rebuild_list_References(self):
self.form.list_References.clear()
items = []
for i in self.references:
item_name = i[0].Name + '-->' + i[1]
items.append(item_name)
for listItemName in sorted(items):
listItem = QtGui.QListWidgetItem(listItemName, self.form.list_References) # listItem = is needed
class ReferenceShapeSelectionObserver:
'''ReferenceShapeSelectionObserver
started on click button addReference'''
def __init__(self, parseSelectionFunction):
self.parseSelectionFunction = parseSelectionFunction
FreeCADGui.Selection.addObserver(self)
FreeCAD.Console.PrintMessage("Select Faces to add them to the list!\n")
def addSelection(self, docName, objName, sub, pos):
selected_object = FreeCAD.getDocument(docName).getObject(objName) # get the obj objName
self.added_obj = (selected_object, sub)
if sub: # on doubleClick the solid is selected and sub will be empty
self.parseSelectionFunction(self.added_obj)
if FreeCAD.GuiUp:
FreeCADGui.addCommand('Fem_BeamSection', _CommandFemBeamSection())

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>249</width>
<height>379</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>1677215</height>
</size>
</property>
<property name="title">
<string>Cross Section</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QLabel" name="l_label_text_3">
<property name="text">
<string>Use FreeCAD Property Editor</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="l_label_text_4">
<property name="text">
<string>to edit the cross section values</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_1">
<property name="title">
<string>Reference</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="l_label_text_1">
<property name="text">
<string>Leave reference blank </string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="l_label_text_2">
<property name="text">
<string>to choose all remaining shapes</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_Reference">
<property name="text">
<string>Add reference</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="list_References"/>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,225 @@
#***************************************************************************
#* *
#* Copyright (c) 2015 - Bernd Hahnebach <bernd@bimstatik.org> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* This program 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 Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
import FreeCAD
if FreeCAD.GuiUp:
import FreeCADGui
import FemGui
from PySide import QtGui
from PySide import QtCore
from pivy import coin
__title__ = "FemShellThickness"
__author__ = "Bernd Hahnebach"
__url__ = "http://www.freecadweb.org"
def makeFemShellThickness(thickness=20.0, name="ShellThickness"):
'''makeFemShellThickness([thickness], [name]): creates an shellthickness object to define a plate thickness'''
obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", name)
_FemShellThickness(obj)
obj.Thickness = thickness
if FreeCAD.GuiUp:
_ViewProviderFemShellThickness(obj.ViewObject)
return obj
class _CommandFemShellThickness:
"The Fem_ShellThickness command definition"
def GetResources(self):
return {'Pixmap': 'fem-shell-thickness',
'MenuText': QtCore.QT_TRANSLATE_NOOP("Fem_ShellThickness", "FEM Shell Plate Thickness Definition ..."),
'Accel': "C, S",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Fem_ShellThickness", "Creates a FEM Shell Thickness")}
def Activated(self):
FreeCAD.ActiveDocument.openTransaction("Create FemShellThickness")
FreeCADGui.addModule("FemShellThickness")
FreeCADGui.doCommand("FemShellThickness.makeFemShellThickness()")
FreeCADGui.doCommand("App.activeDocument()." + FemGui.getActiveAnalysis().Name + ".Member = App.activeDocument()." + FemGui.getActiveAnalysis().Name + ".Member + [App.ActiveDocument.ActiveObject]")
def IsActive(self):
True if FemGui.getActiveAnalysis() else False
class _FemShellThickness:
"The FemShellThickness object"
def __init__(self, obj):
obj.addProperty("App::PropertyLength", "Thickness", "ShellThickness", "set thickness of the shell elements")
obj.addProperty("App::PropertyLinkSubList", "References", "ShellThickness", "List of shell thickness shapes")
obj.Proxy = self
self.Type = "FemShellThickness"
def execute(self, obj):
return
class _ViewProviderFemShellThickness:
"A View Provider for the FemShellThickness object"
def __init__(self, vobj):
vobj.Proxy = self
def getIcon(self):
return ":/icons/fem-shell-thickness.svg"
def attach(self, vobj):
self.ViewObject = vobj
self.Object = vobj.Object
self.standard = coin.SoGroup()
vobj.addDisplayMode(self.standard, "Standard")
def getDisplayModes(self, obj):
return ["Standard"]
def getDefaultDisplayMode(self):
return "Standard"
def updateData(self, obj, prop):
return
def onChanged(self, vobj, prop):
return
def setEdit(self, vobj, mode=0):
taskd = _FemShellThicknessTaskPanel(self.Object)
taskd.obj = vobj.Object
# taskd.update() When is this needed ?
FreeCADGui.Control.showDialog(taskd)
return True
def unsetEdit(self, vobj, mode=0):
FreeCADGui.Control.closeDialog()
return
def doubleClicked(self, vobj):
self.setEdit(vobj)
def __getstate__(self):
return None
def __setstate__(self, state):
return None
class _FemShellThicknessTaskPanel:
'''The TaskPanel for editing References property of FemShellThickness objects'''
def __init__(self, obj):
FreeCADGui.Selection.clearSelection()
self.sel_server = None
self.obj = obj
self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Fem/FemShellThickness.ui")
QtCore.QObject.connect(self.form.pushButton_Reference, QtCore.SIGNAL("clicked()"), self.add_references)
self.form.list_References.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.form.list_References.connect(self.form.list_References, QtCore.SIGNAL("customContextMenuRequested(QPoint)"), self.references_list_right_clicked)
self.previous_references = self.obj.References
self.references = self.obj.References
self.rebuild_list_References()
def accept(self):
if self.sel_server:
FreeCADGui.Selection.removeObserver(self.sel_server)
self.obj.References = self.references
FreeCADGui.ActiveDocument.resetEdit()
FreeCAD.ActiveDocument.recompute()
return True
def reject(self):
if self.sel_server:
FreeCADGui.Selection.removeObserver(self.sel_server)
FreeCADGui.ActiveDocument.resetEdit()
return True
def references_list_right_clicked(self, QPos):
self.form.contextMenu = QtGui.QMenu()
menu_item = self.form.contextMenu.addAction("Remove Reference")
if not self.references:
menu_item.setDisabled(True)
self.form.connect(menu_item, QtCore.SIGNAL("triggered()"), self.remove_reference)
parentPosition = self.form.list_References.mapToGlobal(QtCore.QPoint(0, 0))
self.form.contextMenu.move(parentPosition + QPos)
self.form.contextMenu.show()
def remove_reference(self):
if not self.references:
return
currentItemName = str(self.form.list_References.currentItem().text())
for ref in self.references:
refname_to_compare_listentry = ref[0].Name + '-->' + ref[1]
if refname_to_compare_listentry == currentItemName:
self.references.remove(ref)
self.rebuild_list_References()
def add_references(self):
'''Called if Button add_reference is triggered'''
# in constraints EditTaskPanel the selection is active as soon as the taskpanel is open
# here the addReference button EditTaskPanel has to be triggered to start selection mode
FreeCADGui.Selection.clearSelection()
# start SelectionObserver and parse the function to add the References to the widget
self.sel_server = ReferenceShapeSelectionObserver(self.selectionParser)
def selectionParser(self, selsub):
sel = selsub[0]
sub = selsub[1]
# print 'selection: ', sel.Shape.ShapeType, ' ', sel.Name, ' ', sub
if hasattr(sel, "Shape"):
elt = sel.Shape.getElement(sub)
if elt.ShapeType == 'Face':
if selsub not in self.references:
self.references.append(selsub)
self.rebuild_list_References()
else:
print sel.Name, '-->', sub, ' is already in reference list!'
else:
print 'Selection has no shape!'
def rebuild_list_References(self):
self.form.list_References.clear()
items = []
for i in self.references:
item_name = i[0].Name + '-->' + i[1]
items.append(item_name)
for listItemName in sorted(items):
listItem = QtGui.QListWidgetItem(listItemName, self.form.list_References) # listItem = is needed
class ReferenceShapeSelectionObserver:
'''ReferenceShapeSelectionObserver
started on click button addReference'''
def __init__(self, parseSelectionFunction):
self.parseSelectionFunction = parseSelectionFunction
FreeCADGui.Selection.addObserver(self)
FreeCAD.Console.PrintMessage("Select Faces to add them to the list!\n")
def addSelection(self, docName, objName, sub, pos):
selected_object = FreeCAD.getDocument(docName).getObject(objName) # get the obj objName
self.added_obj = (selected_object, sub)
if lsub: # on doubleClick the solid is selected and sub will be empty
self.parseSelectionFunction(self.added_obj)
if FreeCAD.GuiUp:
FreeCADGui.addCommand('Fem_ShellThickness', _CommandFemShellThickness())

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>249</width>
<height>379</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>1677215</height>
</size>
</property>
<property name="title">
<string>Thickness</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QLabel" name="l_label_text_3">
<property name="text">
<string>Use FreeCAD Property Editor</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="l_label_text_4">
<property name="text">
<string>to edit the thickness value</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_1">
<property name="title">
<string>Reference</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="l_label_text_1">
<property name="text">
<string>Leave reference blank </string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="l_label_text_2">
<property name="text">
<string>to choose all remaining shapes</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_Reference">
<property name="text">
<string>Add reference</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="list_References"/>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -118,11 +118,15 @@ class FemTools(QtCore.QRunnable, QtCore.QObject):
# [{'Object':fixed_constraints, 'NodeSupports':bool}, {}, ...]
# [{'Object':force_constraints, 'NodeLoad':value}, {}, ...
# [{'Object':pressure_constraints, 'xxxxxxxx':value}, {}, ...]
# [{'Object':beam_sections, 'xxxxxxxx':value}, {}, ...]
# [{'Object':shell_thicknesses, 'xxxxxxxx':value}, {}, ...]
self.mesh = None
self.material = []
self.fixed_constraints = []
self.force_constraints = []
self.pressure_constraints = []
self.beam_sections = []
self.shell_thicknesses = []
for m in self.analysis.Member:
if m.isDerivedFrom("Fem::FemMeshObject"):
@ -143,6 +147,14 @@ class FemTools(QtCore.QRunnable, QtCore.QObject):
PressureObjectDict = {}
PressureObjectDict['Object'] = m
self.pressure_constraints.append(PressureObjectDict)
elif hasattr(m, "Proxy") and m.Proxy.Type == 'FemBeamSection':
beam_section_dict = {}
beam_section_dict['Object'] = m
self.beam_sections.append(beam_section_dict)
elif hasattr(m, "Proxy") and m.Proxy.Type == 'FemShellThickness':
shell_thickness_dict = {}
shell_thickness_dict['Object'] = m
self.shell_thicknesses.append(shell_thickness_dict)
def check_prerequisites(self):
message = ""
@ -159,6 +171,20 @@ class FemTools(QtCore.QRunnable, QtCore.QObject):
if self.analysis_type == "static":
if not (self.force_constraints or self.pressure_constraints):
message += "No force-constraint or pressure-constraint defined in the Analysis\n"
if self.beam_sections:
has_no_references = False
for b in self.beam_sections:
if len(b['Object'].References) == 0:
if has_no_references is True:
message += "More than one BeamSection has empty References list (Only one empty References list is allowed!).\n"
has_no_references = True
if self.shell_thicknesses:
has_no_references = False
for s in self.shell_thicknesses:
if len(s['Object'].References) == 0:
if has_no_references is True:
message += "More than one ShellThickness has empty References list (Only one empty References list is allowed!).\n"
has_no_references = True
return message
def write_inp_file(self):
@ -167,9 +193,11 @@ class FemTools(QtCore.QRunnable, QtCore.QObject):
self.inp_file_name = ""
try:
inp_writer = iw.inp_writer(self.analysis, self.mesh, self.material,
self.fixed_constraints, self.force_constraints,
self.pressure_constraints, self.analysis_type,
self.eigenmode_parameters, self.working_dir)
self.fixed_constraints,
self.force_constraints, self.pressure_constraints,
self.beam_sections, self.shell_thicknesses,
self.analysis_type, self.eigenmode_parameters,
self.working_dir)
self.inp_file_name = inp_writer.write_calculix_input_file()
except:
print "Unexpected error when writing CalculiX input file:", sys.exc_info()[0]

View File

@ -104,6 +104,8 @@ void FemGuiExport initFemGui()
Base::Interpreter().loadModule("MechanicalAnalysis");
Base::Interpreter().loadModule("MechanicalMaterial");
Base::Interpreter().loadModule("FemBeamSection");
Base::Interpreter().loadModule("FemShellThickness");
// register preferences pages
new Gui::PrefPageProducer<FemGui::DlgSettingsFemImp> ("FEM");

View File

@ -58,6 +58,8 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
*fem << "Fem_NewMechanicalAnalysis"
<< "Fem_CreateFromShape"
<< "Fem_MechanicalMaterial"
<< "Fem_BeamSection"
<< "Fem_ShellThickness"
<< "Separator"
<< "Fem_CreateNodesSet"
<< "Separator"
@ -86,6 +88,8 @@ Gui::MenuItem* Workbench::setupMenuBar() const
*fem << "Fem_NewMechanicalAnalysis"
<< "Fem_CreateFromShape"
<< "Fem_MechanicalMaterial"
<< "Fem_BeamSection"
<< "Fem_ShellThickness"
<< "Separator"
<< "Fem_CreateNodesSet"
<< "Separator"

View File

@ -590,7 +590,8 @@ class _ResultControlTaskPanel:
def vm_stress_selected(self, state):
FreeCAD.FEM_dialog["results_type"] = "Sabs"
QApplication.setOverrideCursor(Qt.WaitCursor)
self.MeshObject.ViewObject.setNodeColorByScalars(self.result_object.ElementNumbers, self.result_object.StressValues)
if self.suitable_results:
self.MeshObject.ViewObject.setNodeColorByScalars(self.result_object.ElementNumbers, self.result_object.StressValues)
(minm, avg, maxm) = self.get_result_stats("Sabs")
self.set_result_stats("MPa", minm, avg, maxm)
QtGui.qApp.restoreOverrideCursor()
@ -598,12 +599,14 @@ class _ResultControlTaskPanel:
def select_displacement_type(self, disp_type):
QApplication.setOverrideCursor(Qt.WaitCursor)
if disp_type == "Uabs":
self.MeshObject.ViewObject.setNodeColorByScalars(self.result_object.ElementNumbers, self.result_object.DisplacementLengths)
if self.suitable_results:
self.MeshObject.ViewObject.setNodeColorByScalars(self.result_object.ElementNumbers, self.result_object.DisplacementLengths)
else:
match = {"U1": 0, "U2": 1, "U3": 2}
d = zip(*self.result_object.DisplacementVectors)
displacements = list(d[match[disp_type]])
self.MeshObject.ViewObject.setNodeColorByScalars(self.result_object.ElementNumbers, displacements)
if self.suitable_results:
self.MeshObject.ViewObject.setNodeColorByScalars(self.result_object.ElementNumbers, displacements)
(minm, avg, maxm) = self.get_result_stats(disp_type)
self.set_result_stats("mm", minm, avg, maxm)
QtGui.qApp.restoreOverrideCursor()
@ -631,7 +634,8 @@ class _ResultControlTaskPanel:
if FreeCAD.FEM_dialog["result_object"] != self.result_object:
self.update_displacement()
FreeCAD.FEM_dialog["result_object"] = self.result_object
self.MeshObject.ViewObject.setNodeDisplacementByVectors(self.result_object.ElementNumbers, self.result_object.DisplacementVectors)
if self.suitable_results:
self.MeshObject.ViewObject.setNodeDisplacementByVectors(self.result_object.ElementNumbers, self.result_object.DisplacementVectors)
self.update_displacement()
QtGui.qApp.restoreOverrideCursor()
@ -656,6 +660,15 @@ class _ResultControlTaskPanel:
self.MeshObject = i
break
if self.MeshObject.FemMesh.NodeCount == len(self.result_object.ElementNumbers):
self.suitable_results = True
else:
self.suitable_results = False
if not self.MeshObject.FemMesh.VolumeCount:
FreeCAD.Console.PrintError('Graphical output for beam or shell FEM Meshes not yet supported!\n')
else:
FreeCAD.Console.PrintError('Result node numbers are not equal to FEM Mesh NodeCount!\n')
def accept(self):
FreeCADGui.Control.closeDialog()

View File

@ -5,8 +5,12 @@ import time
class inp_writer:
def __init__(self, analysis_obj, mesh_obj, mat_obj, fixed_obj, force_obj,
pressure_obj, analysis_type=None, eigenmode_parameters=None, dir_name=None):
def __init__(self, analysis_obj, mesh_obj, mat_obj,
fixed_obj,
force_obj, pressure_obj,
beamsection_obj, shellthickness_obj,
analysis_type=None, eigenmode_parameters=None,
dir_name=None):
self.dir_name = dir_name
self.analysis = analysis_obj
self.mesh_object = mesh_obj
@ -19,6 +23,8 @@ class inp_writer:
self.eigenfrequeny_range_low = eigenmode_parameters[1]
self.eigenfrequeny_range_high = eigenmode_parameters[2]
self.analysis_type = analysis_type
self.beamsection_objects = beamsection_obj
self.shellthickness_objects = shellthickness_obj
if not dir_name:
self.dir_name = FreeCAD.ActiveDocument.TransientDir.replace('\\', '/') + '/FemAnl_' + analysis_obj.Uid[-4:]
if not os.path.isdir(self.dir_name):
@ -37,6 +43,7 @@ class inp_writer:
self.write_node_sets_constraints_fixed(inpfile)
self.write_node_sets_constraints_force(inpfile)
self.write_materials(inpfile)
self.write_femelementsets(inpfile)
self.write_step_begin(inpfile)
self.write_constraints_fixed(inpfile)
if self.analysis_type is None or self.analysis_type == "static":
@ -54,13 +61,113 @@ class inp_writer:
f.write('\n***********************************************************\n')
f.write('** Element sets for materials and FEM element type (solid, shell, beam)\n')
f.write('** written by {} function\n'.format(sys._getframe().f_code.co_name))
for m in self.material_objects:
if len(self.material_objects) == 1:
f.write('*ELSET,ELSET=MaterialSolidElements\n')
if len(self.material_objects) > 1:
FreeCAD.Console.PrintError('Multiple materials defined, this could result in a broken CalculiX input file!\n')
if len(self.shellthickness_objects) > 1 or len(self.beamsection_objects) > 1:
if not hasattr(self, 'fem_element_table'):
self.fem_element_table = getFemElementTable(self.mesh_object.FemMesh)
for mi, m in enumerate(self.material_objects):
mat_obj = m['Object']
if self.beamsection_objects: # all fem_elements are beamsection_obj --> beam mesh
remaining_material_beam_elementset_line = None
e_count = 0
e_referenced = []
e_not_referenced = []
for bi, b in enumerate(self.beamsection_objects):
beamsection_obj = b['Object']
material_elementset_name = mat_obj.Name + beamsection_obj.Name
# max identifier length in CalculiX for beam sections see http://forum.freecadweb.org/viewtopic.php?f=18&t=12509
if len(material_elementset_name) > 20:
material_elementset_name = 'Mat' + str(mi) + 'Beam' + str(bi)
b['MaterialElementsetName'] = material_elementset_name # the last material is taken in def write_femelementsets()
material_elementset_line = '*ELSET,ELSET=' + material_elementset_name + '\n'
if not beamsection_obj.References:
if len(self.beamsection_objects) == 1: # all beam elements have the section of this beamsection_obj
f.write(material_elementset_line)
f.write('Eall\n')
else: # remaining beam elements have the beamsection_obj of this beamsection_obj
remaining_material_beam_elementset_line = material_elementset_line
else: # use reference shapes for the beamsection_obj of this beamsection_obj
f.write(material_elementset_line)
for ref in beamsection_obj.References:
no = []
el = []
r = ref[0].Shape.getElement(ref[1])
if r.ShapeType == 'Edge':
# print ' BeamSectionReferenceEdge : ', ref[0].Name, ', ', ref[0].Label, ' --> ', ref[1]
no = self.mesh_object.FemMesh.getNodesByEdge(r)
el = getFemElementsByNodes(self.fem_element_table, no)
else:
print ' No Edge, but BeamSection needs Edges as reference shapes!'
for e in sorted(el):
f.write(str(e) + ',\n')
e_count += len(el)
e_referenced += el
# write remaining beamsection elements
if remaining_material_beam_elementset_line:
f.write(remaining_material_beam_elementset_line)
f.write('**remaining elements\n')
for e in self.fem_element_table:
if e not in e_referenced:
e_not_referenced.append(e)
for e in sorted(e_not_referenced):
f.write(str(e) + ',\n')
e_count += len(e_not_referenced)
f.write('\n')
elif self.shellthickness_objects: # all fem_elements are shells --> shell mesh
remaining_material_shellthicknes_elementset_line = None
e_count = 0
e_referenced = []
e_not_referenced = []
for si, s in enumerate(self.shellthickness_objects):
shellthickness_obj = s['Object']
material_elementset_name = mat_obj.Name + shellthickness_obj.Name
if len(material_elementset_name) > 80: # standard max identifier lenght in CalculiX
material_elementset_name = 'Mat' + str(mi) + 'Shell' + str(si)
s['MaterialElementsetName'] = material_elementset_name # the last material is taken in def write_femelementsets()
material_elementset_line = '*ELSET,ELSET=' + material_elementset_name + '\n'
if not shellthickness_obj.References:
if len(self.shellthickness_objects) == 1: # all shell elements have the thickness of this shellthickness_obj
f.write(material_elementset_line)
f.write('Eall\n')
else: # remaining shell elements have the thickness of this shellthickness_obj
remaining_material_shellthicknes_elementset_line = material_elementset_line
else: # use reference shapes for the thickness of this shellthickness_obj
f.write(material_elementset_line)
for ref in shellthickness_obj.References:
no = []
el = []
r = ref[0].Shape.getElement(ref[1])
if r.ShapeType == 'Face':
# print ' ShellThicknessReferenceFace : ', ref[0].Name, ', ', ref[0].Label, ' --> ', ref[1]
no = self.mesh_object.FemMesh.getNodesByFace(r)
el = getFemElementsByNodes(self.fem_element_table, no)
else:
print ' No Face, but ShellThickness needs Faces as reference shapes!'
for e in sorted(el):
f.write(str(e) + ',\n')
e_count += len(el)
e_referenced += el
# write remaining shellthickness elements
if remaining_material_shellthicknes_elementset_line:
f.write(remaining_material_shellthicknes_elementset_line)
f.write('**remaining elements\n')
for e in self.fem_element_table:
if e not in e_referenced:
e_not_referenced.append(e)
for e in sorted(e_not_referenced):
f.write(str(e) + ',\n')
e_count += len(e_not_referenced)
f.write('\n')
else: # all fem_elements are solids --> volume mesh
material_elementset_name = 'MaterialSolidElements'
f.write('*ELSET,ELSET=' + material_elementset_name + '\n')
f.write('Eall\n')
else:
print 'material object count: ', len(self.material_objects)
FreeCAD.Console.PrintError('Multiple materials are not yet supported!\n')
if hasattr(self, 'fem_element_table'):
if e_count != len(self.fem_element_table):
print 'ERROR: self.fem_element_table != e_count'
print 'Elements written to CalculiX file: ', e_count
print 'Elements of the FreeCAD FEM Mesh: ', len(self.fem_element_table)
def write_node_sets_constraints_fixed(self, f):
f.write('\n***********************************************************\n')
@ -121,7 +228,6 @@ class inp_writer:
YM = FreeCAD.Units.Quantity(mat_obj.Material['YoungsModulus'])
YM_in_MPa = YM.getValueAs('MPa')
PR = float(mat_obj.Material['PoissonRatio'])
mat_obj_name = mat_obj.Name
mat_name = mat_obj.Material['Name'][:80]
# write material properties
f.write('*MATERIAL, NAME=' + mat_name + '\n')
@ -132,12 +238,39 @@ class inp_writer:
density_in_tone_per_mm3 = float(density.getValueAs('t/mm^3'))
f.write('*DENSITY \n')
f.write('{0:.3e}, \n'.format(density_in_tone_per_mm3))
# write element properties
if len(self.material_objects) == 1:
f.write('*SOLID SECTION, ELSET=MaterialSolidElements, MATERIAL=' + mat_name + '\n')
else:
if mat_obj_name == 'MechanicalMaterial':
f.write('*SOLID SECTION, ELSET=MaterialSolidElements, MATERIAL=' + mat_name + '\n')
def write_femelementsets(self, f):
f.write('\n***********************************************************\n')
f.write('** Sections\n')
f.write('** written by {} function\n'.format(sys._getframe().f_code.co_name))
for m in self.material_objects:
mat_obj = m['Object']
mat_name = mat_obj.Material['Name'][:80]
if self.beamsection_objects: # all fem_elements are beams
for b in self.beamsection_objects:
beamsection_obj = b['Object']
material_elementset_name = b['MaterialElementsetName']
el_set = 'ELSET=' + material_elementset_name + ', '
material = 'MATERIAL=' + mat_name
el_prop = '*BEAM SECTION, ' + el_set + material + ', SECTION=RECT\n'
sc_prop = str(beamsection_obj.Hight.getValueAs('mm')) + ', ' + str(beamsection_obj.Width.getValueAs('mm')) + '\n'
f.write(el_prop)
f.write(sc_prop)
elif self.shellthickness_objects: # all fem_elements are shells
for s in self.shellthickness_objects:
shellthickness_obj = s['Object']
material_elementset_name = s['MaterialElementsetName']
el_set = 'ELSET=' + material_elementset_name + ', '
material = 'MATERIAL=' + mat_name
el_prop = '*SHELL SECTION, ' + el_set + material + '\n'
sc_prop = str(shellthickness_obj.Thickness.getValueAs('mm')) + '\n'
f.write(el_prop)
f.write(sc_prop)
else: # all fem_elements are solids
el_set = 'ELSET=' + 'MaterialSolidElements' + ', '
material = 'MATERIAL=' + mat_name
el_prop = '*SOLID SECTION, ' + el_set + material + '\n'
f.write(el_prop)
def write_step_begin(self, f):
f.write('\n***********************************************************\n')
@ -156,7 +289,12 @@ class inp_writer:
f.write('*BOUNDARY\n')
f.write(fix_obj_name + ',1\n')
f.write(fix_obj_name + ',2\n')
f.write(fix_obj_name + ',3\n\n')
f.write(fix_obj_name + ',3\n')
if self.beamsection_objects or self.shellthickness_objects:
f.write(fix_obj_name + ',4\n')
f.write(fix_obj_name + ',5\n')
f.write(fix_obj_name + ',6\n')
f.write('\n')
def write_constraints_force(self, f):
f.write('\n***********************************************************\n')

View File

@ -479,6 +479,10 @@ Eall
0.300
*DENSITY
7.900e-09,
***********************************************************
** Sections
** written by write_femelementsets function
*SOLID SECTION, ELSET=MaterialSolidElements, MATERIAL=Steel
***********************************************************
@ -523,8 +527,8 @@ S
***********************************************************
** CalculiX Input file
** written by write_footer function
** written by --> FreeCAD 0.16.5561 (Git)
** written on --> Thu Sep 17 14:54:31 2015
** written by --> FreeCAD 0.16.5604 (Git)
** written on --> Mon Sep 21 16:49:37 2015
** file name -->
** analysis name --> MechanicalAnalysis
**

View File

@ -479,6 +479,10 @@ Eall
0.300
*DENSITY
7.900e-09,
***********************************************************
** Sections
** written by write_femelementsets function
*SOLID SECTION, ELSET=MaterialSolidElements, MATERIAL=Steel
***********************************************************
@ -589,8 +593,8 @@ S
***********************************************************
** CalculiX Input file
** written by write_footer function
** written by --> FreeCAD 0.16.5561 (Git)
** written on --> Thu Sep 17 14:52:57 2015
** written by --> FreeCAD 0.16.5604 (Git)
** written on --> Mon Sep 21 16:46:15 2015
** file name -->
** analysis name --> MechanicalAnalysis
**

View File

@ -455,6 +455,10 @@ Eall
0.360
*DENSITY
1.000e-09,
***********************************************************
** Sections
** written by write_femelementsets function
*SOLID SECTION, ELSET=MaterialSolidElements, MATERIAL=TestMaterial
***********************************************************

View File

@ -455,6 +455,10 @@ Eall
0.360
*DENSITY
1.000e-09,
***********************************************************
** Sections
** written by write_femelementsets function
*SOLID SECTION, ELSET=MaterialSolidElements, MATERIAL=TestMaterial
***********************************************************