Read a giant's temperature off its type as well as its correction, so a radius has one source
d097f4b gave a giant its type's bolometric correction but left its temperature at the dwarf its
colour reads as, and the radius drawn from the two paired a correction for one star with the
temperature of another. The M giants stayed too cool (610 of them at a median 3 275 K, Antares
3 019 against Ohnaka et al.'s 3 660), and a hot giant behind dust got a 30 000 K star's correction
at the temperature of an A star: Menkib, O7.5 Iab at B−V 0.02, was drawn at 9 517 K and 95 R☉,
Alp Cam, O9.5 Ia, at 338, where 14 and 21 are published. Rigel went from 81 to 102 R☉, Alnilam
from 57 to 108.
giantSurface now reads both off the type: G to M giants off van Belle et al.'s (2021, table 8)
interferometric scale, fitted to 191 giants from G1 to M7.75 III, with the correction the dwarf
sequence has at that temperature; O to F giants off the dwarf of their type, for which the table
gains Mamajek's O3 to O9.5 rows (without colours, which do not tell O types apart); carbon and S
stars, which no row reads and which got the Sun's −0.06 at 2 420 K, off the medians of Bergeat et
al. (2001): 2 990 K over the 441 stars of their table 10 and −2.83 over the 383 with a V magnitude,
counted again from VizieR here.
On the shipped catalogue (drawn radius in R☉, before → after, published): Antares 690 → 410 at
3 730 K (680; its luminosity from V is 0.4 dex under Ohnaka's), Aldebaran 48.5 → 44.0 (44.2),
Arcturus 22.5 → 24.1 (25.4), Menkar 160 → 103, Gacrux 118 → 73, Rigel 102 → 67 (74.1), Alnilam
108 → 33, Menkib 95 → 6.9 (14, the dust still dims it), Alp Cam 338 → 31, La Superba 133 → 311
(315) and 544 → 6 977 L☉ (8 090 from Bergeat's bolometric magnitude), 19 Psc 130 → 305 (295).
The 610 M giants now sit at a median 3 644 K (p10 3 386, p90 3 816). Against their own radii
before, the O giants' fall to a median 0.08, the B giants' to 0.62, the M giants' to 0.68, and the
K giants' rise by 8 %. It is not better everywhere: Pollux goes from 8.6 to 10.1 against 8.8,
119 Tau from 700 to 326 against 587, and Mintaka and Alnitak, placed by Hipparcos at 212 and 226
pc where they are about 380, come out 8.8 and 11.8 against 13 to 20.
Tests: the giant case in stellar.spec now checks Antares's temperature against Ohnaka's, and
Aldebaran (to a tenth) and Rigel (to a fifth) against their interferometric radii; new cases give
Menkib its type's 36 100 K and a radius within 2.5 times the published one, and La Superba a
luminosity within a fifth of Bergeat's and 2 990 K; spectral.spec covers dwarfSequenceAtType. The
scene's supergiant case now expects Antares at 350-480 R☉ where it pinned 600-760. Controls:
the luminosity or the temperature ignoring giantSurface, G-M giants read as the dwarf of their
type, their correction taken off the type instead of their temperature, O giants through the
textbook colour clamped at B0, the type index off by one subclass and carbon stars unhandled each
fail the named test.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1014,14 +1014,15 @@ describe('GalaxySystemSceneComponent camera-flight transitions', () => {
|
|||||||
expect(scene.hudReadouts().find((readout) => readout.label === 'Radius')?.value).toBe('0.141 solar radii');
|
expect(scene.hudReadouts().find((readout) => readout.label === 'Radius')?.value).toBe('0.141 solar radii');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('keeps the camera three radii out from a supergiant drawn at the radius its colour and brightness give', async () => {
|
it('keeps the camera three radii out from a supergiant drawn at the radius its type and brightness give', async () => {
|
||||||
const scene = await enter(ANTARES);
|
const scene = await enter(ANTARES);
|
||||||
const radius = scene.starMarkerGeometry.parameters.radius;
|
const radius = scene.starMarkerGeometry.parameters.radius;
|
||||||
// 690 R☉ against the 680 Ohnaka et al. (2013) measure: 3.2 AU.
|
// 410 R☉, 1.9 AU, at M1's 3 730 K: short of the 680 Ohnaka et al. (2013) measure, whose
|
||||||
expect(radius / SUN_RADIUS_AU).toBeGreaterThan(600);
|
// luminosity is 0.4 dex above what V gives at the catalogue's distance.
|
||||||
expect(radius / SUN_RADIUS_AU).toBeLessThan(760);
|
expect(radius / SUN_RADIUS_AU).toBeGreaterThan(350);
|
||||||
|
expect(radius / SUN_RADIUS_AU).toBeLessThan(480);
|
||||||
expect(scene.controls.minDistance).toBeCloseTo(closestApproachAu(radius), 9);
|
expect(scene.controls.minDistance).toBeCloseTo(closestApproachAu(radius), 9);
|
||||||
expect(scene.controls.minDistance).toBeGreaterThan(9);
|
expect(scene.controls.minDistance).toBeGreaterThan(5);
|
||||||
// Settled where its disc stays inside the ring of its neighbours' names, not pressed up to it.
|
// Settled where its disc stays inside the ring of its neighbours' names, not pressed up to it.
|
||||||
expect(engine.getCamera().position.length()).toBeGreaterThan(1.2 * scene.controls.minDistance);
|
expect(engine.getCamera().position.length()).toBeGreaterThan(1.2 * scene.controls.minDistance);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
import { dwarfSequenceAtColor, dwarfSequenceAtTemperature, isGiant, parseSpectralClass, spectralClassification, SPECTRAL_CLASSES, spectralTypeFromColor, spectralTypeToColorIndex, temperatureToColorIndex } from './spectral';
|
import { dwarfSequenceAtColor, dwarfSequenceAtTemperature, dwarfSequenceAtType, isGiant, parseSpectralClass, spectralClassification, SPECTRAL_CLASSES, spectralTypeFromColor, spectralTypeToColorIndex, temperatureToColorIndex } from './spectral';
|
||||||
|
|
||||||
describe('parseSpectralClass', () => {
|
describe('parseSpectralClass', () => {
|
||||||
it('reads a clean class and subclass', () => {
|
it('reads a clean class and subclass', () => {
|
||||||
@@ -195,6 +195,16 @@ describe('dwarfSequenceAtColor', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('dwarfSequenceAtType', () => {
|
||||||
|
it("reads an O type off Mamajek's O rows, which carry no colour, and any other off its own row", () => {
|
||||||
|
expect(dwarfSequenceAtType('O7.5Iab:')).toEqual({ bMinusV: null, temperatureK: 36100, bolometricCorrectionV: -3.33, gMinusV: null });
|
||||||
|
expect(dwarfSequenceAtType('B8Ia')!.temperatureK).toBe(12300);
|
||||||
|
expect(dwarfSequenceAtType('O9.7')!.temperatureK).toBeCloseTo(31900 - 500 * (0.2 / 0.5), 6);
|
||||||
|
expect(dwarfSequenceAtType('M9')!.temperatureK).toBe(2420);
|
||||||
|
expect(dwarfSequenceAtType('Unknown')).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('dwarfSequenceAtTemperature', () => {
|
describe('dwarfSequenceAtTemperature', () => {
|
||||||
it("is a type's own row at its temperature, between two rows between them, and the end row past either end", () => {
|
it("is a type's own row at its temperature, between two rows between them, and the end row past either end", () => {
|
||||||
expect(dwarfSequenceAtTemperature(5770)).toEqual({ bMinusV: 0.65, temperatureK: 5770, bolometricCorrectionV: -0.085, gMinusV: -0.165 });
|
expect(dwarfSequenceAtTemperature(5770)).toEqual({ bMinusV: 0.65, temperatureK: 5770, bolometricCorrectionV: -0.085, gMinusV: -0.165 });
|
||||||
|
|||||||
@@ -114,20 +114,25 @@ export function spectralTypeToColorIndex(spectralType: string | null | undefined
|
|||||||
* J145829+101343, a 580 K brown dwarf, read as B−V 2.00 and "~M6".
|
* J145829+101343, a 580 K brown dwarf, read as B−V 2.00 and "~M6".
|
||||||
*/
|
*/
|
||||||
export function temperatureToColorIndex(temperatureK: number): number | null {
|
export function temperatureToColorIndex(temperatureK: number): number | null {
|
||||||
const [hottest, coolest] = [DWARF_SEQUENCE[0], DWARF_SEQUENCE[DWARF_SEQUENCE.length - 1]];
|
const [hottest, coolest] = [ROWS_WITH_COLOUR[1][0], DWARF_SEQUENCE[DWARF_SEQUENCE.length - 1]];
|
||||||
return temperatureK <= hottest[3] && temperatureK >= coolest[3] ? dwarfSequenceAtTemperature(temperatureK).bMinusV : null;
|
return temperatureK <= hottest[3] && temperatureK >= coolest[3] ? dwarfSequenceAtTemperature(temperatureK).bMinusV : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The mean dwarf sequence: B−V, Gaia BP−RP, effective temperature (K), bolometric correction to V
|
* The mean dwarf sequence: B−V, Gaia BP−RP, effective temperature (K), bolometric correction to V
|
||||||
* and Gaia G−V by spectral type, from Pecaut & Mamajek (2013, ApJS 208, 9, table 5) as Mamajek
|
* and Gaia G−V by spectral type, from Pecaut & Mamajek (2013, ApJS 208, 9, table 5) as Mamajek
|
||||||
* maintains it online (version 2022.04.16), where the Gaia columns were added. From B0, where B−V
|
* maintains it online (version 2022.04.16), where the Gaia columns were added. From O3 to M8.5,
|
||||||
* stops telling types apart — the whole O sequence spans 0.03 of it — to M8.5, past which BP−RP
|
* past which BP−RP turns back. The O rows, read by type only, carry no colour: B−V stops telling
|
||||||
* turns back; BP−RP starts at B9, the bluest it is tabulated for, and G−V at B1.5.
|
* types apart there, the whole O sequence spanning 0.03 of it. BP−RP starts at B9, the bluest it
|
||||||
|
* is tabulated for, and G−V at B1.5.
|
||||||
*/
|
*/
|
||||||
type SequenceRow = readonly [string, number, number | null, number, number, number | null];
|
type SequenceRow = readonly [string, number | null, number | null, number, number, number | null];
|
||||||
|
|
||||||
const DWARF_SEQUENCE: readonly SequenceRow[] = [
|
const DWARF_SEQUENCE: readonly SequenceRow[] = [
|
||||||
|
['O3', null, null, 44900, -4.01, null], ['O4', null, null, 42900, -3.89, null], ['O5', null, null, 41400, -3.76, null],
|
||||||
|
['O5.5', null, null, 40500, -3.67, null], ['O6', null, null, 39500, -3.57, null], ['O6.5', null, null, 38300, -3.49, null],
|
||||||
|
['O7', null, null, 37100, -3.41, null], ['O7.5', null, null, 36100, -3.33, null], ['O8', null, null, 35100, -3.24, null],
|
||||||
|
['O8.5', null, null, 34300, -3.18, null], ['O9', null, null, 33300, -3.11, null], ['O9.5', null, null, 31900, -3.01, null],
|
||||||
['B0', -0.301, null, 31400, -2.99, null], ['B0.5', -0.289, null, 29000, -2.83, null], ['B1', -0.278, null, 26000, -2.58, null],
|
['B0', -0.301, null, 31400, -2.99, null], ['B0.5', -0.289, null, 29000, -2.83, null], ['B1', -0.278, null, 26000, -2.58, null],
|
||||||
['B1.5', -0.252, null, 24500, -2.44, -0.021], ['B2', -0.215, null, 20600, -2.03, -0.008], ['B2.5', -0.198, null, 18500, -1.77, -0.003],
|
['B1.5', -0.252, null, 24500, -2.44, -0.021], ['B2', -0.215, null, 20600, -2.03, -0.008], ['B2.5', -0.198, null, 18500, -1.77, -0.003],
|
||||||
['B3', -0.178, null, 17000, -1.54, 0.001], ['B4', -0.165, null, 16400, -1.49, 0.004], ['B5', -0.156, null, 15700, -1.34, 0.007],
|
['B3', -0.178, null, 17000, -1.54, 0.001], ['B4', -0.165, null, 16400, -1.49, 0.004], ['B5', -0.156, null, 15700, -1.34, 0.007],
|
||||||
@@ -199,8 +204,8 @@ export function spectralClassification(star: { spectralType: string; colorIndex:
|
|||||||
|
|
||||||
/** What the dwarf sequence says of a star of a given colour. */
|
/** What the dwarf sequence says of a star of a given colour. */
|
||||||
export interface DwarfSequencePoint {
|
export interface DwarfSequencePoint {
|
||||||
/** B−V, the colour in the other system's terms where it was read off BP−RP. */
|
/** B−V, the colour in the other system's terms where it was read off BP−RP; `null` among the O rows. */
|
||||||
bMinusV: number;
|
bMinusV: number | null;
|
||||||
temperatureK: number;
|
temperatureK: number;
|
||||||
/** Bolometric correction to V: what V leaves out of the star's total output, in magnitudes. */
|
/** Bolometric correction to V: what V leaves out of the star's total output, in magnitudes. */
|
||||||
bolometricCorrectionV: number;
|
bolometricCorrectionV: number;
|
||||||
@@ -243,6 +248,22 @@ const WHITE_DWARF_BP_RP: readonly (readonly [number, number])[] = [
|
|||||||
[-0.4, 28585], [-0.35, 24521], [-0.3, 22090], [-0.25, 19012], [-0.2, 17079], [-0.15, 15369], [-0.12, 10700]
|
[-0.4, 28585], [-0.35, 24521], [-0.3, 22090], [-0.25, 19012], [-0.2, 17079], [-0.15, 15369], [-0.12, 10700]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The dwarf sequence at a spectral type, between the two rows it falls between, O3 to M8.5; the end
|
||||||
|
* row past either end. `null` for a type with no class the parser reads.
|
||||||
|
*/
|
||||||
|
export function dwarfSequenceAtType(spectralType: string | null | undefined): DwarfSequencePoint | null {
|
||||||
|
const parsed = parseSpectralClass(spectralType);
|
||||||
|
return parsed && sequenceWhere(DWARF_SEQUENCE, (row) => TYPE_INDEX.get(row)!, typeIndex(parsed));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A type as a number rising down the table: ten to a class, O0 at 0. */
|
||||||
|
function typeIndex({ spectralClass, subclass }: { spectralClass: SpectralClass; subclass: number }): number {
|
||||||
|
return SPECTRAL_CLASSES.indexOf(spectralClass) * 10 + subclass;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TYPE_INDEX = new Map(DWARF_SEQUENCE.map((row) => [row, typeIndex(parseSpectralClass(row[0])!)]));
|
||||||
|
|
||||||
/** The dwarf sequence at an effective temperature, between the two types it falls between; the end row past either end. */
|
/** The dwarf sequence at an effective temperature, between the two types it falls between; the end row past either end. */
|
||||||
export function dwarfSequenceAtTemperature(temperatureK: number): DwarfSequencePoint {
|
export function dwarfSequenceAtTemperature(temperatureK: number): DwarfSequencePoint {
|
||||||
return sequenceWhere(ROWS_WITH_COLOUR[1], (row) => -row[3], -temperatureK);
|
return sequenceWhere(ROWS_WITH_COLOUR[1], (row) => -row[3], -temperatureK);
|
||||||
@@ -255,7 +276,7 @@ function sequenceWhere(rows: readonly SequenceRow[], key: (row: SequenceRow) =>
|
|||||||
const t = Math.min(Math.max((value - key(first)) / (key(second) - key(first)), 0), 1);
|
const t = Math.min(Math.max((value - key(first)) / (key(second) - key(first)), 0), 1);
|
||||||
const lerp = (from: number, to: number): number => from + (to - from) * t;
|
const lerp = (from: number, to: number): number => from + (to - from) * t;
|
||||||
return {
|
return {
|
||||||
bMinusV: lerp(first[1], second[1]),
|
bMinusV: first[1] === null || second[1] === null ? null : lerp(first[1], second[1]),
|
||||||
temperatureK: lerp(first[3], second[3]),
|
temperatureK: lerp(first[3], second[3]),
|
||||||
bolometricCorrectionV: lerp(first[4], second[4]),
|
bolometricCorrectionV: lerp(first[4], second[4]),
|
||||||
gMinusV: first[5] === null || second[5] === null ? null : lerp(first[5], second[5])
|
gMinusV: first[5] === null || second[5] === null ? null : lerp(first[5], second[5])
|
||||||
|
|||||||
@@ -122,18 +122,41 @@ describe('luminositySolar', () => {
|
|||||||
expect(derived / 5.53e-4).toBeLessThan(1.5);
|
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", () => {
|
it("reads a giant's temperature and correction both off its type, 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)
|
// Antares, M1 Ib at B−V 1.87: 3 660 K (Ohnaka et al. 2013), where its colour's dwarf is 3 019.
|
||||||
// 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 antares = { magnitude: 1.06, distancePc: 169.78, spectralType: 'M1Ib + B2.5V', magnitudeBand: 'V', colorIndex: 1.865, colorSystem: 'B-V' } as const;
|
||||||
|
expect(Math.abs(effectiveTemperatureK(antares)! - 3660)).toBeLessThan(100);
|
||||||
|
// Aldebaran, K5 III, 44.2 R☉ (Richichi & Roccatagliata 2005), to a tenth; Rigel, B8 Ia, 74.1
|
||||||
|
// (Baines et al. 2018), to a fifth. With a correction off the type beside the colour's
|
||||||
|
// temperature, Rigel came out 101.6; with K5's own correction at 3 902 K, Aldebaran 52.
|
||||||
const aldebaran = { magnitude: 0.87, distancePc: 20.433, spectralType: 'K5III', magnitudeBand: 'V', colorIndex: 1.538, 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 rigel = { magnitude: 0.18, distancePc: 264.55, spectralType: 'B8Ia', magnitudeBand: 'V', colorIndex: -0.03, colorSystem: 'B-V' } as const;
|
||||||
|
for (const [star, published, tolerance] of [[aldebaran, 44.2, 1.1], [rigel, 74.1, 1.2]] as const) {
|
||||||
const radius = radiusFromLuminositySolar(luminositySolar(star)!, effectiveTemperatureK(star)!);
|
const radius = radiusFromLuminositySolar(luminositySolar(star)!, effectiveTemperatureK(star)!);
|
||||||
expect(radius / published).toBeGreaterThan(1 / 1.2);
|
expect(radius / published).toBeGreaterThan(1 / tolerance);
|
||||||
expect(radius / published).toBeLessThan(1.2);
|
expect(radius / published).toBeLessThan(tolerance);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('reads a hot giant reddened by dust at its type, not at the cool star its colour reads as', () => {
|
||||||
|
// Menkib, O7.5 Iab at B−V 0.02: 14 R☉ (Krtička & Kubát 2010). At its colour's 9 517 K and its
|
||||||
|
// type's correction it was drawn at 95; the dust it is behind still leaves it dimmer than it is.
|
||||||
|
const menkib = { magnitude: 3.98, distancePc: 408.881, spectralType: 'O7.5Iab:', magnitudeBand: 'V', colorIndex: 0.016, colorSystem: 'B-V' } as const;
|
||||||
|
expect(effectiveTemperatureK(menkib)).toBeCloseTo(36100, 6);
|
||||||
|
const radius = radiusFromLuminositySolar(luminositySolar(menkib)!, effectiveTemperatureK(menkib)!);
|
||||||
|
expect(radius / 14).toBeGreaterThan(1 / 2.5);
|
||||||
|
expect(radius / 14).toBeLessThan(2.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("gives a carbon star the carbon stars' correction and temperature, not the Sun's correction at an M dwarf's", () => {
|
||||||
|
// La Superba, C7 Iab: Bergeat et al. (2001) have it at bolometric magnitude 2.43, which at the
|
||||||
|
// catalogue's 310 pc is 8 090 L☉. The Sun's −0.06 at 2 420 K gave 544 L☉ and 133 R☉.
|
||||||
|
const laSuperba = { magnitude: 5.42, distancePc: 310.342, spectralType: 'C7Iab', magnitudeBand: 'V', colorIndex: 2.994, colorSystem: 'B-V' } as const;
|
||||||
|
expect(luminositySolar(laSuperba)! / 8090).toBeGreaterThan(1 / 1.2);
|
||||||
|
expect(luminositySolar(laSuperba)! / 8090).toBeLessThan(1.2);
|
||||||
|
expect(effectiveTemperatureK(laSuperba)).toBe(2990);
|
||||||
|
});
|
||||||
|
|
||||||
it('clamps a pathological record instead of producing an absurd luminosity', () => {
|
it('clamps a pathological record instead of producing an absurd luminosity', () => {
|
||||||
const absurd = luminositySolar({ magnitude: -40, distancePc: 5000, spectralType: 'O5V' })!;
|
const absurd = luminositySolar({ magnitude: -40, distancePc: 5000, spectralType: 'O5V' })!;
|
||||||
expect(Number.isFinite(absurd)).toBe(true);
|
expect(Number.isFinite(absurd)).toBe(true);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DwarfSequencePoint, dwarfSequenceAtColor, isGiant, parseSpectralClass, SpectralClass, spectralTypeToColorIndex } from './spectral';
|
import { DwarfSequencePoint, dwarfSequenceAtColor, dwarfSequenceAtTemperature, dwarfSequenceAtType, isGiant, parseSpectralClass, SpectralClass, spectralTypeToColorIndex } from './spectral';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stellar luminosity, derived from the two things the star catalogue actually measures.
|
* Stellar luminosity, derived from the two things the star catalogue actually measures.
|
||||||
@@ -129,14 +129,12 @@ export function luminositySolar(star: StellarPhotometry): number | null {
|
|||||||
// the archive's own figure for 1 449 hosts, the worst tenth was off by 0.29 dex or more, and is
|
// 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.
|
// 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
|
// Not for a star its type says is a giant, though, whose correction is read off its type along
|
||||||
// dwarf, whose correction is larger. Antares, M1 Ib at B−V 1.87, read as an M5 dwarf's −3.26
|
// with its temperature: see giantSurface.
|
||||||
// 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 = sequenceAtColour(star);
|
const sequence = sequenceAtColour(star);
|
||||||
const absoluteV = absolute - (star.magnitudeBand === 'G' ? (sequence?.gMinusV ?? 0) : 0);
|
const absoluteV = absolute - (star.magnitudeBand === 'G' ? (sequence?.gMinusV ?? 0) : 0);
|
||||||
const bolometric =
|
const correction = giantSurface(star.spectralType)?.bolometricCorrectionV ?? sequence?.bolometricCorrectionV ?? bolometricCorrection(star.spectralType);
|
||||||
absoluteV + (sequence && !isGiant(star.spectralType) ? sequence.bolometricCorrectionV : bolometricCorrection(star.spectralType));
|
const bolometric = absoluteV + correction;
|
||||||
const luminosity = Math.pow(10, (SOLAR_BOLOMETRIC_MAGNITUDE - bolometric) / 2.5);
|
const luminosity = Math.pow(10, (SOLAR_BOLOMETRIC_MAGNITUDE - bolometric) / 2.5);
|
||||||
return Math.min(Math.max(luminosity, MIN_LUMINOSITY_SOLAR), MAX_LUMINOSITY_SOLAR);
|
return Math.min(Math.max(luminosity, MIN_LUMINOSITY_SOLAR), MAX_LUMINOSITY_SOLAR);
|
||||||
}
|
}
|
||||||
@@ -163,18 +161,72 @@ export const SOLAR_EFFECTIVE_TEMPERATURE_K = 5772;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Effective temperature, off the dwarf sequence at the star's colour, or at the colour its
|
* 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
|
* spectral type implies where it has none; a giant's off its type (giantSurface). Exactly the
|
||||||
* distance here. A giant is read as the dwarf of its colour: a few hundred kelvin too cool at K,
|
* Sun's for the Sun, which is at zero distance here.
|
||||||
* and Antares, an M1 supergiant, 3 019 K against the 3 660 Ohnaka et al. (2013) measure.
|
|
||||||
*/
|
*/
|
||||||
export function effectiveTemperatureK(star: StellarPhotometry): number | null {
|
export function effectiveTemperatureK(star: StellarPhotometry): number | null {
|
||||||
if (star.distancePc === 0) {
|
if (star.distancePc === 0) {
|
||||||
return SOLAR_EFFECTIVE_TEMPERATURE_K;
|
return SOLAR_EFFECTIVE_TEMPERATURE_K;
|
||||||
}
|
}
|
||||||
// A type's colour past the table is an O star's, which B−V no longer tells apart from B0.
|
// 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;
|
return (
|
||||||
|
giantSurface(star.spectralType)?.temperatureK ??
|
||||||
|
(sequenceAtColour(star) ?? dwarfSequenceAtColor(spectralTypeToColorIndex(star.spectralType), 'B-V', true))?.temperatureK ??
|
||||||
|
null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What a giant's type says of its surface: its effective temperature, and the bolometric
|
||||||
|
* correction the dwarf sequence has at that temperature — both off the type, so that the two a
|
||||||
|
* radius is drawn from come from the same place. `null` for a star that is not a giant, or whose
|
||||||
|
* class the parser cannot read.
|
||||||
|
*
|
||||||
|
* Read off the colour, a giant is the dwarf of its colour, too cool: Antares, M1 Ib at B−V 1.87,
|
||||||
|
* came out 3 019 K against the 3 660 Ohnaka et al. (2013) measure, and the 610 M giants a median
|
||||||
|
* 3 275 K where M2 III is about 3 650. Worse, a hot giant behind dust reads as a far cooler star:
|
||||||
|
* Menkib, O7.5 Iab at B−V 0.02, was 9 517 K, and with its type's correction beside that colour's
|
||||||
|
* temperature it was drawn at 95 R☉, and Alp Cam, O9.5 Ia, at 338, where 14 and 21 are published.
|
||||||
|
*
|
||||||
|
* G to M giants take van Belle et al.'s (2021, ApJ 922, 163, table 8) interferometric scale, fitted
|
||||||
|
* to 191 giants from G1 to M7.75 III: 4 692 K at K0, 3 816 at M0, 3 472 at M4, held at 3 134 past
|
||||||
|
* M7.75. O to F giants take the dwarf of their type, which a supergiant of the same type is within
|
||||||
|
* a few per cent of from B8 on, and a few thousand kelvin cooler than at B0 (Alnilam, B0 Ia, about
|
||||||
|
* 27 000 K against B0 V's 31 400). Carbon and S stars take {@link CARBON_STAR}.
|
||||||
|
*/
|
||||||
|
export function giantSurface(spectralType: string | null | undefined): { temperatureK: number; bolometricCorrectionV: number } | null {
|
||||||
|
if (!isGiant(spectralType)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const primary = (spectralType ?? '').split('+')[0].trim();
|
||||||
|
if (/^[CNRS]/.test(primary)) {
|
||||||
|
return CARBON_STAR;
|
||||||
|
}
|
||||||
|
const parsed = parseSpectralClass(primary);
|
||||||
|
if (!parsed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const { spectralClass, subclass } = parsed;
|
||||||
|
if (spectralClass === 'G' || spectralClass === 'K' || spectralClass === 'M') {
|
||||||
|
// van Belle's index: G0 at 50, K0 at 60, K5 at 65 and M0 at 66, so a K later than K5 falls between.
|
||||||
|
const index = spectralClass === 'G' ? 50 + subclass : spectralClass === 'K' ? 60 + Math.min(subclass, 5) + Math.max(subclass - 5, 0) / 5 : 66 + subclass;
|
||||||
|
const temperatureK = index <= 61 ? 7856 - 52.74 * index : index <= 64 ? 16751 - 199.41 * index : Math.max(9491 - 85.98 * index, 3134);
|
||||||
|
return { temperatureK, bolometricCorrectionV: dwarfSequenceAtTemperature(temperatureK).bolometricCorrectionV };
|
||||||
|
}
|
||||||
|
const dwarf = dwarfSequenceAtType(primary)!;
|
||||||
|
return { temperatureK: dwarf.temperatureK, bolometricCorrectionV: dwarf.bolometricCorrectionV };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A carbon or S star's temperature and bolometric correction to V: the medians of Bergeat, Knapik &
|
||||||
|
* Rutily (2001, A&A 369, 178) over the 441 carbon stars of their table 10, and over the 383 of
|
||||||
|
* those with a V magnitude. No type in the table reads for them, and they were given the Sun's
|
||||||
|
* −0.06 at the M8.5 dwarf's 2 420 K: La Superba came out 544 L☉ and 133 R☉, where Bergeat's own
|
||||||
|
* figures give 8 090 L☉ at the same distance and McDonald et al. (2017) 315 R☉. S stars, between M
|
||||||
|
* and C, are given the carbon stars' figures for want of their own.
|
||||||
|
*/
|
||||||
|
const CARBON_STAR = { temperatureK: 2990, bolometricCorrectionV: -2.83 } as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Radius in solar radii from luminosity and temperature — Stefan-Boltzmann, L = 4πR²σT⁴, in solar
|
* Radius in solar radii from luminosity and temperature — Stefan-Boltzmann, L = 4πR²σT⁴, in solar
|
||||||
* units. Luminosity-class blind, since the luminosity comes from the distance: a giant comes out a
|
* units. Luminosity-class blind, since the luminosity comes from the distance: a giant comes out a
|
||||||
|
|||||||
Reference in New Issue
Block a user