diff --git a/astroquery/hitran/__init__.py b/astroquery/hitran/__init__.py index 2ca074b4f3..9cda4fdc70 100644 --- a/astroquery/hitran/__init__.py +++ b/astroquery/hitran/__init__.py @@ -24,6 +24,6 @@ class Conf(_config.ConfigNamespace): conf = Conf() -from .core import Hitran, HitranClass +from .core import Hitran, HitranClass, parse_hitran_text -__all__ = ['Hitran', 'HitranClass', 'conf'] +__all__ = ['Hitran', 'HitranClass', 'parse_hitran_text', 'conf'] diff --git a/astroquery/hitran/core.py b/astroquery/hitran/core.py index e0aca442ca..4ef260f5d7 100644 --- a/astroquery/hitran/core.py +++ b/astroquery/hitran/core.py @@ -6,7 +6,7 @@ from . import conf from .utils import parse_readme -__all__ = ['Hitran', 'HitranClass'] +__all__ = ['Hitran', 'HitranClass', 'parse_hitran_text'] @async_to_sync @@ -227,26 +227,71 @@ def _parse_result(self, response, *, verbose=False): """ Parse a response into an `~astropy.table.Table` """ - formats = parse_readme(self.FORMATFILE) - - dtypes = [entry['dtype'] for entry in formats.values()] - - rows = [] - for line in response.text.split('\n'): - if line.strip(): - row = [] - start = 0 - for key, entry in formats.items(): - formatter = entry['formatter'] - length = entry['length'] - value = formatter(line[start:start+length]) - row.append(value) - start = start + length - rows.append(row) - - result = Table(rows=rows, names=formats.keys(), dtype=dtypes) - - return result + return parse_hitran_text(response.text, formatfile=self.FORMATFILE) + + @staticmethod + def read(filename, *, formatfile=None): + """ + Read a HITRAN ``.par`` file from disk into an `~astropy.table.Table`. + + Parameters + ---------- + filename : str + Path to a HITRAN ``.par`` (160-column fixed-width) file. + formatfile : str, optional + Path to a HITRAN format ``readme.txt`` file describing the + column layout. Defaults to the bundled HITRAN 2004 format. + + Returns + ------- + result : `~astropy.table.Table` + Parsed line list. + """ + if formatfile is None: + formatfile = HitranClass.FORMATFILE + with open(filename, 'r') as fh: + text = fh.read() + return parse_hitran_text(text, formatfile=formatfile) + + +def parse_hitran_text(text, *, formatfile=HitranClass.FORMATFILE): + """ + Parse the text of a HITRAN ``.par`` line list into an `~astropy.table.Table`. + + Parameters + ---------- + text : str + Contents of a HITRAN ``.par`` file (160-column fixed-width records, + one per line). + formatfile : str, optional + Path to a HITRAN format ``readme.txt`` file describing the column + layout. Defaults to the bundled HITRAN 2004 format. + + Returns + ------- + result : `~astropy.table.Table` + Parsed line list. + """ + formats = parse_readme(formatfile) + + dtypes = [entry['dtype'] for entry in formats.values()] + + rows = [] + for line in text.split('\n'): + if line.strip(): + row = [] + start = 0 + for key, entry in formats.items(): + formatter = entry['formatter'] + length = entry['length'] + value = formatter(line[start:start+length]) + row.append(value) + start = start + length + rows.append(row) + + result = Table(rows=rows, names=formats.keys(), dtype=dtypes) + + return result Hitran = HitranClass() diff --git a/astroquery/hitran/tests/test_hitran.py b/astroquery/hitran/tests/test_hitran.py index 87468852cf..acb88a30fe 100644 --- a/astroquery/hitran/tests/test_hitran.py +++ b/astroquery/hitran/tests/test_hitran.py @@ -5,7 +5,7 @@ from astropy import units as u from astropy.table import Table -from ...hitran import Hitran +from ...hitran import Hitran, parse_hitran_text HITRAN_DATA = 'H2O.data' @@ -21,8 +21,8 @@ def __init__(self): @property def text(self): - with open(self.filename) as f: - return f.read() + with open(self.filename) as fh: + return fh.read() def test_query_async(): @@ -36,19 +36,54 @@ def test_query_async(): np.testing.assert_almost_equal(response['numax'], 10.) +EXPECTED_KEYS = {'molec_id', 'local_iso_id', 'nu', 'sw', 'a', + 'gamma_air', 'gamma_self', 'elower', + 'n_air', 'delta_air', 'global_upper_quanta', + 'global_lower_quanta', 'local_upper_quanta', + 'local_lower_quanta', 'ierr1', 'ierr2', + 'ierr3', 'ierr4', 'ierr5', 'ierr6', 'iref1', + 'iref2', 'iref3', 'iref4', 'iref5', 'iref6', + 'line_mixing_flag', 'gp', 'gpp'} + + def test_query(): hitran = Hitran() response = MockResponseHitran() tbl = hitran._parse_result(response) assert isinstance(tbl, Table) assert len(tbl) == 122 - assert set(tbl.keys()) == set(['molec_id', 'local_iso_id', 'nu', 'sw', 'a', - 'gamma_air', 'gamma_self', 'elower', - 'n_air', 'delta_air', 'global_upper_quanta', - 'global_lower_quanta', 'local_upper_quanta', - 'local_lower_quanta', 'ierr1', 'ierr2', - 'ierr3', 'ierr4', 'ierr5', 'ierr6', 'iref1', - 'iref2', 'iref3', 'iref4', 'iref5', 'iref6', - 'line_mixing_flag', 'gp', 'gpp']) + assert set(tbl.keys()) == EXPECTED_KEYS + assert tbl['molec_id'][0] == 1 + np.testing.assert_almost_equal(tbl['nu'][0], 0.072059) + + +def test_parse_hitran_text(): + with open(data_path(HITRAN_DATA)) as fh: + text = fh.read() + tbl = parse_hitran_text(text) + assert isinstance(tbl, Table) + assert len(tbl) == 122 + assert set(tbl.keys()) == EXPECTED_KEYS + assert tbl['molec_id'][0] == 1 + np.testing.assert_almost_equal(tbl['nu'][0], 0.072059) + + +def test_parse_hitran_text_matches_parse_result(): + """parse_hitran_text and _parse_result must produce identical tables.""" + response = MockResponseHitran() + tbl_from_response = Hitran._parse_result(response) + tbl_from_text = parse_hitran_text(response.text) + assert len(tbl_from_response) == len(tbl_from_text) + assert tbl_from_response.keys() == tbl_from_text.keys() + for key in tbl_from_response.keys(): + np.testing.assert_array_equal(tbl_from_response[key], + tbl_from_text[key]) + + +def test_read_par_file(): + tbl = Hitran.read(data_path(HITRAN_DATA)) + assert isinstance(tbl, Table) + assert len(tbl) == 122 + assert set(tbl.keys()) == EXPECTED_KEYS assert tbl['molec_id'][0] == 1 np.testing.assert_almost_equal(tbl['nu'][0], 0.072059)