Skip to content

feat(nebula-lab): Omega Centauri groundwork, registration measurement and a radial profile diagnostic - #390

Open
apresmoi wants to merge 17 commits into
mainfrom
feat/omega-centauri
Open

apresmoi wants to merge 17 commits into
mainfrom
feat/omega-centauri

Conversation

@apresmoi

@apresmoi apresmoi commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Change

Groundwork for baking Omega Centauri (NGC 5139) as a volumetric emission field, plus the measurement that currently prevents the bake.

A globular cluster has no diffuse gas, so a future bake would represent its integrated starlight. No Omega Centauri volume is baked or promoted by this PR. PolyCSS cannot draw ten million point sources. The lab's scope covers this deliberately — before 1925 the globulars were nebulae, and the lab keeps that appearance-first scope.

Acquired and hash-pinned (reproducible with node labs/nebula/run.mts acquire-images labs/nebula/sources/reference-images.json): the ESO VST/OmegaCAM mosaic eso1119b, the MPG/ESO 2.2m WFI image eso0844a, and a DSS2 FITS cutout through the lab's existing CDS hips2fits path, whose WCS is exact by construction.

Prior. king-abel-profile.json is a single-mass King (1962) profile fixed to the measured core radius (4.54 pc) and half-mass radius (10.42 pc) from Baumgardt & Hilker, Abel-inverted numerically and converged to six significant figures. For a spherical source the projection determines the 3D emissivity uniquely, so depth here is recovered rather than invented — the assumption is sphericity, and Omega Centauri is measurably flattened (ellipticity ~0.17). physical-evidence.json classifies every entry observed, published-model or authored, including the two authored simplifications: the projected King core radius set equal to the measured 3D core radius, and a 0.75 projected/3D half-light ratio.

No star removal. For every other subject stars are contamination; here they are the emission. The recipe uses the existing preserve treatment, not NOX.

The measurement that blocks the bake

simulation-guided-finite-emission@1 needs a baseline reconstruction, which needs an alignment gate that verifyRegistration only grants on >=45 compact sources matched between two registered rasters with a held-out residual check.

Registering each press image against the exact-WCS DSS2 reference:

image matched held out coverage residual
eso1119b VST 2588 863 99% 15.6 arcsec RMS
eso0844a WFI 1507 503 99% 10.5 arcsec RMS

Two independent press images against one fixed astrometric truth, both landing at the same order — 50-75x the pixel-level bar at either telescope's native sampling (0.21 and 0.24 arcsec/px). These are press-release products: mosaicked, resampled north-up, colour-composited, with rounded caption geometry rather than a fitted WCS. The gate is doing its job.

Limitation, stated because it bounds the number. Both fits matched at a 16 arcsec/frame-pixel frame. At that tolerance in a field this crowded some pairs may be false, so the residuals are an upper bound; the held-out split guards against overfitting but not against systematic mispairing. A tighter frame yielded no matches at all.

The bake is therefore recorded as blocked with its measured reason, not forced. No gate file was written. Closing it needs either science-grade frames with real WCS, or the grid-transfer path in registration-transfer.ts to carry astrometry from a star-verified companion.

Tooling

