From 458a27f2fcd311140d3344e115de83a577ed4735 Mon Sep 17 00:00:00 2001 From: UR-0 Date: Wed, 19 Feb 2020 17:56:39 +0100 Subject: [PATCH] FEM: constraint tie, implement object and ccx writer --- src/Mod/Fem/CMakeLists.txt | 2 + src/Mod/Fem/ObjectsFem.py | 17 ++++ .../_ViewProviderFemConstraintTie.py | 80 +++++++++++++++++++ src/Mod/Fem/femobjects/_FemConstraintTie.py | 44 ++++++++++ src/Mod/Fem/femsolver/calculix/writer.py | 75 +++++++++++++++++ src/Mod/Fem/femsolver/writerbase.py | 1 + src/Mod/Fem/femtest/app/test_object.py | 33 ++++++++ src/Mod/Fem/femtools/ccxtools.py | 11 +++ src/Mod/Fem/femtools/membertools.py | 7 ++ 9 files changed, 270 insertions(+) create mode 100644 src/Mod/Fem/femguiobjects/_ViewProviderFemConstraintTie.py create mode 100644 src/Mod/Fem/femobjects/_FemConstraintTie.py diff --git a/src/Mod/Fem/CMakeLists.txt b/src/Mod/Fem/CMakeLists.txt index db0d0d8f7a..2b4f10838c 100755 --- a/src/Mod/Fem/CMakeLists.txt +++ b/src/Mod/Fem/CMakeLists.txt @@ -229,6 +229,7 @@ SET(FemObjectsScripts_SRCS femobjects/_FemConstraintFlowVelocity.py femobjects/_FemConstraintInitialFlowVelocity.py femobjects/_FemConstraintSelfWeight.py + femobjects/_FemConstraintTie.py femobjects/_FemElementFluid1D.py femobjects/_FemElementGeometry1D.py femobjects/_FemElementGeometry2D.py @@ -312,6 +313,7 @@ SET(FemGuiScripts_SRCS femguiobjects/_ViewProviderFemConstraintFlowVelocity.py femguiobjects/_ViewProviderFemConstraintInitialFlowVelocity.py femguiobjects/_ViewProviderFemConstraintSelfWeight.py + femguiobjects/_ViewProviderFemConstraintTie.py femguiobjects/_ViewProviderFemElementFluid1D.py femguiobjects/_ViewProviderFemElementGeometry1D.py femguiobjects/_ViewProviderFemElementGeometry2D.py diff --git a/src/Mod/Fem/ObjectsFem.py b/src/Mod/Fem/ObjectsFem.py index c630e5fd51..4bf1134ad9 100644 --- a/src/Mod/Fem/ObjectsFem.py +++ b/src/Mod/Fem/ObjectsFem.py @@ -258,6 +258,23 @@ def makeConstraintTransform( return obj +def makeConstraintTie( + doc, + name="ConstraintTie" +): + """makeConstraintTie(document, [name]): + creates an tie object to define bonded faces constraint""" + obj = doc.addObject("Fem::ConstraintPython", name) + from femobjects import _FemConstraintTie + _FemConstraintTie._FemConstraintTie(obj) + if FreeCAD.GuiUp: + from femguiobjects import _ViewProviderFemConstraintTie + _ViewProviderFemConstraintTie._ViewProviderFemConstraintTie( + obj.ViewObject + ) + return obj + + # ********* element definition objects *********************************************************** def makeElementFluid1D( doc, diff --git a/src/Mod/Fem/femguiobjects/_ViewProviderFemConstraintTie.py b/src/Mod/Fem/femguiobjects/_ViewProviderFemConstraintTie.py new file mode 100644 index 0000000000..8350d24e27 --- /dev/null +++ b/src/Mod/Fem/femguiobjects/_ViewProviderFemConstraintTie.py @@ -0,0 +1,80 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2015 Bernd Hahnebach * +# * * +# * 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 constraint tie ViewProvider for the document object" +__author__ = "Bernd Hahnebach" +__url__ = "http://www.freecadweb.org" + +## @package ViewProviderFemConstraintTie +# \ingroup FEM +# \brief FreeCAD FEM _ViewProviderFemConstraintTie + +import FreeCAD +import FreeCADGui +import FemGui # needed to display the icons in TreeView + +# for the panel +from PySide import QtCore +from . import FemSelectionWidgets + +False if FemGui.__name__ else True # flake8, dummy FemGui usage + + +class _ViewProviderFemConstraintTie: + "A View Provider for the FemConstraintTie object" + + def __init__(self, vobj): + vobj.Proxy = self + + def getIcon(self): + return ":/icons/fem-constraint-tie.svg" + + def attach(self, vobj): + from pivy import coin + self.ViewObject = vobj + self.Object = vobj.Object + self.standard = coin.SoGroup() + vobj.addDisplayMode(self.standard, "Default") + + def getDisplayModes(self, obj): + return ["Default"] + + def getDefaultDisplayMode(self): + return "Default" + + def updateData(self, obj, prop): + return + + def onChanged(self, vobj, prop): + return + + def setEdit(self, vobj, mode=0): + return True + + def unsetEdit(self, vobj, mode=0): + return True + + def __getstate__(self): + return None + + def __setstate__(self, state): + return None diff --git a/src/Mod/Fem/femobjects/_FemConstraintTie.py b/src/Mod/Fem/femobjects/_FemConstraintTie.py new file mode 100644 index 0000000000..820b966852 --- /dev/null +++ b/src/Mod/Fem/femobjects/_FemConstraintTie.py @@ -0,0 +1,44 @@ +# *************************************************************************** +# * Copyright (c) 2015 Bernd Hahnebach * +# * * +# * 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 constraint tie document object" +__author__ = "Bernd Hahnebach" +__url__ = "https://www.freecadweb.org" + +## @package FemConstraintTie +# \ingroup FEM +# \brief FreeCAD FEM constraint tie object + + +class _FemConstraintTie: + "The FemConstraintTie object" + def __init__(self, obj): + obj.addProperty( + "App::PropertyLength", + "Tolerance", + "Geometry", + "set max gap between tied faces" + ) + obj.Proxy = self + self.Type = "Fem::ConstraintTie" + + def execute(self, obj): + return diff --git a/src/Mod/Fem/femsolver/calculix/writer.py b/src/Mod/Fem/femsolver/calculix/writer.py index 2db7ee84dc..1a2846f7df 100644 --- a/src/Mod/Fem/femsolver/calculix/writer.py +++ b/src/Mod/Fem/femsolver/calculix/writer.py @@ -117,6 +117,8 @@ class FemInputWriterCcx(writerbase.FemInputWriter): self.write_node_sets_constraints_planerotation(inpfile) if self.contact_objects: self.write_surfaces_constraints_contact(inpfile) + if self.tie_objects: + self.write_surfaces_constraints_tie(inpfile) if self.transform_objects: self.write_node_sets_constraints_transform(inpfile) if self.analysis_type == "thermomech" and self.temperature_objects: @@ -143,6 +145,8 @@ class FemInputWriterCcx(writerbase.FemInputWriter): self.write_constraints_planerotation(inpfile) if self.contact_objects: self.write_constraints_contact(inpfile) + if self.tie_objects: + self.write_constraints_tie(inpfile) if self.transform_objects: self.write_constraints_transform(inpfile) @@ -229,6 +233,8 @@ class FemInputWriterCcx(writerbase.FemInputWriter): inpfileHeatflux = open(name + "_Node_Heatlfux.inp", "w") if self.contact_objects: inpfileContact = open(name + "_Surface_Contact.inp", "w") + if self.tie_objects: + inpfileContact = open(name + "_Surface_Tie.inp", "w") if self.transform_objects: inpfileTransform = open(name + "_Node_Transform.inp", "w") @@ -242,6 +248,8 @@ class FemInputWriterCcx(writerbase.FemInputWriter): self.write_node_sets_constraints_planerotation(inpfileNodes) if self.contact_objects: self.write_surfaces_constraints_contact(inpfileContact) + if self.tie_objects: + self.write_surfaces_constraints_tie(inpfileContact) if self.transform_objects: self.write_node_sets_constraints_transform(inpfileTransform) @@ -260,6 +268,12 @@ class FemInputWriterCcx(writerbase.FemInputWriter): if self.contact_objects: inpfileMain.write("*INCLUDE,INPUT=" + include_name + "_Surface_Contact.inp \n") + inpfileMain.write("\n***********************************************************\n") + inpfileMain.write("** Surfaces for tie constraint\n") + inpfileMain.write("** written by write_surfaces_constraints_tie\n") + if self.tie_objects: + inpfileMain.write("*INCLUDE,INPUT=" + include_name + "_Surface_Tie.inp \n") + inpfileMain.write("\n***********************************************************\n") inpfileMain.write("** Node sets for transform constraint\n") inpfileMain.write("** written by write_node_sets_constraints_transform\n") @@ -295,6 +309,8 @@ class FemInputWriterCcx(writerbase.FemInputWriter): self.write_constraints_planerotation(inpfileMain) if self.contact_objects: self.write_constraints_contact(inpfileMain) + if self.tie_objects: + self.write_constraints_tie(inpfileMain) if self.transform_objects: self.write_constraints_transform(inpfileMain) @@ -574,6 +590,46 @@ class FemInputWriterCcx(writerbase.FemInputWriter): for i in femobj["ContactMasterFaces"]: f.write("{},S{}\n".format(i[0], i[1])) + def write_surfaces_constraints_tie(self, f): + # get surface nodes and write them to file + f.write("\n***********************************************************\n") + f.write("** Surfaces for tie constraint\n") + f.write("** written by {} function\n".format(sys._getframe().f_code.co_name)) + obj = 0 + for femobj in self.tie_objects: + # femobj --> dict, FreeCAD document object is femobj["Object"] + tie_obj = femobj["Object"] + f.write("** " + tie_obj.Label + "\n") + cnt = 0 + obj = obj + 1 + for o, elem_tup in tie_obj.References: + for elem in elem_tup: + ref_shape = o.Shape.getElement(elem) + cnt = cnt + 1 + if ref_shape.ShapeType == "Face": + if cnt == 1: + name = "TIE_DEP" + str(obj) + else: + name = "TIE_IND" + str(obj) + f.write("*SURFACE, NAME=" + name + "\n") + + v = self.mesh_object.FemMesh.getccxVolumesByFace(ref_shape) + if len(v) > 0: + # volume elements found + FreeCAD.Console.PrintLog( + "{}, surface {}, {} touching volume elements found\n" + .format(tie_obj.Label, name, len(v)) + ) + for i in v: + f.write("{},S{}\n".format(i[0], i[1])) + else: + # no volume elements found, shell elements not allowed + FreeCAD.Console.PrintError( + "{}, surface {}, Error: " + "No volume elements found!\n" + .format(tie_obj.Label, name) + ) + def write_node_sets_constraints_transform(self, f): # get nodes self.get_constraints_transform_nodes() @@ -1005,6 +1061,25 @@ class FemInputWriterCcx(writerbase.FemInputWriter): stick = (slope / 10.0) f.write(str(friction) + ", " + str(stick) + " \n") + def write_constraints_tie(self, f): + f.write("\n***********************************************************\n") + f.write("** Tie Constraints\n") + f.write("** written by {} function\n".format(sys._getframe().f_code.co_name)) + obj = 0 + for femobj in self.tie_objects: + # femobj --> dict, FreeCAD document object is femobj["Object"] + obj = obj + 1 + tie_obj = femobj["Object"] + f.write("** {}\n".format(tie_obj.Label)) + tolerance = str(tie_obj.Tolerance.getValueAs("mm")).rstrip() + f.write( + "*TIE, POSITION TOLERANCE={}, ADJUST=NO, NAME=TIE{}\n" + .format(tolerance, obj) + ) + ind_surf = "TIE_IND" + str(obj) + dep_surf = "TIE_DEP" + str(obj) + f.write("{},{}\n".format(dep_surf, ind_surf)) + def write_constraints_planerotation(self, f): f.write("\n***********************************************************\n") f.write("** PlaneRotation Constraints\n") diff --git a/src/Mod/Fem/femsolver/writerbase.py b/src/Mod/Fem/femsolver/writerbase.py index d84ce1547a..f080a57004 100644 --- a/src/Mod/Fem/femsolver/writerbase.py +++ b/src/Mod/Fem/femsolver/writerbase.py @@ -66,6 +66,7 @@ class FemInputWriter(): self.pressure_objects = member.cons_pressure self.selfweight_objects = member.cons_selfweight 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 diff --git a/src/Mod/Fem/femtest/app/test_object.py b/src/Mod/Fem/femtest/app/test_object.py index 07510c5776..ad3664d057 100644 --- a/src/Mod/Fem/femtest/app/test_object.py +++ b/src/Mod/Fem/femtest/app/test_object.py @@ -68,6 +68,7 @@ class TestObjectCreate(unittest.TestCase): analysis.addObject(ObjectsFem.makeConstraintBearing(doc)) analysis.addObject(ObjectsFem.makeConstraintBodyHeatSource(doc)) analysis.addObject(ObjectsFem.makeConstraintContact(doc)) + analysis.addObject(ObjectsFem.makeConstraintTie(doc)) analysis.addObject(ObjectsFem.makeConstraintDisplacement(doc)) analysis.addObject(ObjectsFem.makeConstraintElectrostaticPotential(doc)) analysis.addObject(ObjectsFem.makeConstraintFixed(doc)) @@ -260,6 +261,10 @@ class TestObjectType(unittest.TestCase): "Fem::ConstraintTemperature", type_of_obj(ObjectsFem.makeConstraintTemperature(doc)) ) + self.assertEqual( + "Fem::ConstraintTie", + type_of_obj(ObjectsFem.makeConstraintTie(doc)) + ) self.assertEqual( "Fem::ConstraintTransform", type_of_obj(ObjectsFem.makeConstraintTransform(doc)) @@ -453,6 +458,10 @@ class TestObjectType(unittest.TestCase): ObjectsFem.makeConstraintTemperature(doc), "Fem::ConstraintTemperature" )) + self.assertTrue(is_of_type( + ObjectsFem.makeConstraintTie(doc), + "Fem::ConstraintTie" + )) self.assertTrue(is_of_type( ObjectsFem.makeConstraintTransform(doc), "Fem::ConstraintTransform" @@ -855,6 +864,25 @@ class TestObjectType(unittest.TestCase): "Fem::ConstraintTemperature" )) + # ConstraintTie + constraint_tie = ObjectsFem.makeConstraintTie(doc) + self.assertTrue(is_derived_from( + constraint_tie, + "App::DocumentObject" + )) + self.assertTrue(is_derived_from( + constraint_tie, + "Fem::Constraint" + )) + self.assertTrue(is_derived_from( + constraint_tie, + "Fem::ConstraintPython" + )) + self.assertTrue(is_derived_from( + constraint_tie, + "Fem::ConstraintTie" + )) + # ConstraintTransform constraint_transform = ObjectsFem.makeConstraintTransform(doc) self.assertTrue(is_derived_from( @@ -1345,6 +1373,11 @@ class TestObjectType(unittest.TestCase): doc ).isDerivedFrom("Fem::ConstraintTemperature") ) + self.assertTrue( + ObjectsFem.makeConstraintTie( + doc + ).isDerivedFrom("Fem::ConstraintPython") + ) self.assertTrue( ObjectsFem.makeConstraintTransform( doc).isDerivedFrom("Fem::ConstraintTransform") diff --git a/src/Mod/Fem/femtools/ccxtools.py b/src/Mod/Fem/femtools/ccxtools.py index e71e289fa4..6d750f1c0d 100644 --- a/src/Mod/Fem/femtools/ccxtools.py +++ b/src/Mod/Fem/femtools/ccxtools.py @@ -456,6 +456,17 @@ class FemToolsCcx(QtCore.QRunnable, QtCore.QObject): for c in self.member.cons_contact: if len(c["Object"].References) == 0: message += "{} has empty references.".format(c["Object"].Name) + # tie + if self.member.cons_tie: + for c in self.member.cons_tie: + items = 0 + for reference in c["Object"].References: + items += len(reference[1]) + if items != 2: + message += ( + "{} doesn't references exactly two needed faces.\n" + .format(c["Object"].Name) + ) # transform if self.member.cons_transform: for c in self.member.cons_transform: diff --git a/src/Mod/Fem/femtools/membertools.py b/src/Mod/Fem/femtools/membertools.py index ff7fe8fce0..215736b0af 100644 --- a/src/Mod/Fem/femtools/membertools.py +++ b/src/Mod/Fem/femtools/membertools.py @@ -226,6 +226,10 @@ class AnalysisMember(): list of temperatures for the analysis. [{"Object":temerature_obj, "xxxxxxxx":value}, {}, ...] + constraints_tie : list of dictionaries + list of ties for the analysis. + [{"Object":tie_obj, "xxxxxxxx":value}, {}, ...] + constraints_transform : list of dictionaries list of transform constraints from the analysis. [{"Object":transform_obj, "xxxxxxxx":value}, {}, ...] @@ -290,6 +294,9 @@ class AnalysisMember(): self.cons_temperature = self.get_several_member( "Fem::ConstraintTemperature" ) + self.cons_tie = self.get_several_member( + "Fem::ConstraintTie" + ) self.cons_transform = self.get_several_member( "Fem::ConstraintTransform" )