Results and Diagnostics
How to read, compare, and diagnose results produced by Tempo tasks.
- Top-level:
GeneralTempoResult
— the container you get fromrun_task
. - Iteration-level:
InternalIterationResult
— per TEMPO iteration outputs and stats. - Residual statistics: raw/whitened/normalized, grouped by backend and by in-fit vs in-tim.
- White-noise fit: EFAC/EQUAD/offset per backend with normality diagnostics.
- Compact metrics:
res.metrics
for quick scoring and ranking.
This page uses the same types your tests and examples return, so you can copy/paste the snippets below into a REPL.
GeneralTempoResult
The top-level container with everything you usually want after a run.
Key properties:
res.iterations::Vector{InternalIterationResult}
— all iterations, in orderres.final
— alias of the last iteration (res.iterations[end]
)res.last_successful
— last iteration with no engine/parse error and computed stats (ornothing
)res.success::Bool
andres.status::Symbol
— quick verdict and reasonres.convergence
— summary across iterations (wrms_tn and chi2 deltas)res.metrics::Dict{Symbol,Float64}
— small set of scalar numbers for ranking/plotsres.param_estimates::Dict{Symbol, (value, uncertainty)}
— final fit parametersres.par_file_final::Union{TempoParFile,Nothing}
— the output par-file if availableres.subresults
— nested results (e.g., nodes, grid cells) when a task runs many jobsres.metadata::Dict{Symbol,Any}
— timings, paths, seeds, policies, etc.
Convenience fields for quick access:
res.residual_stats
— alias tores.final.stats
res.white_noise_fit
— alias tores.final.white_noise_fit
Example checks:
res.success # true/false
res.status # :ok | :engine_failed | :parse_failed | :files_missing | :unknown
res.final.output # parsed TEMPO basic block and fit table
res.final.stats # residual statistics (see below)
res.metrics[:wrms_fit] # weighted RMS (fit window, whitened)
Compact metrics (res.metrics)
We compute a compact, task-agnostic set of scalars from the final iteration and convergence:
:chi2_fit_basic
— Fit chi-square reported by TEMPO (basic block):wrms_fit
,:wrms_tn_fit
,:chi2_fit
,:chi2r_fit
— from residual stats (in-fit set):pre_post_final
— TEMPO pre/post RMS ratio for the final iteration:delta_wrms_tn
,:delta_chi2
— absolute deltas between last two iterations (convergence):ad_white_fit
— global Anderson–Darling A² after a white-noise fit, if available
When stats include a separate in-TIM set and you enable it, these may also appear:
:wrms_tim
,:wrms_tn_tim
,:chi2_tim
,:chi2r_tim
Safe lookup helper:
using GravityToolsNext: result_metric
wrms = result_metric(res, :wrms_fit) # NaN if missing
chi2 = result_metric(res, :chi2_fit)
Tip: Use isfinite
when ranking many results and fall back to NaN-aware sorting.
Residual statistics
InternalIterationResult.stats
holds a ResidualStatisticsGroup
with two entries:
in_fit
— statistics restricted to the fit windowin_tim
— statistics over all TOAs (may equalin_fit
if there is no time window)
Each entry has:
all::ResidualStatistics
— overall (not split by backend)by_backend::Dict{Symbol,ResidualStatistics}
— per-backend stats
And each ResidualStatistics
includes four views:
raw
— basic stats of residualstn
— basic stats of whitened residuals (after TN plugin)norm_global
— normalized whitened residuals centered by a global weighted meannorm_local
— normalized whitened residuals centered by a local weighted mean
Quick peek:
stats = res.residual_stats # == res.final.stats
show(stats) # pretty, multi-section text/plain view
# Pull a few numbers
wrms_fit = stats.in_fit.all.tn.wrms
rchi2 = stats.in_fit.all.norm_global.red_chisqr
n_by_be = Dict(k => v.norm_global.n for (k,v) in stats.in_fit.by_backend)
Helpers:
in_fit_equals_in_tim(stats_group)
— tells if the same entry is reused (no time window)
White-noise fit (EFAC, EQUAD, offset)
When requested via settings, the final iteration may include a per-backend white-noise calibration:
wn = res.white_noise_fit # ::Union{WhiteNoiseFitResult,Nothing}
wn === nothing && @info "No white-noise fit performed"
If present:
wn.by_backend::Dict{Symbol,WhiteNoiseBackendFitResult}
— parameters and diagnostics per backendwn.failed_backends::Vector{Symbol}
— those that failed the solver or produced non-finite paramswn.global_stats::NormalizedResidualStats
— summary over concatenated normalized residuals from successful backends
Each backend entry stores:
efac::Float64
,equad::Float64
(µs), andoffset::Float64
(µs)ad_objective::Float64
— minimized A²stats::NormalizedResidualStats
— detailed normality metrics for the calibrated residualssuccess::Bool
,converged::Bool
Printing a rich, aligned report:
using GravityToolsNext: print_white_noise_fit_report
for (be, bres) in wn.by_backend
# Reconstruct the two vectors the report expects (residual_tn and original uncertainties)
# If you kept CombinedTOAEntry vectors per backend, pass them directly; otherwise skip.
# print_white_noise_fit_report(be, residuals_tn, uncertainties_orig, bres.efac, bres.equad, bres.offset, bres.ad_objective)
end
# Or quickly inspect the global stats
show(wn.global_stats)
Parameter estimates
The final fit table is summarized into res.param_estimates::Dict{Symbol,NamedTuple}
with (value, uncertainty) for each parameter that has finite values:
est = res.param_estimates
get(est, :F0, (value=NaN, uncertainty=NaN))
Success and convergence
res.success
— by default, mirrors final iteration error:!iserror(res.final.output.error)
res.convergence
— contains fields with deltas and aconverged::Bool
flag summarizing the path
You can build your own criteria by combining res.metrics
and res.convergence
.
Working with nested results
Tasks that execute multiple jobs (prior marginalization, adaptive grid) return a parent GeneralTempoResult
with res.subresults::Vector{GeneralTempoResult}
and a tag res.subresult_type
(e.g., :node
or :grid
). Traverse or aggregate using the same fields and helpers described above:
parent = res
for child in parent.subresults
@info "child wrms" result_metric(child, :wrms_fit)
end
Troubleshooting
- Missing metrics: use
result_metric(res, key)
which returnsNaN
if absent. - No time window: expect
res.residual_stats.in_fit === res.residual_stats.in_tim
. - White-noise failures: check
wn.failed_backends
and per-backendsuccess=false
entries. - Engine crashes: inspect
res.final.output.error
andres.metadata[:status]
if provided.