OpenFOAM: boundary conditions and physical models in a complete case

by giux CFD and OpenFOAM, Featured 38 min read

Seventh operating guide on OpenFOAM installed on a Linux server and used from Windows, after those on server and transfers, monitoring and convergence, quantitative analysis with ParaView, verification and validation, parameter sweeps and mesh generation. This one connects the mesh to the physical problem: boundary conditions, properties and models in a complete case. The deep-dive boxes open with a click: they contain every package file, the analytic reference solution for the duct with expected numerical values, and verification procedures.

Turning a mesh into a complete, verifiable, reproducible CFD case
Practical handbook · OpenFOAM Foundation 14 · Linux computation, Windows workstation

Edition: October 2026 · Level: intermediate · Original exercise licence: GNU GPL v3 or later.

Goal. Connect geometry, physical properties, initial and boundary conditions, numerical algorithms and measurable results. The accompanying package contains one complete case with three execution modes, always preserving the source templates. Optional background boxes can be skipped by readers familiar with fluid mechanics.

Channel diagram and boundary conditions
Channel diagram and boundary conditions

1. From a mesh to a physical problem

A valid mesh does not by itself define a simulation. We must select which equations to solve, which fluid properties to use, which values to impose on each patch, and how to assess whether the results are credible. Boundary conditions are part of the numerical experiment, not merely configuration syntax.

An OpenFOAM case contains 0/ (fields, initial and boundary conditions), constant/ (physical properties and models), system/ (control, discretisation and solution settings), and constant/polyMesh/ after meshing. The present example is steady, laminar, incompressible and isothermal; no temperature or turbulence fields are needed.

Background — governing equations. For a constant-density Newtonian incompressible fluid, continuity requires ∇·U = 0. Momentum conservation combines convection, the pressure gradient and viscous transport. The physical problem is defined by the boundary constraints in addition to the equations themselves.

Deep dive · The equations the incompressibleFluid module actually solves

For a constant-density Newtonian fluid, in kinematic form (divided by ρ):

∇·U = 0
∂U/∂t + ∇·(U U) = −∇p + ∇·[ ν_eff (∇U + (∇U)ᵀ) ]

p = p_phys / ρ      [m²/s²]
ν_eff = ν (laminare)  oppure  ν + ν_t (RAS/LES)

Two practical consequences. First: the solver does not know ρ, so neither pressure nor forces come out in SI units until multiplied by a declared density. Second: in incompressible flow pressure is not a thermodynamic variable but the multiplier enforcing ∇·U = 0; it is therefore defined up to a constant, which is why at least one reference value is needed (here p = 0 at the outlet). The div((nuEff*dev2(T(grad(U))))) term in fvSchemes corresponds to the transposed part of the viscous stress.

Deep dive · A well-posed problem: how many conditions, and where

For incompressible Navier–Stokes, on each portion of the boundary one generally prescribes either velocity (inlets, walls) or a “traction” condition, which OpenFOAM in practice approximates by fixing pressure and a zero velocity gradient (outlets). The proper combination for a duct is therefore: velocity or flow rate at the inlet with zero pressure gradient; fixed pressure at the outlet with zero velocity gradient; no-slip on walls. Prescribing both velocity and pressure on the same patch over-constrains the problem; prescribing pressure nowhere leaves it undetermined.

Deep dive · Why laminar: the dimensionless numbers of the case

In ducts, transition to turbulence occurs under ordinary conditions around Re ≈ 2000–2300 based on the hydraulic diameter; very disturbed inlets can bring it forward, very clean ones can delay it considerably. With Re ≈ 133 the case is laminar by a wide margin. The second number to look at is the ratio of channel length to entrance length, L/L_e ≈ 0.20/0.044 ≈ 4.5: the flow develops and stays developed for about three quarters of the channel, a necessary condition for comparing it with the analytic solution.

2. Version-specific dictionaries: Foundation 14

This handbook targets OpenFOAM Foundation 14, not an OpenCFD/ESI release. In Foundation 14, incompressibleFluid is selected with solver incompressibleFluid; inside system/controlDict and run using foamRun. Kinematic viscosity is defined in constant/physicalProperties using viscosityModel constant; and nu. The momentum transport model is selected in constant/momentumTransport using simulationType laminar;. Copying examples based on transportProperties, turbulenceProperties, application simpleFoam, or different executables can mean mixing incompatible conventions.

Check your installation:

source /opt/OpenFOAM-14/etc/bashrc
foamVersion
command -v foamRun

Rule: after changing OpenFOAM distributions or versions, start with the tutorials belonging to that installation and compare every dictionary. The files provided with this handbook never modify your OpenFOAM installation.

Deep dive · Foundation vs OpenCFD: the differences you meet immediately
AspectOpenFOAM Foundation 14OpenCFD/ESI and older Foundation releases
launchfoamRun with solver incompressibleFluid;simpleFoam, pimpleFoam (application)
viscosityconstant/physicalPropertiesconstant/transportProperties
turbulenceconstant/momentumTransportconstant/turbulenceProperties
algorithmdictionary PIMPLE also for steady casesSIMPLE / PIMPLE
versionfoamVersion → 14foamVersion or log banner

The quickest check is to read the banner of any log and compare file names with those of the installed tutorial. An extra dictionary does not always produce an error: it is simply ignored, which is the most insidious case because you believe you set something the solver never reads.

ls "$FOAM_TUTORIALS/incompressibleFluid"
ls "$FOAM_TUTORIALS/incompressibleFluid/pitzDailySteady/constant" "$FOAM_TUTORIALS/incompressibleFluid/pitzDailySteady/system"
foamDictionary "$FOAM_TUTORIALS/incompressibleFluid/pitzDailySteady/system/fvSolution" -keywords
Deep dive · Converting a simpleFoam case into a Foundation 14 case
In the original caseIn Foundation 14
application simpleFoam;solver incompressibleFluid; and launch with foamRun
constant/transportProperties: transportModel Newtonian; nu …;constant/physicalProperties: viscosityModel constant; nu …;
constant/turbulencePropertiesconstant/momentumTransport
dictionary SIMPLE in fvSolutiondictionary PIMPLE (see pitzDailySteady)
postProcess -func …foamPostProcess -func …

Conversion is not done from memory: copy the equivalent version-14 tutorial and transfer mesh, fields and values of the original case into it, checking each dictionary with foamDictionary -keywords. Read the log of the first run in full, looking for warnings about ignored entries.

3. Geometry and reference values

The example models a three-dimensional rectangular duct: length 0.20 m, height 0.010 m, depth 0.005 m. Its 80 × 16 × 8 = 10,240 cells have three boundary patches: inlet, outlet, and walls. The example fluid has ν = 1 × 10⁻⁶ m²/s and the first scenario sets U = (0.020, 0, 0) m/s at the inlet. We use ρ = 1000 kg/m³ only when converting computed kinematic pressure into pascals: the incompressible constant-kinematic-viscosity solver does not require it as an input.

