Overset meshes
What is in this tutorial?
A CFD solver divides the space round an object into small cells, the mesh, and solves the flow equations in each cell. An overset mesh is a second mesh placed over the first. Where it lies, its cells take over: the cells of the mesh beneath are switched off, which is called hole cutting. Along the edge of the overlap, the interface, each mesh takes the values it needs from the nearest active cells of the other, its donor cells. This is useful when:
- a region needs a finer or different mesh than the one around it;
- a component is added to an existing mesh, such as a propeller on an aircraft, or a new building in a cityscape, without regenerating the whole mesh;
- parts move relative to each other, such as a train entering a tunnel.
zCFD solves the flow separately on each mesh, finds where each mesh is active, and handles the exchange between all of them, including when a run is split across several GPUs. Each overset boundary names the meshes it takes its data from. Overset meshes describes how this works.
This tutorial has two examples. The first places two cylinders over a refinement region over a background mesh, to show how overset meshes are set up and viewed. The second moves a train into a tunnel and checks the pressure wave it makes against a European standard.
What you need?
Work through Simple aerofoil first. The cylinders take about two and a half minutes on an NVIDIA RTX 3060. The train in a tunnel is far larger, about 8 million cells, and needs about 48 GB of GPU memory.
Example 1. Overset cylinders

