Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5152dcb
Add astroquery.linelists.exomol — ExoMol database query module
aditya-pandey-dev Feb 21, 2026
64a7d7b
Add exomol entry to CHANGES.rst
aditya-pandey-dev Feb 21, 2026
f8445f9
Apply ruff formatting to exomol module
aditya-pandey-dev Feb 21, 2026
02165e2
Fix pandas import using pytest.importorskip for CI compatibility
aditya-pandey-dev Feb 21, 2026
7b4f4e3
Fix doctest example in core.py to use code block instead
aditya-pandey-dev Feb 22, 2026
c6fb117
Add exomol docs to toctree and fix automodapi
aditya-pandey-dev Feb 22, 2026
abb9bef
Address review: remove pandas from tests, fix Table conversion, resto…
aditya-pandey-dev Feb 23, 2026
6af1b54
Add radis to tox deps and skip tests if radis unavailable
aditya-pandey-dev Feb 23, 2026
1e6d2fb
Add radis to MOCK_MODULES in conf.py and optional deps for RTD build
aditya-pandey-dev Feb 23, 2026
0ba56e8
Fix importorskip exc_type and toctree indentation for exomol
aditya-pandey-dev Feb 23, 2026
abd6b2d
Revert exc_type: not supported in older pytest versions
aditya-pandey-dev Feb 23, 2026
c7a0d69
Remove unused sys import from test_exomol.py
aditya-pandey-dev Feb 23, 2026
d10be96
Add exc_type=ImportError to importorskip for NumPy 2.5 compat
aditya-pandey-dev Feb 23, 2026
6b37d59
Fix RTD toctree indent and radis skip for all pytest versions
aditya-pandey-dev Feb 23, 2026
9db0db6
Fix radis skip: add noqa F401 and allow_module_level=True
aditya-pandey-dev Feb 24, 2026
2b044be
Fix RTD toctree: match exomol indentation with other entries
aditya-pandey-dev Feb 24, 2026
892567a
Add example output to doctest examples in exomol.rst
aditya-pandey-dev Feb 24, 2026
062e043
Trigger CI re-run
aditya-pandey-dev Feb 24, 2026
55e715c
Address review: rename load_wavenum to wavenum with Quantity support,…
aditya-pandey-dev Feb 28, 2026
bbd89ee
Address review: reimplement get_databases with bs4 scraper, remove RA…
aditya-pandey-dev Feb 28, 2026
121df8e
Add test for get_databases bs4 scraper to improve coverage
aditya-pandey-dev Feb 28, 2026
9312afd
Fix flake8: remove unused bs4 import from test
aditya-pandey-dev Feb 28, 2026
99c856a
Fix flake8 E302/E306: correct blank lines in test_get_databases
aditya-pandey-dev Feb 28, 2026
974a1aa
Fix flake8: rewrite test_get_databases with correct formatting
aditya-pandey-dev Feb 28, 2026
b0d1a6c
Fix E303: remove extra blank line before test_get_databases
aditya-pandey-dev Feb 28, 2026
5618a38
Tests: use skipif RADEX_NOT_AVAILABLE, add Quantity args, move remote…
aditya-pandey-dev Mar 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@
New Tools and Services
----------------------

astroquery.linelists.exomol
^^^^^^^^^^^^^^^^^^^^^^^^^^^