QuantityValueInterpretation
Inlet area A5 × 10⁻⁵ m²Height × depth
Nominal flow Q1 × 10⁻⁶ m³/sQ = A · mean inlet speed
Hydraulic diameter Dₕ6.667 × 10⁻³ mDₕ = 2ab/(a+b)
Hydraulic Reynolds numberapproximately 133.3Re = U Dₕ / ν
Convective time L/U10 sA physical time estimate, not steady-state deltaT

The simple channel deliberately isolates the effects of changes to boundary conditions and viscosity. This is a controlled teaching example, not experimental validation or proof of mesh independence (see Guide 4).

Deep dive · The analytic solution for the rectangular duct: the numbers to expect

For fully developed laminar flow in a rectangular duct there is a series solution (Shah and London). With a 10 mm × 5 mm section (aspect ratio 0.5) the product of Darcy friction factor and Reynolds number is f_D·Re ≈ 62.19 (Fanning 15.55). Hence:

Quantityν = 1·10⁻⁶ m²/sν = 2·10⁻⁶ m²/s
Re (Dₕ)133.366.7
f_D0.4660.933
developed pressure gradient≈ 14.0 Pa/m≈ 28.0 Pa/m
Δp over 0.20 m, developed part only≈ 2.80 Pa (2.80·10⁻³ m²/s²)≈ 5.60 Pa (5.60·10⁻³ m²/s²)
centreline maximum velocity≈ 0.0398 m/s (1.99·U)≈ 0.0398 m/s
entrance length ≈ 0.05·Re·Dₕ≈ 44 mm (22 % del canale)≈ 22 mm

With a uniform inlet the computed Δp will be higher than the developed value: the entrance region adds a lumped loss due to core acceleration and higher wall shear. For a clean comparison with theory measure the gradient between two sections in the developed region, for example x = 0.10 m and x = 0.18 m, not between inlet and outlet. This is a genuine verification check, far more informative than a bare “STATUS OK”.

# expected developed gradient and Δp (Python, standard library only)
fRe, Dh, U, nu, rho, L = 62.19, 2*0.01*0.005/0.015, 0.02, 1e-6, 1000, 0.08
Re = U*Dh/nu
dpdx = fRe/Re/Dh * 0.5*rho*U**2          # Pa/m
print(Re, dpdx, dpdx*L, dpdx*L/rho)     # Re, Pa/m, Pa on L, m2/s2 on L
Deep dive · The case mesh and profile resolution

Cells are 2.5 mm in x and 0.625 mm in y and z: aspect ratio 4, aligned with the flow direction, where gradients are weak. There are 8 cells across the short side (5 mm) and 16 across the long side (10 mm): for a nearly parabolic profile this resolves the shape, but a quantitative comparison with the maximum velocity (1.99·U) or the pressure gradient requires the convergence study of Guide 4, for example with 40×8×4, 80×16×8 and 160×32×16 cells (ratio r = 2).

Deep dive · Why this channel cannot be reduced to 2D

The temptation to save cells with a two-dimensional model leads here to gross errors, because the depth (5 mm) is smaller than the height (10 mm) and the side walls dominate friction. Developed pressure gradient at the same mean velocity (0.02 m/s, μ = 10⁻³ Pa·s):

ModelFormulaGradient
3D rectangular ductf_D·Re = 62.19≈ 14.0 Pa/m
parallel plates, 10 mm gap (2D x–y)12·μ·U / h²2.4 Pa/m
parallel plates, 5 mm gap (2D x–z)12·μ·U / h²9.6 Pa/m

A 2D model is legitimate only when the neglected dimension is much larger than the others (section aspect ratio of at least 8–10) or when the problem is truly planar by symmetry. It is a modelling decision to be justified with numbers, not a computational shortcut.

4. Patches: the contract between mesh and fields

The mesh patch inlet in system/blockMeshDict must appear under exactly that name in both 0/U and 0/p; the same applies to outlet and walls. A geometrical patch type (patch, wall, symmetryPlane, etc.) is distinct from the field boundary type (fixedValue, zeroGradient, noSlip, etc.). These are not interchangeable.

Boundary values, gradients and physical models
Boundary values, gradients and physical models
ConditionWhat it imposesCommon use and limitation
fixedValueFixed field value (Dirichlet)Inlet velocity, pressure reference
zeroGradientZero normal gradient (homogeneous Neumann)Inlet pressure or simple velocity outlet
noSlipZero velocity on a stationary solid wallViscous walls; not equivalent to slip
flowRateInletVelocityPrescribed rate with an inferred normal velocityUseful when Q, rather than U, is known
pressureInletOutletVelocityVelocity condition on a pressure-type boundaryAccommodates reverse flow; does not cure a poor outlet placement
inletOutletOutlet condition with a fallback inflow valueOften used for transported scalars; requires inletValue
symmetryPlane / emptySpecial geometric constraintsMust agree with mesh type; empty applies only to genuinely 2D setups

Warning: zeroGradient does not mean zero pressure or prescribed flow. An outlet placed too close to a wake may affect the solution, whatever boundary formula is chosen; check domain length separately.

Deep dive · Patch type and condition type: which wins and why

The geometric type written in constant/polyMesh/boundary constrains the allowed conditions. empty, symmetryPlane, wedge and cyclic patches are constraint types: the field type must be the same, otherwise the solver stops. The wall type imposes nothing on velocity and pressure fields, but it is required by functions that look for walls: wall distance, RAS wall functions, y⁺ and wallShearStress computation. A wall declared as patch works in laminar flow but silently breaks a turbulent case.

foamDictionary constant/polyMesh/boundary -entry walls/type -value 2>/dev/null || grep -A3 walls constant/polyMesh/boundary
foamDictionary 0/U -entry boundaryField -keywords
foamDictionary 0/p -entry boundaryField -keywords
Deep dive · Inlets and outlets in real situations: the most used conditions
SituationUpNote
known flow rateflowRateInletVelocityzeroGradientuniform profile or, in recent versions, a profile option
measured profilefixedValue non-uniform or tabulatedzeroGradientthe profile must honour the flow rate
reservoir at known pressurepressureInletOutletVelocitytotalPressurethe flow rate becomes a result
outlet with possible backflowinletOutlet (inletValue (0 0 0))fixedValue / totalPressureprevents backflow from entering with arbitrary velocity
moving wallmovingWallVelocity / fixedValuezeroGradientlid-driven cavity, belts
frictionless wallslipzeroGradientonly where friction is truly negligible

Exact names and required entries of each condition are checked in the API documentation of your version or with foamInfo (Part 8 of the path).

foamInfo flowRateInletVelocity
foamInfo pressureInletOutletVelocity
foamInfo totalPressure
Deep dive · Time-dependent and mixed conditions

For an inlet varying in time (gradual start-up, pulsation) use a condition that reads a function of time, for example an interpolated table; for Robin-type conditions combining value and gradient there is mixed. Example of a 10 s inlet ramp, for transient cases only:

