Find the hosts the catalogue already holds, and name them after their planets' star
The archive's default-parameter rows leave sy_dist blank for 100 planets, TRAPPIST-1's seven among them, so the matcher could not place a host the catalogue had drawn since the Gaia nearby query (Gaia DR3 2635476908753563008, 12.47 pc). fetchExoplanets now also reads the Planetary Systems Composite table (pscomppars) and fills a default row's blank host cells from it. Only host columns: a composite row takes each column from its own reference, so orbits still come from the default row alone, one fit per planet. Where both tables give a distance or a position they agree on all 6 225 and 6 352 rows. Three hosts the catalogue holds by name failed the distance ratio test because the archive's sy_dist, from TICv8, contradicts its own parallax: Lalande 21185 (GJ 411) 5.68 pc against 392 mas, Luyten's Star (GJ 273) 5.92 against 263 mas, Struve 2398 B (Gl 725 B) 6.84 against 285 mas. resolveHostStarId now takes the archive's parallax (sy_plx) as a second distance the ratio test accepts. It is a second chance, not a replacement: 47 of 5 959 systems disagree past the tolerance, and for faint far hosts the inverse parallax is the worse figure (K2-238: 538 pc by sy_dist, 6 779 by parallax). Planets on a catalogue star: 2 071 -> 2 090 of 6 354. The 19 gained are TRAPPIST-1 (7), GJ 273 (2), GJ 411 (2), Gl 725 B, HD 62509 (Pollux), K2-65, TOI-2267 A and B, and two brown-dwarf hosts, 2MASS J02192210-3925225 and DENIS-P J082303.1-491201. No planet lost or changed its host. A matched host whose catalogue name is a bare Gaia designation now takes the archive's host name, so search finds TRAPPIST-1, Teegarden's Star, TOI-700, LP 791-18 and K2-18: 575 stars renamed. validateExoplanets refuses a host that is still only a designation, and the star assets are rewritten after the exoplanets for that reason; stars.bin and stars-meta.bin are unchanged. TOI-2267 A and B both land on one Gaia entry, which takes the name TOI-2267 A. Each planet also carries its host's radius, effective temperature and luminosity (10^st_lum), and a mass for 6 344 planets instead of 5 474, from the default row where it gives one and the composite table otherwise, for the star-physics step. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -109,6 +109,17 @@ describe('resolveHostStarId', () => {
|
|||||||
expect(id).toBeNull();
|
expect(id).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// The archive's 5.92 pc is TICv8's; its own parallax, 263.26 mas, says 3.80 pc, which is the
|
||||||
|
// star's. With the parallax given, the same query is Luyten's Star — and a parallax that
|
||||||
|
// contradicts the star as well rescues nothing.
|
||||||
|
it("accepts the archive's parallax where its sy_dist contradicts the star", () => {
|
||||||
|
const luytens = star(100, "Luyten's Star", 111.8496, 5.2258, 3.79);
|
||||||
|
const query = { hostname: 'GJ 273', raDeg: 111.8496, decDeg: 5.2258, distancePc: 5.921535 };
|
||||||
|
|
||||||
|
expect(resolveHostStarId({ ...query, parallaxMas: 263.26 }, [luytens])).toBe(100);
|
||||||
|
expect(resolveHostStarId({ ...query, parallaxMas: 168.9 }, [luytens])).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
// The tolerance is transverse — parsecs on the sky, not an angle — so the same 15″ offset
|
// The tolerance is transverse — parsecs on the sky, not an angle — so the same 15″ offset
|
||||||
// is a match at 50 pc and a stranger at 200 pc.
|
// is a match at 50 pc and a stranger at 200 pc.
|
||||||
it('scales the angular tolerance with the host distance', () => {
|
it('scales the angular tolerance with the host distance', () => {
|
||||||
|
|||||||
@@ -15,6 +15,21 @@ export interface HostStarQuery {
|
|||||||
/** μα·cos δ in mas/yr, as the archive publishes it (`sy_pmra`); missing means unknown. */
|
/** μα·cos δ in mas/yr, as the archive publishes it (`sy_pmra`); missing means unknown. */
|
||||||
pmRaMasPerYear?: number;
|
pmRaMasPerYear?: number;
|
||||||
pmDecMasPerYear?: number;
|
pmDecMasPerYear?: number;
|
||||||
|
/**
|
||||||
|
* The archive's parallax in mas (`sy_plx`), a second distance the ratio test accepts. Its
|
||||||
|
* `sy_dist` comes from TICv8 and contradicts its own parallax past the tolerance for 47 of the
|
||||||
|
* 5 959 systems that publish both — Lalande 21185 at 5.68 pc for 392 mas (2.55 pc), Luyten's
|
||||||
|
* Star at 5.92 for 263 mas, Struve 2398 B at 6.84 for 285 — and those three are in the
|
||||||
|
* catalogue, 0.1″ to 9″ from the archive's direction. A second chance rather than a
|
||||||
|
* replacement: past a few hundred parsecs the inverse of a low-S/N parallax is the worse
|
||||||
|
* estimate (K2-238, 538 pc by `sy_dist`, would be 6 779).
|
||||||
|
*/
|
||||||
|
parallaxMas?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function distancesAgree(a: number, b: number): boolean {
|
||||||
|
const [near, far] = a < b ? [a, b] : [b, a];
|
||||||
|
return (far - near) / near <= MERGE_DISTANCE_RATIO_TOLERANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,6 +144,7 @@ export function resolveHostStarId(
|
|||||||
);
|
);
|
||||||
const carried = raDegDecDistanceToXyz(carriedBack.raDeg, carriedBack.decDeg, 1);
|
const carried = raDegDecDistanceToXyz(carriedBack.raDeg, carriedBack.decDeg, 1);
|
||||||
|
|
||||||
|
const parallaxPc = query.parallaxMas !== undefined && query.parallaxMas > 0 ? 1000 / query.parallaxMas : Number.NaN;
|
||||||
const minCosine = Math.cos(Math.min(Math.PI, HOST_TRANSVERSE_TOLERANCE_PC / query.distancePc));
|
const minCosine = Math.cos(Math.min(Math.PI, HOST_TRANSVERSE_TOLERANCE_PC / query.distancePc));
|
||||||
let best: StarRecord | null = null;
|
let best: StarRecord | null = null;
|
||||||
let bestCosine = -2;
|
let bestCosine = -2;
|
||||||
@@ -146,8 +162,7 @@ export function resolveHostStarId(
|
|||||||
if (cosine < minCosine || cosine <= bestCosine) {
|
if (cosine < minCosine || cosine <= bestCosine) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const [near, far] = query.distancePc < starDistance ? [query.distancePc, starDistance] : [starDistance, query.distancePc];
|
if (!distancesAgree(query.distancePc, starDistance) && !(parallaxPc > 0 && distancesAgree(parallaxPc, starDistance))) {
|
||||||
if ((far - near) / near > MERGE_DISTANCE_RATIO_TOLERANCE) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
best = star;
|
best = star;
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ import { OrbitalElements } from './body.model';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A confirmed exoplanet from the NASA Exoplanet Archive (`Planetary Systems` TAP table),
|
* A confirmed exoplanet from the NASA Exoplanet Archive (`Planetary Systems` TAP table),
|
||||||
* cross-referenced to its host star in the HYG index where possible.
|
* cross-referenced to its host star in the star catalogue.
|
||||||
*/
|
*/
|
||||||
export interface ExoplanetRecord {
|
export interface ExoplanetRecord {
|
||||||
id: string;
|
id: string;
|
||||||
hostStarId: number | null; // null if the host star could not be cross-referenced to HYG
|
/**
|
||||||
|
* The host's star-catalogue id, the HYG or Gaia star it was matched to; null when none is.
|
||||||
|
*/
|
||||||
|
hostStarId: number | null;
|
||||||
hostStarName: string;
|
hostStarName: string;
|
||||||
name: string;
|
name: string;
|
||||||
radiusEarth?: number;
|
radiusEarth?: number;
|
||||||
@@ -20,6 +23,15 @@ export interface ExoplanetRecord {
|
|||||||
periodDays?: number;
|
periodDays?: number;
|
||||||
/** Host star mass in solar masses (`st_mass`); the fallback when no period is published. */
|
/** Host star mass in solar masses (`st_mass`); the fallback when no period is published. */
|
||||||
hostStarMassSolar?: number;
|
hostStarMassSolar?: number;
|
||||||
|
/**
|
||||||
|
* The host's radius in solar radii (`st_rad`), effective temperature in kelvin (`st_teff`) and
|
||||||
|
* luminosity in solar luminosities (10^`st_lum`), where the archive gives them: the planet's
|
||||||
|
* default row first, then the composite table, whose columns may each come from a different
|
||||||
|
* reference. Measured, where `stellar.ts` otherwise derives a luminosity from V and distance.
|
||||||
|
*/
|
||||||
|
hostStarRadiusSolar?: number;
|
||||||
|
hostStarTemperatureK?: number;
|
||||||
|
hostStarLuminositySolar?: number;
|
||||||
/**
|
/**
|
||||||
* The host star's own published astrometry (`ra`, `dec`, `sy_dist`, `sy_pmra`, `sy_pmdec`) —
|
* The host star's own published astrometry (`ra`, `dec`, `sy_dist`, `sy_pmra`, `sy_pmdec`) —
|
||||||
* everything the cross-reference above was resolved from.
|
* everything the cross-reference above was resolved from.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+13
-7
@@ -7,7 +7,7 @@ import { StarRecord, SUN_STAR_ID } from '../../src/app/shared/models/star.model'
|
|||||||
import { fetchDeepSky } from './fetchDeepSky';
|
import { fetchDeepSky } from './fetchDeepSky';
|
||||||
import { fetchExoplanets } from './fetchExoplanets';
|
import { fetchExoplanets } from './fetchExoplanets';
|
||||||
import { fetchSolarSystem } from './fetchSolarSystem';
|
import { fetchSolarSystem } from './fetchSolarSystem';
|
||||||
import { BYTES_PER_STAR_META, BYTES_PER_STAR_POSITION, decodeStarCatalog, encodeStarCatalog } from '../../src/app/shared/models/star-catalog';
|
import { BYTES_PER_STAR_META, BYTES_PER_STAR_POSITION, decodeStarCatalog, encodeStarCatalog, isDesignation } from '../../src/app/shared/models/star-catalog';
|
||||||
import { fetchStars } from './fetchStars';
|
import { fetchStars } from './fetchStars';
|
||||||
import { describeSources } from './sources/registry';
|
import { describeSources } from './sources/registry';
|
||||||
import { dataPath } from './lib/paths';
|
import { dataPath } from './lib/paths';
|
||||||
@@ -155,14 +155,19 @@ function validateBodies(bodies: BodyRecord[]): void {
|
|||||||
assertCondition(planetCount === 8, `Expected 8 planets, found ${planetCount}.`);
|
assertCondition(planetCount === 8, `Expected 8 planets, found ${planetCount}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateExoplanets(exoplanets: ExoplanetRecord[], starIds: Set<number>): void {
|
function validateExoplanets(exoplanets: ExoplanetRecord[], stars: StarRecord[]): void {
|
||||||
assertCondition(exoplanets.length > 0, 'No exoplanets were produced.');
|
assertCondition(exoplanets.length > 0, 'No exoplanets were produced.');
|
||||||
|
const starsById = new Map(stars.map((star) => [star.id, star]));
|
||||||
|
|
||||||
let crossReferenced = 0;
|
let crossReferenced = 0;
|
||||||
for (const exoplanet of exoplanets) {
|
for (const exoplanet of exoplanets) {
|
||||||
assertCondition(!!exoplanet.name, `Exoplanet ${exoplanet.id} has no name.`);
|
assertCondition(!!exoplanet.name, `Exoplanet ${exoplanet.id} has no name.`);
|
||||||
if (exoplanet.hostStarId !== null) {
|
if (exoplanet.hostStarId !== null) {
|
||||||
assertCondition(starIds.has(exoplanet.hostStarId), `Exoplanet ${exoplanet.id} references unknown star id ${exoplanet.hostStarId}.`);
|
const host = starsById.get(exoplanet.hostStarId);
|
||||||
|
assertCondition(host !== undefined, `Exoplanet ${exoplanet.id} references unknown star id ${exoplanet.hostStarId}.`);
|
||||||
|
// A host known only as "Gaia DR3 2635476908753563008" cannot be found by searching for
|
||||||
|
// TRAPPIST-1; fetchExoplanets names it after its host, and 575 were renamed.
|
||||||
|
assertCondition(!isDesignation(host!), `Exoplanet ${exoplanet.id}'s host is only a designation, ${host!.name}, not named after ${exoplanet.hostStarName}.`);
|
||||||
// The Sun has no exoplanets, so any match to it is a matching failure — historically a
|
// The Sun has no exoplanets, so any match to it is a matching failure — historically a
|
||||||
// blank distance column parsing as 0, which puts the host at the origin and matches Sol
|
// blank distance column parsing as 0, which puts the host at the origin and matches Sol
|
||||||
// exactly. Free, permanent tripwire for that whole class of bug.
|
// exactly. Free, permanent tripwire for that whole class of bug.
|
||||||
@@ -183,7 +188,7 @@ function validateExoplanets(exoplanets: ExoplanetRecord[], starIds: Set<number>)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(` ${crossReferenced}/${exoplanets.length} exoplanets cross-referenced to a HYG host star.`);
|
console.log(` ${crossReferenced}/${exoplanets.length} exoplanets have a host star on the map.`);
|
||||||
|
|
||||||
// How many can be propagated at their real rate rather than as if the host were the Sun.
|
// How many can be propagated at their real rate rather than as if the host were the Sun.
|
||||||
const withPeriod = exoplanets.filter((exoplanet) => exoplanet.periodDays !== undefined).length;
|
const withPeriod = exoplanets.filter((exoplanet) => exoplanet.periodDays !== undefined).length;
|
||||||
@@ -257,11 +262,12 @@ async function build(): Promise<void> {
|
|||||||
console.log(describeSources());
|
console.log(describeSources());
|
||||||
console.log();
|
console.log();
|
||||||
|
|
||||||
const stars = await fetchStars();
|
const catalogueStars = await fetchStars();
|
||||||
console.log();
|
console.log();
|
||||||
const bodies = await fetchSolarSystem();
|
const bodies = await fetchSolarSystem();
|
||||||
console.log();
|
console.log();
|
||||||
const exoplanets = await fetchExoplanets(stars);
|
// Names the Gaia designations that host planets, so it is this list, not the one above, that is published.
|
||||||
|
const { exoplanets, stars } = await fetchExoplanets(catalogueStars);
|
||||||
console.log();
|
console.log();
|
||||||
const deepSky = await fetchDeepSky();
|
const deepSky = await fetchDeepSky();
|
||||||
console.log();
|
console.log();
|
||||||
@@ -270,7 +276,7 @@ async function build(): Promise<void> {
|
|||||||
validateStars(stars);
|
validateStars(stars);
|
||||||
validateMerge(stars);
|
validateMerge(stars);
|
||||||
validateBodies(bodies);
|
validateBodies(bodies);
|
||||||
validateExoplanets(exoplanets, new Set(stars.map((star) => star.id)));
|
validateExoplanets(exoplanets, stars);
|
||||||
validateDeepSky(deepSky);
|
validateDeepSky(deepSky);
|
||||||
|
|
||||||
console.log('\nETL completed successfully:');
|
console.log('\nETL completed successfully:');
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import { writeFileSync } from 'node:fs';
|
|||||||
|
|
||||||
import { buildStarNameIndex, resolveHostStarId } from '../../src/app/shared/astro/host-star-matching';
|
import { buildStarNameIndex, resolveHostStarId } from '../../src/app/shared/astro/host-star-matching';
|
||||||
import { ExoplanetRecord } from '../../src/app/shared/models/exoplanet.model';
|
import { ExoplanetRecord } from '../../src/app/shared/models/exoplanet.model';
|
||||||
|
import { isDesignation } from '../../src/app/shared/models/star-catalog';
|
||||||
import { StarRecord } from '../../src/app/shared/models/star.model';
|
import { StarRecord } from '../../src/app/shared/models/star.model';
|
||||||
import { fetchStars } from './fetchStars';
|
import { fetchStars, writeStarAssets } from './fetchStars';
|
||||||
import { parseCsvObjects, parseOptionalNumber } from './lib/csv';
|
import { parseCsvObjects, parseOptionalNumber } from './lib/csv';
|
||||||
import { fetchTextCached } from './lib/http';
|
import { fetchTextCached } from './lib/http';
|
||||||
import { dataPath, ensureDataDir } from './lib/paths';
|
import { dataPath, ensureDataDir } from './lib/paths';
|
||||||
@@ -42,34 +43,80 @@ const TAP_URL = `${TAP_BASE_URL}?query=${TAP_QUERY}`;
|
|||||||
const CACHE_FILE = `exoplanet-archive-ps-${createHash('sha1').update(TAP_URL).digest('hex').slice(0, 8)}.csv`;
|
const CACHE_FILE = `exoplanet-archive-ps-${createHash('sha1').update(TAP_URL).digest('hex').slice(0, 8)}.csv`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Downloads confirmed exoplanets from the NASA Exoplanet Archive (`Planetary Systems` TAP
|
* The host's columns from the Planetary Systems Composite table, for the cells a planet's
|
||||||
* table), cross-references each host star to the HYG index, and writes `exoplanets.json`.
|
* default row leaves blank and for the columns that query does not ask for.
|
||||||
|
*
|
||||||
|
* The default rows are one reference each, which is what keeps a planet's orbit coherent: its
|
||||||
|
* period, semi-major axis, eccentricity and periastron come from one fit. A composite row takes
|
||||||
|
* each column from wherever it is best measured, so an orbit read from it could pair one paper's
|
||||||
|
* eccentricity with another's argument of periastron; none of its orbital columns are asked for.
|
||||||
|
* Its system columns equal the default rows' wherever both are given (6 225 distances, 6 352
|
||||||
|
* positions, none different). What it adds is the 100 distances the default rows leave blank,
|
||||||
|
* TRAPPIST-1's seven among them, 870 host masses, and the parallax, photometry and stellar
|
||||||
|
* parameters below — each of which may come from a different reference.
|
||||||
*/
|
*/
|
||||||
export async function fetchExoplanets(stars?: StarRecord[]): Promise<ExoplanetRecord[]> {
|
const COMPOSITE_COLUMNS = [
|
||||||
|
'pl_name',
|
||||||
|
'ra',
|
||||||
|
'dec',
|
||||||
|
'sy_dist',
|
||||||
|
'sy_plx',
|
||||||
|
'sy_pmra',
|
||||||
|
'sy_pmdec',
|
||||||
|
'sy_bmag',
|
||||||
|
'sy_vmag',
|
||||||
|
'sy_gaiamag',
|
||||||
|
'st_spectype',
|
||||||
|
'st_teff',
|
||||||
|
'st_rad',
|
||||||
|
'st_mass',
|
||||||
|
'st_lum'
|
||||||
|
].join(',');
|
||||||
|
// pl_name is unique here too, one row per planet.
|
||||||
|
const COMPOSITE_URL = `${TAP_BASE_URL}?query=select+${COMPOSITE_COLUMNS}+from+pscomppars+order+by+pl_name&format=csv`;
|
||||||
|
const COMPOSITE_CACHE_FILE = `exoplanet-archive-pscomppars-${createHash('sha1').update(COMPOSITE_URL).digest('hex').slice(0, 8)}.csv`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads confirmed exoplanets from the NASA Exoplanet Archive (`Planetary Systems` TAP
|
||||||
|
* table), cross-references each host star to the star catalogue, and writes `exoplanets.json`
|
||||||
|
* together with the star assets, whose Gaia designations take their host's name. Returns both.
|
||||||
|
*/
|
||||||
|
export async function fetchExoplanets(stars?: StarRecord[]): Promise<{ exoplanets: ExoplanetRecord[]; stars: StarRecord[] }> {
|
||||||
console.log('Fetching confirmed exoplanets from the NASA Exoplanet Archive...');
|
console.log('Fetching confirmed exoplanets from the NASA Exoplanet Archive...');
|
||||||
const knownStars = stars ?? (await fetchStars());
|
const knownStars = stars ?? (await fetchStars());
|
||||||
const nameIndex = buildStarNameIndex(knownStars);
|
const nameIndex = buildStarNameIndex(knownStars);
|
||||||
|
const knownById = new Map(knownStars.map((star) => [star.id, star]));
|
||||||
|
|
||||||
const csv = await fetchTextCached(TAP_URL, CACHE_FILE);
|
const csv = await fetchTextCached(TAP_URL, CACHE_FILE);
|
||||||
const rows = parseCsvObjects(csv);
|
const rows = parseCsvObjects(csv);
|
||||||
|
const composite = new Map(parseCsvObjects(await fetchTextCached(COMPOSITE_URL, COMPOSITE_CACHE_FILE)).map((row) => [row['pl_name'], row]));
|
||||||
|
|
||||||
let matched = 0;
|
let matched = 0;
|
||||||
|
// Catalogue stars known only by their Gaia designation, which take the archive's host name —
|
||||||
|
// the only way "TRAPPIST-1" or "Teegarden's Star" can be found by search.
|
||||||
|
const renamed = new Map<number, string>();
|
||||||
const exoplanets: ExoplanetRecord[] = rows.map((row, index) => {
|
const exoplanets: ExoplanetRecord[] = rows.map((row, index) => {
|
||||||
|
const compositeRow = composite.get(row['pl_name']);
|
||||||
// `parseOptionalNumber`, not `Number`: a blank cell would otherwise become 0, which is a
|
// `parseOptionalNumber`, not `Number`: a blank cell would otherwise become 0, which is a
|
||||||
// finite, plausible-looking coordinate rather than the "not measured" it actually means.
|
// finite, plausible-looking coordinate rather than the "not measured" it actually means.
|
||||||
const raDeg = parseOptionalNumber(row['ra']) ?? Number.NaN;
|
const host = (column: string) => parseOptionalNumber(row[column] || compositeRow?.[column]);
|
||||||
const decDeg = parseOptionalNumber(row['dec']) ?? Number.NaN;
|
const raDeg = host('ra') ?? Number.NaN;
|
||||||
const distancePc = parseOptionalNumber(row['sy_dist']) ?? Number.NaN;
|
const decDeg = host('dec') ?? Number.NaN;
|
||||||
const pmRaMasPerYear = parseOptionalNumber(row['sy_pmra']);
|
const distancePc = host('sy_dist') ?? Number.NaN;
|
||||||
const pmDecMasPerYear = parseOptionalNumber(row['sy_pmdec']);
|
const pmRaMasPerYear = host('sy_pmra');
|
||||||
|
const pmDecMasPerYear = host('sy_pmdec');
|
||||||
|
|
||||||
const hostStarId = resolveHostStarId(
|
const hostStarId = resolveHostStarId(
|
||||||
{ hostname: row['hostname'], raDeg, decDeg, distancePc, pmRaMasPerYear, pmDecMasPerYear },
|
{ hostname: row['hostname'], raDeg, decDeg, distancePc, pmRaMasPerYear, pmDecMasPerYear, parallaxMas: host('sy_plx') },
|
||||||
knownStars,
|
knownStars,
|
||||||
nameIndex
|
nameIndex
|
||||||
);
|
);
|
||||||
if (hostStarId !== null) {
|
if (hostStarId !== null) {
|
||||||
matched++;
|
matched++;
|
||||||
|
const star = knownById.get(hostStarId);
|
||||||
|
if (star?.source === 'gaia' && isDesignation(star) && !renamed.has(hostStarId)) {
|
||||||
|
renamed.set(hostStarId, row['hostname']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -84,12 +131,16 @@ export async function fetchExoplanets(stars?: StarRecord[]): Promise<ExoplanetRe
|
|||||||
// determines the host's gravitational parameter, so keeping it is the difference between
|
// determines the host's gravitational parameter, so keeping it is the difference between
|
||||||
// propagating a planet at its real rate and pretending every host is the Sun.
|
// propagating a planet at its real rate and pretending every host is the Sun.
|
||||||
periodDays: parseOptionalNumber(row['pl_orbper']),
|
periodDays: parseOptionalNumber(row['pl_orbper']),
|
||||||
hostStarMassSolar: parseOptionalNumber(row['st_mass']),
|
hostStarMassSolar: host('st_mass'),
|
||||||
|
hostStarRadiusSolar: host('st_rad'),
|
||||||
|
hostStarTemperatureK: host('st_teff'),
|
||||||
|
// Published as log10(L/L☉).
|
||||||
|
hostStarLuminositySolar: ((logLuminosity) => (logLuminosity === undefined ? undefined : 10 ** logLuminosity))(host('st_lum')),
|
||||||
// Kept so the cross-reference can be redone without the archive; see the record's own
|
// Kept so the cross-reference can be redone without the archive; see the record's own
|
||||||
// documentation. Undefined rather than NaN, which JSON cannot represent.
|
// documentation. Undefined rather than NaN, which JSON cannot represent.
|
||||||
hostRaDeg: parseOptionalNumber(row['ra']),
|
hostRaDeg: host('ra'),
|
||||||
hostDecDeg: parseOptionalNumber(row['dec']),
|
hostDecDeg: host('dec'),
|
||||||
hostDistancePc: parseOptionalNumber(row['sy_dist']),
|
hostDistancePc: host('sy_dist'),
|
||||||
hostPmRaMasPerYear: pmRaMasPerYear,
|
hostPmRaMasPerYear: pmRaMasPerYear,
|
||||||
hostPmDecMasPerYear: pmDecMasPerYear,
|
hostPmDecMasPerYear: pmDecMasPerYear,
|
||||||
orbit: {
|
orbit: {
|
||||||
@@ -101,10 +152,13 @@ export async function fetchExoplanets(stars?: StarRecord[]): Promise<ExoplanetRe
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const allStars = knownStars.map((star) => (renamed.has(star.id) ? { ...star, name: renamed.get(star.id)! } : star));
|
||||||
|
writeStarAssets(allStars);
|
||||||
|
|
||||||
ensureDataDir();
|
ensureDataDir();
|
||||||
writeFileSync(dataPath('exoplanets.json'), JSON.stringify(exoplanets));
|
writeFileSync(dataPath('exoplanets.json'), JSON.stringify(exoplanets));
|
||||||
console.log(` wrote ${exoplanets.length} exoplanets (${matched} cross-referenced to a HYG host star).`);
|
console.log(` wrote ${exoplanets.length} exoplanets (${matched} on a catalogue star, ${renamed.size} Gaia designations named after their host).`);
|
||||||
return exoplanets;
|
return { exoplanets, stars: allStars };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ async function mergeWithOtherSources(hygStars: StarRecord[]): Promise<StarRecord
|
|||||||
return stars;
|
return stars;
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeStarAssets(stars: StarRecord[]): void {
|
export function writeStarAssets(stars: StarRecord[]): void {
|
||||||
ensureDataDir();
|
ensureDataDir();
|
||||||
|
|
||||||
// The layout lives in `star-catalog.ts`, which the app decodes with — one definition, so the
|
// The layout lives in `star-catalog.ts`, which the app decodes with — one definition, so the
|
||||||
|
|||||||
Reference in New Issue
Block a user