FA-71636 / Seismic magnitude estimation / Open access
GNSS peak ground displacement magnitude: magnitude inversion · case 01
PGD magnitudes are several units too high and distance-independent.
ROOT CAUSE
Operator precedence drops the parentheses around B + C log10 R.
VERIFIED REPAIR
Divide by (B + C log10 R).
Unsuccessful approach: Adding A instead of subtracting it shifts every magnitude.
Case contract
obs are [PGD in m, epicentral km]. Per station PGD in cm, R = hypocentral distance; skip PGD < 2 cm or R > 1000 km. log10(PGD) = A + B M + C M log10 R with A=-4.434, B=1.047, C=-0.138, solved for M. Return [mean M rounded 0.01, stations used] or None.
Why this case matters
GNSS PGD magnitudes do not saturate for great earthquakes in early warning.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(obs, depth_km):
A, B, C = -4.434, 1.047, -0.138
ms = []
for pgd_m, epi in obs:
pgd_cm = pgd_m * 100.0
r = math.hypot(epi, depth_km)
if pgd_cm < 2.0 or r > 1000:
continue
ms.append((math.log10(pgd_cm) - A) / B + C * math.log10(r))
if not ms:
return None
return [round(sum(ms) / len(ms), 2), len(ms)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['GNSS set 0', [[[0.005, 700], [0.05, 1100], [0.02, 995], [0.02, 700]], 25], [7.36, 2]], ['GNSS set 1', [[[0.005, 200], [0.005, 10], [1.2, 995], [0.005, 995], [0.02, 995]], 10], [8.88, 2]], ['GNSS set 2', [[[1.2, 200]], 10], [8.93, 1]], ['GNSS set 3', [[[1.2, 1100]], 10], None], ['GNSS set 4', [[[0.005, 995], [0.02, 200], [0.005, 1100], [1.2, 995], [1.2, 60]], 10], [8.3, 3]], ['GNSS set 5', [[[0.005, 10], [0.3, 60], [0.3, 1100], [0.3, 10]], 60], [7.48, 2]], ['GNSS set 6', [[[0.3, 10], [0.005, 200], [1.2, 1100], [0.005, 1100], [0.02, 60]], 60], [6.72, 2]], ['GNSS set 7', [[[0.005, 700], [1.2, 1100]], 25], None]], [['GNSS set 4', [[[0.005, 995], [0.02, 200], [0.005, 1100], [1.2, 995], [1.2, 60]], 10], [8.3, 3]], ['GNSS set 7', [[[0.005, 700], [1.2, 1100]], 25], None], ['GNSS set 8', [[[1.2, 700], [0.005, 10]], 10], [9.95, 1]], ['GNSS set 9', [[[1.2, 60], [0.3, 10], [0.05, 60]], 25], [7.19, 3]], ['GNSS set 10', [[[0.3, 700], [0.02, 1100]], 60], [9.04, 1]], ['GNSS set 11', [[[1.2, 1100], [0.02, 200], [0.02, 200], [0.005, 1100], [0.05, 10]], 25], [6.34, 3]], ['GNSS set 12', [[[0.3, 200]], 25], [8.11, 1]], ['GNSS set 13', [[[0.005, 700], [0.05, 700], [1.2, 995], [0.3, 700]], 25], [9.05, 3]]], [['GNSS set 8', [[[1.2, 700], [0.005, 10]], 10], [9.95, 1]], ['GNSS set 13', [[[0.005, 700], [0.05, 700], [1.2, 995], [0.3, 700]], 25], [9.05, 3]], ['GNSS set 14', [[[1.2, 700], [0.02, 200], [0.02, 700]], 10], [7.89, 3]], ['GNSS set 15', [[[0.005, 60]], 10], None], ['GNSS set 16', [[[1.2, 200], [0.005, 200], [0.02, 10], [0.3, 700]], 25], [7.85, 3]], ['GNSS set 17', [[[0.3, 200], [0.05, 995]], 60], [8.12, 2]], ['GNSS set 18', [[[0.02, 10], [0.3, 1100], [1.2, 200], [0.005, 700], [1.2, 995]], 10], [8.18, 3]], ['GNSS set 19', [[[0.02, 700], [0.02, 60]], 60], [6.65, 2]]], [['GNSS set 11', [[[1.2, 1100], [0.02, 200], [0.02, 200], [0.005, 1100], [0.05, 10]], 25], [6.34, 3]], ['GNSS set 19', [[[0.02, 700], [0.02, 60]], 60], [6.65, 2]], ['GNSS set 21', [[[1.2, 200], [0.02, 1100], [0.005, 700], [0.3, 995]], 10], [9.13, 2]], ['GNSS set 22', [[[0.05, 700], [1.2, 200], [1.2, 10], [1.2, 1100], [0.3, 1100]], 10], [8.04, 3]], ['GNSS set 23', [[[0.3, 700], [0.05, 995], [1.2, 200], [0.005, 10]], 25], [8.69, 3]], ['GNSS set 24', [[[0.02, 1100], [1.2, 200], [0.02, 1100], [0.3, 1100]], 60], [8.96, 1]], ['GNSS set 25', [[[0.005, 1100], [0.3, 60], [1.2, 10], [0.05, 700], [1.2, 60]], 25], [7.78, 4]], ['GNSS set 26', [[[0.05, 995]], 60], [8.11, 1]]], [['GNSS set 14', [[[1.2, 700], [0.02, 200], [0.02, 700]], 10], [7.89, 3]], ['GNSS set 24', [[[0.02, 1100], [1.2, 200], [0.02, 1100], [0.3, 1100]], 60], [8.96, 1]], ['GNSS set 28', [[[0.02, 1100], [0.005, 200], [0.05, 200]], 10], [7.04, 1]], ['GNSS set 29', [[[0.3, 60], [0.02, 1100], [0.3, 995], [1.2, 60]], 60], [8.42, 3]], ['GNSS set 30', [[[0.3, 200], [1.2, 10], [0.3, 1100]], 60], [8.13, 2]], ['GNSS set 31', [[[0.05, 200], [0.05, 700]], 10], [7.44, 2]], ['GNSS set 32', [[[1.2, 1100]], 10], None], ['GNSS set 33', [[[0.02, 10], [0.3, 995], [1.2, 1100], [0.02, 700]], 60], [7.5, 3]]]]
for label, args, expected in fixtures[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| GNSS set 0 | [4.12, 2] | [7.36, 2] | Failed |
| GNSS set 1 | [4.96, 2] | [8.88, 2] | Failed |
| GNSS set 2 | [5.9, 1] | [8.93, 1] | Failed |
| GNSS set 3 | None | None | Passed |
| GNSS set 4 | [5.33, 3] | [8.3, 3] | Failed |
| GNSS set 5 | [5.39, 2] | [7.48, 2] | Failed |
| GNSS set 6 | [4.83, 2] | [6.72, 2] | Failed |
| GNSS set 7 | None | None | Passed |
SHA-256 / 45340db8101377d39fde2065ced4b157d7a44c378b0efbc262f4fd596a45a9e3
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(obs, depth_km):
A, B, C = -4.434, 1.047, -0.138
ms = []
for pgd_m, epi in obs:
pgd_cm = pgd_m * 100.0
r = math.hypot(epi, depth_km)
if pgd_cm < 2.0 or r > 1000:
continue
ms.append((math.log10(pgd_cm) + A) / (B + C * math.log10(r)))
if not ms:
return None
return [round(sum(ms) / len(ms), 2), len(ms)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['GNSS set 0', [[[0.005, 700], [0.05, 1100], [0.02, 995], [0.02, 700]], 25], [7.36, 2]], ['GNSS set 1', [[[0.005, 200], [0.005, 10], [1.2, 995], [0.005, 995], [0.02, 995]], 10], [8.88, 2]], ['GNSS set 2', [[[1.2, 200]], 10], [8.93, 1]], ['GNSS set 3', [[[1.2, 1100]], 10], None], ['GNSS set 4', [[[0.005, 995], [0.02, 200], [0.005, 1100], [1.2, 995], [1.2, 60]], 10], [8.3, 3]], ['GNSS set 5', [[[0.005, 10], [0.3, 60], [0.3, 1100], [0.3, 10]], 60], [7.48, 2]], ['GNSS set 6', [[[0.3, 10], [0.005, 200], [1.2, 1100], [0.005, 1100], [0.02, 60]], 60], [6.72, 2]], ['GNSS set 7', [[[0.005, 700], [1.2, 1100]], 25], None]], [['GNSS set 4', [[[0.005, 995], [0.02, 200], [0.005, 1100], [1.2, 995], [1.2, 60]], 10], [8.3, 3]], ['GNSS set 7', [[[0.005, 700], [1.2, 1100]], 25], None], ['GNSS set 8', [[[1.2, 700], [0.005, 10]], 10], [9.95, 1]], ['GNSS set 9', [[[1.2, 60], [0.3, 10], [0.05, 60]], 25], [7.19, 3]], ['GNSS set 10', [[[0.3, 700], [0.02, 1100]], 60], [9.04, 1]], ['GNSS set 11', [[[1.2, 1100], [0.02, 200], [0.02, 200], [0.005, 1100], [0.05, 10]], 25], [6.34, 3]], ['GNSS set 12', [[[0.3, 200]], 25], [8.11, 1]], ['GNSS set 13', [[[0.005, 700], [0.05, 700], [1.2, 995], [0.3, 700]], 25], [9.05, 3]]], [['GNSS set 8', [[[1.2, 700], [0.005, 10]], 10], [9.95, 1]], ['GNSS set 13', [[[0.005, 700], [0.05, 700], [1.2, 995], [0.3, 700]], 25], [9.05, 3]], ['GNSS set 14', [[[1.2, 700], [0.02, 200], [0.02, 700]], 10], [7.89, 3]], ['GNSS set 15', [[[0.005, 60]], 10], None], ['GNSS set 16', [[[1.2, 200], [0.005, 200], [0.02, 10], [0.3, 700]], 25], [7.85, 3]], ['GNSS set 17', [[[0.3, 200], [0.05, 995]], 60], [8.12, 2]], ['GNSS set 18', [[[0.02, 10], [0.3, 1100], [1.2, 200], [0.005, 700], [1.2, 995]], 10], [8.18, 3]], ['GNSS set 19', [[[0.02, 700], [0.02, 60]], 60], [6.65, 2]]], [['GNSS set 11', [[[1.2, 1100], [0.02, 200], [0.02, 200], [0.005, 1100], [0.05, 10]], 25], [6.34, 3]], ['GNSS set 19', [[[0.02, 700], [0.02, 60]], 60], [6.65, 2]], ['GNSS set 21', [[[1.2, 200], [0.02, 1100], [0.005, 700], [0.3, 995]], 10], [9.13, 2]], ['GNSS set 22', [[[0.05, 700], [1.2, 200], [1.2, 10], [1.2, 1100], [0.3, 1100]], 10], [8.04, 3]], ['GNSS set 23', [[[0.3, 700], [0.05, 995], [1.2, 200], [0.005, 10]], 25], [8.69, 3]], ['GNSS set 24', [[[0.02, 1100], [1.2, 200], [0.02, 1100], [0.3, 1100]], 60], [8.96, 1]], ['GNSS set 25', [[[0.005, 1100], [0.3, 60], [1.2, 10], [0.05, 700], [1.2, 60]], 25], [7.78, 4]], ['GNSS set 26', [[[0.05, 995]], 60], [8.11, 1]]], [['GNSS set 14', [[[1.2, 700], [0.02, 200], [0.02, 700]], 10], [7.89, 3]], ['GNSS set 24', [[[0.02, 1100], [1.2, 200], [0.02, 1100], [0.3, 1100]], 60], [8.96, 1]], ['GNSS set 28', [[[0.02, 1100], [0.005, 200], [0.05, 200]], 10], [7.04, 1]], ['GNSS set 29', [[[0.3, 60], [0.02, 1100], [0.3, 995], [1.2, 60]], 60], [8.42, 3]], ['GNSS set 30', [[[0.3, 200], [1.2, 10], [0.3, 1100]], 60], [8.13, 2]], ['GNSS set 31', [[[0.05, 200], [0.05, 700]], 10], [7.44, 2]], ['GNSS set 32', [[[1.2, 1100]], 10], None], ['GNSS set 33', [[[0.02, 10], [0.3, 995], [1.2, 1100], [0.02, 700]], 60], [7.5, 3]]]]
for label, args, expected in fixtures[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| GNSS set 0 | [-6.42, 2] | [7.36, 2] | Failed |
| GNSS set 1 | [-5.12, 2] | [8.88, 2] | Failed |
| GNSS set 2 | [-3.23, 1] | [8.93, 1] | Failed |
| GNSS set 3 | None | None | Passed |
| GNSS set 4 | [-4.11, 3] | [8.3, 3] | Failed |
| GNSS set 5 | [-3.74, 2] | [7.48, 2] | Failed |
| GNSS set 6 | [-4.49, 2] | [6.72, 2] | Failed |
| GNSS set 7 | None | None | Passed |
SHA-256 / f7c27bf9827b0d52b44e4e057c2af27b5b7cd99411e8d2865ba74993c4fba3b7
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(obs, depth_km):
A, B, C = -4.434, 1.047, -0.138
ms = []
for pgd_m, epi in obs:
pgd_cm = pgd_m * 100.0
r = math.hypot(epi, depth_km)
if pgd_cm < 2.0 or r > 1000:
continue
ms.append((math.log10(pgd_cm) - A) / (B + C * math.log10(r)))
if not ms:
return None
return [round(sum(ms) / len(ms), 2), len(ms)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['GNSS set 0', [[[0.005, 700], [0.05, 1100], [0.02, 995], [0.02, 700]], 25], [7.36, 2]], ['GNSS set 1', [[[0.005, 200], [0.005, 10], [1.2, 995], [0.005, 995], [0.02, 995]], 10], [8.88, 2]], ['GNSS set 2', [[[1.2, 200]], 10], [8.93, 1]], ['GNSS set 3', [[[1.2, 1100]], 10], None], ['GNSS set 4', [[[0.005, 995], [0.02, 200], [0.005, 1100], [1.2, 995], [1.2, 60]], 10], [8.3, 3]], ['GNSS set 5', [[[0.005, 10], [0.3, 60], [0.3, 1100], [0.3, 10]], 60], [7.48, 2]], ['GNSS set 6', [[[0.3, 10], [0.005, 200], [1.2, 1100], [0.005, 1100], [0.02, 60]], 60], [6.72, 2]], ['GNSS set 7', [[[0.005, 700], [1.2, 1100]], 25], None]], [['GNSS set 4', [[[0.005, 995], [0.02, 200], [0.005, 1100], [1.2, 995], [1.2, 60]], 10], [8.3, 3]], ['GNSS set 7', [[[0.005, 700], [1.2, 1100]], 25], None], ['GNSS set 8', [[[1.2, 700], [0.005, 10]], 10], [9.95, 1]], ['GNSS set 9', [[[1.2, 60], [0.3, 10], [0.05, 60]], 25], [7.19, 3]], ['GNSS set 10', [[[0.3, 700], [0.02, 1100]], 60], [9.04, 1]], ['GNSS set 11', [[[1.2, 1100], [0.02, 200], [0.02, 200], [0.005, 1100], [0.05, 10]], 25], [6.34, 3]], ['GNSS set 12', [[[0.3, 200]], 25], [8.11, 1]], ['GNSS set 13', [[[0.005, 700], [0.05, 700], [1.2, 995], [0.3, 700]], 25], [9.05, 3]]], [['GNSS set 8', [[[1.2, 700], [0.005, 10]], 10], [9.95, 1]], ['GNSS set 13', [[[0.005, 700], [0.05, 700], [1.2, 995], [0.3, 700]], 25], [9.05, 3]], ['GNSS set 14', [[[1.2, 700], [0.02, 200], [0.02, 700]], 10], [7.89, 3]], ['GNSS set 15', [[[0.005, 60]], 10], None], ['GNSS set 16', [[[1.2, 200], [0.005, 200], [0.02, 10], [0.3, 700]], 25], [7.85, 3]], ['GNSS set 17', [[[0.3, 200], [0.05, 995]], 60], [8.12, 2]], ['GNSS set 18', [[[0.02, 10], [0.3, 1100], [1.2, 200], [0.005, 700], [1.2, 995]], 10], [8.18, 3]], ['GNSS set 19', [[[0.02, 700], [0.02, 60]], 60], [6.65, 2]]], [['GNSS set 11', [[[1.2, 1100], [0.02, 200], [0.02, 200], [0.005, 1100], [0.05, 10]], 25], [6.34, 3]], ['GNSS set 19', [[[0.02, 700], [0.02, 60]], 60], [6.65, 2]], ['GNSS set 21', [[[1.2, 200], [0.02, 1100], [0.005, 700], [0.3, 995]], 10], [9.13, 2]], ['GNSS set 22', [[[0.05, 700], [1.2, 200], [1.2, 10], [1.2, 1100], [0.3, 1100]], 10], [8.04, 3]], ['GNSS set 23', [[[0.3, 700], [0.05, 995], [1.2, 200], [0.005, 10]], 25], [8.69, 3]], ['GNSS set 24', [[[0.02, 1100], [1.2, 200], [0.02, 1100], [0.3, 1100]], 60], [8.96, 1]], ['GNSS set 25', [[[0.005, 1100], [0.3, 60], [1.2, 10], [0.05, 700], [1.2, 60]], 25], [7.78, 4]], ['GNSS set 26', [[[0.05, 995]], 60], [8.11, 1]]], [['GNSS set 14', [[[1.2, 700], [0.02, 200], [0.02, 700]], 10], [7.89, 3]], ['GNSS set 24', [[[0.02, 1100], [1.2, 200], [0.02, 1100], [0.3, 1100]], 60], [8.96, 1]], ['GNSS set 28', [[[0.02, 1100], [0.005, 200], [0.05, 200]], 10], [7.04, 1]], ['GNSS set 29', [[[0.3, 60], [0.02, 1100], [0.3, 995], [1.2, 60]], 60], [8.42, 3]], ['GNSS set 30', [[[0.3, 200], [1.2, 10], [0.3, 1100]], 60], [8.13, 2]], ['GNSS set 31', [[[0.05, 200], [0.05, 700]], 10], [7.44, 2]], ['GNSS set 32', [[[1.2, 1100]], 10], None], ['GNSS set 33', [[[0.02, 10], [0.3, 995], [1.2, 1100], [0.02, 700]], 60], [7.5, 3]]]]
for label, args, expected in fixtures[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| GNSS set 0 | [7.36, 2] | [7.36, 2] | Passed |
| GNSS set 1 | [8.88, 2] | [8.88, 2] | Passed |
| GNSS set 2 | [8.93, 1] | [8.93, 1] | Passed |
| GNSS set 3 | None | None | Passed |
| GNSS set 4 | [8.3, 3] | [8.3, 3] | Passed |
| GNSS set 5 | [7.48, 2] | [7.48, 2] | Passed |
| GNSS set 6 | [6.72, 2] | [6.72, 2] | Passed |
| GNSS set 7 | None | None | Passed |
SHA-256 / 603be488c8fe5b370d9d8f8850134eb3a5f08c1490d03dd3d257c96cba0550fe
Verification & scope
Stipulated deterministic teaching model of a seismological magnitude procedure; constants and tables are fixed by the contract and no claim of agency or standards conformance is made. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:48:31.433336+00:00.
Case digest / 911f8cb072d648344ac54edf432db8d30349d4c0fae84f2fd10d6c454c001dcb