inlet
{
    type            uniformFixedValue;
    uniformValue    table
    (
        (0    (0    0 0))
        (10   (0.02 0 0))
    );
}

The syntax of time functions (table, tableFile, sine, …) is checked with foamInfo uniformFixedValue. In a steady case a ramp has no physical meaning: “time” counts iterations.

Deep dive · Pressure reference without a fixed-pressure patch

If no patch fixes pressure (for example a closed domain, or a duct with both inlet and outlet at fixed velocity), the pressure level is undetermined and must be fixed in one cell. In the algorithm dictionary add pRefCell (or pRefPoint) and pRefValue. In a closed domain the net imposed flow rate must be exactly zero, otherwise the pressure equation has no solution.

PIMPLE
{
    // ...
    pRefPoint   (0.1 0.005 0.0025);
    pRefValue   0;
}

5. Initial and boundary fields: 0/U and 0/p

The supplied velocity file 0/U sets an initially stationary interior and a uniform inlet:

dimensions      [0 1 -1 0 0 0 0];
internalField   uniform (0 0 0);
boundaryField
{
    inlet  { type fixedValue; value uniform (0.02 0 0); }
    outlet { type zeroGradient; }
    walls  { type noSlip; }
}

The file 0/p fixes a kinematic pressure reference at the outlet:

dimensions      [0 2 -2 0 0 0 0];
internalField   uniform 0;
boundaryField
{
    inlet  { type zeroGradient; }
    outlet { type fixedValue; value uniform 0; }
    walls  { type zeroGradient; }
}

Here, the imposed value p=0 defines a pressure reference; it does not mean the liquid’s absolute pressure is zero. For this incompressible formulation, Δp_physical = ρ · Δp_kinematic; the p field is expressed in m²/s², not in pascals.

Background — initial fields. In a steady-state calculation, internalField uniform (0 0 0) is an initial iterate, not necessarily a physical field at time zero. Inadequate initialisation can slow convergence or lead the numerical procedure toward an undesired solution.

Deep dive · The complete case files: 0/U and 0/p
/* OpenFOAM Foundation 14 - Guide 7 (original educational case) */
FoamFile
{
    version 2.0;
    format ascii;
    class volVectorField;
    location "0";
    object U;
}

dimensions      [0 1 -1 0 0 0 0];
internalField   uniform (0 0 0);
boundaryField
{
    inlet
    {
        type fixedValue;
        value uniform (0.02 0 0);
    }
    outlet
    {
        type zeroGradient;
    }
    walls
    {
        type noSlip;
    }
}
/* OpenFOAM Foundation 14 - Guide 7 (original educational case) */
FoamFile
{
    version 2.0;
    format ascii;
    class volScalarField;
    location "0";
    object p;
}

// Incompressible kinematic pressure: p_phys/rho, in m2/s2.
dimensions      [0 2 -2 0 0 0 0];
internalField   uniform 0;
boundaryField
{
    inlet
    {
        type zeroGradient;
    }
    outlet
    {
        type fixedValue;
        value uniform 0;
    }
    walls
    {
        type zeroGradient;
    }
}

Dimensions follow OpenFOAM’s SI order [mass length time temperature amount current luminosity]: [0 1 -1 0 0 0 0] is m/s, [0 2 -2 0 0 0 0] is m²/s². OpenFOAM checks the dimensional consistency of every operation: a pressure written in Pa [1 -1 -2 0 0 0 0] in a kinematic solver produces a dimension error, not a silent wrong result.

Deep dive · Better initialisation: potentialFoam and non-uniform fields

Starting from zero velocity is legitimate but slow: the first iterations must “invent” a field satisfying continuity. Two practical alternatives. The first is to initialise the internal field with the mean velocity, internalField uniform (0.02 0 0);, which already honours the flow rate. The second is to solve a potential flow satisfying ∇·U = 0 with the same boundary conditions and use it as the initial field:

potentialFoam -help
potentialFoam -writep > logs/log.potentialFoam 2>&1   # writes U (and p) into 0/

potentialFoam overwrites the fields in 0/: run it on the working copy, never on the template, and record the fact. In steady cases the final solution must not depend on initialisation; if it does, the problem has multiple solutions or is not converged.

Deep dive · Uniform inlet or developed profile: consequences for the measurements

The template’s uniform inlet is a convenience: it creates a development region of about 44 mm in which pressure gradient and wall stress exceed the developed value. If the goal is the behaviour of the channel within a network, impose the developed profile or extend the domain upstream and measure only downstream. A practical option, in versions that offer it, is the profile parameter of flowRateInletVelocity (laminar or turbulent boundary-layer profiles); in any case the choice must be declared on the case sheet.

6. Material, transport model and numerical method

The material file constant/physicalProperties contains:

viscosityModel constant;
nu             1e-06;

The file constant/momentumTransport contains:

simulationType laminar;

In system/controlDict, solver incompressibleFluid; chooses the solver module. In system/fvSchemes, ddtSchemes { default steadyState; } configures steady discretisation. In system/fvSolution, PIMPLE controls pressure-velocity coupling while solvers and relaxationFactors select the linear solvers and numerical relaxation. These represent different design choices: physics, momentum model and numerics.

Why not simply switch to kOmegaSST? A RAS turbulence model requires additional fields (k, omega, nut), consistent inlet turbulence conditions, appropriate wall functions and a near-wall mesh strategy. Changing one line is insufficient. With Re ≈ 133 in this channel, a turbulent model would not be justified solely by the nominal channel dimensions.

Deep dive · The complete constant/ and system/ dictionaries
/* OpenFOAM Foundation 14 - Guide 7 (original educational case) */
FoamFile
{
    version 2.0;
    format ascii;
    class dictionary;
    location "constant";
    object physicalProperties;
}

viscosityModel constant;
nu             1e-06;  // m2/s; example fluid
/* OpenFOAM Foundation 14 - Guide 7 (original educational case) */
FoamFile
{
    version 2.0;
    format ascii;
    class dictionary;
    location "constant";
    object momentumTransport;
}

simulationType laminar;
/* OpenFOAM Foundation 14 - Guide 7 (original educational case) */
FoamFile
{
    version 2.0;
    format ascii;
    class dictionary;
    location "system";
    object controlDict;
}

solver           incompressibleFluid;
startFrom        startTime;
startTime        0;
stopAt           endTime;
endTime          700;
deltaT           1;           // pseudo-time iteration for steadyState
writeControl     timeStep;
writeInterval    100;
purgeWrite       0;
writeFormat      ascii;
writePrecision   9;
writeCompression off;
timeFormat       general;
timePrecision    6;
runTimeModifiable true;

