Skip to content
Draft. This manual is new and still being checked against the software as it is verified. Some pages will change. If something here does not match what you see, the software is right — tell us and we will fix the page.

Case directory layout

What the application writes to disk, and where.

The claim this product makes is that it writes an ordinary OpenFOAM case — one you can open in a text editor, run from a shell with the bundled binaries, hand to a colleague who has never seen this application, or diff against a case somebody else built by hand. This page is here so you can check that rather than take it on trust.

The short version: everything under a study directory is standard OpenFOAM. Everything SHD Sim CFD adds sits outside it, in the case directory around it.


Cases live in a workspace directory. The default is Documents/SHD Sim CFD/My Cases, it is remembered between sessions, and --workspace=<dir> overrides it for one run.

My Cases/
└── Submerged body drag study/ <- one directory per case
├── case.json SHD Sim CFD
├── thumbnail.png SHD Sim CFD
├── geometry/ SHD Sim CFD
│ └── hull.step the imported file, copied in
└── run/ SHD Sim CFD
└── case/ <- an ordinary OpenFOAM case from here down

A case is a directory with a case.json in it — that is the test used on import, and an archive with no case.json anywhere is rejected.

The OpenFOAM directories live inside the case, not under AppData. A case has to be the whole case or it cannot be shared, archived or backed up.

Case has Directory
One study run/case/
More than one study run/<study name>/, one per study

Two studies of the same body have different meshes and different solutions, so each gets its own directory rather than meshing over the other’s polyMesh.

Study names are sanitised for the filesystem: \ / : * ? " < > | become _, whitespace runs become _, leading dots are stripped, and an empty result becomes case.

--dump-case=<dir> writes to <dir>/golden/ instead. With no case open at all — the dump and catalogue paths — the fallback root is runs/ under the application’s local data directory.


From here down, nothing is SHD Sim CFD’s own invention.

run/case/
├── 0/ one file per field
├── constant/
│ ├── polyMesh/ written by the mesher
│ ├── triSurface/ the geometry as ASCII STL
│ └── … physical properties
├── system/
│ ├── controlDict
│ ├── fvSchemes
│ ├── fvSolution
│ └── … utility dictionaries
├── <time>/ written by the solver
├── postProcessing/ written by function objects
├── processor*/ written by decomposePar
├── log.<application> one per utility and solver step
└── <case name>.foam an empty ParaView marker, on request
File
system/controlDict Run control, libs, and every function object
system/fvSchemes Discretisation
system/fvSolution Linear solvers and the SIMPLE/PIMPLE/PISO block
system/decomposeParDict Parallel decomposition
system/blockMeshDict The background block
system/snappyHexMeshDict Body-fitted meshing
system/surfaceFeatureExtractDict Feature edges — one entry per geometry part
system/meshQualityDict #includeEtc the shipped defaults, then override maxNonOrtho and minTetQuality
0/<field> One per field in the derived field set

constant/turbulenceProperties is written except for the Euler-Euler, electrostatic, magnetic, MHD, solid-stress, conduction and shallow-water families.

constant/transportProperties or constant/thermophysicalProperties — never both. They are alternatives: a solver that builds a thermophysical model reads the latter and ignores the former, so shipping both leaves one silently unused. Neither is written for the Euler-Euler, electrostatic, solid-stress or shallow-water families.

Family Adds
Marine & Waves constant/waveProperties
Euler-Euler constant/phaseProperties, and per phase constant/thermophysicalProperties.<phase> and constant/turbulenceProperties.<phase>
Lagrangian constant/kinematicCloudProperties
Compressible multiphase constant/thermophysicalProperties.<phase> per phase
Rotating frame (SRF) constant/SRFProperties
Solid stress constant/mechanicalProperties, constant/thermalProperties
Electrostatics, magnetostatics, MHD constant/physicalProperties
Shallow water constant/gravitationalProperties
Spray constant/sprayCloudProperties, constant/combustionProperties, constant/chemistryProperties, constant/reactions, constant/thermo
Premixed combustion constant/combustionProperties
Combustion / Reacting constant/combustionProperties, constant/chemistryProperties, constant/reactions, constant/thermo, constant/radiationProperties
Setting Adds
Gravity on, or a solver that requires it constant/g
A rotating zone constant/MRFProperties
Any cell zone to cut system/topoSetDict
A moving mesh constant/dynamicMeshDict
A 6-DoF body 0/pointDisplacement
An initialisation region, or a VoF fill level system/setFieldsDict
Any fvOptions item, or Coriolis constant/fvOptions
Mesh operation mirrorMesh system/mirrorMeshDict
Mesh operation extrudeMesh system/extrudeMeshDict
Mesh operation createPatch system/createPatchDict
Mesh operation refineMesh with a cell set system/refineMeshDict

Mesh operations that can be driven entirely from the command line — renumberMesh, subsetMesh and the rest — get no dictionary.

A conjugate case has a different shape entirely: almost nothing at the top level, because nothing is solved there.

run/case/
├── 0/<region>/<field>
├── constant/
│ ├── regionProperties
│ ├── g
│ └── <region>/thermophysicalProperties
│ └── <region>/turbulenceProperties fluid regions only
└── system/
├── controlDict fvSchemes fvSolution decomposeParDict
├── blockMeshDict snappyHexMeshDict
├── surfaceFeatureExtractDict meshQualityDict
└── <region>/fvSchemes
└── <region>/fvSolution

