#!/usr/bin/env python """Reproducible retrieval of the Martins, Schaerer & Hillier (2005, A&A 436, 1049) O-star parameter calibration. NOTE ON PROVENANCE: these are model-calibration tables printed only in the paper body; they were never ingested into VizieR (bibcode 2005A&A...436.1049M returns "No catalogue or table was specified or found", and Vizier.find_catalogs turns up nothing). astroquery.vizier therefore cannot retrieve them. Instead we pull the immutable arXiv e-print source (astro-ph/0503346) and parse the six deluxetable blocks by their \\label. arXiv e-prints are version-pinned, so this is reproducible. Tables: tab_V / tab_III / tab_I (theoretical Teff scale) tab_V_obs / tab_III_obs / tab_I_obs (observational Teff scale) Row columns: ST & Teff & logg & Mv & BC & logL & R & Mspec & q0 & q1 & Q0 & Q1 Writes martins2005.csv with (scale, lumclass, SpType, Teff, logg, Mv, BC, logL_Lsun, R_Rsun, Mspec_Msun, logq0, logq1, logQ0_s, logQ1_s). """ import io import os import re import csv import tarfile import urllib.request ARXIV_ID = 'astro-ph/0503346' SRC_URL = f'https://arxiv.org/e-print/{ARXIV_ID}' CACHE = 'martins2005_src.tar.gz' LABELS = { # label -> (scale, lumclass) 'tab_V': ('theoretical', 'V'), 'tab_III': ('theoretical', 'III'), 'tab_I': ('theoretical', 'I'), 'tab_V_obs': ('observational', 'V'), 'tab_III_obs': ('observational', 'III'), 'tab_I_obs': ('observational', 'I')} COLS = ['Teff', 'logg', 'Mv', 'BC', 'logL_Lsun', 'R_Rsun', 'Mspec_Msun', 'logq0', 'logq1', 'logQ0_s', 'logQ1_s'] def get_source_text(): if not os.path.exists(CACHE): req = urllib.request.Request(SRC_URL, headers={'User-Agent': 'Mozilla/5.0'}) with urllib.request.urlopen(req, timeout=60) as r: data = r.read() with open(CACHE, 'wb') as fh: fh.write(data) with tarfile.open(CACHE, 'r:gz') as tf: texname = [m for m in tf.getnames() if m.endswith('.tex')][0] return tf.extractfile(texname).read().decode('latin-1') def parse_tables(tex): rows = [] for label, (scale, lc) in LABELS.items(): # the table block: from \label{label} to the next \end{table*} m = re.search(r'\\label\{' + re.escape(label) + r'\}(.*?)\\end\{table\*\}', tex, re.S) if not m: raise ValueError(f'table {label} not found in source') for line in m.group(1).splitlines(): if '&' not in line or '\\hline' in line: continue cells = [c.strip().rstrip('\\').strip() for c in line.split('&')] cells = [c for c in cells if c != ''] if len(cells) < 12: continue st = cells[0] if not re.match(r'^\d', st): continue vals = [] for c in cells[1:12]: c = c.replace('x', '') # one stray '3.19x' typo in tab_I_obs vals.append(float(c)) rows.append(dict(scale=scale, lumclass=lc, SpType=f'O{st}', **dict(zip(COLS, vals)))) return rows def main(): rows = parse_tables(get_source_text()) with open('martins2005.csv', 'w', newline='') as fh: w = csv.DictWriter(fh, fieldnames=['scale', 'lumclass', 'SpType'] + COLS) w.writeheader() w.writerows(rows) print(f'wrote martins2005.csv ({len(rows)} rows, ' f'{len(set((r["scale"], r["lumclass"]) for r in rows))} tables)') return rows if __name__ == '__main__': main()