functions
{
    inletFlux
    {
        type surfaceFieldValue;
        libs ("libfieldFunctionObjects.so");
        log true;
        writeControl writeTime;
        writeFields false;
        patch inlet;
        operation sum;
        fields (phi);
    }
    outletFlux
    {
        type surfaceFieldValue;
        libs ("libfieldFunctionObjects.so");
        log true;
        writeControl writeTime;
        writeFields false;
        patch outlet;
        operation sum;
        fields (phi);
    }
}
/* OpenFOAM Foundation 14 - Guide 7 (original educational case) */
FoamFile
{
    version 2.0;
    format ascii;
    class dictionary;
    location "system";
    object fvSchemes;
}

ddtSchemes
{
    default steadyState;
}
gradSchemes
{
    default Gauss linear;
}
divSchemes
{
    default none;
    div(phi,U) bounded Gauss linearUpwind grad(U);
    div((nuEff*dev2(T(grad(U))))) Gauss linear;
}
laplacianSchemes
{
    default Gauss linear corrected;
}
interpolationSchemes
{
    default linear;
}
snGradSchemes
{
    default corrected;
}
wallDist
{
    method meshWave;
}
/* OpenFOAM Foundation 14 - Guide 7 (original educational case) */
FoamFile
{
    version 2.0;
    format ascii;
    class dictionary;
    location "system";
    object fvSolution;
}

solvers
{
    p
    {
        solver GAMG;
        smoother DIC;
        tolerance 1e-8;
        relTol 0.05;
    }
    pFinal
    {
        $p;
        relTol 0;
    }
    U
    {
        solver smoothSolver;
        smoother symGaussSeidel;
        tolerance 1e-10;
        relTol 0.1;
    }
    UFinal
    {
        $U;
        relTol 0;
    }
}
PIMPLE
{
    momentumPredictor no;
    nOuterCorrectors 1;
    nNonOrthogonalCorrectors 0;
}
relaxationFactors
{
    fields { p 0.3; }
    equations { U 0.7; }
}
/* OpenFOAM Foundation 14 - Guide 7 (original educational case) */
FoamFile
{
    version 2.0;
    format ascii;
    class dictionary;
    location "system";
    object blockMeshDict;
}

scale 1;
vertices
(
    (0 0 0) (0.20 0 0) (0.20 0.01 0) (0 0.01 0)
    (0 0 0.005) (0.20 0 0.005) (0.20 0.01 0.005) (0 0.01 0.005)
);
blocks ( hex (0 1 2 3 4 5 6 7) (80 16 8) simpleGrading (1 1 1) );
edges ();
boundary
(
    inlet
    {
        type patch;
        faces ((0 4 7 3));
    }
    outlet
    {
        type patch;
        faces ((1 2 6 5));
    }
    walls
    {
        type wall;
        faces ((0 1 5 4) (3 7 6 2) (0 3 2 1) (4 5 6 7));
    }
);
mergePatchPairs ();
Deep dive · The case fvSchemes and fvSolution, entry by entry
EntryChoiceEngineering rationale
ddtSchemessteadyStateno time term: “Time” counts iterations
div(phi,U)bounded Gauss linearUpwind grad(U)second-order upwind; bounded removes the non-zero ∇·U part during convergence, typical for steady runs
laplacianSchemesGauss linear correctedorthogonal mesh: non-orthogonal correction inactive but ready
wallDist meshWave—unused in laminar flow; needed by RAS models
p: GAMG + DICtolerance 1e-8, relTol 0.05multigrid for the elliptic pressure equation
U: smoothSolver symGaussSeidelrelTol 0.1diagonally dominant equation, a simple solver suffices
pFinal / UFinalrelTol 0settings of the last correction of the PIMPLE loop
momentumPredictor no—reduces cost per iteration at low Reynolds number
relaxationFactorsp 0.3, U 0.7classic SIMPLE values for stability

Parts 5 and 6 of the path explain schemes and solvers in detail. To know which entries your installation uses in steady cases, compare with the pitzDailySteady tutorial: it is the safest reference for version-14 keywords.

Deep dive · What it would really take to switch to a RAS model
  • constant/momentumTransport: simulationType RAS; and a sub-dictionary RAS { model kOmegaSST; turbulence on; printCoeffs on; }.
  • New fields 0/k, 0/omega, 0/nut, with consistent inlet conditions and wall functions (kqRWallFunction, omegaWallFunction, nutkWallFunction or low-Reynolds equivalents).
  • Schemes for div(phi,k) and div(phi,omega), solvers and relaxation for k and omega.
  • A wall mesh consistent with the chosen treatment (Guide 6, y⁺).

Typical inlet values: k = 1.5·(I·U)² with intensity I of a few percent and ω = √k / (C_μ^¼·ℓ) with C_μ = 0.09 and a length scale ℓ linked to the hydraulic diameter (for example 0.07·Dₕ). At Re ≈ 133 none of this is justified: a turbulence model in a laminar flow adds spurious viscosity and corrupts Δp. The correct starting point, when needed, is your installation’s RAS tutorial.

Deep dive · Non-Newtonian viscosity: where it is set and how to check it

With fluids such as blood, polymers or slurries the viscosity depends on the strain rate. In Foundation 14 the model is selected in constant/physicalProperties with viscosityModel; available names and required coefficients come from the installed tutorials, not from examples for other versions:

grep -rh --include=physicalProperties "viscosityModel" "$FOAM_TUTORIALS" | sort | uniq -c
grep -rl --include=physicalProperties -E "CrossPowerLaw|BirdCarreau|powerLaw|HerschelBulkley" "$FOAM_TUTORIALS" | head

With variable viscosity this guide’s analytic solution no longer holds, the profile flattens (shear-thinning fluids) and the Reynolds number must be defined with a declared reference viscosity. This step comes after the Newtonian case has been verified.

Deep dive · Relaxation and algorithm: convergence speed versus stability

With the classic factors p 0.3 and U 0.7 the SIMPLE algorithm converges robustly but slowly. The “consistent” variant (SIMPLEC), where available, allows higher factors (for example U 0.9 and no pressure relaxation) and considerably reduces the iteration count. The criterion for changing is empirical but measurable: compare the iterations needed to bring residuals below threshold and check that the final solution (Δp, Q) does not change. Version-14 keywords are checked in pitzDailySteady.

7. Working from Windows

The package layout is:

examples/channel_case/           # complete original template
examples/alternatives/           # flow-rate BC, alternative viscosity
scripts/run_case.sh              # creates unique copies and runs OpenFOAM
scripts/preflight.py             # optional static structural test
figures/                         # illustrations
results/                         # generated by the run script

Extract the package on Windows, edit plain-text configuration with Notepad++ while preserving UTF-8 and Unix LF line endings, and transfer the complete directory with FileZilla over SFTP. Open PowerShell and connect:

ssh user@server.example

From the directory containing examples/ and scripts/ on Linux:

source /opt/OpenFOAM-14/etc/bashrc
foamVersion
chmod +x scripts/run_case.sh
./scripts/run_case.sh velocity

The launcher determines its own directory and creates a unique results/velocity_..._XXXXXX working directory. No existing template or previous run is overwritten. For long calculations use tmux (Guide 1).