acquire-images gains an optional output.resizeWidth: libwebp cannot encode the 14540px VST mosaic at the tested qualities (q80 and q92 both fail, 8192px succeeds), and a crowded star field is close to the worst case for VP8. The recorded interpretation now states any reduction and reads the real bit depth from the file instead of asserting 16-bit; the diff shows the LMC source genuinely is 16-bit while this one is 8-bit, so the old text would have been false here. Derived labs/nebula/sources/*.webp are now ignored — running acquisition previously dirtied the tree with ~70 MB of rebuildable blobs, while the sidecars that carry the pins stay tracked.

Radial profile diagnostic

A third round button beside Levels and Difference map, answering what neither can: does brightness fall off with radius the way the source does?

Levels is a histogram and therefore spatially blind — it can match p50/p90/p99 exactly while the structure sits in the wrong place. The difference map shows where the error is but not its radial shape. For a centrally concentrated, near-spherical subject this is the diagnostic that matters.

GET /__nebula/reconstruction-radial?resultId=<lens> returns azimuthally averaged source and render means per radial bin with per-bin ratio and signed delta, plus halfLightRadiusSource / halfLightRadiusRender / their ratio, rmsLogRatio and worstBin. Empty bins report null, never NaN. It reuses lensLevelPairs / loadLensLevelGrid, so it reads the same pinned grid and material as the other two.

Proved against a control the histogram cannot see, per the skill's rule that a diagnostic must flag a known defect and stay quiet on a fixed one. The bad control is a radius-rank reversal: the pixel at radius-rank i renders the source level of rank N-1-i. Because that is a bijection on the pixel domain, the render and source histograms are identical bin for bin — Levels reads p50/p90 ratios of exactly 1.00 and a worst-bin count of 0. The radial profile still catches it outright. These measurements describe the implementation at 171ad1ccd49e; the later zero-bin repair below changes the RMS score, so 1.62 is historical:

control Levels radial profile
rank-reversal (histogram-identical) ratios 1.00, worst bin 0 core ratio ~0.000-0.007, rim ratio up to x46, rmsLogRatio 1.62, half-light radius x1.57
radial compression k=1.4 halfLightRadiusRatio 0.71, monotone with k
correct ratio ~1 everywhere, rmsLogRatio 0, half-light ratio exactly 1

Mutation-checked: inverting the bin index turned two tests red (core ratio should read far below 1, got 46.23; compressed half-light ratio 1.22), and reverting restored 6/6.

browser-difference-map.ts pinned the rail order as exactly ['levels', 'difference'] with sub-pixel adjacency; it now asserts ['levels', 'difference', 'radial']. The assertion was updated rather than removed, since it deliberately pins the layout.

Verified on the repaired implementation: the live radial browser harness passes for the saved Horálek and VISTA lenses, with three SVG curves, correct lens identities and no browser errors. The isolated preview served this branch’s frontend and read-only radial handler against existing saved artifacts; it did not re-bake them or restart the running lab. Screenshots were inspected locally. An asset-independent browser regression also verifies that completely dark bins remain plotted and lens switching replaces the measurement.

The repair in 712e372fb also keeps zero-render/positive-source bins in both the deviation score and ratio chart. Their raw ratio remains zero; logarithms use a documented finite machine-epsilon floor. Reverting this owner and the panel independently makes their regression tests fail.

Lab bundler fix: bundled command-line guards fired inside workers

Found while trying to run the lab, and this is a live defect on main, not local rot — a fresh bundle from current code reproduces it.

A command-line module guards its argument parsing the usual way:

if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) { /* parse argv */ }

Bundling rewrites every module's import.meta.url to the single output file, and the output is then run as node <outfile>. Both sides become the same path, the guard evaluates true, and the bundled module parses argv and throws its usage error before the real entry point runs:

.local/nebula-lab/compiled/compiler-worker.mjs:5297
TypeError: Usage: toolchain <install|verify>

The import chain is legitimate, so cutting it would be wrong:

compiler worker -> workflows/compiler/compile.ts -> features/observations/recipe.ts
  -> adapters/sources/sky-bands.ts -> tools/objects/observation/sky-band-composite.mts
  -> tools/objects/jwst/imaging/image3.mts -> tools/objects/jwst/toolchain.mts

buildLabModule now gives each bundled module its own source URL back, while the entry point keeps the bundle's — so only the true entry still matches process.argv[1]. Fixing it there covers both bundling paths; an earlier narrower attempt that cleared argv[1] in the worker builder is superseded, because it would have stopped genuine command-line entry points from running at all.

Historical processing evidence (before the CI repair commit). Before: node labs/nebula/run.mts compile-nebula labs/nebula/models/m1/compiler.json died on the usage error. After: the M1 Crab compile ran to "state": "complete" in 576.7 s, producing ejecta geometry plus five lenses painted on shared opacity (Hubble optical, Webb infrared, Webb components, Spitzer infrared, VLA radio, Chandra X-ray pulsar wind) in .local/nebula-lab/compiler/27ed1700b83f.../. Both diagnostics and the new radial panel load against it.

