From 8ae5978146be3b58eca374fa1ae4d7cb92899400 Mon Sep 17 00:00:00 2001 From: Roy-043 <70520633+Roy-043@users.noreply.github.com> Date: Wed, 14 Jul 2021 21:34:47 +0200 Subject: [PATCH 01/20] Draft: fix dimensions with decimals=0 problem The display_external function in units.py did not handle decimals=0 properly. --- src/Mod/Draft/draftutils/units.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Draft/draftutils/units.py b/src/Mod/Draft/draftutils/units.py index bdcbba6acb..71dbcdbaa3 100644 --- a/src/Mod/Draft/draftutils/units.py +++ b/src/Mod/Draft/draftutils/units.py @@ -95,7 +95,7 @@ def display_external(internal_value, if dim == 'Length': q = App.Units.Quantity(internal_value, App.Units.Length) if not unit: - if not decimals and showUnit: + if decimals == None and showUnit: return q.UserString conversion = q.getUserPreferred()[1] From 29ec98df8d2e06f1af19167fc03e0eb5bd7baa64 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Thu, 15 Jul 2021 08:30:12 +0200 Subject: [PATCH 02/20] Material, workaround for precision problem --- src/Mod/Material/MaterialEditor.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/Mod/Material/MaterialEditor.py b/src/Mod/Material/MaterialEditor.py index c3db367b64..51cf4ab14c 100644 --- a/src/Mod/Material/MaterialEditor.py +++ b/src/Mod/Material/MaterialEditor.py @@ -649,17 +649,23 @@ class MaterialsDelegate(QtGui.QStyledItemDelegate): else: super(MaterialsDelegate, self).setEditorData(editor, index) - print("item2={}".format(item.text())) + # print("item2={}".format(item.text())) ui = FreeCADGui.UiLoader() def matProperWidget(parent=None, matproperty=None, Type="String", Value=None, - minimum=None, maximum=None, stepsize=None, precision=None): + minimum=None, maximum=None, stepsize=None, precision=12): '''customs widgets for the material stuff.''' + # FIXME + # Workaround for problem from here: + # https://forum.freecadweb.org/viewtopic.php?f=18&t=56912&start=20#p516811 + # set precision to 12 + # it is strange, but for the user defined values everything works fine + if Type == "String": widget = ui.createWidget("Gui::PrefLineEdit") @@ -675,23 +681,31 @@ def matProperWidget(parent=None, matproperty=None, Type="String", Value=None, lineEdit = widget.children()[1] lineEdit.setText(Value) - elif Type == "Quantity": + elif Type == "Quantity" or Type == "Float": widget = ui.createWidget("Gui::InputField") + # print(matproperty) if hasattr(FreeCAD.Units, matproperty): unit = getattr(FreeCAD.Units, matproperty) quantity = FreeCAD.Units.Quantity(1, unit) widget.setProperty('unit', quantity.getUserPreferred()[2]) else: - FreeCAD.Console.PrintError('Not known unit for property: {}\n'.format(matproperty)) + FreeCAD.Console.PrintWarning( + "Not known unit for property: {}. Probably the Quantity does not have a unit.\n" + .format(matproperty) + ) + # the Gui::InputField is used for Floats too, because of the diggits elif Type == "Integer": widget = ui.createWidget("Gui::UIntSpinBox") - elif Type == "Float": + # elif Type == "Float": - widget = ui.createWidget("Gui::PrefDoubleSpinBox") + # widget = ui.createWidget("Gui::PrefDoubleSpinBox") + # has only 2 diggits precision, but for example RelativePermittivity needs much more + # see material card for Air, thus Workaround + # a "Gui::InputField" without unit is used elif Type == "Enumerator": From 70c8ff15ba875fe2cfca283da9f6e8000cbb3a3c Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Thu, 15 Jul 2021 08:33:16 +0200 Subject: [PATCH 03/20] Material Editor, code formating --- src/Mod/Material/MaterialEditor.py | 106 ++++++++++++++--------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/src/Mod/Material/MaterialEditor.py b/src/Mod/Material/MaterialEditor.py index 51cf4ab14c..4c8e74239e 100644 --- a/src/Mod/Material/MaterialEditor.py +++ b/src/Mod/Material/MaterialEditor.py @@ -39,7 +39,7 @@ if sys.version_info.major >= 3: class MaterialEditor: - def __init__(self, obj=None, prop=None, material=None, card_path=''): + def __init__(self, obj=None, prop=None, material=None, card_path=""): """Initializes, optionally with an object name and a material property name to edit, or directly with a material dictionary.""" @@ -139,7 +139,7 @@ class MaterialEditor: def implementModel(self): - '''implements the model with the material attribute structure.''' + """implements the model with the material attribute structure.""" p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material") widget = self.widget @@ -163,13 +163,13 @@ class MaterialEditor: for properName in group[gg]: pp = properName # property name item = QtGui.QStandardItem(pp) - item.setToolTip(group[gg][properName]['Description']) + item.setToolTip(group[gg][properName]["Description"]) self.internalprops.append(pp) it = QtGui.QStandardItem() - it.setToolTip(group[gg][properName]['Description']) + it.setToolTip(group[gg][properName]["Description"]) - tt = group[gg][properName]['Type'] + tt = group[gg][properName]["Type"] itType = QtGui.QStandardItem(tt) top.appendRow([item, it, itType]) @@ -180,9 +180,9 @@ class MaterialEditor: def updateMatParamsInTree(self, data): - '''updates the contents of the editor with the given dictionary + """updates the contents of the editor with the given dictionary the material property keys where added to the editor already - not known material property keys will be added to the user defined group''' + not known material property keys will be added to the user defined group""" # print(data) model = self.widget.treeView.model() @@ -223,8 +223,8 @@ class MaterialEditor: return self.card_path = self.widget.ComboMaterial.itemData(index) FreeCAD.Console.PrintMessage( - 'choose_material in material editor:\n' - ' {}\n'.format(self.card_path) + "choose_material in material editor:\n" + " {}\n".format(self.card_path) ) if os.path.isfile(self.card_path): from importFCMat import read @@ -234,11 +234,11 @@ class MaterialEditor: # the card could be updated the dict not self.widget.ComboMaterial.setCurrentIndex(index) # set after tree params else: - FreeCAD.Console.PrintError('material card not found: {}\n'.format(self.card_path)) + FreeCAD.Console.PrintError("material card not found: {}\n".format(self.card_path)) def updateCardsInCombo(self): - '''updates the contents of the materials combo with existing material cards''' + """updates the contents of the materials combo with existing material cards""" mat_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material/Cards") sort_by_resources = mat_prefs.GetBool("SortByResources", False) @@ -260,13 +260,13 @@ class MaterialEditor: a_path = card_names_tmp[a_name] card_name_list.append([a_name, a_path, self.icons[a_path]]) - card_name_list.insert(0, [None, '', '']) + card_name_list.insert(0, [None, "", ""]) for mat in card_name_list: self.widget.ComboMaterial.addItem(QtGui.QIcon(mat[2]), mat[0], mat[1]) def openProductURL(self): - '''opens the contents of the ProductURL field in an external browser.''' + """opens the contents of the ProductURL field in an external browser.""" model = self.widget.treeView.model() item = model.findItems(translate("Material", "Product URL"), @@ -340,8 +340,8 @@ class MaterialEditor: def deleteCustomProperty(self, key=None): - '''Deletes a custom property from the editor, - or deletes the value of an internal property.''' + """Deletes a custom property from the editor, + or deletes the value of an internal property.""" widget = self.widget treeView = widget.treeView @@ -377,8 +377,8 @@ class MaterialEditor: def onClickTree(self, index): - '''Checks if the current item is a custom or an internal property, - and enable the delete property or delete value button.''' + """Checks if the current item is a custom or an internal property, + and enable the delete property or delete value button.""" widget = self.widget buttonDeleteProperty = widget.ButtonDeleteProperty @@ -428,18 +428,18 @@ class MaterialEditor: # since text(0) could be translated matkey = self.collapseKey(str(kk)) matvalue = unicode(ii) - if matvalue or (matkey == 'Name'): + if matvalue or (matkey == "Name"): # use only keys which are not empty and the name even if empty d[matkey] = matvalue # self.outputDict(d) return d def outputDict(self, d): - print('MaterialEditor dictionary') + print("MaterialEditor dictionary") for param in d: - print(' {} : {}'.format(param, d[param])) + print(" {} : {}".format(param, d[param])) - ''' + """ def setTexture(self, pattern): "displays a texture preview if needed" self.widget.PreviewVector.hide() @@ -453,7 +453,7 @@ class MaterialEditor: if pattern: self.widget.PreviewVector.setPixmap(QtGui.QPixmap(pattern)) self.widget.PreviewVector.show() - ''' + """ def updatePreviews(self, mat=None): "updates the preview images from the content of the editor" @@ -505,9 +505,9 @@ class MaterialEditor: "Opens a FCMat file" filetuple = QtGui.QFileDialog.getOpenFileName( QtGui.QApplication.activeWindow(), - 'Open FreeCAD Material file', + "Open FreeCAD Material file", self.directory, - '*.FCMat' + "*.FCMat" ) self.card_path = filetuple[0] index = self.widget.ComboMaterial.findData(self.card_path) @@ -518,8 +518,8 @@ class MaterialEditor: if os.path.isfile(self.card_path): if index == -1: FreeCAD.Console.PrintMessage( - 'Card path: {} not found in known cards.' - 'The material parameter only are loaded.\n' + "Card path: {} not found in known cards." + "The material parameter only are loaded.\n" .format(self.card_path) ) from importFCMat import read @@ -548,9 +548,9 @@ class MaterialEditor: name = "Material" filetuple = QtGui.QFileDialog.getSaveFileName( QtGui.QApplication.activeWindow(), - 'Save FreeCAD Material file', - self.directory + '/' + name + '.FCMat', - '*.FCMat' + "Save FreeCAD Material file", + self.directory + "/" + name + ".FCMat", + "*.FCMat" ) # a tuple of two empty strings returns True, so use the filename directly filename = filetuple[0] @@ -573,7 +573,7 @@ class MaterialEditor: class MaterialsDelegate(QtGui.QStyledItemDelegate): - '''provides display and editing facilities for data items from a model.''' + """provides display and editing facilities for data items from a model.""" def __init__(self): "" @@ -582,7 +582,7 @@ class MaterialsDelegate(QtGui.QStyledItemDelegate): def createEditor(self, parent, option, index): - '''returns the widget used to change data from the model.''' + """returns the widget used to change data from the model.""" model = index.model() column = index.column() @@ -628,16 +628,16 @@ class MaterialsDelegate(QtGui.QStyledItemDelegate): def setEditorData(self, editor, index): - '''provides the widget with data to manipulate.''' + """provides the widget with data to manipulate.""" - Type = editor.property('Type') + Type = editor.property("Type") model = index.model() item = model.itemFromIndex(index) print("item1={}".format(item.text())) if Type == "Color": - color = editor.property('color') + color = editor.property("color") color = tuple([v/255.0 for v in color.getRgb()]) item.setText(str(color)) @@ -658,7 +658,7 @@ ui = FreeCADGui.UiLoader() def matProperWidget(parent=None, matproperty=None, Type="String", Value=None, minimum=None, maximum=None, stepsize=None, precision=12): - '''customs widgets for the material stuff.''' + """customs widgets for the material stuff.""" # FIXME # Workaround for problem from here: @@ -688,7 +688,7 @@ def matProperWidget(parent=None, matproperty=None, Type="String", Value=None, if hasattr(FreeCAD.Units, matproperty): unit = getattr(FreeCAD.Units, matproperty) quantity = FreeCAD.Units.Quantity(1, unit) - widget.setProperty('unit', quantity.getUserPreferred()[2]) + widget.setProperty("unit", quantity.getUserPreferred()[2]) else: FreeCAD.Console.PrintWarning( "Not known unit for property: {}. Probably the Quantity does not have a unit.\n" @@ -714,7 +714,7 @@ def matProperWidget(parent=None, matproperty=None, Type="String", Value=None, elif Type == "Boolean": widget = ui.createWidget("Gui::PrefComboBox") - widget.insertItems(0, ['', 'False', 'True']) + widget.insertItems(0, ["", "False", "True"]) elif Type == "Vector": @@ -727,22 +727,22 @@ def matProperWidget(parent=None, matproperty=None, Type="String", Value=None, value = string2tuple(Value) color = QtGui.QColor() color.setRgb(value[0], value[1], value[2], value[3]) - widget.setProperty('color', color) + widget.setProperty("color", color) else: widget = QtGui.QLineEdit() if minimum is not None: - widget.setProperty('minimum', minimum) + widget.setProperty("minimum", minimum) if maximum is not None: - widget.setProperty('maximum', maximum) + widget.setProperty("maximum", maximum) if stepsize is not None: - widget.setProperty('stepsize', stepsize) + widget.setProperty("stepsize", stepsize) if precision is not None: - widget.setProperty('precision', precision) + widget.setProperty("precision", precision) - widget.setProperty('Type', Type) + widget.setProperty("Type", Type) widget.setParent(parent) @@ -752,7 +752,7 @@ def matProperWidget(parent=None, matproperty=None, Type="String", Value=None, def string2tuple(string): "provisionally" value = string[1:-1] - value = value.split(',') + value = value.split(",") value = [int(float(v)*255) for v in value] value = tuple(value) return value @@ -789,26 +789,26 @@ def editMaterial(material=None, card_path=None): return {} -''' +""" # some examples how to open the material editor in Python: import MaterialEditor MaterialEditor.openEditor() doc = FreeCAD.open( - FreeCAD.ConfigGet("AppHomePath") + 'data/examples/FemCalculixCantilever3D.FCStd' + FreeCAD.ConfigGet("AppHomePath") + "data/examples/FemCalculixCantilever3D.FCStd" ) import MaterialEditor -MaterialEditor.openEditor('SolidMaterial', 'Material') +MaterialEditor.openEditor("SolidMaterial", "Material") import MaterialEditor MaterialEditor.editMaterial({ - 'Density': '1234.0 kg/m^3', - 'Name': 'My-Material-Data', - 'PoissonRatio': '0.66', - 'YoungsModulus': '123456 MPa' + "Density": "1234.0 kg/m^3", + "Name": "My-Material-Data", + "PoissonRatio": "0.66", + "YoungsModulus": "123456 MPa" }) import MaterialEditor -MaterialEditor.editMaterial('ABS') +MaterialEditor.editMaterial("ABS") -''' +""" From 01c5c1a63c84c5f173bfdd3889d4f80f530dc9ea Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Thu, 15 Jul 2021 09:19:58 +0200 Subject: [PATCH 04/20] Material editor: add some comments --- src/Mod/Material/MaterialEditor.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/Mod/Material/MaterialEditor.py b/src/Mod/Material/MaterialEditor.py index 4c8e74239e..0dd7d10d1d 100644 --- a/src/Mod/Material/MaterialEditor.py +++ b/src/Mod/Material/MaterialEditor.py @@ -37,6 +37,8 @@ if sys.version_info.major >= 3: unicode = str +# ************************************************************************************************ +# ************************************************************************************************ class MaterialEditor: def __init__(self, obj=None, prop=None, material=None, card_path=""): @@ -571,6 +573,8 @@ class MaterialEditor: return self.widget.exec_() +# ************************************************************************************************ +# ************************************************************************************************ class MaterialsDelegate(QtGui.QStyledItemDelegate): """provides display and editing facilities for data items from a model.""" @@ -649,12 +653,11 @@ class MaterialsDelegate(QtGui.QStyledItemDelegate): else: super(MaterialsDelegate, self).setEditorData(editor, index) - # print("item2={}".format(item.text())) -ui = FreeCADGui.UiLoader() - +# ************************************************************************************************ +# ************************************************************************************************ def matProperWidget(parent=None, matproperty=None, Type="String", Value=None, minimum=None, maximum=None, stepsize=None, precision=12): @@ -664,7 +667,19 @@ def matProperWidget(parent=None, matproperty=None, Type="String", Value=None, # Workaround for problem from here: # https://forum.freecadweb.org/viewtopic.php?f=18&t=56912&start=20#p516811 # set precision to 12 - # it is strange, but for the user defined values everything works fine + # Better would be a similar system like used in FEM material task panel + # if the value in the InputField has not changed the data is not changed + # why does the value changes if the user clicks in + # the value and unit should exact stay as it is displayed before the user + # clicks in the field + + # the user defined properties are of Type String and thus uses a "Gui::PrefLineEdit" + + print(matproperty) + print(Type) + print(Value) + + ui = FreeCADGui.UiLoader() if Type == "String": @@ -789,6 +804,8 @@ def editMaterial(material=None, card_path=None): return {} +# ************************************************************************************************ +# ************************************************************************************************ """ # some examples how to open the material editor in Python: import MaterialEditor From 856bc75710a0f318c836ee273b19c1e6bec92f59 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Thu, 15 Jul 2021 17:16:44 +0200 Subject: [PATCH 05/20] FEM: calculix writer, matgeosets, code formating --- .../Fem/femsolver/calculix/write_femelement_matgeosets.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Mod/Fem/femsolver/calculix/write_femelement_matgeosets.py b/src/Mod/Fem/femsolver/calculix/write_femelement_matgeosets.py index c485af58e4..49b32c9404 100644 --- a/src/Mod/Fem/femsolver/calculix/write_femelement_matgeosets.py +++ b/src/Mod/Fem/femsolver/calculix/write_femelement_matgeosets.py @@ -34,11 +34,14 @@ def write_femelement_matgeosets(f, ccxwriter): # write ccx_elsets to file f.write("\n{}\n".format(59 * "*")) f.write("** Element sets for materials and FEM element type (solid, shell, beam, fluid)\n") + for ccx_elset in ccxwriter.ccx_elsets: - f.write("*ELSET,ELSET=" + ccx_elset["ccx_elset_name"] + "\n") + + f.write("*ELSET,ELSET={}\n".format(ccx_elset["ccx_elset_name"])) + # use six to be sure to be Python 2.7 and 3.x compatible if isinstance(ccx_elset["ccx_elset"], six.string_types): - f.write(ccx_elset["ccx_elset"] + "\n") + f.write("{}\n".format(ccx_elset["ccx_elset"])) else: for elid in ccx_elset["ccx_elset"]: f.write(str(elid) + ",\n") From e0c9587904b3027d2e6d7d235785d16617c9768a Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Thu, 15 Jul 2021 21:06:35 +0200 Subject: [PATCH 06/20] FEM: material task panel, code formating --- src/Mod/Fem/femtaskpanels/task_material_common.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtaskpanels/task_material_common.py b/src/Mod/Fem/femtaskpanels/task_material_common.py index 95248510c5..e4cac0bf6b 100644 --- a/src/Mod/Fem/femtaskpanels/task_material_common.py +++ b/src/Mod/Fem/femtaskpanels/task_material_common.py @@ -487,7 +487,10 @@ class _TaskPanel: "May try the following in Python console:\n" "from FreeCAD import Units\n" "Units.Quantity('{}')\n" - .format(self.material["ThermalConductivity"], self.material["ThermalConductivity"]) + .format( + self.material["ThermalConductivity"], + self.material["ThermalConductivity"] + ) ) self.material["ThermalConductivity"] = "0 W/m/K" if "ThermalConductivity" not in str(Units.Unit(self.material["ThermalConductivity"])): From 8e39ad57556550f962c4e1dea107b893256d8517 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Thu, 15 Jul 2021 17:17:28 +0200 Subject: [PATCH 07/20] FEM: separate getting the mesh sets from writer class --- src/Mod/Fem/CMakeLists.txt | 1 + src/Mod/Fem/femmesh/meshsetsgetter.py | 1047 ++++++++++++++++++++++ src/Mod/Fem/femsolver/calculix/writer.py | 55 +- src/Mod/Fem/femsolver/writerbase.py | 1018 ++------------------- 4 files changed, 1165 insertions(+), 956 deletions(-) create mode 100644 src/Mod/Fem/femmesh/meshsetsgetter.py diff --git a/src/Mod/Fem/CMakeLists.txt b/src/Mod/Fem/CMakeLists.txt index f597274816..4ff7364a4d 100755 --- a/src/Mod/Fem/CMakeLists.txt +++ b/src/Mod/Fem/CMakeLists.txt @@ -133,6 +133,7 @@ SET(FemMesh_SRCS femmesh/__init__.py femmesh/femmesh2mesh.py femmesh/gmshtools.py + femmesh/meshsetsgetter.py femmesh/meshtools.py ) diff --git a/src/Mod/Fem/femmesh/meshsetsgetter.py b/src/Mod/Fem/femmesh/meshsetsgetter.py new file mode 100644 index 0000000000..06f8a575c8 --- /dev/null +++ b/src/Mod/Fem/femmesh/meshsetsgetter.py @@ -0,0 +1,1047 @@ +# *************************************************************************** +# * Copyright (c) 2021 Bernd Hahnebach * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * 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 * +# * * +# *************************************************************************** + +__title__ = "FreeCAD FEM sets getter" +__author__ = "Bernd Hahnebach" +__url__ = "https://www.freecadweb.org" + +## \addtogroup FEM +# @{ + +import FreeCAD + +from femmesh import meshtools +from femtools.femutils import type_of_obj + + +class MeshSetsGetter(): + def __init__( + self, + analysis_obj, + solver_obj, + mesh_obj, + member, + ): + # class attributes from parameter values + self.analysis = analysis_obj + self.solver_obj = solver_obj # TODO without _obj + self.mesh_object = mesh_obj # TODO without _object + self.member = member + + # more attributes + self.analysis_type = self.solver_obj.AnalysisType + self.document = self.analysis.Document + self.fc_ver = FreeCAD.Version() + self.ccx_nall = "Nall" + self.ccx_eall = "Eall" + self.ccx_evolumes = "Evolumes" + self.ccx_efaces = "Efaces" + self.ccx_eedges = "Eedges" + self.mat_geo_sets = [] + if self.mesh_object: + if hasattr(self.mesh_object, "Shape"): + self.theshape = self.mesh_object.Shape + elif hasattr(self.mesh_object, "Part"): + self.theshape = self.mesh_object.Part + else: + FreeCAD.Console.PrintWarning( + "A finite mesh without a link to a Shape was given. " + "Happen on pure mesh objects. " + "Not all methods do work without this link.\n" + ) + # ATM only used in meshtools.get_femelement_direction1D_set + # TODO somehow this is not smart, rare meshes might be used often + self.femmesh = self.mesh_object.FemMesh + else: + FreeCAD.Console.PrintWarning( + "No finite element mesh object was given to the writer class. " + "In rare cases this might not be an error. " + ) + self.femnodes_mesh = {} + self.femelement_table = {} + self.constraint_conflict_nodes = [] + self.femnodes_ele_table = {} + self.femelements_edges_only = [] + self.femelements_faces_only = [] + self.femelement_volumes_table = {} + self.femelement_faces_table = {} + self.femelement_edges_table = {} + self.femelement_count_test = True + + # ******************************************************************************************** + # ******************************************************************************************** + # use set for node sets to be sure all nodes are unique + # use sorted to be sure the order is the same on different runs + # be aware a sorted set returns a list, because set are not sorted by default + # - done in return value of meshtools.get_femnodes_by_femobj_with_references + # TODO FIXME might be appropriate for element sets and surfaceface sets too + + # ******************************************************************************************** + # ******************************************************************************************** + # get all known sets + def get_mesh_sets(self): + FreeCAD.Console.PrintMessage( + "!!!!!!!!!!!!!!!!!!!!!!!!! MeshSetsGetter class :-) !!!!!!!!!!!!!!!!!!!!!!!!!\n" + "Get mesh data for " + "node sets (groups), surface sets (groups) and element sets (groups)\n" + ) + + # materials and element geometry element sets getter + self.get_element_sets_material_and_femelement_geometry() + + # constraints element sets getter + self.get_constraints_centrif_elements() + + # constraints node sets getter + self.get_constraints_fixed_nodes() + self.get_constraints_displacement_nodes() + self.get_constraints_planerotation_nodes() + + # constraints surface sets getter + self.get_constraints_contact_faces() + self.get_constraints_tie_faces() + self.get_constraints_sectionprint_faces() + self.get_constraints_transform_nodes() + self.get_constraints_temperature_nodes() + + # constraints sets with constraint data + self.get_constraints_force_nodeloads() + self.get_constraints_pressure_faces() + self.get_constraints_heatflux_faces() + + # ******************************************************************************************** + # ******************************************************************************************** + # node sets + def get_constraints_fixed_nodes(self): + if not self.member.cons_fixed: + return + # get nodes + for femobj in self.member.cons_fixed: + # femobj --> dict, FreeCAD document object is femobj["Object"] + print_obj_info(femobj["Object"]) + femobj["Nodes"] = meshtools.get_femnodes_by_femobj_with_references( + self.femmesh, + femobj + ) + # add nodes to constraint_conflict_nodes, needed by constraint plane rotation + for node in femobj["Nodes"]: + self.constraint_conflict_nodes.append(node) + # if mixed mesh with solids the node set needs to be split + # because solid nodes do not have rotational degree of freedom + if ( + self.femmesh.Volumes + and ( + len(self.member.geos_shellthickness) > 0 + or len(self.member.geos_beamsection) > 0 + ) + ): + FreeCAD.Console.PrintMessage("We need to find the solid nodes.\n") + if not self.femelement_volumes_table: + self.femelement_volumes_table = meshtools.get_femelement_volumes_table( + self.femmesh + ) + for femobj in self.member.cons_fixed: + # femobj --> dict, FreeCAD document object is femobj["Object"] + nds_solid = [] + nds_faceedge = [] + for n in femobj["Nodes"]: + solid_node = False + for ve in self.femelement_volumes_table: + if n in self.femelement_volumes_table[ve]: + solid_node = True + nds_solid.append(n) + break + if not solid_node: + nds_faceedge.append(n) + femobj["NodesSolid"] = set(nds_solid) + femobj["NodesFaceEdge"] = set(nds_faceedge) + + def get_constraints_displacement_nodes(self): + if not self.member.cons_displacement: + return + # get nodes + for femobj in self.member.cons_displacement: + # femobj --> dict, FreeCAD document object is femobj["Object"] + print_obj_info(femobj["Object"]) + femobj["Nodes"] = meshtools.get_femnodes_by_femobj_with_references( + self.femmesh, + femobj + ) + # add nodes to constraint_conflict_nodes, needed by constraint plane rotation + for node in femobj["Nodes"]: + self.constraint_conflict_nodes.append(node) + + def get_constraints_planerotation_nodes(self): + if not self.member.cons_planerotation: + return + # get nodes + for femobj in self.member.cons_planerotation: + # femobj --> dict, FreeCAD document object is femobj["Object"] + print_obj_info(femobj["Object"]) + femobj["Nodes"] = meshtools.get_femnodes_by_femobj_with_references( + self.femmesh, + femobj + ) + + def get_constraints_transform_nodes(self): + if not self.member.cons_transform: + return + # get nodes + for femobj in self.member.cons_transform: + # femobj --> dict, FreeCAD document object is femobj["Object"] + print_obj_info(femobj["Object"]) + femobj["Nodes"] = meshtools.get_femnodes_by_femobj_with_references( + self.femmesh, + femobj + ) + + def get_constraints_temperature_nodes(self): + if not self.member.cons_temperature: + return + # get nodes + for femobj in self.member.cons_temperature: + # femobj --> dict, FreeCAD document object is femobj["Object"] + print_obj_info(femobj["Object"]) + femobj["Nodes"] = meshtools.get_femnodes_by_femobj_with_references( + self.femmesh, + femobj + ) + + def get_constraints_fluidsection_nodes(self): + if not self.member.geos_fluidsection: + return + # get nodes + for femobj in self.member.geos_fluidsection: + # femobj --> dict, FreeCAD document object is femobj["Object"] + print_obj_info(femobj["Object"]) + femobj["Nodes"] = meshtools.get_femnodes_by_femobj_with_references( + self.femmesh, + femobj + ) + + def get_constraints_force_nodeloads(self): + if not self.member.cons_force: + return + # check shape type of reference shape + for femobj in self.member.cons_force: + # femobj --> dict, FreeCAD document object is femobj["Object"] + print_obj_info(femobj["Object"], log=True) + if femobj["RefShapeType"] == "Vertex": + FreeCAD.Console.PrintLog( + " load on vertices --> The femelement_table " + "and femnodes_mesh are not needed for node load calculation.\n" + ) + elif femobj["RefShapeType"] == "Face" \ + and meshtools.is_solid_femmesh(self.femmesh) \ + and not meshtools.has_no_face_data(self.femmesh): + FreeCAD.Console.PrintLog( + " solid_mesh with face data --> The femelement_table is not " + "needed but the femnodes_mesh is needed for node load calculation.\n" + ) + if not self.femnodes_mesh: + self.femnodes_mesh = self.femmesh.Nodes + else: + FreeCAD.Console.PrintLog( + " mesh without needed data --> The femelement_table " + "and femnodes_mesh are not needed for node load calculation.\n" + ) + if not self.femnodes_mesh: + self.femnodes_mesh = self.femmesh.Nodes + if not self.femelement_table: + self.femelement_table = meshtools.get_femelement_table( + self.femmesh + ) + # get node loads + FreeCAD.Console.PrintLog( + " Finite element mesh nodes will be retrieved by searching " + "the appropriate nodes in the finite element mesh.\n" + ) + FreeCAD.Console.PrintLog( + " The appropriate finite element mesh node load values will " + "be calculated according to the finite element definition.\n" + ) + for femobj in self.member.cons_force: + # femobj --> dict, FreeCAD document object is femobj["Object"] + frc_obj = femobj["Object"] + print_obj_info(frc_obj) + if frc_obj.Force == 0: + FreeCAD.Console.PrintMessage(" Warning --> Force = 0\n") + if femobj["RefShapeType"] == "Vertex": # point load on vertices + femobj["NodeLoadTable"] = meshtools.get_force_obj_vertex_nodeload_table( + self.femmesh, + frc_obj + ) + elif femobj["RefShapeType"] == "Edge": # line load on edges + femobj["NodeLoadTable"] = meshtools.get_force_obj_edge_nodeload_table( + self.femmesh, + self.femelement_table, + self.femnodes_mesh, frc_obj + ) + elif femobj["RefShapeType"] == "Face": # area load on faces + femobj["NodeLoadTable"] = meshtools.get_force_obj_face_nodeload_table( + self.femmesh, + self.femelement_table, + self.femnodes_mesh, frc_obj + ) + + # ******************************************************************************************** + # ******************************************************************************************** + # faces sets + def get_constraints_pressure_faces(self): + if not self.member.cons_pressure: + return + # TODO see comments in get_constraints_force_nodeloads() + # it applies here too. Mhh it applies to all constraints ... + + """ + # deprecated version + # get the faces and face numbers + for femobj in self.member.cons_pressure: + # femobj --> dict, FreeCAD document object is femobj["Object"] + femobj["PressureFaces"] = meshtools.get_pressure_obj_faces_depreciated( + self.femmesh, + femobj + ) + # print(femobj["PressureFaces"]) + """ + + if not self.femnodes_mesh: + self.femnodes_mesh = self.femmesh.Nodes + if not self.femelement_table: + self.femelement_table = meshtools.get_femelement_table(self.femmesh) + if not self.femnodes_ele_table: + self.femnodes_ele_table = meshtools.get_femnodes_ele_table( + self.femnodes_mesh, + self.femelement_table + ) + + for femobj in self.member.cons_pressure: + # femobj --> dict, FreeCAD document object is femobj["Object"] + print_obj_info(femobj["Object"]) + pressure_faces = meshtools.get_pressure_obj_faces( + self.femmesh, + self.femelement_table, + self.femnodes_ele_table, femobj + ) + # the data model is for compatibility reason with deprecated version + # get_pressure_obj_faces_depreciated returns the face ids in a tuple per ref_shape + # some_string was the reference_shape_element_string in deprecated method + # [(some_string, [ele_id, ele_face_id], [ele_id, ele_face_id], ...])] + some_string = "{}: face load".format(femobj["Object"].Name) + femobj["PressureFaces"] = [(some_string, pressure_faces)] + FreeCAD.Console.PrintLog("{}\n".format(femobj["PressureFaces"])) + + def get_constraints_contact_faces(self): + if not self.member.cons_contact: + return + if not self.femnodes_mesh: + self.femnodes_mesh = self.femmesh.Nodes + if not self.femelement_table: + self.femelement_table = meshtools.get_femelement_table(self.femmesh) + if not self.femnodes_ele_table: + self.femnodes_ele_table = meshtools.get_femnodes_ele_table( + self.femnodes_mesh, + self.femelement_table + ) + + for femobj in self.member.cons_contact: + # femobj --> dict, FreeCAD document object is femobj["Object"] + print_obj_info(femobj["Object"]) + contact_slave_faces, contact_master_faces = meshtools.get_contact_obj_faces( + self.femmesh, + self.femelement_table, + self.femnodes_ele_table, femobj + ) + # [ele_id, ele_face_id], [ele_id, ele_face_id], ...] + # whereas the ele_face_id might be ccx specific + femobj["ContactSlaveFaces"] = contact_slave_faces + femobj["ContactMasterFaces"] = contact_master_faces + # FreeCAD.Console.PrintLog("{}\n".format(femobj["ContactSlaveFaces"])) + # FreeCAD.Console.PrintLog("{}\n".format(femobj["ContactMasterFaces"])) + + # information in the regard of element faces constraints + # forum post: https://forum.freecadweb.org/viewtopic.php?f=18&t=42783&p=370286#p366723 + # contact: master and slave could be the same face: rubber of a damper + # tie: master and slave have to be separate faces AFA UR_ K + # section print: only the element faces of solid elements + # from one side of the geometric face are needed + + def get_constraints_tie_faces(self): + if not self.member.cons_tie: + return + if not self.femnodes_mesh: + self.femnodes_mesh = self.femmesh.Nodes + if not self.femelement_table: + self.femelement_table = meshtools.get_femelement_table(self.femmesh) + if not self.femnodes_ele_table: + self.femnodes_ele_table = meshtools.get_femnodes_ele_table( + self.femnodes_mesh, + self.femelement_table + ) + + for femobj in self.member.cons_tie: + # femobj --> dict, FreeCAD document object is femobj["Object"] + print_obj_info(femobj["Object"]) + slave_faces, master_faces = meshtools.get_tie_obj_faces( + self.femmesh, + self.femelement_table, + self.femnodes_ele_table, femobj + ) + # [ele_id, ele_face_id], [ele_id, ele_face_id], ...] + # whereas the ele_face_id might be ccx specific + femobj["TieSlaveFaces"] = slave_faces + femobj["TieMasterFaces"] = master_faces + # FreeCAD.Console.PrintLog("{}\n".format(femobj["ContactSlaveFaces"])) + # FreeCAD.Console.PrintLog("{}\n".format(femobj["ContactMasterFaces"])) + + def get_constraints_sectionprint_faces(self): + if not self.member.cons_sectionprint: + return + # TODO: use meshtools to get the surfaces + # see constraint contact or constraint tie + for femobj in self.member.cons_sectionprint: + # femobj --> dict, FreeCAD document object is femobj["Object"] + sectionprint_obj = femobj["Object"] + if len(sectionprint_obj.References) > 1: + FreeCAD.Console.PrintError( + "Only one reference shape allowed for a section print " + "but {} found: {}\n" + .format(len(sectionprint_obj.References), sectionprint_obj.References) + ) + for o, elem_tup in sectionprint_obj.References: + for elem in elem_tup: + # there should only be one reference for each section print object + # in the gui this is checked + ref_shape = o.Shape.getElement(elem) + if ref_shape.ShapeType == "Face": + v = self.mesh_object.FemMesh.getccxVolumesByFace(ref_shape) + if len(v) > 0: + femobj["SectionPrintFaces"] = v + # volume elements found + FreeCAD.Console.PrintLog( + "{}, surface {}, {} touching volume elements found\n" + .format(sectionprint_obj.Label, sectionprint_obj.Name, len(v)) + ) + else: + # no volume elements found, shell elements not allowed + FreeCAD.Console.PrintError( + "{}, surface {}, Error: " + "No volume elements found!\n" + .format(sectionprint_obj.Label, sectionprint_obj.Name) + ) + else: + # in Gui only Faces can be added + FreeCAD.Console.PrintError( + "Wrong reference shape type for {} " + "Only Faces are allowed, but a {} was found.\n" + .format(sectionprint_obj.Name, ref_shape.ShapeType) + ) + + def get_constraints_heatflux_faces(self): + if not self.member.cons_heatflux: + return + # TODO: use meshtools to get the surfaces (or move to mesh tools) + # see constraint contact or constraint tie and constraint force + # heatflux_obj_face_table: see force_obj_node_load_table + # [ + # ("refshape_name:elemname", face_table), + # ..., + # ("refshape_name:elemname", face_table) + # ] + for femobj in self.member.cons_heatflux: + # femobj --> dict, FreeCAD document object is femobj["Object"] + heatflux_obj = femobj["Object"] + femobj["HeatFluxFaceTable"] = [] + for o, elem_tup in heatflux_obj.References: + for elem in elem_tup: + ho = o.Shape.getElement(elem) + if ho.ShapeType == "Face": + elem_info = "{}:{}".format(o.Name, elem) + face_table = self.mesh_object.FemMesh.getccxVolumesByFace(ho) + femobj["HeatFluxFaceTable"].append((elem_info, face_table)) + + # ******************************************************************************************** + # ******************************************************************************************** + # element sets constraints + def get_constraints_centrif_elements(self): + # get element ids and write them into the femobj + if not self.member.cons_centrif: + return + if ( + len(self.member.cons_centrif) == 1 + and not self.member.cons_centrif[0]["Object"].References + ): + self.member.cons_centrif[0]["FEMElements"] = self.ccx_evolumes + else: + self.get_solid_element_sets(self.member.cons_centrif) + + # ******************************************************************************************** + # ******************************************************************************************** + # element sets material and element geometry + def get_solid_element_sets(self, femobjs): + # get element ids and write them into the femobj + all_found = False + if self.femmesh.GroupCount: + all_found = meshtools.get_femelement_sets_from_group_data( + self.femmesh, + femobjs + ) + FreeCAD.Console.PrintMessage(all_found) + FreeCAD.Console.PrintMessage("\n") + if all_found is False: + if not self.femelement_table: + self.femelement_table = meshtools.get_femelement_table(self.femmesh) + # we're going to use the binary search for get_femelements_by_femnodes() + # thus we need the parameter values self.femnodes_ele_table + if not self.femnodes_mesh: + self.femnodes_mesh = self.femmesh.Nodes + if not self.femnodes_ele_table: + self.femnodes_ele_table = meshtools.get_femnodes_ele_table( + self.femnodes_mesh, + self.femelement_table + ) + control = meshtools.get_femelement_sets( + self.femmesh, + self.femelement_table, + femobjs, + self.femnodes_ele_table + ) + # we only need to set it, if it is still True + if (self.femelement_count_test is True) and (control is False): + self.femelement_count_test = False + + def get_element_geometry2D_elements(self): + # get element ids and write them into the objects + FreeCAD.Console.PrintMessage("Shell thicknesses\n") + if not self.femelement_faces_table: + self.femelement_faces_table = meshtools.get_femelement_faces_table( + self.femmesh + ) + meshtools.get_femelement_sets( + self.femmesh, + self.femelement_faces_table, + self.member.geos_shellthickness + ) + + def get_element_geometry1D_elements(self): + # get element ids and write them into the objects + FreeCAD.Console.PrintMessage("Beam sections\n") + if not self.femelement_edges_table: + self.femelement_edges_table = meshtools.get_femelement_edges_table( + self.femmesh + ) + meshtools.get_femelement_sets( + self.femmesh, + self.femelement_edges_table, + self.member.geos_beamsection + ) + + def get_element_rotation1D_elements(self): + # get for each geometry edge direction the element ids and rotation norma + FreeCAD.Console.PrintMessage("Beam rotations\n") + if not self.femelement_edges_table: + self.femelement_edges_table = meshtools.get_femelement_edges_table( + self.femmesh + ) + meshtools.get_femelement_direction1D_set( + self.femmesh, + self.femelement_edges_table, + self.member.geos_beamrotation, + self.theshape + ) + + def get_element_fluid1D_elements(self): + # get element ids and write them into the objects + FreeCAD.Console.PrintMessage("Fluid sections\n") + if not self.femelement_edges_table: + self.femelement_edges_table = meshtools.get_femelement_edges_table( + self.femmesh + ) + meshtools.get_femelement_sets( + self.femmesh, + self.femelement_edges_table, + self.member.geos_fluidsection + ) + + def get_material_elements(self): + # it only works if either Volumes or Shellthicknesses or Beamsections + # are in the material objects, it means it does not work + # for mixed meshes and multiple materials, this is checked in check_prerequisites + # the femelement_table is only calculated for + # the highest dimension in get_femelement_table + FreeCAD.Console.PrintMessage("Materials\n") + if self.femmesh.Volumes: + # we only could do this for volumes + # if a mesh contains volumes we're going to use them in the analysis + # but a mesh could contain + # the element faces of the volumes as faces + # and the edges of the faces as edges + # there we have to check of some geometric objects + # get element ids and write them into the femobj + self.get_solid_element_sets(self.member.mats_linear) + if self.member.geos_shellthickness: + if not self.femelement_faces_table: + self.femelement_faces_table = meshtools.get_femelement_faces_table( + self.femmesh + ) + meshtools.get_femelement_sets( + self.femmesh, + self.femelement_faces_table, + self.member.mats_linear + ) + if self.member.geos_beamsection or self.member.geos_fluidsection: + if not self.femelement_edges_table: + self.femelement_edges_table = meshtools.get_femelement_edges_table( + self.femmesh + ) + meshtools.get_femelement_sets( + self.femmesh, + self.femelement_edges_table, + self.member.mats_linear + ) + + def get_element_sets_material_and_femelement_geometry(self): + if not self.member.mats_linear: + return + # in any case if we have beams, we're going to need the element ids for the rotation elsets + if self.member.geos_beamsection: + # we will need to split the beam even for one beamobj + # because no beam in z-direction can be used in ccx without a special adjustment + # thus they need an own ccx_elset + self.get_element_rotation1D_elements() + + # get the element ids for face and edge elements and write them into the objects + if len(self.member.geos_shellthickness) > 1: + self.get_element_geometry2D_elements() + if len(self.member.geos_beamsection) > 1: + self.get_element_geometry1D_elements() + if len(self.member.geos_fluidsection) > 1: + self.get_element_fluid1D_elements() + + # get the element ids for material objects and write them into the material object + if len(self.member.mats_linear) > 1: + self.get_material_elements() + + # create the ccx_elsets + if len(self.member.mats_linear) == 1: + if self.femmesh.Volumes: + print("here1") + # we only could do this for volumes, if a mesh contains volumes + # we're going to use them in the analysis + # but a mesh could contain the element faces of the volumes as faces + # and the edges of the faces as edges + # there we have to check for some geometric objects + self.get_ccx_elsets_single_mat_solid() + if len(self.member.geos_shellthickness) == 1: + self.get_ccx_elsets_single_mat_single_shell() + elif len(self.member.geos_shellthickness) > 1: + self.get_ccx_elsets_single_mat_multiple_shell() + if len(self.member.geos_beamsection) == 1: + self.get_ccx_elsets_single_mat_single_beam() + elif len(self.member.geos_beamsection) > 1: + self.get_ccx_elsets_single_mat_multiple_beam() + if len(self.member.geos_fluidsection) == 1: + self.get_ccx_elsets_single_mat_single_fluid() + elif len(self.member.geos_fluidsection) > 1: + self.get_ccx_elsets_single_mat_multiple_fluid() + elif len(self.member.mats_linear) > 1: + if self.femmesh.Volumes: + # we only could do this for volumes, if a mseh contains volumes + # we're going to use them in the analysis + # but a mesh could contain the element faces of the volumes as faces + # and the edges of the faces as edges + # there we have to check for some geometric objects + # volume is a bit special + # because retrieving ids from group mesh data is implemented + self.get_ccx_elsets_multiple_mat_solid() + if len(self.member.geos_shellthickness) == 1: + self.get_ccx_elsets_multiple_mat_single_shell() + elif len(self.member.geos_shellthickness) > 1: + self.get_ccx_elsets_multiple_mat_multiple_shell() + if len(self.member.geos_beamsection) == 1: + self.get_ccx_elsets_multiple_mat_single_beam() + elif len(self.member.geos_beamsection) > 1: + self.get_ccx_elsets_multiple_mat_multiple_beam() + if len(self.member.geos_fluidsection) == 1: + self.get_ccx_elsets_multiple_mat_single_fluid() + elif len(self.member.geos_fluidsection) > 1: + self.get_ccx_elsets_multiple_mat_multiple_fluid() + + # self.mat_geo_sets = [ { + # "ccx_elset" : [e1, e2, e3, ... , en] or elements set name strings + # "ccx_elset_name" : "ccx_identifier_elset" + # "mat_obj_name" : "mat_obj.Name" + # "ccx_mat_name" : "mat_obj.Material["Name"]" !!! not unique !!! + # "beamsection_obj" : "beamsection_obj" if exists + # "fluidsection_obj" : "fluidsection_obj" if exists + # "shellthickness_obj" : shellthickness_obj" if exists + # "beam_normal" : normal vector for beams only + # }, + # {}, ... , {} ] + + # beam + # TODO support multiple beamrotations + # we do not need any more any data from the rotation document object, + # thus we do not need to save the rotation document object name in the else + def get_ccx_elsets_single_mat_single_beam(self): + mat_obj = self.member.mats_linear[0]["Object"] + beamsec_obj = self.member.geos_beamsection[0]["Object"] + beamrot_data = self.member.geos_beamrotation[0] + for i, beamdirection in enumerate(beamrot_data["FEMRotations1D"]): + # ID's for this direction + elset_data = beamdirection["ids"] + names = [ + {"short": "M0"}, + {"short": "B0"}, + {"short": beamrot_data["ShortName"]}, + {"short": "D" + str(i)} + ] + ccx_elset = {} + ccx_elset["ccx_elset"] = elset_data + ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) + ccx_elset["mat_obj_name"] = mat_obj.Name + ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] + ccx_elset["beamsection_obj"] = beamsec_obj + # normal for this direction + ccx_elset["beam_normal"] = beamdirection["normal"] + self.mat_geo_sets.append(ccx_elset) + + def get_ccx_elsets_single_mat_multiple_beam(self): + mat_obj = self.member.mats_linear[0]["Object"] + beamrot_data = self.member.geos_beamrotation[0] + for beamsec_data in self.member.geos_beamsection: + beamsec_obj = beamsec_data["Object"] + beamsec_ids = set(beamsec_data["FEMElements"]) + for i, beamdirection in enumerate(beamrot_data["FEMRotations1D"]): + beamdir_ids = set(beamdirection["ids"]) + # empty intersection sets possible + elset_data = list(sorted(beamsec_ids.intersection(beamdir_ids))) + if elset_data: + names = [ + {"short": "M0"}, + {"short": beamsec_data["ShortName"]}, + {"short": beamrot_data["ShortName"]}, + {"short": "D" + str(i)} + ] + ccx_elset = {} + ccx_elset["ccx_elset"] = elset_data + ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) + ccx_elset["mat_obj_name"] = mat_obj.Name + ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] + ccx_elset["beamsection_obj"] = beamsec_obj + # normal for this direction + ccx_elset["beam_normal"] = beamdirection["normal"] + self.mat_geo_sets.append(ccx_elset) + + def get_ccx_elsets_multiple_mat_single_beam(self): + beamsec_obj = self.member.geos_beamsection[0]["Object"] + beamrot_data = self.member.geos_beamrotation[0] + for mat_data in self.member.mats_linear: + mat_obj = mat_data["Object"] + mat_ids = set(mat_data["FEMElements"]) + for i, beamdirection in enumerate(beamrot_data["FEMRotations1D"]): + beamdir_ids = set(beamdirection["ids"]) + elset_data = list(sorted(mat_ids.intersection(beamdir_ids))) + if elset_data: + names = [ + {"short": mat_data["ShortName"]}, + {"short": "B0"}, + {"short": beamrot_data["ShortName"]}, + {"short": "D" + str(i)} + ] + ccx_elset = {} + ccx_elset["ccx_elset"] = elset_data + ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) + ccx_elset["mat_obj_name"] = mat_obj.Name + ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] + ccx_elset["beamsection_obj"] = beamsec_obj + # normal for this direction + ccx_elset["beam_normal"] = beamdirection["normal"] + self.mat_geo_sets.append(ccx_elset) + + def get_ccx_elsets_multiple_mat_multiple_beam(self): + beamrot_data = self.member.geos_beamrotation[0] + for beamsec_data in self.member.geos_beamsection: + beamsec_obj = beamsec_data["Object"] + beamsec_ids = set(beamsec_data["FEMElements"]) + for mat_data in self.member.mats_linear: + mat_obj = mat_data["Object"] + mat_ids = set(mat_data["FEMElements"]) + for i, beamdirection in enumerate(beamrot_data["FEMRotations1D"]): + beamdir_ids = set(beamdirection["ids"]) + # empty intersection sets possible + elset_data = list(sorted( + beamsec_ids.intersection(mat_ids).intersection(beamdir_ids) + )) + if elset_data: + names = [ + {"short": mat_data["ShortName"]}, + {"short": beamsec_data["ShortName"]}, + {"short": beamrot_data["ShortName"]}, + {"short": "D" + str(i)} + ] + ccx_elset = {} + ccx_elset["ccx_elset"] = elset_data + ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) + ccx_elset["mat_obj_name"] = mat_obj.Name + ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] + ccx_elset["beamsection_obj"] = beamsec_obj + # normal for this direction + ccx_elset["beam_normal"] = beamdirection["normal"] + self.mat_geo_sets.append(ccx_elset) + + # fluid + def get_ccx_elsets_single_mat_single_fluid(self): + mat_obj = self.member.mats_linear[0]["Object"] + fluidsec_obj = self.member.geos_fluidsection[0]["Object"] + elset_data = self.ccx_eedges + names = [{"short": "M0"}, {"short": "F0"}] + ccx_elset = {} + ccx_elset["ccx_elset"] = elset_data + ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) + ccx_elset["mat_obj_name"] = mat_obj.Name + ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] + ccx_elset["fluidsection_obj"] = fluidsec_obj + self.mat_geo_sets.append(ccx_elset) + + def get_ccx_elsets_single_mat_multiple_fluid(self): + mat_obj = self.member.mats_linear[0]["Object"] + for fluidsec_data in self.member.geos_fluidsection: + fluidsec_obj = fluidsec_data["Object"] + elset_data = fluidsec_data["FEMElements"] + names = [{"short": "M0"}, {"short": fluidsec_data["ShortName"]}] + ccx_elset = {} + ccx_elset["ccx_elset"] = elset_data + ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) + ccx_elset["mat_obj_name"] = mat_obj.Name + ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] + ccx_elset["fluidsection_obj"] = fluidsec_obj + self.mat_geo_sets.append(ccx_elset) + + def get_ccx_elsets_multiple_mat_single_fluid(self): + fluidsec_obj = self.member.geos_fluidsection[0]["Object"] + for mat_data in self.member.mats_linear: + mat_obj = mat_data["Object"] + elset_data = mat_data["FEMElements"] + names = [{"short": mat_data["ShortName"]}, {"short": "F0"}] + ccx_elset = {} + ccx_elset["ccx_elset"] = elset_data + ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) + ccx_elset["mat_obj_name"] = mat_obj.Name + ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] + ccx_elset["fluidsection_obj"] = fluidsec_obj + self.mat_geo_sets.append(ccx_elset) + + def get_ccx_elsets_multiple_mat_multiple_fluid(self): + for fluidsec_data in self.member.geos_fluidsection: + fluidsec_obj = fluidsec_data["Object"] + for mat_data in self.member.mats_linear: + mat_obj = mat_data["Object"] + fluidsec_ids = set(fluidsec_data["FEMElements"]) + mat_ids = set(mat_data["FEMElements"]) + # empty intersection sets possible + elset_data = list(sorted(fluidsec_ids.intersection(mat_ids))) + if elset_data: + names = [ + {"short": mat_data["ShortName"]}, + {"short": fluidsec_data["ShortName"]} + ] + ccx_elset = {} + ccx_elset["ccx_elset"] = elset_data + ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) + ccx_elset["mat_obj_name"] = mat_obj.Name + ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] + ccx_elset["fluidsection_obj"] = fluidsec_obj + self.mat_geo_sets.append(ccx_elset) + + # shell + def get_ccx_elsets_single_mat_single_shell(self): + mat_obj = self.member.mats_linear[0]["Object"] + shellth_obj = self.member.geos_shellthickness[0]["Object"] + elset_data = self.ccx_efaces + names = [ + {"long": mat_obj.Name, "short": "M0"}, + {"long": shellth_obj.Name, "short": "S0"} + ] + ccx_elset = {} + ccx_elset["ccx_elset"] = elset_data + ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + ccx_elset["mat_obj_name"] = mat_obj.Name + ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] + ccx_elset["shellthickness_obj"] = shellth_obj + self.mat_geo_sets.append(ccx_elset) + + def get_ccx_elsets_single_mat_multiple_shell(self): + mat_obj = self.member.mats_linear[0]["Object"] + for shellth_data in self.member.geos_shellthickness: + shellth_obj = shellth_data["Object"] + elset_data = shellth_data["FEMElements"] + names = [ + {"long": mat_obj.Name, "short": "M0"}, + {"long": shellth_obj.Name, "short": shellth_data["ShortName"]} + ] + ccx_elset = {} + ccx_elset["ccx_elset"] = elset_data + ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + ccx_elset["mat_obj_name"] = mat_obj.Name + ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] + ccx_elset["shellthickness_obj"] = shellth_obj + self.mat_geo_sets.append(ccx_elset) + + def get_ccx_elsets_multiple_mat_single_shell(self): + shellth_obj = self.member.geos_shellthickness[0]["Object"] + for mat_data in self.member.mats_linear: + mat_obj = mat_data["Object"] + elset_data = mat_data["FEMElements"] + names = [ + {"long": mat_obj.Name, "short": mat_data["ShortName"]}, + {"long": shellth_obj.Name, "short": "S0"} + ] + ccx_elset = {} + ccx_elset["ccx_elset"] = elset_data + ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + ccx_elset["mat_obj_name"] = mat_obj.Name + ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] + ccx_elset["shellthickness_obj"] = shellth_obj + self.mat_geo_sets.append(ccx_elset) + + def get_ccx_elsets_multiple_mat_multiple_shell(self): + for shellth_data in self.member.geos_shellthickness: + shellth_obj = shellth_data["Object"] + for mat_data in self.member.mats_linear: + mat_obj = mat_data["Object"] + shellth_ids = set(shellth_data["FEMElements"]) + mat_ids = set(mat_data["FEMElements"]) + # empty intersection sets possible + elset_data = list(sorted(shellth_ids.intersection(mat_ids))) + if elset_data: + names = [ + {"long": mat_obj.Name, "short": mat_data["ShortName"]}, + {"long": shellth_obj.Name, "short": shellth_data["ShortName"]} + ] + ccx_elset = {} + ccx_elset["ccx_elset"] = elset_data + ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + ccx_elset["mat_obj_name"] = mat_obj.Name + ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] + ccx_elset["shellthickness_obj"] = shellth_obj + self.mat_geo_sets.append(ccx_elset) + + # solid + def get_ccx_elsets_single_mat_solid(self): + print("here2") + mat_obj = self.member.mats_linear[0]["Object"] + elset_data = self.ccx_evolumes + names = [ + {"long": mat_obj.Name, "short": "M0"}, + {"long": "Solid", "short": "Solid"} + ] + ccx_elset = {} + ccx_elset["ccx_elset"] = elset_data + ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + ccx_elset["mat_obj_name"] = mat_obj.Name + ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] + self.mat_geo_sets.append(ccx_elset) + print(self.mat_geo_sets) + + def get_ccx_elsets_multiple_mat_solid(self): + for mat_data in self.member.mats_linear: + mat_obj = mat_data["Object"] + elset_data = mat_data["FEMElements"] + names = [ + {"long": mat_obj.Name, "short": mat_data["ShortName"]}, + {"long": "Solid", "short": "Solid"} + ] + ccx_elset = {} + ccx_elset["ccx_elset"] = elset_data + ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + ccx_elset["mat_obj_name"] = mat_obj.Name + ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] + self.mat_geo_sets.append(ccx_elset) + + +# ************************************************************************************************ +# Helpers + + +# ccx elset names: +# M .. Material +# B .. Beam +# R .. BeamRotation +# D .. Direction +# F .. Fluid +# S .. Shell, +# TODO write comment into input file to elset ids and elset attributes + + +def get_ccx_elset_name_standard(names): + # standard max length = 80 + ccx_elset_name = "" + for name in names: + ccx_elset_name += name["long"] + if len(ccx_elset_name) < 81: + return ccx_elset_name + else: + ccx_elset_name = "" + for name in names: + ccx_elset_name += name["short"] + if len(ccx_elset_name) < 81: + return ccx_elset_name + else: + error = ( + "FEM: Trouble in ccx input file, because an " + "elset name is longer than 80 character! {}\n" + .format(ccx_elset_name) + ) + raise Exception(error) + + +def get_ccx_elset_name_short(names): + # restricted max length = 20 (beam elsets) + ccx_elset_name = "" + for name in names: + ccx_elset_name += name["short"] + if len(ccx_elset_name) < 21: + return ccx_elset_name + else: + error = ( + "FEM: Trouble in ccx input file, because an" + "beam elset name is longer than 20 characters! {}\n" + .format(ccx_elset_name) + ) + raise Exception(error) + + +def print_obj_info(obj, log=False): + if log is False: + FreeCAD.Console.PrintMessage("{}:\n".format(obj.Label)) + FreeCAD.Console.PrintMessage( + " Type: {}, Name: {}\n".format(type_of_obj(obj), obj.Name) + ) + else: + FreeCAD.Console.PrintLog("{}:\n".format(obj.Label)) + FreeCAD.Console.PrintLog( + " Type: {}, Name: {}\n".format(type_of_obj(obj), obj.Name) + ) + +## @} diff --git a/src/Mod/Fem/femsolver/calculix/writer.py b/src/Mod/Fem/femsolver/calculix/writer.py index b81572d0c9..2dccd59542 100644 --- a/src/Mod/Fem/femsolver/calculix/writer.py +++ b/src/Mod/Fem/femsolver/calculix/writer.py @@ -110,7 +110,8 @@ class FemInputWriterCcx(writerbase.FemInputWriter): solver_obj, mesh_obj, member, - dir_name=None + dir_name=None, + mat_geo_sets=None ): writerbase.FemInputWriter.__init__( self, @@ -118,7 +119,8 @@ class FemInputWriterCcx(writerbase.FemInputWriter): solver_obj, mesh_obj, member, - dir_name + dir_name, + mat_geo_sets ) self.mesh_name = self.mesh_object.Name self.file_name = join(self.dir_name, self.mesh_name + ".inp") @@ -132,7 +134,8 @@ class FemInputWriterCcx(writerbase.FemInputWriter): FreeCAD.Console.PrintMessage("Get mesh sets.\n") time_start = time.process_time() - self.get_mesh_sets() + if not self.ccx_elsets: + self.get_mesh_sets() time_getsets = time.process_time() FreeCAD.Console.PrintMessage( @@ -156,44 +159,44 @@ class FemInputWriterCcx(writerbase.FemInputWriter): inpfile = con_fluidsection.handle_fluidsection_liquid_inlet_outlet(inpfile, self) # element sets constraints - self.write_constraints_meshsets(inpfile, self.centrif_objects, con_centrif) + self.write_constraints_meshsets(inpfile, self.member.cons_centrif, con_centrif) # node sets - self.write_constraints_meshsets(inpfile, self.fixed_objects, con_fixed) - self.write_constraints_meshsets(inpfile, self.displacement_objects, con_displacement) - self.write_constraints_meshsets(inpfile, self.planerotation_objects, con_planerotation) - self.write_constraints_meshsets(inpfile, self.transform_objects, con_transform) - self.write_constraints_meshsets(inpfile, self.temperature_objects, con_temperature) + self.write_constraints_meshsets(inpfile, self.member.cons_fixed, con_fixed) + self.write_constraints_meshsets(inpfile, self.member.cons_displacement, con_displacement) + self.write_constraints_meshsets(inpfile, self.member.cons_planerotation, con_planerotation) + self.write_constraints_meshsets(inpfile, self.member.cons_transform, con_transform) + self.write_constraints_meshsets(inpfile, self.member.cons_temperature, con_temperature) # surface sets - self.write_constraints_meshsets(inpfile, self.contact_objects, con_contact) - self.write_constraints_meshsets(inpfile, self.tie_objects, con_tie) - self.write_constraints_meshsets(inpfile, self.sectionprint_objects, con_sectionprint) + self.write_constraints_meshsets(inpfile, self.member.cons_contact, con_contact) + self.write_constraints_meshsets(inpfile, self.member.cons_tie, con_tie) + self.write_constraints_meshsets(inpfile, self.member.cons_sectionprint, con_sectionprint) # materials and fem element types write_femelement_material.write_femelement_material(inpfile, self) - self.write_constraints_propdata(inpfile, self.initialtemperature_objects, con_initialtemp) + self.write_constraints_propdata(inpfile, self.member.cons_initialtemperature, con_initialtemp) write_femelement_geometry.write_femelement_geometry(inpfile, self) # constraints independent from steps - self.write_constraints_propdata(inpfile, self.planerotation_objects, con_planerotation) - self.write_constraints_propdata(inpfile, self.contact_objects, con_contact) - self.write_constraints_propdata(inpfile, self.tie_objects, con_tie) - self.write_constraints_propdata(inpfile, self.transform_objects, con_transform) + self.write_constraints_propdata(inpfile, self.member.cons_planerotation, con_planerotation) + self.write_constraints_propdata(inpfile, self.member.cons_contact, con_contact) + self.write_constraints_propdata(inpfile, self.member.cons_tie, con_tie) + self.write_constraints_propdata(inpfile, self.member.cons_transform, con_transform) # step equation write_step_equation.write_step_equation(inpfile, self) # constraints dependent from steps - self.write_constraints_propdata(inpfile, self.fixed_objects, con_fixed) - self.write_constraints_propdata(inpfile, self.displacement_objects, con_displacement) - self.write_constraints_propdata(inpfile, self.sectionprint_objects, con_sectionprint) - self.write_constraints_propdata(inpfile, self.selfweight_objects, con_selfweight) - self.write_constraints_propdata(inpfile, self.centrif_objects, con_centrif) - self.write_constraints_meshsets(inpfile, self.force_objects, con_force) - self.write_constraints_meshsets(inpfile, self.pressure_objects, con_pressure) - self.write_constraints_propdata(inpfile, self.temperature_objects, con_temperature) - self.write_constraints_meshsets(inpfile, self.heatflux_objects, con_heatflux) + self.write_constraints_propdata(inpfile, self.member.cons_fixed, con_fixed) + self.write_constraints_propdata(inpfile, self.member.cons_displacement, con_displacement) + self.write_constraints_propdata(inpfile, self.member.cons_sectionprint, con_sectionprint) + self.write_constraints_propdata(inpfile, self.member.cons_selfweight, con_selfweight) + self.write_constraints_propdata(inpfile, self.member.cons_centrif, con_centrif) + self.write_constraints_meshsets(inpfile, self.member.cons_force, con_force) + self.write_constraints_meshsets(inpfile, self.member.cons_pressure, con_pressure) + self.write_constraints_propdata(inpfile, self.member.cons_temperature, con_temperature) + self.write_constraints_meshsets(inpfile, self.member.cons_heatflux, con_heatflux) con_fluidsection.write_constraints_fluidsection(inpfile, self) # output and step end diff --git a/src/Mod/Fem/femsolver/writerbase.py b/src/Mod/Fem/femsolver/writerbase.py index 74e1d7104d..e18f48ed46 100644 --- a/src/Mod/Fem/femsolver/writerbase.py +++ b/src/Mod/Fem/femsolver/writerbase.py @@ -33,8 +33,7 @@ from os.path import join import FreeCAD -from femmesh import meshtools -from femtools.femutils import type_of_obj +from femmesh import meshsetsgetter class FemInputWriter(): @@ -44,14 +43,76 @@ class FemInputWriter(): solver_obj, mesh_obj, member, - dir_name=None + dir_name=None, + mat_geo_sets=None ): # class attributes from parameter values self.analysis = analysis_obj self.solver_obj = solver_obj - self.analysis_type = self.solver_obj.AnalysisType self.mesh_object = mesh_obj + self.member = member + # mor attributes + self.analysis_type = self.solver_obj.AnalysisType self.document = self.analysis.Document + # working dir + self.dir_name = dir_name + # if dir_name was not given or if it exists but isn't empty: create a temporary dir + # Purpose: makes sure the analysis can be run even on wired situation + if not dir_name: + FreeCAD.Console.PrintWarning( + "Error: FemInputWriter has no working_dir --> " + "we are going to make a temporary one!\n" + ) + self.dir_name = self.document.TransientDir.replace( + "\\", "/" + ) + "/FemAnl_" + analysis_obj.Uid[-4:] + if not os.path.isdir(self.dir_name): + os.mkdir(self.dir_name) + + # new class attributes + self.fc_ver = FreeCAD.Version() + self.ccx_nall = "Nall" + self.ccx_eall = "Eall" + self.ccx_evolumes = "Evolumes" + self.ccx_efaces = "Efaces" + self.ccx_eedges = "Eedges" + if mat_geo_sets is not None: + self.ccx_elsets = mat_geo_sets + else: + self.ccx_elsets = [] + if self.mesh_object: + if hasattr(self.mesh_object, "Shape"): + self.theshape = self.mesh_object.Shape + elif hasattr(self.mesh_object, "Part"): + self.theshape = self.mesh_object.Part + else: + FreeCAD.Console.PrintWarning( + "A finite mesh without a link to a Shape was given. " + "Happen on pure mesh objects. " + "Not all methods do work without this link.\n" + ) + # ATM only used in meshtools.get_femelement_direction1D_set + # TODO somehow this is not smart, rare meshes might be used often + self.femmesh = self.mesh_object.FemMesh + else: + FreeCAD.Console.PrintWarning( + "No finite element mesh object was given to the writer class. " + "In rare cases this might not be an error. " + ) + + # deprecated, leave for compatibility reasons + # if these are calculated here they are calculated twice :-( + self.femnodes_mesh = {} + self.femelement_table = {} + self.constraint_conflict_nodes = [] + self.femnodes_ele_table = {} + self.femelements_edges_only = [] + self.femelements_faces_only = [] + self.femelement_volumes_table = {} + self.femelement_faces_table = {} + self.femelement_edges_table = {} + self.femelement_count_test = True + # materials self.material_objects = member.mats_linear self.material_nonlinear_objects = member.mats_nonlinear @@ -75,58 +136,14 @@ class FemInputWriter(): self.temperature_objects = member.cons_temperature self.tie_objects = member.cons_tie self.transform_objects = member.cons_transform - # working dir - self.dir_name = dir_name - # if dir_name was not given or if it exists but isn't empty: create a temporary dir - # Purpose: makes sure the analysis can be run even on wired situation - if not dir_name: - FreeCAD.Console.PrintWarning( - "Error: FemInputWriter has no working_dir --> " - "we are going to make a temporary one!\n" - ) - self.dir_name = self.document.TransientDir.replace( - "\\", "/" - ) + "/FemAnl_" + analysis_obj.Uid[-4:] - if not os.path.isdir(self.dir_name): - os.mkdir(self.dir_name) - # new class attributes - self.fc_ver = FreeCAD.Version() - self.ccx_nall = "Nall" - self.ccx_eall = "Eall" - self.ccx_evolumes = "Evolumes" - self.ccx_efaces = "Efaces" - self.ccx_eedges = "Eedges" - self.ccx_elsets = [] - if self.mesh_object: - if hasattr(self.mesh_object, "Shape"): - self.theshape = self.mesh_object.Shape - elif hasattr(self.mesh_object, "Part"): - self.theshape = self.mesh_object.Part - else: - FreeCAD.Console.PrintWarning( - "A finite mesh without a link to a Shape was given. " - "Happen on pure mesh objects. " - "Not all methods do work without this link.\n" - ) - # ATM only used in meshtools.get_femelement_direction1D_set - # TODO somehow this is not smart, rare meshes might be used often - self.femmesh = self.mesh_object.FemMesh - else: - FreeCAD.Console.PrintWarning( - "No finite element mesh object was given to the writer class. " - "In rare cases this might not be an error. " - ) - self.femnodes_mesh = {} - self.femelement_table = {} - self.constraint_conflict_nodes = [] - self.femnodes_ele_table = {} - self.femelements_edges_only = [] - self.femelements_faces_only = [] - self.femelement_volumes_table = {} - self.femelement_faces_table = {} - self.femelement_edges_table = {} - self.femelement_count_test = True + # meshdatagetter, for compatibility, same with all getter methods + self.meshdatagetter = meshsetsgetter.MeshSetsGetter( + self.analysis, + self.solver_obj, + self.mesh_object, + self.member, + ) # ******************************************************************************************** # ******************************************************************************************** @@ -213,7 +230,6 @@ class FemInputWriter(): # - done in return value of meshtools.get_femnodes_by_femobj_with_references # TODO FIXME might be appropriate for element sets and surfaceface sets too - # ******************************************************************************************** # ******************************************************************************************** # get all known sets def get_mesh_sets(self): @@ -246,907 +262,49 @@ class FemInputWriter(): self.get_constraints_heatflux_faces() # ******************************************************************************************** - # ******************************************************************************************** - # node sets + # sets def get_constraints_fixed_nodes(self): - if not self.fixed_objects: - return - # get nodes - for femobj in self.fixed_objects: - # femobj --> dict, FreeCAD document object is femobj["Object"] - print_obj_info(femobj["Object"]) - femobj["Nodes"] = meshtools.get_femnodes_by_femobj_with_references( - self.femmesh, - femobj - ) - # add nodes to constraint_conflict_nodes, needed by constraint plane rotation - for node in femobj["Nodes"]: - self.constraint_conflict_nodes.append(node) - # if mixed mesh with solids the node set needs to be split - # because solid nodes do not have rotational degree of freedom - if self.femmesh.Volumes \ - and (len(self.shellthickness_objects) > 0 or len(self.beamsection_objects) > 0): - FreeCAD.Console.PrintMessage("We need to find the solid nodes.\n") - if not self.femelement_volumes_table: - self.femelement_volumes_table = meshtools.get_femelement_volumes_table( - self.femmesh - ) - for femobj in self.fixed_objects: - # femobj --> dict, FreeCAD document object is femobj["Object"] - nds_solid = [] - nds_faceedge = [] - for n in femobj["Nodes"]: - solid_node = False - for ve in self.femelement_volumes_table: - if n in self.femelement_volumes_table[ve]: - solid_node = True - nds_solid.append(n) - break - if not solid_node: - nds_faceedge.append(n) - femobj["NodesSolid"] = set(nds_solid) - femobj["NodesFaceEdge"] = set(nds_faceedge) + self.meshdatagetter.get_constraints_fixed_nodes() def get_constraints_displacement_nodes(self): - if not self.displacement_objects: - return - # get nodes - for femobj in self.displacement_objects: - # femobj --> dict, FreeCAD document object is femobj["Object"] - print_obj_info(femobj["Object"]) - femobj["Nodes"] = meshtools.get_femnodes_by_femobj_with_references( - self.femmesh, - femobj - ) - # add nodes to constraint_conflict_nodes, needed by constraint plane rotation - for node in femobj["Nodes"]: - self.constraint_conflict_nodes.append(node) + self.meshdatagetter.get_constraints_displacement_nodes() def get_constraints_planerotation_nodes(self): - if not self.planerotation_objects: - return - # get nodes - for femobj in self.planerotation_objects: - # femobj --> dict, FreeCAD document object is femobj["Object"] - print_obj_info(femobj["Object"]) - femobj["Nodes"] = meshtools.get_femnodes_by_femobj_with_references( - self.femmesh, - femobj - ) + self.meshdatagetter.get_constraints_planerotation_nodes() def get_constraints_transform_nodes(self): - if not self.transform_objects: - return - # get nodes - for femobj in self.transform_objects: - # femobj --> dict, FreeCAD document object is femobj["Object"] - print_obj_info(femobj["Object"]) - femobj["Nodes"] = meshtools.get_femnodes_by_femobj_with_references( - self.femmesh, - femobj - ) + self.meshdatagetter.get_constraints_transform_nodes() def get_constraints_temperature_nodes(self): - if not self.temperature_objects: - return - # get nodes - for femobj in self.temperature_objects: - # femobj --> dict, FreeCAD document object is femobj["Object"] - print_obj_info(femobj["Object"]) - femobj["Nodes"] = meshtools.get_femnodes_by_femobj_with_references( - self.femmesh, - femobj - ) + self.meshdatagetter.get_constraints_temperature_nodes() def get_constraints_fluidsection_nodes(self): - if not self.fluidsection_objects: - return - # get nodes - for femobj in self.fluidsection_objects: - # femobj --> dict, FreeCAD document object is femobj["Object"] - print_obj_info(femobj["Object"]) - femobj["Nodes"] = meshtools.get_femnodes_by_femobj_with_references( - self.femmesh, - femobj - ) + self.meshdatagetter.get_constraints_fluidsection_nodes() def get_constraints_force_nodeloads(self): - if not self.force_objects: - return - # check shape type of reference shape - for femobj in self.force_objects: - # femobj --> dict, FreeCAD document object is femobj["Object"] - print_obj_info(femobj["Object"], log=True) - if femobj["RefShapeType"] == "Vertex": - FreeCAD.Console.PrintLog( - " load on vertices --> The femelement_table " - "and femnodes_mesh are not needed for node load calculation.\n" - ) - elif femobj["RefShapeType"] == "Face" \ - and meshtools.is_solid_femmesh(self.femmesh) \ - and not meshtools.has_no_face_data(self.femmesh): - FreeCAD.Console.PrintLog( - " solid_mesh with face data --> The femelement_table is not " - "needed but the femnodes_mesh is needed for node load calculation.\n" - ) - if not self.femnodes_mesh: - self.femnodes_mesh = self.femmesh.Nodes - else: - FreeCAD.Console.PrintLog( - " mesh without needed data --> The femelement_table " - "and femnodes_mesh are not needed for node load calculation.\n" - ) - if not self.femnodes_mesh: - self.femnodes_mesh = self.femmesh.Nodes - if not self.femelement_table: - self.femelement_table = meshtools.get_femelement_table( - self.femmesh - ) - # get node loads - FreeCAD.Console.PrintLog( - " Finite element mesh nodes will be retrieved by searching " - "the appropriate nodes in the finite element mesh.\n" - ) - FreeCAD.Console.PrintLog( - " The appropriate finite element mesh node load values will " - "be calculated according to the finite element definition.\n" - ) - for femobj in self.force_objects: - # femobj --> dict, FreeCAD document object is femobj["Object"] - frc_obj = femobj["Object"] - print_obj_info(frc_obj) - if frc_obj.Force == 0: - FreeCAD.Console.PrintMessage(" Warning --> Force = 0\n") - if femobj["RefShapeType"] == "Vertex": # point load on vertices - femobj["NodeLoadTable"] = meshtools.get_force_obj_vertex_nodeload_table( - self.femmesh, - frc_obj - ) - elif femobj["RefShapeType"] == "Edge": # line load on edges - femobj["NodeLoadTable"] = meshtools.get_force_obj_edge_nodeload_table( - self.femmesh, - self.femelement_table, - self.femnodes_mesh, frc_obj - ) - elif femobj["RefShapeType"] == "Face": # area load on faces - femobj["NodeLoadTable"] = meshtools.get_force_obj_face_nodeload_table( - self.femmesh, - self.femelement_table, - self.femnodes_mesh, frc_obj - ) + self.meshdatagetter.get_constraints_force_nodeloads() - # ******************************************************************************************** - # ******************************************************************************************** - # faces sets def get_constraints_pressure_faces(self): - if not self.pressure_objects: - return - # TODO see comments in get_constraints_force_nodeloads() - # it applies here too. Mhh it applies to all constraints ... - - """ - # deprecated version - # get the faces and face numbers - for femobj in self.pressure_objects: - # femobj --> dict, FreeCAD document object is femobj["Object"] - femobj["PressureFaces"] = meshtools.get_pressure_obj_faces_depreciated( - self.femmesh, - femobj - ) - # print(femobj["PressureFaces"]) - """ - - if not self.femnodes_mesh: - self.femnodes_mesh = self.femmesh.Nodes - if not self.femelement_table: - self.femelement_table = meshtools.get_femelement_table(self.femmesh) - if not self.femnodes_ele_table: - self.femnodes_ele_table = meshtools.get_femnodes_ele_table( - self.femnodes_mesh, - self.femelement_table - ) - - for femobj in self.pressure_objects: - # femobj --> dict, FreeCAD document object is femobj["Object"] - print_obj_info(femobj["Object"]) - pressure_faces = meshtools.get_pressure_obj_faces( - self.femmesh, - self.femelement_table, - self.femnodes_ele_table, femobj - ) - # the data model is for compatibility reason with deprecated version - # get_pressure_obj_faces_depreciated returns the face ids in a tuple per ref_shape - # some_string was the reference_shape_element_string in deprecated method - # [(some_string, [ele_id, ele_face_id], [ele_id, ele_face_id], ...])] - some_string = "{}: face load".format(femobj["Object"].Name) - femobj["PressureFaces"] = [(some_string, pressure_faces)] - FreeCAD.Console.PrintLog("{}\n".format(femobj["PressureFaces"])) + self.meshdatagetter.get_constraints_pressure_faces() def get_constraints_contact_faces(self): - if not self.contact_objects: - return - if not self.femnodes_mesh: - self.femnodes_mesh = self.femmesh.Nodes - if not self.femelement_table: - self.femelement_table = meshtools.get_femelement_table(self.femmesh) - if not self.femnodes_ele_table: - self.femnodes_ele_table = meshtools.get_femnodes_ele_table( - self.femnodes_mesh, - self.femelement_table - ) - - for femobj in self.contact_objects: - # femobj --> dict, FreeCAD document object is femobj["Object"] - print_obj_info(femobj["Object"]) - contact_slave_faces, contact_master_faces = meshtools.get_contact_obj_faces( - self.femmesh, - self.femelement_table, - self.femnodes_ele_table, femobj - ) - # [ele_id, ele_face_id], [ele_id, ele_face_id], ...] - # whereas the ele_face_id might be ccx specific - femobj["ContactSlaveFaces"] = contact_slave_faces - femobj["ContactMasterFaces"] = contact_master_faces - # FreeCAD.Console.PrintLog("{}\n".format(femobj["ContactSlaveFaces"])) - # FreeCAD.Console.PrintLog("{}\n".format(femobj["ContactMasterFaces"])) - - # information in the regard of element faces constraints - # forum post: https://forum.freecadweb.org/viewtopic.php?f=18&t=42783&p=370286#p366723 - # contact: master and slave could be the same face: rubber of a damper - # tie: master and slave have to be separate faces AFA UR_ K - # section print: only the element faces of solid elements - # from one side of the geometric face are needed + self.meshdatagetter.get_constraints_contact_faces() def get_constraints_tie_faces(self): - if not self.tie_objects: - return - if not self.femnodes_mesh: - self.femnodes_mesh = self.femmesh.Nodes - if not self.femelement_table: - self.femelement_table = meshtools.get_femelement_table(self.femmesh) - if not self.femnodes_ele_table: - self.femnodes_ele_table = meshtools.get_femnodes_ele_table( - self.femnodes_mesh, - self.femelement_table - ) - - for femobj in self.tie_objects: - # femobj --> dict, FreeCAD document object is femobj["Object"] - print_obj_info(femobj["Object"]) - slave_faces, master_faces = meshtools.get_tie_obj_faces( - self.femmesh, - self.femelement_table, - self.femnodes_ele_table, femobj - ) - # [ele_id, ele_face_id], [ele_id, ele_face_id], ...] - # whereas the ele_face_id might be ccx specific - femobj["TieSlaveFaces"] = slave_faces - femobj["TieMasterFaces"] = master_faces - # FreeCAD.Console.PrintLog("{}\n".format(femobj["ContactSlaveFaces"])) - # FreeCAD.Console.PrintLog("{}\n".format(femobj["ContactMasterFaces"])) + self.meshdatagetter.get_constraints_tie_faces() def get_constraints_sectionprint_faces(self): - if not self.sectionprint_objects: - return - # TODO: use meshtools to get the surfaces - # see constraint contact or constraint tie - for femobj in self.sectionprint_objects: - # femobj --> dict, FreeCAD document object is femobj["Object"] - sectionprint_obj = femobj["Object"] - if len(sectionprint_obj.References) > 1: - FreeCAD.Console.PrintError( - "Only one reference shape allowed for a section print " - "but {} found: {}\n" - .format(len(sectionprint_obj.References), sectionprint_obj.References) - ) - for o, elem_tup in sectionprint_obj.References: - for elem in elem_tup: - # there should only be one reference for each section print object - # in the gui this is checked - ref_shape = o.Shape.getElement(elem) - if ref_shape.ShapeType == "Face": - v = self.mesh_object.FemMesh.getccxVolumesByFace(ref_shape) - if len(v) > 0: - femobj["SectionPrintFaces"] = v - # volume elements found - FreeCAD.Console.PrintLog( - "{}, surface {}, {} touching volume elements found\n" - .format(sectionprint_obj.Label, sectionprint_obj.Name, len(v)) - ) - else: - # no volume elements found, shell elements not allowed - FreeCAD.Console.PrintError( - "{}, surface {}, Error: " - "No volume elements found!\n" - .format(sectionprint_obj.Label, sectionprint_obj.Name) - ) - else: - # in Gui only Faces can be added - FreeCAD.Console.PrintError( - "Wrong reference shape type for {} " - "Only Faces are allowed, but a {} was found.\n" - .format(sectionprint_obj.Name, ref_shape.ShapeType) - ) + self.meshdatagetter.get_constraints_sectionprint_faces() def get_constraints_heatflux_faces(self): - if not self.heatflux_objects: - return - # TODO: use meshtools to get the surfaces (or move to mesh tools) - # see constraint contact or constraint tie and constraint force - # heatflux_obj_face_table: see force_obj_node_load_table - # [ - # ("refshape_name:elemname", face_table), - # ..., - # ("refshape_name:elemname", face_table) - # ] - for femobj in self.heatflux_objects: - # femobj --> dict, FreeCAD document object is femobj["Object"] - heatflux_obj = femobj["Object"] - femobj["HeatFluxFaceTable"] = [] - for o, elem_tup in heatflux_obj.References: - for elem in elem_tup: - ho = o.Shape.getElement(elem) - if ho.ShapeType == "Face": - elem_info = "{}:{}".format(o.Name, elem) - face_table = self.mesh_object.FemMesh.getccxVolumesByFace(ho) - femobj["HeatFluxFaceTable"].append((elem_info, face_table)) + self.meshdatagetter.get_constraints_heatflux_faces() - # ******************************************************************************************** - # ******************************************************************************************** - # element sets constraints def get_constraints_centrif_elements(self): - # get element ids and write them into the femobj - if not self.centrif_objects: - return - if len(self.centrif_objects) == 1 and not self.centrif_objects[0]["Object"].References: - self.centrif_objects[0]["FEMElements"] = self.ccx_evolumes - else: - self.get_solid_element_sets(self.centrif_objects) - - # ******************************************************************************************** - # ******************************************************************************************** - # element sets material and element geometry - def get_solid_element_sets(self, femobjs): - # get element ids and write them into the femobj - all_found = False - if self.femmesh.GroupCount: - all_found = meshtools.get_femelement_sets_from_group_data( - self.femmesh, - femobjs - ) - FreeCAD.Console.PrintMessage(all_found) - FreeCAD.Console.PrintMessage("\n") - if all_found is False: - if not self.femelement_table: - self.femelement_table = meshtools.get_femelement_table(self.femmesh) - # we're going to use the binary search for get_femelements_by_femnodes() - # thus we need the parameter values self.femnodes_ele_table - if not self.femnodes_mesh: - self.femnodes_mesh = self.femmesh.Nodes - if not self.femnodes_ele_table: - self.femnodes_ele_table = meshtools.get_femnodes_ele_table( - self.femnodes_mesh, - self.femelement_table - ) - control = meshtools.get_femelement_sets( - self.femmesh, - self.femelement_table, - femobjs, - self.femnodes_ele_table - ) - # we only need to set it, if it is still True - if (self.femelement_count_test is True) and (control is False): - self.femelement_count_test = False - - def get_element_geometry2D_elements(self): - # get element ids and write them into the objects - FreeCAD.Console.PrintMessage("Shell thicknesses\n") - if not self.femelement_faces_table: - self.femelement_faces_table = meshtools.get_femelement_faces_table( - self.femmesh - ) - meshtools.get_femelement_sets( - self.femmesh, - self.femelement_faces_table, - self.shellthickness_objects - ) - - def get_element_geometry1D_elements(self): - # get element ids and write them into the objects - FreeCAD.Console.PrintMessage("Beam sections\n") - if not self.femelement_edges_table: - self.femelement_edges_table = meshtools.get_femelement_edges_table( - self.femmesh - ) - meshtools.get_femelement_sets( - self.femmesh, - self.femelement_edges_table, - self.beamsection_objects - ) - - def get_element_rotation1D_elements(self): - # get for each geometry edge direction the element ids and rotation norma - FreeCAD.Console.PrintMessage("Beam rotations\n") - if not self.femelement_edges_table: - self.femelement_edges_table = meshtools.get_femelement_edges_table( - self.femmesh - ) - meshtools.get_femelement_direction1D_set( - self.femmesh, - self.femelement_edges_table, - self.beamrotation_objects, - self.theshape - ) - - def get_element_fluid1D_elements(self): - # get element ids and write them into the objects - FreeCAD.Console.PrintMessage("Fluid sections\n") - if not self.femelement_edges_table: - self.femelement_edges_table = meshtools.get_femelement_edges_table( - self.femmesh - ) - meshtools.get_femelement_sets( - self.femmesh, - self.femelement_edges_table, - self.fluidsection_objects - ) - - def get_material_elements(self): - # it only works if either Volumes or Shellthicknesses or Beamsections - # are in the material objects, it means it does not work - # for mixed meshes and multiple materials, this is checked in check_prerequisites - # the femelement_table is only calculated for - # the highest dimension in get_femelement_table - FreeCAD.Console.PrintMessage("Materials\n") - if self.femmesh.Volumes: - # we only could do this for volumes - # if a mesh contains volumes we're going to use them in the analysis - # but a mesh could contain - # the element faces of the volumes as faces - # and the edges of the faces as edges - # there we have to check of some geometric objects - # get element ids and write them into the femobj - self.get_solid_element_sets(self.material_objects) - if self.shellthickness_objects: - if not self.femelement_faces_table: - self.femelement_faces_table = meshtools.get_femelement_faces_table( - self.femmesh - ) - meshtools.get_femelement_sets( - self.femmesh, - self.femelement_faces_table, - self.material_objects - ) - if self.beamsection_objects or self.fluidsection_objects: - if not self.femelement_edges_table: - self.femelement_edges_table = meshtools.get_femelement_edges_table( - self.femmesh - ) - meshtools.get_femelement_sets( - self.femmesh, - self.femelement_edges_table, - self.material_objects - ) + self.meshdatagetter.get_constraints_centrif_elements() def get_element_sets_material_and_femelement_geometry(self): - if not self.material_objects: - return - # in any case if we have beams, we're going to need the element ids for the rotation elsets - if self.beamsection_objects: - # we will need to split the beam even for one beamobj - # because no beam in z-direction can be used in ccx without a special adjustment - # thus they need an own ccx_elset - self.get_element_rotation1D_elements() + self.meshdatagetter.get_element_sets_material_and_femelement_geometry() + self.ccx_elsets = self.meshdatagetter.mat_geo_sets - # get the element ids for face and edge elements and write them into the objects - if len(self.shellthickness_objects) > 1: - self.get_element_geometry2D_elements() - if len(self.beamsection_objects) > 1: - self.get_element_geometry1D_elements() - if len(self.fluidsection_objects) > 1: - self.get_element_fluid1D_elements() - - # get the element ids for material objects and write them into the material object - if len(self.material_objects) > 1: - self.get_material_elements() - - # create the ccx_elsets - if len(self.material_objects) == 1: - if self.femmesh.Volumes: - # we only could do this for volumes, if a mesh contains volumes - # we're going to use them in the analysis - # but a mesh could contain the element faces of the volumes as faces - # and the edges of the faces as edges - # there we have to check for some geometric objects - self.get_ccx_elsets_single_mat_solid() - if len(self.shellthickness_objects) == 1: - self.get_ccx_elsets_single_mat_single_shell() - elif len(self.shellthickness_objects) > 1: - self.get_ccx_elsets_single_mat_multiple_shell() - if len(self.beamsection_objects) == 1: - self.get_ccx_elsets_single_mat_single_beam() - elif len(self.beamsection_objects) > 1: - self.get_ccx_elsets_single_mat_multiple_beam() - if len(self.fluidsection_objects) == 1: - self.get_ccx_elsets_single_mat_single_fluid() - elif len(self.fluidsection_objects) > 1: - self.get_ccx_elsets_single_mat_multiple_fluid() - elif len(self.material_objects) > 1: - if self.femmesh.Volumes: - # we only could do this for volumes, if a mseh contains volumes - # we're going to use them in the analysis - # but a mesh could contain the element faces of the volumes as faces - # and the edges of the faces as edges - # there we have to check for some geometric objects - # volume is a bit special - # because retrieving ids from group mesh data is implemented - self.get_ccx_elsets_multiple_mat_solid() - if len(self.shellthickness_objects) == 1: - self.get_ccx_elsets_multiple_mat_single_shell() - elif len(self.shellthickness_objects) > 1: - self.get_ccx_elsets_multiple_mat_multiple_shell() - if len(self.beamsection_objects) == 1: - self.get_ccx_elsets_multiple_mat_single_beam() - elif len(self.beamsection_objects) > 1: - self.get_ccx_elsets_multiple_mat_multiple_beam() - if len(self.fluidsection_objects) == 1: - self.get_ccx_elsets_multiple_mat_single_fluid() - elif len(self.fluidsection_objects) > 1: - self.get_ccx_elsets_multiple_mat_multiple_fluid() - - # self.ccx_elsets = [ { - # "ccx_elset" : [e1, e2, e3, ... , en] or elements set name strings - # "ccx_elset_name" : "ccx_identifier_elset" - # "mat_obj_name" : "mat_obj.Name" - # "ccx_mat_name" : "mat_obj.Material["Name"]" !!! not unique !!! - # "beamsection_obj" : "beamsection_obj" if exists - # "fluidsection_obj" : "fluidsection_obj" if exists - # "shellthickness_obj" : shellthickness_obj" if exists - # "beam_normal" : normal vector for beams only - # }, - # {}, ... , {} ] - - # beam - # TODO support multiple beamrotations - # we do not need any more any data from the rotation document object, - # thus we do not need to save the rotation document object name in the else - def get_ccx_elsets_single_mat_single_beam(self): - mat_obj = self.material_objects[0]["Object"] - beamsec_obj = self.beamsection_objects[0]["Object"] - beamrot_data = self.beamrotation_objects[0] - for i, beamdirection in enumerate(beamrot_data["FEMRotations1D"]): - # ID's for this direction - elset_data = beamdirection["ids"] - names = [ - {"short": "M0"}, - {"short": "B0"}, - {"short": beamrot_data["ShortName"]}, - {"short": "D" + str(i)} - ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["beamsection_obj"] = beamsec_obj - # normal for this direction - ccx_elset["beam_normal"] = beamdirection["normal"] - self.ccx_elsets.append(ccx_elset) - - def get_ccx_elsets_single_mat_multiple_beam(self): - mat_obj = self.material_objects[0]["Object"] - beamrot_data = self.beamrotation_objects[0] - for beamsec_data in self.beamsection_objects: - beamsec_obj = beamsec_data["Object"] - beamsec_ids = set(beamsec_data["FEMElements"]) - for i, beamdirection in enumerate(beamrot_data["FEMRotations1D"]): - beamdir_ids = set(beamdirection["ids"]) - # empty intersection sets possible - elset_data = list(sorted(beamsec_ids.intersection(beamdir_ids))) - if elset_data: - names = [ - {"short": "M0"}, - {"short": beamsec_data["ShortName"]}, - {"short": beamrot_data["ShortName"]}, - {"short": "D" + str(i)} - ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["beamsection_obj"] = beamsec_obj - # normal for this direction - ccx_elset["beam_normal"] = beamdirection["normal"] - self.ccx_elsets.append(ccx_elset) - - def get_ccx_elsets_multiple_mat_single_beam(self): - beamsec_obj = self.beamsection_objects[0]["Object"] - beamrot_data = self.beamrotation_objects[0] - for mat_data in self.material_objects: - mat_obj = mat_data["Object"] - mat_ids = set(mat_data["FEMElements"]) - for i, beamdirection in enumerate(beamrot_data["FEMRotations1D"]): - beamdir_ids = set(beamdirection["ids"]) - elset_data = list(sorted(mat_ids.intersection(beamdir_ids))) - if elset_data: - names = [ - {"short": mat_data["ShortName"]}, - {"short": "B0"}, - {"short": beamrot_data["ShortName"]}, - {"short": "D" + str(i)} - ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["beamsection_obj"] = beamsec_obj - # normal for this direction - ccx_elset["beam_normal"] = beamdirection["normal"] - self.ccx_elsets.append(ccx_elset) - - def get_ccx_elsets_multiple_mat_multiple_beam(self): - beamrot_data = self.beamrotation_objects[0] - for beamsec_data in self.beamsection_objects: - beamsec_obj = beamsec_data["Object"] - beamsec_ids = set(beamsec_data["FEMElements"]) - for mat_data in self.material_objects: - mat_obj = mat_data["Object"] - mat_ids = set(mat_data["FEMElements"]) - for i, beamdirection in enumerate(beamrot_data["FEMRotations1D"]): - beamdir_ids = set(beamdirection["ids"]) - # empty intersection sets possible - elset_data = list(sorted( - beamsec_ids.intersection(mat_ids).intersection(beamdir_ids) - )) - if elset_data: - names = [ - {"short": mat_data["ShortName"]}, - {"short": beamsec_data["ShortName"]}, - {"short": beamrot_data["ShortName"]}, - {"short": "D" + str(i)} - ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["beamsection_obj"] = beamsec_obj - # normal for this direction - ccx_elset["beam_normal"] = beamdirection["normal"] - self.ccx_elsets.append(ccx_elset) - - # fluid - def get_ccx_elsets_single_mat_single_fluid(self): - mat_obj = self.material_objects[0]["Object"] - fluidsec_obj = self.fluidsection_objects[0]["Object"] - elset_data = self.ccx_eedges - names = [{"short": "M0"}, {"short": "F0"}] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["fluidsection_obj"] = fluidsec_obj - self.ccx_elsets.append(ccx_elset) - - def get_ccx_elsets_single_mat_multiple_fluid(self): - mat_obj = self.material_objects[0]["Object"] - for fluidsec_data in self.fluidsection_objects: - fluidsec_obj = fluidsec_data["Object"] - elset_data = fluidsec_data["FEMElements"] - names = [{"short": "M0"}, {"short": fluidsec_data["ShortName"]}] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["fluidsection_obj"] = fluidsec_obj - self.ccx_elsets.append(ccx_elset) - - def get_ccx_elsets_multiple_mat_single_fluid(self): - fluidsec_obj = self.fluidsection_objects[0]["Object"] - for mat_data in self.material_objects: - mat_obj = mat_data["Object"] - elset_data = mat_data["FEMElements"] - names = [{"short": mat_data["ShortName"]}, {"short": "F0"}] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["fluidsection_obj"] = fluidsec_obj - self.ccx_elsets.append(ccx_elset) - - def get_ccx_elsets_multiple_mat_multiple_fluid(self): - for fluidsec_data in self.fluidsection_objects: - fluidsec_obj = fluidsec_data["Object"] - for mat_data in self.material_objects: - mat_obj = mat_data["Object"] - fluidsec_ids = set(fluidsec_data["FEMElements"]) - mat_ids = set(mat_data["FEMElements"]) - # empty intersection sets possible - elset_data = list(sorted(fluidsec_ids.intersection(mat_ids))) - if elset_data: - names = [ - {"short": mat_data["ShortName"]}, - {"short": fluidsec_data["ShortName"]} - ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["fluidsection_obj"] = fluidsec_obj - self.ccx_elsets.append(ccx_elset) - - # shell - def get_ccx_elsets_single_mat_single_shell(self): - mat_obj = self.material_objects[0]["Object"] - shellth_obj = self.shellthickness_objects[0]["Object"] - elset_data = self.ccx_efaces - names = [ - {"long": mat_obj.Name, "short": "M0"}, - {"long": shellth_obj.Name, "short": "S0"} - ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["shellthickness_obj"] = shellth_obj - self.ccx_elsets.append(ccx_elset) - - def get_ccx_elsets_single_mat_multiple_shell(self): - mat_obj = self.material_objects[0]["Object"] - for shellth_data in self.shellthickness_objects: - shellth_obj = shellth_data["Object"] - elset_data = shellth_data["FEMElements"] - names = [ - {"long": mat_obj.Name, "short": "M0"}, - {"long": shellth_obj.Name, "short": shellth_data["ShortName"]} - ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["shellthickness_obj"] = shellth_obj - self.ccx_elsets.append(ccx_elset) - - def get_ccx_elsets_multiple_mat_single_shell(self): - shellth_obj = self.shellthickness_objects[0]["Object"] - for mat_data in self.material_objects: - mat_obj = mat_data["Object"] - elset_data = mat_data["FEMElements"] - names = [ - {"long": mat_obj.Name, "short": mat_data["ShortName"]}, - {"long": shellth_obj.Name, "short": "S0"} - ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["shellthickness_obj"] = shellth_obj - self.ccx_elsets.append(ccx_elset) - - def get_ccx_elsets_multiple_mat_multiple_shell(self): - for shellth_data in self.shellthickness_objects: - shellth_obj = shellth_data["Object"] - for mat_data in self.material_objects: - mat_obj = mat_data["Object"] - shellth_ids = set(shellth_data["FEMElements"]) - mat_ids = set(mat_data["FEMElements"]) - # empty intersection sets possible - elset_data = list(sorted(shellth_ids.intersection(mat_ids))) - if elset_data: - names = [ - {"long": mat_obj.Name, "short": mat_data["ShortName"]}, - {"long": shellth_obj.Name, "short": shellth_data["ShortName"]} - ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["shellthickness_obj"] = shellth_obj - self.ccx_elsets.append(ccx_elset) - - # solid - def get_ccx_elsets_single_mat_solid(self): - mat_obj = self.material_objects[0]["Object"] - elset_data = self.ccx_evolumes - names = [ - {"long": mat_obj.Name, "short": "M0"}, - {"long": "Solid", "short": "Solid"} - ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - self.ccx_elsets.append(ccx_elset) - - def get_ccx_elsets_multiple_mat_solid(self): - for mat_data in self.material_objects: - mat_obj = mat_data["Object"] - elset_data = mat_data["FEMElements"] - names = [ - {"long": mat_obj.Name, "short": mat_data["ShortName"]}, - {"long": "Solid", "short": "Solid"} - ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - self.ccx_elsets.append(ccx_elset) - - -# ************************************************************************************************ -# Helpers - - -# ccx elset names: -# M .. Material -# B .. Beam -# R .. BeamRotation -# D .. Direction -# F .. Fluid -# S .. Shell, -# TODO write comment into input file to elset ids and elset attributes - - -def get_ccx_elset_name_standard(names): - # standard max length = 80 - ccx_elset_name = "" - for name in names: - ccx_elset_name += name["long"] - if len(ccx_elset_name) < 81: - return ccx_elset_name - else: - ccx_elset_name = "" - for name in names: - ccx_elset_name += name["short"] - if len(ccx_elset_name) < 81: - return ccx_elset_name - else: - error = ( - "FEM: Trouble in ccx input file, because an " - "elset name is longer than 80 character! {}\n" - .format(ccx_elset_name) - ) - raise Exception(error) - - -def get_ccx_elset_name_short(names): - # restricted max length = 20 (beam elsets) - ccx_elset_name = "" - for name in names: - ccx_elset_name += name["short"] - if len(ccx_elset_name) < 21: - return ccx_elset_name - else: - error = ( - "FEM: Trouble in ccx input file, because an" - "beam elset name is longer than 20 characters! {}\n" - .format(ccx_elset_name) - ) - raise Exception(error) - - -def print_obj_info(obj, log=False): - if log is False: - FreeCAD.Console.PrintMessage("{}:\n".format(obj.Label)) - FreeCAD.Console.PrintMessage( - " Type: {}, Name: {}\n".format(type_of_obj(obj), obj.Name) - ) - else: - FreeCAD.Console.PrintLog("{}:\n".format(obj.Label)) - FreeCAD.Console.PrintLog( - " Type: {}, Name: {}\n".format(type_of_obj(obj), obj.Name) - ) ## @} From 6f254bb27bee172c68bc5aeab30de7951638da88 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Fri, 16 Jul 2021 07:18:17 +0200 Subject: [PATCH 08/20] FEM: mesh data getter, add time and some comments --- src/Mod/Fem/femmesh/meshsetsgetter.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Mod/Fem/femmesh/meshsetsgetter.py b/src/Mod/Fem/femmesh/meshsetsgetter.py index 06f8a575c8..5669b093e2 100644 --- a/src/Mod/Fem/femmesh/meshsetsgetter.py +++ b/src/Mod/Fem/femmesh/meshsetsgetter.py @@ -28,6 +28,8 @@ __url__ = "https://www.freecadweb.org" ## \addtogroup FEM # @{ +import time + import FreeCAD from femmesh import meshtools @@ -100,12 +102,15 @@ class MeshSetsGetter(): # ******************************************************************************************** # get all known sets def get_mesh_sets(self): + FreeCAD.Console.PrintMessage( - "!!!!!!!!!!!!!!!!!!!!!!!!! MeshSetsGetter class :-) !!!!!!!!!!!!!!!!!!!!!!!!!\n" - "Get mesh data for " + "MeshSetsGetter: Get mesh data for " "node sets (groups), surface sets (groups) and element sets (groups)\n" ) + time_start = time.process_time() + FreeCAD.Console.PrintMessage("Get mesh sets.\n") + # materials and element geometry element sets getter self.get_element_sets_material_and_femelement_geometry() @@ -129,6 +134,11 @@ class MeshSetsGetter(): self.get_constraints_pressure_faces() self.get_constraints_heatflux_faces() + setstime = round((time.process_time() - time_start), 3) + FreeCAD.Console.PrintMessage( + "Getting mesh sets or groups time: {} seconds \n".format(setstime) + ) + # ******************************************************************************************** # ******************************************************************************************** # node sets @@ -645,7 +655,6 @@ class MeshSetsGetter(): # create the ccx_elsets if len(self.member.mats_linear) == 1: if self.femmesh.Volumes: - print("here1") # we only could do this for volumes, if a mesh contains volumes # we're going to use them in the analysis # but a mesh could contain the element faces of the volumes as faces @@ -949,7 +958,6 @@ class MeshSetsGetter(): # solid def get_ccx_elsets_single_mat_solid(self): - print("here2") mat_obj = self.member.mats_linear[0]["Object"] elset_data = self.ccx_evolumes names = [ From 5f57d48b551bab7132807bf5f2a689c97d564e53 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Fri, 16 Jul 2021 07:20:20 +0200 Subject: [PATCH 09/20] FEM: calculix writer, code formating --- src/Mod/Fem/femsolver/calculix/writer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/Fem/femsolver/calculix/writer.py b/src/Mod/Fem/femsolver/calculix/writer.py index 2dccd59542..56439cf5bf 100644 --- a/src/Mod/Fem/femsolver/calculix/writer.py +++ b/src/Mod/Fem/femsolver/calculix/writer.py @@ -42,7 +42,7 @@ from . import write_constraint_fixed as con_fixed from . import write_constraint_fluidsection as con_fluidsection from . import write_constraint_force as con_force from . import write_constraint_heatflux as con_heatflux -from . import write_constraint_initialtemperature as con_initialtemp +from . import write_constraint_initialtemperature as con_itemp from . import write_constraint_planerotation as con_planerotation from . import write_constraint_pressure as con_pressure from . import write_constraint_sectionprint as con_sectionprint @@ -175,7 +175,7 @@ class FemInputWriterCcx(writerbase.FemInputWriter): # materials and fem element types write_femelement_material.write_femelement_material(inpfile, self) - self.write_constraints_propdata(inpfile, self.member.cons_initialtemperature, con_initialtemp) + self.write_constraints_propdata(inpfile, self.member.cons_initialtemperature, con_itemp) write_femelement_geometry.write_femelement_geometry(inpfile, self) # constraints independent from steps From 666ce649591f434d6dfb9a9fd1a59976fbb7b71f Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Fri, 16 Jul 2021 07:56:27 +0200 Subject: [PATCH 10/20] FEM: calculix writer, make use of the member objs everywhere --- .../calculix/write_constraint_displacement.py | 2 +- .../femsolver/calculix/write_constraint_fixed.py | 12 +++++++++--- .../calculix/write_constraint_fluidsection.py | 6 +++--- .../calculix/write_femelement_material.py | 8 ++++---- src/Mod/Fem/femsolver/calculix/write_mesh.py | 4 ++-- .../Fem/femsolver/calculix/write_step_output.py | 14 +++++++------- 6 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/Mod/Fem/femsolver/calculix/write_constraint_displacement.py b/src/Mod/Fem/femsolver/calculix/write_constraint_displacement.py index a99da3a0ea..e9f932ca20 100644 --- a/src/Mod/Fem/femsolver/calculix/write_constraint_displacement.py +++ b/src/Mod/Fem/femsolver/calculix/write_constraint_displacement.py @@ -78,7 +78,7 @@ def write_constraint(f, femobj, disp_obj, ccxwriter): elif not disp_obj.zFree: f.write("{},3,3,{:.13G}\n".format(disp_obj.Name, disp_obj.zDisplacement)) - if ccxwriter.beamsection_objects or ccxwriter.shellthickness_objects: + if ccxwriter.member.geos_beamsection or ccxwriter.member.geos_shellthickness: if disp_obj.rotxFix: f.write("{},4\n".format(disp_obj.Name)) elif not disp_obj.rotxFree: diff --git a/src/Mod/Fem/femsolver/calculix/write_constraint_fixed.py b/src/Mod/Fem/femsolver/calculix/write_constraint_fixed.py index a90bc89d0c..fd79db4dda 100644 --- a/src/Mod/Fem/femsolver/calculix/write_constraint_fixed.py +++ b/src/Mod/Fem/femsolver/calculix/write_constraint_fixed.py @@ -57,7 +57,10 @@ def get_after_write_constraint(): def write_meshdata_constraint(f, femobj, fix_obj, ccxwriter): if ( ccxwriter.femmesh.Volumes - and (len(ccxwriter.shellthickness_objects) > 0 or len(ccxwriter.beamsection_objects) > 0) + and ( + len(ccxwriter.member.geos_shellthickness) > 0 + or len(ccxwriter.member.geos_beamsection) > 0 + ) ): if len(femobj["NodesSolid"]) > 0: f.write("*NSET,NSET={}Solid\n".format(fix_obj.Name)) @@ -79,7 +82,10 @@ def write_constraint(f, femobj, fix_obj, ccxwriter): if ( ccxwriter.femmesh.Volumes - and (len(ccxwriter.shellthickness_objects) > 0 or len(ccxwriter.beamsection_objects) > 0) + and ( + len(ccxwriter.member.geos_shellthickness) > 0 + or len(ccxwriter.member.geos_beamsection) > 0 + ) ): if len(femobj["NodesSolid"]) > 0: f.write("*BOUNDARY\n") @@ -101,7 +107,7 @@ def write_constraint(f, femobj, fix_obj, ccxwriter): f.write(fix_obj.Name + ",1\n") f.write(fix_obj.Name + ",2\n") f.write(fix_obj.Name + ",3\n") - if ccxwriter.beamsection_objects or ccxwriter.shellthickness_objects: + if ccxwriter.member.geos_beamsection or ccxwriter.member.geos_shellthickness: f.write(fix_obj.Name + ",4\n") f.write(fix_obj.Name + ",5\n") f.write(fix_obj.Name + ",6\n") diff --git a/src/Mod/Fem/femsolver/calculix/write_constraint_fluidsection.py b/src/Mod/Fem/femsolver/calculix/write_constraint_fluidsection.py index 8abcaa9788..a142629504 100644 --- a/src/Mod/Fem/femsolver/calculix/write_constraint_fluidsection.py +++ b/src/Mod/Fem/femsolver/calculix/write_constraint_fluidsection.py @@ -43,7 +43,7 @@ from femmesh import meshtools # as it is none standard constraint method compared to all other constraints def handle_fluidsection_liquid_inlet_outlet(inpfile, ccxwriter): - if not ccxwriter.fluidsection_objects: + if not ccxwriter.member.geos_fluidsection: return inpfile # Fluid sections: @@ -139,7 +139,7 @@ def handle_fluidsection_liquid_inlet_outlet(inpfile, ccxwriter): # split method into separate methods and move some part into base writer # see also method handle_fluidsection_liquid_inlet_outlet def write_constraints_fluidsection(f, ccxwriter): - if not ccxwriter.fluidsection_objects: + if not ccxwriter.member.geos_fluidsection: return if ccxwriter.analysis_type not in ["thermomech"]: return @@ -158,7 +158,7 @@ def write_constraints_fluidsection(f, ccxwriter): ) # get nodes ccxwriter.get_constraints_fluidsection_nodes() - for femobj in ccxwriter.fluidsection_objects: + for femobj in ccxwriter.member.geos_fluidsection: # femobj --> dict, FreeCAD document object is femobj["Object"] fluidsection_obj = femobj["Object"] f.write("** " + fluidsection_obj.Label + "\n") diff --git a/src/Mod/Fem/femsolver/calculix/write_femelement_material.py b/src/Mod/Fem/femsolver/calculix/write_femelement_material.py index c05cde1123..98747fd7b5 100644 --- a/src/Mod/Fem/femsolver/calculix/write_femelement_material.py +++ b/src/Mod/Fem/femsolver/calculix/write_femelement_material.py @@ -42,16 +42,16 @@ def write_femelement_material(f, ccxwriter): and not ccxwriter.solver_obj.ThermoMechSteadyState ): return True - if ccxwriter.centrif_objects: + if ccxwriter.member.cons_centrif: return True - if ccxwriter.selfweight_objects: + if ccxwriter.member.cons_selfweight: return True return False f.write("\n{}\n".format(59 * "*")) f.write("** Materials\n") f.write("** see information about units at file end\n") - for femobj in ccxwriter.material_objects: + for femobj in ccxwriter.member.mats_linear: # femobj --> dict, FreeCAD document object is femobj["Object"] mat_obj = femobj["Object"] mat_info_name = mat_obj.Material["Name"] @@ -107,7 +107,7 @@ def write_femelement_material(f, ccxwriter): # nonlinear material properties if ccxwriter.solver_obj.MaterialNonlinearity == "nonlinear": - for nlfemobj in ccxwriter.material_nonlinear_objects: + for nlfemobj in ccxwriter.member.mats_nonlinear: # femobj --> dict, FreeCAD document object is nlfemobj["Object"] nl_mat_obj = nlfemobj["Object"] if nl_mat_obj.LinearBaseMaterial == mat_obj: diff --git a/src/Mod/Fem/femsolver/calculix/write_mesh.py b/src/Mod/Fem/femsolver/calculix/write_mesh.py index 86b14a6721..80e4230ea9 100644 --- a/src/Mod/Fem/femsolver/calculix/write_mesh.py +++ b/src/Mod/Fem/femsolver/calculix/write_mesh.py @@ -48,7 +48,7 @@ def write_mesh(ccxwriter): ) # Check to see if fluid sections are in analysis and use D network element type - if ccxwriter.fluidsection_objects: + if ccxwriter.member.geos_fluidsection: meshtools.write_D_network_element_to_inputfile(ccxwriter.femmesh_file) inpfile = codecs.open(ccxwriter.file_name, "w", encoding="utf-8") @@ -65,7 +65,7 @@ def write_mesh(ccxwriter): ) # Check to see if fluid sections are in analysis and use D network element type - if ccxwriter.fluidsection_objects: + if ccxwriter.member.geos_fluidsection: # inpfile is closed meshtools.write_D_network_element_to_inputfile(ccxwriter.femmesh_file) diff --git a/src/Mod/Fem/femsolver/calculix/write_step_output.py b/src/Mod/Fem/femsolver/calculix/write_step_output.py index acfa933def..cf5d8b5613 100644 --- a/src/Mod/Fem/femsolver/calculix/write_step_output.py +++ b/src/Mod/Fem/femsolver/calculix/write_step_output.py @@ -31,9 +31,9 @@ def write_step_output(f, ccxwriter): f.write("\n{}\n".format(59 * "*")) f.write("** Outputs --> frd file\n") if ( - ccxwriter.beamsection_objects - or ccxwriter.shellthickness_objects - or ccxwriter.fluidsection_objects + ccxwriter.member.geos_beamsection + or ccxwriter.member.geos_shellthickness + or ccxwriter.member.geos_fluidsection ): if ccxwriter.solver_obj.BeamShellResultOutput3D is False: f.write("*NODE FILE, OUTPUT=2d\n") @@ -43,13 +43,13 @@ def write_step_output(f, ccxwriter): f.write("*NODE FILE\n") # MPH write out nodal temperatures if thermomechanical if ccxwriter.analysis_type == "thermomech": - if not ccxwriter.fluidsection_objects: + if not ccxwriter.member.geos_fluidsection: f.write("U, NT\n") else: f.write("MF, PS\n") else: f.write("U\n") - if not ccxwriter.fluidsection_objects: + if not ccxwriter.member.geos_fluidsection: f.write("*EL FILE\n") if ccxwriter.solver_obj.MaterialNonlinearity == "nonlinear": f.write("S, E, PEEQ\n") @@ -58,11 +58,11 @@ def write_step_output(f, ccxwriter): # dat file # reaction forces: freecadweb.org/tracker/view.php?id=2934 - if ccxwriter.fixed_objects: + if ccxwriter.member.cons_fixed: f.write("** outputs --> dat file\n") # reaction forces for all Constraint fixed f.write("** reaction forces for Constraint fixed\n") - for femobj in ccxwriter.fixed_objects: + for femobj in ccxwriter.member.cons_fixed: # femobj --> dict, FreeCAD document object is femobj["Object"] fix_obj_name = femobj["Object"].Name f.write("*NODE PRINT, NSET={}, TOTALS=ONLY\n".format(fix_obj_name)) From 1eab74ef4df1c73b59378fe48d2b15a2a43ad74d Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Fri, 16 Jul 2021 12:22:15 +0200 Subject: [PATCH 11/20] Arch: Fixed wrong numbering in arch axis in section mode --- src/Mod/Arch/ArchAxis.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Mod/Arch/ArchAxis.py b/src/Mod/Arch/ArchAxis.py index 917113f657..e65bf40b68 100644 --- a/src/Mod/Arch/ArchAxis.py +++ b/src/Mod/Arch/ArchAxis.py @@ -581,7 +581,7 @@ class _ViewProviderAxis: t.string = self.getNumber(vobj,num) num += 1 if hasattr(vobj,"BubblePosition"): - if vobj.BubblePosition == "Both": + if vobj.BubblePosition in ["Both","Arrow left","Arrow right","Bar left","Bar right"]: if not alt: num -= 1 alt = not alt @@ -593,7 +593,11 @@ class _ViewProviderAxis: if hasattr(vobj,"ShowLabel") and hasattr(vobj.Object,"Labels"): if vobj.ShowLabel: self.labels = coin.SoSeparator() - for i in range(len(vobj.Object.Shape.Edges)): + if hasattr(vobj.Object,"Limit") and vobj.Object.Limit.Value: + n = len(vobj.Object.Shape.Edges)/2 + else: + n = len(vobj.Object.Shape.Edges) + for i in range(n): if len(vobj.Object.Labels) > i: if vobj.Object.Labels[i]: import Draft From 644a77a17f635574053ea8a73e3cdfaf5ccb0918 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Fri, 16 Jul 2021 12:22:56 +0200 Subject: [PATCH 12/20] Arch: Fixed windows invert hinge option --- src/Mod/Arch/ArchWindow.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Mod/Arch/ArchWindow.py b/src/Mod/Arch/ArchWindow.py index 4b9f92cdd5..7d3c049fae 100644 --- a/src/Mod/Arch/ArchWindow.py +++ b/src/Mod/Arch/ArchWindow.py @@ -1140,6 +1140,7 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): pairs = [["Mode"+str(i),"Mode"+str(i+1)] for i in range(1,len(WindowOpeningModes),2)] self.invertPairs(pairs) + FreeCAD.ActiveDocument.recompute() def invertHinge(self): @@ -1147,6 +1148,8 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): pairs = [["Edge6","Edge8"],["Edge5","Edge7"]] self.invertPairs(pairs) + self.invertOpening() + FreeCAD.ActiveDocument.recompute() def invertPairs(self,pairs): @@ -1166,7 +1169,6 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): nparts.append(part) if nparts != self.Object.WindowParts: self.Object.WindowParts = nparts - FreeCAD.ActiveDocument.recompute() else: FreeCAD.Console.PrintWarning(translate("Arch","This window has no defined opening")+"\n") From f8f541c1e73c4a36a08b62b16e4dfb898787dea6 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Fri, 16 Jul 2021 12:24:26 +0200 Subject: [PATCH 13/20] Draft: Use different icon if object has AutoUpdate turned off --- src/Mod/Draft/draftviewproviders/view_base.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Mod/Draft/draftviewproviders/view_base.py b/src/Mod/Draft/draftviewproviders/view_base.py index 162f0db950..43751820b0 100644 --- a/src/Mod/Draft/draftviewproviders/view_base.py +++ b/src/Mod/Draft/draftviewproviders/view_base.py @@ -465,6 +465,9 @@ class ViewProviderDraft(object): return ":/icons/Draft_N-Curve.svg" elif tp in ("ShapeString"): return ":/icons/Draft_ShapeString_tree.svg" + if hasattr(self.Object,"AutoUpdate") and not self.Object.AutoUpdate: + import TechDrawGui + return ":/icons/TechDraw_TreePageUnsync.svg" return ":/icons/Draft_Draft.svg" def claimChildren(self): From 10faf786c8cb1401ab0a66aeab6e533d5fc128e8 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Fri, 16 Jul 2021 12:42:30 +0200 Subject: [PATCH 14/20] Draft: Fixed wrong placement of lines in shape2dview's cutface mode --- src/Mod/Draft/draftobjects/shape2dview.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mod/Draft/draftobjects/shape2dview.py b/src/Mod/Draft/draftobjects/shape2dview.py index 96a2cd10e4..279e8d338a 100644 --- a/src/Mod/Draft/draftobjects/shape2dview.py +++ b/src/Mod/Draft/draftobjects/shape2dview.py @@ -292,11 +292,11 @@ class Shape2DView(DraftObject): if sh.Volume < 0: sh.reverse() c = sh.section(cutp) + if hasattr(obj,"InPlace"): + if not obj.InPlace: + c = self.getProjected(obj, c, proj) faces = [] if (obj.ProjectionMode == "Cutfaces") and (sh.ShapeType == "Solid"): - if hasattr(obj,"InPlace"): - if not obj.InPlace: - c = self.getProjected(obj, c, proj) wires = DraftGeomUtils.findWires(c.Edges) for w in wires: if w.isClosed(): From 895c817f561b0bcd917b24a6940674b70887ecbd Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Fri, 16 Jul 2021 08:23:23 +0200 Subject: [PATCH 15/20] FEM: calculix writer, use mat geo sets instead of ccx elsets --- src/Mod/Fem/femmesh/meshsetsgetter.py | 59 ++++++++++--------- .../calculix/write_constraint_fluidsection.py | 14 ++--- .../calculix/write_femelement_geometry.py | 2 +- .../calculix/write_femelement_matgeosets.py | 4 +- src/Mod/Fem/femsolver/calculix/writer.py | 4 +- src/Mod/Fem/femsolver/writerbase.py | 7 +-- 6 files changed, 44 insertions(+), 46 deletions(-) diff --git a/src/Mod/Fem/femmesh/meshsetsgetter.py b/src/Mod/Fem/femmesh/meshsetsgetter.py index 5669b093e2..6394e77ce3 100644 --- a/src/Mod/Fem/femmesh/meshsetsgetter.py +++ b/src/Mod/Fem/femmesh/meshsetsgetter.py @@ -89,6 +89,7 @@ class MeshSetsGetter(): self.femelement_faces_table = {} self.femelement_edges_table = {} self.femelement_count_test = True + self.mat_geo_sets = [] # ******************************************************************************************** # ******************************************************************************************** @@ -652,7 +653,7 @@ class MeshSetsGetter(): if len(self.member.mats_linear) > 1: self.get_material_elements() - # create the ccx_elsets + # create the mat_geo_sets if len(self.member.mats_linear) == 1: if self.femmesh.Volumes: # we only could do this for volumes, if a mesh contains volumes @@ -660,19 +661,19 @@ class MeshSetsGetter(): # but a mesh could contain the element faces of the volumes as faces # and the edges of the faces as edges # there we have to check for some geometric objects - self.get_ccx_elsets_single_mat_solid() + self.get_mat_geo_sets_single_mat_solid() if len(self.member.geos_shellthickness) == 1: - self.get_ccx_elsets_single_mat_single_shell() + self.get_mat_geo_sets_single_mat_single_shell() elif len(self.member.geos_shellthickness) > 1: - self.get_ccx_elsets_single_mat_multiple_shell() + self.get_mat_geo_sets_single_mat_multiple_shell() if len(self.member.geos_beamsection) == 1: - self.get_ccx_elsets_single_mat_single_beam() + self.get_mat_geo_sets_single_mat_single_beam() elif len(self.member.geos_beamsection) > 1: - self.get_ccx_elsets_single_mat_multiple_beam() + self.get_mat_geo_sets_single_mat_multiple_beam() if len(self.member.geos_fluidsection) == 1: - self.get_ccx_elsets_single_mat_single_fluid() + self.get_mat_geo_sets_single_mat_single_fluid() elif len(self.member.geos_fluidsection) > 1: - self.get_ccx_elsets_single_mat_multiple_fluid() + self.get_mat_geo_sets_single_mat_multiple_fluid() elif len(self.member.mats_linear) > 1: if self.femmesh.Volumes: # we only could do this for volumes, if a mseh contains volumes @@ -682,19 +683,19 @@ class MeshSetsGetter(): # there we have to check for some geometric objects # volume is a bit special # because retrieving ids from group mesh data is implemented - self.get_ccx_elsets_multiple_mat_solid() + self.get_mat_geo_sets_multiple_mat_solid() if len(self.member.geos_shellthickness) == 1: - self.get_ccx_elsets_multiple_mat_single_shell() + self.get_mat_geo_sets_multiple_mat_single_shell() elif len(self.member.geos_shellthickness) > 1: - self.get_ccx_elsets_multiple_mat_multiple_shell() + self.get_mat_geo_sets_multiple_mat_multiple_shell() if len(self.member.geos_beamsection) == 1: - self.get_ccx_elsets_multiple_mat_single_beam() + self.get_mat_geo_sets_multiple_mat_single_beam() elif len(self.member.geos_beamsection) > 1: - self.get_ccx_elsets_multiple_mat_multiple_beam() + self.get_mat_geo_sets_multiple_mat_multiple_beam() if len(self.member.geos_fluidsection) == 1: - self.get_ccx_elsets_multiple_mat_single_fluid() + self.get_mat_geo_sets_multiple_mat_single_fluid() elif len(self.member.geos_fluidsection) > 1: - self.get_ccx_elsets_multiple_mat_multiple_fluid() + self.get_mat_geo_sets_multiple_mat_multiple_fluid() # self.mat_geo_sets = [ { # "ccx_elset" : [e1, e2, e3, ... , en] or elements set name strings @@ -712,7 +713,7 @@ class MeshSetsGetter(): # TODO support multiple beamrotations # we do not need any more any data from the rotation document object, # thus we do not need to save the rotation document object name in the else - def get_ccx_elsets_single_mat_single_beam(self): + def get_mat_geo_sets_single_mat_single_beam(self): mat_obj = self.member.mats_linear[0]["Object"] beamsec_obj = self.member.geos_beamsection[0]["Object"] beamrot_data = self.member.geos_beamrotation[0] @@ -735,7 +736,7 @@ class MeshSetsGetter(): ccx_elset["beam_normal"] = beamdirection["normal"] self.mat_geo_sets.append(ccx_elset) - def get_ccx_elsets_single_mat_multiple_beam(self): + def get_mat_geo_sets_single_mat_multiple_beam(self): mat_obj = self.member.mats_linear[0]["Object"] beamrot_data = self.member.geos_beamrotation[0] for beamsec_data in self.member.geos_beamsection: @@ -762,7 +763,7 @@ class MeshSetsGetter(): ccx_elset["beam_normal"] = beamdirection["normal"] self.mat_geo_sets.append(ccx_elset) - def get_ccx_elsets_multiple_mat_single_beam(self): + def get_mat_geo_sets_multiple_mat_single_beam(self): beamsec_obj = self.member.geos_beamsection[0]["Object"] beamrot_data = self.member.geos_beamrotation[0] for mat_data in self.member.mats_linear: @@ -788,7 +789,7 @@ class MeshSetsGetter(): ccx_elset["beam_normal"] = beamdirection["normal"] self.mat_geo_sets.append(ccx_elset) - def get_ccx_elsets_multiple_mat_multiple_beam(self): + def get_mat_geo_sets_multiple_mat_multiple_beam(self): beamrot_data = self.member.geos_beamrotation[0] for beamsec_data in self.member.geos_beamsection: beamsec_obj = beamsec_data["Object"] @@ -820,7 +821,7 @@ class MeshSetsGetter(): self.mat_geo_sets.append(ccx_elset) # fluid - def get_ccx_elsets_single_mat_single_fluid(self): + def get_mat_geo_sets_single_mat_single_fluid(self): mat_obj = self.member.mats_linear[0]["Object"] fluidsec_obj = self.member.geos_fluidsection[0]["Object"] elset_data = self.ccx_eedges @@ -833,7 +834,7 @@ class MeshSetsGetter(): ccx_elset["fluidsection_obj"] = fluidsec_obj self.mat_geo_sets.append(ccx_elset) - def get_ccx_elsets_single_mat_multiple_fluid(self): + def get_mat_geo_sets_single_mat_multiple_fluid(self): mat_obj = self.member.mats_linear[0]["Object"] for fluidsec_data in self.member.geos_fluidsection: fluidsec_obj = fluidsec_data["Object"] @@ -847,7 +848,7 @@ class MeshSetsGetter(): ccx_elset["fluidsection_obj"] = fluidsec_obj self.mat_geo_sets.append(ccx_elset) - def get_ccx_elsets_multiple_mat_single_fluid(self): + def get_mat_geo_sets_multiple_mat_single_fluid(self): fluidsec_obj = self.member.geos_fluidsection[0]["Object"] for mat_data in self.member.mats_linear: mat_obj = mat_data["Object"] @@ -861,7 +862,7 @@ class MeshSetsGetter(): ccx_elset["fluidsection_obj"] = fluidsec_obj self.mat_geo_sets.append(ccx_elset) - def get_ccx_elsets_multiple_mat_multiple_fluid(self): + def get_mat_geo_sets_multiple_mat_multiple_fluid(self): for fluidsec_data in self.member.geos_fluidsection: fluidsec_obj = fluidsec_data["Object"] for mat_data in self.member.mats_linear: @@ -884,7 +885,7 @@ class MeshSetsGetter(): self.mat_geo_sets.append(ccx_elset) # shell - def get_ccx_elsets_single_mat_single_shell(self): + def get_mat_geo_sets_single_mat_single_shell(self): mat_obj = self.member.mats_linear[0]["Object"] shellth_obj = self.member.geos_shellthickness[0]["Object"] elset_data = self.ccx_efaces @@ -900,7 +901,7 @@ class MeshSetsGetter(): ccx_elset["shellthickness_obj"] = shellth_obj self.mat_geo_sets.append(ccx_elset) - def get_ccx_elsets_single_mat_multiple_shell(self): + def get_mat_geo_sets_single_mat_multiple_shell(self): mat_obj = self.member.mats_linear[0]["Object"] for shellth_data in self.member.geos_shellthickness: shellth_obj = shellth_data["Object"] @@ -917,7 +918,7 @@ class MeshSetsGetter(): ccx_elset["shellthickness_obj"] = shellth_obj self.mat_geo_sets.append(ccx_elset) - def get_ccx_elsets_multiple_mat_single_shell(self): + def get_mat_geo_sets_multiple_mat_single_shell(self): shellth_obj = self.member.geos_shellthickness[0]["Object"] for mat_data in self.member.mats_linear: mat_obj = mat_data["Object"] @@ -934,7 +935,7 @@ class MeshSetsGetter(): ccx_elset["shellthickness_obj"] = shellth_obj self.mat_geo_sets.append(ccx_elset) - def get_ccx_elsets_multiple_mat_multiple_shell(self): + def get_mat_geo_sets_multiple_mat_multiple_shell(self): for shellth_data in self.member.geos_shellthickness: shellth_obj = shellth_data["Object"] for mat_data in self.member.mats_linear: @@ -957,7 +958,7 @@ class MeshSetsGetter(): self.mat_geo_sets.append(ccx_elset) # solid - def get_ccx_elsets_single_mat_solid(self): + def get_mat_geo_sets_single_mat_solid(self): mat_obj = self.member.mats_linear[0]["Object"] elset_data = self.ccx_evolumes names = [ @@ -972,7 +973,7 @@ class MeshSetsGetter(): self.mat_geo_sets.append(ccx_elset) print(self.mat_geo_sets) - def get_ccx_elsets_multiple_mat_solid(self): + def get_mat_geo_sets_multiple_mat_solid(self): for mat_data in self.member.mats_linear: mat_obj = mat_data["Object"] elset_data = mat_data["FEMElements"] diff --git a/src/Mod/Fem/femsolver/calculix/write_constraint_fluidsection.py b/src/Mod/Fem/femsolver/calculix/write_constraint_fluidsection.py index a142629504..1c07af5e5e 100644 --- a/src/Mod/Fem/femsolver/calculix/write_constraint_fluidsection.py +++ b/src/Mod/Fem/femsolver/calculix/write_constraint_fluidsection.py @@ -49,8 +49,8 @@ def handle_fluidsection_liquid_inlet_outlet(inpfile, ccxwriter): # Fluid sections: # fluidsection Liquid inlet outlet objs requires special element definition # to fill ccxwriter.FluidInletoutlet_ele list the ccx_elset are needed - # thus this has to be after the creation of ccx_elsets - # different pipe cross sections will generate ccx_elsets + # thus this has to be after the creation of mat_geo_sets + # different pipe cross sections will generate mat_geo_sets ccxwriter.FluidInletoutlet_ele = [] ccxwriter.fluid_inout_nodes_file = join( @@ -76,8 +76,8 @@ def handle_fluidsection_liquid_inlet_outlet(inpfile, ccxwriter): return fluidsec_obj return None - def is_fluidsection_inoutlet_setnames_possible(ccx_elsets): - for ccx_elset in ccx_elsets: + def is_fluidsection_inoutlet_setnames_possible(mat_geo_sets): + for ccx_elset in mat_geo_sets: if ( ccx_elset["ccx_elset"] and "fluidsection_obj" in ccx_elset # fluid mesh @@ -95,7 +95,7 @@ def handle_fluidsection_liquid_inlet_outlet(inpfile, ccxwriter): # collect elementIDs for fluidsection Liquid inlet outlet objs # if they have element data (happens if not "eall") - for ccx_elset in ccxwriter.ccx_elsets: + for ccx_elset in ccxwriter.mat_geo_sets: fluidsec_obj = get_fluidsection_inoutlet_obj_if_setdata(ccx_elset) if fluidsec_obj is None: continue @@ -118,9 +118,9 @@ def handle_fluidsection_liquid_inlet_outlet(inpfile, ccxwriter): ) # create the correct element definition for fluidsection Liquid inlet outlet objs - # at least one "fluidsection_obj" needs to be in ccx_elsets and has the attributes + # at least one "fluidsection_obj" needs to be in mat_geo_sets and has the attributes # TODO: what if there are other objs in elsets? - if is_fluidsection_inoutlet_setnames_possible(ccxwriter.ccx_elsets) is not None: + if is_fluidsection_inoutlet_setnames_possible(ccxwriter.mat_geo_sets) is not None: # it is not distinguished if split input file # for split input file the main file is just closed and reopend even if not needed inpfile.close() diff --git a/src/Mod/Fem/femsolver/calculix/write_femelement_geometry.py b/src/Mod/Fem/femsolver/calculix/write_femelement_geometry.py index 559dd9cb85..e38b044ee1 100644 --- a/src/Mod/Fem/femsolver/calculix/write_femelement_geometry.py +++ b/src/Mod/Fem/femsolver/calculix/write_femelement_geometry.py @@ -32,7 +32,7 @@ def write_femelement_geometry(f, ccxwriter): f.write("\n{}\n".format(59 * "*")) f.write("** Sections\n") - for ccx_elset in ccxwriter.ccx_elsets: + for ccx_elset in ccxwriter.mat_geo_sets: if ccx_elset["ccx_elset"]: elsetdef = "ELSET={}, ".format(ccx_elset["ccx_elset_name"]) material = "MATERIAL={}".format(ccx_elset["mat_obj_name"]) diff --git a/src/Mod/Fem/femsolver/calculix/write_femelement_matgeosets.py b/src/Mod/Fem/femsolver/calculix/write_femelement_matgeosets.py index 49b32c9404..01f0f6720f 100644 --- a/src/Mod/Fem/femsolver/calculix/write_femelement_matgeosets.py +++ b/src/Mod/Fem/femsolver/calculix/write_femelement_matgeosets.py @@ -31,11 +31,11 @@ import six def write_femelement_matgeosets(f, ccxwriter): - # write ccx_elsets to file + # write mat_geo_sets to file f.write("\n{}\n".format(59 * "*")) f.write("** Element sets for materials and FEM element type (solid, shell, beam, fluid)\n") - for ccx_elset in ccxwriter.ccx_elsets: + for ccx_elset in ccxwriter.mat_geo_sets: f.write("*ELSET,ELSET={}\n".format(ccx_elset["ccx_elset_name"])) diff --git a/src/Mod/Fem/femsolver/calculix/writer.py b/src/Mod/Fem/femsolver/calculix/writer.py index 56439cf5bf..327cbbf0ae 100644 --- a/src/Mod/Fem/femsolver/calculix/writer.py +++ b/src/Mod/Fem/femsolver/calculix/writer.py @@ -134,7 +134,7 @@ class FemInputWriterCcx(writerbase.FemInputWriter): FreeCAD.Console.PrintMessage("Get mesh sets.\n") time_start = time.process_time() - if not self.ccx_elsets: + if not self.mat_geo_sets: self.get_mesh_sets() time_getsets = time.process_time() @@ -155,7 +155,7 @@ class FemInputWriterCcx(writerbase.FemInputWriter): # element sets for materials and element geometry write_femelement_matgeosets.write_femelement_matgeosets(inpfile, self) - # some fluidsection objs need special treatment, ccx_elsets are needed for this + # some fluidsection objs need special treatment, mat_geo_sets are needed for this inpfile = con_fluidsection.handle_fluidsection_liquid_inlet_outlet(inpfile, self) # element sets constraints diff --git a/src/Mod/Fem/femsolver/writerbase.py b/src/Mod/Fem/femsolver/writerbase.py index e18f48ed46..f4870f8918 100644 --- a/src/Mod/Fem/femsolver/writerbase.py +++ b/src/Mod/Fem/femsolver/writerbase.py @@ -76,10 +76,7 @@ class FemInputWriter(): self.ccx_evolumes = "Evolumes" self.ccx_efaces = "Efaces" self.ccx_eedges = "Eedges" - if mat_geo_sets is not None: - self.ccx_elsets = mat_geo_sets - else: - self.ccx_elsets = [] + self.mat_geo_sets = mat_geo_sets if self.mesh_object: if hasattr(self.mesh_object, "Shape"): self.theshape = self.mesh_object.Shape @@ -304,7 +301,7 @@ class FemInputWriter(): def get_element_sets_material_and_femelement_geometry(self): self.meshdatagetter.get_element_sets_material_and_femelement_geometry() - self.ccx_elsets = self.meshdatagetter.mat_geo_sets + self.mat_geo_sets = self.meshdatagetter.mat_geo_sets ## @} From fcb91f36af7b4ff0f501e06f761ba2ab40ab23af Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Fri, 16 Jul 2021 07:44:08 +0200 Subject: [PATCH 16/20] FEM: make use of mesh data getter class for calculix solver and ccxtools --- src/Mod/Fem/femsolver/calculix/tasks.py | 23 ++++++++-- src/Mod/Fem/femsolver/calculix/writer.py | 2 - src/Mod/Fem/femsolver/writerbase.py | 58 +++--------------------- src/Mod/Fem/femtools/ccxtools.py | 19 +++++++- 4 files changed, 43 insertions(+), 59 deletions(-) diff --git a/src/Mod/Fem/femsolver/calculix/tasks.py b/src/Mod/Fem/femsolver/calculix/tasks.py index 94a05b3d3c..d20bc6e158 100644 --- a/src/Mod/Fem/femsolver/calculix/tasks.py +++ b/src/Mod/Fem/femsolver/calculix/tasks.py @@ -40,6 +40,7 @@ from .. import run from .. import settings from feminout import importCcxDatResults from feminout import importCcxFrdResults +from femmesh import meshsetsgetter from femtools import femutils from femtools import membertools @@ -60,12 +61,28 @@ class Prepare(run.Prepare): def run(self): global _inputFileName self.pushStatus("Preparing input files...\n") + + mesh_obj = membertools.get_mesh_to_solve(self.analysis)[0] # pre check done already + + # get mesh set data + # TODO evaluate if it makes sense to add new task + # between check and prepare to the solver frame work + meshdatagetter = meshsetsgetter.MeshSetsGetter( + self.analysis, + self.solver, + mesh_obj, + membertools.AnalysisMember(self.analysis), + ) + meshdatagetter.get_mesh_sets() + + # write input file w = writer.FemInputWriterCcx( self.analysis, self.solver, - membertools.get_mesh_to_solve(self.analysis)[0], # pre check has been done already - membertools.AnalysisMember(self.analysis), - self.directory + mesh_obj, + meshdatagetter.member, + self.directory, + meshdatagetter.mat_geo_sets ) path = w.write_calculix_input_file() # report to user if task succeeded diff --git a/src/Mod/Fem/femsolver/calculix/writer.py b/src/Mod/Fem/femsolver/calculix/writer.py index 327cbbf0ae..76cc60aa45 100644 --- a/src/Mod/Fem/femsolver/calculix/writer.py +++ b/src/Mod/Fem/femsolver/calculix/writer.py @@ -132,10 +132,8 @@ class FemInputWriterCcx(writerbase.FemInputWriter): # write calculix input def write_calculix_input_file(self): - FreeCAD.Console.PrintMessage("Get mesh sets.\n") time_start = time.process_time() if not self.mat_geo_sets: - self.get_mesh_sets() time_getsets = time.process_time() FreeCAD.Console.PrintMessage( diff --git a/src/Mod/Fem/femsolver/writerbase.py b/src/Mod/Fem/femsolver/writerbase.py index f4870f8918..ae7c432c8f 100644 --- a/src/Mod/Fem/femsolver/writerbase.py +++ b/src/Mod/Fem/femsolver/writerbase.py @@ -97,6 +97,7 @@ class FemInputWriter(): "In rare cases this might not be an error. " ) + # ************************************************* # deprecated, leave for compatibility reasons # if these are calculated here they are calculated twice :-( self.femnodes_mesh = {} @@ -110,6 +111,9 @@ class FemInputWriter(): self.femelement_edges_table = {} self.femelement_count_test = True + # deprecated, leave for compatibility reasons + # do not add new objects + # only the ones which exists on 0.19 release are kept # materials self.material_objects = member.mats_linear self.material_nonlinear_objects = member.mats_nonlinear @@ -119,7 +123,6 @@ class FemInputWriter(): self.fluidsection_objects = member.geos_fluidsection self.shellthickness_objects = member.geos_shellthickness # constraints - self.centrif_objects = member.cons_centrif self.contact_objects = member.cons_contact self.displacement_objects = member.cons_displacement self.fixed_objects = member.cons_fixed @@ -128,7 +131,6 @@ class FemInputWriter(): self.initialtemperature_objects = member.cons_initialtemperature self.planerotation_objects = member.cons_planerotation self.pressure_objects = member.cons_pressure - self.sectionprint_objects = member.cons_sectionprint self.selfweight_objects = member.cons_selfweight self.temperature_objects = member.cons_temperature self.tie_objects = member.cons_tie @@ -220,46 +222,8 @@ class FemInputWriter(): f.write(write_after) # ******************************************************************************************** - # ******************************************************************************************** - # use set for node sets to be sure all nodes are unique - # use sorted to be sure the order is the same on different runs - # be aware a sorted set returns a list, because set are not sorted by default - # - done in return value of meshtools.get_femnodes_by_femobj_with_references - # TODO FIXME might be appropriate for element sets and surfaceface sets too - - # ******************************************************************************************** - # get all known sets - def get_mesh_sets(self): - FreeCAD.Console.PrintMessage( - "Get mesh data for " - "node sets (groups), surface sets (groups) and element sets (groups)\n" - ) - - # materials and element geometry element sets getter - self.get_element_sets_material_and_femelement_geometry() - - # constraints element sets getter - self.get_constraints_centrif_elements() - - # constraints node sets getter - self.get_constraints_fixed_nodes() - self.get_constraints_displacement_nodes() - self.get_constraints_planerotation_nodes() - - # constraints surface sets getter - self.get_constraints_contact_faces() - self.get_constraints_tie_faces() - self.get_constraints_sectionprint_faces() - self.get_constraints_transform_nodes() - self.get_constraints_temperature_nodes() - - # constraints sets with constraint data - self.get_constraints_force_nodeloads() - self.get_constraints_pressure_faces() - self.get_constraints_heatflux_faces() - - # ******************************************************************************************** - # sets + # deprecated, do not add new constraints + # only the ones which exists on 0.19 release are kept def get_constraints_fixed_nodes(self): self.meshdatagetter.get_constraints_fixed_nodes() @@ -290,18 +254,8 @@ class FemInputWriter(): def get_constraints_tie_faces(self): self.meshdatagetter.get_constraints_tie_faces() - def get_constraints_sectionprint_faces(self): - self.meshdatagetter.get_constraints_sectionprint_faces() - def get_constraints_heatflux_faces(self): self.meshdatagetter.get_constraints_heatflux_faces() - def get_constraints_centrif_elements(self): - self.meshdatagetter.get_constraints_centrif_elements() - - def get_element_sets_material_and_femelement_geometry(self): - self.meshdatagetter.get_element_sets_material_and_femelement_geometry() - self.mat_geo_sets = self.meshdatagetter.mat_geo_sets - ## @} diff --git a/src/Mod/Fem/femtools/ccxtools.py b/src/Mod/Fem/femtools/ccxtools.py index 2ed2cee588..06e3f950e2 100644 --- a/src/Mod/Fem/femtools/ccxtools.py +++ b/src/Mod/Fem/femtools/ccxtools.py @@ -366,6 +366,20 @@ class FemToolsCcx(QtCore.QRunnable, QtCore.QObject): self.set_inp_file_name() def write_inp_file(self): + + # get mesh set data + # TODO use separate method for getting the mesh set data + from femmesh import meshsetsgetter + meshdatagetter = meshsetsgetter.MeshSetsGetter( + self.analysis, + self.solver, + self.mesh, + membertools.AnalysisMember(self.analysis), + ) + # save the sets into the member objects of the instanz meshdatagetter + meshdatagetter.get_mesh_sets() + + # write input file import femsolver.calculix.writer as iw self.inp_file_name = "" try: @@ -373,8 +387,9 @@ class FemToolsCcx(QtCore.QRunnable, QtCore.QObject): self.analysis, self.solver, self.mesh, - self.member, - self.working_dir + meshdatagetter.member, + self.working_dir, + meshdatagetter.mat_geo_sets ) self.inp_file_name = inp_writer.write_calculix_input_file() except Exception: From e172c075348b1dfeae39cec6ad52862bf027a9ea Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Sat, 17 Jul 2021 20:09:38 +0200 Subject: [PATCH 17/20] FEM: calculix writer, fix time output --- src/Mod/Fem/femsolver/calculix/writer.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/Mod/Fem/femsolver/calculix/writer.py b/src/Mod/Fem/femsolver/calculix/writer.py index 76cc60aa45..e338c2aecb 100644 --- a/src/Mod/Fem/femsolver/calculix/writer.py +++ b/src/Mod/Fem/femsolver/calculix/writer.py @@ -133,13 +133,11 @@ class FemInputWriterCcx(writerbase.FemInputWriter): def write_calculix_input_file(self): time_start = time.process_time() - if not self.mat_geo_sets: - time_getsets = time.process_time() - FreeCAD.Console.PrintMessage( "Start writing CalculiX input file to: {}\n" .format(self.file_name) ) + if self.solver_obj.SplitInputWriter is True: FreeCAD.Console.PrintMessage("Split input file.\n") self.split_inpfile = True @@ -207,19 +205,10 @@ class FemInputWriterCcx(writerbase.FemInputWriter): # close file inpfile.close() - setstime = round((time_getsets - time_start), 3) - writetime = round((time.process_time() - time_getsets), 3) - all_time = round((setstime + writetime), 3) - FreeCAD.Console.PrintMessage( - "Getting mesh sets or groups time: {} seconds \n".format(setstime) - ) + writetime = round((time.process_time() - time_start), 3) FreeCAD.Console.PrintMessage( "Writing time CalculiX input file: {} seconds \n".format(writetime) ) - FreeCAD.Console.PrintMessage( - "Overall time CalculiX input file: {} seconds \n\n" - .format(all_time) - ) # return if self.femelement_count_test is True: From c62334412af1a844fb98a69ff1422709956c05c6 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Sun, 18 Jul 2021 10:14:47 +0200 Subject: [PATCH 18/20] FEM: mesh set getter, output print --- src/Mod/Fem/femmesh/meshsetsgetter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femmesh/meshsetsgetter.py b/src/Mod/Fem/femmesh/meshsetsgetter.py index 6394e77ce3..a0834f5d71 100644 --- a/src/Mod/Fem/femmesh/meshsetsgetter.py +++ b/src/Mod/Fem/femmesh/meshsetsgetter.py @@ -77,7 +77,7 @@ class MeshSetsGetter(): else: FreeCAD.Console.PrintWarning( "No finite element mesh object was given to the writer class. " - "In rare cases this might not be an error. " + "In rare cases this might not be an error.\n" ) self.femnodes_mesh = {} self.femelement_table = {} From 21889b16d5e54c71f0d33225d5d63a653c2faa39 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Sun, 18 Jul 2021 14:29:43 +0200 Subject: [PATCH 19/20] FEM: writer base, typo --- src/Mod/Fem/femsolver/writerbase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femsolver/writerbase.py b/src/Mod/Fem/femsolver/writerbase.py index ae7c432c8f..645456632b 100644 --- a/src/Mod/Fem/femsolver/writerbase.py +++ b/src/Mod/Fem/femsolver/writerbase.py @@ -51,7 +51,7 @@ class FemInputWriter(): self.solver_obj = solver_obj self.mesh_object = mesh_obj self.member = member - # mor attributes + # more attributes self.analysis_type = self.solver_obj.AnalysisType self.document = self.analysis.Document # working dir From 2aeba3674ae06bb8ce3cf2f23e30028acad718ac Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Sun, 18 Jul 2021 14:36:04 +0200 Subject: [PATCH 20/20] FEM: tasks and writer, use generic solver input writing method name --- src/Mod/Fem/femsolver/calculix/tasks.py | 2 +- src/Mod/Fem/femsolver/calculix/writer.py | 2 +- src/Mod/Fem/femsolver/elmer/tasks.py | 2 +- src/Mod/Fem/femsolver/elmer/writer.py | 2 +- src/Mod/Fem/femsolver/z88/tasks.py | 2 +- src/Mod/Fem/femsolver/z88/writer.py | 2 +- src/Mod/Fem/femtools/ccxtools.py | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Mod/Fem/femsolver/calculix/tasks.py b/src/Mod/Fem/femsolver/calculix/tasks.py index d20bc6e158..ea99c1a347 100644 --- a/src/Mod/Fem/femsolver/calculix/tasks.py +++ b/src/Mod/Fem/femsolver/calculix/tasks.py @@ -84,7 +84,7 @@ class Prepare(run.Prepare): self.directory, meshdatagetter.mat_geo_sets ) - path = w.write_calculix_input_file() + path = w.write_solver_input() # report to user if task succeeded if path != "": self.pushStatus("Write completed!") diff --git a/src/Mod/Fem/femsolver/calculix/writer.py b/src/Mod/Fem/femsolver/calculix/writer.py index e338c2aecb..6f9d365a86 100644 --- a/src/Mod/Fem/femsolver/calculix/writer.py +++ b/src/Mod/Fem/femsolver/calculix/writer.py @@ -130,7 +130,7 @@ class FemInputWriterCcx(writerbase.FemInputWriter): # ******************************************************************************************** # write calculix input - def write_calculix_input_file(self): + def write_solver_input(self): time_start = time.process_time() FreeCAD.Console.PrintMessage( diff --git a/src/Mod/Fem/femsolver/elmer/tasks.py b/src/Mod/Fem/femsolver/elmer/tasks.py index cd5c5e33b6..b56792fa17 100644 --- a/src/Mod/Fem/femsolver/elmer/tasks.py +++ b/src/Mod/Fem/femsolver/elmer/tasks.py @@ -84,7 +84,7 @@ class Prepare(run.Prepare): FreeCAD.Console.PrintLog("Machine testmode: {}\n".format(self.testmode)) w = writer.Writer(self.solver, self.directory) try: - w.write() + w.write_solver_input() self.checkHandled(w) except writer.WriteError as e: self.report.error(str(e)) diff --git a/src/Mod/Fem/femsolver/elmer/writer.py b/src/Mod/Fem/femsolver/elmer/writer.py index b8138d95a4..ae2120886c 100644 --- a/src/Mod/Fem/femsolver/elmer/writer.py +++ b/src/Mod/Fem/femsolver/elmer/writer.py @@ -79,7 +79,7 @@ class Writer(object): def getHandledConstraints(self): return self._handledObjects - def write(self): + def write_solver_input(self): self._handleRedifinedConstants() self._handleSimulation() self._handleHeat() diff --git a/src/Mod/Fem/femsolver/z88/tasks.py b/src/Mod/Fem/femsolver/z88/tasks.py index 774e994a43..e931c4d5bc 100644 --- a/src/Mod/Fem/femsolver/z88/tasks.py +++ b/src/Mod/Fem/femsolver/z88/tasks.py @@ -61,7 +61,7 @@ class Prepare(run.Prepare): membertools.AnalysisMember(self.analysis), self.directory ) - path = w.write_z88_input() + path = w.write_solver_input() # report to user if task succeeded if path is not None: self.pushStatus("Write completed!") diff --git a/src/Mod/Fem/femsolver/z88/writer.py b/src/Mod/Fem/femsolver/z88/writer.py index 7d145fa115..873ecfc6da 100644 --- a/src/Mod/Fem/femsolver/z88/writer.py +++ b/src/Mod/Fem/femsolver/z88/writer.py @@ -63,7 +63,7 @@ class FemInputWriterZ88(writerbase.FemInputWriter): "FemInputWriterZ88 --> self.file_name --> " + self.file_name + "\n" ) - def write_z88_input(self): + def write_solver_input(self): timestart = time.process_time() FreeCAD.Console.PrintMessage("Write z88 input files to: {}\n".format(self.dir_name)) if not self.femnodes_mesh: diff --git a/src/Mod/Fem/femtools/ccxtools.py b/src/Mod/Fem/femtools/ccxtools.py index 06e3f950e2..7f262bfa85 100644 --- a/src/Mod/Fem/femtools/ccxtools.py +++ b/src/Mod/Fem/femtools/ccxtools.py @@ -391,7 +391,7 @@ class FemToolsCcx(QtCore.QRunnable, QtCore.QObject): self.working_dir, meshdatagetter.mat_geo_sets ) - self.inp_file_name = inp_writer.write_calculix_input_file() + self.inp_file_name = inp_writer.write_solver_input() except Exception: FreeCAD.Console.PrintError( "Unexpected error when writing CalculiX input file: {}\n"