From caa573f9787fef77df9f23cd0bb9ff0435ce2308 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 03:13:43 +0000 Subject: [PATCH] test: adopt native-validated 24-target probe layout Horizontal y=56 and vertical x=38 through box 1, normal coordinates [12.5..26.125] (root128 physical). Nearest-ACTIVE mapping with deterministic tie rule (distance, finer level, smaller j, smaller i), duplicate-cell rejection, stored centers and displacements. Replaces my earlier offsets, which mapped two target pairs to the same U128 cell. U512 spacing 128/512 and root step r -> U512 step 4r unchanged. Tests: 24 unique cells per case, nested 3 L0 + 5 L1 + 4 L2 per transect, U128 max displacement 0.625. Co-Authored-By: Claude Opus 5.5 --- docs/validation/taylor_green.md | 23 ++++--- tests/validation/taylor_green/run_case.py | 2 +- tests/validation/taylor_green/shear_probes.py | 66 +++++++++++-------- .../taylor_green/test_shear_wave.py | 14 ++-- 4 files changed, 63 insertions(+), 42 deletions(-) diff --git a/docs/validation/taylor_green.md b/docs/validation/taylor_green.md index 8793328..31dc563 100644 --- a/docs/validation/taylor_green.md +++ b/docs/validation/taylor_green.md @@ -421,12 +421,18 @@ Labels: ### Signed interface-probe traces (opt-in `--probe-trace`, mechanism diagnostic) -`shear_probes.py` (pinned) defines 24 fixed points on two interface-normal -transects, all at least 8 root cells from any box corner: -- V: y = 64.1, crossing the vertical faces x = 16 (L0/L1) and x = 22 (L1/L2); -- H: x = 90.1, crossing the horizontal faces y = 24 and y = 30; -- offsets from the outer face: −3.9, −1.9, −0.4, 0.1, 0.6, 2.1, 4.1, 5.6, 6.1, - 6.6, 8.1, 10.1. +`shear_probes.py` (pinned) uses native's validated 24-target layout through +box 1, in root-128 physical coordinates: +- a horizontal line y = 56, crossing the vertical faces x = 16 and 22; +- a vertical line x = 38, crossing the horizontal faces y = 16 and 22; +- normal coordinates 12.5, 14.5, 15.5, 16.25, 17.25, 19.25, 20.75, 21.75, + 22.125, 23.125, 24.125, 26.125. + +Each target maps to the nearest ACTIVE leaf center. The deterministic tie +rule is: distance, then finer level, then smaller j, then smaller i. This +gives 24 unique cells in every case, and nested has 3 L0 + 5 L1 + 4 L2 per +transect. Maximum displacement from target to center is 0.625 root cells in +U128 (0.177 in U512). Allowed cases: - nested U128, nested U512, nested projected depth 3; @@ -435,9 +441,8 @@ Allowed cases: The option can't be combined with `--final-snapshot`. Sampling: -- Each point is read from the ACTIVE leaf cell containing it. The receipt - stores the leaf level, `i`/`j`, and the actual cell center, all in - root-physical units (N=128). +- The receipt stores each probe's leaf level, `i`/`j`, actual cell center + and displacement from its target, all in root-physical units (N=128). - Grids differ between cases, so centers are *not* collocated. Analytic fields are evaluated at each actual center (`shear_probes.residuals`), and comparisons across cases are between residuals at nearby but distinct diff --git a/tests/validation/taylor_green/run_case.py b/tests/validation/taylor_green/run_case.py index 60b8ad9..468e88b 100644 --- a/tests/validation/taylor_green/run_case.py +++ b/tests/validation/taylor_green/run_case.py @@ -25,7 +25,7 @@ NESTED_SHEAR = "nested_shear_n128" # fixed three-level/two-region shear preset SHEAR_NESTED_SHA256 = "fa6cfe87fb4df8c6d732c637c885276833c81973b950c7724d734f8fc350bcb0" TWIN_SHEAR = "twin_shear_n128" # depth-2 twin: nested L0->L1 boxes without L2 SHEAR_TWIN_SHA256 = "1cbf37f18e89aea577581631c4565f51e68d22a3739b698816ef64acbf2b2bb4" -SHEAR_PROBES_SHA256 = "98ea8090ace48f04b83dfb015db10e681e414e57eb748b34c0fbb90fb790d13b" # signed interface-probe mechanism diagnostic +SHEAR_PROBES_SHA256 = "53ba8af8ac9c8078c40f84488c7ade4c11ccc91dd1fc275ff806042e9af30166" # signed interface-probe mechanism diagnostic def flow_functions(flow="taylor_green"): diff --git a/tests/validation/taylor_green/shear_probes.py b/tests/validation/taylor_green/shear_probes.py index 47521d0..75df0b0 100644 --- a/tests/validation/taylor_green/shear_probes.py +++ b/tests/validation/taylor_green/shear_probes.py @@ -1,12 +1,14 @@ """Signed interface-probe traces for the nested/twin shear presets (mechanism diagnostic). -No pass gates. 24 fixed physical points on two interface-normal transects, away -from corners, crossing L0->L1 and (nested only) L1->L2: - V: y = 64.1, across the vertical faces x = 16 (L0/L1) and x = 22 (L1/L2) of box 1; - H: x = 90.1, across the horizontal faces y = 24 (L0/L1) and y = 30 (L1/L2) of box 2. -Each point is sampled at the ACTIVE leaf cell containing it, so cases with -different grids (U128, U512, nested, twin) give different cell centers: the -actual center and level are stored and analytic fields are evaluated there. +No pass gates. Native-validated 24-target layout through box 1, in root-128 +physical coordinates, crossing L0->L1 and (nested only) L1->L2 away from corners: + horizontal line y = 56 across the vertical faces x = 16 and x = 22; + vertical line x = 38 across the horizontal faces y = 16 and y = 22; + normal coordinates NORMAL_COORDS on both (nested: 3 L0 + 5 L1 + 4 L2 each). +Each target maps to the nearest ACTIVE leaf center (tie rule: distance, then +finer level, then smaller j, then smaller i). Grids differ between cases +(U128, U512, nested, twin), so actual centers, levels and displacements are +stored and analytic fields are evaluated at the actual centers. Samples are POST-collision G at root synchronization only (root-step units); no substep content is observed, and grid-scale alternation can alias. """ @@ -19,9 +21,10 @@ import shear_wave as sw from oracle import CX, CY, U ROOT_N = 128 -OFFSETS = (-3.9, -1.9, -0.4, 0.1, 0.6, 2.1, 4.1, 5.6, 6.1, 6.6, 8.1, 10.1) # from outer face -TRANSECTS = {"V": dict(axis=0, face=16.0, other=64.1), # x varies, y fixed - "H": dict(axis=1, face=24.0, other=90.1)} # y varies, x fixed +NORMAL_COORDS = (12.5, 14.5, 15.5, 16.25, 17.25, 19.25, 20.75, 21.75, + 22.125, 23.125, 24.125, 26.125) +TRANSECTS = {"horizontal_y56": dict(axis=0, other=56.0), # x varies, y fixed + "vertical_x38": dict(axis=1, other=38.0)} # y varies, x fixed STEPS = tuple(range(0, 129)) + tuple(range(1920, 2049)) # root steps, both inclusive TRACE_MAX_BYTES = 600_000 @@ -29,10 +32,9 @@ TRACE_MAX_BYTES = 600_000 def points(): out = [] for name, t in TRANSECTS.items(): - for offset in OFFSETS: - along = t["face"] + offset + for along in NORMAL_COORDS: xy = (along, t["other"]) if t["axis"] == 0 else (t["other"], along) - out.append((name, offset, xy)) + out.append((name, along, xy)) return out @@ -42,25 +44,33 @@ def case_steps(n): def select(masks, n=ROOT_N): - """Leaf cell containing each point: finest level whose ACTIVE mask covers it. + """Nearest ACTIVE leaf center for each target (deterministic tie rule). - Points and stored centers are in root-physical units (root cell = 1); a case - with root N = n has cells of size ROOT_N / (n * 2**level) there. + Targets and stored centers are in root-physical units (root cell = 1); a case + with root N = n has cell size h = ROOT_N / (n * 2**level) there (U512: 0.25). """ if n % ROOT_N: raise ValueError("case root N must be a multiple of 128") probes = [] - for name, offset, (x, y) in points(): - for level in reversed(range(len(masks))): - scale = (n // ROOT_N) * 2 ** level - i, j = int(math.floor(x * scale)), int(math.floor(y * scale)) - if masks[level][j, i]: - h = 1.0 / scale - probes.append(dict(transect=name, offset=offset, point=[x, y], level=level, - i=i, j=j, center=[h * (i + 0.5), h * (j + 0.5)])) - break - else: - raise ValueError(f"point {(x, y)} is not covered by an ACTIVE leaf") + for name, along, (x, y) in points(): + best = None + for level, mask in enumerate(masks): + h = ROOT_N / (n * 2 ** level) + i0, j0 = int(math.floor(x / h)), int(math.floor(y / h)) + for j in range(j0 - 2, j0 + 3): + for i in range(i0 - 2, i0 + 3): + if 0 <= j < mask.shape[0] and 0 <= i < mask.shape[1] and mask[j, i]: + cx, cy = h * (i + 0.5), h * (j + 0.5) + key = (math.hypot(cx - x, cy - y), -level, j, i) + if best is None or key < best[0]: + best = (key, level, i, j, cx, cy) + if best is None: + raise ValueError(f"no ACTIVE leaf near target {(x, y)}") + _, level, i, j, cx, cy = best + probes.append(dict(transect=name, normal=along, point=[x, y], level=level, i=i, j=j, + center=[cx, cy], displacement=[cx - x, cy - y])) + if len({(p["level"], p["i"], p["j"]) for p in probes}) != len(probes): + raise ValueError("probe targets map to duplicate cells") return probes @@ -102,7 +112,7 @@ def validate(trace): import json if trace.get("steps") != list(STEPS) or len(trace["values"]) != len(STEPS): raise ValueError("trace steps differ from the fixed schedule") - if len(trace["probes"]) != len(OFFSETS) * len(TRANSECTS): + if len(trace["probes"]) != len(NORMAL_COORDS) * len(TRANSECTS): raise ValueError("unexpected probe count") width = 3 * len(trace["probes"]) if any(len(r) != width or not all(math.isfinite(v) for v in r) for r in trace["values"]): diff --git a/tests/validation/taylor_green/test_shear_wave.py b/tests/validation/taylor_green/test_shear_wave.py index 9579791..a80350e 100644 --- a/tests/validation/taylor_green/test_shear_wave.py +++ b/tests/validation/taylor_green/test_shear_wave.py @@ -977,11 +977,17 @@ class ProbeTraceTests(unittest.TestCase): u512 = sp.select([np.ones((512, 512), bool)], 512) self.assertTrue(all(p["level"] == 0 for p in u128 + u512)) for case in (nested, twin, u128, u512): + self.assertEqual(len({(p["level"], p["i"], p["j"]) for p in case}), 24) # unique for p in case: - # The stored center is the actual cell center containing the point (root units). - h = 1.0 / (2 ** p["level"]) if case is not u512 else 0.25 - self.assertLessEqual(abs(p["center"][0] - p["point"][0]), h / 2) - self.assertLessEqual(abs(p["center"][1] - p["point"][1]), h / 2) + dx, dy = p["displacement"] + self.assertEqual([p["center"][0] - p["point"][0], p["center"][1] - p["point"][1]], + [dx, dy]) + self.assertEqual(max(math.hypot(*p["displacement"]) for p in u128), 0.625) + self.assertLessEqual(max(math.hypot(*p["displacement"]) for p in u512), 0.25 * 0.7072) + # Nested: 3 L0 + 5 L1 + 4 L2 per transect, as checked by native review. + for name in sp.TRANSECTS: + levels = [p["level"] for p in nested if p["transect"] == name] + self.assertEqual([levels.count(v) for v in (0, 1, 2)], [3, 5, 4]) # Different grids give different centers: no pretended collocation. self.assertNotEqual(u128[3]["center"], u512[3]["center"]) self.assertEqual(sp.case_steps(512)[:3], [0, 4, 8])