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
20 changes: 20 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: ci

on:
push:
branches: [main]
pull_request:

jobs:
test:
strategy:
fail-fast: false
matrix:
python: ["3.10", "3.12"]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
with:
python-version: ${{ matrix.python }}
- run: uv run --extra test pytest tests -q -m "not integration"
11 changes: 11 additions & 0 deletions .github/workflows/publish-wheel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: Publish wheel to R2

on:
push:
tags: ["v*"]
workflow_dispatch:

jobs:
publish:
uses: svd-ai-lab/sim-plugin-shared/.github/workflows/publish-wheel.yml@main
secrets: inherit
34 changes: 34 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: release

on:
push:
tags: ["v*"]

jobs:
build-and-release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
with:
python-version: "3.12"

- name: Verify tag matches pyproject.toml version
run: |
tag="${GITHUB_REF#refs/tags/v}"
ver=$(uv run --no-project python -c "import tomllib, pathlib; print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['version'])")
if [ "$tag" != "$ver" ]; then
echo "tag v$tag does not match pyproject version $ver" >&2
exit 1
fi

- run: uv build

- uses: softprops/action-gh-release@v2
with:
files: |
dist/*.whl
dist/*.tar.gz
generate_release_notes: true
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Changelog

## 0.1.1 - 2026-05-05

- Add runtime health, UI mode, project identity, and systems summary inspect targets.
- Enable GUI diagnostics for visible Workbench sessions and add bounded session execution.
- Add user-scenario README guidance, project review loops, and Workbench-to-Mechanical handoff guidance.
- Add CI, release publishing workflow, and stricter wheel-content checks.

## 0.1.0 - 2026-04-29

- Extract the Workbench driver and bundled skill into an out-of-tree sim plugin.
- Add protocol conformance and initial packaging smoke tests.
124 changes: 103 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,139 @@
# sim-plugin-workbench

[Ansys Workbench](https://www.ansys.com/products/ansys-workbench) (PyWorkbench / RunWB2) driver for [sim-cli](https://github.com/svd-ai-lab/sim-cli), distributed as an out-of-tree plugin via Python `entry_points`.
Use Codex, Claude Code, or another AI agent to orchestrate
[Ansys Workbench](https://www.ansys.com/products/ansys-workbench) projects from
the workflow you already use.

`sim-plugin-workbench` gives an agent practical Workbench control paths:
create and update analysis systems, run Workbench journals, inspect live session
health, summarize systems and cells, manage project checkpoints, and hand the
Model cell off to solver-specific plugins such as Mechanical.

The Workbench application and SDK are not bundled. Bring your own Workbench
installation. See [LICENSE-NOTICE.md](LICENSE-NOTICE.md).

## What an agent can do with Workbench

- Create or update Workbench analysis systems through journals.
- Keep Engineering Data, Geometry, and Model cells organized before solver work.
- Use a persistent PyWorkbench session when the SDK is available.
- Fall back to RunWB2 journal execution when the SDK path is unavailable.
- Inspect session health, UI capabilities, project identity, and system/cell
state before continuing a workflow.
- Prepare a clean handoff to Mechanical for setup, solve, and result extraction.

## Choose the right Workbench workflow

### 1. Persistent SDK session

Use this for repeatable agent-driven orchestration:

```powershell
sim connect --solver workbench --ui-mode gui
sim inspect session.health
sim exec --file step.wbjn
sim inspect workbench.systems.summary
```

The SDK path is preferred because it keeps state across steps and lets the
agent inspect Workbench state between bounded journal snippets.

### 2. RunWB2 fallback

Use this when the SDK is unavailable or incompatible:

```powershell
sim run --solver workbench path/to/journal.wbjn
```

RunWB2 is reliable for one-shot journals, but it should not be treated as a
rich live session. Inspect the JSON result written by the journal and save a
project checkpoint before handing off to another solver.

### 3. Workbench-to-Mechanical handoff

Use Workbench for cells 1-3: Engineering Data, Geometry, and Model. Use
Mechanical for cells 4-6: setup, solution, and results. Before handoff, inspect:

```powershell
sim inspect workbench.project.identity
sim inspect workbench.systems.summary
```

The Workbench side should have a project checkpoint and a refreshed Model cell.
The Mechanical side should confirm the expected analysis tree before adding
loads, supports, mesh controls, solve settings, or result objects.

## Install

```bash
pip install git+https://github.com/svd-ai-lab/sim-plugin-workbench@main
pip install sim-plugin-workbench
```

You also need a working Ansys Workbench installation on the same host. The driver detects via `AWP_ROOTxxx` env vars, `PATH` (`RunWB2`), and default install dirs. See [LICENSE-NOTICE.md](LICENSE-NOTICE.md).
You can also install through sim-cli:

```bash
sim plugin install sim-plugin-workbench
```

After install, sim-cli auto-discovers the driver:
After installation, sim-cli auto-discovers the driver and bundled skill:

```bash
sim drivers | grep workbench
sim check workbench
sim run --solver workbench path/to/journal.wbjn
```

## How it works
## Agent quickstart

The plugin registers via two entry-point groups:
Give an agent this instruction when the task is about Workbench:

```toml
[project.entry-points."sim.drivers"]
workbench = "sim_plugin_workbench:WorkbenchDriver"
```text
Use the bundled Workbench skill from sim-plugin-workbench. First identify
whether the task needs a persistent SDK session, a RunWB2 one-shot journal, or
a Workbench-to-Mechanical handoff. For persistent work, connect with
`sim connect --solver workbench --ui-mode gui`, then inspect `session.health`,
`workbench.project.identity`, and `workbench.systems.summary`. Execute one
bounded journal step at a time, inspect the result, and save or update a
project checkpoint before handing the Model cell to Mechanical.
```

[project.entry-points."sim.skills"]
workbench = "sim_plugin_workbench:skills_dir"
The bundled skill entry point is:

```text
src/sim_plugin_workbench/_skills/workbench/SKILL.md
```

`sim.drivers` exposes the driver class; `sim.skills` exposes a directory of skill files bundled inside the wheel.
## How it relates to sim-cli

The driver is dual-mode:
`sim-plugin-workbench` extends [sim-cli](https://github.com/svd-ai-lab/sim-cli).
sim-cli provides the common agent runtime surface (`connect`, `exec`, `inspect`,
`run`, `screenshot`), while this plugin supplies Workbench detection, journal
execution, persistent session handling, and bundled Workbench agent guidance.

1. **PyWorkbench SDK** (`ansys-workbench-core`) — preferred, persistent gRPC session.
2. **RunWB2 batch** (`RunWB2 -B -R journal.wbjn`) — automatic fallback when SDK is unavailable or its `launch_workbench` fails.
The plugin registers three entry-point groups:

IronPython journals write JSON results to `%TEMP%/sim_wb_result.json` because RunWB2 does not pipe stdout.
```toml
[project.entry-points."sim.drivers"]
workbench = "sim_plugin_workbench:WorkbenchDriver"

## Supported versions
[project.entry-points."sim.skills"]
workbench = "sim_plugin_workbench:skills_dir"

See [`src/sim_plugin_workbench/compatibility.yaml`](src/sim_plugin_workbench/compatibility.yaml) for the SDK / solver matrix. Profiles cover Ansys 24.1, 24.2, 25.1, 25.2 against `ansys-workbench-core` 0.4.x through 0.13.x.
[project.entry-points."sim.plugins"]
workbench = "sim_plugin_workbench:plugin_info"
```

## Develop

```bash
git clone https://github.com/svd-ai-lab/sim-plugin-workbench
cd sim-plugin-workbench
uv sync
uv run pytest
uv run pytest tests -m "not integration"
```

End-to-end (`@pytest.mark.integration`) tests require a real Workbench install and are skipped otherwise.
End-to-end tests require a local Workbench installation and are skipped unless
their prerequisites are available.

## License

Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "sim-plugin-workbench"
version = "0.1.0"
version = "0.1.1"
description = "Ansys Workbench (PyWorkbench) driver for sim-cli, distributed as an out-of-tree plugin"
readme = "README.md"
license = "Apache-2.0"
Expand Down Expand Up @@ -47,7 +47,9 @@ workbench = "sim_plugin_workbench:plugin_info"

[project.urls]
Homepage = "https://github.com/svd-ai-lab/sim-plugin-workbench"
Repository = "https://github.com/svd-ai-lab/sim-plugin-workbench"
Issues = "https://github.com/svd-ai-lab/sim-plugin-workbench/issues"
Changelog = "https://github.com/svd-ai-lab/sim-plugin-workbench/blob/main/CHANGELOG.md"

[tool.hatch.build.targets.wheel]
packages = ["src/sim_plugin_workbench"]
Expand Down
24 changes: 15 additions & 9 deletions src/sim_plugin_workbench/_skills/workbench/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ Concepts, API patterns, and version-agnostic reference:
| `base/snippets/` | Numbered IronPython journal snippets (01 through 05). Each writes results to `%TEMP%/sim_wb_result.json`. |
| `base/examples/` | Official PyWorkbench examples from pyansys.com — Fluent workflow, PyMechanical integration, logging, cooled turbine blade, cyclic symmetry, axisymmetric rotor, material designer. |
| `base/workflows/static_structural/` | **6-step Static Structural walkthrough** (Engineering Data → Geometry → Model → Setup → Solution → Results). Per-cell API reference + gotchas + executable `walk_workflow.py`. |
| `base/workflows/project_review_loop.md` | Required checkpoint loop: inspect health, project identity, systems summary, then run one bounded journal step. |
| `base/workflows/debug_failed_exec.md` | Failure triage loop for failed Workbench snippets and RunWB2 journals. |
| `base/workflows/mechanical_handoff.md` | Workbench-to-Mechanical checklist for Static Structural workflows. |
| `base/known_issues.md` | Vendor quirks, SDK version constraints, IronPython limitations. |

## sdk/<active_sdk_layer>/ — PyWorkbench SDK specifics
Expand Down Expand Up @@ -71,17 +74,20 @@ Concepts, API patterns, and version-agnostic reference:
acceptance criteria — if missing, ask the user.
5. **Acceptance ≠ exit code.** Validate against physics-based criteria
(e.g., component count, temperature range), not just exit code.
6. **Workbench owns cells 1-3.** Engineering Data, Geometry, and Model
orchestration belongs here. Mechanical setup, solve, and results belong to
`solver=mechanical`.

---

## Required protocol (one paragraph)

After `/connect` succeeds: read `base/reference/pyworkbench_api.md` and
`base/reference/journal_scripting.md` to understand the execution model.
Gather Category A inputs from the user. Execute IronPython journals
incrementally via `sim exec`, checking `last.result` after every step.
Use snippets from `base/snippets/` adapted to the user's task. After the
final step, evaluate against the user's acceptance criteria. For the
canonical smoke test, the snippet sequence is `01_smoke_test` →
`02_create_static_structural`, followed by verifying 6 standard
components exist.
After `/connect` succeeds: read `base/reference/pyworkbench_api.md`,
`base/reference/journal_scripting.md`, and
`base/workflows/project_review_loop.md`. Inspect `session.health`,
`workbench.project.identity`, and `workbench.systems.summary`. Execute
IronPython journals incrementally via `sim exec`, checking `last.result` and
`workbench.systems.summary` after every step. Use snippets from
`base/snippets/` adapted to the user's task. Before handoff, read
`base/workflows/mechanical_handoff.md` and confirm Workbench has a refreshed
Model cell. Mechanical owns setup, solve, and result extraction.
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ mechanical = connect_to_mechanical(ip='localhost', port=server_port)
print(mechanical.project_directory)
```

Output: `C:\Users\ansys\AppData\Local\Temp\WB_ansys_19576_2\wbnew_files\`
Output: `<temporary Workbench project files directory>`

## Analysis Execution

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ print(mechanical.project_directory)

Output:
```
C:\Users\ansys\AppData\Local\Temp\WB_ansys_8664_2\wbnew_files\
<temporary Workbench project files directory>
```

## Running Solver Script
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ systems. Use the Fluent solver API for mesh and result operations.
**Affects**: All SDK versions, all solver versions
**Category**: Skill
**Description**: Files uploaded via `wb.upload_file()` land in the server's
TEMP directory (`C:\Users\<user>\AppData\Local\Temp`), not in the IronPython
TEMP directory (`%TEMP%`), not in the IronPython
process's `os.getcwd()` (which is `c:\windows\system32\spool\drivers\x64\3`).
IronPython scripts must use `os.environ.get("TEMP")` as base path to find
uploaded files.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,5 @@ when the Workbench session terminates.

- `system_name` is the Workbench system identifier (e.g., "SYS", "SYS 1")
- The system must exist in the project before starting its server
- Sub-solver licenses are consumed when the server starts
- Start only the sub-solver server needed for the current workflow
- Only one server per system at a time
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
# Workbench Analysis System Templates

Available system templates for `GetTemplate(TemplateName=...)`.
Verified on Ansys 24.1.

Before assuming a template exists, ask the live Workbench session:

```python
templates = GetAllVisibleTemplates()
```

Template availability depends on installed products and license features.
Template availability depends on the local Workbench configuration.
For example, a Workbench environment may expose CFD/project templates but
not Mechanical templates. If `GetTemplate(...)` raises a
`Template ... not found in Project` error, treat it as a missing
Expand Down Expand Up @@ -70,11 +69,10 @@ system2 = template2.CreateSystem()

## Notes

- The exact list of available templates depends on the installed Ansys
products and licenses.
- The exact list of available templates depends on the local Workbench
configuration.
- Use `GetAllVisibleTemplates()` during smoke setup and skip or choose a
template that is present instead of assuming Mechanical templates exist.
- `Solver` parameter values: `"ANSYS"` (Mechanical), `"FLUENT"`,
`"CFX"`, `"MAXWELL"`, etc.
- Some templates may not be available if the corresponding product is
not installed.
- Some templates may not be available in a given environment.
Loading
Loading