Safe workflow from case templates to recorded evidence
Safe workflow from case templates to recorded evidence
Deep dive · The complete run_case.sh script
#!/usr/bin/env bash
# GNU GPL v3 or later - Guide 7. Never changes the shipped template.
set -Eeuo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
TEMPLATE="$ROOT/examples/channel_case"
OUTPUT="$ROOT/results"
MODE="${1:-}"
case "$MODE" in
  velocity|flowrate|viscosity) ;;
  *) echo "Usage: $0 velocity|flowrate|viscosity" >&2; exit 2;;
esac
for executable in blockMesh checkMesh foamRun; do
  command -v "$executable" >/dev/null 2>&1 || { echo "Missing $executable: source OpenFOAM-14/etc/bashrc" >&2; exit 127; }
done
mkdir -p "$OUTPUT"
DEST="$(mktemp -d "$OUTPUT/${MODE}_$(date -u +%Y%m%dT%H%M%SZ)_XXXXXX")"
cp -a "$TEMPLATE/." "$DEST/"
mkdir -p "$DEST/logs"
if [[ "$MODE" == flowrate ]]; then
  cp "$ROOT/examples/alternatives/U_flowrate" "$DEST/0/U"
elif [[ "$MODE" == viscosity ]]; then
  cp "$ROOT/examples/alternatives/physicalProperties_high_nu" "$DEST/constant/physicalProperties"
fi
printf '%s\n' "Mode: $MODE" "Case: $DEST" "UTC: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >"$DEST/logs/RUN_INFO.txt"
CURRENT_STEP=initialisation
onexit() {
  status=$?
  if (( status == 0 )); then
    printf 'OK\n' >"$DEST/logs/STATUS.txt"
  else
    printf 'FAILED during %s (exit code %s)\n' "$CURRENT_STEP" "$status" >"$DEST/logs/STATUS.txt"
  fi
}
trap onexit EXIT
CURRENT_STEP=blockMesh
blockMesh -case "$DEST" >"$DEST/logs/log.blockMesh" 2>&1
CURRENT_STEP=checkMesh
checkMesh -case "$DEST" -allGeometry -allTopology >"$DEST/logs/log.checkMesh" 2>&1
CURRENT_STEP=foamRun
foamRun -case "$DEST" >"$DEST/logs/log.foamRun" 2>&1
printf 'Completed in %s\n' "$DEST"
  • mktemp -d … _XXXXXX creates a directory with a random suffix: two launches in the same second never collide.
  • The trap onexit EXIT writes STATUS.txt in every case, even when set -e stops the script: the file says at which stage and with which code it stopped.
  • Variants are obtained by replacing a single file in the copy (0/U or constant/physicalProperties): the template is never touched.
  • Exit codes: 2 for an unknown mode, 127 if an OpenFOAM executable is missing; otherwise the failed utility’s code, reported in STATUS.txt.
Deep dive · The preflight.py static check
#!/usr/bin/env python3
# GNU GPL v3 or later - static structural checks, no OpenFOAM runtime required.
from pathlib import Path
import re, sys
BASE = Path(__file__).resolve().parents[1]
CASE = BASE/'examples/channel_case'
check = [('0/U','volVectorField','U'),('0/p','volScalarField','p'),('constant/physicalProperties','dictionary','physicalProperties'),('constant/momentumTransport','dictionary','momentumTransport'),('system/blockMeshDict','dictionary','blockMeshDict'),('system/controlDict','dictionary','controlDict'),('system/fvSchemes','dictionary','fvSchemes'),('system/fvSolution','dictionary','fvSolution')]
for relative,kind,name in check:
    s=(CASE/relative).read_text()
    assert re.search(r'\bclass\s+'+kind+r'\s*;',s),relative
    assert re.search(r'\bobject\s+'+name+r'\s*;',s),relative
    assert s.count('{') == s.count('}'),relative
    assert s.count('(') == s.count(')'),relative
for fname in ('0/U','0/p'):
    s=(CASE/fname).read_text()
    for patch in ('inlet','outlet','walls'):
        assert re.search(r'\b'+patch+r'\s*\{',s),f'{fname} missing patch {patch}'
u=(CASE/'0/U').read_text()
p=(CASE/'0/p').read_text()
assert 'dimensions      [0 1 -1 0 0 0 0]' in u
assert 'dimensions      [0 2 -2 0 0 0 0]' in p
assert 'solver           incompressibleFluid' in (CASE/'system/controlDict').read_text()
A=.01*.005; U=.02; nu=1e-6; D=2*.01*.005/(.01+.005)
print(f'PASS: 8 dictionaries; patches match; 80*16*8=10240 cells; A={A:.8g} m2; Q={A*U:.8g} m3/s; Re_Dh={U*D/nu:.2f}')
print('WARNING: static check only; does not run blockMesh/checkMesh/foamRun.')

It also runs on Windows, before the transfer: it checks class and name of each dictionary, bracket balance, presence of the three patches in the fields, the dimensions of U and p and the selected solver, and prints reference values (area, flow rate, Reynolds). It does not replace execution: a syntactically correct dictionary can be physically wrong.

python scripts/preflight.py
Deep dive · Running the three variants in sequence, unattended

The three exercises are independent and short: they can be launched in sequence in a tmux session, with a final summary. Each variant creates its own directory, so one failure does not stop the others if set -e is avoided in the loop:

tmux new -s guide7
for m in velocity flowrate viscosity; do ./scripts/run_case.sh "$m" || echo "$m failed"; done
for d in results/*/; do printf '%s %s\n' "$d" "$(cat "$d/logs/STATUS.txt")"; done
python3 scripts/collect_fluxes.py results > flux_summary.csv

8. Three exercises: change one parameter at a time

Exercise A — fixed inlet velocity. Run ./scripts/run_case.sh velocity. It copies the template, executes blockMesh, runs checkMesh -allGeometry -allTopology, and starts foamRun. A new directory contains logs/log.blockMesh, logs/log.checkMesh, logs/log.foamRun, logs/STATUS.txt and logs/RUN_INFO.txt.

Exercise B — prescribed volumetric flow rate. Run ./scripts/run_case.sh flowrate. In the copied 0/U, the inlet is replaced by:

inlet
{
    type flowRateInletVelocity;
    volumetricFlowRate 1e-06;
}

With A = 5×10⁻⁵ m², this corresponds to 0.02 m/s mean inlet speed. The local profile need not be identical in every flow setup, but the pair tests two equivalent ways to specify the same nominal inlet. In flowRateInletVelocity, the positive 1e-06 denotes flow into the domain.

Exercise C — twice the kinematic viscosity. Run ./scripts/run_case.sh viscosity. The copied constant/physicalProperties changes ν to 2×10⁻⁶ m²/s, leaving the fixed inlet unchanged. The hydraulic Reynolds number drops to about 66.7. The pressure loss can increase; precise proportionality with viscosity presumes fully developed laminar conditions, not every inlet development region.

Reynolds number and kinematic viscosity for this example
Reynolds number and kinematic viscosity for this example
Deep dive · The two package variants, complete
/* OpenFOAM Foundation 14 - Guide 7 (original educational case) */
FoamFile
{
    version 2.0;
    format ascii;
    class volVectorField;
    location "0";
    object U;
}

