- run_case: finite_row accepts list diagnostics; sample() shared by sample and READY paths; verify_source pins shear_wave.py path+SHA256 for --flow shear_wave (default TG flow unchanged, does not import it). - assess_shear_wave.py: fixed N128 four-case contract and 4-JSON CLI, reusing assess_projected_pairs provenance/build/workload/time checks plus shear metadata; missing/duplicate/mixed/extra records -> INVALID_SET. - tests: mocked refined runner sample (list diagnostics, NaN, TG unchanged), contract pass + reproduced false-pass mutations, divergence check uses analytic E.k=0 and a derived h^2 truncation + roundoff bound with h-halving scaling instead of a fixed 1e-12. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
16 KiB
Periodic 2D Taylor–Green AMR campaign (prepared, not GPU-qualified)
This validation harness complements DFG Re100 and Jet/body tests. It measures smooth 2D periodic decay and errors crossing fixed refinement interfaces at Re_L=100 and 1000. It does not certify 3D turbulence, LES, Jet performance, wall conditions, dynamic refinement or general high-Re flows.
Reference and conventions
Primary implementation-author documentation, checked 2026-10-18:
- TensorMesh, exact velocity and pressure with a consistent phase: https://docs.tensor-mesh.com/example_gallery/fluid/taylor_green.html
- AeroSim Nassu, 2D decay, energy and lattice-length Re definition: https://docs.aerosim.io/nassu/validation/analytical/04_taylor_green_vortex_2d/README.html
We use the TensorMesh phase, rescaled to length L=N root cells:
k = 2*pi/L; A(t) = U*exp(-2*nu*k*k*t)
ux = -A*cos(k*x)*sin(k*y)
uy = A*sin(k*x)*cos(k*y)
p/rho0 = -A*A/4 * (cos(2*k*x)+cos(2*k*y))
E = <|u|^2>/2 = U*U/4 * exp(-4*nu*k*k*t)
Pressure is kinematic, with rho0=1. A global velocity sign reversal leaves pressure unchanged; exchanging sine/cosine without the corresponding pressure phase can reverse its sign. CPU tests independently check momentum residuals. The nonlinear acceleration is a pressure gradient, not zero; the AeroSim page's prose calling advection zero is not adopted. Do not mix phase variants from different references. The formulas above are verified by substitution, not by trusting a copied pressure expression.
Re_L=UL/nu, not Re_k=U/(nuk)=Re_L/(2pi). U=.03 in lattice units, Ma=U/cs=.0519615, cs^2=1/3, nu_root=.03N/Re_L. Acoustic refinement uses h_l=dt_l=2^-l: lattice velocity stays U, nu_l=2^lnu_root and tau_l=.5+3nu_l. The worst root tau is .50288 (N32/Re1000), omega=1.98855, inside the existing config guard. No viscosity clamping or guard override.
All initial arrays are physical FP64 equilibrium populations, supplied to the public shifted-FP32 postcollision-G initializer. rho=1+3p at each true cell center. These are valid equilibrium postcollision states, but do not include Chapman–Enskog nonequilibrium stress; startup acoustic/kinetic error remains in the measurements and is shared with uniform baselines. The incompressible reference is not an exact finite-Mach LBM solution. This is why resolution alone need not remove the full error floor.
Geometry and workload
All patches use level-wide union form, so origins are zero, including fine arrays with holes. Coordinates are (index+.5)*2^-level. Metrics integrate only ACTIVE leaves, each weighted by 4^-level. Covered cells and halo cells never enter norms or mass. Geometry tests prove exact single coverage at root-cell resolution. All blocks are 8x8, and both root dimensions are periodic.
At each root N=32,64:
- 2 levels: L1 rectangle (N/8,N/8,7N/8,3N/4), in L0 indices.
- 3 levels: same L1 plus L2 rectangle (3N/4,N/2,3N/2,5N/4), in L1 indices.
These off-axis boundaries cross nonzero flow and gradients rather than sitting only on symmetry or stagnation lines. Whole geometry scales with N.
16 cases: at each Re, uniform N=32,64,128,256 plus AMR (root32/root64)x(depth2/depth3). Each AMR case therefore has both a same-root uniform baseline and a uniform baseline at its finest spacing. Uniform N is its own lattice length, so nu and step count are scaled to represent the same nondimensional solution. Samples t*=tU/L = 0,.12,.24,.36,.48. End time is 16*N base steps, a meaningful .48 domain turnover or 3.016 vortex-scale turnover times (scale 1/k). At Re1000 energy decays about 7.3%; at Re100 about 53.1%. No plots or full fields; each case writes five diagnostic rows and provenance, incrementally.
Running (GPU owner must schedule launches)
CPU checks, from the repository root:
PYTHONPATH=src python -m unittest discover -s tests/validation/taylor_green -p 'test_*.py' -v
One GPU case, on the scheduled worker after checking occupancy:
CUDA_VISIBLE_DEVICES=<physical-id> python tests/validation/taylor_green/run_case.py --repo /path/to/reviewed/checkout --re 100 --n 32 --depth 1 --output output/taylor_green/re100_n32_d1.json
Depth 1 is uniform. Run uniform cases before AMR for each Re; inspect baseline screening failures before proceeding. For a complete matrix, repeat the uniform call with N=32,64,128,256 and AMR calls with N=32,64 / depth=2,3, at each Re. Use serial process startup on the parent's assigned Horsea GPUs. This harness does not schedule or acquire GPUs. No GPU was used during preparation.
python tests/validation/taylor_green/assess.py output/taylor_green/re*_n*_d*.json --output output/taylor_green/assessment.json
The local API/geometry review used clean staging commit
cd3a657f9d. This is not a claim of results on d79
or any historical commit. Each runtime output records actual Git HEAD/dirty
state, an aggregate source-tree hash, plus SHA256 of all harness Python files. Assessment refuses dirty checkouts and mixed source
or harness identities. Keep reports outside the checkout or .gitignore them so
new outputs do not change its dirty flag between cases. The source-tree hash excludes bytecode caches; production qualification requires
a clean checkout.
The runner uses the existing PeriodicBlockStepper / FixedPatchStepper periodic
paths only. It does not depend on the new channel-root implementation.
Preregistered assessment, before any GPU results
All sampled times must satisfy finite screening ceilings:
- Relative composite velocity L2 <= 5%
- Relative globally gauge-adjusted pressure L2 <= 15%
- Relative kinetic-energy error <= 5%
- Composite mass drift from downloaded t0 <= 1e-5
These are screening budgets, not literature-certified tolerances. Acceptance also requires all 16 cases, common source/harness, correct nondimensional times and viscosity, and baseline-relative checks on worst post-startup-sample errors:
- AMR error <= max(2 * same-root uniform error, declared numerical floor).
- Root64 AMR error <= max(.9 * root32 AMR error, floor), for each depth/Re.
- Uniform256 error <= max(.9 * uniform32 error, floor).
Floors: velocity .002, pressure .02, energy .004. They are declared low-Mach / equilibrium-startup budgets, not fitted to observed results. Ma^2=.0027 motivates their scale; it is an asymptotic expectation, not a proof of those coefficients. The coarse baseline sets the AMR budget because much of the domain remains coarse. The finest uniform error and AMR/fine ratio are always reported, with no claim that partial refinement should match a fully fine domain. A pass below a floor is bounded accuracy, not a measured convergence order. Report errors and ratios even for a pass. Failure requires diagnosis or a separately preregistered follow-up, never retroactive tolerance tuning. Higher Re is deliberately absent; Re100/1000 must first be stable, resolved and reviewed.
Diagnostics use one global pressure gauge, direct cell-center analytic values, kinetic energy per unit reference mass, and actual leaf cell areas. Composite midpoint quadrature introduces a small initial energy integration error, retained rather than subtracted. Mass drift is relative to downloaded t0, so density initialization and mixed-grid quadrature do not masquerade as time drift.
Pilot scheduling and transfer-consistency scope
Start with one uniform case and one matched AMR case; the 16-case matrix is a qualification plan, not authorization to launch every case automatically. The GPU owner reviews the pilot before expanding it. Each process prints a flushed READY only after initialization, a real base step exercising every level's kernel/halo/transfer path, and a finite positive-density download check. That step belongs to the measured trajectory; it is not discarded warmup. PROGRESS messages contain each of the five diagnostic samples. READY alone is not an accuracy pass. Reports include per-level active cells, hops and collision updates.
The complete matrix costs 768,540,672 active collision updates and 21,504 root intervals, excluding ghost streaming, halos, transfers and diagnostic work. Uniform N256 is the largest case (268,435,456 updates / 4096 intervals per Re). AMR N64/depth3 needs 50,987,008 updates / 1024 intervals. These are work counts, not GPU wall-time estimates. Baseline comparisons remain available on failed screens and partial pilots; absent/nonfinite operands are null, never passing.
Acoustic viscosity scaling and unchanged lattice velocity do not by themselves prove nonequilibrium-stress consistency across refinement interfaces. A smooth collocated Chapman–Enskog postcollision nonequilibrium has a (tau-1)*h factor; the existing implementation instead uses volumetric piecewise-constant explosion and stream-only ghosts. Whether that actual transfer construction achieves the needed stress accuracy is an unresolved question for this pilot to measure. It is not a proven defect, and a collocated-state scaling factor must not be inserted into this different transfer operator without a separate derivation and validation. This campaign measures the current operator and makes no theoretical stress-consistency certification or production-kernel change.
Repository integration
The four Python harness files remain together under tests/validation/taylor_green, so runtime provenance hashes precisely this harness directory. Formatting changes produce a new harness identity; preparation and integrated receipts must not be mixed. All analytic definitions, numerical floors, assessment gates, sample times, and source-import assertions are unchanged. Production source is unchanged from the cd3 snapshot; these periodic tests do not waive the separate channel GPU gate.
Oblique shear wave (test-only diagnostic, not GPU-qualified)
shear_wave.py reuses the frozen oracle's constants, cell centers and
geometry. oracle.py is byte-identical, and test_shear_wave.py pins its
eec4677 SHA-256. run_case.py --flow shear_wave selects the shear wave;
the default flow and its receipt are the unchanged Taylor–Green path.
Convention
Integer mode (2,1) on the L=N periodic root domain:
- the wavevector
k = (2*pi/L)*(2,1)points 26.565° from +x; - mode (1,2) would point 63.435°, and a wrong-mode test catches a swap.
Polarization is E = (-1,2)/sqrt(5), perpendicular to k:
u = U*E*sin(k.x + phi)*exp(-nu*|k|^2*t), phi = 0, p = const, rho0 = 1
nu = U*N/100, the Taylor–Green Re100 viscosity at the same N. This is an
exact incompressible Navier–Stokes solution. The initial condition is FP64
second-order equilibrium at the actual leaf centers, with no
non-equilibrium part.
Physical caveats
-
Scope of the cubic-term argument. The second-order D2Q9 equilibrium lacks the cubic third moment
rho*u_a*u_b*u_gof the Maxwellian (Dellar, https://people.maths.ox.ac.uk/dellar/papers/Galilean.pdf, eq. 3). At Navier–Stokes order this produces a stress error proportional tod_g(rho u_a u_b u_g).For the ideal constant-density, zero-background transverse wave, k.E = 0 makes that contracted derivative vanish. That is the only claim made. It does not remove other discrete or finite-Mach errors. It is not guaranteed once AMR introduces density, longitudinal or higher-mode contamination. The analytic NS field itself is exact.
-
Startup. Equilibrium initialization is not the discrete shear eigenmode (Mei et al., https://doi.org/10.1016/j.compfluid.2005.08.008).
- The decay rate is a free-intercept least-squares fit of ln R over the
samples with t* >= 0.12, where
R = hypot(a_hat, b_hat). - The intercept cancels only a constant amplitude prefactor, and only once a single exponential dominates.
- Per-interval rates and the maximum log-fit residual are kept, so a lingering transient stays visible.
- Late sampling alone does not show that transients have gone.
- The decay rate is a free-intercept least-squares fit of ln R over the
samples with t* >= 0.12, where
Metrics
Computed on ACTIVE leaf cells, each weighted by its area 4**-level in
root-cell units. The partition must cover the domain exactly once, and one
global pressure gauge is used.
a_hat,b_hat: weighted least-squares fit ofu.Eonto sin/cos, using the 2x2 Gram matrix of the actual composite quadrature. A wrong Fourier mode need not project to exactly zero on this quadrature.amplitude_error = R/A_exact - 1.phase_drift = atan2(b_hat, a_hat), in radians, reported separately.- Rates are positive (minus the fitted slope).
- Per root step they are compared with
nu*|k|^2. Uniform 2N uses its ownnu = 0.0768at N=256 and doubled step counts. - In t* they are compared with
5*(2*pi)^2/Re = 1.97392088. rate_error = |rate/exact - 1|.
- Per root step they are compared with
velocity_l2,residual_l2(velocity left after removing the fitted mode) andlongitudinal_l2(theu.k_hatpart), all relative to the exact-field norm. A longitudinal perturbation also raises the full-vector residual and the velocity L2.spurious_pressure_rms: the raw gauged rms ofp = (rho-1)/3.spurious_pressure_scaled: that rms divided byA_exact(t)^2/4, a continuum reference scale. It is not the discrete, globally gauged Taylor–Green norm on the composite grid. Exact pressure is constant, so no relative-to-exact claim is made. Max and per-level values are kept.
Preregistered screens (shear_wave.assess)
Run set: uniform N, uniform 2N, and refined depth-2 runs at root N.
eps(N) = (|k| h_root)^2 = 5*(2*pi/N)^2: dimensionless, coefficient one, 0.01205 at N=128. This is a chosen screen, not a proven error bound.- Absolute screens:
- rate_error, max |phase_drift| (radians) and max residual_l2 must each be <= eps;
- velocity_l2 <= 0.05, mass <= 1e-5, spurious_pressure_scaled <= 0.02.
- Budgets keep the
assess.pyform,refined <= max(2*control_N, floor):- floors are 0.002 for rate and residual, 0.02 for pressure;
- because the pressure ceiling and floor are both 0.02, the comparative pressure budget adds no constraint unless the root control exceeds 0.01;
- raw ratios to both the root and finest controls, including raw pressure rms, are always reported.
- Outcomes are kept distinct:
- INVALID_CONTROL: a control is missing, incomplete or nonfinite, or the records come from mixed sources;
- CONTROL_SCREEN_FAIL: complete, finite controls that fail a screen. Refined results are still computed and retained.
- Screens are never retuned.
Limitations
One flow, one phase and one rectangle, SRT only. The rectangle's vertical faces span 0.625 tangential periods and its horizontal faces 1.5, so this is not placement robustness. Two resolutions show a trend, not an order.
Receipt assessment and runner integrity
- Fixed four-case N128 contract:
python assess_shear_wave.py U128.json U256.json R128_constant.json R128_projected.json --output report.json --expected-revision <sha>. It reuses the shared provenance, build, workload and sample-time checks fromassess_projected_pairs.py, and adds shear metadata checks (flow, mode, polarization, angle, phase, eps, limits, disabled early trace, and pinnedshear_wave.pyhash).- Missing, extra, duplicate or mixed-source records give
INVALID_SET, as does any failed shared check. - Otherwise the result comes from
shear_wave.assess, which separatesINVALID_CONTROLfromCONTROL_SCREEN_FAIL.
- Missing, extra, duplicate or mixed-source records give
run_case.verify_source(..., flow="shear_wave")requires the importedshear_wave.pyto be the selected harness file with the pinnedSHEAR_WAVE_SHA256. The default TG flow does not import it.- Sample and READY finiteness use
finite_row, which accepts scalar and list diagnostics. For TG's all-scalar rows it behaves the same as before. - Merge note: native
92b8afeadds ascaled_n256receipt block. When the two are merged, keep both blocks and make--flow shear_waveandscaled_n256mutually exclusive. Not merged here.