Skip to content

Ghost probe: every face and edge ghost on a linear field ​

IDGroupMilestoneBackendsFigureRegression
GPInfrastructureM4 (#65 P0, P1), M6 (#68 C2, D1)CPU, FNLyes—

Case and scope ​

The directional WENO stencils read only the face ghosts: the cells outside a block along one axis. The dissipative terms of M4 take cross derivatives, such as the tangential derivatives of the viscous stress or of the current at a face. Those read the edge ghosts too: the cells outside a block along two axes, in the rows next to a block edge. Before M4 nothing checked what an edge ghost held. GP checks every one, on every path that fills it: the same-level copy and the MPI exchange, the 2:1 restriction and tricubic fill, the inter-realm seams (1:1 mirror, misaligned, 2:1 refined), and the physical boundary conditions.

Oracle. Each case holds a linear field, q=q1(1+g⋅x), with every velocity component non-zero so that every wall sign flip shows. The copy, the restriction and the tricubic fill reproduce such a field exactly. Each case takes one step at CFL = 1e-30, which moves the field by ∼10−30 relative; the post-step write then fills the ghosts in the order of a Runge–Kutta stage (seams, then the intra-realm exchange and the boundary conditions) and writes them. ghost_probe.py compares every ghost with the value a stencil must read:

  • the field at the ghost's own centre, inside the realm or across a seam;
  • along an axis where the ghost lies beyond a physical face, that face's condition applied to the value of its donor: the coordinate mirrored with the normal momentum negated for an inviscid wall (and the normal field on MHD); mirrored with the velocity reflected about the wall velocity for a no-slip wall (and the temperature Tw2/T for an isothermal one); the first interior cell for extrapolation; the inflow state for inflow; the wrapped coordinate for periodic. Beyond two faces the conditions compose in the backends' order (inflow first, else the first face in axis order acts on a donor valued the same way).

The no-slip walls are not linear in the state; the probe evaluates them with an independent implementation of the rule of Boundary conditions, so walls3d checks the plumbing (which donor, which face, which wall parameters, in which order), not the physics of the wall. The physics comes with the viscous fluxes (VV-3, Couette).

The poison. The negligible step leaves a stale ghost equal to a fresh one, so the oracle alone cannot see a ghost read before its donor is written. Every case therefore runs with [diagnostics] ghost_poison = .true.: each ghost is set to NaN before the fill, and a ghost that reads an unfilled donor is written as NaN, which the probe reports as an infinite error. Its negative control is the defect it found (see Findings).

The polynomial probe (#68 D1). check.sh --degree N starts from the field q1(1+∑dgdxdN) instead. The copies and the boundary conditions still reproduce it, but each seam operator only up to its degree (the mean restriction 1, tricubic 3, lagrange6 and quintic 5), so at N>1 the probe checks the high-order operators in situ: their footprints, anchor shifts and every exchange path.

Models and numerics ​

ItemChoiceCode
ModelsEuler; ideal MHD with GLM (box3d) and with EGLM (walls3d), γ=1.4models
Stepone step at CFL = 1e-30 (WENO5 splitting, SSP-33; the update is negligible)compute_residuals_weno, adam_flume_cpu_object.F90
Poisonevery ghost NaN before the fill of a field writepoison_ghosts, adam_flume_cpu_object.F90; poison_ghosts_dev, adam_flume_fnl_kernels.F90
Intra-realm exchangesame-level copy, MPI exchange, 2:1 restriction and fill (tricubic default; lagrange6, quintic options)update_ghost, adam_flume_cpu_object.F90; update_ghost_local, update_ghost_mpi, interp_seam_ghost, restrict_seam_high_order, adam_field_object.F90; boundary conditions
Inter-realm seams1:1 mirror, misaligned, 2:1 refinedfill_seam_from_peer_forest, adam_flume_cpu_object.F90; seam_fill, adam_seam_exchange.F90; boundary conditions
Physical boundariesinflow, extrapolation, periodic, inviscid, no-slip and isothermal walls; edges compose the conditions of their facesset_boundary_conditions, adam_flume_cpu_object.F90; realm_edge_face, realm_edge_donor, wall_noslip_ghost, adam_flume_bc_object.F90; set_boundary_conditions_dev, adam_flume_fnl_kernels.F90; boundary conditions
Field writepost-step save, ghosts refilled in Runge–Kutta stage order (seams first)post_step_forest, save_simulation_data, adam_flume_cpu_object.F90

Initial and boundary conditions ​

Common to every case: linear initial condition with q1=(ρ,u,v,w,p)=(1,0.3,−0.2,0.1,1) and B=(0.8,0.5,−0.3) on MHD, g=(0.05,0.11,0.07) (gz=0 on a null z axis); ngc = 3; it_max = 0, time_max = 0, CFL = 1e-30; ghost_poison = .true.; MHD cleaning glm_ch = 3, glm_alpha = 0.18.

Case (make_probe.py)GridBoundariesPaths
box3d (Euler and MHD)[0,1]3, octree, 2×2×2 blocks of 83, the block at the origin refinedxmin inflow (ρ,u,v,w,p=1.2,0.4,0.1,−0.1,1.1), ymin, ymax, zmin inviscid walls, xmax, zmax extrapolation2:1 seams meeting an inflow face and two walls in 3-D
channel2d[0,2]×[0,1], quadtree (nk = 1), blocks of 82, two uniform levels, the box [0,0.5]×[0,0.25] refinedx periodic, ymin inviscid wall, ymax extrapolationthe 2:1 seam crossing the periodic boundary at the wall
mirror3dtwo realms [0,1]3 and [1,2]×[0,1]2, each 2×2×2 blocks of 831:1 mirror seam at x=1; xmin inflow, y and zmin walls, the rest extrapolationseam edges in every plane
refined3das mirror3d, the second realm one level finera coupling = refined (2:1) seamthe 2:1 inter-realm seam
walls3d (Euler and MHD with EGLM)as box3dymin moving wall-noslip (wall_u = 0.2, wall_w = -0.1), ymax resting wall-noslip, xmax wall-isothermal (wall_v = 0.15, wall_temperature = 0.9), zmin inviscid wall, xmin inflow, zmax extrapolationthe no-slip walls of #65 P1 around a refined corner
stepthe Woodward–Colella forest at N=80 with its boxes, linearised (v=0.5, w=0.3)inflow, outflow, wallsmisaligned mirror seams, a re-entrant corner
RunRanks
every case, CPU1, 2, 3
every case, FNL1, 2

Faces and edges must hold the expected value to 10−12 of the field scale. Corners are reported, not asserted, since no M4 stencil reads them, and neither are the ghosts that lie outside every realm (solid, inside the step).

Input files ​

FileRole
src/tests/flume/verification/ghosts/make_probe.pywrites the cases; --linearize rewrites the step forest into a probe run
src/tests/flume/verification/step/make_step.pythe step forest (--cells 80 --refine)
src/tests/flume/verification/ghosts/check.shruns the cases (--np "1 2 3", --legs LIST: a leg is a case, box3d, box3d-mhd, channel2d, mirror3d, refined3d, walls3d, walls3d-mhd, step, default all; --keep, --seam-restriction R, --seam-ghost-fill F, --degree N)
src/tests/flume/verification/ghosts/ghost_probe.pythe expected value of every ghost, the report by class (face, edge, corner) and by what lies beyond, the bound (--tol 1e-12)
src/tests/flume/verification/make_doc_figures.py (fig_ghosts)the figure, from ghosts/check.sh --legs step --np 2 --keep and the before-P0 run kept as ghosts/work-before-p0-np2-step (built from 3f51451d)
bash
cd src/tests/flume/verification/ghosts && ./check.sh                    # CPU, np 1 2 3, every case
FLUME_EXE=$PWD/../../../../../exe/adam_flume_fnl ./check.sh --np "1 2"  # FNL
./check.sh --legs box3d,channel2d,refined3d --degree 5 --seam-restriction lagrange6 --seam-ghost-fill quintic

Results ​

Ghost probe on the step forest, before and after #65 P0

Each point is a face or edge ghost of the step forest (N=80, refined boxes, 2 ranks), coloured by its error. Top: before P0, the realm edges at the step corner, the domain corners and the inflow corners carry O(10−1) errors; every other ghost is at round-off. Bottom: after.

ProbeBoundCPU (np 1–3)FNL (np 1–2)
linear field, every case, every face and edge group10−12≤1.3⋅10−15≤1.3⋅10−15
linear field, --seam-restriction lagrange6 (box3d, channel2d, refined3d)10−12≤1.1⋅10−15≤1.1⋅10−15
degree 5, lagrange6 + quintic (box3d, channel2d, refined3d)10−12≤5.8⋅10−15≤5.8⋅10−15
degree 5, tricubic (negative control)must failmisses by ∼10−5, seam groups only
degree 2, mean restriction (negative control)must failmisses by ∼4⋅10−5, seam groups only
the step forest before P0—realm edges off by up to 0.78

Related cases: Forward-facing step and Multi-realm runs run the seams this probe inspects; D1, quintic fill uses the polynomial probe.

Findings ​

  • What it found (P0). Before P0, a ghost beyond two realm faces held a copy of its inward diagonal cell. That covers wall plus wall at the step corner, wall plus seam where the step face meets the A–B seam, inflow plus wall, and the like. On the step forest those ghosts were off by up to 0.78 of the field scale: the wrong position, and the wrong sign of the normal momentum. They now compose the conditions of the faces, as Boundary conditions describes. The face ghosts, and the edges with another block beyond one of their faces, were already right.
  • What the poison found (P1, after P0 had been committed). An extrapolation ghost copied the previous ghost of its chain along the normal. When the ghost also lies beyond a block interface along another axis, deeper than along the normal, that previous ghost is in the same crown, so in the same device launch: on FNL the rows raced. Two identical FNL runs of sod-x wrote different edge and corner ghosts (the interior identical), and with the poison GP failed the block+extrapolation edges on every case (NaN read). The CPU walks each row outward and read its donors fresh, by the row order alone. The P0 probe could not see it: without the poison the stale value equalled the fresh one. An extrapolation ghost now copies the first interior cell along its normal directly (the same value, and a donor the exchange has filled), on both backends; with the poison every case passes and two FNL runs are bit for bit equal.
  • A gap in its own instrument. The step-0 file is written while each realm initialises, before the forest connects the realms, so its seam ghosts are unfilled. GP therefore reads the file written after the negligible step, whose write refills the seams first.
  • The negative controls of the polynomial probe are not part of check.sh; they are run by hand with --seam-ghost-fill tricubic --degree 5 and --seam-restriction mean --degree 2.