diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..8ac6b8c4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5995fa47..c5d71b06 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,64 +10,73 @@ on: pull_request: workflow_dispatch: - jobs: - test: + core: + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 + with: + posargs: '--ascii-dbase-root ~/.chianti' + toxdeps: tox-pypi-filter + envs: | + - linux: py312 + coverage: 'codecov' + cache-path: ~/.chianti + cache-key: chianti-${{ github.event.number }} + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + tests: + needs: [core] uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 with: posargs: '--ascii-dbase-root ~/.chianti' - toxdeps: "'tox<4' tox-pypi-filter" + toxdeps: tox-pypi-filter envs: | - macos: py311 - - windows: py311 - - linux: py39 - - linux: py310 - - linux: py311 + - windows: py310 coverage: 'codecov' cache-path: ~/.chianti cache-key: chianti-${{ github.event.number }} secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + test_full_database: - needs: [test] + needs: [tests] uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 with: posargs: '--ascii-dbase-root ~/.chianti --include-all-files' - toxdeps: "'tox<4' tox-pypi-filter" + toxdeps: tox-pypi-filter envs: | - linux: py311 cache-path: ~/.chianti cache-key: chianti-${{ github.event.number }} + test_database_v7: - needs: [test] + needs: [tests] uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 with: posargs: '--ascii-dbase-root ~/.chianti --ascii-dbase-url http://download.chiantidatabase.org/CHIANTI_v7.1.4_database.tar.gz --disable-file-hash --skip-version-check' - toxdeps: "'tox<4' tox-pypi-filter" + toxdeps: tox-pypi-filter envs: | - linux: py311 + test_database_v9: - needs: [test] + needs: [tests] uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 with: posargs: '--ascii-dbase-root ~/.chianti --ascii-dbase-url http://download.chiantidatabase.org/CHIANTI_v9.0.1_database.tar.gz --disable-file-hash --skip-version-check' - toxdeps: "'tox<4' tox-pypi-filter" + toxdeps: tox-pypi-filter envs: | - linux: py311 - precommit: - uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 - with: - toxdeps: "'tox<4' tox-pypi-filter" - envs: | - - linux: pre-commit + docs: - needs: [test] + needs: [tests] uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 with: - toxdeps: "'tox<4' tox-pypi-filter" + toxdeps: tox-pypi-filter envs: | - - linux: build_docs - python-version: '3.11' + - linux: build_docs-gallery + python-version: '3.12' + publish: # Build wheels when pushing to any branch except main # publish.yml will only publish if tagged ^v.* @@ -81,12 +90,12 @@ jobs: github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'Run publish') ) - needs: [test,test_full_database] + needs: [tests,test_full_database] uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish_pure_python.yml@main with: test_extras: 'dev' test_command: 'pytest -p no:warnings --doctest-rst --pyargs fiasco' submodules: false - python-version: '3.11' + python-version: '3.12' secrets: pypi_token: ${{ secrets.PYPI_TOKEN }} diff --git a/.gitignore b/.gitignore index d5b17d7f..5fa8bd72 100644 --- a/.gitignore +++ b/.gitignore @@ -155,3 +155,5 @@ htmlcov/ # IDEs .idea +.history +.vscode diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 095b1134..4da73c21 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,12 +1,12 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.1.5" + rev: "v0.4.4" hooks: - id: ruff args: ["--fix"] - repo: https://github.com/PyCQA/isort - rev: 5.12.0 + rev: 5.13.2 hooks: - id: isort name: isort @@ -17,7 +17,7 @@ repos: - python - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.6.0 hooks: - id: check-ast - id: check-case-conflict diff --git a/docs/conf.py b/docs/conf.py index d27ac6a0..867b9e0d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -2,20 +2,21 @@ # Configuration file for the Sphinx documentation builder. # import configparser +import datetime import os import pathlib +from sphinx_gallery.sorting import ExplicitOrder + # This file does only contain a selection of the most common options. For a # full list see the documentation: # http://www.sphinx-doc.org/en/master/config # -- Project information ----------------------------------------------------- - project = 'fiasco' -copyright = '2022, Will Barnes' +copyright = f'{datetime.datetime.now(datetime.UTC).year}, Will Barnes' author = 'Will Barnes' - # The full version, including alpha/beta/rc tags from fiasco import __version__ @@ -23,11 +24,8 @@ is_development = '.dev' in __version__ # -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. extensions = [ + 'sphinxcontrib.bibtex', 'matplotlib.sphinxext.plot_directive', 'sphinx.ext.autodoc', 'sphinx.ext.intersphinx', @@ -40,61 +38,54 @@ 'sphinx.ext.mathjax', 'sphinx_automodapi.automodapi', 'sphinx_automodapi.smart_resolver', - 'sphinxcontrib.bibtex', 'sphinx_design', + 'sphinx_gallery.gen_gallery', ] - -# Add any paths that contain templates here, relative to this directory. -# templates_path = ['_templates'] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: source_suffix = '.rst' - -# The master toctree document. master_doc = 'index' - -# The reST default role (used for this markup: `text`) to use for all -# documents. Set to the "smart" one. default_role = 'obj' - -# Disable having a separate return type row napoleon_use_rtype = False - -# Disable google style docstrings napoleon_google_docstring = False -# -- Options for intersphinx extension --------------------------------------- +# Enable nitpicky mode, which forces links to be non-broken +nitpicky = True +# This is not used. See docs/nitpick-exceptions file for the actual listing. +nitpick_ignore = [] +for line in open('nitpick-exceptions'): + if line.strip() == "" or line.startswith("#"): + continue + dtype, target = line.split(None, 1) + target = target.strip() + nitpick_ignore.append((dtype, target)) -# Example configuration for intersphinx: refer to the Python standard library. +# -- Options for intersphinx extension --------------------------------------- intersphinx_mapping = { - 'python': ('https://docs.python.org/3/', - (None, 'http://data.astropy.org/intersphinx/python3.inv')), - 'numpy': ('https://numpy.org/doc/stable/', - (None, 'http://data.astropy.org/intersphinx/numpy.inv')), - 'scipy': ('https://docs.scipy.org/doc/scipy/reference/', - (None, 'http://data.astropy.org/intersphinx/scipy.inv')), - 'matplotlib': ('https://matplotlib.org/', - (None, 'http://data.astropy.org/intersphinx/matplotlib.inv')), - 'astropy': ('https://docs.astropy.org/en/stable', None), - 'sunpy': ('https://docs.sunpy.org/en/stable/', None), - 'aiapy': ('https://aiapy.readthedocs.io/en/stable/', None), + "python": ( + "https://docs.python.org/3/", + (None, "http://www.astropy.org/astropy-data/intersphinx/python3.inv"), + ), + "numpy": ( + "https://numpy.org/doc/stable/", + (None, "http://www.astropy.org/astropy-data/intersphinx/numpy.inv"), + ), + "scipy": ( + "https://docs.scipy.org/doc/scipy/reference/", + (None, "http://www.astropy.org/astropy-data/intersphinx/scipy.inv"), + ), 'plasmapy': ('https://docs.plasmapy.org/en/stable', None), + 'sunpy': ('https://docs.sunpy.org/en/stable/', None), + "aiapy": ("https://aiapy.readthedocs.io/en/stable/", None), + "asdf": ("https://asdf.readthedocs.io/en/stable/", None), + "astropy": ("https://docs.astropy.org/en/stable/", None), + "matplotlib": ("https://matplotlib.org/stable", None), + "packaging": ("https://packaging.pypa.io/en/stable/", None), } # -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. - html_theme = 'pydata_sphinx_theme' -# -- Sphinx Book Theme Options ----------------------------------------------------- +# -- Sphinx Book Theme Options ----------------------------------------------- html_theme_options = { "use_edit_page_button": True, "icon_links": [ @@ -114,9 +105,6 @@ "icon": "fa-solid fa-wine-glass", } ], - #"secondary_sidebar_items": { - # "index": [] # Remove secondary sidebar on landing page - #}, "announcement": "fiasco currently only supports version 8 of the CHIANTI database.", } html_context = { @@ -126,16 +114,15 @@ "doc_path": "docs", } html_logo = '_static/fiasco-logo.png' - - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] - +# Set path for BibTeX file for all of our references +bibtex_bibfiles = ['references.bib'] +# Sidebar removal +html_sidebars = { + "quick_start*": [], + "how_to_guides*": [], +} # Render inheritance diagrams in SVG graphviz_output_format = "svg" - graphviz_dot_args = [ '-Nfontsize=10', '-Nfontname=Helvetica Neue, Helvetica, Arial, sans-serif', @@ -145,9 +132,7 @@ '-Gfontname=Helvetica Neue, Helvetica, Arial, sans-serif' ] -# Set path for BibTeX file for all of our references -bibtex_bibfiles = ['references.bib'] - +# -- Database on RTD ----------------------------------------------- ON_RTD = os.environ.get('READTHEDOCS') == 'True' ON_GHA = os.environ.get('CI') == 'true' @@ -173,17 +158,21 @@ c.write(f) # -- Sphinx gallery ----------------------------------------------------------- -from sphinx_gallery.sorting import ExampleTitleSortKey, ExplicitOrder # NOQA: E402 - -extensions += ['sphinx_gallery.gen_gallery'] sphinx_gallery_conf = { + 'backreferences_dir': os.path.join('generated', 'modules'), + 'filename_pattern': '^((?!skip_).)*$', + 'examples_dirs': os.path.join('..', 'examples'), 'subsection_order': ExplicitOrder([ '../examples/user_guide/', '../examples/idl_comparisons/', ]), - 'within_subsection_order': ExampleTitleSortKey, - 'examples_dirs': '../examples', # path to your example scripts - 'gallery_dirs': 'generated/gallery', # path to where to save gallery generated output - 'filename_pattern': '^((?!skip_).)*$', - 'default_thumb_file': '_static/fiasco-logo.png' + 'within_subsection_order': 'ExampleTitleSortKey', + 'gallery_dirs': os.path.join('generated', 'gallery'), + 'matplotlib_animations': True, + "default_thumb_file": '_static/fiasco-logo.png', + 'abort_on_example_error': False, + 'plot_gallery': 'True', + 'remove_config_comments': True, + 'doc_module': ('fiasco',), + 'only_warn_on_example_error': True, } diff --git a/docs/index.rst b/docs/index.rst index 4551b654..7b35d9e1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,7 +1,8 @@ fiasco Documentation -===================== +==================== Welcome to the documentation for fiasco. + fiasco provides a Python interface to the `CHIANTI atomic database`_. In addition to several high-level abstractions of the atomic data, fiasco also provides many common atomic physics calculations. The package takes its name from the Italian word *fiasco*, or flask, `the typical style of bottle `_ used to serve the *Chianti Classico* wine. @@ -47,13 +48,13 @@ In the same way, the `fiasco` package serves up the CHIANTI atomic database. Technical description of the inputs, outputs, and behavior of each component of fiasco .. toctree:: - :maxdepth: 1 - :hidden: - - quick_start - generated/gallery/index - how_to_guides - topic_guides/index - reference/index + :maxdepth: 1 + :hidden: + + quick_start + generated/gallery/index + how_to_guides + topic_guides/index + reference/index .. _CHIANTI atomic database: http://www.chiantidatabase.org/ diff --git a/docs/nitpick-exceptions b/docs/nitpick-exceptions new file mode 100644 index 00000000..c61bbcf3 --- /dev/null +++ b/docs/nitpick-exceptions @@ -0,0 +1,28 @@ +# Prevents sphinx nitpicky mode picking up on optional +# (see https://github.com/sphinx-doc/sphinx/issues/6861) +# Even if it was "fixed", still broken +py:class optional +# See https://github.com/numpy/numpy/issues/10039 +py:obj numpy.datetime64 +# There's no specific file or function classes to link to +py:class any type +py:class array-like +py:class file object +py:class function +py:class path-like +py:class str-like +py:class time-like +py:obj function +py:obj iterable +# Units +py:class Unit('1 / cm3') +py:class Unit('erg') +py:class Unit('Angstrom') +py:class Unit('1 / cm5') +py:class Unit('K') + +py:obj array-like +# Can't explain why it hates these math functions +py:obj psi_{text{norm}} +py:obj A_{text{sum}} +py:obj frac{1}{psi_{text{norm}}} int_{0}^{1} psi(y) dy = 2 diff --git a/docs/reference/code_ref/base.rst b/docs/reference/code_ref/base.rst new file mode 100644 index 00000000..7855ffc7 --- /dev/null +++ b/docs/reference/code_ref/base.rst @@ -0,0 +1 @@ +.. automodapi:: fiasco.base diff --git a/docs/reference/code_ref/fiasco.rst b/docs/reference/code_ref/fiasco.rst index fd854770..b676b248 100644 --- a/docs/reference/code_ref/fiasco.rst +++ b/docs/reference/code_ref/fiasco.rst @@ -1,6 +1 @@ -fiasco -====== - .. automodapi:: fiasco - :no-heading: - :skip: setup_paths diff --git a/docs/reference/code_ref/io.rst b/docs/reference/code_ref/io.rst index e35fe6cf..1c44192f 100644 --- a/docs/reference/code_ref/io.rst +++ b/docs/reference/code_ref/io.rst @@ -1,7 +1,3 @@ -fiasco io -========= -Parsers for raw CHIANTI atomic data. fiasco uses these parsers to transform the raw ASCII files into an HDF5 version of the CHIANTI database. - .. automodapi:: fiasco.io .. automodapi:: fiasco.io.sources diff --git a/docs/reference/code_ref/util.rst b/docs/reference/code_ref/util.rst index f92bdb80..9e078cf3 100644 --- a/docs/reference/code_ref/util.rst +++ b/docs/reference/code_ref/util.rst @@ -1,5 +1 @@ -fiasco util -=========== - .. automodapi:: fiasco.util - :no-heading: diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 0c0eec1c..8159d780 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -1,17 +1,18 @@ .. _fiasco-reference: Reference -========== +========= .. _fiasco-reference-api: API ----- +--- .. toctree:: :maxdepth: 1 code_ref/fiasco + code_ref/base code_ref/io code_ref/util diff --git a/docs/references.bib b/docs/references.bib index fb76e507..95826cb6 100644 --- a/docs/references.bib +++ b/docs/references.bib @@ -434,7 +434,8 @@ @techreport{dere_chianti_2017 month = dec, number = {8}, urldate = {2022-08-21}, - langid = {english} + langid = {english}, + institution = {None} } @techreport{young_chianti_2019, @@ -443,5 +444,6 @@ @techreport{young_chianti_2019 year = {2019}, month = feb, number = {24}, - urldate = {2024-01-09} + urldate = {2024-01-09}, + institution = {None} } diff --git a/docs/topic_guides/index.rst b/docs/topic_guides/index.rst index 40d6ecde..f7f94c99 100644 --- a/docs/topic_guides/index.rst +++ b/docs/topic_guides/index.rst @@ -1,7 +1,7 @@ .. _fiasco-topic-guide: Topic Guides -============= +============ .. toctree:: :maxdepth: 1 diff --git a/examples/user_guide/ion_collection.py b/examples/user_guide/ion_collection.py index 705802a5..538ecde1 100644 --- a/examples/user_guide/ion_collection.py +++ b/examples/user_guide/ion_collection.py @@ -1,6 +1,6 @@ """ -Creating an `IonCollection` -=========================== +Creating an ``IonCollection`` +============================= This example shows how to create an `~fiasco.IonCollection` object. """ diff --git a/examples/user_guide/ion_metadata.py b/examples/user_guide/ion_metadata.py index 7ca2e216..f1c38018 100644 --- a/examples/user_guide/ion_metadata.py +++ b/examples/user_guide/ion_metadata.py @@ -1,6 +1,6 @@ """ -Creating an `Ion` -================== +Creating an ``Ion`` +=================== This example shows how to create a `~fiasco.Ion` object and access various pieces of metadata. diff --git a/examples/user_guide/plot_contribution_function.py b/examples/user_guide/plot_contribution_function.py index a7a1c9c3..9cc57cf5 100644 --- a/examples/user_guide/plot_contribution_function.py +++ b/examples/user_guide/plot_contribution_function.py @@ -16,7 +16,7 @@ quantity_support() ############################################################################### -# Specify the plasma properties; note that an `Ion` has to be created with a +# Specify the plasma properties; note that an `~fiasco.Ion` has to be created with a # range of temperatures, but the density is only used later in the contribution # function calculation. Te = np.geomspace(0.1, 100, 51) * u.MK diff --git a/examples/user_guide/plot_ioneq.py b/examples/user_guide/plot_ioneq.py index 727d5e72..905183b3 100644 --- a/examples/user_guide/plot_ioneq.py +++ b/examples/user_guide/plot_ioneq.py @@ -41,9 +41,9 @@ ################################################ # The CHIANTI database also includes tabulated ionization equilibria for -# all ions in the database. The `ioneq` attribute on each +# all ions in the database. The ``ioneq`` attribute on each # `~fiasco.Ion` object returns the tabulated population -# fractions interpolated onto the `temperature` array. +# fractions interpolated onto the ``temperature`` array. # Note that these population fractions returned by `~fiasco.Ion.ioneq` are # stored in the CHIANTI database and therefore are set to NaN # for temperatures outside of the tabulated temperature data given in CHIANTI. diff --git a/fiasco/__init__.py b/fiasco/__init__.py index 553ee06d..86fbff7d 100644 --- a/fiasco/__init__.py +++ b/fiasco/__init__.py @@ -1,11 +1,11 @@ """ -A Python interface to the CHIANTI atomic database +fiasco: A Python interface to the CHIANTI atomic database """ -from fiasco.collections import * -from fiasco.elements import * -from fiasco.fiasco import * -from fiasco.ions import * -from fiasco.levels import * +from fiasco.collections import IonCollection +from fiasco.elements import Element +from fiasco.fiasco import list_elements, list_ions, proton_electron_ratio +from fiasco.ions import Ion +from fiasco.levels import Level, Transitions from fiasco.util.util import setup_paths try: @@ -19,3 +19,5 @@ from fiasco.util.logger import _init_log log = _init_log() + +__all__ = ["IonCollection", "Element", "list_elements", "list_ions", "proton_electron_ratio", "Ion", "Level", "Transitions", "defaults", "log", "__version__"] diff --git a/fiasco/base.py b/fiasco/base.py index 34699b3e..748215a3 100644 --- a/fiasco/base.py +++ b/fiasco/base.py @@ -1,5 +1,7 @@ """ -Base classes for access to CHIANTI ion data +Base classes for access to CHIANTI ion data. + +These classes are not meant to be instantiated directly by the user. """ import plasmapy.particles @@ -13,7 +15,7 @@ from fiasco.util import check_database, parse_ion_name from fiasco.util.exceptions import MissingIonError -__all__ = ['IonBase', 'ContinuumBase'] +__all__ = ['Base', 'IonBase', 'ContinuumBase'] class Base: @@ -23,9 +25,12 @@ class Base: Parameters ---------- ion_name : str + Name of ion, e.g. for Fe V, 'Fe 5', 'iron 5', 'Fe 4+' hdf5_dbase_root : str, optional + Path to the root of the CHIANTI HDF5 database. + If not provided, the default path is used. kwargs : - kwargs are passed to `check_database`. + Further keyword arguments are passed to `fiasco.util.check_database`. """ def __init__(self, ion_name, hdf5_dbase_root=None, **kwargs): @@ -129,17 +134,15 @@ class IonBase(Base): """ Base class for accessing CHIANTI data attached to a particular ion - .. note:: This is not meant to be instantiated directly by the user - and primarily serves as a base class for `~fiasco.Ion`. + .. note:: + This is not meant to be instantiated directly by the user + and primarily serves as a base class for `~fiasco.Ion`. Parameters ---------- ion_name : `str` Name of ion, e.g. for Fe V, 'Fe 5', 'iron 5', 'Fe 4+' hdf5_path : `str`, optional - - Examples - -------- """ @property diff --git a/fiasco/collections.py b/fiasco/collections.py index b63485dd..d123e18a 100644 --- a/fiasco/collections.py +++ b/fiasco/collections.py @@ -17,14 +17,11 @@ class IonCollection: """ - Container for holding multiple ions. Instantiate with ions, elements, or another - ion collection. - - Examples - -------- + Container for holding multiple ions. + Instantiate with ions, elements, or another ion collection. """ - def __init__(self, *args, **kwargs): + def __init__(self, *args): # Import here to avoid circular imports from fiasco import log self.log = log @@ -48,7 +45,7 @@ def __getitem__(self, value): return IonCollection(*ions) def __contains__(self, value): - if isinstance(value, (str, tuple)): + if isinstance(value, (str, tuple)): # NOQA: UP038 pair = parse_ion_name(value) elif isinstance(value, fiasco.Ion): pair = value._base_rep @@ -315,8 +312,8 @@ def radiative_loss(self, density: u.cm**(-3), **kwargs): for ion in self: try: g = ion.contribution_function(density, **kwargs) - except MissingDatasetException: - # TODO: log the mission ion + except MissingDatasetException as e: + self.log.warning(f'{ion.ion_name} failed to be added to the contribution function: {e}') continue rad_loss += g.sum(axis=2) diff --git a/fiasco/elements.py b/fiasco/elements.py index 890d400b..8ee41989 100644 --- a/fiasco/elements.py +++ b/fiasco/elements.py @@ -116,7 +116,7 @@ def equilibrium_ionization(self): return u.Quantity(ioneq) def __getitem__(self, value): - if isinstance(value, (str, tuple)): + if isinstance(value, (str, tuple)): # NOQA: UP038 _, value = parse_ion_name(value) value -= 1 return super().__getitem__(value) diff --git a/fiasco/fiasco.py b/fiasco/fiasco.py index 1eb0ed18..a8ff87ae 100644 --- a/fiasco/fiasco.py +++ b/fiasco/fiasco.py @@ -1,5 +1,5 @@ """ -Package-level functions +Package-level functions. """ import astropy.units as u import numpy as np @@ -22,7 +22,7 @@ def list_elements(hdf5_dbase_root=None, sort=True): Parameters ---------- hdf5_dbase_root: path-like, optional - If not specified, will default to that specified in `fiasco.defaults`. + If not specified, will default to that specified in ``fiasco.defaults``. sort: `bool`, optional If True, sort the list of elements by increasing atomic number. """ @@ -47,7 +47,7 @@ def list_ions(hdf5_dbase_root=None, sort=True): Parameters ---------- hdf5_dbase_root: path-like, optional - If not specified, will default to that specified in `fiasco.defaults`. + If not specified, will default to that specified in ``fiasco.defaults``. sort: `bool`, optional If True, sort the list of elements by increasing atomic number. """ diff --git a/fiasco/io/__init__.py b/fiasco/io/__init__.py index 83adc8de..dc71ae2d 100644 --- a/fiasco/io/__init__.py +++ b/fiasco/io/__init__.py @@ -1,6 +1,9 @@ """ -I/O utilities for reading and building CHIANTI data files +Parsers for raw CHIANTI atomic data. +fiasco uses these parsers to transform the raw ASCII files into an HDF5 version of the CHIANTI database. """ -from fiasco.io.datalayer import * +from fiasco.io.datalayer import DataIndexer, DataIndexerHDF5 from fiasco.io.factory import Parser from fiasco.io.generic import GenericIonParser, GenericParser + +__all__ = ["DataIndexer", "DataIndexerHDF5", "Parser", "GenericIonParser", "GenericParser"] diff --git a/fiasco/io/datalayer.py b/fiasco/io/datalayer.py index 26c17550..5da279ce 100644 --- a/fiasco/io/datalayer.py +++ b/fiasco/io/datalayer.py @@ -1,5 +1,5 @@ """ -Access layer for interfacing with CHIANTI stored in HDF5 +Access layer for interfacing with CHIANTI stored in HDF5. """ import astropy.units as u import h5py @@ -10,14 +10,14 @@ from fiasco.util.exceptions import MissingDatabaseError -__all__ = ['DataIndexer'] +__all__ = ['DataIndexer', 'DataIndexerHDF5'] class DataIndexer: """ Data access layer for each distinct CHIANTI dataset - Acts as an interface layer between `Ion` and the CHIANTI data. All data that the user interacts + Acts as an interface layer between `~fiasco.Ion` and the CHIANTI data. All data that the user interacts with passes through this layer. .. warning:: This object is not meant to be instantiated directly by the user. Rather, instances @@ -58,7 +58,7 @@ def create_indexer(cls, hdf5_path, top_level_path): """ Create an instance as long as the dataset exists. This class method exists so that None can be returned if the dataset specified by - `top_level_path` does not exist. + ``top_level_path`` does not exist. """ hdf5_path = pathlib.Path(hdf5_path) if not hdf5_path.is_file(): diff --git a/fiasco/io/factory.py b/fiasco/io/factory.py index 385e8c65..d9c69d8f 100644 --- a/fiasco/io/factory.py +++ b/fiasco/io/factory.py @@ -1,11 +1,12 @@ """ -Factory and interface for file parser +Factory and interface for file parser. """ import pathlib from fiasco.io.generic import GenericParser from fiasco.io.sources import * +__all__ = ['Parser'] class ParserFactory(type): """ @@ -33,8 +34,7 @@ def __call__(cls, *args, **kwargs): filetype_name = path.stem filetype_ext = path.suffix[1:] - subclass_dict = {c.filetype: c for c in all_subclasses(GenericParser) - if hasattr(c, 'filetype')} + subclass_dict = {c.filetype: c for c in all_subclasses(GenericParser) if hasattr(c, 'filetype')} if filetype_ext in subclass_dict: return subclass_dict[filetype_ext](*args, **kwargs) elif filetype_name in subclass_dict: @@ -58,9 +58,6 @@ class Parser(GenericParser, metaclass=ParserFactory): The Parser ingests the name of a raw ASCII data file and builds an `astropy.table.QTable` from it. A predefined parser is created based on the file extension, but a custom parser can also be used. - - Examples - -------- """ - def __init__(self, filename, custom_parser=None, **kwargs): + def __init__(self, filename, **kwargs): super().__init__(filename, **kwargs) diff --git a/fiasco/io/generic.py b/fiasco/io/generic.py index e271eed8..7695f4a6 100644 --- a/fiasco/io/generic.py +++ b/fiasco/io/generic.py @@ -1,5 +1,5 @@ """ -Base class for file parser +Base class for file parser. """ import astropy.units as u import h5py @@ -15,6 +15,7 @@ from fiasco.util.exceptions import MissingASCIIFileError from fiasco.util.util import read_chianti_version +__all__ = ['GenericParser', 'GenericIonParser'] class GenericParser: """ diff --git a/fiasco/io/sources/__init__.py b/fiasco/io/sources/__init__.py index eaa8dc46..40007069 100644 --- a/fiasco/io/sources/__init__.py +++ b/fiasco/io/sources/__init__.py @@ -1,5 +1,5 @@ """ -Classes for parsing CHIANTI data sources +Classes for parsing CHIANTI data sources. """ from fiasco.io.sources.continuum_sources import * from fiasco.io.sources.ion_sources import * diff --git a/fiasco/io/sources/continuum_sources.py b/fiasco/io/sources/continuum_sources.py index 80840c90..d120b9f4 100644 --- a/fiasco/io/sources/continuum_sources.py +++ b/fiasco/io/sources/continuum_sources.py @@ -257,10 +257,10 @@ class HSeqParser(GenericParser): Notes ----- - * The parameter :math: `\psi_{\text{norm}}` (called :math: `A_{\text{sum}}` in CHIANTI) is a normalization factor of - the integral of the spectral distribution function :math:`\psi(y)` from 0 to 1, such - that :math: `\frac{1}{\psi_{\text{norm}}} \int_{0}^{1} \psi(y) dy = 2`. This normalization is only used For - hydrogenic ions. + * The parameter :math: `\psi_{\text{norm}}` (called :math: `A_{\text{sum}}` in CHIANTI) is a + normalization factor of the integral of the spectral distribution function :math:`\psi(y)` from 0 to 1, + such that :math: `\frac{1}{\psi_{\text{norm}}} \int_{0}^{1} \psi(y) dy = 2`. + This normalization is only used for hydrogenic ions. """ filetype = 'hseq_2photon' dtypes = [int, float, int, float, float, float] diff --git a/fiasco/ions.py b/fiasco/ions.py index efc90e5d..8cc11b7a 100644 --- a/fiasco/ions.py +++ b/fiasco/ions.py @@ -36,7 +36,7 @@ class Ion(IonBase, ContinuumBase): ---------- ion_name : `str` or `tuple` Name of the ion. This can be either a string denoting the name or a tuple containing the - atomic number and ionization stage. See `~fiasco.util.parse_ion` for a list of all possible + atomic number and ionization stage. See `~fiasco.util.parse_ion_name` for a list of all possible input formats. temperature : `~astropy.units.Quantity` Temperature array over which to evaluate temperature dependent quantities. @@ -1553,7 +1553,7 @@ def two_photon(self, In both cases, the energy of the two photons emitted equals the energy difference of the two levels. As a consequence, no photons can be emitted beneath the rest wavelength for a given transition. - See the introduction of :cite:t:`drake_1986` for a concise description of the process. + See the introduction of :cite:t:`drake_twophoton_1986` for a concise description of the process. The emission is given by diff --git a/fiasco/util/decorators.py b/fiasco/util/decorators.py index 65cc3227..a4b6c01c 100644 --- a/fiasco/util/decorators.py +++ b/fiasco/util/decorators.py @@ -1,5 +1,5 @@ """ -Useful function/method decorators +Useful function/method decorators. """ from functools import wraps diff --git a/fiasco/util/exceptions.py b/fiasco/util/exceptions.py index 8b0814ce..f98921d1 100644 --- a/fiasco/util/exceptions.py +++ b/fiasco/util/exceptions.py @@ -1,7 +1,14 @@ """ -Custom exceptions +Custom exceptions for fiasco. """ +__all__ = [ + 'MissingIonError', + 'MissingDatabaseError', + 'MissingASCIIFileError', + 'MissingDatasetException', + 'UnsupportedVersionError' +] class MissingIonError(Exception): """ diff --git a/fiasco/util/logger.py b/fiasco/util/logger.py index e7823d8c..974cf75d 100644 --- a/fiasco/util/logger.py +++ b/fiasco/util/logger.py @@ -1,7 +1,11 @@ +""" +Logging utilities for fiasco. +""" import logging from astropy.logger import AstropyLogger +__all__ = ['_init_log', '_config_to_loggerConf'] def _init_log(config=None): """ diff --git a/fiasco/util/setup_db.py b/fiasco/util/setup_db.py index e9253382..e44b361e 100644 --- a/fiasco/util/setup_db.py +++ b/fiasco/util/setup_db.py @@ -28,7 +28,7 @@ ] LATEST_VERSION = SUPPORTED_VERSIONS[-1] -__all__ = ['check_database', 'download_dbase', 'get_test_file_list', 'build_hdf5_dbase'] +__all__ = ['check_database', 'check_database_version', 'download_dbase', 'md5hash', 'get_test_file_list', 'build_hdf5_dbase'] def check_database(hdf5_dbase_root, **kwargs): @@ -157,7 +157,7 @@ def build_hdf5_dbase(ascii_dbase_root, hdf5_dbase_root, files=None, check_hash=F Path to HDF5 file files : `list` or `dict`, optional A list of files to update in the HDF5 database. By default, - this is all of the files in `ascii_dbase_root`. + this is all of the files in ``ascii_dbase_root``. check_hash: `bool`, optional If True, check the file hash before adding it to the database. Building the database will fail if any of the hashes is not as expected. diff --git a/fiasco/util/tools.py b/fiasco/util/tools.py index f4c5b9a0..5b8d0684 100644 --- a/fiasco/util/tools.py +++ b/fiasco/util/tools.py @@ -132,10 +132,10 @@ def burgess_tully_descale(x, y, energy_ratio, c, scaling_type): x : `array-like` Scaled temperature. First dimension should have length ``n``, the number of transitions. The second dimension will be the number of spline points, but may - be different for each row. If each row has ``l`` spline points, `x` should - have shape ``(n,l)``. If they are not all equal, `x` will have shape ``(n,)``. + be different for each row. If each row has ``l`` spline points, ``x`` should + have shape ``(n,l)``. If they are not all equal, ``x`` will have shape ``(n,)``. y : `array-like` - Scaled collision strength. Must have the same dimensions as `x`. + Scaled collision strength. Must have the same dimensions as ``x``. energy_ratio : `array-like` Ratio between the thermal energy and that of each transition with shape ``(n,m)``, where ``m`` is the dimension of the temperature array. @@ -148,7 +148,7 @@ def burgess_tully_descale(x, y, energy_ratio, c, scaling_type): Returns ------- upsilon : `array-like` - Descaled collision strength or cross-section with the same shape as `energy_ratio`. + Descaled collision strength or cross-section with the same shape as ``energy_ratio``. """ # NOTE: Arrays with staggered number of columns, which have an 'object' # dtype (denoted by 'O') appear to be 1D, but should not be cast to 2D diff --git a/fiasco/util/util.py b/fiasco/util/util.py index 1ea0badc..7141b601 100644 --- a/fiasco/util/util.py +++ b/fiasco/util/util.py @@ -24,12 +24,12 @@ def parse_ion_name(ion_name): of the following representations of Fe 18, that is, an iron ion with 17 electrons removed and a total charge of +17, will return (26,18): - 1. `'Fe 18'`, `'fe 18'` (atomic symbol and ionization stage) - 2. `'Fe 17+'` (atomic symbol and charge state) - 3. `'Iron 18'`, `'iron 18'` (element name and ionization stage) - 4. `'Fe XVIII'`, `'fe xviii'` (atomic symbol and ionization stage in spectroscopic notation) - 5. `'26 18'` (atomic number and ionization stage) - 6. `(26, 18)` (tuple of any combination of the above) + 1. ``'Fe 18'`, ``'fe 18'`` (atomic symbol and ionization stage) + 2. ``'Fe 17+'`` (atomic symbol and charge state) + 3. ``'Iron 18'``, ``'iron 18'`` (element name and ionization stage) + 4. ``'Fe XVIII'``, ``'fe xviii'`` (atomic symbol and ionization stage in spectroscopic notation) + 5. ``'26 18'`` (atomic number and ionization stage) + 6. ``(26, 18)`` (tuple of any combination of the above) """ if isinstance(ion_name, tuple): element, ion = ion_name @@ -158,7 +158,8 @@ def query_yes_no(question, default="yes"): elif default == "no": prompt = " [y/N] " else: - raise ValueError("invalid default answer: '%s'" % default) + msg = f"invalid default answer: {default}" + raise ValueError(msg) while True: sys.stdout.write(question + prompt) diff --git a/pyproject.toml b/pyproject.toml index e2d31cb3..f39f93ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,9 +19,9 @@ classifiers = [ "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Scientific/Engineering :: Physics", "Topic :: Scientific/Engineering :: Astronomy", @@ -35,12 +35,12 @@ keywords = [ "atomic data", "atomic physics", ] -requires-python = ">=3.9" +requires-python = ">=3.10" dependencies = [ "astropy", - "numpy", - "h5py", "fortranformat", + "h5py", + "numpy", "plasmapy", ] @@ -54,12 +54,12 @@ Issues = "https://github.com/wtbarnes/fiasco/issues/" all = ["fiasco"] test = [ "fiasco[all]", - "asdf", "asdf-astropy", - "pytest", + "asdf", + "matplotlib", "pytest-astropy", "pytest-cov", - "matplotlib", + "pytest", ] test_idl = [ "fiasco[test]", @@ -67,16 +67,16 @@ test_idl = [ ] docs =[ "fiasco[all]", - "sphinx>=4.2", - "sphinx-automodapi", - "sphinx-gallery", "aiapy", + "asdf-astropy", + "asdf", + "hissw", "pydata-sphinx-theme", + "sphinx-automodapi", "sphinx-design", + "sphinx-gallery", + "sphinx", "sphinxcontrib-bibtex", - "hissw", - "asdf", - "asdf-astropy", ] dev = ["fiasco[test,docs]"] @@ -90,6 +90,10 @@ testpaths = [ "fiasco", "docs" ] +norecursedirs = [ + "docs/_build", + "docs/generated", +] doctest_plus = "enabled" text_file_format = "rst" addopts = "--doctest-rst" @@ -103,6 +107,8 @@ filterwarnings =[ "ignore:\\nPyarrow will become a required dependency of pandas in the next major release of pandas:DeprecationWarning", # Files created with previous versions of asdf-astropy issue a warning if you have a different version installed "ignore::asdf.exceptions.AsdfWarning", + # Python 3.14 issue with extracting tarball + "ignore:.*filter extracted tar archives and reject files or modify their metadata.*:DeprecationWarning", ] [tool.coverage.run] @@ -131,41 +137,40 @@ skip = "*.fts,*.fits,venv,*.pro,*.bib,*.asdf,*.json" ignore-words-list = "te,emiss" [tool.ruff] -target-version = "py39" +target-version = "py310" line-length = 110 exclude=[ - ".git,", - "__pycache__", - "build", - "fiasco/version.py", + ".git,", + "__pycache__", + "build", + "fiasco/version.py", ] show-fixes = true -show-source = true - -select = [ - "E", - "F", - "W", - "UP", - "PT", - #"RET", - #"TID", - +output-format = "full" + +lint.select = [ + "E", + "F", + "W", + "UP", + "PT", + #"RET", + #"TID", ] -extend-ignore = [ - # pycodestyle (E, W) - "E501", # LineTooLong # TODO! fix - "E741", # Ambiguous variable name - - # pytest (PT) - "PT001", # Always use pytest.fixture() - "PT004", # Fixtures which don't return anything should have leading _ - "PT007", # Parametrize should be lists of tuples # TODO! fix - "PT011", # Too broad exception assert # TODO! fix - "PT023", # Always use () on pytest decorators +lint.extend-ignore = [ + # pycodestyle (E, W) + "E501", # LineTooLong # TODO! fix + "E741", # Ambiguous variable name + + # pytest (PT) + "PT001", # Always use pytest.fixture() + "PT004", # Fixtures which don't return anything should have leading _ + "PT007", # Parametrize should be lists of tuples # TODO! fix + "PT011", # Too broad exception assert # TODO! fix + "PT023", # Always use () on pytest decorators ] -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] # Part of configuration, not a package. "setup.py" = ["INP001"] "conftest.py" = ["INP001"] @@ -174,9 +179,9 @@ extend-ignore = [ # Module level imports do not need to be at the top of a file here "docs/conf.py" = ["E402"] "__init__.py" = ["E402", "F401", "F403"] -"test_*.py" = ["B011", "D", "E402", "PGH001", "S101"] +"test_*.py" = ["B011", "D", "E402", "S101"] # Allow star import for the factory "fiasco/io/factory.py" = ["F403"] -[tool.ruff.pydocstyle] +[tool.ruff.lint.pydocstyle] convention = "numpy" diff --git a/tox.ini b/tox.ini index 0ce5b450..a84d409c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,68 +1,58 @@ [tox] +min_version = 4.0 +requires = + tox-pypi-filter >= 0.14 envlist = - py{39,310,311} - build_docs + py{310,311,312} + build_docs{,-gallery} codestyle -isolated_build = true -requires = - setuptools >= 30.3.0 - pip >= 19.3.1 - tox-pypi-filter >= 0.10 [testenv] - -# The following option combined with the use of the tox-pypi-filter above allows -# project-wide pinning of dependencies, e.g. if new versions of pytest do not -# work correctly with pytest-astropy plugins. Most of the time the pinnings file -# should be empty. pypi_filter_requirements = https://raw.githubusercontent.com/sunpy/sunpy/main/.test_package_pins.txt - -# Pass through the following environment variables which may be needed for the CI -passenv = - HOME - WINDIR - LC_ALL - LC_CTYPE - CC - CI - TRAVIS - -# Run the tests in a temporary directory to make sure that we don't import -# the package from the source tree +# Run the tests in a temporary directory to make sure that we don't import the source tree changedir = .tmp/{envname} - -# tox environments are constructed with so-called 'factors' (or terms) -# separated by hyphens, e.g. test-devdeps-cov. Lines below starting with factor: -# will only take effect if that factor is included in the environment name. To -# see a list of example environments that can be run, along with a description, -# run: -# -# tox -l -v -# description = run tests - deps = -# The following indicates which extras_require from setup.cfg will be installed extras = test commands = - pytest --pyargs fiasco {toxinidir}/docs --cov fiasco --cov-report=xml {posargs} --showlocals - + pip freeze --all --no-input + pytest \ + -vvv \ + -r fEs \ + --showlocals \ + --pyargs fiasco \ + --cov-report=xml \ + --cov=fiasco \ + {toxinidir}/docs \ + {posargs} [testenv:build_docs] changedir = docs description = invoke sphinx-build to build the HTML docs extras = docs commands = - sphinx-build -b html . _build/html {posargs} - - -[testenv:pre-commit] + pip freeze --all --no-input + sphinx-build \ + --color \ + -W \ + --keep-going \ + -b html \ + -d _build/.doctrees \ + . \ + _build/html \ + gallery: -D plot_gallery=1 \ + !gallery: -D plot_gallery=0 \ + {posargs} + python -c 'import pathlib; print("Documentation available under file://\{0\}".format(pathlib.Path(r"{toxinidir}") / "docs" / "_build" / "index.html"))' + +[testenv:codestyle] +pypi_filter = skip_install = true description = Run all style and file checks with pre-commit deps = pre-commit commands = pre-commit install-hooks - pre-commit run --all-files + pre-commit run --color always --all-files --show-diff-on-failure