diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0f11b54d..cdce05bd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -49,7 +49,7 @@ jobs: - name: Install ngspice shell: powershell run: | - curl.exe -L -o ngspice.7z "https://downloads.sourceforge.net/project/ngspice/ng-spice-rework/45.2/ngspice-45.2_64.7z" + curl.exe -L -o ngspice.7z "https://sourceforge.net/projects/ngspice/files/ng-spice-rework/old-releases/45.2/ngspice-45.2_64.7z" 7z x ngspice.7z -o"C:\Program Files" @@ -79,9 +79,17 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - name: Install ngspice - run: | - brew install ngspice # for power electronics problems + # - name: Install ngspice + # run: | + # # for power electronics problems + # VERSION=45.2 + # brew tap-new local/oldies + # brew extract --version=$VERSION ngspice local/oldies + # # Fix outdated URL: + # sed -i -e "s|ng-spice-rework/|ng-spice-rework/old-releases/|" /opt/homebrew/Library/Taps/local/homebrew-oldies/Formula/ngspice@${VERSION}.rb + # brew install local/oldies/ngspice@$VERSION + # brew pin ngspice@$VERSION + # ngspice --version - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/engibench/problems/power_electronics/utils/ngspice.py b/engibench/problems/power_electronics/utils/ngspice.py index ddba6769..a76f5cc3 100644 --- a/engibench/problems/power_electronics/utils/ngspice.py +++ b/engibench/problems/power_electronics/utils/ngspice.py @@ -3,6 +3,7 @@ import os import platform import re +import shutil import subprocess MIN_SUPPORTED_VERSION: int = 42 # Major version number of ngspice @@ -28,27 +29,25 @@ def _get_ngspice_path(self) -> str: """Get the path to the ngspice executable based on the operating system. Returns: - The path to ngspice executable or None if not found + The path to the ngspice executable """ if self.system == "windows": # For Windows, use the bundled ngspice.exe - # Look for ngspice in Spice64 folder and common install locations + # Look for ngspice in PATH (e.g. Chocolatey), Spice64 folder and common install locations possible_paths = [ self.ngspice_windows_path, - "ngspice.exe", + shutil.which("ngspice"), os.path.normpath(os.path.join("C:/Program Files/Spice64/bin/ngspice.exe")), os.path.normpath(os.path.join("C:/Program Files (x86)/ngspice/bin/ngspice.exe")), ] - for path in possible_paths: - if path and os.path.exists(path): - ngspice_path: str | None = path - break - else: - ngspice_path = possible_paths[0] # Default to first path if none found - if ngspice_path is None or not os.path.exists(ngspice_path): + ngspice_path: str | None = next((p for p in possible_paths if p and os.path.exists(p)), None) + if ngspice_path is None: raise FileNotFoundError( - f"ngspice.exe not found at {ngspice_path}. You can download it from https://sourceforge.net/projects/ngspice/files/ng-spice-rework/45.2/. You can also see our GitHub Actions workflow (test.yml) for how to automatically install it." + "ngspice.exe not found. You can install it via Chocolatey " + "(`choco install ngspice`) or download it from " + "https://sourceforge.net/projects/ngspice/files/ng-spice-rework/. " + "You can also see our GitHub Actions workflow (test.yml) for how to automatically install it." ) return ngspice_path if self.system in ["darwin", "linux"]: @@ -108,18 +107,21 @@ def version(self) -> int: subprocess.CalledProcessError: If ngspice fails to run """ if self.system == "windows": + # Try finding the version from the docs folder (for SourceForge binary package) pattern_int = re.compile(r"ngspice-(\d+)-manual\.pdf") pattern_dec = re.compile(r"ngspice-(\d+\.\d+)-manual\.pdf") docs_path = os.path.normpath(os.path.join(os.path.dirname(self._ngspice_path), "../docs/")) - for filename in os.listdir(docs_path): - match_int = pattern_int.match(filename) - match_dec = pattern_dec.match(filename) - if match_int: - return int(match_int.group(1)) # Already returns just the major version - if match_dec: - return int(match_dec.group(1).split(".")[0]) # Return only the major version - raise NgSpiceManualNotFoundError + try: + for filename in os.listdir(docs_path): + match_int = pattern_int.match(filename) + match_dec = pattern_dec.match(filename) + if match_int: + return int(match_int.group(1)) # Already returns just the major version + if match_dec: + return int(match_dec.group(1).split(".")[0]) # Return only the major version + except OSError: + print(f"Could not read ngspice docs folder at {docs_path!r}, falling back to --version flag.") cmd = [self._ngspice_path, "--version"] result = subprocess.run(cmd, capture_output=True, text=True, check=True) diff --git a/tests/test_problem_implementations.py b/tests/test_problem_implementations.py index 6cdca799..575b634a 100644 --- a/tests/test_problem_implementations.py +++ b/tests/test_problem_implementations.py @@ -102,6 +102,8 @@ def test_python_problem_impl(problem_class: type[Problem]) -> None: """ if problem_class.container_id is not None and not sys.platform.startswith("linux"): pytest.skip(f"Skipping containerized problem {problem_class.__name__} on non-linux platform") + if problem_class.__module__.startswith("engibench.problems.power_electronics") and sys.platform == "darwin": + pytest.skip(f"Skipping {problem_class.__name__} on MacOs") print(f"Testing optimization and simulation for {problem_class.__name__}...") # Initialize problem and get a random design problem = problem_class(seed=1)