Skip to content

Checking a case

The case check finds many of the mistakes that make urbanMicroclimateFoam stop at the start, or compute something other than what the case says (the codes below). Others, such as a misspelt standard OpenFOAM boundary condition type, are not caught yet.

The check reads nothing beyond what opening the case already read, so it is fast enough to run again after every edit.

Run it with umcf-cli check <case> (Command line), or from Rust with umcf_validate::check(&case). The GUI will run it before a run.

Each problem has a level, a code and the file it is about, and where there is one, the region, patch and field:

Level Meaning
Error The run fails, or doesn't do what the case says
Warning Probably a mistake, or something the check couldn't verify
Info Worth knowing

Boundary conditions against the mesh

Every field needs an entry for every patch of its region's mesh. umcfGui picks the entry for a patch the way OpenFOAM does (in both OpenFOAM-8 and 12):

A key written twice (quoted or not) is merged into its first place, entry by entry: a value written only the first time is kept, one written again takes the later value.

  1. the entry named after the patch;
  2. else an entry named after one of the patch's groups. If several match, the one written last wins. A patch's groups are its inGroups in the boundary file, plus the ones OpenFOAM adds itself:
  3. constraint patches (empty, cyclic, symmetryPlane, …) are in the group of their type;
  4. wall patches (wall, mappedWall, mappedWallVariableThickness, …) are in wall;
  5. mapped patches are in mapped (OpenFOAM-12) or mappedPatch (OpenFOAM-8).

