From e7e824804421d04a30dd7e500d76772ca4ad5a44 Mon Sep 17 00:00:00 2001 From: Stephen Nneji Date: Mon, 7 Sep 2026 13:55:51 +0100 Subject: [PATCH 1/2] Run MATLAB RAT directly --- rascal2/core/runner.py | 27 ++++++++++--- rascal2/dialogs/settings_dialog.py | 65 +++++++++++++++++++++--------- rascal2/ui/presenter.py | 10 ++++- 3 files changed, 76 insertions(+), 26 deletions(-) diff --git a/rascal2/core/runner.py b/rascal2/core/runner.py index cce96075..a8ca701d 100644 --- a/rascal2/core/runner.py +++ b/rascal2/core/runner.py @@ -1,5 +1,6 @@ """QObject for running rat.""" +from io import StringIO import os import sys from dataclasses import dataclass @@ -194,7 +195,12 @@ def init_matlab_engine(problem_definition, engine_ready, engine_output, msg_queu MATLAB engine future or Exception from MatlabHelper. """ engine_future = rat.wrappers.MatlabWrapper.loader - if engine_future is None and any([file["language"] == "matlab" for file in problem_definition.customFiles.files]): + files = ( + problem_definition["custom_files"] + if isinstance(problem_definition, dict) + else problem_definition.customFiles.files + ) + if engine_future is None and any([file["language"] == "matlab" for file in files]): if not engine_output: msg_queue.put(LogData(INFO, "Attempting to start Matlab...")) @@ -227,7 +233,7 @@ def is_empty_bayes_result(result): result : Union[ratapi.outputs.Results, ratapi.outputs.BayesResults] The calculation results. """ - return isinstance(result, rat.BayesResults) and result.chain.shape == (1, 2) + return isinstance(result, rat.BayesResults) and result.chain.size == 2 def run( @@ -272,9 +278,20 @@ def run( try: sys.path.append(working_dir) - engine_future = init_matlab_engine(problem_definition, engine_ready, engine_output, msg_queue) - problem_definition, output_results, bayes_results = rat.rat_core.RATMain(problem_definition, cpp_controls) - results = rat.outputs.make_results(procedure, output_results, bayes_results) + engine_future = init_matlab_engine(problem_definition, engine_ready, engine_output, queue) + + if isinstance(cpp_controls, dict): + ipc_path = cpp_controls.pop("ipc_path") + matlab_rat_path = cpp_controls.pop("matlab_rat_path") + problem, results = rat.matlab.run_matlab_directly( + problem_definition, cpp_controls, matlab_rat_path, ipc_path, stderr=StringIO(), stdout=StringIO() + ) + problem_definition = rat.inputs.make_problem(problem) + else: + problem_definition, output_results, bayes_results = rat.rat_core.RATMain( + problem_definition, cpp_controls + ) + results = rat.outputs.make_results(procedure, output_results, bayes_results) except Exception as err: queue.put(err) go_event.clear() diff --git a/rascal2/dialogs/settings_dialog.py b/rascal2/dialogs/settings_dialog.py index 1dead18a..a41537b0 100644 --- a/rascal2/dialogs/settings_dialog.py +++ b/rascal2/dialogs/settings_dialog.py @@ -7,8 +7,9 @@ from rascal2.config import LOGGER, SETTINGS, MatlabHelper from rascal2.paths import MATLAB_ARCH_FILE -from rascal2.settings import SettingsGroups, change_ui_style +from rascal2.settings import SettingsGroups, change_ui_style, get_global_settings from rascal2.theme import IconEngine + from rascal2.widgets.inputs import get_validated_input @@ -144,9 +145,8 @@ def __init__(self): form_layout.setVerticalSpacing(10) form_layout.setHorizontalSpacing(0) - label_layout = QtWidgets.QHBoxLayout() - label_layout.addWidget(QtWidgets.QLabel("Current Matlab Directory:")) - label_layout.addStretch(1) + matlab_dir_label = QtWidgets.QLabel("Current Matlab Directory:") + form_layout.addWidget(matlab_dir_label, 0, 0, 1, 6) self.matlab_path = QtWidgets.QLineEdit(self) self.matlab_path.setText(MatlabHelper().matlab_dir) self.matlab_path.setReadOnly(True) @@ -155,24 +155,39 @@ def __init__(self): browse_button = QtWidgets.QPushButton(QtGui.QIcon(IconEngine("browse-light.png")), "Browse") browse_button.clicked.connect(self.open_folder_selector) - form_layout.addWidget(self.matlab_path, 0, 0, 1, 4) - form_layout.addWidget(browse_button, 0, 4, 1, 1) + form_layout.addWidget(self.matlab_path, 1, 0, 1, 5) + form_layout.addWidget(browse_button, 1, 5, 1, 1) - main_layout = QtWidgets.QVBoxLayout() if not getattr(sys, "frozen", False): - self.setEnabled(False) - main_layout.addWidget( - QtWidgets.QLabel( - "The current matlab path can only be changed when running in bundle.
" - "For non-bundle, You can change which Matlab to use by pip installing a
" - "different version of matlabengine." - ) + browse_button.setEnabled(False) + desc_text = ( + "The current matlab path can only be changed when running in bundle.
" + "For non-bundle, You can change which Matlab to use by pip installing a " + "different version
of matlabengine.
" ) - main_layout.addLayout(label_layout) - main_layout.addLayout(form_layout) - main_layout.addStretch(1) - - self.setLayout(main_layout) + matlab_dir_label.setText(f"{matlab_dir_label.text()}
{desc_text}") + + desc_label = QtWidgets.QLabel( + "MATLAB RAT Directory (Optional):
" + "Running fully in MATLAB can provide more performance for custom files." + ) + form_layout.addWidget(desc_label, 3, 0, 1, 6) + self.rat_path = QtWidgets.QLineEdit(self) + self.rat_path.setText(get_global_settings().value("matlab_rat_path", "")) + self.rat_path.setReadOnly(True) + self.rat_path.setPlaceholderText("Select MATLAB RAT directory") + self.rat_path.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) + + browse_button = QtWidgets.QPushButton("Browse") + browse_button.clicked.connect(lambda: self.set_matlab_rat_dir(clear=False)) + clear_button = QtWidgets.QPushButton("Clear") + clear_button.clicked.connect(lambda: self.set_matlab_rat_dir(clear=True)) + form_layout.addWidget(self.rat_path, 4, 0, 1, 4) + form_layout.addWidget(browse_button, 4, 4, 1, 1) + form_layout.addWidget(clear_button, 4, 5, 1, 1) + form_layout.setRowStretch(5, 1) + + self.setLayout(form_layout) self.changed = False def open_folder_selector(self) -> None: @@ -187,6 +202,18 @@ def open_folder_selector(self) -> None: self.matlab_path.setText(folder_name) self.changed = True + def set_matlab_rat_dir(self, clear=False): + if clear: + self.rat_path.setText("") + get_global_settings().remove("matlab_rat_path") + else: + folder_name = QtWidgets.QFileDialog.getExistingDirectory( + self, "Select MATLAB Directory", self.rat_path.text() + ) + if folder_name: + self.rat_path.setText(folder_name) + get_global_settings().setValue("matlab_rat_path", folder_name) + def set_matlab_paths(self): """Update MATLAB paths in arch file.""" if not self.changed: diff --git a/rascal2/ui/presenter.py b/rascal2/ui/presenter.py index 276de54b..b6698abc 100644 --- a/rascal2/ui/presenter.py +++ b/rascal2/ui/presenter.py @@ -12,7 +12,7 @@ from rascal2.core.enums import UnsavedReply from rascal2.core.runner import LogData, RATRunner from rascal2.core.writer import write_result_to_zipped_csvs -from rascal2.settings import update_recent_projects +from rascal2.settings import update_recent_projects, get_global_settings from .model import InvalidResultWarning, MainWindowModel, validate_plot_data @@ -243,8 +243,14 @@ def run(self): self.model.controls.initialise_IPC() working_dir = os.getcwd() - rat_inputs = rat.inputs.make_input(self.model.project, self.model.controls) display_on = self.model.controls.display != rat.utils.enums.Display.Off + rat_inputs = rat.inputs.make_input(self.model.project, self.model.controls) + + matlab_rat_path = get_global_settings().value("matlab_rat_path", "") + if any([file.language == "matlab" for file in self.model.project.custom_files]) and matlab_rat_path: + # Run in MATLAB RAT + rat_inputs = self.model.project.to_dict(), self.model.controls.model_dump() + rat_inputs[1].update({"ipc_path": self.model.controls._IPCFilePath, "matlab_rat_path": matlab_rat_path}) self.runner.set_runner_args(rat_inputs, self.model.controls.procedure, display_on, working_dir) self.view.terminal_widget.write("Initializing RAT Process...") self.runner.start() From bfaf6ef5a20a55eeb0630b4be286912c33c5daac Mon Sep 17 00:00:00 2001 From: Stephen Nneji Date: Tue, 8 Sep 2026 09:48:19 +0100 Subject: [PATCH 2/2] Always run calculate with normal route --- rascal2/core/runner.py | 2 +- rascal2/dialogs/settings_dialog.py | 3 --- rascal2/ui/presenter.py | 11 ++++++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/rascal2/core/runner.py b/rascal2/core/runner.py index a8ca701d..199f44e9 100644 --- a/rascal2/core/runner.py +++ b/rascal2/core/runner.py @@ -1,9 +1,9 @@ """QObject for running rat.""" -from io import StringIO import os import sys from dataclasses import dataclass +from io import StringIO from logging import INFO from multiprocessing import Event, Process, Queue diff --git a/rascal2/dialogs/settings_dialog.py b/rascal2/dialogs/settings_dialog.py index a41537b0..36703431 100644 --- a/rascal2/dialogs/settings_dialog.py +++ b/rascal2/dialogs/settings_dialog.py @@ -9,7 +9,6 @@ from rascal2.paths import MATLAB_ARCH_FILE from rascal2.settings import SettingsGroups, change_ui_style, get_global_settings from rascal2.theme import IconEngine - from rascal2.widgets.inputs import get_validated_input @@ -39,8 +38,6 @@ def __init__(self, parent): self.tab_widget.addTab(SettingsTab(self, SettingsGroups.General), SettingsGroups.General) self.tab_widget.addTab(SettingsTab(self, SettingsGroups.Plotting), SettingsGroups.Plotting) self.tab_widget.addTab(self.matlab_tab, "Matlab") - self.tab_widget.setTabVisible(0, parent.presenter.model.save_path != "") - self.tab_widget.setTabVisible(1, parent.presenter.model.save_path != "") self.reset_button = QtWidgets.QPushButton("Reset to Defaults", self) self.reset_button.clicked.connect(self.reset_default_settings) diff --git a/rascal2/ui/presenter.py b/rascal2/ui/presenter.py index b6698abc..c36dcdb0 100644 --- a/rascal2/ui/presenter.py +++ b/rascal2/ui/presenter.py @@ -12,7 +12,7 @@ from rascal2.core.enums import UnsavedReply from rascal2.core.runner import LogData, RATRunner from rascal2.core.writer import write_result_to_zipped_csvs -from rascal2.settings import update_recent_projects, get_global_settings +from rascal2.settings import get_global_settings, update_recent_projects from .model import InvalidResultWarning, MainWindowModel, validate_plot_data @@ -245,13 +245,18 @@ def run(self): working_dir = os.getcwd() display_on = self.model.controls.display != rat.utils.enums.Display.Off rat_inputs = rat.inputs.make_input(self.model.project, self.model.controls) + procedure = self.model.controls.procedure matlab_rat_path = get_global_settings().value("matlab_rat_path", "") - if any([file.language == "matlab" for file in self.model.project.custom_files]) and matlab_rat_path: + if ( + procedure != rat.utils.enums.Procedures.Calculate + and any([file.language == "matlab" for file in self.model.project.custom_files]) + and matlab_rat_path + ): # Run in MATLAB RAT rat_inputs = self.model.project.to_dict(), self.model.controls.model_dump() rat_inputs[1].update({"ipc_path": self.model.controls._IPCFilePath, "matlab_rat_path": matlab_rat_path}) - self.runner.set_runner_args(rat_inputs, self.model.controls.procedure, display_on, working_dir) + self.runner.set_runner_args(rat_inputs, procedure, display_on, working_dir) self.view.terminal_widget.write("Initializing RAT Process...") self.runner.start()