def export_fenics_mesh(obj, meshfileString): if not (meshfileString[-4:] == ".xml" or meshfileString[-5:] == ".hdf5"): error = "Error: only xml or hdf5 mesh is supported by gmsh conversion" FreeCAD.Console.PrintError(error) return error meshfileStem = (meshfileString[:-4]) if isinstance(meshfileStem, (type(b"bytes type"),)): meshfileStem = meshfileStem.decode('utf8') gmsh = CaeMesherGmsh.CaeMesherGmsh(obj, CfdTools.getParentAnalysisObject(obj)) meshfile = gmsh.export_mesh(u"Gmsh MSH", meshfileStem + u".msh") if meshfile: msg = "Info: Mesh has been written to `{}` by Gmsh\n".format(meshfile) FreeCAD.Console.PrintMessage(msg) # once Fenics is installed, dolfin-convert should be in path #comandlist = [u'dolfin-convert', u'-i gmsh', unicode(meshfileStem) + u".msh", unicode(meshfileStem) + u".xml"] comandlist = ['dolfin-convert {}.msh {}.xml'.format(meshfileStem,meshfileStem)] # work only with shell = True # mixed str and unicode in comandlist cause error to run in subprocess error = _run_command(comandlist) if not os.path.exists(meshfileStem+"_facet_region.xml"): FreeCAD.Console.PrintWarning("Mesh boundary file `{}` not generated\n".format(meshfileStem+"_physical_region.xml")) if error: return error if meshfileString[-5:] == ".hdf5": raise NotImplementedError('') else: error = "Failed to write mesh file `{}` by Gmsh\n".format(meshfileString) FreeCAD.Console.PrintError(error) return error
def doubleClicked(self, vobj): if FreeCADGui.activeWorkbench().name() != 'CfdWorkbench': FreeCADGui.activateWorkbench("CfdWorkbench") doc = FreeCADGui.getDocument(vobj.Object.Document) # it should be possible to find the AnalysisObject although it is not a documentObjectGroup if not FemGui.getActiveAnalysis(): analysis_obj = CfdTools.getParentAnalysisObject(self.Object) if analysis_obj: FemGui.setActiveAnalysis(analysis_obj) else: FreeCAD.Console.PrintError( 'No Active Analysis is detected from solver object in the active Document!\n' ) if not doc.getInEdit(): if FemGui.getActiveAnalysis().Document is FreeCAD.ActiveDocument: if self.Object in FemGui.getActiveAnalysis().Group: doc.setEdit(vobj.Object.Name) else: FreeCAD.Console.PrintError( 'Activate the analysis this solver belongs to!\n') else: FreeCAD.Console.PrintError( 'Active Analysis is not in active Document!\n') else: FreeCAD.Console.PrintError( 'Active Task Dialog found! Please close this one first!\n') return True
def __init__(self, cart_mesh_obj): self.mesh_obj = cart_mesh_obj self.analysis = CfdTools.getParentAnalysisObject(self.mesh_obj) self.part_obj = self.mesh_obj.Part # Part to mesh self.scale = 0.001 # Scale mm to m # Default to 2 % of bounding box characteristic length self.clmax = Units.Quantity(self.mesh_obj.CharacteristicLengthMax).Value if self.clmax <= 0.0: shape = self.part_obj.Shape cl_bound_box = math.sqrt(shape.BoundBox.XLength**2 + shape.BoundBox.YLength**2 + shape.BoundBox.ZLength**2) self.clmax = 0.02*cl_bound_box # Always in internal format, i.e. mm # Only used by gmsh - what purpose? self.clmin = 0.0 self.dimension = self.mesh_obj.ElementDimension shape_face_names = [] for (i, f) in enumerate(self.part_obj.Shape.Faces): face_name = ("face{}".format(i)) shape_face_names.append(face_name) self.mesh_obj.ShapeFaceNames = shape_face_names self.cf_settings = {} self.snappy_settings = {} self.gmsh_settings = {} self.two_d_settings = {} self.error = False output_path = CfdTools.getOutputPath(self.analysis) self.getFilePaths(output_path)
def export(objectslist, fileString): "called when freecad exports a mesh file supprted by gmsh generation" if len(objectslist) != 1: FreeCAD.Console.PrintError( "This exporter can only export one object.\n") return obj = objectslist[0] if not obj.isDerivedFrom("Fem::FemMeshObject"): FreeCAD.Console.PrintError("No FEM mesh object selected.\n") return if not obj.Proxy.Type == 'FemMeshGmsh': FreeCAD.Console.PrintError( "Object selected is not a FemMeshGmsh type\n") return gmsh = CaeMesherGmsh.CaeMesherGmsh(obj, CfdTools.getParentAnalysisObject(obj)) if fileString != "": fileName, fileExtension = os.path.splitext(fileString) for k in CaeMesherGmsh.CaeMesherGmsh.output_format_suffix: if CaeMesherGmsh.CaeMesherGmsh.output_format_suffix[ k] == fileExtension.lower(): ret = gmsh.export_mesh(k, fileString) if not ret: FreeCAD.Console.PrintError( "Mesh is written to `{}` by Gmsh\n".format(ret)) return FreeCAD.Console.PrintError( "Export mesh format with suffix `{}` is not supported by Gmsh\n". format(fileExtension.lower()))
def doubleClicked(self, vobj): doc = FreeCADGui.getDocument(vobj.Object.Document) if not CfdTools.getActiveAnalysis(): analysis_obj = CfdTools.getParentAnalysisObject(self.Object) if analysis_obj: CfdTools.setActiveAnalysis(analysis_obj) else: CfdTools.cfdError('No parent analysis object detected') if not doc.getInEdit(): doc.setEdit(vobj.Object.Name) else: FreeCAD.Console.PrintError('Active Task Dialog found! Please close this one first!\n') return True
def setEdit(self, vobj, mode): analysis_object = CfdTools.getParentAnalysisObject(self.Object) if analysis_object is None: FreeCAD.Console.PrintError("Boundary must have a parent analysis object") return False # hide all meshes for o in FreeCAD.ActiveDocument.Objects: if o.isDerivedFrom("Fem::FemMeshObject"): o.ViewObject.hide() # show task panel taskd = _TaskPanelCfdFluidBoundary(self.Object) taskd.obj = vobj.Object FreeCADGui.Control.showDialog(taskd) return True
def setEdit(self, vobj, mode): analysis_object = CfdTools.getParentAnalysisObject(self.Object) if analysis_object is None: CfdTools.cfdError("No parent analysis object found") return False physics_model = CfdTools.getPhysicsModel(analysis_object) if not physics_model: CfdTools.cfdError("Analysis object must have a physics object") return False import _TaskPanelCfdFluidProperties taskd = _TaskPanelCfdFluidProperties.TaskPanelCfdFluidProperties(self.Object, physics_model) taskd.obj = vobj.Object FreeCADGui.Control.showDialog(taskd) return True
def writeMesh(self): import importlib importlib.reload(CfdMeshTools) self.console_message_cart = '' self.Start = time.time() self.Timer.start() # Re-initialise CfdMeshTools with new parameters self.store() FreeCADGui.addModule("CfdMeshTools") FreeCADGui.addModule("CfdTools") FreeCADGui.doCommand( "FreeCAD.ActiveDocument." + self.mesh_obj.Name + ".Proxy.cart_mesh = " "CfdMeshTools.CfdMeshTools(FreeCAD.ActiveDocument." + self.mesh_obj.Name + ")") FreeCADGui.doCommand("cart_mesh = FreeCAD.ActiveDocument." + self.mesh_obj.Name + ".Proxy.cart_mesh") cart_mesh = self.mesh_obj.Proxy.cart_mesh self.consoleMessage("Preparing meshing ...") try: QApplication.setOverrideCursor(Qt.WaitCursor) setQuantity(self.form.if_max, str(cart_mesh.getClmax())) print('Part to mesh:\n Name: ' + cart_mesh.part_obj.Name + ', Label: ' + cart_mesh.part_obj.Label + ', ShapeType: ' + cart_mesh.part_obj.Shape.ShapeType) print(' CharacteristicLengthMax: ' + str(cart_mesh.clmax)) analysis = CfdTools.getParentAnalysisObject(self.mesh_obj) FreeCADGui.doCommand( "cart_mesh.getFilePaths(CfdTools.getOutputPath(FreeCAD.ActiveDocument." + analysis.Name + "))") FreeCADGui.doCommand("cart_mesh.setupMeshCaseDir()") self.consoleMessage("Exporting mesh refinement data ...") FreeCADGui.doCommand("cart_mesh.processRefinements()" ) # Writes stls so need file structure FreeCADGui.doCommand("cart_mesh.processDimension()") FreeCADGui.doCommand("cart_mesh.writeMeshCase()") self.consoleMessage("Exporting the part surfaces ...") FreeCADGui.doCommand("cart_mesh.writePartFile()") self.consoleMessage("Mesh case written to {}".format( self.cart_mesh.meshCaseDir)) except Exception as ex: self.consoleMessage("Error " + type(ex).__name__ + ": " + str(ex), '#FF0000') raise finally: self.Timer.stop() QApplication.restoreOverrideCursor() self.updateUI()
def export_foam_mesh(obj, meshfileString, foamCaseFolder=None): # support only 3D gmsh = CaeMesherGmsh.CaeMesherGmsh(obj, CfdTools.getParentAnalysisObject(obj)) meshfile = gmsh.export_mesh(u"Gmsh MSH", meshfileString) if meshfile: msg = "Info: Mesh is not written to `{}` by Gmsh\n".format(meshfile) FreeCAD.Console.PrintMessage(msg) if not foamCaseFolder: comandlist = [u'gmshToFoam', u'-case', foamCaseFolder, meshfile] else: comandlist = [u'gmshToFoam', meshfile] return _run_command(comandlist) else: error = "Mesh is NOT written to `{}` by Gmsh\n".format(meshfileString) FreeCAD.Console.PrintError(error) return error
def __init__(self, solver): if solver and solver.isDerivedFrom("Fem::FemSolverObjectPython"): ## @var solver # solver of the analysis. Used to store the active solver and analysis parameters self.solver = solver else: raise TypeError( "FemSolver object is missing in constructing CfdRunnable object" ) self.analysis = CfdTools.getParentAnalysisObject(self.solver) if self.analysis: self.results_present = False self.result_object = None else: raise Exception('FEM: No active analysis found!')
def setEdit(self, vobj, mode): analysis_object = CfdTools.getParentAnalysisObject(self.Object) if analysis_object is None: CfdTools.cfdError("No parent analysis object found") return False physics_model, is_present = CfdTools.getPhysicsModel(analysis_object) if not is_present: CfdTools.cfdError("Analysis object must have a physics object") return False boundaries = CfdTools.getCfdBoundaryGroup(analysis_object) import _TaskPanelCfdInitialiseInternalFlowField taskd = _TaskPanelCfdInitialiseInternalFlowField._TaskPanelCfdInitialiseInternalFlowField( self.Object, physics_model, boundaries) taskd.obj = vobj.Object FreeCADGui.Control.showDialog(taskd) return True
def accept(self): changed_transient = False if self.obj.PhysicsModel['Time'] != self.physicsModel['Time']: changed_transient = True self.obj.PhysicsModel = self.physicsModel doc = FreeCADGui.getDocument(self.obj.Document) doc.resetEdit() FreeCADGui.doCommand( "\nphys = FreeCAD.ActiveDocument.{}.PhysicsModel".format( self.obj.Name)) FreeCADGui.doCommand("phys['Time'] = '{}'".format( self.physicsModel['Time'])) FreeCADGui.doCommand("phys['Flow'] = '{}'".format( self.physicsModel['Flow'])) FreeCADGui.doCommand("phys['Turbulence'] = '{}'".format( self.physicsModel['Turbulence'])) FreeCADGui.doCommand("phys['TurbulenceModel'] = '{}'".format( self.physicsModel['TurbulenceModel'])) FreeCADGui.doCommand("phys['Thermal'] = {}".format( self.physicsModel['Thermal'])) FreeCADGui.doCommand("phys['Gravity'] = {}".format( self.physicsModel['Gravity'])) FreeCADGui.doCommand( "FreeCAD.ActiveDocument.{}.PhysicsModel = phys".format( self.obj.Name)) if changed_transient: # TODO # For now, init the solver object's time values to sensible defaults for steady or transient # The user can then edit further a = CfdTools.getParentAnalysisObject(self.obj) if a: sol = CfdTools.getSolver(a) if sol: FreeCADGui.doCommand( "\nsol = FreeCAD.ActiveDocument.{}".format(sol.Name)) if self.obj.PhysicsModel['Time'] == 'Steady': FreeCADGui.doCommand("sol.EndTime = 1000\n" "sol.TimeStep = 1\n" "sol.WriteInterval = 100\n") else: FreeCADGui.doCommand("sol.EndTime = 1\n" "sol.TimeStep = 0.001\n" "sol.WriteInterval = 0.1\n")
def initialiseUponReload(self): if self.physicsModel['Time'] == 'Steady': self.form.radioButtonSteady.toggle() elif self.physicsModel['Time'] == 'Transient': self.form.radioButtonTransient.toggle() if self.physicsModel['Flow'] == 'Incompressible': self.form.radioButtonIncompressible.toggle() elif self.physicsModel['Flow'] == 'Compressible': self.form.radioButtonCompressible.toggle() if self.physicsModel['Turbulence'] == 'Laminar': self.form.turbulenceCheckBox.toggle() self.form.radioButtonLaminar.toggle() elif self.physicsModel['Turbulence'] == 'RANS': self.form.turbulenceCheckBox.toggle() self.form.radioButtonRANS.toggle() ti = CfdTools.indexOrDefault(RANS_MODELS, self.physicsModel.get('TurbulenceType'), 0) self.form.turbulenceComboBox.setCurrentIndex(ti) if self.physicsModel['Thermal'] == "Energy": self.form.thermalCheckBox.toggle() self.form.radioButtonEnergy.toggle() elif self.physicsModel['Thermal'] == "Buoyancy": self.form.thermalCheckBox.toggle() self.form.radioButtonBuoyancy.toggle() material_objs = CfdTools.getMaterials( CfdTools.getParentAnalysisObject(self.obj)) if len(material_objs) > 1: self.form.gravityFrame.setVisible(True) # Add if absent for backward file compatibility if not self.physicsModel.get('Gravity'): self.physicsModel['Gravity'] = { 'gx': 0.0, 'gy': -9.81, 'gz': 0.0 } gx = self.physicsModel['Gravity']['gx'] gy = self.physicsModel['Gravity']['gy'] gz = self.physicsModel['Gravity']['gz'] setInputFieldQuantity(self.form.gx, "{} m/s^2".format(gx)) setInputFieldQuantity(self.form.gy, "{} m/s^2".format(gy)) setInputFieldQuantity(self.form.gz, "{} m/s^2".format(gz))
def __init__(self, obj): self.obj = obj analysis_obj = CfdTools.getParentAnalysisObject(obj) solver_obj = CfdTools.getSolver(analysis_obj) material_objs = CfdTools.getMaterials(analysis_obj) self.boundaryWidget = CfdBoundaryWidget(obj, None, solver_obj, material_objs) # fill the table in each variable tab, saved from the previous setup, existing case if BC name, done in each widget # geometry selection widget, only face is needed as boundary for CFD # GeometryElementsSelection(ref, eltypes=[], multigeom=True) # allow_multiple_geom_types = multigeom self.selectionWidget = FemSelectionWidgets.GeometryElementsSelection( obj.References, ['Face'], False) # check references, has to be after initialization of selectionWidget try: self.selectionWidget.has_equal_references_shape_types( ) # boundarySelector has no such method except: print( '`selectionWidget.has_equal_references_shape_types()` is only available in FreeCAD 0.18+' ) #the magic to have two widgets in one taskpanel self.form = [self.selectionWidget, self.boundaryWidget] if True: # todo: check if solver is 'OpenFOAM' from CfdFoamTools import getVariableList solverSettings = CfdTools.getSolverSettings( solver_obj) # physical_model variable_list = getVariableList(solverSettings) # TODO: if boundary_settings is empty dict, default setting for each variable could be provided if "FoamBoundarySettings" in self.obj.PropertiesList and self.obj.FoamBoundarySettings: self.foam_boundary_conditions = self.obj.FoamBoundarySettings else: print("debug print: variable_list", variable_list) self.foam_boundary_conditions = { var: {} for var in variable_list } # {varible: bc_dict, ...} from FoamCaseBuilder.FoamBoundaryWidget import FoamBoundaryWidget s = {"variables": self.foam_boundary_conditions} self.foamWidget = FoamBoundaryWidget(s) self.form.append(self.foamWidget)
def setEdit(self, vobj, mode): analysis_object = CfdTools.getParentAnalysisObject(self.Object) if analysis_object is None: CfdTools.cfdError("Boundary must have a parent analysis object") return False physics_model, is_present = CfdTools.getPhysicsModel(analysis_object) if not is_present: CfdTools.cfdError("Analysis object must have a physics object") return False import _TaskPanelCfdFluidBoundary taskd = _TaskPanelCfdFluidBoundary.TaskPanelCfdFluidBoundary(self.Object, physics_model) for obj in FreeCAD.ActiveDocument.Objects: if obj.isDerivedFrom("Fem::FemMeshObject"): obj.ViewObject.hide() obj.Part.ViewObject.show() self.Object.ViewObject.show() taskd.obj = vobj.Object FreeCADGui.Control.showDialog(taskd) return True
def __init__(self, cart_mesh_obj): self.mesh_obj = cart_mesh_obj self.analysis = CfdTools.getParentAnalysisObject(self.mesh_obj) self.part_obj = self.mesh_obj.Part # Part to mesh self.scale = 0.001 # Scale mm to m # Default to 2 % of bounding box characteristic length self.clmax = Units.Quantity( self.mesh_obj.CharacteristicLengthMax).Value if self.clmax <= 0.0: shape = self.part_obj.Shape cl_bound_mag = math.sqrt(shape.BoundBox.XLength**2 + shape.BoundBox.YLength**2 + shape.BoundBox.ZLength**2) cl_bound_min = min( min(shape.BoundBox.XLength, shape.BoundBox.YLength), shape.BoundBox.ZLength) self.clmax = min( 0.02 * cl_bound_mag, 0.4 * cl_bound_min) # Always in internal format, i.e. mm # Only used by gmsh - what purpose? self.clmin = 0.0 self.dimension = self.mesh_obj.ElementDimension self.cf_settings = {} self.snappy_settings = {} self.gmsh_settings = {} self.two_d_settings = {} self.error = False output_path = CfdTools.getOutputPath(self.analysis) self.getFilePaths(output_path) # 2D array of list of faces (index into shape) in each patch, indexed by [bc_id+1][meshregion_id+1] self.patch_faces = [] # 2D array of names of each patch, indexed by [bc_id+1][meshregion_id+1] self.patch_names = []
def __init__(self, obj): self.obj = obj analysis_obj = CfdTools.getParentAnalysisObject(obj) solver_obj = CfdTools.getSolver(analysis_obj) material_objs = CfdTools.getMaterials(analysis_obj) from CfdBoundaryWidget import CfdBoundaryWidget self.boundaryWidget = CfdBoundaryWidget(obj, None, solver_obj, material_objs) # fill the table in each variable tab, saved from the previous setup, existing case if BC name, done in each widget # geometry selection widget, only face is needed as boundary self.selectionWidget = FemSelectionWidgets.BoundarySelector() self.selectionWidget.setReferences(obj.References) # check references, has to be after initialisation of selectionWidget try: self.selectionWidget.has_equal_references_shape_types() except: RuntimeError('this function only works for FreeCAD 0.18') #the magic to have two widgets in one taskpanel self.form = [self.selectionWidget, self.boundaryWidget] if True: # todo: check if solver is 'OpenFOAM' from CfdFoamTools import getVariableList solverSettings = CfdTools.getSolverSettings(solver_obj) # physical_model variable_list = getVariableList(solverSettings) # build a parameterTabWidget, with each tab has a tableView # TODO: if boundary_settings is empty dict, default setting for each variable could be provided if not self.obj.FoamBoundarySettings: self.foam_boundary_conditions = {'U': {"key": "value"}, 'p':{"key": "value"}} # {varible: bc_dict, ...} else: self.foam_boundary_conditions = self.obj.FoamBoundarySettings from FoamCaseBuilder.FoamBoundaryWidget import FoamBoundaryWidget self.foamWidget = FoamBoundaryWidget(self.foam_boundary_conditions) self.form.append(self.foamWidget)
def processDimension(self): """ Additional checking/processing for 2D vs 3D """ # 3D cfMesh and snappyHexMesh, and 2D by conversion, while in future cfMesh may support 2D directly if self.dimension != '3D' and self.dimension != '2D': FreeCAD.Console.PrintError( 'Invalid element dimension. Setting to 3D.') self.dimension = '3D' print(' ElementDimension: ' + self.dimension) # Check for 2D boundaries twoDPlanes = [] analysis_obj = CfdTools.getParentAnalysisObject(self.mesh_obj) if not analysis_obj: analysis_obj = CfdTools.getActiveAnalysis() if analysis_obj: boundaries = CfdTools.getCfdBoundaryGroup(analysis_obj) for b in boundaries: if b.BoundaryType == 'constraint' and \ b.BoundarySubType == 'twoDBoundingPlane': twoDPlanes.append(b.Name) if self.dimension == '2D': self.two_d_settings['ConvertTo2D'] = True if len(twoDPlanes) != 2: raise RuntimeError( "For 2D meshing, two separate, parallel, 2D bounding planes must be present as " "boundary conditions in the CFD analysis object.") doc_name = str(analysis_obj.Document.Name) fFObjName = twoDPlanes[0] bFObjName = twoDPlanes[1] frontObj = FreeCAD.getDocument(doc_name).getObject(fFObjName) backObj = FreeCAD.getDocument(doc_name).getObject(bFObjName) fShape = frontObj.Shape bShape = backObj.Shape if len(fShape.Faces) == 0 or len(bShape.Faces) == 0: raise RuntimeError("A 2D bounding plane is empty.") else: allFFacesPlanar = True allBFacesPlanar = True for faces in fShape.Faces: if not isinstance(faces.Surface, Part.Plane): allFFacesPlanar = False break for faces in bShape.Faces: if not isinstance(faces.Surface, Part.Plane): allBFacesPlanar = False break if allFFacesPlanar and allBFacesPlanar: A1 = fShape.Faces[0].Surface.Axis A1.multiply(1.0 / A1.Length) A2 = bShape.Faces[0].Surface.Axis A2.multiply(1.0 / A2.Length) if (A1 - A2).Length <= 1e-6 or (A1 + A2).Length <= 1e-6: if len(frontObj.Shape.Vertexes) == len(backObj.Shape.Vertexes) and \ len(frontObj.Shape.Vertexes) > 0 and \ abs(frontObj.Shape.Area) > 0 and \ abs(frontObj.Shape.Area - backObj.Shape.Area)/abs(frontObj.Shape.Area) < 1e-6: self.two_d_settings[ 'Distance'] = fShape.distToShape( bShape)[0] / 1000 else: raise RuntimeError( "2D bounding planes do not match up.") else: raise RuntimeError( "2D bounding planes are not aligned.") else: raise RuntimeError( "2D bounding planes need to be flat surfaces.") case = CfdCaseWriterFoam.CfdCaseWriterFoam(analysis_obj) case.settings = {} case.settings['createPatchesFromSnappyBaffles'] = False case.setupPatchNames() keys = list(case.settings['createPatches'].keys()) frontPatchIndex = keys.index(frontObj.Label) self.two_d_settings['FrontFaceList'] = case.settings[ 'createPatches'][keys[frontPatchIndex]]['PatchNamesList'] backPatchIndex = keys.index(backObj.Label) self.two_d_settings['BackFaceList'] = case.settings[ 'createPatches'][keys[backPatchIndex]]['PatchNamesList'] if not self.two_d_settings[ 'BackFaceList'] or not self.two_d_settings['FrontFaceList']: raise RuntimeError( "2D front and/or back plane(s) could not be found in the shape being meshed." ) self.two_d_settings['BackFace'] = self.two_d_settings[ 'BackFaceList'][0] else: self.two_d_settings['ConvertTo2D'] = False if len(twoDPlanes): raise RuntimeError( "2D bounding planes can not be used in 3D mesh")
def __init__(self, obj): FreeCADGui.Selection.clearSelection() self.sel_server = None self.obj = obj self.shapeListOrig = list(self.obj.shapeList) self.partNameList = list(self.obj.partNameList) self.partNameListOrig = list(self.obj.partNameList) self.form = FreeCADGui.PySideUic.loadUi(os.path.join(os.path.dirname(__file__), "TaskPanelCfdZone.ui")) self.form.selectReference.clicked.connect(self.selectReference) self.form.listWidget.itemPressed.connect(self.setSelection) self.form.pushButtonDelete.clicked.connect(self.deleteFeature) if self.obj.Name.startswith('PorousZone'): self.p = dict(self.obj.porousZoneProperties) self.form.stackedWidgetZoneType.setCurrentIndex(0) self.form.comboBoxCorrelation.currentIndexChanged.connect(self.comboBoxCorrelationChanged) self.form.e1x.textEdited.connect(self.e1Changed) self.form.e1y.textEdited.connect(self.e1Changed) self.form.e1z.textEdited.connect(self.e1Changed) self.form.e2x.textEdited.connect(self.e2Changed) self.form.e2y.textEdited.connect(self.e2Changed) self.form.e2z.textEdited.connect(self.e2Changed) self.form.e3x.textEdited.connect(self.e3Changed) self.form.e3y.textEdited.connect(self.e3Changed) self.form.e3z.textEdited.connect(self.e3Changed) self.form.e1x.editingFinished.connect(self.e1Done) self.form.e1y.editingFinished.connect(self.e1Done) self.form.e1z.editingFinished.connect(self.e1Done) self.form.e2x.editingFinished.connect(self.e2Done) self.form.e2y.editingFinished.connect(self.e2Done) self.form.e2z.editingFinished.connect(self.e2Done) self.form.e3x.editingFinished.connect(self.e3Done) self.form.e3y.editingFinished.connect(self.e3Done) self.form.e3z.editingFinished.connect(self.e3Done) self.lastEVectorChanged = 1 self.lastLastEVectorChanged = 2 self.form.comboAspectRatio.currentIndexChanged.connect(self.comboAspectRatioChanged) self.form.pushButtonDelete.setEnabled(False) self.form.comboBoxCorrelation.addItems(POROUS_CORRELATION_NAMES) self.form.comboAspectRatio.addItems(ASPECT_RATIO_NAMES) elif self.obj.Name.startswith('InitialisationZone'): self.p = dict(self.obj.initialisationZoneProperties) self.form.stackedWidgetZoneType.setCurrentIndex(1) self.form.comboFluid.currentIndexChanged.connect(self.comboFluidChanged) self.form.checkAlpha.stateChanged.connect(self.checkAlphaChanged) self.form.checkVelocity.stateChanged.connect(self.checkVelocityChanged) self.form.checkPressure.stateChanged.connect(self.checkPressureChanged) self.form.inputVolumeFraction.valueChanged.connect(self.inputVolumeFractionChanged) self.form.inputUx.valueChanged.connect(self.inputUxChanged) self.form.inputUy.valueChanged.connect(self.inputUyChanged) self.form.inputUz.valueChanged.connect(self.inputUzChanged) self.form.inputPressure.valueChanged.connect(self.inputPressureChanged) material_objs = CfdTools.getMaterials(CfdTools.getParentAnalysisObject(obj)) self.form.frameVolumeFraction.setVisible(len(material_objs) > 1) if len(material_objs) > 1: fluid_names = [m.Label for m in material_objs] self.form.comboFluid.addItems(fluid_names[:-1]) self.setInitialValues()
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 = FemGeomTools.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 = FemGeomTools.get_element( self.part_obj, eleml ) # the method getElement(element) does not return Solid elements ele_vertexes = FemGeomTools.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'] = {} # 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") 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) boundary_face_list = [] for bc_id, bc_obj in enumerate(bc_group): for ri, ref in enumerate(bc_obj.References): try: bf = CfdTools.resolveReference(ref) except RuntimeError as re: raise RuntimeError( "Error processing boundary condition {}: {}". format(bc_obj.Label, str(re))) boundary_face_list.append((bf, (bc_id, ref, ri))) # Match them up to faces in the main geometry bc_matched_faces = CfdTools.matchFaces(boundary_face_list, mesh_face_list) # Make list of all boundary layer mesh regions for cfMesh bl_matched_faces = [] if self.mesh_obj.MeshUtility == 'cfMesh': CfdTools.cfdMessage("Matching boundary layer regions\n") bl_face_list = [] for mr_id, mr_obj in enumerate(mr_objs): if mr_obj.NumberLayers > 1 and not mr_obj.Internal: for ri, r in enumerate(mr_obj.References): try: f = CfdTools.resolveReference(r) except RuntimeError as re: raise RuntimeError( "Error processing mesh refinement {}: {}". format(mr_obj.Label, str(re))) bl_face_list.append((f, (mr_id, r, ri))) # Match them up bl_matched_faces = CfdTools.matchFaces(bl_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, bref, ri = bc_matched_faces[k][0] nb2, bref2, ri2 = bc_matched_faces[prev_k][0] CfdTools.cfdMessage( "Boundary '{}' reference {}:{} also assigned as " "boundary '{}' reference {}:{} - ignoring duplicate\n". format(bc_group[nb].Label, bref[0], bref[1], bc_group[nb2].Label, bref2[0], bref2[1])) else: bc_match_per_shape_face[match] = k bl_match_per_shape_face = [-1] * len(mesh_face_list) for k in range(len(bl_matched_faces)): match = bl_matched_faces[k][1] prev_k = bl_match_per_shape_face[match] if prev_k >= 0: nr, ref, ri = bl_matched_faces[k][0] nr2, ref2, ri2 = bl_matched_faces[prev_k][0] CfdTools.cfdMessage( "Mesh refinement '{}' reference {}:{} also assigned as " "mesh refinement '{}' reference {}:{} - ignoring duplicate\n" .format(mr_objs[nr].Label, ref[0], ref[1], mr_objs[nr2].Label, ref2[0], ref2[1])) else: bl_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 = bl_match_per_shape_face[i] nb = -1 nr = -1 if k >= 0: nb, bref, bri = bc_matched_faces[k][0] if l >= 0: nr, ref, rri = bl_matched_faces[l][0] self.patch_faces[nb + 1][nr + 1].append(i) # Additionally for snappy, match baffles to any surface mesh refinements # as well as matching each surface mesh refinement region to boundary conditions mr_face_list = [] bc_mr_matched_faces = [] if self.mesh_obj.MeshUtility == 'snappyHexMesh': CfdTools.cfdMessage("Matching surface geometries\n") for mr_id, mr_obj in enumerate(mr_objs): if not mr_obj.Internal: for ri, r in enumerate(mr_obj.References): try: f = CfdTools.resolveReference(r) except RuntimeError as re: raise RuntimeError( "Error processing mesh refinement {}: {}". format(mr_obj.Label, str(re))) mr_face_list.append((f, (mr_id, r, ri))) # Match mesh regions to the boundary conditions, to identify boundary conditions on supplementary # geometry (including on baffles) bc_mr_matched_faces = CfdTools.matchFaces( boundary_face_list, mr_face_list) 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 = [-1] * len(bc_obj.References) for m in baffle_matches: mr_match_per_baffle_ref[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, mri in enumerate(mr_match_per_baffle_ref): baffle_patch_refs[mri + 1].append( bc_obj.References[ri]) # 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 {} ...". format(bc_obj.Label, ri)) facemesh = MeshPart.meshFromShape( shape, LinearDeflection=self.mesh_obj. STLLinearDeflection) CfdTools.cfdMessage(" writing to file\n") with open( os.path.join(self.triSurfaceDir, solid_name + '.stl'), 'w') as fid: CfdTools.writePatchToStl( solid_name, facemesh, fid, self.scale) 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 } mr_matched_faces = [] if self.mesh_obj.MeshUtility == 'snappyHexMesh': # Match mesh regions to the primary geometry mr_matched_faces = CfdTools.matchFaces(mr_face_list, mesh_face_list) 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)) # 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 = [-1] * len(mr_obj.References) for m in bc_matches: bc_match_per_mr_ref[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][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, bci in enumerate(bc_match_per_mr_ref): if bci > -2: mr_patch_refs[bci + 1].append(mr_obj.References[ri]) # 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]): 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 {} ..." .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: facemesh = MeshPart.meshFromShape( shape, LinearDeflection=self.mesh_obj. STLLinearDeflection) CfdTools.cfdMessage(" writing to file\n") with open( os.path.join(self.triSurfaceDir, mr_patch_name + '.stl'), 'w') as fid: CfdTools.writePatchToStl( mr_patch_name, facemesh, fid, self.scale) if self.mesh_obj.MeshUtility == 'cfMesh': if not Internal: cf_settings['MeshRegions'][mr_patch_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: 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, record matched boundary layer patches if self.mesh_obj.MeshUtility == 'cfMesh' and mr_obj.NumberLayers > 1 and not Internal: for k in range(len(self.patch_faces)): # 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.patch_names[k][mr_id]] = { 'NumberLayers': mr_obj.NumberLayers, 'ExpansionRatio': expratio, 'FirstLayerHeight': self.scale * Units.Quantity(mr_obj.FirstLayerHeight).Value }
def __init__(self, obj): FreeCADGui.Selection.clearSelection() self.sel_server = None self.obj = obj self.ReferencesOrig = list(self.obj.References) self.form = FreeCADGui.PySideUic.loadUi( os.path.join(os.path.dirname(__file__), "TaskPanelCfdZone.ui")) self.form.framePorousZone.setVisible(False) self.form.frameInitialisationZone.setVisible(False) self.alphas = {} if self.obj.Name.startswith('PorousZone'): self.form.framePorousZone.setVisible(True) self.form.comboBoxCorrelation.currentIndexChanged.connect( self.updateUI) self.form.e1x.textEdited.connect(self.e1Changed) self.form.e1y.textEdited.connect(self.e1Changed) self.form.e1z.textEdited.connect(self.e1Changed) self.form.e2x.textEdited.connect(self.e2Changed) self.form.e2y.textEdited.connect(self.e2Changed) self.form.e2z.textEdited.connect(self.e2Changed) self.form.e3x.textEdited.connect(self.e3Changed) self.form.e3y.textEdited.connect(self.e3Changed) self.form.e3z.textEdited.connect(self.e3Changed) self.form.e1x.editingFinished.connect(self.e1Done) self.form.e1y.editingFinished.connect(self.e1Done) self.form.e1z.editingFinished.connect(self.e1Done) self.form.e2x.editingFinished.connect(self.e2Done) self.form.e2y.editingFinished.connect(self.e2Done) self.form.e2z.editingFinished.connect(self.e2Done) self.form.e3x.editingFinished.connect(self.e3Done) self.form.e3y.editingFinished.connect(self.e3Done) self.form.e3z.editingFinished.connect(self.e3Done) self.lastEVectorChanged = 1 self.lastLastEVectorChanged = 2 self.form.comboAspectRatio.currentIndexChanged.connect( self.comboAspectRatioChanged) self.form.comboBoxCorrelation.addItems( CfdZone.POROUS_CORRELATION_NAMES) self.form.comboAspectRatio.addItems(CfdZone.ASPECT_RATIO_NAMES) elif self.obj.Name.startswith('InitialisationZone'): self.form.frameInitialisationZone.setVisible(True) self.form.comboFluid.currentIndexChanged.connect( self.comboFluidChanged) self.form.checkAlpha.stateChanged.connect(self.updateUI) self.form.checkVelocity.stateChanged.connect(self.updateUI) self.form.checkPressure.stateChanged.connect(self.updateUI) self.form.inputVolumeFraction.valueChanged.connect( self.inputVolumeFractionChanged) material_objs = CfdTools.getMaterials( CfdTools.getParentAnalysisObject(obj)) self.form.frameVolumeFraction.setVisible(len(material_objs) > 1) if len(material_objs) > 1: fluid_names = [m.Label for m in material_objs] self.form.comboFluid.addItems(fluid_names[:-1]) self.load() self.comboFluidChanged() self.updateUI() # Face list selection panel - modifies obj.References passed to it self.faceSelector = CfdFaceSelectWidget.CfdFaceSelectWidget( self.form.faceSelectWidget, self.obj, False, True)
def accept(self): doc = FreeCADGui.getDocument(self.obj.Document) doc.resetEdit() FreeCADGui.doCommand("\nobj = FreeCAD.ActiveDocument.{}".format( self.obj.Name)) changed_transient = False if self.form.radioButtonSteady.isChecked(): if self.obj.Time != 'Steady': changed_transient = True FreeCADGui.doCommand("obj.Time = 'Steady'") elif self.form.radioButtonTransient.isChecked(): if self.obj.Time != 'Transient': changed_transient = True FreeCADGui.doCommand("obj.Time = 'Transient'") if self.form.radioButtonSinglePhase.isChecked(): FreeCADGui.doCommand("obj.Phase = 'Single'") elif self.form.radioButtonFreeSurface.isChecked(): FreeCADGui.doCommand("obj.Phase = 'FreeSurface'") if self.form.radioButtonIncompressible.isChecked(): FreeCADGui.doCommand("obj.Flow = 'Incompressible'") FreeCADGui.doCommand("obj.Thermal = 'None'") elif self.form.radioButtonCompressible.isChecked(): if self.form.checkBoxHighMach.isChecked(): FreeCADGui.doCommand("obj.Flow = 'HighMachCompressible'") FreeCADGui.doCommand("obj.Thermal = 'Energy'") else: FreeCADGui.doCommand("obj.Flow = 'Compressible'") FreeCADGui.doCommand("obj.Thermal = 'None'") if self.form.viscousCheckBox.isChecked(): if self.form.radioButtonLaminar.isChecked(): FreeCADGui.doCommand("obj.Turbulence = 'Laminar'") elif self.form.radioButtonRANS.isChecked(): FreeCADGui.doCommand("obj.Turbulence = 'RANS'") FreeCADGui.doCommand("obj.TurbulenceModel = '{}'".format( self.form.turbulenceComboBox.currentText())) else: FreeCADGui.doCommand("obj.Turbulence = 'Inviscid'") #if self.form.radioButtonEnergy.isChecked(): # FreeCADGui.doCommand("obj.Thermal = 'Energy'") #elif self.form.radioButtonBuoyancy.isChecked(): # FreeCADGui.doCommand("obj.Thermal = 'Buoyancy'") FreeCADGui.doCommand("obj.gx = '{}'".format(self.form.gx.text())) FreeCADGui.doCommand("obj.gy = '{}'".format(self.form.gy.text())) FreeCADGui.doCommand("obj.gz = '{}'".format(self.form.gz.text())) if changed_transient: # TODO # For now, init the solver object's time values to sensible defaults for steady or transient # The user can then edit further a = CfdTools.getParentAnalysisObject(self.obj) if a: sol = CfdTools.getSolver(a) if sol: FreeCADGui.doCommand( "\nsol = FreeCAD.ActiveDocument.{}".format(sol.Name)) if self.obj.Time == 'Steady': FreeCADGui.doCommand("sol.EndTime = 1000\n" "sol.TimeStep = 1\n" "sol.WriteInterval = 100\n") else: FreeCADGui.doCommand("sol.EndTime = 1\n" "sol.TimeStep = 0.001\n" "sol.WriteInterval = 0.1\n")