def start_logs(self): Console.PrintLog("\nGmsh FEM mesh run is being started.\n") Console.PrintLog( " Part to mesh: Name --> {}, Label --> {}, ShapeType --> {}\n". format(self.part_obj.Name, self.part_obj.Label, self.part_obj.Shape.ShapeType)) Console.PrintLog(" CharacteristicLengthMax: {}\n".format(self.clmax)) Console.PrintLog(" CharacteristicLengthMin: {}\n".format(self.clmin)) Console.PrintLog(" ElementOrder: {}\n".format(self.order))
def get_z88_element_type(femmesh, femelement_table=None): if not femmesh: Console.PrintError("Error: No femmesh.\n") if not femelement_table: Console.PrintError("The femelement_table need to be calculated.\n") femelement_table = meshtools.get_femelement_table(femmesh) # in some cases lowest key in femelement_table is not [1] for elem in sorted(femelement_table): elem_length = len(femelement_table[elem]) Console.PrintLog( "Node count of first element: {}\n".format(elem_length)) break # break after the first elem if meshtools.is_solid_femmesh(femmesh): if femmesh.TetraCount == femmesh.VolumeCount: if elem_length == 4: return 17 elif elem_length == 10: return 16 else: Console.PrintMessage("Tetra with neither 4 nor 10 nodes.\n") elif femmesh.HexaCount == femmesh.VolumeCount: if elem_length == 8: return 1 elif elem_length == 20: return 10 else: Console.PrintError("Hexa with neither 8 nor 20 nodes.\n") return 0 else: Console.PrintError("no tetra, no hexa or Mixed Volume Elements.\n") elif meshtools.is_face_femmesh(femmesh): if femmesh.TriangleCount == femmesh.FaceCount: if elem_length == 3: Console.PrintError("tria3mesh, not supported by Z88.\n") return 0 elif elem_length == 6: return 24 else: Console.PrintError("Tria with neither 3 nor 6 nodes.\n") return 0 elif femmesh.QuadrangleCount == femmesh.FaceCount: if elem_length == 4: Console.PrintError("quad4mesh, not supported by Z88.\n") return 0 elif elem_length == 8: return 23 else: Console.PrintError("Quad with neither 4 nor 8 nodes.\n") return 0 else: Console.PrintError("no tria, no quad\n") return 0 elif meshtools.is_edge_femmesh(femmesh): Console.PrintMessage( "Edge femmesh will be exported as 3D truss element nr 4.\n") return 4 else: Console.PrintError("Neither edge nor face nor solid femmesh.\n") return 0 return 0
def Initialize(self): """When the workbench is first loaded.""" # pylint: disable=no-self-use, import-outside-toplevel from PySide.QtCore import QT_TRANSLATE_NOOP from FreeCAD import Console from FreeCADGui import addIconPath, addPreferencePage from Render import RENDER_COMMANDS, ICONDIR, PREFPAGE commands = RENDER_COMMANDS self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Render"), commands) self.appendMenu(QT_TRANSLATE_NOOP("Workbench", "&Render"), commands) addIconPath(ICONDIR) addPreferencePage(PREFPAGE, "Render") Console.PrintLog("Loading Render module...done\n")
def _handleRedifinedConstants(self): """ redefine constants in self.constsdef according constant redefine objects """ permittivity_objs = self._getMember("Fem::ConstantVacuumPermittivity") if len(permittivity_objs) == 1: Console.PrintLog("Constand permittivity overwriting.\n") self._setConstant("PermittivityOfVacuum", permittivity_objs[0].VacuumPermittivity) elif len(permittivity_objs) > 1: Console.PrintError( "More than one permittivity constant overwriting objects ({} objs). " "The permittivity constant overwriting is ignored.\n".format( len(permittivity_objs)))
def get_dimension(self): # Dimension # known_element_dimensions = ["From Shape", "1D", "2D", "3D"] # if not given, Gmsh uses the highest available. # A use case for not "From Shape" would be a surface (2D) mesh of a solid if self.dimension == "From Shape": 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) Console.PrintError("You can not mesh a Vertex.\n") self.dimension = "0" elif shty == "Compound": # print(" Found a " + shty) Console.PrintLog( " Found a Compound. Since it could contain" "any kind of shape dimension 3 is used.\n" ) self.dimension = "3" # dimension 3 works for 2D and 1d shapes as well else: self.dimension = "0" Console.PrintError( "Could not retrieve Dimension from shape type. Please choose dimension.\n" ) elif self.dimension == "3D": self.dimension = "3" elif self.dimension == "2D": self.dimension = "2" elif self.dimension == "1D": self.dimension = "1" else: Console.PrintError("Error in dimension\n") Console.PrintMessage(" ElementDimension: " + self.dimension + "\n")
def Initialize(self): """Initialize GUI when the workbench is first loaded (callback). This method is called by FreeCAD framework when the workbench is first loaded. """ # pylint: disable=no-self-use, import-outside-toplevel from PySide.QtCore import QT_TRANSLATE_NOOP from FreeCAD import Console from FreeCADGui import addIconPath, addPreferencePage, addLanguagePath from Render import RENDER_COMMANDS, TRANSDIR, ICONDIR, PREFPAGE addLanguagePath(TRANSDIR) self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Render"), RENDER_COMMANDS) self.appendMenu(QT_TRANSLATE_NOOP("Workbench", "&Render"), RENDER_COMMANDS) addIconPath(ICONDIR) addPreferencePage(PREFPAGE, "Render") msg = QT_TRANSLATE_NOOP("Render", "Loading Render module... done") + "\n" Console.PrintLog(msg)
def read_z88_disp(z88_disp_input): """ read a z88 disp file and extract the nodes and elements z88 Displacement output file is z88o2.txt works with Z88OS14 """ nodes = {} mode_disp = {} mode_results = {} results = [] z88_disp_file = pyopen(z88_disp_input, "r") for no, line in enumerate(z88_disp_file): lno = no + 1 linelist = line.split() if lno >= 6: # disp line node_no = int(linelist[0]) mode_disp_x = float(linelist[1]) mode_disp_y = float(linelist[2]) if len(linelist) > 3: mode_disp_z = float(linelist[3]) else: mode_disp_z = 0.0 mode_disp[node_no] = FreeCAD.Vector(mode_disp_x, mode_disp_y, mode_disp_z) nodes[node_no] = node_no mode_results["disp"] = mode_disp results.append(mode_results) for r in results[0]["disp"]: Console.PrintLog("{} --> {}\n".format(r, results[0]["disp"][r])) z88_disp_file.close() return {"Nodes": nodes, "Results": results}
def importFrd(filename, analysis=None, result_name_prefix=""): from . import importToolsFem import ObjectsFem if analysis: doc = analysis.Document else: doc = FreeCAD.ActiveDocument m = read_frd_result(filename) result_mesh_object = None res_obj = None if len(m["Nodes"]) > 0: mesh = importToolsFem.make_femmesh(m) result_mesh_object = ObjectsFem.makeMeshResult(doc, "ResultMesh") result_mesh_object.FemMesh = mesh res_mesh_is_compacted = False nodenumbers_for_compacted_mesh = [] number_of_increments = len(m["Results"]) Console.PrintLog("Increments: " + str(number_of_increments) + "\n") if len(m["Results"]) > 0: for result_set in m["Results"]: if "number" in result_set: eigenmode_number = result_set["number"] else: eigenmode_number = 0 step_time = result_set["time"] step_time = round(step_time, 2) if eigenmode_number > 0: results_name = ("{}Mode{}_Results".format( result_name_prefix, eigenmode_number)) elif number_of_increments > 1: results_name = ("{}Time{}_Results".format( result_name_prefix, step_time)) else: results_name = ("{}Results".format(result_name_prefix)) res_obj = ObjectsFem.makeResultMechanical(doc, results_name) res_obj.Mesh = result_mesh_object res_obj = importToolsFem.fill_femresult_mechanical( res_obj, result_set) if analysis: analysis.addObject(res_obj) # complementary result object calculations import femresult.resulttools as restools import femtools.femutils as femutils if not res_obj.MassFlowRate: # information 1: # only compact result if not Flow 1D results # compact result object, workaround for bug 2873 # https://www.freecadweb.org/tracker/view.php?id=2873 # information 2: # if the result data has multiple result sets there will be multiple result objs # they all will use one mesh obj # on the first res obj fill the mesh obj will be compacted, thus # it does not need to be compacted on further result sets # but NodeNumbers need to be compacted for every result set (res object fill) # example frd file: https://forum.freecadweb.org/viewtopic.php?t=32649#p274291 if res_mesh_is_compacted is False: # first result set, compact FemMesh and NodeNumbers res_obj = restools.compact_result(res_obj) res_mesh_is_compacted = True nodenumbers_for_compacted_mesh = res_obj.NodeNumbers else: # all other result sets, do not compact FemMesh, only set NodeNumbers res_obj.NodeNumbers = nodenumbers_for_compacted_mesh # fill DisplacementLengths res_obj = restools.add_disp_apps(res_obj) # fill vonMises res_obj = restools.add_von_mises(res_obj) if res_obj.getParentGroup(): has_reinforced_mat = False for obj in res_obj.getParentGroup().Group: if obj.isDerivedFrom("App::MaterialObjectPython") \ and femutils.is_of_type(obj, "Fem::MaterialReinforced"): has_reinforced_mat = True restools.add_principal_stress_reinforced(res_obj) break if has_reinforced_mat is False: # fill PrincipalMax, PrincipalMed, PrincipalMin, MaxShear res_obj = restools.add_principal_stress_std(res_obj) else: # if a pure frd file was opened no analysis and thus no parent group # fill PrincipalMax, PrincipalMed, PrincipalMin, MaxShear res_obj = restools.add_principal_stress_std(res_obj) # fill Stats res_obj = restools.fill_femresult_stats(res_obj) else: error_message = ( "Nodes, but no results found in frd file. " "It means there only is a mesh but no results in frd file. " "Usually this happens for: \n" "- analysis type 'NOANALYSIS'\n" "- if CalculiX returned no results " "(happens on nonpositive jacobian determinant in at least one element)\n" "- just no frd results where requestet in input file " "(neither 'node file' nor 'el file' in output section')\n") Console.PrintMessage(error_message) # create a result obj, even if we have no results but a result mesh in frd file # see error message above for more information if not res_obj: if result_name_prefix: results_name = ("{}_Results".format(result_name_prefix)) else: results_name = ("Results".format(result_name_prefix)) res_obj = ObjectsFem.makeResultMechanical(doc, results_name) res_obj.Mesh = result_mesh_object # TODO, node numbers in result obj could be set if analysis: analysis.addObject(res_obj) if FreeCAD.GuiUp: if analysis: import FemGui FemGui.setActiveAnalysis(analysis) doc.recompute() else: Console.PrintError( "Problem on frd file import. No nodes found in frd file.\n") # None will be returned # or would it be better to raise an exception if there are not even nodes in frd file return res_obj
def read_z88_mesh( z88_mesh_input ): """ reads a z88 mesh file z88i1.txt (Z88OSV14) or z88structure.txt (Z88AuroraV3) and extracts the nodes and elements """ nodes = {} elements_hexa8 = {} elements_penta6 = {} elements_tetra4 = {} elements_tetra10 = {} elements_penta15 = {} elements_hexa20 = {} elements_tria3 = {} elements_tria6 = {} elements_quad4 = {} elements_quad8 = {} elements_seg2 = {} elements_seg3 = {} input_continues = False # elem = -1 z88_element_type = 0 z88_mesh_file = pyopen(z88_mesh_input, "r") mesh_info = z88_mesh_file.readline().strip().split() nodes_dimension = int(mesh_info[0]) nodes_count = int(mesh_info[1]) elements_count = int(mesh_info[2]) kflag = int(mesh_info[4]) # for non rotational elements ist --> kflag = 0 --> cartesian, kflag = 1 polar coordinates if kflag: Console.PrintError( "KFLAG = 1, Rotational coordinates not supported at the moment\n" ) return {} nodes_first_line = 2 # first line is mesh_info nodes_last_line = nodes_count + 1 elemts_first_line = nodes_last_line + 1 elements_last_line = elemts_first_line - 1 + elements_count * 2 Console.PrintLog("{}\n".format(nodes_count)) Console.PrintLog("{}\n".format(elements_count)) Console.PrintLog("{}\n".format(nodes_last_line)) Console.PrintLog("{}\n".format(elemts_first_line)) Console.PrintLog("{}\n".format(elements_last_line)) z88_mesh_file.seek(0) # go back to the beginning of the file for no, line in enumerate(z88_mesh_file): lno = no + 1 linecolumns = line.split() if lno >= nodes_first_line and lno <= nodes_last_line: # node line node_no = int(linecolumns[0]) node_x = float(linecolumns[2]) node_y = float(linecolumns[3]) if nodes_dimension == 2: node_z = 0.0 elif nodes_dimension == 3: node_z = float(linecolumns[4]) nodes[node_no] = FreeCAD.Vector(node_x, node_y, node_z) if lno >= elemts_first_line and lno <= elements_last_line: # first element line if not input_continues: elem_no = int(linecolumns[0]) z88_element_type = int(linecolumns[1]) input_continues = True # second element line elif input_continues: # not supported elements if z88_element_type == 8: # torus8 Console.PrintError( "Z88 Element No. 8, torus8\n" ) Console.PrintError( "Rotational elements are not supported at the moment\n" ) return {} elif z88_element_type == 12: # torus12 Console.PrintError( "Z88 Element No. 12, torus12\n" ) Console.PrintError( "Rotational elements are not supported at the moment\n" ) return {} elif z88_element_type == 15: # torus6 Console.PrintError( "Z88 Element No. 15, torus6\n" ) Console.PrintError( "Rotational elements are not supported at the moment\n" ) return {} elif z88_element_type == 19: # platte16 Console.PrintError( "Z88 Element No. 19, platte16\n" ) Console.PrintError( "Not supported at the moment\n" ) return {} elif z88_element_type == 21: # schale16, mixture made from hexa8 and hexa20 (thickness is linear) Console.PrintError( "Z88 Element No. 21, schale16\n" ) Console.PrintError( "Not supported at the moment\n" ) return {} elif z88_element_type == 22: # schale12, mixtrue made from prism6 and prism15 (thickness is linear) Console.PrintError( "Z88 Element No. 22, schale12\n" ) Console.PrintError( "Not supported at the moment\n" ) return {} # supported elements elif z88_element_type == 2 \ or z88_element_type == 4 \ or z88_element_type == 5 \ or z88_element_type == 9 \ or z88_element_type == 13 \ or z88_element_type == 25: # stab4 or stab5 or welle5 or beam13 or beam25 Z88 --> seg2 FreeCAD # N1, N2 nd1 = int(linecolumns[0]) nd2 = int(linecolumns[1]) elements_seg2[elem_no] = (nd1, nd2) input_continues = False elif z88_element_type == 3 or z88_element_type == 14 or z88_element_type == 24: # scheibe3 or scheibe14 or schale24 Z88 --> tria6 FreeCAD # N1, N2, N3, N4, N5, N6 nd1 = int(linecolumns[0]) nd2 = int(linecolumns[1]) nd3 = int(linecolumns[2]) nd4 = int(linecolumns[3]) nd5 = int(linecolumns[4]) nd6 = int(linecolumns[5]) elements_tria6[elem_no] = (nd1, nd2, nd3, nd4, nd5, nd6) input_continues = False elif z88_element_type == 7 or z88_element_type == 20 or z88_element_type == 23: # scheibe7 or platte20 or schale23 Z88 --> quad8 FreeCAD # N1, N2, N3, N4, N5, N6, N7, N8 nd1 = int(linecolumns[0]) nd2 = int(linecolumns[1]) nd3 = int(linecolumns[2]) nd4 = int(linecolumns[3]) nd5 = int(linecolumns[4]) nd6 = int(linecolumns[5]) nd7 = int(linecolumns[6]) nd8 = int(linecolumns[7]) elements_quad8[elem_no] = (nd1, nd2, nd3, nd4, nd5, nd6, nd7, nd8) input_continues = False elif z88_element_type == 17: # volume17 Z88 --> tetra4 FreeCAD # N4, N2, N3, N1 nd1 = int(linecolumns[0]) nd2 = int(linecolumns[1]) nd3 = int(linecolumns[2]) nd4 = int(linecolumns[3]) elements_tetra4[elem_no] = (nd4, nd2, nd3, nd1) input_continues = False elif z88_element_type == 16: # volume16 Z88 --> tetra10 FreeCAD # N1, N2, N4, N3, N5, N8, N10, N7, N6, N9 # Z88 to FC is different as FC to Z88 nd1 = int(linecolumns[0]) nd2 = int(linecolumns[1]) nd3 = int(linecolumns[2]) nd4 = int(linecolumns[3]) nd5 = int(linecolumns[4]) nd6 = int(linecolumns[5]) nd7 = int(linecolumns[6]) nd8 = int(linecolumns[7]) nd9 = int(linecolumns[8]) nd10 = int(linecolumns[9]) elements_tetra10[elem_no] = ( nd1, nd2, nd4, nd3, nd5, nd8, nd10, nd7, nd6, nd9 ) input_continues = False elif z88_element_type == 1: # volume1 Z88 --> hexa8 FreeCAD # N1, N2, N3, N4, N5, N6, N7, N8 nd1 = int(linecolumns[0]) nd2 = int(linecolumns[1]) nd3 = int(linecolumns[2]) nd4 = int(linecolumns[3]) nd5 = int(linecolumns[4]) nd6 = int(linecolumns[5]) nd7 = int(linecolumns[6]) nd8 = int(linecolumns[7]) elements_hexa8[elem_no] = (nd1, nd2, nd3, nd4, nd5, nd6, nd7, nd8) input_continues = False elif z88_element_type == 10: # volume10 Z88 --> hexa20 FreeCAD # N2, N3, N4, N1, N6, N7, N8, N5, N10, N11 # N12, N9, N14, N15, N16, N13, N18, N19, N20, N17 # or turn by 90 degree and they match ! # N1, N2, N3, N4, N5, N6, N7, N8, N9, N10 # N11, N12, N13, N14, N15, N16, N17, N18, N19, N20 nd1 = int(linecolumns[0]) nd2 = int(linecolumns[1]) nd3 = int(linecolumns[2]) nd4 = int(linecolumns[3]) nd5 = int(linecolumns[4]) nd6 = int(linecolumns[5]) nd7 = int(linecolumns[6]) nd8 = int(linecolumns[7]) nd9 = int(linecolumns[8]) nd10 = int(linecolumns[9]) nd11 = int(linecolumns[10]) nd12 = int(linecolumns[11]) nd13 = int(linecolumns[12]) nd14 = int(linecolumns[13]) nd15 = int(linecolumns[14]) nd16 = int(linecolumns[15]) nd17 = int(linecolumns[16]) nd18 = int(linecolumns[17]) nd19 = int(linecolumns[18]) nd20 = int(linecolumns[19]) elements_hexa20[elem_no] = ( nd1, nd2, nd3, nd4, nd5, nd6, nd7, nd8, nd9, nd10, nd11, nd12, nd13, nd14, nd15, nd16, nd17, nd18, nd19, nd20 ) input_continues = False # unknown elements # some examples have -1 for some teaching reasons to show some other stuff else: Console.PrintError("Unknown element\n") return {} for n in nodes: Console.PrintLog(str(n) + " " + str(nodes[n]) + "\n") for e in elements_tria6: Console.PrintLog(str(e) + " " + str(elements_tria6[e]) + "\n") FreeCAD.Console.PrintLog("\n") z88_mesh_file.close() return { "Nodes": nodes, "Seg2Elem": elements_seg2, "Seg3Elem": elements_seg3, "Tria3Elem": elements_tria3, "Tria6Elem": elements_tria6, "Quad4Elem": elements_quad4, "Quad8Elem": elements_quad8, "Tetra4Elem": elements_tetra4, "Tetra10Elem": elements_tetra10, "Hexa8Elem": elements_hexa8, "Hexa20Elem": elements_hexa20, "Penta6Elem": elements_penta6, "Penta15Elem": elements_penta15 }
def importAvs(filename, analysis=None, result_name_prefix=""): import ObjectsFem from feminout import importToolsFem TUNE = False if TUNE: import time cur = time.time() if analysis: doc = analysis.Document else: doc = FreeCAD.ActiveDocument m = read_avs_result(filename) result_mesh_object = None res_obj = None if TUNE: new = time.time() Console.PrintMessage("dtime 1:" + '%8.3f' % (new - cur) + "\n") cur = new if len(m["Nodes"]) > 0: mesh = importToolsFem.make_femmesh(m) if TUNE: new = time.time() Console.PrintMessage("dtime 2:" + '%8.3f' % (new - cur) + "\n") cur = new result_mesh_object = ObjectsFem.makeMeshResult(doc, "ResultMesh") result_mesh_object.FemMesh = mesh res_mesh_is_compacted = False nodenumbers_for_compacted_mesh = [] number_of_increments = len(m["Results"]) Console.PrintLog("Increments: " + str(number_of_increments) + "\n") if TUNE: new = time.time() Console.PrintMessage("dtime 3:" + '%8.3f' % (new - cur) + "\n") cur = new if len(m["Results"]) > 0: for result_set in m["Results"]: if "number" in result_set: eigenmode_number = result_set["number"] else: eigenmode_number = 0 step_time = result_set["time"] step_time = round(step_time, 2) if eigenmode_number > 0: results_name = ("{}Mode{}_Results".format( result_name_prefix, eigenmode_number)) elif number_of_increments > 1: results_name = ("{}Time{}_Results".format( result_name_prefix, step_time)) else: results_name = ("{}Results".format(result_name_prefix)) if TUNE: new = time.time() Console.PrintMessage("dtime 4:" + '%8.3f' % (new - cur) + "\n") cur = new res_obj = ObjectsFem.makeResultMechanical(doc, results_name) res_obj.Mesh = result_mesh_object if TUNE: new = time.time() Console.PrintMessage("dtime 5:" + '%8.3f' % (new - cur) + "\n") cur = new res_obj = importToolsFem.fill_femresult_mechanical( res_obj, result_set) if analysis: # need to be here, becasause later on, the analysis objs are needed # see fill of principal stresses analysis.addObject(res_obj) if TUNE: new = time.time() Console.PrintMessage("dtime 6:" + '%8.3f' % (new - cur) + "\n") cur = new # more result object calculations from femresult import resulttools from femtools import femutils if not res_obj.MassFlowRate: if res_mesh_is_compacted is False: # first result set, compact FemMesh and NodeNumbers res_obj = resulttools.compact_result(res_obj) res_mesh_is_compacted = True nodenumbers_for_compacted_mesh = res_obj.NodeNumbers else: # all other result sets, do not compact FemMesh, only set NodeNumbers res_obj.NodeNumbers = nodenumbers_for_compacted_mesh if TUNE: new = time.time() Console.PrintMessage("dtime 7:" + '%8.3f' % (new - cur) + "\n") cur = new # fill DisplacementLengths res_obj = resulttools.add_disp_apps(res_obj) if TUNE: new = time.time() Console.PrintMessage("dtime 8:" + '%8.3f' % (new - cur) + "\n") cur = new # fill vonMises mstress = [] for nid in res_obj.NodeNumbers: mstress.append(result_set["mises"][nid]) res_obj.vonMises = mstress if TUNE: new = time.time() Console.PrintMessage("dtime 9:" + '%8.3f' % (new - cur) + "\n") cur = new # fill principal stress prinstress1 = [] prinstress2 = [] prinstress3 = [] for nid in res_obj.NodeNumbers: pstr = result_set["pstress"][nid] prinstress1.append(pstr[0]) prinstress2.append(pstr[1]) prinstress3.append(pstr[2]) res_obj.PrincipalMax = prinstress1 res_obj.PrincipalMed = prinstress2 res_obj.PrincipalMin = prinstress3 # fill Stats res_obj = resulttools.fill_femresult_stats(res_obj) if TUNE: new = time.time() Console.PrintMessage("dtime10:" + '%8.3f' % (new - cur) + "\n") cur = new else: error_message = ( "Nodes, but no results found in avs file. " "It means there only is a mesh but no results in avs file. " "Usually this happens for: \n" "- analysis type 'NOANALYSIS'\n" "- if FrontISTR returned no results " "(happens on nonpositive jacobian determinant in at least one element)\n" "- just no avs results where requestet in input file " "(neither 'node file' nor 'el file' in output section')\n") Console.PrintWarning(error_message) # create a result obj, even if we have no results but a result mesh in avs file # see error message above for more information if not res_obj: if result_name_prefix: results_name = ("{}_Results".format(result_name_prefix)) else: results_name = ("Results".format(result_name_prefix)) res_obj = ObjectsFem.makeResultMechanical(doc, results_name) res_obj.Mesh = result_mesh_object # TODO, node numbers in result obj could be set if analysis: analysis.addObject(res_obj) if FreeCAD.GuiUp: if analysis: import FemGui FemGui.setActiveAnalysis(analysis) doc.recompute() if TUNE: new = time.time() Console.PrintMessage("dtime11:" + '%8.3f' % (new - cur) + "\n") cur = new else: Console.PrintError( "Problem on avs file import. No nodes found in avs file.\n") # None will be returned # or would it be better to raise an exception if there are not even nodes in avs file? return res_obj
def read_mesh_block(mesh_block): """ Reading mesh block from XML file. The mesh block only contains cells and vertices. """ dim = int(mesh_block.get("dim")) cell_type = mesh_block.get("celltype") vertex_size = 0 Console.PrintLog("Mesh dimension: %d\n" % (dim, )) Console.PrintLog("Mesh cell type: %s\n" % (cell_type, )) # every cell type contains a dict with key=dimension and value=number cells_parts_dim = { "point": { 0: 1 }, "interval": { 0: 2, 1: 1 }, "triangle": { 0: 3, 1: 3, 2: 1 }, "tetrahedron": { 0: 4, 1: 6, 2: 4, 3: 1 }, "quadrilateral": { 0: 4, 1: 4, 2: 1 }, "hexahedron": { 0: 8, 1: 12, 2: 6, 3: 1 } } find_vertices = mesh_block.find("vertices") find_cells = mesh_block.find("cells") nodes_dict = {} cell_dict = {} if find_vertices is None: Console.PrintWarning("No vertices found!\n") else: vertex_size = int(find_vertices.attrib.get("size")) Console.PrintLog("Reading %d vertices\n" % (vertex_size, )) for vertex in find_vertices: ind = int(vertex.get("index")) if vertex.tag.lower() == "vertex": [node_x, node_y, node_z] = [ float(vertex.get(coord, 0.)) for coord in ["x", "y", "z"] ] nodes_dict[ind + 1] = FreeCAD.Vector( node_x, node_y, node_z) # increase node index by one, since fenics starts at 0, FreeCAD at 1 # print("%d %f %f %f" % (ind, node_x, node_y, node_z)) else: Console.PrintWarning("found strange vertex tag: %s\n" % (vertex.tag, )) if find_cells is None: Console.PrintWarning("No cells found!\n") else: Console.PrintLog("Reading %d cells\n" % (int(find_cells.attrib.get("size")), )) for cell in find_cells: ind = int(cell.get("index")) if cell.tag.lower() != cell_type.lower(): Console.PrintWarning( "Strange mismatch between cell type {} and cell tag {}\n" .format(cell_type, cell.tag.lower())) num_vertices = cells_parts_dim[cell_type][0] vtupel = tuple([ int(cell.get("v" + str(vnum))) + 1 for vnum in range(num_vertices) ]) # generate "v0", "v1", ... from dimension lookup table # increase numbers by one to match FC numbering convention cell_dict[ind + 1] = vtupel # valtupel = tuple([ind] + list(vtupel)) # print(("%d " + ("%d "*len(vtupel))) % valtupel) return (nodes_dict, cell_dict, cell_type, dim)
def read_fenics_mesh_xml(xmlfilename): """ Returns element dictionary to be evaluated by make_femmesh later """ Fenics_to_FreeCAD_dict = { "triangle": "tria3", "tetrahedron": "tetra4", "hexahedron": "hexa8", "interval": "seg2", "quadrilateral": "quad4", } def read_mesh_block(mesh_block): """ Reading mesh block from XML file. The mesh block only contains cells and vertices. """ dim = int(mesh_block.get("dim")) cell_type = mesh_block.get("celltype") vertex_size = 0 Console.PrintLog("Mesh dimension: %d\n" % (dim, )) Console.PrintLog("Mesh cell type: %s\n" % (cell_type, )) # every cell type contains a dict with key=dimension and value=number cells_parts_dim = { "point": { 0: 1 }, "interval": { 0: 2, 1: 1 }, "triangle": { 0: 3, 1: 3, 2: 1 }, "tetrahedron": { 0: 4, 1: 6, 2: 4, 3: 1 }, "quadrilateral": { 0: 4, 1: 4, 2: 1 }, "hexahedron": { 0: 8, 1: 12, 2: 6, 3: 1 } } find_vertices = mesh_block.find("vertices") find_cells = mesh_block.find("cells") nodes_dict = {} cell_dict = {} if find_vertices is None: Console.PrintWarning("No vertices found!\n") else: vertex_size = int(find_vertices.attrib.get("size")) Console.PrintLog("Reading %d vertices\n" % (vertex_size, )) for vertex in find_vertices: ind = int(vertex.get("index")) if vertex.tag.lower() == "vertex": [node_x, node_y, node_z] = [ float(vertex.get(coord, 0.)) for coord in ["x", "y", "z"] ] nodes_dict[ind + 1] = FreeCAD.Vector( node_x, node_y, node_z) # increase node index by one, since fenics starts at 0, FreeCAD at 1 # print("%d %f %f %f" % (ind, node_x, node_y, node_z)) else: Console.PrintWarning("found strange vertex tag: %s\n" % (vertex.tag, )) if find_cells is None: Console.PrintWarning("No cells found!\n") else: Console.PrintLog("Reading %d cells\n" % (int(find_cells.attrib.get("size")), )) for cell in find_cells: ind = int(cell.get("index")) if cell.tag.lower() != cell_type.lower(): Console.PrintWarning( "Strange mismatch between cell type {} and cell tag {}\n" .format(cell_type, cell.tag.lower())) num_vertices = cells_parts_dim[cell_type][0] vtupel = tuple([ int(cell.get("v" + str(vnum))) + 1 for vnum in range(num_vertices) ]) # generate "v0", "v1", ... from dimension lookup table # increase numbers by one to match FC numbering convention cell_dict[ind + 1] = vtupel # valtupel = tuple([ind] + list(vtupel)) # print(("%d " + ("%d "*len(vtupel))) % valtupel) return (nodes_dict, cell_dict, cell_type, dim) def generate_lower_dimensional_structures(nodes, cell_dict, cell_type, dim): def correct_volume_det(element_dict): """ Checks whether the cell elements all have the same volume (<0?) sign (is necessary to avoid negative Jacobian errors). Works only with tet4 and tri3 elements at the moment """ if dim == 3: for (ind, tet) in list(element_dict["tetra4"].items()): v0 = nodes[tet[0]] v1 = nodes[tet[1]] v2 = nodes[tet[2]] v3 = nodes[tet[3]] a = v1 - v0 b = v2 - v0 c = v3 - v0 if a.dot(b.cross(c)) > 0: element_dict["tetra4"][ind] = (tet[1], tet[0], tet[2], tet[3]) if dim == 2: nz = FreeCAD.Vector(0., 0., 1.) for (ind, tria) in list(element_dict["tria3"].items()): v0 = nodes[tria[0]] v1 = nodes[tria[1]] v2 = nodes[tria[2]] a = v1 - v0 b = v2 - v0 if nz.dot(a.cross(b)) < 0: element_dict["tria3"][ind] = (tria[1], tria[0], tria[2]) element_dict = {} element_counter = {} # TODO: remove upper level lookup for (key, val) in list(Fenics_to_FreeCAD_dict.items()): element_dict[val] = {} element_counter[ key] = 0 # count every distinct element and sub element type def addtupletodict(di, tpl, counter): sortedtpl = tuple(sorted(tpl)) if di.get(sortedtpl) is None: di[sortedtpl] = counter counter += 1 return counter def invertdict(dic): invdic = {} for (key, it) in list(dic.items()): invdic[it] = key return invdic num_vert_dict = { "interval": 2, "triangle": 3, "tetrahedron": 4, "hexahedron": 8, "quadrilateral": 4 } lower_dims_dict = { "interval": [], "triangle": ["interval"], "tetrahedron": ["triangle", "interval"], "hexahedron": ["quadrilateral", "interval"], "quadrilateral": ["interval"] } # generate cell list from file # read vertex list from cells # generate lower dimensional objects in mesh from cell for (cell_index, cell) in list(cell_dict.items()): cell_lower_dims = lower_dims_dict[cell_type] element_counter[cell_type] += 1 element_dict[Fenics_to_FreeCAD_dict[cell_type]][ cell] = element_counter[cell_type] for ld in cell_lower_dims: for vertextuple in itertools.combinations( cell, num_vert_dict[ld]): element_counter[ld] = addtupletodict( element_dict[Fenics_to_FreeCAD_dict[ld]], vertextuple, element_counter[ld]) length_counter = len(nodes) # maintain distinct counting values # print("nodes") # print("len & len counter", length_counter) for (key, val_dict) in list(element_dict.items()): # to ensure distinct indices for FreeCAD # print("key: ", key) for (vkey, it) in list(val_dict.items()): val_dict[ vkey] = it + length_counter # maintain distinct element numbers len_val_dict = len(val_dict) if len_val_dict > 0: length_counter += len_val_dict + 1 # only if preceding list is not empty # print("len: ", len_val_dict) # print("lencounter: ", length_counter) # inverse of the dict (dict[key] = val -> dict[val] = key) element_dict[key] = invertdict(val_dict) correct_volume_det(element_dict) # corrects negative determinants return element_dict # returns complete element dictionary nodes = {} element_dict = {} # TODO: remove two times initialization for val in list(Fenics_to_FreeCAD_dict.values()): element_dict[val] = {} tree = ET.parse(xmlfilename) root = tree.getroot() if root.tag.lower() != "dolfin": Console.PrintWarning("Strange root tag, should be dolfin!\n") find_mesh = root.find("mesh") if find_mesh is not None: # these are consistency checks of the XML structure Console.PrintMessage("Mesh found\n") (nodes, cells_dict, cell_type, dim) = read_mesh_block(find_mesh) element_dict = generate_lower_dimensional_structures( nodes, cells_dict, cell_type, dim) Console.PrintMessage("Show min max element dict") for (elm, numbers) in list(element_dict.items()): lst = sorted(list(numbers.items()), key=lambda x: x[0]) if lst != []: Console.PrintWarning(elm, " min: ", lst[0], " max: ", lst[-1], "\n") else: Console.PrintError("No mesh found") if root.find("data") is not None: Console.PrintLog("Internal mesh data found\n") return { "Nodes": nodes, "Seg2Elem": element_dict["seg2"], "Seg3Elem": {}, "Tria3Elem": element_dict["tria3"], "Tria6Elem": {}, "Quad4Elem": element_dict["quad4"], "Quad8Elem": {}, "Tetra4Elem": element_dict["tetra4"], "Tetra10Elem": {}, "Hexa8Elem": {}, "Hexa20Elem": {}, "Penta6Elem": {}, "Penta15Elem": {} }
def get_boundary_layer_data(self): # mesh boundary layer # currently only one boundary layer setting object is allowed # but multiple boundary can be selected # Mesh.CharacteristicLengthMin, must be zero # or a value less than first inflation layer height if not self.mesh_obj.MeshBoundaryLayerList: # print(" No mesh boundary layer setting document object.") pass else: Console.PrintMessage(" Mesh boundary layers, we need to get the elements.\n") if self.part_obj.Shape.ShapeType == "Compound": # see http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&start=40#p149467 and # http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&p=149520#p149520 err = ( "Gmsh could return unexpected meshes for a boolean split tools Compound. " "It is strongly recommended to extract the shape to mesh " "from the Compound and use this one." ) Console.PrintError(err + "\n") for mr_obj in self.mesh_obj.MeshBoundaryLayerList: if mr_obj.MinimumThickness and Units.Quantity(mr_obj.MinimumThickness).Value > 0: if mr_obj.References: belem_list = [] for sub in mr_obj.References: # print(sub[0]) # Part the elements belongs to # check if the shape of the mesh boundary_layer is an # element of the Part to mesh # if not try to find the element in the shape to mesh search_ele_in_shape_to_mesh = False if not self.part_obj.Shape.isSame(sub[0].Shape): Console.PrintLog( " One element of the mesh boundary layer {} is " "not an element of the Part to mesh.\n" "But we are going to try to find it in " "the Shape to mesh :-)\n" .format(mr_obj.Name) ) search_ele_in_shape_to_mesh = True for elems in sub[1]: # print(elems) # elems --> element if search_ele_in_shape_to_mesh: # we try to find the element it in the Shape to mesh # and use the found element as elems # the method getElement(element) does not return Solid elements ele_shape = geomtools.get_element(sub[0], elems) found_element = geomtools.find_element_in_shape( self.part_obj.Shape, ele_shape ) if found_element: # also elems = found_element else: Console.PrintError( "One element of the mesh boundary layer {} could " "not be found in the Part to mesh. " "It will be ignored.\n" .format(mr_obj.Name) ) # print(elems) # element if elems not in self.bl_boundary_list: # fetch settings in DocumentObject # fan setting is not implemented belem_list.append(elems) self.bl_boundary_list.append(elems) else: Console.PrintError( "The element {} of the mesh boundary " "layer {} has been added " "to another mesh boundary layer.\n" .format(elems, mr_obj.Name) ) setting = {} setting["hwall_n"] = Units.Quantity(mr_obj.MinimumThickness).Value setting["ratio"] = mr_obj.GrowthRate setting["thickness"] = sum([ setting["hwall_n"] * setting["ratio"] ** i for i in range( mr_obj.NumberOfLayers ) ]) # setting["hwall_n"] * 5 # tangential cell dimension setting["hwall_t"] = setting["thickness"] # hfar: cell dimension outside boundary # should be set later if some character length is set if self.clmax > setting["thickness"] * 0.8 \ and self.clmax < setting["thickness"] * 1.6: setting["hfar"] = self.clmax else: # set a value for safety, it may works as background mesh cell size setting["hfar"] = setting["thickness"] # from face name -> face id is done in geo file write up # TODO: fan angle setup is not implemented yet if self.dimension == "2": setting["EdgesList"] = belem_list elif self.dimension == "3": setting["FacesList"] = belem_list else: Console.PrintError( "boundary layer is only supported for 2D and 3D mesh.\n" ) self.bl_setting_list.append(setting) else: Console.PrintError( "The mesh boundary layer: {} is not used to create " "the mesh because the reference list is empty.\n" .format(mr_obj.Name) ) else: Console.PrintError( "The mesh boundary layer: {} is not used to create " "the mesh because the min thickness is 0.0 mm.\n" .format(mr_obj.Name) ) Console.PrintMessage(" {}\n".format(self.bl_setting_list))
def get_region_data(self): # mesh regions if not self.mesh_obj.MeshRegionList: # print(" No mesh regions.") pass else: Console.PrintMessage(' Mesh regions, we need to get the elements.\n') # by the use of MeshRegion object and a BooleanSplitCompound # there could be problems with node numbers see # http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&start=40#p149467 # http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&p=149520#p149520 part = self.part_obj if ( self.mesh_obj.MeshRegionList and part.Shape.ShapeType == "Compound" and ( femutils.is_of_type(part, "FeatureBooleanFragments") or femutils.is_of_type(part, "FeatureSlice") or femutils.is_of_type(part, "FeatureXOR") ) ): error_message = ( " The mesh to shape is a boolean split tools Compound " "and the mesh has mesh region list. " "Gmsh could return unexpected meshes in such circumstances. " "It is strongly recommended to extract the shape to mesh " "from the Compound and use this one." ) Console.PrintError(error_message + "\n") # TODO: no gui popup because FreeCAD will be in a endless output loop # as long as the pop up is on --> maybe find a better solution for # either of both --> thus the pop up is in task panel for mr_obj in self.mesh_obj.MeshRegionList: # print(mr_obj.Name) # print(mr_obj.CharacteristicLength) # print(Units.Quantity(mr_obj.CharacteristicLength).Value) if mr_obj.CharacteristicLength: if mr_obj.References: for sub in mr_obj.References: # print(sub[0]) # Part the elements belongs to # check if the shape of the mesh region # is an element of the Part to mesh # if not try to find the element in the shape to mesh search_ele_in_shape_to_mesh = False if not self.part_obj.Shape.isSame(sub[0].Shape): Console.PrintLog( " One element of the meshregion {} is " "not an element of the Part to mesh.\n" "But we are going to try to find it in " "the Shape to mesh :-)\n" .format(mr_obj.Name) ) search_ele_in_shape_to_mesh = True for elems in sub[1]: # print(elems) # elems --> element if search_ele_in_shape_to_mesh: # we're going to try to find the element in the # Shape to mesh and use the found element as elems # the method getElement(element) # does not return Solid elements ele_shape = geomtools.get_element(sub[0], elems) found_element = geomtools.find_element_in_shape( self.part_obj.Shape, ele_shape ) if found_element: elems = found_element else: Console.PrintError( "One element of the meshregion {} could not be found " "in the Part to mesh. It will be ignored.\n" .format(mr_obj.Name) ) # print(elems) # element if elems not in self.ele_length_map: self.ele_length_map[elems] = Units.Quantity( mr_obj.CharacteristicLength ).Value else: Console.PrintError( "The element {} of the meshregion {} has " "been added to another mesh region.\n" .format(elems, mr_obj.Name) ) else: Console.PrintError( "The meshregion: {} is not used to create the mesh " "because the reference list is empty.\n" .format(mr_obj.Name) ) else: Console.PrintError( "The meshregion: {} is not used to create the " "mesh because the CharacteristicLength is 0.0 mm.\n" .format(mr_obj.Name) ) for eleml in self.ele_length_map: # the method getElement(element) does not return Solid elements ele_shape = geomtools.get_element(self.part_obj, eleml) ele_vertexes = geomtools.get_vertexes_by_element(self.part_obj.Shape, ele_shape) self.ele_node_map[eleml] = ele_vertexes Console.PrintMessage(" {}\n".format(self.ele_length_map)) Console.PrintMessage(" {}\n".format(self.ele_node_map))
def importFrame3DD( filename, analysis=None, result_name_prefix="" ): from . import importToolsFem #from . import Fem.feminout.importToolsFem import ObjectsFem #import result mesh only Console.PrintMessage( "Read Frame3DD results from Frame3DD file: {}\n" .format(filename) ) Frame3DD_file = pyopen(filename, "r") nodes = {} elem = -1 elemType = 0 nDisp=0 numNode = 0 numFixedNode = 0 numMember = 0 numLC = 0 isDebug=0 NodeList = {} MemberList = {} tline=[] for line in Frame3DD_file: tline.append(line.strip()) for i in range(len(tline)): #Console.PrintError(tline[i]) tStrNode="In 2D problems the Y-axis is vertical. In 3D problems the Z-axis is vertical." if tline[i].strip() == tStrNode: #Console.PrintError("FEM: nodes found.\n") i=i+1 i=i+1 data = tline[i].split() #12 NODES 12 FIXED NODES 21 FRAME ELEMENTS 2 LOAD CASES numNode = int(data[0]) numFixedNode = int(data[2]) numMember = int(data[5]) numLC = int(data[8]) i=i+1 # = fp.readline().strip() i=i+1 # = fp.readline().strip() i=i+1 # = fp.readline().strip() #print ("") #print ("numNode: "+str(numNode)) for id in range(numNode): # node #1 0.000000 0.000000 0.000000 0.000 1 1 1 1 1 0 i=i+1 #print (tline[i]) dataNode = tline[i].split() elem = int(dataNode[0]) nodes_x = float(dataNode[1]) nodes_y = float(dataNode[2]) nodes_z = float(dataNode[3]) nodes[elem] = FreeCAD.Vector(nodes_x, nodes_y, nodes_z) NodeList[id] = Node(str(id+1), nodes_x, nodes_y, nodes_z ) i=i+1 i=i+1 #print ("") #print ("numMember: "+str(numMember)) for id in range(numMember): # Member i=i+1 #print (tline[i]) dataNode = tline[i].split() elem = int(dataNode[0]) nd1 = int(dataNode[1]) nd2 = int(dataNode[2]) MemberList[id] = Member(str(id+1) ,nd1, nd2) #print ("") #print ("numFixedNode: "+str(numFixedNode)) #print ("numLC: "+str(numLC)) femmesh = Fem.FemMesh() # nodes #print ("Add nodes") for id in NodeList: # node #femmesh.addNode(NodeList[id].x,NodeList[id].y,NodeList[id].z, int(id)+1 ) femmesh.addNode(NodeList[id].x,NodeList[id].y,NodeList[id].z, int(id)+1 ) # elements for id in MemberList: n1 = MemberList[id].n1 n2 = MemberList[id].n2 femmesh.addEdge([int(n1), int(n2)], int(id)+1) # close Frame3DD file if loop over all lines is finished Frame3DD_file.close() if not nodes: Console.PrintError("FEM: No nodes found in Frame3DD file.\n") else: #Console.PrintError(tline[i]) result_mesh_object = None #mesh = importToolsFem.make_femmesh(m) result_mesh_object = ObjectsFem.makeMeshResult( FreeCAD.ActiveDocument, "ResultMesh" ) result_mesh_object.FemMesh = femmesh res_mesh_is_compacted = False nodenumbers_for_compacted_mesh = [] #import result mm = read_Frame3DD_result(filename) number_of_increments = len(mm["Results"]) Console.PrintLog( "Increments: " + str(number_of_increments) + "\n" ) if len(mm["Results"]) > 0: res_obj=[] iLC=0 iModal=0 results_name="Elastic" for result_set in mm["Results"]: if (iLC<numLC): results_name="Elastic" res_obj.append(ObjectsFem.makeResultMechanical(FreeCAD.ActiveDocument, results_name+str(iLC))) else: results_name="Modal" res_obj.append(ObjectsFem.makeResultMechanical(FreeCAD.ActiveDocument, results_name+str(iModal))) iModal+=1 res_obj[iLC].Mesh = result_mesh_object res_obj[iLC] = importToolsFem.fill_femresult_mechanical(res_obj[iLC], result_set) if analysis: analysis.addObject(res_obj[iLC]) # complementary result object calculations import femresult.resulttools as restools import femtools.femutils as femutils if not res_obj[iLC].MassFlowRate: if res_mesh_is_compacted is False: # first result set, compact FemMesh and NodeNumbers res_obj[iLC] = restools.compact_result(res_obj[iLC]) res_mesh_is_compacted = True nodenumbers_for_compacted_mesh = res_obj[iLC].NodeNumbers else: # all other result sets, do not compact FemMesh, only set NodeNumbers res_obj[iLC].NodeNumbers = nodenumbers_for_compacted_mesh # fill DisplacementLengths res_obj[iLC] = restools.add_disp_apps(res_obj[iLC]) # fill StressValues res_obj[iLC] = restools.add_von_mises(res_obj[iLC]) if res_obj[iLC].getParentGroup(): has_reinforced_mat = False for obj in res_obj[iLC].getParentGroup().Group: if obj.isDerivedFrom("App::MaterialObjectPython") \ and femutils.is_of_type(obj, "Fem::MaterialReinforced"): has_reinforced_mat = True restools.add_principal_stress_reinforced(res_obj[iLC]) break if has_reinforced_mat is False: # fill PrincipalMax, PrincipalMed, PrincipalMin, MaxShear res_obj[iLC] = restools.add_principal_stress_std(res_obj[iLC]) else: # if a pure Frame3DD file was opened no analysis and thus no parent group # fill PrincipalMax, PrincipalMed, PrincipalMin, MaxShear res_obj[iLC] = restools.add_principal_stress_std(res_obj[iLC]) # fill Stats res_obj[iLC] = restools.fill_femresult_stats(res_obj[iLC]) iLC+=1 return res_obj else: error_message = ( "We have nodes only.\n" ) Console.PrintMessage(error_message) if analysis: analysis.addObject(result_mesh_object) if FreeCAD.GuiUp: if analysis: import FemGui FemGui.setActiveAnalysis(analysis) FreeCAD.ActiveDocument.recompute()
def importFrame3DDCase(filename, analysis=None, result_name_prefix=""): from . import importToolsFem import ObjectsFem m = read_Frame3DD_case(filename) result_mesh_object = None if len(m["Nodes"]) > 0: mesh = importToolsFem.make_femmesh(m) result_mesh_object = ObjectsFem.makeMeshResult(FreeCAD.ActiveDocument, "ResultMesh") result_mesh_object.FemMesh = mesh res_mesh_is_compacted = False nodenumbers_for_compacted_mesh = [] number_of_increments = len(m["Results"]) Console.PrintLog("Increments: " + str(number_of_increments) + "\n") if len(m["Results"]) > 0: for result_set in m["Results"]: if "number" in result_set: eigenmode_number = result_set["number"] else: eigenmode_number = 0 step_time = result_set["time"] step_time = round(step_time, 2) if eigenmode_number > 0: results_name = ("{}Mode{}_Results".format( result_name_prefix, eigenmode_number)) elif number_of_increments > 1: results_name = ("{}Time{}_Results".format( result_name_prefix, step_time)) else: results_name = ("{}Results".format(result_name_prefix)) res_obj = ObjectsFem.makeResultMechanical( FreeCAD.ActiveDocument, results_name) res_obj.Mesh = result_mesh_object res_obj = importToolsFem.fill_femresult_mechanical( res_obj, result_set) if analysis: analysis.addObject(res_obj) # complementary result object calculations import femresult.resulttools as restools import femtools.femutils as femutils if not res_obj.MassFlowRate: # information 1: # only compact result if not Flow 1D results # compact result object, workaround for bug 2873 # https://www.freecadweb.org/tracker/view.php?id=2873 # information 2: # if the result data has multiple result sets there will be multiple result objs # they all will use one mesh obj # on the first res obj fill the mesh obj will be compacted, thus # it does not need to be compacted on further result sets # but NodeNumbers need to be compacted for every result set (res object fill) # example Frame3DD file: https://forum.freecadweb.org/viewtopic.php?t=32649#p274291 if res_mesh_is_compacted is False: # first result set, compact FemMesh and NodeNumbers res_obj = restools.compact_result(res_obj) res_mesh_is_compacted = True nodenumbers_for_compacted_mesh = res_obj.NodeNumbers else: # all other result sets, do not compact FemMesh, only set NodeNumbers res_obj.NodeNumbers = nodenumbers_for_compacted_mesh # fill DisplacementLengths res_obj = restools.add_disp_apps(res_obj) # fill StressValues res_obj = restools.add_von_mises(res_obj) if res_obj.getParentGroup(): has_reinforced_mat = False for obj in res_obj.getParentGroup().Group: if obj.isDerivedFrom("App::MaterialObjectPython") \ and femutils.is_of_type(obj, "Fem::MaterialReinforced"): has_reinforced_mat = True restools.add_principal_stress_reinforced(res_obj) break if has_reinforced_mat is False: # fill PrincipalMax, PrincipalMed, PrincipalMin, MaxShear res_obj = restools.add_principal_stress_std(res_obj) else: # if a pure Frame3DD file was opened no analysis and thus no parent group # fill PrincipalMax, PrincipalMed, PrincipalMin, MaxShear res_obj = restools.add_principal_stress_std(res_obj) # fill Stats res_obj = restools.fill_femresult_stats(res_obj) return res_obj else: error_message = ( "We have nodes but no results in Frame3DD file, " "which means we only have a mesh in Frame3DD file. " "Usually this happens for analysis type 'NOANALYSIS' " "or if Frame3DD returned no results because " "of nonpositive jacobian determinant in at least one element.\n" ) Console.PrintMessage(error_message) if analysis: analysis.addObject(result_mesh_object) if FreeCAD.GuiUp: if analysis: import FemGui FemGui.setActiveAnalysis(analysis) FreeCAD.ActiveDocument.recompute() else: Console.PrintError( "Problem on Frame3DD file import. No nodes found in Frame3DD file.\n" )
def make_femmesh(mesh_data): """ makes an FreeCAD FEM Mesh object from FEM Mesh data """ import Fem mesh = Fem.FemMesh() m = mesh_data if ("Nodes" in m) and (len(m["Nodes"]) > 0): FreeCAD.Console.PrintLog("Found: nodes\n") if (("Seg2Elem" in m) or ("Seg3Elem" in m) or ("Tria3Elem" in m) or ("Tria6Elem" in m) or ("Quad4Elem" in m) or ("Quad8Elem" in m) or ("Tetra4Elem" in m) or ("Tetra10Elem" in m) or ("Penta6Elem" in m) or ("Penta15Elem" in m) or ("Hexa8Elem" in m) or ("Hexa20Elem" in m)): nds = m["Nodes"] FreeCAD.Console.PrintLog("Found: elements\n") for i in nds: n = nds[i] mesh.addNode(n[0], n[1], n[2], i) elms_hexa8 = m["Hexa8Elem"] for i in elms_hexa8: e = elms_hexa8[i] mesh.addVolume( [e[0], e[1], e[2], e[3], e[4], e[5], e[6], e[7]], i) elms_penta6 = m["Penta6Elem"] for i in elms_penta6: e = elms_penta6[i] mesh.addVolume([e[0], e[1], e[2], e[3], e[4], e[5]], i) elms_tetra4 = m["Tetra4Elem"] for i in elms_tetra4: e = elms_tetra4[i] mesh.addVolume([e[0], e[1], e[2], e[3]], i) elms_tetra10 = m["Tetra10Elem"] for i in elms_tetra10: e = elms_tetra10[i] mesh.addVolume([ e[0], e[1], e[2], e[3], e[4], e[5], e[6], e[7], e[8], e[9] ], i) elms_penta15 = m["Penta15Elem"] for i in elms_penta15: e = elms_penta15[i] mesh.addVolume([ e[0], e[1], e[2], e[3], e[4], e[5], e[6], e[7], e[8], e[9], e[10], e[11], e[12], e[13], e[14] ], i) elms_hexa20 = m["Hexa20Elem"] for i in elms_hexa20: e = elms_hexa20[i] mesh.addVolume([ e[0], e[1], e[2], e[3], e[4], e[5], e[6], e[7], e[8], e[9], e[10], e[11], e[12], e[13], e[14], e[15], e[16], e[17], e[18], e[19] ], i) elms_tria3 = m["Tria3Elem"] for i in elms_tria3: e = elms_tria3[i] mesh.addFace([e[0], e[1], e[2]], i) elms_tria6 = m["Tria6Elem"] for i in elms_tria6: e = elms_tria6[i] mesh.addFace([e[0], e[1], e[2], e[3], e[4], e[5]], i) elms_quad4 = m["Quad4Elem"] for i in elms_quad4: e = elms_quad4[i] mesh.addFace([e[0], e[1], e[2], e[3]], i) elms_quad8 = m["Quad8Elem"] for i in elms_quad8: e = elms_quad8[i] mesh.addFace([e[0], e[1], e[2], e[3], e[4], e[5], e[6], e[7]], i) elms_seg2 = m["Seg2Elem"] for i in elms_seg2: e = elms_seg2[i] mesh.addEdge([e[0], e[1]], i) elms_seg3 = m["Seg3Elem"] for i in elms_seg3: e = elms_seg3[i] mesh.addEdge([e[0], e[1], e[2]], i) Console.PrintLog( "imported mesh: {} nodes, {} HEXA8, {} PENTA6, {} TETRA4, {} TETRA10, {} PENTA15\n" .format(len(nds), len(elms_hexa8), len(elms_penta6), len(elms_tetra4), len(elms_tetra10), len(elms_penta15))) Console.PrintLog( "imported mesh: {} " "HEXA20, {} TRIA3, {} TRIA6, {} QUAD4, {} QUAD8, {} SEG2, {} SEG3\n" .format(len(elms_hexa20), len(elms_tria3), len(elms_tria6), len(elms_quad4), len(elms_quad8), len(elms_seg2), len(elms_seg3))) else: Console.PrintError("No Elements found!\n") else: Console.PrintError("No Nodes found!\n") return mesh