Grid Generation Options
Grid
The Grid block defines the block-structured Cartesian mesh and its refinement.
The solver uses an octree-like, anisotropic refinement strategy: blocks can be refined by factors of two, and refinement can occur per direction (dx, dy, dz may differ).
All coordinates and lengths use the same unit system as the rest of the input (consistent across Geometries and Domain).
Core Parameters
-
bounds— Domain extents in solver units, ordered as[xmin, xmax, ymin, ymax, zmin, zmax]. -
block_cells— Integer triplet giving cells per block in(i, j, k). Each entry must be divisible by 4. -
num_blocks— Integer triplet giving the block lattice(Ni, Nj, Nk). Combined withblock_cellsandbounds, this defines the base grid before refinement. -
dx_target,dx_tolerance— Alternative tonum_blocksandblock_cellsfor defining the base grid.-
Provide a target cell size
[dx, dy, dz]and a one-sided relative tolerancedx_tolerance(e.g.,0.05means [-5%, 0%]).
The realized spacing is never coarser than the target, and may be up todx_tolerancefiner:- For each direction
d ∈ {x,y,z}:
dx_target[d] * (1 - dx_tolerance) ≤ dx_realized[d] ≤ dx_target[d].
- For each direction
-
The solver computes a block layout that meets the above, component-wise, supporting anisotropic vectors.
-
Refinement guarantee: The base spacing is chosen so that factor-2 octree refinements can exactly reach
dx_target.
If you later refine to the target, the AMR levels land exactly on power-of-two fractions of the base. -
Interaction with zone spacings:
If a refinement zone requests a spacing different fromdx_target, it will be realized exactly only if it is an integer power-of-two multiple of the base spacing in each direction.
Otherwise it is rounded to the nearest achievable level (never coarser than requested).
-
-
exchange_cells— Ghost/halo cells per direction[nx, ny, nz]. Must be even.
Usually, you can just set exchange_cells = [2, 2, 2] and forget about it.
-
exchange_order— Polynomial order used for data exchange between blocks of differing refinement (controls inter-level interpolation accuracy).
This is not a grid-generation parameter and remains applicable even when loading a grid file.exchange_order = 0is a special option that is actually 1st-order and is more robust for high-speed flow problems with shocks. -
grid_file— If a path is provided, the solver loads a prebuilt grid and ignores all grid-generation parameters.
exchange_orderstill applies.
Example (two ways to define the base grid)
A) Explicit topology
Grid
{
block_cells = [16, 16, 16]
num_blocks = [3, 3, 3]
bounds = [-30.0*Lscale, 30.0*Lscale, -30.0*Lscale, 30.0*Lscale, -30.0*Lscale, 30.0*Lscale]
exchange_cells = [2, 2, 2]
exchange_order = 2
grid_file = none
}
B) Target spacing
Grid
{
dx_target = [dxmin, dxmin, dxmin]
dx_tolerance = 0.05
bounds = [-0.9, 1.9, -1.3, 1.3, 0.0, 1.3]
exchange_cells = [2, 2, 2]
exchange_order = 0
grid_file = none
}
Refinement
Refinement is configured under Grid → Refinement.
It comprises a spacing dictionary (named [dx, dy, dz] triplets) and a set of refinement zones that apply those spacings near features or within regions.
Spacing
Define any number of named spacings. These names are referenced by zones and AMR rules.
Refinement
{
Spacing
{
body = [4*dxmin, 4*dxmin, 4*dxmin]
nose = [dxmin, dxmin, dxmin]
finest = [dxmin, dxmin, 0.5*dxmin] // anisotropic allowed
}
...
}
- Entries are expressions resolved against
Dictionary(e.g.,4*dxmin). - Anisotropy is supported:
dx ≠ dy ≠ dzis fine.
RefinementZones
A zone refines blocks that intersect a geometric predicate, to the target spacing (subject to the octree constraints).
A zone may also restrict refinement to near geometry surface via on_surface and distance.
Common fields across specifiers:
specifier— the zone type (see list below).spacing— a key fromSpacing.on_surface(optional, defaultfalse) — iftrue, triggers only near surfaces; some specifiers force this totrue.distance— physical distance threshold (supports expressions, e.g.,"25*dxmin").
Supported specifier values:
-
surface_component
Fields:geometry— a geometry name or"any".component— a patch/component name on that geometry (must exist).
Behavior:on_surfaceis forced totrue.distanceis required; blocks withindistanceof the selected surface component are refined.
-
box
Fields:bounds—[xmin, xmax, ymin, ymax, zmin, zmax](expressions allowed).
Behavior: refine blocks whose AABB intersects this box.
-
sphere
Fields:center—[x, y, z]radius—<expr or number>
Behavior: refine blocks that intersect the sphere.
-
cylinder
Fields:center—[x, y, z](point on axis)axis—[ax, ay, az](normalized internally)radius—<expr or number>height—<expr or number>
Behavior: refine blocks that intersect the finite cylinder.
-
triangulation
Fields:filename— surface mesh file; must be manifold.
Behavior: refine blocks whose AABB touches the triangulation boundary or whose center is inside the mesh.
Additional notes:
- Zones run in two passes: first “generic” surface zones (
surface_componentwithcomponent == "any") then the rest. "any"is valid forgeometry; the solver expands it to all registered geometries.- Expressions are resolved via
Dictionaryfor all numeric fields.
Example
Refinement
{
Spacing
{
body = [4*dxmin, 4*dxmin, 4*dxmin]
nose = [dxmin, dxmin, dxmin]
}
RefinementZones
{
surf_any
{
specifier = "surface_component"
geometry = "any"
component = "any"
spacing = "body"
distance = "25*dxmin"
}
nose_only
{
specifier = "surface_component"
geometry = "fuselage"
component = "nose"
spacing = "nose"
distance = "10*dxmin"
}
tip_box
{
specifier = "box"
bounds = [0.55, 0.90, -0.10, 0.10, 0.20, 0.55]
spacing = "nose"
}
bubble
{
specifier = "sphere"
center = [0.70, 0.00, 0.40]
radius = "5*dxmin"
spacing = "nose"
}
}
}
AMR (Adaptive Mesh Refinement)
AMR adds dynamic refinement during the run, based on solution fields.
Each AMR rule selects blocks to refine to a named spacing if a trigger condition is met.
Current support
specifier="relative_difference"variable="pressure"(future expansion planned)
Time Gating for AMR Rules
AMR rules are activated/updated according to four “time threshold” controls, each with a specifier that chooses the axis of time:
tchar/relative_tchar— characteristic time, absolute/relativetime/relative_time— physical timet, absolute/relativetimestep/relative_timestep— step indexnt, absolute/relative
Fields and defaults (from the code):
| Field | Meaning | Default value | Default specifier |
|---|---|---|---|
start_interval | Don’t trigger before this threshold | 0 | timestep |
update_interval | Minimum time between successive updates | =output_interval | timestep |
output_interval | Minimum time between outputs (if used) | 10000 | timestep |
stop_interval | Stop triggering after this threshold | +∞ | timestep |
Example
AMR
{
ref0
{
specifier = "relative_difference"
variable = "pressure"
threshold = 0.05
spacing = "nose"
start_interval = 500
start_specifier = relative_timestep
update_interval = 500
update_specifier = relative_timestep
stop_interval = 1000000000
}
}
Semantics:
- The solver tracks a
time_axis_twith{t, nt, tchar}and their relative counterparts. - A rule may act when (current − last) ≥ interval and we are within
[start, stop]. - Only update cadence matters for AMR refinement (output cadence is relevant in other features such as IO sampling).
Tips & Pitfalls
- Order your zones thoughtfully. The solver ensures “generic” zones run first, but overlapping zones can still interact; test key regions.
- Use dictionary expressions to keep spacing and distances consistent across the file.
Complete Example
Grid
{
block_cells = [12, 12, 12]
num_blocks = [4, 6, 6]
bounds = [-0.9, 1.9, -1.3, 1.3, 0.0, 1.3]
exchange_cells = [2, 2, 2]
exchange_order = 0
grid_file = none
Refinement
{
Spacing
{
body = [4*dxmin, 4*dxmin, 4*dxmin]
nose = [dxmin, dxmin, dxmin]
}
RefinementZones
{
surf2
{
specifier = "surface_component"
component = "any"
geometry = "any"
spacing = "body"
distance = "25*dxmin"
}
surf1
{
specifier = "surface_component"
component = "nose"
geometry = "any"
spacing = "nose"
distance = "10*dxmin"
}
}
AMR
{
ref0
{
specifier = "relative_difference"
variable = "pressure"
threshold = 0.05
spacing = "nose"
start_interval = 500
start_specifier = relative_timestep
update_interval = 500
update_specifier = relative_timestep
stop_interval = 1000000000
}
}
}
}