Files
CelerisLab/docs/validation/viv_re150_predeclare.md
ClaudeandClaude Opus 5.5 48ee0982c9 tests/docs: unique-name driver loading; moving-path lifecycle and settings notes; VIV predeclare renamed
Tests load research drivers via importlib under unique module names
instead of a bare 'import run' (shared-process collision between the
Dütsch and VIV drivers). lifecycle.md qualifies immutable geometry and the
host-only get_flags for translating circles; supported_settings.md points to
the dense-only moving-circle feature; leontini_viv_predeclare.md is renamed
viv_re150_predeclare.md (Re 150 Kolahdouz/Qin case) with references updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 16:12:05 +00:00

149 lines
8.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Single-cylinder free transverse VIV, Re 150 (Kolahdouz/Qin) — PREDECLARATION (agreed at 1abfe954)
(File renamed from `leontini_viv_predeclare.md`; the Leontini Re 200 case is only the queued second case below.)
Stage goal (Yanqi via TRON): prescribed Dütsch case + one VIV case both
"reasonably close" to a predeclared reference = development-stage success, then
cleanup. The frozen Dütsch settling failure (0/28 pairs at 31.25 T) stays on
record. Practical closeness is not a formal validation claim. Agreed by
Claude, Grok and Astra at 1abfe954; implemented in research/viv_re150/.
## Primary case (one case)
- Reference: Qin et al., arXiv:2003.12186, §4.5 (maximum amplitudes at
U_red = 3…8: .075, .551, .520, .459, .370, .084 D in the text, as quoted by
Grok/Astra) for the target; setup from Kolahdouz et al., arXiv:2003.12046,
§4.1.1 (read 2026-10-30 via ar5iv): U* sweep 3–8 with amplitudes only in
Fig. 3(b) (no text values), domain [−30, 45] D × [−30, 30] D with the
cylinder "initially at rest and centered at the origin", uniform inflow on
the left, zero normal traction and tangential velocity at the outlet, zero
normal velocity and tangential traction on y = ±30 D; no initial
perturbation and no total simulated time stated.
- Re = 150, U* = U/(f_n D) = 5, m/(rho D²) = 2 (m* = rho_s/rho_f = 8/pi),
zeta = 0, f_n = sqrt(K/M)/(2 pi) in vacuum. Transverse DOF only.
- Target: A_max = .520 D (peak amplitude, Qin §4.5). Multi-code reference
(Kolahdouz ILE-NS, Qin II-LBM). Reference uncertainty: not stated; the
spread between codes is not quantified here.
## Our reproduction (declared differences)
- D = 40, MRT, dense double buffer; U_lat = .04, nu = U D / 150 = .010667,
tau = .532.
- Box [0, 75 D] × [0, 60 D] (3000 × 2400) with the cylinder centre at
(30 D, 30 D): 30 D from the inlet, 45 D from the outlet, centred in y
(Kolahdouz §4.1.1). Qin's own box ([0, 40 D] × [0, 20 D], centre (10 D, 10 D))
is the target paper's, not ours.
- Boundaries: uniform Zou–He inlet, NEQ outlet (rho = 1, backflow clamp),
free-slip y walls. Kolahdouz uses a traction outlet: declared mismatch.
- Qin's own VIV box (40 D × 20 D) is not used; a smaller box would be a
declared cost cut with more blockage, not "the same case".
- Allowed y offset ±1.5 D; peak transverse speed ≈ .026 cells/step (bound .5).
## Structure, coupling and time levels
- M = 2 rho D² (lattice rho = 1), f_n = U/(5 D) = 2e-4 per step, K = M (2 pi f_n)²,
zeta = 0. Equation of motion uses the structural mass only.
- Per-step sequence (binding): known (y_n, v_n) and the last completed force
F_last → candidate (y_{n+1}, v_{n+1}) by the update below → `set_body` with
offset y_{n+1} and wall velocity (y_{n+1} − y_n)/dt with lattice dt = 1 (interval average; a
different quantity from the endpoint v_{n+1}) → `run(1)` → only on success:
commit y_n ← y_{n+1}, v_n ← v_{n+1}, F_last ← force read for that step (failure
semantics: see the window section). Whether the
measured force represents the interval's start, midpoint or end is not
asserted; the one-step lag is declared, not corrected.
- Initial state: fluid initialized by `Simulation.initialize()` to the uniform
inlet velocity U everywhere (the code's actual initial condition; the
source gives no fluid initial condition), body at rest at the declared
centre, y_0 = v_0 = 0, F_last = 0.
- Structural update over one step with constant force F = F_last (exact
harmonic solution, zeta = 0): y_e = F/K, w = 2 pi f_n,
y^{n+1} = y_e + (y^n − y_e) cos w + (v^n / w) sin w,
v^{n+1} = −(y^n − y_e) w sin w + v^n cos w.
- Primary RHS (single coupled run): CME + Wen (theory-motivated stage choice:
Galilean-invariant link correction; G4 could not separate it from CME).
Raw CME and the node terms are logged as diagnostics of that trajectory;
the node impulse is not added. A run with CME-only as RHS is a separate,
labelled sensitivity case, decided separately. Neither RHS is claimed proven.
## Window, failure semantics, acceptance (engineering, development stage)
Clocks: lattice dt = 1; T_n = 1/f_n = 5,000 steps. Response cycles are
counted from the measured signal; T_n is used only for the seed check.
- Start: body at rest at the declared centre (the only initial condition the
source states), y_0 = v_0 = 0, F_last = 0; fluid uniform at U (as
initialized by the code; our choice, not the source's).
- Seed (conditional, one time, declared): at step n_seed = 200,000
(t = 40 T_n), if max |y_t| over states t in [175,000, 200,000) is below
.01 D, then
in that step, after the check and before the candidate update, set
v_n += 2 pi f_n (.01 D) (F_last unchanged) and proceed normally; the
geometry moves continuously through the update. `seed_applied`, step and
size are logged; never repeated or changed.
- Cycle boundary (discrete, fixed threshold): step n closes a cycle iff
y[n−1] < 0 and y[n] ≥ 0 (upward crossing of the equilibrium y = 0);
y_0 = 0 is not a crossing; no boundary fewer than 2 samples after the
previous one.
- Per cycle: positive peak max(y), negative peak min(y), mean offset
(time-mean of y), amplitude a = (max − min)/2 (the only amplitude used for
settling).
- Eligibility: a completed cycle is counted (toward the settling proof and
the 150 budget) only if a ≥ .01 D; ineligible cycles are logged, never
counted, so near-zero motion cannot settle or pass.
- Seed reset: if the seed is applied, the incomplete cycle at n_seed is
discarded and the settling proof starts empty; the 150 budget and the
native cap keep running from t = 0.
- Windows: adjacent non-overlapping blocks of 10 counted cycles;
W_k = mean amplitude of block k (W_k ≥ .01 D by eligibility, so no zero
denominator).
- Block rule R(k), k ≥ 2: |W_k − W_{k−1}| / W_{k−1} ≤ .02 and, for the last 5
cycles of block k, max_i |a_i − ā_5| / ā_5 ≤ .01 with ā_5 their mean.
- Settled at the end of block k when R(k) holds. Scoring = the next two
blocks k+1, k+2 (20 counted cycles); both must satisfy R. If one fails,
that block becomes the new reference: the settling proof restarts from it
and continues within the caps (deterministic; no other retry). A pass
requires one complete stable 20-cycle scoring window before a cap; success
stops after its 20th cycle.
- Caps (both always armed; the first hit wins and is reported): 150 counted
response cycles including the 20 scoring cycles (so settling must complete
by counted cycle 130) → `timeout_response_cycles`; 1,000,000 native steps
(200 T_n) → `timeout_native_steps`.
- Durations reported: total steps, t U/D, counted and uncounted cycles,
measured T_resp (mean boundary interval); N_resp / f_n only as a labelled
secondary clock.
- Growth is not a failure: initial VIV growth is physical. Checked every
step (FAIL-CLOSED): non-finite y, v or force; |y| > 1.0 D (the tighter
gate; about twice the largest reference peak, .551 D) or the ±1.5 D offset
box reached; no_donor > 0; a failed upload/step/sync.
- On any failure the driver stops and reports; it never retries,
sub-iterates, retunes the RHS or parameters, or starts a second case.
Device-side failures (upload/step/sync, no_donor) mark the Simulation
failed through the existing lifecycle; input rejection by `set_body`
(offset box, speed, step bound) and timeouts end the driver run without
that mark.
- Target metric over the 20 scoring cycles: A_max/D = max over those cycles
of max(|positive peak|, |negative peak|) about y = 0 (Qin reports "maximum
amplitude"); also reported: mean amplitude, positive and negative peaks,
mean offset, f/f_n (no target).
- Acceptance: |A_max − .520 D| ≤ .052 D (±10 %), stated together with the
boundary mismatch and the absence of a quoted reference uncertainty. A pass
is "reasonably close" for the stage: not grid convergence, not force-RHS
validation, and it does not touch the Dütsch settling record.
- Driver reads force and the small diagnostics per step only; field reads
only at a few predeclared snapshots. No runtime claim: there is no measured
timing on this grid yet (the D40/60D receipt is a different, sampled case).
## Plumbing smoke (not the benchmark)
`run.py --smoke --D 20 --steps 32` runs exactly 32 steps of the same BC
package and coupling loop (no seed, settle or score; never scored);
status `smoke_ok` iff every step is finite, no_donor stays 0 and the body
stays inside its box. The physical case is only the default invocation
(`--D 40`, no `--smoke`).
## Queued, not in this stage
Leontini et al. JFS 22 (2006) Re = 200, m* = 10, zeta = 0.01, U* = 5.0 (needs
the owners' c_crit convention and a Fig. 1 digitization). D80, literature
claims, pinball, AMR, BC redesign, threshold changes.