Residuals and Fit Quality¶
Residual analysis is the bridge between model output and observation quality.
Residuals.calculate is the primary entrypoint; helper functions provide
fine-grained control for diagnostics.
Simple Residuals.calculate Example¶
import numpy as np
from adam_core.coordinates import CartesianCoordinates, CoordinateCovariances, Origin, OriginCodes
from adam_core.coordinates.residuals import Residuals
from adam_core.time import Timestamp
t = Timestamp.from_mjd(np.array([60200.0, 60200.1]), scale="tdb")
cov = CoordinateCovariances.from_sigmas(np.full((2, 6), 1e-8))
observed = CartesianCoordinates.from_kwargs(
x=[1.0, 1.1], y=[0.0, 0.1], z=[0.0, 0.0],
vx=[0.0, 0.0], vy=[0.01, 0.01], vz=[0.0, 0.0],
time=t,
covariance=cov,
origin=Origin.from_OriginCodes(OriginCodes.SUN, size=2),
frame="ecliptic",
)
predicted = CartesianCoordinates.from_kwargs(
x=[1.0, 1.10001], y=[0.0, 0.09999], z=[0.0, 0.0],
vx=[0.0, 0.0], vy=[0.01, 0.01], vz=[0.0, 0.0],
time=t,
covariance=CoordinateCovariances.from_sigmas(np.full((2, 6), 5e-9)),
origin=Origin.from_OriginCodes(OriginCodes.SUN, size=2),
frame="ecliptic",
)
residuals = Residuals.calculate(observed, predicted, use_predicted_covariance=True)
residual_matrix = residuals.to_array()
chi2 = residuals.chi2.to_pylist()
dof = residuals.dof.to_pylist()
p = residuals.probability.to_pylist()
Atomic Helper Functions¶
import numpy as np
from adam_core.coordinates.residuals import (
calculate_chi2,
calculate_reduced_chi2,
bound_longitude_residuals,
apply_cosine_latitude_correction,
)
r = np.array([[0.01, -0.02, 0.0, 0.0, 0.0, 0.0]])
c = np.diag([1e-4, 1e-4, 1e-4, 1e-6, 1e-6, 1e-6])[None, :, :]
chi2 = calculate_chi2(r, c)
reduced = calculate_reduced_chi2(residuals, parameters=6)
# Spherical-specific helpers.
observed_sph = np.array([[1.0, 359.0, 20.0, 0.0, 0.0, 0.0]])
raw_resid_sph = np.array([[0.0, 358.0, 0.0, 0.0, 5.0, 0.0]])
wrapped = bound_longitude_residuals(observed_sph, raw_resid_sph)
corrected, corrected_cov = apply_cosine_latitude_correction(
observed_sph[:, 2],
wrapped,
c.copy(),
)
Predicted Covariance Tradeoff¶
# Include both model + observation uncertainty.
r_total = Residuals.calculate(observed, predicted, use_predicted_covariance=True)
# Observation-only weighting.
r_obs_only = Residuals.calculate(observed, predicted, use_predicted_covariance=False)
Use observation-only weighting for strict measurement-gating. Use combined weighting when prediction uncertainty is a material part of the decision.
Spherical Residuals Behavior¶
For SphericalCoordinates, Residuals.calculate automatically:
wraps longitude residuals into [-180, 180] with boundary-aware sign handling
applies cosine(latitude) scaling to longitude and longitudinal-rate terms
applies equivalent scaling to covariance before chi-square evaluation
Failure Modes Worth Testing¶
observed/predicted type mismatch
frame mismatch
origin mismatch
missing diagonal covariance values
length mismatch when predicted is not scalar-broadcastable