dimensions      [0 1 -1 0 0 0 0];
internalField   uniform (0 0 0);
boundaryField
{
    inlet
    {
        type flowRateInletVelocity;
        volumetricFlowRate 1e-06; // m3/s (positive into domain)
    }
    outlet { type zeroGradient; }
    walls  { type noSlip; }
}
/* OpenFOAM Foundation 14 - Guide 7 (original educational case) */
FoamFile
{
    version 2.0;
    format ascii;
    class dictionary;
    location "constant";
    object physicalProperties;
}

viscosityModel constant;
nu             2e-06;  // m2/s

With flowRateInletVelocity and a uniform profile the imposed velocity is Q/A = 1·10⁻⁶ / 5·10⁻⁵ = 0.02 m/s: identical to exercise A. The A–B comparison is therefore a consistency test: the two solutions must coincide within solver tolerance. A noticeable difference points to an area, sign or unit error.

Deep dive · What must change between A and C, in numbers

Doubling ν at fixed velocity, in the developed region the pressure gradient doubles (about 14 → 28 Pa/m), because f_D = 62.19/Re and Re halves; the entrance length halves (44 → 22 mm); the developed profile and hence the maximum velocity stay the same, because in laminar flow the profile shape does not depend on ν. The total inlet–outlet Δp grows less than proportionally, because the entrance contribution grows less than the viscous part.

If the run shows a 2.00 ratio between gradients measured in the developed region, the case is verified; if it shows a ratio of 2 on the total Δp, you are probably comparing different quantities or the solution is not converged.

Deep dive · Two more exercises to complete the study
  • D — Outlet position. Extend the channel to 0.30 m (vertices and x cell count in proportion, 120 × 16 × 8) keeping everything else: the gradient measured between 0.10 and 0.18 m must not change. If it does, the outlet was affecting the solution.
  • E — Refinement. Repeat A with 40 × 8 × 4 and 160 × 32 × 16 cells (r = 2) and apply the Guide 4 procedure to the developed pressure gradient. Compare the extrapolated value with 14.0 Pa/m: a complete verification, numerical and analytic together. The finest mesh has 81,920 cells, still light.

9. Run checks, fluxes and conservation

The numerical outcome must be assessed from logs: STATUS.txt = OK means only that the process completed without reporting a command failure; it does not prove scientific convergence. Inspect FOAM FATAL ERROR, final residuals, continuity errors and the stability of physical observables. The two function objects inletFlux and outletFlux use surfaceFieldValue to sum phi on each patch, following the domain-outward normal convention:

Q_inlet  < 0    # inlet points inward; outward surface normal gives negative flux
Q_outlet > 0    # outlet flow has positive outward flux
Q_inlet + Q_outlet ≈ 0  # volumetric mass-balance consistency

In this incompressible solver, phi is a volumetric flux (m³/s). The example writes values at field-write intervals. Evaluate a relative conservation mismatch such as |Qin + Qout| / max(|Qin|, |Qout|) without treating any arbitrary tolerance as universal. For a pressure-drop study, area-average p on selected sections in ParaView (Guide 3), then multiply the kinematic pressure difference by the selected density to express it in Pa.

Background — conservation versus convergence. Low residuals do not guarantee correct conservation or sensible physical boundary constraints. Conversely, a small inlet–outlet flow mismatch alone does not certify an accurate velocity field. Both types of checks matter.

Deep dive · Convergence of a steady case: automatic criteria and quantity monitoring

The case runs 700 iterations without a stop criterion. In serious use a residual control is added, so the run stops when the initial residuals of all equations fall below declared thresholds. In version 14 the entry goes into the algorithm dictionary; check the exact syntax in the pitzDailySteady tutorial:

grep -n -A6 residualControl "$FOAM_TUTORIALS/incompressibleFluid/pitzDailySteady/system/fvSolution"

Residuals alone are not enough: the history of the quantities of interest goes alongside. Here the natural choice is to add mean-pressure measurements on two sections in the developed region and check that their difference stays constant over the last few hundred iterations. An example function object to add to functions in the working copy:

pSections
{
    type            surfaces;
    libs            ("libsampling.so");
    writeControl    writeTime;
    surfaceFormat   raw;
    fields          (p U);
    interpolationScheme cellPoint;
    surfaces
    {
        x010 { type cuttingPlane; point (0.10 0 0); normal (1 0 0); interpolate true; }
        x018 { type cuttingPlane; point (0.18 0 0); normal (1 0 0); interpolate true; }
    }
}

Sampling-surface keywords have changed between versions: before using it compare with foamInfo surfaces or the files in $FOAM_ETC/caseDicts/functions. Alternatively, section averages are computed in ParaView (Guide 3).

Deep dive · Continuity error in the log: how to read it

At every iteration the log prints a “time step continuity errors” line with three numbers: mean local error (sum local), global and cumulative. They are normalised errors on flux divergence after the pressure correction: they must become very small and the “global” one must oscillate around zero without drift. A drifting cumulative value indicates systematic mass loss, typically from too loose pressure tolerances.

grep "continuity errors" logs/log.foamRun | tail -n 3
grep -E "^Time =|Solving for (Ux|p)" logs/log.foamRun | tail -n 8
Deep dive · Comparing the velocity profile with the exact solution

In ParaView, Plot Over Line from (0.15, 0, 0.0025) to (0.15, 0.01, 0.0025) samples the profile across the height at mid-depth, in the developed region. The following script (standard library only) computes the series solution at the same 21 points; the RMSE between the two profiles, normalised by U, measures the local discretisation error (Guide 3).

#!/usr/bin/env python3
"""Analytic fully developed laminar profile in a rectangular duct (series solution).
Prints u/U_mean along the 10 mm side at mid-depth, to compare with a ParaView Plot Over Line CSV."""
import math
H, W, U = 0.010, 0.005, 0.02          # duct height (y), depth (z), mean velocity
a, b = W / 2, H / 2                   # half-widths: a short side, b long side
s = sum(math.tanh((2*k+1)*math.pi*b/(2*a)) / (2*k+1)**5 for k in range(200))
fac = 1 - 192*a/(math.pi**5*b)*s
G = 3*U/(a*a*fac)                      # (-dp/dx)/mu, from the mean velocity

def u(yy, zz, N=60):                  # yy across the short side, zz across the long side
    out = 0.0
    for k in range(N):
        n = 2*k + 1
        arg = n*math.pi*b/(2*a)
        r = math.cosh(n*math.pi*zz/(2*a))/math.cosh(arg) if arg < 700 else math.exp(n*math.pi*(abs(zz)-b)/(2*a))
        out += (-1)**k * (1 - r) * math.cos(n*math.pi*yy/(2*a)) / n**3
    return 16*a*a/math.pi**3 * G * out

