def moment(ship, draft=None, roll=Units.parseQuantity("0 deg"), trim=Units.parseQuantity("0 deg")): """Compute the moment required to trim the ship 1cm Position arguments: ship -- Ship object (see createShip) Keyword arguments: draft -- Ship draft (Design ship draft by default) roll -- Roll angle (0 degrees by default) trim -- Trim angle (0 degrees by default) Returned value: Moment required to trim the ship 1cm. Such moment is positive if it cause a positive trim angle. The moment is expressed as a mass by a distance, not as a force by a distance """ disp_orig, B_orig, _ = displacement(ship, draft, roll, trim) xcb_orig = Units.Quantity(B_orig.x, Units.Length) factor = 10.0 x = 0.5 * ship.Length.getValueAs('cm').Value y = 1.0 angle = math.atan2(y, x) * Units.Radian trim_new = trim + factor * angle disp_new, B_new, _ = displacement(ship, draft, roll, trim_new) xcb_new = Units.Quantity(B_new.x, Units.Length) mom0 = -disp_orig * xcb_orig mom1 = -disp_new * xcb_new return (mom1 - mom0) / factor
def solve(ship, weights, tanks, rolls, var_trim=True): """Compute the ship GZ stability curve Position arguments: ship -- Ship object weights -- List of weights to consider tanks -- List of tanks to consider (each one should be a tuple with the tank instance, the density of the fluid inside, and the filling level ratio) rolls -- List of roll angles Keyword arguments: var_trim -- True if the equilibrium trim should be computed for each roll angle, False if null trim angle can be used instead. Returned value: List of GZ curve points. Each point contains the GZ stability length, the equilibrium draft, and the equilibrium trim angle (0 deg if var_trim is False) """ # Get the unloaded weight (ignoring the tanks for the moment). W = Units.parseQuantity("0 kg") mom_x = Units.parseQuantity("0 kg*m") mom_y = Units.parseQuantity("0 kg*m") mom_z = Units.parseQuantity("0 kg*m") for w in weights: W += w.Proxy.getMass(w) m = w.Proxy.getMoment(w) mom_x += m[0] mom_y += m[1] mom_z += m[2] COG = Vector(mom_x / W, mom_y / W, mom_z / W) W = W * G # Get the tanks weight TW = Units.parseQuantity("0 kg") VOLS = [] for t in tanks: # t[0] = tank object # t[1] = load density # t[2] = filling level vol = t[0].Proxy.getVolume(t[0], t[2]) VOLS.append(vol) TW += vol * t[1] TW = TW * G points = [] for i,roll in enumerate(rolls): App.Console.PrintMessage("{0} / {1}\n".format(i + 1, len(rolls))) point = solve_point(W, COG, TW, VOLS, ship, tanks, roll, var_trim) if point is None: return [] points.append(point) return points
def floatingArea(ship, draft=None, roll=Units.parseQuantity("0 deg"), trim=Units.parseQuantity("0 deg")): """Compute the ship floating area Position arguments: ship -- Ship object (see createShip) Keyword arguments: draft -- Ship draft (Design ship draft by default) roll -- Roll angle (0 degrees by default) trim -- Trim angle (0 degrees by default) Returned values: area -- Ship floating area cf -- Floating area coefficient """ if draft is None: draft = ship.Draft # We want to intersect the whole ship with the free surface, so in this case # we must not use the underwater side (or the tool will fail) shape, _ = placeShipShape(ship.Shape.copy(), draft, roll, trim) try: f = Part.Face(shape.slice(Vector(0,0,1), 0.0)) area = Units.Quantity(f.Area, Units.Area) except Part.OCCError: msg = QtGui.QApplication.translate( "ship_console", "Part.OCCError: Floating area cannot be computed", None) App.Console.PrintError(msg + '\n') area = Units.Quantity(0.0, Units.Area) bbox = shape.BoundBox Area = (bbox.XMax - bbox.XMin) * (bbox.YMax - bbox.YMin) try: cf = area.Value / Area except ZeroDivisionError: msg = QtGui.QApplication.translate( "ship_console", "ZeroDivisionError: Null area found during the floating area" " computation!", None) App.Console.PrintError(msg + '\n') cf = 0.0 return area, cf
def mainFrameCoeff(ship, draft=None): """Compute the main frame coefficient Position arguments: ship -- Ship object (see createShip) Keyword arguments: draft -- Ship draft (Design ship draft by default) Returned value: Ship main frame area coefficient """ if draft is None: draft = ship.Draft shape, _ = placeShipShape(ship.Shape.copy(), draft, Units.parseQuantity("0 deg"), Units.parseQuantity("0 deg")) shape = getUnderwaterSide(shape) try: f = Part.Face(shape.slice(Vector(1,0,0), 0.0)) area = f.Area except Part.OCCError: msg = QtGui.QApplication.translate( "ship_console", "Part.OCCError: Main frame area cannot be computed", None) App.Console.PrintError(msg + '\n') area = 0.0 bbox = shape.BoundBox Area = (bbox.YMax - bbox.YMin) * (bbox.ZMax - bbox.ZMin) try: cm = area / Area except ZeroDivisionError: msg = QtGui.QApplication.translate( "ship_console", "ZeroDivisionError: Null area found during the main frame area" " coefficient computation!", None) App.Console.PrintError(msg + '\n') cm = 0.0 return cm
def accept(self): """Create the ship instance""" mw = self.getMainWindow() form = mw.findChild(QtGui.QWidget, "TaskPanel") form.ship = self.widget(QtGui.QComboBox, "Ship") form.weight = self.widget(QtGui.QLineEdit, "Weight") ship = self.ships[form.ship.currentIndex()] density = Units.parseQuantity(Locale.fromString(form.weight.text())) Tools.createWeight(self.shapes, ship, density) return True
def wettedArea(shape, draft, roll=Units.parseQuantity("0 deg"), trim=Units.parseQuantity("0 deg")): """Compute the ship wetted area Position arguments: shape -- External faces of the ship hull draft -- Ship draft Keyword arguments: roll -- Roll angle (0 degrees by default) trim -- Trim angle (0 degrees by default) Returned value: The wetted area, i.e. The underwater side area """ shape, _ = placeShipShape(shape.copy(), draft, roll, trim) shape = getUnderwaterSide(shape, force=False) area = 0.0 for f in shape.Faces: area = area + f.Area return Units.Quantity(area, Units.Area)
def BMT(ship, draft=None, trim=Units.parseQuantity("0 deg")): """Calculate "ship Bouyance center" - "transversal metacenter" radius Position arguments: ship -- Ship object (see createShip) Keyword arguments: draft -- Ship draft (Design ship draft by default) trim -- Trim angle (0 degrees by default) Returned value: BMT radius """ if draft is None: draft = ship.Draft roll = Units.parseQuantity("0 deg") _, B0, _ = displacement(ship, draft, roll, trim) nRoll = 2 maxRoll = Units.parseQuantity("7 deg") BM = 0.0 for i in range(nRoll): roll = (maxRoll / nRoll) * (i + 1) _, B1, _ = displacement(ship, draft, roll, trim) # * M # / \ # / \ BM ==|> BM = (BB/2) / sin(alpha/2) # / \ # *-------* # BB BB = B1 - B0 BB.x = 0.0 # nRoll is actually representing the weight function BM += 0.5 * BB.Length / math.sin(math.radians(0.5 * roll)) / nRoll return Units.Quantity(BM, Units.Length)
def solve_point(W, COG, TW, VOLS, ship, tanks, roll, var_trim=True): """ Compute the ship GZ value. @param W Empty ship weight. @param COG Empty ship Center of mass. @param TW Tanks weights. @param VOLS List of tank volumes. @param tanks Considered tanks. @param roll Roll angle. @param var_trim True if the trim angle should be recomputed at each roll angle, False otherwise. @return GZ value, equilibrium draft, and equilibrium trim angle (0 if variable trim has not been requested) """ # Look for the equilibrium draft (and eventually the trim angle too) max_draft = Units.Quantity(ship.Shape.BoundBox.ZMax, Units.Length) draft = ship.Draft max_disp = Units.Quantity(ship.Shape.Volume, Units.Volume) * DENS * G if max_disp < W + TW: msg = QtGui.QApplication.translate( "ship_console", "Too much weight! The ship will never displace water enough", None) App.Console.PrintError(msg + ' ({} vs. {})\n'.format( (max_disp / G).UserString, ((W + TW) / G).UserString)) return None trim = Units.parseQuantity("0 deg") for i in range(MAX_EQUILIBRIUM_ITERS): # Get the displacement, and the bouyance application point disp, B, _ = Hydrostatics.displacement(ship, draft, roll, trim) disp *= G # Add the tanks effect on the center of gravity mom_x = Units.Quantity(COG.x, Units.Length) * W mom_y = Units.Quantity(COG.y, Units.Length) * W mom_z = Units.Quantity(COG.z, Units.Length) * W for i,t in enumerate(tanks): tank_weight = VOLS[i] * t[1] * G tank_cog = t[0].Proxy.getCoG(t[0], VOLS[i], roll, trim) mom_x += Units.Quantity(tank_cog.x, Units.Length) * tank_weight mom_y += Units.Quantity(tank_cog.y, Units.Length) * tank_weight mom_z += Units.Quantity(tank_cog.z, Units.Length) * tank_weight cog_x = mom_x / (W + TW) cog_y = mom_y / (W + TW) cog_z = mom_z / (W + TW) # Compute the errors draft_error = -((disp - W - TW) / max_disp).Value R_x = cog_x - Units.Quantity(B.x, Units.Length) R_y = cog_y - Units.Quantity(B.y, Units.Length) R_z = cog_z - Units.Quantity(B.z, Units.Length) if not var_trim: trim_error = 0.0 else: trim_error = -TRIM_RELAX_FACTOR * R_x / ship.Length # Check if we can tolerate the errors if abs(draft_error) < 0.01 and abs(trim_error) < 0.1: break # Get the new draft and trim draft += draft_error * max_draft trim += trim_error * Units.Degree # GZ should be provided in the Free surface oriented frame of reference c = math.cos(roll.getValueAs('rad')) s = math.sin(roll.getValueAs('rad')) return c * R_y - s * R_z, draft, trim
def areas(ship, n, draft=None, roll=Units.parseQuantity("0 deg"), trim=Units.parseQuantity("0 deg")): """Compute the ship transversal areas Position arguments: ship -- Ship object (see createShip) n -- Number of points to compute Keyword arguments: draft -- Ship draft (Design ship draft by default) roll -- Roll angle (0 degrees by default) trim -- Trim angle (0 degrees by default) Returned value: List of sections, each section contains 2 values, the x longitudinal coordinate, and the transversal area. If n < 2, an empty list will be returned. """ if n < 2: return [] if draft is None: draft = ship.Draft shape, _ = placeShipShape(ship.Shape.copy(), draft, roll, trim) shape = getUnderwaterSide(shape) # Sections distance computation bbox = shape.BoundBox xmin = bbox.XMin xmax = bbox.XMax dx = (xmax - xmin) / (n - 1.0) # Since we are computing the sections in the total length (not in the # length between perpendiculars), we can grant that the starting and # ending sections have null area areas = [(Units.Quantity(xmin, Units.Length), Units.Quantity(0.0, Units.Area))] # And since we just need to compute areas we will create boxes with its # front face at the desired transversal area position, computing the # common solid part, dividing it by faces, and getting only the desired # ones. App.Console.PrintMessage("Computing transversal areas...\n") App.Console.PrintMessage("Some Inventor representation errors can be" " shown, please ignore them.\n") for i in range(1, n - 1): App.Console.PrintMessage("{0} / {1}\n".format(i, n - 2)) x = xmin + i * dx try: f = Part.Face(shape.slice(Vector(1,0,0), x)) except Part.OCCError: msg = QtGui.QApplication.translate( "ship_console", "Part.OCCError: Transversal area computation failed", None) App.Console.PrintError(msg + '\n') areas.append((Units.Quantity(x, Units.Length), Units.Quantity(0.0, Units.Area))) continue # It is a valid face, so we can add this area areas.append((Units.Quantity(x, Units.Length), Units.Quantity(f.Area, Units.Area))) # Last area is equal to zero (due to the total length usage) areas.append((Units.Quantity(xmax, Units.Length), Units.Quantity(0.0, Units.Area))) App.Console.PrintMessage("Done!\n") return areas
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 = meshtools.get_element( sub[0], elems) found_element = meshtools.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" ) 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 gz(lc, rolls, var_trim=True): """Compute the ship GZ stability curve Position arguments: lc -- Load condition spreadsheet rolls -- List of roll angles to compute Keyword arguments: var_trim -- True if the equilibrium trim should be computed for each roll angle, False if null trim angle can be used instead. Returned value: List of GZ curve points. Each point contains the GZ stability length, the equilibrium draft, and the equilibrium trim angle (0 deg if var_trim is False) """ # B1 cell must be a ship # B2 cell must be the loading condition itself doc = lc.Document try: if lc not in doc.getObjectsByLabel(lc.get('B2')): return[] ships = doc.getObjectsByLabel(lc.get('B1')) if len(ships) != 1: if len(ships) == 0: msg = QtGui.QApplication.translate( "ship_console", "Wrong Ship label! (no instances labeled as" "'{}' found)", None) App.Console.PrintError(msg + '\n'.format( lc.get('B1'))) else: msg = QtGui.QApplication.translate( "ship_console", "Ambiguous Ship label! ({} instances labeled as" "'{}' found)", None) App.Console.PrintError(msg + '\n'.format( len(ships), lc.get('B1'))) return[] ship = ships[0] if ship is None or not ship.PropertiesList.index("IsShip"): return[] except ValueError: return[] # Extract the weights and the tanks weights = [] index = 6 while True: try: ws = doc.getObjectsByLabel(lc.get('A{}'.format(index))) except ValueError: break index += 1 if len(ws) != 1: if len(ws) == 0: msg = QtGui.QApplication.translate( "ship_console", "Wrong Weight label! (no instances labeled as" "'{}' found)", None) App.Console.PrintError(msg + '\n'.format( lc.get('A{}'.format(index - 1)))) else: msg = QtGui.QApplication.translate( "ship_console", "Ambiguous Weight label! ({} instances labeled as" "'{}' found)", None) App.Console.PrintError(msg + '\n'.format( len(ws), lc.get('A{}'.format(index - 1)))) continue w = ws[0] try: if w is None or not w.PropertiesList.index("IsWeight"): msg = QtGui.QApplication.translate( "ship_console", "Invalid Weight! (the object labeled as" "'{}' is not a weight)", None) App.Console.PrintError(msg + '\n'.format( len(ws), lc.get('A{}'.format(index - 1)))) continue except ValueError: continue weights.append(w) tanks = [] index = 6 while True: try: ts = doc.getObjectsByLabel(lc.get('C{}'.format(index))) dens = float(lc.get('D{}'.format(index))) level = float(lc.get('E{}'.format(index))) dens = Units.parseQuantity("{} kg/m^3".format(dens)) except ValueError: break index += 1 if len(ts) != 1: if len(ts) == 0: msg = QtGui.QApplication.translate( "ship_console", "Wrong Tank label! (no instances labeled as" "'{}' found)", None) App.Console.PrintError(msg + '\n'.format( lc.get('C{}'.format(index - 1)))) else: msg = QtGui.QApplication.translate( "ship_console", "Ambiguous Tank label! ({} instances labeled as" "'{}' found)", None) App.Console.PrintError(msg + '\n'.format( len(ts), lc.get('C{}'.format(index - 1)))) continue t = ts[0] try: if t is None or not t.PropertiesList.index("IsTank"): msg = QtGui.QApplication.translate( "ship_console", "Invalid Tank! (the object labeled as" "'{}' is not a tank)", None) App.Console.PrintError(msg + '\n'.format( len(ws), lc.get('C{}'.format(index - 1)))) continue except ValueError: continue tanks.append((t, dens, level)) return solve(ship, weights, tanks, rolls, var_trim)
def getFromUi(value, unit, outputDim): quantity = Units.Quantity(str(value) + str(unit)) return convert(quantity, outputDim)
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: # other part obj might not have a Proxy, thus an exception would be raised if part.Shape.ShapeType == "Compound" and hasattr( part, "Proxy"): if part.Proxy.Type == "FeatureBooleanFragments" \ or part.Proxy.Type == "FeatureSlice" \ or part.Proxy.Type == "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 = meshtools.get_element( sub[0], elems) found_element = meshtools.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 = meshtools.get_element(self.part_obj, eleml) ele_vertexes = meshtools.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 check_material_keys(self): # FreeCAD units definition is at file end of src/Base/Unit.cpp if not self.material: FreeCAD.Console.PrintMessage( "For some reason all material data is empty!\n") self.material["Name"] = "NoName" if "Density" in self.material: if "Density" not in str(Units.Unit(self.material["Density"])): FreeCAD.Console.PrintMessage( "Density in material data seems to have no unit " "or a wrong unit (reset the value): {}\n".format( self.material["Name"])) self.material["Density"] = "0 kg/m^3" else: FreeCAD.Console.PrintMessage("Density not found in {}\n".format( self.material["Name"])) self.material["Density"] = "0 kg/m^3" if self.obj.Category == "Solid": # mechanical properties if "YoungsModulus" in self.material: # unit type of YoungsModulus is Pressure if "Pressure" not in str( Units.Unit(self.material["YoungsModulus"])): FreeCAD.Console.PrintMessage( "YoungsModulus in material data seems to have no unit " "or a wrong unit (reset the value): {}\n".format( self.material["Name"])) self.material["YoungsModulus"] = "0 MPa" else: FreeCAD.Console.PrintMessage( "YoungsModulus not found in {}\n".format( self.material["Name"])) self.material["YoungsModulus"] = "0 MPa" if "PoissonRatio" in self.material: # PoissonRatio does not have a unit, but it is checked it there is no value at all try: float(self.material["PoissonRatio"]) except ValueError: FreeCAD.Console.PrintMessage( "PoissonRatio has wrong or no data (reset the value): {}\n" .format(self.material["PoissonRatio"])) self.material["PoissonRatio"] = "0" else: FreeCAD.Console.PrintMessage( "PoissonRatio not found in {}\n".format( self.material["Name"])) self.material["PoissonRatio"] = "0" if self.obj.Category == "Fluid": # Fluidic properties if "KinematicViscosity" in self.material: ki_vis = self.material["KinematicViscosity"] if "KinematicViscosity" not in str(Units.Unit(ki_vis)): FreeCAD.Console.PrintMessage( "KinematicViscosity in material data seems to have no unit " "or a wrong unit (reset the value): {}\n".format( self.material["Name"])) self.material["KinematicViscosity"] = "0 m^2/s" else: FreeCAD.Console.PrintMessage( "KinematicViscosity not found in {}\n".format( self.material["Name"])) self.material["KinematicViscosity"] = "0 m^2/s" if "VolumetricThermalExpansionCoefficient" in self.material: # unit type VolumetricThermalExpansionCoefficient is ThermalExpansionCoefficient vol_ther_ex_co = self.material[ "VolumetricThermalExpansionCoefficient"] if "VolumetricThermalExpansionCoefficient" not in str( Units.Unit(vol_ther_ex_co)): FreeCAD.Console.PrintMessage( "VolumetricThermalExpansionCoefficient in material data " "seems to have no unit or a wrong unit (reset the value): {}\n" .format(self.material["Name"])) self.material[ "VolumetricThermalExpansionCoefficient"] = "0 m^3/m^3/K" else: FreeCAD.Console.PrintMessage( "VolumetricThermalExpansionCoefficient not found in {}\n". format(self.material["Name"])) self.material[ "VolumetricThermalExpansionCoefficient"] = "0 m^3/m^3/K" # Thermal properties if "ThermalConductivity" in self.material: if "ThermalConductivity" not in str( Units.Unit(self.material["ThermalConductivity"])): FreeCAD.Console.PrintMessage( "ThermalConductivity in material data seems to have no unit " "or a wrong unit (reset the value): {}\n".format( self.material["Name"])) self.material["ThermalConductivity"] = "0 W/m/K" else: FreeCAD.Console.PrintMessage( "ThermalConductivity not found in {}\n".format( self.material["Name"])) self.material["ThermalConductivity"] = "0 W/m/K" if "ThermalExpansionCoefficient" in self.material: the_ex_co = self.material["ThermalExpansionCoefficient"] if "ThermalExpansionCoefficient" not in str(Units.Unit(the_ex_co)): FreeCAD.Console.PrintMessage( "ThermalExpansionCoefficient in material data seems to have no unit " "or a wrong unit (reset the value): {}\n".format( self.material["Name"])) self.material["ThermalExpansionCoefficient"] = "0 um/m/K" else: FreeCAD.Console.PrintMessage( "ThermalExpansionCoefficient not found in {}\n".format( self.material["Name"])) self.material["ThermalExpansionCoefficient"] = "0 um/m/K" if "SpecificHeat" in self.material: if "SpecificHeat" not in str( Units.Unit(self.material["SpecificHeat"])): FreeCAD.Console.PrintMessage( "SpecificHeat in material data seems to have no unit " "or a wrong unit (reset the value): {}\n".format( self.material["Name"])) self.material["SpecificHeat"] = "0 J/kg/K" else: FreeCAD.Console.PrintMessage( "SpecificHeat not found in {}\n".format(self.material["Name"])) self.material["SpecificHeat"] = "0 J/kg/K" FreeCAD.Console.PrintMessage("\n")
def processRefinements(self): """ Process mesh refinements """ mr_objs = CfdTools.getMeshRefinementObjs(self.mesh_obj) cf_settings = self.cf_settings cf_settings['MeshRegions'] = {} cf_settings['BoundaryLayers'] = {} cf_settings['InternalRegions'] = {} snappy_settings = self.snappy_settings snappy_settings['MeshRegions'] = {} snappy_settings['BoundaryLayers'] = {} snappy_settings['InternalRegions'] = {} # Make list of all faces in meshed shape with original index mesh_face_list = list( zip(self.mesh_obj.Part.Shape.Faces, range(len(self.mesh_obj.Part.Shape.Faces)))) # Make list of all boundary references CfdTools.cfdMessage("Matching boundary patches\n") boundary_face_list = [] bc_group = None analysis_obj = CfdTools.getParentAnalysisObject(self.mesh_obj) if not analysis_obj: analysis_obj = CfdTools.getActiveAnalysis() if analysis_obj: bc_group = CfdTools.getCfdBoundaryGroup(analysis_obj) for bc_id, bc_obj in enumerate(bc_group): for ri, ref in enumerate(bc_obj.ShapeRefs): try: bf = CfdTools.resolveReference(ref) except RuntimeError as re: raise RuntimeError( "Error processing boundary condition {}: {}".format( bc_obj.Label, str(re))) for si, s in enumerate(bf): boundary_face_list += [(sf, (bc_id, ri, si)) for sf in s[0].Faces] # Match them up to faces in the main geometry bc_matched_faces = CfdTools.matchFaces(boundary_face_list, mesh_face_list) # Check for and filter duplicates bc_match_per_shape_face = [-1] * len(mesh_face_list) for k in range(len(bc_matched_faces)): match = bc_matched_faces[k][1] prev_k = bc_match_per_shape_face[match] if prev_k >= 0: nb, ri, si = bc_matched_faces[k][0] nb2, ri2, si2 = bc_matched_faces[prev_k][0] bc = bc_group[nb] bc2 = bc_group[nb2] CfdTools.cfdWarning( "Boundary '{}' reference {}:{} also assigned as " "boundary '{}' reference {}:{} - ignoring duplicate\n". format(bc.Label, bc.ShapeRefs[ri][0].Name, bc.ShapeRefs[ri][1][si], bc2.Label, bc.ShapeRefs[ri][0].Name, bc.ShapeRefs[ri][1][si])) else: bc_match_per_shape_face[match] = k # Match relevant mesh regions to the shape being meshed: boundary layer mesh regions for cfMesh, # all surface mesh refinements for snappyHexMesh, and extrusion patches for all meshers. # For cfMesh, surface mesh refinements are written as separate surfaces so need not be matched CfdTools.cfdMessage("Matching mesh refinement regions\n") mr_face_list = [] for mr_id, mr_obj in enumerate(mr_objs): if mr_obj.Extrusion or ( self.mesh_obj.MeshUtility == 'cfMesh' and not mr_obj.Internal and mr_obj.NumberLayers > 0) or ( self.mesh_obj.MeshUtility == 'snappyHexMesh' and not mr_obj.Internal): for ri, r in enumerate(mr_obj.ShapeRefs): try: bf = CfdTools.resolveReference(r) except RuntimeError as re: raise RuntimeError( "Error processing mesh refinement {}: {}".format( mr_obj.Label, str(re))) for si, s in enumerate(bf): mr_face_list += [(f, (mr_id, ri, si)) for f in s[0].Faces] # Match them up to the primary geometry mr_matched_faces = CfdTools.matchFaces(mr_face_list, mesh_face_list) # Check for and filter duplicates mr_match_per_shape_face = [-1] * len(mesh_face_list) for k in range(len(mr_matched_faces)): match = mr_matched_faces[k][1] prev_k = mr_match_per_shape_face[match] if prev_k >= 0: nr, ri, si = mr_matched_faces[k][0] nr2, ri2, si2 = mr_matched_faces[prev_k][0] CfdTools.cfdWarning( "Mesh refinement '{}' reference {}:{} also assigned as " "mesh refinement '{}' reference {}:{} - ignoring duplicate\n" .format(mr_objs[nr].Label, mr_objs[nr].ShapeRefs[ri][0].Name, mr_objs[nr].ShapeRefs[ri][1][si], mr_objs[nr2].Label, mr_objs[nr2].ShapeRefs[ri2][0].Name, mr_objs[nr2].ShapeRefs[ri2][1][si2])) else: mr_match_per_shape_face[match] = k self.patch_faces = [] self.patch_names = [] for k in range(len(bc_group) + 1): self.patch_faces.append([]) self.patch_names.append([]) for l in range(len(mr_objs) + 1): self.patch_faces[k].append([]) self.patch_names[k].append("patch_" + str(k) + "_" + str(l)) for i in range(len(mesh_face_list)): k = bc_match_per_shape_face[i] l = mr_match_per_shape_face[i] nb = -1 nr = -1 if k >= 0: nb, bri, bsi = bc_matched_faces[k][0] if l >= 0: nr, rri, ssi = mr_matched_faces[l][0] self.patch_faces[nb + 1][nr + 1].append(i) # For gmsh, match mesh refinement with vertices in original mesh mr_matched_vertices = [] if self.mesh_obj.MeshUtility == 'gmsh': # Make list of all vertices in meshed shape with original index mesh_vertices_list = list( zip(self.mesh_obj.Part.Shape.Vertexes, range(len(self.mesh_obj.Part.Shape.Vertexes)))) CfdTools.cfdMessage("Matching mesh refinements\n") mr_vertices_list = [] for mr_id, mr_obj in enumerate(mr_objs): if not mr_obj.Internal: for ri, r in enumerate(mr_obj.ShapeRefs): try: bf = CfdTools.resolveReference(r) except RuntimeError as re: raise RuntimeError( "Error processing mesh refinement {}: {}". format(mr_obj.Label, str(re))) for si, s in enumerate(bf): mr_vertices_list += [(v, (mr_id, ri, si)) for v in s[0].Vertexes] mr_matched_vertices = CfdTools.matchFaces(mr_vertices_list, mesh_vertices_list) self.ele_length_map = {} self.ele_node_map = {} # For snappyHexMesh, also match surface mesh refinements to the boundary conditions, to identify boundary # conditions on supplementary geometry defined by the surface mesh refinements # Also matches baffles to surface mesh refinements bc_mr_matched_faces = [] if self.mesh_obj.MeshUtility == 'snappyHexMesh': bc_mr_matched_faces = CfdTools.matchFaces(boundary_face_list, mr_face_list) # Handle baffles for bc_id, bc_obj in enumerate(bc_group): if bc_obj.BoundaryType == 'baffle': baffle_matches = [ m for m in bc_mr_matched_faces if m[0][0] == bc_id ] mr_match_per_baffle_ref = [] for r in bc_obj.ShapeRefs: mr_match_per_baffle_ref += [[-1] * len(r[1])] for m in baffle_matches: mr_match_per_baffle_ref[m[0][1]][m[0][2]] = m[1][0] # For each mesh region, the refs that are part of this baffle baffle_patch_refs = [[] for ri in range(len(mr_objs) + 1)] for ri, mr in enumerate(mr_match_per_baffle_ref): for si, mri in enumerate(mr_match_per_baffle_ref[ri]): baffle_patch_refs[mri + 1].append( (bc_obj.ShapeRefs[ri][0], (bc_obj.ShapeRefs[ri][1][si], ))) # Write these geometries for ri, refs in enumerate(baffle_patch_refs): try: shape = CfdTools.makeShapeFromReferences(refs) except RuntimeError as re: raise RuntimeError( "Error processing baffle {}: {}".format( bc_obj.Label, str(re))) solid_name = bc_obj.Name + "_" + str(ri) if shape: CfdTools.cfdMessage( "Triangulating baffle {}, section {}\n".format( bc_obj.Label, ri)) writeSurfaceMeshFromShape(shape, self.triSurfaceDir, solid_name, self.mesh_obj) if ri > 0: # The parts of the baffle corresponding to a surface mesh region obj mr_obj = mr_objs[ri - 1] refinement_level = CfdTools.relLenToRefinementLevel( mr_obj.RelativeLength) edge_level = CfdTools.relLenToRefinementLevel( mr_obj.RegionEdgeRefinement) else: # The parts of the baffle with no refinement obj refinement_level = 0 edge_level = 0 snappy_settings['MeshRegions'][solid_name] = { 'RefinementLevel': refinement_level, 'EdgeRefinementLevel': edge_level, 'MaxRefinementLevel': max(refinement_level, edge_level), 'Baffle': True } for mr_id, mr_obj in enumerate(mr_objs): Internal = mr_obj.Internal mr_rellen = mr_obj.RelativeLength if mr_rellen > 1.0: mr_rellen = 1.0 FreeCAD.Console.PrintError( "The mesh refinement region '{}' should not use a relative length greater " "than unity.\n".format(mr_obj.Name)) elif mr_rellen < 0.001: mr_rellen = 0.001 # Relative length should not be less than 0.1% of base length FreeCAD.Console.PrintError( "The mesh refinement region '{}' should not use a relative length smaller " "than 0.001.\n".format(mr_obj.Name)) if self.mesh_obj.MeshUtility == 'gmsh': # Generate element maps for gmsh if not Internal: mesh_vertex_idx = [ mf[1] for mf in mr_matched_vertices if mf[0][0] == mr_id ] self.ele_length_map[mr_obj.Name] = mr_rellen * self.clmax self.ele_node_map[mr_obj.Name] = mesh_vertex_idx else: # Find any matches with boundary conditions; mark those matching baffles for removal bc_matches = [ m for m in bc_mr_matched_faces if m[1][0] == mr_id ] bc_match_per_mr_ref = [] for ri, r in enumerate(mr_obj.ShapeRefs): bc_match_per_mr_ref.append([-1] * len(r[1])) for m in bc_matches: bc_match_per_mr_ref[m[1][1]][m[1][2]] = -2 if bc_group[ m[0][0]].BoundaryType == 'baffle' else m[0][0] # Unmatch those in primary geometry main_geom_matches = [ m for m in mr_matched_faces if m[0][0] == mr_id ] for m in main_geom_matches: bc_match_per_mr_ref[m[0][1]][m[0][2]] = -1 # For each boundary, the refs that are part of this mesh region mr_patch_refs = [[] for ri in range(len(bc_group) + 1)] for ri, m in enumerate(bc_match_per_mr_ref): for si, bci in enumerate(m): if bci > -2: mr_patch_refs[bci + 1].append( (mr_obj.ShapeRefs[ri][0], (mr_obj.ShapeRefs[ri][1][si], ))) # Loop over and write the sub-sections of this mesh object for bi in range(len(mr_patch_refs)): if len(mr_patch_refs[bi]) and not mr_obj.Extrusion: if bi == 0: mr_patch_name = mr_obj.Name else: mr_patch_name = self.patch_names[bi][mr_id + 1] CfdTools.cfdMessage( "Triangulating mesh refinement region {}, section {}\n" .format(mr_obj.Label, bi)) try: shape = CfdTools.makeShapeFromReferences( mr_patch_refs[bi]) except RuntimeError as re: raise RuntimeError( "Error processing mesh refinement region {}: {}" .format(mr_obj.Label, str(re))) if shape: writeSurfaceMeshFromShape(shape, self.triSurfaceDir, mr_patch_name, self.mesh_obj) refinement_level = CfdTools.relLenToRefinementLevel( mr_obj.RelativeLength) if self.mesh_obj.MeshUtility == 'cfMesh': if not Internal: cf_settings['MeshRegions'][mr_patch_name] = { 'RefinementLevel': refinement_level, 'RefinementThickness': self.scale * Units.Quantity( mr_obj.RefinementThickness).Value, } else: cf_settings['InternalRegions'][mr_obj.Name] = { 'RefinementLevel': refinement_level, 'RelativeLength': mr_rellen * self.clmax * self.scale } elif self.mesh_obj.MeshUtility == 'snappyHexMesh': if not Internal: edge_level = CfdTools.relLenToRefinementLevel( mr_obj.RegionEdgeRefinement) snappy_settings['MeshRegions'][ mr_patch_name] = { 'RefinementLevel': refinement_level, 'EdgeRefinementLevel': edge_level, 'MaxRefinementLevel': max(refinement_level, edge_level), 'Baffle': False } else: snappy_settings['InternalRegions'][ mr_patch_name] = { 'RefinementLevel': refinement_level } # In addition, for cfMesh and SnappyHesMesh, record matched boundary layer patches if (self.mesh_obj.MeshUtility == 'cfMesh' or self.mesh_obj.MeshUtility == 'snappyHexMesh') \ and mr_obj.NumberLayers > 0 and not Internal and not mr_obj.Extrusion: for k in range(len(self.patch_faces)): if len(self.patch_faces[k][mr_id + 1]): # Limit expansion ratio to greater than 1.0 and less than 1.2 expratio = mr_obj.ExpansionRatio expratio = min(1.2, max(1.0, expratio)) if self.mesh_obj.MeshUtility == 'cfMesh': cf_settings['BoundaryLayers'][self.patch_names[k][mr_id + 1]] = \ { 'NumberLayers': mr_obj.NumberLayers, 'ExpansionRatio': expratio, 'FirstLayerHeight': self.scale * Units.Quantity(mr_obj.FirstLayerHeight).Value } elif self.mesh_obj.MeshUtility == 'snappyHexMesh': snappy_settings['BoundaryLayers'][self.patch_names[k][mr_id + 1]] = \ { 'NumberLayers': mr_obj.NumberLayers, 'ExpansionRatio': expratio, # 'FinalLayerHeight': self.scale * Units.Quantity(mr_obj.FinalLayerHeight).Value }
def parse(pathobj): # pylint: disable=global-statement global PRECISION global UNIT_FORMAT global UNIT_SPEED_FORMAT out = "" lastcommand = None precision_string = '.' + str(PRECISION) + 'f' # params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control the order of parameters params = ['X', 'Y', 'Z', 'A', 'B', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L'] if hasattr(pathobj, "Group"): # We have a compound or project. if OUTPUT_COMMENTS: out += linenumber() + "(compound: " + pathobj.Label + ")\n" for p in pathobj.Group: out += parse(p) return out else: # parsing simple path if not hasattr( pathobj, "Path"): # groups might contain non-path things like stock. return out if OUTPUT_COMMENTS: out += linenumber() + "(Path: " + pathobj.Label + ")\n" for c in pathobj.Path.Commands: outstring = [] command = c.Name outstring.append(command) # if modal: only print the command if it is not the same as the last one if MODAL is True: if command == lastcommand: outstring.pop(0) # Now add the remaining parameters in order for param in params: if param in c.Parameters: if param == 'F': speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0: outstring.append(param + format( float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string)) elif param == 'S': outstring.append( param + format(c.Parameters[param], precision_string)) elif param == 'T': outstring.append( param + format(c.Parameters['T'], precision_string)) else: pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) outstring.append( param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)) # store the latest command lastcommand = command # Check for Tool Change: if command == 'M6': if OUTPUT_COMMENTS: out += linenumber() + "(begin toolchange)\n" for line in TOOL_CHANGE.splitlines(True): out += linenumber() + line if command == "message": if OUTPUT_COMMENTS is False: out = [] else: outstring.pop(0) # remove the command # prepend a line number and append a newline if len(outstring) >= 1: if OUTPUT_LINE_NUMBERS: outstring.insert(0, (linenumber())) # append the line to the final output for w in outstring: out += w + COMMAND_SPACE out = out.strip() + "\n" return out
def DVBalloonTest(): path = os.path.dirname(os.path.abspath(__file__)) print('TDBalloon path: ' + path) templateFileSpec = path + '/TestTemplate.svg' FreeCAD.newDocument("TDBalloon") FreeCAD.setActiveDocument("TDBalloon") FreeCAD.ActiveDocument = FreeCAD.getDocument("TDBalloon") #make source feature box = FreeCAD.ActiveDocument.addObject("Part::Box", "Box") sphere = FreeCAD.ActiveDocument.addObject("Part::Sphere", "Sphere") #make a page page = FreeCAD.ActiveDocument.addObject('TechDraw::DrawPage', 'Page') FreeCAD.ActiveDocument.addObject('TechDraw::DrawSVGTemplate', 'Template') FreeCAD.ActiveDocument.Template.Template = templateFileSpec FreeCAD.ActiveDocument.Page.Template = FreeCAD.ActiveDocument.Template page.Scale = 5.0 # page.ViewObject.show() # unit tests run in console mode #make Views view1 = FreeCAD.ActiveDocument.addObject('TechDraw::DrawViewPart', 'View') FreeCAD.ActiveDocument.View.Source = [FreeCAD.ActiveDocument.Box] rc = page.addView(view1) view1.X = Units.Quantity(30.0, Units.Length) view1.Y = Units.Quantity(150.0, Units.Length) view2 = FreeCAD.activeDocument().addObject('TechDraw::DrawViewPart', 'View001') FreeCAD.activeDocument().View001.Source = [FreeCAD.activeDocument().Sphere] rc = page.addView(view2) view2.X = Units.Quantity(220.0, Units.Length) view2.Y = Units.Quantity(150.0, Units.Length) FreeCAD.ActiveDocument.recompute() print("Place balloon") balloon1 = FreeCAD.ActiveDocument.addObject('TechDraw::DrawViewBalloon', 'Balloon1') balloon1.SourceView = view1 balloon1.OriginIsSet = 1 balloon1.OriginX = view1.X + Units.Quantity(20.0, Units.Length) balloon1.OriginY = view1.Y + Units.Quantity(20.0, Units.Length) balloon1.Text = "1" balloon1.Y = balloon1.OriginX + Units.Quantity(20.0, Units.Length) balloon1.X = balloon1.OriginY + Units.Quantity(20.0, Units.Length) print("adding balloon1 to page") rc = page.addView(balloon1) balloon2 = FreeCAD.ActiveDocument.addObject('TechDraw::DrawViewBalloon', 'Balloon2') balloon2.SourceView = view2 balloon2.OriginIsSet = 1 balloon2.OriginX = view2.X + Units.Quantity(20.0, Units.Length) balloon2.OriginY = view2.Y + Units.Quantity(20.0, Units.Length) balloon2.Text = "2" balloon2.Y = balloon2.OriginX + Units.Quantity(20.0, Units.Length) balloon2.X = balloon2.OriginY + Units.Quantity(20.0, Units.Length) print("adding balloon2 to page") rc = page.addView(balloon2) FreeCAD.ActiveDocument.recompute() rc = False if ("Up-to-date" in balloon2.State) and ("Up-to-date" in balloon2.State): rc = True FreeCAD.closeDocument("TDBalloon") return rc
def action(self, arg): """Handle the 3D scene events. This is installed as an EventCallback in the Inventor view. Parameters ---------- arg: dict Dictionary with strings that indicates the type of event received from the 3D view. """ import DraftGeomUtils plane = App.DraftWorkingPlane if arg["Type"] == "SoKeyboardEvent": if arg["Key"] == "ESCAPE": self.finish() elif arg["Type"] == "SoLocation2Event": # mouse movement detection self.point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg) # this is to make sure radius is what you see on screen if self.center and DraftVecUtils.dist(self.point, self.center) > 0: viewdelta = DraftVecUtils.project(self.point.sub(self.center), plane.axis) if not DraftVecUtils.isNull(viewdelta): self.point = self.point.add(viewdelta.negative()) if self.step == 0: # choose center if gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT): if not self.altdown: self.altdown = True self.ui.switchUi(True) else: if self.altdown: self.altdown = False self.ui.switchUi(False) elif self.step == 1: # choose radius if len(self.tangents) == 2: cir = DraftGeomUtils.circleFrom2tan1pt( self.tangents[0], self.tangents[1], self.point) _c = DraftGeomUtils.findClosestCircle(self.point, cir) self.center = _c.Center self.arctrack.setCenter(self.center) elif self.tangents and self.tanpoints: cir = DraftGeomUtils.circleFrom1tan2pt( self.tangents[0], self.tanpoints[0], self.point) _c = DraftGeomUtils.findClosestCircle(self.point, cir) self.center = _c.Center self.arctrack.setCenter(self.center) if gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT): if not self.altdown: self.altdown = True if info: ob = self.doc.getObject(info['Object']) num = int(info['Component'].lstrip('Edge')) - 1 ed = ob.Shape.Edges[num] if len(self.tangents) == 2: cir = DraftGeomUtils.circleFrom3tan( self.tangents[0], self.tangents[1], ed) cl = DraftGeomUtils.findClosestCircle( self.point, cir) self.center = cl.Center self.rad = cl.Radius self.arctrack.setCenter(self.center) else: self.rad = self.center.add( DraftGeomUtils.findDistance( self.center, ed).sub(self.center)).Length else: self.rad = DraftVecUtils.dist(self.point, self.center) else: if self.altdown: self.altdown = False self.rad = DraftVecUtils.dist(self.point, self.center) self.ui.setRadiusValue(self.rad, "Length") self.arctrack.setRadius(self.rad) self.linetrack.p1(self.center) self.linetrack.p2(self.point) self.linetrack.on() elif (self.step == 2): # choose first angle currentrad = DraftVecUtils.dist(self.point, self.center) if currentrad != 0: angle = DraftVecUtils.angle(plane.u, self.point.sub(self.center), plane.axis) else: angle = 0 self.linetrack.p2( DraftVecUtils.scaleTo(self.point.sub(self.center), self.rad).add(self.center)) self.ui.setRadiusValue(math.degrees(angle), unit="Angle") self.firstangle = angle else: # choose second angle currentrad = DraftVecUtils.dist(self.point, self.center) if currentrad != 0: angle = DraftVecUtils.angle(plane.u, self.point.sub(self.center), plane.axis) else: angle = 0 self.linetrack.p2( DraftVecUtils.scaleTo(self.point.sub(self.center), self.rad).add(self.center)) self.updateAngle(angle) self.ui.setRadiusValue(math.degrees(self.angle), unit="Angle") self.arctrack.setApertureAngle(self.angle) gui_tool_utils.redraw3DView() elif arg["Type"] == "SoMouseButtonEvent": # mouse click if arg["State"] == "DOWN" and arg["Button"] == "BUTTON1": if self.point: if self.step == 0: # choose center if not self.support: gui_tool_utils.getSupport(arg) (self.point, ctrlPoint, info) = gui_tool_utils.getPoint(self, arg) if gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT): snapped = self.view.getObjectInfo( (arg["Position"][0], arg["Position"][1])) if snapped: ob = self.doc.getObject(snapped['Object']) num = int( snapped['Component'].lstrip('Edge')) - 1 ed = ob.Shape.Edges[num] self.tangents.append(ed) if len(self.tangents) == 2: self.arctrack.on() self.ui.radiusUi() self.step = 1 self.ui.setNextFocus() self.linetrack.on() _msg(translate("draft", "Pick radius")) else: if len(self.tangents) == 1: self.tanpoints.append(self.point) else: self.center = self.point self.node = [self.point] self.arctrack.setCenter(self.center) self.linetrack.p1(self.center) self.linetrack.p2( self.view.getPoint(arg["Position"][0], arg["Position"][1])) self.arctrack.on() self.ui.radiusUi() self.step = 1 self.ui.setNextFocus() self.linetrack.on() _msg(translate("draft", "Pick radius")) if self.planetrack: self.planetrack.set(self.point) elif self.step == 1: # choose radius if self.closedCircle: self.drawArc() else: self.ui.labelRadius.setText( translate("draft", "Start angle")) self.ui.radiusValue.setToolTip( translate("draft", "Start angle")) self.ui.radiusValue.setText( U.Quantity(0, U.Angle).UserString) self.linetrack.p1(self.center) self.linetrack.on() self.step = 2 _msg(translate("draft", "Pick start angle")) elif self.step == 2: # choose first angle self.ui.labelRadius.setText( translate("draft", "Aperture angle")) self.ui.radiusValue.setToolTip( translate("draft", "Aperture angle")) self.step = 3 # scale center->point vector for proper display # u = DraftVecUtils.scaleTo(self.point.sub(self.center), self.rad) obsolete? self.arctrack.setStartAngle(self.firstangle) _msg(translate("draft", "Pick aperture")) else: # choose second angle self.step = 4 self.drawArc()
def processExtrusions(self): """ Find and process any extrusion objects """ twoD_extrusion_objs = [] other_extrusion_objs = [] mesh_refinements = CfdTools.getMeshRefinementObjs(self.mesh_obj) self.extrusion_settings['ExtrusionsPresent'] = False self.extrusion_settings['ExtrudeTo2D'] = False self.extrusion_settings['Extrude2DPlanar'] = False for mr in mesh_refinements: if mr.Extrusion: self.extrusion_settings['ExtrusionsPresent'] = True if mr.ExtrusionType == '2DPlanar' or mr.ExtrusionType == '2DWedge': twoD_extrusion_objs.append(mr) else: other_extrusion_objs.append(mr) if mr.ExtrusionType == '2DPlanar': self.extrusion_settings['Extrude2DPlanar'] = True if len(twoD_extrusion_objs) > 1: raise RuntimeError( "For 2D meshing, there must be exactly one 2D mesh extrusion object." ) elif len(twoD_extrusion_objs) == 1: self.extrusion_settings['ExtrudeTo2D'] = True all_extrusion_objs = other_extrusion_objs + twoD_extrusion_objs # Ensure 2D extrusion happens last self.extrusion_settings['Extrusions'] = [] for extrusion_obj in all_extrusion_objs: extrusion_shape = extrusion_obj.Shape this_extrusion_settings = {} if len(extrusion_shape.Faces) == 0: raise RuntimeError("Extrusion object '{}' is empty.".format( extrusion_obj.Label)) this_extrusion_settings[ 'KeepExistingMesh'] = extrusion_obj.KeepExistingMesh if extrusion_obj.ExtrusionType == '2DPlanar' or extrusion_obj.ExtrusionType == '2DWedge': this_extrusion_settings['KeepExistingMesh'] = False all_faces_planar = True for faces in extrusion_shape.Faces: if not isinstance(faces.Surface, Part.Plane): all_faces_planar = False break if not all_faces_planar: raise RuntimeError( "2D mesh extrusion surface must be a flat plane.") normal = extrusion_shape.Faces[0].Surface.Axis normal.multiply(1.0 / normal.Length) this_extrusion_settings['Normal'] = (normal.x, normal.y, normal.z) this_extrusion_settings[ 'ExtrusionType'] = extrusion_obj.ExtrusionType # Get the names of the faces being extruded mri = mesh_refinements.index(extrusion_obj) efl = [] for l, ff in enumerate(self.patch_faces): f = ff[mri + 1] if len(f): efl.append(self.patch_names[l][mri + 1]) if not efl: raise RuntimeError( "Extrusion patch for '{}' could not be found in the shape being meshed." .format(extrusion_obj.Label)) this_extrusion_settings['FrontFaceList'] = tuple(efl) this_extrusion_settings['BackFace'] = efl[0] + '_back' this_extrusion_settings[ 'Distance'] = extrusion_obj.ExtrusionThickness.getValueAs('m') this_extrusion_settings[ 'Angle'] = extrusion_obj.ExtrusionAngle.getValueAs('deg') this_extrusion_settings[ 'NumLayers'] = extrusion_obj.ExtrusionLayers this_extrusion_settings[ 'ExpansionRatio'] = extrusion_obj.ExtrusionRatio this_extrusion_settings['AxisPoint'] = \ tuple(Units.Quantity(p, Units.Length).getValueAs('m') for p in extrusion_obj.ExtrusionAxisPoint) axis_direction = extrusion_obj.ExtrusionAxisDirection # Flip axis if necessary to go in same direction as patch normal (otherwise negative volume cells result) if len(extrusion_shape.Faces) > 0: in_plane_vector = extrusion_shape.Faces[ 0].CenterOfMass - extrusion_obj.ExtrusionAxisPoint extrusion_normal = extrusion_obj.ExtrusionAxisDirection.cross( in_plane_vector) face_normal = extrusion_shape.Faces[0].normalAt(0.5, 0.5) if extrusion_normal.dot(face_normal) < 0: axis_direction = -axis_direction this_extrusion_settings['AxisDirection'] = tuple( d for d in axis_direction) self.extrusion_settings['Extrusions'].append( this_extrusion_settings)
def writeMeshCase(self): """ Collect case settings, and finally build a runnable case. """ CfdTools.cfdMessage( "Populating mesh dictionaries in folder {}\n".format( self.meshCaseDir)) # cfMesh settings if self.mesh_obj.MeshUtility == "cfMesh": self.cf_settings['ClMax'] = self.clmax * self.scale if len(self.cf_settings['BoundaryLayers']) > 0: self.cf_settings['BoundaryLayerPresent'] = True else: self.cf_settings['BoundaryLayerPresent'] = False if len(self.cf_settings["InternalRegions"]) > 0: self.cf_settings['InternalRefinementRegionsPresent'] = True else: self.cf_settings['InternalRefinementRegionsPresent'] = False # SnappyHexMesh settings elif self.mesh_obj.MeshUtility == "snappyHexMesh": bound_box = self.part_obj.Shape.BoundBox bC = 5 # Number of background mesh buffer cells x_min = (bound_box.XMin - bC * self.clmax) * self.scale x_max = (bound_box.XMax + bC * self.clmax) * self.scale y_min = (bound_box.YMin - bC * self.clmax) * self.scale y_max = (bound_box.YMax + bC * self.clmax) * self.scale z_min = (bound_box.ZMin - bC * self.clmax) * self.scale z_max = (bound_box.ZMax + bC * self.clmax) * self.scale cells_x = int(math.ceil(bound_box.XLength / self.clmax) + 2 * bC) cells_y = int(math.ceil(bound_box.YLength / self.clmax) + 2 * bC) cells_z = int(math.ceil(bound_box.ZLength / self.clmax) + 2 * bC) snappy_settings = self.snappy_settings snappy_settings['BlockMesh'] = { "xMin": x_min, "xMax": x_max, "yMin": y_min, "yMax": y_max, "zMin": z_min, "zMax": z_max, "cellsX": cells_x, "cellsY": cells_y, "cellsZ": cells_z } if len(self.snappy_settings['BoundaryLayers']) > 0: self.snappy_settings['BoundaryLayerPresent'] = True else: self.snappy_settings['BoundaryLayerPresent'] = False if self.mesh_obj.ImplicitEdgeDetection: snappy_settings['ImplicitEdgeDetection'] = True else: snappy_settings['ImplicitEdgeDetection'] = False inside_x = Units.Quantity( self.mesh_obj.PointInMesh.get('x')).Value * self.scale inside_y = Units.Quantity( self.mesh_obj.PointInMesh.get('y')).Value * self.scale inside_z = Units.Quantity( self.mesh_obj.PointInMesh.get('z')).Value * self.scale shape_patch_names_list = [] for k in range(len(self.patch_faces)): for j in range(len(self.patch_faces[k])): if len(self.patch_faces[k][j]): shape_patch_names_list.append(self.patch_names[k][j]) snappy_settings['ShapePatchNames'] = tuple(shape_patch_names_list) snappy_settings[ 'EdgeRefinementLevel'] = CfdTools.relLenToRefinementLevel( self.mesh_obj.EdgeRefinement) snappy_settings['PointInMesh'] = \ { "x": inside_x, "y": inside_y, "z": inside_z } snappy_settings[ 'CellsBetweenLevels'] = self.mesh_obj.CellsBetweenLevels if len(self.snappy_settings["InternalRegions"]) > 0: self.snappy_settings['InternalRefinementRegionsPresent'] = True else: self.snappy_settings[ 'InternalRefinementRegionsPresent'] = False # GMSH settings elif self.mesh_obj.MeshUtility == "gmsh": exe = CfdTools.getGmshExecutable() self.gmsh_settings['Executable'] = CfdTools.translatePath(exe) self.gmsh_settings['HasLengthMap'] = False if self.ele_length_map: self.gmsh_settings['HasLengthMap'] = True self.gmsh_settings['LengthMap'] = self.ele_length_map self.gmsh_settings['NodeMap'] = {} for e in self.ele_length_map: ele_nodes = (''.join( (str(n + 1) + ', ') for n in self.ele_node_map[e])).rstrip(', ') self.gmsh_settings['NodeMap'][e] = ele_nodes self.gmsh_settings['ClMax'] = self.clmax self.gmsh_settings['ClMin'] = self.clmin sols = (''.join( (str(n + 1) + ', ') for n in range(len(self.mesh_obj.Part.Shape.Solids))) ).rstrip(', ') self.gmsh_settings['Solids'] = sols self.gmsh_settings['BoundaryFaceMap'] = {} for k in range(len(self.patch_faces)): for l in range(len(self.patch_faces[k])): patch_faces = self.patch_faces[k][l] patch_name = self.patch_names[k][l] if len(patch_faces): self.gmsh_settings['BoundaryFaceMap'][ patch_name] = ', '.join( str(fi + 1) for fi in patch_faces) # Perform initialisation here rather than __init__ in case of path changes self.template_path = os.path.join(CfdTools.getModulePath(), "Data", "Templates", "mesh") mesh_region_present = False if self.mesh_obj.MeshUtility == "cfMesh" and len(self.cf_settings['MeshRegions']) > 0 or \ self.mesh_obj.MeshUtility == "snappyHexMesh" and len(self.snappy_settings['MeshRegions']) > 0: mesh_region_present = True self.settings = { 'Name': self.part_obj.Name, 'MeshPath': self.meshCaseDir, 'FoamRuntime': CfdTools.getFoamRuntime(), 'MeshUtility': self.mesh_obj.MeshUtility, 'MeshRegionPresent': mesh_region_present, 'CfSettings': self.cf_settings, 'SnappySettings': self.snappy_settings, 'GmshSettings': self.gmsh_settings, 'ExtrusionSettings': self.extrusion_settings, 'ConvertToDualMesh': self.mesh_obj.ConvertToDualMesh } if CfdTools.getFoamRuntime() != 'WindowsDocker': self.settings['TranslatedFoamPath'] = CfdTools.translatePath( CfdTools.getFoamDir()) if self.mesh_obj.NumberOfProcesses <= 1: self.settings['ParallelMesh'] = False self.settings['NumberOfProcesses'] = 1 else: self.settings['ParallelMesh'] = True self.settings[ 'NumberOfProcesses'] = self.mesh_obj.NumberOfProcesses self.settings['NumberOfThreads'] = self.mesh_obj.NumberOfThreads TemplateBuilder(self.meshCaseDir, self.template_path, self.settings) # Update Allmesh permission - will fail silently on Windows fname = os.path.join(self.meshCaseDir, "Allmesh") import stat s = os.stat(fname) os.chmod(fname, s.st_mode | stat.S_IEXEC) self.analysis.NeedsMeshRewrite = False CfdTools.cfdMessage( "Successfully wrote meshCase to folder {}\n".format( self.meshCaseDir))
def writeMeshCase(self): """ Collect case settings, and finally build a runnable case. """ CfdTools.cfdMessage( "Populating mesh dictionaries in folder {}\n".format( self.meshCaseDir)) if self.mesh_obj.MeshUtility == "cfMesh": self.cf_settings['ClMax'] = self.clmax * self.scale if len(self.cf_settings['BoundaryLayers']) > 0: self.cf_settings['BoundaryLayerPresent'] = True else: self.cf_settings['BoundaryLayerPresent'] = False if len(self.cf_settings["InternalRegions"]) > 0: self.cf_settings['InternalRefinementRegionsPresent'] = True else: self.cf_settings['InternalRefinementRegionsPresent'] = False elif self.mesh_obj.MeshUtility == "snappyHexMesh": bound_box = self.part_obj.Shape.BoundBox bC = 5 # Number of background mesh buffer cells x_min = (bound_box.XMin - bC * self.clmax) * self.scale x_max = (bound_box.XMax + bC * self.clmax) * self.scale y_min = (bound_box.YMin - bC * self.clmax) * self.scale y_max = (bound_box.YMax + bC * self.clmax) * self.scale z_min = (bound_box.ZMin - bC * self.clmax) * self.scale z_max = (bound_box.ZMax + bC * self.clmax) * self.scale cells_x = int(math.ceil(bound_box.XLength / self.clmax) + 2 * bC) cells_y = int(math.ceil(bound_box.YLength / self.clmax) + 2 * bC) cells_z = int(math.ceil(bound_box.ZLength / self.clmax) + 2 * bC) snappy_settings = self.snappy_settings snappy_settings['BlockMesh'] = { "xMin": x_min, "xMax": x_max, "yMin": y_min, "yMax": y_max, "zMin": z_min, "zMax": z_max, "cellsX": cells_x, "cellsY": cells_y, "cellsZ": cells_z } inside_x = Units.Quantity( self.mesh_obj.PointInMesh.get('x')).Value * self.scale inside_y = Units.Quantity( self.mesh_obj.PointInMesh.get('y')).Value * self.scale inside_z = Units.Quantity( self.mesh_obj.PointInMesh.get('z')).Value * self.scale shape_face_names_list = [] for i in self.mesh_obj.ShapeFaceNames: shape_face_names_list.append(i) snappy_settings['ShapeFaceNames'] = tuple(shape_face_names_list) snappy_settings[ 'EdgeRefinementLevel'] = CfdTools.relLenToRefinementLevel( self.mesh_obj.EdgeRefinement) snappy_settings['PointInMesh'] = { "x": inside_x, "y": inside_y, "z": inside_z } snappy_settings[ 'CellsBetweenLevels'] = self.mesh_obj.CellsBetweenLevels if self.mesh_obj.NumberCores <= 1: self.mesh_obj.NumberCores = 1 snappy_settings['ParallelMesh'] = False else: snappy_settings['ParallelMesh'] = True snappy_settings['NumberCores'] = self.mesh_obj.NumberCores if len(self.snappy_settings["InternalRegions"]) > 0: self.snappy_settings['InternalRefinementRegionsPresent'] = True else: self.snappy_settings[ 'InternalRefinementRegionsPresent'] = False elif self.mesh_obj.MeshUtility == "gmsh": if platform.system() == "Windows": exe = os.path.join(FreeCAD.getHomePath(), 'bin', 'gmsh.exe') else: exe = subprocess.check_output( ["which", "gmsh"], universal_newlines=True).rstrip('\n') self.gmsh_settings['Executable'] = CfdTools.translatePath(exe) self.gmsh_settings['ShapeFile'] = self.temp_file_shape self.gmsh_settings['HasLengthMap'] = False if self.ele_length_map: self.gmsh_settings['HasLengthMap'] = True self.gmsh_settings['LengthMap'] = self.ele_length_map self.gmsh_settings['NodeMap'] = {} for e in self.ele_length_map: ele_nodes = (''.join( (str(n + 1) + ', ') for n in self.ele_node_map[e])).rstrip(', ') self.gmsh_settings['NodeMap'][e] = ele_nodes self.gmsh_settings['ClMax'] = self.clmax self.gmsh_settings['ClMin'] = self.clmin sols = (''.join( (str(n + 1) + ', ') for n in range(len(self.mesh_obj.Part.Shape.Solids))) ).rstrip(', ') self.gmsh_settings['Solids'] = sols self.gmsh_settings['BoundaryFaceMap'] = {} # Write one boundary per face for i in range(len(self.mesh_obj.Part.Shape.Faces)): self.gmsh_settings['BoundaryFaceMap']['face' + str(i)] = i + 1 self.gmsh_settings['MeshFile'] = self.temp_file_mesh # Perform initialisation here rather than __init__ in case of path changes self.template_path = os.path.join(CfdTools.get_module_path(), "data", "defaultsMesh") mesh_region_present = False if self.mesh_obj.MeshUtility == "cfMesh" and len(self.cf_settings['MeshRegions']) > 0 or \ self.mesh_obj.MeshUtility == "snappyHexMesh" and len(self.snappy_settings['MeshRegions']) > 0: mesh_region_present = True self.settings = { 'Name': self.part_obj.Name, 'MeshPath': self.meshCaseDir, 'FoamRuntime': CfdTools.getFoamRuntime(), 'TranslatedFoamPath': CfdTools.translatePath(CfdTools.getFoamDir()), 'MeshUtility': self.mesh_obj.MeshUtility, 'MeshRegionPresent': mesh_region_present, 'CfSettings': self.cf_settings, 'SnappySettings': self.snappy_settings, 'GmshSettings': self.gmsh_settings, 'TwoDSettings': self.two_d_settings } TemplateBuilder.TemplateBuilder(self.meshCaseDir, self.template_path, self.settings) # Update Allmesh permission - will fail silently on Windows fname = os.path.join(self.meshCaseDir, "Allmesh") import stat s = os.stat(fname) os.chmod(fname, s.st_mode | stat.S_IEXEC) CfdTools.cfdMessage( "Successfully wrote meshCase to folder {}\n".format( self.meshCaseDir))
def parse(pathobj): global DRILL_RETRACT_MODE global MOTION_MODE global CURRENT_X global CURRENT_Y global CURRENT_Z out = '' lastcommand = None precision_string = '.' + str(PRECISION) + 'f' params = [ 'X', 'Y', 'Z', 'A', 'B', 'C', 'U', 'V', 'W', 'I', 'J', 'K', 'F', 'S', 'T', 'Q', 'R', 'L', 'P' ] if hasattr(pathobj, 'Group'): # We have a compound or project. if OUTPUT_COMMENTS: out += linenumber() + '(Compound: ' + pathobj.Label + ')\n' for p in pathobj.Group: out += parse(p) return out else: # Parsing simple path # groups might contain non-path things like stock. if not hasattr(pathobj, 'Path'): return out if OUTPUT_COMMENTS and OUTPUT_PATH: out += linenumber() + '(Path: ' + pathobj.Label + ')\n' for c in pathobj.Path.Commands: outlist = [] command = c.Name outlist.append(command) # Debug: # print('pathobj.Path.Commands:', c) # If modal is True, delete duplicate commands: if MODAL: if command == lastcommand: outlist.pop(0) # Add the remaining parameters in order: for param in params: if param in c.Parameters: if param == 'F': if command not in RAPID_MOVES: feedRate = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) if feedRate.getValueAs(UNIT_FEED_FORMAT) > 0.0: outlist.append(param + format( float(feedRate.getValueAs( UNIT_FEED_FORMAT)), precision_string)) elif param == 'T': outlist.append(param + str(int(c.Parameters[param]))) elif param in ['H', 'D', 'S', 'P', 'L']: outlist.append(param + str(c.Parameters[param])) elif param in ['A', 'B', 'C']: outlist.append( param + format(c.Parameters[param], precision_string)) # [X, Y, Z, U, V, W, I, J, K, R, Q] else: pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) outlist.append( param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)) # Store the latest command: lastcommand = command # Capture the current position for subsequent calculations: if command in MOTION_COMMANDS: if 'X' in c.Parameters: CURRENT_X = Units.Quantity(c.Parameters['X'], FreeCAD.Units.Length) if 'Y' in c.Parameters: CURRENT_Y = Units.Quantity(c.Parameters['Y'], FreeCAD.Units.Length) if 'Z' in c.Parameters: CURRENT_Z = Units.Quantity(c.Parameters['Z'], FreeCAD.Units.Length) if command in ('G98', 'G99'): DRILL_RETRACT_MODE = command if TRANSLATE_DRILL_CYCLES: if command in ('G81', 'G82', 'G83'): out += drill_translate(outlist, command, c.Parameters) # Erase the line just translated: outlist = [] if SPINDLE_WAIT > 0: if command in ('M3', 'M03', 'M4', 'M04'): out += linenumber() + format_outlist(outlist) + '\n' # RRF: P for milliseconds, S for seconds, change P to S out += linenumber() out += format_outlist(['G4', 'S%s' % SPINDLE_WAIT]) out += '\n' outlist = [] # Check for Tool Change: if command in ('M6', 'M06'): if OUTPUT_COMMENTS: out += linenumber() + '(Begin toolchange)\n' if OUTPUT_TOOL_CHANGE: for line in TOOL_CHANGE.splitlines(True): out += linenumber() + line + '\n' outlist[0] = ' ' outlist[-1] = ('T' + str(int(c.Parameters['T']))) if not OUTPUT_TOOL_CHANGE and OUTPUT_COMMENTS: # next 2 lines could also be replaced by a single line as "outlist = []" outlist[0] = ' ' outlist[-1] = ' ' if not OUTPUT_TOOL_CHANGE and not OUTPUT_COMMENTS: outlist = [] if command == 'message': if OUTPUT_COMMENTS: outlist.pop(0) # remove the command else: out = [] if command in SUPPRESS_COMMANDS: outlist[0] = '(' + outlist[0] outlist[-1] = outlist[-1] + ')' # Remove embedded comments: if not OUTPUT_COMMENTS: tmplist = [] list_index = 0 while list_index < len(outlist): left_index = outlist[list_index].find('(') if left_index == -1: # Not a comment tmplist.append(outlist[list_index]) else: # This line contains a comment, and possibly more right_index = outlist[list_index].find(')') comment_area = outlist[list_index][ left_index:right_index + 1] line_minus_comment = outlist[list_index].replace( comment_area, '').strip() if line_minus_comment: # Line contained more than just a comment tmplist.append(line_minus_comment) list_index += 1 # Done removing comments outlist = tmplist # Prepend a line number and append a newline if len(outlist) >= 1: out += linenumber() + format_outlist(outlist) + '\n' return out
def parse(pathobj): out = "" lastcommand = None precision_string = '.' + str(PRECISION) + 'f' currLocation = {} # keep track for no doubles # The params list control the order of parameters params = [ 'X', 'Y', 'Z', 'A', 'B', 'C', 'I', 'J', 'K', 'R', 'F', 'S', 'T', 'H', 'L', 'Q' ] firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": 0.0}) currLocation.update(firstmove.Parameters) # set First location Parameters if hasattr(pathobj, "Group"): # We have a compound or project. # if OUTPUT_COMMENTS: # out += linenumber() + "(compound: " + pathobj.Label + ")\n" for p in pathobj.Group: out += parse(p) return out else: # parsing simple path # groups might contain non-path things like stock. if not hasattr(pathobj, "Path"): return out # if OUTPUT_COMMENTS: # out += linenumber() + "(" + pathobj.Label + ")\n" for c in pathobj.Path.Commands: commandlist = [ ] # list of elements in the command, code and params. command = c.Name.strip() # command M or G code or comment string commandlist.append(command) # if modal: only print the command if it is not the same as the last one if MODAL is True: if command == lastcommand: commandlist.pop(0) if c.Name[0] == '(' and not OUTPUT_COMMENTS: # command is a comment continue # Now add the remaining parameters in order for param in params: if param in c.Parameters: if param == 'F' and ( currLocation[param] != c.Parameters[param] or REPEAT_ARGUMENTS): if c.Name not in ["G0", "G00"]: # No F in G0 speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0: commandlist.append(param + format( float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string)) else: continue elif param == 'T': commandlist.append(param + str(int(c.Parameters['T']))) elif param == 'H': commandlist.append(param + str(int(c.Parameters['H']))) elif param == 'D': commandlist.append(param + str(int(c.Parameters['D']))) elif param == 'S': commandlist.append(param + str(int(c.Parameters['S']))) else: if (not REPEAT_ARGUMENTS) and ( param in currLocation) and (currLocation[param] == c.Parameters[param]): continue else: pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) commandlist.append( param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)) # store the latest command lastcommand = command currLocation.update(c.Parameters) # Check for Tool Change: if command == 'M6': for line in TOOL_CHANGE.splitlines(True): out += linenumber() + line # add height offset if USE_TLO: tool_height = '\nG43 H' + str(int(c.Parameters['T'])) commandlist.append(tool_height) if command == "message": if OUTPUT_COMMENTS is False: out = [] else: commandlist.pop(0) # remove the command # prepend a line number and append a newline if len(commandlist) >= 1: if OUTPUT_LINE_NUMBERS: commandlist.insert(0, (linenumber())) # append the line to the final output for w in commandlist: out += w.strip() + COMMAND_SPACE if (trace_gcode): print("parse : >>{}".format(out)) out = out.strip() + "\n" return out
def drill_translate(outlist, cmd, params): global DRILL_RETRACT_MODE global MOTION_MODE global CURRENT_X global CURRENT_Y global CURRENT_Z global UNITS global UNIT_FORMAT global UNIT_FEED_FORMAT class Drill: # Using a class is necessary for the nested functions. gcode = '' strFormat = '.' + str(PRECISION) + 'f' if OUTPUT_COMMENTS: # Comment the original command outlist[0] = '(' + outlist[0] outlist[-1] = outlist[-1] + ')' Drill.gcode += linenumber() + format_outlist(outlist) + '\n' # Cycle conversion only converts the cycles in the XY plane (G17). # --> ZX (G18) and YZ (G19) planes produce false gcode. drill_X = Units.Quantity(params['X'], FreeCAD.Units.Length) drill_Y = Units.Quantity(params['Y'], FreeCAD.Units.Length) drill_Z = Units.Quantity(params['Z'], FreeCAD.Units.Length) drill_R = Units.Quantity(params['R'], FreeCAD.Units.Length) drill_F = Units.Quantity(params['F'], FreeCAD.Units.Velocity) if cmd == 'G82': drill_DwellTime = params['P'] elif cmd == 'G83': drill_Step = Units.Quantity(params['Q'], FreeCAD.Units.Length) # R less than Z is error if drill_R < drill_Z: Drill.gcode += linenumber() + '(drill cycle error: R less than Z )\n' return Drill.gcode # Z height to retract to when drill cycle is done: if DRILL_RETRACT_MODE == 'G98' and CURRENT_Z > drill_R: RETRACT_Z = CURRENT_Z else: RETRACT_Z = drill_R # Z motion nested functions: def rapid_Z_to(new_Z): Drill.gcode += linenumber() + 'G0 Z' Drill.gcode += format(float(new_Z.getValueAs(UNIT_FORMAT)), strFormat) + '\n' def feed_Z_to(new_Z): Drill.gcode += linenumber() + 'G1 Z' Drill.gcode += format(float(new_Z.getValueAs(UNIT_FORMAT)), strFormat) + ' F' Drill.gcode += format(float(drill_F.getValueAs(UNIT_FEED_FORMAT)), '.2f') + '\n' # Make sure that Z is not below RETRACT_Z: if CURRENT_Z < RETRACT_Z: rapid_Z_to(RETRACT_Z) # Rapid to hole position XY: Drill.gcode += linenumber() + 'G0 X' Drill.gcode += format(float(drill_X.getValueAs(UNIT_FORMAT)), strFormat) + ' Y' Drill.gcode += format(float(drill_Y.getValueAs(UNIT_FORMAT)), strFormat) + '\n' # Rapid to R: rapid_Z_to(drill_R) # ************************************************************************* # * Drill cycles: * # * G80 Cancel the drill cycle * # * G81 Drill full depth in one pass * # * G82 Drill full depth in one pass, and pause at the bottom * # * G83 Drill in pecks, raising the drill to R height after each peck * # * In preparation for a rapid to the next hole position: * # * G98 After the hole has been drilled, retract to the initial Z value * # * G99 After the hole has been drilled, retract to R height * # * Select G99 only if safe to move from hole to hole at the R height * # ************************************************************************* if cmd in ('G81', 'G82'): feed_Z_to(drill_Z) # Drill hole in one step if cmd == 'G82': # Dwell time delay at the bottom of the hole Drill.gcode += linenumber() + 'G4 S' + str(drill_DwellTime) + '\n' # RRF uses P for milliseconds, S for seconds, change P to S elif cmd == 'G83': # Peck drill cycle: chip_Space = drill_Step * 0.5 next_Stop_Z = drill_R - drill_Step while next_Stop_Z >= drill_Z: feed_Z_to(next_Stop_Z) # Drill one peck of depth # Set next depth, next_Stop_Z is still at the current hole depth if (next_Stop_Z - drill_Step) >= drill_Z: # Rapid up to clear chips: rapid_Z_to(drill_R) # Rapid down to just above last peck depth: rapid_Z_to(next_Stop_Z + chip_Space) # Update next_Stop_Z to next depth: next_Stop_Z -= drill_Step elif next_Stop_Z == drill_Z: break # Done else: # More to drill, but less than drill_Step # Rapid up to clear chips: rapid_Z_to(drill_R) # Rapid down to just above last peck depth: rapid_Z_to(next_Stop_Z + chip_Space) # Dril remainder of the hole depth: feed_Z_to(drill_Z) break # Done rapid_Z_to(RETRACT_Z) # Done, retract the drill return Drill.gcode
def check_prerequisites(self): from FreeCAD import Units message = "" # analysis if not self.analysis: message += "No active Analysis\n" if self.analysis_type not in self.known_analysis_types: message += "Unknown analysis type: {}\n".format(self.analysis_type) if not self.working_dir: message += "Working directory not set\n" import os if not (os.path.isdir(self.working_dir)): message += "Working directory \'{}\' doesn't exist.".format( self.working_dir) # solver if not self.solver: message += "No solver object defined in the analysis\n" else: if self.analysis_type == "frequency": if not hasattr(self.solver, "EigenmodeHighLimit"): message += "Frequency analysis: Solver has no EigenmodeHighLimit.\n" elif not hasattr(self.solver, "EigenmodeLowLimit"): message += "Frequency analysis: Solver has no EigenmodeLowLimit.\n" elif not hasattr(self.solver, "EigenmodesCount"): message += "Frequency analysis: Solver has no EigenmodesCount.\n" if hasattr(self.solver, "MaterialNonlinearity" ) and self.solver.MaterialNonlinearity == "nonlinear": if not self.materials_nonlinear: message += "Solver is set to nonlinear materials, but there is no nonlinear material in the analysis.\n" if self.solver.SolverType == 'FemSolverCalculix' and self.solver.GeometricalNonlinearity != "nonlinear": # nonlinear geometry --> should be set https://forum.freecadweb.org/viewtopic.php?f=18&t=23101&p=180489#p180489 message += "Solver CalculiX triggers nonlinear geometry for nonlinear material, thus it should to be set too.\n" # mesh if not self.mesh: message += "No mesh object defined in the analysis\n" if self.mesh: if self.mesh.FemMesh.VolumeCount == 0 and self.mesh.FemMesh.FaceCount > 0 and not self.shell_thicknesses: message += "FEM mesh has no volume elements, either define a shell thicknesses or provide a FEM mesh with volume elements.\n" if self.mesh.FemMesh.VolumeCount == 0 and self.mesh.FemMesh.FaceCount == 0 and self.mesh.FemMesh.EdgeCount > 0 and not self.beam_sections and not self.fluid_sections: message += "FEM mesh has no volume and no shell elements, either define a beam/fluid section or provide a FEM mesh with volume elements.\n" if self.mesh.FemMesh.VolumeCount == 0 and self.mesh.FemMesh.FaceCount == 0 and self.mesh.FemMesh.EdgeCount == 0: message += "FEM mesh has neither volume nor shell or edge elements. Provide a FEM mesh with elements!\n" # material linear and nonlinear if not self.materials_linear: message += "No material object defined in the analysis\n" has_no_references = False for m in self.materials_linear: if len(m['Object'].References) == 0: if has_no_references is True: message += "More than one material has an empty references list (Only one empty references list is allowed!).\n" has_no_references = True mat_ref_shty = '' for m in self.materials_linear: ref_shty = get_refshape_type(m['Object']) if not mat_ref_shty: mat_ref_shty = ref_shty if mat_ref_shty and ref_shty and ref_shty != mat_ref_shty: # mat_ref_shty could be empty in one material, only the not empty ones should have the same shape type message += 'Some material objects do not have the same reference shape type (all material objects must have the same reference shape type, at the moment).\n' for m in self.materials_linear: mat_map = m['Object'].Material mat_obj = m['Object'] if mat_obj.Category == 'Solid': if 'YoungsModulus' in mat_map: # print(Units.Quantity(mat_map['YoungsModulus']).Value) if not Units.Quantity(mat_map['YoungsModulus']).Value: message += "Value of YoungsModulus is set to 0.0.\n" else: message += "No YoungsModulus defined for at least one material.\n" if 'PoissonRatio' not in mat_map: message += "No PoissonRatio defined for at least one material.\n" # PoissonRatio is allowed to be 0.0 (in ccx), but it should be set anyway. if self.analysis_type == "frequency" or self.selfweight_constraints: if 'Density' not in mat_map: message += "No Density defined for at least one material.\n" if self.analysis_type == "thermomech": if 'ThermalConductivity' in mat_map: if not Units.Quantity( mat_map['ThermalConductivity']).Value: message += "Value of ThermalConductivity is set to 0.0.\n" else: message += "Thermomechanical analysis: No ThermalConductivity defined for at least one material.\n" if 'ThermalExpansionCoefficient' not in mat_map and mat_obj.Category == 'Solid': message += "Thermomechanical analysis: No ThermalExpansionCoefficient defined for at least one material.\n" # allowed to be 0.0 (in ccx) if 'SpecificHeat' not in mat_map: message += "Thermomechanical analysis: No SpecificHeat defined for at least one material.\n" # allowed to be 0.0 (in ccx) for m in self.materials_linear: has_nonlinear_material = False for nlm in self.materials_nonlinear: if nlm['Object'].LinearBaseMaterial == m['Object']: if has_nonlinear_material is False: has_nonlinear_material = True else: message += "At least two nonlinear materials use the same linear base material. Only one nonlinear material for each linear material allowed.\n" # which analysis needs which constraints # no check in the regard of loads existence (constraint force, pressure, self weight) is done because an analysis without loads at all is an valid analysis too if self.analysis_type == "static": if not (self.fixed_constraints or self.displacement_constraints): message += "Static analysis: Neither constraint fixed nor constraint displacement defined.\n" if self.analysis_type == "thermomech": if not self.initialtemperature_constraints: if not self.fluid_sections: message += "Thermomechanical analysis: No initial temperature defined.\n" if len(self.initialtemperature_constraints) > 1: message += "Thermomechanical analysis: Only one initial temperature is allowed.\n" # constraints # fixed if self.fixed_constraints: for c in self.fixed_constraints: if len(c['Object'].References) == 0: message += "At least one constraint fixed has an empty reference.\n" # displacement if self.displacement_constraints: for di in self.displacement_constraints: if len(di['Object'].References) == 0: message += "At least one constraint displacement has an empty reference.\n" # plane rotation if self.planerotation_constraints: for c in self.planerotation_constraints: if len(c['Object'].References) == 0: message += "At least one constraint plane rotation has an empty reference.\n" # contact if self.contact_constraints: for c in self.contact_constraints: if len(c['Object'].References) == 0: message += "At least one constraint contact has an empty reference.\n" # transform if self.transform_constraints: for c in self.transform_constraints: if len(c['Object'].References) == 0: message += "At least one constraint transform has an empty reference.\n" # pressure if self.pressure_constraints: for c in self.pressure_constraints: if len(c['Object'].References) == 0: message += "At least one constraint pressure has an empty reference.\n" # force if self.force_constraints: for c in self.force_constraints: if len(c['Object'].References) == 0: message += "At least one constraint force has an empty reference.\n" # temperature if self.temperature_constraints: for c in self.temperature_constraints: if len(c['Object'].References) == 0: message += "At least one constraint temperature has an empty reference.\n" # heat flux if self.heatflux_constraints: for c in self.heatflux_constraints: if len(c['Object'].References) == 0: message += "At least one constraint heat flux has an empty reference.\n" # beam section if self.beam_sections: if self.shell_thicknesses: # this needs to be checked only once either here or in shell_thicknesses message += "Beam Sections and shell thicknesses in one analysis is not supported at the moment.\n" if self.fluid_sections: # this needs to be checked only once either here or in shell_thicknesses message += "Beam Sections and Fluid Sections in one analysis is not supported at the moment.\n" has_no_references = False for b in self.beam_sections: if len(b['Object'].References) == 0: if has_no_references is True: message += "More than one beam section has an empty references list (Only one empty references list is allowed!).\n" has_no_references = True if self.mesh: if self.mesh.FemMesh.FaceCount > 0 or self.mesh.FemMesh.VolumeCount > 0: message += "Beam sections defined but FEM mesh has volume or shell elements.\n" if self.mesh.FemMesh.EdgeCount == 0: message += "Beam sections defined but FEM mesh has no edge elements.\n" # shell thickness if self.shell_thicknesses: has_no_references = False for s in self.shell_thicknesses: if len(s['Object'].References) == 0: if has_no_references is True: message += "More than one shell thickness has an empty references list (Only one empty references list is allowed!).\n" has_no_references = True if self.mesh: if self.mesh.FemMesh.VolumeCount > 0: message += "Shell thicknesses defined but FEM mesh has volume elements.\n" if self.mesh.FemMesh.FaceCount == 0: message += "Shell thicknesses defined but FEM mesh has no shell elements.\n" # fluid section if self.fluid_sections: if not self.selfweight_constraints: message += "A fluid network analysis requires self weight constraint to be applied" if self.analysis_type != "thermomech": message += "A fluid network analysis can only be done in a thermomech analysis" has_no_references = False for f in self.fluid_sections: if len(f['Object'].References) == 0: if has_no_references is True: message += "More than one fluid section has an empty references list (Only one empty references list is allowed!).\n" has_no_references = True if self.mesh: if self.mesh.FemMesh.FaceCount > 0 or self.mesh.FemMesh.VolumeCount > 0: message += "Fluid sections defined but FEM mesh has volume or shell elements.\n" if self.mesh.FemMesh.EdgeCount == 0: message += "Fluid sections defined but FEM mesh has no edge elements.\n" return message
def parse(pathobj): global PRECISION global MODAL global OUTPUT_DOUBLES global UNIT_FORMAT global UNIT_SPEED_FORMAT out = "" lastcommand = None precision_string = '.' + str(PRECISION) + 'f' currLocation = {} # keep track for no doubles # the order of parameters # linuxcnc doesn't want K properties on XY plane Arcs need work. params = [ 'X', 'Y', 'Z', 'A', 'B', 'C', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L', 'H', 'D', 'P' ] firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": 0.0}) currLocation.update(firstmove.Parameters) # set First location Parameters if hasattr(pathobj, "Group"): # We have a compound or project. # if OUTPUT_COMMENTS: # out += linenumber() + "(compound: " + pathobj.Label + ")\n" for p in pathobj.Group: out += parse(p) return out else: # parsing simple path # groups might contain non-path things like stock. if not hasattr(pathobj, "Path"): return out # if OUTPUT_COMMENTS: # out += linenumber() + "(" + pathobj.Label + ")\n" for c in pathobj.Path.Commands: outstring = [] command = c.Name outstring.append(command) # if modal: suppress the command if it is the same as the last one if MODAL is True: if command == lastcommand: outstring.pop(0) if c.Name[0] == '(' and not OUTPUT_COMMENTS: # command is a comment continue # Now add the remaining parameters in order for param in params: if param in c.Parameters: if param == 'F' and ( currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES): if c.Name not in [ "G0", "G00" ]: # linuxcnc doesn't use rapid speeds speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0: outstring.append(param + format( float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string)) else: continue elif param == 'T': outstring.append(param + str(int(c.Parameters['T']))) elif param == 'H': outstring.append(param + str(int(c.Parameters['H']))) elif param == 'D': outstring.append(param + str(int(c.Parameters['D']))) elif param == 'S': outstring.append(param + str(int(c.Parameters['S']))) else: if (not OUTPUT_DOUBLES) and ( param in currLocation) and (currLocation[param] == c.Parameters[param]): continue else: pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) outstring.append( param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)) # store the latest command lastcommand = command currLocation.update(c.Parameters) # Check for Tool Change: if command == 'M6': # if OUTPUT_COMMENTS: # out += linenumber() + "(begin toolchange)\n" for line in TOOL_CHANGE.splitlines(True): out += linenumber() + line if command == "message": if OUTPUT_COMMENTS is False: out = [] else: outstring.pop(0) # remove the command # prepend a line number and append a newline if len(outstring) >= 1: if OUTPUT_LINE_NUMBERS: outstring.insert(0, (linenumber())) # append the line to the final output for w in outstring: out += w + COMMAND_SPACE out = out.strip() + "\n" return out
def convert(quantityStr, unit): quantity = Units.Quantity(quantityStr) for key, setting in UNITS.items(): unit = unit.replace(key, setting) return float(quantity.getValueAs(unit))
def __init__(self): self.name = "Polar array" _log(_tr("Task panel:") + " {}".format(_tr(self.name))) # The .ui file must be loaded into an attribute # called `self.form` so that it is displayed in the task panel. ui_file = ":/ui/TaskPanel_PolarArray.ui" self.form = Gui.PySideUic.loadUi(ui_file) icon_name = "Draft_PolarArray" svg = ":/icons/" + icon_name pix = QtGui.QPixmap(svg) icon = QtGui.QIcon.fromTheme(icon_name, QtGui.QIcon(svg)) self.form.setWindowIcon(icon) self.form.setWindowTitle(_tr(self.name)) self.form.label_icon.setPixmap(pix.scaled(32, 32)) # ------------------------------------------------------------------- # Default values for the internal function, # and for the task panel interface start_angle = U.Quantity(360.0, App.Units.Angle) angle_unit = start_angle.getUserPreferred()[2] self.angle = start_angle.Value self.number = 5 self.form.spinbox_angle.setProperty('rawValue', self.angle) self.form.spinbox_angle.setProperty('unit', angle_unit) self.form.spinbox_number.setValue(self.number) start_point = U.Quantity(0.0, App.Units.Length) length_unit = start_point.getUserPreferred()[2] self.center = App.Vector(start_point.Value, start_point.Value, start_point.Value) self.form.input_c_x.setProperty('rawValue', self.center.x) self.form.input_c_x.setProperty('unit', length_unit) self.form.input_c_y.setProperty('rawValue', self.center.y) self.form.input_c_y.setProperty('unit', length_unit) self.form.input_c_z.setProperty('rawValue', self.center.z) self.form.input_c_z.setProperty('unit', length_unit) self.fuse = utils.get_param("Draft_array_fuse", False) self.use_link = utils.get_param("Draft_array_Link", True) self.form.checkbox_fuse.setChecked(self.fuse) self.form.checkbox_link.setChecked(self.use_link) # ------------------------------------------------------------------- # Some objects need to be selected before we can execute the function. self.selection = None # This is used to test the input of the internal function. # It should be changed to True before we can execute the function. self.valid_input = False self.set_widget_callbacks() self.tr_true = QT_TRANSLATE_NOOP("Draft", "True") self.tr_false = QT_TRANSLATE_NOOP("Draft", "False") # The mask is not used at the moment, but could be used in the future # by a callback to restrict the coordinates of the pointer. self.mask = ""
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" 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 == "Frontal Delaunay": self.algorithm3D = "5" elif algo3D == "Frontal Hex": self.algorithm3D = "6" elif algo3D == "MMG3D": self.algorithm3D = "7" elif algo3D == "R-tree": self.algorithm3D = "9" else: self.algorithm3D = "1" # 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 onChanged(self,vobj,prop): if prop in ["Text","Decimals","ShowUnit"]: if hasattr(self,"text1") and hasattr(self,"text2") and hasattr(vobj,"Text"): self.text1.string.deleteValues(0) self.text2.string.deleteValues(0) text1 = [] text2 = [] first = True for t in vobj.Text: if t: if hasattr(vobj.Object,"Area"): from FreeCAD import Units q = Units.Quantity(vobj.Object.Area,Units.Area).getUserPreferred() qt = vobj.Object.Area/q[1] if hasattr(vobj,"Decimals"): if vobj.Decimals == 0: qt = str(int(qt)) else: f = "%."+str(abs(vobj.Decimals))+"f" qt = f % qt else: qt = str(qt) if hasattr(vobj,"ShowUnit"): if vobj.ShowUnit: qt = qt + q[2].replace("^2",u"\xb2") # square symbol t = t.replace("$area",qt) t = t.replace("$label",vobj.Object.Label) if hasattr(vobj.Object,"Tag"): t = t.replace("$tag",vobj.Object.Tag) if hasattr(vobj.Object,"FinishFloor"): t = t.replace("$floor",vobj.Object.FinishFloor) if hasattr(vobj.Object,"FinishWalls"): t = t.replace("$walls",vobj.Object.FinishWalls) if hasattr(vobj.Object,"FinishCeiling"): t = t.replace("$ceiling",vobj.Object.FinishCeiling) if first: text1.append(t.encode("utf8")) else: text2.append(t.encode("utf8")) first = False if text1: self.text1.string.setValues(text1) if text2: self.text2.string.setValues(text2) elif prop == "FontName": if hasattr(self,"font") and hasattr(vobj,"FontName"): self.font.name = str(vobj.FontName) elif (prop == "FontSize"): if hasattr(self,"font") and hasattr(vobj,"FontSize"): self.font.size = vobj.FontSize.Value elif (prop == "FirstLine"): if hasattr(self,"header") and hasattr(vobj,"FontSize") and hasattr(vobj,"FirstLine"): scale = vobj.FirstLine.Value/vobj.FontSize.Value self.header.scaleFactor.setValue([scale,scale,scale]) elif prop == "TextColor": if hasattr(self,"color") and hasattr(vobj,"TextColor"): c = vobj.TextColor self.color.rgb.setValue(c[0],c[1],c[2]) elif prop == "TextPosition": if hasattr(self,"coords") and hasattr(self,"header") and hasattr(vobj,"TextPosition") and hasattr(vobj,"FirstLine"): pos = self.getTextPosition(vobj) self.coords.translation.setValue([pos.x,pos.y,pos.z]) up = vobj.FirstLine.Value * vobj.LineSpacing self.header.translation.setValue([0,up,0]) elif prop == "LineSpacing": if hasattr(self,"text1") and hasattr(self,"text2") and hasattr(vobj,"LineSpacing"): self.text1.spacing = vobj.LineSpacing self.text2.spacing = vobj.LineSpacing self.onChanged(vobj,"TextPosition") elif prop == "TextAlign": if hasattr(self,"text1") and hasattr(self,"text2") and hasattr(vobj,"TextAlign"): from pivy import coin if vobj.TextAlign == "Center": self.text1.justification = coin.SoAsciiText.CENTER self.text2.justification = coin.SoAsciiText.CENTER elif vobj.TextAlign == "Right": self.text1.justification = coin.SoAsciiText.RIGHT self.text2.justification = coin.SoAsciiText.RIGHT else: self.text1.justification = coin.SoAsciiText.LEFT self.text2.justification = coin.SoAsciiText.LEFT elif prop == "Visibility": if vobj.Visibility: self.label.whichChild = 0 else: self.label.whichChild = -1
#* * #*************************************************************************** import math import FreeCAD as App import FreeCADGui as Gui from FreeCAD import Vector, Matrix, Placement import Part from FreeCAD import Units import Instance as ShipInstance import WeightInstance import TankInstance from shipHydrostatics import Tools as Hydrostatics G = Units.parseQuantity("9.81 m/s^2") MAX_EQUILIBRIUM_ITERS = 10 DENS = Units.parseQuantity("1025 kg/m^3") TRIM_RELAX_FACTOR = 10.0 def solve(ship, weights, tanks, rolls, var_trim=True): """Compute the ship GZ stability curve Position arguments: ship -- Ship object weights -- List of weights to consider tanks -- List of tanks to consider (each one should be a tuple with the tank instance, the density of the fluid inside, and the filling level ratio) rolls -- List of roll angles
#* * #*************************************************************************** import math import random from FreeCAD import Vector, Rotation, Matrix, Placement import Part from FreeCAD import Units import FreeCAD as App import FreeCADGui as Gui from PySide import QtGui, QtCore from .. import Instance from ..shipUtils import Math from ..shipUtils import Units as USys DENS = Units.parseQuantity("1025 kg/m^3") # Salt water COMMON_BOOLEAN_ITERATIONS = 10 def placeShipShape(shape, draft, roll, trim): """Move the ship shape such that the free surface matches with the plane z=0. The transformation will be applied on the input shape, so copy it before calling this method if it should be preserved. Position arguments: shape -- Ship shape draft -- Ship draft roll -- Roll angle trim -- Trim angle Returned values:
def get_distances(self): """Get the distance parameters from the widgets.""" r_d_str = self.form.spinbox_r_distance.text() tan_d_str = self.form.spinbox_tan_distance.text() return (U.Quantity(r_d_str).Value, U.Quantity(tan_d_str).Value)
def process_output(self, text): log_lines = text.split('\n') prev_niter = self.niter for line in log_lines: line = line.rstrip() split = line.split() # Only record the first residual per outer iteration if line.startswith(u"Time = "): try: time_val = float(line.lstrip(u"Time = ")) except ValueError: pass else: self.prev_time = self.latest_time self.latest_time = time_val self.prev_num_outer_iters = self.latest_outer_iter if self.latest_time > 0: # Don't keep spurious time zero self.latest_outer_iter = 0 self.niter += 1 self.in_forces_section = None self.in_forcecoeffs_section = None if line.find(u"PIMPLE: iteration ") >= 0 or line.find( u"pseudoTime: iteration ") >= 0: self.latest_outer_iter += 1 # Don't increment counter on first outer iter as this was already done with time if self.latest_outer_iter > 1: self.niter += 1 if line.startswith(u"forces") and (line.endswith(u"write:") or line.endswith(u"execute:")): self.in_forces_section = split[1] if line.startswith(u"forceCoeffs") and ( line.endswith(u"write:") or line.endswith(u"execute:")): self.in_forcecoeffs_section = split[1] if not line.strip(): # Blank line self.in_forces_section = None self.in_forcecoeffs_section = None # Add a point to the time axis for each outer iteration if self.niter > len(self.time): self.time.append(self.latest_time) if self.latest_outer_iter > 0: # Outer-iteration case # Create virtual times to space the residuals of the outer iterations nicely on the time graph self.prev_num_outer_iters = max(self.prev_num_outer_iters, self.latest_outer_iter) for i in range(self.latest_outer_iter): self.time[-( self.latest_outer_iter - i)] = self.prev_time + ( self.latest_time - self.prev_time) * ( (i + 1) / self.prev_num_outer_iters) if "Ux," in split and self.niter > len(self.UxResiduals): self.UxResiduals.append(float(split[7].split(',')[0])) if "Uy," in split and self.niter > len(self.UyResiduals): self.UyResiduals.append(float(split[7].split(',')[0])) if "Uz," in split and self.niter > len(self.UzResiduals): self.UzResiduals.append(float(split[7].split(',')[0])) if "p," in split and self.niter > len(self.pResiduals): self.pResiduals.append(float(split[7].split(',')[0])) if "p_rgh," in split and self.niter > len(self.pResiduals): self.pResiduals.append(float(split[7].split(',')[0])) if "h," in split and self.niter > len(self.EResiduals): self.EResiduals.append(float(split[7].split(',')[0])) # HiSA coupled residuals if "Residual:" in split and self.niter > len(self.rhoResiduals): self.rhoResiduals.append(float(split[4])) self.UxResiduals.append(float(split[5].lstrip('('))) self.UyResiduals.append(float(split[6])) self.UzResiduals.append(float(split[7].rstrip(')'))) self.EResiduals.append(float(split[8])) if "k," in split and self.niter > len(self.kResiduals): self.kResiduals.append(float(split[7].split(',')[0])) if "epsilon," in split and self.niter > len(self.epsilonResiduals): self.epsilonResiduals.append(float(split[7].split(',')[0])) if "omega," in split and self.niter > len(self.omegaResiduals): self.omegaResiduals.append(float(split[7].split(',')[0])) if "nuTilda," in split and self.niter > len(self.nuTildaResiduals): self.nuTildaResiduals.append(float(split[7].split(',')[0])) if "gammaInt," in split and self.niter > len( self.gammaIntResiduals): self.gammaIntResiduals.append(float(split[7].split(',')[0])) if "ReThetat," in split and self.niter > len( self.ReThetatResiduals): self.ReThetatResiduals.append(float(split[7].split(',')[0])) # Force monitors if self.in_forces_section: f = self.forces[self.in_forces_section] if (("Pressure" in split) or ("pressure" in split)) and self.niter - 1 > len(f['pressureXForces']): f['pressureXForces'].append(float(split[2].lstrip("("))) f['pressureYForces'].append(float(split[3])) f['pressureZForces'].append(float(split[4].rstrip(")"))) if (("Viscous" in split) or ("viscous" in split)) and self.niter - 1 > len(f['viscousXForces']): f['viscousXForces'].append(float(split[2].lstrip("("))) f['viscousYForces'].append(float(split[3])) f['viscousZForces'].append(float(split[4].rstrip(")"))) # Force coefficient monitors if self.in_forcecoeffs_section: fc = self.force_coeffs[self.in_forcecoeffs_section] if "Cd" in split and self.niter - 1 > len(fc['cdCoeffs']): fc['cdCoeffs'].append(float(split[2])) if "Cl" in split and self.niter - 1 > len(fc['clCoeffs']): fc['clCoeffs'].append(float(split[2])) # Update plots if self.niter > 1 and self.niter > prev_niter: self.solver.Proxy.residual_plotter.updateValues( self.time, OrderedDict([('$\\rho$', self.rhoResiduals), ('$U_x$', self.UxResiduals), ('$U_y$', self.UyResiduals), ('$U_z$', self.UzResiduals), ('$p$', self.pResiduals), ('$E$', self.EResiduals), ('$k$', self.kResiduals), ('$\\epsilon$', self.epsilonResiduals), ('$\\tilde{\\nu}$', self.nuTildaResiduals), ('$\\omega$', self.omegaResiduals), ('$\\gamma$', self.gammaIntResiduals), ('$Re_{\\theta}$', self.ReThetatResiduals)])) for fn in self.forces: f = self.forces[fn] self.solver.Proxy.forces_plotters[fn].updateValues( self.time, OrderedDict([('$Pressure_x$', f['pressureXForces']), ('$Pressure_y$', f['pressureYForces']), ('$Pressure_z$', f['pressureZForces']), ('$Viscous_x$', f['viscousXForces']), ('$Viscous_y$', f['viscousYForces']), ('$Viscous_z$', f['viscousZForces'])])) for fcn in self.force_coeffs: fc = self.force_coeffs[fcn] self.solver.Proxy.force_coeffs_plotters[fcn].updateValues( self.time, OrderedDict([('$C_D$', fc['cdCoeffs']), ('$C_L$', fc['clCoeffs'])])) # Probes for pn in self.probes: p = self.probes[pn] if p['file'] is None: working_dir = CfdTools.getOutputPath(self.analysis) case_name = self.solver.InputCaseName solver_dir = os.path.abspath( os.path.join(working_dir, case_name)) try: f = open( os.path.join(solver_dir, 'postProcessing', pn, '0', p['field'])) p['file'] = f except OSError: pass if p['file']: ntimes = len(p['time']) is_vector = False for l in p['file'].readlines(): l = l.strip() if len(l) and not l.startswith('#'): s = l.split() p['time'].append(float(s[0])) if s[1].startswith('('): is_vector = True while len(p['values']) < len(s) - 1: p['values'].append([]) for i in range(1, len(s)): s[i] = s[i].lstrip('(').rstrip(')') p['values'][i - 1].append(float(s[i])) if len(p['time']) > ntimes: legends = [] for pi in p['points']: points_str = '({}, {}, {}) m'.format( *(Units.Quantity(pij, Units.Length).getValueAs('m') for pij in (pi.x, pi.y, pi.z))) if is_vector: legends.append('{}$_x$ @ '.format(p['field']) + points_str) legends.append('{}$_y$ @ '.format(p['field']) + points_str) legends.append('{}$_z$ @ '.format(p['field']) + points_str) else: legends.append('${}$ @ '.format(p['field']) + points_str) self.solver.Proxy.probes_plotters[pn].updateValues( p['time'], OrderedDict(zip(legends, p['values'])))
#*************************************************************************** import math import random from FreeCAD import Vector, Rotation, Matrix, Placement import Part from FreeCAD import Units import FreeCAD as App import FreeCADGui as Gui from PySide import QtGui, QtCore import Instance from shipUtils import Math import shipUtils.Units as USys DENS = Units.parseQuantity("1025 kg/m^3") # Salt water COMMON_BOOLEAN_ITERATIONS = 10 def placeShipShape(shape, draft, roll, trim): """Move the ship shape such that the free surface matches with the plane z=0. The transformation will be applied on the input shape, so copy it before calling this method if it should be preserved. Position arguments: shape -- Ship shape draft -- Ship draft roll -- Roll angle trim -- Trim angle Returned values:
def get_region_data(self): # mesh regions self.ele_length_map = {} # { 'ElementString' : element length } self.ele_node_map = {} # { 'ElementString' : [element nodes] } if not self.mesh_obj.MeshRegionList: print(' No mesh regions.') else: print(' Mesh regions, we need to get the elements.') # 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: if part.Shape.ShapeType == "Compound" and hasattr( part, "Proxy" ): # other part obj might not have a Proxy, thus an exception would be raised if (part.Proxy.Type == "FeatureBooleanFragments" or part.Proxy.Type == "FeatureSlice" or part.Proxy.Type == "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." FreeCAD.Console.PrintError(error_message + "\n") # TODO no gui popup because FreeCAD will be in a endless prind loop as long as the pop up is on --> my be 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): # print(" One element of the meshregion " + mr_obj.Name + " is not an element of the Part to mesh.") # print(" But we gone try to find it in the Shape to mesh :-)") search_ele_in_shape_to_mesh = True for elems in sub[1]: # print(elems) # elems --> element if search_ele_in_shape_to_mesh: # we gone try to find the element it in the Shape to mesh and use the found element as elems ele_shape = FemMeshTools.get_element( sub[0], elems ) # the method getElement(element) does not return Solid elements found_element = FemMeshTools.find_element_in_shape( self.part_obj.Shape, ele_shape) if found_element: elems = found_element else: FreeCAD.Console.PrintError( "One element of the meshregion " + mr_obj.Name + " could not be found in the Part to mesh. It will be ignored.\n" ) # print(elems) # element if elems not in self.ele_length_map: self.ele_length_map[ elems] = Units.Quantity( mr_obj.CharacteristicLength).Value else: FreeCAD.Console.PrintError( "The element " + elems + " of the meshregion " + mr_obj.Name + " has been added to another mesh region.\n" ) else: FreeCAD.Console.PrintError( "The meshregion: " + mr_obj.Name + " is not used to create the mesh because the reference list is empty.\n" ) else: FreeCAD.Console.PrintError( "The meshregion: " + mr_obj.Name + " is not used to create the mesh because the CharacteristicLength is 0.0 mm.\n" ) for eleml in self.ele_length_map: ele_shape = FemMeshTools.get_element( self.part_obj, eleml ) # the method getElement(element) does not return Solid elements ele_vertexes = FemMeshTools.get_vertexes_by_element( self.part_obj.Shape, ele_shape) self.ele_node_map[eleml] = ele_vertexes print(' {}'.format(self.ele_length_map)) print(' {}'.format(self.ele_node_map))
def displacement(ship, draft=None, roll=Units.parseQuantity("0 deg"), trim=Units.parseQuantity("0 deg")): """Compute the ship displacement Position arguments: ship -- Ship object (see createShip) Keyword arguments: draft -- Ship draft (Design ship draft by default) roll -- Roll angle (0 degrees by default) trim -- Trim angle (0 degrees by default) Returned values: disp -- The ship displacement (a density of the water of 1025 kg/m^3 is assumed) B -- Bouyance application point, i.e. Center of mass of the underwater side Cb -- Block coefficient The Bouyance center is referred to the original ship position. """ if draft is None: draft = ship.Draft shape, base_z = placeShipShape(ship.Shape.copy(), draft, roll, trim) shape = getUnderwaterSide(shape) vol = 0.0 cog = Vector() if len(shape.Solids) > 0: for solid in shape.Solids: vol += solid.Volume sCoG = solid.CenterOfMass cog.x = cog.x + sCoG.x * solid.Volume cog.y = cog.y + sCoG.y * solid.Volume cog.z = cog.z + sCoG.z * solid.Volume cog.x = cog.x / vol cog.y = cog.y / vol cog.z = cog.z / vol bbox = shape.BoundBox Vol = (bbox.XMax - bbox.XMin) * (bbox.YMax - bbox.YMin) * abs(bbox.ZMin) # Undo the transformations on the bouyance point B = Part.Point(Vector(cog.x, cog.y, cog.z)) m = Matrix() m.move(Vector(0.0, 0.0, draft)) m.move(Vector(-draft * math.sin(trim.getValueAs("rad")), 0.0, 0.0)) m.rotateY(trim.getValueAs("rad")) m.move(Vector(0.0, -draft * math.sin(roll.getValueAs("rad")), base_z)) m.rotateX(-roll.getValueAs("rad")) B.transform(m) try: cb = vol / Vol except ZeroDivisionError: msg = QtGui.QApplication.translate( "ship_console", "ZeroDivisionError: Null volume found during the displacement" " computation!", None) App.Console.PrintError(msg + '\n') cb = 0.0 # Return the computed data return (DENS * Units.Quantity(vol, Units.Volume), Vector(B.X, B.Y, B.Z), cb)
def parse(pathobj): global DRILL_RETRACT_MODE global MOTION_MODE global CURRENT_X global CURRENT_Y global CURRENT_Z out = "" lastcommand = None precision_string = '.' + str(PRECISION) + 'f' params = [ 'X', 'Y', 'Z', 'A', 'B', 'C', 'U', 'V', 'W', 'I', 'J', 'K', 'F', 'S', 'T', 'Q', 'R', 'L', 'P' ] if hasattr(pathobj, "Group"): # We have a compound or project. if OUTPUT_COMMENTS: out += linenumber() + "(Compound: " + pathobj.Label + ")\n" for p in pathobj.Group: out += parse(p) return out else: # parsing simple path if not hasattr( pathobj, "Path"): # groups might contain non-path things like stock. return out if OUTPUT_COMMENTS: out += linenumber() + "(Path: " + pathobj.Label + ")\n" for c in pathobj.Path.Commands: outstring = [] command = c.Name outstring.append(command) # if modal: only print the command if it is not the same as the last one if MODAL: if command == lastcommand: outstring.pop(0) # Now add the remaining parameters in order for param in params: if param in c.Parameters: if param == 'F': if command not in RAPID_MOVES: speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0: outstring.append(param + format( float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string)) elif param in ['T', 'H', 'D', 'S', 'P', 'L']: outstring.append(param + str(c.Parameters[param])) elif param in ['A', 'B', 'C']: outstring.append( param + format(c.Parameters[param], precision_string)) else: # [X, Y, Z, U, V, W, I, J, K, R, Q] (Conversion eventuelle mm/inches) pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) outstring.append( param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)) # store the latest command lastcommand = command # Memorizes the current position for calculating the related movements and the withdrawal plan if command in MOTION_COMMANDS: if 'X' in c.Parameters: CURRENT_X = Units.Quantity(c.Parameters['X'], FreeCAD.Units.Length) if 'Y' in c.Parameters: CURRENT_Y = Units.Quantity(c.Parameters['Y'], FreeCAD.Units.Length) if 'Z' in c.Parameters: CURRENT_Z = Units.Quantity(c.Parameters['Z'], FreeCAD.Units.Length) if command in ('G98', 'G99'): DRILL_RETRACT_MODE = command if command in ('G90', 'G91'): MOTION_MODE = command if TRANSLATE_DRILL_CYCLES: if command in ('G81', 'G82', 'G83'): out += drill_translate(outstring, command, c.Parameters) # Erase the line we just translated outstring = [] if SPINDLE_WAIT > 0: if command in ('M3', 'M03', 'M4', 'M04'): out += linenumber() + format_outstring(outstring) + "\n" out += linenumber() + format_outstring( ['G4', 'P%s' % SPINDLE_WAIT]) + "\n" outstring = [] # Check for Tool Change: if command in ('M6', 'M06'): if OUTPUT_COMMENTS: out += linenumber() + "(Begin toolchange)\n" if not OUTPUT_TOOL_CHANGE: outstring.insert(0, "(") outstring.append(")") else: for line in TOOL_CHANGE.splitlines(True): out += linenumber() + line if command == "message": if OUTPUT_COMMENTS is False: out = [] else: outstring.pop(0) # remove the command if command in SUPPRESS_COMMANDS: outstring.insert(0, "(") outstring.append(")") # prepend a line number and append a newline if len(outstring) >= 1: out += linenumber() + format_outstring(outstring) + "\n" return out
def processRefinements(self): """ Process mesh refinements """ mr_objs = CfdTools.getMeshRefinementObjs(self.mesh_obj) if self.mesh_obj.MeshUtility == "gmsh": # mesh regions self.ele_length_map = {} # { 'ElementString' : element length } self.ele_node_map = {} # { 'ElementString' : [element nodes] } if not mr_objs: print(' No mesh refinements') else: print(' Mesh refinements found - getting elements') 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." FreeCAD.Console.PrintError(err + "\n") for mr_obj in mr_objs: if mr_obj.RelativeLength: if mr_obj.References: for sub in mr_obj.References: # 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 ref = FreeCAD.ActiveDocument.getObject(sub[0]) if not self.part_obj.Shape.isSame(ref.Shape): search_ele_in_shape_to_mesh = True elems = sub[1] if search_ele_in_shape_to_mesh: # Try to find the element in the Shape to mesh ele_shape = FemMeshTools.get_element( ref, elems ) # the method getElement(element) does not return Solid elements found_element = CfdTools.findElementInShape( self.part_obj.Shape, ele_shape) if found_element: elems = found_element else: FreeCAD.Console.PrintError( "One element of the meshregion " + mr_obj.Name + " could not be found in the Part to mesh. It will be ignored.\n" ) elems = None if elems: if elems not in self.ele_length_map: # self.ele_length_map[elems] = Units.Quantity(mr_obj.CharacteristicLength).Value mr_rellen = mr_obj.RelativeLength if mr_rellen > 1.0: mr_rellen = 1.0 FreeCAD.Console.PrintError( "The meshregion: " + mr_obj.Name + " should not use a relative length greater than unity.\n" ) elif mr_rellen < 0.01: mr_rellen = 0.01 # Relative length should not be less than 1/100 of base length FreeCAD.Console.PrintError( "The meshregion: " + mr_obj.Name + " should not use a relative length smaller than 0.01.\n" ) self.ele_length_map[ elems] = mr_rellen * self.clmax else: FreeCAD.Console.PrintError( "The element " + elems + " of the mesh refinement " + mr_obj.Name + " has been added to another mesh refinement.\n" ) else: FreeCAD.Console.PrintError( "The meshregion: " + mr_obj.Name + " is not used to create the mesh because the reference list is empty.\n" ) else: FreeCAD.Console.PrintError( "The meshregion: " + mr_obj.Name + " is not used to create the mesh because the CharacteristicLength is 0.0 mm.\n" ) for eleml in self.ele_length_map: ele_shape = FemMeshTools.get_element( self.part_obj, eleml ) # the method getElement(element) does not return Solid elements ele_vertexes = FemMeshTools.get_vertexes_by_element( self.part_obj.Shape, ele_shape) self.ele_node_map[eleml] = ele_vertexes else: cf_settings = self.cf_settings cf_settings['MeshRegions'] = {} cf_settings['BoundaryLayers'] = {} cf_settings['InternalRegions'] = {} snappy_settings = self.snappy_settings snappy_settings['MeshRegions'] = {} snappy_settings['InternalRegions'] = {} from collections import defaultdict ele_meshpatch_map = defaultdict(list) if not mr_objs: print(' No mesh refinement') else: print(' Mesh refinements - getting the elements') if "Boolean" in self.part_obj.Name: err = "Cartesian meshes should not be generated for boolean split compounds." FreeCAD.Console.PrintError(err + "\n") # Make list of list of all references for their corresponding mesh object bl_matched_faces = [] if self.mesh_obj.MeshUtility == 'cfMesh': region_face_lists = [] for mr_id, mr_obj in enumerate(mr_objs): region_face_lists.append([]) if mr_obj.NumberLayers > 1 and not mr_obj.Internal: refs = mr_obj.References for r in refs: region_face_lists[mr_id].append(r) CfdTools.cfdMessage("Matching refinement regions") bl_matched_faces = CfdTools.matchFacesToTargetShape( region_face_lists, self.mesh_obj.Part.Shape) for mr_id, mr_obj in enumerate(mr_objs): Internal = mr_obj.Internal if mr_obj.RelativeLength: # Store parameters per region mr_rellen = mr_obj.RelativeLength if mr_rellen > 1.0: mr_rellen = 1.0 FreeCAD.Console.PrintError( "The meshregion: {} should not use a relative length greater " "than unity.\n".format(mr_obj.Name)) elif mr_rellen < 0.001: mr_rellen = 0.001 # Relative length should not be less than 0.1% of base length FreeCAD.Console.PrintError( "The meshregion: {} should not use a relative length smaller " "than 0.001.\n".format(mr_obj.Name)) if not (self.mesh_obj.MeshUtility == 'snappyHexMesh' and mr_obj.Baffle): fid = open( os.path.join(self.triSurfaceDir, mr_obj.Name + '.stl'), 'w') snappy_mesh_region_list = [] patch_list = [] for (si, sub) in enumerate(mr_obj.References): shape = FreeCAD.ActiveDocument.getObject( sub[0]).Shape elem = sub[1] if self.mesh_obj.MeshUtility == 'snappyHexMesh' and mr_obj.Baffle: # Save baffle references or faces individually baffle = "{}{}{}".format( mr_obj.Name, sub[0], elem) fid = open( os.path.join(self.triSurfaceDir, baffle + ".stl"), 'w') snappy_mesh_region_list.append(baffle) if elem.startswith( 'Solid' ): # getElement doesn't work with solids for some reason elt = shape.Solids[int(elem.lstrip('Solid')) - 1] else: elt = shape.getElement(elem) if elt.ShapeType == 'Face' or elt.ShapeType == 'Solid': CfdTools.cfdMessage( "Triangulating part: {}:{} ...".format( FreeCAD.ActiveDocument.getObject( sub[0]).Label, sub[1])) facemesh = MeshPart.meshFromShape( elt, LinearDeflection=self.mesh_obj. STLLinearDeflection) CfdTools.cfdMessage(" writing to file\n") fid.write("solid {}{}{}\n".format( mr_obj.Name, sub[0], elem)) for face in facemesh.Facets: fid.write(" facet normal 0 0 0\n") fid.write(" outer loop\n") for i in range(3): p = [ i * self.scale for i in face.Points[i] ] fid.write( " vertex {} {} {}\n".format( p[0], p[1], p[2])) fid.write(" endloop\n") fid.write(" endfacet\n") fid.write("endsolid {}{}{}\n".format( mr_obj.Name, sub[0], elem)) if self.mesh_obj.MeshUtility == 'snappyHexMesh' and mr_obj.Baffle: fid.close() if not (self.mesh_obj.MeshUtility == 'snappyHexMesh' and mr_obj.Baffle): fid.close() if self.mesh_obj.MeshUtility == 'cfMesh' and mr_obj.NumberLayers > 1 and not Internal: for (i, mf) in enumerate(bl_matched_faces): for j in range(len(mf)): if mr_id == mf[j][0]: sfN = self.mesh_obj.ShapeFaceNames[i] ele_meshpatch_map[mr_obj.Name].append( sfN) patch_list.append(sfN) # Limit expansion ratio to greater than 1.0 and less than 1.2 expratio = mr_obj.ExpansionRatio expratio = min(1.2, max(1.0, expratio)) cf_settings['BoundaryLayers'][ self.mesh_obj. ShapeFaceNames[i]] = { 'NumberLayers': mr_obj.NumberLayers, 'ExpansionRatio': expratio, 'FirstLayerHeight': self.scale * Units.Quantity( mr_obj.FirstLayerHeight). Value } if self.mesh_obj.MeshUtility == 'cfMesh': if not Internal: cf_settings['MeshRegions'][mr_obj.Name] = { 'RelativeLength': mr_rellen * self.clmax * self.scale, 'RefinementThickness': self.scale * Units.Quantity( mr_obj.RefinementThickness).Value, } else: cf_settings['InternalRegions'][mr_obj.Name] = { 'RelativeLength': mr_rellen * self.clmax * self.scale } elif self.mesh_obj.MeshUtility == 'snappyHexMesh': refinement_level = CfdTools.relLenToRefinementLevel( mr_obj.RelativeLength) if not Internal: if not mr_obj.Baffle: snappy_mesh_region_list.append(mr_obj.Name) edge_level = CfdTools.relLenToRefinementLevel( mr_obj.RegionEdgeRefinement) for rL in range(len(snappy_mesh_region_list)): mrName = mr_obj.Name + snappy_mesh_region_list[ rL] snappy_settings['MeshRegions'][mrName] = { 'RegionName': snappy_mesh_region_list[rL], 'RefinementLevel': refinement_level, 'EdgeRefinementLevel': edge_level, 'MaxRefinementLevel': max(refinement_level, edge_level), 'Baffle': mr_obj.Baffle } else: snappy_settings['InternalRegions'][ mr_obj.Name] = { 'RefinementLevel': refinement_level } else: FreeCAD.Console.PrintError( "The meshregion: " + mr_obj.Name + " is not used to create the mesh because the " "CharacteristicLength is 0.0 mm or the reference list is empty.\n" )
def drill_translate(outstring, cmd, params): global DRILL_RETRACT_MODE global MOTION_MODE global CURRENT_X global CURRENT_Y global CURRENT_Z global UNITS global UNIT_FORMAT global UNIT_SPEED_FORMAT strFormat = '.' + str(PRECISION) + 'f' trBuff = "" if OUTPUT_COMMENTS: # Comment the original command outstring[0] = "(" + outstring[0] outstring[-1] = outstring[-1] + ")" trBuff += linenumber() + format_outstring(outstring) + "\n" # cycle conversion # currently only cycles in XY are provided (G17) # other plains ZX (G18) and YZ (G19) are not dealt with : Z drilling only. drill_X = Units.Quantity(params['X'], FreeCAD.Units.Length) drill_Y = Units.Quantity(params['Y'], FreeCAD.Units.Length) drill_Z = Units.Quantity(params['Z'], FreeCAD.Units.Length) RETRACT_Z = Units.Quantity(params['R'], FreeCAD.Units.Length) # R less than Z is error if RETRACT_Z < drill_Z: trBuff += linenumber() + "(drill cycle error: R less than Z )\n" return trBuff if MOTION_MODE == 'G91': # G91 relative movements drill_X += CURRENT_X drill_Y += CURRENT_Y drill_Z += CURRENT_Z RETRACT_Z += CURRENT_Z if DRILL_RETRACT_MODE == 'G98' and CURRENT_Z >= RETRACT_Z: RETRACT_Z = CURRENT_Z # get the other parameters drill_Speed = Units.Quantity(params['F'], FreeCAD.Units.Velocity) if cmd == 'G83': drill_Step = Units.Quantity(params['Q'], FreeCAD.Units.Length) a_bit = drill_Step * 0.05 # NIST 3.5.16.4 G83 Cycle: "current hole bottom, backed off a bit." elif cmd == 'G82': drill_DwellTime = params['P'] # wrap this block to ensure machine MOTION_MODE is restored in case of error try: if MOTION_MODE == 'G91': trBuff += linenumber( ) + "G90\n" # force absolute coordinates during cycles strG0_RETRACT_Z = 'G0 Z' + format( float(RETRACT_Z.getValueAs(UNIT_FORMAT)), strFormat) + "\n" strF_Drill_Speed = ' F' + format( float(drill_Speed.getValueAs(UNIT_SPEED_FORMAT)), '.2f') + "\n" # preliminary mouvement(s) if CURRENT_Z < RETRACT_Z: trBuff += linenumber() + strG0_RETRACT_Z trBuff += linenumber() + 'G0 X' + format( float(drill_X.getValueAs(UNIT_FORMAT)), strFormat) + ' Y' + format( float(drill_Y.getValueAs(UNIT_FORMAT)), strFormat) + "\n" if CURRENT_Z > RETRACT_Z: # trBuff += linenumber() + 'G0 Z' + format(float(CURRENT_Z.getValueAs(UNIT_FORMAT)), strFormat) + "\n" # not following NIST 3.5.16.1 Preliminary and In-Between Motion trBuff += linenumber() + strG0_RETRACT_Z last_Stop_Z = RETRACT_Z # drill moves if cmd in ('G81', 'G82'): trBuff += linenumber() + 'G1 Z' + format( float(drill_Z.getValueAs(UNIT_FORMAT)), strFormat) + strF_Drill_Speed # pause where applicable if cmd == 'G82': trBuff += linenumber() + 'G4 P' + str(drill_DwellTime) + "\n" trBuff += linenumber() + strG0_RETRACT_Z else: # 'G83' if params['Q'] != 0: while 1: if last_Stop_Z != RETRACT_Z: clearance_depth = last_Stop_Z + a_bit trBuff += linenumber() + 'G0 Z' + format( float(clearance_depth.getValueAs(UNIT_FORMAT)), strFormat) + "\n" next_Stop_Z = last_Stop_Z - drill_Step if next_Stop_Z > drill_Z: trBuff += linenumber() + 'G1 Z' + format( float(next_Stop_Z.getValueAs(UNIT_FORMAT)), strFormat) + strF_Drill_Speed trBuff += linenumber() + strG0_RETRACT_Z last_Stop_Z = next_Stop_Z else: trBuff += linenumber() + 'G1 Z' + format( float(drill_Z.getValueAs(UNIT_FORMAT)), strFormat) + strF_Drill_Speed trBuff += linenumber() + strG0_RETRACT_Z break except Exception as e: pass if MOTION_MODE == 'G91': trBuff += linenumber() + 'G91' # Restore if changed return trBuff
def areas(ship, n, draft=None, roll=Units.parseQuantity("0 deg"), trim=Units.parseQuantity("0 deg")): """Compute the ship transversal areas Position arguments: ship -- Ship object (see createShip) n -- Number of points to compute Keyword arguments: draft -- Ship draft (Design ship draft by default) roll -- Roll angle (0 degrees by default) trim -- Trim angle (0 degrees by default) Returned value: List of sections, each section contains 2 values, the x longitudinal coordinate, and the transversal area. If n < 2, an empty list will be returned. """ if n < 2: return [] if draft is None: draft = ship.Draft shape, _ = placeShipShape(ship.Shape.copy(), draft, roll, trim) shape = getUnderwaterSide(shape) # Sections distance computation bbox = shape.BoundBox xmin = bbox.XMin xmax = bbox.XMax dx = (xmax - xmin) / (n - 1.0) # Since we are computing the sections in the total length (not in the # length between perpendiculars), we can grant that the starting and # ending sections have null area areas = [(Units.Quantity(xmin, Units.Length), Units.Quantity(0.0, Units.Area))] # And since we just need to compute areas we will create boxes with its # front face at the desired transversal area position, computing the # common solid part, dividing it by faces, and getting only the desired # ones. App.Console.PrintMessage("Computing transversal areas...\n") App.Console.PrintMessage("Some Inventor representation errors can be" " shown, please ignore them.\n") for i in range(1, n - 1): App.Console.PrintMessage("{0} / {1}\n".format(i, n - 2)) x = xmin + i * dx try: f = Part.Face(shape.slice(Vector(1, 0, 0), x)) except Part.OCCError: msg = QtGui.QApplication.translate( "ship_console", "Part.OCCError: Transversal area computation failed", None) App.Console.PrintError(msg + '\n') areas.append((Units.Quantity(x, Units.Length), Units.Quantity(0.0, Units.Area))) continue # It is a valid face, so we can add this area areas.append( (Units.Quantity(x, Units.Length), Units.Quantity(f.Area, Units.Area))) # Last area is equal to zero (due to the total length usage) areas.append( (Units.Quantity(xmax, Units.Length), Units.Quantity(0.0, Units.Area))) App.Console.PrintMessage("Done!\n") return areas
def getClmax(self): return Units.Quantity(self.clmax, Units.Length)
def accept(self): if not self.ship: return False if self.running: return self.save() mw = self.getMainWindow() form = mw.findChild(QtGui.QWidget, "TaskPanel") form.trim = self.widget(QtGui.QLineEdit, "Trim") form.minDraft = self.widget(QtGui.QLineEdit, "MinDraft") form.maxDraft = self.widget(QtGui.QLineEdit, "MaxDraft") form.nDraft = self.widget(QtGui.QSpinBox, "NDraft") trim = Units.parseQuantity(Locale.fromString(form.trim.text())) min_draft = Units.parseQuantity(Locale.fromString(form.minDraft.text())) max_draft = Units.parseQuantity(Locale.fromString(form.maxDraft.text())) n_draft = form.nDraft.value() draft = min_draft drafts = [draft] dDraft = (max_draft - min_draft) / (n_draft - 1) for i in range(1, n_draft): draft = draft + dDraft drafts.append(draft) # Get external faces self.loop = QtCore.QEventLoop() self.timer = QtCore.QTimer() self.timer.setSingleShot(True) QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.loop, QtCore.SLOT("quit()")) self.running = True faces = self.externalFaces(self.ship.Shape) if not self.running: return False if len(faces) == 0: msg = QtGui.QApplication.translate( "ship_console", "Failure detecting external faces from the ship object", None) App.Console.PrintError(msg + '\n') return False faces = Part.makeShell(faces) # Get the hydrostatics msg = QtGui.QApplication.translate( "ship_console", "Computing hydrostatics", None) App.Console.PrintMessage(msg + '...\n') points = [] for i in range(len(drafts)): App.Console.PrintMessage("\t{} / {}\n".format(i + 1, len(drafts))) draft = drafts[i] point = Tools.Point(self.ship, faces, draft, trim) points.append(point) self.timer.start(0.0) self.loop.exec_() if(not self.running): break PlotAux.Plot(self.ship, trim, points) return True