def on_save_case(self, save_as=None):
        """ Defines what happens when save case button is clicked.
        Saves a freecad scene definition, and a dump of dsph data for the case."""
        if Case.the().was_not_saved() or save_as:
            save_name, _ = QtGui.QFileDialog.getSaveFileName(
                self, __("Save Case"),
                Case.the().info.last_used_directory)
            Case.the().info.update_last_used_directory(save_name)
        else:
            save_name = Case.the().path

        if not save_name:
            return

        Case.the().info.needs_to_run_gencase = True
        save_case(save_name, Case.the())
        save_current_freecad_document(Case.the().path)
        if Case.the().has_materials() and Case.the(
        ).execution_parameters.rigidalgorithm not in (2, 3):
            warning_dialog(
                __("Properties and Material information wasn't written. See details for more info."
                   ),
                __("The case being saved has some properties/materials defined in one of its MKs.\n"
                   "However, the solid-solid interaction on the execution parameters must be set to DEM or CHRONO for this feature to work."
                   ))
        self.need_refresh.emit()
Exemplo n.º 2
0
    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_nnparameters_button(self):
     """ Defines nnparameters button behaviour"""
     if Case.the().executable_paths.dsphysics.find('NNewtonian') == -1:
         warning_dialog(
             __("DualSPHysics executable is not set as Non-Newtonian!"))
     else:
         NonNewtonianDialog(parent=get_fc_main_window())
         self.accept()
 def on_lsparameters_button(self):
     """ Defines lsparameters button behaviour"""
     if Case.the().executable_paths.dsphysics.find('LiquidSediment') == -1:
         warning_dialog(
             __("DualSPHysics executable is not set as Liquid-Sediment!"))
     else:
         LiquidSedimentDialog(parent=get_fc_main_window())
         self.accept()
 def __init__(self, run_out, parent = None):
     super().__init__(parent = parent)
     
     self.setWindowTitle("Run summary")
     
     if os.path.isfile(run_out):
         self.setMinimumHeight(600)
         self.setMinimumWidth(600)
         self.layout = QtGui.QVBoxLayout()
         run_textbox = QtGui.QTextEdit()
         run_textbox.setReadOnly(True)
         self.layout.addWidget(run_textbox)
         file = open(run_out, 'r') 
         text = ''
         for line in file: 
             text += line
         run_textbox.setText(text)
         self.setLayout(self.layout)
         self.show()
     else:
         warning_dialog("First run simulation!")
