-
-
Notifications
You must be signed in to change notification settings - Fork 433
Add astroquery.linelists.exomol — ExoMol database query module #3536
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
aditya-pandey-dev
wants to merge
26
commits into
astropy:main
Choose a base branch
from
aditya-pandey-dev:feature/exomol-linelists
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
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 64a7d7b
Add exomol entry to CHANGES.rst
aditya-pandey-dev f8445f9
Apply ruff formatting to exomol module
aditya-pandey-dev 02165e2
Fix pandas import using pytest.importorskip for CI compatibility
aditya-pandey-dev 7b4f4e3
Fix doctest example in core.py to use code block instead
aditya-pandey-dev c6fb117
Add exomol docs to toctree and fix automodapi
aditya-pandey-dev abb9bef
Address review: remove pandas from tests, fix Table conversion, resto…
aditya-pandey-dev 6af1b54
Add radis to tox deps and skip tests if radis unavailable
aditya-pandey-dev 1e6d2fb
Add radis to MOCK_MODULES in conf.py and optional deps for RTD build
aditya-pandey-dev 0ba56e8
Fix importorskip exc_type and toctree indentation for exomol
aditya-pandey-dev abd6b2d
Revert exc_type: not supported in older pytest versions
aditya-pandey-dev c7a0d69
Remove unused sys import from test_exomol.py
aditya-pandey-dev d10be96
Add exc_type=ImportError to importorskip for NumPy 2.5 compat
aditya-pandey-dev 6b37d59
Fix RTD toctree indent and radis skip for all pytest versions
aditya-pandey-dev 9db0db6
Fix radis skip: add noqa F401 and allow_module_level=True
aditya-pandey-dev 2b044be
Fix RTD toctree: match exomol indentation with other entries
aditya-pandey-dev 892567a
Add example output to doctest examples in exomol.rst
aditya-pandey-dev 062e043
Trigger CI re-run
aditya-pandey-dev 55e715c
Address review: rename load_wavenum to wavenum with Quantity support,…
aditya-pandey-dev bbd89ee
Address review: reimplement get_databases with bs4 scraper, remove RA…
aditya-pandey-dev 121df8e
Add test for get_databases bs4 scraper to improve coverage
aditya-pandey-dev 9312afd
Fix flake8: remove unused bs4 import from test
aditya-pandey-dev 99c856a
Fix flake8 E302/E306: correct blank lines in test_get_databases
aditya-pandey-dev 974a1aa
Fix flake8: rewrite test_get_databases with correct formatting
aditya-pandey-dev b0d1a6c
Fix E303: remove extra blank line before test_get_databases
aditya-pandey-dev 5618a38
Tests: use skipif RADEX_NOT_AVAILABLE, add Quantity args, move remote…
aditya-pandey-dev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
| 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() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these should be
Quantitys