Skip to content

Command line

umcf-cli works on a case without the GUI. It runs the same commands as the desktop app (umcf-gui). Build it with cargo build --release -p umcf-cli; the program is target/release/umcf-cli.

Checking a case

The examples below write umcf-cli for short: put target/release on your PATH, or give the full path.

target/release/umcf-cli check path/to/case

This opens the case and prints every problem the case check finds, errors first, then a summary:

error    workflow.all-regions         Allrun: line 46: `reconstructPar -allRegions` reads the region list from controlDict `regionSolvers`, …
warning  fv.options-ignored           system/air/fvOptions [air]: OpenFOAM-12 reads fvOptions only when there is no fvConstraints: …
1 error, 3 warnings, 2 notes

Each line gives the level, the code, then the file and, in brackets, the region, patch and field.

Option Does
--json Prints one JSON object: errors, warnings, infos (the counts) and diagnostics, a list of {level, code, path, region, patch, field, message}
--no-fields Doesn't read the field files. Opening is faster, but boundary conditions are not checked: each field file gets a note saying so. A region with no field files at all is still an error (region.no-fields), so the status can still be 1

The exit status is 0 when there are no errors (warnings are allowed), 1 when there are, and 2 when the case could not be checked (it isn't a directory, or doesn't open). In a script: umcf-cli check case || exit 1.

Checking a mesh

umcf-cli mesh path/to/case [--region air] [--all-geometry] [--json]

This reads each region's mesh (or only --region's) and runs the mesh quality checks; --all-geometry adds those of checkMesh -allGeometry. It prints the problems found, then one line per region:

warning  mesh.aspect-ratio            constant/buildings/polyMesh [buildings]: 2880 cells have an aspect ratio above 1000 (max 2046): cells 0, 1, 2, …
air: 25588 cells, non-orthogonality max 69.2°, skewness max 0.82, 0 errors, 0 warnings

--json prints, for each region: - quality: the numbers (cells, faces, volumes, face areas, aspect ratio, non-orthogonality, skewness, openness), with the first 1000 face or cell labels of each problem and their count; - checkmesh: what checkMesh left in the case, its logs (verdict, failed checks, remarks) and sets (each -writeSets set with its count, first 1000 labels and whether it is stale); - diagnostics: the problems as above, the native checks' and checkMesh's.

The top-level diagnostics holds notes about the case's checkMesh logs as a whole, such as a log for no region of the case.

The exit status is 0 when there are no errors, 1 when there are, and 2 when the case can't be opened. A mesh that can't be read is an error of its region (mesh.unreadable); the other regions are still checked.

Checking surfaces

umcf-cli surface path/to/case [--file walls.stl] [--json]
umcf-cli surface path/to/file.stl [--json]

This checks the STL surfaces in the case's constant/triSurface (or the one --file names), or one file anywhere, for what breaks meshing, and prints the problems found, then a line with the counts. --json prints each surface's numbers with the triangles of each finding. The exit status is 0 when there are no errors, 1 when there are, and 2 when the case or file can't be opened.

Drawing the case

umcf-cli render path/to/case --out case.png [--view top] [--size 1600x1000] [--edges]
                [--region air] [--surface walls.stl] [--select air/inlet] [--pick 640,400] [--json]

This draws the case's meshes (their boundary patches) or STL surfaces into a PNG file with the CPU renderer, and can select and pick patches first: see 3D scene and selection. The exit status is 0 when the image is written and 2 when the case can't be opened or drawn.

Running commands

umcf-cli run --case path/to/case --cmd 'control.set={"endTime": 7200}' --cmd case.save

This runs the commands in order and stops at the first that fails. Each command is given as <id>=<parameters>, with the parameters as a JSON object; a command without parameters is just its id. --case opens the case first (it is the command case.open).