Exemplo n.º 6
0
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
Exemplo n.º 7
0
 def compute_warnings(self) -> None:
     """ Checks the resulting output for a [Warnings] tab and adds a button to see them. """
     details_text: str = self.run_details_text.toPlainText()
     if "[WARNINGS]" not in details_text:
         return
     warning_list: list = details_text.split("[WARNINGS]\n")[1].split(
         "\n\n")[0].split("\n")
     self.run_group_label_completed.setText(
         "<b style='color: #ABA400'>{}</b>".format(
             __("Simulation completed with warnings.")))
     try:
         self.run_button_warnings.clicked.disconnect()
     except RuntimeError:  # If nothing is yet connected it will throw an exception.
         pass
     self.run_button_warnings.clicked.connect(lambda _=False, text=(
         LINE_END * 2).join(warning_list): warning_dialog(text))
     self.run_button_warnings.show()
    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()
    def __init__(self, parent=None):
        super().__init__(parent=parent)

        self.title = 'Non-Newtonian parameters'
        self.left = 0
        self.top = 0
        self.width = 300
        self.height = 200
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        self.fluids = self.get_used_mkfluids()
        fluid_labels = self.get_used_mkfluid_labels()

        if Case.the().info.is_gencase_done:

            self.fluid_phases_num = len(self.fluids)

            if self.fluid_phases_num == 2:

                self.execution_options_tab = ExecutionOptionsTab()

                self.execution_options_tab.rheology_treatment.setCurrentIndex(
                    int(Case.the().custom.nnparam.rheology_treatment))
                self.execution_options_tab.velocity_gradient_type.setCurrentIndex(
                    int(Case.the().custom.nnparam.velocity_gradient_type))
                self.execution_options_tab.relaxation_dt.setText(
                    Case.the().custom.nnparam.relaxation_dt)
                self.execution_options_tab.visco_treatment.setChecked(
                    Case.the().custom.nnparam.visco_treatment)

                self.nonnewtonian_parameters_tabwidget = QtGui.QTabWidget()
                self.nonnewtonian_parameters_tabwidget.addTab(
                    self.execution_options_tab, "Execution")

                self.phases_parameters_tab_list = []
                for current_phase in range(self.fluid_phases_num):
                    self.phases_parameters_tab_list.append(
                        PhaseParametersTab())
                    self.set_phase_parameters_tab_entries(current_phase)

                    self.nonnewtonian_parameters_tabwidget.addTab(
                        self.phases_parameters_tab_list[current_phase],
                        fluid_labels[current_phase])

                apply_button = QtGui.QPushButton('Apply')
                apply_button.clicked.connect(self.on_apply_button)
                apply_button.setToolTip('Save current settings')

                reset_button = QtGui.QPushButton('Reset')
                reset_button.clicked.connect(self.set_default_parameters)
                reset_button.setToolTip('Reset default settings')

                cancel_button = QtGui.QPushButton('Cancel')
                cancel_button.clicked.connect(self.close)
                cancel_button.setToolTip('Discard any unapplied change')

                self.layout = QtGui.QGridLayout(self)
                self.layout.addWidget(self.nonnewtonian_parameters_tabwidget,
                                      0, 0, 1, 3)
                self.layout.addWidget(apply_button, 1, 0, 1, 1)
                self.layout.addWidget(reset_button, 1, 1, 1, 1)
                self.layout.addWidget(cancel_button, 1, 2, 1, 1)
                self.setLayout(self.layout)

                self.setMinimumWidth(400)
                self.center()

                self.exec_()
            else:
                warning_dialog(
                    'Liquid-Sediment simulation requires exactly 2 fluid phases'
                )
        else:
            warning_dialog(
                'Need to run GenCase before setting liquid-sediment parameters'
            )
    def on_multilayeredmb_menu(self, action):
        """ Defines MLPiston menu behaviour"""
        # Get currently selected object
        try:
            selection = FreeCADGui.Selection.getSelection()[0]
        except IndexError:
            error_dialog(__("You must select an object"))
            return

        # Check if object is in the simulation
        if not Case.the().is_object_in_simulation(selection.Name):
            error_dialog(
                __("The selected object must be added to the simulation"))
            return

        # Check if it is fluid and warn the user.
        if Case.the().get_simulation_object(
                selection.Name).type == ObjectType.FLUID:
            error_dialog(
                __("You can't apply a piston movement to a fluid.\nPlease select a boundary and try again"
                   ))
            return

        # Get selection mk
        selection_obj = Case.the().get_simulation_object(selection.Name)
        selection_mk: int = selection_obj.obj_mk
        mk_properties: MKBasedProperties = Case.the().get_mk_based_properties(
            selection_obj.type, selection_mk)

        # Check that this mk has no other motions applied
        if mk_properties.has_movements():
            # MK has motions applied. Warn the user and delete them
            motion_delete_warning = ok_cancel_dialog(
                APP_NAME,
                __("This mk already has motions applied. Setting a Multi-layered piston will delete all of its movement. Are you sure?"
                   ))
            if motion_delete_warning == QtGui.QMessageBox.Cancel:
                return
            mk_properties.remove_all_movements()

        # 1D or 2D piston
        if __("1 Dimension") in action.text():
            if mk_properties.mlayerpiston and not isinstance(
                    mk_properties.mlayerpiston, MLPiston1D):
                overwrite_warn = ok_cancel_dialog(
                    APP_NAME,
                    __("You're about to overwrite a previous coupling movement for this mk. Are you sure?"
                       ))
                if overwrite_warn == QtGui.QMessageBox.Cancel:
                    return

            config_dialog = MLPiston1DConfigDialog(selection_mk,
                                                   mk_properties.mlayerpiston,
                                                   parent=get_fc_main_window())
            if config_dialog.result() == QtGui.QDialog.Accepted:
                warning_dialog(
                    __("All changes have been applied for mk = {}").format(
                        selection_mk))
            mk_properties.mlayerpiston = config_dialog.mlpiston1d

        if __("2 Dimensions") in action.text():
            # Check that there's no other multilayered piston for this mk
            if mk_properties.mlayerpiston and not isinstance(
                    mk_properties.mlayerpiston, MLPiston2D):
                overwrite_warn = ok_cancel_dialog(
                    APP_NAME,
                    __("You're about to overwrite a previous coupling movement for this mk. Are you sure?"
                       ))
                if overwrite_warn == QtGui.QMessageBox.Cancel:
                    return

            config_dialog = MLPiston2DConfigDialog(selection_mk,
                                                   mk_properties.mlayerpiston,
                                                   parent=get_fc_main_window())
            if config_dialog.result() == QtGui.QDialog.Accepted:
                warning_dialog(
                    __("All changes have been applied for mk = {}").format(
                        selection_mk))
            mk_properties.mlayerpiston = config_dialog.mlpiston2d

        self.accept()