diff --git a/CMakeLists.txt b/CMakeLists.txt index 9aee103f56..acd65efbe0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -229,7 +229,7 @@ if(MSVC) # Copy libpack 'bin' directory contents to build 'bin' per user request - only IF NOT EXISTS already if (NOT EXISTS ${CMAKE_BINARY_DIR}/bin/DLLs) set(COPY_LIBPACK_BIN_TO_BUILD ON) - option(FREECAD_COPY_LIBPACK_BIN_TO_BUILD "Copy larger libpack dependency 'bin' folder to the build direcctory." OFF) + option(FREECAD_COPY_LIBPACK_BIN_TO_BUILD "Copy larger libpack dependency 'bin' folder to the build directory." OFF) endif() else() message("Libpack NOT found.\nIf you intend to use a Windows libpack, set the FREECAD_LIBPACK_DIR to the libpack directory.") diff --git a/src/Gui/DlgMacroRecordImp.h b/src/Gui/DlgMacroRecordImp.h index e9a95eb57f..7f48f890dd 100644 --- a/src/Gui/DlgMacroRecordImp.h +++ b/src/Gui/DlgMacroRecordImp.h @@ -37,7 +37,7 @@ namespace Dialog { * \author Jürgen Riegel */ class DlgMacroRecordImp : public QDialog, public Ui_DlgMacroRecord, public Gui::WindowParameter -{ +{ Q_OBJECT public: @@ -52,8 +52,8 @@ protected Q_SLOTS: void on_lineEditMacroPath_textChanged ( const QString & ); protected: - /// conviniance pointer - MacroManager* macroManager; + /// convenience pointer + MacroManager* macroManager; QString macroPath; // Macro file to save in }; diff --git a/src/Mod/Draft/DraftGeomUtils.py b/src/Mod/Draft/DraftGeomUtils.py index 13a8dcced1..c41f210421 100644 --- a/src/Mod/Draft/DraftGeomUtils.py +++ b/src/Mod/Draft/DraftGeomUtils.py @@ -47,9 +47,11 @@ params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft") def precision(): "precision(): returns the Draft precision setting" - # Set precision level with a cap to avoid overspecification that:- - # 1 - whilst it is precise enough (e.g. that OCC would consider 2 points are conincident) (not sure what it should be 10 or otherwise); - # 2 - but FreeCAD / OCC can handle 'internally' (e.g. otherwise user may set something like 15 that the code would never consider 2 points are coincident as internal float is not that precise); + # Set precision level with a cap to avoid overspecification that: + # 1 - whilst it is precise enough (e.g. that OCC would consider 2 points are coincident) + # (not sure what it should be 10 or otherwise); + # 2 - but FreeCAD/OCC can handle 'internally' (e.g. otherwise user may set something like + # 15 that the code would never consider 2 points are coincident as internal float is not that precise); precisionMax = 10 precisionInt = params.GetInt("precision",6) diff --git a/src/Mod/Fem/App/FemMesh.cpp b/src/Mod/Fem/App/FemMesh.cpp index f478c0a381..1eee031a41 100644 --- a/src/Mod/Fem/App/FemMesh.cpp +++ b/src/Mod/Fem/App/FemMesh.cpp @@ -946,10 +946,10 @@ std::set FemMesh::getFacesOnly(void) const // get the volume nodes // if the face nodes are a subset of the volume nodes // add the face to the volume faces and break - // if face not belongs to a volume + // if face doesn't belong to a volume // add it to faces only // - // This means it is iterated over a lot of volumes many times, this is quite expensive ! + // This means it is iterated over a lot of volumes many times, this is quite expensive! // // TODO make this faster // Idea: @@ -959,7 +959,7 @@ std::set FemMesh::getFacesOnly(void) const // if not in volume faces // add it to the faces only // - // but the volume faces does not seam know their global mesh ID, I could not found any method in SMESH + // but the volume faces do not seem to know their global mesh ID, I could not find any method in SMESH std::set resultIDs; @@ -2050,4 +2050,3 @@ Base::Quantity FemMesh::getVolume(void)const } - diff --git a/src/Mod/Fem/App/FemVTKTools.cpp b/src/Mod/Fem/App/FemVTKTools.cpp index 250454bf1e..a0b4fd04c4 100644 --- a/src/Mod/Fem/App/FemVTKTools.cpp +++ b/src/Mod/Fem/App/FemVTKTools.cpp @@ -660,6 +660,10 @@ std::map _getFreeCADMechResultVectorProperties() { // App::PropertyVectorList will be a list of vectors in vtk std::map resFCVecProp; resFCVecProp["DisplacementVectors"] = "Displacement"; + // https://forum.freecadweb.org/viewtopic.php?f=18&t=33106&start=70#p296317 + resFCVecProp["PS1Vector"] = "Major Principal Stress"; + resFCVecProp["PS2Vector"] = "Intermediate Principal Stress"; + resFCVecProp["PS3Vector"] = "Minor Principal Stress"; return resFCVecProp; } @@ -686,11 +690,16 @@ std::map _getFreeCADMechResultScalarProperties() { resFCScalProp["NodeStrainXZ"] = "Strain xz component"; resFCScalProp["NodeStrainYZ"] = "Strain yz component"; resFCScalProp["Peeq"] = "Equivalent Plastic Strain"; - resFCScalProp["PrincipalMax"] = "Major Principal Stress"; // can be plotted in Paraview as THE MAJOR PRINCIPAL STRESS MAGNITUDE - resFCScalProp["PrincipalMed"] = "Intermediate Principal Stress"; // can be plotted in Paraview as THE INTERMEDIATE PRINCIPAL STRESS MAGNITUDE - resFCScalProp["PrincipalMin"] = "Minor Principal Stress"; // can be plotted in Paraview as THE MINOR PRINCIPAL STRESS MAGNITUDE + // https://forum.freecadweb.org/viewtopic.php?f=18&t=33106&start=70#p296317 + // resFCScalProp["PrincipalMax"] = "Major Principal Stress"; // can be plotted in Paraview as THE MAJOR PRINCIPAL STRESS MAGNITUDE + // resFCScalProp["PrincipalMed"] = "Intermediate Principal Stress"; // can be plotted in Paraview as THE INTERMEDIATE PRINCIPAL STRESS MAGNITUDE + // resFCScalProp["PrincipalMin"] = "Minor Principal Stress"; // can be plotted in Paraview as THE MINOR PRINCIPAL STRESS MAGNITUDE resFCScalProp["StressValues"] = "von Mises Stress"; resFCScalProp["Temperature"] = "Temperature"; + resFCScalProp["MohrCoulomb"] = "MohrCoulomb"; + resFCScalProp["ReinforcementRatio_x"] = "ReinforcementRatio_x"; + resFCScalProp["ReinforcementRatio_y"] = "ReinforcementRatio_y"; + resFCScalProp["ReinforcementRatio_z"] = "ReinforcementRatio_z"; resFCScalProp["UserDefined"] = "UserDefinedMyName"; // this is empty or am I wrong ?! resFCScalProp["MassFlowRate"] = "Mass Flow Rate"; diff --git a/src/Mod/Fem/CMakeLists.txt b/src/Mod/Fem/CMakeLists.txt index 88460c7304..4507fb1ce4 100755 --- a/src/Mod/Fem/CMakeLists.txt +++ b/src/Mod/Fem/CMakeLists.txt @@ -36,11 +36,13 @@ SET(FemExamples_SRCS femexamples/__init__.py femexamples/ccx_cantilever_std.py femexamples/manager.py + femexamples/rc_wall_2d.py ) SET(FemExampleMeshes_SRCS femexamples/meshes/__init__.py femexamples/meshes/mesh_canticcx_tetra10.py + femexamples/meshes/mesh_rc_wall_2d_tria6.py ) SET(FemInOut_SRCS @@ -206,6 +208,7 @@ SET(FemObjectsScripts_SRCS femobjects/_FemElementGeometry2D.py femobjects/_FemElementRotation1D.py femobjects/_FemMaterial.py + femobjects/_FemMaterialReinforced.py femobjects/_FemMaterialMechanicalNonlinear.py femobjects/_FemMeshBoundaryLayer.py femobjects/_FemMeshGmsh.py @@ -286,6 +289,7 @@ SET(FemGuiScripts_SRCS femguiobjects/_ViewProviderFemElementGeometry2D.py femguiobjects/_ViewProviderFemElementRotation1D.py femguiobjects/_ViewProviderFemMaterial.py + femguiobjects/_ViewProviderFemMaterialReinforced.py femguiobjects/_ViewProviderFemMaterialMechanicalNonlinear.py femguiobjects/_ViewProviderFemMeshBoundaryLayer.py femguiobjects/_ViewProviderFemMeshGmsh.py diff --git a/src/Mod/Fem/Gui/CMakeLists.txt b/src/Mod/Fem/Gui/CMakeLists.txt index e9076d55ee..722b5b287c 100755 --- a/src/Mod/Fem/Gui/CMakeLists.txt +++ b/src/Mod/Fem/Gui/CMakeLists.txt @@ -401,6 +401,7 @@ SET(FemGuiPythonUI_SRCS Resources/ui/FlowVelocity.ui Resources/ui/InitialFlowVelocity.ui Resources/ui/Material.ui + Resources/ui/MaterialReinforcement.ui Resources/ui/MeshBoundaryLayer.ui Resources/ui/MeshGmsh.ui Resources/ui/MeshGroup.ui diff --git a/src/Mod/Fem/Gui/Resources/Fem.qrc b/src/Mod/Fem/Gui/Resources/Fem.qrc index 04cbc0624d..f0b001711b 100755 --- a/src/Mod/Fem/Gui/Resources/Fem.qrc +++ b/src/Mod/Fem/Gui/Resources/Fem.qrc @@ -52,6 +52,7 @@ icons/fem-material.svg icons/fem-material-fluid.svg icons/fem-material-nonlinear.svg + icons/fem-material-reinforced.svg icons/fem-post-data-pipline.svg icons/fem-post-filter-clip-region.svg icons/fem-post-filter-clip-scalar.svg diff --git a/src/Mod/Fem/Gui/Resources/icons/fem-material-reinforced.svg b/src/Mod/Fem/Gui/Resources/icons/fem-material-reinforced.svg new file mode 100644 index 0000000000..cfcc4d6667 --- /dev/null +++ b/src/Mod/Fem/Gui/Resources/icons/fem-material-reinforced.svg @@ -0,0 +1,686 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + [Przemo Firszt] + + + fem-material + 2015-07-28 + 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/ui/MaterialReinforcement.ui b/src/Mod/Fem/Gui/Resources/ui/MaterialReinforcement.ui new file mode 100755 index 0000000000..67fa6f6ec1 --- /dev/null +++ b/src/Mod/Fem/Gui/Resources/ui/MaterialReinforcement.ui @@ -0,0 +1,183 @@ + + + FemMaterial + + + + 0 + 0 + 396 + 448 + + + + FEM material + + + Qt::LeftToRight + + + + + + + 16777215 + 1677215 + + + + Matrix Material + + + + + + + + + Choose + + + + + + + + Material + + + + + + + Name + + + + + + + TextLabel + + + + + + + Edit + + + + + + + Properties + + + + + + + + + Description + + + true + + + + + + + + + + + 16777215 + 1677215 + + + + Reinforcement Material + + + + + + + + + Choose + + + + + + + + Material + + + + + + + Name + + + + + + + TextLabel + + + + + + + Edit + + + + + + + Properties + + + + + + + + + Description + + + true + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/src/Mod/Fem/Gui/Workbench.cpp b/src/Mod/Fem/Gui/Workbench.cpp index 84646e8c71..10598fbf14 100755 --- a/src/Mod/Fem/Gui/Workbench.cpp +++ b/src/Mod/Fem/Gui/Workbench.cpp @@ -87,6 +87,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const << "FEM_MaterialSolid" << "FEM_MaterialFluid" << "FEM_MaterialMechanicalNonlinear" + << "FEM_MaterialReinforced" << "FEM_MaterialEditor" << "Separator" << "FEM_ElementGeometry1D" @@ -190,6 +191,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const *material << "FEM_MaterialSolid" << "FEM_MaterialFluid" << "FEM_MaterialMechanicalNonlinear" + << "FEM_MaterialReinforced" << "FEM_MaterialEditor"; Gui::MenuItem* elec = new Gui::MenuItem; diff --git a/src/Mod/Fem/ObjectsFem.py b/src/Mod/Fem/ObjectsFem.py index a49fcabeaf..6ba8a97ea5 100644 --- a/src/Mod/Fem/ObjectsFem.py +++ b/src/Mod/Fem/ObjectsFem.py @@ -372,6 +372,21 @@ def makeMaterialMechanicalNonlinear( return obj +def makeMaterialReinforced( + doc, + name="MaterialReinforced" +): + '''makeMaterialReinforced(document, [matrix_material], [reinforcement_material], [name]): + creates a reinforced material object''' + obj = doc.addObject("App::MaterialObjectPython", name) + from femobjects import _FemMaterialReinforced + _FemMaterialReinforced._FemMaterialReinforced(obj) + if FreeCAD.GuiUp: + from femguiobjects import _ViewProviderFemMaterialReinforced + _ViewProviderFemMaterialReinforced._ViewProviderFemMaterialReinforced(obj.ViewObject) + return obj + + def makeMaterialSolid( doc, name="MechanicalSolidMaterial" diff --git a/src/Mod/Fem/TestFem.py b/src/Mod/Fem/TestFem.py index 03fa8380db..c2412f51f5 100644 --- a/src/Mod/Fem/TestFem.py +++ b/src/Mod/Fem/TestFem.py @@ -126,7 +126,9 @@ gf() ./bin/FreeCADCmd --run-test "femtest.testobject.TestObjectType.test_femobjects_derivedfromstd" ./bin/FreeCADCmd --run-test "femtest.testresult.TestResult.test_read_frd_massflow_networkpressure" ./bin/FreeCADCmd --run-test "femtest.testresult.TestResult.test_stress_von_mises" -./bin/FreeCADCmd --run-test "femtest.testresult.TestResult.test_stress_principal" +./bin/FreeCADCmd --run-test "femtest.testresult.TestResult.test_stress_principal_std" +./bin/FreeCADCmd --run-test "femtest.testresult.TestResult.test_stress_principal_reinforced" +./bin/FreeCADCmd --run-test "femtest.testresult.TestResult.test_rho" ./bin/FreeCADCmd --run-test "femtest.testresult.TestResult.test_disp_abs" ./bin/FreeCADCmd --run-test "femtest.testsolverframework.TestSolverFrameWork.test_solver_framework" @@ -214,7 +216,13 @@ import unittest unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName("femtest.testresult.TestResult.test_stress_von_mises")) import unittest -unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName("femtest.testresult.TestResult.test_stress_principal")) +unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName("femtest.testresult.TestResult.test_stress_principal_std")) + +import unittest +unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName("femtest.testresult.TestResult.test_stress_principal_reinforced")) + +import unittest +unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName("femtest.testresult.TestResult.test_rho")) import unittest unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName("femtest.testresult.TestResult.test_disp_abs")) diff --git a/src/Mod/Fem/femcommands/commands.py b/src/Mod/Fem/femcommands/commands.py index 4a08def59f..e14f1d0146 100644 --- a/src/Mod/Fem/femcommands/commands.py +++ b/src/Mod/Fem/femcommands/commands.py @@ -665,6 +665,37 @@ class _CommandFemMaterialMechanicalNonlinear(CommandManager): FreeCAD.ActiveDocument.recompute() +class _CommandFemMaterialReinforced(CommandManager): + "The FEM_MaterialReinforced command definition" + def __init__(self): + super(_CommandFemMaterialReinforced, self).__init__() + self.resources = { + 'Pixmap': 'fem-material-reinforced', + 'MenuText': QtCore.QT_TRANSLATE_NOOP( + "FEM_MaterialReinforced", + "Reinforced material (concrete)" + ), + 'Accel': "M, M", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP( + "FEM_MaterialReinforced", + "Creates a material for reinforced matrix material such as concrete" + ) + } + self.is_active = 'with_analysis' + + def Activated(self): + FreeCAD.ActiveDocument.openTransaction("Create Reinforced Material") + FreeCADGui.addModule("ObjectsFem") + FreeCADGui.doCommand( + "FemGui.getActiveAnalysis().addObject(ObjectsFem." + "makeMaterialReinforced(FreeCAD.ActiveDocument, 'ReinforcedMaterial'))" + ) + FreeCADGui.doCommand( + "FreeCADGui.ActiveDocument.setEdit(FreeCAD.ActiveDocument.ActiveObject.Name)" + ) + FreeCAD.ActiveDocument.recompute() + + class _CommandFemMaterialSolid(CommandManager): "The FEM_MaterialSolid command definition" def __init__(self): @@ -1296,6 +1327,10 @@ FreeCADGui.addCommand( 'FEM_MaterialMechanicalNonlinear', _CommandFemMaterialMechanicalNonlinear() ) +FreeCADGui.addCommand( + 'FEM_MaterialReinforced', + _CommandFemMaterialReinforced() +) FreeCADGui.addCommand( 'FEM_MaterialSolid', _CommandFemMaterialSolid() diff --git a/src/Mod/Fem/femexamples/manager.py b/src/Mod/Fem/femexamples/manager.py index 0d9eecf32b..3d54e1a8d6 100644 --- a/src/Mod/Fem/femexamples/manager.py +++ b/src/Mod/Fem/femexamples/manager.py @@ -75,6 +75,7 @@ def run_all(): run_ccx_cantileverfaceload() run_ccx_cantilevernodeload() run_ccx_cantileverprescribeddisplacement() + run_rcwall2d() def run_ccx_cantileverfaceload(solver=None, base_name=None): @@ -119,6 +120,20 @@ def run_ccx_cantileverprescribeddisplacement(solver=None, base_name=None): return doc +def run_rcwall2d(solver=None, base_name=None): + + from femexamples.rc_wall_2d import setup_rcwall2d as setup + doc = setup() + + if base_name is None: + base_name = 'RC_FIB_Wall_2D' + if solver is not None: + base_name += ('_' + solver) + run_analysis(doc, base_name) + + return doc + + ''' from femexamples.manager import * @@ -132,4 +147,6 @@ doc = run_ccx_cantilevernodeload('calculix') doc = run_ccx_cantilevernodeload('ccxtools') doc = run_ccx_cantilevernodeload('z88') +doc = run_rcwall2d() + ''' diff --git a/src/Mod/Fem/femexamples/meshes/mesh_rc_wall_2d_tria6.py b/src/Mod/Fem/femexamples/meshes/mesh_rc_wall_2d_tria6.py new file mode 100644 index 0000000000..c257afb5eb --- /dev/null +++ b/src/Mod/Fem/femexamples/meshes/mesh_rc_wall_2d_tria6.py @@ -0,0 +1,1343 @@ +def create_nodes(femmesh): + # nodes + femmesh.addNode(0.0, 0.0, 0.0, 1) + femmesh.addNode(0.0, 2000.0, 0.0, 2) + femmesh.addNode(500.0, 0.0, 0.0, 3) + femmesh.addNode(4000.0, 2000.0, 0.0, 4) + femmesh.addNode(3500.0, 0.0, 0.0, 5) + femmesh.addNode(4000.0, 0.0, 0.0, 6) + femmesh.addNode(0.0, -2000.0, 0.0, 7) + femmesh.addNode(500.0, -2000.0, 0.0, 8) + femmesh.addNode(3500.0, -2000.0, 0.0, 9) + femmesh.addNode(4000.0, -2000.0, 0.0, 10) + femmesh.addNode(0.0, 250.0, 0.0, 11) + femmesh.addNode(0.0, 500.0, 0.0, 12) + femmesh.addNode(0.0, 750.0, 0.0, 13) + femmesh.addNode(0.0, 1000.0, 0.0, 14) + femmesh.addNode(0.0, 1250.0, 0.0, 15) + femmesh.addNode(0.0, 1500.0, 0.0, 16) + femmesh.addNode(0.0, 1750.0, 0.0, 17) + femmesh.addNode(0.0, 125.0, 0.0, 18) + femmesh.addNode(0.0, 375.0, 0.0, 19) + femmesh.addNode(0.0, 625.0, 0.0, 20) + femmesh.addNode(0.0, 875.0, 0.0, 21) + femmesh.addNode(0.0, 1125.0, 0.0, 22) + femmesh.addNode(0.0, 1375.0, 0.0, 23) + femmesh.addNode(0.0, 1625.0, 0.0, 24) + femmesh.addNode(0.0, 1875.0, 0.0, 25) + femmesh.addNode(250.0, 0.0, 0.0, 26) + femmesh.addNode(125.0, 0.0, 0.0, 27) + femmesh.addNode(375.0, 0.0, 0.0, 28) + femmesh.addNode(250.0, 2000.0, 0.0, 29) + femmesh.addNode(500.0, 2000.0, 0.0, 30) + femmesh.addNode(750.0, 2000.0, 0.0, 31) + femmesh.addNode(1000.0, 2000.0, 0.0, 32) + femmesh.addNode(1250.0, 2000.0, 0.0, 33) + femmesh.addNode(1500.0, 2000.0, 0.0, 34) + femmesh.addNode(1750.0, 2000.0, 0.0, 35) + femmesh.addNode(2000.0, 2000.0, 0.0, 36) + femmesh.addNode(2250.0, 2000.0, 0.0, 37) + femmesh.addNode(2500.0, 2000.0, 0.0, 38) + femmesh.addNode(2750.0, 2000.0, 0.0, 39) + femmesh.addNode(3000.0, 2000.0, 0.0, 40) + femmesh.addNode(3250.0, 2000.0, 0.0, 41) + femmesh.addNode(3500.0, 2000.0, 0.0, 42) + femmesh.addNode(3750.0, 2000.0, 0.0, 43) + femmesh.addNode(125.0, 2000.0, 0.0, 44) + femmesh.addNode(375.0, 2000.0, 0.0, 45) + femmesh.addNode(625.0, 2000.0, 0.0, 46) + femmesh.addNode(875.0, 2000.0, 0.0, 47) + femmesh.addNode(1125.0, 2000.0, 0.0, 48) + femmesh.addNode(1375.0, 2000.0, 0.0, 49) + femmesh.addNode(1625.0, 2000.0, 0.0, 50) + femmesh.addNode(1875.0, 2000.0, 0.0, 51) + femmesh.addNode(2125.0, 2000.0, 0.0, 52) + femmesh.addNode(2375.0, 2000.0, 0.0, 53) + femmesh.addNode(2625.0, 2000.0, 0.0, 54) + femmesh.addNode(2875.0, 2000.0, 0.0, 55) + femmesh.addNode(3125.0, 2000.0, 0.0, 56) + femmesh.addNode(3375.0, 2000.0, 0.0, 57) + femmesh.addNode(3625.0, 2000.0, 0.0, 58) + femmesh.addNode(3875.0, 2000.0, 0.0, 59) + femmesh.addNode(750.0, 0.0, 0.0, 60) + femmesh.addNode(1000.0, 0.0, 0.0, 61) + femmesh.addNode(1250.0, 0.0, 0.0, 62) + femmesh.addNode(1500.0, 0.0, 0.0, 63) + femmesh.addNode(1750.0, 0.0, 0.0, 64) + femmesh.addNode(2000.0, 0.0, 0.0, 65) + femmesh.addNode(2250.0, 0.0, 0.0, 66) + femmesh.addNode(2500.0, 0.0, 0.0, 67) + femmesh.addNode(2750.0, 0.0, 0.0, 68) + femmesh.addNode(3000.0, 0.0, 0.0, 69) + femmesh.addNode(3250.0, 0.0, 0.0, 70) + femmesh.addNode(625.0, 0.0, 0.0, 71) + femmesh.addNode(875.0, 0.0, 0.0, 72) + femmesh.addNode(1125.0, 0.0, 0.0, 73) + femmesh.addNode(1375.0, 0.0, 0.0, 74) + femmesh.addNode(1625.0, 0.0, 0.0, 75) + femmesh.addNode(1875.0, 0.0, 0.0, 76) + femmesh.addNode(2125.0, 0.0, 0.0, 77) + femmesh.addNode(2375.0, 0.0, 0.0, 78) + femmesh.addNode(2625.0, 0.0, 0.0, 79) + femmesh.addNode(2875.0, 0.0, 0.0, 80) + femmesh.addNode(3125.0, 0.0, 0.0, 81) + femmesh.addNode(3375.0, 0.0, 0.0, 82) + femmesh.addNode(4000.0, 250.0, 0.0, 83) + femmesh.addNode(4000.0, 500.0, 0.0, 84) + femmesh.addNode(4000.0, 750.0, 0.0, 85) + femmesh.addNode(4000.0, 1000.0, 0.0, 86) + femmesh.addNode(4000.0, 1250.0, 0.0, 87) + femmesh.addNode(4000.0, 1500.0, 0.0, 88) + femmesh.addNode(4000.0, 1750.0, 0.0, 89) + femmesh.addNode(4000.0, 125.0, 0.0, 90) + femmesh.addNode(4000.0, 375.0, 0.0, 91) + femmesh.addNode(4000.0, 625.0, 0.0, 92) + femmesh.addNode(4000.0, 875.0, 0.0, 93) + femmesh.addNode(4000.0, 1125.0, 0.0, 94) + femmesh.addNode(4000.0, 1375.0, 0.0, 95) + femmesh.addNode(4000.0, 1625.0, 0.0, 96) + femmesh.addNode(4000.0, 1875.0, 0.0, 97) + femmesh.addNode(3750.0, 0.0, 0.0, 98) + femmesh.addNode(3625.0, 0.0, 0.0, 99) + femmesh.addNode(3875.0, 0.0, 0.0, 100) + femmesh.addNode(0.0, -1750.0, 0.0, 101) + femmesh.addNode(0.0, -1500.0, 0.0, 102) + femmesh.addNode(0.0, -1250.0, 0.0, 103) + femmesh.addNode(0.0, -1000.0, 0.0, 104) + femmesh.addNode(0.0, -750.0, 0.0, 105) + femmesh.addNode(0.0, -500.0, 0.0, 106) + femmesh.addNode(0.0, -250.0, 0.0, 107) + femmesh.addNode(0.0, -1875.0, 0.0, 108) + femmesh.addNode(0.0, -1625.0, 0.0, 109) + femmesh.addNode(0.0, -1375.0, 0.0, 110) + femmesh.addNode(0.0, -1125.0, 0.0, 111) + femmesh.addNode(0.0, -875.0, 0.0, 112) + femmesh.addNode(0.0, -625.0, 0.0, 113) + femmesh.addNode(0.0, -375.0, 0.0, 114) + femmesh.addNode(0.0, -125.0, 0.0, 115) + femmesh.addNode(250.0, -2000.0, 0.0, 116) + femmesh.addNode(125.0, -2000.0, 0.0, 117) + femmesh.addNode(375.0, -2000.0, 0.0, 118) + femmesh.addNode(500.0, -1750.0, 0.0, 119) + femmesh.addNode(500.0, -1500.0, 0.0, 120) + femmesh.addNode(500.0, -1250.0, 0.0, 121) + femmesh.addNode(500.0, -1000.0, 0.0, 122) + femmesh.addNode(500.0, -750.0, 0.0, 123) + femmesh.addNode(500.0, -500.0, 0.0, 124) + femmesh.addNode(500.0, -250.0, 0.0, 125) + femmesh.addNode(500.0, -1875.0, 0.0, 126) + femmesh.addNode(500.0, -1625.0, 0.0, 127) + femmesh.addNode(500.0, -1375.0, 0.0, 128) + femmesh.addNode(500.0, -1125.0, 0.0, 129) + femmesh.addNode(500.0, -875.0, 0.0, 130) + femmesh.addNode(500.0, -625.0, 0.0, 131) + femmesh.addNode(500.0, -375.0, 0.0, 132) + femmesh.addNode(500.0, -125.0, 0.0, 133) + femmesh.addNode(3500.0, -1750.0, 0.0, 134) + femmesh.addNode(3500.0, -1500.0, 0.0, 135) + femmesh.addNode(3500.0, -1250.0, 0.0, 136) + femmesh.addNode(3500.0, -1000.0, 0.0, 137) + femmesh.addNode(3500.0, -750.0, 0.0, 138) + femmesh.addNode(3500.0, -500.0, 0.0, 139) + femmesh.addNode(3500.0, -250.0, 0.0, 140) + femmesh.addNode(3500.0, -1875.0, 0.0, 141) + femmesh.addNode(3500.0, -1625.0, 0.0, 142) + femmesh.addNode(3500.0, -1375.0, 0.0, 143) + femmesh.addNode(3500.0, -1125.0, 0.0, 144) + femmesh.addNode(3500.0, -875.0, 0.0, 145) + femmesh.addNode(3500.0, -625.0, 0.0, 146) + femmesh.addNode(3500.0, -375.0, 0.0, 147) + femmesh.addNode(3500.0, -125.0, 0.0, 148) + femmesh.addNode(3750.0, -2000.0, 0.0, 149) + femmesh.addNode(3625.0, -2000.0, 0.0, 150) + femmesh.addNode(3875.0, -2000.0, 0.0, 151) + femmesh.addNode(4000.0, -1750.0, 0.0, 152) + femmesh.addNode(4000.0, -1500.0, 0.0, 153) + femmesh.addNode(4000.0, -1250.0, 0.0, 154) + femmesh.addNode(4000.0, -1000.0, 0.0, 155) + femmesh.addNode(4000.0, -750.0, 0.0, 156) + femmesh.addNode(4000.0, -500.0, 0.0, 157) + femmesh.addNode(4000.0, -250.0, 0.0, 158) + femmesh.addNode(4000.0, -1875.0, 0.0, 159) + femmesh.addNode(4000.0, -1625.0, 0.0, 160) + femmesh.addNode(4000.0, -1375.0, 0.0, 161) + femmesh.addNode(4000.0, -1125.0, 0.0, 162) + femmesh.addNode(4000.0, -875.0, 0.0, 163) + femmesh.addNode(4000.0, -625.0, 0.0, 164) + femmesh.addNode(4000.0, -375.0, 0.0, 165) + femmesh.addNode(4000.0, -125.0, 0.0, 166) + femmesh.addNode(1151.2155027143724, 991.1595002148733, 0.0, 167) + femmesh.addNode(2345.552182966278, 992.5521461426597, 0.0, 168) + femmesh.addNode(3153.541159295079, 1199.5398685297591, 0.0, 169) + femmesh.addNode(1750.0, 695.3125, 0.0, 170) + femmesh.addNode(1681.25, 1347.65625, 0.0, 171) + femmesh.addNode(2881.435948455562, 621.2099677847261, 0.0, 172) + femmesh.addNode(606.0449604906953, 635.4082348210767, 0.0, 173) + femmesh.addNode(657.2976568366603, 1353.3222602999324, 0.0, 174) + femmesh.addNode(3445.1692479821986, 652.3366992631719, 0.0, 175) + femmesh.addNode(2674.4322928661404, 1434.2147374924186, 0.0, 176) + femmesh.addNode(2185.539194525211, 1473.115044788804, 0.0, 177) + femmesh.addNode(1273.2425124792012, 510.71781405990015, 0.0, 178) + femmesh.addNode(2215.6598753905773, 520.7910541541594, 0.0, 179) + femmesh.addNode(1115.670904262049, 1485.8186742572047, 0.0, 180) + femmesh.addNode(3519.3278588807784, 1519.3278588807786, 0.0, 181) + femmesh.addNode(388.34501905880387, 1026.9203157654522, 0.0, 182) + femmesh.addNode(3614.4372160624785, 1020.2250489643089, 0.0, 183) + femmesh.addNode(3105.4482841646395, 1593.0462413828066, 0.0, 184) + femmesh.addNode(2818.493830446949, 990.0975624837915, 0.0, 185) + femmesh.addNode(1959.2775916812711, 1064.8265300145413, 0.0, 186) + femmesh.addNode(1546.3069778764673, 1011.232872978363, 0.0, 187) + femmesh.addNode(2591.0841961277283, 363.09805979524884, 0.0, 188) + femmesh.addNode(900.8498339227292, 372.74375656728824, 0.0, 189) + femmesh.addNode(3159.4114197567123, 364.99779587913827, 0.0, 190) + femmesh.addNode(390.87897826169245, 345.62828737063484, 0.0, 191) + femmesh.addNode(413.062332090631, 1650.5023139707077, 0.0, 192) + femmesh.addNode(1599.1990546119257, 331.5972138397307, 0.0, 193) + femmesh.addNode(1495.152113940753, 1619.2944541079414, 0.0, 194) + femmesh.addNode(1863.5454857801321, 1658.2389001513154, 0.0, 195) + femmesh.addNode(778.2580283531327, 931.2966961410348, 0.0, 196) + femmesh.addNode(3674.5511265257346, 338.9844927685506, 0.0, 197) + femmesh.addNode(3172.7789481491654, 832.1374169291745, 0.0, 198) + femmesh.addNode(784.7082624860842, 1653.7264044731728, 0.0, 199) + femmesh.addNode(2554.739497815064, 712.365487790745, 0.0, 200) + femmesh.addNode(1943.1567961046958, 330.9370445232647, 0.0, 201) + femmesh.addNode(981.3704439777061, 695.0014769598008, 0.0, 202) + femmesh.addNode(2453.2517051286445, 1672.8747213645538, 0.0, 203) + femmesh.addNode(907.6249789310948, 1264.8279056360939, 0.0, 204) + femmesh.addNode(1342.8357754800325, 1305.982903143703, 0.0, 205) + femmesh.addNode(301.68045436748366, 1278.0614848964112, 0.0, 206) + femmesh.addNode(321.4019295935336, 712.8743177284927, 0.0, 207) + femmesh.addNode(2426.6601885758864, 1313.9261459477857, 0.0, 208) + femmesh.addNode(2867.9421371538983, 263.92048664402716, 0.0, 209) + femmesh.addNode(2046.149001386518, 725.1823701492324, 0.0, 210) + femmesh.addNode(2854.1111443273658, 1741.083702840498, 0.0, 211) + femmesh.addNode(3695.4452236106526, 1279.177794976091, 0.0, 212) + femmesh.addNode(3685.7292713722686, 751.032843822758, 0.0, 213) + femmesh.addNode(624.8652096522102, 258.70949318509355, 0.0, 214) + femmesh.addNode(1466.6263015417012, 719.2247946131447, 0.0, 215) + femmesh.addNode(3719.1293832862143, 1719.129383286214, 0.0, 216) + femmesh.addNode(2945.872854070339, 1363.49471903432, 0.0, 217) + femmesh.addNode(3344.6356052877895, 1737.2354549848008, 0.0, 218) + femmesh.addNode(1942.1605314429821, 1315.7220947444227, 0.0, 219) + femmesh.addNode(2093.97656761822, 1743.5731735527538, 0.0, 220) + femmesh.addNode(2337.404696236552, 259.86552863747835, 0.0, 221) + femmesh.addNode(1136.4178616265353, 256.9280165667089, 0.0, 222) + femmesh.addNode(3436.9983781650485, 1261.4961473823978, 0.0, 223) + femmesh.addNode(3406.2893160086924, 251.07304786541548, 0.0, 224) + femmesh.addNode(2215.636485028124, 1177.419912858442, 0.0, 225) + femmesh.addNode(1126.6678439977832, 1759.6448940404007, 0.0, 226) + femmesh.addNode(2335.9075546219287, 765.8443516431239, 0.0, 227) + femmesh.addNode(623.3677071526471, 1794.4665663127212, 0.0, 228) + femmesh.addNode(1183.6674808624416, 743.4840698227251, 0.0, 229) + femmesh.addNode(676.4452885380255, 1103.2769600640684, 0.0, 230) + femmesh.addNode(1747.334612316145, 938.7081036305343, 0.0, 231) + femmesh.addNode(1403.1870558121493, 254.37888532175242, 0.0, 232) + femmesh.addNode(3314.847513203245, 987.8243030056708, 0.0, 233) + femmesh.addNode(1640.5623138995034, 1785.4652572759182, 0.0, 234) + femmesh.addNode(2694.302594697601, 1191.5326623293304, 0.0, 235) + femmesh.addNode(3249.075663998931, 1405.1235272166903, 0.0, 236) + femmesh.addNode(3194.7171333591295, 605.4257226147388, 0.0, 237) + femmesh.addNode(3787.3499723629325, 540.6602594003787, 0.0, 238) + femmesh.addNode(185.04426967129564, 1086.6700079903421, 0.0, 239) + femmesh.addNode(3046.934155784866, 1000.6169552050135, 0.0, 240) + femmesh.addNode(1969.4468096267958, 523.2169628726109, 0.0, 241) + femmesh.addNode(2129.5909553861516, 192.09252274746703, 0.0, 242) + femmesh.addNode(1136.6994499219468, 1211.2946783273387, 0.0, 243) + femmesh.addNode(2597.352264247392, 1808.9701221137507, 0.0, 244) + femmesh.addNode(3819.1744978371717, 1088.9949533284503, 0.0, 245) + femmesh.addNode(2584.2256241005716, 935.7579854262561, 0.0, 246) + femmesh.addNode(3125.0, 1815.2509614904245, 0.0, 247) + femmesh.addNode(3777.6248611905694, 1504.537741959381, 0.0, 248) + femmesh.addNode(588.8115450690148, 859.6463157782961, 0.0, 249) + femmesh.addNode(2143.2683627665697, 936.9869149561475, 0.0, 250) + femmesh.addNode(2754.7204686764762, 780.5893488071242, 0.0, 251) + femmesh.addNode(178.93890438705535, 903.482590983227, 0.0, 252) + femmesh.addNode(1747.982107629327, 1148.2128944222316, 0.0, 253) + femmesh.addNode(1666.5850159712468, 1563.4019198648652, 0.0, 254) + femmesh.addNode(851.0519754307722, 1495.0619168135465, 0.0, 255) + femmesh.addNode(3481.0728671542547, 837.1465204467868, 0.0, 256) + femmesh.addNode(2443.601401640797, 530.7979808791658, 0.0, 257) + femmesh.addNode(205.94642509722854, 499.4693567890037, 0.0, 258) + femmesh.addNode(206.07459453719548, 1499.857298977669, 0.0, 259) + femmesh.addNode(834.6180802078031, 567.491198370818, 0.0, 260) + femmesh.addNode(1766.7929131536002, 212.0366200123763, 0.0, 261) + femmesh.addNode(1372.868119293763, 1786.5908878427776, 0.0, 262) + femmesh.addNode(217.93466600459956, 214.41626585864105, 0.0, 263) + femmesh.addNode(211.16821383061668, 1789.0339304012352, 0.0, 264) + femmesh.addNode(1346.4369194052335, 1104.0442859810998, 0.0, 265) + femmesh.addNode(2625.0, 168.47319683138994, 0.0, 266) + femmesh.addNode(875.0000000000001, 168.27854442880616, 0.0, 267) + femmesh.addNode(3124.9999999999995, 166.32624951132104, 0.0, 268) + femmesh.addNode(2725.0455335697234, 531.4266954931735, 0.0, 269) + femmesh.addNode(1522.1536134860664, 504.98683031131714, 0.0, 270) + femmesh.addNode(3802.9015660741943, 197.09843392580635, 0.0, 271) + femmesh.addNode(1505.6503066232626, 1428.2716333977896, 0.0, 272) + femmesh.addNode(1051.6943688460692, 524.3943887245401, 0.0, 273) + femmesh.addNode(3359.4878194613807, 477.16337384383746, 0.0, 274) + femmesh.addNode(1528.4103478357204, 1224.233473320531, 0.0, 275) + femmesh.addNode(3535.438527288789, 1801.7330078935504, 0.0, 276) + femmesh.addNode(434.67162780804375, 542.3401347012916, 0.0, 277) + femmesh.addNode(451.9072533191967, 1434.6301952460099, 0.0, 278) + femmesh.addNode(2981.795085118534, 448.0055367511777, 0.0, 279) + femmesh.addNode(2978.1800808120247, 805.0128289707615, 0.0, 280) + femmesh.addNode(935.1388659912074, 1056.3754597693553, 0.0, 281) + femmesh.addNode(1754.4192556759315, 451.41573980441683, 0.0, 282) + femmesh.addNode(2361.398753416636, 1810.7310101916673, 0.0, 283) + femmesh.addNode(2422.5421676233855, 1516.583426074803, 0.0, 284) + femmesh.addNode(880.47047742822, 1807.7722912938007, 0.0, 285) + femmesh.addNode(630.2325312193318, 1563.618276186015, 0.0, 286) + femmesh.addNode(444.11067943331193, 184.20265254642115, 0.0, 287) + femmesh.addNode(399.0489951122346, 1843.0986785146765, 0.0, 288) + femmesh.addNode(1326.4741772662737, 1564.2672411316362, 0.0, 289) + femmesh.addNode(1873.5987279934327, 1848.0564063658549, 0.0, 290) + femmesh.addNode(2857.8120840696374, 1538.7765004950957, 0.0, 291) + femmesh.addNode(3615.420858496385, 153.92013164518346, 0.0, 292) + femmesh.addNode(1986.4322329809784, 1519.8742672628473, 0.0, 293) + femmesh.addNode(795.7903110696158, 761.2330506728068, 0.0, 294) + femmesh.addNode(3827.887082573013, 916.2471888959267, 0.0, 295) + femmesh.addNode(2931.8289188589665, 1149.0563535164429, 0.0, 296) + femmesh.addNode(3857.0724217345637, 373.58588179238143, 0.0, 297) + femmesh.addNode(1603.8358047155352, 159.6025438347719, 0.0, 298) + femmesh.addNode(2632.697761232849, 1624.134712542077, 0.0, 299) + femmesh.addNode(981.8039384323351, 874.5293213047062, 0.0, 300) + femmesh.addNode(680.2099424162964, 475.33856352911357, 0.0, 301) + femmesh.addNode(3559.7627922855345, 501.8751194940187, 0.0, 302) + femmesh.addNode(974.4233579675891, 1639.6167774462617, 0.0, 303) + femmesh.addNode(495.13513442403405, 1239.242243254375, 0.0, 304) + femmesh.addNode(1377.2739687571882, 899.7018057699061, 0.0, 305) + femmesh.addNode(3867.2823458215535, 1867.2823458215535, 0.0, 306) + femmesh.addNode(3343.8770189511474, 1557.6498333315424, 0.0, 307) + femmesh.addNode(1824.8290135155744, 1476.484523517676, 0.0, 308) + femmesh.addNode(1577.5083720983002, 852.8360153983897, 0.0, 309) + femmesh.addNode(1914.9321930628041, 875.9179316201704, 0.0, 310) + femmesh.addNode(2284.573533854269, 1640.5742507791397, 0.0, 311) + femmesh.addNode(2119.0518265489545, 365.3806225869961, 0.0, 312) + femmesh.addNode(2453.275415073692, 1122.2377705408946, 0.0, 313) + femmesh.addNode(3322.401080864958, 774.4507864829744, 0.0, 314) + femmesh.addNode(666.8714726873429, 1228.2996101820004, 0.0, 315) + femmesh.addNode(792.0351337345601, 1184.052432850081, 0.0, 316) + femmesh.addNode(782.4613178838775, 1309.0750829680132, 0.0, 317) + femmesh.addNode(108.96733300229978, 232.20813292932053, 0.0, 318) + femmesh.addNode(211.94054555091407, 356.9428113238224, 0.0, 319) + femmesh.addNode(102.97321254861427, 374.7346783945019, 0.0, 320) + femmesh.addNode(208.62140418390607, 1644.4456146894522, 0.0, 321) + femmesh.addNode(105.58410691530834, 1769.5169652006175, 0.0, 322) + femmesh.addNode(103.03729726859774, 1624.9286494888345, 0.0, 323) + femmesh.addNode(1854.974854629148, 271.4868322678205, 0.0, 324) + femmesh.addNode(1883.3964565768001, 106.01831000618814, 0.0, 325) + femmesh.addNode(1971.578398052348, 165.46852226163236, 0.0, 326) + femmesh.addNode(2802.055572163683, 1870.541851420249, 0.0, 327) + femmesh.addNode(2927.055572163683, 1870.541851420249, 0.0, 328) + femmesh.addNode(2684.367443781871, 1312.8736999108746, 0.0, 329) + femmesh.addNode(2820.08772438397, 1277.5136906818252, 0.0, 330) + femmesh.addNode(2810.15257346824, 1398.8547282633695, 0.0, 331) + femmesh.addNode(3888.8124305952847, 1377.2688709796905, 0.0, 332) + femmesh.addNode(3736.535042400611, 1391.8577684677362, 0.0, 333) + femmesh.addNode(3847.7226118053263, 1264.5888974880454, 0.0, 334) + femmesh.addNode(3086.4486070855774, 705.2192757927501, 0.0, 335) + femmesh.addNode(2929.8080146337934, 713.1113983777439, 0.0, 336) + femmesh.addNode(3038.0765409073456, 613.3178451997325, 0.0, 337) + femmesh.addNode(2989.555572163683, 1778.1673321654612, 0.0, 338) + femmesh.addNode(3062.5, 1907.6254807452124, 0.0, 339) + femmesh.addNode(2924.8686111362163, 355.96301169760244, 0.0, 340) + femmesh.addNode(2853.420309344129, 489.7161161221756, 0.0, 341) + femmesh.addNode(2796.493835361811, 397.67359106860033, 0.0, 342) + femmesh.addNode(2813.0657567782837, 1170.2945079228866, 0.0, 343) + femmesh.addNode(2938.850886464653, 1256.2755362753815, 0.0, 344) + femmesh.addNode(102.97321254861427, 499.7346783945019, 0.0, 345) + femmesh.addNode(103.03729726859774, 1499.9286494888345, 0.0, 346) + femmesh.addNode(2658.064864848726, 447.26237764421114, 0.0, 347) + femmesh.addNode(2729.5131666408133, 313.509273219638, 0.0, 348) + femmesh.addNode(529.768418730127, 400.5248139431926, 0.0, 349) + femmesh.addNode(412.7753030348681, 443.9842110359632, 0.0, 350) + femmesh.addNode(507.8720939569513, 302.1688902778642, 0.0, 351) + femmesh.addNode(652.5375760342533, 367.0240283571036, 0.0, 352) + femmesh.addNode(557.44078511217, 508.8393491152026, 0.0, 353) + femmesh.addNode(3888.8124305952847, 1502.2688709796905, 0.0, 354) + femmesh.addNode(2064.7954776930756, 96.04626137373351, 0.0, 355) + femmesh.addNode(2036.3738757454237, 261.51478363536586, 0.0, 356) + femmesh.addNode(983.3614398464106, 1490.4402955353758, 0.0, 357) + femmesh.addNode(879.3384771809335, 1379.9449112248203, 0.0, 358) + femmesh.addNode(1011.6479415965719, 1375.3232899466493, 0.0, 359) + femmesh.addNode(102.97321254861427, 624.7346783945019, 0.0, 360) + femmesh.addNode(263.67417734538105, 606.1718372587482, 0.0, 361) + femmesh.addNode(160.7009647967668, 731.4371588642464, 0.0, 362) + femmesh.addNode(253.87752445233957, 1388.95939193704, 0.0, 363) + femmesh.addNode(103.03729726859774, 1374.9286494888345, 0.0, 364) + femmesh.addNode(150.84022718374183, 1264.0307424482057, 0.0, 365) + femmesh.addNode(2569.482560957818, 824.0617366085005, 0.0, 366) + femmesh.addNode(2460.0665893612504, 850.8011685346901, 0.0, 367) + femmesh.addNode(2445.323526218496, 739.1049197169344, 0.0, 368) + femmesh.addNode(2464.888903533425, 964.155065784458, 0.0, 369) + femmesh.addNode(2340.7298687941034, 879.1982488928918, 0.0, 370) + femmesh.addNode(3748.377122238392, 1611.8335626227977, 0.0, 371) + femmesh.addNode(3888.8124305952847, 1627.2688709796905, 0.0, 372) + femmesh.addNode(3859.564691643107, 1734.564691643107, 0.0, 373) + femmesh.addNode(2200.5878397766674, 1325.2674788236232, 0.0, 374) + femmesh.addNode(2321.1483368020054, 1245.673029403114, 0.0, 375) + femmesh.addNode(2306.0996915505484, 1393.5205953682948, 0.0, 376) + femmesh.addNode(1436.4340596468815, 1893.2954439213888, 0.0, 377) + femmesh.addNode(1506.7152165966331, 1786.028072559348, 0.0, 378) + femmesh.addNode(1570.2811569497517, 1892.7326286379591, 0.0, 379) + femmesh.addNode(2933.971068576949, 131.96024332201358, 0.0, 380) + femmesh.addNode(2808.971068576949, 131.96024332201358, 0.0, 381) + femmesh.addNode(2931.6155167870484, 534.6077522679519, 0.0, 382) + femmesh.addNode(3088.256109238832, 526.7156296829583, 0.0, 383) + femmesh.addNode(2548.487230244763, 1475.3990817836107, 0.0, 384) + femmesh.addNode(2424.601178099636, 1415.2547860112943, 0.0, 385) + femmesh.addNode(2550.5462407210134, 1374.0704417201023, 0.0, 386) + femmesh.addNode(1226.5710106320284, 1661.9560675860184, 0.0, 387) + femmesh.addNode(1349.6711482800183, 1675.429064487207, 0.0, 388) + femmesh.addNode(1249.767981645773, 1773.1178909415892, 0.0, 389) + femmesh.addNode(1022.1622144265208, 1238.0612919817163, 0.0, 390) + femmesh.addNode(1126.185177091998, 1348.5566762922717, 0.0, 391) + femmesh.addNode(805.7920772646164, 1079.8262099167118, 0.0, 392) + femmesh.addNode(921.3819224611511, 1160.6016827027247, 0.0, 393) + femmesh.addNode(1239.7676127009895, 1258.6387907355208, 0.0, 394) + femmesh.addNode(1229.2533398710407, 1395.9007887004539, 0.0, 395) + femmesh.addNode(597.428252779855, 747.5272752996864, 0.0, 396) + femmesh.addNode(455.1067373312742, 786.2603167533944, 0.0, 397) + femmesh.addNode(463.72344504211446, 674.1412762747847, 0.0, 398) + femmesh.addNode(488.57828206390934, 943.2833157718742, 0.0, 399) + femmesh.addNode(354.8734743261688, 869.8973167469724, 0.0, 400) + femmesh.addNode(687.432604826105, 129.35474659254677, 0.0, 401) + femmesh.addNode(562.432604826105, 129.35474659254677, 0.0, 402) + femmesh.addNode(1334.6549763731532, 1435.1250721376696, 0.0, 403) + femmesh.addNode(1221.0725407641612, 1525.0429576944205, 0.0, 404) + femmesh.addNode(1494.389957513884, 612.1058124622309, 0.0, 405) + femmesh.addNode(1636.076806743033, 600.1496651556586, 0.0, 406) + femmesh.addNode(1608.3131507708506, 707.2686473065723, 0.0, 407) + femmesh.addNode(754.1748161337163, 1424.1920885567395, 0.0, 408) + femmesh.addNode(1462.6703346491079, 379.6828578165348, 0.0, 409) + femmesh.addNode(1397.698062982634, 507.8523221856086, 0.0, 410) + femmesh.addNode(1338.2147841456754, 382.5483496908263, 0.0, 411) + femmesh.addNode(378.03677870078866, 627.6072262148921, 0.0, 412) + femmesh.addNode(520.3582941493695, 588.8741847611841, 0.0, 413) + femmesh.addNode(1269.8024587193422, 255.65345094423066, 0.0, 414) + femmesh.addNode(1204.8301870528683, 383.8229153133045, 0.0, 415) + femmesh.addNode(3736.5396218676005, 645.8465516115683, 0.0, 416) + femmesh.addNode(3893.674986181466, 645.3301297001893, 0.0, 417) + femmesh.addNode(3842.8646356861345, 750.516421911379, 0.0, 418) + femmesh.addNode(1638.286434580999, 478.201285057867, 0.0, 419) + femmesh.addNode(1752.2096278379659, 573.3641199022084, 0.0, 420) + femmesh.addNode(3115.2241420823198, 1704.1486014366155, 0.0, 421) + femmesh.addNode(2979.7797142460026, 1667.0649721116524, 0.0, 422) + femmesh.addNode(2653.565027049495, 1529.174725017248, 0.0, 423) + femmesh.addNode(2527.6199644281173, 1570.35906930844, 0.0, 424) + femmesh.addNode(2007.797905506657, 624.1996665109216, 0.0, 425) + femmesh.addNode(2092.5533425086865, 522.0040085133851, 0.0, 426) + femmesh.addNode(2130.9044383885475, 622.9867121516959, 0.0, 427) + femmesh.addNode(1434.010116617258, 1702.9426709753595, 0.0, 428) + femmesh.addNode(1567.8572139201283, 1702.3798556919298, 0.0, 429) + femmesh.addNode(1005.7089308132677, 212.6032804977575, 0.0, 430) + femmesh.addNode(937.5, 84.13927221440308, 0.0, 431) + femmesh.addNode(1068.2089308132677, 128.46400828335445, 0.0, 432) + femmesh.addNode(1121.169374129916, 1622.7317841488027, 0.0, 433) + femmesh.addNode(3297.3178026438945, 1868.6177274924003, 0.0, 434) + femmesh.addNode(3422.3178026438945, 1868.6177274924003, 0.0, 435) + femmesh.addNode(2562.5, 84.23659841569497, 0.0, 436) + femmesh.addNode(2481.202348118276, 214.16936273443415, 0.0, 437) + femmesh.addNode(2418.702348118276, 129.93276431873917, 0.0, 438) + femmesh.addNode(283.64196172292964, 965.2014533743396, 0.0, 439) + femmesh.addNode(250.17041699029448, 808.1784543558599, 0.0, 440) + femmesh.addNode(3566.221800887851, 1270.3369711792443, 0.0, 441) + femmesh.addNode(3525.7177971137635, 1140.8605981733533, 0.0, 442) + femmesh.addNode(3654.9412198365653, 1149.7014219702, 0.0, 443) + femmesh.addNode(3478.1631185229135, 1390.4120031315883, 0.0, 444) + femmesh.addNode(3607.3865412457153, 1399.2528269284348, 0.0, 445) + femmesh.addNode(92.52213483564782, 1168.335003995171, 0.0, 446) + femmesh.addNode(243.36236201938965, 1182.3657464433768, 0.0, 447) + femmesh.addNode(1193.2089308132677, 128.46400828335445, 0.0, 448) + femmesh.addNode(2293.702348118276, 129.93276431873917, 0.0, 449) + femmesh.addNode(2078.898508235553, 1246.5710038014324, 0.0, 450) + femmesh.addNode(2063.8498629840965, 1394.4185697666135, 0.0, 451) + femmesh.addNode(2430.6993767083177, 1905.3655050958337, 0.0, 452) + femmesh.addNode(2479.375508832014, 1809.850566152709, 0.0, 453) + femmesh.addNode(2548.6761321236963, 1904.4850610568753, 0.0, 454) + femmesh.addNode(3617.1569594056346, 420.42980613128464, 0.0, 455) + femmesh.addNode(3483.0260541471134, 376.47408367971707, 0.0, 456) + femmesh.addNode(3540.4202212672135, 295.028770316983, 0.0, 457) + femmesh.addNode(3459.6253058734574, 489.5192466689281, 0.0, 458) + femmesh.addNode(3382.8885677350363, 364.11821085462645, 0.0, 459) + femmesh.addNode(2803.2407410126425, 576.3183316389498, 0.0, 460) + femmesh.addNode(2407.32522927264, 1741.8028657781106, 0.0, 461) + femmesh.addNode(2525.3019846880184, 1740.9224217391522, 0.0, 462) + femmesh.addNode(1117.6809248542554, 633.9392292736327, 0.0, 463) + femmesh.addNode(1162.4684406626352, 517.5561013922202, 0.0, 464) + femmesh.addNode(1228.4549966708214, 627.1009419413126, 0.0, 465) + femmesh.addNode(3075.479514480595, 818.575122949968, 0.0, 466) + femmesh.addNode(3183.7480407541475, 718.7815697719566, 0.0, 467) + femmesh.addNode(3100.2376575399726, 1100.0784118673864, 0.0, 468) + femmesh.addNode(3180.8908344940555, 994.2206291053421, 0.0, 469) + femmesh.addNode(3234.194336249162, 1093.6820857677149, 0.0, 470) + femmesh.addNode(3109.8565519670155, 916.377186067094, 0.0, 471) + femmesh.addNode(3243.8132306762054, 909.9808599674227, 0.0, 472) + femmesh.addNode(89.46945219352767, 826.7412954916135, 0.0, 473) + femmesh.addNode(286.6946443650497, 1056.795161877897, 0.0, 474) + femmesh.addNode(345.0127367131438, 1152.4909003309317, 0.0, 475) + femmesh.addNode(2275.7837150062533, 643.3177028986416, 0.0, 476) + femmesh.addNode(2191.0282780042235, 745.5133608961781, 0.0, 477) + femmesh.addNode(441.740076741419, 1133.0812795099137, 0.0, 478) + femmesh.addNode(398.40779439575886, 1258.651864075393, 0.0, 479) + femmesh.addNode(751.9190922904336, 1801.119428803261, 0.0, 480) + femmesh.addNode(815.23523871411, 1903.8861456469003, 0.0, 481) + femmesh.addNode(686.6838535763236, 1897.2332831563606, 0.0, 482) + femmesh.addNode(1758.3964565768001, 106.01831000618814, 0.0, 483) + femmesh.addNode(1016.5324064118877, 609.6979328421705, 0.0, 484) + femmesh.addNode(1082.5189624200739, 719.242773391263, 0.0, 485) + femmesh.addNode(2304.040681074298, 1494.8492354318034, 0.0, 486) + femmesh.addNode(3453.144658004346, 125.53652393270774, 0.0, 487) + femmesh.addNode(3328.144658004346, 125.53652393270774, 0.0, 488) + femmesh.addNode(3201.308411647005, 1302.3316978732246, 0.0, 489) + femmesh.addNode(3097.474259034635, 1384.3091231255053, 0.0, 490) + femmesh.addNode(3049.707006682709, 1281.5172937820396, 0.0, 491) + femmesh.addNode(3177.2619740817854, 1499.0848842997484, 0.0, 492) + femmesh.addNode(3025.660569117489, 1478.2704802085632, 0.0, 493) + femmesh.addNode(856.6984471721701, 993.836077955195, 0.0, 494) + femmesh.addNode(727.351658445579, 1017.2868281025516, 0.0, 495) + femmesh.addNode(3265.6446580043457, 208.69964868836826, 0.0, 496) + femmesh.addNode(3187.5, 83.16312475566052, 0.0, 497) + femmesh.addNode(534.487944542761, 221.45607286575733, 0.0, 498) + femmesh.addNode(472.05533971665596, 92.10132627321057, 0.0, 499) + femmesh.addNode(2746.471068576949, 216.19684173770855, 0.0, 500) + femmesh.addNode(2687.5, 84.23659841569497, 0.0, 501) + femmesh.addNode(3893.674986181466, 520.3301297001893, 0.0, 502) + femmesh.addNode(2608.042098063864, 265.7856283133194, 0.0, 503) + femmesh.addNode(3062.5, 83.16312475566052, 0.0, 504) + femmesh.addNode(2996.4710685769487, 215.12336807767412, 0.0, 505) + femmesh.addNode(3142.205709878356, 265.66202269522967, 0.0, 506) + femmesh.addNode(3013.6767784553053, 314.4591412615827, 0.0, 507) + femmesh.addNode(887.9249169613647, 270.5111504980472, 0.0, 508) + femmesh.addNode(1018.6338477746323, 314.83588656699857, 0.0, 509) + femmesh.addNode(3384.2017614398546, 1462.2256930487345, 0.0, 510) + femmesh.addNode(3343.0370210819897, 1333.3098372995441, 0.0, 511) + femmesh.addNode(2560.481391636744, 1252.729404138558, 0.0, 512) + femmesh.addNode(3431.602438915963, 1538.4888461061605, 0.0, 513) + femmesh.addNode(3296.476341475039, 1481.3866802741163, 0.0, 514) + femmesh.addNode(2464.24444618214, 311.4817942163636, 0.0, 515) + femmesh.addNode(2087.4570383546975, 1121.1232214364918, 0.0, 516) + femmesh.addNode(1950.7190615621266, 1190.2743123794821, 0.0, 517) + femmesh.addNode(2399.413799019985, 1057.394958341777, 0.0, 518) + femmesh.addNode(2334.4559500509076, 1149.8288416996684, 0.0, 519) + femmesh.addNode(2280.594333997201, 1084.986029500551, 0.0, 520) + femmesh.addNode(1325.1468912020714, 731.3544322179349, 0.0, 521) + femmesh.addNode(1369.9344070104512, 614.9713043365224, 0.0, 522) + femmesh.addNode(561.6838535763236, 1897.2332831563606, 0.0, 523) + femmesh.addNode(3756.8081769726405, 833.6400163593423, 0.0, 524) + femmesh.addNode(3721.1621493177454, 968.2361189301178, 0.0, 525) + femmesh.addNode(3650.0832437173735, 885.6289463935334, 0.0, 526) + femmesh.addNode(2439.967801824789, 1218.08195824434, 0.0, 527) + femmesh.addNode(3070.6032524376233, 406.501666315158, 0.0, 528) + femmesh.addNode(3440.037066288289, 1769.4842314391756, 0.0, 529) + femmesh.addNode(3517.7192636443942, 1900.866503946775, 0.0, 530) + femmesh.addNode(2046.98828380911, 1871.786586776377, 0.0, 531) + femmesh.addNode(2171.98828380911, 1871.786586776377, 0.0, 532) + femmesh.addNode(312.1152729606238, 1719.7681221859716, 0.0, 533) + femmesh.addNode(309.56846331391324, 1575.1798064741884, 0.0, 534) + femmesh.addNode(304.406822133146, 280.0222766146379, 0.0, 535) + femmesh.addNode(298.4127016794605, 422.5488220798193, 0.0, 536) + femmesh.addNode(3375.9229456841467, 1124.6602251940344, 0.0, 537) + femmesh.addNode(3464.6423646328617, 1004.0246759849899, 0.0, 538) + femmesh.addNode(3295.269768730064, 1230.5180079560785, 0.0, 539) + femmesh.addNode(1747.658359972736, 1043.4604990263829, 0.0, 540) + femmesh.addNode(1647.144542752897, 1079.7228837002972, 0.0, 541) + femmesh.addNode(1646.820795096306, 974.9704883044487, 0.0, 542) + femmesh.addNode(832.589369957152, 1730.7493478834867, 0.0, 543) + femmesh.addNode(704.0379848193656, 1724.096485392947, 0.0, 544) + femmesh.addNode(3642.7192636443942, 1900.866503946775, 0.0, 545) + femmesh.addNode(3627.2839552875016, 1760.4311955898822, 0.0, 546) + femmesh.addNode(3734.564691643107, 1859.564691643107, 0.0, 547) + femmesh.addNode(3757.309860723912, 1184.0863741522708, 0.0, 548) + femmesh.addNode(3909.587248918586, 1169.4974766642251, 0.0, 549) + femmesh.addNode(1045.047131114819, 1562.7177258517331, 0.0, 550) + femmesh.addNode(912.7376666991806, 1567.3393471299041, 0.0, 551) + femmesh.addNode(632.6284168035202, 981.4616379211823, 0.0, 552) + femmesh.addNode(532.3951537984146, 1065.0986379147603, 0.0, 553) + femmesh.addNode(683.5347867110737, 895.4715059596654, 0.0, 554) + femmesh.addNode(2517.3427988842627, 446.94802033720737, 0.0, 555) + femmesh.addNode(2390.5030489386745, 395.33175475832206, 0.0, 556) + femmesh.addNode(2329.630638515687, 525.7945175166626, 0.0, 557) + femmesh.addNode(2276.5322858135646, 390.32829139581884, 0.0, 558) + femmesh.addNode(1638.1962277325238, 1186.2231838713813, 0.0, 559) + femmesh.addNode(1537.3586628560938, 1117.7331731494469, 0.0, 560) + femmesh.addNode(1859.7234048133978, 609.2647314363055, 0.0, 561) + femmesh.addNode(1898.0745006932589, 710.2474350746162, 0.0, 562) + femmesh.addNode(3648.4763600356737, 1511.9328004200797, 0.0, 563) + femmesh.addNode(1326.5935279060745, 127.18944266087621, 0.0, 564) + femmesh.addNode(3282.8503678827024, 308.03542187227686, 0.0, 565) + femmesh.addNode(1980.540597224661, 800.5501508847015, 0.0, 566) + femmesh.addNode(1832.466096531402, 785.6152158100851, 0.0, 567) + femmesh.addNode(3502.4660201338666, 577.1059093785952, 0.0, 568) + femmesh.addNode(3622.7460318289013, 626.4539816583883, 0.0, 569) + femmesh.addNode(3565.4492596772334, 701.6847715429649, 0.0, 570) + femmesh.addNode(3527.3831930847837, 1660.5304333871645, 0.0, 571) + femmesh.addNode(3619.2286210834964, 1619.2286210834964, 0.0, 572) + femmesh.addNode(1560.676334048996, 418.29202207552396, 0.0, 573) + femmesh.addNode(1501.1930552120375, 292.9880495807416, 0.0, 574) + femmesh.addNode(749.9326048261051, 213.49401880694984, 0.0, 575) + femmesh.addNode(762.8575217874698, 315.7266248761909, 0.0, 576) + femmesh.addNode(812.5, 84.13927221440308, 0.0, 577) + femmesh.addNode(3431.981732084284, 1628.2816569327897, 0.0, 578) + femmesh.addNode(2044.249318087875, 444.2987927298035, 0.0, 579) + femmesh.addNode(2167.355850969766, 443.08583837057773, 0.0, 580) + femmesh.addNode(2673.6761321236963, 1904.4850610568753, 0.0, 581) + femmesh.addNode(2725.731704287379, 1775.0269124771244, 0.0, 582) + femmesh.addNode(108.96733300229978, 107.20813292932053, 0.0, 583) + femmesh.addNode(233.96733300229977, 107.20813292932053, 0.0, 584) + femmesh.addNode(105.58410691530834, 1894.5169652006175, 0.0, 585) + femmesh.addNode(230.58410691530833, 1894.5169652006175, 0.0, 586) + femmesh.addNode(2855.9616141985016, 1639.9301016677969, 0.0, 587) + femmesh.addNode(2981.6301841171385, 1565.9113709389512, 0.0, 588) + femmesh.addNode(521.6474316549813, 1607.0602950783614, 0.0, 589) + femmesh.addNode(626.8001191859894, 1679.042421249368, 0.0, 590) + femmesh.addNode(518.215019621639, 1722.4844401417145, 0.0, 591) + femmesh.addNode(1063.3339219988916, 1879.8224470202003, 0.0, 592) + femmesh.addNode(1188.3339219988916, 1879.8224470202003, 0.0, 593) + femmesh.addNode(1848.7880258903137, 391.1763921638408, 0.0, 594) + femmesh.addNode(1760.606084414766, 331.72617990839655, 0.0, 595) + femmesh.addNode(3547.755041608367, 928.6857847055478, 0.0, 596) + femmesh.addNode(3583.401069263262, 794.0896821347724, 0.0, 597) + femmesh.addNode(3463.1210575682267, 744.7416098549793, 0.0, 598) + femmesh.addNode(3716.8058569498253, 1054.6100011463795, 0.0, 599) + femmesh.addNode(417.49482884750216, 264.915469958528, 0.0, 600) + femmesh.addNode(1410.8131456035135, 1591.7808476197888, 0.0, 601) + femmesh.addNode(2639.264109399086, 1063.6453238777933, 0.0, 602) + femmesh.addNode(2701.35972727376, 962.9277739550239, 0.0, 603) + femmesh.addNode(2756.398212572275, 1090.815112406561, 0.0, 604) + femmesh.addNode(1924.9888593805554, 1589.0565837070812, 0.0, 605) + femmesh.addNode(2040.2044002995992, 1631.7237204078006, 0.0, 606) + femmesh.addNode(1978.761026699176, 1700.9060368520345, 0.0, 607) + femmesh.addNode(541.0698922692643, 1499.1242357160124, 0.0, 608) + femmesh.addNode(432.48479270491384, 1542.5662546083588, 0.0, 609) + femmesh.addNode(347.05533971665596, 92.10132627321057, 0.0, 610) + femmesh.addNode(331.02267271895573, 199.30945920253112, 0.0, 611) + femmesh.addNode(1451.5935279060745, 127.18944266087621, 0.0, 612) + femmesh.addNode(1663.7541860491501, 774.0742576991948, 0.0, 613) + femmesh.addNode(1522.0673368200007, 786.0304050057672, 0.0, 614) + femmesh.addNode(790.5298881695128, 424.04116004820094, 0.0, 615) + femmesh.addNode(2085.9857137530944, 1496.4946560258256, 0.0, 616) + femmesh.addNode(2139.7578810717155, 1608.344109170779, 0.0, 617) + femmesh.addNode(3913.9435412865064, 833.1235944479633, 0.0, 618) + femmesh.addNode(1695.2811569497517, 1892.7326286379591, 0.0, 619) + femmesh.addNode(2898.3369556294865, 897.5551957272764, 0.0, 620) + femmesh.addNode(3012.5571182984454, 902.8148920878875, 0.0, 621) + femmesh.addNode(2932.713993115907, 995.3572588444025, 0.0, 622) + femmesh.addNode(1983.7876478058265, 1795.8147899593043, 0.0, 623) + femmesh.addNode(1868.5721068867824, 1753.1476532585853, 0.0, 624) + femmesh.addNode(3823.530790205092, 1002.6210711121885, 0.0, 625) + femmesh.addNode(2542.9747331807466, 1648.5047169533154, 0.0, 626) + femmesh.addNode(2437.896936376015, 1594.7290737196784, 0.0, 627) + femmesh.addNode(1936.7993639967162, 1924.0282031829274, 0.0, 628) + femmesh.addNode(3042.685039077023, 1174.298111023101, 0.0, 629) + femmesh.addNode(2573.789004885646, 1156.8852164351124, 0.0, 630) + femmesh.addNode(2518.7505195871317, 1028.9978779835753, 0.0, 631) + femmesh.addNode(1845.0713195361545, 1231.9674945833272, 0.0, 632) + femmesh.addNode(1853.629849655299, 1106.5197122183863, 0.0, 633) + femmesh.addNode(1714.6160538146635, 1247.9345722111157, 0.0, 634) + femmesh.addNode(1811.705265721491, 1331.6891723722115, 0.0, 635) + femmesh.addNode(1094.0561152363023, 390.6612026456245, 0.0, 636) + femmesh.addNode(976.2721013843992, 448.56907264591416, 0.0, 637) + femmesh.addNode(92.52213483564782, 1043.335003995171, 0.0, 638) + femmesh.addNode(3933.6411729107767, 1808.6411729107767, 0.0, 639) + femmesh.addNode(3793.2058645538837, 1793.2058645538837, 0.0, 640) + femmesh.addNode(3808.6411729107767, 1933.6411729107767, 0.0, 641) + femmesh.addNode(2866.4502747442502, 792.8010888889428, 0.0, 642) + femmesh.addNode(2786.6071495617125, 885.3434556454579, 0.0, 643) + femmesh.addNode(3901.450783037097, 98.54921696290317, 0.0, 644) + femmesh.addNode(3776.450783037097, 98.54921696290317, 0.0, 645) + femmesh.addNode(3901.450783037097, 223.5492169629032, 0.0, 646) + femmesh.addNode(3187.5, 1907.6254807452124, 0.0, 647) + femmesh.addNode(3234.8178026438945, 1776.2432082376126, 0.0, 648) + femmesh.addNode(3225.0419447262148, 1665.1408481838037, 0.0, 649) + femmesh.addNode(2179.4524238973468, 1057.2034139072948, 0.0, 650) + femmesh.addNode(2244.410272866424, 964.7695305494036, 0.0, 651) + femmesh.addNode(1676.8091551439286, 391.50647682207375, 0.0, 652) + femmesh.addNode(1682.995983882763, 271.8169169260535, 0.0, 653) + femmesh.addNode(3909.587248918586, 1044.4974766642251, 0.0, 654) + femmesh.addNode(181.9915870291755, 995.0762994867846, 0.0, 655) + femmesh.addNode(3344.2563121194685, 1647.4426441581716, 0.0, 656) + femmesh.addNode(2233.4978258113515, 225.9790256924727, 0.0, 657) + femmesh.addNode(2189.7954776930756, 96.04626137373351, 0.0, 658) + femmesh.addNode(2901.8424690699885, 1451.1356097647079, 0.0, 659) + femmesh.addNode(2051.2729772239204, 1000.9067224853444, 0.0, 660) + femmesh.addNode(2389.7544781313627, 648.3211662611449, 0.0, 661) + femmesh.addNode(2499.1704497279306, 621.5817343349554, 0.0, 662) + femmesh.addNode(3177.064276557921, 485.2117592469385, 0.0, 663) + femmesh.addNode(940.23523871411, 1903.8861456469003, 0.0, 664) + femmesh.addNode(1003.5691607130016, 1783.7085926671007, 0.0, 665) + femmesh.addNode(3224.6626515578937, 1575.3480373571745, 0.0, 666) + femmesh.addNode(2766.122188467889, 1486.495618993757, 0.0, 667) + femmesh.addNode(3673.5563823242337, 521.2676894471987, 0.0, 668) + femmesh.addNode(2228.228261392753, 312.62307561223724, 0.0, 669) + femmesh.addNode(2305.6993767083177, 1905.3655050958337, 0.0, 670) + femmesh.addNode(1311.4340596468815, 1893.2954439213888, 0.0, 671) + femmesh.addNode(2875.1613746529574, 1069.5769580001172, 0.0, 672) + femmesh.addNode(2639.8925156923938, 621.8960916419592, 0.0, 673) + femmesh.addNode(2739.8830011231, 656.0080221501489, 0.0, 674) + femmesh.addNode(2654.72998324577, 746.4774182989346, 0.0, 675) + femmesh.addNode(1883.4947724792783, 1396.1033091310494, 0.0, 676) + femmesh.addNode(1753.0395067577872, 1412.0703867588381, 0.0, 677) + femmesh.addNode(1964.2963822119802, 1417.798181003635, 0.0, 678) + femmesh.addNode(2239.587958694249, 851.4156332996357, 0.0, 679) + femmesh.addNode(2094.708682076544, 831.08464255269, 0.0, 680) + femmesh.addNode(2235.0563641897397, 1556.8446477839718, 0.0, 681) + femmesh.addNode(2189.2750507362443, 1692.0737121659467, 0.0, 682) + femmesh.addNode(1477.3911704277443, 876.2689105841479, 0.0, 683) + femmesh.addNode(1421.9501351494446, 809.4633001915254, 0.0, 684) + femmesh.addNode(1561.9076749873839, 932.0344441883764, 0.0, 685) + femmesh.addNode(1461.7904733168277, 955.4673393741346, 0.0, 686) + femmesh.addNode(1035.919157956577, 1133.8350690483471, 0.0, 687) + femmesh.addNode(1043.17718435279, 1023.7674799921143, 0.0, 688) + femmesh.addNode(1143.9574763181595, 1101.227089271106, 0.0, 689) + femmesh.addNode(2227.687660517428, 1777.1520918722106, 0.0, 690) + femmesh.addNode(2818.0782085660194, 700.8996582959252, 0.0, 691) + femmesh.addNode(3510.8550872525384, 202.49658975529945, 0.0, 692) + femmesh.addNode(3644.9859925110595, 246.45231220686702, 0.0, 693) + femmesh.addNode(3557.7104292481927, 76.96006582259173, 0.0, 694) + femmesh.addNode(1066.5097205733537, 932.8444107597898, 0.0, 695) + femmesh.addNode(1082.7357096473884, 809.0066955637157, 0.0, 696) + femmesh.addNode(1167.441491788407, 867.3217850187992, 0.0, 697) + femmesh.addNode(981.5871912050206, 784.7653991322535, 0.0, 698) + femmesh.addNode(927.4469176979046, 1723.6945343700313, 0.0, 699) + femmesh.addNode(1050.5456009826862, 1699.6308357433313, 0.0, 700) + femmesh.addNode(585.7902114810297, 1171.2596016592215, 0.0, 701) + femmesh.addNode(888.7971247509754, 817.8811859887564, 0.0, 702) + femmesh.addNode(888.5803775236609, 728.1172638163039, 0.0, 703) + femmesh.addNode(3259.4496196090467, 421.08058486148786, 0.0, 704) + femmesh.addNode(2989.3815373219163, 1074.836654360728, 0.0, 705) + femmesh.addNode(1861.9330326513636, 487.31635133851387, 0.0, 706) + femmesh.addNode(1956.3018028657457, 427.0770036979378, 0.0, 707) + femmesh.addNode(643.1274514534958, 555.3733991750951, 0.0, 708) + femmesh.addNode(2031.1043113268252, 348.1588335551304, 0.0, 709) + femmesh.addNode(1280.4707248098148, 821.5929377963156, 0.0, 710) + femmesh.addNode(1264.2447357357803, 945.4306529923897, 0.0, 711) + femmesh.addNode(89.46945219352767, 951.7412954916135, 0.0, 712) + femmesh.addNode(2743.4044527801075, 1682.6092076912876, 0.0, 713) + femmesh.addNode(2745.2549226512433, 1581.4556065185864, 0.0, 714) + femmesh.addNode(880.0309833927339, 902.9130087228705, 0.0, 715) + femmesh.addNode(787.0241697113743, 846.2648734069207, 0.0, 716) + femmesh.addNode(1676.9179023577676, 79.80127191738595, 0.0, 717) + femmesh.addNode(1551.9179023577676, 79.80127191738595, 0.0, 718) + femmesh.addNode(1831.1334026894747, 907.3130176253524, 0.0, 719) + femmesh.addNode(1748.6673061580725, 817.0103018152672, 0.0, 720) + femmesh.addNode(879.5658102268367, 1646.6715909597174, 0.0, 721) + femmesh.addNode(817.8801189584283, 1574.3941606433596, 0.0, 722) + femmesh.addNode(1437.423633620477, 1164.1388796508154, 0.0, 723) + femmesh.addNode(1435.6230616578764, 1265.1081882321168, 0.0, 724) + femmesh.addNode(1344.636347442633, 1205.0135945624015, 0.0, 725) + femmesh.addNode(1446.3719486408504, 1057.6385794797316, 0.0, 726) + femmesh.addNode(1853.306101998708, 1001.7673168225379, 0.0, 727) + femmesh.addNode(2669.473046388524, 858.1736671166902, 0.0, 728) + femmesh.addNode(943.1562245269362, 545.942793547679, 0.0, 729) + femmesh.addNode(867.7339570652662, 470.11747746905314, 0.0, 730) + femmesh.addNode(907.9942620927546, 631.2463376653094, 0.0, 731) + femmesh.addNode(324.5244975561173, 1921.5493392573383, 0.0, 732) + femmesh.addNode(449.5244975561173, 1921.5493392573383, 0.0, 733) + femmesh.addNode(3247.590014507062, 803.2941017060745, 0.0, 734) + femmesh.addNode(3318.624297034102, 881.1375447443227, 0.0, 735) + femmesh.addNode(1937.1048923720377, 970.3722308173558, 0.0, 736) + femmesh.addNode(2029.100277914687, 906.452423288159, 0.0, 737) + femmesh.addNode(1653.573664935375, 1674.4335885703917, 0.0, 738) + femmesh.addNode(1765.0652508756893, 1610.8204100080902, 0.0, 739) + femmesh.addNode(1752.0538998398179, 1721.8520787136167, 0.0, 740) + femmesh.addNode(2584.3234676052602, 531.1123381861696, 0.0, 741) + femmesh.addNode(3397.96019017875, 912.4854117262288, 0.0, 742) + femmesh.addNode(3913.9435412865064, 958.1235944479633, 0.0, 743) + femmesh.addNode(1580.8685649559998, 1591.3481869864033, 0.0, 744) + femmesh.addNode(511.20835113244084, 1818.7826224136988, 0.0, 745) + femmesh.addNode(406.0556636014328, 1746.8004962426921, 0.0, 746) + femmesh.addNode(1757.080520946468, 1816.7608318208866, 0.0, 747) + femmesh.addNode(1811.7993639967162, 1924.0282031829274, 0.0, 748) + femmesh.addNode(707.4703968527081, 1608.672340329594, 0.0, 749) + femmesh.addNode(2124.3213909675533, 278.73657266723154, 0.0, 750) + femmesh.addNode(3933.6411729107767, 1933.6411729107767, 0.0, 751) + femmesh.addNode(3682.7104292481927, 76.96006582259173, 0.0, 752) + femmesh.addNode(1361.8554440812109, 1001.873045875503, 0.0, 753) + femmesh.addNode(1248.826211059803, 1047.6018930979867, 0.0, 754) + femmesh.addNode(1241.5681846635903, 1157.6694821542192, 0.0, 755) + femmesh.addNode(1604.83017391786, 1285.9448616602654, 0.0, 756) + femmesh.addNode(1517.0303272294914, 1326.2525533591602, 0.0, 757) + femmesh.addNode(1593.4501533116313, 1387.9639416988948, 0.0, 758) + femmesh.addNode(1424.2430410516477, 1367.1272682707463, 0.0, 759) + femmesh.addNode(815.2041956387095, 664.3621245218123, 0.0, 760) + femmesh.addNode(3928.5362108672816, 436.7929408961907, 0.0, 761) + femmesh.addNode(3928.5362108672816, 311.7929408961907, 0.0, 762) + femmesh.addNode(3402.3285337217894, 564.7500365535047, 0.0, 763) + femmesh.addNode(3401.7369740096065, 805.7986534648805, 0.0, 764) + femmesh.addNode(1662.4214922072226, 895.772059514462, 0.0, 765) + femmesh.addNode(3277.102476410255, 541.2945482292881, 0.0, 766) + femmesh.addNode(3319.943190670664, 628.8812109389553, 0.0, 767) + femmesh.addNode(757.4140113120498, 521.4148809499658, 0.0, 768) + femmesh.addNode(643.765094027996, 1458.4702682429738, 0.0, 769) + femmesh.addNode(554.6024550779284, 1393.976227772971, 0.0, 770) + femmesh.addNode(1685.3143589345677, 185.8195819235741, 0.0, 771) + femmesh.addNode(1503.5114302638422, 206.99071457826216, 0.0, 772) + femmesh.addNode(1601.5174296637306, 245.5998788372513, 0.0, 773) + femmesh.addNode(376.7938538433402, 1356.3458400712107, 0.0, 774) + femmesh.addNode(328.9909239281961, 1467.2437471118394, 0.0, 775) + femmesh.addNode(320.30902645263615, 520.9047457451477, 0.0, 776) + femmesh.addNode(576.2163956303472, 1296.2822517771538, 0.0, 777) + femmesh.addNode(2615.0250127401205, 1716.5524173279139, 0.0, 778) + femmesh.addNode(740.642253325052, 1529.3400964997809, 0.0, 779) + femmesh.addNode(305.1086044714256, 1816.066304457956, 0.0, 780) + femmesh.addNode(3765.8117741301494, 356.285187280466, 0.0, 781) + femmesh.addNode(3822.211197048748, 457.12307059638005, 0.0, 782) + femmesh.addNode(3730.950549444334, 439.8223760844646, 0.0, 783) + femmesh.addNode(1586.1176612972547, 1495.8367766313274, 0.0, 784) + femmesh.addNode(1673.9175079856234, 1455.5290849324326, 0.0, 785) + femmesh.addNode(1500.401210282008, 1523.7830437528655, 0.0, 786) + femmesh.addNode(1416.0622419447682, 1496.269437264713, 0.0, 787) + femmesh.addNode(692.3009280693152, 810.4396832255514, 0.0, 788) + femmesh.addNode(700.9176357801555, 698.3206427469418, 0.0, 789) + femmesh.addNode(3383.7851644235784, 713.3937428730732, 0.0, 790) + femmesh.addNode(3258.559107112044, 689.9382545488565, 0.0, 791) + femmesh.addNode(958.4714022117712, 965.4523905370307, 0.0, 792) + femmesh.addNode(1745.7070147434106, 1519.9432216912705, 0.0, 793) + femmesh.addNode(1844.1872496478532, 1567.3617118344957, 0.0, 794) + femmesh.addNode(3709.1612122852894, 175.50928278549492, 0.0, 795) + femmesh.addNode(473.52119387161537, 1336.9362192501924, 0.0, 796) + femmesh.addNode(720.3315203492492, 601.4497165959474, 0.0, 797) + femmesh.addNode(3829.9869939043792, 285.3421578590939, 0.0, 798) + femmesh.addNode(3738.7263462999645, 268.0414633471785, 0.0, 799) + femmesh.addNode(1905.6306232482764, 1498.1793953902616, 0.0, 800) + femmesh.addNode(2322.986143635452, 1725.6526304854035, 0.0, 801) + femmesh.addNode(2368.912619491457, 1656.7244860718467, 0.0, 802) + femmesh.addNode(2353.5578507388273, 1578.5788384269713, 0.0, 803) + femmesh.addNode(251.59438775510205, -1642.5382653061224, 0.0, 804) + femmesh.addNode(250.0, -1125.0, 0.0, 805) + femmesh.addNode(244.79166666666666, -621.527777777778, 0.0, 806) + femmesh.addNode(253.65823412698398, -349.26835317460336, 0.0, 807) + femmesh.addNode(250.26573129251702, -1377.9230442176872, 0.0, 808) + femmesh.addNode(249.1319444444444, -874.4212962962965, 0.0, 809) + femmesh.addNode(183.5156907411028, -1822.3611654157708, 0.0, 810) + femmesh.addNode(175.62426452602392, -169.8009014101267, 0.0, 811) + femmesh.addNode(340.96396806154064, -151.6577109724219, 0.0, 812) + femmesh.addNode(337.022015699241, -1842.9798861443787, 0.0, 813) + femmesh.addNode(375.1328656462585, -1313.9615221088436, 0.0, 814) + femmesh.addNode(375.1328656462585, -1438.9615221088436, 0.0, 815) + femmesh.addNode(374.5659722222222, -812.2106481481483, 0.0, 816) + femmesh.addNode(374.5659722222222, -937.2106481481483, 0.0, 817) + femmesh.addNode(250.1328656462585, -1251.4615221088436, 0.0, 818) + femmesh.addNode(375.0, -1187.5, 0.0, 819) + femmesh.addNode(250.93005952380952, -1510.2306547619048, 0.0, 820) + femmesh.addNode(375.797193877551, -1571.2691326530612, 0.0, 821) + femmesh.addNode(249.5659722222222, -999.7106481481483, 0.0, 822) + femmesh.addNode(375.0, -1062.5, 0.0, 823) + femmesh.addNode(246.96180555555554, -747.9745370370374, 0.0, 824) + femmesh.addNode(372.3958333333333, -685.7638888888891, 0.0, 825) + femmesh.addNode(249.2249503968253, -485.3980654761908, 0.0, 826) + femmesh.addNode(126.82911706349199, -424.6341765873017, 0.0, 827) + femmesh.addNode(122.39583333333333, -560.7638888888891, 0.0, 828) + femmesh.addNode(376.829117063492, -424.6341765873017, 0.0, 829) + femmesh.addNode(372.3958333333333, -560.7638888888891, 0.0, 830) + femmesh.addNode(126.82911706349199, -299.6341765873017, 0.0, 831) + femmesh.addNode(376.829117063492, -299.6341765873017, 0.0, 832) + femmesh.addNode(125.79719387755102, -1696.2691326530612, 0.0, 833) + femmesh.addNode(125.79719387755102, -1571.2691326530612, 0.0, 834) + femmesh.addNode(125.0, -1187.5, 0.0, 835) + femmesh.addNode(125.0, -1062.5, 0.0, 836) + femmesh.addNode(122.39583333333333, -685.7638888888891, 0.0, 837) + femmesh.addNode(375.797193877551, -1696.2691326530612, 0.0, 838) + femmesh.addNode(124.5659722222222, -937.2106481481483, 0.0, 839) + femmesh.addNode(125.13286564625851, -1313.9615221088436, 0.0, 840) + femmesh.addNode(124.5659722222222, -812.2106481481483, 0.0, 841) + femmesh.addNode(125.13286564625851, -1438.9615221088436, 0.0, 842) + femmesh.addNode(294.30820172717154, -1742.7590757252506, 0.0, 843) + femmesh.addNode(418.5110078496205, -1796.4899430721894, 0.0, 844) + femmesh.addNode(91.7578453705514, -1911.1805827078854, 0.0, 845) + femmesh.addNode(91.7578453705514, -1786.1805827078854, 0.0, 846) + femmesh.addNode(216.7578453705514, -1911.1805827078854, 0.0, 847) + femmesh.addNode(217.55503924810242, -1732.4497153609466, 0.0, 848) + femmesh.addNode(420.4819840307703, -200.82885548621107, 0.0, 849) + femmesh.addNode(297.3111010942623, -250.46303207351275, 0.0, 850) + femmesh.addNode(214.64124932650395, -259.53462729236503, 0.0, 851) + femmesh.addNode(87.81213226301196, -209.90045070506335, 0.0, 852) + femmesh.addNode(87.81213226301196, -84.90045070506335, 0.0, 853) + femmesh.addNode(212.81213226301196, -84.90045070506335, 0.0, 854) + femmesh.addNode(260.2688532201719, -1832.6705257800747, 0.0, 855) + femmesh.addNode(295.4819840307703, -75.82885548621107, 0.0, 856) + femmesh.addNode(420.4819840307703, -75.82885548621107, 0.0, 857) + femmesh.addNode(418.5110078496205, -1921.4899430721894, 0.0, 858) + femmesh.addNode(293.5110078496205, -1921.4899430721894, 0.0, 859) + femmesh.addNode(258.2941162937823, -160.7293061912742, 0.0, 860) + femmesh.addNode(3751.594387755102, -1642.5382653061224, 0.0, 861) + femmesh.addNode(3750.0, -1125.0, 0.0, 862) + femmesh.addNode(3744.7916666666665, -621.527777777778, 0.0, 863) + femmesh.addNode(3753.658234126984, -349.26835317460336, 0.0, 864) + femmesh.addNode(3750.265731292517, -1377.9230442176872, 0.0, 865) + femmesh.addNode(3749.1319444444443, -874.4212962962965, 0.0, 866) + femmesh.addNode(3683.515690741103, -1822.3611654157708, 0.0, 867) + femmesh.addNode(3675.624264526024, -169.8009014101267, 0.0, 868) + femmesh.addNode(3840.9639680615405, -151.6577109724219, 0.0, 869) + femmesh.addNode(3837.022015699241, -1842.9798861443787, 0.0, 870) + femmesh.addNode(3875.1328656462583, -1313.9615221088436, 0.0, 871) + femmesh.addNode(3875.1328656462583, -1438.9615221088436, 0.0, 872) + femmesh.addNode(3874.565972222222, -812.2106481481483, 0.0, 873) + femmesh.addNode(3874.565972222222, -937.2106481481483, 0.0, 874) + femmesh.addNode(3750.1328656462583, -1251.4615221088436, 0.0, 875) + femmesh.addNode(3875.0, -1187.5, 0.0, 876) + femmesh.addNode(3746.9618055555557, -747.9745370370374, 0.0, 877) + femmesh.addNode(3872.395833333333, -685.7638888888891, 0.0, 878) + femmesh.addNode(3750.9300595238096, -1510.2306547619048, 0.0, 879) + femmesh.addNode(3875.797193877551, -1571.2691326530612, 0.0, 880) + femmesh.addNode(3749.565972222222, -999.7106481481483, 0.0, 881) + femmesh.addNode(3875.0, -1062.5, 0.0, 882) + femmesh.addNode(3876.829117063492, -424.6341765873017, 0.0, 883) + femmesh.addNode(3749.224950396825, -485.3980654761908, 0.0, 884) + femmesh.addNode(3872.395833333333, -560.7638888888891, 0.0, 885) + femmesh.addNode(3626.829117063492, -424.6341765873017, 0.0, 886) + femmesh.addNode(3622.395833333333, -560.7638888888891, 0.0, 887) + femmesh.addNode(3626.829117063492, -299.6341765873017, 0.0, 888) + femmesh.addNode(3876.829117063492, -299.6341765873017, 0.0, 889) + femmesh.addNode(3625.797193877551, -1696.2691326530612, 0.0, 890) + femmesh.addNode(3625.797193877551, -1571.2691326530612, 0.0, 891) + femmesh.addNode(3625.0, -1187.5, 0.0, 892) + femmesh.addNode(3625.0, -1062.5, 0.0, 893) + femmesh.addNode(3622.395833333333, -685.7638888888891, 0.0, 894) + femmesh.addNode(3875.797193877551, -1696.2691326530612, 0.0, 895) + femmesh.addNode(3624.565972222222, -937.2106481481483, 0.0, 896) + femmesh.addNode(3625.1328656462583, -1438.9615221088436, 0.0, 897) + femmesh.addNode(3625.1328656462583, -1313.9615221088436, 0.0, 898) + femmesh.addNode(3624.565972222222, -812.2106481481483, 0.0, 899) + femmesh.addNode(3794.3082017271718, -1742.7590757252506, 0.0, 900) + femmesh.addNode(3918.5110078496205, -1796.4899430721894, 0.0, 901) + femmesh.addNode(3591.7578453705514, -1911.1805827078854, 0.0, 902) + femmesh.addNode(3591.7578453705514, -1786.1805827078854, 0.0, 903) + femmesh.addNode(3716.7578453705514, -1911.1805827078854, 0.0, 904) + femmesh.addNode(3717.5550392481027, -1732.4497153609466, 0.0, 905) + femmesh.addNode(3920.48198403077, -200.82885548621107, 0.0, 906) + femmesh.addNode(3797.311101094262, -250.46303207351275, 0.0, 907) + femmesh.addNode(3714.6412493265043, -259.53462729236503, 0.0, 908) + femmesh.addNode(3587.8121322630122, -209.90045070506335, 0.0, 909) + femmesh.addNode(3587.8121322630122, -84.90045070506335, 0.0, 910) + femmesh.addNode(3712.8121322630122, -84.90045070506335, 0.0, 911) + femmesh.addNode(3760.268853220172, -1832.6705257800747, 0.0, 912) + femmesh.addNode(3795.48198403077, -75.82885548621107, 0.0, 913) + femmesh.addNode(3920.48198403077, -75.82885548621107, 0.0, 914) + femmesh.addNode(3918.5110078496205, -1921.4899430721894, 0.0, 915) + femmesh.addNode(3793.5110078496205, -1921.4899430721894, 0.0, 916) + femmesh.addNode(3758.2941162937823, -160.7293061912742, 0.0, 917) + return True + + +def create_elements(femmesh): + # elements + femmesh.addFace([174, 230, 204, 315, 316, 317], 21) + femmesh.addFace([11, 263, 258, 318, 319, 320], 22) + femmesh.addFace([259, 264, 17, 321, 322, 323], 23) + femmesh.addFace([201, 261, 65, 324, 325, 326], 24) + femmesh.addFace([39, 211, 40, 327, 328, 55], 25) + femmesh.addFace([176, 235, 217, 329, 330, 331], 26) + femmesh.addFace([87, 248, 212, 332, 333, 334], 27) + femmesh.addFace([237, 280, 172, 335, 336, 337], 28) + femmesh.addFace([211, 247, 40, 338, 339, 328], 29) + femmesh.addFace([209, 279, 269, 340, 341, 342], 30) + femmesh.addFace([235, 296, 217, 343, 344, 330], 31) + femmesh.addFace([11, 258, 12, 320, 345, 19], 32) + femmesh.addFace([16, 259, 17, 346, 323, 24], 33) + femmesh.addFace([209, 269, 188, 342, 347, 348], 34) + femmesh.addFace([214, 277, 191, 349, 350, 351], 35) + femmesh.addFace([214, 301, 277, 352, 353, 349], 36) + femmesh.addFace([88, 248, 87, 354, 332, 95], 37) + femmesh.addFace([65, 242, 201, 355, 356, 326], 38) + femmesh.addFace([180, 255, 204, 357, 358, 359], 39) + femmesh.addFace([13, 258, 207, 360, 361, 362], 40) + femmesh.addFace([206, 259, 15, 363, 364, 365], 41) + femmesh.addFace([200, 246, 227, 366, 367, 368], 42) + femmesh.addFace([227, 246, 168, 367, 369, 370], 43) + femmesh.addFace([216, 248, 89, 371, 372, 373], 44) + femmesh.addFace([177, 225, 208, 374, 375, 376], 45) + femmesh.addFace([34, 262, 234, 377, 378, 379], 46) + femmesh.addFace([69, 209, 68, 380, 381, 80], 47) + femmesh.addFace([172, 279, 237, 382, 383, 337], 48) + femmesh.addFace([176, 284, 208, 384, 385, 386], 49) + femmesh.addFace([226, 289, 262, 387, 388, 389], 50) + femmesh.addFace([204, 243, 180, 390, 391, 359], 51) + femmesh.addFace([230, 281, 204, 392, 393, 316], 52) + femmesh.addFace([89, 248, 88, 372, 354, 96], 53) + femmesh.addFace([180, 243, 205, 391, 394, 395], 54) + femmesh.addFace([173, 249, 207, 396, 397, 398], 55) + femmesh.addFace([207, 249, 182, 397, 399, 400], 56) + femmesh.addFace([60, 214, 3, 401, 402, 71], 57) + femmesh.addFace([205, 289, 180, 403, 404, 395], 58) + femmesh.addFace([215, 270, 170, 405, 406, 407], 59) + femmesh.addFace([204, 255, 174, 358, 408, 317], 60) + femmesh.addFace([232, 270, 178, 409, 410, 411], 61) + femmesh.addFace([207, 277, 173, 412, 413, 398], 62) + femmesh.addFace([222, 232, 178, 414, 411, 415], 63) + femmesh.addFace([213, 238, 85, 416, 417, 418], 64) + femmesh.addFace([270, 282, 170, 419, 420, 406], 65) + femmesh.addFace([184, 247, 211, 421, 338, 422], 66) + femmesh.addFace([176, 299, 284, 423, 424, 384], 67) + femmesh.addFace([210, 241, 179, 425, 426, 427], 68) + femmesh.addFace([234, 262, 194, 378, 428, 429], 69) + femmesh.addFace([222, 267, 61, 430, 431, 432], 70) + femmesh.addFace([180, 289, 226, 404, 387, 433], 71) + femmesh.addFace([41, 218, 42, 434, 435, 57], 72) + femmesh.addFace([67, 266, 221, 436, 437, 438], 73) + femmesh.addFace([182, 252, 207, 439, 440, 400], 74) + femmesh.addFace([212, 223, 183, 441, 442, 443], 75) + femmesh.addFace([181, 223, 212, 444, 441, 445], 76) + femmesh.addFace([15, 239, 206, 446, 447, 365], 77) + femmesh.addFace([62, 222, 61, 448, 432, 73], 78) + femmesh.addFace([67, 221, 66, 438, 449, 78], 79) + femmesh.addFace([219, 225, 177, 450, 374, 451], 80) + femmesh.addFace([38, 283, 244, 452, 453, 454], 81) + femmesh.addFace([197, 302, 224, 455, 456, 457], 82) + femmesh.addFace([224, 302, 274, 456, 458, 459], 83) + femmesh.addFace([269, 279, 172, 341, 382, 460], 84) + femmesh.addFace([244, 283, 203, 453, 461, 462], 85) + femmesh.addFace([229, 273, 178, 463, 464, 465], 86) + femmesh.addFace([198, 280, 237, 466, 335, 467], 87) + femmesh.addFace([169, 240, 233, 468, 469, 470], 88) + femmesh.addFace([233, 240, 198, 469, 471, 472], 89) + femmesh.addFace([12, 258, 13, 345, 360, 20], 90) + femmesh.addFace([15, 259, 16, 364, 346, 23], 91) + femmesh.addFace([207, 252, 13, 440, 473, 362], 92) + femmesh.addFace([206, 239, 182, 447, 474, 475], 93) + femmesh.addFace([179, 227, 210, 476, 477, 427], 94) + femmesh.addFace([182, 304, 206, 478, 479, 475], 95) + femmesh.addFace([228, 285, 31, 480, 481, 482], 96) + femmesh.addFace([65, 261, 64, 325, 483, 76], 97) + femmesh.addFace([202, 273, 229, 484, 463, 485], 98) + femmesh.addFace([208, 284, 177, 385, 486, 376], 99) + femmesh.addFace([5, 224, 70, 487, 488, 82], 100) + femmesh.addFace([169, 236, 217, 489, 490, 491], 101) + femmesh.addFace([217, 236, 184, 490, 492, 493], 102) + femmesh.addFace([196, 281, 230, 494, 392, 495], 103) + femmesh.addFace([224, 268, 70, 496, 497, 488], 104) + femmesh.addFace([214, 287, 3, 498, 499, 402], 105) + femmesh.addFace([209, 266, 68, 500, 501, 381], 106) + femmesh.addFace([85, 238, 84, 417, 502, 92], 107) + femmesh.addFace([188, 266, 209, 503, 500, 348], 108) + femmesh.addFace([69, 268, 209, 504, 505, 380], 109) + femmesh.addFace([209, 268, 190, 505, 506, 507], 110) + femmesh.addFace([189, 267, 222, 508, 430, 509], 111) + femmesh.addFace([181, 236, 223, 510, 511, 444], 112) + femmesh.addFace([208, 235, 176, 512, 329, 386], 113) + femmesh.addFace([181, 307, 236, 513, 514, 510], 114) + femmesh.addFace([221, 266, 188, 437, 503, 515], 115) + femmesh.addFace([186, 225, 219, 516, 450, 517], 116) + femmesh.addFace([168, 313, 225, 518, 519, 520], 117) + femmesh.addFace([215, 229, 178, 521, 465, 522], 118) + femmesh.addFace([30, 228, 31, 523, 482, 46], 119) + femmesh.addFace([213, 295, 183, 524, 525, 526], 120) + femmesh.addFace([225, 313, 208, 519, 527, 375], 121) + femmesh.addFace([190, 279, 209, 528, 340, 507], 122) + femmesh.addFace([218, 276, 42, 529, 530, 435], 123) + femmesh.addFace([36, 220, 37, 531, 532, 52], 124) + femmesh.addFace([192, 264, 259, 533, 321, 534], 125) + femmesh.addFace([258, 263, 191, 319, 535, 536], 126) + femmesh.addFace([223, 233, 183, 537, 538, 442], 127) + femmesh.addFace([169, 233, 223, 470, 537, 539], 128) + femmesh.addFace([231, 253, 187, 540, 541, 542], 129) + femmesh.addFace([199, 285, 228, 543, 480, 544], 130) + femmesh.addFace([43, 276, 216, 545, 546, 547], 131) + femmesh.addFace([212, 245, 87, 548, 549, 334], 132) + femmesh.addFace([180, 303, 255, 550, 551, 357], 133) + femmesh.addFace([182, 249, 230, 399, 552, 553], 134) + femmesh.addFace([230, 249, 196, 552, 554, 495], 135) + femmesh.addFace([188, 257, 221, 555, 556, 515], 136) + femmesh.addFace([221, 257, 179, 556, 557, 558], 137) + femmesh.addFace([253, 275, 187, 559, 560, 541], 138) + femmesh.addFace([170, 241, 210, 561, 425, 562], 139) + femmesh.addFace([212, 248, 181, 333, 563, 445], 140) + femmesh.addFace([62, 232, 222, 564, 414, 448], 141) + femmesh.addFace([190, 268, 224, 506, 496, 565], 142) + femmesh.addFace([210, 310, 170, 566, 567, 562], 143) + femmesh.addFace([175, 302, 213, 568, 569, 570], 144) + femmesh.addFace([216, 276, 181, 546, 571, 572], 145) + femmesh.addFace([193, 270, 232, 573, 409, 574], 146) + femmesh.addFace([214, 267, 189, 575, 508, 576], 147) + femmesh.addFace([60, 267, 214, 577, 575, 401], 148) + femmesh.addFace([181, 276, 218, 571, 529, 578], 149) + femmesh.addFace([241, 312, 179, 579, 580, 426], 150) + femmesh.addFace([39, 244, 211, 581, 582, 327], 151) + femmesh.addFace([1, 263, 11, 583, 318, 18], 152) + femmesh.addFace([26, 263, 1, 584, 583, 27], 153) + femmesh.addFace([17, 264, 2, 322, 585, 25], 154) + femmesh.addFace([2, 264, 29, 585, 586, 44], 155) + femmesh.addFace([211, 291, 184, 587, 588, 422], 156) + femmesh.addFace([192, 286, 228, 589, 590, 591], 157) + femmesh.addFace([32, 226, 33, 592, 593, 48], 158) + femmesh.addFace([181, 248, 216, 563, 371, 572], 159) + femmesh.addFace([201, 282, 261, 594, 595, 324], 160) + femmesh.addFace([183, 256, 213, 596, 597, 526], 161) + femmesh.addFace([213, 256, 175, 597, 598, 570], 162) + femmesh.addFace([183, 245, 212, 599, 548, 443], 163) + femmesh.addFace([191, 287, 214, 600, 498, 351], 164) + femmesh.addFace([262, 289, 194, 388, 601, 428], 165) + femmesh.addFace([42, 276, 43, 530, 545, 58], 166) + femmesh.addFace([235, 246, 185, 602, 603, 604], 167) + femmesh.addFace([195, 293, 220, 605, 606, 607], 168) + femmesh.addFace([278, 286, 192, 608, 589, 609], 169) + femmesh.addFace([26, 287, 263, 610, 611, 584], 170) + femmesh.addFace([63, 232, 62, 612, 564, 74], 171) + femmesh.addFace([170, 309, 215, 613, 614, 407], 172) + femmesh.addFace([189, 301, 214, 615, 352, 576], 173) + femmesh.addFace([220, 293, 177, 606, 616, 617], 174) + femmesh.addFace([85, 295, 213, 618, 524, 418], 175) + femmesh.addFace([34, 234, 35, 379, 619, 50], 176) + femmesh.addFace([185, 280, 240, 620, 621, 622], 177) + femmesh.addFace([220, 290, 195, 623, 624, 607], 178) + femmesh.addFace([183, 295, 245, 525, 625, 599], 179) + femmesh.addFace([284, 299, 203, 424, 626, 627], 180) + femmesh.addFace([3, 287, 26, 499, 610, 28], 181) + femmesh.addFace([36, 290, 220, 628, 623, 531], 182) + femmesh.addFace([217, 296, 169, 344, 629, 491], 183) + femmesh.addFace([235, 313, 246, 630, 631, 602], 184) + femmesh.addFace([219, 253, 186, 632, 633, 517], 185) + femmesh.addFace([171, 253, 219, 634, 632, 635], 186) + femmesh.addFace([222, 273, 189, 636, 637, 509], 187) + femmesh.addFace([178, 273, 222, 464, 636, 415], 188) + femmesh.addFace([14, 239, 15, 638, 446, 22], 189) + femmesh.addFace([89, 306, 216, 639, 640, 373], 190) + femmesh.addFace([216, 306, 43, 640, 641, 547], 191) + femmesh.addFace([251, 280, 185, 642, 620, 643], 192) + femmesh.addFace([6, 271, 98, 644, 645, 100], 193) + femmesh.addFace([83, 271, 6, 646, 644, 90], 194) + femmesh.addFace([41, 247, 218, 647, 648, 434], 195) + femmesh.addFace([218, 247, 184, 648, 421, 649], 196) + femmesh.addFace([223, 236, 169, 511, 489, 539], 197) + femmesh.addFace([225, 250, 168, 650, 651, 520], 198) + femmesh.addFace([261, 282, 193, 595, 652, 653], 199) + femmesh.addFace([87, 245, 86, 549, 654, 94], 200) + femmesh.addFace([239, 252, 182, 655, 439, 474], 201) + femmesh.addFace([218, 307, 181, 656, 513, 578], 202) + femmesh.addFace([221, 242, 66, 657, 658, 449], 203) + femmesh.addFace([184, 291, 217, 588, 659, 493], 204) + femmesh.addFace([186, 250, 225, 660, 650, 516], 205) + femmesh.addFace([179, 257, 227, 557, 661, 476], 206) + femmesh.addFace([178, 270, 215, 410, 405, 522], 207) + femmesh.addFace([227, 257, 200, 661, 662, 368], 208) + femmesh.addFace([237, 279, 190, 383, 528, 663], 209) + femmesh.addFace([32, 285, 226, 664, 665, 592], 210) + femmesh.addFace([184, 307, 218, 666, 656, 649], 211) + femmesh.addFace([217, 291, 176, 659, 667, 331], 212) + femmesh.addFace([213, 302, 238, 569, 668, 416], 213) + femmesh.addFace([179, 312, 221, 580, 669, 558], 214) + femmesh.addFace([37, 283, 38, 670, 452, 53], 215) + femmesh.addFace([33, 262, 34, 671, 377, 49], 216) + femmesh.addFace([185, 296, 235, 672, 343, 604], 217) + femmesh.addFace([200, 269, 251, 673, 674, 675], 218) + femmesh.addFace([219, 308, 171, 676, 677, 635], 219) + femmesh.addFace([177, 293, 219, 616, 678, 451], 220) + femmesh.addFace([168, 250, 227, 651, 679, 370], 221) + femmesh.addFace([227, 250, 210, 679, 680, 477], 222) + femmesh.addFace([177, 311, 220, 681, 682, 617], 223) + femmesh.addFace([215, 309, 305, 614, 683, 684], 224) + femmesh.addFace([305, 309, 187, 683, 685, 686], 225) + femmesh.addFace([204, 281, 243, 393, 687, 390], 226) + femmesh.addFace([243, 281, 167, 687, 688, 689], 227) + femmesh.addFace([220, 283, 37, 690, 670, 532], 228) + femmesh.addFace([251, 269, 172, 674, 460, 691], 229) + femmesh.addFace([224, 292, 197, 692, 693, 457], 230) + femmesh.addFace([5, 292, 224, 694, 692, 487], 231) + femmesh.addFace([167, 300, 229, 695, 696, 697], 232) + femmesh.addFace([229, 300, 202, 696, 698, 485], 233) + femmesh.addFace([66, 242, 65, 658, 355, 77], 234) + femmesh.addFace([285, 303, 226, 699, 700, 665], 235) + femmesh.addFace([38, 244, 39, 454, 581, 54], 236) + femmesh.addFace([230, 304, 182, 701, 478, 553], 237) + femmesh.addFace([202, 300, 294, 698, 702, 703], 238) + femmesh.addFace([224, 274, 190, 459, 704, 565], 239) + femmesh.addFace([169, 296, 240, 629, 705, 468], 240) + femmesh.addFace([226, 262, 33, 389, 671, 593], 241) + femmesh.addFace([40, 247, 41, 339, 647, 56], 242) + femmesh.addFace([241, 282, 201, 706, 594, 707], 243) + femmesh.addFace([277, 301, 173, 353, 708, 413], 244) + femmesh.addFace([201, 312, 241, 709, 579, 707], 245) + femmesh.addFace([193, 282, 270, 652, 419, 573], 246) + femmesh.addFace([229, 305, 167, 710, 711, 697], 247) + femmesh.addFace([13, 252, 14, 473, 712, 21], 248) + femmesh.addFace([211, 299, 291, 713, 714, 587], 249) + femmesh.addFace([294, 300, 196, 702, 715, 716], 250) + femmesh.addFace([64, 298, 63, 717, 718, 75], 251) + femmesh.addFace([170, 310, 231, 567, 719, 720], 252) + femmesh.addFace([226, 303, 180, 700, 550, 433], 253) + femmesh.addFace([255, 303, 199, 551, 721, 722], 254) + femmesh.addFace([265, 275, 205, 723, 724, 725], 255) + femmesh.addFace([187, 275, 265, 560, 723, 726], 256) + femmesh.addFace([186, 253, 231, 633, 540, 727], 257) + femmesh.addFace([200, 251, 246, 675, 728, 366], 258) + femmesh.addFace([189, 273, 260, 637, 729, 730], 259) + femmesh.addFace([260, 273, 202, 729, 484, 731], 260) + femmesh.addFace([68, 266, 67, 501, 436, 79], 261) + femmesh.addFace([61, 267, 60, 431, 577, 72], 262) + femmesh.addFace([29, 288, 30, 732, 733, 45], 263) + femmesh.addFace([198, 314, 233, 734, 735, 472], 264) + femmesh.addFace([291, 299, 176, 714, 423, 667], 265) + femmesh.addFace([186, 310, 250, 736, 737, 660], 266) + femmesh.addFace([250, 310, 210, 737, 566, 680], 267) + femmesh.addFace([263, 287, 191, 611, 600, 535], 268) + femmesh.addFace([234, 254, 195, 738, 739, 740], 269) + femmesh.addFace([70, 268, 69, 497, 504, 81], 270) + femmesh.addFace([257, 269, 200, 741, 673, 662], 271) + femmesh.addFace([188, 269, 257, 347, 741, 555], 272) + femmesh.addFace([233, 256, 183, 742, 596, 538], 273) + femmesh.addFace([86, 295, 85, 743, 618, 93], 274) + femmesh.addFace([194, 254, 234, 744, 738, 429], 275) + femmesh.addFace([30, 288, 228, 733, 745, 523], 276) + femmesh.addFace([228, 288, 192, 745, 746, 591], 277) + femmesh.addFace([236, 307, 184, 514, 666, 492], 278) + femmesh.addFace([234, 290, 35, 747, 748, 619], 279) + femmesh.addFace([195, 290, 234, 624, 747, 740], 280) + femmesh.addFace([14, 252, 239, 712, 655, 638], 281) + femmesh.addFace([228, 286, 199, 590, 749, 544], 282) + femmesh.addFace([221, 312, 242, 669, 750, 657], 283) + femmesh.addFace([43, 306, 4, 641, 751, 59], 284) + femmesh.addFace([4, 306, 89, 751, 639, 97], 285) + femmesh.addFace([98, 292, 5, 752, 694, 99], 286) + femmesh.addFace([167, 305, 265, 711, 753, 754], 287) + femmesh.addFace([246, 251, 185, 728, 643, 603], 288) + femmesh.addFace([167, 265, 243, 754, 755, 689], 289) + femmesh.addFace([243, 265, 205, 755, 725, 394], 290) + femmesh.addFace([31, 285, 32, 481, 664, 47], 291) + femmesh.addFace([35, 290, 36, 748, 628, 51], 292) + femmesh.addFace([171, 275, 253, 756, 559, 634], 293) + femmesh.addFace([272, 275, 171, 757, 756, 758], 294) + femmesh.addFace([205, 275, 272, 724, 757, 759], 295) + femmesh.addFace([215, 305, 229, 684, 710, 521], 296) + femmesh.addFace([202, 294, 260, 703, 760, 731], 297) + femmesh.addFace([84, 297, 83, 761, 762, 91], 298) + femmesh.addFace([274, 302, 175, 458, 568, 763], 299) + femmesh.addFace([233, 314, 256, 735, 764, 742], 300) + femmesh.addFace([187, 309, 231, 685, 765, 542], 301) + femmesh.addFace([240, 280, 198, 621, 466, 471], 302) + femmesh.addFace([237, 274, 175, 766, 763, 767], 303) + femmesh.addFace([190, 274, 237, 704, 766, 663], 304) + femmesh.addFace([260, 301, 189, 768, 615, 730], 305) + femmesh.addFace([174, 286, 278, 769, 608, 770], 306) + femmesh.addFace([261, 298, 64, 771, 717, 483], 307) + femmesh.addFace([199, 303, 285, 721, 699, 543], 308) + femmesh.addFace([63, 298, 232, 718, 772, 612], 309) + femmesh.addFace([232, 298, 193, 772, 773, 574], 310) + femmesh.addFace([206, 278, 259, 774, 775, 363], 311) + femmesh.addFace([259, 278, 192, 775, 609, 534], 312) + femmesh.addFace([191, 277, 258, 350, 776, 536], 313) + femmesh.addFace([258, 277, 207, 776, 412, 361], 314) + femmesh.addFace([174, 304, 230, 777, 701, 315], 315) + femmesh.addFace([246, 313, 168, 631, 518, 369], 316) + femmesh.addFace([244, 299, 211, 778, 713, 582], 317) + femmesh.addFace([255, 286, 174, 779, 769, 408], 318) + femmesh.addFace([199, 286, 255, 749, 779, 722], 319) + femmesh.addFace([231, 310, 186, 719, 736, 727], 320) + femmesh.addFace([231, 309, 170, 765, 613, 720], 321) + femmesh.addFace([264, 288, 29, 780, 732, 586], 322) + femmesh.addFace([197, 297, 238, 781, 782, 783], 323) + femmesh.addFace([170, 282, 241, 420, 706, 561], 324) + femmesh.addFace([254, 272, 171, 784, 758, 785], 325) + femmesh.addFace([194, 272, 254, 786, 784, 744], 326) + femmesh.addFace([272, 289, 205, 787, 403, 759], 327) + femmesh.addFace([249, 294, 196, 788, 716, 554], 328) + femmesh.addFace([173, 294, 249, 789, 788, 396], 329) + femmesh.addFace([240, 296, 185, 705, 672, 622], 330) + femmesh.addFace([208, 313, 235, 527, 630, 512], 331) + femmesh.addFace([175, 314, 237, 790, 791, 767], 332) + femmesh.addFace([237, 314, 198, 791, 734, 467], 333) + femmesh.addFace([238, 297, 84, 782, 761, 502], 334) + femmesh.addFace([172, 280, 251, 336, 642, 691], 335) + femmesh.addFace([238, 302, 197, 668, 455, 783], 336) + femmesh.addFace([245, 295, 86, 625, 743, 654], 337) + femmesh.addFace([196, 300, 281, 715, 792, 494], 338) + femmesh.addFace([203, 299, 244, 626, 778, 462], 339) + femmesh.addFace([192, 288, 264, 746, 780, 533], 340) + femmesh.addFace([254, 308, 195, 793, 794, 739], 341) + femmesh.addFace([271, 292, 98, 795, 752, 645], 342) + femmesh.addFace([242, 312, 201, 750, 709, 356], 343) + femmesh.addFace([206, 304, 278, 479, 796, 774], 344) + femmesh.addFace([278, 304, 174, 796, 777, 770], 345) + femmesh.addFace([260, 294, 173, 760, 789, 797], 346) + femmesh.addFace([271, 297, 197, 798, 781, 799], 347) + femmesh.addFace([193, 298, 261, 773, 771, 653], 348) + femmesh.addFace([194, 289, 272, 601, 787, 786], 349) + femmesh.addFace([197, 292, 271, 693, 795, 799], 350) + femmesh.addFace([173, 301, 260, 708, 768, 797], 351) + femmesh.addFace([171, 308, 254, 677, 793, 785], 352) + femmesh.addFace([83, 297, 271, 762, 798, 646], 353) + femmesh.addFace([256, 314, 175, 764, 790, 598], 354) + femmesh.addFace([265, 305, 187, 753, 686, 726], 355) + femmesh.addFace([293, 308, 219, 800, 676, 678], 356) + femmesh.addFace([195, 308, 293, 794, 800, 605], 357) + femmesh.addFace([281, 300, 167, 792, 695, 688], 358) + femmesh.addFace([220, 311, 283, 682, 801, 690], 359) + femmesh.addFace([283, 311, 203, 801, 802, 461], 360) + femmesh.addFace([203, 311, 284, 802, 803, 627], 361) + femmesh.addFace([284, 311, 177, 803, 681, 486], 362) + femmesh.addFace([121, 808, 120, 814, 815, 128], 363) + femmesh.addFace([123, 809, 122, 816, 817, 130], 364) + femmesh.addFace([805, 808, 121, 818, 814, 819], 365) + femmesh.addFace([120, 808, 804, 815, 820, 821], 366) + femmesh.addFace([122, 809, 805, 817, 822, 823], 367) + femmesh.addFace([806, 809, 123, 824, 816, 825], 368) + femmesh.addFace([806, 807, 106, 826, 827, 828], 369) + femmesh.addFace([124, 807, 806, 829, 826, 830], 370) + femmesh.addFace([106, 807, 107, 827, 831, 114], 371) + femmesh.addFace([125, 807, 124, 832, 829, 132], 372) + femmesh.addFace([101, 804, 102, 833, 834, 109], 373) + femmesh.addFace([103, 805, 104, 835, 836, 111], 374) + femmesh.addFace([105, 806, 106, 837, 828, 113], 375) + femmesh.addFace([120, 804, 119, 821, 838, 127], 376) + femmesh.addFace([122, 805, 121, 823, 819, 129], 377) + femmesh.addFace([124, 806, 123, 830, 825, 131], 378) + femmesh.addFace([805, 809, 104, 822, 839, 836], 379) + femmesh.addFace([103, 808, 805, 840, 818, 835], 380) + femmesh.addFace([105, 809, 806, 841, 824, 837], 381) + femmesh.addFace([804, 808, 102, 820, 842, 834], 382) + femmesh.addFace([102, 808, 103, 842, 840, 110], 383) + femmesh.addFace([104, 809, 105, 839, 841, 112], 384) + femmesh.addFace([804, 813, 119, 843, 844, 838], 385) + femmesh.addFace([7, 810, 101, 845, 846, 108], 386) + femmesh.addFace([116, 810, 7, 847, 845, 117], 387) + femmesh.addFace([101, 810, 804, 846, 848, 833], 388) + femmesh.addFace([125, 812, 807, 849, 850, 832], 389) + femmesh.addFace([807, 811, 107, 851, 852, 831], 390) + femmesh.addFace([1, 811, 26, 853, 854, 27], 391) + femmesh.addFace([107, 811, 1, 852, 853, 115], 392) + femmesh.addFace([810, 813, 804, 855, 843, 848], 393) + femmesh.addFace([26, 812, 3, 856, 857, 28], 394) + femmesh.addFace([3, 812, 125, 857, 849, 133], 395) + femmesh.addFace([8, 813, 116, 858, 859, 118], 396) + femmesh.addFace([119, 813, 8, 844, 858, 126], 397) + femmesh.addFace([807, 812, 811, 850, 860, 851], 398) + femmesh.addFace([116, 813, 810, 859, 855, 847], 399) + femmesh.addFace([811, 812, 26, 860, 856, 854], 400) + femmesh.addFace([154, 865, 153, 871, 872, 161], 401) + femmesh.addFace([156, 866, 155, 873, 874, 163], 402) + femmesh.addFace([862, 865, 154, 875, 871, 876], 403) + femmesh.addFace([863, 866, 156, 877, 873, 878], 404) + femmesh.addFace([153, 865, 861, 872, 879, 880], 405) + femmesh.addFace([155, 866, 862, 874, 881, 882], 406) + femmesh.addFace([157, 864, 863, 883, 884, 885], 407) + femmesh.addFace([863, 864, 139, 884, 886, 887], 408) + femmesh.addFace([139, 864, 140, 886, 888, 147], 409) + femmesh.addFace([158, 864, 157, 889, 883, 165], 410) + femmesh.addFace([134, 861, 135, 890, 891, 142], 411) + femmesh.addFace([136, 862, 137, 892, 893, 144], 412) + femmesh.addFace([138, 863, 139, 894, 887, 146], 413) + femmesh.addFace([153, 861, 152, 880, 895, 160], 414) + femmesh.addFace([155, 862, 154, 882, 876, 162], 415) + femmesh.addFace([157, 863, 156, 885, 878, 164], 416) + femmesh.addFace([862, 866, 137, 881, 896, 893], 417) + femmesh.addFace([861, 865, 135, 879, 897, 891], 418) + femmesh.addFace([136, 865, 862, 898, 875, 892], 419) + femmesh.addFace([138, 866, 863, 899, 877, 894], 420) + femmesh.addFace([135, 865, 136, 897, 898, 143], 421) + femmesh.addFace([137, 866, 138, 896, 899, 145], 422) + femmesh.addFace([861, 870, 152, 900, 901, 895], 423) + femmesh.addFace([9, 867, 134, 902, 903, 141], 424) + femmesh.addFace([149, 867, 9, 904, 902, 150], 425) + femmesh.addFace([134, 867, 861, 903, 905, 890], 426) + femmesh.addFace([158, 869, 864, 906, 907, 889], 427) + femmesh.addFace([864, 868, 140, 908, 909, 888], 428) + femmesh.addFace([5, 868, 98, 910, 911, 99], 429) + femmesh.addFace([140, 868, 5, 909, 910, 148], 430) + femmesh.addFace([867, 870, 861, 912, 900, 905], 431) + femmesh.addFace([98, 869, 6, 913, 914, 100], 432) + femmesh.addFace([6, 869, 158, 914, 906, 166], 433) + femmesh.addFace([10, 870, 149, 915, 916, 151], 434) + femmesh.addFace([152, 870, 10, 901, 915, 159], 435) + femmesh.addFace([864, 869, 868, 907, 917, 908], 436) + femmesh.addFace([149, 870, 867, 916, 912, 904], 437) + femmesh.addFace([868, 869, 98, 917, 913, 911], 438) + return True diff --git a/src/Mod/Fem/femexamples/rc_wall_2d.py b/src/Mod/Fem/femexamples/rc_wall_2d.py new file mode 100644 index 0000000000..a8d86b8167 --- /dev/null +++ b/src/Mod/Fem/femexamples/rc_wall_2d.py @@ -0,0 +1,165 @@ +# *************************************************************************** +# * Copyright (c) 2019 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. * +# * * +# * 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 Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with FreeCAD; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** + + +import FreeCAD +import ObjectsFem +import Fem + +mesh_name = 'Mesh' # needs to be Mesh to work with unit tests + + +def init_doc(doc=None): + if doc is None: + doc = FreeCAD.newDocument() + return doc + + +def setup_rcwall2d(doc=None, solver='ccxtools'): + # setup reinfoced wall in 2D + + if doc is None: + doc = init_doc() + + # part + from FreeCAD import Vector as vec + import Part + from Part import makeLine as ln + + v1 = vec(0, -2000, 0) + v2 = vec(500, -2000, 0) + v3 = vec(500, 0, 0) + v4 = vec(3500, 0, 0) + v5 = vec(3500, -2000, 0) + v6 = vec(4000, -2000, 0) + v7 = vec(4000, 2000, 0) + v8 = vec(0, 2000, 0) + l1 = ln(v1, v2) + l2 = ln(v2, v3) + l3 = ln(v3, v4) + l4 = ln(v4, v5) + l5 = ln(v5, v6) + l6 = ln(v6, v7) + l7 = ln(v7, v8) + l8 = ln(v8, v1) + rcwall = doc.addObject("Part::Feature", "FIB_Wall") + rcwall.Shape = Part.Face(Part.Wire([l1, l2, l3, l4, l5, l6, l7, l8])) + + # analysis + analysis = ObjectsFem.makeAnalysis(doc, 'Analysis') + + solver + # TODO How to pass multiple solver for one analysis in one doc + if solver is None: + pass # no solver is added + elif solver is 'calculix': + solver = analysis.addObject( + ObjectsFem.makeSolverCalculix(doc, 'SolverCalculiX') + )[0] + solver.AnalysisType = 'static' + solver.GeometricalNonlinearity = 'linear' + solver.ThermoMechSteadyState = False + solver.MatrixSolverType = 'default' + solver.IterationsControlParameterTimeUse = False + elif solver is 'ccxtools': + solver = analysis.addObject( + ObjectsFem.makeSolverCalculixCcxTools(doc, 'CalculiXccxTools') + )[0] + solver.AnalysisType = 'static' + solver.GeometricalNonlinearity = 'linear' + solver.ThermoMechSteadyState = False + solver.MatrixSolverType = 'default' + solver.IterationsControlParameterTimeUse = False + solver.WorkingDir = u'' + + # shell thickness + thickness = analysis.addObject( + ObjectsFem.makeElementGeometry2D(doc, 0, 'ShellThickness') + )[0] + thickness.Thickness = 150.0 + + # material + matrixprop = {} + matrixprop['Name'] = "Concrete-EN-C35/45" + matrixprop['YoungsModulus'] = "32000 MPa" + matrixprop['PoissonRatio'] = "0.17" + matrixprop['CompressiveStrength'] = "15.75 MPa" + # make some hint on the possible angle units in material system + matrixprop['AngleOfFriction'] = "30 deg" + matrixprop['Density'] = '2500 kg/m^3' + reinfoprop = {} + reinfoprop['Name'] = "Reinforcement-FIB-B500" + reinfoprop['YieldStrength'] = "315 MPa" + # not an official FreeCAD material property + reinfoprop['ReinforcementRatio'] = "0.0" + material_reinforced = analysis.addObject( + ObjectsFem.makeMaterialReinforced(doc, 'MaterialReinforced') + )[0] + material_reinforced.Material = matrixprop + material_reinforced.Reinforcement = reinfoprop + + # fixed_constraint + fixed_constraint = analysis.addObject( + ObjectsFem.makeConstraintFixed(doc, name="ConstraintFixed") + )[0] + fixed_constraint.References = [(rcwall, "Edge1"), (rcwall, "Edge5")] + + # force constraint + force_constraint = doc.Analysis.addObject( + ObjectsFem.makeConstraintForce(doc, name="ConstraintForce") + )[0] + force_constraint.References = [(rcwall, "Edge7")] + force_constraint.Force = 1000000.0 + force_constraint.Direction = (rcwall, ["Edge8"]) + force_constraint.Reversed = False + + # displacement_constraint + displacement_constraint = doc.Analysis.addObject( + ObjectsFem.makeConstraintDisplacement(doc, name="ConstraintDisplacmentPrescribed") + )[0] + displacement_constraint.References = [(rcwall, "Face1")] + displacement_constraint.zFix = True + + # mesh + from femexamples.meshes.mesh_rc_wall_2d_tria6 import create_nodes, create_elements + fem_mesh = Fem.FemMesh() + control = create_nodes(fem_mesh) + if not control: + print('ERROR on creating nodes') + control = create_elements(fem_mesh) + if not control: + print('ERROR on creating elements') + femmesh_obj = analysis.addObject( + doc.addObject('Fem::FemMeshObject', mesh_name) + )[0] + femmesh_obj.FemMesh = fem_mesh + + doc.recompute() + return doc + + +''' +from femexamples import rc_wall_2d as rc +rc.setup_rcwall2d() + +''' diff --git a/src/Mod/Fem/femguiobjects/ViewProviderFemConstraint.py b/src/Mod/Fem/femguiobjects/ViewProviderFemConstraint.py index 67a96badbc..7aea052974 100644 --- a/src/Mod/Fem/femguiobjects/ViewProviderFemConstraint.py +++ b/src/Mod/Fem/femguiobjects/ViewProviderFemConstraint.py @@ -32,10 +32,11 @@ __url__ = "http://www.freecadweb.org" import FreeCAD import FreeCADGui import FemGui # needed to display the icons in TreeView -False if False else FemGui.__name__ # dummy usage of FemGui for flake8, just returns 'FemGui' from pivy import coin +False if False else FemGui.__name__ # flake8, dummy FemGui usage, returns 'FemGui' + class ViewProxy(object): """Proxy View Provider for Pythons base constraint.""" diff --git a/src/Mod/Fem/femguiobjects/_ViewProviderFemConstraintSelfWeight.py b/src/Mod/Fem/femguiobjects/_ViewProviderFemConstraintSelfWeight.py index 438e26c755..f8b6855d7d 100644 --- a/src/Mod/Fem/femguiobjects/_ViewProviderFemConstraintSelfWeight.py +++ b/src/Mod/Fem/femguiobjects/_ViewProviderFemConstraintSelfWeight.py @@ -31,7 +31,8 @@ __url__ = "http://www.freecadweb.org" import FreeCAD import FreeCADGui import FemGui # needed to display the icons in TreeView -False if False else FemGui.__name__ # dummy usage of FemGui for flake8, just returns 'FemGui' + +False if False else FemGui.__name__ # flake8, dummy FemGui usage, returns 'FemGui' class _ViewProviderFemConstraintSelfWeight: diff --git a/src/Mod/Fem/femguiobjects/_ViewProviderFemElementFluid1D.py b/src/Mod/Fem/femguiobjects/_ViewProviderFemElementFluid1D.py index 1fb9a5b010..8a96793e25 100644 --- a/src/Mod/Fem/femguiobjects/_ViewProviderFemElementFluid1D.py +++ b/src/Mod/Fem/femguiobjects/_ViewProviderFemElementFluid1D.py @@ -33,7 +33,6 @@ __url__ = "http://www.freecadweb.org" import FreeCAD import FreeCADGui import FemGui # needed to display the icons in TreeView -False if False else FemGui.__name__ # dummy usage of FemGui for flake8, just returns 'FemGui' # for the panel from femobjects import _FemElementFluid1D @@ -41,6 +40,8 @@ from PySide import QtCore from PySide import QtGui from . import FemSelectionWidgets +False if False else FemGui.__name__ # flake8, dummy FemGui usage, returns 'FemGui' + class _ViewProviderFemElementFluid1D: "A View Provider for the FemElementFluid1D object" diff --git a/src/Mod/Fem/femguiobjects/_ViewProviderFemElementGeometry1D.py b/src/Mod/Fem/femguiobjects/_ViewProviderFemElementGeometry1D.py index 04f209df89..a6994fe51a 100644 --- a/src/Mod/Fem/femguiobjects/_ViewProviderFemElementGeometry1D.py +++ b/src/Mod/Fem/femguiobjects/_ViewProviderFemElementGeometry1D.py @@ -31,13 +31,14 @@ __url__ = "http://www.freecadweb.org" import FreeCAD import FreeCADGui import FemGui # needed to display the icons in TreeView -False if False else FemGui.__name__ # dummy usage of FemGui for flake8, just returns 'FemGui' # for the panel from femobjects import _FemElementGeometry1D from PySide import QtCore from . import FemSelectionWidgets +False if False else FemGui.__name__ # flake8, dummy FemGui usage, returns 'FemGui' + class _ViewProviderFemElementGeometry1D: "A View Provider for the FemElementGeometry1D object" diff --git a/src/Mod/Fem/femguiobjects/_ViewProviderFemElementGeometry2D.py b/src/Mod/Fem/femguiobjects/_ViewProviderFemElementGeometry2D.py index e71984927f..090aaa2da6 100644 --- a/src/Mod/Fem/femguiobjects/_ViewProviderFemElementGeometry2D.py +++ b/src/Mod/Fem/femguiobjects/_ViewProviderFemElementGeometry2D.py @@ -31,12 +31,13 @@ __url__ = "http://www.freecadweb.org" import FreeCAD import FreeCADGui import FemGui # needed to display the icons in TreeView -False if False else FemGui.__name__ # dummy usage of FemGui for flake8, just returns 'FemGui' # for the panel from PySide import QtCore from . import FemSelectionWidgets +False if False else FemGui.__name__ # flake8, dummy FemGui usage, returns 'FemGui' + class _ViewProviderFemElementGeometry2D: "A View Provider for the FemElementGeometry2D object" diff --git a/src/Mod/Fem/femguiobjects/_ViewProviderFemElementRotation1D.py b/src/Mod/Fem/femguiobjects/_ViewProviderFemElementRotation1D.py index 8fb0320fbd..6dd9715c59 100644 --- a/src/Mod/Fem/femguiobjects/_ViewProviderFemElementRotation1D.py +++ b/src/Mod/Fem/femguiobjects/_ViewProviderFemElementRotation1D.py @@ -31,12 +31,13 @@ __url__ = "http://www.freecadweb.org" import FreeCAD import FreeCADGui import FemGui # needed to display the icons in TreeView -False if False else FemGui.__name__ # dummy usage of FemGui for flake8, just returns 'FemGui' # for the panel from PySide import QtCore from . import FemSelectionWidgets +False if False else FemGui.__name__ # flake8, dummy FemGui usage, returns 'FemGui' + class _ViewProviderFemElementRotation1D: "A View Provider for the FemElementRotation1D object" diff --git a/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterial.py b/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterial.py index a21e5105ce..cf05d7fdd9 100644 --- a/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterial.py +++ b/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterial.py @@ -31,7 +31,6 @@ __url__ = "http://www.freecadweb.org" import FreeCAD import FreeCADGui import FemGui # needed to display the icons in TreeView -False if False else FemGui.__name__ # dummy usage of FemGui for flake8, just returns 'FemGui' # for the panel from FreeCAD import Units @@ -39,6 +38,9 @@ from . import FemSelectionWidgets from PySide import QtCore from PySide import QtGui import sys + +False if False else FemGui.__name__ # flake8, dummy FemGui usage, returns 'FemGui' + if sys.version_info.major >= 3: unicode = str diff --git a/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterialMechanicalNonlinear.py b/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterialMechanicalNonlinear.py index 1dcc3179e9..e15fb6f16a 100644 --- a/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterialMechanicalNonlinear.py +++ b/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterialMechanicalNonlinear.py @@ -31,10 +31,11 @@ __url__ = "http://www.freecadweb.org" import FreeCAD import FreeCADGui import FemGui # needed to display the icons in TreeView -False if False else FemGui.__name__ # dummy usage of FemGui for flake8, just returns 'FemGui' from pivy import coin +False if False else FemGui.__name__ # flake8, dummy FemGui usage, returns 'FemGui' + class _ViewProviderFemMaterialMechanicalNonlinear: "A View Provider for the FemMaterialMechanicalNonlinear object" diff --git a/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterialReinforced.py b/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterialReinforced.py new file mode 100644 index 0000000000..76596d0680 --- /dev/null +++ b/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterialReinforced.py @@ -0,0 +1,502 @@ +# *************************************************************************** +# * Copyright (c) 2019 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 material reinforced ViewProvider for the document object" +__author__ = "Bernd Hahnebach" +__url__ = "http://www.freecadweb.org" + +## @package ViewProviderFemMaterialReinforced +# \ingroup FEM +# \brief FreeCAD FEM _ViewProviderFemMaterialReinforced + +import FreeCAD +import FreeCADGui +import FemGui # needed to display the icons in TreeView + + +# task panel +# from . import FemSelectionWidgets +from PySide import QtCore +from PySide import QtGui +import sys + +False if False else FemGui.__name__ # flake8, dummy FemGui usage, returns 'FemGui' + +if sys.version_info.major >= 3: + unicode = str + + +class _ViewProviderFemMaterialReinforced: + "A View Provider for the FemMaterialReinfocement object" + def __init__(self, vobj): + vobj.Proxy = self + + def getIcon(self): + return ":/icons/fem-material-reinforced.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 updateData(self, obj, prop): + return + + def onChanged(self, vobj, prop): + return + + def setEdit(self, vobj, mode=0): + # hide all meshes + for o in FreeCAD.ActiveDocument.Objects: + if o.isDerivedFrom("Fem::FemMeshObject"): + o.ViewObject.hide() + # hide all meshes + for o in FreeCAD.ActiveDocument.Objects: + if o.isDerivedFrom("Fem::FemMeshObject"): + o.ViewObject.hide() + # show task panel + taskd = _TaskPanelFemMaterialReinforced(self.Object) + taskd.obj = vobj.Object + FreeCADGui.Control.showDialog(taskd) + return True + + def unsetEdit(self, vobj, mode=0): + FreeCADGui.Control.closeDialog() + return True + + def doubleClicked(self, vobj): + guidoc = FreeCADGui.getDocument(vobj.Object.Document) + # check if another VP is in edit mode + # https://forum.freecadweb.org/viewtopic.php?t=13077#p104702 + if not guidoc.getInEdit(): + guidoc.setEdit(vobj.Object.Name) + else: + from PySide.QtGui import QMessageBox + message = ( + 'Active Task Dialog found! ' + 'Please close this one before opening a new one!' + ) + QMessageBox.critical(None, "Error in tree view", message) + FreeCAD.Console.PrintError(message + '\n') + return True + + def __getstate__(self): + return None + + def __setstate__(self, state): + return None + + +class _TaskPanelFemMaterialReinforced: + '''The editmode TaskPanel for FemMaterialReinforced objects''' + + if sys.version_info.major >= 3: + unicode = str + + def __init__(self, obj): + + FreeCAD.Console.PrintMessage('\n') # empty line on start task panel + self.obj = obj + + # init matrix and reinforcement material + self.material_m = self.obj.Material + self.card_path_m = '' + self.has_transient_mat_m = False + self.material_r = self.obj.Reinforcement + self.card_path_r = '' + self.has_transient_mat_r = False + # mat_card is the FCMat file + # card_name is the file name of the mat_card + # card_path is the whole file path of the mat_card + # material_name is the value of the key name in FreeCAD material dictionary + # they might not match because of special letters in the material_name which are + # changed in the card_name to english standard characters + + # init for collecting all mat data and icons + self.materials = {} # { card_path : FreeCAD material dict } + self.cards = {} # { card_path : card_names, ... } + self.icons = {} # { card_path : icon_path } + + # parameter widget + self.parameterWidget = FreeCADGui.PySideUic.loadUi( + FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/MaterialReinforcement.ui" + ) + + # globals + QtCore.QObject.connect( + self.parameterWidget.cb_materials_m, + QtCore.SIGNAL("activated(int)"), + self.choose_material_m + ) + QtCore.QObject.connect( + self.parameterWidget.pb_edit_m, + QtCore.SIGNAL("clicked()"), + self.edit_material_m + ) + QtCore.QObject.connect( + self.parameterWidget.cb_materials_r, + QtCore.SIGNAL("activated(int)"), + self.choose_material_r + ) + QtCore.QObject.connect( + self.parameterWidget.pb_edit_r, + QtCore.SIGNAL("clicked()"), + self.edit_material_r + ) + + # get all available materials (fill self.materials, self.cards and self.icons) + from materialtools.cardutils import import_materials as getmats + self.materials, self.cards, self.icons = getmats() + # fill the material comboboxes with material cards + self.add_cards_to_combo_boxes() + + # search for exact the mat_card_m and mat_card_r in all known cards + # choose the current matrix material + self.card_path_m = self.get_material_card(self.material_m) + FreeCAD.Console.PrintLog('card_path: {}'.format(self.card_path_m)) + if not self.card_path_m: + # we have not found our material in self.materials dict :-( + # we're going to add a user-defined temporary material: a document material + FreeCAD.Console.PrintMessage( + "Previously used material card cannot be found in material directories. " + "Add document material.\n" + ) + self.card_path_m = '_Document_Matrix_Material' + self.materials[self.card_path_m] = self.material_m + self.parameterWidget.cb_materials_m.addItem( + QtGui.QIcon(":/icons/help-browser.svg"), + self.card_path_m, + self.card_path_m + ) + index = self.parameterWidget.cb_materials_m.findData(self.card_path_m) + # print(index) + # fill input fields and set the current material in the cb widget + self.choose_material_m(index) + else: + # we found our exact material in self.materials dict :-) + FreeCAD.Console.PrintLog( + "Previously used material card was found in material directories. " + "We will use this material.\n" + ) + index = self.parameterWidget.cb_materials_m.findData(self.card_path_m) + # set the current material in the cb widget + self.choose_material_m(index) + + # choose the current reinforcement material + self.card_path_r = self.get_material_card(self.material_r) + FreeCAD.Console.PrintLog('card_path: {}'.format(self.card_path_r)) + if not self.card_path_r: + # we have not found our material in self.materials dict :-( + # we're going to add a user-defined temporary material: a document material + FreeCAD.Console.PrintMessage( + "Previously used material card cannot be found in material directories. " + "Add document material.\n" + ) + self.card_path_r = '_Document_Reinforcement_Material' + self.materials[self.card_path_r] = self.material_r + self.parameterWidget.cb_materials_r.addItem( + QtGui.QIcon(":/icons/help-browser.svg"), + self.card_path_r, + self.card_path_r + ) + index = self.parameterWidget.cb_materials_r.findData(self.card_path_r) + # set the current material in the cb widget + self.choose_material_r(index) + else: + # we found our exact material in self.materials dict :-) + FreeCAD.Console.PrintLog( + "Previously used material card was found in material directories. " + "We will use this material.\n" + ) + index = self.parameterWidget.cb_materials_r.findData(self.card_path_r) + # print(index) + # fill input fields and set the current material in the cb widget + self.choose_material_r(index) + + # set up the form + self.form = self.parameterWidget + + # leave task panel *************************************************************************** + def accept(self): + from materialtools.cardutils import check_mat_units as checkunits + if checkunits(self.material_m) is True and checkunits(self.material_r) is True: + self.obj.Material = self.material_m + self.obj.Reinforcement = self.material_r + else: + error_message = ( + 'Due to some wrong material quantity units in the changed ' + 'material data, the task panel changes where not accepted.\n' + ) + FreeCAD.Console.PrintError(error_message) + QtGui.QMessageBox.critical(None, "Material data not changed", error_message) + self.recompute_and_set_back_all() + return True + + def reject(self): + self.recompute_and_set_back_all() + return True + + def recompute_and_set_back_all(self): + guidoc = FreeCADGui.getDocument(self.obj.Document) + guidoc.Document.recompute() + guidoc.resetEdit() + self.output_obj_mat_param() + + def output_obj_mat_param(self): + self.print_mat_dict(self.obj.Material) + self.print_mat_dict(self.obj.Reinforcement) + print('\n') + + def print_mat_dict(self, mat_dict): + if 'Name' in mat_dict: + print('Material: {}'.format(mat_dict['Name'])) + else: + print('Matrix material: no Name') + for key in mat_dict: + print(' {}: {}'.format(key, mat_dict[key])) + + # choose material card *********************************************************************** + def get_material_card(self, material): + for a_mat in self.materials: + unmatched_items = set(self.materials[a_mat].items()) ^ set(material.items()) + # print(a_mat + ' --> unmatched_items = ' + str(len(unmatched_items))) + if len(unmatched_items) < 4: + FreeCAD.Console.PrintLog('{}\n'.format(unmatched_items)) + if len(unmatched_items) == 0: + return a_mat + return "" + + def choose_material_m(self, index): + if index < 0: + return + # get the whole card path + self.card_path_m = self.parameterWidget.cb_materials_m.itemData(index) + FreeCAD.Console.PrintMessage( + 'choose_material in FEM material task panel:\n' + ' {}\n'.format(self.card_path_m) + ) + self.material_m = self.materials[self.card_path_m] + self.parameterWidget.cb_materials_m.setCurrentIndex(index) + gen_mat_desc = "" + gen_mat_name = "" + if 'Description' in self.material_m: + gen_mat_desc = self.material_m['Description'] + if 'Name' in self.material_m: + gen_mat_name = self.material_m['Name'] + self.parameterWidget.l_description_m.setText(gen_mat_desc) + self.parameterWidget.l_name_m.setText(gen_mat_name) + + def choose_material_r(self, index): + if index < 0: + return + # get the whole card path + self.card_path_r = self.parameterWidget.cb_materials_r.itemData(index) + FreeCAD.Console.PrintMessage( + 'choose_material in FEM material task panel:\n' + ' {}\n'.format(self.card_path_r) + ) + self.material_r = self.materials[self.card_path_r] + self.parameterWidget.cb_materials_r.setCurrentIndex(index) + gen_mat_desc = "" + gen_mat_name = "" + if 'Description' in self.material_r: + gen_mat_desc = self.material_r['Description'] + if 'Name' in self.material_r: + gen_mat_name = self.material_r['Name'] + self.parameterWidget.l_description_r.setText(gen_mat_desc) + self.parameterWidget.l_name_r.setText(gen_mat_name) + + # transient material is needed if the user changed mat parameter by the mat editor + def set_transient_material_m(self): + self.card_path_m = '_Transient_Matrix_Material' + self.materials[self.card_path_m] = self.material_m # = the current matrix mat dict + index = self.parameterWidget.cb_materials_m.findData(self.card_path_m) + self.choose_material_m(index) + + def add_transient_material_m(self): + self.has_transient_mat_m = True + self.card_path_m = '_Transient_Matrix_Material' + self.parameterWidget.cb_materials_m.addItem( + QtGui.QIcon(":/icons/help-browser.svg"), + self.card_path_m, + self.card_path_m + ) + self.set_transient_material_m() + + def set_transient_material_r(self): + self.card_path_r = '_Transient_Reinforcement_Material' + self.materials[self.card_path_r] = self.material_r # = the current reinforced mat dict + index = self.parameterWidget.cb_materials_r.findData(self.card_path_r) + self.choose_material_r(index) + + def add_transient_material_r(self): + self.has_transient_mat_r = True + self.card_path_r = '_Transient_Reinforcement_Material' + self.parameterWidget.cb_materials_r.addItem( + QtGui.QIcon(":/icons/help-browser.svg"), + self.card_path_r, + self.card_path_r + ) + self.set_transient_material_r() + + # edit material parameter ******************************************************************** + # TODO, also all mat parameter checks should be moved to material editor + # and mat parameter checks should be done on analysis precheck in according to the analysis + # should be checked if all needed parameter are defined and have all right values and units + def edit_material_m(self): + # opens the material editor to choose a material or edit material params + import MaterialEditor + if self.card_path_m not in self.cards: + FreeCAD.Console.PrintLog( + 'Card path not in cards, material dict will be used to open Material Editor.\n' + ) + new_material_params = MaterialEditor.editMaterial(material=self.material_m) + else: + new_material_params = MaterialEditor.editMaterial(card_path=self.card_path_m) + # material editor returns the mat_dict only, not a card_path + # if the material editor was canceled a empty dict will be returned + # do not change the self.material + # check if dict is not empty (do not use 'is True') + if new_material_params: + # check material quantity units + from materialtools.cardutils import check_mat_units as checkunits + if checkunits(new_material_params) is True: + self.material_m = new_material_params + self.card_path_m = self.get_material_card(self.material_m) + FreeCAD.Console.PrintMessage('card_path: {}\n'.format(self.card_path_m)) + if not self.card_path_m: + FreeCAD.Console.PrintMessage( + "Material card chosen by the material editor " + "was not found in material directories.\n" + "Either the card does not exist or some material " + "parameter where changed in material editor.\n" + ) + if self.has_transient_mat_m is False: + self.add_transient_material_m() + else: + self.set_transient_material_m() + else: + # we found our exact material in self.materials dict :-) + FreeCAD.Console.PrintLog( + "Material card chosen by the material editor " + "was found in material directories. " + "The found material card will be used.\n" + ) + index = self.parameterWidget.cb_materials_m.findData(self.card_path_m) + # print(index) + # set the current material in the cb widget + self.choose_material_m(index) + else: + error_message = ( + 'Due to some wrong material quantity units in data passed ' + 'by the material editor, the material data was not changed.\n' + ) + FreeCAD.Console.PrintError(error_message) + QtGui.QMessageBox.critical(None, "Material data not changed", error_message) + else: + FreeCAD.Console.PrintMessage('No changes where made by the material editor.\n') + + def edit_material_r(self): + # opens the material editor to choose a material or edit material params + import MaterialEditor + if self.card_path_r not in self.cards: + FreeCAD.Console.PrintLog( + 'Card path not in cards, material dict will be used to open Material Editor.\n' + ) + new_material_params = MaterialEditor.editMaterial(material=self.material_r) + else: + new_material_params = MaterialEditor.editMaterial(card_path=self.card_path_r) + # material editor returns the mat_dict only, not a card_path + # if the material editor was canceled a empty dict will be returned + # do not change the self.material + # check if dict is not empty (do not use 'is True') + if new_material_params: + # check material quantity units + from materialtools.cardutils import check_mat_units as checkunits + if checkunits(new_material_params) is True: + self.material_r = new_material_params + self.card_path_r = self.get_material_card(self.material_r) + FreeCAD.Console.PrintMessage('card_path: {}\n'.format(self.card_path_r)) + if not self.card_path_r: + FreeCAD.Console.PrintMessage( + "Material card chosen by the material editor " + "was not found in material directories.\n" + "Either the card does not exist or some material " + "parameter where changed in material editor.\n" + ) + if self.has_transient_mat_r is False: + self.add_transient_material_r() + else: + self.set_transient_material_r() + else: + # we found our exact material in self.materials dict :-) + FreeCAD.Console.PrintLog( + "Material card chosen by the material editor " + "was found in material directories. " + "The found material card will be used.\n" + ) + index = self.parameterWidget.cb_materials_r.findData(self.card_path_r) + # print(index) + # set the current material in the cb widget + self.choose_material_r(index) + else: + error_message = ( + 'Due to some wrong material quantity units in data passed ' + 'by the material editor, the material data was not changed.\n' + ) + FreeCAD.Console.PrintError(error_message) + QtGui.QMessageBox.critical(None, "Material data not changed", error_message) + else: + FreeCAD.Console.PrintMessage('No changes where made by the material editor.\n') + + # fill the combo box with cards ************************************************************** + def add_cards_to_combo_boxes(self): + # fill comboboxes, in combo box the card name is used not the material name + self.parameterWidget.cb_materials_m.clear() + self.parameterWidget.cb_materials_r.clear() + + mat_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material/Cards") + sort_by_resources = mat_prefs.GetBool("SortByResources", False) + + card_name_list = [] # [ [card_name, card_path, icon_path], ... ] + + if sort_by_resources is True: + for a_path in sorted(self.materials.keys()): + card_name_list.append([self.cards[a_path], a_path, self.icons[a_path]]) + else: + card_names_tmp = {} + for path, name in self.cards.items(): + card_names_tmp[name] = path + for a_name in sorted(card_names_tmp.keys()): + a_path = card_names_tmp[a_name] + card_name_list.append([a_name, a_path, self.icons[a_path]]) + + for mat in card_name_list: + self.parameterWidget.cb_materials_m.addItem(QtGui.QIcon(mat[2]), mat[0], mat[1]) + self.parameterWidget.cb_materials_r.addItem(QtGui.QIcon(mat[2]), mat[0], mat[1]) + # the whole card path is added to the combo box to make it unique + # see def choose_material: + # for assignment of self.card_path the path form the parameterWidget ist used diff --git a/src/Mod/Fem/femguiobjects/_ViewProviderFemMeshBoundaryLayer.py b/src/Mod/Fem/femguiobjects/_ViewProviderFemMeshBoundaryLayer.py index 5153b139d3..ec1b2d3d5a 100644 --- a/src/Mod/Fem/femguiobjects/_ViewProviderFemMeshBoundaryLayer.py +++ b/src/Mod/Fem/femguiobjects/_ViewProviderFemMeshBoundaryLayer.py @@ -31,12 +31,13 @@ __url__ = "http://www.freecadweb.org" import FreeCAD import FreeCADGui import FemGui # needed to display the icons in TreeView -False if False else FemGui.__name__ # dummy usage of FemGui for flake8, just returns 'FemGui' # for the panel from PySide import QtCore from . import FemSelectionWidgets +False if False else FemGui.__name__ # flake8, dummy FemGui usage, returns 'FemGui' + class _ViewProviderFemMeshBoundaryLayer: "A View Provider for the FemMeshBoundaryLayer object" diff --git a/src/Mod/Fem/femguiobjects/_ViewProviderFemMeshGroup.py b/src/Mod/Fem/femguiobjects/_ViewProviderFemMeshGroup.py index d1065c9b1e..1df60fbe10 100644 --- a/src/Mod/Fem/femguiobjects/_ViewProviderFemMeshGroup.py +++ b/src/Mod/Fem/femguiobjects/_ViewProviderFemMeshGroup.py @@ -31,12 +31,13 @@ __url__ = "http://www.freecadweb.org" import FreeCAD import FreeCADGui import FemGui # needed to display the icons in TreeView -False if False else FemGui.__name__ # dummy usage of FemGui for flake8, just returns 'FemGui' # for the panel from PySide import QtCore from . import FemSelectionWidgets +False if False else FemGui.__name__ # flake8, dummy FemGui usage, returns 'FemGui' + class _ViewProviderFemMeshGroup: "A View Provider for the FemMeshGroup object" diff --git a/src/Mod/Fem/femguiobjects/_ViewProviderFemMeshRegion.py b/src/Mod/Fem/femguiobjects/_ViewProviderFemMeshRegion.py index 6a8dc38fd5..ea54cc8865 100644 --- a/src/Mod/Fem/femguiobjects/_ViewProviderFemMeshRegion.py +++ b/src/Mod/Fem/femguiobjects/_ViewProviderFemMeshRegion.py @@ -31,12 +31,13 @@ __url__ = "http://www.freecadweb.org" import FreeCAD import FreeCADGui import FemGui # needed to display the icons in TreeView -False if False else FemGui.__name__ # dummy usage of FemGui for flake8, just returns 'FemGui' # for the panel from PySide import QtCore from . import FemSelectionWidgets +False if False else FemGui.__name__ # flake8, dummy FemGui usage, returns 'FemGui' + class _ViewProviderFemMeshRegion: "A View Provider for the FemMeshRegion object" diff --git a/src/Mod/Fem/femguiobjects/_ViewProviderFemResultMechanical.py b/src/Mod/Fem/femguiobjects/_ViewProviderFemResultMechanical.py index 1d4f823d92..a80e90560d 100644 --- a/src/Mod/Fem/femguiobjects/_ViewProviderFemResultMechanical.py +++ b/src/Mod/Fem/femguiobjects/_ViewProviderFemResultMechanical.py @@ -31,10 +31,8 @@ __url__ = "http://www.freecadweb.org" import FreeCAD import FreeCADGui import FemGui # needed to display the icons in TreeView -False if False else FemGui.__name__ # dummy usage of FemGui for flake8, just returns 'FemGui' # for the panel -import FemGui import femresult.resulttools as resulttools from PySide import QtCore from PySide import QtGui @@ -42,6 +40,8 @@ from PySide.QtCore import Qt from PySide.QtGui import QApplication import numpy as np +False if False else FemGui.__name__ # flake8, dummy FemGui usage, returns 'FemGui' + class _ViewProviderFemResultMechanical: "A View Provider for the FemResultObject Python derived FemResult class" @@ -467,6 +467,28 @@ class _TaskPanelFemResultShow: exy = np.array(self.result_obj.NodeStrainXY) exz = np.array(self.result_obj.NodeStrainXZ) eyz = np.array(self.result_obj.NodeStrainYZ) + + # Display of Reinforcement Ratios and Mohr Coulomb Criterion + rx = np.array(self.result_obj.ReinforcementRatio_x) + ry = np.array(self.result_obj.ReinforcementRatio_y) + rz = np.array(self.result_obj.ReinforcementRatio_z) + mc = np.array(self.result_obj.MohrCoulomb) + + ps1vector = np.array(self.result_obj.PS1Vector) + s1x = np.array(ps1vector[:, 0]) + s1y = np.array(ps1vector[:, 1]) + s1z = np.array(ps1vector[:, 2]) + + ps2vector = np.array(self.result_obj.PS2Vector) + s2x = np.array(ps2vector[:, 0]) + s2y = np.array(ps2vector[:, 1]) + s2z = np.array(ps2vector[:, 2]) + + ps3vector = np.array(self.result_obj.PS1Vector) + s3x = np.array(ps3vector[:, 0]) + s3y = np.array(ps3vector[:, 1]) + s3z = np.array(ps3vector[:, 2]) + userdefined_eq = self.form.user_def_eq.toPlainText() # Get equation to be used UserDefinedFormula = eval(userdefined_eq).tolist() self.result_obj.UserDefined = UserDefinedFormula @@ -486,7 +508,8 @@ class _TaskPanelFemResultShow: del x, y, z, T, Von, Peeq, P1, P2, P3 del sxx, syy, szz, sxy, sxz, syz del exx, eyy, ezz, exy, exz, eyz - del MF, NP + del MF, NP, rx, ry, rz, mc + del s1x, s1y, s1z, s2x, s2y, s2z, s3x, s3y, s3z def select_displacement_type(self, disp_type): QApplication.setOverrideCursor(Qt.WaitCursor) @@ -634,12 +657,13 @@ def hide_femmeshes_postpiplines(): def hide_parts_constraints(): + from FemGui import getActiveAnalysis fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/General") hide_constraints = fem_prefs.GetBool("HideConstraint", False) if hide_constraints: for o in FreeCAD.ActiveDocument.Objects: if o.isDerivedFrom('Fem::FemAnalysis'): - for acnstrmesh in FemGui.getActiveAnalysis().Group: + for acnstrmesh in getActiveAnalysis().Group: if "Constraint" in acnstrmesh.TypeId: acnstrmesh.ViewObject.Visibility = False break diff --git a/src/Mod/Fem/feminout/convert2TetGen.py b/src/Mod/Fem/feminout/convert2TetGen.py index 208fcc29dc..13aca5a2d8 100644 --- a/src/Mod/Fem/feminout/convert2TetGen.py +++ b/src/Mod/Fem/feminout/convert2TetGen.py @@ -26,12 +26,11 @@ # Make mesh of pn junction in TetGen format import FreeCAD +import Mesh App = FreeCAD # shortcut if FreeCAD.GuiUp: import FreeCADGui Gui = FreeCADGui # shortcut -# import Part -import Mesh ## \addtogroup FEM # @{ @@ -46,14 +45,6 @@ def exportMeshToTetGenPoly(meshToExport, filePath, beVerbose=1): f = open(filePath, 'w') f.write("# This file was generated from FreeCAD geometry\n") f.write("# Part 1 - node list\n") - ''' - f.write("%(TotalNumOfPoints)i %(NumOfDimensions)i %(NumOfProperties)i %(BoundaryMarkerExists)i\n" % { - 'TotalNumOfPoints': len(allVertices), - 'NumOfDimensions': 3, - 'NumOfProperties': 0, - 'BoundaryMarkerExists': 0 - }) - ''' f.write( "TotalNumOfPoints: {}, NumOfDimensions; {}, " "NumOfProperties: {}, BoundaryMarkerExists: {}\n" diff --git a/src/Mod/Fem/feminout/importCcxFrdResults.py b/src/Mod/Fem/feminout/importCcxFrdResults.py index f9253a62f7..23280ddbc3 100644 --- a/src/Mod/Fem/feminout/importCcxFrdResults.py +++ b/src/Mod/Fem/feminout/importCcxFrdResults.py @@ -120,19 +120,31 @@ def importFrd( analysis_object.addObject(res_obj) # complementary result object calculations import femresult.resulttools as restools + import femtools.femutils as femutils if not res_obj.MassFlowRate: # only compact result if not Flow 1D results # compact result object, workaround for bug 2873 # https://www.freecadweb.org/tracker/view.php?id=2873 res_obj = restools.compact_result(res_obj) + # fill DisplacementLengths res_obj = restools.add_disp_apps(res_obj) # fill StressValues res_obj = restools.add_von_mises(res_obj) - # fill PrincipalMax, PrincipalMed, PrincipalMin, MaxShear - res_obj = restools.add_principal_stress(res_obj) + if res_obj.getParentGroup(): + has_reinforced_mat = False + for obj in res_obj.getParentGroup().Group: + if obj.isDerivedFrom('App::MaterialObjectPython') \ + and femutils.is_of_type(obj, 'Fem::MaterialReinforced'): + has_reinforced_mat = True + restools.add_principal_stress_reinforced(res_obj) + break + if has_reinforced_mat is False: + # fill PrincipalMax, PrincipalMed, PrincipalMin, MaxShear + res_obj = restools.add_principal_stress_std(res_obj) # fill Stats res_obj = restools.fill_femresult_stats(res_obj) + else: error_message = ( "We have nodes but no results in frd file, " diff --git a/src/Mod/Fem/feminout/importFenicsMesh.py b/src/Mod/Fem/feminout/importFenicsMesh.py index 2db3c2103c..356cfe7961 100644 --- a/src/Mod/Fem/feminout/importFenicsMesh.py +++ b/src/Mod/Fem/feminout/importFenicsMesh.py @@ -31,17 +31,15 @@ __url__ = "http://www.freecadweb.org" import os import FreeCAD - -if FreeCAD.GuiUp: - import FreeCADGui - from PySide import QtGui, QtCore - - from . import importToolsFem from . import readFenicsXML from . import writeFenicsXML from . import writeFenicsXDMF +if FreeCAD.GuiUp: + import FreeCADGui + from PySide import QtGui, QtCore + # Template copied from importZ88Mesh.py. Thanks Bernd! # ********* generic FreeCAD import and export methods ********* diff --git a/src/Mod/Fem/feminout/importZ88Mesh.py b/src/Mod/Fem/feminout/importZ88Mesh.py index a6835e98aa..5692187452 100644 --- a/src/Mod/Fem/feminout/importZ88Mesh.py +++ b/src/Mod/Fem/feminout/importZ88Mesh.py @@ -484,12 +484,14 @@ def write_z88_mesh_to_file( # z88_element_type is checked for every element # but mixed elements are not supported up to date n = femelement_table[element] - if z88_element_type == 2 \ - or z88_element_type == 4 \ - or z88_element_type == 5 \ - or z88_element_type == 9 \ - or z88_element_type == 13 \ - or z88_element_type == 25: + if ( + z88_element_type == 2 + or z88_element_type == 4 + or z88_element_type == 5 + or z88_element_type == 9 + or z88_element_type == 13 + or z88_element_type == 25 + ): # seg2 FreeCAD --> stab4 Z88 # N1, N2 f.write("{0} {1}\n".format(element, z88_element_type, element)) diff --git a/src/Mod/Fem/femmesh/meshtools.py b/src/Mod/Fem/femmesh/meshtools.py index aa906e352f..6b589b0358 100644 --- a/src/Mod/Fem/femmesh/meshtools.py +++ b/src/Mod/Fem/femmesh/meshtools.py @@ -201,14 +201,14 @@ def get_femnodes_ele_table( ): '''the femnodes_ele_table contains for each node its membership in elements {nodeID : [[eleID, NodePosition], [], ...], nodeID : [[], [], ...], ...} - stored informatation are: + stored information is: element number, the number of nodes per element the position of the node in the element. The position of the node in the element is coded as a set bit at that position in a bit array (integer) FIXME: the number of nodes per element should be replaced by the type of the element - but I did not know, how to get this from the mesh. + but I did not know how to get this from the mesh. Since the femelement_table contains either volume or face or edgemesh the femnodes_ele_table only has either volume or face or edge elements @@ -1799,9 +1799,9 @@ def get_anlysis_empty_references_group_elements( aShape ): '''get the elementIDs if the Reference shape is empty - see get_analysis_group_elements() for more informatations + see get_analysis_group_elements() for more information on solid meshes only material objects could have an - empty reference without being something wrong! + empty reference without there being something wrong! face meshes could have empty ShellThickness and edge meshes could have empty BeamSection/FluidSection ''' diff --git a/src/Mod/Fem/femobjects/_FemMaterialReinforced.py b/src/Mod/Fem/femobjects/_FemMaterialReinforced.py new file mode 100644 index 0000000000..d8b970e0db --- /dev/null +++ b/src/Mod/Fem/femobjects/_FemMaterialReinforced.py @@ -0,0 +1,59 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2019 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 reinforced material" +__author__ = "Bernd Hahnebach" +__url__ = "http://www.freecadweb.org" + +## @package FemMaterialReinforced +# \ingroup FEM +# \brief FreeCAD FEM _FemMaterialReinforced + + +class _FemMaterialReinforced: + "The FemMaterialReinforced object" + def __init__(self, obj): + obj.addProperty( + "App::PropertyLinkSubList", + "References", + "Material", + "List of material shapes" + ) + obj.addProperty( + "App::PropertyMap", + "Reinforcement", + "Composites", + "Reinforcement material properties" + ) + obj.addProperty( + "App::PropertyEnumeration", + "Category", + "Material", + "Matrix material properties" + ) + obj.Category = ['Solid'] + obj.Category = 'Solid' + obj.Proxy = self + self.Type = "Fem::MaterialReinforced" + + def execute(self, obj): + return diff --git a/src/Mod/Fem/femobjects/_FemResultMechanical.py b/src/Mod/Fem/femobjects/_FemResultMechanical.py index 721678122f..81da7d554b 100644 --- a/src/Mod/Fem/femobjects/_FemResultMechanical.py +++ b/src/Mod/Fem/femobjects/_FemResultMechanical.py @@ -80,6 +80,56 @@ class _FemResultMechanical(): "List of equivalent plastic strain values", True ) + obj.addProperty( + "App::PropertyFloatList", + "MohrCoulomb", + "NodeData", + "List of Mohr Coulomb stress values", + True + ) + obj.addProperty( + "App::PropertyFloatList", + "ReinforcementRatio_x", + "NodeData", + "Reinforcement ratio x-direction", + True + ) + obj.addProperty( + "App::PropertyFloatList", + "ReinforcementRatio_y", + "NodeData", + "Reinforcement ratio y-direction", + True + ) + obj.addProperty( + "App::PropertyFloatList", + "ReinforcementRatio_z", + "NodeData", + "Reinforcement ratio z-direction", + True + ) + obj.addProperty( + "App::PropertyVectorList", + "PS1Vector", + "NodeData", + "List of 1st Principal Stress Vectors", + True + ) + obj.addProperty( + "App::PropertyVectorList", + "PS2Vector", + "NodeData", + "List of 2nd Principal Stress Vectors", + True + ) + obj.addProperty( + "App::PropertyVectorList", + "PS3Vector", + "NodeData", + "List of 3rd Principal Stress Vectors", + True + ) + # readonly in propertyEditor of comboView obj.addProperty( "App::PropertyFloatList", diff --git a/src/Mod/Fem/femresult/resulttools.py b/src/Mod/Fem/femresult/resulttools.py index 10343b9064..22eeceba19 100644 --- a/src/Mod/Fem/femresult/resulttools.py +++ b/src/Mod/Fem/femresult/resulttools.py @@ -310,7 +310,7 @@ def add_von_mises(res_obj): return res_obj -def add_principal_stress(res_obj): +def add_principal_stress_std(res_obj): prinstress1 = [] prinstress2 = [] prinstress3 = [] @@ -324,7 +324,7 @@ def add_principal_stress(res_obj): res_obj.NodeStressYZ ) for Sxx, Syy, Szz, Sxy, Sxz, Syz in iterator: - prin1, prin2, prin3, shear = calculate_principal_stress((Sxx, Syy, Szz, Sxy, Sxz, Syz)) + prin1, prin2, prin3, shear = calculate_principal_stress_std((Sxx, Syy, Szz, Sxy, Sxz, Syz)) prinstress1.append(prin1) prinstress2.append(prin2) prinstress3.append(prin3) @@ -337,6 +337,165 @@ def add_principal_stress(res_obj): return res_obj +def get_concrete_nodes(res_obj): + + # + # HarryvL: determine concrete / non-concrete nodes + # + + from femmesh.meshtools import get_femnodes_by_refshape + femmesh = res_obj.Mesh.FemMesh + nsr = femmesh.NodeCount # nsr number of stress results + + # ic[iic]: + # ic = flag for material type; iic = node number + # ic = 0: NOT ASSIGNED + # ic = 1: ReinforcedMaterial + # ic = 2: NOT ReinforcedMaterial + ic = np.zeros(nsr) + + for obj in res_obj.getParentGroup().Group: + if obj.isDerivedFrom('App::MaterialObjectPython') \ + and femutils.is_of_type(obj, 'Fem::MaterialReinforced'): + print("ReinforcedMaterial") + if obj.References == []: + for iic in range(nsr): + if ic[iic] == 0: + ic[iic] = 1 + else: + for ref in obj.References: + concrete_nodes = get_femnodes_by_refshape(femmesh, ref) + for cn in concrete_nodes: + ic[cn - 1] = 1 + elif obj.isDerivedFrom('App::MaterialObjectPython') \ + and femutils.is_of_type(obj, 'Fem::Material'): + print("NOT ReinforcedMaterial") + if obj.References == []: + for iic in range(nsr): + if ic[iic] == 0: + ic[iic] = 2 + else: + for ref in obj.References: + non_concrete_nodes = get_femnodes_by_refshape(femmesh, ref) + for ncn in non_concrete_nodes: + ic[ncn - 1] = 2 + return ic + + +def add_principal_stress_reinforced(res_obj): + + # + # HarryvL: determine concrete / non-concrete nodes + # + ic = get_concrete_nodes(res_obj) + + # + # calculate principal and max Shear and fill them in res_obj + # + prinstress1 = [] + prinstress2 = [] + prinstress3 = [] + shearstress = [] + ps1v = [] + ps2v = [] + ps3v = [] + # + # HarryvL: additional arrays to hold reinforcement ratios + # and mohr coulomb stress + # + rhx = [] + rhy = [] + rhz = [] + moc = [] + + # material parameter + for obj in res_obj.getParentGroup().Group: + if femutils.is_of_type(obj, 'Fem::MaterialReinforced'): + matrix_af = float( + FreeCAD.Units.Quantity(obj.Material['AngleOfFriction']).getValueAs('rad') + ) + matrix_cs = float( + FreeCAD.Units.Quantity(obj.Material['CompressiveStrength']).getValueAs('MPa') + ) + reinforce_yield = float( + FreeCAD.Units.Quantity(obj.Reinforcement['YieldStrength']).getValueAs('MPa') + ) + # print(matrix_af) + # print(matrix_cs) + # print(reinforce_yield) + + iterator = zip( + res_obj.NodeStressXX, + res_obj.NodeStressYY, + res_obj.NodeStressZZ, + res_obj.NodeStressXY, + res_obj.NodeStressXZ, + res_obj.NodeStressYZ + ) + for isv, stress_tensor in enumerate(iterator): + + rhox = 0. + rhoy = 0. + rhoz = 0. + mc = 0. + + if ic[isv] == 1: + # + # HarryvL: for concrete scxx etc. are affected by + # reinforcement (see calculate_rho(stress_tensor)). for all other + # materials scxx etc. are the original stresses + # + rhox, rhoy, rhoz = calculate_rho( + stress_tensor, + reinforce_yield + ) + + prin1, prin2, prin3, shear, psv = calculate_principal_stress_reinforced( + stress_tensor + ) + + prinstress1.append(prin1) + prinstress2.append(prin2) + prinstress3.append(prin3) + shearstress.append(shear) + ps1v.append(psv[0]) + ps2v.append(psv[1]) + ps3v.append(psv[2]) + + # + # reinforcement ratios and mohr coulomb criterion + # + rhx.append(rhox) + rhy.append(rhoy) + rhz.append(rhoz) + if ic[isv] == 1: + mc = calculate_mohr_coulomb(prin1, prin3, matrix_af, matrix_cs) + moc.append(mc) + + res_obj.PrincipalMax = prinstress1 + res_obj.PrincipalMed = prinstress2 + res_obj.PrincipalMin = prinstress3 + res_obj.MaxShear = shearstress + # + # HarryvL: additional concrete and principal stress plot + # results for use in _ViewProviderFemResultMechanical + # + res_obj.ReinforcementRatio_x = rhx + res_obj.ReinforcementRatio_y = rhy + res_obj.ReinforcementRatio_z = rhz + res_obj.MohrCoulomb = moc + + res_obj.PS1Vector = ps1v + res_obj.PS2Vector = ps2v + res_obj.PS3Vector = ps3v + + FreeCAD.Console.PrintMessage( + 'Added principal stress and max shear values as well as' + 'reinforcment rations, Mohr Coloumb values.\n' + ) + return res_obj + + def compact_result(res_obj): ''' compacts result.Mesh and appropriate result.NodeNumbers @@ -373,7 +532,7 @@ def calculate_von_mises(stress_tensor): return np.sqrt(1.5 * np.linalg.norm(normal - pressure)**2 + 3.0 * np.linalg.norm(shear)**2) -def calculate_principal_stress(stress_tensor): +def calculate_principal_stress_std(stress_tensor): s11 = stress_tensor[0] # Sxx s22 = stress_tensor[1] # Syy s33 = stress_tensor[2] # Szz @@ -399,6 +558,223 @@ def calculate_principal_stress(stress_tensor): # https://forum.freecadweb.org/viewtopic.php?f=22&t=33911&start=10#p284229 +def calculate_principal_stress_reinforced(stress_tensor): + # + # HarryvL - calculate principal stress vectors and values + # - for total stresses use stress_tensor[0], stress_tensor[1], stress_tensor[2] + # on the diagonal of the stress tensor + # + # difference to the original method: + # https://forum.freecadweb.org/viewtopic.php?f=18&t=33106&start=90#p296539 + # + + s11 = stress_tensor[0] # Sxx + s22 = stress_tensor[1] # Syy + s33 = stress_tensor[2] # Szz + s12 = stress_tensor[3] # Sxy + s31 = stress_tensor[4] # Sxz + s23 = stress_tensor[5] # Syz + sigma = np.array([ + [s11, s12, s31], + [s12, s22, s23], + [s31, s23, s33] + ]) # https://forum.freecadweb.org/viewtopic.php?f=18&t=24637&start=10#p240408 + + eigenvalues, eigenvectors = np.linalg.eig(sigma) + + # + # HarryvL: suppress complex eigenvalue and vectors that may occur for + # near-zero (numerical noise) stress fields + # + + eigenvalues = eigenvalues.real + eigenvectors = eigenvectors.real + + eigenvectors[:, 0] = eigenvalues[0] * eigenvectors[:, 0] + eigenvectors[:, 1] = eigenvalues[1] * eigenvectors[:, 1] + eigenvectors[:, 2] = eigenvalues[2] * eigenvectors[:, 2] + + idx = eigenvalues.argsort()[::-1] + eigenvalues = eigenvalues[idx] + eigenvectors = eigenvectors[:, idx] + + maxshear = (eigenvalues[0] - eigenvalues[2]) / 2.0 + + return (eigenvalues[0], eigenvalues[1], eigenvalues[2], maxshear, + tuple([tuple(row) for row in eigenvectors.T])) + + +def calculate_rho(stress_tensor, fy): + + # + # HarryvL - Calculation of Reinforcement Ratios and + # Concrete Stresses according to http://heronjournal.nl/53-4/3.pdf + # - See post: + # https://forum.freecadweb.org/viewtopic.php?f=18&t=28821 + # fy: factored yield strength of reinforcement bars + # + + rmin = 1.0e9 + eqmin = 14 + + sxx = stress_tensor[0] + syy = stress_tensor[1] + szz = stress_tensor[2] + sxy = stress_tensor[3] + syz = stress_tensor[5] + sxz = stress_tensor[4] + + rhox = np.zeros(15) + rhoy = np.zeros(15) + rhoz = np.zeros(15) + + # i1=sxx+syy+szz NOT USED + # i2=sxx*syy+syy*szz+szz*sxx-sxy**2-sxz**2-syz**2 NOT USED + i3 = (sxx * syy * szz + 2 * sxy * sxz * syz - sxx * syz**2 + - syy * sxz**2 - szz * sxy**2) + + # Solution (5) + d = (sxx * syy - sxy**2) + if d != 0.: + rhoz[0] = i3 / d / fy + + # Solution (6) + d = (sxx * szz - sxz**2) + if d != 0.: + rhoy[1] = i3 / d / fy + + # Solution (7) + d = (syy * szz - syz**2) + if d != 0.: + rhox[2] = i3 / d / fy + + # Solution (9) + if sxx != 0.: + fc = sxz * sxy / sxx - syz + fxy = sxy**2 / sxx + fxz = sxz**2 / sxx + + # Solution (9+) + rhoy[3] = syy - fxy + fc + rhoy[3] /= fy + rhoz[3] = szz - fxz + fc + rhoz[3] /= fy + + # Solution (9-) + rhoy[4] = syy - fxy - fc + rhoy[4] /= fy + rhoz[4] = szz - fxz - fc + rhoz[4] /= fy + + # Solution (10) + if syy != 0.: + fc = syz * sxy / syy - sxz + fxy = sxy**2 / syy + fyz = syz**2 / syy + + # Solution (10+) + rhox[5] = sxx - fxy + fc + rhox[5] /= fy + rhoz[5] = szz - fyz + fc + rhoz[5] /= fy + + # Solution (10-)vm + rhox[6] = sxx - fxy - fc + + rhox[6] /= fy + rhoz[6] = szz - fyz - fc + rhoz[6] /= fy + + # Solution (11) + if szz != 0.: + fc = sxz * syz / szz - sxy + fxz = sxz**2 / szz + fyz = syz**2 / szz + + # Solution (11+) + rhox[7] = sxx - fxz + fc + rhox[7] /= fy + rhoy[7] = syy - fyz + fc + rhoy[7] /= fy + + # Solution (11-) + rhox[8] = sxx - fxz - fc + rhox[8] /= fy + rhoy[8] = syy - fyz - fc + rhoy[8] /= fy + + # Solution (13) + rhox[9] = (sxx + sxy + sxz) / fy + rhoy[9] = (syy + sxy + syz) / fy + rhoz[9] = (szz + sxz + syz) / fy + + # Solution (14) + rhox[10] = (sxx + sxy - sxz) / fy + rhoy[10] = (syy + sxy - syz) / fy + rhoz[10] = (szz - sxz - syz) / fy + + # Solution (15) + rhox[11] = (sxx - sxy - sxz) / fy + rhoy[11] = (syy - sxy + syz) / fy + rhoz[11] = (szz - sxz + syz) / fy + + # Solution (16) + rhox[12] = (sxx - sxy + sxz) / fy + rhoy[12] = (syy - sxy - syz) / fy + rhoz[12] = (szz + sxz - syz) / fy + + # Solution (17) + if syz != 0.: + rhox[13] = (sxx - sxy * sxz / syz) / fy + if sxz != 0.: + rhoy[13] = (syy - sxy * syz / sxz) / fy + if sxy != 0.: + rhoz[13] = (szz - sxz * syz / sxy) / fy + + for ir in range(0, rhox.size): + + if rhox[ir] >= -1.e-10 and rhoy[ir] >= -1.e-10 and rhoz[ir] > -1.e-10: + + # Concrete Stresses + scxx = sxx - rhox[ir] * fy + scyy = syy - rhoy[ir] * fy + sczz = szz - rhoz[ir] * fy + ic1 = (scxx + scyy + sczz) + ic2 = (scxx * scyy + scyy * sczz + sczz * scxx - sxy**2 + - sxz**2 - syz**2) + ic3 = (scxx * scyy * sczz + 2 * sxy * sxz * syz - scxx * syz**2 + - scyy * sxz**2 - sczz * sxy**2) + + if ic1 <= 1.e-6 and ic2 >= -1.e-6 and ic3 <= 1.0e-6: + + rsum = rhox[ir] + rhoy[ir] + rhoz[ir] + + if rsum < rmin and rsum > 0.: + rmin = rsum + eqmin = ir + + return rhox[eqmin], rhoy[eqmin], rhoz[eqmin] + + +def calculate_mohr_coulomb(prin1, prin3, phi, fck): + # + # HarryvL - Calculation of Mohr Coulomb yield criterion to judge + # concrete curshing and shear failure + # phi: angle of internal friction + # fck: factored compressive strength of the matrix material (usually concrete) + # + + coh = fck * (1 - np.sin(phi)) / 2 / np.cos(phi) + + mc_stress = ((prin1 - prin3) + (prin1 + prin3) * np.sin(phi) + - 2. * coh * np.cos(phi)) + + if mc_stress < 0.: + mc_stress = 0. + + return mc_stress + + def calculate_disp_abs(displacements): # see https://forum.freecadweb.org/viewtopic.php?f=18&t=33106&start=100#p296657 return [np.linalg.norm(nd) for nd in displacements] diff --git a/src/Mod/Fem/femsolver/calculix/tasks.py b/src/Mod/Fem/femsolver/calculix/tasks.py index 8dbd07c246..5d1d02c305 100644 --- a/src/Mod/Fem/femsolver/calculix/tasks.py +++ b/src/Mod/Fem/femsolver/calculix/tasks.py @@ -32,8 +32,6 @@ import subprocess import os.path import FreeCAD -if FreeCAD.GuiUp: - from PySide import QtGui import femtools.femutils as femutils import feminout.importCcxFrdResults as importCcxFrdResults import feminout.importCcxDatResults as importCcxDatResults @@ -42,6 +40,9 @@ from .. import run from .. import settings from . import writer +if FreeCAD.GuiUp: + from PySide import QtGui + _inputFileName = None @@ -184,9 +185,14 @@ class _Container(object): # get member # materials - self.materials_linear = self.get_several_member( + std_mats = self.get_several_member( 'Fem::Material' ) + rei_mats = self.get_several_member( + 'Fem::MaterialReinforced' + ) + self.materials_linear = std_mats + rei_mats + self.materials_nonlinear = self.get_several_member( 'Fem::MaterialMechanicalNonlinear' ) diff --git a/src/Mod/Fem/femsolver/z88/tasks.py b/src/Mod/Fem/femsolver/z88/tasks.py index 45c50f9d36..e067861016 100644 --- a/src/Mod/Fem/femsolver/z88/tasks.py +++ b/src/Mod/Fem/femsolver/z88/tasks.py @@ -31,8 +31,6 @@ import subprocess import os.path import FreeCAD -if FreeCAD.GuiUp: - from PySide import QtGui import femtools.femutils as femutils import feminout.importZ88O2Results as importZ88O2Results @@ -40,6 +38,9 @@ from .. import run from .. import settings from . import writer +if FreeCAD.GuiUp: + from PySide import QtGui + class Check(run.Check): diff --git a/src/Mod/Fem/femtest/testcommon.py b/src/Mod/Fem/femtest/testcommon.py index ab99160aa1..5f1aab9fb4 100644 --- a/src/Mod/Fem/femtest/testcommon.py +++ b/src/Mod/Fem/femtest/testcommon.py @@ -29,8 +29,6 @@ import unittest from . import utilstest as testtools from .utilstest import fcc_print -from os.path import join - class TestFemCommon(unittest.TestCase): fcc_print('import TestFemCommon') @@ -59,9 +57,9 @@ class TestFemCommon(unittest.TestCase): slab.Width = 500.00 cf = ObjectsFem.makeConstraintFixed(doc) ref_eles = [] - # FreeCAD list property seam not to support append, - # thus we need some workaround - # which is on many elements even much faster + # FreeCAD list property doesn't seem to support append, + # thus we need a workaround + # which on many elements is even much faster for i, face in enumerate(slab.Shape.Edges): ref_eles.append("Edge%d" % (i + 1)) cf.References = [(slab, ref_eles)] @@ -77,11 +75,11 @@ class TestFemCommon(unittest.TestCase): def test_pyimport_all_FEM_modules( self ): - # we're going to try to import all python modules from FreeCAD Fem + # we're going to try to import all python modules from FreeCAD FEM pymodules = [] - # collect all Python modules in Fem - pymodules += testtools.collect_python_modules('') # Fem main dir + # collect all Python modules in FEM + pymodules += testtools.collect_python_modules('') # FEM main dir pymodules += testtools.collect_python_modules('femexamples') pymodules += testtools.collect_python_modules('feminout') pymodules += testtools.collect_python_modules('femmesh') @@ -90,8 +88,8 @@ class TestFemCommon(unittest.TestCase): pymodules += testtools.collect_python_modules('femtest') pymodules += testtools.collect_python_modules('femtools') pymodules += testtools.collect_python_modules('femsolver') - # TODO test with join on windows, the use of os.path.join - # in following code seams to make problems on windws os + # TODO test with join on Windows, the use of os.path.join + # in the following code seems to create problems on Windows OS pymodules += testtools.collect_python_modules('femsolver/elmer') pymodules += testtools.collect_python_modules('femsolver/elmer/equations') pymodules += testtools.collect_python_modules('femsolver/z88') diff --git a/src/Mod/Fem/femtest/testobject.py b/src/Mod/Fem/femtest/testobject.py index a56b076a07..4adb6e968b 100644 --- a/src/Mod/Fem/femtest/testobject.py +++ b/src/Mod/Fem/femtest/testobject.py @@ -82,6 +82,7 @@ class TestObjectCreate(unittest.TestCase): analysis.addObject(ObjectsFem.makeMaterialFluid(doc)) mat = analysis.addObject(ObjectsFem.makeMaterialSolid(doc))[0] analysis.addObject(ObjectsFem.makeMaterialMechanicalNonlinear(doc, mat)) + analysis.addObject(ObjectsFem.makeMaterialReinforced(doc)) msh = analysis.addObject(ObjectsFem.makeMeshGmsh(doc))[0] analysis.addObject(ObjectsFem.makeMeshBoundaryLayer(doc, msh)) @@ -266,6 +267,10 @@ class TestObjectType(unittest.TestCase): 'Fem::MaterialMechanicalNonlinear', type_of_obj(ObjectsFem.makeMaterialMechanicalNonlinear(doc, materialsolid)) ) + self.assertEqual( + 'Fem::MaterialReinforced', + type_of_obj(ObjectsFem.makeMaterialReinforced(doc)) + ) mesh = ObjectsFem.makeMeshGmsh(doc) self.assertEqual( 'Fem::FemMeshGmsh', @@ -330,7 +335,7 @@ class TestObjectType(unittest.TestCase): 'Fem::FemEquationElmerHeat', type_of_obj(ObjectsFem.makeEquationHeat(doc, solverelmer)) ) - # is = 43 (just copy in empty file to test) + # is = 44 tests (just copy in empty file to test) # TODO: vtk post objs # TODO: use different type for fluid and solid material @@ -450,6 +455,10 @@ class TestObjectType(unittest.TestCase): ObjectsFem.makeMaterialMechanicalNonlinear(doc, materialsolid), 'Fem::MaterialMechanicalNonlinear' )) + self.assertTrue(is_of_type( + ObjectsFem.makeMaterialReinforced(doc), + 'Fem::MaterialReinforced' + )) mesh = ObjectsFem.makeMeshGmsh(doc) self.assertTrue(is_of_type( mesh, @@ -516,7 +525,7 @@ class TestObjectType(unittest.TestCase): ObjectsFem.makeEquationHeat(doc, solverelmer), 'Fem::FemEquationElmerHeat' )) - # is = 43 (just copy in empty file to test) + # is = 44 tests (just copy in empty file to test) # ******************************************************************************************** def test_femobjects_derivedfromfem( @@ -899,6 +908,20 @@ class TestObjectType(unittest.TestCase): 'Fem::MaterialMechanicalNonlinear' )) + # MaterialReinforced + self.assertTrue(is_derived_from( + ObjectsFem.makeMaterialReinforced(doc), + 'App::DocumentObject' + )) + self.assertTrue(is_derived_from( + ObjectsFem.makeMaterialReinforced(doc), + 'App::MaterialObjectPython' + )) + self.assertTrue(is_derived_from( + ObjectsFem.makeMaterialReinforced(doc), + 'Fem::MaterialReinforced' + )) + # FemMeshGmsh self.assertTrue(is_derived_from( mesh, 'App::DocumentObject' @@ -1270,6 +1293,11 @@ class TestObjectType(unittest.TestCase): materialsolid ).isDerivedFrom('Fem::FeaturePython') ) + self.assertTrue( + ObjectsFem.makeMaterialReinforced( + doc + ).isDerivedFrom('App::MaterialObjectPython') + ) mesh = ObjectsFem.makeMeshGmsh(doc) self.assertTrue( mesh.isDerivedFrom('Fem::FemMeshObjectPython') @@ -1356,7 +1384,7 @@ class TestObjectType(unittest.TestCase): solverelmer ).isDerivedFrom('App::FeaturePython') ) - # is = 43 (just copy in empty file to test) + # is = 44 tests (just copy in empty file to test) # ******************************************************************************************** def tearDown( diff --git a/src/Mod/Fem/femtest/testresult.py b/src/Mod/Fem/femtest/testresult.py index 2d75557422..d03fb24588 100644 --- a/src/Mod/Fem/femtest/testresult.py +++ b/src/Mod/Fem/femtest/testresult.py @@ -313,11 +313,11 @@ class TestResult(unittest.TestCase): ) # ******************************************************************************************** - def test_stress_principal( + def test_stress_principal_std( self ): expected_principal = (-178.0076, -194.0749, -468.9075, 145.4499) - from femresult.resulttools import calculate_principal_stress as pr + from femresult.resulttools import calculate_principal_stress_std as pr prin = pr(self.get_stress_values()) rounded_prin = ( round(prin[0], 4), @@ -332,6 +332,102 @@ class TestResult(unittest.TestCase): "Calculated principal stresses are not the expected values." ) + # ******************************************************************************************** + def test_stress_principal_reinforced( + self + ): + expected_principal = (-178.0076, -194.0749, -468.9075, 145.4499) + from femresult.resulttools import calculate_principal_stress_reinforced as prrc + prin = prrc(self.get_stress_values()) + rounded_prin = ( + round(prin[0], 4), + round(prin[1], 4), + round(prin[2], 4), + round(prin[3], 4)) + # fcc_print(rounded_prin) + self.assertEqual( + rounded_prin, + expected_principal, + "Calculated principal reinforced stresses are not the expected values." + ) + + # ******************************************************************************************** + def test_rho( + self + ): + data = ( + ( + # Case1: Governing Eq.14 + (2.000, -2.000, 5.000, 6.000, -4.000, 2.000), + (0.02400, 0.00400, 0.01400) + ), + ( + # Case2: Governing Eq.10+ + (-3.000, -7.000, 0.000, 6.000, -4.000, 2.000), + (0.00886, 0.00000, 0.00571), + ), + ( + # Case3: Governing Eq.5 + (-1.000, -7.000, 10.000, 0.000, 0.000, 5.000), + (0.00000, 0.00000, 0.02714) + ), + ( + # Case4: Governing Eq.13 + (3.000, 0.000, 10.000, 0.000, 5.000, 0.000), + (0.01600, 0.00000, 0.03000) + ), + ( + # Case5: Governing Eq.11- + (10.000, 7.000, -3.000, 3.000, 1.000, -2.000), + (0.02533, 0.02133, 0.00000) + ), + ( + # Case6: Governing Eq.14 + (4.000, -7.000, 3.000, 7.000, 0.000, -5.000), + (0.02200, 0.01000, 0.01600) + ), + ( + # Case7: Governing Eq.14 + (8.000, -14.000, 6.000, 14.000, 0.000, -10.000), + (0.04400, 0.02000, 0.03200) + ), + ( + # Case8: Governing Eq.17 + (1.000, 0.000, 3.000, 10.000, -8.000, 7.000), + (0.02486, 0.01750, 0.01720) + ), + ( + # Case9: Governing Eq.13 + (0.000, 0.000, 0.000, 10.000, 8.000, 7.000), + (0.03600, 0.03400, 0.03000) + ), + ( + # Case10: Governing Eq.13 + (15.000, 0.000, 0.000, 0.000, 0.000, 0.000), + (0.03000, 0.00000, 0.00000) + ), + ( + # Case11: Governing Eq.13 + (0.000, 0.000, 0.000, 5.000, 0.000, 0.000), + (0.01000, 0.01000, 0.00000) + ) + ) + + from femresult.resulttools import calculate_rho as calrho + for i, case in enumerate(data): + res = calrho(case[0], 500) + rhores = ( + round(res[0], 5), + round(res[1], 5), + round(res[2], 5) + ) + # fcc_print('Case{}: {}'.format(i + 1 , rhores)) + self.assertEqual( + rhores, case[1], + "Calculated rho are not the expected Case{}." + .format(i + 1) + ) + # ******************************************************************************************** def test_disp_abs( self diff --git a/src/Mod/Fem/femtools/ccxtools.py b/src/Mod/Fem/femtools/ccxtools.py index 5f26c6d440..48e6910829 100644 --- a/src/Mod/Fem/femtools/ccxtools.py +++ b/src/Mod/Fem/femtools/ccxtools.py @@ -223,7 +223,10 @@ class FemToolsCcx(QtCore.QRunnable, QtCore.QObject): ## @var materials_linear # list of linear materials from the analysis. Updated with update_objects - self.materials_linear = self._get_several_member('Fem::Material') + self.materials_linear = ( + self._get_several_member('Fem::Material') + + self._get_several_member('Fem::MaterialReinforced') + ) ## @var materials_nonlinear # list of nonlinear materials from the analysis. Updated with update_objects self.materials_nonlinear = self._get_several_member('Fem::MaterialMechanicalNonlinear') @@ -408,6 +411,47 @@ class FemToolsCcx(QtCore.QRunnable, QtCore.QObject): "Thermomechanical analysis: No SpecificHeat " "defined for at least one material.\n" # allowed to be 0.0 (in ccx) ) + if femutils.is_of_type(mat_obj, 'Fem::MaterialReinforced'): + # additional tests for reinforced materials, + # they are needed for result calculation not for ccx analysis + mat_map_m = mat_obj.Material + if 'AngleOfFriction' in mat_map_m: + # print(Units.Quantity(mat_map_m['AngleOfFriction']).Value) + if not Units.Quantity(mat_map_m['AngleOfFriction']).Value: + message += ( + "Value of AngleOfFriction is set to 0.0 " + "for the matrix of a reinforced material.\n" + ) + else: + message += ( + "No AngleOfFriction defined for the matrix " + "of at least one reinforced material.\n" + ) + if 'CompressiveStrength' in mat_map_m: + # print(Units.Quantity(mat_map_m['CompressiveStrength']).Value) + if not Units.Quantity(mat_map_m['CompressiveStrength']).Value: + message += ( + "Value of CompressiveStrength is set to 0.0 " + "for the matrix of a reinforced material.\n" + ) + else: + message += ( + "No CompressiveStrength defined for the matrinx " + "of at least one reinforced material.\n" + ) + mat_map_r = mat_obj.Reinforcement + if 'YieldStrength' in mat_map_r: + # print(Units.Quantity(mat_map_r['YieldStrength']).Value) + if not Units.Quantity(mat_map_r['YieldStrength']).Value: + message += ( + "Value of YieldStrength is set to 0.0 " + "for the reinforcement of a reinforced material.\n" + ) + else: + message += ( + "No YieldStrength defined for the reinforcement " + "of at least one reinforced material.\n" + ) if len(self.materials_linear) == 1: mobj = self.materials_linear[0]['Object'] if hasattr(mobj, 'References') and mobj.References: diff --git a/src/Mod/Material/StandardMaterial/Concrete-EN-C35_45.FCMat b/src/Mod/Material/StandardMaterial/Concrete-EN-C35_45.FCMat new file mode 100644 index 0000000000..deede90fe5 --- /dev/null +++ b/src/Mod/Material/StandardMaterial/Concrete-EN-C35_45.FCMat @@ -0,0 +1,20 @@ +; Concrete-EN-C35_45 +; (c) 2019 Bernd Hahnebach (CC-BY 3.0) +; information about the content of such cards can be found on the wiki: +; https://www.freecadweb.org/wiki/Material +; file created by FreeCAD 0.19.16886 (Git) + +[General] +Name = Concrete-EN-C35/45 +Description = Concrete matrix for reinforcement material examples, 0.6 x 0.75 x 35 MPa = 15.75 MPa (https://forum.freecadweb.org/viewtopic.php?f=18&t=33106&start=200#p311075) +Father = Aggregate + +[Mechanical] +AngleOfFriction = 30 deg +CompressiveStrength = 15.75 MPa +Density = 2500 kg/m^3 +PoissonRatio = 0.17 +YoungsModulus = 32000 MPa + +[Cost] +ProductURL = https://en.wikipedia.org/wiki/Concrete diff --git a/src/Mod/Material/StandardMaterial/Reinforcement-FIB-B500.FCMat b/src/Mod/Material/StandardMaterial/Reinforcement-FIB-B500.FCMat new file mode 100644 index 0000000000..7898ffbf46 --- /dev/null +++ b/src/Mod/Material/StandardMaterial/Reinforcement-FIB-B500.FCMat @@ -0,0 +1,15 @@ +; Reinforcement-FIB-B500 +; (c) 2019 Bernd Hahnebach (CC-BY 3.0) +; information about the content of such cards can be found on the wiki: +; https://www.freecadweb.org/wiki/Material +; file created by FreeCAD 0.19.16886 (Git) + +[General] +Name = Reinforcement-Harry +Description = Reinforcement inside concrete for reinforcement material examples, from fib examples, 0.84 x 0.75 x 500 MPa = 315 MPa (https://forum.freecadweb.org/viewtopic.php?f=18&t=33106&start=200#p311705) + +[Mechanical] +Density = 7850 kg/m^3 +PoissonRatio = 0.3 +YieldStrength = 315 MPa +YoungsModulus = 210000 MPa diff --git a/src/Mod/Material/StandardMaterial/Tools/Materials.csv b/src/Mod/Material/StandardMaterial/Tools/Materials.csv index 944f2de6ce..0d99b073d1 100644 --- a/src/Mod/Material/StandardMaterial/Tools/Materials.csv +++ b/src/Mod/Material/StandardMaterial/Tools/Materials.csv @@ -6,6 +6,7 @@ AlMgSi1F31|AlMgSi1F31|AlMgSi1F31|||||Metal|Aluminium|Aluminium|3.2315.72|DIN 172 Aluminum-6061-T6|Aluminum 6061-T6|||Precipitation-hardened, Nonferrous Aluminum alloy|||Metal|Aluminium|Aluminium||||||||2700 kg/m^3||0.33|||310 MPa||69000 MPa||167 W/m/K|||||||||||||||||||||||||https://en.wikipedia.org/wiki/6061_aluminium_alloy|| AlZn4-5Mg1F35|AlZn4,5Mg1F35|AlZn4,5Mg1F35|||||Metal|Aluminium|Aluminium|3.4335.71|DIN 1725||||||2700 kg/m^3||0.3|27000 MPa|10|350 MPa|290 MPa|70000 MPa|||0.000023 m/m/K|||||||||||||||||||||||||| CalculiX-Steel|CalculiX-Steel|||Standard steel material for CalculiX sample calculations|||Metal||||||||||7900 kg/m^3||0.3|||||210000 MPa|590 J/kg/K|43 W/m/K|0.000012 m/m/K|||||||||||||||||||||||||| +Concrete-EN-C35_45|Concrete-EN-C35/45|||Concrete matrix for reinforcement material examples, 0.6 x 0.75 x 35 MPa = 15.75 MPa (https://forum.freecadweb.org/viewtopic.php?f=18&t=33106&start=200#p311075)|||Aggregate||||||||30 deg|15.75 MPa|2500 kg/m^3||0.17|||||32000 MPa|||||||||||||||||||||||||||https://en.wikipedia.org/wiki/Concrete|| Concrete-Generic|Concrete|||A standard C-25 construction concrete|||Aggregate|||||||Masterformat 03 33 13||25 MPa|2400 kg/m^3||0.17|||||32000 MPa|||||||||||||||||||||||||||https://en.wikipedia.org/wiki/Concrete|| Glass-E-GlassFibre|Glass-E|||Glass Fibre|||Glass|||||||||1080 MPa|2580 kg/m^3|||||3445 MPa|||||||||||||||||||||||||||||https://en.wikipedia.org/wiki/Glass_fiber|| Glass-Generic|Glass|||Generic soda-lime glass|Normalglas|Szkło sodowe|Glass||||||||||2520 kg/m^3||0.22|||||72000 MPa|||0.000009 m/m/K||||||||||||||||||||||||https://en.wikipedia.org/wiki/Soda-lime_glass|| @@ -17,6 +18,7 @@ PLA-Generic|PLA|||Polylactic acid or polylactide (PLA, Poly) is a biodegradable PP-Generic|Polypropylene||||||||||||http://www.matweb.com/search/DataSheet.aspx?MatGUID=a882a1c603374e278d062f106dfda95b|||10 MPa|916 kg/m^3||0.44|||79.7 MPa|32.8 MPa|1470 MPa|1920 J/kg/K|0.249 W/m/K|0.000123 m/m/K||||||||||||||||||||||||https://en.wikipedia.org/wiki/Polypropylene|| PTFE-Generic|PTFE||||||||||||http://www.matweb.com/search/datasheet.aspx?MatGUID=4d14eac958e5401a8fd152e1261b6843|||10.2 MPa|2070 kg/m^3||0.46|||34.6 MPa|20.5 MPa|564 MPa|1010 J/kg/K|0.263 W/m/K|0.000108 m/m/K||||||||||||||||||||||||https://en.wikipedia.org/wiki/Polytetrafluoroethylene|| PVC-Generic|PVC||||||||||||http://www.matweb.com/search/DataSheet.aspx?MatGUID=1f650966ec834bb8833dd4c6e3116079||||1380 kg/m^3||0.38|||50 MPa||2800 MPa|1000 J/kg/K|0.150 W/m/K|0.000075 m/m/K||||||||||||||||||||||||https://en.wikipedia.org/wiki/Polyvinyl_chloride|| +Reinforcement-FIB-B500|Reinforcement-Harry|||Reinforcement inside concrete for reinforcement material examples, from fib examples, 0.84 x 0.75 x 500 MPa = 315 MPa (https://forum.freecadweb.org/viewtopic.php?f=18&t=33106&start=200#p311705)|||||||||||||7850 kg/m^3||0.3||||315 MPa|210000 MPa||||||||||||||||||||||||||||| Steel-15CrNi6|15CrNi6|||High-strength carbon steel|höchste Beanspruchung, Achsen, Wellen, Ritzel, Nocken, Kegelräder|Wysokowytrzymała stal węglowa|Metal|Case-hardened steel|Einsatzstahl|1.5919|EN 10084||||||7800 kg/m^3||0.3|81000 MPa|9|1000 MPa|680 MPa|210000 MPa|||0.000011 m/m/K|||||||||||||||||||||||||| Steel-17CrNiMo6|17CrNiMo6|17CrNiMo6|||höchste Beanspruchung, Wellen, Zahnräder||Metal|Case-hardened steel|Einsatzstahl|1.6587|EN 10084||||||7800 kg/m^3||0.3|81000 MPa|8|1150 MPa|830 MPa|210000 MPa|||0.000011 m/m/K|||||||||||||||||||||||||| Steel-1C22|1C22|C22||Case hardened alloy steel|kleiner Vergütungsdurchmesser, gut schweißbar|Stal stopowa do nawęglania|Metal|Heat-treatable steel|Vergütungsstahl|1.0402|EN 10083-1||||||7800 kg/m^3||0.3|81000 MPa|27|400 MPa|230 MPa|210000 MPa|||0.000011 m/m/K|||||||||||||||||||||||||| diff --git a/src/Mod/Material/StandardMaterial/Tools/Materials.ods b/src/Mod/Material/StandardMaterial/Tools/Materials.ods index df9f0fa9b4..3972e8edd1 100644 Binary files a/src/Mod/Material/StandardMaterial/Tools/Materials.ods and b/src/Mod/Material/StandardMaterial/Tools/Materials.ods differ diff --git a/src/Mod/Material/StandardMaterial/Tools/make_ods.sh b/src/Mod/Material/StandardMaterial/Tools/make_ods.sh index 6ce269f53a..f64e4a99cd 100755 --- a/src/Mod/Material/StandardMaterial/Tools/make_ods.sh +++ b/src/Mod/Material/StandardMaterial/Tools/make_ods.sh @@ -4,7 +4,7 @@ FCMAT_DIR="../" MATERIALS_FILE=Materials -# Remove exisitng $MATERIALS_FILE.csv +# Remove existing $MATERIALS_FILE.csv if [ -f "$MATERIALS_FILE.csv" ] then rm $MATERIALS_FILE.csv diff --git a/src/Mod/Material/importFCMat.py b/src/Mod/Material/importFCMat.py index 76b980f85f..91a6161ac9 100644 --- a/src/Mod/Material/importFCMat.py +++ b/src/Mod/Material/importFCMat.py @@ -89,7 +89,7 @@ def decode(name): # in the module Material.py is another implementation of reading and writing FCMat files # this implementation uses the ConfigParser module # in ViewProviderFemMaterial in add_cards_from_a_dir() the parser from Material.py is used -# since this mixture seams to be there for ages it should not be changed for 0.18 +# since this mixture seems to have be there for ages it should not be changed for 0.18 # TODO: get rid of this mixture in FreeCAD 0.19 # Metainformation @@ -102,7 +102,7 @@ def decode(name): def read(filename): "reads a FCMat file and returns a dictionary from it" # the reader should return a dictionary in any case even if the file - # has problems, a empty dict shuld be returned un such case + # has problems, an empty dict should be returned in such case if isinstance(filename, unicode): if sys.version_info.major < 3: filename = filename.encode(sys.getfilesystemencoding()) @@ -117,7 +117,7 @@ def read(filename): for ln, line in enumerate(f): ln += 1 # enumerate starts with 0, but we would like to have the real line number if line.startswith('#'): - # a # is assumed to be a comment which is ignored + # a '#' is assumed to be a comment which is ignored continue # the use of line number is not smart for a data model # a wrong user edit could break the file @@ -141,7 +141,7 @@ def read(filename): # [ is a Section if line[0] not in ";[": # split once on first occurrence - # a link could contain a = and thus would be split + # a link could contain a '=' and thus would be split k = line.split("=", 1) if len(k) == 2: v = k[1].strip() @@ -223,7 +223,7 @@ def write(filename, dictionary, write_group_section=True): f.write("\n[" + s["keyname"] + "]\n") for k, i in s.items(): if (k != "keyname" and i != '') or k == "Name": - # use only keys which are not empty and the name even if empty + # use only keys which are not empty and the name, even if empty if sys.version_info.major >= 3: f.write(k + " = " + i + "\n") else: diff --git a/src/Mod/Material/materialtools/cardutils.py b/src/Mod/Material/materialtools/cardutils.py index 9487416f19..2586e8ac2e 100644 --- a/src/Mod/Material/materialtools/cardutils.py +++ b/src/Mod/Material/materialtools/cardutils.py @@ -607,7 +607,8 @@ def output_value_unit_info(param, value): def check_mat_units(mat): known_quantities = get_known_material_quantity_parameter() - # check if the param is a Quantity according card template, than chaeck unit + # check if the param is a Quantity according to the card template + # then check unit # print(known_quantities) units_ok = True for param, value in mat.items(): diff --git a/src/Mod/Part/App/Attacher.cpp b/src/Mod/Part/App/Attacher.cpp index eef3f8f556..d26cd243b4 100644 --- a/src/Mod/Part/App/Attacher.cpp +++ b/src/Mod/Part/App/Attacher.cpp @@ -48,6 +48,7 @@ # include # include # include +# include # include # include # include @@ -2061,17 +2062,8 @@ Base::Placement AttachEnginePoint::calculateAttachedPlacement(Base::Placement or throw Base::ValueError("Null shape in AttachEnginePoint::calculateAttachedPlacement()!"); if (shapes[1]->IsNull()) throw Base::ValueError("Null shape in AttachEnginePoint::calculateAttachedPlacement()!"); - BRepExtrema_DistShapeShape distancer (*(shapes[0]), *(shapes[1])); - if (!distancer.IsDone()) - throw Base::ValueError("AttachEnginePoint::calculateAttachedPlacement: proximity calculation failed."); - if (distancer.NbSolution()>1) - Base::Console().Warning("AttachEnginePoint::calculateAttachedPlacement: proximity calculation gave %i solutions, ambiguous.\n",int(distancer.NbSolution())); - gp_Pnt p1 = distancer.PointOnShape1(1); - gp_Pnt p2 = distancer.PointOnShape2(1); - if (mmode == mm0ProximityPoint1) - BasePoint = p1; - else - BasePoint = p2; + + BasePoint = getProximityPoint(mmode, *(shapes[0]), *(shapes[1])); }break; case mm0CenterOfMass:{ GProp_GProps gpr = AttachEngine::getInertialPropsOfShape(shapes); @@ -2093,3 +2085,61 @@ Base::Placement AttachEnginePoint::calculateAttachedPlacement(Base::Placement or return plm; } +gp_Pnt AttachEnginePoint::getProximityPoint(eMapMode mmode, const TopoDS_Shape& s1, const TopoDS_Shape& s2) const +{ + // #0003921: Crash when opening document with datum point intersecting line and plane + // + // BRepExtrema_DistanceSS is used inside BRepExtrema_DistShapeShape and can cause + // a crash if the input shape is an unlimited face. + // So, when the input is a face and an edge then before checking for minimum distances + // try to determine intersection points. + try { + TopoDS_Shape face, edge; + if (s1.ShapeType() == TopAbs_FACE && + s2.ShapeType() == TopAbs_EDGE) { + face = s1; + edge = s2; + } + else if (s1.ShapeType() == TopAbs_EDGE && + s2.ShapeType() == TopAbs_FACE) { + edge = s1; + face = s2; + } + + // edge and face + if (!edge.IsNull() && !face.IsNull()) { + BRepAdaptor_Curve crv(TopoDS::Edge(edge)); + BRepIntCurveSurface_Inter intCS; + intCS.Init(face, crv.Curve(), Precision::Confusion()); + std::vector points; + for (; intCS.More(); intCS.Next()) { + gp_Pnt pnt = intCS.Pnt(); + points.push_back(pnt); + } + + if (points.size() > 1) + Base::Console().Warning("AttachEnginePoint::calculateAttachedPlacement: proximity calculation gave %d solutions, ambiguous.\n", int(points.size())); + + // if an intersection is found return the first hit + // otherwise continue with BRepExtrema_DistShapeShape + if (!points.empty()) + return points.front(); + } + } + catch (Standard_Failure) { + // ignore + } + + BRepExtrema_DistShapeShape distancer (s1, s2); + if (!distancer.IsDone()) + throw Base::ValueError("AttachEnginePoint::calculateAttachedPlacement: proximity calculation failed."); + if (distancer.NbSolution() > 1) + Base::Console().Warning("AttachEnginePoint::calculateAttachedPlacement: proximity calculation gave %i solutions, ambiguous.\n",int(distancer.NbSolution())); + + gp_Pnt p1 = distancer.PointOnShape1(1); + gp_Pnt p2 = distancer.PointOnShape2(1); + if (mmode == mm0ProximityPoint1) + return p1; + else + return p2; +} diff --git a/src/Mod/Part/App/Attacher.h b/src/Mod/Part/App/Attacher.h index 114a067e8b..6341cc1fe1 100644 --- a/src/Mod/Part/App/Attacher.h +++ b/src/Mod/Part/App/Attacher.h @@ -456,6 +456,9 @@ public: AttachEnginePoint(); virtual AttachEnginePoint* copy() const; virtual Base::Placement calculateAttachedPlacement(Base::Placement origPlacement) const; + +private: + gp_Pnt getProximityPoint(eMapMode mode, const TopoDS_Shape& s1, const TopoDS_Shape& s2) const; }; //==================================================================== diff --git a/src/Mod/Path/PathScripts/PathToolControllerGui.py b/src/Mod/Path/PathScripts/PathToolControllerGui.py index 7ec5788765..8b03c2aca8 100644 --- a/src/Mod/Path/PathScripts/PathToolControllerGui.py +++ b/src/Mod/Path/PathScripts/PathToolControllerGui.py @@ -34,7 +34,7 @@ import PathScripts.PathUtil as PathUtil from PySide import QtCore, QtGui -# Qt tanslation handling +# Qt translation handling def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) diff --git a/src/Mod/Path/PathScripts/post/grbl_post.py b/src/Mod/Path/PathScripts/post/grbl_post.py index e369887ebe..0beac78a58 100755 --- a/src/Mod/Path/PathScripts/post/grbl_post.py +++ b/src/Mod/Path/PathScripts/post/grbl_post.py @@ -46,10 +46,10 @@ grbl_post.export(object, "/path/to/file.ncc") # Default values for command line arguments: OUTPUT_COMMENTS = True # default output of comments in output gCode file OUTPUT_HEADER = True # default output header in output gCode file -OUTPUT_LINE_NUMBERS = False # default does'nt utput lines numbers in output gCode file +OUTPUT_LINE_NUMBERS = False # default doesn't output line numbers in output gCode file SHOW_EDITOR = True # default show the resulting file dialog output in GUI PRECISION = 3 # Default precision for metric (see http://linuxcnc.org/docs/2.7/html/gcode/overview.html#_g_code_best_practices) -TRANSLATE_DRILL_CYCLES = False # If true, G81, G82 & G83 are translated in G0/G1 moves +TRANSLATE_DRILL_CYCLES = False # If true, G81, G82 & G83 are translated in G0/G1 moves PREAMBLE = '''G17 G90 ''' # default preamble text will appear at the beginning of the gCode output file. POSTAMBLE = '''M5 diff --git a/src/Mod/Spreadsheet/App/Spreadsheet_legacy.py b/src/Mod/Spreadsheet/App/Spreadsheet_legacy.py index 1bd8b8073d..a4db993f28 100644 --- a/src/Mod/Spreadsheet/App/Spreadsheet_legacy.py +++ b/src/Mod/Spreadsheet/App/Spreadsheet_legacy.py @@ -45,7 +45,7 @@ class MathParser: if self.vars.get(var) != None: raise RuntimeError("Cannot redefine the value of " + var) self.vars[var] = vars[var] - + def getValue(self): value = self.parseExpression() self.skipWhitespace() @@ -56,23 +56,23 @@ class MathParser: "' at index " + str(self.index)) return value - + def peek(self): return self.string[self.index:self.index + 1] - + def hasNext(self): return self.index < len(self.string) - + def skipWhitespace(self): while self.hasNext(): if self.peek() in ' \t\n\r': self.index += 1 else: return - + def parseExpression(self): return self.parseAddition() - + def parseAddition(self): values = [self.parseMultiplication()] while True: @@ -87,7 +87,7 @@ class MathParser: else: break return sum(values) - + def parseMultiplication(self): values = [self.parseParenthesis()] while True: @@ -112,7 +112,7 @@ class MathParser: for factor in values: value *= factor return value - + def parseParenthesis(self): self.skipWhitespace() char = self.peek() @@ -128,7 +128,7 @@ class MathParser: return value else: return self.parseNegative() - + def parseNegative(self): self.skipWhitespace() char = self.peek() @@ -137,7 +137,7 @@ class MathParser: return -1 * self.parseParenthesis() else: return self.parseValue() - + def parseValue(self): self.skipWhitespace() char = self.peek() @@ -145,7 +145,7 @@ class MathParser: return self.parseNumber() else: return self.parseVariable() - + def parseVariable(self): self.skipWhitespace() var = '' @@ -156,7 +156,7 @@ class MathParser: self.index += 1 else: break - + value = self.vars.get(var, None) if value == None: raise ValueError( @@ -164,15 +164,15 @@ class MathParser: var + "'") return float(value) - + def parseNumber(self): self.skipWhitespace() strValue = '' decimal_found = False char = '' - + while self.hasNext(): - char = self.peek() + char = self.peek() if char == '.': if decimal_found: raise SyntaxError( @@ -186,7 +186,7 @@ class MathParser: else: break self.index += 1 - + if len(strValue) == 0: if char == '': raise SyntaxError("Unexpected end found") @@ -197,21 +197,21 @@ class MathParser: " but instead I found a '" + char + "'. What's up with that?") - + return float(strValue) class Spreadsheet: """An object representing a spreadsheet. Can be used as a FreeCAD object or as a standalone python object. Cells of the spreadsheet can be got/set as arguments, as: - + myspreadsheet = Spreadsheet() myspreadsheet.a1 = 54 print(myspreadsheet.a1) myspreadsheet.a2 = "My text" myspreadsheet.b1 = "=a1*3" print(myspreadsheet.b1) - + The cell names are case-insensitive (a1 = A1) """ @@ -269,13 +269,13 @@ class Spreadsheet: return self._cells[key] else: return self.__dict__.__getitem__(key) - + def __setitem__(self, key, value): __setattr__(self, key, value) def __getitem__(self, key): return __getattr__(self, key) - + def __getstate__(self): self._cells["Type"] = self.Type if hasattr(self,"Object"): @@ -347,7 +347,7 @@ class Spreadsheet: return True else: return False - + def isNumeric(self,key): "isNumeric(cell): returns True if the given cell returns a number" key = key.lower() @@ -376,7 +376,7 @@ class Spreadsheet: else: if not nu: # forbidden to set items at row 0 - if v == "0": + if v == "0": return False if v.isalpha(): if not allowMoreThanOneLetter: @@ -394,7 +394,7 @@ class Spreadsheet: c = '' r = '' for ch in key: - if ch.isalpha(): + if ch.isalpha(): c += ch else: r += ch @@ -420,7 +420,7 @@ class Spreadsheet: if index in [c,r]: cells[k] = self._cells[k] return cells - + def evaluate(self,key): "evaluate(key): evaluates the given formula" key = key.lower() @@ -458,7 +458,7 @@ class Spreadsheet: co.Proxy.setCells(co,obj) elif Draft.getType(co) == "SpreadsheetPropertyController": co.Proxy.compute(co) - + def getControlledCells(self,obj): "returns a list of cells managed by controllers" cells = [] @@ -499,18 +499,18 @@ class ViewProviderSpreadsheet(object): self.editor = SpreadsheetView(vobj.Object) addSpreadsheetView(self.editor) return True - + def unsetEdit(self,vobj,mode=0): return False def doubleClicked(self,vobj): self.setEdit(vobj) - + def claimChildren(self): if hasattr(self,"Object"): if hasattr(self.Object,"Controllers"): return self.Object.Controllers - + def __getstate__(self): return None @@ -532,17 +532,17 @@ class SpreadsheetController: obj.FilterType = ["Object Type","Object Name"] obj.DataType = ["Get Property","Count"] obj.Direction = ["Horizontal","Vertical"] - + def execute(self,obj): pass - + def __getstate__(self): return self.Type def __setstate__(self,state): if state: self.Type = state - + def onChanged(self,obj,prop): if prop == "DataType": if obj.DataType == "Count": @@ -659,7 +659,7 @@ class SpreadsheetPropertyController: obj.addProperty("App::PropertyString","TargetProperty","Base","The property or constraint of the target object to control") obj.addProperty("App::PropertyString","Cell","Base","The cell that contains the value to apply to the property") obj.TargetType = ["Property","Constraint"] - + def execute(self,obj): pass @@ -708,14 +708,14 @@ class SpreadsheetPropertyController: except: if DEBUG: print("unable to set constraint ",obj.TargetProperty, " of object ",obj.TargetObject.Name, " to ",value) - + def __getstate__(self): return self.Type def __setstate__(self,state): if state: self.Type = state - + def onChanged(self,obj,prop): pass @@ -736,7 +736,7 @@ class SpreadsheetView(QtGui.QWidget): def __init__(self,spreadsheet=None): from DraftTools import translate QtGui.QWidget.__init__(self) - + self.setWindowTitle(str(translate("Spreadsheet","Spreadsheet"))) self.setObjectName("Spreadsheet viewer") self.verticalLayout = QtGui.QVBoxLayout(self) @@ -877,7 +877,7 @@ class SpreadsheetView(QtGui.QWidget): if content == None: content = "" self.lineEdit.setText(str(content)) - + def getEditLine(self): "called when something has been entered in the edit line" txt = str(self.lineEdit.text()) @@ -988,8 +988,8 @@ def makeSpreadsheet(): def makeSpreadsheetController(spreadsheet,cell=None,direction=None): - """makeSpreadsheetController(spreadsheet,[cell,direction]): adds a - controller to the given spreadsheet. Call can be a starting cell such as "A5", + """makeSpreadsheetController(spreadsheet,[cell,direction]): adds a + controller to the given spreadsheet. Call can be a starting cell such as "A5", and direction can be "Horizontal" or "Vertical".""" obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython","CellController") SpreadsheetController(obj) @@ -1006,7 +1006,7 @@ def makeSpreadsheetController(spreadsheet,cell=None,direction=None): def makeSpreadsheetPropertyController(spreadsheet,object=None,prop=None,cell=None): - """makeSpreadsheetPropertyController(spreadsheet,[object,prop,cell]): adds a + """makeSpreadsheetPropertyController(spreadsheet,[object,prop,cell]): adds a property controller, targeting the given object if any, to the given spreadsheet. You can give a property (such as "Length" or "Proxy.Length") and a cell address (such as "B6").""" @@ -1092,7 +1092,7 @@ def export(exportList,filename): return obj = exportList[0] if Draft.getType(obj) != "Spreadsheet": - print("Spreadhseet: The selected object is not a spreadsheet") + print("Spreadsheet: The selected object is not a spreadsheet") return if not obj.Proxy._cells: print("Spreadsheet: The selected spreadsheet contains no cell") diff --git a/vagrant/Xenial/reset b/vagrant/Xenial/reset index d4ca716316..f0600bc6e4 100755 --- a/vagrant/Xenial/reset +++ b/vagrant/Xenial/reset @@ -13,13 +13,13 @@ if [ "$(id -u)" != 0 ]; then fi echo "This script will permanently destroy and reset all state in snapd" -echo "You will loose all of your installed snaps" +echo "You will lose all of your installed snaps" echo echo "Type: DESTROY to remove all of your snap state" echo read consent -if [ "$consent" != "DESTROY" ]; then +if [ "$consent" != "DESTROY" ]; then echo "No consent, aborting" exit 0 fi