print("y_m,u_over_Umean")
for i in range(21):
    y = H*i/20                        # 0 ... 10 mm, wall to wall
    print(f"{y:.4f},{u(0.0, y - b)/U:.4f}")
y [mm]012345678910
u/U00.991.531.821.951.991.951.821.530.990

The profile along the long side is flatter at the centre than a parabola, because at mid-depth the gradient is dominated by the nearby walls (short side). A Poiseuille parabola between parallel plates would give a maximum of 1.5·U: a result close to 1.5 would indicate a geometry error or an unintentionally two-dimensional case.

10. Common faults and targeted diagnosis

SymptomSuspected causeSpecific check
Missing patchName mismatch between mesh, 0/U or 0/pCompare constant/polyMesh/boundary with fields
physicalProperties errorMixing different OpenFOAM releasesCompare with $FOAM_TUTORIALS/incompressibleFluid/cylinder
Pressure dimension errorConfusing Pa with kinematic pressureExpected [0 2 -2 0 0 0 0]
DivergenceInconsistent inlet, poor mesh, aggressive schemeRead the first error, rerun checkMesh, simplify
Inlet/outlet imbalanceUnconverged solution or reverse flowCheck face-normal signs and iteration history
RAS model missing k, omegaEnabling a model without its fieldsStart from a valid RAS tutorial and verify wall conditions
Unexpected pressure dropGeometry, inlet development, units or wrong probesCheck dimensions, density and section placement

Altering a boundary condition changes the physical question. Always log and compare that change in a separate case copy, never silently modify a published result.

Deep dive · Reading the first error: anatomy of a FOAM FATAL ERROR

A fatal error reports the message, the file and often the line of the dictionary involved, and the code function that raised it. The useful message is the one before “From function”; the stack lines that follow are for developers. The most frequent errors at this stage: “Cannot find patchField entry for walls” (different patch name), “keyword nu is undefined” (dictionary from another release), “Different dimensions for (p + …)” (wrong units), “Unknown patchField type” (misspelt condition or not available in the version).

grep -n -m1 -B2 -A12 "FOAM FATAL" logs/log.foamRun
cat logs/STATUS.txt
Deep dive · Divergence in the first iterations: a four-step procedure
  • Read in which equation the residual blows up first (Ux or p) and at which iteration.
  • Re-run checkMesh and compare with Guide 6: a freshly modified mesh is the first suspect.
  • Reduce relaxation factors (for example p 0.2, U 0.5) and temporarily switch to bounded Gauss upwind for div(phi,U): if it then converges, the problem is numerical; if it still diverges, it is in the boundary conditions.
  • Restore the original settings starting from the converged solution (startFrom latestTime): the second-order scheme must be the one used for the final result.

11. What to retain as scientific evidence

For each variant, archive 0/, constant/, system/, solver/version, logs/, cell count, inlet/outlet fluxes, pressure difference, unit conventions, convergence criteria and the ParaView .pvsm state if used. FileZilla can download small logs and CSVs for Windows-based analysis; there is usually no reason to transfer every large flow-field file if pvserver can read them on Linux.

Final checklist

  • ☐ I have confirmed Foundation 14 and the incompressibleFluid module.
  • ☐ Every mesh patch matches its field configuration.
  • ☐ I have checked area, units and nominal flow rate.
  • ☐ The original templates remain unchanged.
  • ☐ Meshing, mesh validation and solution ran in an identifiable new case.
  • ☐ I have inspected convergence, conservation and physical observables.
  • ☐ I have distinguished kinematic pressure from pressure in pascals.
  • ☐ I have recorded exactly which setting differs across the three cases.
Deep dive · A ready-to-fill case sheet
FieldExercise A (design values)
version and solverOpenFOAM Foundation 14, foamRun / incompressibleFluid
mesh80×16×8 = 10,240 cells, checkMesh OK
fluidν = 1e-6 m²/s; ρ = 1000 kg/m³ (post-processing only)
inletfixedValue U = 0.02 m/s, Q = 1e-6 m³/s
outlet / wallsp = 0 (fixedValue) / noSlip
numericssteadyState, linearUpwind, PIMPLE, p 0.3 / U 0.7
stop criterion700 iterations (to be replaced by residualControl)
Q_in, Q_out, imbalancefrom collect_fluxes.py
p gradient in developed regionexpected ≈ 1.40e-2 m²/s² per m (14 Pa/m)
Deep dive · A fingerprinted delivery archive

The same scheme as Guide 5 applies here: one archive per variant, without the intermediate fields that can be regenerated, with file fingerprints and an environment description.

