Skip to content

Gaps

What umcfGui can't do yet, most important first. Work is picked from the top. A row is removed in the same pull request that closes it.

Next

The goal they lead to: a working case built in umcfGui from STL surfaces of its regions, with a blockMesh background, its patches set up, its radiation and weather data set, and a mesh that can be reset and made again. What a case needs is the specification, and the common workflow the design rows 4–9 and 12 build.

# Gap Why it matters Where
1 Patches of an existing mesh, the rest: faces painted in the 3D view with a brush (a click picks one, a rectangle the ones in it), running createPatch on the prepared patches from umcfGui (with the runner, row 12), and a new name followed into the results of other times, the processor* directories and boundaryData. The meshing dictionaries follow once they are generated from umcfWorkflow (rows 6–9); until then patch.set lists where the old name is still written. Retyping, regrouping and renaming (patch.set) and new patches from faces picked or selected in a rectangle, labels, a box or an STL surface (patch.create) are there, for the mesh as it is A patch is split most directly where it is seen; until createPatch runs, the new patch can't be given conditions or looked at model, engine, ui-egui
2 The case check knows what a case needs: the fixed names (air, vegetation, air_to_vegetation), a vegetation patch named like every air patch coupled to a solid, sky (patch, mapped) or surface (wall, mappedWall, the canopy) in the radiation region per version, mapped patch types under the coupled conditions, every material a cell zone and the zones covering each cell once, the radiation tables there and covering the run, radiating patches without a view factor entry (not agglomerated: slow) These stop a run, or worse, run wrong without a word; they can be checked in existing cases now validate, foam-mesh (cell zones), schema
3 The mesh inside, in the 3D view: cells cut by a plane or near a surface, the layers at a wall, and the faces and cells a quality check names drawn where they are. The view reads only a region's boundary; the inside is read on request, per region: turning on a cut plane, a layer view or the faces of a check reads that region's whole mesh once (owner, neighbour, the internal faces: about what mesh.check reads, 4 GB for a 16-million-cell region), kept, like the boundary objects, under the memory budget planned for hidden objects (the least recently shown dropped first; see Hidden regions and surfaces stay in memory below), and the status bar says which regions are read whole and what they take. mesh.check hands what it names (faces and cells, with their points) to the view, so showing them reads nothing again. A cut plane draws only the cells it crosses. Each layer (a region's mesh, a patch, a patch group, a surface or one of its regions) shown as surfaces, wireframe or points, with its own opacity, is there (display) Mesh quality control needs to see the mesh where it matters, not only its boundary render, foam-mesh, ui-egui, engine
4 The common workflow's description, system/umcfWorkflow, the rest: solids' materials in the format, commands that edit it entry by entry (a patch's role, rename a patch, add a region, a solid, a material), the scripts written from it, and a new case from it (case.new: the region list and run controls for OpenFOAM-8 or -12, the solver dictionaries of each region kind from templates; region.add). The format with regions, kinds, meshing steps, patches with roles and partners, radiation and the solver, read with its problems named, imported from a case (workflow.import, workflow.show) and checked against the case, is there Nothing creates a case yet, and a case's names are written in a dozen hand-edited places that must agree model, schema (templates per version), engine
5 The dictionaries generated from umcfWorkflow by patch role and the coupling rules: createPatchDict, changeDictionaryDict (the mappedWall pairs, with trees the vegetation patch of the same name), topoSetDict (or the setSet batch), viewFactorsDict, and 0.orig/<region>/<field> with an entry per patch by role (inlet, outlet, side, top, ground, coupled, canopy, interior, bottom, sky), the cell zones named by it (materials, the tree zone), and each material's buildingMaterials entry from a library of materials; a generated dictionary marked as such and backed up before it replaces a hand edit, while an existing 0.orig field is edited in place (only a missing one is made) Coupling by hand touches a dozen files that must agree model, schema, engine
6 Box and block meshes in umcfWorkflow, per region: a box from the surfaces' bounds plus a margin, cell size or counts, grading, the six patches with names and roles, and block layouts (the 3×3 cases' fluid regions with the buildings as missing blocks, solids as blocks), written to blockMeshDict; and a region split from another by a cell zone (splitMeshRegions, the street canyon's vegetation) (mesh.block) Every chain starts from it; block regions are how the reference cases make their solids model, geom, engine, ui-egui
7 Meshing from region STL surfaces in umcfWorkflow: per fluid region the surfaces with their levels, layers and roles (buildings cut out with layers, trees refined or cut out), refinement boxes, feature edges, written to snappyHexMeshDict.<region>, with the checks before running (inside point in the air and outside every surface, the box covering the surfaces, closed surfaces where the inside matters, unique patch names) A new case comes out of meshing with the right patches model, geom, validate, engine, ui-egui
8 Solids by extrusion in umcfWorkflow: layers (material, thickness, cells, grading) from a building surface or the ground, or from the snapped air patch for solids that meet the air face for face, written to the extrudeMeshDicts, the patch renames and the material cell zones; leaf area density from the tree surface (topoSet, setFields) The STL chain's solids and trees are made this way model, engine
9 The scripts from umcfWorkflow (Allprepare, Allrun, Allclean for its version; today they are written from the scripts read), with the fields put in place after meshing (0.orig to 0, setFields); the STL chain and the split vegetation read into a umcfWorkflow (the kept background, extrudeMesh, splitMeshRegions, the moves between constant/polyMesh and the regions); and reset (mesh.reset), which puts the case back before meshing After a change to the geometry or the patches the mesh is out of date model (workflow), validate, engine
10 Site, date and weather: latitude, longitude, time zone, start date and the mesh's north, from which the sun table (sunPosVector) and the irradiance (IDN, Idif) are computed, and the outdoor tables (Tambient, wambient, cloudCover, LW) read from a weather file; emissivity, albedo and view factor settings per patch Radiation data can't be set in the case otherwise; the solver itself has no calendar model (site, weather), engine, ui-egui
11 Radiation outputs kept up to date: mark what faceAgglomerate, calcLAI, viewFactorsGen and solarRayTracingGen wrote as stale after any of the changes the chain lists for it (a mesh, patch, view factor dictionary or decomposition change for all four; the sun or irradiance tables and LAD for calcLAI and solarRayTracingGen), and name the steps to run again Stale view factors run without an error model, validate, engine
12 Running the steps anywhere (see the common workflow): run.export (the scripts as a command), a runner that reaches OpenFOAM natively on Linux, through WSL on Windows, in a virtual machine or container (macOS), or on a remote machine by ssh with the case synchronised, as steps or SLURM/PBS jobs, with the logs streamed back and a step cancelled OpenFOAM runs only on Linux; umcfGui runs on Linux, Windows and macOS model, runner, engine

Known limits

Limit Effect Possible fix
Case::open reads each start-time field file through before the case opens Only their outlines are kept, in parallel, but each file is decompressed to its end (up to 8 GB): the Hong Kong project opens at its latest time in 2.6 s, most of it the 600 MB U and phi; a case with many multi-GB fields waits for all of them (Cancel and --no-fields skip them) Open first and read the fields in the background, the boundary conditions shown as they come
bc.set sets the patch's own entry only A condition given by a group or a pattern ("(wall\|roof).*") isn't changed: the patch gets an entry of its own, which takes precedence; setting the group's entry needs dict.set-style editing of field files A bc.set target for a group or pattern entry
A same-type bc.set can add or change keys, not remove them Removing a key from a condition means giving the patch another type and back, or editing the file A remove list in bc.set
Field files over the edit limit (256 MB) are not edited Their conditions are shown and checked, but can't be set here Rewrite the boundaryField part of a big file without reading its lists into numbers
One file changed outside since the case read it stops every save case.save checks all files first and writes none when one changed on disk (also after an undo past a saved command), so unrelated edits wait until the case is opened again Save the files that can be saved and list the conflicts
An edited field file is kept whole per change, for undo Ten condition changes on a 500 MB field file hold ten copies until the undo history drops them Store the edits as steps to replay, or cap undo by memory
The workflow covers the 3×3 pattern: snappyHexMesh and blockMesh regions in one case Meshing chains spread over several cases (the Hong Kong project's extrusions, sourceCase) are checked step by step but not read into a workflow Workflow steps for extrusion and for meshes taken from another case
patch.create with a box or a surface reads the region's boundary again for each new patch About 3 s on the 16-million-cell air region of the HK project for one new patch (its points and boundary faces; no cells), behind the busy card Take the boundary from the 3D scene, which has read it already
A surface, face set or workflow description that is a symbolic link is not replaced whole by a command Repairing, scaling or replacing a surface linked to a shared STL is refused, and so is workflow.import over a linked system/umcfWorkflow: what the link points to stays as it is. Entry edits (dict.set on a linked description, bc.set on a linked field) are still saved through the link when it points inside the case; a link out of the case is refused at save, as for any file A replace option that puts a file of its own in place of the link, or one that writes through it, with a backup of the file it points to
case.save checks for links before it writes anything, not right before each file A file a command put whole that is turned into a link while the save runs is written through it A write that doesn't follow a link at the path, checked by the case's file system right before the rename
case.save writes files but never deletes one An undo after saving a command that made a file (a face set, a new createPatchDict) leaves that file in the case Deleting the files a save made when an undo takes them away, with a backup
A script's effect is followed only for commands the check knows After a Python helper or an unknown utility, the rest of that script is not checked for missing files Declare a helper's outputs in the workflow
dict.set quotes a new key only when it starts with ( A new pattern key that doesn't ("U.*") is written as a plain word, which OpenFOAM doesn't match as a pattern; existing quoted keys are found and kept A parameter that marks a key as a pattern
Building-material coefficients are not checked for their keywords Each entry of buildingMaterials reads its coefficients through its own dictionary (VanGenuchtenCoeffs_); a missing one is found only when the run starts Map each material type's coefficient dictionary to its place in transportProperties
Keywords read under a condition or after the start are not required A keyword read only in some branch, a loop or a later time step is found only when the run reaches it Follow the conditions the solver can decide from the case (a flag in the same dictionary)
Field values are not checked, and a field's length only against the owner header's note A NaN or infinite value in a binary field, or a negative k or epsilon, may not be reported at all; in a mesh whose owner header has no sizes (written by hand or by an old tool), a field of the wrong length is found only when the solver reads it The counts without the note: nInternalFaces and nPoints from the neighbour and points list headers, nCells once the mesh is read whole (mesh.check, the mesh inside). The values: a check of their own that reads them as a stream when asked
The mesh quality check runs on one core, holds the whole mesh, and can't be cancelled 14.6 s and 4.0 GB for the 16-million-cell air region of the HK project, during which the GUI would wait Split the face loop over threads, as the reader does; read the files as a stream, without holding them whole (the memory work of the 3D scene); run it as a job with a cancel flag
Hidden regions and surfaces stay in memory, and regions are read in parallel What the scene has read of a region or surface (with its BVH once it was picked in) is kept while it is hidden, until another case opens or reload: as much as showing everything at once. Up to one region per core is read at once, each holding what it has read: the five regions of the HK project peak at 1.8 GB A memory budget for what is hidden, the least recently shown dropped first; readers started only while their files fit a memory budget
The 3D scene checks a mesh's boundary against the owner header only The check finds a boundary whose patches don't tile the faces after the internal ones (they must start where the owner header's nInternalFaces says, when noted, and end at the faces file's count, which must match the header's nFaces; only the file's first 64 KB are read for it). It doesn't identify the mesh: a stale boundary with the same counts but other patch sizes passes and draws faces in the wrong patches. Without the note, where the internal faces end isn't checked either, so a boundary that tiles a different range of faces also passes Warn when boundary is older than faces and points (OpenFOAM writes a mesh's files together; only a hint, as copying a case can change file times)
The 3D scene keeps all of a region's points, and reads binary mesh files whole Drawing the 16-million-cell air region of the HK project takes 3.8 s and 0.97 GB the first time (5.6 s and 4.0 GB when the whole mesh was read, 3.8 s and 1.8 GB before the files were read as a stream). ASCII points and faces files with a header, compressed or not, are read as a stream (CaseFs::reader): the file isn't held, nor the text of the points or of the internal faces (only that of the boundary faces). A binary file is held whole while it is read, as are faces, owner and neighbour for the mesh quality check Keep only the points boundary faces use; read binary files, and the mesh check's faces, owner and neighbour, as a stream too
The desktop app's 3D view is drawn by the CPU renderer, on one core Turning a mesh of millions of boundary faces is slow (the view draws at half resolution while it turns) A wgpu viewport drawing the same scene; simplified display meshes built off the UI thread
A job says only how long it has run The window's commands that may read the case run as jobs, with Cancel for those that only read (picking reads nothing: the view takes clicks once its scene is read; the commands given on the command line run before the window opens). Cancel stops a streamed read (ASCII points and faces, field files) within a megabyte, but a file read whole (binary, the mesh check's faces, owner and neighbour) only before the next one, and a job shows the seconds it has run, not how far it is Streaming the files still read whole; progress from the readers (files and bytes read)
Command parameters are typed as JSON in the desktop app Usable, not friendly Forms from typed parameter descriptions in the registry
No control channel yet umcf-gui --control (AGENTS.md §7) and ui.screenshot are not there; screenshots come from --screenshot A JSON-lines server in ui-egui over the engine
Only STL surfaces are checked OBJ, VTK and the other surface formats OpenFOAM reads in constant/triSurface are skipped by geometry.check Readers for OBJ and VTK in geom
Transparent layers are sorted by triangle, the farthest first, after the edges and points Where transparent triangles cross each other, or a long one lies over a short one, the nearer can be drawn first: a few pixels in the wrong order. A transparent surface also tints the wireframe edges and points behind it Depth peeling, or a GPU viewport
A surface command keeps a copy of the file per change, for undo Repairing a 500 MB surface ten times holds ten copies until the undo history (100 steps) drops them Keep one copy per surface and store the repairs as steps to replay, or cap undo by memory
Standard OpenFOAM boundary-condition and model types are not in the catalog A misspelt zeroGradient is not caught before the run; the condition card offers a short hand-written list of OpenFOAM's common types Extend xtask catalog to the OpenFOAM sources the solver builds against