Skip to content
SHD Sim
Menu
Documentation menu

Draft. This manual is new and still growing. If something here does not match what you see in the software, the software is right — tell us and we will fix the page.

Run a script and record a macro

Drive the application from a JavaScript file instead of by hand: change one thing, mesh, solve, read a number back, decide, and do it again.

Runs on the free tier, within whatever the tier allows the script to do.

Record what you already do

The quickest way to start a script is not to write one.

  1. Open the case.
  2. ModelSCRIPTRecord macro, and choose where to save the .js file.
  3. Do the work by hand. The command relabels itself Stop recording and its tooltip counts the lines written so far.
  4. ModelSCRIPTStop recording.

The recorder is driven by the undo history rather than by the controls, so anything an undo can see it can see. It writes what changed, not how: a slider dragged from 10 to 80 records as one line setting 80. Run state — the cell count, whether a mesh exists, the last outcome, the run directory — is written by the application about the case rather than by you into it, and is not recorded; where one of those changed, the file gets a comment line reading // changed but not recorded: and the names.

The file opens with a header of comment lines — a timestamp, the case, and how to run the file — then sim.openCase("<name>"). Geometry import, meshing and solving are not edits and are not recorded. Closing the window ends a recording the way the button does, so the last edits are in the file rather than lost with the buffer.

--record=<file.js> does the same from a terminal, and records until the application exits.

Run one

ModelSCRIPTRun script, and choose the .js file. The Log dock opens; output, errors and the exit code go there. Only one script runs at a time — the command is greyed with "A script is already running" while one is.

From a terminal, --script=<file.js> runs it and exits with its code. Put it last on the command line: everything before it has already run, and anything after it runs before the script's first yield resumes. If any earlier flag on that command line was an edit the application refused, --script= does not run at all, rather than automating a case that is not the one that was asked for.

What a script may do

A script runs in the application's own JavaScript engine with exactly one object in scope: sim. CaseState and the other internals are deliberately not reachable, so a script written against sim.set("reynolds", 200) survives a rename of the pane that owns the value.

The long operations — an import, a mesh, a solve, a report — take minutes and the engine has no await. Write yield in front of them instead; the file is compiled as the body of a generator, so a top-level yield is legal.

sim.openCase("hull")
yield sim.importGeometry("D:/geometry/hull.obj")
sim.set("baseCellSize", 0.04)
const mesh = yield sim.mesh()
sim.log(mesh.cells + " cells, max non-orthogonality " + mesh.nonOrtho)
const run = yield sim.run()
sim.log("run " + run.outcome + ", " + run.warnings + " warning(s)")
yield sim.results()
const p = yield sim.probe("p", [0.25, 0.05, 0])
sim.log("p at the probe: " + p)

Broadly, sim covers:

  • Casescatalogue(), newCase(), openCase(), saveCase(), cases(), importMesh(), and yield importGeometry(). caseName and version are properties, not calls.
  • Setupproperties() lists every property a script may change; get(), set(), boundaries(), boundary(), setBoundary(), problems(). Reading is wider than writing: get() also returns what the case derivedsim.get("solver"), sim.get("meshExists"), sim.get("lastOutcome") — which is most of what a script branches on, and none of which can be assigned.
  • Work, which you yieldwait(), mesh(), run(). stop() only asks a running solve to stop and write, and is not yielded.
  • Resultsresults(), probe(), sampleLine(), monitors(), saveImage(), report(), dumpCase().
  • Files and exitreadFile(), writeFile(), log(), fail(), exit().

sim.importGeometry() must be waited for. Reading a surface is asynchronous, so a script that carries straight on meshes the empty domain box, solves it, and reports numbers for a case that names the model throughout. It throws if no body arrives.

sim.run() refuses to start a case the Problems tab has errors on, and says which. Both sim.mesh() and sim.run() throw if the job did not work, so a script does not check a return value for failure — an uncaught error stops the script, logs the message with the line number, and exits non-zero. A solve counts as having worked if it converged, or if it was stopped deliberately, by sim.stop() or by the wall-clock ceiling; both leave results worth reading. Anything else throws, with the outcome and the run directory to look in. Both take an options object with a timeout in seconds, and a job that exceeds it throws rather than hanging the script for ever; sim.run({end: 200}) also sets the end iteration or time.

What they return on success is worth reading: mesh() gives cells, nonOrtho, ok and the problem rows; run() gives outcome, converged, errors, warnings and directory.

yield sim.results() resolves to {ready, error, times, fields, cells, boundsMin, boundsMax}; ready is false when there is nothing loaded to read, and error says why. sim.monitors() returns an array — one {label, latest, tailMean} per trace — so it is looped over, not indexed by name, and it is empty for a run that wrote no monitors.

sim.dumpCase(dir) writes the generated OpenFOAM case and creates the directory if it is not there. The folder picker deliberately does not; a path a script author wrote down is not a picked folder.

A syntax error is reported with the line number of the file, not of the wrapper.

Check it worked

  • The Log dock ends with finished with code 0.
  • For a recording, open the .js file: every edit you made is a sim.set line, and the comment lines name anything that changed but was not recorded.
  • Add yield sim.mesh() and yield sim.run() to a recorded macro where the work should happen — the recorder writes the edits, not the jobs.