Skip to main content

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 with block_cells and bounds, this defines the base grid before refinement.

  • dx_target, dx_toleranceAlternative to num_blocks and block_cells for defining the base grid.

    • Provide a target cell size [dx, dy, dz] and a one-sided relative tolerance dx_tolerance (e.g., 0.05 means [-5%, 0%]).
      The realized spacing is never coarser than the target, and may be up to dx_tolerance finer:

      • For each direction d ∈ {x,y,z}:
        dx_target[d] * (1 - dx_tolerance) ≤ dx_realized[d] ≤ dx_target[d].
    • 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 from dx_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.

Setting exchange cells

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 = 0 is 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_order still 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 ≠ dz is 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 from Spacing.
  • on_surface (optional, default false) — if true, triggers only near surfaces; some specifiers force this to true.
  • distance — physical distance threshold (supports expressions, e.g., "25*dxmin").

Supported specifier values:

  1. surface_component
    Fields:

    • geometry — a geometry name or "any".
    • component — a patch/component name on that geometry (must exist).
      Behavior:
    • on_surface is forced to true.
    • distance is required; blocks within distance of the selected surface component are refined.
  2. box
    Fields:

    • bounds[xmin, xmax, ymin, ymax, zmin, zmax] (expressions allowed).
      Behavior: refine blocks whose AABB intersects this box.
  3. sphere
    Fields:

    • center[x, y, z]
    • radius<expr or number>
      Behavior: refine blocks that intersect the sphere.
  4. 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.
  5. 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_component with component == "any") then the rest.
  • "any" is valid for geometry; the solver expands it to all registered geometries.
  • Expressions are resolved via Dictionary for 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/relative
  • time / relative_time — physical time t, absolute/relative
  • timestep / relative_timestep — step index nt, absolute/relative

Fields and defaults (from the code):

FieldMeaningDefault valueDefault specifier
start_intervalDon’t trigger before this threshold0timestep
update_intervalMinimum time between successive updates=output_intervaltimestep
output_intervalMinimum time between outputs (if used)10000timestep
stop_intervalStop 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_t with {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
}
}
}
}