def load_case(load_path: str) -> "Case": """ Loads a case from the given folder and returns its Case data. """ refocus_cwd() project_folder_path = path.dirname(load_path) freecad_document_file_path = path.abspath("{}/DSPH_Case.FCStd".format(project_folder_path)) if not path.isfile(freecad_document_file_path): error_dialog(__("DSPH_Case.FCStd file could not be found. Please check if the project was moved or the file was renamed.")) return None if document_count() and not prompt_close_all_documents(): return None FreeCAD.open(project_folder_path + "/DSPH_Case.FCStd") with open(load_path, "rb") as load_picklefile: try: loaded_data = pickle.load(load_picklefile) if not loaded_data.version: warning_dialog(__("The case data you're trying to load is older than version 0.6 and cannot be loaded.")) prompt_close_all_documents(prompt=False) return None if loaded_data.version < VERSION: warning_dialog(__("The case data you are loading is from a previous version ({}) of this software. They may be missing features or errors.").format(loaded_data.version)) elif loaded_data.version > VERSION: warning_dialog(__("You're loading a case data from a future version ({}) of this software. You should upgrade DesignSPHysics as they may be errors using this file.").format(loaded_data.version)) return loaded_data except AttributeError: error_dialog(__("There was an error opening the case. Case Data file seems to be corrupted.")) return None
def on_execute_gencase(self): """ Saves data into disk and uses GenCase to generate the case files.""" self.on_save_case() if not Case.the().executable_paths.gencase: warning_dialog(__("GenCase executable is not set.")) return gencase_full_path = path.abspath(Case.the().executable_paths.gencase) arguments = [ "{path}/{name}_Def".format(path=Case.the().path, name=Case.the().name), "{path}/{name}_out/{name}".format(path=Case.the().path, name=Case.the().name), "-save:+all" ] cmd_string = "{} {}".format(gencase_full_path, " ".join(arguments)) refocus_cwd() process = QtCore.QProcess(get_fc_main_window()) process.setWorkingDirectory(Case.the().path) ensure_process_is_executable_or_fail(gencase_full_path) process.start(gencase_full_path, arguments) debug("Executing -> {}".format(cmd_string)) process.waitForFinished() try: output = str(process.readAllStandardOutput().data(), encoding='utf-8') except UnicodeDecodeError: output = str(process.readAllStandardOutput().data(), encoding='latin1') if process.exitCode(): Case.the().info.is_gencase_done = False error_dialog( __("Error executing GenCase. Did you add objects to the case?. Another reason could be memory issues. View details for more info." ), output) else: try: total_particles_text = output[ output.index("Total particles: "):output.index(" (bound=")] total_particles = int( total_particles_text[total_particles_text.index(": ") + 2:]) Case.the().info.particle_number = total_particles GencaseCompletedDialog(particle_count=total_particles, detail_text=output, cmd_string=cmd_string, parent=get_fc_main_window()).show() Case.the().info.is_gencase_done = True self.on_save_case() Case.the().info.needs_to_run_gencase = False except ValueError: print_exc() Case.the().info.is_gencase_done = False Case.the().info.needs_to_run_gencase = True # Refresh widget enable/disable status as GenCase finishes self.gencase_completed.emit(Case.the().info.is_gencase_done)
def on_ex_simulate(self): """ Defines what happens on simulation button press. It shows the run window and starts a background process with dualsphysics running. Updates the window with useful info.""" refocus_cwd() if Case.the().info.needs_to_run_gencase: # Warning window about save_case warning_dialog("You should run GenCase again. Otherwise, the obtained results may not be as expected") static_params_exe = [Case.the().get_out_xml_file_path(), Case.the().get_out_folder_path(), "-{device}".format(device=self.device_selector.currentText().lower()), "-svres"] additional_parameters = list() if Case.the().info.run_additional_parameters: additional_parameters = Case.the().info.run_additional_parameters.split(" ") final_params_ex = static_params_exe + additional_parameters cmd_string = "{} {}".format(Case.the().executable_paths.dsphysics, " ".join(final_params_ex)) run_dialog = RunDialog(case_name=Case.the().name, processor=self.device_selector.currentText(), number_of_particles=Case.the().info.particle_number, cmd_string=cmd_string, parent=get_fc_main_window()) run_dialog.set_value(0) run_dialog.run_update(0, 0, None) Case.the().info.is_simulation_done = False run_fs_watcher = QtCore.QFileSystemWatcher() self.simulation_started.emit() # Cancel button handler def on_cancel(): log(__("Stopping simulation")) if process: process.kill() run_dialog.hide_all() Case.the().info.is_simulation_done = True self.simulation_cancelled.emit() run_dialog.cancelled.connect(on_cancel) # Launch simulation and watch filesystem to monitor simulation filelist = [f for f in os.listdir(Case.the().path + "/" + Case.the().name + "_out/") if f.startswith("Part")] for f in filelist: os.remove(Case.the().path + "/" + Case.the().name + "_out/" + f) def on_dsph_sim_finished(exit_code): """ Simulation finish handler. Defines what happens when the process finishes.""" # Reads output and completes the progress bar output = str(process.readAllStandardOutput().data(), encoding='utf-8') run_dialog.set_detail_text(str(output)) run_dialog.run_complete() run_fs_watcher.removePath(Case.the().path + "/" + Case.the().name + "_out/") if exit_code == 0: # Simulation went correctly Case.the().info.is_simulation_done = True Case.the().info.needs_to_run_gencase = False self.simulation_complete.emit(True) else: # In case of an error Case.the().info.needs_to_run_gencase = True if "exception" in str(output).lower(): log("There was an error on the execution. Opening an error dialog for that.") run_dialog.hide() self.simulation_complete.emit(False) error_dialog(__("An error occurred during execution. Make sure that parameters exist and are properly defined. " "You can also check your execution device (update the driver of your GPU). Read the details for more information."), str(output)) save_case(Case.the().path, Case.the()) # Launches a QProcess in background process = QtCore.QProcess(get_fc_main_window()) process.finished.connect(on_dsph_sim_finished) process.start(Case.the().executable_paths.dsphysics, final_params_ex) def on_fs_change(): """ Executed each time the filesystem changes. This updates the percentage of the simulation and its details.""" run_file_data = "" with open(Case.the().path + "/" + Case.the().name + "_out/Run.out", "r") as run_file: run_file_data = run_file.readlines() # Fill details window run_dialog.set_detail_text("".join(run_file_data)) # Set percentage scale based on timemax for l in run_file_data: if Case.the().execution_parameters.timemax == -1: if "TimeMax=" in l: Case.the().execution_parameters.timemax = float(l.split("=")[1]) current_value: float = 0.0 totalpartsout: int = 0 last_estimated_time = None # Update execution metrics last_part_lines = list(filter(lambda x: "Part_" in x and "stored" not in x and " " in x, run_file_data)) if last_part_lines: current_value = (float(last_part_lines[-1].split(None)[1]) * float(100)) / float(Case.the().execution_parameters.timemax) else: current_value = None # Update particles out last_particles_out_lines = list(filter(lambda x: "(total: " in x and "Particles out:" in x, run_file_data)) if last_particles_out_lines: totalpartsout = int(last_particles_out_lines[-1].split("(total: ")[1].split(")")[0]) try: last_estimated_time = str(" ".join(last_part_lines[-1].split(None)[-2:])) except IndexError: last_estimated_time = None # Update run dialog run_dialog.run_update(current_value, totalpartsout, last_estimated_time) # Set filesystem watcher to the out directory. run_fs_watcher.addPath(Case.the().path + "/" + Case.the().name + "_out/") run_fs_watcher.directoryChanged.connect(on_fs_change) # Handle error on simulation start if process.state() == QtCore.QProcess.NotRunning: # Probably error happened. run_fs_watcher.removePath(Case.the().path + "/" + Case.the().name + "_out/") process = None error_dialog("Error on simulation start. Check that the DualSPHysics executable is correctly set.") else: run_dialog.show()
except IOError: error("Unable to copy {} into {}".format(filename, save_name)) # Dumps all the case data to an XML file. XMLExporter().save_to_disk(save_name, case) case.version = VERSION # Save data array on disk. It is saved as a binary file with Pickle. try: with open(save_name + "/casedata.dsphdata", "wb") as picklefile: pickle.dump(case, picklefile, PICKLE_PROTOCOL) except Exception: print_exc() error_dialog(__("There was a problem saving the DSPH information file (casedata.dsphdata).")) refocus_cwd() def get_default_config_file(): """ Gets the default-config.json from disk """ current_script_folder = path.dirname(path.realpath(__file__)) with open("{}/../default-config.json".format(current_script_folder)) as data_file: loaded_data = json.load(data_file) if "win" in platform: to_ret = loaded_data["windows"] elif "linux" in platform: to_ret = loaded_data["linux"] return to_ret