FA-12181 / Astronomical coordinate conventions / Open access
SIP distortion is evaluated after the linear WCS transform · case 01
SIP distortion is evaluated after the linear WCS transform.
ROOT CAUSE
Pixel-space distortion coefficients are evaluated in intermediate world coordinates.
VERIFIED REPAIR
Evaluate the stipulated SIP polynomial in pixel offsets before applying the linear matrix.
Unsuccessful approach: Moving only the square term before the matrix leaves the mixed term in the wrong basis.
Case contract
Reduced SIP model for reference-relative pixel offsets u,v: corrected pair (u+a*u*u+b*u*v,v), followed by diagonal linear matrix (sx,sy). Return intermediate coordinates, no sky projection or full SIP implementation.
Why this case matters
Astronomical coordinate adapters must preserve convention semantics. Primary background: https://docs.astropy.org/en/stable/api/astropy.wcs.WCS.html
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(u, v, a, b, sx, sy):
return [sx*u+a*(sx*u)**2+b*(sx*u)*(sy*v),sy*v]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('square and cross distortion', solve(N,2,1,1,3,4), [3*(N+N*N+2*N),8])
check('cross term only', solve(N,2,0,1,3,4), [9*N,8])
check('square only', solve(N,0,1,0,3,4), [3*(N+N*N),0])
check('reference origin', solve(0,0,1,1,3,4), [0,0])
check('identity transform', solve(N,2,1,1,1,1), [N+N*N+2*N,2])
check('no distortion', solve(N,-2,0,0,3,4), [3*N,-8])
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 |
|---|---|---|---|
| square and cross distortion | [36, 8] | [12, 8] | Failed |
| cross term only | [27, 8] | [9, 8] | Failed |
| square only | [12, 0] | [6, 0] | Failed |
| reference origin | [0, 0] | [0, 0] | Passed |
| identity transform | [4, 2] | [4, 2] | Passed |
| no distortion | [3, -8] | [3, -8] | Passed |
SHA-256 / a0736d8663079cee35c466074501ecd4c8f7874c59784d50e6aed3520fc84570
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(u, v, a, b, sx, sy):
return [sx*(u+a*u*u)+b*(sx*u)*(sy*v),sy*v]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('square and cross distortion', solve(N,2,1,1,3,4), [3*(N+N*N+2*N),8])
check('cross term only', solve(N,2,0,1,3,4), [9*N,8])
check('square only', solve(N,0,1,0,3,4), [3*(N+N*N),0])
check('reference origin', solve(0,0,1,1,3,4), [0,0])
check('identity transform', solve(N,2,1,1,1,1), [N+N*N+2*N,2])
check('no distortion', solve(N,-2,0,0,3,4), [3*N,-8])
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 |
|---|---|---|---|
| square and cross distortion | [30, 8] | [12, 8] | Failed |
| cross term only | [27, 8] | [9, 8] | Failed |
| square only | [6, 0] | [6, 0] | Passed |
| reference origin | [0, 0] | [0, 0] | Passed |
| identity transform | [4, 2] | [4, 2] | Passed |
| no distortion | [3, -8] | [3, -8] | Passed |
SHA-256 / 9d885096e1f3b9338b125316a6e6d67e846053617dc15e7c94bb41d990f4b28d
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(u, v, a, b, sx, sy):
return [sx*(u+a*u*u+b*u*v),sy*v]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('square and cross distortion', solve(N,2,1,1,3,4), [3*(N+N*N+2*N),8])
check('cross term only', solve(N,2,0,1,3,4), [9*N,8])
check('square only', solve(N,0,1,0,3,4), [3*(N+N*N),0])
check('reference origin', solve(0,0,1,1,3,4), [0,0])
check('identity transform', solve(N,2,1,1,1,1), [N+N*N+2*N,2])
check('no distortion', solve(N,-2,0,0,3,4), [3*N,-8])
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 |
|---|---|---|---|
| square and cross distortion | [12, 8] | [12, 8] | Passed |
| cross term only | [9, 8] | [9, 8] | Passed |
| square only | [6, 0] | [6, 0] | Passed |
| reference origin | [0, 0] | [0, 0] | Passed |
| identity transform | [4, 2] | [4, 2] | Passed |
| no distortion | [3, -8] | [3, -8] | Passed |
SHA-256 / 0d9eac558fd64ba6851da847f12b4a69172e561f464af2df741a31851e02a797
Verification & scope
Reduced deterministic algebraic model only; no ephemeris, full sky projection, or physical accuracy claims. 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:38:54.579426+00:00.
Case digest / 7d0692112b37736a3674861465775ba1b9c484dc34b15565bb7dbb27ca0202c1