research: Dütsch error budget — settling at one fifth of comparison bands, conservative drift
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
9cfcd8bb96
commit
c1c282b79c
@@ -134,12 +134,19 @@ ledger; verdict PASS or FAIL-CLOSED.
|
|||||||
Scales: `F_ref = 0.5 rho U_max² D`, `U_ref = U_max`; never a near-zero mean
|
Scales: `F_ref = 0.5 rho U_max² D`, `U_ref = U_max`; never a near-zero mean
|
||||||
force as denominator.
|
force as denominator.
|
||||||
|
|
||||||
- Settling metrics, cycle k−1 → k, phase-aligned by step within the cycle:
|
- Settling metrics, cycle k−1 → k, phase-aligned by step within the cycle
|
||||||
- waveform `RMS(dFx) / F_ref ≤ .02`;
|
(error budget: one fifth of the tightest comparison band of the same
|
||||||
- `|d mean|`, `|d RMS|`, `|d peak|` of Fx, each `/ F_ref ≤ .01`;
|
observable, so residual drift cannot obscure a domain or resolution pass):
|
||||||
- scored profiles (fluid-only) `|du| / U_max`, `|dv| / U_max ≤ .03` at the
|
- waveform `RMS(dFx) / F_ref ≤ .002`;
|
||||||
|
- `|d mean|`, `|d RMS|`, `|d peak|` of Fx, each `/ F_ref ≤ .002`;
|
||||||
|
- scored profiles (fluid-only) `|du| / U_max`, `|dv| / U_max ≤ .004` at the
|
||||||
three phases × four stations;
|
three phases × four stations;
|
||||||
- Morison `|dc_d| ≤ .02`, `|dc_i| ≤ .02`.
|
- Morison `|dc_d| ≤ .004`, `|dc_i| ≤ .004`.
|
||||||
|
- Conservative comparison: every domain and cross-resolution criterion is
|
||||||
|
applied to `|difference| + drift_a + drift_b`, where drift is the largest
|
||||||
|
pair metric of that case inside the scored window.
|
||||||
|
- These constants (protocol.py SETTLE, DOMAIN, CROSS) are frozen before any
|
||||||
|
physical result; no post-result loosening.
|
||||||
- Rule: all metrics for N = 3 consecutive whole paper cycles → settled.
|
- Rule: all metrics for N = 3 consecutive whole paper cycles → settled.
|
||||||
|
|
||||||
Cycle indexing (worked example, binding). Paper cycle k spans
|
Cycle indexing (worked example, binding). Paper cycle k spans
|
||||||
|
|||||||
@@ -117,7 +117,9 @@ def main(argv):
|
|||||||
entry.update(duration_T=case["steps"] / case["T"], K_case=k_case, pairs=pairs,
|
entry.update(duration_T=case["steps"] / case["T"], K_case=k_case, pairs=pairs,
|
||||||
coverage={k: coverage(case, k) for k in sorted(cycle_slices(case)) if k > p.DISCARD},
|
coverage={k: coverage(case, k) for k in sorted(cycle_slices(case)) if k > p.DISCARD},
|
||||||
no_donor=float(case["diag"][:, 13].sum()), eq_only=float(case["diag"][:, 12].sum()))
|
no_donor=float(case["diag"][:, 13].sum()), eq_only=float(case["diag"][:, 12].sum()))
|
||||||
if window:
|
if window: # residual drift inside the scored window (pairs K..K+2)
|
||||||
|
entry["window_drift"] = {key: max(pairs[k][key] for k in window)
|
||||||
|
for key in ("wave", "profile", "morison")}
|
||||||
entry["morison_obs"] = cycle_morison(case, window)
|
entry["morison_obs"] = cycle_morison(case, window)
|
||||||
entry["morison"] = {name: cycle_morison(case, window, fx)
|
entry["morison"] = {name: cycle_morison(case, window, fx)
|
||||||
for name, fx in candidates(case).items()}
|
for name, fx in candidates(case).items()}
|
||||||
@@ -146,6 +148,19 @@ def main(argv):
|
|||||||
d = max(max(abs(prof[key][0] - fine_p[key][0]), abs(prof[key][1] - fine_p[key][1]))
|
d = max(max(abs(prof[key][0] - fine_p[key][0]), abs(prof[key][1] - fine_p[key][1]))
|
||||||
for key in keys) / case["u_max"]
|
for key in keys) / case["u_max"]
|
||||||
entry.setdefault("profile_max_vs_finest", {})[k] = float(d)
|
entry.setdefault("profile_max_vs_finest", {})[k] = float(d)
|
||||||
|
ref_entry = report["cases"][cases.index(fine)]
|
||||||
|
for entry in report["cases"]: # conservative: difference + both drifts
|
||||||
|
if entry is ref_entry:
|
||||||
|
continue
|
||||||
|
drift = {key: entry["window_drift"][key] + ref_entry["window_drift"][key]
|
||||||
|
for key in ("wave", "profile", "morison")}
|
||||||
|
dm = max(abs(entry["morison"]["cme"][0] - ref_entry["morison"]["cme"][0]),
|
||||||
|
abs(entry["morison"]["cme"][1] - ref_entry["morison"]["cme"][1]))
|
||||||
|
entry["conservative"] = dict(
|
||||||
|
wave=entry["wave_rms_vs_finest"] + drift["wave"],
|
||||||
|
profile=max(entry.get("profile_max_vs_finest", {}).values(), default=math.inf)
|
||||||
|
+ drift["profile"],
|
||||||
|
morison_cme=dm + drift["morison"])
|
||||||
print(json.dumps(report, indent=1, default=float))
|
print(json.dumps(report, indent=1, default=float))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,12 @@ STATIONS = (-0.6, 0.0, 0.6, 1.2) # lab-fixed x/D from the mean position
|
|||||||
PHASES = (180.0, 210.0, 330.0) # degrees; bins [p, p + 1)
|
PHASES = (180.0, 210.0, 330.0) # degrees; bins [p, p + 1)
|
||||||
X2 = tuple(np.round(np.arange(-1.5, 1.5001, 0.05), 10)) # declared x2/D points
|
X2 = tuple(np.round(np.arange(-1.5, 1.5001, 0.05), 10)) # declared x2/D points
|
||||||
DISCARD, LAST_SCORED, N_PAIRS = 2, 11, 3 # cycles 0..2 discarded, cap 12 T
|
DISCARD, LAST_SCORED, N_PAIRS = 2, 11, 3 # cycles 0..2 discarded, cap 12 T
|
||||||
SETTLE = dict(wave=.02, scalar=.01, profile=.03, morison=.02)
|
# A-priori bands. Settling is one fifth of the tightest comparison band of the
|
||||||
DOMAIN = dict(wave=.01, morison=.02, profile=.02) # a-priori, vs the 60 D reference
|
# same observable, and comparisons add both cases' residual window drift
|
||||||
|
# (conservative: |difference| + drift_a + drift_b <= band).
|
||||||
|
DOMAIN = dict(wave=.01, morison=.02, profile=.02) # vs the 60 D reference at D = 40
|
||||||
|
CROSS = dict(wave=.02, profile=.02) # D40 vs D80
|
||||||
|
SETTLE = dict(wave=.002, scalar=.002, profile=.004, morison=.004)
|
||||||
|
|
||||||
|
|
||||||
def amplitude(D):
|
def amplitude(D):
|
||||||
|
|||||||
Reference in New Issue
Block a user