diff --git a/src/Mod/Fem/App/AppFem.cpp b/src/Mod/Fem/App/AppFem.cpp index 1de53aae3f..f70df02c00 100644 --- a/src/Mod/Fem/App/AppFem.cpp +++ b/src/Mod/Fem/App/AppFem.cpp @@ -136,6 +136,7 @@ PyMODINIT_FUNC initFem() Fem::FeaturePython ::init(); Fem::FemMesh ::init(); Fem::FemMeshObject ::init(); + Fem::FemMeshObjectPython ::init(); Fem::FemMeshShapeObject ::init(); Fem::FemMeshShapeNetgenObject ::init(); Fem::PropertyFemMesh ::init(); diff --git a/src/Mod/Fem/App/CMakeLists.txt b/src/Mod/Fem/App/CMakeLists.txt index 2c69918e83..f84b4a9966 100755 --- a/src/Mod/Fem/App/CMakeLists.txt +++ b/src/Mod/Fem/App/CMakeLists.txt @@ -68,23 +68,26 @@ SET(FemScripts_SRCS _CommandBeamSection.py _CommandControlSolver.py _CommandConstraintSelfWeight.py - _CommandMechanicalMaterial.py - _CommandShowResult.py _CommandMaterialMechanicalNonlinear.py - _CommandMeshFromShape.py + _CommandMechanicalMaterial.py + _CommandMeshGmshFromShape.py + _CommandMeshNetgenFromShape.py _CommandPurgeResults.py _CommandRunSolver.py _CommandShellThickness.py + _CommandShowResult.py _CommandSolverCalculix.py _CommandSolverZ88.py _FemBeamSection.py _FemConstraintSelfWeight.py _FemMaterialMechanicalNonlinear.py + _FemMeshGmsh.py _FemShellThickness.py _FemSolverCalculix.py _FemSolverZ88.py _MechanicalMaterial.py _TaskPanelFemBeamSection.py + _TaskPanelFemMeshGmsh.py _TaskPanelFemShellThickness.py _TaskPanelFemSolverCalculix.py _TaskPanelMechanicalMaterial.py @@ -92,6 +95,7 @@ SET(FemScripts_SRCS _ViewProviderFemBeamSection.py _ViewProviderFemConstraintSelfWeight.py _ViewProviderFemMaterialMechanicalNonlinear.py + _ViewProviderFemMeshGmsh.py _ViewProviderFemShellThickness.py _ViewProviderFemSolverCalculix.py _ViewProviderFemSolverZ88.py @@ -107,10 +111,12 @@ SET(FemScripts_SRCS FemBeamSection.py FemCommands.py FemConstraintSelfWeight.py + FemGmshTools.py FemInputWriter.py FemInputWriterCcx.py FemInputWriterZ88.py FemMaterialMechanicalNonlinear.py + FemMeshGmsh.py FemMeshTools.py FemShellThickness.py FemSolverCalculix.py @@ -123,6 +129,7 @@ SET(FemScripts_SRCS TestFem.py z88DispReader.py TaskPanelFemBeamSection.ui + TaskPanelFemMeshGmsh.ui TaskPanelFemShellThickness.ui TaskPanelFemSolverCalculix.ui TaskPanelMechanicalMaterial.ui diff --git a/src/Mod/Fem/App/FemMeshObject.cpp b/src/Mod/Fem/App/FemMeshObject.cpp index 768d388204..57a5ce7cda 100644 --- a/src/Mod/Fem/App/FemMeshObject.cpp +++ b/src/Mod/Fem/App/FemMeshObject.cpp @@ -29,6 +29,7 @@ #include "FemMeshObject.h" #include "FemMesh.h" #include +#include #include using namespace Fem; @@ -70,3 +71,25 @@ void FemMeshObject::onChanged(const Property* prop) } } + +// Python feature --------------------------------------------------------- + +namespace App { +/// @cond DOXERR +PROPERTY_SOURCE_TEMPLATE(Fem::FemMeshObjectPython, Fem::FemMeshObject) +template<> const char* Fem::FemMeshObjectPython::getViewProviderName(void) const { + return "FemGui::ViewProviderFemMeshPython"; +} + +template<> PyObject* Fem::FemMeshObjectPython::getPyObject(void) { + if (PythonObject.is(Py::_None())) { + // ref counter is set to 1 + PythonObject = Py::Object(new App::FeaturePythonPyT(this),true); + } + return Py::new_reference_to(PythonObject); +} + +// explicit template instantiation +template class AppFemExport FeaturePythonT; + +} diff --git a/src/Mod/Fem/App/FemMeshObject.h b/src/Mod/Fem/App/FemMeshObject.h index 0a774c0605..161b4d2f9c 100644 --- a/src/Mod/Fem/App/FemMeshObject.h +++ b/src/Mod/Fem/App/FemMeshObject.h @@ -24,6 +24,7 @@ #ifndef Fem_FemMeshObject_H #define Fem_FemMeshObject_H +#include #include #include #include @@ -60,6 +61,9 @@ protected: virtual void onChanged (const App::Property* prop); }; +typedef App::FeaturePythonT FemMeshObjectPython; + + } //namespace Fem diff --git a/src/Mod/Fem/App/FemSolverObject.cpp b/src/Mod/Fem/App/FemSolverObject.cpp index ae7b89605d..c318a3f2eb 100644 --- a/src/Mod/Fem/App/FemSolverObject.cpp +++ b/src/Mod/Fem/App/FemSolverObject.cpp @@ -28,8 +28,6 @@ #endif #include "FemSolverObject.h" - -#include #include #include @@ -42,20 +40,7 @@ PROPERTY_SOURCE(Fem::FemSolverObject, App::DocumentObject) FemSolverObject::FemSolverObject() { - - /* - ADD_PROPERTY_TYPE(SolverName,("Calculix"), "Data",Prop_None,"Solver program name"); - ADD_PROPERTY_TYPE(Category,("FEM"), "Data",Prop_None,"FEM, CFD ..."); - ADD_PROPERTY_TYPE(Module,(""), "Data",Prop_None,"Python module name"); - ADD_PROPERTY_TYPE(ExternalCaseEditor,(""), "Data",Prop_None,"External case editor programe"); - ADD_PROPERTY_TYPE(ExternalResultViewer,(""), "Data",Prop_None,"External result viewer name"); - - ADD_PROPERTY_TYPE(AnalysisType,("Static"), "Solver",Prop_None,"Specific analysis type"); - ADD_PROPERTY_TYPE(WorkingDir,(Base::FileInfo::getTempPath()), "Solver",Prop_None,"Solver working directory"); - ADD_PROPERTY_TYPE(InputCaseName,("TestCase"), "Solver",Prop_None,"Solver input file without suffix"); - ADD_PROPERTY_TYPE(Parallel,(false), "Solver",Prop_None,"Run solver in parallel like MPI"); - ADD_PROPERTY_TYPE(ResultObtained,(false), "Solver",Prop_None,"if true, result has been obtained"); - */ + // Attributes are implemented in the FemSolverObjectPython } FemSolverObject::~FemSolverObject() diff --git a/src/Mod/Fem/App/FemSolverObject.h b/src/Mod/Fem/App/FemSolverObject.h index 0632a4c874..65713246c8 100644 --- a/src/Mod/Fem/App/FemSolverObject.h +++ b/src/Mod/Fem/App/FemSolverObject.h @@ -25,11 +25,7 @@ #ifndef Fem_FemSolverObject_H #define Fem_FemSolverObject_H -#include -#include -#include #include -#include "FemSolverObject.h" namespace Fem { @@ -43,29 +39,7 @@ public: FemSolverObject(void); virtual ~FemSolverObject(); - /* - /// Solver name, unique to identify solver in registered_solver dict - App::PropertyString SolverName; - /// CAE category like FEM, all capitalised letters - App::PropertyString Category; - /// python module name - App::PropertyString Module; - /// Path or program name for external case editor, empty string means using FreeCAD to view - App::PropertyString ExternalCaseEditor; - /// Path to External Result Viewer like Paraview, empty string means using FreeCAD - App::PropertyString ExternalResultViewer; - - /// for FEM: Static, Frequency, etc - App::PropertyString AnalysisType; - /// Path of working dir for the solver - App::PropertyString WorkingDir; - /// name for the case file without suffix - App::PropertyString InputCaseName; - /// run parallel in MPI (message passing interface)/multiple cores or serial(single CPU) - App::PropertyBool Parallel; - /// result has been obtained, purge result may be needed for rerun - App::PropertyBool ResultObtained; - */ + // Attributes are implemented in the FemSolverObjectPython /// returns the type name of the ViewProvider virtual const char* getViewProviderName(void) const { diff --git a/src/Mod/Fem/CMakeLists.txt b/src/Mod/Fem/CMakeLists.txt index 2503886744..1d5636a81a 100755 --- a/src/Mod/Fem/CMakeLists.txt +++ b/src/Mod/Fem/CMakeLists.txt @@ -24,7 +24,7 @@ INSTALL( TestFem.py FemCommands.py - _CommandMeshFromShape.py + _CommandMeshNetgenFromShape.py _CommandPurgeResults.py _CommandRunSolver.py _CommandControlSolver.py @@ -36,6 +36,15 @@ INSTALL( FemAnalysis.py _CommandAnalysis.py + FemMeshGmsh.py + _FemMeshGmsh.py + _ViewProviderFemMeshGmsh.py + _CommandMeshGmshFromShape.py + _TaskPanelFemMeshGmsh.py + TaskPanelFemMeshGmsh.ui + + FemGmshTools.py + FemBeamSection.py _FemBeamSection.py _ViewProviderFemBeamSection.py @@ -50,6 +59,11 @@ INSTALL( _TaskPanelFemShellThickness.py TaskPanelFemShellThickness.ui + FemConstraintSelfWeight.py + _FemConstraintSelfWeight.py + _ViewProviderFemConstraintSelfWeight.py + _CommandConstraintSelfWeight.py + MechanicalMaterial.py _MechanicalMaterial.py _ViewProviderMechanicalMaterial.py @@ -85,11 +99,6 @@ INSTALL( _ViewProviderFemSolverZ88.py _CommandSolverZ88.py - FemConstraintSelfWeight.py - _FemConstraintSelfWeight.py - _ViewProviderFemConstraintSelfWeight.py - _CommandConstraintSelfWeight.py - DESTINATION Mod/Fem ) diff --git a/src/Mod/Fem/FemAnalysis.py b/src/Mod/Fem/FemAnalysis.py index be5d599bc2..3e0897ebbc 100644 --- a/src/Mod/Fem/FemAnalysis.py +++ b/src/Mod/Fem/FemAnalysis.py @@ -20,8 +20,6 @@ # * * # *************************************************************************** -import FreeCAD - __title__ = "FEM Analysis managment" __author__ = "Juergen Riegel" __url__ = "http://www.freecadweb.org" @@ -29,6 +27,9 @@ __url__ = "http://www.freecadweb.org" ## \addtogroup FEM # @{ +import FreeCAD + + def makeFemAnalysis(name): '''makeFemAnalysis(name): makes a Fem Analysis object''' obj = FreeCAD.ActiveDocument.addObject("Fem::FemAnalysisPython", name) diff --git a/src/Mod/Fem/FemBeamSection.py b/src/Mod/Fem/FemBeamSection.py index cc41584e70..934168cb50 100644 --- a/src/Mod/Fem/FemBeamSection.py +++ b/src/Mod/Fem/FemBeamSection.py @@ -24,12 +24,12 @@ __title__ = "FemBeamSection" __author__ = "Bernd Hahnebach" __url__ = "http://www.freecadweb.org" +## \addtogroup FEM +# @{ import FreeCAD import _FemBeamSection -## \addtogroup FEM -# @{ def makeFemBeamSection(width=20.0, height=20.0, name="BeamSection"): '''makeFemBeamSection([width], [height], [name]): creates an beamsection object to define a cross section''' diff --git a/src/Mod/Fem/FemCommands.py b/src/Mod/Fem/FemCommands.py index 356fa275c0..76e310a0c9 100644 --- a/src/Mod/Fem/FemCommands.py +++ b/src/Mod/Fem/FemCommands.py @@ -25,15 +25,15 @@ __title__ = "Fem Commands" __author__ = "Przemo Firszt" __url__ = "http://www.freecadweb.org" -import FreeCAD +## \addtogroup FEM +# @{ +import FreeCAD if FreeCAD.GuiUp: import FreeCADGui import FemGui from PySide import QtCore -## \addtogroup FEM -# @{ class FemCommands(object): def __init__(self): diff --git a/src/Mod/Fem/FemConstraintSelfWeight.py b/src/Mod/Fem/FemConstraintSelfWeight.py index 2544d4a3a5..3f919ffa68 100644 --- a/src/Mod/Fem/FemConstraintSelfWeight.py +++ b/src/Mod/Fem/FemConstraintSelfWeight.py @@ -24,12 +24,12 @@ __title__ = "FemConstraintSelfWeight" __author__ = "Bernd Hahnebach" __url__ = "http://www.freecadweb.org" +## \addtogroup FEM +# @{ import FreeCAD import _FemConstraintSelfWeight -## \addtogroup FEM -# @{ def makeFemConstraintSelfWeight(name="FemConstraintSelfWeight"): '''makeFemFemConstraintSelfWeight([name]): creates an self weight object to define a gravity load''' diff --git a/src/Mod/Fem/FemGmshTools.py b/src/Mod/Fem/FemGmshTools.py new file mode 100644 index 0000000000..a93c373650 --- /dev/null +++ b/src/Mod/Fem/FemGmshTools.py @@ -0,0 +1,271 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2016 - 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__ = "Tools for the work with GMSH mesher" +__author__ = "Bernd Hahnebach" +__url__ = "http://www.freecadweb.org" + +## \addtogroup FEM +# @{ + +import FreeCAD +import Fem +import subprocess +import tempfile +from platform import system + + +class FemGmshTools(): + def __init__(self, gmsh_mesh_obj, analysis=None): + self.mesh_obj = gmsh_mesh_obj + if analysis: + self.analysis = analysis + # group meshing turned on + else: + self.analysis = None + # group meshing turned off + + # part to mesh + self.part_obj = self.mesh_obj.Part + + # clmax, ElementSizeMax: float, 0.0 = 1e+22 + self.clmax = self.mesh_obj.ElementSizeMax + if self.clmax == 0.0: + self.clmax = 1e+22 + + # clmin, ElementSizeMin: float + self.clmin = self.mesh_obj.ElementSizeMin + + # order, ElementOrder: ['Auto', '1st', '2nd'] + self.order = self.mesh_obj.ElementOrder + if self.order == '1st': + self.order = '1' + elif self.order == 'Auto' or self.order == '2nd': + self.order = '2' + else: + print('Error in order') + + # dimension, ElementDimension: ['Auto', '1D', '2D', '3D'] + self.dimension = self.mesh_obj.ElementDimension + + def create_mesh(self): + print("\nWe gone start GMSH FEM mesh run!") + print(' Part to mesh: Name --> ' + self.part_obj.Name + ', Label --> ' + self.part_obj.Label + ', ShapeType --> ' + self.part_obj.Shape.ShapeType) + print(' ElementSizeMax: ' + str(self.clmax)) + print(' ElementSizeMin: ' + str(self.clmin)) + print(' ElementOrder: ' + self.order) + self.get_dimension() + self.get_tmp_file_paths() + self.get_gmsh_command() + self.get_group_data() + self.write_part_file() + self.write_geo() + error = self.run_gmsh_with_geo() + self.read_and_set_new_mesh() + return error + + def get_dimension(self): + # Dimension + # GMSH uses the hightest availabe. + # A use case for not auto would be a surface (2D) mesh of a solid or other 3d shape + if self.dimension == 'Auto': + shty = self.part_obj.Shape.ShapeType + if shty == 'Solid' or shty == 'CompSolid': + # print('Found: ' + shty) + self.dimension = '3' + elif shty == 'Face' or shty == 'Shell': + # print('Found: ' + shty) + self.dimension = '2' + elif shty == 'Edge' or shty == 'Wire': + # print('Found: ' + shty) + self.dimension = '1' + elif shty == 'Vertex': + # print('Found: ' + shty) + FreeCAD.Console.PrintError("You can not mesh a Vertex.\n") + self.dimension = '0' + elif shty == 'Compound': + print('Found: ' + shty) + print('I do not know what is inside your Compound. Dimension was set to 3 anyway.') + # TODO check contents of Compound + # use dimension 3 on any shape works for 2D and 1d meshes as well ! + # but not in combination with sewfaces or connectfaces + self.dimension = '3' + else: + self.dimension = '0' + FreeCAD.Console.PrintError('Could not retrive Dimension from shape type. Please choose dimension.') + elif self.dimension == '3D': + self.dimension = '3' + elif self.dimension == '2D': + self.dimension = '2' + elif self.dimension == '1D': + self.dimension = '1' + else: + print('Error in dimension') + print(' ElementDimension: ' + self.dimension) + + def get_tmp_file_paths(self): + if system() == "Linux": + path_sep = "/" + elif system() == "Windows": + path_sep = "\\" + else: + path_sep = "/" + tmpdir = tempfile.gettempdir() + # geometry file + self.temp_file_geometry = tmpdir + path_sep + self.part_obj.Name + '_Geometry.brep' + print(' ' + self.temp_file_geometry) + # mesh file + self.mesh_name = self.part_obj.Name + '_Mesh_TmpGmsh' + self.temp_file_mesh = tmpdir + path_sep + self.mesh_name + '.unv' + print(' ' + self.temp_file_mesh) + # GMSH input file + self.temp_file_geo = tmpdir + path_sep + 'shape2mesh.geo' + print(' ' + self.temp_file_geo) + + def get_gmsh_command(self): + self.gmsh_bin = None + gmsh_std_location = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/Gmsh").GetBool("UseStandardGmshLocation") + if gmsh_std_location: + if system() == "Windows": + gmsh_path = FreeCAD.getHomePath() + "bin/gmsh.exe" + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/Gmsh").SetString("gmshBinaryPath", gmsh_path) + self.gmsh_bin = gmsh_path + elif system() == "Linux": + p1 = subprocess.Popen(['which', 'gmsh'], stdout=subprocess.PIPE) + if p1.wait() == 0: + gmsh_path = p1.stdout.read().split('\n')[0] + elif p1.wait() == 1: + error_message = "GMSH binary gmsh not found in standard system binary path. Please install gmsh or set path to binary in FEM preferences tab GMSH.\n" + # if FreeCAD.GuiUp: + # QtGui.QMessageBox.critical(None, "No GMSH binary ccx", error_message) + raise Exception(error_message) + self.gmsh_bin = gmsh_path + else: + if not self.gmsh_bin: + self.gmsh_bin = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/Gmsh").GetString("gmshBinaryPath", "") + if not self.gmsh_bin: # in prefs not set, we will try to use something reasonable + if system() == "Linux": + self.gmsh_bin = "gmsh" + elif system() == "Windows": + self.gmsh_bin = FreeCAD.getHomePath() + "bin/gmsh.exe" + else: + self.gmsh_bin = "gmsh" + self.gmsh_bin = self.gmsh_bin + print(' ' + self.gmsh_bin) + + def get_group_data(self): + if self.analysis: + print(' Group meshing.') + import FemMeshTools + self.group_elements = FemMeshTools.get_analysis_group_elements(self.analysis, self.part_obj) + print(self.group_elements) + else: + print(' NO group meshing.') + + def write_part_file(self): + self.part_obj.Shape.exportBrep(self.temp_file_geometry) + + def write_geo(self): + geo = open(self.temp_file_geo, "w") + geo.write('Merge "' + self.temp_file_geometry + '";\n') + geo.write("\n") + if self.analysis and self.group_elements: + print(' We gone have found elements to make mesh groups for!') + geo.write("// group data\n") + # we use the element name of FreeCAD which starts with 1 (example: 'Face1'), same as GMSH + for group in self.group_elements: + gdata = self.group_elements[group] + # print(gdata) + # geo.write("// " + group + "\n") + ele_nr = '' + if gdata[0].startswith('Solid'): + physical_type = 'Volume' + for ele in gdata: + ele_nr += (ele.lstrip('Solid') + ', ') + elif gdata[0].startswith('Face'): + physical_type = 'Surface' + for ele in gdata: + ele_nr += (ele.lstrip('Face') + ', ') + elif gdata[0].startswith('Edge') or gdata[0].startswith('Vertex'): + geo.write("// " + group + " group data not written. Edges or Vertexes group data not supported.\n") + print(' Groups for Edges or Vertexes reference shapes not handeled yet.') + if ele_nr: + ele_nr = ele_nr.rstrip(', ') + # print(ele_nr) + geo.write('Physical ' + physical_type + '("' + group + '") = {' + ele_nr + '};\n') + geo.write("\n") + geo.write("Mesh.CharacteristicLengthMax = " + str(self.clmax) + ";\n") + geo.write("Mesh.CharacteristicLengthMin = " + str(self.clmin) + ";\n") + geo.write("Mesh.ElementOrder = " + self.order + ";\n") + geo.write("//Mesh.HighOrderOptimize = 1;\n") # but does not really work, in GUI it does + geo.write("Mesh.Algorithm3D = 1;\n") + geo.write("Mesh.Algorithm = 2;\n") + geo.write("Mesh " + self.dimension + ";\n") + geo.write("Mesh.Format = 2;\n") # unv + if self.analysis and self.group_elements: + geo.write("// For each group save not only the elements but the nodes too.;\n") + geo.write("Mesh.SaveGroupsOfNodes = 1;\n") + geo.write("// Needed for Group meshing too, because for one material there is no group defined;\n") # belongs to Mesh.SaveAll but anly needed if there are groups + geo.write("// Ignore Physical definitions and save all elements;\n") + geo.write("Mesh.SaveAll = 1;\n") + geo.write("\n") + geo.write('Save "' + self.temp_file_mesh + '";\n') + geo.write("\n\n") + geo.write("//////////////////////////////////////////////////////////////////////\n") + geo.write("// GMSH documentation:\n") + geo.write("// http://gmsh.info/doc/texinfo/gmsh.html#Mesh\n") + geo.write("//\n") + geo.write("// We do not check if something went wrong, like negative jacobians etc. You can run GMSH manually yourself: \n") + geo.write("//\n") + geo.write("// to see full GMSH log, run in bash:\n") + geo.write("// " + self.gmsh_bin + " - " + self.temp_file_geo + "\n") + geo.write("//\n") + geo.write("// to run GMSH and keep file in GMSH GUI (with log), run in bash:\n") + geo.write("// " + self.gmsh_bin + " " + self.temp_file_geo + "\n") + geo.close + + def run_gmsh_with_geo(self): + self.error = False + comandlist = [self.gmsh_bin, '-', self.temp_file_geo] + # print(comandlist) + try: + p = subprocess.Popen(comandlist, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output, error = p.communicate() + # print(output) # stdout is still cut at some point but the warnings are in stderr and thus printed :-) + # print(error) + except: + error = 'Error executing: {}\n'.format(self.gmsh_command) + FreeCAD.Console.PrintError(error) + self.error = True + return error + + def read_and_set_new_mesh(self): + if not self.error: + fem_mesh = Fem.read(self.temp_file_mesh) + self.mesh_obj.FemMesh = fem_mesh + print(' The Part should have a pretty new FEM mesh!') + else: + print('No mesh was created.') + del self.temp_file_geometry + del self.temp_file_mesh + +# @} diff --git a/src/Mod/Fem/FemInputWriter.py b/src/Mod/Fem/FemInputWriter.py index 970a78e641..f9d2e439e6 100644 --- a/src/Mod/Fem/FemInputWriter.py +++ b/src/Mod/Fem/FemInputWriter.py @@ -20,27 +20,17 @@ # * * # *************************************************************************** - -''' -- next step would be save the constraints node and element data in the in the FreeCAD FEM Mesh Object - and link them to the appropriate constraint object -- if the informations are used by the FEM Mesh file exporter FreeCAD would support writing FEM Mesh Groups -- which is a most needed feature of FEM module -- smesh supports mesh groups, how about pythonbinding in FreeCAD. Is there somethin implemented allready? -''' - - __title__ = "FemInputWriter" __author__ = "Bernd Hahnebach" __url__ = "http://www.freecadweb.org" +## \addtogroup FEM +# @{ import FreeCAD import FemMeshTools import os -## \addtogroup FEM -# @{ class FemInputWriter(): def __init__(self, @@ -85,6 +75,7 @@ class FemInputWriter(): self.femnodes_mesh = {} self.femelement_table = {} self.constraint_conflict_nodes = [] + self.femnodes_ele_table = {} def get_constraints_fixed_nodes(self): # get nodes @@ -159,9 +150,25 @@ class FemInputWriter(): def get_constraints_pressure_faces(self): # TODO see comments in get_constraints_force_nodeloads(), it applies here too. Mhh it applies to all constraints ... + ''' + # depreciated version # get the faces and face numbers for femobj in self.pressure_objects: # femobj --> dict, FreeCAD document object is femobj['Object'] - femobj['PressureFaces'] = FemMeshTools.get_pressure_obj_faces(self.femmesh, femobj) - # print femobj['PressureFaces'] + femobj['PressureFaces'] = FemMeshTools.get_pressure_obj_faces_depreciated(self.femmesh, femobj) + # print(femobj['PressureFaces']) + ''' + + if not self.femnodes_mesh: + self.femnodes_mesh = self.femmesh.Nodes + if not self.femelement_table: + self.femelement_table = FemMeshTools.get_femelement_table(self.femmesh) + if not self.femnodes_ele_table: + self.femnodes_ele_table = FemMeshTools.get_femnodes_ele_table(self.femnodes_mesh, self.femelement_table) + + for femobj in self.pressure_objects: # femobj --> dict, FreeCAD document object is femobj['Object'] + pressure_faces = FemMeshTools.get_pressure_obj_faces(self.femmesh, self.femelement_table, self.femnodes_ele_table, femobj) + # print(len(pressure_faces)) + femobj['PressureFaces'] = [(femobj['Object'].Name + ': face load', pressure_faces)] + # print(femobj['PressureFaces']) # @} diff --git a/src/Mod/Fem/FemInputWriterCcx.py b/src/Mod/Fem/FemInputWriterCcx.py index 378a442406..283da75e62 100644 --- a/src/Mod/Fem/FemInputWriterCcx.py +++ b/src/Mod/Fem/FemInputWriterCcx.py @@ -21,11 +21,12 @@ # * * # *************************************************************************** - __title__ = "FemInputWriterCcx" __author__ = "Przemo Firszt, Bernd Hahnebach" __url__ = "http://www.freecadweb.org" +## \addtogroup FEM +# @{ import FreeCAD import os @@ -34,8 +35,6 @@ import time import FemMeshTools import FemInputWriter -## \addtogroup FEM -# @{ class FemInputWriterCcx(FemInputWriter.FemInputWriter): def __init__(self, @@ -66,7 +65,7 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): print('FemInputWriterCcx --> self.file_name --> ' + self.file_name) def write_calculix_input_file(self): - if self.solver_obj.SplitInputWriter == True: + if self.solver_obj.SplitInputWriter is True: self.write_calculix_splitted_input_file() else: self.write_calculix_one_input_file() @@ -974,7 +973,12 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): if all_found is False: if not self.femelement_table: self.femelement_table = FemMeshTools.get_femelement_table(self.femmesh) - FemMeshTools.get_femelement_sets(self.femmesh, self.femelement_table, self.material_objects) + # we gone use the binary search for get_femelements_by_femnodes(), thus we need the parameter values self.femnodes_ele_table + if not self.femnodes_mesh: + self.femnodes_mesh = self.femmesh.Nodes + if not self.femnodes_ele_table: + self.femnodes_ele_table = FemMeshTools.get_femnodes_ele_table(self.femnodes_mesh, self.femelement_table) + FemMeshTools.get_femelement_sets(self.femmesh, self.femelement_table, self.material_objects, self.femnodes_ele_table) for mat_data in self.material_objects: mat_obj = mat_data['Object'] ccx_elset = {} diff --git a/src/Mod/Fem/FemInputWriterZ88.py b/src/Mod/Fem/FemInputWriterZ88.py index 081ab13ee1..9090496b6c 100644 --- a/src/Mod/Fem/FemInputWriterZ88.py +++ b/src/Mod/Fem/FemInputWriterZ88.py @@ -20,19 +20,18 @@ # * * # *************************************************************************** - __title__ = "FemInputWriterZ88" __author__ = "Bernd Hahnebach" __url__ = "http://www.freecadweb.org" +## \addtogroup FEM +# @{ import FreeCAD import FemMeshTools import importZ88Mesh import FemInputWriter -## \addtogroup FEM -# @{ class FemInputWriterZ88(FemInputWriter.FemInputWriter): def __init__(self, diff --git a/src/Mod/Fem/FemMaterialMechanicalNonlinear.py b/src/Mod/Fem/FemMaterialMechanicalNonlinear.py index 65209e65e8..55dbf2ba9b 100644 --- a/src/Mod/Fem/FemMaterialMechanicalNonlinear.py +++ b/src/Mod/Fem/FemMaterialMechanicalNonlinear.py @@ -24,13 +24,13 @@ __title__ = "FemMaterialMechanicalNonlinear" __author__ = "Bernd Hahnebach" __url__ = "http://www.freecadweb.org" +## \addtogroup FEM +# @{ import FreeCAD import FemGui import _FemMaterialMechanicalNonlinear -## \addtogroup FEM -# @{ def makeFemMaterialMechanicalNonlinear(base_material, name="MechanicalMaterialNonlinear"): '''makeFemMaterialMechanicalNonlinear(base_material, [name]): creates an nonlinear material object''' diff --git a/src/Mod/Fem/FemMeshGmsh.py b/src/Mod/Fem/FemMeshGmsh.py new file mode 100644 index 0000000000..fbc98684a1 --- /dev/null +++ b/src/Mod/Fem/FemMeshGmsh.py @@ -0,0 +1,43 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2016 - 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__ = "FemMeshGmsh" +__author__ = "Bernd Hahnebach" +__url__ = "http://www.freecadweb.org" + +## \addtogroup FEM +# @{ + +import FreeCAD +import _FemMeshGmsh + + +def makeFemMeshGmsh(name="FEMMeshGMSH"): + '''makeFemMeshGmsh(name): makes a GMSH FEM mesh object''' + obj = FreeCAD.ActiveDocument.addObject("Fem::FemMeshObjectPython", name) + _FemMeshGmsh._FemMeshGmsh(obj) + if FreeCAD.GuiUp: + import _ViewProviderFemMeshGmsh + _ViewProviderFemMeshGmsh._ViewProviderFemMeshGmsh(obj.ViewObject) + return obj + +# @} diff --git a/src/Mod/Fem/FemMeshTools.py b/src/Mod/Fem/FemMeshTools.py index a1b375dac6..42ace7d64e 100644 --- a/src/Mod/Fem/FemMeshTools.py +++ b/src/Mod/Fem/FemMeshTools.py @@ -20,35 +20,39 @@ # * * # *************************************************************************** - __title__ = "Tools for the work with FEM meshes" __author__ = "Bernd Hahnebach" __url__ = "http://www.freecadweb.org" +## \addtogroup FEM +# @{ import FreeCAD -## \addtogroup FEM -# @{ def get_femnodes_by_femobj_with_references(femmesh, femobj): node_set = [] if femmesh.GroupCount: node_set = get_femnode_set_from_group_data(femmesh, femobj) - # print 'node_set_group: ', node_set + # print('node_set_group: ', node_set) if not node_set: node_set = get_femnodes_by_references(femmesh, femobj['Object'].References) - # print 'node_set_nogroup: ', node_set + # print('node_set_nogroup: ', node_set) return node_set -def get_femelements_by_references(femmesh, femelement_table, references): +def get_femelements_by_references(femmesh, femelement_table, references, femnodes_ele_table=None): '''get the femelements for a list of references ''' references_femelements = [] for ref in references: ref_femnodes = get_femnodes_by_refshape(femmesh, ref) # femnodes for the current ref - references_femelements += get_femelements_by_femnodes(femelement_table, ref_femnodes) # femelements for all references + if femnodes_ele_table: + # blind fast binary search, works for volumes only + references_femelements += get_femelements_by_femnodes_bin(femelement_table, femnodes_ele_table, ref_femnodes) # femelements for all references + else: + # standars search + references_femelements += get_femelements_by_femnodes_std(femelement_table, ref_femnodes) # femelements for all references return references_femelements @@ -103,12 +107,160 @@ def get_femelement_table(femmesh): return femelement_table -def get_femelements_by_femnodes(femelement_table, node_list): +def get_femnodes_ele_table(femnodes_mesh, femelement_table): + '''the femnodes_ele_table contains for each node its membership in elements + {nodeID : [[eleID, NodePosition], [], ...], nodeID : [[], [], ...], ...} + stored informatation are: + 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. + Since the femelement_table contains either volume or face or edgemesh the femnodes_ele_table only + has either volume or face or edge elements, see get_femelement_table() + ''' + femnodes_ele_table = {} # node_dict in ulrichs class + for n in femnodes_mesh: # initialize it with sorted node keys and empty lists + femnodes_ele_table[n] = [] + for ele in femelement_table: + ele_list = femelement_table[ele] + # print(ele_list) + pos = int(1) + for ele_node in ele_list: + femnodes_ele_table[ele_node].append([ele, pos]) + pos = pos << 1 + print('len femnodes_ele_table:' + str(len(femnodes_ele_table))) + # print('femnodes_ele_table: ', femnodes_ele_table) + return femnodes_ele_table + + +def get_copy_of_empty_femelement_table(femelement_table): + '''{eleID : 0, eleID : 0, ...} + ''' + empty_femelement_table = {} + for ele in femelement_table: # initialize it with sorted element keys and empty int + empty_femelement_table[ele] = 0 + return empty_femelement_table.copy() + + +def get_bit_pattern_dict(femelement_table, femnodes_ele_table, node_set): + '''Now we are looking for nodes inside of the Faces = filling the bit_pattern_dict + {eleID : [lenEleNodes, binary_position]} + see forumpost for a ver good explanation whats really happening + http://forum.freecadweb.org/viewtopic.php?f=18&p=141133&sid=013c93f496a63872951d2ce521702ffa#p141108 + The bit_pattern_dict holds later an integer (bit array) for each element, which gives us + the information we are searching for: + Is this element part of the node list (searching for elements) or has this element a face we are searching for? + The number in the ele_dict is organized as a bit array. + The corresponding bit is set, if the node of the node_set is contained in the element. + ''' + print('len femnodes_ele_table:' + str(len(femnodes_ele_table))) + print('len node_set: ' + str(len(node_set))) + # print('node_set: ', node_set) + bit_pattern_dict = get_copy_of_empty_femelement_table(femelement_table) + # # initializing the bit_pattern_dict + for ele in femelement_table: + len_ele = len(femelement_table[ele]) + bit_pattern_dict[ele] = [len_ele, 0] + for node in node_set: + for nList in femnodes_ele_table[node]: + bit_pattern_dict[nList[0]][1] += nList[1] + print('len bit_pattern_dict:' + str(len(bit_pattern_dict))) + # print('bit_pattern_dict: ', bit_pattern_dict) + return bit_pattern_dict + + +def get_ccxelement_faces_from_binary_search(bit_pattern_dict): + '''get the CalculiX element face numbers + ''' + tet10_mask = { + 119: 1, + 411: 2, + 717: 3, + 814: 4} + tet4_mask = { + 7: 1, + 11: 2, + 13: 3, + 14: 4} + hex8_mask = { + 240: 1, + 15: 2, + 102: 3, + 204: 4, + 153: 5, + 51: 6} + hex20_mask = { + 61680: 1, + 3855: 2, + 402022: 3, + 804044: 4, + 624793: 5, + 201011: 6} + pent6_mask = { + 56: 1, + 7: 2, + 54: 3, + 45: 4, + 27: 5} + pent15_mask = { + 3640: 1, + 455: 2, + 25782: 3, + 22829: 4, + 12891: 5} + vol_dict = { + 4: tet4_mask, + 6: pent6_mask, + 8: hex8_mask, + 10: tet10_mask, + 15: pent15_mask, + 20: hex20_mask} + faces = [] + for ele in bit_pattern_dict: + mask_dict = vol_dict[bit_pattern_dict[ele][0]] + for key in mask_dict: + if (key & bit_pattern_dict[ele][1]) == key: + faces.append([ele, mask_dict[key]]) + print('found Faces: ', len(faces)) + print('faces: ', faces) + return faces + + +def get_femelements_by_femnodes_bin(femelement_table, femnodes_ele_table, node_list): + '''for every femelement of femelement_table + if all nodes of the femelement are in node_list, + the femelement is added to the list which is returned + blind fast binary search, but workd for volumes only + ''' + print('binary search: get_femelements_by_femnodes_bin') + vol_masks = { + 4: 15, + 6: 63, + 8: 255, + 10: 1023, + 15: 32767, + 20: 1048575} + # Now we are looking for nodes inside of the Volumes = filling the bit_pattern_dict + print('len femnodes_ele_table:' + str(len(femnodes_ele_table))) + bit_pattern_dict = get_bit_pattern_dict(femelement_table, femnodes_ele_table, node_list) + # search + ele_list = [] # The ele_list contains the result of the search. + for ele in bit_pattern_dict: + # print('bit_pattern_dict[ele][0]: ', bit_pattern_dict[ele][0]) + if bit_pattern_dict[ele][1] == vol_masks[bit_pattern_dict[ele][0]]: + ele_list.append(ele) + print('found Volumes: ', len(ele_list)) + print(' volumes: ', len(ele_list)) + return ele_list + + +def get_femelements_by_femnodes_std(femelement_table, node_list): '''for every femelement of femelement_table if all nodes of the femelement are in node_list, the femelement is added to the list which is returned e: elementlist nodes: nodelist ''' + print('std search: get_femelements_by_femnodes_std') e = [] # elementlist for elementID in sorted(femelement_table): nodecount = 0 @@ -172,11 +324,11 @@ def get_femvolumeelements_by_femfacenodes(femelement_table, node_list): e.append(elementID) else: FreeCAD.Console.PrintError('Error in get_femvolumeelements_by_femfacenodes(): not known volume element: ' + el_nd_ct + '\n') - # print sorted(e) + # print(sorted(e)) return e -def get_femelement_sets(femmesh, femelement_table, fem_objects): # fem_objects = FreeCAD FEM document objects +def get_femelement_sets(femmesh, femelement_table, fem_objects, femnodes_ele_table=None): # fem_objects = FreeCAD FEM document objects # get femelements for reference shapes of each obj.References count_femelements = 0 referenced_femelements = [] @@ -186,7 +338,7 @@ def get_femelement_sets(femmesh, femelement_table, fem_objects): # fem_objects fem_object['ShortName'] = get_elset_short_name(obj, fem_object_i) # unique short identifier if obj.References: ref_shape_femelements = [] - ref_shape_femelements = get_femelements_by_references(femmesh, femelement_table, obj.References) + ref_shape_femelements = get_femelements_by_references(femmesh, femelement_table, obj.References, femnodes_ele_table) referenced_femelements += ref_shape_femelements count_femelements += len(ref_shape_femelements) fem_object['FEMElements'] = ref_shape_femelements @@ -213,10 +365,11 @@ def get_femnode_set_from_group_data(femmesh, fem_object): # we assume the mesh group data fits with the reference shapes, no check is done in this regard !!! # what happens if a reference shape was changed, but the mesh and the mesh groups were not created new !?! obj = fem_object['Object'] + group_nodes = None if femmesh.GroupCount: for g in femmesh.Groups: grp_name = femmesh.getGroupName(g) - if grp_name.startswith(obj.Name + '_'): + if grp_name.startswith(obj.Name): if femmesh.getGroupElementType(g) == "Node": print("Constraint: " + obj.Name + " --> " + "mesh group: " + grp_name) group_nodes = femmesh.getGroupElements(g) # == ref_shape_femelements @@ -235,7 +388,7 @@ def get_femelement_sets_from_group_data(femmesh, fem_objects): if femmesh.GroupCount: for g in femmesh.Groups: grp_name = femmesh.getGroupName(g) - if grp_name.startswith(obj.Name + '_'): + if grp_name.startswith(obj.Name): if femmesh.getGroupElementType(g) == "Volume": print("Constraint: " + obj.Name + " --> " + "mesh group: " + grp_name) group_elements = femmesh.getGroupElements(g) # == ref_shape_femelements @@ -372,7 +525,7 @@ def get_force_obj_edge_nodeload_table(femmesh, femelement_table, femnodes_mesh, return force_obj_node_load_table -def get_pressure_obj_faces(femmesh, femobj): +def get_pressure_obj_faces_depreciated(femmesh, femobj): pressure_faces = [] for o, elem_tup in femobj['Object'].References: for elem in elem_tup: @@ -384,6 +537,16 @@ def get_pressure_obj_faces(femmesh, femobj): return pressure_faces +def get_pressure_obj_faces(femmesh, femelement_table, femnodes_ele_table, femobj): + # get the nodes + prs_face_node_set = get_femnodes_by_femobj_with_references(femmesh, femobj) # sorted and duplicates removed + # print('prs_face_node_set: ', prs_face_node_set) + # fill the bit_pattern_dict and search for the faces + bit_pattern_dict = get_bit_pattern_dict(femelement_table, femnodes_ele_table, prs_face_node_set) + pressure_faces = get_ccxelement_faces_from_binary_search(bit_pattern_dict) + return pressure_faces + + def get_force_obj_face_nodeload_table(femmesh, femelement_table, femnodes_mesh, frc_obj): # force_obj_node_load_table = [('refshape_name.elemname',node_load_table), ..., ('refshape_name.elemname',node_load_table)] force_obj_node_load_table = [] @@ -483,7 +646,7 @@ def get_ref_edgenodes_table(femmesh, femelement_table, refedge): # FIXME duplicate_mesh_elements: as soon as contact ans springs are supported the user should decide on which edge the load is applied edge_table = delete_duplicate_mesh_elements(edge_table) elif is_edge_femmesh(femmesh): - refedge_fem_edgeelements = get_femelements_by_femnodes(femelement_table, refedge_nodes) + refedge_fem_edgeelements = get_femelements_by_femnodes_std(femelement_table, refedge_nodes) for elem in refedge_fem_edgeelements: edge_table[elem] = femelement_table[elem] # { edgeID : ( nodeID, ... , nodeID )} # all nodes off this femedgeelement return edge_table @@ -571,10 +734,10 @@ def get_ref_facenodes_table(femmesh, femelement_table, ref_face): face_table[mf] = femmesh.getElementNodes(mf) elif is_face_femmesh(femmesh): ref_face_nodes = femmesh.getNodesByFace(ref_face) - ref_face_elements = get_femelements_by_femnodes(femelement_table, ref_face_nodes) + ref_face_elements = get_femelements_by_femnodes_std(femelement_table, ref_face_nodes) for mf in ref_face_elements: face_table[mf] = femelement_table[mf] - # print face_table + # print(face_table) return face_table @@ -588,10 +751,10 @@ def build_mesh_faces_of_volume_elements(face_table, femelement_table): index = femelement_table[veID].index(n) # print(index) face_nodenumber_table[veID].append(index + 1) # lokale node number = index + 1 - # print 'VolElement:', veID - # print ' --> ', femelement_table[veID] - # print ' --> ', face_table[veID] - # print ' --> ', face_nodenumber_table[veID] + # print('VolElement:', veID) + # print(' --> ', femelement_table[veID]) + # print(' --> ', face_table[veID]) + # print(' --> ', face_nodenumber_table[veID]) for veID in face_nodenumber_table: vol_node_ct = len(femelement_table[veID]) face_node_indexs = sorted(face_nodenumber_table[veID]) @@ -681,7 +844,7 @@ def build_mesh_faces_of_volume_elements(face_table, femelement_table): i -= 1 # node_number starts with 1, index starts with 0 --> index = node number - 1 face_nodes.append(femelement_table[veID][i]) face_table[veID] = face_nodes # reset the entry in face_table - # print ' --> ', face_table[veID] + # print(' --> ', face_table[veID]) return face_table @@ -840,13 +1003,13 @@ def get_analysis_group_elements(aAnalysis, aPart): {ConstraintName : ['ShapeType of the Elements'], [ElementID, ElementID, ...], ...} ''' aShape = aPart.Shape - group_elements = {} + group_elements = {} # { name : [element, element, ... , element]} empty_references = [] for m in aAnalysis.Member: if hasattr(m, "References"): # print(m.Name) key = m.Name - indexes = [] + elements = [] stype = None if m.References: for r in m.References: @@ -868,117 +1031,134 @@ def get_analysis_group_elements(aAnalysis, aPart): # print(ref_shape) found_element = find_element_in_shape(aShape, ref_shape) if found_element is not None: - indexes.append(found_element) + elements.append(found_element) else: FreeCAD.Console.PrintError('Problem: No element found for: ' + str(ref_shape) + '\n') print(' ' + m.Name) print(' ' + str(m.References)) print(' ' + r[0].Name) - group_elements[key] = [stype, sorted(indexes)] + group_elements[key] = sorted(elements) else: - print('Empty reference: ' + m.Name) + print(' Empty reference: ' + m.Name) empty_references.append(m) if empty_references: if len(empty_references) == 1: group_elements = get_anlysis_empty_references_group_elements(group_elements, aAnalysis, aShape) else: - FreeCAD.Console.PrintError('Error: more than one object with empty references!\n') - print(empty_references) + FreeCAD.Console.PrintError('Problem: more than one object with empty references.\n') + print('We gone try to get the empty material references anyway.\n') + # ShellThickness and BeamSection could have empty references, but on solid meshes only materials should have empty references + for er in empty_references: + print(er.Name) + group_elements = get_anlysis_empty_references_group_elements(group_elements, aAnalysis, aShape) # check if all groups have elements: for g in group_elements: - # print group_elements[g][1] - if len(group_elements[g][1]) == 0: + # print(group_elements[g]) + if len(group_elements[g]) == 0: FreeCAD.Console.PrintError('Error: shapes for: ' + g + 'not found!\n') return group_elements def get_anlysis_empty_references_group_elements(group_elements, aAnalysis, aShape): '''get the elementIDs if the Reference shape is empty - see get_analysis_group_elements() + see get_analysis_group_elements() for more informatations + on solid meshes only material objects could have an empty reference without beeing something wrong! + face meshes could have empty ShellThickness and edge meshes could have empty BeamSection ''' + # print(group_elements) material_ref_shapes = [] material_shape_type = '' missed_material_refshapes = [] empty_reference_material = None for m in aAnalysis.Member: - # only materials could have an empty reference without beeing something wrong! if m.isDerivedFrom("App::MaterialObjectPython"): - if hasattr(m, "References") and m.References: - if not material_shape_type: - material_shape_type = group_elements[m.Name][0] - elif material_shape_type != group_elements[m.Name][0]: - print('Problem, material shape type does not match get_anlysis_empty_references_group_elements') - for i in group_elements[m.Name][1]: - material_ref_shapes.append(i) - elif hasattr(m, "References") and not m.References: + if hasattr(m, "References") and not m.References: if not empty_reference_material: empty_reference_material = m.Name else: - print('Problem in get_anlysis_empty_references_group_elements, we seams to have two materials with empty referneces') + FreeCAD.Console.PrintError('Problem in get_anlysis_empty_references_group_elements, we seams to have two or more materials with empty referneces') return {} + elif hasattr(m, "References") and m.References: + # ShapeType ot the group elements, strip the number of the first group element + # http://stackoverflow.com/questions/12851791/removing-numbers-from-string + group_shape_type = ''.join(i for i in group_elements[m.Name][0] if not i.isdigit()) + if not material_shape_type: + material_shape_type = group_shape_type + elif material_shape_type != group_shape_type: + FreeCAD.Console.PrintError('Problem, material shape type does not match get_anlysis_empty_references_group_elements') + for ele in group_elements[m.Name]: + material_ref_shapes.append(ele) if material_shape_type == 'Solid': # print(len(aShape.Solids)) for i in range(len(aShape.Solids)): - if i not in material_ref_shapes: - missed_material_refshapes.append(i) + ele = 'Solid' + str(i + 1) + if ele not in material_ref_shapes: + missed_material_refshapes.append(ele) elif material_shape_type == 'Face': # print(len(aShape.Faces)) for i in range(len(aShape.Faces)): - if i not in material_ref_shapes: - missed_material_refshapes.append(i) + ele = 'Face' + str(i + 1) + if ele not in material_ref_shapes: + missed_material_refshapes.append(ele) elif material_shape_type == 'Edge': # print(len(aShape.Edges)) for i in range(len(aShape.Edges)): - if i not in material_ref_shapes: - missed_material_refshapes.append(i) + ele = 'Edge' + str(i + 1) + if ele not in material_ref_shapes: + missed_material_refshapes.append(ele) else: - print('It seams we only have one material with no reference shapes. Means the whole solid is one material. Since we only support material groups for Solids at the moment this should be Solid') - material_shape_type = 'Solid' - missed_material_refshapes.append(0) + print(' One material with no reference shapes. No need to make a group for materials.') + # make no changes group_elements + return group_elements # print(sorted(material_ref_shapes)) # print(sorted(missed_material_refshapes)) # print(group_elements) - group_elements[empty_reference_material] = [material_shape_type, sorted(missed_material_refshapes)] + group_elements[empty_reference_material] = sorted(missed_material_refshapes) # print(group_elements) return group_elements def find_element_in_shape(aShape, anElement): # import Part - if anElement.ShapeType == 'Solid' or anElement.ShapeType == 'CompSolid': + ele_st = anElement.ShapeType + if ele_st == 'Solid' or ele_st == 'CompSolid': for index, solid in enumerate(aShape.Solids): # print(is_same_geometry(solid, anElement)) if is_same_geometry(solid, anElement): # print(index) # Part.show(aShape.Solids[index]) - return index + ele = ele_st + str(index + 1) + return ele FreeCAD.Console.PrintError('Solid ' + str(anElement) + ' not found in: ' + str(aShape) + '\n') - if anElement.ShapeType == 'Solid' and aShape.ShapeType == 'Solid': + if ele_st == 'Solid' and aShape.ShapeType == 'Solid': print('We have been searching for a Solid in a Solid and we have not found it. In most cases this should be searching for a Solid inside a CompSolid. Check the ShapeType of your Part to mesh.') # Part.show(anElement) # Part.show(aShape) - elif anElement.ShapeType == 'Face' or anElement.ShapeType == 'Shell': + elif ele_st == 'Face' or ele_st == 'Shell': for index, face in enumerate(aShape.Faces): # print(is_same_geometry(face, anElement)) if is_same_geometry(face, anElement): # print(index) # Part.show(aShape.Faces[index]) - return index - elif anElement.ShapeType == 'Edge' or anElement.ShapeType == 'Wire': - for index, face in enumerate(aShape.Edges): - # print(is_same_geometry(face, anElement)) - if is_same_geometry(face, anElement): + ele = ele_st + str(index + 1) + return ele + elif ele_st == 'Edge' or ele_st == 'Wire': + for index, edge in enumerate(aShape.Edges): + # print(is_same_geometry(edge, anElement)) + if is_same_geometry(edge, anElement): # print(index) # Part.show(aShape.Edges[index]) - return index - elif anElement.ShapeType == 'Vertex': - for index, face in enumerate(aShape.Vertexes): - # print(is_same_geometry(face, anElement)) - if is_same_geometry(face, anElement): + ele = ele_st + str(index + 1) + return ele + elif ele_st == 'Vertex': + for index, vertex in enumerate(aShape.Vertexes): + # print(is_same_geometry(vertex, anElement)) + if is_same_geometry(vertex, anElement): # print(index) # Part.show(aShape.Vertexes[index]) - return index - elif anElement.ShapeType == 'Compound': + ele = ele_st + str(index + 1) + return ele + elif ele_st == 'Compound': FreeCAD.Console.PrintError('Compound is not supported.\n') diff --git a/src/Mod/Fem/FemSelectionObserver.py b/src/Mod/Fem/FemSelectionObserver.py index 05385bf647..7e9dc9c5c7 100644 --- a/src/Mod/Fem/FemSelectionObserver.py +++ b/src/Mod/Fem/FemSelectionObserver.py @@ -24,12 +24,12 @@ __title__ = "Selection Observer" __author__ = "Bernd Hahnebach" __url__ = "http://www.freecadweb.org" +## \addtogroup FEM +# @{ import FreeCAD import FreeCADGui -## \addtogroup FEM -# @{ class FemSelectionObserver: '''FemSelectionObserver''' diff --git a/src/Mod/Fem/FemShellThickness.py b/src/Mod/Fem/FemShellThickness.py index 617b15712e..89c95c9629 100644 --- a/src/Mod/Fem/FemShellThickness.py +++ b/src/Mod/Fem/FemShellThickness.py @@ -24,12 +24,12 @@ __title__ = "FemShellThickness" __author__ = "Bernd Hahnebach" __url__ = "http://www.freecadweb.org" +## \addtogroup FEM +# @{ import FreeCAD import _FemShellThickness -## \addtogroup FEM -# @{ def makeFemShellThickness(thickness=20.0, name="ShellThickness"): '''makeFemShellThickness([thickness], [name]): creates an shellthickness object to define a plate thickness''' diff --git a/src/Mod/Fem/FemSolverCalculix.py b/src/Mod/Fem/FemSolverCalculix.py index fcc0e87d71..c4c70f013d 100644 --- a/src/Mod/Fem/FemSolverCalculix.py +++ b/src/Mod/Fem/FemSolverCalculix.py @@ -24,12 +24,12 @@ __title__ = "FemSolverCalculix" __author__ = "Bernd Hahnebach" __url__ = "http://www.freecadweb.org" +## \addtogroup FEM +# @{ import FreeCAD import _FemSolverCalculix -## \addtogroup FEM -# @{ def makeFemSolverCalculix(name="CalculiX"): '''makeSolverCalculix(name): makes a Calculix solver object''' diff --git a/src/Mod/Fem/FemSolverZ88.py b/src/Mod/Fem/FemSolverZ88.py index ce1ed5a10a..70c483b49c 100644 --- a/src/Mod/Fem/FemSolverZ88.py +++ b/src/Mod/Fem/FemSolverZ88.py @@ -24,12 +24,12 @@ __title__ = "FemSolverZ88" __author__ = "Bernd Hahnebach" __url__ = "http://www.freecadweb.org" +## \addtogroup FEM +# @{ import FreeCAD import _FemSolverZ88 -## \addtogroup FEM -# @{ def makeFemSolverZ88(name="Z88"): '''makeSolverZ88(name): makes a Z88 solver object''' diff --git a/src/Mod/Fem/FemTools.py b/src/Mod/Fem/FemTools.py index 70bbd4f0f1..3d11fb989d 100644 --- a/src/Mod/Fem/FemTools.py +++ b/src/Mod/Fem/FemTools.py @@ -21,17 +21,16 @@ # * * # *************************************************************************** - __title__ = "Fem Tools super class" __author__ = "Przemo Firszt, Bernd Hahnebach" __url__ = "http://www.freecadweb.org" +## \addtogroup FEM +# @{ import FreeCAD from PySide import QtCore -## \addtogroup FEM -# @{ class FemTools(QtCore.QRunnable, QtCore.QObject): ## The constructor diff --git a/src/Mod/Fem/FemToolsCcx.py b/src/Mod/Fem/FemToolsCcx.py index 147ba24548..58540573ad 100644 --- a/src/Mod/Fem/FemToolsCcx.py +++ b/src/Mod/Fem/FemToolsCcx.py @@ -21,11 +21,12 @@ # * * # *************************************************************************** - __title__ = "FemToolsCcx" __author__ = "Przemo Firszt, Bernd Hahnebach" __url__ = "http://www.freecadweb.org" +## \addtogroup FEM +# @{ import FreeCAD import FemTools @@ -33,8 +34,6 @@ from PySide import QtCore if FreeCAD.GuiUp: from PySide import QtGui -## \addtogroup FEM -# @{ class FemToolsCcx(FemTools.FemTools): @@ -242,6 +241,9 @@ class FemToolsCcx(FemTools.FemTools): for m in self.analysis.Member: if m.isDerivedFrom("Fem::FemResultObject"): self.results_present = True + break + else: + FreeCAD.Console.PrintError('FEM: No result object in active Analysis.\n') else: raise Exception('FEM: No results found at {}!'.format(frd_result_file)) @@ -256,7 +258,7 @@ class FemToolsCcx(FemTools.FemTools): else: raise Exception('FEM: No .dat results found at {}!'.format(dat_result_file)) if mode_frequencies: - print(mode_frequencies) + # print(mode_frequencies) for m in self.analysis.Member: if m.isDerivedFrom("Fem::FemResultObject") and m.Eigenmode > 0: for mf in mode_frequencies: diff --git a/src/Mod/Fem/FemToolsZ88.py b/src/Mod/Fem/FemToolsZ88.py index 0d24c92c60..6f68220325 100644 --- a/src/Mod/Fem/FemToolsZ88.py +++ b/src/Mod/Fem/FemToolsZ88.py @@ -20,19 +20,18 @@ # * * # *************************************************************************** - __title__ = "FemToolsZ88" __author__ = "Bernd Hahnebach" __url__ = "http://www.freecadweb.org" +## \addtogroup FEM +# @{ import FreeCAD import FemTools from PySide import QtCore from PySide.QtGui import QApplication -## \addtogroup FEM -# @{ class FemToolsZ88(FemTools.FemTools): @@ -110,8 +109,7 @@ class FemToolsZ88(FemTools.FemTools): self.z88_binary = z88_path else: if not z88_binary: - self.fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/Z88") - z88_binary = self.fem_prefs.GetString("z88BinaryPath", "") + z88_binary = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/Z88").GetString("z88BinaryPath", "") if not z88_binary: if system() == "Linux": z88_binary = "z88r" diff --git a/src/Mod/Fem/Gui/AppFemGui.cpp b/src/Mod/Fem/Gui/AppFemGui.cpp index ee950db3a4..e8bb5266d1 100644 --- a/src/Mod/Fem/Gui/AppFemGui.cpp +++ b/src/Mod/Fem/Gui/AppFemGui.cpp @@ -35,6 +35,7 @@ #include "PropertyFemMeshItem.h" #include "DlgSettingsFemGeneralImp.h" #include "DlgSettingsFemCcxImp.h" +#include "DlgSettingsFemGmshImp.h" #include "DlgSettingsFemZ88Imp.h" #include "ViewProviderFemMesh.h" #include "ViewProviderFemMeshShape.h" @@ -109,6 +110,7 @@ PyMODINIT_FUNC initFemGui() FemGui::ViewProviderFemAnalysis ::init(); FemGui::ViewProviderFemAnalysisPython ::init(); FemGui::ViewProviderFemMesh ::init(); + FemGui::ViewProviderFemMeshPython ::init(); FemGui::ViewProviderFemMeshShape ::init(); FemGui::ViewProviderFemMeshShapeNetgen ::init(); FemGui::ViewProviderSolver ::init(); @@ -153,6 +155,7 @@ PyMODINIT_FUNC initFemGui() // register preferences pages new Gui::PrefPageProducer (QT_TRANSLATE_NOOP("QObject","FEM")); new Gui::PrefPageProducer (QT_TRANSLATE_NOOP("QObject","FEM")); + new Gui::PrefPageProducer (QT_TRANSLATE_NOOP("QObject","FEM")); new Gui::PrefPageProducer (QT_TRANSLATE_NOOP("QObject","FEM")); // add resources and reloads the translators diff --git a/src/Mod/Fem/Gui/CMakeLists.txt b/src/Mod/Fem/Gui/CMakeLists.txt index 661b7835b2..6c4dc26414 100755 --- a/src/Mod/Fem/Gui/CMakeLists.txt +++ b/src/Mod/Fem/Gui/CMakeLists.txt @@ -44,6 +44,7 @@ SOURCE_GROUP("Python" FILES ${Python_SRCS}) set(FemGui_MOC_HDRS DlgSettingsFemCcxImp.h DlgSettingsFemGeneralImp.h + DlgSettingsFemGmshImp.h DlgSettingsFemZ88Imp.h PropertyFemMeshItem.h TaskObjectName.h @@ -83,6 +84,7 @@ SOURCE_GROUP("Moc" FILES ${FemGui_MOC_SRCS}) set(FemGui_UIC_SRCS DlgSettingsFemCcx.ui DlgSettingsFemGeneral.ui + DlgSettingsFemGmsh.ui DlgSettingsFemZ88.ui TaskCreateNodeSet.ui TaskObjectName.ui @@ -125,6 +127,9 @@ SET(FemGui_DLG_SRCS DlgSettingsFemGeneral.ui DlgSettingsFemGeneralImp.cpp DlgSettingsFemGeneralImp.h + DlgSettingsFemGmsh.ui + DlgSettingsFemGmshImp.cpp + DlgSettingsFemGmshImp.h DlgSettingsFemZ88.ui DlgSettingsFemZ88Imp.cpp DlgSettingsFemZ88Imp.h diff --git a/src/Mod/Fem/Gui/DlgSettingsFemGmsh.ui b/src/Mod/Fem/Gui/DlgSettingsFemGmsh.ui new file mode 100644 index 0000000000..3be9f8bff2 --- /dev/null +++ b/src/Mod/Fem/Gui/DlgSettingsFemGmsh.ui @@ -0,0 +1,230 @@ + + + FemGui::DlgSettingsFemGmshImp + + + + 0 + 0 + 372 + 144 + + + + GMSH + + + + + + + + + 0 + 0 + + + + Qt::LeftToRight + + + GMSH + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + QLayout::SetNoConstraint + + + + + + + Use standard gmsh binary path + + + true + + + UseStandardGmshLocation + + + Mod/Fem/Gmsh + + + + + + + GMSH binary + + + + + + + false + + + + 100 + 0 + + + + gmsh binary path + + + + + + + false + + + + 0 + 0 + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 0 + 0 + + + + Leave blank to use default gmsh binary file + + + gmshBinaryPath + + + Mod/Fem/Gmsh + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Gui::PrefCheckBox + QCheckBox +
Gui/PrefWidgets.h
+
+ + Gui::FileChooser + QWidget +
Gui/FileDialog.h
+
+ + Gui::PrefFileChooser + Gui::FileChooser +
Gui/PrefWidgets.h
+
+
+ + + + + + cb_gmsh_binary_std + toggled(bool) + l_gmsh_binary_path + setEnabled(bool) + + + 406 + 45 + + + 148 + 68 + + + + + cb_gmsh_binary_std + toggled(bool) + fc_gmsh_binary_path + setEnabled(bool) + + + 406 + 45 + + + 406 + 68 + + + + + cb_gmsh_binary_std + toggled(bool) + l_gmsh_binary_path + setDisabled(bool) + + + 406 + 45 + + + 148 + 68 + + + + + cb_gmsh_binary_std + toggled(bool) + fc_gmsh_binary_path + setDisabled(bool) + + + 406 + 45 + + + 406 + 68 + + + + +
diff --git a/src/Mod/Fem/Gui/DlgSettingsFemGmshImp.cpp b/src/Mod/Fem/Gui/DlgSettingsFemGmshImp.cpp new file mode 100644 index 0000000000..adbd5fb9aa --- /dev/null +++ b/src/Mod/Fem/Gui/DlgSettingsFemGmshImp.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (c) 2016 FreeCAD Developers * + * Author: Bernd Hahnebach * + * Based on src/Mod/Fem/Gui/DlgSettingsFemCcxImp.cpp * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#include "Gui/Application.h" +#include "DlgSettingsFemGmshImp.h" +#include + +using namespace FemGui; + +DlgSettingsFemGmshImp::DlgSettingsFemGmshImp( QWidget* parent ) + : PreferencePage( parent ) +{ + this->setupUi(this); +} + +DlgSettingsFemGmshImp::~DlgSettingsFemGmshImp() +{ + // no need to delete child widgets, Qt does it all for us +} + +void DlgSettingsFemGmshImp::saveSettings() +{ + cb_gmsh_binary_std->onSave(); + fc_gmsh_binary_path->onSave(); +} + +void DlgSettingsFemGmshImp::loadSettings() +{ + cb_gmsh_binary_std->onRestore(); + fc_gmsh_binary_path->onRestore(); +} + +/** + * Sets the strings of the subwidgets using the current language. + */ +void DlgSettingsFemGmshImp::changeEvent(QEvent *e) +{ + if (e->type() == QEvent::LanguageChange) { + } + else { + QWidget::changeEvent(e); + } +} + +#include "moc_DlgSettingsFemGmshImp.cpp" diff --git a/src/Mod/Fem/Gui/DlgSettingsFemGmshImp.h b/src/Mod/Fem/Gui/DlgSettingsFemGmshImp.h new file mode 100644 index 0000000000..33596da736 --- /dev/null +++ b/src/Mod/Fem/Gui/DlgSettingsFemGmshImp.h @@ -0,0 +1,50 @@ + /************************************************************************** + * Copyright (c) 2016 FreeCAD Developers * + * Author: Bernd Hahnebach * + * Based on src/Mod/Fem/Gui/DlgSettingsFemCcx.h * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef FEMGUI_DLGSETTINGSFEMGMSHIMP_H +#define FEMGUI_DLGSETTINGSFEMGMSHIMP_H + +#include "ui_DlgSettingsFemGmsh.h" +#include + +namespace FemGui { + +class DlgSettingsFemGmshImp : public Gui::Dialog::PreferencePage, public Ui_DlgSettingsFemGmshImp +{ + Q_OBJECT + +public: + DlgSettingsFemGmshImp( QWidget* parent = 0 ); + ~DlgSettingsFemGmshImp(); + +protected: + void saveSettings(); + void loadSettings(); + void changeEvent(QEvent *e); +}; + +} // namespace FemGui + +#endif // FEMGUI_DLGSETTINGSFEMGMSHIMP_H diff --git a/src/Mod/Fem/Gui/Resources/Fem.qrc b/src/Mod/Fem/Gui/Resources/Fem.qrc index cffc4a2a73..7eb3d8a58d 100755 --- a/src/Mod/Fem/Gui/Resources/Fem.qrc +++ b/src/Mod/Fem/Gui/Resources/Fem.qrc @@ -1,6 +1,8 @@ icons/fem-fem-mesh-from-shape.svg + icons/fem-fem-mesh-gmsh-from-shape.svg + icons/fem-fem-mesh-netgen-from-shape.svg icons/fem-fem-mesh-create-node-by-poly.svg icons/fem-analysis.svg icons/fem-cfd-analysis.svg diff --git a/src/Mod/Fem/Gui/Resources/icons/fem-fem-mesh-gmsh-from-shape.svg b/src/Mod/Fem/Gui/Resources/icons/fem-fem-mesh-gmsh-from-shape.svg new file mode 100644 index 0000000000..d9fd5d8cf4 --- /dev/null +++ b/src/Mod/Fem/Gui/Resources/icons/fem-fem-mesh-gmsh-from-shape.svg @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + G + G + + diff --git a/src/Mod/Fem/Gui/Resources/icons/fem-fem-mesh-netgen-from-shape.svg b/src/Mod/Fem/Gui/Resources/icons/fem-fem-mesh-netgen-from-shape.svg new file mode 100644 index 0000000000..34c5bb60ef --- /dev/null +++ b/src/Mod/Fem/Gui/Resources/icons/fem-fem-mesh-netgen-from-shape.svg @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + N + N + + diff --git a/src/Mod/Fem/Gui/ViewProviderFemMesh.cpp b/src/Mod/Fem/Gui/ViewProviderFemMesh.cpp index 9ba4d148a9..689a96ecec 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemMesh.cpp +++ b/src/Mod/Fem/Gui/ViewProviderFemMesh.cpp @@ -2464,3 +2464,15 @@ void ViewProviderFEMMeshBuilder::createMesh(const App::Property* prop, } + + +// Python feature ----------------------------------------------------------------------- + +namespace Gui { +/// @cond DOXERR +PROPERTY_SOURCE_TEMPLATE(FemGui::ViewProviderFemMeshPython, FemGui::ViewProviderFemMesh) +/// @endcond + +// explicit template instantiation +template class FemGuiExport ViewProviderPythonFeatureT; +} diff --git a/src/Mod/Fem/Gui/ViewProviderFemMesh.h b/src/Mod/Fem/Gui/ViewProviderFemMesh.h index d2b9d35ce8..6ba9f90c76 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemMesh.h +++ b/src/Mod/Fem/Gui/ViewProviderFemMesh.h @@ -26,6 +26,7 @@ #include #include +#include #include @@ -163,6 +164,9 @@ private: class Private; }; +typedef Gui::ViewProviderPythonFeatureT ViewProviderFemMeshPython; + + } //namespace FemGui diff --git a/src/Mod/Fem/Gui/ViewProviderFemMeshPy.xml b/src/Mod/Fem/Gui/ViewProviderFemMeshPy.xml index 75814be5f6..866ebb85ba 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemMeshPy.xml +++ b/src/Mod/Fem/Gui/ViewProviderFemMeshPy.xml @@ -1,13 +1,13 @@ diff --git a/src/Mod/Fem/Gui/Workbench.cpp b/src/Mod/Fem/Gui/Workbench.cpp index e785f27bd1..543289e9bd 100755 --- a/src/Mod/Fem/Gui/Workbench.cpp +++ b/src/Mod/Fem/Gui/Workbench.cpp @@ -58,13 +58,14 @@ Gui::ToolBarItem* Workbench::setupToolBars() const *fem << "Fem_Analysis" << "Fem_SolverCalculix" // << "Fem_SolverZ88" - << "Fem_MeshFromShape" + << "Fem_MeshNetgenFromShape" + << "Fem_MeshGmshFromShape" << "Fem_MechanicalMaterial" << "Fem_MaterialMechanicalNonlinear" << "Fem_BeamSection" << "Fem_ShellThickness" << "Separator" - << "Fem_CreateNodesSet" + //<< "Fem_CreateNodesSet" << "Separator" << "Fem_ConstraintFixed" << "Fem_ConstraintDisplacement" @@ -75,12 +76,11 @@ Gui::ToolBarItem* Workbench::setupToolBars() const << "Fem_ConstraintSelfWeight" << "Fem_ConstraintForce" << "Fem_ConstraintPressure" - << "Separator" - << "Fem_ConstraintBearing" - << "Fem_ConstraintGear" - << "Fem_ConstraintPulley" - << "Separator" - << "Fem_ConstraintFluidBoundary" + //<< "Separator" + //<< "Fem_ConstraintBearing" + //<< "Fem_ConstraintGear" + //<< "Fem_ConstraintPulley" + //<< "Fem_ConstraintFluidBoundary" << "Separator" << "Fem_ConstraintTemperature" << "Fem_ConstraintHeatflux" @@ -119,7 +119,8 @@ Gui::MenuItem* Workbench::setupMenuBar() const *fem << "Fem_Analysis" << "Fem_SolverCalculix" << "Fem_SolverZ88" - << "Fem_MeshFromShape" + << "Fem_MeshNetgenFromShape" + << "Fem_MeshGmshFromShape" << "Fem_MechanicalMaterial" << "Fem_MaterialMechanicalNonlinear" << "Fem_BeamSection" diff --git a/src/Mod/Fem/Init.py b/src/Mod/Fem/Init.py index 0dcfbc573b..865ebb09c5 100644 --- a/src/Mod/Fem/Init.py +++ b/src/Mod/Fem/Init.py @@ -25,10 +25,8 @@ # * Juergen Riegel 2002 * # ***************************************************************************/ - import FreeCAD - FreeCAD.addExportType("TetGen file (*.poly)", "convert2TetGen") FreeCAD.addImportType("FEM formats (*.unv *.med *.dat *.bdf)", "Fem") if("BUILD_FEM_VTK" in FreeCAD.__cmake__): diff --git a/src/Mod/Fem/InitGui.py b/src/Mod/Fem/InitGui.py index b144fb31ca..4aa64f99c8 100644 --- a/src/Mod/Fem/InitGui.py +++ b/src/Mod/Fem/InitGui.py @@ -29,7 +29,6 @@ #* Juergen Riegel 2002 * #***************************************************************************/ - import FreeCAD import FreeCADGui @@ -50,8 +49,8 @@ class FemWorkbench (Workbench): import _CommandRunSolver import _CommandPurgeResults import _CommandControlSolver - - import _CommandMeshFromShape + import _CommandMeshGmshFromShape + import _CommandMeshNetgenFromShape import _CommandAnalysis import _CommandShellThickness import _CommandBeamSection diff --git a/src/Mod/Fem/MechanicalMaterial.py b/src/Mod/Fem/MechanicalMaterial.py index cf4355bf79..a54ab9ef2a 100644 --- a/src/Mod/Fem/MechanicalMaterial.py +++ b/src/Mod/Fem/MechanicalMaterial.py @@ -24,12 +24,12 @@ __title__ = "MechanicalMaterial" __author__ = "Juergen Riegel, Bernd Hahnebach" __url__ = "http://www.freecadweb.org" +## \addtogroup FEM +# @{ import FreeCAD import _MechanicalMaterial -## \addtogroup FEM -# @{ def makeMechanicalMaterial(name): '''makeMaterial(name): makes an Material diff --git a/src/Mod/Fem/TaskPanelFemMeshGmsh.ui b/src/Mod/Fem/TaskPanelFemMeshGmsh.ui new file mode 100644 index 0000000000..48f738a8ac --- /dev/null +++ b/src/Mod/Fem/TaskPanelFemMeshGmsh.ui @@ -0,0 +1,203 @@ + + + GmshMesh + + + + 0 + 0 + 400 + 413 + + + + FEM Mesh by GMSH + + + + + + + 16777215 + 1677215 + + + + FEM Mesh Parameter + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Mesh element dimension: + + + + + + + + + + Mesh element order: + + + + + + + + + + Max element size (0.0 = Auto): + + + + + + + + 0 + 0 + + + + + 80 + 20 + + + + 0.0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 1.000000000000000 + + + 1000000000.000000000000000 + + + mm + + + 2 + + + 0.000000000000000 + + + + + + + Min element size (0.0 = Auto): + + + + + + + + 0 + 0 + + + + + 80 + 20 + + + + 0.0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 1.000000000000000 + + + 1000000000.000000000000000 + + + mm + + + 2 + + + 0.000000000000000 + + + + + + + + + + + + + 16777215 + 1677215 + + + + GMSH + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + + + + 12 + + + + Time: + + + + + + + QTextEdit::NoWrap + + + + + + + + + + + + + + + Gui::InputField + QLineEdit +
Gui/InputField.h
+
+
+ + +
diff --git a/src/Mod/Fem/_CommandAnalysis.py b/src/Mod/Fem/_CommandAnalysis.py index 779e979b47..11803349ae 100644 --- a/src/Mod/Fem/_CommandAnalysis.py +++ b/src/Mod/Fem/_CommandAnalysis.py @@ -29,10 +29,8 @@ __url__ = "http://www.freecadweb.org" import FreeCAD from FemCommands import FemCommands - -if FreeCAD.GuiUp: - import FreeCADGui - from PySide import QtCore +import FreeCADGui +from PySide import QtCore class _CommandAnalysis(FemCommands): @@ -58,11 +56,6 @@ class _CommandAnalysis(FemCommands): if (len(sel) == 1): if(sel[0].isDerivedFrom("Fem::FemMeshObject")): FreeCADGui.doCommand("FemGui.getActiveAnalysis().Member = FemGui.getActiveAnalysis().Member + [App.activeDocument()." + sel[0].Name + "]") - if(sel[0].isDerivedFrom("Part::Feature")): - FreeCADGui.doCommand("App.activeDocument().addObject('Fem::FemMeshShapeNetgenObject', '" + sel[0].Name + "_Mesh')") - FreeCADGui.doCommand("App.activeDocument().ActiveObject.Shape = App.activeDocument()." + sel[0].Name) - FreeCADGui.doCommand("FemGui.getActiveAnalysis().Member = FemGui.getActiveAnalysis().Member + [App.activeDocument().ActiveObject]") - FreeCADGui.doCommand("Gui.activeDocument().setEdit(App.ActiveDocument.ActiveObject.Name)") FreeCADGui.Selection.clearSelection() if FreeCAD.GuiUp: diff --git a/src/Mod/Fem/_CommandBeamSection.py b/src/Mod/Fem/_CommandBeamSection.py index 299b1e4bbb..8f411756fb 100644 --- a/src/Mod/Fem/_CommandBeamSection.py +++ b/src/Mod/Fem/_CommandBeamSection.py @@ -29,10 +29,8 @@ __url__ = "http://www.freecadweb.org" import FreeCAD from FemCommands import FemCommands - -if FreeCAD.GuiUp: - import FreeCADGui - from PySide import QtCore +import FreeCADGui +from PySide import QtCore class _CommandBeamSection(FemCommands): diff --git a/src/Mod/Fem/_CommandConstraintSelfWeight.py b/src/Mod/Fem/_CommandConstraintSelfWeight.py index b41ea042a6..604c653d05 100644 --- a/src/Mod/Fem/_CommandConstraintSelfWeight.py +++ b/src/Mod/Fem/_CommandConstraintSelfWeight.py @@ -29,10 +29,8 @@ __url__ = "http://www.freecadweb.org" import FreeCAD from FemCommands import FemCommands - -if FreeCAD.GuiUp: - import FreeCADGui - from PySide import QtCore +import FreeCADGui +from PySide import QtCore class _CommandConstraintSelfWeight(FemCommands): diff --git a/src/Mod/Fem/_CommandControlSolver.py b/src/Mod/Fem/_CommandControlSolver.py index e2ddca16aa..b5029bb138 100644 --- a/src/Mod/Fem/_CommandControlSolver.py +++ b/src/Mod/Fem/_CommandControlSolver.py @@ -29,10 +29,8 @@ __url__ = "http://www.freecadweb.org" import FreeCAD from FemCommands import FemCommands - -if FreeCAD.GuiUp: - import FreeCADGui - from PySide import QtCore +import FreeCADGui +from PySide import QtCore class _CommandControlSolver(FemCommands): diff --git a/src/Mod/Fem/_CommandMaterialMechanicalNonlinear.py b/src/Mod/Fem/_CommandMaterialMechanicalNonlinear.py index b27f7f98a2..2e78978fa6 100644 --- a/src/Mod/Fem/_CommandMaterialMechanicalNonlinear.py +++ b/src/Mod/Fem/_CommandMaterialMechanicalNonlinear.py @@ -29,11 +29,9 @@ __url__ = "http://www.freecadweb.org" import FreeCAD from FemCommands import FemCommands - -if FreeCAD.GuiUp: - import FreeCADGui - import FemGui - from PySide import QtCore +import FreeCADGui +import FemGui +from PySide import QtCore class _CommandMaterialMechanicalNonlinear(FemCommands): diff --git a/src/Mod/Fem/_CommandMechanicalMaterial.py b/src/Mod/Fem/_CommandMechanicalMaterial.py index df00f90bcf..97e312318b 100644 --- a/src/Mod/Fem/_CommandMechanicalMaterial.py +++ b/src/Mod/Fem/_CommandMechanicalMaterial.py @@ -29,11 +29,9 @@ __url__ = "http://www.freecadweb.org" import FreeCAD from FemCommands import FemCommands - -if FreeCAD.GuiUp: - import FreeCADGui - import FemGui - from PySide import QtCore +import FreeCADGui +import FemGui +from PySide import QtCore class _CommandMechanicalMaterial(FemCommands): diff --git a/src/Mod/Fem/_CommandMeshGmshFromShape.py b/src/Mod/Fem/_CommandMeshGmshFromShape.py new file mode 100644 index 0000000000..c279232880 --- /dev/null +++ b/src/Mod/Fem/_CommandMeshGmshFromShape.py @@ -0,0 +1,65 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2016 - 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__ = "Command GMSH Mesh From Shape" +__author__ = "Bernd Hahnebach" +__url__ = "http://www.freecadweb.org" + +## @package CommandMeshGmshFromShape +# \ingroup FEM + +import FreeCAD +from FemCommands import FemCommands +import FreeCADGui +import FemGui +from PySide import QtCore + + +class _CommandMeshGmshFromShape(FemCommands): + # the Fem_MeshGmshFromShape command definition + def __init__(self): + super(_CommandMeshGmshFromShape, self).__init__() + self.resources = {'Pixmap': 'fem-fem-mesh-gmsh-from-shape', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Fem_MeshGmshFromShape", "FEM mesh from shape by GMSH"), + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Fem_MeshGmshFromShape", "Create a FEM mesh from a shape by GMSH mesher")} + self.is_active = 'with_part_feature' + + def Activated(self): + FreeCAD.ActiveDocument.openTransaction("Create FEM mesh by GMSH") + FreeCADGui.addModule("FemGui") + sel = FreeCADGui.Selection.getSelection() + if (len(sel) == 1): + if(sel[0].isDerivedFrom("Part::Feature")): + mesh_obj_name = sel[0].Name + "_Mesh" + FreeCADGui.addModule("FemMeshGmsh") + FreeCADGui.doCommand("FemMeshGmsh.makeFemMeshGmsh('" + mesh_obj_name + "')") + FreeCADGui.doCommand("App.ActiveDocument.ActiveObject.Part = App.ActiveDocument." + sel[0].Name) + if FemGui.getActiveAnalysis(): + FreeCADGui.addModule("FemGui") + FreeCADGui.doCommand("FemGui.getActiveAnalysis().Member = FemGui.getActiveAnalysis().Member + [App.ActiveDocument.ActiveObject]") + FreeCADGui.doCommand("Gui.ActiveDocument.setEdit(App.ActiveDocument.ActiveObject.Name)") + + FreeCADGui.Selection.clearSelection() + + +if FreeCAD.GuiUp: + FreeCADGui.addCommand('Fem_MeshGmshFromShape', _CommandMeshGmshFromShape()) diff --git a/src/Mod/Fem/_CommandMeshFromShape.py b/src/Mod/Fem/_CommandMeshNetgenFromShape.py similarity index 79% rename from src/Mod/Fem/_CommandMeshFromShape.py rename to src/Mod/Fem/_CommandMeshNetgenFromShape.py index 11578a7caa..b44a161db5 100644 --- a/src/Mod/Fem/_CommandMeshFromShape.py +++ b/src/Mod/Fem/_CommandMeshNetgenFromShape.py @@ -20,32 +20,30 @@ # * * # *************************************************************************** -__title__ = "Command Mesh From Shape" +__title__ = "Command Mesh Netgen From Shape" __author__ = "Juergen Riegel" __url__ = "http://www.freecadweb.org" -## @package CommandMeshFromShape +## @package CommandMeshNetgenFromShape # \ingroup FEM import FreeCAD from FemCommands import FemCommands - -if FreeCAD.GuiUp: - import FreeCADGui - from PySide import QtCore +import FreeCADGui +from PySide import QtCore -class _CommandMeshFromShape(FemCommands): - # the Fem_MeshFromShape command definition +class _CommandMeshNetgenFromShape(FemCommands): + # the Fem_MeshNetgenFromShape command definition def __init__(self): - super(_CommandMeshFromShape, self).__init__() - self.resources = {'Pixmap': 'fem-fem-mesh-from-shape', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Fem_MeshFromShape", "FEM mesh from shape"), - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Fem_MeshFromShape", "Create a FEM volume mesh from a solid shape")} + super(_CommandMeshNetgenFromShape, self).__init__() + self.resources = {'Pixmap': 'fem-fem-mesh-netgen-from-shape', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Fem_MeshFromShape", "FEM mesh from shape by Netgen"), + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Fem_MeshFromShape", "Create a FEM volume mesh from a solid or face shape by Netgen internal mesher")} self.is_active = 'with_part_feature' def Activated(self): - FreeCAD.ActiveDocument.openTransaction("Create FEM mesh") + FreeCAD.ActiveDocument.openTransaction("Create FEM mesh Netgen") FreeCADGui.addModule("FemGui") sel = FreeCADGui.Selection.getSelection() if (len(sel) == 1): @@ -58,4 +56,4 @@ class _CommandMeshFromShape(FemCommands): if FreeCAD.GuiUp: - FreeCADGui.addCommand('Fem_MeshFromShape', _CommandMeshFromShape()) + FreeCADGui.addCommand('Fem_MeshNetgenFromShape', _CommandMeshNetgenFromShape()) diff --git a/src/Mod/Fem/_CommandPurgeResults.py b/src/Mod/Fem/_CommandPurgeResults.py index f6137a283d..edd60fcdda 100644 --- a/src/Mod/Fem/_CommandPurgeResults.py +++ b/src/Mod/Fem/_CommandPurgeResults.py @@ -30,10 +30,8 @@ __url__ = "http://www.freecadweb.org" import FreeCAD from FemCommands import FemCommands import FemTools - -if FreeCAD.GuiUp: - import FreeCADGui - from PySide import QtCore +import FreeCADGui +from PySide import QtCore class _CommandPurgeResults(FemCommands): diff --git a/src/Mod/Fem/_CommandRunSolver.py b/src/Mod/Fem/_CommandRunSolver.py index b705108bfd..1476c46bdb 100644 --- a/src/Mod/Fem/_CommandRunSolver.py +++ b/src/Mod/Fem/_CommandRunSolver.py @@ -29,10 +29,8 @@ __url__ = "http://www.freecadweb.org" import FreeCAD from FemCommands import FemCommands - -if FreeCAD.GuiUp: - import FreeCADGui - from PySide import QtCore, QtGui +import FreeCADGui +from PySide import QtCore, QtGui class _CommandRunSolver(FemCommands): diff --git a/src/Mod/Fem/_CommandShellThickness.py b/src/Mod/Fem/_CommandShellThickness.py index ffd5fb2165..1b77f7642c 100644 --- a/src/Mod/Fem/_CommandShellThickness.py +++ b/src/Mod/Fem/_CommandShellThickness.py @@ -29,10 +29,8 @@ __url__ = "http://www.freecadweb.org" import FreeCAD from FemCommands import FemCommands - -if FreeCAD.GuiUp: - import FreeCADGui - from PySide import QtCore +import FreeCADGui +from PySide import QtCore class _CommandShellThickness(FemCommands): diff --git a/src/Mod/Fem/_CommandShowResult.py b/src/Mod/Fem/_CommandShowResult.py index 793e3ef709..017e38d4cf 100644 --- a/src/Mod/Fem/_CommandShowResult.py +++ b/src/Mod/Fem/_CommandShowResult.py @@ -29,10 +29,8 @@ __url__ = "http://www.freecadweb.org" import FreeCAD from FemCommands import FemCommands - -if FreeCAD.GuiUp: - import FreeCADGui - from PySide import QtCore, QtGui +import FreeCADGui +from PySide import QtCore, QtGui class _CommandShowResult(FemCommands): diff --git a/src/Mod/Fem/_CommandSolverCalculix.py b/src/Mod/Fem/_CommandSolverCalculix.py index c075f7ad52..768e10b8fe 100644 --- a/src/Mod/Fem/_CommandSolverCalculix.py +++ b/src/Mod/Fem/_CommandSolverCalculix.py @@ -29,11 +29,9 @@ __url__ = "http://www.freecadweb.org" import FreeCAD from FemCommands import FemCommands - -if FreeCAD.GuiUp: - import FreeCADGui - import FemGui - from PySide import QtCore +import FreeCADGui +import FemGui +from PySide import QtCore class _CommandSolverCalculix(FemCommands): diff --git a/src/Mod/Fem/_CommandSolverZ88.py b/src/Mod/Fem/_CommandSolverZ88.py index e3bd8738bb..1a1c5db34e 100644 --- a/src/Mod/Fem/_CommandSolverZ88.py +++ b/src/Mod/Fem/_CommandSolverZ88.py @@ -29,10 +29,8 @@ __url__ = "http://www.freecadweb.org" import FreeCAD from FemCommands import FemCommands - -if FreeCAD.GuiUp: - import FreeCADGui - from PySide import QtCore +import FreeCADGui +from PySide import QtCore class _CommandSolverZ88(FemCommands): diff --git a/src/Mod/Fem/_FemBeamSection.py b/src/Mod/Fem/_FemBeamSection.py index 5a0df22f70..032c815a4f 100644 --- a/src/Mod/Fem/_FemBeamSection.py +++ b/src/Mod/Fem/_FemBeamSection.py @@ -27,6 +27,7 @@ __url__ = "http://www.freecadweb.org" ## @package FemBeamSection # \ingroup FEM + class _FemBeamSection: "The FemBeamSection object" def __init__(self, obj): diff --git a/src/Mod/Fem/_FemConstraintSelfWeight.py b/src/Mod/Fem/_FemConstraintSelfWeight.py index 57045fcd99..28a5e2b649 100644 --- a/src/Mod/Fem/_FemConstraintSelfWeight.py +++ b/src/Mod/Fem/_FemConstraintSelfWeight.py @@ -27,6 +27,7 @@ __url__ = "http://www.freecadweb.org" ## @package FemConstraintSelfWeight # \ingroup FEM + class _FemConstraintSelfWeight: "The FemConstraintSelfWeight object" def __init__(self, obj): diff --git a/src/Mod/Fem/_FemMaterialMechanicalNonlinear.py b/src/Mod/Fem/_FemMaterialMechanicalNonlinear.py index 769ad280e9..9d73865216 100644 --- a/src/Mod/Fem/_FemMaterialMechanicalNonlinear.py +++ b/src/Mod/Fem/_FemMaterialMechanicalNonlinear.py @@ -27,6 +27,7 @@ __url__ = "http://www.freecadweb.org" ## @package FemMaterialMechanicalNonLinear # \ingroup FEM + class _FemMaterialMechanicalNonlinear: "The FemMaterialMechanicalNonlinear object" def __init__(self, obj): diff --git a/src/Mod/Fem/_FemMeshGmsh.py b/src/Mod/Fem/_FemMeshGmsh.py new file mode 100644 index 0000000000..2dc0833e4b --- /dev/null +++ b/src/Mod/Fem/_FemMeshGmsh.py @@ -0,0 +1,69 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2016 - 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__ = "_FemMeshGmsh" +__author__ = "Bernd Hahnebach" +__url__ = "http://www.freecadweb.org" + +## @package FemMeshGmsh +# \ingroup FEM + + +class _FemMeshGmsh(): + """The Fem::FemMeshObject's Proxy python type, add GMSH specific properties + """ + + # they will be used from the task panel too, thus they need to be outside of the __init__ + known_element_dimensions = ['Auto', '1D', '2D', '3D'] + known_element_orders = ['Auto', '1st', '2nd'] + + def __init__(self, obj): + self.Type = "FemMeshGmsh" + self.Object = obj # keep a ref to the DocObj for nonGui usage + obj.Proxy = self # link between App::DocumentObject to this object + + obj.addProperty("App::PropertyLink", "Part", "FEM Mesh", "Part object to mesh") + obj.Part = None + + obj.addProperty("App::PropertyFloat", "ElementSizeMax", "FEM Mesh Params", "Max mesh element size (0.0 = infinity)") + obj.ElementSizeMax = 0.0 # will be 1e+22 + + obj.addProperty("App::PropertyFloat", "ElementSizeMin", "FEM Mesh Params", "Min mesh element size") + obj.ElementSizeMin = 0.0 + + obj.addProperty("App::PropertyEnumeration", "ElementDimension", "FEM Mesh Params", "Dimension of mesh elements (Auto = according ShapeType of part to mesh)") + obj.ElementDimension = _FemMeshGmsh.known_element_dimensions + obj.ElementDimension = 'Auto' # according ShapeType of Part to mesh + + obj.addProperty("App::PropertyEnumeration", "ElementOrder", "FEM Mesh Params", "Order of mesh elements (Auto will be 2nd)") + obj.ElementOrder = _FemMeshGmsh.known_element_orders + obj.ElementOrder = 'Auto' # = 2nd + + def execute(self, obj): + return + + def __getstate__(self): + return self.Type + + def __setstate__(self, state): + if state: + self.Type = state diff --git a/src/Mod/Fem/_FemShellThickness.py b/src/Mod/Fem/_FemShellThickness.py index 1a042fb7ed..7d9d94e3d8 100644 --- a/src/Mod/Fem/_FemShellThickness.py +++ b/src/Mod/Fem/_FemShellThickness.py @@ -27,6 +27,7 @@ __url__ = "http://www.freecadweb.org" ## @package FemShellThickness # \ingroup FEM + class _FemShellThickness: "The FemShellThickness object" def __init__(self, obj): diff --git a/src/Mod/Fem/_FemSolverCalculix.py b/src/Mod/Fem/_FemSolverCalculix.py index 3a7c89db77..0f54e5febb 100644 --- a/src/Mod/Fem/_FemSolverCalculix.py +++ b/src/Mod/Fem/_FemSolverCalculix.py @@ -42,7 +42,7 @@ class _FemSolverCalculix(): obj.addProperty("App::PropertyString", "SolverType", "Base", "Type of the solver", 1) # the 1 set the property to ReadOnly obj.SolverType = str(self.Type) - fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/General") + # fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/General") # not needed ATM ccx_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/Ccx") obj.addProperty("App::PropertyPath", "WorkingDir", "Fem", "Working directory for calculations, will only be used it is left blank in preferences") diff --git a/src/Mod/Fem/_MechanicalMaterial.py b/src/Mod/Fem/_MechanicalMaterial.py index 14fb9fa756..48f72dd113 100644 --- a/src/Mod/Fem/_MechanicalMaterial.py +++ b/src/Mod/Fem/_MechanicalMaterial.py @@ -27,6 +27,7 @@ __url__ = "http://www.freecadweb.org" ## @package MechanicalMaterial # \ingroup FEM + class _MechanicalMaterial: "The Material object" def __init__(self, obj): diff --git a/src/Mod/Fem/_TaskPanelFemMeshGmsh.py b/src/Mod/Fem/_TaskPanelFemMeshGmsh.py new file mode 100644 index 0000000000..8abd45fe4b --- /dev/null +++ b/src/Mod/Fem/_TaskPanelFemMeshGmsh.py @@ -0,0 +1,170 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2016 - 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__ = "_TaskPanelFemMeshGmsh" +__author__ = "Bernd Hahnebach" +__url__ = "http://www.freecadweb.org" + +## @package TaskPanelFemMeshGmsh +# \ingroup FEM + +import FreeCAD +import time +import _FemMeshGmsh +import FreeCADGui +from PySide import QtGui +from PySide import QtCore +from PySide.QtCore import Qt +from PySide.QtGui import QApplication + + +class _TaskPanelFemMeshGmsh: + '''The TaskPanel for editing References property of FemMeshGmsh objects and creation of new FEM mesh''' + def __init__(self, obj): + self.mesh_obj = obj + self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Fem/TaskPanelFemMeshGmsh.ui") + + self.Timer = QtCore.QTimer() + self.Timer.start(100) # 100 milli seconds + self.gmsh_runs = False + self.console_message_gmsh = '' + + QtCore.QObject.connect(self.form.if_max, QtCore.SIGNAL("valueChanged(double)"), self.max_changed) + QtCore.QObject.connect(self.form.if_min, QtCore.SIGNAL("valueChanged(double)"), self.min_changed) + QtCore.QObject.connect(self.form.cb_dimension, QtCore.SIGNAL("activated(int)"), self.choose_dimension) + QtCore.QObject.connect(self.form.cb_order, QtCore.SIGNAL("activated(int)"), self.choose_order) + QtCore.QObject.connect(self.Timer, QtCore.SIGNAL("timeout()"), self.update_timer_text) + + self.form.cb_dimension.addItems(_FemMeshGmsh._FemMeshGmsh.known_element_dimensions) + self.form.cb_order.addItems(_FemMeshGmsh._FemMeshGmsh.known_element_orders) + + self.get_mesh_params() + self.get_active_analysis() + self.update() + + def getStandardButtons(self): + return int(QtGui.QDialogButtonBox.Apply | QtGui.QDialogButtonBox.Close) + # show a apply and a close button + # def reject() is called on close button + # def clicked(self, button) is needed, to access the apply button + # def accept() in no longer needed, since there is no OK button + + def reject(self): + FreeCADGui.ActiveDocument.resetEdit() + FreeCAD.ActiveDocument.recompute() + return True + + def clicked(self, button): + if button == QtGui.QDialogButtonBox.Apply: + self.run_gmsh() + + def get_mesh_params(self): + self.clmax = self.mesh_obj.ElementSizeMax + self.clmin = self.mesh_obj.ElementSizeMin + self.order = self.mesh_obj.ElementOrder + self.dimension = self.mesh_obj.ElementDimension + + def set_mesh_params(self): + self.mesh_obj.ElementSizeMax = self.clmax + self.mesh_obj.ElementSizeMin = self.clmin + self.mesh_obj.ElementOrder = self.order + self.mesh_obj.ElementDimension = self.dimension + + def update(self): + 'fills the widgets' + self.form.if_max.setText("{} mm".format(self.clmax)) + self.form.if_min.setText("{} mm".format(self.clmin)) + index_dimension = self.form.cb_dimension.findText(self.dimension) + self.form.cb_dimension.setCurrentIndex(index_dimension) + index_order = self.form.cb_order.findText(self.order) + self.form.cb_order.setCurrentIndex(index_order) + + def console_log(self, message="", color="#000000"): + self.console_message_gmsh = self.console_message_gmsh + '{0:4.1f}: {2}
'.\ + format(time.time() - self.Start, color, message.encode('utf-8', 'replace')) + self.form.te_output.setText(self.console_message_gmsh) + self.form.te_output.moveCursor(QtGui.QTextCursor.End) + + def update_timer_text(self): + # print('timer1') + if self.gmsh_runs: + print('timer2') + # print('Time: {0:4.1f}: '.format(time.time() - self.Start)) + self.form.l_time.setText('Time: {0:4.1f}: '.format(time.time() - self.Start)) + + def max_changed(self, value): + self.clmax = float(value) + + def min_changed(self, value): + self.clmin = float(value) + + def choose_dimension(self, index): + if index < 0: + return + self.form.cb_dimension.setCurrentIndex(index) + self.dimension = str(self.form.cb_dimension.itemText(index)) # form returns unicode + + def choose_order(self, index): + if index < 0: + return + self.form.cb_order.setCurrentIndex(index) + self.order = str(self.form.cb_order.itemText(index)) # form returns unicode + + def run_gmsh(self): + QApplication.setOverrideCursor(Qt.WaitCursor) + self.Start = time.time() + self.form.l_time.setText('Time: {0:4.1f}: '.format(time.time() - self.Start)) + self.console_message_gmsh = '' + self.gmsh_runs = True + self.console_log("We gone start ...") + self.get_active_analysis() + self.set_mesh_params() + import FemGmshTools + gmsh_mesh = FemGmshTools.FemGmshTools(self.obj, self.analysis) + self.console_log("Start GMSH ...") + error = gmsh_mesh.create_mesh() + if error: + print(error) + self.console_log('GMSH had warnings ...') + self.console_log(error, '#FF0000') + else: + self.console_log('Clean run of GMSH') + self.console_log("GMSH done!") + self.form.l_time.setText('Time: {0:4.1f}: '.format(time.time() - self.Start)) + self.Timer.stop() + self.update() + QApplication.restoreOverrideCursor() + + def get_active_analysis(self): + import FemGui + self.analysis = FemGui.getActiveAnalysis() + if self.analysis: + for m in FemGui.getActiveAnalysis().Member: + if m.Name == self.mesh_obj.Name: + print(self.analysis.Name) + return + else: + # print('Mesh is not member of active analysis, means no group meshing') + self.analysis = None # no group meshing + else: + # print('No active analyis, means no group meshing') + self.analysis = None # no group meshing diff --git a/src/Mod/Fem/_TaskPanelFemSolverCalculix.py b/src/Mod/Fem/_TaskPanelFemSolverCalculix.py index 3dfff975d8..92b60aea68 100644 --- a/src/Mod/Fem/_TaskPanelFemSolverCalculix.py +++ b/src/Mod/Fem/_TaskPanelFemSolverCalculix.py @@ -31,13 +31,11 @@ import FemToolsCcx import FreeCAD import os import time - -if FreeCAD.GuiUp: - import FreeCADGui - import FemGui - from PySide import QtCore, QtGui - from PySide.QtCore import Qt - from PySide.QtGui import QApplication +import FreeCADGui +import FemGui +from PySide import QtCore, QtGui +from PySide.QtCore import Qt +from PySide.QtGui import QApplication class _TaskPanelFemSolverCalculix: @@ -83,6 +81,25 @@ class _TaskPanelFemSolverCalculix: self.update() + def getStandardButtons(self): + # only show a close button + # def accept() in no longer needed, since there is no OK button + return int(QtGui.QDialogButtonBox.Close) + + def reject(self): + FreeCADGui.ActiveDocument.resetEdit() + + def update(self): + 'fills the widgets' + self.form.le_working_dir.setText(self.solver_object.WorkingDir) + if self.solver_object.AnalysisType == 'static': + self.form.rb_static_analysis.setChecked(True) + elif self.solver_object.AnalysisType == 'frequency': + self.form.rb_frequency_analysis.setChecked(True) + elif self.solver_object.AnalysisType == 'thermomech': + self.form.rb_thermomech_analysis.setChecked(True) + return + def femConsoleMessage(self, message="", color="#000000"): self.fem_console_message = self.fem_console_message + '{0:4.1f}: {2}
'.\ format(time.time() - self.Start, color, message.encode('utf-8', 'replace')) @@ -152,26 +169,6 @@ class _TaskPanelFemSolverCalculix: QApplication.restoreOverrideCursor() self.form.l_time.setText('Time: {0:4.1f}: '.format(time.time() - self.Start)) - def getStandardButtons(self): - return int(QtGui.QDialogButtonBox.Close) - - def update(self): - 'fills the widgets' - self.form.le_working_dir.setText(self.solver_object.WorkingDir) - if self.solver_object.AnalysisType == 'static': - self.form.rb_static_analysis.setChecked(True) - elif self.solver_object.AnalysisType == 'frequency': - self.form.rb_frequency_analysis.setChecked(True) - elif self.solver_object.AnalysisType == 'thermomech': - self.form.rb_thermomech_analysis.setChecked(True) - return - - def accept(self): - FreeCADGui.ActiveDocument.resetEdit() - - def reject(self): - FreeCADGui.ActiveDocument.resetEdit() - def choose_working_dir(self): current_wd = self.setup_working_dir() wd = QtGui.QFileDialog.getExistingDirectory(None, 'Choose CalculiX working directory', diff --git a/src/Mod/Fem/_TaskPanelShowResult.py b/src/Mod/Fem/_TaskPanelShowResult.py index 07968f7593..5c713eb979 100644 --- a/src/Mod/Fem/_TaskPanelShowResult.py +++ b/src/Mod/Fem/_TaskPanelShowResult.py @@ -31,12 +31,11 @@ import FreeCAD import FemTools import numpy as np -if FreeCAD.GuiUp: - import FreeCADGui - import FemGui - from PySide import QtCore, QtGui - from PySide.QtCore import Qt - from PySide.QtGui import QApplication +import FreeCADGui +import FemGui +from PySide import QtCore, QtGui +from PySide.QtCore import Qt +from PySide.QtGui import QApplication class _TaskPanelShowResult: @@ -304,10 +303,6 @@ class _TaskPanelShowResult: def update(self): self.MeshObject = None self.result_object = get_results_object(FreeCADGui.Selection.getSelection()) - # Disable temperature radio button if it does ot exist in results - if len(self.result_object.Temperature) == 1: - self.form.rb_temperature.setEnabled(0) - for i in FemGui.getActiveAnalysis().Member: if i.isDerivedFrom("Fem::FemMeshObject"): self.MeshObject = i @@ -315,13 +310,21 @@ class _TaskPanelShowResult: self.suitable_results = False if self.result_object: + # Disable temperature radio button if it does ot exist in results + if len(self.result_object.Temperature) == 1: + self.form.rb_temperature.setEnabled(0) + if (self.MeshObject.FemMesh.NodeCount == len(self.result_object.NodeNumbers)): self.suitable_results = True else: if not self.MeshObject.FemMesh.VolumeCount: - FreeCAD.Console.PrintError('Graphical bending stress output for beam or shell FEM Meshes not yet supported!\n') + FreeCAD.Console.PrintError('FEM: Graphical bending stress output for beam or shell FEM Meshes not yet supported.\n') else: - FreeCAD.Console.PrintError('Result node numbers are not equal to FEM Mesh NodeCount!\n') + FreeCAD.Console.PrintError('FEM: Result node numbers are not equal to FEM Mesh NodeCount.\n') + else: + error_message = 'FEM: Result task panel, no result object to display results for.\n' + FreeCAD.Console.PrintError(error_message) + QtGui.QMessageBox.critical(None, 'No result object', error_message) def accept(self): FreeCADGui.Control.closeDialog() diff --git a/src/Mod/Fem/_ViewProviderFemMeshGmsh.py b/src/Mod/Fem/_ViewProviderFemMeshGmsh.py new file mode 100644 index 0000000000..981bfeed62 --- /dev/null +++ b/src/Mod/Fem/_ViewProviderFemMeshGmsh.py @@ -0,0 +1,111 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2016 - 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__ = "_ViewProviderFemMeshGmsh" +__author__ = "Bernd Hahnebach" +__url__ = "http://www.freecadweb.org" + +## @package ViewProviderFemMeshGmsh +# \ingroup FEM + +import FreeCAD +import FreeCADGui +import FemGui + + +class _ViewProviderFemMeshGmsh: + "A View Provider for the FemMeshGmsh object" + def __init__(self, vobj): + vobj.Proxy = self + + def getIcon(self): + return ":/icons/fem-fem-mesh-from-shape.svg" + + def attach(self, vobj): + self.ViewObject = vobj + self.Object = vobj.Object + + def updateData(self, obj, prop): + return + + def onChanged(self, vobj, prop): + return + + def setEdit(self, vobj, mode): + self.ViewObject.show() # show the mesh on edit if it is hided + import _TaskPanelFemMeshGmsh + taskd = _TaskPanelFemMeshGmsh._TaskPanelFemMeshGmsh(self.Object) + taskd.obj = vobj.Object + FreeCADGui.Control.showDialog(taskd) + return True + + def unsetEdit(self, vobj, mode): + FreeCADGui.Control.closeDialog() + return + + def doubleClicked(self, vobj): + # Group meshing is only active on active analysis, we should make sure the analysis the mesh belongs too is active + gui_doc = FreeCADGui.getDocument(vobj.Object.Document) + if not gui_doc.getInEdit(): + # may be go the other way around and just activate the analysis the user has doubleClicked on ?! + # not a fast one, we need to iterate over all member of all analysis to know to which analyis the object belongs too!!! + if FemGui.getActiveAnalysis() is not None: + if FemGui.getActiveAnalysis().Document is FreeCAD.ActiveDocument: + if self.Object in FemGui.getActiveAnalysis().Member: + if not gui_doc.getInEdit(): + gui_doc.setEdit(vobj.Object.Name) + else: + FreeCAD.Console.PrintError('Activate the analysis this mesh belongs to!\n') + else: + print('Mesh does not belong to the active analysis.') + for o in gui_doc.Document.Objects: + if o.isDerivedFrom('Fem::FemAnalysisPython'): + for m in o.Member: + if m == self.Object: + FemGui.setActiveAnalysis(o) + print('Analysis the Mesh belongs too was activated.') + gui_doc.setEdit(vobj.Object.Name) + break + else: + FreeCAD.Console.PrintError('Active Analysis is not in active Document!\n') + else: + # no active analysis, we gone have a look if the obj belongs to a non active analysis, + for o in gui_doc.Document.Objects: + if o.isDerivedFrom('Fem::FemAnalysisPython'): + for m in o.Member: + if m == self.Object: + FemGui.setActiveAnalysis(o) + print('Analysis the Mesh belongs too was activated.') + gui_doc.setEdit(vobj.Object.Name) + break + else: + print('Mesh GMSH object does not belong to an analysis. Group meshing will is deactivated.') + gui_doc.setEdit(vobj.Object.Name) + else: + FreeCAD.Console.PrintError('Active Task Dialog found! Please close this one first!\n') + return True + + def __getstate__(self): + return None + + def __setstate__(self, state): + return None diff --git a/src/Mod/Fem/_ViewProviderFemSolverCalculix.py b/src/Mod/Fem/_ViewProviderFemSolverCalculix.py index 557de6363c..38e0c53a6d 100644 --- a/src/Mod/Fem/_ViewProviderFemSolverCalculix.py +++ b/src/Mod/Fem/_ViewProviderFemSolverCalculix.py @@ -20,7 +20,7 @@ # * * # *************************************************************************** -__title__ = "_FemViewProviderSolverCalculix" +__title__ = "_ViewProviderFemSolverCalculix" __author__ = "Bernd Hahnebach" __url__ = "http://www.freecadweb.org" diff --git a/src/Mod/Fem/_ViewProviderFemSolverZ88.py b/src/Mod/Fem/_ViewProviderFemSolverZ88.py index dc7dc9f38d..5750e2f757 100644 --- a/src/Mod/Fem/_ViewProviderFemSolverZ88.py +++ b/src/Mod/Fem/_ViewProviderFemSolverZ88.py @@ -20,7 +20,7 @@ # * * # *************************************************************************** -__title__ = "_FemViewProviderSolverZ88" +__title__ = "_ViewProviderFemSolverZ88" __author__ = "Bernd Hahnebach" __url__ = "http://www.freecadweb.org" diff --git a/src/Mod/Fem/ccxDatReader.py b/src/Mod/Fem/ccxDatReader.py index bd59328498..ebda82e4b1 100644 --- a/src/Mod/Fem/ccxDatReader.py +++ b/src/Mod/Fem/ccxDatReader.py @@ -20,10 +20,6 @@ # * * # *************************************************************************** - -import FreeCAD -import os - __title__ = "ccxDatReader" __author__ = "Przemo Firszt" __url__ = "http://www.freecadweb.org" @@ -31,6 +27,10 @@ __url__ = "http://www.freecadweb.org" ## @package ccxDatReader # \ingroup FEM +import FreeCAD +import os + + if open.__module__ == '__builtin__': # because we'll redefine open below pyopen = open diff --git a/src/Mod/Fem/ccxFrdReader.py b/src/Mod/Fem/ccxFrdReader.py index 09a9701758..d312ebc81d 100644 --- a/src/Mod/Fem/ccxFrdReader.py +++ b/src/Mod/Fem/ccxFrdReader.py @@ -22,12 +22,6 @@ # * * # *************************************************************************** - -import FreeCAD -import os -from math import pow, sqrt -import numpy as np - __title__ = "FreeCAD Calculix library" __author__ = "Juergen Riegel , Michael Hindley, Bernd Hahnebach" __url__ = "http://www.freecadweb.org" @@ -35,6 +29,12 @@ __url__ = "http://www.freecadweb.org" ## @package ccxFrdReader # \ingroup FEM +import FreeCAD +import os +from math import pow, sqrt +import numpy as np + + if open.__module__ == '__builtin__': pyopen = open # because we'll redefine open below @@ -331,6 +331,8 @@ def readResult(frd_input): elements_found = False frd_file.close() + if not nodes: + FreeCAD.Console.PrintError('FEM: No nodes found in Frd file.\n') return {'Nodes': nodes, 'Hexa8Elem': elements_hexa8, 'Penta6Elem': elements_penta6, 'Tetra4Elem': elements_tetra4, 'Tetra10Elem': elements_tetra10, 'Penta15Elem': elements_penta15, 'Hexa20Elem': elements_hexa20, 'Tria3Elem': elements_tria3, 'Tria6Elem': elements_tria6, diff --git a/src/Mod/Fem/importInpMesh.py b/src/Mod/Fem/importInpMesh.py index 1a736ab436..aae2740f15 100644 --- a/src/Mod/Fem/importInpMesh.py +++ b/src/Mod/Fem/importInpMesh.py @@ -20,11 +20,6 @@ # * * # *************************************************************************** -import FemMeshTools -import FreeCAD -import os -import string - __title__ = "FreeCAD .inp file reader" __author__ = "Frantisek Loeffelmann " __url__ = "http://www.freecadweb.org" @@ -33,6 +28,12 @@ __date__ = "04/08/2016" ## @package importInpMesh # \ingroup FEM +import FemMeshTools +import FreeCAD +import os +import string + + if open.__module__ == '__builtin__': pyopen = open # because we'll redefine open below diff --git a/src/Mod/Fem/importZ88Mesh.py b/src/Mod/Fem/importZ88Mesh.py index 5ba0da9957..e21b296fd8 100644 --- a/src/Mod/Fem/importZ88Mesh.py +++ b/src/Mod/Fem/importZ88Mesh.py @@ -20,12 +20,6 @@ # * * # *************************************************************************** - -import FreeCAD -import os -import FemMeshTools - - __title__ = "FreeCAD Z88 Mesh reader and writer" __author__ = "Bernd Hahnebach " __url__ = "http://www.freecadweb.org" @@ -33,6 +27,11 @@ __url__ = "http://www.freecadweb.org" ## @package importZ88Mesh # \ingroup FEM +import FreeCAD +import os +import FemMeshTools + + Debug = False if open.__module__ == '__builtin__': diff --git a/src/Mod/Fem/test_files/ccx/cube_static.inp b/src/Mod/Fem/test_files/ccx/cube_static.inp index a832a6c8d9..09f6380124 100644 --- a/src/Mod/Fem/test_files/ccx/cube_static.inp +++ b/src/Mod/Fem/test_files/ccx/cube_static.inp @@ -547,7 +547,7 @@ FemConstraintFixed,3 ** Element + CalculiX face + load in [MPa] ** written by write_constraints_pressure function *DLOAD -** face load on shape: Box:Face2 +** FemConstraintPressure: face load 635,P1,1000.0 638,P3,1000.0 641,P2,1000.0 diff --git a/src/Mod/Fem/z88DispReader.py b/src/Mod/Fem/z88DispReader.py index d930e9d741..35e24a202d 100644 --- a/src/Mod/Fem/z88DispReader.py +++ b/src/Mod/Fem/z88DispReader.py @@ -20,12 +20,6 @@ # * * # *************************************************************************** - -import FreeCAD -import os -from math import pow, sqrt - - __title__ = "FreeCAD Z88 Disp Reader" __author__ = "Bernd Hahnebach " __url__ = "http://www.freecadweb.org" @@ -33,6 +27,11 @@ __url__ = "http://www.freecadweb.org" ## @package z88DispReader # \ingroup FEM +import FreeCAD +import os +from math import pow, sqrt + + Debug = False if open.__module__ == '__builtin__':