OpenFOAM Cases
An OpenFOAM case is a directory, not a file: the mesh lives in <case>/constant/polyMesh/. The extension reads and writes one.
Opening a case
Open the case's .foam marker — the empty <case>/something.foam file that ParaView also uses. VS Code editors bind to files, not folders, so the marker is the handle:
cavity/
cavity.foam ← open this
constant/polyMesh/
points faces owner neighbour boundaryIf your case has no marker, create an empty file with any name and a .foam extension next to constant/. Nothing reads its contents.
A case exported from this extension already has one.
What you get
- The volume cells as Elements.
- The boundary faces as Conditions.
- One SubModelPart per boundary patch, named as
constant/polyMesh/boundarynames it —inlet,outlet,movingWall. This is what lets you assign boundary conditions in the Problemtype section; without the names the boundary would be one anonymous surface.
points.gz, faces.gz and friends are decompressed automatically, so a case written with writeCompression on opens normally.
While the preview is open it watches constant/polyMesh/ for mesh changes and the time directories for new steps and field edits, so re-running blockMesh or advancing a solver refreshes the view in place.
Time-directory fields and the timeline
Numeric time directories (0, 0.5, 1e-3, …) are listed as the timeline: the preview's timeline bar scrubs them, Plot over time samples them, and mesh_field_series reads them headless. Each step shows the selected directory's fields over the selected mesh:
volScalarField/volVectorField/volTensorField/volSymmTensorFieldarrive as Elemental fields;pointScalarField/pointVectorFieldas Nodal fields.internalField uniform ...andnonuniform List<...>are read (plain or.gz); an explicit uniformboundaryFieldpatch value arrives as a Conditional field of the same variable.- The field file's
dimensions [..]vector (kg m s K mol A cd) is kept on the field. Field pickers, the scalar bar, capture legends and the CSV headers of the data table, time series and probe line then carry the unit (p [m²/s²],p_Pa [Pa]). A file with nodimensionsline leaves the field's units unknown — never guessed from its name — and its headers are unchanged. Comparing two fields whose dimensions are both known and different is refused; convert one first with Kinematic pressure → Pa (see Mesh editing), which takes an explicit density or density field. Export writes each field's recorded dimensions back, and[0 0 0 0 0 0 0]only for a field whose units are unknown. - Binary fields,
#include/coded/substituted content, surface fields and nonuniform patch values are skipped with a warning — the geometry still opens. - A directory whose
<time>/polyMeshexists overlays its files overconstant/polyMeshfor its own step (a moving mesh commonly overrides onlypoints); otherwise every step shares the constant mesh.
Headless, mesh_info reports the selected step's fields plus every available time value, and timeStep selects one (0 is the first, negative counts back from the last).
Zones, regions and decomposed cases
- Zones.
cellZones,faceZonesandpointZonesbecome SubModelParts, like the boundary patches. - Multi-region cases. A case with no top-level
constant/polyMeshbut oneconstant/<region>/polyMeshper region opens with every region merged. Each region is a top-level SubModelPart named after it, with its patches as children. A field present in several regions (sayTin bothfluidandsolid) arrives as one field spanning them. Headless,regionselects a single region instead. - Decomposed cases. A case written by
decomposePar(processorN/constant/polyMeshplus the*ProcAddressingfiles) is reconstructed into one mesh, and patch names are taken fromprocessor0's boundary. Fields underprocessorN/<time>/are not reconstructed; the Information panel says so. RunreconstructParfirst if you need them.
If constant/polyMesh/boundary is missing, the mesh loads with no boundary faces at all, because that file is what defines them. points, faces and owner are required; if one is missing, the open fails and names it.
Saving and exporting
Save writes back into a single-region case, but only the files it can own. The file you opened is an empty marker and the mesh is in sibling files, so Save compares your model with the case as it is on disk and lists exactly which files it will write before it does. Every other file in the case (system/, the rest of constant/, other time directories, sets/, …) is left byte-for-byte as it was, and nothing is ever deleted. There are two cases.
- Only geometry or field values changed (translate, scale, rotate, smooth, a computed field). Save rewrites just
constant/polyMesh/points— a point you did not move keeps its original text — and, for an edited field, only theinternalFieldentry of that field's file in the time directory on screen, so itsdimensionsand realboundaryFieldsurvive. A field that has no file yet is written new, withzeroGradienton every patch. Nothing keyed by cell, face or point number can go stale, so zones, sets and the other time directories all stay valid. - The mesh topology changed (refine, crop, remesh, renumber, a patch rename). Save rewrites the six polyMesh files plus the zone files (
cellZones/faceZones/pointZones, rewritten from the model's genuine zone parts — blocks and patches never qualify, so no new bogus zone is added), which renumbers things, so it refuses by file name when anything else in the case is keyed by the old numbering:sets/,cellLevel,pointLevel,refinementHistory,surfaceIndex, a moving-mesh<time>/polyMesh, and any time-directory field holding anonuniformlist. A zone file the model no longer carries would be deleted by the rewrite, so it blocks until removed explicitly. A case with polyhedral cells is refused too, because those are decomposed into tetrahedra on read. Remove or regenerate the named files yourself, or use Export.
Patch types that need extra keys, such as cyclic (neighbourPatch, transform, inGroups, …), now keep those keys when the boundary file is rewritten. A multi-region case saves topology edits per region (each changed region's polyMesh rewritten from its wrapper part, unchanged regions byte-identical) and field edits per region (surgical internalField replacement, or a wholesale rewrite from the model's values when the cell order changed); history in other time directories is never rewritten. A decomposed case saves coordinates per processor as before, field edits sliced per processor through cellProcAddressing, and topology edits by re-partitioning natively over the same processor count — new addressing, new processor patches, zones and fields rewritten per processor. Whatever cannot be matched is refused by file name instead of written approximately.
Use Export ▸ Solvers ▸ OpenFOAM or Save As… to write a new case directory instead. Exporting into the same directory under a different .foam name is refused too — it is the same constant/polyMesh, and only Save may rewrite it.
An exported case also gets a 0/<VAR> file for each Elemental field that covers every volume cell (volScalarField, volVectorField or volTensorField by component count, with fixedValue on a patch whose conditions all carry one uniform value of that variable and zeroGradient on every other patch). A field that covers only part of the mesh is skipped and named in a warning. Each file keeps its field's recorded dimensions, and [0 0 0 0 0 0 0] is written only for a field whose units are unknown.
Exporting to any other format (.mdpa, .vtu, …) is unrestricted. A problem archive bundles the marker plus constant/polyMesh/, so File ▸ Save problem works on a case directly.
Headless
mesh_info, mesh_convert, mesh_transform and mesh_field_series all take a .foam marker path like any other mesh. mesh_transform without outputPath and mesh_convert onto its own input are the in-place save and follow the same rules and refusals as Save; any other write into the case (a different .foam name beside it, a sub-part or derived export, a batch output) is refused. For a multi-region case, mesh_info and mesh_convert take region to read one region instead of all of them. mesh_info reports the patch SubModelParts, the selected step's fields and every available time value, and re-reads correctly after blockMesh reruns or a solver rewrites a field — its freshness is keyed on the polyMesh and time-directory files, not on the marker, which never changes.