Edits change the case in memory. Only case.save writes them to disk:

  • It writes only the files that changed, and edits each one in place (comments, layout and #includes stay as they were).
  • The first time a file is saved, its original is copied under .umcfgui/backup/<session>/ in the case.
  • If a file changed on disk since the case was opened, nothing is saved. Each file is checked once more right before it is replaced; a file changed in that moment stops the save, and the error names the files already written.

A run that ends with unsaved edits says so.

When a command fails, the case in memory is left as it was before that command: control.set with several entries sets all of them or none. Files already written are not: a case.save that stops part way leaves the files it wrote saved (the error names them).

Option Does
--cmd '<id>=<json>' One command; repeat it for more
--cmds <file> Commands from a file, one JSON object per line: {"command": "control.set", "params": {"endTime": 7200}}. Blank lines and lines starting with # are skipped
--journal <file> When the run ends, writes the edits made to the case open at that point, in the same format. Opening a case is not recorded, and starts a new journal
--json One JSON line per command: {command, ok: true, message, data}, or {command, ok: false, message} for the one that failed

A journal from --journal holds the edits of one case, and runs again with --cmds on whichever case is open: umcf-cli run --case copy --cmds journal.jsonl makes the same edits to copy. A run that opens a second case journals only the edits made after that.

The exit status is 0 when every command ran, 1 when one failed or the journal could not be written (the commands before may have run, and case.save may have written files), and 2 for bad arguments.

The commands

umcf-cli commands lists them with their parameters (--json for programs).

Command Does
case.open Opens a case: path (required), fields (false skips reading field files), discard (true opens even if the open case has unsaved changes)
case.save Writes the changed files (dictionaries, fields, surfaces, face sets, the workflow description), after a backup of each; a file a command wrote whole is refused when it is a symbolic link, checked once before anything is written (Cases)
case.info The case: OpenFOAM version, kind, regions with their patch and field counts, processor* directories, unsaved files, what didn't read
case.check The case check
workflow.show The workflow description: system/umcfWorkflow as read, with its problems, or as workflow.import would write it when the case has none
workflow.import Writes system/umcfWorkflow from the case's scripts and meshes, with notes on what it couldn't tell: replace (true writes over the case's own)
geometry.import Copies an STL file into constant/triSurface (Surfaces): path (required), name, replace, scale
geometry.trees Writes tree crowns as one surface in constant/triSurface: the trees from table (a file of the case), path (a file outside it) or trees ([{"x": 10, "y": 5, "z": 0, "diameter": 6, "height": 8}]); name (default trees.stl), solid (default trees), replace
geometry.check The surface checks: file (one surface of the case), path (a file anywhere, no case needed), or neither (every STL surface in constant/triSurface)
geometry.repair The repairs of one surface: file (required), weld (a distance in metres), degenerate, orient (true/false; without any of the three: degenerate and orient)
geometry.scale Multiplies a surface's coordinates: file, factor (both required)
scene.snapshot Draws the case in 3D into a PNG image (png, base64): regions, surfaces, view, azimuth, elevation, zoom, width, height, edges, colour_by, reload, display (layers as surfaces, wireframe, points or hidden, with an opacity: display modes), or camera as it returns it (3D scene)
select.pick Selects the patch or surface region under a pixel of that image, or along a ray, and lists everything under it: mode (replace, add, toggle, none), depth, faces (true: the mesh face under it, not its patch), and the snapshot's parameters (Picking)
select.rect Selects what is in a rect ([x0, y0, x1, y1]) of that image, seen or (through: true) hidden too: mode (replace, add, remove, none), faces, and the snapshot's parameters (Selecting in a rectangle)
select.set, select.clear Sets the selection from items ({"region", "patch"}, {"region", "face"}, {"surface", "solid"}) with mode (replace, add, remove), or empties it
patch.set Changes patches of a region's mesh: type, groups (["wall"]), entries ({"neighbourRegion": "air"}), name (one patch; its field files and the mapped patches of other regions that name it follow), patches ([{"region", "patch"}]; default: the selected patches)
patch.create Prepares a new patch for one run of createPatch -dict …createPatchDict.umcf (the line for the case's version is in the result) from boundary faces: name (required), region (default: the region of the selected patches, or of the picked faces when they are the faces), type (default patch), groups, entries, and the faces: the selected faces when none of the following is given, or one of faces (labels), box ({"min": [x, y, z], "max": [x, y, z]}) or surface ({"file", "solid", "distance"}); from (patch names, for box and surface)
bc.set Sets the boundary condition of patches in a field of the start time: field, type (both required), value (300 is uniform 300, [2, 0, 0] is uniform (2 0 0), or OpenFOAM text), entries (other keys: {"inletValue": "uniform 0"}), defaults (keys only a new entry gets, such as {"value": "uniform 300"}: a patch keeping its entry keeps its own), patches ([{"region", "patch"}]; default: the selected patches). Returns the case check's findings for those patches
mesh.check The mesh quality checks: region (default: every region with a mesh), all_geometry (true: also the checks of checkMesh -allGeometry)
control.set Sets system/controlDict entries, by name: {"endTime": 7200, "writeInterval": 600}
dict.set Sets one entry of any dictionary the case loaded: file, path ("PIMPLE/nOuterCorrectors"), value. Parent dictionaries that are missing are added
dict.remove Removes one entry: file, path
edit.undo, edit.redo Step back and forward through the edits (up to 100)

Values are given as JSON:

  • a number or true/false is written as it is;
  • an array becomes an OpenFOAM list: [0, 0, -9.81] is (0 0 -9.81);
  • a string is OpenFOAM text, written as given: "uniform 300", "Gauss linear", or a dictionary such as "{ type fixedValue; value uniform 300; }".

A value can only change its own entry. Text that would end the entry early is refused, such as a ; or anything after a dictionary's closing }. A comment in a value is dropped. Keys are plain OpenFOAM words (endTime, div(phi,U)) or patterns, given without their quotes ((U|k) reaches "(U|k)", and a new one starting with ( is written quoted; a new pattern such as "U.*" can't be added yet, as it would be written unquoted, as a plain word): directives (#include), macros ($x) and keys with unbalanced brackets are refused. So are unknown parameter names, which catches typos.