From 2d17269a113eb31220a7b12f3e0aca34b8c45bdc Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Wed, 5 Mar 2025 21:04:40 +0100 Subject: [PATCH 1/5] FEM: add rigid body FEM example --- src/Mod/Fem/femexamples/ccx_rigid_body.py | 156 ++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 src/Mod/Fem/femexamples/ccx_rigid_body.py diff --git a/src/Mod/Fem/femexamples/ccx_rigid_body.py b/src/Mod/Fem/femexamples/ccx_rigid_body.py new file mode 100644 index 0000000000..cb0d9bfc11 --- /dev/null +++ b/src/Mod/Fem/femexamples/ccx_rigid_body.py @@ -0,0 +1,156 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +# *************************************************************************** +# * Copyright (c) 2025 Jakub Michalski * +# * * +# * This file is part of FreeCAD. * +# * * +# * FreeCAD is free software: you can redistribute it and/or modify it * +# * under the terms of the GNU Lesser General Public License as * +# * published by the Free Software Foundation, either version 2.1 of the * +# * License, or (at your option) any later version. * +# * * +# * FreeCAD is distributed in the hope that it will be useful, but * +# * WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +# * Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Lesser General Public * +# * License along with FreeCAD. If not, see * +# * . * +# * * +# *************************************************************************** + +import FreeCAD as App + +import Part +import Sketcher +import Fem +import ObjectsFem + +from . import manager +from .manager import get_meshname +from .manager import init_doc + + +def get_information(): + return { + "name": "Rigid body constraint", + "meshtype": "solid", + "meshelement": "Tet10", + "constraints": ["fixed", "rigid body"], + "solvers": ["ccxtools"], + "material": "solid", + "equations": ["mechanical"], + } + + +def get_explanation(header=""): + return ( + header + + """ + +To run the example from Python console use: +from femexamples.ccx_rigid_body import setup +setup() + + +Analytical solution - max xz stress = 2.547 MPa = 2,547e6 Pa + +""" + ) + + +def setup(doc=None, solvertype="ccxtools"): + + # init FreeCAD document + if doc is None: + doc = init_doc() + + # explanation object + # just keep the following line and change text string in get_explanation method + manager.add_explanation_obj(doc, get_explanation(manager.get_header(get_information()))) + + # geometric object + body = doc.addObject("PartDesign::Body", "Body") + sketch = doc.addObject("Sketcher::SketchObject", "Sketch") + body.addObject(sketch) + sketch.AttachmentSupport = (doc.getObject('XY_Plane'),['']) + sketch.MapMode = 'FlatFace' + sketch.addGeometry(Part.Ellipse(App.Vector(40, 0, 0), + App.Vector(0, 20, 0),App.Vector(0, 0, 0))) + sketch.exposeInternalGeometry(0) + sketch.addConstraint(Sketcher.Constraint('Distance',0,3,1,1,100)) + sketch.addConstraint(Sketcher.Constraint('Angle',1,0)) + sketch.addConstraint(Sketcher.Constraint('Distance',0,3,2,1,50)) + sketch.addConstraint(Sketcher.Constraint('Coincident', 0, 3, -1, 1)) + pad = body.Document.addObject("PartDesign::Pad","Pad") + body.addObject(pad) + pad.Length = 1000 + pad.Profile = sketch + sketch.ViewObject.Visibility = False + body.Document.recompute() + doc.recompute() + if App.GuiUp: + body.ViewObject.Document.activeView().viewAxonometric() + body.ViewObject.Document.activeView().fitAll() + + # analysis + analysis = ObjectsFem.makeAnalysis(doc, "Analysis") + + # solver, + if solvertype == "ccxtools": + solver_obj = ObjectsFem.makeSolverCalculiXCcxTools(doc, "CalculiXCcxTools") + solver_obj.WorkingDir = "" + else: + FreeCAD.Console.PrintWarning( + "Unknown or unsupported solver type: {}. " + "No solver object was created.\n".format(solvertype) + ) + if solvertype == "ccxtools": + solver_obj.SplitInputWriter = False + solver_obj.AnalysisType = "static" + analysis.addObject(solver_obj) + + # material + material_obj = ObjectsFem.makeMaterialSolid(doc, "MechanicalMaterial") + mat = material_obj.Material + mat["Name"] = "CalculiX-Steel" + mat["YoungsModulus"] = "210000 MPa" + mat["PoissonRatio"] = "0.30" + material_obj.Material = mat + analysis.addObject(material_obj) + + # constraint fixed + con_fixed = ObjectsFem.makeConstraintFixed(doc, "ConstraintFixed") + con_fixed.References = [(body, "Face2")] + analysis.addObject(con_fixed) + + # constraint rigid body + con_rb = ObjectsFem.makeConstraintRigidBody(doc, "ConstraintRigidBody") + con_rb.References = [(body, "Face3")] + con_rb.ReferenceNode = App.Vector(0.000000, 0.000000, 1000.000000) + con_rb.Rotation = App.Rotation(App.Vector(0.000000, 0.000000, 1.000000), Radian=0.000000) + con_rb.MomentZ = "1,00 kJ" + con_rb.RotationalModeZ = "Load" + analysis.addObject(con_rb) + + # mesh + femmesh_obj = analysis.addObject(ObjectsFem.makeMeshGmsh(doc, get_meshname()))[0] + femmesh_obj.Shape = body + femmesh_obj.ElementOrder = "2nd" + femmesh_obj.CharacteristicLengthMax = "15 mm" + femmesh_obj.ViewObject.Visibility = False + + # generate the mesh + from femmesh import gmshtools + + gmsh_mesh = gmshtools.GmshTools(femmesh_obj, analysis) + try: + error = gmsh_mesh.create_mesh() + except Exception: + error = sys.exc_info()[1] + FreeCAD.Console.PrintError(f"Unexpected error when creating mesh: {error}\n") + + doc.recompute() + return doc From b885e8a90ff4baebe83e0f653bec43d41e94db05 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Wed, 5 Mar 2025 21:05:42 +0100 Subject: [PATCH 2/5] FEM: Update CMakeLists.txt --- src/Mod/Fem/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mod/Fem/CMakeLists.txt b/src/Mod/Fem/CMakeLists.txt index 1560c2b672..44aff6f11c 100755 --- a/src/Mod/Fem/CMakeLists.txt +++ b/src/Mod/Fem/CMakeLists.txt @@ -66,6 +66,7 @@ SET(FemExamples_SRCS femexamples/ccx_cantilever_faceload.py femexamples/ccx_cantilever_nodeload.py femexamples/ccx_cantilever_prescribeddisplacement.py + femexamples/ccx_rigid_body.py femexamples/constraint_centrif.py femexamples/constraint_contact_shell_shell.py femexamples/constraint_contact_solid_solid.py From fe43ab7bbe9b917c9356f86b8546130ba069e45e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 20:11:25 +0000 Subject: [PATCH 3/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/Mod/Fem/femexamples/ccx_rigid_body.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Mod/Fem/femexamples/ccx_rigid_body.py b/src/Mod/Fem/femexamples/ccx_rigid_body.py index cb0d9bfc11..4e0649003a 100644 --- a/src/Mod/Fem/femexamples/ccx_rigid_body.py +++ b/src/Mod/Fem/femexamples/ccx_rigid_body.py @@ -75,16 +75,17 @@ def setup(doc=None, solvertype="ccxtools"): body = doc.addObject("PartDesign::Body", "Body") sketch = doc.addObject("Sketcher::SketchObject", "Sketch") body.addObject(sketch) - sketch.AttachmentSupport = (doc.getObject('XY_Plane'),['']) - sketch.MapMode = 'FlatFace' - sketch.addGeometry(Part.Ellipse(App.Vector(40, 0, 0), - App.Vector(0, 20, 0),App.Vector(0, 0, 0))) + sketch.AttachmentSupport = (doc.getObject("XY_Plane"), [""]) + sketch.MapMode = "FlatFace" + sketch.addGeometry( + Part.Ellipse(App.Vector(40, 0, 0), App.Vector(0, 20, 0), App.Vector(0, 0, 0)) + ) sketch.exposeInternalGeometry(0) - sketch.addConstraint(Sketcher.Constraint('Distance',0,3,1,1,100)) - sketch.addConstraint(Sketcher.Constraint('Angle',1,0)) - sketch.addConstraint(Sketcher.Constraint('Distance',0,3,2,1,50)) - sketch.addConstraint(Sketcher.Constraint('Coincident', 0, 3, -1, 1)) - pad = body.Document.addObject("PartDesign::Pad","Pad") + sketch.addConstraint(Sketcher.Constraint("Distance", 0, 3, 1, 1, 100)) + sketch.addConstraint(Sketcher.Constraint("Angle", 1, 0)) + sketch.addConstraint(Sketcher.Constraint("Distance", 0, 3, 2, 1, 50)) + sketch.addConstraint(Sketcher.Constraint("Coincident", 0, 3, -1, 1)) + pad = body.Document.addObject("PartDesign::Pad", "Pad") body.addObject(pad) pad.Length = 1000 pad.Profile = sketch From f4bfbfb493031f8f9f4c30050fa875aa4a97453b Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 6 Mar 2025 09:30:09 +0100 Subject: [PATCH 4/5] FEM: Update ccx_rigid_body.py --- src/Mod/Fem/femexamples/ccx_rigid_body.py | 44 ++++++++++------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/src/Mod/Fem/femexamples/ccx_rigid_body.py b/src/Mod/Fem/femexamples/ccx_rigid_body.py index 4e0649003a..7fe815c6b3 100644 --- a/src/Mod/Fem/femexamples/ccx_rigid_body.py +++ b/src/Mod/Fem/femexamples/ccx_rigid_body.py @@ -24,7 +24,6 @@ import FreeCAD as App import Part -import Sketcher import Fem import ObjectsFem @@ -55,7 +54,7 @@ from femexamples.ccx_rigid_body import setup setup() -Analytical solution - max xz stress = 2.547 MPa = 2,547e6 Pa +Analytical solution - max xz stress = 2.547 MPa = 2.547e6 Pa """ ) @@ -72,29 +71,21 @@ def setup(doc=None, solvertype="ccxtools"): manager.add_explanation_obj(doc, get_explanation(manager.get_header(get_information()))) # geometric object - body = doc.addObject("PartDesign::Body", "Body") - sketch = doc.addObject("Sketcher::SketchObject", "Sketch") - body.addObject(sketch) - sketch.AttachmentSupport = (doc.getObject("XY_Plane"), [""]) - sketch.MapMode = "FlatFace" - sketch.addGeometry( - Part.Ellipse(App.Vector(40, 0, 0), App.Vector(0, 20, 0), App.Vector(0, 0, 0)) - ) - sketch.exposeInternalGeometry(0) - sketch.addConstraint(Sketcher.Constraint("Distance", 0, 3, 1, 1, 100)) - sketch.addConstraint(Sketcher.Constraint("Angle", 1, 0)) - sketch.addConstraint(Sketcher.Constraint("Distance", 0, 3, 2, 1, 50)) - sketch.addConstraint(Sketcher.Constraint("Coincident", 0, 3, -1, 1)) - pad = body.Document.addObject("PartDesign::Pad", "Pad") - body.addObject(pad) - pad.Length = 1000 - pad.Profile = sketch - sketch.ViewObject.Visibility = False - body.Document.recompute() + ellipse = doc.addObject("Part::Ellipse","Ellipse") + ellipse.MajorRadius='100,00 mm' + ellipse.MinorRadius='50,00 mm' + ellipse.Angle1='0,00 °' + ellipse.Angle2='360,00 °' + ellipse.Placement=App.Placement(App.Vector(0.00,0.00,0.00),App.Rotation(App.Vector(0.00,0.00,1.00),0.00)) + extrude = doc.addObject('Part::Extrusion','Extrude') + extrude.Base = ellipse + extrude.LengthFwd = 1000 + extrude.Solid = True + ellipse.Visibility = False doc.recompute() if App.GuiUp: - body.ViewObject.Document.activeView().viewAxonometric() - body.ViewObject.Document.activeView().fitAll() + extrude.ViewObject.Document.activeView().viewAxonometric() + extrude.ViewObject.Document.activeView().fitAll() # analysis analysis = ObjectsFem.makeAnalysis(doc, "Analysis") @@ -120,16 +111,17 @@ def setup(doc=None, solvertype="ccxtools"): mat["YoungsModulus"] = "210000 MPa" mat["PoissonRatio"] = "0.30" material_obj.Material = mat + material_obj.References = [(extrude, "Solid1")] analysis.addObject(material_obj) # constraint fixed con_fixed = ObjectsFem.makeConstraintFixed(doc, "ConstraintFixed") - con_fixed.References = [(body, "Face2")] + con_fixed.References = [(extrude, "Face2")] analysis.addObject(con_fixed) # constraint rigid body con_rb = ObjectsFem.makeConstraintRigidBody(doc, "ConstraintRigidBody") - con_rb.References = [(body, "Face3")] + con_rb.References = [(extrude, "Face3")] con_rb.ReferenceNode = App.Vector(0.000000, 0.000000, 1000.000000) con_rb.Rotation = App.Rotation(App.Vector(0.000000, 0.000000, 1.000000), Radian=0.000000) con_rb.MomentZ = "1,00 kJ" @@ -138,7 +130,7 @@ def setup(doc=None, solvertype="ccxtools"): # mesh femmesh_obj = analysis.addObject(ObjectsFem.makeMeshGmsh(doc, get_meshname()))[0] - femmesh_obj.Shape = body + femmesh_obj.Shape = extrude femmesh_obj.ElementOrder = "2nd" femmesh_obj.CharacteristicLengthMax = "15 mm" femmesh_obj.ViewObject.Visibility = False From 1fc630413f70222fa896cd5f6bfdbab46191c2f8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 6 Mar 2025 08:34:52 +0000 Subject: [PATCH 5/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/Mod/Fem/femexamples/ccx_rigid_body.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Mod/Fem/femexamples/ccx_rigid_body.py b/src/Mod/Fem/femexamples/ccx_rigid_body.py index 7fe815c6b3..42241f0478 100644 --- a/src/Mod/Fem/femexamples/ccx_rigid_body.py +++ b/src/Mod/Fem/femexamples/ccx_rigid_body.py @@ -71,13 +71,15 @@ def setup(doc=None, solvertype="ccxtools"): manager.add_explanation_obj(doc, get_explanation(manager.get_header(get_information()))) # geometric object - ellipse = doc.addObject("Part::Ellipse","Ellipse") - ellipse.MajorRadius='100,00 mm' - ellipse.MinorRadius='50,00 mm' - ellipse.Angle1='0,00 °' - ellipse.Angle2='360,00 °' - ellipse.Placement=App.Placement(App.Vector(0.00,0.00,0.00),App.Rotation(App.Vector(0.00,0.00,1.00),0.00)) - extrude = doc.addObject('Part::Extrusion','Extrude') + ellipse = doc.addObject("Part::Ellipse", "Ellipse") + ellipse.MajorRadius = "100,00 mm" + ellipse.MinorRadius = "50,00 mm" + ellipse.Angle1 = "0,00 °" + ellipse.Angle2 = "360,00 °" + ellipse.Placement = App.Placement( + App.Vector(0.00, 0.00, 0.00), App.Rotation(App.Vector(0.00, 0.00, 1.00), 0.00) + ) + extrude = doc.addObject("Part::Extrusion", "Extrude") extrude.Base = ellipse extrude.LengthFwd = 1000 extrude.Solid = True