- Added new ``astroquery.linelists.exomol`` module for querying the ExoMol
molecular line list database. Wraps RADIS ExoMol reader into the standard
astroquery ``BaseQuery`` pattern. [#3536]


noirlab
^^^^^^^

Expand Down
1 change: 1 addition & 0 deletions astroquery/linelists/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
This module contains sub-modules to support molecular and atomic line list
modules and common utilities for parsing catalog files.
"""
from .exomol import ExoMol # noqa: F401
4 changes: 4 additions & 0 deletions astroquery/linelists/exomol/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Licensed under a 3-clause BSD style license - see LICENSE.rst
from .core import ExoMol, ExoMolClass

__all__ = ["ExoMol", "ExoMolClass"]
283 changes: 283 additions & 0 deletions astroquery/linelists/exomol/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
# Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
ExoMol database query module for astroquery.
Queries the ExoMol database directly via its API.

References
----------
Tennyson et al. 2020, J. Quant. Spectrosc. Radiat. Transf.
https://www.exomol.com
RADIS: https://github.com/radis/radis (issue #925)
"""

from astropy import units as u
from astropy.table import Table
from astroquery.query import BaseQuery
from astropy import log

__all__ = ["ExoMol", "ExoMolClass"]

EXOMOL_URL = "https://www.exomol.com"


class ExoMolClass(BaseQuery):
"""
Queries the `ExoMol <https://www.exomol.com>`_ database for molecular
line lists used in exoplanet atmosphere modelling.

This module queries ExoMol directly and optionally uses RADIS
(``radis``) for line list fetching via :meth:`query_lines`.

.. note::

The :meth:`query_lines` and :meth:`get_partition_function` methods
require `RADIS <https://radis.readthedocs.io>`_ as an optional
dependency. Install it with::

pip install radis

All other methods (``get_molecule_list``, ``get_databases``)
work without RADIS.

Examples
--------
Query CO lines between 2000-2100 cm\\ :sup:`-1`::

from astropy import units as u
from astroquery.linelists.exomol import ExoMol

result = ExoMol.query_lines('CO',
wavenum_min=2000*u.cm**-1,
wavenum_max=2100*u.cm**-1)
print(result)
"""

URL = EXOMOL_URL
TIMEOUT = 60

def get_molecule_list(self, *, cache=True):
"""
Retrieve list of all molecules available in ExoMol.

Parameters
----------
cache : bool, optional
Cache HTTP response. Default ``True``.

Returns
-------
list of str
Sorted list of molecule names available in ExoMol.
"""
url = f"{self.URL}/db/exomol.all"
response = self._request("GET", url, cache=cache, timeout=self.TIMEOUT)
response.raise_for_status()
molecules = []
for line in response.text.splitlines():
line = line.strip()
if line and not line.startswith("#"):
parts = line.split()
if parts:
molecules.append(parts[0])
return sorted(list(set(molecules)))

def get_databases(self, molecule, *, cache=True):
"""
Get available line list databases for a given molecule.

Scrapes the ExoMol website to find available databases.

Parameters
----------
molecule : str
Molecule formula e.g. ``'H2O'``, ``'CO'``, ``'CH4'``.
cache : bool, optional
Cache results. Default ``True``.

Returns
-------
list of str
Available database names for this molecule.

Examples
--------
.. code-block:: python

from astroquery.linelists.exomol import ExoMol
dbs = ExoMol.get_databases('H2O')
print(dbs) # doctest: +SKIP

"""
try:
from bs4 import BeautifulSoup
except ImportError as e:
raise ImportError(
"The 'beautifulsoup4' package is required for get_databases(). "
"Install it with: pip install beautifulsoup4"
) from e

url = f"{self.URL}/data/molecules/{molecule}/"
response = self._request("GET", url, cache=cache, timeout=self.TIMEOUT)
response.raise_for_status()

soup = BeautifulSoup(response.text, "html.parser")
databases = []
for link in soup.find_all("a", href=True):
href = link["href"]
if (
href.startswith(f"/data/molecules/{molecule}/")
and href != f"/data/molecules/{molecule}/"
):
db_name = href.rstrip("/").split("/")[-1]
if db_name and db_name != molecule:
databases.append(db_name)

return sorted(list(set(databases)))

def query_lines(
self,
molecule,
database=None,
isotopologue="1",
wavenum_min=None,
wavenum_max=None,
broadening_species=None,
*,
cache=True,
):
"""
Fetch ExoMol line list for a given molecule.

Parameters
----------
molecule : str
Molecule formula e.g. ``'H2O'``, ``'CO'``, ``'SiO'``.
database : str, optional
ExoMol database name e.g. ``'POKAZATEL'`` for H2O.
If ``None``, uses the ExoMol-recommended database.
isotopologue : str, optional
Isotopologue number. Default ``'1'`` (most abundant).
wavenum_min : `~astropy.units.Quantity`, optional
Minimum wavenumber, e.g. ``2000*u.cm**-1``.
wavenum_max : `~astropy.units.Quantity`, optional
Maximum wavenumber, e.g. ``2100*u.cm**-1``.
broadening_species : str or list of str, optional
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should be Quantitys

Pressure-broadening partner(s).
Examples: ``'H2'``, ``['H2', 'He']``, ``'air'``.
If ``None``, downloads all available broadening files.
See RADIS issue #917 for broadening parameter details.
cache : bool, optional
Cache downloaded line list files. Default ``True``.

Returns
-------
`~astropy.table.Table`
Line list table with columns for wavenumber (wav),
line intensity (int), Einstein A coefficient (A),
and lower/upper state energies (El, Eu).

Examples
--------
Query CO lines::

from astropy import units as u
from astroquery.linelists.exomol import ExoMol

result = ExoMol.query_lines('CO',
wavenum_min=2000*u.cm**-1,
wavenum_max=2100*u.cm**-1)

Query H2O with H2+He broadening::

result = ExoMol.query_lines('H2O',
database='POKAZATEL',
broadening_species=['H2', 'He'],
wavenum_min=1000*u.cm**-1,
wavenum_max=1100*u.cm**-1)
"""
try:
from radis.io.exomol import fetch_exomol
except ImportError as e:
raise ImportError(
"The 'radis' package is required for query_lines(). "
"Install it with: pip install radis"
) from e

# Convert Quantity to float (cm^-1) for RADIS
wavenum_min_value = None
wavenum_max_value = None

if wavenum_min is not None:
if hasattr(wavenum_min, "unit"):
wavenum_min_value = wavenum_min.to(u.cm**-1).value
else:
wavenum_min_value = float(wavenum_min)

if wavenum_max is not None:
if hasattr(wavenum_max, "unit"):
wavenum_max_value = wavenum_max.to(u.cm**-1).value
else:
wavenum_max_value = float(wavenum_max)

log.info(
f"Querying ExoMol for {molecule} "
f"[{wavenum_min_value}-{wavenum_max_value} cm-1]"
)

df = fetch_exomol(
molecule=molecule,
database=database,
isotope=isotopologue,
load_wavenum_min=wavenum_min_value,
load_wavenum_max=wavenum_max_value,
broadening_species=broadening_species
if broadening_species is not None
else "air",
cache=cache,
verbose=False,
)
return df if isinstance(df, Table) else Table.from_pandas(df)

def get_partition_function(
self, molecule, database=None, isotopologue="1", *, cache=True
):
"""
Get partition function Q(T) for a molecule from ExoMol.

Parameters
----------
molecule : str
Molecule formula e.g. ``'CO'``, ``'H2O'``.
database : str, optional
ExoMol database name. If ``None``, uses recommended database.
isotopologue : str, optional
Isotopologue number. Default ``'1'``.
cache : bool, optional
Cache downloaded files. Default ``True``.

Returns
-------
`~astropy.table.Table`
Table with columns for temperature T (K) and partition
function Q(T).
"""
try:
from radis.io.exomol import fetch_exomol
except ImportError as e:
raise ImportError(
"The 'radis' package is required for get_partition_function(). "
"Install it with: pip install radis"
) from e

df = fetch_exomol(
molecule=molecule,
database=database,
isotope=isotopologue,
return_partition_function=True,
cache=cache,
verbose=False,
)
return df if isinstance(df, Table) else Table.from_pandas(df)


ExoMol = ExoMolClass()
5 changes: 5 additions & 0 deletions astroquery/linelists/exomol/exomol.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[linelists.exomol]
## ExoMol server base URL
server = https://www.exomol.com
## Request timeout in seconds
timeout = 60
Empty file.
Loading
Loading