From d097f4b477d65f3886d2806558f775b30824cab8 Mon Sep 17 00:00:00 2001 From: Senrokai Date: Tue, 29 Sep 2026 19:53:22 +0200 Subject: [PATCH] Correct a giant's light by its type, not by the cooler dwarf its colour reads as MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3392f06 read every star's bolometric correction off the dwarf sequence at its colour, so a giant got the correction of the cooler dwarf of that colour, which is larger. Antares, M1 Ib at B−V 1.87, took an M5 dwarf's −3.26 and came out 172 023 L☉ and 1 516 R☉, a 7.06 AU sphere, against the 680 R☉ Ohnaka et al. (2013) measure; 119 Tau 2 838 against 587, Menkar 204 against 89. isGiant (spectral.ts) reads luminosity class I to III off a type's primary component, or HYG's g and c prefixes, and a giant keeps its type's correction. Drawn radius against the published one (no planets, so derived), before and after: Antares 2.23 -> 1.01, 119 Tau 4.84 -> 1.43, Menkar 2.29 -> 1.80, Scheat 1.92 -> 1.42, Aldebaran 1.95 -> 1.10, Mirach 1.96 -> 1.27, 41 Com 2.14 -> 1.56; Betelgeuse 0.76 -> 0.89. It is not better everywhere: Gacrux goes from 1.23 to 1.40 and Arcturus from 1.02 to 0.88. 10 808 stars have a giant's type, 10 794 of them a colour. Against the archive's own luminosity for the 130 giant hosts, the median error moves from 0.038 to 0.052 dex and the 90th percentile from 0.246 to 0.204; 17 are off by over 1.5 times, against 19. Controls, each failing its named test (1 of 781): the giant given the dwarf's correction, a giant companion read as the primary, a IV read as a I, and the prefixes ignored. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/app/shared/astro/spectral.spec.ts | 16 +++++++++++++++- src/app/shared/astro/spectral.ts | 12 ++++++++++++ src/app/shared/astro/stellar.spec.ts | 12 ++++++++++++ src/app/shared/astro/stellar.ts | 13 ++++++++++--- 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/app/shared/astro/spectral.spec.ts b/src/app/shared/astro/spectral.spec.ts index 00056a8..daa67ec 100644 --- a/src/app/shared/astro/spectral.spec.ts +++ b/src/app/shared/astro/spectral.spec.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; -import { dwarfSequenceAtColor, parseSpectralClass, SPECTRAL_CLASSES, spectralTypeFromColor, spectralTypeToColorIndex, temperatureToColorIndex } from './spectral'; +import { dwarfSequenceAtColor, isGiant, parseSpectralClass, SPECTRAL_CLASSES, spectralTypeFromColor, spectralTypeToColorIndex, temperatureToColorIndex } from './spectral'; describe('parseSpectralClass', () => { it('reads a clean class and subclass', () => { @@ -46,6 +46,20 @@ 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']) { + expect(isGiant(type), type).toBe(true); + } + }); + + it('leaves dwarfs, subgiants, a dwarf with a giant companion and the unclassified alone', () => { + for (const type of ['G2V', 'B2IV', 'F0IVn', 'M5Ve', 'K1V + M3III', 'g-k', 'Unknown', 'DA', '']) { + expect(isGiant(type), type).toBe(false); + } + }); +}); + describe('spectralTypeToColorIndex', () => { it('places the Sun near its real B-V of 0.65', () => { expect(spectralTypeToColorIndex('G2V')).toBeCloseTo(0.626, 2); diff --git a/src/app/shared/astro/spectral.ts b/src/app/shared/astro/spectral.ts index a079918..3169075 100644 --- a/src/app/shared/astro/spectral.ts +++ b/src/app/shared/astro/spectral.ts @@ -69,6 +69,18 @@ export function parseSpectralClass( return { spectralClass, subclass }; } +/** Luminosity class I (with Ia, Iab, Ib), II or III, not the I of a IV. */ +const GIANT_LUMINOSITY_CLASS = /(? { expect(derived / 5.53e-4).toBeLessThan(1.5); }); + it("gives a giant its type's correction, not the cooler dwarf's its colour reads as", () => { + // Antares, M1 Ib at B−V 1.87 and 170 pc, and Aldebaran, K5 III: 680 R☉ (Ohnaka et al. 2013) + // and 44.2 (Richichi & Roccatagliata 2005). As dwarfs they came out 1 516 and 86. + const antares = { magnitude: 1.06, distancePc: 169.78, spectralType: 'M1Ib + B2.5V', magnitudeBand: 'V', colorIndex: 1.865, colorSystem: 'B-V' } as const; + const aldebaran = { magnitude: 0.87, distancePc: 20.433, spectralType: 'K5III', magnitudeBand: 'V', colorIndex: 1.538, colorSystem: 'B-V' } as const; + for (const [star, published] of [[antares, 680], [aldebaran, 44.2]] as const) { + const radius = radiusFromLuminositySolar(luminositySolar(star)!, effectiveTemperatureK(star)!); + expect(radius / published).toBeGreaterThan(1 / 1.2); + expect(radius / published).toBeLessThan(1.2); + } + }); + it('clamps a pathological record instead of producing an absurd luminosity', () => { const absurd = luminositySolar({ magnitude: -40, distancePc: 5000, spectralType: 'O5V' })!; expect(Number.isFinite(absurd)).toBe(true); diff --git a/src/app/shared/astro/stellar.ts b/src/app/shared/astro/stellar.ts index ac56995..963226a 100644 --- a/src/app/shared/astro/stellar.ts +++ b/src/app/shared/astro/stellar.ts @@ -1,4 +1,4 @@ -import { dwarfSequenceAtColor, parseSpectralClass, SpectralClass, spectralTypeToColorIndex } from './spectral'; +import { dwarfSequenceAtColor, isGiant, parseSpectralClass, SpectralClass, spectralTypeToColorIndex } from './spectral'; /** * Stellar luminosity, derived from the two things the star catalogue actually measures. @@ -128,9 +128,15 @@ export function luminositySolar(star: StellarPhotometry): number | null { // given the Sun's correction, and TRAPPIST-1 came out at a seventh of its luminosity. Against // the archive's own figure for 1 449 hosts, the worst tenth was off by 0.29 dex or more, and is // now off by 0.12. + // + // Not for a star its type says is a giant, though: at its colour the dwarf sequence is a cooler + // 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 absoluteV = absolute - (star.magnitudeBand === 'G' ? (sequence?.gMinusV ?? 0) : 0); - const bolometric = absoluteV + (sequence?.bolometricCorrectionV ?? bolometricCorrection(star.spectralType)); + const bolometric = + absoluteV + (sequence && !isGiant(star.spectralType) ? sequence.bolometricCorrectionV : bolometricCorrection(star.spectralType)); const luminosity = Math.pow(10, (SOLAR_BOLOMETRIC_MAGNITUDE - bolometric) / 2.5); return Math.min(Math.max(luminosity, MIN_LUMINOSITY_SOLAR), MAX_LUMINOSITY_SOLAR); } @@ -141,7 +147,8 @@ export const SOLAR_EFFECTIVE_TEMPERATURE_K = 5772; /** * Effective temperature, off the dwarf sequence at the star's colour, or at the colour its * spectral type implies where it has none. Exactly the Sun's for the Sun, which is at zero - * distance here. A giant is read as the dwarf of its colour: a few hundred kelvin too cool at K. + * distance here. A giant is read as the dwarf of its colour: a few hundred kelvin too cool at K, + * and Antares, an M1 supergiant, 3 019 K against the 3 660 Ohnaka et al. (2013) measure. */ export function effectiveTemperatureK(star: StellarPhotometry): number | null { if (star.distancePc === 0) {