From 2d3953f65fff0db0bfa23d9877601a828ba24a7a Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 21 Mar 2023 18:05:33 +0100 Subject: [PATCH] [FEM] Elmer add support for nonlinear elasticity solver - adds new equation "Deformation" (this name since the stress solver got in FreeCAD the misleading name "elasticity") - this way change icon of elastic solver to make the difference clear --- src/Mod/Fem/CMakeLists.txt | 2 + src/Mod/Fem/Gui/Resources/Fem.qrc | 1 + .../icons/FEM_EquationDeformation.svg | 47 ++++ .../icons/FEM_EquationElasticity.svg | 12 +- src/Mod/Fem/Gui/Workbench.cpp | 2 + src/Mod/Fem/ObjectsFem.py | 14 ++ src/Mod/Fem/femcommands/commands.py | 23 +- .../femsolver/elmer/equations/deformation.py | 120 ++++++++++ .../elmer/equations/deformation_writer.py | 222 ++++++++++++++++++ .../elmer/equations/elasticity_writer.py | 2 +- src/Mod/Fem/femsolver/elmer/writer.py | 32 ++- src/Mod/Fem/femsolver/equationbase.py | 10 + src/Mod/Fem/femtest/app/test_object.py | 36 ++- 13 files changed, 512 insertions(+), 11 deletions(-) create mode 100644 src/Mod/Fem/Gui/Resources/icons/FEM_EquationDeformation.svg create mode 100644 src/Mod/Fem/femsolver/elmer/equations/deformation.py create mode 100644 src/Mod/Fem/femsolver/elmer/equations/deformation_writer.py diff --git a/src/Mod/Fem/CMakeLists.txt b/src/Mod/Fem/CMakeLists.txt index 6a12f99cf7..5d816ac362 100755 --- a/src/Mod/Fem/CMakeLists.txt +++ b/src/Mod/Fem/CMakeLists.txt @@ -259,6 +259,8 @@ SET(FemSolverElmer_SRCS SET(FemSolverElmerEquations_SRCS femsolver/elmer/equations/__init__.py + femsolver/elmer/equations/deformation.py + femsolver/elmer/equations/deformation_writer.py femsolver/elmer/equations/elasticity.py femsolver/elmer/equations/elasticity_writer.py femsolver/elmer/equations/electricforce.py diff --git a/src/Mod/Fem/Gui/Resources/Fem.qrc b/src/Mod/Fem/Gui/Resources/Fem.qrc index 25860e232f..4c72952889 100755 --- a/src/Mod/Fem/Gui/Resources/Fem.qrc +++ b/src/Mod/Fem/Gui/Resources/Fem.qrc @@ -41,6 +41,7 @@ icons/FEM_ElementGeometry1D.svg icons/FEM_ElementGeometry2D.svg icons/FEM_ElementRotation1D.svg + icons/FEM_EquationDeformation.svg icons/FEM_EquationElasticity.svg icons/FEM_EquationElectricforce.svg icons/FEM_EquationElectrostatic.svg diff --git a/src/Mod/Fem/Gui/Resources/icons/FEM_EquationDeformation.svg b/src/Mod/Fem/Gui/Resources/icons/FEM_EquationDeformation.svg new file mode 100644 index 0000000000..a4db53daa6 --- /dev/null +++ b/src/Mod/Fem/Gui/Resources/icons/FEM_EquationDeformation.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + image/svg+xml + + + + [Alexander Gryson] + + + 2017-03-11 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/ + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + [agryson] Alexander Gryson + + + + + + + + + + + diff --git a/src/Mod/Fem/Gui/Resources/icons/FEM_EquationElasticity.svg b/src/Mod/Fem/Gui/Resources/icons/FEM_EquationElasticity.svg index 2e9bcbcb01..526ff547ca 100644 --- a/src/Mod/Fem/Gui/Resources/icons/FEM_EquationElasticity.svg +++ b/src/Mod/Fem/Gui/Resources/icons/FEM_EquationElasticity.svg @@ -1,11 +1,16 @@ + + + + - + + @@ -17,7 +22,6 @@ [Alexander Gryson] - fem-warp 2017-03-11 http://www.freecadweb.org/wiki/index.php?title=Artwork @@ -41,8 +45,6 @@ - - - + diff --git a/src/Mod/Fem/Gui/Workbench.cpp b/src/Mod/Fem/Gui/Workbench.cpp index 70c14c5a7f..789d97a1c2 100755 --- a/src/Mod/Fem/Gui/Workbench.cpp +++ b/src/Mod/Fem/Gui/Workbench.cpp @@ -180,6 +180,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const << "FEM_SolverZ88" << "Separator" << "FEM_EquationElasticity" + << "FEM_EquationDeformation" << "FEM_CompEmEquations" << "FEM_EquationFlow" << "FEM_EquationFlux" @@ -348,6 +349,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const << "FEM_SolverZ88" << "Separator" << "FEM_EquationElasticity" + << "FEM_EquationDeformation" << "FEM_CompEmEquations" << "FEM_EquationFlow" << "FEM_EquationFlux" diff --git a/src/Mod/Fem/ObjectsFem.py b/src/Mod/Fem/ObjectsFem.py index 91e9378d07..c496ed88eb 100644 --- a/src/Mod/Fem/ObjectsFem.py +++ b/src/Mod/Fem/ObjectsFem.py @@ -753,6 +753,20 @@ def makePostVtkResult( # ********* solver objects *********************************************************************** +def makeEquationDeformation( + doc, + base_solver=None, + name="Deformation" +): + """makeEquationDeformation(document, [base_solver], [name]): + creates a FEM deformation (nonlinear elasticity) equation for a solver""" + from femsolver.elmer.equations import deformation + obj = deformation.create(doc, name) + if base_solver: + base_solver.addObject(obj) + return obj + + def makeEquationElasticity( doc, base_solver=None, diff --git a/src/Mod/Fem/femcommands/commands.py b/src/Mod/Fem/femcommands/commands.py index 732b546249..9bc5a579e5 100644 --- a/src/Mod/Fem/femcommands/commands.py +++ b/src/Mod/Fem/femcommands/commands.py @@ -431,6 +431,23 @@ class _ElementRotation1D(CommandManager): self.do_activated = "add_obj_on_gui_noset_edit" +class _EquationDeformation(CommandManager): + "The FEM_EquationDeformation command definition" + + def __init__(self): + super(_EquationDeformation, self).__init__() + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_EquationDeformation", + "Deformation equation" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_EquationDeformation", + "Creates a FEM equation for\n deformation (nonlinear elasticity)" + ) + self.is_active = "with_solver_elmer" + self.do_activated = "add_obj_on_gui_selobj_noset_edit" + + class _EquationElasticity(CommandManager): "The FEM_EquationElasticity command definition" @@ -442,7 +459,7 @@ class _EquationElasticity(CommandManager): ) self.tooltip = Qt.QT_TRANSLATE_NOOP( "FEM_EquationElasticity", - "Creates a FEM equation for elasticity" + "Creates a FEM equation for\n elasticity (stress)" ) self.is_active = "with_solver_elmer" self.do_activated = "add_obj_on_gui_selobj_noset_edit" @@ -1232,6 +1249,10 @@ FreeCADGui.addCommand( "FEM_ElementRotation1D", _ElementRotation1D() ) +FreeCADGui.addCommand( + "FEM_EquationDeformation", + _EquationDeformation() +) FreeCADGui.addCommand( "FEM_EquationElasticity", _EquationElasticity() diff --git a/src/Mod/Fem/femsolver/elmer/equations/deformation.py b/src/Mod/Fem/femsolver/elmer/equations/deformation.py new file mode 100644 index 0000000000..2c79ece569 --- /dev/null +++ b/src/Mod/Fem/femsolver/elmer/equations/deformation.py @@ -0,0 +1,120 @@ +# *************************************************************************** +# * Copyright (c) 2023 Uwe Stöhr * +# * * +# * 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 solver Elmer equation object Deformation" +__author__ = "Uwe Stöhr" +__url__ = "https://www.freecadweb.org" + +## \addtogroup FEM +# @{ + +from femtools import femutils +from ... import equationbase +from . import linear + + +def create(doc, name="Deformation"): + return femutils.createObject( + doc, name, Proxy, ViewProxy) + + +class Proxy(linear.Proxy, equationbase.DeformationProxy): + + Type = "Fem::EquationElmerDeformation" + + def __init__(self, obj): + super(Proxy, self).__init__(obj) + + obj.addProperty( + "App::PropertyBool", + "CalculatePangle", + "Deformation", + "Compute principal stress angles" + ) + obj.addProperty( + "App::PropertyBool", + "CalculatePrincipal", + "Deformation", + "Compute principal stress components" + ) + obj.addProperty( + "App::PropertyBool", + "CalculateStrains", + "Deformation", + "Compute the strain tensor" + ) + obj.addProperty( + "App::PropertyBool", + "CalculateStresses", + "Deformation", + "Compute stress tensor and vanMises" + ) + obj.addProperty( + "App::PropertyBool", + "InitializeStateVariables", + "Deformation", + "See Elmer manual for info" + ) + obj.addProperty( + "App::PropertyBool", + "MixedFormulation", + "Deformation", + "See Elmer manual for info" + ) + obj.addProperty( + "App::PropertyBool", + "NeoHookeanMaterial", + "Deformation", + ( + "Uses the neo-Hookean material model" + ) + ) + obj.addProperty( + "App::PropertyBool", + "PlaneStress", + "Equation", + ( + "Computes solution according to plane\nstress situation.\n" + "Applies only for 2D geometry." + ) + ) + obj.addProperty( + "App::PropertyString", + "Variable", + "Deformation", + "Only for a 2D model change the '3' to '2'" + ) + + obj.Priority = 10 + obj.CalculatePrincipal = True + # according to Elmer tutorial and forum, for stresses direct solving + # is recommended -> tests showed 10 times faster and even more accurate + obj.LinearSolverType = "Direct" + obj.LinearDirectMethod = "Umfpack" + obj.Variable = "-dofs 3 Displacement" + + +class ViewProxy(linear.ViewProxy, equationbase.DeformationViewProxy): + pass + +## @} diff --git a/src/Mod/Fem/femsolver/elmer/equations/deformation_writer.py b/src/Mod/Fem/femsolver/elmer/equations/deformation_writer.py new file mode 100644 index 0000000000..c73809840e --- /dev/null +++ b/src/Mod/Fem/femsolver/elmer/equations/deformation_writer.py @@ -0,0 +1,222 @@ +# *************************************************************************** +# * Copyright (c) 2023 Uwe Stöhr * +# * * +# * 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 Elasticity Elmer writer" +__author__ = "Uwe Stöhr" +__url__ = "https://www.freecad.org" + +## \addtogroup FEM +# @{ + +from FreeCAD import Console +from FreeCAD import Units + +from .. import sifio +from .. import writer as general_writer +from femtools import femutils + +class DeformationWriter: + + def __init__(self, writer, solver): + self.write = writer + self.solver = solver + + def getDeformationSolver(self, equation): + s = self.write.createLinearSolver(equation) + # output the equation parameters + s["Equation"] = "Nonlinear elasticity solver" + s["Procedure"] = sifio.FileAttr("ElasticSolve/ElasticSolver") + if equation.CalculateStrains is True: + s["Calculate Strains"] = equation.CalculateStrains + if equation.CalculateStresses is True: + s["Calculate Stresses"] = equation.CalculateStresses + if equation.CalculatePrincipal is True: + s["Calculate Principal"] = equation.CalculatePrincipal + if equation.CalculatePangle is True: + s["Calculate Pangle"] = equation.CalculatePangle + if equation.InitializeStateVariables is True: + s["Initialize State Variables"] = equation.InitializeStateVariables + if equation.MixedFormulation is True: + s["Mixed Formulation"] = equation.MixedFormulation + if equation.NeoHookeanMaterial is True: + s["Neo-Hookean Material"] = equation.NeoHookeanMaterial + s["Exec Solver"] = "Always" + s["Optimize Bandwidth"] = True + s["Stabilize"] = equation.Stabilize + s["Variable"] = equation.Variable + return s + + def handleDeformationEquation(self, bodies, equation): + for b in bodies: + # not for bodies with fluid material + if not self.write.isBodyMaterialFluid(b): + if equation.PlaneStress: + self.write.equation(b, "Plane Stress", equation.PlaneStress) + + def handleDeformationConstants(self): + pass + + def handleDeformationBndConditions(self): + for obj in self.write.getMember("Fem::ConstraintPressure"): + if obj.References: + for name in obj.References[0][1]: + pressure = self.write.getFromUi(obj.Pressure, "MPa", "M/(L*T^2)") + if not obj.Reversed: + pressure *= -1 + self.write.boundary(name, "Normal Force", pressure) + self.write.handled(obj) + for obj in self.write.getMember("Fem::ConstraintFixed"): + if obj.References: + for name in obj.References[0][1]: + self.write.boundary(name, "Displacement 1", 0.0) + self.write.boundary(name, "Displacement 2", 0.0) + self.write.boundary(name, "Displacement 3", 0.0) + self.write.handled(obj) + for obj in self.write.getMember("Fem::ConstraintForce"): + if obj.References: + for name in obj.References[0][1]: + force = self.write.getFromUi(obj.Force, "N", "M*L*T^-2") + self.write.boundary(name, "Force 1", obj.DirectionVector.x * force) + self.write.boundary(name, "Force 2", obj.DirectionVector.y * force) + self.write.boundary(name, "Force 3", obj.DirectionVector.z * force) + self.write.boundary(name, "Force 1 Normalize by Area", True) + self.write.boundary(name, "Force 2 Normalize by Area", True) + self.write.boundary(name, "Force 3 Normalize by Area", True) + self.write.handled(obj) + for obj in self.write.getMember("Fem::ConstraintDisplacement"): + if obj.References: + for name in obj.References[0][1]: + if not obj.xFree: + self.write.boundary( + name, "Displacement 1", obj.xDisplacement * 0.001) + elif obj.xFix: + self.write.boundary(name, "Displacement 1", 0.0) + if not obj.yFree: + self.write.boundary( + name, "Displacement 2", obj.yDisplacement * 0.001) + elif obj.yFix: + self.write.boundary(name, "Displacement 2", 0.0) + if not obj.zFree: + self.write.boundary( + name, "Displacement 3", obj.zDisplacement * 0.001) + elif obj.zFix: + self.write.boundary(name, "Displacement 3", 0.0) + self.write.handled(obj) + + def handleDeformationInitial(self, bodies): + pass + + def handleDeformationBodyForces(self, bodies): + obj = self.write.getSingleMember("Fem::ConstraintSelfWeight") + if obj is not None: + for name in bodies: + gravity = self.write.convert(self.write.constsdef["Gravity"], "L/T^2") + if self.write.getBodyMaterial(name) is None: + raise general_writer.WriteError( + "The body {} is not referenced in any material.\n\n".format(name) + ) + m = self.write.getBodyMaterial(name).Material + + densityQuantity = Units.Quantity(m["Density"]) + dimension = "M/L^3" + if name.startswith("Edge"): + # not tested, bernd + # TODO: test + densityQuantity.Unit = Units.Unit(-2, 1) + dimension = "M/L^2" + density = self.write.convert(densityQuantity, dimension) + + force1 = gravity * obj.Gravity_x * density + force2 = gravity * obj.Gravity_y * density + force3 = gravity * obj.Gravity_z * density + self.write.bodyForce(name, "Stress Bodyforce 1", force1) + self.write.bodyForce(name, "Stress Bodyforce 2", force2) + self.write.bodyForce(name, "Stress Bodyforce 3", force3) + self.write.handled(obj) + + def handleDeformationMaterial(self, bodies): + # density + # is needed for self weight constraints and frequency analysis + density_needed = False + for equation in self.solver.Group: + if femutils.is_of_type(equation, "Fem::EquationElmerElasticity"): + if equation.EigenAnalysis is True: + density_needed = True + break # there could be a second equation without frequency + gravObj = self.write.getSingleMember("Fem::ConstraintSelfWeight") + if gravObj is not None: + density_needed = True + # temperature + tempObj = self.write.getSingleMember("Fem::ConstraintInitialTemperature") + if tempObj is not None: + refTemp = self.write.getFromUi(tempObj.initialTemperature, "K", "O") + for name in bodies: + self.write.material(name, "Reference Temperature", refTemp) + # get the material data for all bodies + for obj in self.write.getMember("App::MaterialObject"): + m = obj.Material + refs = ( + obj.References[0][1] + if obj.References + else self.write.getAllBodies() + ) + for name in (n for n in refs if n in bodies): + # don't evaluate fluid material + if self.write.isBodyMaterialFluid(name): + break + if "YoungsModulus" not in m: + Console.PrintMessage("m: {}\n".format(m)) + # it is no fluid but also no solid + # -> user set no material reference at all + # that now material is known + raise general_writer.WriteError( + "There are two or more materials with empty references.\n\n" + "Set for the materials to what solid they belong to.\n" + ) + self.write.material(name, "Name", m["Name"]) + if density_needed is True: + self.write.material( + name, "Density", + self.write.getDensity(m) + ) + self.write.material( + name, "Youngs Modulus", + self._getYoungsModulus(m) + ) + self.write.material( + name, "Poisson ratio", + float(m["PoissonRatio"]) + ) + if tempObj: + self.write.material( + name, "Heat expansion Coefficient", + self.write.convert(m["ThermalExpansionCoefficient"], "O^-1") + ) + + def _getYoungsModulus(self, m): + youngsModulus = self.write.convert(m["YoungsModulus"], "M/(L*T^2)") + if self.write.getMeshDimension() == 2: + youngsModulus *= 1e3 + return youngsModulus + +## @} diff --git a/src/Mod/Fem/femsolver/elmer/equations/elasticity_writer.py b/src/Mod/Fem/femsolver/elmer/equations/elasticity_writer.py index 63fbed1bb5..fabee1449a 100644 --- a/src/Mod/Fem/femsolver/elmer/equations/elasticity_writer.py +++ b/src/Mod/Fem/femsolver/elmer/equations/elasticity_writer.py @@ -36,7 +36,7 @@ from .. import writer as general_writer from femtools import femutils from . import elasticity -class Elasticitywriter: +class ElasticityWriter: def __init__(self, writer, solver): self.write = writer diff --git a/src/Mod/Fem/femsolver/elmer/writer.py b/src/Mod/Fem/femsolver/elmer/writer.py index 4cd3d4975d..8a6361e353 100644 --- a/src/Mod/Fem/femsolver/elmer/writer.py +++ b/src/Mod/Fem/femsolver/elmer/writer.py @@ -48,6 +48,7 @@ from femmesh import gmshtools from femtools import constants from femtools import femutils from femtools import membertools +from .equations import deformation_writer as DEF_writer from .equations import elasticity_writer as EL_writer from .equations import electricforce_writer as EF_writer from .equations import electrostatic_writer as ES_writer @@ -94,6 +95,7 @@ class Writer(object): def write_solver_input(self): self._handleRedifinedConstants() self._handleSimulation() + self._handleDeformation() self._handleElasticity() self._handleElectricforce() self._handleElectrostatic() @@ -405,11 +407,39 @@ class Writer(object): ) solver.TimestepSizes = [0.1] + #------------------------------------------------------------------------------------------- + # Deformation + + def _handleDeformation(self): + DEFW = DEF_writer.DeformationWriter(self, self.solver) + activeIn = [] + for equation in self.solver.Group: + if femutils.is_of_type(equation, "Fem::EquationElmerDeformation"): + if not self._haveMaterialSolid(): + raise WriteError( + "The Deformation equation requires at least one body with a solid material!" + ) + if equation.References: + activeIn = equation.References[0][1] + else: + activeIn = self.getAllBodies() + solverSection = DEFW.getDeformationSolver(equation) + for body in activeIn: + if not self.isBodyMaterialFluid(body): + self._addSolver(body, solverSection) + DEFW.handleDeformationEquation(activeIn, equation) + if activeIn: + DEFW.handleDeformationConstants() + DEFW.handleDeformationBndConditions() + DEFW.handleDeformationInitial(activeIn) + DEFW.handleDeformationBodyForces(activeIn) + DEFW.handleDeformationMaterial(activeIn) + #------------------------------------------------------------------------------------------- # Elasticity def _handleElasticity(self): - ELW = EL_writer.Elasticitywriter(self, self.solver) + ELW = EL_writer.ElasticityWriter(self, self.solver) activeIn = [] for equation in self.solver.Group: if femutils.is_of_type(equation, "Fem::EquationElmerElasticity"): diff --git a/src/Mod/Fem/femsolver/equationbase.py b/src/Mod/Fem/femsolver/equationbase.py index a540c4165a..d926195e45 100644 --- a/src/Mod/Fem/femsolver/equationbase.py +++ b/src/Mod/Fem/femsolver/equationbase.py @@ -69,6 +69,16 @@ class BaseViewProxy(object): return mode +class DeformationProxy(BaseProxy): + pass + + +class DeformationViewProxy(BaseViewProxy): + + def getIcon(self): + return ":/icons/FEM_EquationDeformation.svg" + + class ElasticityProxy(BaseProxy): pass diff --git a/src/Mod/Fem/femtest/app/test_object.py b/src/Mod/Fem/femtest/app/test_object.py index a0123a499a..662f6eeccf 100644 --- a/src/Mod/Fem/femtest/app/test_object.py +++ b/src/Mod/Fem/femtest/app/test_object.py @@ -84,14 +84,14 @@ class TestObjectCreate(unittest.TestCase): # thus they are not added to the analysis group ATM # https://forum.freecadweb.org/viewtopic.php?t=25283 # thus they should not be counted - # solver children: equations --> 8 + # solver children: equations --> 9 # gmsh mesh children: group, region, boundary layer --> 3 # result children: mesh result --> 1 # post pipeline children: region, scalar, cut, wrap --> 5 # analysis itself is not in analysis group --> 1 - # thus: -18 + # thus: -19 - self.assertEqual(len(doc.Analysis.Group), count_defmake - 18) + self.assertEqual(len(doc.Analysis.Group), count_defmake - 19) self.assertEqual(len(doc.Objects), count_defmake) fcc_print("doc objects count: {}, method: {}".format( @@ -346,6 +346,10 @@ class TestObjectType(unittest.TestCase): "Fem::SolverZ88", type_of_obj(ObjectsFem.makeSolverZ88(doc)) ) + self.assertEqual( + "Fem::EquationElmerDeformation", + type_of_obj(ObjectsFem.makeEquationDeformation(doc, solverelmer)) + ) self.assertEqual( "Fem::EquationElmerElasticity", type_of_obj(ObjectsFem.makeEquationElasticity(doc, solverelmer)) @@ -589,6 +593,10 @@ class TestObjectType(unittest.TestCase): ObjectsFem.makeSolverZ88(doc), "Fem::SolverZ88" )) + self.assertTrue(is_of_type( + ObjectsFem.makeEquationDeformation(doc, solverelmer), + "Fem::EquationElmerDeformation" + )) self.assertTrue(is_of_type( ObjectsFem.makeEquationElasticity(doc, solverelmer), "Fem::EquationElmerElasticity" @@ -1371,6 +1379,21 @@ class TestObjectType(unittest.TestCase): "Fem::SolverZ88" )) + # EquationElmerDeformation + equation_deformation = ObjectsFem.makeEquationDeformation(doc, solver_elmer) + self.assertTrue(is_derived_from( + equation_deformation, + "App::DocumentObject" + )) + self.assertTrue(is_derived_from( + equation_deformation, + "App::FeaturePython" + )) + self.assertTrue(is_derived_from( + equation_deformation, + "Fem::EquationElmerDeformation" + )) + # EquationElmerElasticity equation_elasticity = ObjectsFem.makeEquationElasticity(doc, solver_elmer) self.assertTrue(is_derived_from( @@ -1744,6 +1767,12 @@ class TestObjectType(unittest.TestCase): doc ).isDerivedFrom("Fem::FemSolverObjectPython") ) + self.assertTrue( + ObjectsFem.makeEquationDeformation( + doc, + solverelmer + ).isDerivedFrom("App::FeaturePython") + ) self.assertTrue( ObjectsFem.makeEquationElasticity( doc, @@ -1868,6 +1897,7 @@ def create_all_fem_objects_doc( analysis.addObject(ObjectsFem.makeSolverMystran(doc)) analysis.addObject(ObjectsFem.makeSolverZ88(doc)) + ObjectsFem.makeEquationDeformation(doc, sol) ObjectsFem.makeEquationElasticity(doc, sol) ObjectsFem.makeEquationElectricforce(doc, sol) ObjectsFem.makeEquationElectrostatic(doc, sol)