class SimpleBlockMeshGenerator: def __init__(self, mesh_config: SimpleBlockMeshConfig, fragmentation_config: FragmentationConfig, execution_config: ExecutionConfig = ExecutionConfig()): self.mesh_config = mesh_config self.fragmentation_config = fragmentation_config self.exec_config = execution_config self.out_file = "system/blockMeshDict" if self.exec_config is not None: self.out_file = os.path.join(execution_config.execution_folder, self.out_file) self._profiler = Profiler(enable_profiler) def __del__(self): self._profiler.print_report() def create(self, custom_out_file=None): _logger.info("\n\n===== Run geometry generating") self._profiler.start("Geometry creating") self._print_configuration() self._calculate_points() self._calculate_fragmentation() self._calculate_boundary() text = self._format_text() file_to_write = self.out_file if custom_out_file != 0 and (custom_out_file is not None) and (len(custom_out_file)): file_to_write = custom_out_file if custom_out_file == 0: file_to_write = 0 self.save_geometry(text, file_to_write) self._profiler.stop("Geometry creating") _logger.info("===== End geometry generating\n\n") def generate(self): _logger.info("\n\n===== Run block mesh generating") self._profiler.start("Mesh generating") self.generate_mesh() self._profiler.stop("Mesh generating") # Just because there a lots of logs from blockMesh command _logger.info("===== End block mesh generating\n\n") def _print_configuration(self): _logger.log(LogLvl.LOG_INFO, "Generate mesh with size:") _logger.log( LogLvl.LOG_INFO, "width_mm: {}\theight_mm: {}\tlength_mm: {}".format( self.mesh_config.width_mm, self.mesh_config.height_mm, self.mesh_config.length_mm)) _logger.log(LogLvl.LOG_INFO, "Generate mesh with fragmentation:") _logger.log( LogLvl.LOG_INFO, "{:>25}{:>25}{:>25}\n".format( "width_fragmentation: " + str(self.fragmentation_config.width), "height_fragmentation: " + str(self.fragmentation_config.height), "length_fragmentation: " + str(self.fragmentation_config.length))) def _calculate_points(self): # TODO add check, that convertToMeters is 0.001 (m to mm) width_mm = self.mesh_config.width_mm height_mm = self.mesh_config.height_mm length_mm = self.mesh_config.length_mm p1 = [0, 0, 0] p2 = [width_mm, 0, 0] p3 = [width_mm, height_mm, 0] p4 = [0, height_mm, 0] p5 = [0, 0, length_mm] p6 = [width_mm, 0, length_mm] p7 = [width_mm, height_mm, length_mm] p8 = [0, height_mm, length_mm] arr = [p1, p2, p3, p4, p5, p6, p7, p8] self.points = "" for i in range(len(arr)): self.points += " ({} {} {})".format(arr[i][0], arr[i][1], arr[i][2]) if i + 1 != len(arr): self.points += "\n" def _calculate_fragmentation(self): mesh_elem_size_mm = self.fragmentation_config.elem_size_mm assert (self.mesh_config.width_mm >= mesh_elem_size_mm) assert (self.mesh_config.height_mm >= mesh_elem_size_mm) assert (self.mesh_config.length_mm >= mesh_elem_size_mm) # TODO enable dynamic fragmentation # length_fragmentation = int(float(length_mm) / mesh_elem_size_mm) # height_fragmentation = int(float(height_mm) / mesh_elem_size_mm) # width_fragmentation = int(float(width_mm) / mesh_elem_size_mm) # TODO try to use 6 2 1 (default in tutorial) # x - width, y - height, z - length self.fragmentation = " hex (0 1 2 3 4 5 6 7) ({} {} {}) simpleGrading (1.0 1.0 1.0)".format( self.fragmentation_config.width, self.fragmentation_config.height, self.fragmentation_config.length) def _calculate_boundary(self): _logger.error("Not implemented") def _format_text(self): t = Template(MESH_FILE_TEMPLATE) boundary = """ topSurface { type patch; faces ( (2 3 7 6) ); } bottomSurface { type patch; faces ( (0 1 5 4) ); } rearFixedEnd { type patch; faces ( (4 5 6 7) ); } frontTractionEnd { type patch; faces ( (0 1 2 3) ); } leftSurface { type patch; faces ( (0 3 7 4) ); } rightSurface { type patch; faces ( (1 2 6 5) ); }""" return t.substitute(points=self.points, fragmentation=self.fragmentation, boundary=boundary) @staticmethod def save_geometry(text, filename): if filename != 0: _logger.debug("Save file to: {}".format(filename)) f = open(filename, "w+") f.writelines(text) f.close() else: print(text) def generate_mesh(self): # FIXME temporary solution # FIXME Not check that is not None openfoam_folder = "OPENFOAM FOLDER NOT SPECIFIED" if self.exec_config is not None: if self.exec_config.openfoam_folder is not None: openfoam_folder = self.exec_config.openfoam_folder env_script = "ENV SCRIPT NOT SPECIFIED" if self.exec_config is not None: if self.exec_config.prepare_env_script is not None: env_script = self.exec_config.prepare_env_script # Make sure script have commented lines, where FOAM_INST_DIR is set prepare_call = "export FOAM_INST_DIR=" + openfoam_folder prepare_call += "; " prepare_call += ". " + env_script prepare_call += "; " prepare_call += "cd " + self.exec_config.execution_folder try: command = "{}; {}".format(prepare_call, "blockMesh") _logger.info(command) if _logger.log_lvl == LogLvl.LOG_DEBUG or print_mesh_stats: subprocess.call("{}".format(command), shell=True) else: subprocess.call("{} > /dev/null".format(command), shell=True) except OSError: raise OSError( "blockMesh not found. Please check that you have prepared OpenFOAM environment" )
class Executor: # openfoam_solver = "solidEquilibriumDisplacementFoamMod" openfoam_solver = "solidDisplacementFoamMod" def __init__(self, exec_conf: ExecutionConfig, mesh_conf, fragmentation_conf: FragmentationConfig): self._profiler = Profiler(enable_profiler) _logger.info("Solver: {}".format(self.openfoam_solver)) self.exec_config = exec_conf self.result_dir = exec_conf.output_dir files.create_directory(self.result_dir) self.results = {} result_file_geom_prefix = None if type(mesh_conf) is SimpleBlockMeshConfig: result_file_geom_prefix = "gw{}_gh{}_gl{}".format( mesh_conf.width_mm, mesh_conf.height_mm, mesh_conf.length_mm) self.geom_values = "{}\t{}\t{}".format( mesh_conf.width_mm, mesh_conf.height_mm, mesh_conf.length_mm) self.geom_titles = "Geometry width\tGeometry height\tGeometry length" else: result_file_geom_prefix = "" self.geom_values = "" self.geom_titles = "" for line_idx in range(len(mesh_conf.width_lines)): result_file_geom_prefix += "w{}={}_".format(line_idx, mesh_conf.width_lines[line_idx]) self.geom_titles += "{} {}\t".format("Width line", line_idx) self.geom_values += "{}\t".format(mesh_conf.width_lines[line_idx]) for line_idx in range(len(mesh_conf.height_distance)): result_file_geom_prefix += "h{}={}_".format(line_idx, mesh_conf.height_distance[line_idx]) self.geom_titles += "{} {}\t".format("Height line", line_idx) self.geom_values += "{}\t".format(mesh_conf.height_distance[line_idx]) result_file_geom_prefix += "l={}".format(mesh_conf.length) self.geom_titles += "{}".format("Length") self.geom_values += "{}".format(mesh_conf.length) _logger.debug(result_file_geom_prefix) _logger.debug(self.geom_values) fragmentation_options_line = "fw{}_fh{}_fl{}".format( fragmentation_conf.width, fragmentation_conf.height, fragmentation_conf.length) self.fragmentation_values = "{}\t{}\t{}".format( fragmentation_conf.width, fragmentation_conf.height, fragmentation_conf.length) result_file_name = "result_{}_{}.txt".format(result_file_geom_prefix, fragmentation_options_line) self.result_file = os.path.join(self.result_dir, result_file_name) self.parsed_name = datetime.datetime.now().strftime("%Y-%m-%d-%H.txt") def __del__(self): self._profiler.print_report() def run(self): _logger.info("\n\n===== Run calculation") self.__run_execution() _logger.info("===== End calculation\n\n") _logger.info("\n\n===== Run result parsing") self._profiler.start("Parse results") self.__parse_output_from_file("sigmaEq") self.__parse_output_from_file("D") self.__parse_output_from_file("Time") self._profiler.stop("Parse results") _logger.info("===== End result parsing\n\n") def __run_execution(self): self._profiler.start("Run solver") # FIXME no check if none prepare_call = "export FOAM_INST_DIR=" + self.exec_config.openfoam_folder prepare_call += "; " prepare_call += ". " + "$HOME/prog/OpenFOAM/OpenFOAM-dev/etc/bashrc_modified" prepare_call += "; " prepare_call += "cd " + self.exec_config.execution_folder try: subprocess.call(["{}; {} > {}".format(prepare_call, self.openfoam_solver, self.result_file)], shell=True) except OSError: _logger.error("{} not found.".format(self.openfoam_solver)) _logger.error("Please make sure you are using modified version of OpenFOAM and env is prepared") self._profiler.stop("Run solver") def __parse_time(self, text): found_exec = re.findall(r'ExecutionTime = (\d+.?\d*) s', text)[-1] found_clock = re.findall(r'ClockTime = (\d+.?\d*) s', text)[-1] exec_time = float(found_exec) clock_time = float(found_clock) # Save result to file file_parsed_name = "{}-{}".format("time", self.parsed_name) file_parsed_result = os.path.join(self.result_dir, file_parsed_name) formatted_result = "{geometry}\t{fragmentation}\t{exec_time}\t{clock_time}\n".format( geometry=self.geom_values, fragmentation=self.fragmentation_values, exec_time=exec_time, clock_time=clock_time) with open(file_parsed_result, "a") as log_file: if os.stat(file_parsed_result).st_size == 0: log_file.write("{}" "\tFragmentation width\tFragmentation height\tFragmentation length" "\t{}\t{}\n".format(self.geom_titles, "Execution time", "Clock time")) log_file.write(formatted_result) def __parse_output(self, param_to_parse, text): _logger.debug("Parse: {}".format(param_to_parse)) start_index = text.rfind("Max {} = ".format(param_to_parse)) len = text[start_index:].find('\n') _logger.debug(text[start_index:start_index + len]) # (.123 250000 1.00009e+06 160325 7.29635e-10 2.36271e+06 0 -2.40131e-45 5.12455e-06 2.01673e-06 1.18136e-05) p = re.compile(r'[-+]?[0-9]*\.?[0-9]+[eE]?[-+]?[0-9]*') values = p.findall(text[start_index:start_index + len]) _logger.debug("Found values: {}".format(values)) float_val = [] for val in values: float_val.append(float(val)) _logger.debug("Values as float: {}".format(float_val)) max_value = -1. # FIXME Workaround for cantilever beam to use D which is with -D flag if param_to_parse == "sigmaEq": max_value = max(float_val) elif param_to_parse == "D": max_value = abs(max(float_val, key=abs)) _logger.info("Max (Min) {}: {}".format(param_to_parse, max_value)) # Save to map self.results[param_to_parse] = max_value # Save result to file file_parsed_name = "{}-{}".format(param_to_parse, self.parsed_name) file_parsed_result = os.path.join(self.result_dir, file_parsed_name) formatted_result = "{geometry}\t{fragmentation}\t{value}\n".format(geometry=self.geom_values, fragmentation=self.fragmentation_values, value=max_value) with open(file_parsed_result, "a") as log_file: if os.stat(file_parsed_result).st_size == 0: log_file.write("{}" "\tFragmentation width\tFragmentation height\tFragmentation length" "\t{}\n".format(self.geom_titles, param_to_parse)) log_file.write(formatted_result) def __parse_output_from_file(self, param_to_parse): with open(self.result_file, 'rt') as file: contents = file.read() # TODO yeaah, custom call for "time"! if param_to_parse == "Time": self.__parse_time(contents) else: self.__parse_output(param_to_parse, contents) def get_results(self): return self.results