def __init__(self, analysis_obj): self.case_folder = None self.mesh_file_name = None self.template_path = None self.analysis_obj = analysis_obj self.solver_obj = CfdTools.getSolver(analysis_obj) self.physics_model = CfdTools.getPhysicsModel(analysis_obj) self.mesh_obj = CfdTools.getMesh(analysis_obj) self.material_objs = CfdTools.getMaterials(analysis_obj) self.bc_group = CfdTools.getCfdBoundaryGroup(analysis_obj) self.initial_conditions = CfdTools.getInitialConditions(analysis_obj) self.reporting_functions = CfdTools.getReportingFunctionsGroup( analysis_obj) self.scalar_transport_objs = CfdTools.getScalarTransportFunctionsGroup( analysis_obj) self.porous_zone_objs = CfdTools.getPorousZoneObjects(analysis_obj) self.initialisation_zone_objs = CfdTools.getInitialisationZoneObjects( analysis_obj) self.zone_objs = CfdTools.getZoneObjects(analysis_obj) self.dynamic_mesh_refinement_obj = CfdTools.getDynamicMeshAdaptation( analysis_obj) self.mesh_generated = False self.working_dir = CfdTools.getOutputPath(self.analysis_obj) self.progressCallback = None self.settings = None
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.extrusion_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 = [] self.progressCallback = None
def __init__(self, solver_runner_obj): ui_path = os.path.join(CfdTools.getModulePath(), 'Gui', "TaskPanelCfdSolverControl.ui") self.form = FreeCADGui.PySideUic.loadUi(ui_path) self.analysis_object = CfdTools.getActiveAnalysis() self.solver_runner = solver_runner_obj self.solver_object = solver_runner_obj.solver # update UI self.console_message = '' self.solver_object.Proxy.solver_process = CfdConsoleProcess( finished_hook=self.solverFinished, stdout_hook=self.gotOutputLines, stderr_hook=self.gotErrorLines) self.Timer = QtCore.QTimer() self.Timer.setInterval(1000) self.Timer.timeout.connect(self.updateText) self.form.terminateSolver.clicked.connect(self.killSolverProcess) self.form.terminateSolver.setEnabled(False) self.open_paraview = QtCore.QProcess() self.working_dir = CfdTools.getOutputPath(self.analysis_object) self.updateUI() # Connect Signals and Slots self.form.pb_write_inp.clicked.connect(self.write_input_file_handler) self.form.pb_edit_inp.clicked.connect(self.editSolverInputFile) self.form.pb_run_solver.clicked.connect(self.runSolverProcess) self.form.pb_paraview.clicked.connect(self.openParaview) self.Start = time.time() self.Timer.start()
def test_new_analysis(self): fccPrint('--------------- Start of CFD tests ---------------') fccPrint('Checking CFD {} analysis ...'.format(self.__class__.__doc_name)) self.createNewAnalysis() self.assertTrue(self.analysis, "CfdTest of analysis failed") fccPrint('Checking CFD {} physics object ...'.format(self.__class__.__doc_name)) self.createNewPhysics() self.assertTrue(self.physics_object, "CfdTest of physics object failed") self.analysis.addObject(self.physics_object) fccPrint('Checking CFD {} initialise ...'.format(self.__class__.__doc_name)) self.createNewInitialise() self.assertTrue(self.initialise_object, "CfdTest of initialise failed") self.analysis.addObject(self.initialise_object) fccPrint('Checking CFD {} fluid property ...'.format(self.__class__.__doc_name)) self.createNewFluidProperty() self.assertTrue(self.material_object, "CfdTest of fluid property failed") self.analysis.addObject(self.material_object) fccPrint('Checking Cfd {} velocity inlet boundary ...'.format(self.__class__.__doc_name)) self.createInletBoundary() self.assertTrue(self.inlet_boundary, "CfdTest of inlet boundary failed") self.analysis.addObject(self.inlet_boundary) fccPrint('Checking Cfd {} velocity outlet boundary ...'.format(self.__class__.__doc_name)) self.createOutletBoundary() self.assertTrue(self.outlet_boundary, "CfdTest of outlet boundary failed") self.analysis.addObject(self.outlet_boundary) fccPrint('Checking Cfd {} wall boundary ...'.format(self.__class__.__doc_name)) self.createWallBoundary() self.assertTrue(self.wall_boundary, "CfdTest of wall boundary failed") self.analysis.addObject(self.wall_boundary) fccPrint('Checking Cfd {} slip boundary ...'.format(self.__class__.__doc_name)) self.createSlipBoundary() self.assertTrue(self.slip_boundary, "CfdTest of slip boundary failed") self.analysis.addObject(self.slip_boundary) fccPrint('Checking CFD {} mesh ...'.format(self.__class__.__doc_name)) self.createNewMesh('mesh') self.assertTrue(self.mesh_object, "CfdTest of mesh failed") self.analysis.addObject(self.mesh_object) fccPrint('Checking CFD {} solver ...'.format(self.__class__.__doc_name)) self.createNewSolver() self.assertTrue(self.solver_object, self.__class__.__doc_name + " of solver failed") self.analysis.addObject(self.solver_object) fccPrint('Writing {} case files ...'.format(self.__class__.__doc_name)) self.analysis.OutputPath = temp_dir self.solver_object.InputCaseName = "case" + self.__class__.__doc_name self.mesh_object.CaseName = "meshCase" + self.__class__.__doc_name self.writeCaseFiles() mesh_ref_dir = os.path.join(test_file_dir, "cases", self.__class__.__doc_name, "meshCase") mesh_case_dir = os.path.join(CfdTools.getOutputPath(self.analysis), self.mesh_object.CaseName) ref_dir = os.path.join(test_file_dir, "cases", self.__class__.__doc_name, "case") case_dir = os.path.join(CfdTools.getOutputPath(self.analysis), self.solver_object.InputCaseName) comparePaths(mesh_ref_dir, mesh_case_dir, self) comparePaths(ref_dir, case_dir, self) #shutil.rmtree(mesh_case_dir) #shutil.rmtree(case_dir) fccPrint('--------------- End of CFD tests ---------------')
def runSolverProcess(self): self.Start = time.time() # Check for changes that require remesh if FreeCAD.GuiUp and (self.analysis_object.NeedsMeshRewrite or self.analysis_object.NeedsCaseRewrite or self.analysis_object.NeedsMeshRerun): if self.analysis_object.NeedsCaseRewrite: if self.analysis_object.NeedsMeshRewrite or self.analysis_object.NeedsMeshRerun: text = "The case may need to be re-meshed and the case setup re-written based on changes " + \ "you have made to the model.\n\nRe-mesh and re-write case setup first?" else: text = "The case setup may need to be re-written based on changes " + \ "you have made to the model.\n\nRe-write case setup first?" else: if self.analysis_object.NeedsMeshRewrite or self.analysis_object.NeedsMeshRerun: text = "The case may need to be re-meshed based on changes " + \ "you have made to the model.\n\nRe-mesh case first?" if QtGui.QMessageBox.question(None, "CfdOF Workbench", text, defaultButton=QtGui.QMessageBox.Yes ) == QtGui.QMessageBox.Yes: self.Start = time.time() if self.analysis_object.NeedsMeshRewrite or self.analysis_object.NeedsMeshRerun: from CfdOF.Mesh import CfdMeshTools mesh_obj = CfdTools.getMeshObject(self.analysis_object) cart_mesh = CfdMeshTools.CfdMeshTools(mesh_obj) cart_mesh.progressCallback = self.consoleMessage if self.analysis_object.NeedsMeshRewrite: #Write mesh cart_mesh.writeMesh() self.analysis_object.NeedsMeshRewrite = False self.analysis_object.NeedsMeshRerun = True if self.analysis_object.NeedsCaseRewrite: self.write_input_file_handler() if self.analysis_object.NeedsMeshRerun: # Run mesher self.solver_object.Proxy.solver_process = CfdConsoleProcess( finished_hook=self.mesherFinished, stdout_hook=self.gotOutputLines, stderr_hook=self.gotErrorLines) cart_mesh.error = False cmd = CfdTools.makeRunCommand('./Allmesh', cart_mesh.meshCaseDir, source_env=False) FreeCAD.Console.PrintMessage('Executing: ' + ' '.join(cmd) + '\\n') env_vars = CfdTools.getRunEnvironment() self.solver_object.Proxy.solver_process.start( cmd, env_vars=env_vars) if self.solver_object.Proxy.solver_process.waitForStarted( ): # Setting solve button to inactive to ensure that two instances of the same simulation aren't started # simultaneously self.form.pb_write_inp.setEnabled(False) self.form.pb_run_solver.setEnabled(False) self.form.terminateSolver.setEnabled(True) self.consoleMessage("Mesher started ...") return else: self.Start = time.time() QApplication.setOverrideCursor(Qt.WaitCursor) FreeCADGui.doCommand("from CfdOF import CfdTools") FreeCADGui.doCommand("from CfdOF import CfdConsoleProcess") self.solver_object.Proxy.solver_runner = self.solver_runner FreeCADGui.doCommand("proxy = FreeCAD.ActiveDocument." + self.solver_object.Name + ".Proxy") # This is a workaround to emit code into macro without actually running it FreeCADGui.doCommand("proxy.running_from_macro = True") self.solver_object.Proxy.running_from_macro = False FreeCADGui.doCommand( "if proxy.running_from_macro:\n" + " analysis_object = FreeCAD.ActiveDocument." + self.analysis_object.Name + "\n" + " solver_object = FreeCAD.ActiveDocument." + self.solver_object.Name + "\n" + " working_dir = CfdTools.getOutputPath(analysis_object)\n" + " case_name = solver_object.InputCaseName\n" + " solver_directory = os.path.abspath(os.path.join(working_dir, case_name))\n" + " import CfdRunnableFoam\n" + " solver_runner = CfdRunnableFoam.CfdRunnableFoam(analysis_object, solver_object)\n" + " cmd = solver_runner.get_solver_cmd(solver_directory)\n" + " FreeCAD.Console.PrintMessage(' '.join(cmd) + '\\n')\n" + " env_vars = solver_runner.getRunEnvironment()\n" + " solver_process = CfdConsoleProcess.CfdConsoleProcess(stdout_hook=solver_runner.process_output)\n" + " solver_process.start(cmd, env_vars=env_vars)\n" + " solver_process.waitForFinished()\n") working_dir = CfdTools.getOutputPath(self.analysis_object) case_name = self.solver_object.InputCaseName solver_directory = os.path.abspath(os.path.join( working_dir, case_name)) cmd = self.solver_runner.get_solver_cmd(solver_directory) FreeCAD.Console.PrintMessage(' '.join(cmd) + '\\n') env_vars = self.solver_runner.getRunEnvironment() self.solver_object.Proxy.solver_process = CfdConsoleProcess( finished_hook=self.solverFinished, stdout_hook=self.gotOutputLines, stderr_hook=self.gotErrorLines) self.solver_object.Proxy.solver_process.start(cmd, env_vars=env_vars) if self.solver_object.Proxy.solver_process.waitForStarted(): # Setting solve button to inactive to ensure that two instances of the same simulation aren't started # simultaneously self.form.pb_write_inp.setEnabled(False) self.form.pb_run_solver.setEnabled(False) self.form.terminateSolver.setEnabled(True) self.form.pb_paraview.setEnabled(True) self.consoleMessage("Solver started") else: self.consoleMessage("Error starting solver") QApplication.restoreOverrideCursor()
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'])))