A group with the same name as a patch is ignored, as OpenFOAM does. 3. else nothing, for an empty patch: OpenFOAM gives it empty itself; 4. else a quoted key that is exactly the patch name, else the last quoted pattern that matches the whole patch name, such as "(wall|roof).*". OpenFOAM reads patterns as POSIX extended regular expressions (glibc's, on Linux).

Code Level What it means
bc.missing Error A patch has no entry: OpenFOAM stops with Cannot find patchField entry. It is only Info when the boundaryField takes entries from an #include or $macro, or has a pattern this check can't read: the entry may be there
bc.no-boundary-field Error The field file has no boundaryField, and nothing (#include, $macro) that could bring one in
bc.constraint Error An empty, wedge, symmetryPlane, cyclic, … patch has a condition of another type and no patchType naming the patch type. Constraint patches need their own type. Only Info when the entry came from a group or pattern and the boundaryField also includes entries (#includeEtc "caseDicts/setConstraintTypes" brings group entries that win over patterns)
bc.unused Warning An entry or pattern matches no patch of the mesh: a renamed or removed patch, or a typo. Entries for processor patches (processor, "procBoundary.*" { type processor; }) are left alone: they are kept for the decomposed mesh
bc.pattern Warning A quoted pattern can't be read the way OpenFOAM reads it: it uses \d (a literal d for OpenFOAM; \w, \s and \b are fine), has unbalanced parentheses, or is too long. The patches it covers aren't checked
field.object Info The header's object is not the file's name: probably a copy (T_old saying object T). Errors in such a file are reported as Info
bc.unknown Error A compressible::CFDHAM… type that urbanMicroclimateFoam doesn't have, usually with a "did you mean"
bc.version Error An urbanMicroclimateFoam boundary condition that exists only in the solver for the other OpenFOAM version

Only field files are checked: files whose class is a field (volScalarField, surfaceScalarField, …). Data tables in a time directory, such as 0/air/Tambient read by a tableFile, are not, and neither are copies the solver never reads (T~, T.orig, T.bak, T.old, …). A type or patchType written as a $macro is not judged. Quoted words (type "empty";) read like plain ones, as in OpenFOAM. Standard OpenFOAM types (fixedValue, inletOutlet, …) are not in the solver catalog, so a misspelt one is not reported yet.

Coupled regions

Regions exchange heat and moisture through mapped* patches. The partner comes from neighbourRegion/neighbourPatch (OpenFOAM-12, which still reads the old names) or sampleRegion/samplePatch (OpenFOAM-8).

Code Level What it means
coupling.region Error The partner region is not one of the case's regions, or is empty (neighbourRegion "";: OpenFOAM stops reading the boundary file; next to an OpenFOAM-12 coupleGroup it is a coupling.group error). A patch without one couples within its own region
coupling.group Error An OpenFOAM-12 patch has a coupleGroup and also neighbourRegion, neighbourPatch (or the sample* names) or samePatch true: OpenFOAM-12 stops, it takes one or the other. OpenFOAM-8 ignores the extra keywords. mappedInternal reads no coupleGroup and is checked by its region
coupling.patch Error The partner region has no patch of that name. Not checked for patches that use no patch there: mappedInternal, sampleMode nearestCell, nearestOnlyCell or nearestFace, or samplePatch none
coupling.not-mutual Warning The partner points back into this region, but at another patch. One-way sampling (vegetation reading the air's patches) is normal and not reported

A patch with a coupleGroup finds its partner by group, so its region and patch names are not checked further.

A meshing step names regions that only exist once the steps are put together, so coupling is checked in solver cases only.

Solver types

The model types a region's dictionaries select are checked against the solver catalog for the case's OpenFOAM version. This covers vegetationModel, grassModel, solarLoadModel, the solar absorption and scattering models, and buildingMaterialModel. They are checked where the solver reads them: - the top level of vegetationProperties, grassProperties and solarLoadProperties; - each entry of the buildingMaterials ( { … } ) list in transportProperties.

These files sit directly in constant/<region>/ (or constant/). Other files, such as vegetationProperties.orig or a nested alternatives { … }, are left alone. A selector written twice counts once, with its last value.

Code Level What it means
model.unknown Error The solver has no such type. The message lists the ones it has, and the closest match. Only a Warning when controlDict loads extra libs, which may add it
model.version Error The type exists only in the solver for the other OpenFOAM version (for example OSB in an OpenFOAM-8 case)

Fields the solver reads

Every region needs the fields the solver reads for its kind of region, and the fields the models selected there read. A missing one stops the run at the start. The list comes from the solver source, per version (Solver coverage), plus what OpenFOAM itself reads for what the solver sets up:

Region Read by Fields
Fluid urbanMicroclimateFoam U, w, gcr, p_rgh at the start time, g in constant/<region>
Fluid OpenFOAM's thermophysics T, p
Fluid OpenFOAM's RAS or LES turbulence (momentumTransport) nut, alphat, and the RAS model's own: k and epsilon (kEpsilon, realizableKE, RNGkEpsilon), k and omega (kOmega, kOmegaSST)
Fluid vegetationModel simplifiedVegetation LAD, always from 0, whatever time the run starts from
Fluid RAS model porousrealizableKE (the solver's) k, epsilon; LAD from 0 (OpenFOAM-8) or constant/<region> (OpenFOAM-12)
Solid urbanMicroclimateFoam Ts, ws, pc, and g in constant/<region>
Vegetation urbanMicroclimateFoam T
Fluid, vegetation radiation model viewFactorSky (radiationProperties) qr
Fluid, vegetation solar load model directAndDiffuse (solarLoadProperties) qs

Notes: - Fields read only if present (phi, Tl, Tg) are not required. - Files the radiation utilities write (F, finalAgglom, …) are covered by the script checks. - A model can read different fields in each of its constructors. Only the constructors the solver builds it with count (it builds solarLoadModel from a field, so directAndDiffuse needs qs, not the Qs its dictionary constructor reads). A read in a member function, which may never run, is not required.

Code Level What it means
field.missing Error A field the solver or a selected model reads is not where it is read from. Only a Warning in a decomposed case, whose parallel run reads processor*/. Fields read from the start time are not reported for a region that has none there (region.no-fields); those read from 0 or constant/<region> still are

Field sizes

A nonuniform internalField has one value per cell of a volume field (vol…Field), per internal face of a surface field (surface…Field) and per point of a point field (point…Field). OpenFOAM stops on any other length. The check compares the list's length with the counts in the owner header of the mesh the run reads, which OpenFOAM writes there (nPoints:… nCells:… nFaces:… nInternalFaces:…): the region's mesh in the latest time directory up to the start time that has one (meshing without -overwrite writes one), else the one in constant. A mesh without that note is not checked.

Code Level What it means
field.size Error The internalField lists another number of values than the mesh has cells, internal faces or points: most often a field from another mesh (copied in, or left from before meshing again). Only a Warning in a decomposed case, whose parallel run reads processor*/, and Info in a copy (field.object)

Keywords the selected models need

The vegetation, grass, solar load and radiation model selected in a region's dictionary (vegetationProperties, grassProperties, solarLoadProperties, radiationProperties) reads its coefficients from <Type>Coeffs in the same file. The keywords it reads with lookup whenever the solver builds it must be there. Those are the keywords read in the constructors the solver uses, or in a function each of them calls, outside any condition (a block run only on the master process counts: every run has one) (Solver coverage). For example, simplifiedVegetation needs a1, a2, a3, C, D0, H, kc, l, nEvapSides, Rg0, Rl0 and rsMin, and viewFactorSky needs smoothing and constantEmissivity.

A dictionary whose entries can come from elsewhere (#include, $macro) or match by pattern ("a.*") is not judged.

Code Level What it means
keyword.missing Error A keyword the selected model reads when it is built is not in its <Type>Coeffs dictionary (or that dictionary is not in the file)

Regions and run controls

Code Level What it means
region.no-mesh Warning A region has no mesh in constant/<region>/polyMesh (not meshed yet, or the case is only decomposed)
region.no-fields Error A region of a solver case has no fields in the time directory the run starts from (for example startFrom latestTime and the latest time has only the solid regions). Only a Warning in a decomposed case, whose parallel run reads processor*/
control.application Warning application is not set in controlDict
control.value Error startFrom or stopAt has a value OpenFOAM doesn't know
control.end-time Error endTime is not after the start time, with stopAt endTime and startFrom startTime (or firstTime)
control.finished Info The run resumes from the latest time (startFrom latestTime, also OpenFOAM's default), which is already at endTime: it does nothing until endTime is raised
control.step Error deltaT or writeInterval is zero or negative, or writeInterval is below 1 with writeControl timeStep
case.version Warning The OpenFOAM version can't be told from the case, or the case has files for both (a partly ported case)
case.note Warning / Info What opening the case reported: files that didn't read, a missing start time, files the version ignores

Values written as $macros are not judged.

Scripts

The case's scripts (Allprepare, Allrun, Allclean, *.sh and batch job files) are read as described in The case workflow and checked against the case and its OpenFOAM version. Each problem names the script and line.

Code Level What it means
workflow.utility Error A utility the case's OpenFOAM version doesn't have: setSet and foamCleanPolyMesh are not in OpenFOAM-12, foamCleanCase and foamRun not in OpenFOAM-8, surfaceFeatureExtract in neither (OpenFOAM-12 only prints that surfaceFeatures replaced it). Only a Warning inside an if, and for OpenFOAM-12 names that run their replacement (simpleFoam runs foamRun -solver incompressibleFluid, postProcess runs foamPostProcess)
workflow.all-regions Error -allRegions (decomposePar, reconstructPar) in OpenFOAM-12 without regionSolvers in controlDict, from which OpenFOAM-12 takes the region list, or in OpenFOAM-8 without constant/regionProperties. A Warning when regionSolvers names other regions than the case has
workflow.np Error mpirun -np N (or runParallel) starts another number of processes than the case is decomposed into. A Warning when runParallel has no system/decomposeParDict to take the count from
workflow.ntasks Warning A batch job asks for fewer tasks (#SBATCH --ntasks) than it starts processes
workflow.decompose Warning OpenFOAM-8: regions decomposed into different counts (system/<r>/decomposeParDict, which decomposePar -region reads)
workflow.decomposed Warning The processor directories are from another decomposition than decomposeParDict says
workflow.calclai Error calcLAI runs, and the case has no region named vegetation: calcLAI reads that region by name
workflow.missing Warning A file a step reads isn't there when the step runs: a copy's or move's source, or the dictionary the utility reads, where the version reads it (for example system/<r>/topoSetDict; OpenFOAM-8 viewFactorsGen reads constant/<r>/viewFactorsDict). Earlier copies, moves, removals and meshing steps are followed, Allprepare then Allrun. After a command whose effect isn't known, the rest of the script (and of the scripts after it) is not judged; steps in an if, or after a test and &&, are not judged. blockMesh also finds its dictionary in constant/[<r>/]polyMesh/
workflow.overwrites Warning Allprepare or Allrun copies one dictionary over another that differs: the copy is what the run uses, and edits to the other are lost each time the script runs. Info for system/blockMeshDict and system/snappyHexMeshDict, the working copies of the region variants
workflow.shebang Warning The first line is a broken #! line ($#!/bin/sh): the script runs only because a shell runs it itself
workflow.custom Info A step that doesn't fit the workflow
workflow.not-needed Info A step that fits but isn't needed
workflow.description Warning The workflow description (system/umcfWorkflow) has a key it doesn't know or a value it can't read, names a region the case doesn't have or leaves one out, gives a region another kind, names a patch its mesh doesn't have or a partner that isn't there, couples a patch to no partner, or is for another OpenFOAM version. Info for a mesh patch it leaves out, and for a patch without a role

Finite-volume constraints and pressure limits (OpenFOAM-12)

Code Level What it means
fv.options-ignored Warning A region has both fvConstraints and fvOptions: OpenFOAM-12 reads fvOptions only when there is no fvConstraints, so its constraints (a limitTemperature, say) don't apply
fv.limit-pressure Warning A limitPressure constraint: urbanMicroclimateFoam (commit 5da4b3a) constrains the p_rgh equation and U, never p, so it has no effect
control.pressure-limits Error SIMPLE in a fluid region's fvSolution has pMin, pMax, pMinFactor, pMaxFactor, rhoMin or rhoMax: OpenFOAM-12 stops and asks for them in fvConstraints

Tested on

The check runs on the local reference cases: the Hong Kong project's meshing chain and runs, the 3×3 test cases and the large parallel cases, in OpenFOAM-8 and 12. These cases ran, so every error there must be real. Those found: - the solid-initialisation run of the Hong Kong project starts from its latest time, which has no air fields, so running it again would fail; - a 3×3 case ported to OpenFOAM-12 ends its Allrun with reconstructPar -allRegions and has no regionSolvers; its fvConstraints also hides the fvOptions with the air's temperature limit; - the large 3×3 cases (OpenFOAM-12) clean with foamCleanPolyMesh, which OpenFOAM-12 doesn't have.