def makeFillet(objs, radius=100, chamfer=False, delete=False): """Create a fillet between two lines or edges. Parameters ---------- objs : list List of two objects of type wire, or edges. radius : float, optional It defaults to 100 mm. The curvature of the fillet. chamfer : bool, optional It defaults to `False`. If it is `True` it no longer produces a rounded fillet but a chamfer (straight edge) with the value of the `radius`. delete : bool, optional It defaults to `False`. If it is `True` it will delete the pair of objects that are used to create the fillet. Otherwise, the original objects will still be there. Returns ------- Part::Part2DObject The object of type `'Fillet'`. It returns `None` if it fails producing the object. """ if len(objs) != 2: FCC.PrintError("makeFillet: " + translate("draft", "two elements needed") + "\n") return None e1, e2 = _extract_edges(objs) edges = DraftGeomUtils.fillet([e1, e2], radius, chamfer) if len(edges) < 3: FCC.PrintError("makeFillet: " + translate("draft", "radius too large")) FCC.PrintError(", r=" + str(radius) + "\n") return None _d = translate("draft", "length: ") FCC.PrintMessage("e1, " + _d + str(edges[0].Length) + "\n") FCC.PrintMessage("e2, " + _d + str(edges[1].Length) + "\n") FCC.PrintMessage("e3, " + _d + str(edges[2].Length) + "\n") try: wire = Part.Wire(edges) except Part.OCCError: return None obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython", "Fillet") Fillet(obj) obj.Shape = wire obj.Length = wire.Length obj.Start = wire.Vertexes[0].Point obj.End = wire.Vertexes[-1].Point obj.FilletRadius = radius if delete: FreeCAD.ActiveDocument.removeObject(objs[0].Name) FreeCAD.ActiveDocument.removeObject(objs[1].Name) _r = translate("draft", "removed original objects") FCC.PrintMessage("makeFillet: " + _r + "\n") if FreeCAD.GuiUp: Draft._ViewProviderWire(obj.ViewObject) Draft.formatObject(obj) Draft.select(obj) return obj
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 import_z88_disp(filename, analysis=None, result_name_prefix=None): """insert a FreeCAD FEM mechanical result object in the ActiveDocument pure usage: import feminout.importZ88O2Results as importZ88O2Results disp_file = "/pathtofile/z88o2.txt" importZ88O2Results.import_z88_disp(disp_file) the z888i1.txt FEMMesh file needs to be in the same directory as z88o2.txt # ahh, make a new document first ;-) """ import ObjectsFem from . import importZ88Mesh from . import importToolsFem from femresult import resulttools if result_name_prefix is None: result_name_prefix = "" disp_read = read_z88_disp(filename) result_mesh_object = None if len(disp_read["Nodes"]) > 0: if analysis: analysis_object = analysis # read result mesh if filename.endswith("z88o2.txt"): mesh_file = filename.replace("o2", "i1") mesh_data = importZ88Mesh.read_z88_mesh(mesh_file) femmesh = importToolsFem.make_femmesh(mesh_data) result_mesh_object = ObjectsFem.makeMeshResult( FreeCAD.ActiveDocument, "Result_mesh") result_mesh_object.FemMesh = femmesh else: Console.PrintError("Z88 mesh file z88i1.txt not found.\n") return None # create result obj for result_set in disp_read["Results"]: results_name = result_name_prefix + "results" res_obj = ObjectsFem.makeResultMechanical(FreeCAD.ActiveDocument, results_name) res_obj.Mesh = result_mesh_object res_obj = importToolsFem.fill_femresult_mechanical( res_obj, result_set) res_obj = resulttools.add_disp_apps( res_obj) # fill DisplacementLengths res_obj = resulttools.fill_femresult_stats(res_obj) # fill Stats if analysis: analysis_object.addObject(res_obj) if FreeCAD.GuiUp: if analysis: import FemGui FemGui.setActiveAnalysis(analysis_object) FreeCAD.ActiveDocument.recompute() else: Console.PrintError( "Problem on Z88 result file import. No nodes found in Z88 result file.\n" ) return None 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 write_z88_mesh_to_file( femnodes_mesh, femelement_table, z88_element_type, f ): node_dimension = 3 # 2 for 2D not supported if ( z88_element_type == 4 or z88_element_type == 17 or z88_element_type == 16 or z88_element_type == 1 or z88_element_type == 10 ): node_dof = 3 elif ( z88_element_type == 23 or z88_element_type == 24 ): node_dof = 6 # schalenelemente else: Console.PrintError("Error: wrong z88_element_type.\n") return node_count = len(femnodes_mesh) element_count = len(femelement_table) dofs = node_dof * node_count unknown_flag = 0 written_by = "written by FreeCAD" # first line, some z88 specific stuff f.write("{0} {1} {2} {3} {4} {5}\n".format( node_dimension, node_count, element_count, dofs, unknown_flag, written_by) ) # nodes for node in femnodes_mesh: vec = femnodes_mesh[node] f.write( "{0} {1} {2:.6f} {3:.6f} {4:.6f}\n" .format(node, node_dof, vec.x, vec.y, vec.z) ) # elements for element in femelement_table: # z88_element_type is checked for every element # but mixed elements are not supported up to date n = femelement_table[element] if ( z88_element_type == 2 or z88_element_type == 4 or z88_element_type == 5 or z88_element_type == 9 or z88_element_type == 13 or z88_element_type == 25 ): # seg2 FreeCAD --> stab4 Z88 # N1, N2 f.write("{0} {1}\n".format(element, z88_element_type)) f.write("{0} {1}\n".format( n[0], n[1])) elif z88_element_type == 3 or z88_element_type == 14 or z88_element_type == 24: # tria6 FreeCAD --> schale24 Z88 # N1, N2, N3, N4, N5, N6 f.write("{0} {1}\n".format(element, z88_element_type)) f.write("{0} {1} {2} {3} {4} {5}\n".format( n[0], n[1], n[2], n[3], n[4], n[5])) elif z88_element_type == 7 or z88_element_type == 20 or z88_element_type == 23: # quad8 FreeCAD --> schale23 Z88 # N1, N2, N3, N4, N5, N6, N7, N8 f.write("{0} {1}\n".format(element, z88_element_type)) f.write("{0} {1} {2} {3} {4} {5} {6} {7}\n".format( n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7])) elif z88_element_type == 17: # tetra4 FreeCAD --> volume17 Z88 # N4, N2, N3, N1 f.write("{0} {1}\n".format(element, z88_element_type)) f.write("{0} {1} {2} {3}\n".format( n[3], n[1], n[2], n[0])) elif z88_element_type == 16: # tetra10 FreeCAD --> volume16 Z88 # N1, N2, N4, N3, N5, N9, N8, N6, N10, N7, FC to Z88 is different as Z88 to FC f.write("{0} {1}\n".format(element, z88_element_type)) f.write("{0} {1} {2} {3} {4} {5} {6} {7} {8} {9}\n".format( n[0], n[1], n[3], n[2], n[4], n[8], n[7], n[5], n[9], n[6])) elif z88_element_type == 1: # hexa8 FreeCAD --> volume1 Z88 # N1, N2, N3, N4, N5, N6, N7, N8 f.write("{0} {1}\n".format(element, z88_element_type)) f.write("{0} {1} {2} {3} {4} {5} {6} {7}\n".format( n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7])) elif z88_element_type == 10: # hexa20 FreeCAD --> volume10 Z88 # 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 f.write("{0} {1}\n".format(element, z88_element_type)) f.write( "{0} {1} {2} {3} {4} {5} {6} {7} {8} {9} " "{10} {11} {12} {13} {14} {15} {16} {17} {18} {19}\n" .format( n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8], n[9], n[10], n[11], n[12], n[13], n[14], n[15], n[16], n[17], n[18], n[19] ) ) else: Console.PrintError( "Writing of Z88 elementtype {0} not supported.\n".format(z88_element_type) ) # TODO support schale12 (made from prism15) and schale16 (made from hexa20) return
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 from femresult import resulttools from femtools import 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 = 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 # fill DisplacementLengths res_obj = resulttools.add_disp_apps(res_obj) # fill vonMises res_obj = resulttools.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 resulttools.add_principal_stress_reinforced( res_obj) break if has_reinforced_mat is False: # fill PrincipalMax, PrincipalMed, PrincipalMin, MaxShear res_obj = resulttools.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 = resulttools.add_principal_stress_std(res_obj) # fill Stats res_obj = resulttools.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 write_fenics_mesh_codim_xdmf(fem_mesh_obj, topologynode, nodes_dict, codim=0, encoding=ENCODING_ASCII): mesh_dimension = get_FemMeshObjectDimension(fem_mesh_obj) element_types = get_FemMeshObjectElementTypes( fem_mesh_obj, remove_zero_element_entries=True) element_order = get_FemMeshObjectOrder(fem_mesh_obj) # we get all elements from mesh to decide which one to write by selection of codim """ nodeindices = [( nodes_dict[ind] for ind in fem_mesh_obj.FemMesh.getElementNodes(fc_volume_ind) ) for (fen_ind, fc_volume_ind) in enumerate(fc_cells)] """ writeout_element_dimension = mesh_dimension - codim (num_topo, name_topo, dim_topo) = (0, "", 0) for (num, name, dim) in element_types: if writeout_element_dimension == dim: (num_topo, name_topo, dim_topo) = (num, name, dim) (topology_type, nodes_per_element) = FreeCAD_to_Fenics_XDMF_dict[(name_topo, element_order)] topologynode.set("TopologyType", topology_type) topologynode.set("NumberOfElements", str(num_topo)) topologynode.set("NodesPerElement", str(nodes_per_element)) if dim_topo == 3: fc_topo = fem_mesh_obj.FemMesh.Volumes elif dim_topo == 2: fc_topo = fem_mesh_obj.FemMesh.Faces elif dim_topo == 1: fc_topo = fem_mesh_obj.FemMesh.Edges elif dim_topo == 0: fc_topo = fem_mesh_obj.FemMesh.Nodes else: fc_topo = [] Console.PrintError( "Dimension of mesh incompatible with export XDMF function: %d\n" % (dim_topo, )) nodeindices = [ (nodes_dict[ind] for ind in fem_mesh_obj.FemMesh.getElementNodes(fc_topo_ind)) for (fen_ind, fc_topo_ind) in enumerate(fc_topo) ] if encoding == ENCODING_ASCII: dataitem = ET.SubElement(topologynode, "DataItem", NumberType="UInt", Dimensions="%d %d" % (num_topo, nodes_per_element), Format="XML") dataitem.text = numpy_array_to_str( tuples_to_numpy(nodeindices, nodes_per_element)) elif encoding == ENCODING_HDF5: pass return fc_topo
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 read_frd_result(frd_input): Console.PrintMessage( "Read ccx results from frd file: {}\n".format(frd_input)) inout_nodes = [] inout_nodes_file = frd_input.rsplit(".", 1)[0] + "_inout_nodes.txt" if os.path.exists(inout_nodes_file): Console.PrintMessage( "Read special 1DFlow nodes data form: {}\n".format( inout_nodes_file)) f = pyopen(inout_nodes_file, "r") lines = f.readlines() for line in lines: a = line.split(",") inout_nodes.append(a) f.close() Console.PrintMessage("{}\n".format(inout_nodes)) frd_file = pyopen(frd_input, "r") 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 = {} results = [] mode_results = {} mode_results["number"] = float("NaN") mode_results["time"] = float("NaN") mode_disp = {} mode_stress = {} mode_strain = {} mode_peeq = {} mode_temp = {} mode_massflow = {} mode_networkpressure = {} nodes_found = False elements_found = False mode_time_found = False mode_disp_found = False mode_stress_found = False mode_strain_found = False mode_peeq_found = False mode_temp_found = False mode_massflow_found = False mode_networkpressure_found = False end_of_section_found = False end_of_frd_data_found = False input_continues = False mode_eigen_changed = False mode_time_changed = False eigenmode = 0 eigentemp = 0 elem = -1 elemType = 0 timestep = 0 timetemp = 0 for line in frd_file: # Check if we found nodes section if line[4:6] == "2C": nodes_found = True if nodes_found and (line[1:3] == "-1"): # we found a nodes line, lets extract the node and coordinate data elem = int(line[4:13]) nodes_x = float(line[13:25]) nodes_y = float(line[25:37]) nodes_z = float(line[37:49]) nodes[elem] = FreeCAD.Vector(nodes_x, nodes_y, nodes_z) # Check if we found elements section if line[4:6] == "3C": elements_found = True if elements_found and (line[1:3] == "-1"): # we found a first element line, lets extract element number elem = int(line[4:13]) elemType = int(line[14:18]) if elements_found and (line[1:3] == "-2"): # we found a second element line, lets extract the elements # node order fits with node order in writeAbaqus() in FemMesh.cpp if elemType == 1: # C3D8 CalculiX --> hexa8 FreeCAD # N6, N7, N8, N5, N2, N3, N4, N1 nd1 = int(line[3:13]) nd2 = int(line[13:23]) nd3 = int(line[23:33]) nd4 = int(line[33:43]) nd5 = int(line[43:53]) nd6 = int(line[53:63]) nd7 = int(line[63:73]) nd8 = int(line[73:83]) elements_hexa8[elem] = (nd6, nd7, nd8, nd5, nd2, nd3, nd4, nd1) elif elemType == 2: # C3D6 Calculix --> penta6 FreeCAD # N5, N6, N4, N2, N3, N1 nd1 = int(line[3:13]) nd2 = int(line[13:23]) nd3 = int(line[23:33]) nd4 = int(line[33:43]) nd5 = int(line[43:53]) nd6 = int(line[53:63]) elements_penta6[elem] = (nd5, nd6, nd4, nd2, nd3, nd1) elif elemType == 3: # C3D4 Calculix --> tetra4 FreeCAD # N2, N1, N3, N4 nd1 = int(line[3:13]) nd2 = int(line[13:23]) nd3 = int(line[23:33]) nd4 = int(line[33:43]) elements_tetra4[elem] = (nd2, nd1, nd3, nd4) elif elemType == 4 and input_continues is False: # first line # C3D20 Calculix --> hexa20 FreeCAD # N6, N7, N8, N5, N2, N3, N4, N1, N14, N15 # N16, N13, N10, N11, N12, N9, N18, N19, N20, N17 nd1 = int(line[3:13]) nd2 = int(line[13:23]) nd3 = int(line[23:33]) nd4 = int(line[33:43]) nd5 = int(line[43:53]) nd6 = int(line[53:63]) nd7 = int(line[63:73]) nd8 = int(line[73:83]) nd9 = int(line[83:93]) nd10 = int(line[93:103]) input_continues = True elif elemType == 4 and input_continues is True: # second line nd11 = int(line[3:13]) nd12 = int(line[13:23]) nd13 = int(line[23:33]) nd14 = int(line[33:43]) nd15 = int(line[43:53]) nd16 = int(line[53:63]) nd17 = int(line[63:73]) nd18 = int(line[73:83]) nd19 = int(line[83:93]) nd20 = int(line[93:103]) input_continues = False """ CalculiX uses a different node order in input file *.inp and result file *.frd for hexa20 (C3D20) according to Guido (the developer of ccx): see note in in first line of cgx manuel part element types ccx (and thus the *.inp) follows the ABAQUS convention documented in the ccx-documentation cgx (and thus the *.frd) follows the FAM2 convention documented in the cgx-documentation FAM32 is from the company FEGS limited maybe this company does not exist any more elements_hexa20[elem] = ( nd6, nd7, nd8, nd5, nd2, nd3, nd4, nd1, nd14, nd15, nd16, nd13, nd10, nd11, nd12, nd9, nd18, nd19, nd20, nd17 ) elements_hexa20[elem] = ( nd6, nd7, nd8, nd5, nd2, nd3, nd4, nd1, nd14, nd15, nd16, nd13, nd18, nd19, nd20, nd17, nd10, nd11, nd12, nd9 ) hexa20 import works with the following frd file node assignment """ elements_hexa20[elem] = (nd8, nd5, nd6, nd7, nd4, nd1, nd2, nd3, nd20, nd17, nd18, nd19, nd12, nd9, nd10, nd11, nd16, nd13, nd14, nd15) elif elemType == 5 and input_continues is False: # first line # C3D15 Calculix --> penta15 FreeCAD # N5, N6, N4, N2, N3, N1, N11, N12, N10, N8, N9, N7, N14, N15, N13 nd1 = int(line[3:13]) nd2 = int(line[13:23]) nd3 = int(line[23:33]) nd4 = int(line[33:43]) nd5 = int(line[43:53]) nd6 = int(line[53:63]) nd7 = int(line[63:73]) nd8 = int(line[73:83]) nd9 = int(line[83:93]) nd10 = int(line[93:103]) input_continues = True elif elemType == 5 and input_continues is True: # second line nd11 = int(line[3:13]) nd12 = int(line[13:23]) nd13 = int(line[23:33]) nd14 = int(line[33:43]) nd15 = int(line[43:53]) input_continues = False """ CalculiX uses a different node order in input file *.inp and result file *.frd for penta15 (C3D15) see notes at hexa20 elements_penta15[elem] = ( nd5, nd6, nd4, nd2, nd3, nd1, nd11, nd12, nd10, nd8, nd9, nd7, nd14, nd15, nd13 ) # order of the *.inp file """ elements_penta15[elem] = (nd5, nd6, nd4, nd2, nd3, nd1, nd14, nd15, nd13, nd8, nd9, nd7, nd11, nd12, nd10) elif elemType == 6: # C3D10 Calculix --> tetra10 FreeCAD # N2, N1, N3, N4, N5, N7, N6, N9, N8, N10 nd1 = int(line[3:13]) nd2 = int(line[13:23]) nd3 = int(line[23:33]) nd4 = int(line[33:43]) nd5 = int(line[43:53]) nd6 = int(line[53:63]) nd7 = int(line[63:73]) nd8 = int(line[73:83]) nd9 = int(line[83:93]) nd10 = int(line[93:103]) elements_tetra10[elem] = (nd2, nd1, nd3, nd4, nd5, nd7, nd6, nd9, nd8, nd10) elif elemType == 7: # S3 Calculix --> tria3 FreeCAD # N1, N2, N3 nd1 = int(line[3:13]) nd2 = int(line[13:23]) nd3 = int(line[23:33]) elements_tria3[elem] = (nd1, nd2, nd3) elif elemType == 8: # S6 CalculiX --> tria6 FreeCAD # N1, N2, N3, N4, N5, N6 nd1 = int(line[3:13]) nd2 = int(line[13:23]) nd3 = int(line[23:33]) nd4 = int(line[33:43]) nd5 = int(line[43:53]) nd6 = int(line[53:63]) elements_tria6[elem] = (nd1, nd2, nd3, nd4, nd5, nd6) elif elemType == 9: # S4 CalculiX --> quad4 FreeCAD # N1, N2, N3, N4 nd1 = int(line[3:13]) nd2 = int(line[13:23]) nd3 = int(line[23:33]) nd4 = int(line[33:43]) elements_quad4[elem] = (nd1, nd2, nd3, nd4) elif elemType == 10: # S8 CalculiX --> quad8 FreeCAD # N1, N2, N3, N4, N5, N6, N7, N8 nd1 = int(line[3:13]) nd2 = int(line[13:23]) nd3 = int(line[23:33]) nd4 = int(line[33:43]) nd5 = int(line[43:53]) nd6 = int(line[53:63]) nd7 = int(line[63:73]) nd8 = int(line[73:83]) elements_quad8[elem] = (nd1, nd2, nd3, nd4, nd5, nd6, nd7, nd8) elif elemType == 11: # B31 CalculiX --> seg2 FreeCAD # N1, N2 nd1 = int(line[3:13]) nd2 = int(line[13:23]) elements_seg2[elem] = (nd1, nd2) elif elemType == 12: # B32 CalculiX --> seg3 FreeCAD # Also D element element number # CalculiX uses a different node order in # input file *.inp and result file *.frd for seg3 (B32) # see notes at hexa20 # N1, N2 ,N3 nd1 = int(line[3:13]) nd2 = int(line[13:23]) nd3 = int(line[23:33]) if inout_nodes: for i in range(len(inout_nodes)): if nd1 == int(inout_nodes[i][1]): # fluid inlet node numbering elements_seg3[elem] = (int(inout_nodes[i][2]), nd3, nd1) elif nd3 == int(inout_nodes[i][1]): # fluid outlet node numbering elements_seg3[elem] = (nd1, int(inout_nodes[i][2]), nd3) else: # normal node numbering for D, B32 elements elements_seg3[elem] = (nd1, nd2, nd3) # Check if we found new eigenmode line if line[5:10] == "PMODE": eigentemp = int(line[30:36]) if eigentemp > eigenmode: eigenmode = eigentemp mode_eigen_changed = True # Check if we found new time step if line[4:10] == "1PSTEP": mode_time_found = True if mode_time_found and (line[2:7] == "100CL"): # we found the new time step line # !!! be careful here, there is timetemp and timestep! # TODO: use more differ names timetemp = float(line[13:25]) if timetemp > timestep: timestep = timetemp mode_time_changed = True # Check if we found displacement section if line[5:9] == "DISP": mode_disp_found = True if mode_disp_found and (line[1:3] == "-1"): # we found a displacement line elem = int(line[4:13]) mode_disp_x = float(line[13:25]) mode_disp_y = float(line[25:37]) mode_disp_z = float(line[37:49]) mode_disp[elem] = FreeCAD.Vector(mode_disp_x, mode_disp_y, mode_disp_z) # Check if we found stress section if line[5:11] == "STRESS": mode_stress_found = True if mode_stress_found and (line[1:3] == "-1"): # we found a stress line elem = int(line[4:13]) stress_1 = float(line[13:25]) stress_2 = float(line[25:37]) stress_3 = float(line[37:49]) stress_4 = float(line[49:61]) stress_5 = float(line[61:73]) stress_6 = float(line[73:85]) # CalculiX frd files: (Sxx, Syy, Szz, Sxy, Syz, Szx) # FreeCAD: (Sxx, Syy, Szz, Sxy, Sxz, Syz) # thus exchange the last two entries mode_stress[elem] = (stress_1, stress_2, stress_3, stress_4, stress_6, stress_5) # Check if we found strain section if line[5:13] == "TOSTRAIN": mode_strain_found = True if mode_strain_found and (line[1:3] == "-1"): # we found a strain line in the frd file elem = int(line[4:13]) strain_1 = float(line[13:25]) strain_2 = float(line[25:37]) strain_3 = float(line[37:49]) strain_4 = float(line[49:61]) strain_5 = float(line[61:73]) strain_6 = float(line[73:85]) # CalculiX frd files: (Exx, Eyy, Ezz, Exy, Eyz, Ezx) # FreeCAD: (Exx, Eyy, Ezz, Exy, Exz, Eyz) # thus exchange the last two entries mode_strain[elem] = (strain_1, strain_2, strain_3, strain_4, strain_6, strain_5) # Check if we found an equivalent plastic strain section if line[5:7] == "PE": mode_peeq_found = True if mode_peeq_found and (line[1:3] == "-1"): # we found an equivalent plastic strain line elem = int(line[4:13]) peeq = float(line[13:25]) mode_peeq[elem] = (peeq) # Check if we found a temperature section if line[5:11] == "NDTEMP": mode_temp_found = True if mode_temp_found and (line[1:3] == "-1"): # we found a temperature line elem = int(line[4:13]) temperature = float(line[13:25]) mode_temp[elem] = (temperature) # Check if we found a mass flow section if line[5:11] == "MAFLOW": mode_massflow_found = True if mode_massflow_found and (line[1:3] == "-1"): # we found a mass flow line elem = int(line[4:13]) massflow = float(line[13:25]) mode_massflow[elem] = (massflow * 1000 ) # convert units to kg/s from t/s if inout_nodes: for i in range(len(inout_nodes)): if elem == int(inout_nodes[i][1]): node = int(inout_nodes[i][2]) # convert units to kg/s from t/s mode_massflow[node] = (massflow * 1000) # Check if we found a network pressure section if line[5:11] == "STPRES": mode_networkpressure_found = True if mode_networkpressure_found and (line[1:3] == "-1"): # we found a network pressure line elem = int(line[4:13]) networkpressure = float(line[13:25]) mode_networkpressure[elem] = (networkpressure) if inout_nodes: for i in range(len(inout_nodes)): if elem == int(inout_nodes[i][1]): node = int(inout_nodes[i][2]) mode_networkpressure[node] = (networkpressure) # Check if we found the end of a section if line[1:3] == "-3": end_of_section_found = True if nodes_found: nodes_found = False node_element_section = True if elements_found: elements_found = False node_element_section = True if mode_disp_found: mode_results["disp"] = mode_disp mode_disp = {} mode_disp_found = False node_element_section = False if mode_stress_found: mode_results["stress"] = mode_stress mode_stress = {} mode_stress_found = False node_element_section = False if mode_strain_found: mode_results["strain"] = mode_strain mode_strain = {} mode_strain_found = False node_element_section = False if mode_peeq_found: mode_results["peeq"] = mode_peeq mode_peeq = {} mode_peeq_found = False node_element_section = False if mode_temp_found: mode_results["temp"] = mode_temp mode_temp = {} mode_temp_found = False node_element_section = False if mode_massflow_found: mode_results["mflow"] = mode_massflow mode_massflow = {} mode_massflow_found = False node_element_section = False if mode_networkpressure_found: mode_results["npressure"] = mode_networkpressure mode_networkpressure_found = False mode_networkpressure = {} node_element_section = False """ print("---- End of Section --> Mode_Results may be changed ----") for key in sorted(mode_results.keys()): if key is "number" or key is "time": print(key + " --> " + str(mode_results[key])) else: print(key + " --> " + str(len(mode_results[key]))) print("----Mode_Results----\n") """ # Check if we found the end of frd data if line[1:5] == "9999": end_of_frd_data_found = True if (mode_eigen_changed or mode_time_changed or end_of_frd_data_found) \ and end_of_section_found \ and not node_element_section: """ print("\n\n----Append mode_results to results") print(line) for key in sorted(mode_results.keys()): if key is "number" or key is "time": print(key + " --> " + str(mode_results[key])) else: print(key + " --> " + str(len(mode_results[key]))) print("----Append Mode_Results----\n") """ # append mode_results to results and reset mode_result results.append(mode_results) mode_results = {} # https://forum.freecadweb.org/viewtopic.php?f=18&t=32649&start=10#p274686 mode_results["number"] = float("NaN") mode_results["time"] = float("NaN") end_of_section_found = False # on changed --> write changed values in mode_result # will be the first to do on an empty mode_result if mode_eigen_changed: mode_results["number"] = eigenmode mode_eigen_changed = False if mode_time_changed: mode_results["time"] = timestep # mode_results["time"] = 0 # Don't return time if static # Why? mode_time_found = False mode_time_changed = False # here we are in the indent of loop for every line in frd file # do not add a print here :-) # close frd file if loop over all lines is finished frd_file.close() """ # debug prints and checks with the read data print("\n\n----RESULTS values begin----") print(len(results)) # print("\n") # print(results) print("----RESULTS values end----\n\n") """ if not inout_nodes: if results: if "mflow" in results[0] or "npressure" in results[0]: Console.PrintError( "We have mflow or npressure, but no inout_nodes file.\n") if not nodes: Console.PrintError("FEM: No nodes found in Frd file.\n") 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, "Results": results }
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 __init__(self, gmsh_mesh_obj, analysis=None): # mesh obj self.mesh_obj = gmsh_mesh_obj # analysis if analysis: self.analysis = analysis else: self.analysis = None # part to mesh self.part_obj = self.mesh_obj.Part # clmax, CharacteristicLengthMax: float, 0.0 = 1e+22 self.clmax = Units.Quantity( self.mesh_obj.CharacteristicLengthMax).Value if self.clmax == 0.0: self.clmax = 1e+22 # clmin, CharacteristicLengthMin: float self.clmin = Units.Quantity( self.mesh_obj.CharacteristicLengthMin).Value # geotol, GeometryTolerance: float, 0.0 = 1e-08 self.geotol = self.mesh_obj.GeometryTolerance if self.geotol == 0.0: self.geotol = 1e-08 # order # known_element_orders = ["1st", "2nd"] self.order = self.mesh_obj.ElementOrder if self.order == "1st": self.order = "1" elif self.order == "2nd": self.order = "2" else: Console.PrintError("Error in order\n") # dimension self.dimension = self.mesh_obj.ElementDimension # Algorithm2D algo2D = self.mesh_obj.Algorithm2D if algo2D == "Automatic": self.algorithm2D = "2" elif algo2D == "MeshAdapt": self.algorithm2D = "1" elif algo2D == "Delaunay": self.algorithm2D = "5" elif algo2D == "Frontal": self.algorithm2D = "6" elif algo2D == "BAMG": self.algorithm2D = "7" elif algo2D == "DelQuad": self.algorithm2D = "8" elif algo2D == "Packing Parallelograms": self.algorithm2D = "9" else: self.algorithm2D = "2" # Algorithm3D algo3D = self.mesh_obj.Algorithm3D if algo3D == "Automatic": self.algorithm3D = "1" elif algo3D == "Delaunay": self.algorithm3D = "1" elif algo3D == "New Delaunay": self.algorithm3D = "2" elif algo3D == "Frontal": self.algorithm3D = "4" elif algo3D == "MMG3D": self.algorithm3D = "7" elif algo3D == "R-tree": self.algorithm3D = "9" elif algo3D == "HXT": self.algorithm3D = "10" else: self.algorithm3D = "1" # RecombinationAlgorithm algoRecombo = self.mesh_obj.RecombinationAlgorithm if algoRecombo == "Simple": self.RecombinationAlgorithm = "0" elif algoRecombo == "Blossom": self.RecombinationAlgorithm = "1" elif algoRecombo == "Simple full-quad": self.RecombinationAlgorithm = "2" elif algoRecombo == "Blossom full-quad": self.RecombinationAlgorithm = "3" else: self.algoRecombo = "0" # HighOrderOptimize optimizers = self.mesh_obj.HighOrderOptimize if optimizers == "None": self.HighOrderOptimize = "0" elif optimizers == "Optimization": self.HighOrderOptimize = "1" elif optimizers == "Elastic+Optimization": self.HighOrderOptimize = "2" elif optimizers == "Elastic": self.HighOrderOptimize = "3" elif optimizers == "Fast Curving": self.HighOrderOptimize = "4" else: self.HighOrderOptimize = "0" # mesh groups if self.mesh_obj.GroupsOfNodes is True: self.group_nodes_export = True else: self.group_nodes_export = False self.group_elements = {} # mesh regions self.ele_length_map = {} # { "ElementString" : element length } self.ele_node_map = {} # { "ElementString" : [element nodes] } # mesh boundary layer self.bl_setting_list = [ ] # list of dict, each item map to MeshBoundaryLayer object self.bl_boundary_list = [ ] # to remove duplicated boundary edge or faces # other initializations self.temp_file_geometry = "" self.temp_file_mesh = "" self.temp_file_geo = "" self.mesh_name = "" self.gmsh_bin = "" self.error = False
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 read_Frame3DD_case(Frame3DD_input): Console.PrintMessage( "Read Frame3DD results from Frame3DD file: {}\n".format( Frame3DD_input)) inout_nodes = [] inout_nodes_file = Frame3DD_input.rsplit(".", 1)[0] + "_inout_nodes.txt" if os.path.exists(inout_nodes_file): Console.PrintMessage( "Read special 1DFlow nodes data form: {}\n".format( inout_nodes_file)) f = pyopen(inout_nodes_file, "r") lines = f.readlines() for line in lines: a = line.split(",") inout_nodes.append(a) f.close() Console.PrintMessage("{}\n".format(inout_nodes)) Frame3DD_file = pyopen(Frame3DD_input, "r") 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 = {} results = [] mode_results = {} mode_results["number"] = float("NaN") mode_results["time"] = float("NaN") mode_disp = {} mode_stress = {} mode_strain = {} mode_peeq = {} mode_temp = {} mode_massflow = {} mode_networkpressure = {} nodes_found = False elements_found = False mode_time_found = False mode_disp_found = False mode_stress_found = False mode_strain_found = False mode_peeq_found = False mode_temp_found = False mode_massflow_found = False mode_networkpressure_found = False end_of_section_found = False end_of_Frame3DD_data_found = False input_continues = False mode_eigen_changed = False mode_time_changed = False eigenmode = 0 eigentemp = 0 elem = -1 elemType = 0 timestep = 0 timetemp = 0 tline = [] for line in Frame3DD_file: tline.append(line.strip()) i = 1 #Console.PrintError(tline[i]+'\n') #node 1111111111111111111111111111111111111111111111111111111 while 1: i = i + 1 #print (tline[i]) if len(tline[i]) == 0 or tline[i][0] == '#': continue else: break print("") data = tline[i].split() numNode = int(data[0]) print("numNode: " + str(numNode)) while 1: i = i + 1 #print (tline[i]) if len(tline[i]) == 0 or tline[i][0] == '#': continue else: break 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) for id in range(1, numNode): # node #1 0.000000 0.000000 0.000000 0.000 1 1 1 1 1 0 while 1: i = i + 1 #print (tline[i]) if len(tline[i]) == 0 or tline[i][0] == '#': continue else: break 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) #number of nodes with reactions 22222222222222222222222222222222222222222 while 1: i = i + 1 #print (tline[i]) if len(tline[i]) == 0 or tline[i][0] == '#': continue else: break print("") data = tline[i].split() numReaction = int(data[0]) print("numReaction: " + str(numReaction)) if (numReaction > 0): while 1: i = i + 1 #print (tline[i]) if len(tline[i]) == 0 or tline[i][0] == '#': continue else: break print(tline[i]) dataReaction = tline[i].split() #elem = int(dataReaction[0]) #nodes_x = float(dataReaction[1]) #nodes_y = float(dataReaction[2]) #nodes_z = float(dataReaction[3]) #nodes[elem] = FreeCAD.Vector(nodes_x, nodes_y, nodes_z) for id in range(1, numReaction): # node #1 0.000000 0.000000 0.000000 0.000 1 1 1 1 1 0 #i=i+1 while 1: i = i + 1 #print (tline[i]) if len(tline[i]) == 0 or tline[i][0] == '#': continue else: break 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) #Member 333333333333333333333333333333333333333333333333333333 while 1: i = i + 1 #print (tline[i]) if len(tline[i]) == 0 or tline[i][0] == '#': continue else: break print("") data = tline[i].split() numMember = int(data[0]) print("numMember: " + str(numMember)) while 1: i = i + 1 #print (tline[i]) if len(tline[i]) == 0 or tline[i][0] == '#': continue else: break print(tline[i]) dataNode = tline[i].split() elem = int(dataNode[0]) nd1 = int(dataNode[1]) nd2 = int(dataNode[2]) elements_seg2[elem] = (nd1, nd2) for id in range(1, numMember): # Member #i=i+1 while 1: i = i + 1 #print (tline[i]) if len(tline[i]) == 0 or tline[i][0] == '#': continue else: break print(tline[i]) dataNode = tline[i].split() elem = int(dataNode[0]) nd1 = int(dataNode[1]) nd2 = int(dataNode[2]) elements_seg2[elem] = (nd1, nd2) # here we are in the indent of loop for every line in Frame3DD file # do not add a print here :-) # close Frame3DD file if loop over all lines is finished Frame3DD_file.close() """ # debug prints and checks with the read data print("\n\n----RESULTS values begin----") print(len(results)) # print("\n") # print(results) print("----RESULTS values end----\n\n") """ if not inout_nodes: if results: if "mflow" in results[0] or "npressure" in results[0]: Console.PrintError( "We have mflow or npressure, but no inout_nodes file.\n") if not nodes: Console.PrintError("FEM: No nodes found in Frame3DD file.\n") 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, "Results": results }
def read_inp(file_name): """read .inp file """ # ATM only mesh reading is supported (no boundary conditions) class elements(): tria3 = {} tria6 = {} quad4 = {} quad8 = {} tetra4 = {} tetra10 = {} hexa8 = {} hexa20 = {} penta6 = {} penta15 = {} seg2 = {} seg3 = {} error_seg3 = False # to print "not supported" nodes = {} model_definition = True f = pyopen(file_name, "r") line = "\n" include = "" f_include = None while line != "": if include: line = f_include.readline() if line == "": f_include.close() include = "" line = f.readline() else: line = f.readline() if line.strip() == "": continue elif line[0] == "*": # start/end of a reading set if line[0:2] == "**": # comments continue if line[:8].upper() == "*INCLUDE": start = 1 + line.index("=") include = line[start:].strip().strip('"') include_path = os.path.normpath(include) if os.path.isfile(include_path) is True: f_include = pyopen(include_path, "r") else: path_start = os.path.split(file_name)[0] include_full_path = os.path.join(path_start, include_path) f_include = pyopen(include_full_path, "r") continue read_node = False elm_category = [] elm_2nd_line = False # reading nodes if (line[:5].upper() == "*NODE") and (model_definition is True): read_node = True elif read_node is True: line_list = line.split(",") number = int(line_list[0]) x = float(line_list[1]) y = float(line_list[2]) z = float(line_list[3]) nodes[number] = [x, y, z] # reading elements elif line[:8].upper() == "*ELEMENT": line_list = line[8:].upper().split(",") for line_part in line_list: if line_part.lstrip()[:4] == "TYPE": elm_type = line_part.split("=")[1].strip() if elm_type in ["S3", "CPS3", "CPE3", "CAX3"]: elm_category = elements.tria3 number_of_nodes = 3 elif elm_type in ["S6", "CPS6", "CPE6", "CAX6"]: elm_category = elements.tria6 number_of_nodes = 6 elif elm_type in [ "S4", "S4R", "CPS4", "CPS4R", "CPE4", "CPE4R", "CAX4", "CAX4R" ]: elm_category = elements.quad4 number_of_nodes = 4 elif elm_type in [ "S8", "S8R", "CPS8", "CPS8R", "CPE8", "CPE8R", "CAX8", "CAX8R" ]: elm_category = elements.quad8 number_of_nodes = 8 elif elm_type == "C3D4": elm_category = elements.tetra4 number_of_nodes = 4 elif elm_type == "C3D10": elm_category = elements.tetra10 number_of_nodes = 10 elif elm_type in ["C3D8", "C3D8R", "C3D8I"]: elm_category = elements.hexa8 number_of_nodes = 8 elif elm_type in ["C3D20", "C3D20R", "C3D20RI"]: elm_category = elements.hexa20 number_of_nodes = 20 elif elm_type == "C3D6": elm_category = elements.penta6 number_of_nodes = 6 elif elm_type == "C3D15": elm_category = elements.penta15 number_of_nodes = 15 elif elm_type in ["B31", "B31R", "T3D2"]: elm_category = elements.seg2 number_of_nodes = 2 elif elm_type in ["B32", "B32R", "T3D3"]: elm_category = elements.seg3 number_of_nodes = 3 error_seg3 = True # to print "not supported" elif elm_category != []: line_list = line.split(",") if elm_2nd_line is False: number = int(line_list[0]) elm_category[number] = [] pos = 1 else: pos = 0 elm_2nd_line = False for en in range(pos, pos + number_of_nodes - len(elm_category[number])): try: enode = int(line_list[en]) elm_category[number].append(enode) except: elm_2nd_line = True break elif line[:5].upper() == "*STEP": model_definition = False if error_seg3 is True: # to print "not supported" Console.PrintError( "Error: seg3 (3-node beam element type) not supported, yet.\n") f.close() # switch from the CalculiX node numbering to the FreeCAD node numbering # numbering do not change: tria3, tria6, quad4, quad8, seg2 for en in elements.tetra4: n = elements.tetra4[en] elements.tetra4[en] = [n[1], n[0], n[2], n[3]] for en in elements.tetra10: n = elements.tetra10[en] elements.tetra10[en] = [ n[1], n[0], n[2], n[3], n[4], n[6], n[5], n[8], n[7], n[9] ] for en in elements.hexa8: n = elements.hexa8[en] elements.hexa8[en] = [n[5], n[6], n[7], n[4], n[1], n[2], n[3], n[0]] for en in elements.hexa20: n = elements.hexa20[en] elements.hexa20[en] = [ n[5], n[6], n[7], n[4], n[1], n[2], n[3], n[0], n[13], n[14], n[15], n[12], n[9], n[10], n[11], n[8], n[17], n[18], n[19], n[16] ] for en in elements.penta6: n = elements.penta6[en] elements.penta6[en] = [n[4], n[5], n[3], n[1], n[2], n[0]] for en in elements.penta15: n = elements.penta15[en] elements.penta15[en] = [ n[4], n[5], n[3], n[1], n[2], n[0], n[10], n[11], n[9], n[7], n[8], n[6], n[13], n[14], n[12] ] for en in elements.seg3: n = elements.seg3[en] elements.seg3[en] = [n[0], n[2], n[1]] 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 process(filename): """Process the filename and create a Draft Wire from the data. The common airfoil dat format has many flavors. This code should work with almost every dialect. Parameters ---------- filename : str The path to the filename to be opened. Returns ------- Part::Part2DObject or None. The created Draft Wire object or None if the file contains less than 3 points. """ # Regex to identify data rows and throw away unused metadata xval = r'(?P<xval>\-?\s*\d*\.*\d*([Ee]\-?\d+)?)' yval = r'(?P<yval>\-?\s*\d*\.*\d*([Ee]\-?\d+)?)' _regex = r'^\s*' + xval + r'\,?\s*' + yval + r'\s*$' regex = re.compile(_regex) afile = pythonopen(filename, 'r') # read the airfoil name which is always at the first line airfoilname = afile.readline().strip() coords = [] # upside = True # last_x = None # Collect the data for lin in afile: curdat = regex.match(lin) if (curdat is not None and curdat.group("xval") and curdat.group("yval")): x = float(curdat.group("xval")) y = float(curdat.group("yval")) # Some files specify the number of upper and lower points on the 2nd line: # " 67. 72." # See: http://airfoiltools.com/airfoil # This line must be skipped: if x < 2 and y < 2: # the normal processing coords.append(Vector(x, y, 0)) afile.close() if len(coords) < 3: FCC.PrintError(translate("ImportAirfoilDAT", "Did not find enough coordinates") + "\n") return None # sometimes coords are divided in upper an lower side # so that x-coordinate begin new from leading or trailing edge # check for start coordinates in the middle of list if coords[0:-1].count(coords[0]) > 1: flippoint = coords.index(coords[0], 1) upper = coords[0:flippoint] lower = coords[flippoint+1:] lower.reverse() for i in lower: upper.append(i) coords = upper # do we use the parametric Draft Wire? if useDraftWire: obj = Draft.make_wire(coords, True) # obj.label = airfoilname else: # alternate solution, uses common Part Faces lines = [] first_v = None last_v = None for v in coords: if first_v is None: first_v = v # Line between v and last_v if they're not equal if (last_v is not None) and (last_v != v): lines.append(Part.makeLine(last_v, v)) # The new last_v last_v = v # close the wire if needed if last_v != first_v: lines.append(Part.makeLine(last_v, first_v)) wire = Part.Wire(lines) face = Part.Face(wire) obj = FreeCAD.ActiveDocument.addObject('Part::Feature', airfoilname) obj.Shape = face return obj
def get_parameters(source_arc, as_dict=True): """ Given a minimum of existing parameters, return a fully-described arc """ _result = Arc(source_arc) #Vector order: #Radius in / out, Tangent in / out, Middle, and Chord points = [_result.start, _result.end, _result.center, _result.pi] point_count = len([_v for _v in points if _v]) #define the curve start at the origin if none is provided if point_count == 0: points[0] = Vector() vecs = [ support.safe_sub(_result.start, _result.center, True), support.safe_sub(_result.end, _result.center, True), support.safe_sub(_result.pi, _result.start, True), support.safe_sub(_result.end, _result.pi, True), support.safe_sub(_result.pi, _result.center, True), support.safe_sub(_result.end, _result.start, True) ] mat = get_scalar_matrix(vecs) _p = get_lengths(_result, mat) if not _p: Console.PrintError(""" Invalid curve definition: cannot determine radius / tangent lengths. Arc: """ + str(_result)) _result.radius = 0.0 return _result _result.update(_p) _p = get_delta(_result, mat) if not _p: Console.PrintError( 'Invalid curve definition: cannot determine central angle.' + '\nArc:\n' + str(_result)) return None _result.update(_p) _p = get_rotation(_result, vecs) if not _p: Console.PrintError( 'Invalid curve definition: cannot determine curve direction.' + '\nArc:\n' + str(_result)) return None _result.update(_p) _p = get_bearings(_result, mat, _result.get('Delta'), _result.get('Direction')) if not _p: Console.PrintError( 'Invalid curve definition: cannot determine curve bearings.' + '\nArc:\n' + str(_result)) return None _result.update(_p) _p = get_missing_parameters(_result, _result, points) if not _p: Console.PrintError( 'Invalid curve definition: cannot calculate all parameters.' + '\nArc:\n' + str(_result)) return None _result.update(_p) _p = get_coordinates(_result, points) if not _p: Console.PrintError( 'Invalid curve definition: cannot calculate coordinates' + '\nArc:\n' + str(_result)) return None _result.update(_p) #get rid of the Bearings dict since we're done using it #_result.pop('Bearings') #merge the _result with the original dict to preserve other values if as_dict: return _result.to_dict() return _result