Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 12 additions & 3 deletions solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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,
Expand Down
135 changes: 118 additions & 17 deletions su2gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.')
Expand All @@ -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)
Expand Down Expand Up @@ -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()

Expand Down
63 changes: 63 additions & 0 deletions user_config.py
Original file line number Diff line number Diff line change
@@ -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)