Test-only. New shear_wave.py (exact field, equilibrium initializer reusing frozen oracle constants/geometry, area-weighted composite metrics with a 2x2 Gram-matrix modal fit, late-time rate fit, preregistered assess with max(2*control, floor) budgets). run_case.py gains --flow (default taylor_green, unchanged receipt). oracle.py byte-identical (pinned by test). CPU manufactured checks in test_shear_wave.py; docs record caveats. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
246 lines
14 KiB
Markdown
246 lines
14 KiB
Markdown
# 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=U*L/nu, not Re_k=U/(nu*k)=Re_L/(2*pi). U=.03 in lattice units,
|
||
Ma=U/cs=.0519615, cs^2=1/3, nu_root=.03*N/Re_L. Acoustic refinement uses
|
||
h_l=dt_l=2^-l: lattice velocity stays U, nu_l=2^l*nu_root and
|
||
tau_l=.5+3*nu_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
|
||
cd3a657f9d3833e812c3cca173525f165b48710a. 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 it. The default flow
|
||
is still the frozen Taylor–Green oracle, and the default receipt is unchanged.
|
||
|
||
Convention: integer mode (2,1) on the L=N periodic root domain, so
|
||
`k = (2*pi/L)*(2,1)` points 26.565° from +x. Mode (1,2) would point 63.435°.
|
||
A wrong-mode test catches a swap between the two. 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
|
||
|
||
Here `nu = U*N/100`, the Taylor–Green Re100 viscosity at the same N. The
|
||
initial condition is FP64 second-order equilibrium at the actual leaf
|
||
centers, with no non-equilibrium part.
|
||
|
||
Physical caveats:
|
||
1. *Finite-Mach error.* The second-order D2Q9 equilibrium has third moment
|
||
`sum_i f_eq c_a c_b c_g = rho*cs^2*(u_a d_bg + u_b d_ag + u_g d_ab)`. The
|
||
`(c.u)^2` term contributes only even moments. The continuum target also
|
||
has `rho*u_a*u_b*u_g`. At Navier–Stokes (first Chapman–Enskog) order,
|
||
the resulting stress error is proportional to `d_g(rho u_a u_b u_g)`.
|
||
For this field with uniform density, `u.grad = 0` and `div u = 0`, so
|
||
that particular term vanishes.
|
||
|
||
This does *not* show that all finite-Mach or cubic error vanishes. It
|
||
does not cover:
|
||
- higher Chapman–Enskog orders;
|
||
- lattice truncation for the oblique wavevector;
|
||
- FP32 storage;
|
||
- any density nonuniformity, which interfaces or anisotropy can create.
|
||
2. *Startup.* Equilibrium initialization omits the non-equilibrium stress.
|
||
The resulting kinetic transient lasts O(tau) steps and may shift the
|
||
in-phase amplitude. The decay rate is fitted from `ln(a_hat)` using
|
||
only samples with t* >= 0.12, which is about 1.9N root steps and much
|
||
longer than tau. This is intended to reduce startup sensitivity. It
|
||
has not been shown to cancel the bias, and that remains unverified
|
||
until measured.
|
||
|
||
Metrics are computed on ACTIVE leaf cells only, each weighted by its area
|
||
`4**-level` in root-cell units. The partition must cover the domain exactly
|
||
once. One global pressure gauge is used, never one per level.
|
||
|
||
- `a_hat`, `b_hat`: weighted least-squares fit of `u.E` onto the sin and cos
|
||
modes, solved with the 2x2 Gram matrix of the actual composite quadrature.
|
||
- `phase_drift = atan2(b_hat, a_hat)`, in radians.
|
||
- `rate_error = |rate_fit/(nu|k|^2) - 1|`, dimensionless. Rates are per root
|
||
step.
|
||
- `velocity_l2`: error relative to the exact field.
|
||
- `residual_l2`: the velocity left after subtracting the fitted mode,
|
||
relative to the exact-field norm.
|
||
- `longitudinal_l2`: the `u.k_hat` part of the velocity, relative to the
|
||
exact-field norm.
|
||
- `pressure_rel = p_rms/(A(t)^2/4)`: the gauged composite rms of
|
||
`p = (rho-1)/3`, divided by a *fixed analytic scale*.
|
||
- A²/4 is the continuum rms of the Taylor–Green exact pressure at the
|
||
same amplitude. That makes the number comparable in magnitude with TG
|
||
`pressure_l2`, but it is not that norm. TG normalizes by the discrete,
|
||
globally gauged exact-pressure norm on the composite grid.
|
||
- Exact pressure here is constant, so no relative-to-exact claim is made.
|
||
- Max and per-level values are reported for localization.
|
||
|
||
Preregistered screens (`shear_wave.assess`):
|
||
- Run set: uniform N, uniform 2N, and refined depth-2 runs at root N, with
|
||
constant and/or projected_linear explosion.
|
||
- `eps(N) = (|k| h_root)^2 = 5*(2*pi/N)^2`, dimensionless, coefficient one.
|
||
It is 0.01205 at N=128.
|
||
- Absolute screens: rate_error, max |phase_drift| (radians) and max
|
||
residual_l2 must each be <= eps. This is a preregistered diagnostic
|
||
choice of the second-order truncation scale, *not* a proven error bound.
|
||
- Existing ceilings also apply: velocity_l2 <= 0.05, mass <= 1e-5 and
|
||
pressure_rel <= 0.02.
|
||
- Budget form, as in `assess.py`:
|
||
`refined <= max(2*control_N, floor)`, with floors 0.002 for rate and
|
||
residual and 0.02 for pressure.
|
||
- The ratio to the 2N control is reported, not gated.
|
||
- A control failing an absolute screen makes the set INVALID_CONTROL. The
|
||
screens are never retuned.
|
||
|
||
Scope: one flow, one rectangle, SRT only. Two resolutions show a trend,
|
||
not an order. This does not establish placement robustness.
|