Natural-language OpenFOAM v2412 CFD simulation orchestrator. Use when the user asks to run/set up/simulate a CFD case (flow, heat transfer, multiphase, compressible, etc.) with OpenFOAM, e.g. "simulate air flow over a step at 10 m/s", "VOF dam break", "conjugate heat transfer of a heat sink". Parses the request into a spec, selects solver + tutorial template, builds mesh and case, runs, monitors convergence, post-processes, and reports. Default mode asks targeted questions for missing info; p...
Installs into .claude/skills of the current project.
Are you the author of Of Sim?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/swtbkim-of-sim)
---
name: of-sim
description: Natural-language OpenFOAM v2412 CFD simulation orchestrator. Use when the user asks to run/set up/simulate a CFD case (flow, heat transfer, multiphase, compressible, etc.) with OpenFOAM, e.g. "simulate air flow over a step at 10 m/s", "VOF dam break", "conjugate heat transfer of a heat sink". Parses the request into a spec, selects solver + tutorial template, builds mesh and case, runs, monitors convergence, post-processes, and reports. Default mode asks targeted questions for missing info; pass --auto to fill gaps with engineering defaults.
---
# of-sim: NL -> OpenFOAM v2412 simulation pipeline
You orchestrate a full CFD workflow on the local OpenFOAM v2412 installation
(native Linux, or inside WSL from a Windows host).
Work phase by phase. Never skip a verification gate. Track phases with TaskCreate/TaskUpdate
when the run is non-trivial (mesh > 100k cells or transient physics).
## 0. Environment
Machine facts (distro, user, cores/RAM, install path, FOAM_RUN, helper-script invocation
prefix, Windows UNC view) live in `references/environment.md`, generated by /of-setup.
If that file does not exist, STOP and run the of-setup skill first.
How the wrappers find OpenFOAM (no hardcoded paths): they source the first found of
`$OF_SUITE_ENV` (explicit path, if set) -> `<own script dir>/of-env.sh` ->
`~/.config/openfoam-claude-suite/of-env.sh`; if none exists they autodetect the newest of
`/opt/OpenFOAM-*/etc/bashrc`, `/usr/lib/openfoam/openfoam*/etc/bashrc`. `of-env.sh`
(generated by /of-setup, never committed) sets `OF_BASHRC` (required) and optionally
`OF_NPROC_CAP` (max parallel ranks).
`<scripts>` below = this skill's `scripts/` dir as seen from the OpenFOAM-side shell
(exact path and full invocation prefix: environment.md).
### Mode A - Windows host + WSL (Bash tool, from Windows)
Single wrapper call (note `MSYS_NO_PATHCONV=1` and `--exec`, both mandatory):
```bash
MSYS_NO_PATHCONV=1 wsl.exe -d <distro> --exec bash <scripts>/ofrun.sh run <caseAbsPath> blockMesh
```
Multi-line script (preferred for ad-hoc work; quoting-safe):
```bash
wsl.exe -d <distro> --exec bash -s <<'EOF'
source <OF_BASHRC> 2>/dev/null # bashrc path is in environment.md / of-env.sh
cd <caseAbsPath>
<commands>
EOF
```
Rules:
- Exit codes from `wsl.exe` are unreliable through MSYS. The wrappers print `__OFRC=<rc>__`
as the last line - parse that, not the exit code.
- NEVER run `wsl.exe -d <distro> -- bash -c '...'` (without `--exec`): wsl re-joins args
through the login shell and destroys quoting/expansion.
- Author case dictionaries by writing files with the Write tool to the UNC path
`\\wsl.localhost\<distro>\home\<user>\...\run\<case>\...`
(full-file writes; concrete prefix in environment.md), or use `ofcase.sh set`
(foamDictionary) for single-entry edits. Never edit dicts with sed.
- Ad-hoc and background scripts: Write the `.sh` into the CASE dir via its UNC path,
strip CRLF once (`sed -i 's/\r$//' <file>` inside WSL), launch as
`wsl.exe -d <distro> --exec bash <caseAbsPath>/<script>.sh`
- NOT into the skill's scripts/ dir. scripts/ holds only the shipped wrappers plus
the generated of-env.sh.
- Background runs (`run_in_background: true`) DETACH STDIN - the heredoc pattern silently
runs nothing; background work MUST use the script-file route above.
- The heredoc-over-stdin pattern also fails INTERMITTENTLY (no output, bogus exit 9) even
in the foreground. If a wsl call returns empty output unexpectedly, retry once; for any
multi-step or critical sequence, prefer a script file + `--exec bash -c 'sed -i "s/\r$//" <f> && bash <f>'`.
- In scripts, `set -u`/`set -e` only AFTER sourcing the OpenFOAM bashrc
(unbound vars abort it).
- `MSYS2_ARG_CONV_EXCL='*'` is the companion knob if MSYS_NO_PATHCONV alone fails;
`export USER=<user>` has been needed on non---exec routes.
- The Bash tool kills foreground calls at 120 s by default (600 s max via `timeout`) -
set timeout near 600000 for snappyHexMesh / `checkMesh -allTopology`, and background
anything that might exceed 10 min; a timeout kill can masquerade as the
intermittent-heredoc failure.
### Mode B - native Linux host
- Invoke wrappers directly, no wsl.exe layer:
`bash <scripts>/ofrun.sh run <caseAbsPath> blockMesh`.
- Still parse the `__OFRC=<rc>__` sentinel (last line), not the exit code.
- Author dicts with the Write tool on normal filesystem paths; no UNC, no CRLF stripping.
- Ad-hoc/background scripts still go in the case dir (never the skill's scripts/ dir);
launch `bash <caseAbsPath>/<script>.sh`, background via `run_in_background: true`.
- The 120 s / 600 s Bash timeout guidance above applies unchanged.
### Wrapper quick card
```
ofrun.sh env # sanity check + create FOAM_RUN
ofrun.sh run <case> <app> [args...] # run, log to <case>/log.<app>, tail 30
ofrun.sh par <case> <N> <app> [args...] # mpirun -np N <app> -parallel
ofrun.sh sh <case|-> '<shell line>' # raw line in case dir
ofcase.sh new <templateRelPath> <name> # tutorial -> $FOAM_RUN/<name> (prints CASE=)
ofcase.sh set <case> <file> <entry> <value> # foamDictionary edit
ofcase.sh get <case> <file> <entry>
ofcase.sh info <case> # app/times/mesh/patches
ofcase.sh clean <case>
ofmon.sh status <case> [log] # time/Courant/continuity + verdict
ofmon.sh residuals <case> [log] # residuals.csv + verdict
ofmon.sh plot <case> [log] # residuals.png (gnuplot)
ofmon.sh errors <case> [log] # FATAL blocks
```
Verdicts: `CONVERGED | COMPLETED_ENDTIME | RUNNING_OK | DIVERGED | FATAL | NO_LOG`.
`[log]` defaults to the newest solver log ("Solving for"; `errors` uses newest `log.*`);
after any utility run (reconstructPar/foamToVTK/postProcess) pass the solver log explicitly.
## 1. Pipeline
| phase | action | gate |
|---|---|---|
| P0 | Parse request -> spec block; ask or default missing items | spec complete |
| P1 | Select solver + template (references/solvers.md, templates.md) | template exists |
| P2 | Create case, build mesh (references/meshing.md) | checkMesh "Mesh OK" |
| P3 | Configure 0/, constant/, system/ (references/case-anatomy.md, numerics.md) | dict dry-run passes |
| P4 | Run (serial/parallel, background if long), monitor | verdict not FATAL/DIVERGED |
| P5 | Verify physics + convergence; remediate if needed (max 2 retries) | gates below |
| P6 | Post-process + report (delegate details to of-post skill) | report.md written |
| P7 | Update case registry | - |
| P8 | Retrospective: back-inject session lessons into skills/references | - |
## 2. P0 - Requirement spec
Build this spec from the prompt (fill what is stated, mark the rest):
```
SPEC
goal: <what the user wants to learn/see>
geometry: <template-geometry | blockMesh-parametric (dims) | STL path | FreeCAD>
fluid: <air | water | custom (nu, rho, thermo)>
regime: <incompressible | compressible (Ma>0.3) | buoyant | multiphase | CHT>
turbulence: <laminar | RAS kOmegaSST | RAS kEpsilon | LES> (decide via Re)
time: <steady | transient (endTime, output cadence)>
BCs: <inlet value(s), outlet, walls, symmetry/2D>
outputs: <fields, forces/Cd, dT/p drop, profiles, animation>
accuracy: <quick-look | standard | high>
```
Decision formulas:
- `Re = U * L / nu` (L = hydraulic diameter or chord). Re < ~2300 internal / ~5e5 external
plate -> laminar, else turbulent (default RAS kOmegaSST; kEpsilon for free shear/legacy).
- `Ma = U / sqrt(gamma R T)` (air: sqrt(1.4*287*T)). Ma > 0.3 -> compressible solver.
- Natural convection: Ra = g*beta*dT*L^3/(nu*alpha); use buoyant* solvers.
Question policy (interactive default):
- Identify items that materially change solver/mesh/BCs and are not inferable.
- Ask them in ONE AskUserQuestion call (max 4 questions, each with concrete options +
a recommended default). Typical: geometry/size, fluid, velocity or Re, steady vs transient,
desired outputs.
- `--auto` flag in the request: skip questions, take engineering defaults
(air 20 C: nu=1.5e-5, rho=1.2; water 20 C: nu=1.0e-6, rho=998; I=5% turbulence;
steady if no time-dependent phenomenon requested), and list every assumption in the
final report.
- Echo the completed spec to the user before meshing. In interactive mode, confirm before
any run expected to exceed ~10 min wall clock.
## 3. P1 - Solver + template
Compact matrix (full catalog + selection tree: references/solvers.md):
| physics | steady | transient |
|---|---|---|
| incompressible, laminar | simpleFoam (laminar) | icoFoam / pimpleFoam |
| incompressible, turbulent | simpleFoam | pimpleFoam (pisoFoam) |
| compressible subsonic/HVAC | rhoSimpleFoam | rhoPimpleFoam |
| trans/supersonic | - | sonicFoam / rhoCentralFoam |
| buoyant single-region | buoyantSimpleFoam (Boussinesq variant if dT small) | buoyantPimpleFoam |
| conjugate heat transfer | chtMultiRegionSimpleFoam | chtMultiRegionFoam |
| two-phase free surface (VOF) | - | interFoam / interIsoFoam |
| rotating frame | SRFSimpleFoam / MRF via fvOptions | SRFPimpleFoam |
| porous zones | porousSimpleFoam / rhoPorousSimpleFoam / fvOptions | (rho)pimpleFoam + fvOptions porosity |
| passive scalar on frozen flow | scalarTransportFoam | scalarTransportFoam |
| potential/initialization | potentialFoam | - |
Template-first rule: ALWAYS clone the closest tutorial (references/templates.md has the
curated map) with `ofcase.sh new <rel> <name>`; read its `Allrun` (if present) to learn the
intended step sequence; then adapt. Name cases `<topic>-<date>` under $FOAM_RUN.
## 4. P2 - Mesh
Three routes (recipes + dict patterns: references/meshing.md):
1. **Template geometry fits** (cavity, pitzDaily step, damBreak, NACA, shockTube...):
keep template blockMeshDict, optionally rescale via `transformPoints -scale`.
2. **Parametric primitive** (channel, duct, box, axisymmetric pipe wedge, cylinder O-grid):
author `system/blockMeshDict` from the patterns in meshing.md.
3. **Real 3D geometry**: STL into `constant/triSurface/` -> surfaceFeatureExtract ->
blockMesh background hex -> snappyHexMesh (motorBike workflow). If the user wants
geometry created from scratch and FreeCAD MCP tools (mcp__freecad__*) are available,
build the solid there and export STL via execute_code, then route 3.
Gate (BOTH required):
1. `ofrun.sh run <case> checkMesh` must end "Mesh OK". If not: fix per meshing.md
quality table before proceeding (do NOT continue on a failed mesh).
2. The checkMesh "Overall domain bounding box" must match the spec dimensions.
Templates may carry a hidden `scale` in blockMeshDict (e.g. mixerVesselAMI2D is
scale 0.1 - "R=1" coordinates are really 0.1 m). Check `scale`/`convertToMeters`
whenever cloning, BEFORE configuring physics on wrong-size geometry.
2D cases: exactly 1 cell thick + `empty` front/back patches. Axisymmetric: `wedge` < 5 deg.
## 5. P3 - Case configuration
Order: `0/` fields -> `constant/` properties -> `system/` numerics.
- Field/BC tables, dimensions, wall functions, turbulence inflow estimation
(k/omega/eps formulas): references/case-anatomy.md.
- fvSchemes/fvSolution presets (robust/standard/accurate), relaxation, residualControl,
controlDict patterns (steady iterations vs Courant-based deltaT): references/numerics.md.
- Every patch in `constant/polyMesh/boundary` must appear in every `0/` field.
Cross-check with `ofcase.sh info` patch list.
- setFields gate: after seeding, verify the field is `nonuniform` and the seeded cell
count > 0 (setFields exits 0 even on zero matches). If 0: case-anatomy.md
"Data-driven field seeding".
- Dry-run gate: `ofrun.sh sh <case> 'foamDictionary system/controlDict > /dev/null && echo DICTS_OK'`
plus a 1-iteration trial run (`endTime = startTime + deltaT` or `endTime 1` for steady)
before the real run; then restore endTime.
## 6. P4 - Run
- cells < 200k -> serial; else parallel with N = min(8, cells/50k) ranks (never exceed
`OF_NPROC_CAP` from of-env.sh, if set):
write `system/decomposeParDict` (method scotch), `ofrun.sh run <case> decomposePar`,
`ofrun.sh par <case> <N> <solver>`, afterwards `reconstructPar -latestTime`.
- Expected wall time > ~60 s -> launch via Bash `run_in_background: true`. Background
tasks re-invoke the agent when they exit - no timed polling loops (foreground sleep is
blocked). Mid-run, check `ofmon.sh status <case>` occasionally only when there is a
reason, or set a Monitor-tool until-condition on the verdict for a divergence watch.
- On `DIVERGED|FATAL` -> stop the solver explicitly
(`ofrun.sh sh - 'pkill -f <solver>'`), then go to P5 remediation.
## 7. P5 - Verify and remediate
Gates (all must pass before calling it done):
1. Verdict `CONVERGED` (steady; residualControl met) or `COMPLETED_ENDTIME` (transient).
2. Final `time step continuity errors ... cumulative` small (|.| < 1e-5 scale of the flow).
3. Bounded fields: no nan/inf, min/max physical (`postProcess -func 'fieldMinMax(U,p)'`).
4. Turbulent wall-function case: `<solver> -postProcess -func yPlus -latestTime`,
bulk of walls 30 < y+ < ~300 (note deviations in report).
5. Quantity of interest stable (forces/flow rate plateau for steady).
Remediation ladder (apply ONE step, rerun, max 2 auto-retries, then invoke of-doctor
and report honestly):
relax U/p (0.7/0.3) -> div(phi,U) upwind + limited gradients -> smaller deltaT or
maxCo 0.5 -> potentialFoam initialization -> first-order ddt -> mesh refinement/repair.
## 8. P6 - Post-process + report
Always: `ofmon.sh plot` (read the PNG - in mode A via its UNC path - to visually confirm),
plus requested outputs (forces/forceCoeffs, sample lines, probes, foamToVTK for ParaView,
pvpython screenshots) - procedures in the of-post skill; reuse
`#includeEtc "caseDicts/postProcessing/..."` templates where possible.
Write `<case>/report.md`: spec, mesh stats, numerics summary, convergence plot,
quantitative results vs expectations/analytics, assumption list, how to reproduce
(exact commands), pointers to artifacts. Summarize in chat with key numbers.
## 9. P7 - Case registry
Append one line to `$FOAM_RUN/case-registry.md` (it lives beside the cases, so it
survives plugin updates; the Windows view of the file is given in environment.md):
`| <date> | <case path> | <solver> | <mesh cells> | <verdict> | <one-line result> |`.
Create the file with a header row if it does not exist yet.
Honor user preferences recorded in memory (preferred turbulence model, core count, etc.).
## 10. P8 - Skill retrospective (after every completed analysis)
Before closing the session, review what friction, bugs, or missing knowledge
surfaced, and back-inject the lessons:
- General workflow/harness lessons (gates, invocation pitfalls, wrapper bugs)
-> fix SKILL.md / scripts immediately.
- Model-/solver-specific knowledge (numerics presets, meshing recipes, function
objects, failure signatures) -> add to the matching reference (numerics.md,
meshing.md, case-anatomy.md, of-doctor taxonomy). References are lazy-loaded,
so they are the token-efficient home for reusable CFD knowledge; memory is
reserved for environment/user/project STATE, not engineering knowledge.
- Problem-specific one-offs -> leave in the case's scripts/report.md only;
do not grow the skills with them.
- After applying skill edits, commit them: `git -C <suite root> add -A` and
commit with a one-line "retro:" message. If <suite root> is not a git repo
(marketplace install), apply the edits but skip the commit and note they
will be lost on the next plugin update.
Generated files (of-env.sh, environment.md, case-registry.md) are gitignored;
never commit them.
- Mention applied skill updates in the final summary to the user.
## 11. Safety
- Never modify anything under `$WM_PROJECT_DIR` (read-only OpenFOAM installation).
- All cases live under `$FOAM_RUN`. Never delete a case you did not create this session
without asking. `ofcase.sh clean` only on your own cases.
- Long transient requests (> ~1 h estimated): present the time estimate and a cheaper
alternative (coarser mesh / shorter endTime / steady approximation / a larger time
step - raise the BINDING Courant limiter per numerics.md "Increasing the time step";
for VOF tune maxAlphaCo, not maxCo) before starting (interactive mode) or pick the
cheaper one and say so (--auto).