From 488500ed831d7f77d31e0426e6241137fb6b1d46 Mon Sep 17 00:00:00 2001 From: Urvashi Date: Thu, 20 Mar 2025 14:35:42 +0530 Subject: [PATCH 1/2] Added user_config.py and modified solver.py &su2gui>py --- solver.py | 15 ++++-- su2gui.py | 135 ++++++++++++++++++++++++++++++++++++++++++------- user_config.py | 63 +++++++++++++++++++++++ 3 files changed, 193 insertions(+), 20 deletions(-) create mode 100644 user_config.py diff --git a/solver.py b/solver.py index 3d34548..8032fcb 100644 --- a/solver.py +++ b/solver.py @@ -245,13 +245,22 @@ def update_material(convergence_val, **kwargs): # start SU2 solver def su2_play(): - global proc_SU2 + # Use stored SU2_CFD path or fallback to "SU2_CFD" if not set + su2_cfd_path = getattr(state, "su2_cfd_path", None) + if not su2_cfd_path: + # Try to get from config as fallback + from user_config import get_su2_path + su2_cfd_path = get_su2_path() + if not su2_cfd_path: + log("error", "SU2_CFD path not configured. Please restart SU2GUI to configure the path.") + return + # every time we press the button we switch the state state.solver_running = not state.solver_running if state.solver_running: - log("info", "### SU2 solver started!") + log("info", f"### SU2 solver started using {su2_cfd_path}!") # change the solver button icon state.solver_icon="mdi-stop-circle" @@ -275,7 +284,7 @@ def su2_play(): # run SU2_CFD with config.cfg with open(BASE / "user" / state.case_name / "su2.out", "w") as outfile: with open(BASE / "user" / state.case_name / "su2.err", "w") as errfile: - proc_SU2 = subprocess.Popen(['SU2_CFD', state.filename_cfg_export], + proc_SU2 = subprocess.Popen([su2_cfd_path, state.filename_cfg_export], cwd= BASE / "user" / state.case_name, text=True, stdout=outfile, diff --git a/su2gui.py b/su2gui.py index faf2cb4..8ccfc22 100644 --- a/su2gui.py +++ b/su2gui.py @@ -33,7 +33,11 @@ # Logging funtions from logger import log, clear_logs , Error_dialog_card, Warn_dialog_card, logs_tab +# Config tab from config import * +# User configuration +from user_config import get_su2_path, set_su2_path, clear_config +import platform import vtk # vtm reader @@ -1504,26 +1508,94 @@ def standard_buttons(): # CLI # ----------------------------------------------------------------------------- -def check_su2(): - # Check if SU2 is installed by trying to locate the SU2_CFD command - su2_command = shutil.which("SU2_CFD") +def check_su2(path=None): + """Check if SU2 is installed and accessible, prompt for path if not.""" + + # First check if a path is provided as a parameter + if path: + executable = path + if platform.system() == "Windows" and not executable.lower().endswith('.exe'): + executable += ".exe" - if su2_command: - print("SU2_GUI is Able to access SU2_CFD.") + if os.path.isfile(executable): + try: + result = subprocess.run([executable, "--help"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + timeout=5) + if "SU2" in result.stdout or "SU2" in result.stderr: + print(f"Using provided SU2_CFD from: {path}") + set_su2_path(path) + return path + else: + print("The provided file does not appear to be SU2_CFD. Falling back to other methods.") + except (subprocess.SubprocessError, OSError): + print("Could not execute the provided file. Falling back to other methods.") + + # Try to get the path from config + su2_path = get_su2_path() + + # If path exists in config, check if it's valid + if su2_path: + executable = su2_path + if platform.system() == "Windows" and not executable.lower().endswith('.exe'): + executable += ".exe" + + if os.path.isfile(executable) and os.access(executable, os.X_OK): + print(f"Using SU2_CFD from stored configuration: {su2_path}") + return su2_path + + # Try to find SU2_CFD in PATH + su2_command = shutil.which("SU2_CFD") + + if su2_command: + print("SU2_GUI is able to access SU2_CFD from system PATH.") + # Store this path for future use + set_su2_path(su2_command) + return su2_command + + print("\nSU2 is not found in PATH or stored configuration.") + print("Please provide the path to the SU2_CFD executable.") + print("Example: /opt/su2/bin/SU2_CFD or C:\\Program Files\\SU2\\bin\\SU2_CFD.exe") + + while True: + # Get path from user + user_path = input("SU2_CFD path: ").strip() + + if not user_path: + response = input("Continue without SU2? (y/n): ").strip().lower() + if response != 'y': + print("Process aborted. Please install SU2 and try again.") + exit(1) + return None + + # Check if the path is valid + executable = user_path + if platform.system() == "Windows" and not executable.lower().endswith('.exe'): + executable += ".exe" + + if os.path.isfile(executable): + # Test if it's actually SU2_CFD by running it with --help + try: + result = subprocess.run([executable, "--help"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + timeout=5) + if "SU2" in result.stdout or "SU2" in result.stderr: + print(f"SU2_CFD found at: {user_path}") + # Store this path for future use + set_su2_path(user_path) + return user_path + else: + print("The file does not appear to be SU2_CFD. Please provide the correct path.") + except (subprocess.SubprocessError, OSError): + print("Could not execute the file. Please ensure it has execute permissions.") else: - print("\nSU2 is not installed.") - print("Please install SU2 from the following link:") - print("https://su2code.github.io/download/") - - # Prompt user to continue or exit - response = input("Would you like to continue without SU2? (y/n): ").strip().lower() - if response != 'y' or response != '': - exit("Process aborted. Please install SU2 and try again.") - + print("File not found. Please provide a valid path.") def main(): - # check if SU2 is installed - check_su2() # Argument parsing parser = argparse.ArgumentParser(description='Start the SU2 GUI application.') @@ -1532,13 +1604,36 @@ def main(): parser.add_argument('-m', '--mesh', type=str, help='Path to the SU2 mesh file in .su2 format.') parser.add_argument('--config', type=str, help='Path to the configuration file.') parser.add_argument('--restart', type=str, help='Path to the restart file in .csv/.dat format.') - + parser.add_argument('--su2', type=str, help='Path to the SU2_CFD executable. Overrides stored path.') + parser.add_argument('--clear-data', action='store_true', help='Clear all application data including saved configurations and cases.') + parser.add_argument('-v', '--version', action='store_true', help='Print the version of SU2GUI and exit.') + + args = parser.parse_args() mesh_path = args.mesh config_path = args.config restart_path = args.restart case = args.case + su2_path = args.su2 + clear_data = args.clear_data + version = args.version + + if version: + print(f"SU2GUI version 1.0.2") + exit(0) + + if clear_data: + clear_config() + print("All application data cleared.") + exit(0) + + # Check if SU2 is installed and get the path + su2_path = check_su2(su2_path) + + # Store su2_path for use in solver.py + state.su2_cfd_path = su2_path + if case: case_args(case) @@ -1595,6 +1690,12 @@ def main(): elif restart_path: log("error", f"The restart file {restart_path} does not exist, and was not loaded.") + # If --su2 argument is provided, update the stored path + if args.su2: + from user_config import set_su2_path + set_su2_path(args.su2) + state.su2_cfd_path = args.su2 + # Flush all states at once state.flush() diff --git a/user_config.py b/user_config.py new file mode 100644 index 0000000..4de20e6 --- /dev/null +++ b/user_config.py @@ -0,0 +1,63 @@ +""" +Configuration settings management for SU2GUI. +This module handles reading, writing, and validating user configuration. +""" + +import os +import json +import platform +from pathlib import Path + +def get_config_dir(): + """Return the platform-specific directory for configuration files.""" + if platform.system() == "Windows": + base_dir = os.getenv("APPDATA") + return Path(base_dir) / "su2gui" + else: # Unix-like (Linux, macOS) + return Path.home() / ".su2gui" + +def get_config_file(): + """Return the path to the configuration file.""" + return get_config_dir() / "UserConfig.json" + +def read_config(): + """Read and return the configuration dictionary.""" + config_file = get_config_file() + if not config_file.exists(): + return {} + + try: + with open(config_file, 'r') as f: + return json.load(f) + except (json.JSONDecodeError, IOError): + return {} + +def write_config(config): + """Write the configuration dictionary to file.""" + config_dir = get_config_dir() + config_file = get_config_file() + + # Ensure the directory exists + config_dir.mkdir(parents=True, exist_ok=True) + + with open(config_file, 'w') as f: + json.dump(config, f, indent=2) + +def clear_config(): + """Clear all stored configuration settings.""" + config_file = get_config_file() + if config_file.exists(): + config_file.unlink() + return True + return False + +def get_su2_path(): + """Get the path to SU2_CFD executable.""" + config = read_config() + return config.get('su2_cfd_path') + +def set_su2_path(path): + """Set the path to SU2_CFD executable.""" + config = read_config() + config['su2_cfd_path'] = path + write_config(config) From ca016d4bbb4d3deecf1b59cd383025cece3e4259 Mon Sep 17 00:00:00 2001 From: urvidev <163973810+urvidev@users.noreply.github.com> Date: Thu, 20 Mar 2025 15:19:08 +0530 Subject: [PATCH 2/2] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 87ec74e..7af0ed6 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Please note that SU2GUI is currently under development. We welcome any feedback ## Overview Here is an overview of how SU2GUI works: -![overview_su2gui](https://github.com/bigfooted/su2gui/assets/7050568/c4c8e171-dfeb-4b68-8950-8b4064cafe2e) +![overview_su2gui](./img/overview_su2gui.png) ### Getting Started #### Prerequisites