From 31e0c04fe1e3686b7f614be00f70bd34720a6ab3 Mon Sep 17 00:00:00 2001 From: Senrokai Date: Tue, 29 Sep 2026 20:01:07 +0200 Subject: [PATCH] Read a colour past either end of the dwarf table at that end, where the star has no type instead MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dwarfSequenceAtColor answers null outside Pecaut & Mamajek's table, B−V −0.301 to 2.16 and BP−RP −0.12 to 5.1, and effectiveTemperatureK then fell back on the type, which Gaia's stars do not have and carbon stars' parser does not read. 219 stars with a measured colour got no temperature and so no radius, and were drawn at the Sun's radius in the Sun's colour: 110 white dwarfs within 50 pc, 38 Gaia stars redder than BP−RP 5.1 (Gaia DR3 6439125097427143808, an ultracool dwarf 4.0 pc away), HD 46687, La Superba and the other carbon stars, and an O8 star. Past the table, the colour is now read at the row it is past (clampToTable), but only for a star with no readable type: beside a type an off-table colour is more often the bad measurement — HD 49748 is G5 V at B−V −0.32 — so the type still wins there, as it did. The luminosity reads the same point, so a star past the red end also gets the M8.5 row's G−V and correction. A type's own colour past the table, which only O types have, is read at B0. Carbon and S stars (C, N, R, S) now count as giants, so their correction stays their type's, not an M8.5 dwarf's −5.78. Measured on the shipped catalogue: stars without a temperature 3 053 -> 2 834, without a radius 3 077 -> 2 858; all 292 stars with an off-table colour now have a temperature, against 73. Gaia DR3 6439125097427143808 is 2 420 K and 0.110 R☉ (M8.5 V: 0.104); the 110 white dwarfs a median 0.018 R☉ at 10 700 K (0.0013 to 0.034); HD 46687 212 R☉ at 2 420 K and La Superba 133; audit #16's Gaia DR3 5612323414549657984, k1 Pup, B6 V at BP−RP −0.15, 203 L☉ and 4.1 R☉ against 143 and none (FLAME gives 336 and 3.49). The 2 851 stars left without a radius have neither a colour nor a readable type, or no band. Controls, each failing its named test: no clamp for an untyped star (2 of 784 failed), the clamp winning over a type, an O type's colour unclamped, the luminosity off the unclamped sequence, and carbon stars not counted as giants (1 of 784 each); clampToTable ignored (3 of 784). Co-Authored-By: Claude Opus 5.5 (1M context) --- src/app/shared/astro/spectral.spec.ts | 10 ++++++++-- src/app/shared/astro/spectral.ts | 20 ++++++++++++-------- src/app/shared/astro/stellar.spec.ts | 19 +++++++++++++++++++ src/app/shared/astro/stellar.ts | 24 ++++++++++++++++++++---- 4 files changed, 59 insertions(+), 14 deletions(-) diff --git a/src/app/shared/astro/spectral.spec.ts b/src/app/shared/astro/spectral.spec.ts index daa67ec..6e63e9b 100644 --- a/src/app/shared/astro/spectral.spec.ts +++ b/src/app/shared/astro/spectral.spec.ts @@ -47,8 +47,8 @@ describe('parseSpectralClass', () => { }); describe('isGiant', () => { - it('reads luminosity classes I to III off the primary, and the giant and supergiant prefixes', () => { - for (const type of ['M1Ib + B2.5V', 'K5III', 'M2II-IIIvar', 'C7Iab', 'K0IIIb', 'gK0', 'cM2']) { + it('reads luminosity classes I to III off the primary, the giant and supergiant prefixes, and carbon and S stars', () => { + for (const type of ['M1Ib + B2.5V', 'K5III', 'M2II-IIIvar', 'C7Iab', 'K0IIIb', 'gK0', 'cM2', 'N5', 'Ce+', 'S57:']) { expect(isGiant(type), type).toBe(true); } }); @@ -166,4 +166,10 @@ describe('dwarfSequenceAtColor', () => { expect(dwarfSequenceAtColor(null)).toBeNull(); expect(dwarfSequenceAtColor(-0.29, 'B-V')!.gMinusV).toBeNull(); }); + + it('reads the row at the end a colour is past, when asked to', () => { + expect(dwarfSequenceAtColor(2.2, 'B-V', true)).toEqual({ temperatureK: 2420, bolometricCorrectionV: -5.78, gMinusV: -3.09 }); + expect(dwarfSequenceAtColor(-0.15, 'BP-RP', true)).toEqual({ temperatureK: 10700, bolometricCorrectionV: -0.42, gMinusV: 0.018 }); + expect(dwarfSequenceAtColor(null, 'B-V', true)).toBeNull(); + }); }); diff --git a/src/app/shared/astro/spectral.ts b/src/app/shared/astro/spectral.ts index 3169075..e1f7728 100644 --- a/src/app/shared/astro/spectral.ts +++ b/src/app/shared/astro/spectral.ts @@ -73,12 +73,14 @@ export function parseSpectralClass( const GIANT_LUMINOSITY_CLASS = /(? row[column] !== null); - if (colorIndex === null || !(colorIndex >= rows[0][column]! && colorIndex <= rows[rows.length - 1][column]!)) { + const [bluest, reddest] = [rows[0][column]!, rows[rows.length - 1][column]!]; + if (colorIndex === null || !Number.isFinite(colorIndex) || (!clampToTable && !(colorIndex >= bluest && colorIndex <= reddest))) { return null; } - const above = Math.max(1, rows.findIndex((row) => row[column]! >= colorIndex)); + const colour = Math.min(Math.max(colorIndex, bluest), reddest); + const above = Math.max(1, rows.findIndex((row) => row[column]! >= colour)); const [blue, red] = [rows[above - 1], rows[above]]; - const t = (colorIndex - blue[column]!) / (red[column]! - blue[column]!); + const t = (colour - blue[column]!) / (red[column]! - blue[column]!); const lerp = (from: number, to: number): number => from + (to - from) * t; return { temperatureK: lerp(blue[3], red[3]), diff --git a/src/app/shared/astro/stellar.spec.ts b/src/app/shared/astro/stellar.spec.ts index b1dec4c..8a30ca5 100644 --- a/src/app/shared/astro/stellar.spec.ts +++ b/src/app/shared/astro/stellar.spec.ts @@ -158,6 +158,17 @@ describe('effectiveTemperatureK', () => { expect(effectiveTemperatureK({ magnitude: 11, distancePc: 5, spectralType: 'M5Ve', colorIndex: null })).toBeCloseTo(3106, 0); expect(effectiveTemperatureK({ magnitude: 11, distancePc: 5, spectralType: 'Unknown', colorIndex: null })).toBeNull(); }); + + it('reads a colour past the table at its end where there is no type, and the type where there is', () => { + // An ultracool dwarf redder than M8.5, a white dwarf bluer than B9, and an O star B−V puts at B0. + expect(effectiveTemperatureK({ magnitude: 14.005, distancePc: 4.005, spectralType: 'Unknown', magnitudeBand: 'G', colorIndex: 5.113, colorSystem: 'BP-RP' })).toBe(2420); + expect(effectiveTemperatureK({ magnitude: 14, distancePc: 25, spectralType: 'Unknown', magnitudeBand: 'G', colorIndex: -0.25, colorSystem: 'BP-RP' })).toBe(10700); + expect(effectiveTemperatureK({ magnitude: 7, distancePc: 121, spectralType: 'O8', colorIndex: -0.31, colorSystem: 'B-V' })).toBe(31400); + // HD 49748, G5 V at B−V −0.32: the colour is the one that is wrong. + const g5 = effectiveTemperatureK({ magnitude: 9, distancePc: 184, spectralType: 'G5V', colorIndex: null })!; + expect(effectiveTemperatureK({ magnitude: 9, distancePc: 184, spectralType: 'G5V', colorIndex: -0.319, colorSystem: 'B-V' })).toBe(g5); + expect(g5).toBeGreaterThan(5500); + }); }); describe('radiusFromLuminositySolar', () => { @@ -165,6 +176,14 @@ describe('radiusFromLuminositySolar', () => { expect(radiusFromLuminositySolar(1, SOLAR_EFFECTIVE_TEMPERATURE_K)).toBeCloseTo(1, 12); }); + it("gives an ultracool dwarf redder than the table an M8.5 dwarf's radius, not none", () => { + // Gaia DR3 6439125097427143808, 4.0 pc away at BP−RP 5.11; M8.5 V is 0.104 R☉ (Mamajek). + const star = { magnitude: 14.005, distancePc: 4.005, spectralType: 'Unknown', magnitudeBand: 'G', colorIndex: 5.113, colorSystem: 'BP-RP' } as const; + const radius = radiusFromLuminositySolar(luminositySolar(star)!, effectiveTemperatureK(star)!); + expect(radius / 0.104).toBeGreaterThan(1 / 1.2); + expect(radius / 0.104).toBeLessThan(1.2); + }); + it('gives Sirius and TRAPPIST-1 their published radii from colour and brightness alone', () => { // 1.711 R☉ (Liebert et al. 2005) and 0.119 R☉ (Agol et al. 2021), each to within a fifth. for (const [star, published] of [ diff --git a/src/app/shared/astro/stellar.ts b/src/app/shared/astro/stellar.ts index 963226a..37742c1 100644 --- a/src/app/shared/astro/stellar.ts +++ b/src/app/shared/astro/stellar.ts @@ -1,4 +1,4 @@ -import { dwarfSequenceAtColor, isGiant, parseSpectralClass, SpectralClass, spectralTypeToColorIndex } from './spectral'; +import { DwarfSequencePoint, dwarfSequenceAtColor, isGiant, parseSpectralClass, SpectralClass, spectralTypeToColorIndex } from './spectral'; /** * Stellar luminosity, derived from the two things the star catalogue actually measures. @@ -133,7 +133,7 @@ export function luminositySolar(star: StellarPhotometry): number | null { // dwarf, whose correction is larger. Antares, M1 Ib at B−V 1.87, read as an M5 dwarf's −3.26 // came out 1 516 R☉ against the 680 Ohnaka et al. (2013) measure, and 119 Tau 2 838 against 587; // its type's −1.55 gives 690. - const sequence = star.colorIndex != null ? dwarfSequenceAtColor(star.colorIndex, star.colorSystem) : null; + const sequence = sequenceAtColour(star); const absoluteV = absolute - (star.magnitudeBand === 'G' ? (sequence?.gMinusV ?? 0) : 0); const bolometric = absoluteV + (sequence && !isGiant(star.spectralType) ? sequence.bolometricCorrectionV : bolometricCorrection(star.spectralType)); @@ -141,6 +141,22 @@ export function luminositySolar(star: StellarPhotometry): number | null { return Math.min(Math.max(luminosity, MIN_LUMINOSITY_SOLAR), MAX_LUMINOSITY_SOLAR); } +/** + * The dwarf sequence at a star's colour — at the end of the table a colour is past, where the star + * has no type to go by instead. Past the red end are the ultracool dwarfs Gaia measures redder than + * BP−RP 5.1, M8.5; past the blue end, its white dwarfs and hot stars bluer than −0.12, B9, and + * B−V's O stars. Unclamped, they had no temperature and were drawn at the Sun's: Gaia DR3 + * 6439125097427143808, an ultracool dwarf 4.0 pc away, and 110 white dwarfs within 50 pc, all at + * 1 R☉. Beside a type, an off-table colour is more often a bad one than an extreme star — HD + * 49748, G5 V, at B−V −0.32 — and the type is read instead. + */ +function sequenceAtColour(star: StellarPhotometry): DwarfSequencePoint | null { + if (star.colorIndex == null) { + return null; + } + return dwarfSequenceAtColor(star.colorIndex, star.colorSystem) ?? (parseSpectralClass(star.spectralType) ? null : dwarfSequenceAtColor(star.colorIndex, star.colorSystem, true)); +} + /** The Sun's effective temperature, the IAU 2015 nominal value. */ export const SOLAR_EFFECTIVE_TEMPERATURE_K = 5772; @@ -154,8 +170,8 @@ export function effectiveTemperatureK(star: StellarPhotometry): number | null { if (star.distancePc === 0) { return SOLAR_EFFECTIVE_TEMPERATURE_K; } - const measured = star.colorIndex != null ? dwarfSequenceAtColor(star.colorIndex, star.colorSystem) : null; - return (measured ?? dwarfSequenceAtColor(spectralTypeToColorIndex(star.spectralType)))?.temperatureK ?? null; + // A type's colour past the table is an O star's, which B−V no longer tells apart from B0. + return (sequenceAtColour(star) ?? dwarfSequenceAtColor(spectralTypeToColorIndex(star.spectralType), 'B-V', true))?.temperatureK ?? null; } /**