def Activated(self, name=translate("draft", "Fillet")): DraftTools.Creator.Activated(self, name) if not self.doc: FCC.PrintWarning(translate("draft", "No active document") + "\n") return if self.ui: self.rad = 100 self.chamfer = False self.delete = False label = translate("draft", "Fillet radius") tooltip = translate("draft", "Radius of fillet") # Call the Task panel for a radius # The graphical widgets are defined in DraftGui self.ui.taskUi(title=name, icon="Draft_Fillet") self.ui.radiusUi() self.ui.sourceCmd = self self.ui.labelRadius.setText(label) self.ui.radiusValue.setToolTip(tooltip) self.ui.setRadiusValue(self.rad, "Length") self.ui.check_delete = self.ui._checkbox("isdelete", self.ui.layout, checked=self.delete) self.ui.check_delete.setText( translate("draft", "Delete original objects")) self.ui.check_delete.show() self.ui.check_chamfer = self.ui._checkbox("ischamfer", self.ui.layout, checked=self.chamfer) self.ui.check_chamfer.setText(translate("draft", "Create chamfer")) self.ui.check_chamfer.show() QtCore.QObject.connect(self.ui.check_delete, QtCore.SIGNAL("stateChanged(int)"), self.set_delete) QtCore.QObject.connect(self.ui.check_chamfer, QtCore.SIGNAL("stateChanged(int)"), self.set_chamfer) self.linetrack = trackers.lineTracker(dotted=True) self.arctrack = trackers.arcTracker() # self.call = self.view.addEventCallback("SoEvent", self.action) FCC.PrintMessage(translate("draft", "Enter radius") + "\n")
def _handleSimulation(self): self._simulation("Coordinate System", "Cartesian 3D") self._simulation("Coordinate Mapping", (1, 2, 3)) if self.unit_schema == Units.Scheme.SI2: self._simulation("Coordinate Scaling", 0.001) Console.PrintMessage( "'Coordinate Scaling = Real 0.001' was inserted into the solver input file.\n" ) self._simulation("Simulation Type", "Steady state") self._simulation("Steady State Max Iterations", 1) self._simulation("Output Intervals", 1) self._simulation("Timestepping Method", "BDF") self._simulation("BDF Order", 1) self._simulation("Use Mesh Names", True) self._simulation( "Steady State Max Iterations", self.solver.SteadyStateMaxIterations) self._simulation( "Steady State Min Iterations", self.solver.SteadyStateMinIterations)
def _writeMesh(self): mesh = self._getSingleMember("Fem::FemMeshObject") unvPath = os.path.join(self.directory, "mesh.unv") groups = [] groups.extend(self._builder.getBodyNames()) groups.extend(self._builder.getBoundaryNames()) self._exportToUnv(groups, mesh, unvPath) if self.testmode: Console.PrintMessage( "Solver Elmer testmode, ElmerGrid will not be used. " "It might not be installed.\n") else: binary = settings.get_binary("ElmerGrid") if binary is None: raise WriteError("Could not find ElmerGrid binary.") args = [ binary, _ELMERGRID_IFORMAT, _ELMERGRID_OFORMAT, unvPath, "-out", self.directory ] subprocess.call(args, stdout=subprocess.DEVNULL)
def __init__(self): Console.PrintMessage("BodyRenderer\n") self.width = None self.depth = None self.height = None self.hole_style = None self.holes_offset = None self.doc = None self.brick = None self.top_datum_plane = None self.top_inside_datum_plane = None self.front_inside_datum_plane = None self.back_inside_datum_plane = None self.left_inside_datum_plane = None self.right_inside_datum_plane = None self.xy_plane = None
def _render_tubes_or_sticks(self, body_pad_sketch): Console.PrintMessage("_render_tubes_or_sticks()\n") tubes = self.depth > 1 and self.width > 1 tube_ribs = tubes and self.height > 1 and (self.depth > 2 or self.width > 2) sticks = not tubes and (self.depth > 1 or self.width > 1) stick_ribs = sticks and self.height > 1 and not self.hole_style == HoleStyle.HOLE if tube_ribs: self._render_tube_ribs() if tubes: self._render_tubes(body_pad_sketch) if stick_ribs: self._render_stick_ribs() if sticks: self._render_sticks(body_pad_sketch)
def findStartPoint(op, feat_num, cut_region_tp, cut_region_tp_polytree, helixRadius, toolRadius, scale_factor): #searching for biggest area to cut, by decremental offseting from target cut_region #start offset is max x or y size of the stock/2 maxLen = max(op.stock.Shape.BoundBox.XLength, op.stock.Shape.BoundBox.YLength) starting_offset = maxLen * scale_factor / 2 #try with some reasonable step step = toolRadius / 4 while starting_offset >= helixRadius: of.Clear() of.AddPaths(cut_region_tp, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) offsetPaths = of.Execute(-(starting_offset)) #showPath(op,offsetPaths,scale_factor) for path in offsetPaths: #find center pt = GeomUtils.centroid(path) #showTool("STP", pt, scale_factor, (1,0,1)) if not isOutsideCutRegion(pt, cut_region_tp_polytree): of.Clear() of.AddPath([pt, [pt[0] + 1, pt[1]]], pyclipper.JT_ROUND, pyclipper.ET_OPENROUND) cleared_helix = of.Execute(helixRadius + toolRadius) Console.PrintMessage( "Start point: %f,%f\n" % (1.0 * pt[0] / scale_factor, 1.0 * pt[1] / scale_factor)) return cleared_helix, pt # sceneClearPaths("STP") # sceneDrawPath("STP", path, scale_factor, (1, 0, 1)) # messageBox("Continue") starting_offset = starting_offset - step Console.PrintError("Unable to find starting point (for path no:%d)!\n" % feat_num) #sceneClearPaths("STP") return None, None
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 onOK(self): if self.mirroredPartName != '': # the mirrored object's name symPartName = self.mirroredPartName.text() symObjName = self.selObj.Label + '_mirrored' linkObjName = self.selObj.Label + '_link' # create Part symPart = App.ActiveDocument.addObject('App::Part', symPartName) # add an LCS at the root of the Part, and attach it to the 'Origin' lcs = symPart.newObject('PartDesign::CoordinateSystem', 'LCS_' + symPart.Name) lcs.Support = [(symPart.Origin.OriginFeatures[0], '')] lcs.MapMode = 'ObjectXY' lcs.MapReversed = False # if there is a Parts group, put the symmetrical part there partsGroup = Asm4.getPartsGroup() if partsGroup: partsGroup.addObject(symPart) # create a link to the original link = symPart.newObject('App::Link', linkObjName) link.LinkedObject = self.selObj link.Label = linkObjName link.Visibility = False # create the mirrored object symObj = symPart.newObject('Part::Mirroring', symObjName) symObj.Source = link # set the symmetry plane if self.symPlane.currentText() == 'X-Y': symObj.Normal = App.Vector(0, 0, 1) elif self.symPlane.currentText() == 'X-Z': symObj.Normal = App.Vector(0, 1, 0) elif self.symPlane.currentText() == 'Y-Z': symObj.Normal = App.Vector(1, 0, 0) else: FCC.PrintMessage( "ERROR : You shouldn't see this message from mirrorPartCmd()\n" ) # recompute symPart.recompute() App.ActiveDocument.recompute() self.UI.close()
def onDatumClicked( self ): # clear the selection in the GUI window Gui.Selection.clearSelection() # keep the fastener selected Gui.Selection.addSelection( self.activeDoc.Name, 'Model', self.selectedFastener.Name+'.') # LCS in the parent if self.attLCSlist.selectedItems(): #a_LCS = self.attLCSlist.selectedItems()[0].text() a_LCS = self.attLCStable[ self.attLCSlist.currentRow() ].Name # get the part where the selected LCS is #a_Part = self.parentList.currentText() a_Part = self.parentTable[ self.parentList.currentIndex() ] # parent assembly and sister part need a different treatment if a_Part == 'Parent Assembly': linkDot = '' else: linkDot = a_Part+'.' Gui.Selection.addSelection( self.activeDoc.Name, 'Model', linkDot+a_LCS+'.') FCC.PrintMessage("selection: "+ linkDot+a_LCS+'.' +"\n") # show the resulting placement self.onApply()
def _add_vertical_sketch_segment(geometries, constraints, length, ver_vec_start, ver_vec_end, reverse): Console.PrintMessage("_add_vertical_sketch_segment({},{})\n".format( length, reverse)) segment_count = len(geometries) geometries.append(Part.LineSegment(ver_vec_start, ver_vec_end)) constraints.append(Sketcher.Constraint("Vertical", segment_count)) if segment_count > 0: constraints.append( Sketcher.Constraint("Coincident", segment_count - 1, SKETCH_GEOMETRY_VERTEX_END_INDEX, segment_count, SKETCH_GEOMETRY_VERTEX_START_INDEX)) constraints.append( Sketcher.Constraint("DistanceY", segment_count, SKETCH_GEOMETRY_VERTEX_START_INDEX, segment_count, SKETCH_GEOMETRY_VERTEX_END_INDEX, (-1 if reverse else 1) * length))
def get_tmp_file_paths(self, param_working_dir=None, create=False): self.working_dir = "" # try to use given working dir if param_working_dir is not None: self.working_dir = param_working_dir if femutils.check_working_dir(self.working_dir) is not True: if create is True: Console.PrintMessage( "Dir given as parameter \'{}\' doesn't exist, " "but parameter to create it is set to True. " "Dir will be created.\n".format(self.working_dir)) from os import mkdir mkdir(param_working_dir) else: Console.PrintError( "Dir given as parameter \'{}\' doesn't exist " "and create parameter is set to False.\n".format( self.working_dir)) self.working_dir = femutils.get_pref_working_dir( self.mesh_obj) Console.PrintMessage( "Dir \'{}\' will be used instead.\n".format( self.working_dir)) else: self.working_dir = femutils.get_pref_working_dir(self.mesh_obj) # check working_dir exist, if not use a tmp dir and inform the user if femutils.check_working_dir(self.working_dir) is not True: Console.PrintError( "Dir \'{}\' doesn't exist or cannot be created.\n".format( self.working_dir)) self.working_dir = femutils.get_temp_dir(self.mesh_obj) Console.PrintMessage("Dir \'{}\' will be used instead.\n".format( self.working_dir)) # file paths _geometry_name = self.part_obj.Name + "_Geometry" self.mesh_name = self.part_obj.Name + "_Mesh" from os.path import join self.temp_file_geometry = join(self.working_dir, _geometry_name + ".brep") # geometry file self.temp_file_mesh = join(self.working_dir, self.mesh_name + ".unv") # mesh file self.temp_file_geo = join(self.working_dir, "shape2mesh.geo") # Gmsh input file Console.PrintMessage(" " + self.temp_file_geometry + "\n") Console.PrintMessage(" " + self.temp_file_mesh + "\n") Console.PrintMessage(" " + self.temp_file_geo + "\n")
def gettranslation(data): """Retrieve a translation (move) vector from `data`. Parameters ---------- data : list Different types of data. Returns ------- Base::Vector3 A vector with X, Y, or Z displacement, or (0, 0, 0). """ FCC.PrintMessage("found translation %s \n" % data) if data[0] == "Z": return Vector(0, 0, float(data[1])) elif data[0] == "Y": return Vector(0, float(data[1]), 0) elif data[0] == "X": return Vector(float(data[1]), 0, 0) return Vector(0, 0, 0)
def _exportToUnv(self, groups, mesh, meshPath): unvGmshFd, unvGmshPath = tempfile.mkstemp(suffix=".unv") brepFd, brepPath = tempfile.mkstemp(suffix=".brep") geoFd, geoPath = tempfile.mkstemp(suffix=".geo") os.close(brepFd) os.close(geoFd) os.close(unvGmshFd) tools = gmshtools.GmshTools(mesh) tools.group_elements = {g: [g] for g in groups} tools.group_nodes_export = False tools.ele_length_map = {} tools.temp_file_geometry = brepPath tools.temp_file_geo = geoPath tools.temp_file_mesh = unvGmshPath tools.get_dimension() tools.get_region_data() tools.get_boundary_layer_data() tools.write_part_file() tools.write_geo() if self.testmode: Console.PrintMessage( "Solver Elmer testmode, Gmsh will not be used. " "It might not be installed.\n") import shutil shutil.copyfile(geoPath, os.path.join(self.directory, "group_mesh.geo")) else: tools.get_gmsh_command() tools.run_gmsh_with_geo() ioMesh = Fem.FemMesh() ioMesh.read(unvGmshPath) ioMesh.write(meshPath) os.remove(brepPath) os.remove(geoPath) os.remove(unvGmshPath)
def getline(data): """Turns an OCA line definition into a FreeCAD Part.Edge. Parameters ---------- data : list Different types of data. Returns ------- Part.Edge An edge object from the points in `data`. """ FCC.PrintMessage("found line %s \n" % data) verts = [] for p in range(len(data)): if data[p] == "P": verts.append(getpoint(data[p:p + 4])) elif data[p][0] == "P": verts.append(getpoint([data[p]])) L = Part.LineSegment(verts[0], verts[1]) return L.toShape()
def addSelection(self, doc, obj, sub, pnt): # Selection object # Since both 3D view clicks and manual tree selection gets into the same callback # we will determine by clicked coordinates, for manual tree selections the coordinates are (0,0,0) FCC.PrintMessage('Clicked on :' + obj + '@' + sub + '\n') if pnt != (0, 0, 0): # 3D view click # Get linked object name that handles sub-sub-assembly #subObjName = Asm4.getLinkedObjectName(doc, obj, sub) objList = App.getDocument(doc).getObject(obj).getSubObjectList(sub) # Build the name of the selected sub-object for multiple sub-assembly levels subObjName = '' for subObj in objList: if subObj.TypeId == 'App::Link': subObjName = subObjName + subObj.Name + '.' if subObjName != '': # set the selection to the selected object Gui.Selection.clearSelection() Gui.Selection.addSelection(doc, obj, subObjName) # set the selected object drop-down to this object global taskUI link = App.ActiveDocument.getObject(subObjName[0:-1]) #FCC.PrintMessage('LinkedObject = '+link.LinkedObject.Name+'\n') # try to find this link in the parents parent_found = False parent_index = 1 for item in taskUI.parentTable[1:]: if item.Name == link.Name: parent_found = True break else: parent_index = parent_index + 1 if not parent_found: parent_index = 0 taskUI.parentList.setCurrentIndex(parent_index) # select the Parent Assembly else: taskUI.parentList.setCurrentIndex(1)
def convertToDwg(dxffilename, dwgfilename): """Convert a DXF file to a DWG file. If the converter is found it is used, otherwise the conversion fails. Parameters ---------- dxffilename : str The input DXF file dwgfilename : str The output DWG file Returns ------- str The same `dwgfilename` file path. """ import os, subprocess import shutil if shutil.which("dxf2dwg"): proc = subprocess.Popen( ("dxf2dwg", dxffilename, "-y", "-o", dwgfilename)) proc.communicate() return dwgfilename teigha = getTeighaConverter() if teigha: indir = os.path.dirname(dxffilename) outdir = os.path.dirname(dwgfilename) basename = os.path.basename(dxffilename) cmdline = ('"%s" "%s" "%s" "ACAD2000" "DWG" "0" "1" "%s"' % (teigha, indir, outdir, basename)) FCC.PrintMessage( translate("ImportDWG", "Converting:") + " " + cmdline + "\n") subprocess.call(cmdline, shell=True) # os.system(cmdline) return dwgfilename return None
def render(self, context): Console.PrintMessage("render\n") self.doc = context.doc self.brick = context.brick self.width = context.width self.depth = context.depth self.style = context.side_studs_style self.front = context.side_studs_front self.back = context.side_studs_back self.left = context.side_studs_left self.right = context.side_studs_right self.front_datum_plane = context.front_datum_plane self.back_datum_plane = context.back_datum_plane self.left_datum_plane = context.left_datum_plane self.right_datum_plane = context.right_datum_plane if self.front: self._render_side_studs_outside("front", self.front_datum_plane, self.width, True) if self.style == SideStudStyle.HOLE: self._render_side_studs_inside("front", self.front_datum_plane, self.width, False) if self.back: self._render_side_studs_outside("back", self.back_datum_plane, self.width, False) if self.style == SideStudStyle.HOLE: self._render_side_studs_inside("back", self.back_datum_plane, self.width, True) if self.left: self._render_side_studs_outside("left", self.left_datum_plane, self.depth, False) if self.style == SideStudStyle.HOLE: self._render_side_studs_inside("left", self.left_datum_plane, self.depth, True) if self.right: self._render_side_studs_outside("right", self.right_datum_plane, self.depth, True) if self.style == SideStudStyle.HOLE: self._render_side_studs_inside("right", self.right_datum_plane, self.depth, False)
def getpoint(data): """Turn an OCA point definition into a FreeCAD.Vector. Parameters ---------- data : list Different types of data. Returns ------- Base::Vector3 A vector with the data arranged, depending on the contents of `data`. """ FCC.PrintMessage("found point %s \n" % data) if len(data) == 3: return Vector(float(data[0]), float(data[1]), float(data[2])) elif (data[0] == "P") and (len(data) == 4): return Vector(float(data[1]), float(data[2]), float(data[3])) elif (data[0][0] == "P") and (len(data[0]) > 1): if len(data) == 1: return objects[data[0]] else: if data[1][0] == "R": return objects[data[0]].add(objects[data[1]]) elif data[1][0] == "C": # Error: DraftGeomUtils.findProjection() # doesn't exist return DraftGeomUtils.findProjection(objects[data[0]], objects[data[1]]) elif data[0][0] == "C": if objects[data[0]]: p1 = objects[data[0]].Curve.Position if len(data) == 1: return p1 else: if data[1][0] == "L": L = objects[data[1]] return p1.add(DraftGeomUtils.vec(L))
def getarea(data): """Turn an OCA area definition into a FreeCAD Part.Wire. Parameters ---------- data : list Different types of data. Returns ------- Part.Wire A wire object from the points in `data`. """ FCC.PrintMessage("found area %s \n" % data) if data[0] == "S": if data[1] == "POL": pts = data[2:] verts = [] for p in pts: if p[0] == "P": verts.append(getpoint([p])) w = Part.makePolygon(verts) return w
def _extract_edges(objs): """Extract the edges from the given objects (Draft lines or Edges). objs : list of Draft Lines or Part.Edges The list of edges from which to create the fillet. """ o1, o2 = objs if hasattr(o1, "PropertiesList"): if "Proxy" in o1.PropertiesList: if hasattr(o1.Proxy, "Type"): if o1.Proxy.Type in ("Wire", "Fillet"): e1 = o1.Shape.Edges[0] elif "Shape" in o1.PropertiesList: if o1.Shape.ShapeType in ("Wire", "Edge"): e1 = o1.Shape elif hasattr(o1, "ShapeType"): if o1.ShapeType in "Edge": e1 = o1 if hasattr(o1, "Label"): FCC.PrintMessage("o1: " + o1.Label) else: FCC.PrintMessage("o1: 1") FCC.PrintMessage(", length: " + str(e1.Length) + "\n") if hasattr(o2, "PropertiesList"): if "Proxy" in o2.PropertiesList: if hasattr(o2.Proxy, "Type"): if o2.Proxy.Type in ("Wire", "Fillet"): e2 = o2.Shape.Edges[0] elif "Shape" in o2.PropertiesList: if o2.Shape.ShapeType in ("Wire", "Edge"): e2 = o2.Shape elif hasattr(o2, "ShapeType"): if o2.ShapeType in "Edge": e2 = o2 if hasattr(o2, "Label"): FCC.PrintMessage("o2: " + o2.Label) else: FCC.PrintMessage("o2: 2") FCC.PrintMessage(", length: " + str(e2.Length) + "\n") return e1, e2
def add_heated_beds_and_spool_holder_rods_to_cut_list(cut_list_table_rows, num_z_axes, document): frame = retrieve_frame_from_document(document) if frame is None: Console.PrintMessage( 'Frame must be added to document to calculate length of heated bed rods and spool holder rod.\n' ) return cut_list_table_rows frame_size = frame.Size.Value rod_length_equal_to_frame_length = convert_value_to_quantity_and_format( frame_size) one_inch = 25.4 # 2 heated bed rods per pair of Z axes num_heated_bed_rods = (num_z_axes / 2) * 2 log_warning_if_odd_number_of_z_axes(num_z_axes, num_heated_bed_rods) if num_heated_bed_rods > 0: cut_list_table_rows = cut_list_table_rows + [ { 'quantity': num_heated_bed_rods, 'description': 'Heated Bed Rod', 'length': rod_length_equal_to_frame_length } ] return cut_list_table_rows + [{ 'quantity': '1', 'description': 'Spool Holder Rod', 'length': rod_length_equal_to_frame_length }, { 'quantity': '2', 'description': 'Spool Holder Rod', 'length': convert_value_to_quantity_and_format(frame_size - one_inch) }]
def setupObject(self, obj): FCC.PrintMessage('Triggered by setupObject() in VariantLink\n') obj.LinkedObject = obj.SourceObject
def __init__(self): FCC.PrintMessage('Initialising ...\n') self.Object = None
def onLostLinkToObject(self, obj): FCC.PrintMessage('Triggered onLostLinkToObject() in VariantLink\n') obj.LinkedObject = obj.SourceObject return
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 Activated(self): # check that the Fasteners WB has been loaded before: if not 'FSChangeParams' in Gui.listCommands(): Gui.activateWorkbench('FastenersWorkbench') Gui.activateWorkbench('Assembly4Workbench') # if something is selected container = None fsClass = self.FSclass fsType = None selObj = None if len(Gui.Selection.getSelection()) == 1: selObj = Gui.Selection.getSelection()[0] # if it's a container, we'll put it there if selObj.TypeId == 'App::Part': container = selObj # if a fastener is selected, we duplicate it elif isFastener(selObj): try: fs = screwTables[selObj.type][0] if fs in ['Screw', 'Nut', 'Washer']: fsClass = fs fsType = selObj.type container = selObj.getParentGeoFeatureGroup() except: FCC.PrintMessage( "Selected object doesn't seem to be a valid fastener, ignoring\n" ) # create the fastener newFastener = App.ActiveDocument.addObject("Part::FeaturePython", fsClass) # if a previous fastener was selected, we match its parameters if fsType: FS.FSScrewObject(newFastener, fsType, None) newFastener.recompute() newFastener.diameter = selObj.diameter newFastener.recompute() if hasattr(newFastener, 'length'): try: newFastener.length = selObj.length except: FCC.PrintMessage("Length \"" + selObj.length + "\" is not available, ignoring\n") # we create a new fastener as asked else: if fsClass == 'Screw': FS.FSScrewObject(newFastener, 'ISO7045', None) elif fsClass == 'Nut': FS.FSScrewObject(newFastener, 'ISO4032', None) elif fsClass == 'Washer': FS.FSScrewObject(newFastener, 'ISO7089', None) elif fsClass == 'ThreadedRod': FS.FSThreadedRodObject(newFastener, None) # make the Proxy and stuff newFastener.Label = newFastener.Proxy.itemText FS.FSViewProviderTree(newFastener.ViewObject) # if a container was selected, put it there if container: container.addObject(newFastener) # apply custom Asm4 colours: try: newFastener.ViewObject.ShapeColor = self.FScolor[fsClass] except: FCC.PrintMessage("unknown fastener type \"" + str(fsClass) + "\", ignoring\n") # add AttachmentEngine # oooops, no, creates problems because it creates an AttachmentOffset property that collides with Asm4 # newFastener.addExtension("Part::AttachExtensionPython") # ... and select it newFastener.recompute() Gui.Selection.clearSelection() Gui.Selection.addSelection(newFastener)
def __init__(self): # self.base = QtGui.QWidget() # self.form = self.base self.form = QtGui.QWidget() self.form.setWindowIcon(QtGui.QIcon(iconFile)) self.form.setWindowTitle('Attach a Fastener in the assembly') # get the current active document to avoid errors if user changes tab self.activeDoc = App.activeDocument() # the parent (top-level) assembly is the App::Part called Model (hard-coded) self.rootAssembly = Asm4.getAssembly() # has been checked before calling self.selectedFastener = getSelectionFS() # check where the fastener was attached to (self.old_Parent, separator, self.old_parentLCS) = self.selectedFastener.AttachedTo.partition('#') # get and store the Placement's current ExpressionEngine: self.old_EE = Asm4.placementEE(self.selectedFastener.ExpressionEngine) if hasattr(self.selectedFastener, 'AttachmentOffset'): self.old_AO = self.selectedFastener.AttachmentOffset else: self.old_AO = None # Now we can draw the UI self.drawUI() self.initUI() # now self.parentList and self.parentTable are available # find all the linked parts in the assembly for obj in self.activeDoc.findObjects("App::Link"): if self.rootAssembly.getObject(obj.Name) is not None and hasattr( obj.LinkedObject, 'isDerivedFrom'): linkedObj = obj.LinkedObject FCC.PrintMessage("found link to " + linkedObj.Name) if linkedObj.isDerivedFrom( 'App::Part') or linkedObj.isDerivedFrom( 'PartDesign::Body'): # add to the object table holding the objects ... self.parentTable.append(obj) # ... and add to the drop-down combo box with the assembly tree's parts objIcon = linkedObj.ViewObject.Icon objText = Asm4.labelName(obj) self.parentList.addItem(objIcon, objText, obj) # decode the old ExpressionEngine # if the decode is unsuccessful, old_Expression is set to False # and old_attPart and old_attLCS are set to 'None' old_Parent = '' old_parentPart = '' old_parentLCS = '' if self.old_EE and self.old_Parent: (old_Parent, old_parentPart, old_parentLCS) = self.splitExpressionFastener( self.old_EE, self.old_Parent) # find the oldPart in the part list... parent_index = 1 if old_Parent == 'Parent Assembly': parent_found = True else: parent_found = False for item in self.parentTable[1:]: if item.Name == old_Parent: parent_found = True break else: parent_index += 1 if not parent_found: parent_index = 0 self.parentList.setCurrentIndex(parent_index) # this should have triggered self.getPartLCS() to fill the LCS list # find the oldLCS in the list of LCS of the linked part... lcs_found = [] lcs_found = self.attLCSlist.findItems(old_parentLCS, QtCore.Qt.MatchExactly) # may-be it was renamed, see if we can find it as (name) if not lcs_found: lcs_found = self.attLCSlist.findItems('(' + old_parentLCS + ')', QtCore.Qt.MatchContains) if lcs_found: # ... and select it self.attLCSlist.setCurrentItem(lcs_found[0]) Gui.Selection.addObserver(self, 0)
def get_z88_element_type( femmesh, femelement_table=None ): import femmesh.meshtools as FemMeshTools 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 = FemMeshTools.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 FemMeshTools.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 FemMeshTools.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 FemMeshTools.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 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_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": {} }