constant/regionProperties is what tells the solver — and the application, when it reads the case back — that this is a multi-region case.


One file per field in 0/. Which fields exist is derived from the analysis type and the turbulence model, not chosen; see Turbulence and viscosity models and Analysis types and solvers.

Each file carries dimensions, internalField and a boundaryField with one entry per patch. Dimensions are derived from the field name and are never asked for. The patch entries are the fan-out described in Boundary condition types.


Nothing below is written by the application. It is what the bundled OpenFOAM utilities produce, in the places they normally produce them.

Path Written by
constant/triSurface/body.stl, or one .stl per geometry part Written by the application before meshing, from the imported geometry
constant/triSurface/*.eMesh surfaceFeatureExtract
constant/polyMesh/ blockMesh, then snappyHexMesh
constant/<region>/polyMesh/ splitMeshRegions
<time>/ directories The solver
postProcessing/<function>/<time>/<file>.dat The function objects in controlDict
processor*/ decomposePar, run with -force so an earlier decomposition is replaced. reconstructPar reads them back afterwards
system/data The solver, as a cache of the last run’s residuals
log.<application> The application, capturing each step’s merged output

log.checkMesh, log.blockMesh, log.snappyHexMesh, log.simpleFoam and so on. This is the convention OpenFOAM users expect, and it is the only record left once the log dock is cleared or the application is closed. It is also what the application reads back to recover the mesh quality figures for a case it did not mesh in this session.

system/data is deleted before every fresh run. A diverged run writes nan into it and the next attempt then dies before its first iteration with Wrong token type — expected scalar value, found on line 2: word 'nan', which says nothing about the divergence that actually happened. The per-rank copies under processor*/system/data are deleted too, because decomposePar does not overwrite what is already in a processor directory.

snappyHexMesh -overwrite writes its layer-added points into 0/polyMesh/ and leaves the pre-layer positions in constant/. A time directory shadows constant/ entirely, so the solver would read one and the utilities the other.

Immediately after snapping, the application moves 0/polyMesh/* into constant/polyMesh/ — and 0/<region>/polyMesh/* into constant/<region>/polyMesh/ — and says so in the log. Everything downstream then reads the mesh the mesher actually produced.

This is a fix-up, not a format change. What ends up in constant/polyMesh is an ordinary polyMesh.


The case is regenerated in full on every write. Nothing is patched in place.

Directory On rewrite
0/ Written, and pruned — any field file no longer part of the case is deleted
constant/ Written over. Nothing is deleted
system/ Written over. Nothing is deleted

0/ is pruned because OpenFOAM reads every field in the start time directory. A field left behind by an earlier configuration is still read: switching a case to a laminar solver left 0/k, 0/omega and 0/nut on disk, and renumberMesh then failed on 0/k because its boundaryField named patches the current mesh no longer had.

constant/ and system/ are not pruned, deliberately. They can legitimately hold a dictionary the application does not generate — a chemistry mechanism, an optimisationDict — and deleting those would throw away work. A dictionary you add by hand to constant/ or system/ survives; a field you add to 0/ does not.

Fields are not written at all on the pass that imports a mesh. Until the converter has run, nobody knows what the patches are called.

Paths are sanitised before anything is written: absolute paths, drive letters, UNC prefixes, .. traversal and the characters < > : " | ? * are all refused, and the refused paths are reported rather than silently dropped.


The distinction this page exists to make.

Standard OpenFOAM — everything inside run/<study>/. The dictionary names, the directory structure, the FoamFile header, the keyword-and-semicolon syntax, the dimension sets, log.<application>, postProcessing/, processor*/, system/data, the time directories. A case copied out of run/ and run from a shell with the bundled binaries behaves exactly as it does here.

SHD Sim CFD’s own — everything outside it.

case.json The case model: analysis type, every setting, every study, the post-processing pipeline. This is what the application reads; it does not reconstruct a case by parsing the dictionaries back
thumbnail.png 320 px render of the viewport, captured on save, so the library is scannable
geometry/ The imported geometry file, copied into the case so the case is portable
run/ The container for study directories. Not an OpenFOAM concept
exports/ CSV, images and animation frames, stamped with the time they were written

Two things inside the case are ours in content but standard in form:

  • The banner comment at the top of every generated dictionary reads Generated by SHD Sim CFD and Do not edit by hand. It is a comment. The FoamFile block below it is standard.
  • The .foam marker file, written on request for ParaView, is named after the case rather than case.foam. It is empty either way — ParaView reads the directory it sits in.

Nothing in the case directory is encrypted, compressed, binary-only or keyed to an installation. writeFormat defaults to binary for the time directories, which is an OpenFOAM setting and can be changed to ascii; every dictionary the application writes is ASCII regardless.


Open the case directory from the log dock, or find it at

Documents/SHD Sim CFD/My Cases/<case>/run/<study>/

Read system/controlDict, then run the solver named in its application entry against that directory using the bundled binaries. If it behaves differently from the way it behaved inside the application, that is a defect worth reporting.