Four meshes make up this case: a coarse background, a finer refinement region over it, and a mesh round each of two cylinders over that.
Step 1. Download the files
The bundle can be downloaded here.
cd ~
wget https://zcfd.zenotech.com/v2026.11.16174/tutorial_5_overset_cylinders.tgz
tar xzf tutorial_5_overset_cylinders.tgz
cd tutorial_5_overset_cylinders
It contains the control file overset_cylinders.py and three meshes: background.h5, refinement_region.h5 and
cylinder.h5, which both cylinders use.
Step 2. Walk through the input
The control file, overset_cylinders.py, is a short Python script. All it does is build one Python dictionary, called
parameters, that tells zCFD everything about the run: the physics, the numerical method, what to write out, and
the mesh with its boundaries. This step walks through that dictionary from top to bottom. Each heading below is the
name of a key, written exactly as it appears in the file, and the headings nest the same way the dictionary does:
numerical scheme, for example, sits inside solver. Under each heading you'll find what that part controls, what
each setting means, and why it has the value it has here. Each section ends with a link to that part's reference
page, which lists every setting it accepts and its default; you don't need it to follow the tutorial. On a wide
screen the whole control file sits on the right, with the part being described highlighted as you scroll.
Here model has one entry for each of the four meshes.
parameters = {
"config version": 2,
"solver": {
"solver settings": {"type": "compressible"},
"equations": {"type": "viscous", "precondition": True},
"fluid properties": {
"material": "air",
"gamma": 1.4,
"gas constant": 287.0,
"sutherlands const": 110.4,
"prandtl no": 0.72,
"turbulent prandtl no": 0.9,
},
"reference conditions": {
"IC_1": {
"temperature": 277.7777,
"pressure": 101325.0,
"v": {"vector": [1.0, 0.0, 0.0], "mach": 0.2},
"reynolds no": 20.0,
"reference length": 1.0,
}
},
"initialisation": {"initial conditions": "IC_1"},
"numerical scheme": {"order": "second"},
"time settings": {"type": "steady"},
"convergence control": {
"scheme": {"name": "implicit euler", "stage": 1},
"cfl": {
"cfl": 20,
"cfl viscous factor": 1.0e-6,
"cfl ramp": [{"type": "exponential", "factor": 1.05, "initial": 0.1}],
},
"cycles": 2000,
},
"output settings": {
"report": {
"frequency": 5,
"monitor": {
"MR_1": {
"name": "probe",
"point": [1.07, 0.25, 0.313],
"variables": ["cp"],
}
},
},
"solution": {
"format": "vtk",
"surface variables": ["V", "p", "T", "rho", "cp"],
"volume variables": ["V", "p", "T", "rho", "mach", "cp", "overset"],
},
},
},
"model": {
"background": {
"mesh": "background.h5",
"transforms": {
"transform": [
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, -0.5],
[0.0, 0.0, 0.0, 1.0],
]
},
"boundary conditions": {
"BC_1": {"zones": [9, 11], "type": "symmetry"},
"BC_2": {
"zones": [10],
"type": "farfield",
"condition": "IC_1",
"kind": "riemann",
},
},
},
"refinement_region": {
"mesh": "refinement_region.h5",
"boundary conditions": {
"BC_1": {"zones": [9, 11], "type": "symmetry"},
"BC_2": {
"zones": [10],
"type": "interface",
"kind": "overset",
"connection": {"model": ["background"]},
"additional active layers": 3,
},
},
},
"cylinder_2": {
"mesh": "cylinder.h5",
"transforms": {
"transform": [
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, -0.8],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
]
},
"boundary conditions": {
"BC_1": {"zones": [11, 12], "type": "symmetry"},
"BC_2": {
"zones": [13],
"type": "interface",
"kind": "overset",
"connection": {"model": ["refinement_region"]},
"additional active layers": 3,
},
"BC_3": {"zones": [10], "type": "wall", "kind": "no slip"},
},
},
"cylinder_1": {
"mesh": "cylinder.h5",
"transforms": {
"transform": [
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.8],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
]
},
"boundary conditions": {
"BC_1": {"zones": [11, 12], "type": "symmetry"},
"BC_2": {
"zones": [13],
"type": "interface",
"kind": "overset",
"connection": {"model": ["refinement_region"]},
"additional active layers": 3,
},
"BC_3": {"zones": [10], "type": "wall", "kind": "no slip"},
},
},
},
}
config version
"config version": 2 says which layout of the control file this is. It may be written with a space or an underscore.
solver
solver holds the settings shared by all four meshes.
solver settings
solver settings chooses the solver.
type:compressible, the density-based solver.
equations
equations sets which equations are solved.
type:viscous, the laminar Navier–Stokes equations; the flow round each cylinder is laminar at a Reynolds number of 20. The Reynolds number compares the flow's inertia with its viscosity.precondition:True, low-Mach-number preconditioning, recommended below about Mach 0.3.
fluid properties
fluid properties describes the gas.
material:air, an ideal gas, withgamma1.4 andgas constant287.0 J/kg/K.sutherlands const: 110.4 K, for the viscosity's change with temperature.prandtl no0.72 andturbulent prandtl no0.9; the turbulent one plays no part in a laminar run.
reference conditions
reference conditions defines named flow states.
IC_1
IC_1 is the freestream.
temperature277.7777 K andpressure101325 Pa.v:vector[1, 0, 0] andmach0.2, so the flow runs along +x at 66.8 m/s.reynolds no: 20, based on thereference length, 1.0 m, the diameter of each cylinder. Above a Reynolds number of about 47, a single cylinder sheds vortices: they peel off alternately behind it, so the flow never settles to a steady state. Two cylinders 1.6 diameters apart behave more like one wider body, so they start to shed sooner; 20 is chosen to stay below that, so the flow settles to a steady state. The run confirms it by converging.
initialisation
initialisation sets the starting state.
initial conditions:IC_1in every cell of every mesh.
numerical scheme
numerical scheme sets the spatial discretisation.
order:second, with the default Roe flux and van Albada limiter. Choosing a limiter covers both.
time settings
time settings says how the run advances.
type:steady. The flow at this Reynolds number does not change in time, so the solver simply iterates, in a made-up pseudo-time, until the solution stops changing. Choosing a time-marching scheme explains the choice.
convergence control
convergence control sets how the pseudo-time iterations converge to the
steady state.
scheme
name:implicit euler. An overset case needs an implicit pseudo-time scheme; see Practical consequences.stage: 1. It has no effect under implicit Euler, which has only one stage.
cfl
cfl: 20, the pseudo-time CFL number. This overset stack diverges in its first cycles at 100 or more.cfl viscous factor: 1.0e-6. It scales the viscous part of each cell's pseudo-time step estimate, so at this value the convective wave speeds alone set the step.cfl ramp: starts at 0.1 and grows by 1.05 a cycle, so the first cycles, from a flow that has not yet seen the cylinders, are gentle.
cycles
cycles: 2000, enough for the residuals, which measure how far the equations are from being satisfied, to fall several orders of magnitude, and for the probe to settle.
output settings
output settings sets the report, written as the run goes, and the solution
files. Each model gets its own report and solution files, in its own folder.
report
frequency: a row every 5 cycles in each model's report, with that model's residuals.monitor:MR_1,namedprobe, recordscpat thepoint[1.07, 0.25, 0.313], between and behind the cylinders. Every model's report has the columnprobe_cp, but only the model whose active cell holds the point fills it: here the refinement region. In the others it stays zero.
solution
format:vtk, VTK HDF files for ParaView.surface variables:V,p,T,rhoandcpon each model's boundaries.volume variables: the same, withmach, andoverset: 0 in a cell another mesh has taken over, 1 in a cell that is active, and 2 in a cell kept active only byadditional active layers, in the band where two meshes overlap. Step 5 uses it to show only the active cells.- With no
frequencyset, the solution files and the checkpoint are written once, at the end of the run.
model
model has one entry for each mesh. Each name, such as background, names that model's output
folder, overset_cylinders_OUTPUT/background/, and its files. The order matters: each mesh oversets meshes before
it in the list. background is walked in full below; the others differ only as each says.
background
The coarse mesh beneath the others, 100 m square.
mesh
background.h5, 10,000 cells.
transforms
transforms places the mesh when it is read.
transform: a 4 × 4 matrix whose last column translates the mesh. Here it moves the mesh 0.5 m down z, so that its span, built from z = 0.5 to 1.5, lines up with the other meshes' span, z = 0 to 1. Placing and moving a mesh describes the matrix.
boundary conditions
boundary conditions assign a condition to each group of the mesh's zones.
BC_1
zones[9, 11], the two faces normal to the span,typesymmetry, so one cell across the span is enough.
BC_2
zones[10], the outer boundary,typefarfieldwithconditionIC_1andkindriemann.
refinement_region
A finer Cartesian mesh round both cylinders, 12,800 cells in refinement_region.h5, with no transform.
boundary conditions
Its BC_1 is the same symmetry. Its BC_2 is where it meets the background:
BC_2
zones: [10], the outer boundary of the refinement region.typeinterfaceandkindoverset: the boundary of an overset mesh. The background cells that lie under the refinement region are not solved, and the two meshes exchange the solution across this boundary.connection:model["background"], the mesh it takes its data from. Naming it tells the solver exactly which pair of meshes to map; without it, the solver maps against every model before this one.additional active layers: 3. The hole the refinement region cuts in the background would otherwise match its own outline exactly, leaving no band where both meshes are solved, and the interface faces would find no active cell to take their data from. Three extra layers of background cells are kept active round the hole, so the two meshes overlap. Donor search explains why this matters.
Overset interface lists its other settings.
cylinder_2
The lower cylinder: cylinder.h5, 5040 cells round a cylinder of 1 m diameter.
transforms:transformmoves it 0.8 m down y.BC_1: symmetry on zones [11, 12].BC_2: an overset interface on zone 13, its outer boundary, withconnectionmodel["refinement_region"]and, for the same reason as the refinement region's,additional active layers3.BC_3: zone 10, the cylinder surface, a no-slipwall.
cylinder_1
The upper cylinder: the same mesh and conditions as cylinder_2, moved 0.8 m up y instead. One mesh file can serve
several models, each placed by its own transform.
The two cylinder meshes overlap each other between the cylinders. That is allowed, because each names its own donor, the refinement region; in each place the solution is taken from the topmost mesh.
Step 3. Run
In the activated environment, check the control file and run it:
cd ~/tutorial_5_overset_cylinders/
validate_input overset_cylinders.py
run_zcfd -c overset_cylinders.py
All four meshes are read from the mesh key of each model. These are extracts from the log of a run of this
tutorial, overset_cylinders.log, with ... marking lines left out:
Parameters validated successfully
...
Reading file background.h5
Global Mesh Stats: Cells: 10000 Faces: 40200
...
Reading file refinement_region.h5
Global Mesh Stats: Cells: 12800 Faces: 51440
...
Reading file cylinder.h5
Global Mesh Stats: Cells: 5040 Faces: 20280
...
Overset hole cut: blanked 168 background cells enclosed by the hole (inside the overset mesh's solid bodies)
...
Overset mapping: 104 of 288 (36.1111%) background halo centres found no active donor cell in the overset mesh. Those faces fall back to the unmapped face treatment instead of interpolated data. The meshes do not overlap deeply enough: increase 'additional active layers' on the overset interface.
...
============================================================
Overset Mesh Hierarchy (4 models, 6 pairs)
============================================================
background [root]
└── refinement_region [overset]
├── cylinder_2 [overset]
└── cylinder_1 [overset]
Peer overlaps: background ↔ cylinder_2, background ↔ cylinder_1, cylinder_2 ↔ cylinder_1
============================================================
...
Solve Complete: 2000 cycles in 2m 27.7s (avg 73.8 ms/cycle)
...
zCFD finished: all requested cycles are complete (2000 in total).
- Each mesh is read in turn;
cylinder.h5is read twice, once for each cylinder. - The hole cut blanks the background cells inside each cylinder.
- The hierarchy shows which mesh oversets which, as the
connectionentries ask. - As the run goes, a report box every few cycles gives each model's residuals separately.
- The log may print a few lines starting
Overset mapping:. Each reports the share of an interface's halo cells, the extra cells along the interface that hold the other mesh's values, that found no active donor cell to take data from; those faces use the unmapped-face treatment instead of interpolated data. They are diagnostics, not errors. The threeadditional active layersset above reduce them, and this case converges cleanly with the few that remain. Donor search explains these lines. - The run ends after its 2000 cycles.
Output files
| File/Directory | Description |
|---|---|
overset_cylinders.log | A copy of the output to screen during the run. |
overset_cylinders_status.yaml | How the run was started: version, date, control file, meshes. |
overset_cylinders_OUTPUT/background/ | The background's report, checkpoint, volume and surface files. |
overset_cylinders_OUTPUT/refinement_region/ | The same for the refinement region. |
overset_cylinders_OUTPUT/cylinder_1/ | The same for the upper cylinder. |
overset_cylinders_OUTPUT/cylinder_2/ | The same for the lower cylinder. |
overset_cylinders_OUTPUT/LOGGING/ | A separate log for each MPI rank, used for debugging. |
Each model folder holds files named after the model, such as cylinder_1/cylinder_1_report.csv and
cylinder_1/cylinder_1.vtkhdf.
Step 4. Monitor
Open the run in zMon:
zmon overset_cylinders.py
zMon shows each model's residuals and monitors against cycle; Monitoring a run describes the panels.
- Each model has its own residuals. The cylinder meshes start with the largest, as the flow first meets the cylinders, and fall more than four orders of magnitude. The refinement region starts from the uniform freestream, so its residual starts near zero, rises as the disturbance reaches it, then falls. The background's residual stays at round-off level, around 10<sup>-18</sup>, throughout.
- The
probe_cpmonitor sits in the wake between and behind the cylinders. Choose the refinement region's report: it is the mesh whose active cell holds the point, and the other models show zero. Once the run has converged the probe holds a steady value.
Step 5. Post-process
First activate your zCFD environment, which provides Python with zutil, h5py and ParaView:
source <ZCFD_INSTALL_PATH>/bin/activate
Python
This script prints each model's largest and final density residual, and the final value at the probe. Save it as
print_convergence.py beside the control file:
from zutil.fileutils import get_zcfd_result
result = get_zcfd_result("overset_cylinders.py")
for model, report in result.model_reports.items():
rho = report.data["rho"]
print(f"{model}: density residual fell from {rho.max():.1e} to {rho.iloc[-1]:.1e}")
probe = result.model_reports["refinement_region"].data["probe_cp"]
print(f"probe cp at the end of the run: {probe.iloc[-1]:.3f}")
python print_convergence.py
ParaView
Overset meshes keep their solutions separate, so you can load only the meshes covering the region you want. To view the whole domain as one, combine them in ParaView:
- Load the four files
overset_cylinders_OUTPUT/background/background.vtkhdf,.../refinement_region/refinement_region.vtkhdf,.../cylinder_1/cylinder_1.vtkhdfand.../cylinder_2/cylinder_2.vtkhdf.
- Select all four in the pipeline browser and apply Filters > Group Datasets. The new GroupDatasets1 holds all four.
- Apply Filters > Merge Blocks to GroupDatasets1. ParaView reads each .vtkhdf file as one block per partition, which Append Datasets cannot take; MergeBlocks1 is a single dataset holding the cells of all four meshes.
- Apply Filters > Threshold to MergeBlocks1 on the
oversetvariable, with the lower threshold raised from 0 to 1. Threshold1 keeps only the active cells. - Apply Filters > Cell Data to Point Data to Threshold1, which makes later interpolation more accurate.
- Apply Filters > Slice with its normal along z. The slice holds data from all four meshes.
- Colour Slice1 by
VMagnitude, with Surface With Edges in the representation toolbar.
Streamlines and other filters work the same way. Where meshes move during an unsteady run, ParaView updates their positions.
The cylinder meshes have holes where the cylinders are, so the refinement region shows through behind them. To hide it, add a cylinder of unit diameter with Sources > Cylinder at [0, 0, 0], rotate it 90° about x and translate it by −0.8 in y; repeat with +0.8 for the other cylinder. The view then looks like this:

To bring the cylinder meshes to the front instead, view each mesh separately and give each cylinder mesh a small offset towards the viewer, normal to the slice (0.0, 0.001, 0.0015, 0.002). That is closer to what the solver sees, since the covered cells of the meshes beneath are not solved.

Example 2. Train in a tunnel

A train entering a tunnel pushes a pressure wave ahead of it, which leaves the far end as a micro pressure wave: a boom. This example predicts the wave, with the set-up of Section 7.6 of EN 14067-5, Railway applications — Aerodynamics — Part 5: Requirements and test procedures for aerodynamics in tunnels [1]. For the reference train entering the reference tunnel at 250 km/h, the maximum entry pressure gradient dp/dt should lie between 8800 Pa/s and 9700 Pa/s.
Step 1. Download the files
The bundle can be downloaded here.
cd ~
wget https://zcfd.zenotech.com/v2026.11.16174/traintunnel.tgz
tar xzf traintunnel.tgz
cd traintunnel
It contains the control file traintunnel.py and two meshes: tunnel.h5, 7.07 million cells, and train.h5,
1.06 million.
Step 2. Walk through the input
The control file, traintunnel.py, is a short Python script. All it does is build one Python dictionary, called
parameters, that tells zCFD everything about the run: the physics, the numerical method, what to write out, and
the mesh with its boundaries. This step walks through that dictionary from top to bottom. Each heading below is the
name of a key, written exactly as it appears in the file, and the headings nest the same way the dictionary does:
numerical scheme, for example, sits inside solver. Under each heading you'll find what that part controls, what
each setting means, and why it has the value it has here. Each section ends with a link to that part's reference
page, which lists every setting it accepts and its default; you don't need it to follow the tutorial. On a wide
screen the whole control file sits on the right, with the part being described highlighted as you scroll.
Here model has two entries, the tunnel and the train. Where a block works as in the cylinders above, it
is covered briefly.
parameters = {
"config version": 2,
"solver": {
"solver settings": {"type": "compressible"},
"initialisation": {"initial conditions": "IC_1"},
"equations": {"type": "euler"},
"reference conditions": {
"IC_1": {
"temperature": 292.7777,
"pressure": 101325.0,
"v": {"vector": [1.0, 0.0, 0.0], "mach": 1e-10},
"reference length": 1.0,
}
},
"time settings": {
"type": "unsteady",
"kind": "dual time stepping",
"total time": 3.6,
"time step": 0.001,
"start": 0,
"order": 1,
},
"convergence control": {
"scheme": {"name": "implicit euler", "stage": 1},
"cfl": {"cfl": 30},
"cycles": 5,
},
"numerical scheme": {
"order": "second",
},
"fluid properties": {
"material": "ideal gas",
"gamma": 1.4,
"gas constant": 287.0,
"sutherlands const": 110.4,
"prandtl no": 0.72,
},
"output settings": {
"report": {
"frequency": 10,
"monitor": {
"MR_1": {
"name": "probe",
"point": [-140, 1.5, 0],
"variables": ["p"],
}
},
},
"solution": {
"format": "vtk",
"surface variables": ["overset", "V", "p", "T", "rho"],
"volume variables": ["overset", "V", "p", "T", "rho", "m", "sponge"],
},
},
},
"model": {
"background": {
"mesh": "tunnel.h5",
"boundary conditions": {
"BC_1": {"zones": [11, 13], "type": "symmetry"},
"BC_2": {
"zones": [12],
"type": "farfield",
"kind": "riemann",
"condition": "IC_1",
"sponge layer damping": {
"distance": 20.0,
"damping factor": 2.037495,
"condition": "IC_1",
},
},
},
},
"train": {
"mesh": "train.h5",
"transforms": {
"transform": [
[1.0, 0.0, 0.0, 110.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
],
"motion": {
"type": "translating",
"velocity": [-69.4, 0.0, 0.0],
"moving mesh": True,
"ramp time": 1.5,
},
},
"boundary conditions": {
"BC_1": {"zones": [10], "type": "symmetry"},
"BC_2": {
"zones": [9],
"type": "interface",
"kind": "overset",
"create overset halos": False,
"interpolation method": "nearest cell",
},
"BC_3": {"zones": [8], "type": "wall", "kind": "slip"},
},
},
},
}
config version
"config version": 2, the layout of the control file.
solver
solver holds the settings shared by both meshes.
solver settings
type:compressible, the density-based solver.
initialisation
initial conditions:IC_1in every cell: still air everywhere, with the train at rest.
equations
type:euler, the inviscid Euler equations. The pressure wave is a compressible, inviscid effect, so viscosity is left out, which keeps a case of this size affordable.
reference conditions
IC_1
temperature292.7777 K (19.6 °C) andpressure101325 Pa.v:vector[1, 0, 0] andmach1e-10: still air. The train moves; the air does not. The Mach number is not set to exactly zero, because the solver scales by it.reference length: 1.0 m.
time settings
typeunsteady,kinddual time stepping: the train moves, so the run follows the flow in time. Each physical time step is solved like a small steady problem, by the inner iterations set inconvergence control.total time: 3.6 s, long enough for the train to accelerate and enter the tunnel.time step: 0.001 s, 3600 steps.start: 0, no steady cycles first.order: 1, first order in time.
convergence control
scheme:implicit euler. Itsstageof 1 has no effect.cfl: 30, without a ramp.cycles: 5 inner cycles per time step.
numerical scheme
order:second, with the default Roe flux and van Albada limiter.
fluid properties
material:ideal gas, withgamma1.4 andgas constant287.0 J/kg/K.sutherlands constandprandtl noplay no part in an Euler run, which has no viscosity or heat conduction.
output settings
report
frequency: a row every 10 cycles.monitor:MR_1,namedprobe, records the pressurepat thepoint[-140, 1.5, 0], inside the tunnel. It falls in the tunnel mesh, so its columnprobe_pis filled intraintunnel_OUTPUT/background/background_report.csv. Its history gives the entry pressure gradient in Step 5.
solution
format:vtk, VTK HDF files for ParaView.surface variables:overset,V,p,Tandrho.volume variables: the same, with the Mach numbermandsponge, the damping applied by the sponge layer below.
model
background
The tunnel and the air round it. Its name gives the output folder, traintunnel_OUTPUT/background/.
mesh
tunnel.h5, 7.07 million cells.
boundary conditions
BC_1
zones[11, 13],typesymmetry.
BC_2
zones: [12], the outer boundary.type:farfield, withkindriemannandconditionIC_1.sponge layer damping: withindistance20 m of the farfield, the solution is damped towards itscondition,IC_1, at a rate rising to thedamping factor, 2.037495, at the boundary. The sponge absorbs pressure waves before they reach the farfield, where they would reflect back into the domain.
train
The train, with its output in traintunnel_OUTPUT/train/.
mesh
train.h5, 1.06 million cells.
transforms
transform: a 4 × 4 matrix whose last column places the train 110 m along x before it starts to move.motion: how the train moves during the run.type:translating, a straight-line motion.velocity: [-69.4, 0, 0] m/s, 250 km/h towards −x and the tunnel.moving mesh:True, so the train mesh itself moves through the tunnel mesh. Every time step it is moved to the position the motion gives at the end of the step, and its overset mappings are recalculated.ramp time: 1.5 s. The speed rises linearly from rest to the full velocity over the first 1.5 s, which avoids the transients of an impulsive start.
A moving mesh is allowed only in an unsteady compressible run, on a mesh that also has an overset or sliding
interface. Placing and moving a mesh describes transforms.
boundary conditions
BC_1
zones[10],typesymmetry.
BC_2
zones[9], the outer boundary of the train mesh,typeinterface,kindoverset. It names noconnection, so it is mapped against every model before it: here only the tunnel.create overset halos:False. Donor values are taken at the boundary face centres. The default samples at the halo cell centres, just outside the train mesh. On this moving train, some of those points have no donor in the tunnel mesh.interpolation method:nearest cell: each point takes the value of the donor cell that contains it.
BC_3
zones: [8], the train's surface.typewallwithkindslip: in the Euler equations the flow slides along a wall rather than stopping at it.
Step 3. Run
The case needs about 48 GB of GPU memory. Run it in the activated environment:
validate_input traintunnel.py
run_zcfd -c traintunnel.py
Step 4. Monitor
zmon traintunnel.py
zMon shows each model's residuals, and the probe_p monitor in the reports panel of the background model: the
pressure inside the tunnel rising as the train enters.
Step 5. Post-process
First activate your zCFD environment, which provides Python with zutil, h5py and ParaView:
source <ZCFD_INSTALL_PATH>/bin/activate
Python
This script plots the entry pressure gradient: how fast the pressure inside the tunnel rises, the time derivative
of probe_p. Save it as plot_entry_pressure_gradient.py in the case directory:
import matplotlib.pyplot as plt
import numpy as np
from zutil.fileutils import get_zcfd_result
time_step = 0.001
result = get_zcfd_result("traintunnel.py")
p = np.array(result.model_reports["background"].get_array("probe_p"))
dpdt = np.diff(p) / time_step
time = time_step * np.arange(len(dpdt))
fig, ax = plt.subplots()
ax.minorticks_on()
ax.grid(which="both")
ax.set_title(f"Maximum entry pressure gradient = {dpdt.max():.1f} Pa/s")
ax.set_xlabel("Time [s]")
ax.set_ylabel("dPdt [Pa/s]")
ax.set_xlim(0, 3.6)
ax.set_ylim(-100, 12000)
ax.plot(time, dpdt, label="250 km/h")
ax.axhline(8800, label="min valid dP/dT", c="grey")
ax.axhline(9700, label="max valid dP/dT", c="grey")
ax.legend()
fig.savefig("EntryPressureGradient.png")
Run it:
python plot_entry_pressure_gradient.py
It plots the entry pressure gradient against time. The maximum is 9660 Pa/s, within the 8800 Pa/s to 9700 Pa/s the standard specifies.

:::
Citations
- EN 14067-5:2006 - Railway applications - Aerodynamics - Part 5: Requirements and test procedures for aerodynamics in tunnels, see