for d in results/*/; do
  ( cd "$d" && { date -u; foamVersion; } > logs/environment.txt 2>&1
    find 0 constant system logs postProcessing -type f -print0 | sort -z | xargs -0 sha256sum > logs/files.sha256 )
  tar czf "$(basename "$d").tar.gz" -C "$d" 0 constant/physicalProperties constant/momentumTransport system logs postProcessing
done

12. Operational appendix: patch measurements and comparison criteria

The function objects are defined in the functions section of system/controlDict. The supplied case already includes inletFlux and outletFlux, each summing phi over a selected mesh patch. For example, the actual inlet measurement block is:

inletFlux
{
    type surfaceFieldValue;
    libs ("libfieldFunctionObjects.so");
    log true;
    writeControl writeTime;
    writeFields false;
    patch inlet;
    operation sum;
    fields (phi);
}

A typical postProcessing/inletFlux/0/surfaceFieldValue.dat file stores a numerical time marker and a flux value on each data line. A corresponding outlet file is written under postProcessing/outletFlux/.... Do not compare an early inlet line with a late outlet line: measurements must refer to the same calculation state. The provided portable Python script scripts/collect_fluxes.py selects the latest available value for both objects in each run directory and generates a CSV. Missing data remain empty: values are never made up. You may download only logs/ and postProcessing/ with FileZilla, provided the nested directory layout is preserved.

python scripts/collect_fluxes.py results > flux_summary.csv

Worked measurement reasoning. An imposed flow of 1×10⁻⁶ m³/s is 1 mL/s. For the specified orientation, a physically consistent result should have approximately −1×10⁻⁶ m³/s through the inlet and +1×10⁻⁶ m³/s through the outlet. Do not imply more significant digits than the discretisation and solver justify. Compare pressure on defined physical sections, rather than arbitrarily chosen cells.

A common experimental-design error: change inlet flow, viscosity, mesh and outlet position all at once, then attribute the change in ΔP to a single cause. The three separate runs instead isolate one change at a time. For quantitative work, also check mesh independence, inlet-development effects and numerical sensitivity.

Background — iterative time. When ddtSchemes is set to steadyState, deltaT 1 and the printed Time values essentially index pseudo-time/iterations. A trend against this index is not a physical transient response. True unsteady analysis requires different temporal schemes, numerical controls, time-step verification and a Courant-number assessment.

Deep dive · The complete collect_fluxes.py script
#!/usr/bin/env python3
# GPL-3.0-or-later. Parse OpenFOAM 14 surfaceFieldValue output on Linux or Windows.
"""Usage: python scripts/collect_fluxes.py results > flux_summary.csv
Read last numeric time row from postProcessing/{inletFlux,outletFlux}/*/surfaceFieldValue.dat.
Missing values remain blank; script never fabricates simulation outputs.
"""
from pathlib import Path
from csv import writer
from sys import argv, stdout, stderr
from math import isfinite

def read_series(case, name):
    rows={}
    for f in (case/'postProcessing'/name).glob('*/surfaceFieldValue.dat'):
        with f.open(encoding='utf-8',errors='replace') as inp:
            for line in inp:
                if not line.strip() or line.lstrip().startswith('#'):
                    continue
                parts=line.split()
                if len(parts)<2:continue
                try: x=float(parts[0]);y=float(parts[1])
                except ValueError:continue
                if isfinite(x) and isfinite(y):
                    old=rows.get(x)
                    if old is None or f.stat().st_mtime >= old[0]:
                        rows[x]=(f.stat().st_mtime,y)
    return {t:value for t,(_,value) in rows.items()}

def main():
    if len(argv)!=2:
        print('usage: collect_fluxes.py <results-directory>',file=stderr);return 2
    root=Path(argv[1]);
    if not root.is_dir():
        print(f'not a directory: {root}',file=stderr);return 2
    cw=writer(stdout,lineterminator='\n')
    cw.writerow(['case','status','Q_inlet_m3s','Q_outlet_m3s','imbalance_relative'])
    for case in sorted(p for p in root.iterdir() if p.is_dir()):
        status_file=case/'logs'/'STATUS.txt'
        status=status_file.read_text(encoding='utf-8').strip() if status_file.exists() else 'UNKNOWN'
        ins=read_series(case,'inletFlux');outs=read_series(case,'outletFlux')
        common=ins.keys() & outs.keys()
        t=max(common) if common else None
        qi=ins.get(t);qo=outs.get(t)
        imb=abs(qi+qo)/max(abs(qi),abs(qo)) if qi is not None and qo is not None and max(abs(qi),abs(qo))>0 else None
        cw.writerow([case.name,status,qi if qi is not None else '',qo if qo is not None else '',imb if imb is not None else ''])
    return 0
if __name__=='__main__':raise SystemExit(main())
  • It keeps one value per time; if the same time appears in several files (restarts), the most recently modified file wins.
  • It uses the last time common to both measurements, so it never compares different times.
  • The imbalance is relative: |Qin + Qout| / max(|Qin|, |Qout|); if a value is missing the cell stays empty.

With the nominal case one expects Q_in ≈ −1·10⁻⁶ and Q_out ≈ +1·10⁻⁶ m³/s; the relative imbalance of a converged incompressible case is of the order of the pressure tolerance, well below 10⁻⁶.

Deep dive · Also measuring wall shear stress and comparing it with Δp

In the developed region the force balance on a volume of length L gives Δp·A = τ̄_w·P·L, with P the wetted perimeter (30 mm). It is a second independent check: with 14 Pa/m and A/P = Dₕ/4 = 1.667 mm the expected mean wall stress is τ̄_w ≈ 0.023 Pa. In OpenFOAM wall shear stress is computed with the wallShearStress function object (kinematic, multiply by ρ) and averaged over the patch in ParaView or with surfaceFieldValue.

foamPostProcess -help
foamPostProcess -func wallShearStress -latestTime   # on the working copy, after the run

13. Repeating measurements and building a credible comparison

For every run, retrieve at least logs/, postProcessing/, 0/, constant/ and system/ via FileZilla. Import the CSVs into your Windows analysis tool, label every column with its units, and record imposed speed/rate, ν, Re, measured flux, conservation mismatch, and ΔP when available. Do not draw a pressure-loss comparison until the measurement sections and averaging procedures are consistent across cases.

ComparisonHeld constantChanged variableInterpretation to check
A versus BNominal Q, ν and meshfixedValue versus flowRateInletVelocityPractical equivalence despite inlet-profile development
A versus CGeometry, imposed inlet U, meshν from 1×10⁻⁶ to 2×10⁻⁶Reynolds number and viscous resistance change
All threePatch names and solverOnly the declared parameterConservation, residuals and observable stability

Verification does not end when the script reports OK. Review the results independently: look for unexplained systematic changes, compare mean speeds and pressure gradients, assess entrance and outlet effects and plan mesh refinement. Each refined mesh must be saved as a new case, and the comparison extended according to Guide 4.

Deep dive · How small the difference between A and B must be

A and B impose the same inlet field, so the difference between their solutions measures only iterative noise. A reasonable engineering criterion: the difference in the quantities of interest (Δp between the two sections, Q) must be at least an order of magnitude smaller than any effect you want to attribute to physics, for example the A–C difference. If A and B differ by more than 1 %, iterative convergence is insufficient and the A–C comparison is not yet interpretable.

Deep dive · The comparison sheet: columns and units
case,inlet_bc,nu_m2_s,Re_Dh,Q_in_m3_s,Q_out_m3_s,imbalance_rel,dpdx_developed_Pa_m,dp_total_Pa,umax_m_s,iterations,notes
A_velocity,fixedValue 0.02,1e-6,133.3,,,,,,,,
B_flowrate,flowRateInletVelocity 1e-6,1e-6,133.3,,,,,,,,
C_viscosity,fixedValue 0.02,2e-6,66.7,,,,,,,,

Expected values for verification: dpdx_developed ≈ 14.0 / 14.0 / 28.0 Pa/m; umax ≈ 0.0398 m/s in all three cases. Empty columns are filled with data from collect_fluxes.py and ParaView; units are in the column name, so the file stays readable out of context.

14. Official sources and verification limits

Verification status: exercise files have passed static checks for structure, patch names, scripts, units and hand-calculated reference quantities. First execution on a Linux server should be treated as an acceptance test; use Guides 2–4 for numerical and physical verification.

Deep dive · Reading for the next step
  • R. K. Shah, A. L. London, Laminar Flow Forced Convection in Ducts, Academic Press, 1978. Solutions and correlations for ducts of any section, including f·Re for rectangles.
  • F. M. White, Viscous Fluid Flow, McGraw-Hill. Laminar duct flow, entrance length, boundary layer.
  • S. B. Pope, Turbulent Flows, Cambridge University Press, 2000. Foundations of the RAS and LES models mentioned in section 6.
  • Tutorials in your installation: incompressibleFluid/pitzDailySteady, incompressibleFluid/cylinder, to compare entry by entry with the guide’s case.

Related guides: Linux server from Windows, monitoring and convergence, ParaView for quantitative analysis, verification and validation, parameter sweeps, mesh generation and quality checks and the Learning OpenFOAM 14 path.

#boundary conditions #CFD #incompressibleFluid #OpenFOAM #physical models