The CI repair additionally replaces only parsed import.meta.url expressions (including bracket notation), preserving strings, regexes and template text. The regression executes a bundled imported CLI and its real entry; reverting the fix fails it.

Separately, the compile also needs the NOX model and its pinned Python environment, which live outside git; prepare-processing-environment requires Python 3.9-3.12 and fails on 3.14. That is environment setup, not a code change, and nothing here alters it.

Omega Centauri: measured 3D shape from the literature

With image-derived geometry blocked, the shape comes from published measurements instead, recorded in shape-evidence.md and the extended ledger.

quantity finding
Flattening 1-b/a from ~0.08 in the core to ~0.17-0.19 outside (White & Shawl 1987)
Inclination two disagreeing measurements: 50+/-4 deg (van de Ven et al. 2006) and 43.9+/-1.3 deg (Haeberle et al. 2024, oMEGACat II)
Profile family a Wilson model fits better than King (McLaughlin & van der Marel 2005)
Rotation ~6 km/s at 6-10 arcmin (Reijns et al. 2006); the inner ~0.5 pc counter-rotates (Pechetti et al. 2024)
Distance confirmed 5426+/-47 pc (Baumgardt & Vasiliev 2021), consistent with the ledger
Position angle not sourced — recorded as a gap
Triaxiality no dedicated fit found

Two of these undercut the spherical King profile already on this branch: the flattening varies with radius rather than being one ellipticity, and the preferred profile family is Wilson, not King. Both are recorded rather than quietly corrected, because the fitted Wilson parameters were not retrieved either.

An oblate model is worth building as a bulk approximation and is clearly better than spherical, but it must carry the inclination as a range (40-55 deg) instead of a false single value, and it cannot claim to represent the counter-rotating core. The position angle has to come from a primary source before the model can be oriented on the sky at all.

Evidence

Current verification: 31e3c9210444133510f50649444bc5480afa2b8c (lab repairs 712e372fb plus the shared Atlas fix). Passed: all nine local Contract lint steps; all nebula strict typechecks (197 test files); ownership and dependency boundaries; 25 nebula integration/provenance checks; the CI-selected lab suite (158 passed, two historical-cache skips); lab production build; the real two-lens browser harness and isolated browser regression. Three intentional reversions independently fail the new bundler, radial-statistics and radial-panel tests. Grok’s read-only final review and focused re-review completed with no remaining P0–P3 findings. The shared Atlas merge does not change these lab inputs or implementations. GitHub checks remain the merge gate.

The King derivation reproduces all 201 radius/emissivity values at their recorded six-decimal precision, with matching parameters. Its manually recorded provenance fields remain distinct from numerical stdout. No new acquisition, registration sweep, NOX processing or volume bake was run for this CI repair; the original acquisition and registration records remain historical evidence.

The previously reported documentation failure is resolved by merging current main: the changed-document link check now reports no errors. The last old CI run’s single root failure was an unreachable shared astronomy guide; its five downstream jobs failed before running their tests. Those cascaded statuses were not five independent defects in this PR.

The per-pair match table is omitted from the committed report — 69,749 lines — and is reproducible by re-running verify-against-dss2-fits.mts against the pinned sources.

apresmoi and others added 6 commits September 19, 2026 17:24
…stration residual for Omega Centauri

eso1119b and eso0844a each register against an exact-WCS hips2fits DSS2 cutout with 2500+/1500+ held-out-validated matched stars, but at ~10-16 arcsec RMS -- too coarse for a pixel-level bake gate. Recorded as a measured blocker, not a workaround.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@apresmoi apresmoi changed the title feat(nebula-lab): acquire Omega Centauri and measure its registration against real astrometry feat(nebula-lab): Omega Centauri groundwork, registration measurement and a radial profile diagnostic Sep 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant