"""Cross-block constant registry for VMS CAS Audit. Blocks register their numeric constants here. The master runner checks global consistency after all blocks complete. """ import math _registry = {} # canonical_name -> list of (value, source) def register(name, value, source): """Register a constant value from a block. Args: name: canonical name (e.g., 'c', 'h', 'eps0') value: numeric value in SI units source: source block identifier (e.g., 'F0007') """ # Safe cast: handle SymPy objects that need .evalf() before float() v = float(value.evalf()) if hasattr(value, "evalf") else float(value) _registry.setdefault(name, []).append((v, source)) def get(name): """Retrieve most recently registered value for a constant.""" if name not in _registry or not _registry[name]: raise KeyError(f"Constant '{name}' not registered") return _registry[name][-1][0] def get_registry(): """Return the full registry.""" return _registry def check_consistency(tol_map=None): """Check all registered constants for cross-block consistency. Returns (all_pass, report_lines). """ default_tol = 1e-6 if tol_map is None: tol_map = { 'c': default_tol, 'eps0': default_tol, 'mu0': default_tol, 'h': default_tol, 'hbar': default_tol, 'kB': default_tol, 'NA': default_tol, 'sigma_sb': default_tol, 'G': 1e-4, 'e': default_tol, 'me': 1e-6, } all_pass = True lines = [] for name in sorted(_registry.keys()): entries = _registry[name] vals = [v for v, _ in entries] srcs = [s for _, s in entries] tol = tol_map.get(name, default_tol) if len(vals) < 2: lines.append(f" {name:10s} : SKIP (found in {len(vals)} block)") continue ref = sorted(vals)[len(vals) // 2] # median max_dev = max(abs(v - ref) / max(abs(ref), 1e-300) for v in vals) ok = max_dev <= tol all_pass = all_pass and ok tag = "PASS" if ok else "FAIL" lines.append(f" {name:10s} : {tag} (n={len(vals)}, max rel dev={max_dev:.3g}, tol={tol:.3g})") if not ok: for v, s in zip(vals, srcs): lines.append(f" {s:30s} {v:.15g}") return all_pass, lines def check_identities(): """Check cross-block identities: c^2*eps0*mu0=1, hbar=h/2pi, sigma formula. Returns (all_pass, report_lines). """ all_pass = True lines = [] tol = 1e-6 # Group constants by source block block_consts = {} for name, entries in _registry.items(): for val, src in entries: block_consts.setdefault(src, {})[name] = val # 1) c^2 * eps0 * mu0 = 1 id1_pass = True id1_any = False for src, consts in block_consts.items(): if 'c' in consts and 'eps0' in consts and 'mu0' in consts: id1_any = True lhs = consts['c'] ** 2 * consts['eps0'] * consts['mu0'] if abs(lhs - 1) > tol: id1_pass = False tag = "PASS" if id1_pass else "FAIL" lines.append(f" c^2*eps0*mu0=1 : {tag}" if id1_any else " c^2*eps0*mu0=1 : SKIP") all_pass = all_pass and (id1_pass or not id1_any) # 2) hbar = h / (2*pi) id2_pass = True id2_any = False for src, consts in block_consts.items(): if 'h' in consts and 'hbar' in consts: id2_any = True rel = abs(consts['hbar'] - consts['h'] / (2 * math.pi)) rel /= max(abs(consts['h'] / (2 * math.pi)), 1e-300) if rel > tol: id2_pass = False tag = "PASS" if id2_pass else "FAIL" lines.append(f" hbar=h/(2pi) : {tag}" if id2_any else " hbar=h/(2pi) : SKIP") all_pass = all_pass and (id2_pass or not id2_any) # 3) sigma_sb = pi^2 * kB^4 / (60 * hbar^3 * c^2) id3_pass = True id3_any = False for src, consts in block_consts.items(): if all(k in consts for k in ('sigma_sb', 'kB', 'hbar', 'c')): id3_any = True sig = consts['sigma_sb'] formula = (math.pi ** 2 * consts['kB'] ** 4) / \ (60 * consts['hbar'] ** 3 * consts['c'] ** 2) rel = abs(sig - formula) / max(abs(formula), 1e-300) # Looser tolerance (5e-4) because the formula compounds four # constants (kB⁴, ℏ³, c²) — rounding in any one propagates. if rel > 5e-4: id3_pass = False tag = "PASS" if id3_pass else "FAIL" lines.append(f" sigma formula : {tag}" if id3_any else " sigma formula : SKIP") all_pass = all_pass and (id3_pass or not id3_any) return all_pass, lines def clear(): """Reset registry (for testing).""" _registry.clear()