From 94e837fbc86405c4f5293a2a28cf5076ea20bc16 Mon Sep 17 00:00:00 2001 From: harshitha1201 Date: Mon, 3 Mar 2025 19:15:40 +0000 Subject: [PATCH] minute change --- .github/workflows/bridge.yml | 37 +-- .../workflows/carsus-data-repos-config.yml | 22 +- .github/workflows/codestyle.yml | 32 +- .github/workflows/docs-build.yml | 2 +- .github/workflows/save_atomic_files.yml | 10 +- .github/workflows/tests.yml | 16 +- .pre-commit-config.yaml | 13 + README.rst | 2 +- carsus/__init__.py | 7 +- carsus/_astropy_init.py | 5 +- carsus/base.py | 2 +- carsus/data/cmfgen_config.yml | 35 ++- carsus/data/knox_long_recombination_zeta.dat | 13 +- carsus/io/base.py | 7 +- carsus/io/chianti_/__init__.py | 3 +- carsus/io/chianti_/chianti_.py | 281 ++++++++++-------- carsus/io/cmfgen/__init__.py | 16 +- carsus/io/cmfgen/base.py | 35 +-- carsus/io/cmfgen/hdfgen.py | 19 +- carsus/io/cmfgen/util.py | 158 ++++++---- carsus/io/kurucz/__init__.py | 2 +- carsus/io/molecules/molecules.py | 1 - carsus/io/nist/__init__.py | 16 +- carsus/io/nist/ionization_grammar.py | 28 +- carsus/io/nist/weightscomp.py | 2 +- carsus/io/nist/weightscomp_grammar.py | 182 ++++++++---- carsus/io/nndc/__init__.py | 8 +- carsus/io/nndc/ensdf.py | 46 ++- carsus/io/nndc/parsers.py | 34 +-- carsus/io/nuclear/__init__.py | 2 +- carsus/io/nuclear/nndc.py | 40 ++- carsus/io/output/__init__.py | 2 +- carsus/io/output/base.py | 89 ++++-- carsus/io/output/collisions.py | 30 +- carsus/io/output/ionization_energies.py | 3 +- carsus/io/output/levels_lines.py | 32 +- carsus/io/output/macro_atom.py | 6 +- carsus/io/output/photo_ionization.py | 15 +- carsus/io/tests/conftest.py | 10 +- carsus/io/tests/test_base.py | 28 +- carsus/io/tests/test_chianti.py | 1 - carsus/io/tests/test_gfall.py | 114 ++++--- carsus/io/tests/test_ionization.py | 80 ++--- carsus/io/tests/test_ionization_grammar.py | 68 ++--- carsus/io/tests/test_nist_weightscomp.py | 39 ++- .../io/tests/test_nist_weightscomp_grammar.py | 193 +++++++----- carsus/io/tests/test_nndc.py | 178 +++++++---- carsus/io/tests/test_util.py | 30 +- carsus/io/tests/test_zeta.py | 6 +- carsus/io/util.py | 28 +- carsus/io/vald/__init__.py | 2 +- carsus/io/zeta.py | 14 +- carsus/tests/data/gftest.all | 2 +- .../data/knox_long_recombination_zeta.dat | 13 +- carsus/tests/data/nndc/052_6.csv | 2 +- carsus/tests/data/nndc/052_7.csv | 2 +- carsus/tests/data/nndc/052_8.csv | 2 +- carsus/tests/data/nndc/056_8.csv | 2 +- carsus/util/__init__.py | 15 +- carsus/util/colored_logger.py | 42 +-- carsus/util/hash.py | 5 +- carsus/util/helpers.py | 42 +-- carsus/util/selected.py | 46 ++- carsus/util/tests/test_helpers.py | 36 +-- carsus/util/tests/test_selected.py | 128 ++++---- docs/_templates/autosummary/base.rst | 2 +- docs/_templates/autosummary/module.rst | 2 +- docs/conf.py | 100 ++++--- docs/development/index.rst | 2 +- docs/development/testing.rst | 9 +- docs/development/units.rst | 4 +- docs/installation.rst | 2 +- docs/physics/einstein_coeff.rst | 2 +- docs/publications.bib | 4 +- docs/reference/level_index_id_number.rst | 8 +- docs/reference/notation.rst | 10 +- setup.py | 15 +- 77 files changed, 1515 insertions(+), 1026 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.github/workflows/bridge.yml b/.github/workflows/bridge.yml index 5d3a2666f..3f248aa44 100644 --- a/.github/workflows/bridge.yml +++ b/.github/workflows/bridge.yml @@ -1,13 +1,13 @@ name: tardis-carsus-bridge -on: +on: workflow_call: # to be called by save_atomic_files workflow outputs: ref-check-trig: value: ${{ jobs.tardis-build.outputs.step-ref-check-trig}} workflow_dispatch: # for running manually - + env: XUVTOP: /tmp/chianti CHIANTI_DL_URL: https://download.chiantidatabase.org @@ -17,9 +17,9 @@ env: CMFGEN_DB_VER: atomic_data_15nov16.tar.gz # original reference data REF1_PATH: ${{ github.workspace }}/tardis-refdata/unit_test_data_org.h5 - # generated reference data using the new atomic file + # generated reference data using the new atomic file REF2_PATH: ${{ github.workspace }}/tardis-refdata/unit_test_data.h5 - PYTEST_FLAGS: --tardis-refdata=${{ github.workspace }}/tardis-refdata + PYTEST_FLAGS: --tardis-refdata=${{ github.workspace }}/tardis-refdata --generate-reference CACHE_NUMBER: 1 @@ -34,7 +34,7 @@ jobs: - uses: actions/checkout@v2 with: path: carsus/ - + - uses: actions/cache@v3 with: path: ${{ env.XUVTOP }} @@ -47,7 +47,7 @@ jobs: wget -q ${{ env.CHIANTI_DL_URL }}/${{ env.CHIANTI_DB_VER }} -O ${{ env.XUVTOP }}/chianti.tar.gz tar -zxf ${{ env.XUVTOP }}/chianti.tar.gz -C ${{ env.XUVTOP }} if: steps.chianti-cache.outputs.cache-hit != 'true' - + - uses: actions/cache@v3 with: path: /tmp/atomic @@ -65,7 +65,7 @@ jobs: environment-file: conda-lock.yml init-shell: bash cache-environment: true - cache-downloads: true + cache-downloads: true environment-name: carsus - name: Install package @@ -76,13 +76,13 @@ jobs: jupyter nbconvert ${{ env.NBCONVERT_FLAGS }} carsus/docs/tardis_atomdata_ref.ipynb env: CARSUS_REFDATA: ${{ github.workspace }}/carsus-refdata - + - name: Upload Atom Data uses: actions/upload-artifact@v2 with: name: atom-data path: carsus/docs/kurucz_cd23_chianti_H_He.h5 - + tardis-build: needs: carsus-build runs-on: ubuntu-latest @@ -93,10 +93,10 @@ jobs: - uses: actions/checkout@v2 with: repository: tardis-sn/tardis - + - name: Download refdata_compare notebook run: wget https://raw.githubusercontent.com/tardis-sn/tardis-refdata/master/notebooks/ref_data_compare_from_paths.ipynb - + - name: Clone tardis-sn/tardis-refdata uses: actions/checkout@v2 with: @@ -109,6 +109,7 @@ jobs: - name: Restore LFS cache uses: actions/cache@v3 + id: lfs-cache with: path: tardis-refdata/.git/lfs key: ${{ runner.os }}-lfs-${{ hashFiles('tardis-refdata/.lfs-assets-id') }}-v${{ env.CACHE_NUMBER }} @@ -139,18 +140,18 @@ jobs: - name: Install package run: pip install -e . - + - name: Download atom data uses: actions/download-artifact@v4.1.7 with: name: atom-data path: carsus/docs/kurucz_cd23_chianti_H_He.h5 - + - name: Replace Atomic Data run: cp carsus/docs/kurucz_cd23_chianti_H_He.h5/kurucz_cd23_chianti_H_He.h5 tardis-refdata/atom_data/kurucz_cd23_chianti_H_He.h5 - + - name: Generate Reference Data Copy - run: cp tardis-refdata/unit_test_data.h5 tardis-refdata/unit_test_data_org.h5 + run: cp tardis-refdata/unit_test_data.h5 tardis-refdata/unit_test_data_org.h5 - name: Generate New Reference Data run: pytest tardis ${{ env.PYTEST_FLAGS }} @@ -158,13 +159,13 @@ jobs: - name: Compare Reference Data run: | jupyter nbconvert ${{ env.NBCONVERT_FLAGS }} ref_data_compare_from_paths.ipynb - + - name: Upload comparison notebook uses: actions/upload-artifact@v2 with: name: Comparison Notebook path: ref_data_compare_from_paths.html - + - name: Check if reference data are equal id: ref-equal-check continue-on-error: true @@ -173,7 +174,7 @@ jobs: # continue-on-error check would display a green tick even when the step passes # but is necessary for other steps to run # if the workflow was called by save-atomic-files workflow(the only other option) - # and refdata comparison failed this workflow would pass but the + # and refdata comparison failed this workflow would pass but the # bridge-ref-check job in save-atomic-files about refdata comparison would fail - name: Fail the workflow if reference data check fails if: github.event_name == 'workflow_dispatch' && steps.ref-equal-check.outcome == 'failure' diff --git a/.github/workflows/carsus-data-repos-config.yml b/.github/workflows/carsus-data-repos-config.yml index 14de0b9c3..0b9bc5dcf 100644 --- a/.github/workflows/carsus-data-repos-config.yml +++ b/.github/workflows/carsus-data-repos-config.yml @@ -1,8 +1,8 @@ name: carsus-data-repos-config -on: - workflow_dispatch: - +on: + workflow_dispatch: + env: NBCONVERT_FLAGS: --execute --ExecutePreprocessor.timeout=600 --to html @@ -35,14 +35,14 @@ jobs: with: path: carsus-data-nist key: ${{ runner.os }}-cache-${{ hashFiles('carsus-data-nist/**/*.lock') }} - + - run: | for repo in carsus-data-cmfgen carsus-data-kurucz carsus-data-nist; do repo_path="$repo" - + if [ ! -d $repo_path ] ; then - git clone https://github.com/tardis-sn/$repo $repo_path - else + git clone https://github.com/tardis-sn/$repo $repo_path + else cd $repo_path git fetch git checkout origin/main @@ -55,20 +55,20 @@ jobs: environment-file: conda-lock.yml init-shell: bash cache-environment: true - cache-downloads: true + cache-downloads: true environment-name: carsus - name: Install package run: pip install -e carsus/ - + - name: Run notebooks run: | jupyter nbconvert ${{ env.NBCONVERT_FLAGS }} carsus/docs/tardis_atomdata_ref.ipynb env: CARSUS_REFDATA: ${{ github.workspace }}/carsus-refdata - + - name: Upload Atom Data uses: actions/upload-artifact@v3 with: name: atom-data - path: carsus/docs/kurucz_cd23_cmfgen_H_Si.h5 \ No newline at end of file + path: carsus/docs/kurucz_cd23_cmfgen_H_Si.h5 diff --git a/.github/workflows/codestyle.yml b/.github/workflows/codestyle.yml index c2fc88bfc..0aa970123 100644 --- a/.github/workflows/codestyle.yml +++ b/.github/workflows/codestyle.yml @@ -14,7 +14,7 @@ defaults: shell: bash -l {0} jobs: - ruff: + ruff: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -31,13 +31,13 @@ jobs: - name: Download Lock File run: wget -q https://raw.githubusercontent.com/tardis-sn/tardis/master/conda-linux-64.lock - + - name: Generate Cache Key - run: | + run: | file_hash=$(cat conda-linux-64.lock | shasum -a 256 | cut -d' ' -f1) echo "file_hash=$file_hash" >> "${GITHUB_OUTPUT}" id: cache-environment-key - + - uses: mamba-org/setup-micromamba@v1 with: environment-file: conda-linux-64.lock @@ -46,11 +46,11 @@ jobs: environment-name: tardis cache-environment: true cache-downloads: true - + - name: Show statistics pull request if: github.event_name == 'pull_request_target' run: ruff check --statistics --show-fixes $(git --no-pager diff --name-only origin/master HEAD --) | tee ruff_stats.txt - + - name: Show entire output pull request if: github.event_name == 'pull_request_target' run: ruff check --output-format=concise $(git --no-pager diff --name-only origin/master HEAD --) | tee ruff_full.txt @@ -58,7 +58,7 @@ jobs: - name: Show statistics push if: github.event_name == 'push' run: ruff check --statistics --show-fixes . | tee ruff_stats.txt - + - name: Show entire output push if: github.event_name == 'push' run: ruff check --output-format=concise . | tee ruff_full.txt @@ -70,11 +70,11 @@ jobs: path: ruff_stats.txt - name: Entire output read - id: ruff_complete + id: ruff_complete uses: juliangruber/read-file-action@v1.0.0 with: path: ruff_full.txt - + - name: Find Comment if: always() && github.event_name == 'pull_request_target' uses: peter-evans/find-comment@v1 @@ -82,7 +82,7 @@ jobs: with: issue-number: ${{ github.event.number }} body-includes: I ran ruff on the latest commit - + - name: Post comment if: github.event_name == 'pull_request_target' uses: peter-evans/create-or-update-comment@v2 @@ -93,10 +93,10 @@ jobs: edit-mode: replace body: | *\*beep\* \*bop\** - Hi human, - I ran ruff on the latest commit (${{ github.event.pull_request.head.sha }}). + Hi human, + I ran ruff on the latest commit (${{ github.event.pull_request.head.sha }}). Here are the outputs produced. - Results can also be downloaded as artifacts [**here**](${{ env.URL }}). + Results can also be downloaded as artifacts [**here**](${{ env.URL }}). Summarised output:
@@ -112,11 +112,11 @@ jobs: ```diff ${{ steps.ruff_complete.outputs.content }} ``` - +
env: URL: https://github.com/${{ github.repository_owner }}/${{ github.event.repository.name }}/actions/runs/${{ github.run_id }}?check_suite_focus=true - + - name: Save results artifact uses: actions/upload-artifact@v4 if: always() @@ -124,4 +124,4 @@ jobs: name: ruff-results path: | ruff_full.txt - ruff_stats.txt \ No newline at end of file + ruff_stats.txt diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml index d096ab784..e2cb970a8 100644 --- a/.github/workflows/docs-build.yml +++ b/.github/workflows/docs-build.yml @@ -117,7 +117,7 @@ jobs: environment-file: conda-lock.yml init-shell: bash cache-environment: true - cache-downloads: true + cache-downloads: true environment-name: carsus - name: Install package diff --git a/.github/workflows/save_atomic_files.yml b/.github/workflows/save_atomic_files.yml index 2d5c1f604..a1e7f80d7 100644 --- a/.github/workflows/save_atomic_files.yml +++ b/.github/workflows/save_atomic_files.yml @@ -1,6 +1,6 @@ name: save-atomic-files -on: +on: workflow_dispatch: schedule: - cron: '0 0 * * 0' # run at midnight every Sunday @@ -8,7 +8,7 @@ on: jobs: create-artifacts: uses: ./.github/workflows/bridge.yml - + zip_artifacts: needs: create-artifacts runs-on: ubuntu-latest @@ -34,13 +34,13 @@ jobs: password: ${{ secrets.GH_DROPOFF_PASSWORD }} source: '*_*_*.zip' target: "/storage/github_dropoffs/tardis/" - + bridge-ref-check: needs: create-artifacts runs-on: ubuntu-latest if: needs.create-artifacts.outputs.ref-check-trig == 'failure' # fails if refdata comparison failed in the bridge - # continue-on-error wouldn't put a red cross + # continue-on-error wouldn't put a red cross # even if the bridge passes on refdata compare step steps: - - run: exit 1 \ No newline at end of file + - run: exit 1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0b73eeccc..7eaf1564b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -27,7 +27,7 @@ env: CHIANTI_DL_URL: https://download.chiantidatabase.org CHIANTI_DB_VER: CHIANTI_v9.0.1_database.tar.gz PYTEST_FLAGS: --remote-data --refdata=carsus-refdata - --cov=carsus --cov-report=xml --cov-report=html + --cov=carsus --cov-report=xml --cov-report=html NBCONVERT_CMD: jupyter nbconvert --execute --ExecutePreprocessor.timeout=600 --to html CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} CMFGEN_DL_URL: http://kookaburra.phyast.pitt.edu/hillier/cmfgen_files @@ -70,29 +70,29 @@ jobs: wget -q ${{ env.CHIANTI_DL_URL }}/${{ env.CHIANTI_DB_VER }} -O ${{ env.XUVTOP }}/chianti.tar.gz tar -zxf ${{ env.XUVTOP }}/chianti.tar.gz -C ${{ env.XUVTOP }} if: steps.chianti-cache.outputs.cache-hit != 'true' - + - uses: actions/cache@v4 with: path: /tmp/atomic key: ${{ env.CMFGEN_DB_VER }} id: cmfgen-cache - + - name: Download CMFGEN database run: | wget -q -U "Mozilla/4.0" ${{ env.CMFGEN_DL_URL }}/${{ env.CMFGEN_DB_VER }} -O /tmp/atomic.tar.gz tar -zxf /tmp/atomic.tar.gz -C /tmp if: steps.cmfgen-cache.outputs.cache-hit != 'true' - + - name: Download Lock File run: wget -q https://raw.githubusercontent.com/tardis-sn/tardis/master/conda-${{ matrix.label }}.lock if: matrix.pip == true - + - name: Generate Cache Key - run: | + run: | file_hash=$(cat conda-${{ matrix.label}}.lock | shasum -a 256 | cut -d' ' -f1) echo "file_hash=$file_hash" >> "${GITHUB_OUTPUT}" id: cache-environment-key - + - uses: mamba-org/setup-micromamba@v1 with: environment-file: conda-${{ matrix.label }}.lock @@ -106,7 +106,7 @@ jobs: run: pip install -e . - name: Run tests - run: pytest carsus ${{ env.PYTEST_FLAGS }} + run: pytest carsus ${{ env.PYTEST_FLAGS }} - name: Upload to Codecov run: bash <(curl -s https://codecov.io/bash) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..f6231e9f5 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,13 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 # Check for latest version if needed + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - repo: https://github.com/psf/black + rev: 23.12.1 # Use latest stable version + hooks: + - id: black + language_version: python3 diff --git a/README.rst b/README.rst index 5aa54aa4a..81fb0d827 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,7 @@ Carsus ====== -Carsus is a package to manage atomic datasets. It can read data from a variety of +Carsus is a package to manage atomic datasets. It can read data from a variety of sources and output them to file formats readable by radiative transfer codes. .. image:: https://codecov.io/gh/tardis-sn/carsus/branch/master/graph/badge.svg?token=wzEPZc4JYv diff --git a/carsus/__init__.py b/carsus/__init__.py index 61408ae0f..34f28fb11 100644 --- a/carsus/__init__.py +++ b/carsus/__init__.py @@ -3,7 +3,8 @@ # Packages may add whatever they like to this file, but # should keep this content at the top. # ---------------------------------------------------------------------------- -from ._astropy_init import * # noqa +from ._astropy_init import * # noqa + # ---------------------------------------------------------------------------- __all__ = [] @@ -18,14 +19,14 @@ logging.captureWarnings(True) -logger = logging.getLogger('carsus') +logger = logging.getLogger("carsus") logger.setLevel(logging.INFO) console_handler = logging.StreamHandler(sys.stdout) console_formatter = ColoredFormatter(COLOR_FORMAT) console_handler.setFormatter(console_formatter) logger.addHandler(console_handler) -logging.getLogger('py.warnings').addHandler(console_handler) +logging.getLogger("py.warnings").addHandler(console_handler) # Set atomic file format version FORMAT_VERSION = "1.0" diff --git a/carsus/_astropy_init.py b/carsus/_astropy_init.py index bd4532450..fa5b5c769 100644 --- a/carsus/_astropy_init.py +++ b/carsus/_astropy_init.py @@ -1,13 +1,14 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst import os -__all__ = ['__version__', 'test'] +__all__ = ["__version__", "test"] try: from .version import version as __version__ except ImportError: - __version__ = '' + __version__ = "" # Create the test function for self test from astropy.tests.runner import TestRunner + test = TestRunner.make_test_runner_in(os.path.dirname(__file__)) diff --git a/carsus/base.py b/carsus/base.py index 799dd3a6d..4b3ed5a48 100644 --- a/carsus/base.py +++ b/carsus/base.py @@ -4,4 +4,4 @@ logger = logging.getLogger(__name__) -basic_atomic_data_fname = Path(carsus.__path__[0]) / "data" / "basic_atomic_data.csv" \ No newline at end of file +basic_atomic_data_fname = Path(carsus.__path__[0]) / "data" / "basic_atomic_data.csv" diff --git a/carsus/data/cmfgen_config.yml b/carsus/data/cmfgen_config.yml index 7101dcc58..88f184ed6 100644 --- a/carsus/data/cmfgen_config.yml +++ b/carsus/data/cmfgen_config.yml @@ -24,7 +24,7 @@ atom: He: ion_charge: - 0: + 0: date: 15jul15 osc: hei_osc col: heicol.dat @@ -40,21 +40,21 @@ atom: C: ion_charge: - 0: + 0: date: 12dec04 osc: ci_split_osc - col: cicol.dat + col: cicol.dat pho: - phot_smooth_50 - 1: + 1: date: 30oct12 osc: c2osc_rev.dat col: c2col.dat pho: - phot_sm_3000.dat - 2: + 2: date: 23dec04 osc: ciiiosc_st_split_big.dat col: ciiicol.dat @@ -62,23 +62,23 @@ atom: - ciiiphot_sm_a_500.dat - ciiiphot_sm_b_500.dat - 3: + 3: date: 30oct12 osc: civosc_a12_split.dat col: civcol.dat pho: - civphot_a12.dat - + Si: ion_charge: - 0: + 0: date: 23nov11 osc: SiI_OSC col: col_data pho: - SiI_PHOT_DATA - 1: + 1: date: 16sep15 osc: si2_osc_nahar col: si2_col @@ -138,14 +138,14 @@ atom: Fe: ion_charge: - 1: + 1: date: 10sep16 osc: fe2_osc col: fe2_col.dat pho: - phot_op.dat - 2: + 2: date: 30oct12 osc: FeIII_OSC col: col_data.dat @@ -155,7 +155,7 @@ atom: Ca: ion_charge: - 1: + 1: date: 30oct12 osc: ca2_osc_split.dat col: ca2col.dat @@ -164,14 +164,14 @@ atom: O: ion_charge: - 0: + 0: date: 20sep11 osc: oi_osc_mchf col: oi_col pho: - phot_nosm_A - phot_nosm_B - 1: + 1: date: 23mar05 osc: o2osc_fin.dat col: o2col.dat @@ -180,7 +180,7 @@ atom: Ti: ion_charge: - 1: + 1: date: 18oct00 osc: tkii_osc.dat col: col_guess.dat @@ -191,11 +191,11 @@ atom: osc: tkiii_osc.dat col: col_guess.dat pho: - - phot_data.dat + - phot_data.dat Ni: ion_charge: - 1: + 1: date: 30oct12 osc: nkii_osc.dat col: col_data_bautista @@ -207,4 +207,3 @@ atom: col: col_data.dat pho: - phot_data.dat - diff --git a/carsus/data/knox_long_recombination_zeta.dat b/carsus/data/knox_long_recombination_zeta.dat index 748f64890..c7a676978 100644 --- a/carsus/data/knox_long_recombination_zeta.dat +++ b/carsus/data/knox_long_recombination_zeta.dat @@ -1,10 +1,10 @@ #name atomic_number ion_number t02000 t04000 t06000 t08000 t10000 t12000 t14000 t16000 t18000 t20000 t22000 t24000 t26000 t28000 t30000 t32000 t34000 t36000 t38000 t40000 Frac 1 1 0.3390 0.3846 0.4132 0.4338 0.4498 0.4627 0.4735 0.4828 0.4908 0.4978 0.5040 0.5096 0.5147 0.5193 0.5235 0.5273 0.5309 0.5342 0.5373 0.5401 -Frac 1 2 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. +Frac 1 2 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. #above is a dummy line (no recombination INTO a state with no electrons) which helps us to read the data Frac 2 1 0.4012 0.4669 0.5073 0.5363 0.5587 0.5768 0.5920 0.6050 0.6163 0.6263 0.6352 0.6432 0.6505 0.6571 0.6632 0.6689 0.6741 0.6790 0.6835 0.6878 Frac 2 2 0.2660 0.2993 0.3220 0.3395 0.3538 0.3659 0.3764 0.3856 0.3939 0.4013 0.4081 0.4143 0.4201 0.4254 0.4304 0.4350 0.4394 0.4435 0.4474 0.4511 -Frac 2 3 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. +Frac 2 3 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. #above is a dummy line (no recombination INTO a state with no electrons) which helps us to read the data Frac 3 1 0.2044 0.2549 0.2923 0.3218 0.3459 0.3662 0.3838 0.3992 0.4128 0.4251 0.4362 0.4463 0.4556 0.4641 0.4721 0.4795 0.4864 0.4929 0.4990 0.5047 Frac 3 2 0.2110 0.2429 0.2664 0.2854 0.3013 0.3151 0.3272 0.3381 0.3479 0.3568 0.3651 0.3727 0.3797 0.3864 0.3926 0.3984 0.4039 0.4091 0.4141 0.4188 @@ -24,7 +24,7 @@ Frac 6 3 0.3092 0.3468 0.3719 0.3910 0.4066 0.4197 0.4311 0.4412 0.4501 0.4582 Frac 6 4 0.1645 0.1680 0.1731 0.1787 0.1844 0.1900 0.1955 0.2009 0.2060 0.2110 0.2158 0.2205 0.2249 0.2293 0.2334 0.2375 0.2414 0.2451 0.2488 0.2523 Frac 6 5 0.1481 0.1568 0.1638 0.1700 0.1757 0.1809 0.1858 0.1903 0.1947 0.1988 0.2027 0.2064 0.2099 0.2133 0.2166 0.2198 0.2228 0.2258 0.2286 0.2314 Frac 6 6 0.1951 0.2128 0.2246 0.2339 0.2417 0.2485 0.2546 0.2602 0.2653 0.2700 0.2744 0.2785 0.2824 0.2860 0.2895 0.2929 0.2961 0.2991 0.3021 0.3049 -Frac 6 7 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. +Frac 6 7 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. #above is a dummy line (no recombination INTO a state with no electrons) which helps us to read the data Frac 7 1 0.4497 0.5323 0.5825 0.6179 0.6449 0.6665 0.6844 0.6995 0.7125 0.7240 0.7341 0.7431 0.7513 0.7587 0.7655 0.7717 0.7774 0.7828 0.7877 0.7923 Frac 7 2 0.4377 0.4964 0.5348 0.5634 0.5861 0.6050 0.6211 0.6350 0.6473 0.6583 0.6682 0.6771 0.6853 0.6929 0.6999 0.7063 0.7124 0.7180 0.7234 0.7284 @@ -33,7 +33,7 @@ Frac 7 4 0.2813 0.3061 0.3198 0.3296 0.3374 0.3441 0.3499 0.3552 0.3600 0.3644 Frac 7 5 0.1800 0.1832 0.1866 0.1903 0.1941 0.1980 0.2019 0.2057 0.2094 0.2130 0.2166 0.2201 0.2234 0.2267 0.2299 0.2330 0.2360 0.2390 0.2418 0.2446 Frac 7 6 0.1337 0.1423 0.1488 0.1544 0.1594 0.1640 0.1684 0.1724 0.1762 0.1799 0.1833 0.1866 0.1898 0.1928 0.1957 0.1986 0.2013 0.2039 0.2065 0.2090 Frac 7 7 0.1882 0.2047 0.2156 0.2240 0.2311 0.2373 0.2428 0.2478 0.2524 0.2566 0.2606 0.2643 0.2679 0.2712 0.2744 0.2775 0.2804 0.2832 0.2858 0.2884 -Frac 7 8 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. +Frac 7 8 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. #above is a dummy line (no recombination INTO a state with no electrons) which helps us to read the data Frac 8 1 0.3621 0.4425 0.4939 0.5313 0.5604 0.5840 0.6038 0.6206 0.6353 0.6483 0.6598 0.6701 0.6795 0.6881 0.6960 0.7032 0.7100 0.7162 0.7221 0.7276 Frac 8 2 0.3429 0.4036 0.4449 0.4764 0.5018 0.5231 0.5413 0.5573 0.5714 0.5840 0.5955 0.6059 0.6155 0.6243 0.6325 0.6401 0.6472 0.6539 0.6602 0.6661 @@ -43,7 +43,7 @@ Frac 8 5 0.2627 0.2901 0.3043 0.3141 0.3216 0.3278 0.3332 0.3379 0.3422 0.3462 Frac 8 6 0.1599 0.1653 0.1698 0.1740 0.1782 0.1822 0.1861 0.1899 0.1935 0.1971 0.2006 0.2039 0.2071 0.2103 0.2134 0.2163 0.2192 0.2220 0.2248 0.2275 Frac 8 7 0.1244 0.1329 0.1388 0.1438 0.1482 0.1523 0.1560 0.1596 0.1629 0.1661 0.1691 0.1720 0.1748 0.1774 0.1800 0.1825 0.1849 0.1873 0.1895 0.1917 Frac 8 8 0.1827 0.1987 0.2089 0.2167 0.2233 0.2289 0.2340 0.2386 0.2428 0.2467 0.2503 0.2538 0.2570 0.2601 0.2630 0.2658 0.2685 0.2711 0.2735 0.2759 -Frac 8 9 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. +Frac 8 9 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. #above is a dummy line (no recombination INTO a state with no electrons) which helps us to read the data Frac 9 1 0.6675 0.7112 0.7390 0.7590 0.7745 0.7870 0.7974 0.8064 0.8141 0.8209 0.8269 0.8324 0.8373 0.8418 0.8459 0.8497 0.8532 0.8565 0.8595 0.8623 Frac 9 2 0.0461 0.1164 0.1678 0.2083 0.2418 0.2703 0.2951 0.3169 0.3364 0.3540 0.3700 0.3847 0.3982 0.4108 0.4224 0.4333 0.4435 0.4532 0.4622 0.4708 @@ -64,7 +64,7 @@ Frac 10 7 0.2306 0.2637 0.2802 0.2909 0.2989 0.3051 0.3104 0.3148 0.3188 0.3224 Frac 10 8 0.1462 0.1523 0.1558 0.1589 0.1618 0.1645 0.1672 0.1699 0.1724 0.1749 0.1774 0.1798 0.1821 0.1844 0.1867 0.1889 0.1910 0.1931 0.1952 0.1972 Frac 10 9 0.1170 0.1248 0.1295 0.1332 0.1363 0.1392 0.1419 0.1444 0.1468 0.1491 0.1512 0.1533 0.1554 0.1573 0.1592 0.1611 0.1628 0.1646 0.1663 0.1679 Frac 10 10 0.1725 0.1883 0.1978 0.2049 0.2107 0.2158 0.2202 0.2242 0.2279 0.2313 0.2345 0.2374 0.2403 0.2429 0.2455 0.2479 0.2502 0.2525 0.2547 0.2567 -Frac 10 11 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. +Frac 10 11 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. #above is a dummy line (no recombination INTO a state with no electrons) which helps us to read the data Frac 11 1 0.2564 0.2930 0.3204 0.3419 0.3595 0.3743 0.3871 0.3982 0.4081 0.4169 0.4249 0.4321 0.4388 0.4449 0.4505 0.4558 0.4607 0.4653 0.4696 0.4736 Frac 11 2 0.1967 0.2254 0.2447 0.2594 0.2714 0.2814 0.2902 0.2979 0.3047 0.3109 0.3166 0.3218 0.3266 0.3310 0.3352 0.3391 0.3427 0.3462 0.3495 0.3526 @@ -417,4 +417,3 @@ Frac 28 25 0.6784 0.6305 0.6000 0.5771 0.5588 0.5434 0.5301 0.5185 0.5081 0.4987 Frac 28 26 0.0859 0.1091 0.1189 0.1245 0.1281 0.1307 0.1326 0.1341 0.1353 0.1363 0.1372 0.1380 0.1386 0.1393 0.1398 0.1403 0.1408 0.1413 0.1417 0.1421 Frac 28 27 0.0645 0.0839 0.0920 0.0965 0.0994 0.1014 0.1030 0.1041 0.1051 0.1059 0.1065 0.1071 0.1076 0.1081 0.1085 0.1088 0.1092 0.1095 0.1098 0.1101 Frac 28 28 0.1149 0.1385 0.1502 0.1579 0.1636 0.1680 0.1717 0.1748 0.1776 0.1800 0.1823 0.1843 0.1861 0.1878 0.1895 0.1910 0.1924 0.1938 0.1950 0.1963 - diff --git a/carsus/io/base.py b/carsus/io/base.py index edc4d8da0..e29517e8b 100644 --- a/carsus/io/base.py +++ b/carsus/io/base.py @@ -28,6 +28,7 @@ class BaseParser(object): Call an instance with input data to invoke the `load` method. """ + __metaclass__ = ABCMeta def __init__(self, input_data=None): @@ -88,6 +89,7 @@ class BasePyparser(BaseParser): notes NaN """ + __metaclass__ = ABCMeta def __init__(self, grammar, columns, input_data=None): @@ -99,7 +101,8 @@ def load(self, input_data): results = self.grammar.scanString(input_data) base = list() # list of dicts that will be passed to the base for tokens, start, end in results: - tokens_dict = to_flat_dict(tokens) # make a flattened dict with the column names as keys + tokens_dict = to_flat_dict( + tokens + ) # make a flattened dict with the column names as keys base.append(tokens_dict) self.base = pd.DataFrame(data=base, columns=self.columns) - diff --git a/carsus/io/chianti_/__init__.py b/carsus/io/chianti_/__init__.py index e07b1bd9f..bcabdf19c 100644 --- a/carsus/io/chianti_/__init__.py +++ b/carsus/io/chianti_/__init__.py @@ -1,2 +1 @@ -from carsus.io.chianti_.chianti_ import (ChiantiIonReader, - ChiantiReader) +from carsus.io.chianti_.chianti_ import ChiantiIonReader, ChiantiReader diff --git a/carsus/io/chianti_/chianti_.py b/carsus/io/chianti_/chianti_.py index 909a297fe..4d930b8d8 100644 --- a/carsus/io/chianti_/chianti_.py +++ b/carsus/io/chianti_/chianti_.py @@ -12,17 +12,18 @@ # Compatibility with older versions and pip versions: try: from ChiantiPy.tools.io import versionRead - import ChiantiPy.core as ch + import ChiantiPy.core as ch except ImportError: # Shamefully copied from their GitHub source: import chianti.core as ch + def versionRead(): """ Read the version number of the CHIANTI database """ - xuvtop = os.environ['XUVTOP'] - vFileName = os.path.join(xuvtop, 'VERSION') + xuvtop = os.environ["XUVTOP"] + vFileName = os.path.join(xuvtop, "VERSION") vFile = open(vFileName) versionStr = vFile.readline() vFile.close() @@ -32,14 +33,13 @@ def versionRead(): logger = logging.getLogger(__name__) masterlist_ions_path = os.path.join( - os.getenv('XUVTOP'), "masterlist", "masterlist_ions.pkl" + os.getenv("XUVTOP"), "masterlist", "masterlist_ions.pkl" ) -masterlist_ions_file = open(masterlist_ions_path, 'rb') +masterlist_ions_file = open(masterlist_ions_path, "rb") masterlist_ions = pickle.load(masterlist_ions_file).keys() # Exclude the "d" ions for now -masterlist_ions = [_ for _ in masterlist_ions - if re.match(r"^[a-z]+_\d+$", _)] +masterlist_ions = [_ for _ in masterlist_ions if re.match(r"^[a-z]+_\d+$", _)] masterlist_version = versionRead() @@ -50,65 +50,64 @@ class ChiantiIonReaderError(Exception): class ChiantiIonReader(object): """ - Class for reading ion data from the CHIANTI database + Class for reading ion data from the CHIANTI database - Attributes - ---------- - ion: chianti.core.ion instance + Attributes + ---------- + ion: chianti.core.ion instance - Methods - ------- - levels - Return a DataFrame with the data for ion's levels + Methods + ------- + levels + Return a DataFrame with the data for ion's levels - lines - Return a DataFrame with the data for ion's lines + lines + Return a DataFrame with the data for ion's lines - collisions - Return a DataFrame with the data for ion's electron collisions + collisions + Return a DataFrame with the data for ion's electron collisions - bound_levels - Same as `levels`, but only for bound levels (with energy < ionization_potential) + bound_levels + Same as `levels`, but only for bound levels (with energy < ionization_potential) - bound_lines - Same as `lines`, but only for bound levels (with energy < ionization_potential) + bound_lines + Same as `lines`, but only for bound levels (with energy < ionization_potential) - bound_collisions - Same as `collisions`, but only for bound levels (with energy < ionization_potential) + bound_collisions + Same as `collisions`, but only for bound levels (with energy < ionization_potential) """ elvlc_dict = { - 'lvl': 'level_index', - 'ecm': 'energy', # cm-1 - 'ecmth': 'energy_theoretical', # cm-1 - 'j': 'J', - 'spd': 'L', - 'spin': 'spin_multiplicity', - 'pretty': 'pretty', # configuration + term - 'label': 'label' + "lvl": "level_index", + "ecm": "energy", # cm-1 + "ecmth": "energy_theoretical", # cm-1 + "j": "J", + "spd": "L", + "spin": "spin_multiplicity", + "pretty": "pretty", # configuration + term + "label": "label", } wgfa_dict = { - 'avalue': 'a_value', - 'gf': 'gf_value', - 'lvl1': 'lower_level_index', - 'lvl2': 'upper_level_index', - 'wvl': 'wavelength' + "avalue": "a_value", + "gf": "gf_value", + "lvl1": "lower_level_index", + "lvl2": "upper_level_index", + "wvl": "wavelength", } scups_dict = { - 'btemp': 'temperatures', - 'bscups': 'collision_strengths', - 'gf': 'gf_value', - 'de': 'energy', # Rydberg - 'lvl1': 'lower_level_index', - 'lvl2': 'upper_level_index', - 'ttype': 'ttype', # BT92 Transition type - 'cups': 'cups' # BT92 scaling parameter + "btemp": "temperatures", + "bscups": "collision_strengths", + "gf": "gf_value", + "de": "energy", # Rydberg + "lvl1": "lower_level_index", + "lvl2": "upper_level_index", + "ttype": "ttype", # BT92 Transition type + "cups": "cups", # BT92 scaling parameter } def __init__(self, ion_name): - # dummy temperature to avoid bug in ChiantiPy https://github.com/chianti-atomic/ChiantiPy/issues/466 self.ion = ch.ion(ion_name, temperature=1) self._levels = None @@ -136,26 +135,26 @@ def collisions(self): @property def last_bound_level(self): ionization_potential = u.eV.to( - u.Unit("cm-1"), value=self.ion.Ip, equivalencies=u.spectral()) - last_row = self.levels.loc[self.levels['energy'] - < ionization_potential].tail(1) + u.Unit("cm-1"), value=self.ion.Ip, equivalencies=u.spectral() + ) + last_row = self.levels.loc[self.levels["energy"] < ionization_potential].tail(1) return last_row.index[0] @property def bound_levels(self): - return self.levels.loc[:self.last_bound_level] + return self.levels.loc[: self.last_bound_level] def filter_bound_transitions(self, transitions): - """ Filter transitions DataFrames on bound levels. + """Filter transitions DataFrames on bound levels. - The most succinct and accurate way to do this is to use slicing on multi index, - but due to some bug in pandas out-of-range rows are included in the resulting DataFrame. + The most succinct and accurate way to do this is to use slicing on multi index, + but due to some bug in pandas out-of-range rows are included in the resulting DataFrame. """ transitions = transitions.reset_index() - transitions = transitions.loc[transitions["upper_level_index"] - <= self.last_bound_level] - transitions = transitions.set_index( - ["lower_level_index", "upper_level_index"]) + transitions = transitions.loc[ + transitions["upper_level_index"] <= self.last_bound_level + ] + transitions = transitions.set_index(["lower_level_index", "upper_level_index"]) transitions = transitions.sort_index() return transitions @@ -170,12 +169,12 @@ def bound_collisions(self): return bound_collisions def read_levels(self): - try: elvlc = self.ion.Elvlc except AttributeError: raise ChiantiIonReaderError( - "No levels data is available for ion {}".format(self.ion.Spectroscopic)) + "No levels data is available for ion {}".format(self.ion.Spectroscopic) + ) levels_dict = {} @@ -184,21 +183,25 @@ def read_levels(self): # Check that ground level energy is 0 try: - for key in ['energy', 'energy_theoretical']: + for key in ["energy", "energy_theoretical"]: assert_almost_equal(levels_dict[key][0], 0) except AssertionError: - raise ValueError('Level 0 energy is not 0.0') + raise ValueError("Level 0 energy is not 0.0") levels = pd.DataFrame(levels_dict) # Replace empty labels with NaN - with pd.option_context('future.no_silent_downcasting', True): - levels.loc[:, "label"] = levels["label"].replace( - r'\s+', np.nan, regex=True).infer_objects(copy=False) + with pd.option_context("future.no_silent_downcasting", True): + levels.loc[:, "label"] = ( + levels["label"] + .replace(r"\s+", np.nan, regex=True) + .infer_objects(copy=False) + ) # Extract configuration and term from the "pretty" column levels[["term", "configuration"]] = levels["pretty"].str.rsplit( - ' ', expand=True, n=1) + " ", expand=True, n=1 + ) levels = levels.drop("pretty", axis=1) levels = levels.set_index("level_index") @@ -207,12 +210,12 @@ def read_levels(self): return levels def read_lines(self): - try: wgfa = self.ion.Wgfa except AttributeError: raise ChiantiIonReaderError( - "No lines data is available for ion {}".format(self.ion.Spectroscopic)) + "No lines data is available for ion {}".format(self.ion.Spectroscopic) + ) lines_dict = {} @@ -242,12 +245,14 @@ def parse_wavelength(row): return lines def read_collisions(self): - try: scups = self.ion.Scups except AttributeError: raise ChiantiIonReaderError( - "No collision data is available for ion {}".format(self.ion.Spectroscopic)) + "No collision data is available for ion {}".format( + self.ion.Spectroscopic + ) + ) collisions_dict = {} @@ -256,26 +261,25 @@ def read_collisions(self): collisions = pd.DataFrame(collisions_dict) - collisions = collisions.set_index( - ["lower_level_index", "upper_level_index"]) + collisions = collisions.set_index(["lower_level_index", "upper_level_index"]) collisions = collisions.sort_index() return collisions class ChiantiReader: - """ - Class for extracting levels, lines and collisional data - from Chianti. + """ + Class for extracting levels, lines and collisional data + from Chianti. - Mimics the GFALLReader class. + Mimics the GFALLReader class. - Attributes - ---------- - levels : DataFrame - lines : DataFrame - collisions: DataFrame - version : str + Attributes + ---------- + levels : DataFrame + lines : DataFrame + collisions: DataFrame + version : str """ @@ -290,7 +294,7 @@ def __init__(self, ions, collisions=False, priority=10): Grab collisional data, by default False. priority: int - Priority of the current data source. + Priority of the current data source. """ self.ions = parse_selected_species(ions) self.priority = priority @@ -308,7 +312,6 @@ def _get_levels_lines(self, get_collisions=False): lns_list = [] col_list = [] for ion in self.ions: - ch_ion = convert_species_tuple2chianti_str(ion) reader = ChiantiIonReader(ch_ion) @@ -318,72 +321,94 @@ def _get_levels_lines(self, get_collisions=False): lns = reader.lines except ChiantiIonReaderError: - logger.info(f'Missing levels/lines data for `{ch_ion}`.') + logger.info(f"Missing levels/lines data for `{ch_ion}`.") continue - lvl['atomic_number'] = ion[0] - lvl['ion_charge'] = ion[1] + lvl["atomic_number"] = ion[0] + lvl["ion_charge"] = ion[1] # Indexes must start from zero lvl.index = range(0, len(lvl)) - lvl.index.name = 'level_index' + lvl.index.name = "level_index" lvl_list.append(reader.levels) - lns['atomic_number'] = ion[0] - lns['ion_charge'] = ion[1] + lns["atomic_number"] = ion[0] + lns["ion_charge"] = ion[1] lns_list.append(lns) if get_collisions: try: col = reader.collisions - col['atomic_number'] = ion[0] - col['ion_charge'] = ion[1] + col["atomic_number"] = ion[0] + col["ion_charge"] = ion[1] col_list.append(col) except ChiantiIonReaderError: - logger.info(f'Missing collisional data for `{ch_ion}`.') + logger.info(f"Missing collisional data for `{ch_ion}`.") levels = pd.concat(lvl_list, sort=True) - levels = levels.rename(columns={'J': 'j'}) - levels['method'] = None - levels['priority'] = self.priority + levels = levels.rename(columns={"J": "j"}) + levels["method"] = None + levels["priority"] = self.priority levels = levels.reset_index() - levels = levels.set_index( - ['atomic_number', 'ion_charge', 'level_index']) - levels = levels[['energy', 'j', 'label', 'method', 'priority']] + levels = levels.set_index(["atomic_number", "ion_charge", "level_index"]) + levels = levels[["energy", "j", "label", "method", "priority"]] lines = pd.concat(lns_list, sort=True) lines = lines.reset_index() - lines = lines.rename(columns={'lower_level_index': 'level_index_lower', - 'upper_level_index': 'level_index_upper', - 'gf_value': 'gf'}) + lines = lines.rename( + columns={ + "lower_level_index": "level_index_lower", + "upper_level_index": "level_index_upper", + "gf_value": "gf", + } + ) # Kurucz levels starts from zero, Chianti from 1. - lines['level_index_lower'] = lines['level_index_lower'] - 1 - lines['level_index_upper'] = lines['level_index_upper'] - 1 - - lines = lines.set_index(['atomic_number', 'ion_charge', - 'level_index_lower', 'level_index_upper']) - lines['energy_upper'] = None - lines['energy_lower'] = None - lines['j_upper'] = None - lines['j_lower'] = None - lines = lines[['energy_upper', 'j_upper', 'energy_lower', 'j_lower', - 'wavelength', 'gf']] - - lines['wavelength'] = u.Quantity(lines['wavelength'], u.AA).to('nm').value - - col_columns = ['temperatures', 'collision_strengths', 'gf', 'energy', 'ttype', 'cups'] + lines["level_index_lower"] = lines["level_index_lower"] - 1 + lines["level_index_upper"] = lines["level_index_upper"] - 1 + + lines = lines.set_index( + ["atomic_number", "ion_charge", "level_index_lower", "level_index_upper"] + ) + lines["energy_upper"] = None + lines["energy_lower"] = None + lines["j_upper"] = None + lines["j_lower"] = None + lines = lines[ + ["energy_upper", "j_upper", "energy_lower", "j_lower", "wavelength", "gf"] + ] + + lines["wavelength"] = u.Quantity(lines["wavelength"], u.AA).to("nm").value + + col_columns = [ + "temperatures", + "collision_strengths", + "gf", + "energy", + "ttype", + "cups", + ] if get_collisions: collisions = pd.concat(col_list, sort=True) collisions = collisions.reset_index() - collisions = collisions.rename(columns={'lower_level_index': 'level_index_lower', - 'upper_level_index': 'level_index_upper', - 'gf_value': 'gf',}) - collisions['level_index_lower'] -= 1 - collisions['level_index_upper'] -= 1 - collisions = collisions.set_index(['atomic_number', 'ion_charge', - 'level_index_lower', 'level_index_upper']) + collisions = collisions.rename( + columns={ + "lower_level_index": "level_index_lower", + "upper_level_index": "level_index_upper", + "gf_value": "gf", + } + ) + collisions["level_index_lower"] -= 1 + collisions["level_index_upper"] -= 1 + collisions = collisions.set_index( + [ + "atomic_number", + "ion_charge", + "level_index_lower", + "level_index_upper", + ] + ) collisions = collisions[col_columns] self.collisions = collisions @@ -399,7 +424,7 @@ def to_hdf(self, fname): Path to the HDF5 output file """ - with pd.HDFStore(fname, 'w') as f: - f.put('/levels', self.levels) - f.put('/lines', self.lines) - f.put('/collisions', self.collisions) + with pd.HDFStore(fname, "w") as f: + f.put("/levels", self.levels) + f.put("/lines", self.lines) + f.put("/collisions", self.collisions) diff --git a/carsus/io/cmfgen/__init__.py b/carsus/io/cmfgen/__init__.py index 7c88cac4e..d19c435fc 100644 --- a/carsus/io/cmfgen/__init__.py +++ b/carsus/io/cmfgen/__init__.py @@ -1,8 +1,10 @@ -from carsus.io.cmfgen.base import (CMFGENOscillatorStrengthsParser, - CMFGENEnergyLevelsParser, - CMFGENCollisionalStrengthsParser, - CMFGENPhoCrossSectionsParser, - CMFGENHydLParser, - CMFGENHydGauntBfParser, - CMFGENReader) +from carsus.io.cmfgen.base import ( + CMFGENOscillatorStrengthsParser, + CMFGENEnergyLevelsParser, + CMFGENCollisionalStrengthsParser, + CMFGENPhoCrossSectionsParser, + CMFGENHydLParser, + CMFGENHydGauntBfParser, + CMFGENReader, +) from carsus.io.cmfgen.hdfgen import hdf_dump diff --git a/carsus/io/cmfgen/base.py b/carsus/io/cmfgen/base.py index 043a22613..73c853548 100644 --- a/carsus/io/cmfgen/base.py +++ b/carsus/io/cmfgen/base.py @@ -259,7 +259,6 @@ def _table_gen(self, f): meta = {} for line in f: - try: value = line.split()[0] @@ -275,7 +274,6 @@ def _table_gen(self, f): if f"!{num_key}" in line: n_points = int(value) for i in range(n_points): - values = f.readline().split() if len(values) == 8: # Verner & Yakolev (1995) ground state fits data.append( @@ -295,12 +293,10 @@ def _table_gen(self, f): yield arr, meta def load(self, fname): - data = [] header = parse_header(fname) with open_cmfgen_file(fname) as f: while True: - arr, meta_ = next(self._table_gen(f), None) df = pd.DataFrame.from_records(arr) @@ -554,7 +550,9 @@ def from_config( ions = parse_selected_species(ions) if cross_sections and (1, 0) not in ions: - logger.warning("Selecting H 0 from CMFGEN (required to ingest cross-sections).") + logger.warning( + "Selecting H 0 from CMFGEN (required to ingest cross-sections)." + ) ions.insert(0, (1, 0)) for ion in ions: @@ -571,8 +569,9 @@ def from_config( logger.info(f"Configuration schema found for {symbol} {ion[1]}.") except KeyError: - raise KeyError(f'Configuration schema missing for {symbol} {ion[1]}.' - 'Please check the CMFGEN configuration file: carsus/data/cmfgen_config.yml' + raise KeyError( + f"Configuration schema missing for {symbol} {ion[1]}." + "Please check the CMFGEN configuration file: carsus/data/cmfgen_config.yml" ) osc_fname = BASE_PATH.joinpath(ion_keys["osc"]).as_posix() @@ -648,13 +647,14 @@ def cross_sections_squeeze( based on cross-section types and their respective papers. """ if hyd_n_phixs_stop2start_energy_ratio < 1: - raise ValueError("hyd_n_phixs_stop2start_energy_ratio needs to be bigger than 1.") + raise ValueError( + "hyd_n_phixs_stop2start_energy_ratio needs to be bigger than 1." + ) phixs_table_list = [] n_targets = len(reader_phixs) for i in range(n_targets): - target = reader_phixs[i] lower_level_label = target.attrs["Configuration name"] cross_section_type = target.attrs["Type of cross-section"] @@ -674,7 +674,7 @@ def cross_sections_squeeze( lambda_angstrom = match["Lam(A)"].tolist() level_number = (match["ID"] - 1).tolist() - + # match is > 1 just for J-splitted levels for j in range(len(match)): threshold_energy_ryd = ( @@ -689,7 +689,6 @@ def cross_sections_squeeze( CrossSectionType.OPACITY_PROJECT_SC, CrossSectionType.OPACITY_PROJECT_SC_SM, ]: - diff = target["energy"].diff().dropna() assert (diff >= 0).all() @@ -701,7 +700,6 @@ def cross_sections_squeeze( CrossSectionType.SEATON_FITS, CrossSectionType.SEATON_FITS_OFFSET, ]: - fit_coeff_list = target["fit_coeff"].to_list() if len(fit_coeff_list) not in [1, 3, 4]: @@ -741,7 +739,6 @@ def cross_sections_squeeze( CrossSectionType.HYDROGENIC_SPLIT_L, CrossSectionType.HYDROGENIC_SPLIT_L_OFFSET, ]: - fit_coeff_list = target["fit_coeff"].to_list() fit_coeff_list[0:3] = [int(x) for x in fit_coeff_list[0:3]] @@ -825,7 +822,7 @@ def cross_sections_squeeze( df = pd.DataFrame(phixs_table, columns=["energy", "sigma"]) df["level_index"] = level_number[j] - + phixs_table_list.append(df) ion_phixs_table = pd.concat(phixs_table_list) @@ -833,10 +830,10 @@ def cross_sections_squeeze( return ion_phixs_table def _get_levels_lines( - self, - data, - hyd_n_phixs_stop2start_energy_ratio, - hyd_n_phixs_num_points, + self, + data, + hyd_n_phixs_stop2start_energy_ratio, + hyd_n_phixs_num_points, ): """ Generates `levels`, `lines` and (optionally) `ionization_energies` and @@ -890,7 +887,6 @@ def _get_levels_lines( if "cross_sections" in reader.keys(): if ion == (1, 0): - # Extracted from the header of HYD files since the `data` object # passed to this method does not contain header information. @@ -905,7 +901,6 @@ def _get_levels_lines( hyd_gaunt_factor, hyd_gaunt_energy_grid_ryd = {}, {} for n in range(1, n_levels + 1): - lambda_angstrom = lvl.loc[n - 1, "Lam(A)"] e_threshold_ev = HC_IN_EV_ANGSTROM / lambda_angstrom diff --git a/carsus/io/cmfgen/hdfgen.py b/carsus/io/cmfgen/hdfgen.py index 629aff609..e1e47ac96 100644 --- a/carsus/io/cmfgen/hdfgen.py +++ b/carsus/io/cmfgen/hdfgen.py @@ -3,6 +3,7 @@ logger = logging.getLogger(__name__) + def hdf_dump(cmfgen_dir, patterns, parser, chunk_size=10, ignore_patterns=[]): """Function to parse and dump the entire CMFGEN database. @@ -20,21 +21,20 @@ def hdf_dump(cmfgen_dir, patterns, parser, chunk_size=10, ignore_patterns=[]): String patterns to ignore, by default [] """ files = [] - ignore_patterns = ['.h5'] + ignore_patterns + ignore_patterns = [".h5"] + ignore_patterns for case in patterns: - path = f'{cmfgen_dir}/**/*{case}*' + path = f"{cmfgen_dir}/**/*{case}*" files = files + glob.glob(path, recursive=True) for i in ignore_patterns: files = [f for f in files if i not in f] n = chunk_size - files_chunked = [files[i:i+n] for i in range(0, len(files), n)] - logger.info(f'{len(files)} files selected.') + files_chunked = [files[i : i + n] for i in range(0, len(files), n)] + logger.info(f"{len(files)} files selected.") # Divide read/dump in chunks for less I/O for chunk in files_chunked: - _ = [] for fname in chunk: try: @@ -43,17 +43,16 @@ def hdf_dump(cmfgen_dir, patterns, parser, chunk_size=10, ignore_patterns=[]): # tip: check `find_row` except TypeError: - logger.warning(f'`TypeError` raised while parsing `{fname}`.') + logger.warning(f"`TypeError` raised while parsing `{fname}`.") # tip: check `to_float` except UnboundLocalError: - logger.warning(f'`UnboundLocalError` raised while parsing `{fname}`.') + logger.warning(f"`UnboundLocalError` raised while parsing `{fname}`.") except IsADirectoryError: - logger.warning(f'`{fname}` is a directory.') + logger.warning(f"`{fname}` is a directory.") for obj in _: obj.to_hdf() - logger.info(f'Finished.') - + logger.info(f"Finished.") diff --git a/carsus/io/cmfgen/util.py b/carsus/io/cmfgen/util.py index dde1352f8..e7339b1ea 100644 --- a/carsus/io/cmfgen/util.py +++ b/carsus/io/cmfgen/util.py @@ -8,17 +8,35 @@ from enum import IntEnum, unique -RYD_TO_EV = u.rydberg.to('eV') -H_IN_EV_SECONDS = const.h.to('eV s').value -HC_IN_EV_ANGSTROM = (const.h * const.c).to('eV angstrom').value +RYD_TO_EV = u.rydberg.to("eV") +H_IN_EV_SECONDS = const.h.to("eV s").value +HC_IN_EV_ANGSTROM = (const.h * const.c).to("eV angstrom").value CMFGEN_ATOM_DICT = { - 'H': 'HYD', 'He': 'HE', 'C': 'CARB', 'N': 'NIT', - 'O': 'OXY', 'F': 'FLU', 'Ne': 'NEON', 'Na': 'NA', - 'Mg': 'MG', 'Al': 'AL', 'Si': 'SIL', 'P': 'PHOS', - 'S': 'SUL', 'Cl': 'CHL', 'Ar': 'ARG', 'K': 'POT', - 'Ca': 'CA', 'Sc': 'SCAN', 'Ti': 'TIT', 'V': 'VAN', - 'Cr': 'CHRO', 'Mn': 'MAN', 'Fe': 'FE', 'Co': 'COB', - 'Ni': 'NICK' + "H": "HYD", + "He": "HE", + "C": "CARB", + "N": "NIT", + "O": "OXY", + "F": "FLU", + "Ne": "NEON", + "Na": "NA", + "Mg": "MG", + "Al": "AL", + "Si": "SIL", + "P": "PHOS", + "S": "SUL", + "Cl": "CHL", + "Ar": "ARG", + "K": "POT", + "Ca": "CA", + "Sc": "SCAN", + "Ti": "TIT", + "V": "VAN", + "Cr": "CHRO", + "Mn": "MAN", + "Fe": "FE", + "Co": "COB", + "Ni": "NICK", } @@ -39,8 +57,12 @@ class CrossSectionType(IntEnum): POINTS_TABLE = 22 -def open_cmfgen_file(fname, encoding='ISO-8859-1'): - return gzip.open(fname, 'rt') if fname.endswith('.gz') else open(fname, encoding=encoding) +def open_cmfgen_file(fname, encoding="ISO-8859-1"): + return ( + gzip.open(fname, "rt") + if fname.endswith(".gz") + else open(fname, encoding=encoding) + ) def to_float(string): @@ -56,15 +78,17 @@ def to_float(string): float """ - typos = {'1-.00': '10.00', # `MG/VIII/23oct02/phot_sm_3000`, line 23340 - '*********': 'NaN',} # `SUL/V/08jul99/phot_op.big`, lines 9255-9257 + typos = { + "1-.00": "10.00", # `MG/VIII/23oct02/phot_sm_3000`, line 23340 + "*********": "NaN", + } # `SUL/V/08jul99/phot_op.big`, lines 9255-9257 string = typos.get(string) if string in typos.keys() else string - return float(string.replace('D', 'E')) + return float(string.replace("D", "E")) -def find_row(fname, string1, string2=None, how='AND'): +def find_row(fname, string1, string2=None, how="AND"): """ Search for strings in plain text files and returns the matching\ line (or row number). @@ -88,22 +112,21 @@ def find_row(fname, string1, string2=None, how='AND'): """ if string2 is None: - string2 = '' + string2 = "" with open_cmfgen_file(fname) as f: n = 0 for line in f: - n += 1 - if how == 'OR': + if how == "OR": if string1 in line or string2 in line: break - if how == 'AND': + if how == "AND": if string1 in line and string2 in line: break - if how == 'AND NOT': + if how == "AND NOT": if string1 in line and string2 not in line: break @@ -135,18 +158,20 @@ def parse_header(fname, start=0, stop=50): header = {} with open_cmfgen_file(fname) as f: for line in itertools.islice(f, start, stop): - if '!' in line: - value, key = line.split('!') - + if "!" in line: + value, key = line.split("!") + if len(key) > 1: - key = key.split('(')[0] + key = key.split("(")[0] header[key.strip()] = value.strip() return header -def get_seaton_phixs_table(threshold_energy_ryd, sigma_t, beta, s, nu_0=None, n_points=1000): +def get_seaton_phixs_table( + threshold_energy_ryd, sigma_t, beta, s, nu_0=None, n_points=1000 +): """ References: @@ -161,19 +186,28 @@ def get_seaton_phixs_table(threshold_energy_ryd, sigma_t, beta, s, nu_0=None, n_ phixs_table = np.empty((len(energy_grid), 2)) for i, c in enumerate(energy_grid): - energy_div_threshold = 1 + 20 * (c ** 2) # Taken from `artisatomic` + energy_div_threshold = 1 + 20 * (c**2) # Taken from `artisatomic` if nu_0 is None: - threshold_div_energy = energy_div_threshold ** -1 - cross_section = sigma_t * (beta + (1 - beta) * threshold_div_energy) * (threshold_div_energy ** s) + threshold_div_energy = energy_div_threshold**-1 + cross_section = ( + sigma_t + * (beta + (1 - beta) * threshold_div_energy) + * (threshold_div_energy**s) + ) else: threshold_energy_ev = threshold_energy_ryd * RYD_TO_EV - offset_threshold_div_energy = energy_div_threshold**-1 * (1 + (nu_0 * 1e15 * H_IN_EV_SECONDS) / threshold_energy_ev) + offset_threshold_div_energy = energy_div_threshold**-1 * ( + 1 + (nu_0 * 1e15 * H_IN_EV_SECONDS) / threshold_energy_ev + ) if offset_threshold_div_energy < 1.0: - cross_section = sigma_t * (beta + (1 - beta) * (offset_threshold_div_energy)) * \ - (offset_threshold_div_energy ** s) + cross_section = ( + sigma_t + * (beta + (1 - beta) * (offset_threshold_div_energy)) + * (offset_threshold_div_energy**s) + ) else: cross_section = 0.0 @@ -229,7 +263,15 @@ def get_hydrogenic_n_phixs_table( return phixs_table -def get_hydrogenic_nl_phixs_table(hyd_phixs_energy_grid_ryd, hyd_phixs, threshold_energy_ryd, n, l_start, l_end, nu_0=None): +def get_hydrogenic_nl_phixs_table( + hyd_phixs_energy_grid_ryd, + hyd_phixs, + threshold_energy_ryd, + n, + l_start, + l_end, + nu_0=None, +): """ Citation required. """ @@ -241,14 +283,19 @@ def get_hydrogenic_nl_phixs_table(hyd_phixs_energy_grid_ryd, hyd_phixs, threshol phixs_table = np.empty((len(energy_grid), 2)) threshold_energy_ev = threshold_energy_ryd * RYD_TO_EV - scale_factor = 1 / threshold_energy_ryd / (n ** 2) / ((l_end - l_start + 1) * (l_end + l_start + 1)) + scale_factor = ( + 1 + / threshold_energy_ryd + / (n**2) + / ((l_end - l_start + 1) * (l_end + l_start + 1)) + ) for i, energy_ryd in enumerate(energy_grid): energy_div_threshold = energy_ryd / energy_grid[0] if nu_0 is None: u = energy_div_threshold else: - e_0 = (nu_0 * 1e15 * H_IN_EV_SECONDS) + e_0 = nu_0 * 1e15 * H_IN_EV_SECONDS u = threshold_energy_ev * energy_div_threshold / (e_0 + threshold_energy_ev) if u > 0: cross_section = 0.0 @@ -269,20 +316,19 @@ def get_opproject_phixs_table(threshold_energy_ryd, a, b, c, d, e, n_points=1000 """ References: - Atomic data for opacity calculations. IX. The lithium isoelectronic + Atomic data for opacity calculations. IX. The lithium isoelectronic sequence. Peach, G. ; Saraph, H. E. ; Seaton, M. J. - Journal of Physics B: Atomic, Molecular, and Optical Physics, + Journal of Physics B: Atomic, Molecular, and Optical Physics, Volume 21, Issue 22, pp. 3669-3683 (1988). """ energy_grid = np.linspace(0.0, 1.0, n_points, endpoint=False) phixs_table = np.empty((len(energy_grid), 2)) for i, cb in enumerate(energy_grid): - - energy_div_threshold = 1 + 20 * (cb ** 2) + energy_div_threshold = 1 + 20 * (cb**2) u = energy_div_threshold x = np.log10(min(u, e)) @@ -301,21 +347,21 @@ def get_hummer_phixs_table(threshold_energy_ryd, a, b, c, d, e, f, g, h, n_point A Fast and Accurate Method for Evaluating the Nonrelativistic Free-free Gaunt Factor for Hydrogenic Ions. - + Hummer, D. G. Astrophysical Journal v.327, p.477 """ - # Only applies to `He`. The threshold cross sections seems ok, but + # Only applies to `He`. The threshold cross sections seems ok, but # energy dependence could be slightly wrong. What is the `h` parameter # that is not used?. - + energy_grid = np.linspace(0.0, 1.0, n_points, endpoint=False) phixs_table = np.empty((len(energy_grid), 2)) for i, c_en in enumerate(energy_grid): - energy_div_threshold = 1 + 20 * (c_en ** 2) + energy_div_threshold = 1 + 20 * (c_en**2) x = np.log10(energy_div_threshold) if x < e: @@ -343,17 +389,21 @@ def get_vy95_phixs_table(threshold_energy_ryd, fit_coeff_table, n_points=1000): phixs_table = np.empty((len(energy_grid), 2)) for i, c in enumerate(energy_grid): - - energy_div_threshold = 1 + 20 * (c ** 2) + energy_div_threshold = 1 + 20 * (c**2) cross_section = 0.0 for index, row in fit_coeff_table.iterrows(): - y = energy_div_threshold * row.at['E'] / row.at['E_0'] - P = row.at['P'] - Q = 5.5 + row.at['l'] - 0.5 * row.at['P'] - y_a = row.at['y(a)'] - y_w = row.at['y(w)'] - cross_section += row.at['sigma_0'] * ((y - 1) ** 2 + y_w ** 2) * (y ** -Q) * ((1 + np.sqrt(y / y_a)) ** -P) + y = energy_div_threshold * row.at["E"] / row.at["E_0"] + P = row.at["P"] + Q = 5.5 + row.at["l"] - 0.5 * row.at["P"] + y_a = row.at["y(a)"] + y_w = row.at["y(w)"] + cross_section += ( + row.at["sigma_0"] + * ((y - 1) ** 2 + y_w**2) + * (y**-Q) + * ((1 + np.sqrt(y / y_a)) ** -P) + ) phixs_table[i] = energy_div_threshold * threshold_energy_ryd, cross_section @@ -364,14 +414,14 @@ def get_leibowitz_phixs_table(threshold_energy_ryd, a, b, c, d, e, f, n_points=1 """ References: - Radiative Transition Probabilities and Recombination Coefficients + Radiative Transition Probabilities and Recombination Coefficients of the Ion C IV. Leibowitz, E. M. J. Quant. Spectrosc. Radiat. Transfer. Vol 12, pp. 299-306. """ - + raise NotImplementedError @@ -389,7 +439,7 @@ def get_null_phixs_table(threshold_energy_ryd): numpy.ndarray Photoionization cross sections table with zero cross sections. """ - energy = np.array([1., 100.]) * threshold_energy_ryd + energy = np.array([1.0, 100.0]) * threshold_energy_ryd phixs = np.zeros_like(energy) phixs_table = np.column_stack([energy, phixs]) diff --git a/carsus/io/kurucz/__init__.py b/carsus/io/kurucz/__init__.py index 0eea9ef7e..a86d8897b 100644 --- a/carsus/io/kurucz/__init__.py +++ b/carsus/io/kurucz/__init__.py @@ -1 +1 @@ -from .gfall import GFALLReader \ No newline at end of file +from .gfall import GFALLReader diff --git a/carsus/io/molecules/molecules.py b/carsus/io/molecules/molecules.py index ce7ee744c..9430a0e36 100644 --- a/carsus/io/molecules/molecules.py +++ b/carsus/io/molecules/molecules.py @@ -42,7 +42,6 @@ class BarklemCollet2016Reader(object): """ def __init__(self, fpath=None): - self.fpath = BARKLEM_COLLET_DATA_URL if fpath is None else fpath self._barklem_2016_raw = None diff --git a/carsus/io/nist/__init__.py b/carsus/io/nist/__init__.py index 9da0aeb25..0e94aa5d7 100644 --- a/carsus/io/nist/__init__.py +++ b/carsus/io/nist/__init__.py @@ -1,6 +1,10 @@ -from carsus.io.nist.weightscomp import (download_weightscomp, - NISTWeightsCompPyparser, - NISTWeightsComp) -from carsus.io.nist.ionization import (download_ionization_energies, - NISTIonizationEnergiesParser, - NISTIonizationEnergies) \ No newline at end of file +from carsus.io.nist.weightscomp import ( + download_weightscomp, + NISTWeightsCompPyparser, + NISTWeightsComp, +) +from carsus.io.nist.ionization import ( + download_ionization_energies, + NISTIonizationEnergiesParser, + NISTIonizationEnergies, +) diff --git a/carsus/io/nist/ionization_grammar.py b/carsus/io/nist/ionization_grammar.py index 2fdd1cb62..213720143 100644 --- a/carsus/io/nist/ionization_grammar.py +++ b/carsus/io/nist/ionization_grammar.py @@ -35,7 +35,8 @@ def parse_j(tokens): if len(tokens) == 1: return float(tokens[0]) else: - return float(tokens[0])/tokens[1] + return float(tokens[0]) / tokens[1] + J.setParseAction(parse_j) @@ -49,15 +50,23 @@ def parse_j(tokens): ls_term = mult.setResultsName("mult") + L.setResultsName("L") # jj_term ::= "(" + J + "," + J + ")" -jj_term = Literal("(") + J.setResultsName("first_J") + "," + \ - J.setResultsName("second_J") + Literal(")") +jj_term = ( + Literal("(") + + J.setResultsName("first_J") + + "," + + J.setResultsName("second_J") + + Literal(")") +) # level ::= [ ls_term | jj_term ] + ["*"] + [(J | )] -level = Optional( - Group(ls_term).setResultsName("ls_term") | - Group(jj_term).setResultsName("jj_term")) + \ - Optional("*") + \ - Optional((J | Suppress("<") + J + Suppress(">")).setResultsName("J")) +level = ( + Optional( + Group(ls_term).setResultsName("ls_term") + | Group(jj_term).setResultsName("jj_term") + ) + + Optional("*") + + Optional((J | Suppress("<") + J + Suppress(">")).setResultsName("J")) +) def parse_parity(tokens): @@ -66,4 +75,5 @@ def parse_parity(tokens): else: tokens["parity"] = 0 -level.setParseAction(parse_parity) \ No newline at end of file + +level.setParseAction(parse_parity) diff --git a/carsus/io/nist/weightscomp.py b/carsus/io/nist/weightscomp.py index 3971c3661..92d37ebe3 100644 --- a/carsus/io/nist/weightscomp.py +++ b/carsus/io/nist/weightscomp.py @@ -186,7 +186,7 @@ def _prepare_data(self, atoms): if atomic_numbers[-1] > 94: raise ValueError( "Atoms with atomic number greater than 94 are not stable, and cannot be handled by the NIST parser." - ) + ) atom_data_list = [] diff --git a/carsus/io/nist/weightscomp_grammar.py b/carsus/io/nist/weightscomp_grammar.py index cb5421967..e135bffb6 100644 --- a/carsus/io/nist/weightscomp_grammar.py +++ b/carsus/io/nist/weightscomp_grammar.py @@ -33,47 +33,103 @@ The grammar follows python BNF notation. """ -from pyparsing import Word, Literal, Suppress, Group, Dict, Optional, delimitedList, oneOf, nums, alphas +from pyparsing import ( + Word, + Literal, + Suppress, + Group, + Dict, + Optional, + delimitedList, + oneOf, + nums, + alphas, +) from uncertainties import ufloat_fromstr -__all__ = ['ATOM_NUM_COL', 'SYMBOL_COL', 'MASS_NUM_COL', \ - 'AM_VAL_COL', 'AM_SD_COL', 'AM_THEOR_COL', \ - 'IC_VAL_COL', 'IC_SD_COL', 'AW_TYPE_COL', 'AW_VAL_COL', 'AW_SD_COL', \ - 'AW_LWR_BND_COL', 'AW_UPR_BND_COL', 'AW_STABLE_MASS_NUM_COL',\ - 'NOTES_COL', 'GENERAL_COLS', 'ATOM_MASS_COLS', 'ISOTOP_COMP_COLS', \ - 'ATOM_WEIGHT_COLS', 'COLUMNS', 'COLUMN_NAMES_MAPPING', \ - 'VAL_SD', 'INTERVAL', 'STABLE_MASS_NUM',\ - 'float_', 'ufloat', 'ufloat_theor', 'notes', 'atomic_weight', \ - 'isotopic_comp', 'atomic_mass', 'symbol', 'column_name', 'isotope'] - -GENERAL_COLS = ['atomic_number', 'symbol', 'mass_number'] +__all__ = [ + "ATOM_NUM_COL", + "SYMBOL_COL", + "MASS_NUM_COL", + "AM_VAL_COL", + "AM_SD_COL", + "AM_THEOR_COL", + "IC_VAL_COL", + "IC_SD_COL", + "AW_TYPE_COL", + "AW_VAL_COL", + "AW_SD_COL", + "AW_LWR_BND_COL", + "AW_UPR_BND_COL", + "AW_STABLE_MASS_NUM_COL", + "NOTES_COL", + "GENERAL_COLS", + "ATOM_MASS_COLS", + "ISOTOP_COMP_COLS", + "ATOM_WEIGHT_COLS", + "COLUMNS", + "COLUMN_NAMES_MAPPING", + "VAL_SD", + "INTERVAL", + "STABLE_MASS_NUM", + "float_", + "ufloat", + "ufloat_theor", + "notes", + "atomic_weight", + "isotopic_comp", + "atomic_mass", + "symbol", + "column_name", + "isotope", +] + +GENERAL_COLS = ["atomic_number", "symbol", "mass_number"] ATOM_NUM_COL, SYMBOL_COL, MASS_NUM_COL = GENERAL_COLS -ATOM_MASS_COLS = ['atomic_mass_nominal_value', 'atomic_mass_std_dev', 'atomic_mass_theoretical'] +ATOM_MASS_COLS = [ + "atomic_mass_nominal_value", + "atomic_mass_std_dev", + "atomic_mass_theoretical", +] AM_VAL_COL, AM_SD_COL, AM_THEOR_COL = ATOM_MASS_COLS -ISOTOP_COMP_COLS = ['isotopic_comp_nominal_value', 'isotopic_comp_std_dev'] +ISOTOP_COMP_COLS = ["isotopic_comp_nominal_value", "isotopic_comp_std_dev"] IC_VAL_COL, IC_SD_COL = ISOTOP_COMP_COLS -ATOM_WEIGHT_COLS = ['atomic_weight_type', 'atomic_weight_nominal_value', 'atomic_weight_std_dev', - 'atomic_weight_lwr_bnd', 'atomic_weight_upr_bnd', 'atomic_weight_stable_mass_number'] -AW_TYPE_COL, AW_VAL_COL, AW_SD_COL, \ -AW_LWR_BND_COL, AW_UPR_BND_COL, AW_STABLE_MASS_NUM_COL = ATOM_WEIGHT_COLS - -NOTES_COL = 'notes' - -COLUMNS = GENERAL_COLS + ATOM_MASS_COLS + ISOTOP_COMP_COLS + ATOM_WEIGHT_COLS + [NOTES_COL] +ATOM_WEIGHT_COLS = [ + "atomic_weight_type", + "atomic_weight_nominal_value", + "atomic_weight_std_dev", + "atomic_weight_lwr_bnd", + "atomic_weight_upr_bnd", + "atomic_weight_stable_mass_number", +] +( + AW_TYPE_COL, + AW_VAL_COL, + AW_SD_COL, + AW_LWR_BND_COL, + AW_UPR_BND_COL, + AW_STABLE_MASS_NUM_COL, +) = ATOM_WEIGHT_COLS + +NOTES_COL = "notes" + +COLUMNS = ( + GENERAL_COLS + ATOM_MASS_COLS + ISOTOP_COMP_COLS + ATOM_WEIGHT_COLS + [NOTES_COL] +) COLUMN_NAMES_MAPPING = { - 'Atomic Number': 'atomic_number', - 'Atomic Symbol': 'symbol', - 'Mass Number': 'mass_number', - 'Relative Atomic Mass': 'atomic_mass', - 'Isotopic Composition': 'isotopic_comp', - 'Standard Atomic Weight': 'atomic_weight', - 'Notes': 'notes' + "Atomic Number": "atomic_number", + "Atomic Symbol": "symbol", + "Mass Number": "mass_number", + "Relative Atomic Mass": "atomic_mass", + "Isotopic Composition": "isotopic_comp", + "Standard Atomic Weight": "atomic_weight", + "Notes": "notes", } ATOMIC_WEIGHT_TYPES = [0, 1, 2] @@ -97,24 +153,27 @@ decimal.setParseAction(to_int) # float ::= digit+ "." digit* -float_ = Word(nums, nums+'.') +float_ = Word(nums, nums + ".") to_float = lambda t: float(t[0]) float_.setParseAction(to_float) # ufloat ::= float "(" decimal ")" -ufloat = Word(nums, nums+'.') + LPAREN + Word(nums) + RPAREN +ufloat = Word(nums, nums + ".") + LPAREN + Word(nums) + RPAREN def to_nom_value_and_std_dev(tokens): u = ufloat_fromstr("".join(tokens)) - tokens['nominal_value'] = u.nominal_value - tokens['std_dev'] = u.std_dev + tokens["nominal_value"] = u.nominal_value + tokens["std_dev"] = u.std_dev return tokens + ufloat.setParseAction(to_nom_value_and_std_dev) # ufloat_theor ::= float "(" decimal ["#"] ")" -ufloat_theor = Word(nums, nums+'.') + LPAREN + Word(nums) + Optional(Literal("#")) + RPAREN +ufloat_theor = ( + Word(nums, nums + ".") + LPAREN + Word(nums) + Optional(Literal("#")) + RPAREN +) def to_nom_val_and_std_dev_theor(tokens): @@ -125,6 +184,7 @@ def to_nom_val_and_std_dev_theor(tokens): tokens["theoretical"] = False to_nom_value_and_std_dev(tokens) + ufloat_theor.setParseAction(to_nom_val_and_std_dev_theor) # notes ::= note_value ("," note_value)* @@ -134,25 +194,36 @@ def to_nom_val_and_std_dev_theor(tokens): mass_number = decimal # atomic_weight ::= ufloat | ( "[" float "," float "]" ) | ( "[" mass_number "]" ) -atomic_weight = ufloat | \ - ( LBRACK + float_.setResultsName("lwr_bnd") + Suppress(",") + float_.setResultsName("upr_bnd") + RBRACK ) | \ - ( LBRACK + mass_number.setResultsName("stable_mass_number") + RBRACK ) +atomic_weight = ( + ufloat + | ( + LBRACK + + float_.setResultsName("lwr_bnd") + + Suppress(",") + + float_.setResultsName("upr_bnd") + + RBRACK + ) + | (LBRACK + mass_number.setResultsName("stable_mass_number") + RBRACK) +) def set_atomic_weight_type(tokens): - if tokens.nominal_value != '': - tokens['type'] = VAL_SD - elif tokens.lwr_bnd != '': - tokens['type'] = INTERVAL + if tokens.nominal_value != "": + tokens["type"] = VAL_SD + elif tokens.lwr_bnd != "": + tokens["type"] = INTERVAL else: - tokens['type'] = STABLE_MASS_NUM + tokens["type"] = STABLE_MASS_NUM return tokens -atomic_weight.setParseAction( set_atomic_weight_type) + +atomic_weight.setParseAction(set_atomic_weight_type) # isotopic_comp ::= ufloat | "1" -isotopic_comp = ufloat | Literal("1").setParseAction(to_int).setResultsName('nominal_value') +isotopic_comp = ufloat | Literal("1").setParseAction(to_int).setResultsName( + "nominal_value" +) # atomic_mass ::= ufloat_theor atomic_mass = ufloat_theor @@ -165,7 +236,9 @@ def set_atomic_weight_type(tokens): # column_name = 'Atomic Number' | 'Atomic Symbol' | 'Mass Number' | 'Relative Atomic Mass' | \ # 'Isotopic Composition' | 'Standard Atomic Weight' | 'Notes' -column_name = oneOf(COLUMN_NAMES_MAPPING.keys()).setParseAction(lambda t: COLUMN_NAMES_MAPPING[t[0]]) +column_name = oneOf(COLUMN_NAMES_MAPPING.keys()).setParseAction( + lambda t: COLUMN_NAMES_MAPPING[t[0]] +) # isotope ::= column_name eq atomic_number \ # column_name eq symbol \ @@ -174,19 +247,22 @@ def set_atomic_weight_type(tokens): # column_name eq [isotopic_comp] \ # column_name eq [atomic_weight] \ # column_name eq [notes] -isotope = Dict( Group(column_name + EQ + atomic_number) ) + \ - Dict( Group(column_name + EQ + symbol )) + \ - Dict( Group(column_name + EQ + mass_number) ) + \ - Dict( Group(column_name + EQ + atomic_mass) ) + \ - Dict( Group(column_name + EQ + Optional(isotopic_comp)) ) + \ - Dict( Group(column_name + EQ + Optional(atomic_weight)) ) + \ - Dict( Group(column_name + EQ + Optional(notes)) ) +isotope = ( + Dict(Group(column_name + EQ + atomic_number)) + + Dict(Group(column_name + EQ + symbol)) + + Dict(Group(column_name + EQ + mass_number)) + + Dict(Group(column_name + EQ + atomic_mass)) + + Dict(Group(column_name + EQ + Optional(isotopic_comp))) + + Dict(Group(column_name + EQ + Optional(atomic_weight))) + + Dict(Group(column_name + EQ + Optional(notes))) +) def remove_empty_keys(tokens): for key, item in list(tokens.items()): - if item == '': + if item == "": del tokens[key] return tokens + isotope.setParseAction(remove_empty_keys) diff --git a/carsus/io/nndc/__init__.py b/carsus/io/nndc/__init__.py index 2dfc687f9..d613f4afa 100644 --- a/carsus/io/nndc/__init__.py +++ b/carsus/io/nndc/__init__.py @@ -1,3 +1,5 @@ -from nuclear.io.nndc.base import (store_decay_radiation, - download_decay_radiation, - get_decay_radiation_database) \ No newline at end of file +from nuclear.io.nndc.base import ( + store_decay_radiation, + download_decay_radiation, + get_decay_radiation_database, +) diff --git a/carsus/io/nndc/ensdf.py b/carsus/io/nndc/ensdf.py index cc544b3ed..77db4e51b 100644 --- a/carsus/io/nndc/ensdf.py +++ b/carsus/io/nndc/ensdf.py @@ -1,9 +1,7 @@ class ENSDFReader: - def __init__(self, fname): - #### Still seemingly missing several bits of data from NNDC ### - raise NotImplementedError('Not quite working yet') + raise NotImplementedError("Not quite working yet") raw_decay_list = ensdf.decays(fname)[0] self.parent_nuc_id = raw_decay_list[0] @@ -28,11 +26,21 @@ def __init__(self, fname): @staticmethod def _gamma_to_dataframe(gamma_list): - columns = ['level_1_id', 'level_2_id', 'parent_id', 'daughter_id', - 'energy', 'energy_uncert', 'intensity', 'intensity_uncert', - 'electron_conversion_intensity', 'electron_conversion_intensity_uncert', - 'total_transition_intensity', 'total_transition_intensity_uncert'] - columns += ['{0}_electron_conversion_intensity'.format(item) for item in 'klm'] + columns = [ + "level_1_id", + "level_2_id", + "parent_id", + "daughter_id", + "energy", + "energy_uncert", + "intensity", + "intensity_uncert", + "electron_conversion_intensity", + "electron_conversion_intensity_uncert", + "total_transition_intensity", + "total_transition_intensity_uncert", + ] + columns += ["{0}_electron_conversion_intensity".format(item) for item in "klm"] return pd.DataFrame(data=gamma_list, columns=columns) @@ -41,7 +49,7 @@ def _alpha_to_dataframe(alpha_list): if alpha_list == []: return None else: - raise NotImplementedError('Not implemented yet') + raise NotImplementedError("Not implemented yet") # parent nuclide id in state_id form # child nuclide id in state_id form @@ -52,14 +60,26 @@ def _alpha_to_dataframe(alpha_list): def _beta_minus_to_dataframe(beta_minus_list): if beta_minus_list == []: return None - columns = ['parent_id', 'child_id', 'endpoint_energy', 'average_energy', 'intensity'] + columns = [ + "parent_id", + "child_id", + "endpoint_energy", + "average_energy", + "intensity", + ] return pd.DataFrame(beta_minus_list, columns=columns) @staticmethod def _beta_plus_to_dataframe(beta_plus_list): if beta_plus_list == []: return None - columns = ['parent_id', 'child_id', 'endpoint_energy', 'average_energy', 'intensity', - 'electron_capture_intensity'] - columns += ['{0}_electron_conversion_intensity'.format(item) for item in 'klm'] + columns = [ + "parent_id", + "child_id", + "endpoint_energy", + "average_energy", + "intensity", + "electron_capture_intensity", + ] + columns += ["{0}_electron_conversion_intensity".format(item) for item in "klm"] return pd.DataFrame(beta_plus_list, columns=columns) diff --git a/carsus/io/nndc/parsers.py b/carsus/io/nndc/parsers.py index 47fd83418..c4348105e 100644 --- a/carsus/io/nndc/parsers.py +++ b/carsus/io/nndc/parsers.py @@ -6,12 +6,11 @@ import numpy as np -def uncertainty_parser(unc_raw_str, split_unc_symbol='%'): - unc_raw_str = unc_raw_str.replace('×10+', 'e+') +def uncertainty_parser(unc_raw_str, split_unc_symbol="%"): + unc_raw_str = unc_raw_str.replace("×10+", "e+") unc_raw_str = unc_raw_str.lower() - value_unc_pair = [item.strip() - for item in unc_raw_str.split(split_unc_symbol)] + value_unc_pair = [item.strip() for item in unc_raw_str.split(split_unc_symbol)] if len(value_unc_pair) == 1: # if no uncertainty given return float(value_unc_pair[0]), np.nan @@ -41,34 +40,30 @@ def _sanititze_table(self, df): df.dropna(inplace=True) if "intensity" in df.columns: - intensity_raw = df["intensity"].apply( - uncertainty_parser) + intensity_raw = df["intensity"].apply(uncertainty_parser) df.loc[:, "intensity"] = [item[0] for item in intensity_raw] - df.loc[:, "intensity_unc"] = [item[1] - for item in intensity_raw] + df.loc[:, "intensity_unc"] = [item[1] for item in intensity_raw] if "energy" in df.columns: - energy = df["energy"].apply( - uncertainty_parser, split_unc_symbol=' ') + energy = df["energy"].apply(uncertainty_parser, split_unc_symbol=" ") df.loc[:, "energy"] = [item[0] for item in energy] df.loc[:, "energy_unc"] = [item[1] for item in energy] if "end_point_energy" in df.columns: end_point_energy = df["end_point_energy"].apply( - uncertainty_parser, split_unc_symbol=' ') - df.loc[:, "end_point_energy"] = [ - item[0] for item in end_point_energy] - df.loc[:, "end_point_energy_unc"] = [ - item[1] for item in end_point_energy] + uncertainty_parser, split_unc_symbol=" " + ) + df.loc[:, "end_point_energy"] = [item[0] for item in end_point_energy] + df.loc[:, "end_point_energy_unc"] = [item[1] for item in end_point_energy] if "dose" in df.columns: del df["dose"] - df['heading'] = self.html_name + df["heading"] = self.html_name return df def _default_parse(self, html_table): df = self._convert_html_to_df(html_table, self.columns) df = self._sanititze_table(df) - df['type'] = self.type + df["type"] = self.type return df def parse(self, html_table): @@ -81,7 +76,6 @@ class ElectronTableParser(BaseParser): columns = ["type", "energy", "intensity", "dose"] - class BetaPlusTableParser(BaseParser): html_name = "Beta+" type = "e+" @@ -104,8 +98,8 @@ def parse(self, html_table): x_ray_mask = df.type.str.startswith("XR") - df['type'] = 'x_rays' - df.loc[~x_ray_mask, 'type'] = 'gamma_rays' + df["type"] = "x_rays" + df.loc[~x_ray_mask, "type"] = "gamma_rays" return df diff --git a/carsus/io/nuclear/__init__.py b/carsus/io/nuclear/__init__.py index d44d7d19c..952096d8c 100644 --- a/carsus/io/nuclear/__init__.py +++ b/carsus/io/nuclear/__init__.py @@ -1 +1 @@ -from .nndc import NNDCReader \ No newline at end of file +from .nndc import NNDCReader diff --git a/carsus/io/nuclear/nndc.py b/carsus/io/nuclear/nndc.py index 698461e89..3a96c05ac 100644 --- a/carsus/io/nuclear/nndc.py +++ b/carsus/io/nuclear/nndc.py @@ -38,11 +38,16 @@ def __init__(self, dirname=None, remote=False): if dirname is None: if remote: try: - subprocess.run(['git', 'clone', NNDC_SOURCE_URL, DECAY_DATA_SOURCE_DIR], check=True) + subprocess.run( + ["git", "clone", NNDC_SOURCE_URL, DECAY_DATA_SOURCE_DIR], + check=True, + ) logger.info(f"Downloading NNDC decay data from {NNDC_SOURCE_URL}") except subprocess.CalledProcessError: - logger.warning(f"Failed to clone the repository.\n" - "Check if the repository already exists at {DECAY_DATA_SOURCE_DIR}") + logger.warning( + f"Failed to clone the repository.\n" + "Check if the repository already exists at {DECAY_DATA_SOURCE_DIR}" + ) self.dirname = DECAY_DATA_SOURCE_DIR / "csv" else: self.dirname = dirname @@ -90,8 +95,8 @@ def _set_group_true(self, group): A groupby object that contains information about the groups. """ - if group['Metastable'].any(): - group['Metastable'] = True + if group["Metastable"].any(): + group["Metastable"] = True return group def _add_metastable_column(self, decay_data=None): @@ -108,17 +113,22 @@ def _add_metastable_column(self, decay_data=None): # Create a boolean metastable state column before the 'Decay Mode' column metastable_df.insert(7, "Metastable", False) - metastable_filters = (metastable_df["Decay Mode"] == "IT") & (metastable_df["Decay Mode Value"] != 0.0) & ( - metastable_df["Parent E(level)"] != 0.0) + metastable_filters = ( + (metastable_df["Decay Mode"] == "IT") + & (metastable_df["Decay Mode Value"] != 0.0) + & (metastable_df["Parent E(level)"] != 0.0) + ) - metastable_df.loc[metastable_filters, 'Metastable'] = True + metastable_df.loc[metastable_filters, "Metastable"] = True # avoid duplicate indices since metastable_df is a result of pd.concat operation metastable_df = metastable_df.reset_index() # Group by the combination of these columns - group_criteria = ['Parent E(level)', 'T1/2 (sec)', 'Isotope'] - metastable_df = metastable_df.groupby(group_criteria).apply(self._set_group_true) + group_criteria = ["Parent E(level)", "T1/2 (sec)", "Isotope"] + metastable_df = metastable_df.groupby(group_criteria).apply( + self._set_group_true + ) return metastable_df @@ -128,11 +138,13 @@ def _prepare_nuclear_dataframes(self): the 'Metastable' and 'Isotope' columns, setting the latter as the index. """ decay_data_raw = self._get_nuclear_decay_dataframe() - decay_data_raw["Isotope"] = decay_data_raw.Element.map(str) + decay_data_raw.A.map(str) + decay_data_raw["Isotope"] = decay_data_raw.Element.map( + str + ) + decay_data_raw.A.map(str) decay_data = self._add_metastable_column(decay_data_raw) - decay_data = decay_data.set_index(['Isotope']).drop(['index'], axis=1) + decay_data = decay_data.set_index(["Isotope"]).drop(["index"], axis=1) decay_data = decay_data.sort_values(by=decay_data.columns.tolist()) return decay_data @@ -152,5 +164,5 @@ def to_hdf(self, fpath=None): target_fname = Path().joinpath(fpath, "compiled_ensdf_csv.h5") - with pd.HDFStore(target_fname, 'w') as f: - f.put('/decay_data', self.decay_data) + with pd.HDFStore(target_fname, "w") as f: + f.put("/decay_data", self.decay_data) diff --git a/carsus/io/output/__init__.py b/carsus/io/output/__init__.py index e01478c52..40a70d115 100644 --- a/carsus/io/output/__init__.py +++ b/carsus/io/output/__init__.py @@ -1 +1 @@ -from carsus.io.output.base import TARDISAtomData \ No newline at end of file +from carsus.io.output.base import TARDISAtomData diff --git a/carsus/io/output/base.py b/carsus/io/output/base.py index 017701d0d..c741bb32a 100644 --- a/carsus/io/output/base.py +++ b/carsus/io/output/base.py @@ -34,6 +34,7 @@ class TARDISAtomData: macro_atom_references_prepared : pandas.DataFrame """ + def __init__( self, atomic_weights, @@ -52,7 +53,7 @@ def __init__( collisions_param={"temperatures": np.arange(2000, 50000, 2000)}, ): self.atomic_weights = atomic_weights - + self.gfall_reader = gfall_reader self.zeta_data = zeta_data self.chianti_reader = chianti_reader @@ -63,18 +64,28 @@ def __init__( self.levels_lines_param = levels_lines_param self.collisions_param = collisions_param - self.ionization_energies_preparer = IonizationEnergiesPreparer(self.cmfgen_reader, ionization_energies) + self.ionization_energies_preparer = IonizationEnergiesPreparer( + self.cmfgen_reader, ionization_energies + ) - self.levels_lines_preparer = LevelsLinesPreparer(self.ionization_energies, self.gfall_reader, self.chianti_reader, self.cmfgen_reader) + self.levels_lines_preparer = LevelsLinesPreparer( + self.ionization_energies, + self.gfall_reader, + self.chianti_reader, + self.cmfgen_reader, + ) self.levels_all = self.levels_lines_preparer.all_levels_data self.lines_all = self.levels_lines_preparer.all_lines_data self.levels_lines_preparer.create_levels_lines(**self.levels_lines_param) - self.levels, self.lines = self.levels_lines_preparer.levels, self.levels_lines_preparer.lines + self.levels, self.lines = ( + self.levels_lines_preparer.levels, + self.levels_lines_preparer.lines, + ) self.macro_atom_preparer = MacroAtomPreparer(self.levels, self.lines) self.macro_atom_preparer.create_macro_atom() self.macro_atom_preparer.create_macro_atom_references() - + if ((cmfgen_reader is not None) and hasattr(cmfgen_reader, "collisions")) and ( (chianti_reader is not None) and hasattr(chianti_reader, "collisions") ): @@ -86,19 +97,31 @@ def __init__( if cmfgen_reader is not None and hasattr(cmfgen_reader, "collisions"): self.collisions_preparer = CollisionsPreparer(self.cmfgen_reader) elif hasattr(chianti_reader, "collisions"): - self.collisions_preparer = ChiantiCollisionsPreparer(self.chianti_reader, self.levels, self.levels_all, self.lines_all, self.levels_lines_preparer.chianti_ions, self.collisions_param) + self.collisions_preparer = ChiantiCollisionsPreparer( + self.chianti_reader, + self.levels, + self.levels_all, + self.lines_all, + self.levels_lines_preparer.chianti_ions, + self.collisions_param, + ) else: logger.warning("No source of collisions was selected.") self.collisions_preparer = None if (cmfgen_reader is not None) and hasattr(cmfgen_reader, "cross_sections"): - self.cross_sections_preparer = PhotoIonizationPreparer(self.levels, self.levels_all, self.lines_all, self.cmfgen_reader, self.levels_lines_preparer.cmfgen_ions) + self.cross_sections_preparer = PhotoIonizationPreparer( + self.levels, + self.levels_all, + self.lines_all, + self.cmfgen_reader, + self.levels_lines_preparer.cmfgen_ions, + ) else: self.cross_sections_preparer = None - logger.info("Finished.") - + @property def ionization_energies(self): return self.ionization_energies_preparer.ionization_energies @@ -120,7 +143,7 @@ def cross_sections_prepared(self): return self.cross_sections_preparer.cross_sections_prepared else: return None - + @property def levels_prepared(self): return self.levels_lines_preparer.levels_prepared @@ -128,23 +151,23 @@ def levels_prepared(self): @property def lines_prepared(self): return self.levels_lines_preparer.lines_prepared - - @property + + @property def macro_atom(self): return self.macro_atom_preparer.macro_atom - - @property + + @property def macro_atom_references(self): return self.macro_atom_preparer.macro_atom_references @property def macro_atom_prepared(self): return self.macro_atom_preparer.macro_atom_prepared - + @property def macro_atom_references_prepared(self): return self.macro_atom_preparer.macro_atom_references_prepared - + @property def collisions_prepared(self): if self.collisions_preparer is not None: @@ -152,7 +175,7 @@ def collisions_prepared(self): return self.collisions_preparer.collisions_prepared else: return None - + @property def collisions_metadata(self): if self.collisions_preparer is not None: @@ -180,25 +203,41 @@ def to_hdf(self, fname): from carsus import FORMAT_VERSION required_outputs = { - "/atom_data": self.atomic_weights.base, + "/atom_data": self.atomic_weights.base, "/ionization_data": self.ionization_energies_prepared, "/zeta_data": self.zeta_data.base, "/levels_data": self.levels_prepared, "/lines_data": self.lines_prepared, "/macro_atom_data": self.macro_atom_prepared, - "/macro_atom_references": self.macro_atom_references_prepared} - + "/macro_atom_references": self.macro_atom_references_prepared, + } + optional_outputs = { "/nuclear_decay_rad": (self.nndc_reader, "decay_data"), "/linelist_atoms": (self.vald_reader, "linelist_atoms"), "/linelist_molecules": (self.vald_reader, "linelist_molecules"), - "/molecules/equilibrium_constants": (self.barklem_2016_data, "equilibrium_constants"), - "/molecules/ionization_energies": (self.barklem_2016_data, "ionization_energies"), - "/molecules/dissociation_energies": (self.barklem_2016_data, "dissociation_energies"), - "/molecules/partition_functions": (self.barklem_2016_data, "partition_functions"), + "/molecules/equilibrium_constants": ( + self.barklem_2016_data, + "equilibrium_constants", + ), + "/molecules/ionization_energies": ( + self.barklem_2016_data, + "ionization_energies", + ), + "/molecules/dissociation_energies": ( + self.barklem_2016_data, + "dissociation_energies", + ), + "/molecules/partition_functions": ( + self.barklem_2016_data, + "partition_functions", + ), "/collisions_data": (self, "collisions_prepared"), "/collisions_metadata": (self, "collisions_metadata"), - "/photoionization_data": (self.cross_sections_preparer, "cross_sections_prepared"), + "/photoionization_data": ( + self.cross_sections_preparer, + "cross_sections_prepared", + ), } with pd.HDFStore(fname, "w") as f: diff --git a/carsus/io/output/collisions.py b/carsus/io/output/collisions.py index bb24d3694..38cfc02c8 100644 --- a/carsus/io/output/collisions.py +++ b/carsus/io/output/collisions.py @@ -10,6 +10,7 @@ logger = logging.getLogger(__name__) + class CollisionsPreparer: def __init__(self, reader): collisions = reader.collisions.copy() @@ -24,7 +25,7 @@ def __init__(self, reader): self.collisions = collisions self.collisions_metadata = reader.collisional_metadata - + def prepare_collisions(self): """ Prepare the DataFrame with electron collisions for TARDIS. @@ -61,18 +62,18 @@ def prepare_collisions(self): self.collisions.reset_index().loc[:, collisions_columns].copy() ) self.collisions_prepared = collisions_prepared.set_index(collisions_index) - + class ChiantiCollisionsPreparer(CollisionsPreparer): def __init__( - self, - chianti_reader, - levels, - levels_all, - lines_all, - chianti_ions, - collisions_param = {"temperatures": np.arange(2000, 50000, 2000)} - ): + self, + chianti_reader, + levels, + levels_all, + lines_all, + chianti_ions, + collisions_param={"temperatures": np.arange(2000, 50000, 2000)}, + ): self.chianti_reader = chianti_reader self.levels = levels self.levels_all = levels_all @@ -87,7 +88,7 @@ def __init__( "info": None, } ) - + def create_chianti_collisions(self, temperatures=np.arange(2000, 50000, 2000)): """ Generates the definitive `collisions` DataFrame by adding new columns @@ -211,9 +212,8 @@ def create_chianti_collisions(self, temperatures=np.arange(2000, 50000, 2000)): return collisions -def calculate_collisional_strength( - row, temperatures, kb_ev, c_ul_temperature_cols - ): + +def calculate_collisional_strength(row, temperatures, kb_ev, c_ul_temperature_cols): """ Function to calculation upsilon from Burgess & Tully 1992 (TType 1 - 4; Eq. 23 - 38). @@ -258,4 +258,4 @@ def calculate_collisional_strength( #### 1992A&A...254..436B Equation 20 & 22 ##### collisional_ul_factor = 8.63e-6 * upsilon / (g_u * temperatures**0.5) - return pd.Series(data=collisional_ul_factor, index=c_ul_temperature_cols) \ No newline at end of file + return pd.Series(data=collisional_ul_factor, index=c_ul_temperature_cols) diff --git a/carsus/io/output/ionization_energies.py b/carsus/io/output/ionization_energies.py index db845438f..c712e1715 100644 --- a/carsus/io/output/ionization_energies.py +++ b/carsus/io/output/ionization_energies.py @@ -1,5 +1,6 @@ import copy + class IonizationEnergiesPreparer: def __init__(self, cmfgen_reader, ionization_energies): self.ionization_energies = ionization_energies @@ -35,4 +36,4 @@ def ionization_energies_prepared(self): ["atomic_number", "ion_number"] ) - return ionization_energies_prepared.squeeze() \ No newline at end of file + return ionization_energies_prepared.squeeze() diff --git a/carsus/io/output/levels_lines.py b/carsus/io/output/levels_lines.py index 354522291..154d7c5f2 100644 --- a/carsus/io/output/levels_lines.py +++ b/carsus/io/output/levels_lines.py @@ -16,8 +16,11 @@ logger = logging.getLogger(__name__) + class LevelsLinesPreparer: - def __init__(self, ionization_energies, gfall_reader, chianti_reader, cmfgen_reader): + def __init__( + self, ionization_energies, gfall_reader, chianti_reader, cmfgen_reader + ): self.ionization_energies = ionization_energies self.gfall_reader = gfall_reader self.chianti_reader = chianti_reader @@ -60,7 +63,7 @@ def solve_priorities(levels): ) == set([]) return gfall_ions, chianti_ions, cmfgen_ions - + @staticmethod def _create_metastable_flags(levels, lines, levels_metastable_loggf_threshold=-3): """ @@ -94,7 +97,7 @@ def _create_metastable_flags(levels, lines, levels_metastable_loggf_threshold=-3 metastable_flags.name = "metastable" return metastable_flags - + def ingest_multiple_sources(self, attribute): """Takes dataframes from multiple readers and merges them @@ -121,7 +124,7 @@ def ingest_multiple_sources(self, attribute): cmfgen = getattr(self.cmfgen_reader, attribute) cmfgen["ds_id"] = 5 sources.append(cmfgen) - + return pd.concat(sources, sort=True) # replace with functools.cached_property with Python > 3.8 @@ -166,7 +169,9 @@ def all_levels_data(self): ) def to_string(x): - return [f"{convert_atomic_number2symbol(ion[0])} {ion[1]}" for ion in sorted(x)] + return [ + f"{convert_atomic_number2symbol(ion[0])} {ion[1]}" for ion in sorted(x) + ] gfall_str = ", ".join(to_string(self.gfall_ions)) logger.info(f"GFALL selected species: {gfall_str}.") @@ -308,7 +313,7 @@ def all_lines_data(self): ] return lines - + @property def levels_prepared(self): """ @@ -495,6 +500,7 @@ def create_levels_lines( self.levels = levels self.lines = lines + def create_einstein_coeff(lines): """ Create Einstein coefficients columns for the `lines` DataFrame. @@ -509,18 +515,10 @@ def create_einstein_coeff(lines): const.m_e.cgs.value * const.c.cgs.value ) - lines["B_lu"] = ( - einstein_coeff * lines["f_lu"] / (const.h.cgs.value * lines["nu"]) - ) + lines["B_lu"] = einstein_coeff * lines["f_lu"] / (const.h.cgs.value * lines["nu"]) - lines["B_ul"] = ( - einstein_coeff * lines["f_ul"] / (const.h.cgs.value * lines["nu"]) - ) + lines["B_ul"] = einstein_coeff * lines["f_ul"] / (const.h.cgs.value * lines["nu"]) lines["A_ul"] = ( - 2 - * einstein_coeff - * lines["nu"] ** 2 - / const.c.cgs.value**2 - * lines["f_ul"] + 2 * einstein_coeff * lines["nu"] ** 2 / const.c.cgs.value**2 * lines["f_ul"] ) diff --git a/carsus/io/output/macro_atom.py b/carsus/io/output/macro_atom.py index 58910e078..52b2a7347 100644 --- a/carsus/io/output/macro_atom.py +++ b/carsus/io/output/macro_atom.py @@ -9,7 +9,8 @@ P_INTERNAL_DOWN = 0 P_INTERNAL_UP = 1 -class MacroAtomPreparer(): + +class MacroAtomPreparer: def __init__(self, levels, lines): self.levels = levels self.lines = lines @@ -158,7 +159,6 @@ def create_macro_atom_references(self): self.macro_atom_references = macro_atom_references - @property def macro_atom_prepared(self): """ @@ -221,4 +221,4 @@ def macro_atom_references_prepared(self): ["atomic_number", "ion_number", "source_level_number"] ) - return macro_atom_references_prepared \ No newline at end of file + return macro_atom_references_prepared diff --git a/carsus/io/output/photo_ionization.py b/carsus/io/output/photo_ionization.py index ba64b2a86..e56ffb3fa 100644 --- a/carsus/io/output/photo_ionization.py +++ b/carsus/io/output/photo_ionization.py @@ -8,6 +8,7 @@ logger = logging.getLogger(__name__) + class PhotoIonizationPreparer: def __init__(self, levels, levels_all, lines_all, cmfgen_reader, cmfgen_ions): self.levels = levels @@ -61,17 +62,21 @@ def cross_sections(self): # Levels are already cleaned, just drop the NaN's after join cross_sections = cross_sections.dropna() - cross_sections["energy"] = u.Quantity(cross_sections["energy"], "Ry").to( - "Hz", equivalencies=u.spectral() - ).value - cross_sections["sigma"] = u.Quantity(cross_sections["sigma"], "Mbarn").to("cm2").value + cross_sections["energy"] = ( + u.Quantity(cross_sections["energy"], "Ry") + .to("Hz", equivalencies=u.spectral()) + .value + ) + cross_sections["sigma"] = ( + u.Quantity(cross_sections["sigma"], "Mbarn").to("cm2").value + ) cross_sections["level_number"] = cross_sections["level_number"].astype("int") cross_sections = cross_sections.rename( columns={"energy": "nu", "sigma": "x_sect"} ) return cross_sections - + # replace with functools.cached_property with Python > 3.8 @property @functools.lru_cache() diff --git a/carsus/io/tests/conftest.py b/carsus/io/tests/conftest.py index 7a7cd05cf..9a7269228 100644 --- a/carsus/io/tests/conftest.py +++ b/carsus/io/tests/conftest.py @@ -6,14 +6,14 @@ @pytest.fixture def entry(): - name = Word(alphas, alphas+'_') - value = Word(nums, nums+".").setResultsName('nominal_value') - uncert = Word(nums).setResultsName('std_dev') + name = Word(alphas, alphas + "_") + value = Word(nums, nums + ".").setResultsName("nominal_value") + uncert = Word(nums).setResultsName("std_dev") value_uncert = value + Suppress("(") + uncert + Suppress(")") - return Dict( Group(name + Suppress("=") + value_uncert ) ) + return Dict(Group(name + Suppress("=") + value_uncert)) @pytest.fixture def aw_pyparser(entry): columns = ["atomic_weight_nominal_value", "atomic_weight_std_dev"] - return BasePyparser(entry, columns) \ No newline at end of file + return BasePyparser(entry, columns) diff --git a/carsus/io/tests/test_base.py b/carsus/io/tests/test_base.py index b9a012479..af050e68b 100644 --- a/carsus/io/tests/test_base.py +++ b/carsus/io/tests/test_base.py @@ -5,21 +5,31 @@ from carsus.io.nist.weightscomp_grammar import AW_SD_COL, AW_VAL_COL -@pytest.mark.parametrize("test_input,expected",[ - ("""atomic_weight = 6.8083492038(23) +@pytest.mark.parametrize( + "test_input,expected", + [ + ( + """atomic_weight = 6.8083492038(23) atomic_weight = 8.8239833(11) atomic_weight = 2.19802(3)""", - [{AW_VAL_COL: "6.8083492038", AW_SD_COL: '23'}, - {AW_VAL_COL: "8.8239833", AW_SD_COL: '11'}, - {AW_VAL_COL: "2.19802", AW_SD_COL: '3'}]) -]) + [ + {AW_VAL_COL: "6.8083492038", AW_SD_COL: "23"}, + {AW_VAL_COL: "8.8239833", AW_SD_COL: "11"}, + {AW_VAL_COL: "2.19802", AW_SD_COL: "3"}, + ], + ) + ], +) def test_pyparser_load(test_input, expected, aw_pyparser): aw_pyparser.load(test_input) - assert_frame_equal(aw_pyparser.base, - pd.DataFrame(data=expected, columns=[AW_VAL_COL, AW_SD_COL]), check_names=False) + assert_frame_equal( + aw_pyparser.base, + pd.DataFrame(data=expected, columns=[AW_VAL_COL, AW_SD_COL]), + check_names=False, + ) def test_pyparser_callable(aw_pyparser): aw_pyparser(input_data="atomic_weight = 6.8083492038(23)") assert aw_pyparser.base.loc[0, AW_VAL_COL] == "6.8083492038" - assert aw_pyparser.base.loc[0, AW_SD_COL] == "23" \ No newline at end of file + assert aw_pyparser.base.loc[0, AW_SD_COL] == "23" diff --git a/carsus/io/tests/test_chianti.py b/carsus/io/tests/test_chianti.py index ce96a3e6e..9a915e0be 100644 --- a/carsus/io/tests/test_chianti.py +++ b/carsus/io/tests/test_chianti.py @@ -31,7 +31,6 @@ def test_chianti_reader_read_collisions(self, ch_ion_reader): return ch_ion_reader.collisions - class TestChiantiReader: @pytest.fixture(scope="class", params=["H-He", "N"]) def ch_reader(self, request): diff --git a/carsus/io/tests/test_gfall.py b/carsus/io/tests/test_gfall.py index 5ed0b7363..de85177fc 100644 --- a/carsus/io/tests/test_gfall.py +++ b/carsus/io/tests/test_gfall.py @@ -4,6 +4,7 @@ from numpy.testing import assert_almost_equal, assert_allclose from carsus.io.kurucz import GFALLReader + @pytest.fixture() def gfall_rdr(gfall_fname): return GFALLReader(fname=gfall_fname) @@ -39,49 +40,75 @@ def lines(gfall_rdr): return gfall_rdr.lines -@pytest.mark.parametrize("index, wavelength, element_code, e_first, e_second",[ - (14, 72.5537, 4.02, 983355.0, 1121184.0), - (37, 2.4898, 7.05, 0.0, 4016390.0) -]) -def test_grall_reader_gfall_raw(gfall_raw, index, wavelength, element_code, e_first, e_second): +@pytest.mark.parametrize( + "index, wavelength, element_code, e_first, e_second", + [(14, 72.5537, 4.02, 983355.0, 1121184.0), (37, 2.4898, 7.05, 0.0, 4016390.0)], +) +def test_grall_reader_gfall_raw( + gfall_raw, index, wavelength, element_code, e_first, e_second +): row = gfall_raw.loc[index] assert_almost_equal(row["element_code"], element_code) assert_almost_equal(row["wavelength"], wavelength) assert_allclose([row["e_first"], row["e_second"]], [e_first, e_second]) + @pytest.mark.remote_data -@pytest.mark.parametrize("index, wavelength, element_code, e_first, e_second",[ - (14, 72.5537, 4.02, 983355.0, 1121184.0), - (37, 2.4898, 7.05, 0.0, 4016390.0) -]) -def test_grall_reader_gfall_raw_http(gfall_raw_http, index, wavelength, element_code, e_first, e_second): +@pytest.mark.parametrize( + "index, wavelength, element_code, e_first, e_second", + [(14, 72.5537, 4.02, 983355.0, 1121184.0), (37, 2.4898, 7.05, 0.0, 4016390.0)], +) +def test_grall_reader_gfall_raw_http( + gfall_raw_http, index, wavelength, element_code, e_first, e_second +): row = gfall_raw_http.loc[index] assert_almost_equal(row["element_code"], element_code) assert_almost_equal(row["wavelength"], wavelength) assert_allclose([row["e_first"], row["e_second"]], [e_first, e_second]) -@pytest.mark.parametrize("index, wavelength, atomic_number, ion_charge, " - "energy_lower, energy_upper, energy_lower_predicted, energy_upper_predicted",[ - (12, 67.5615, 4, 2, 983369.8, 1131383.0, False, False), - (17, 74.6230, 4, 2, 997455.000, 1131462.0, False, False), - (41, 16.1220, 7, 5, 3385890.000, 4006160.0, False, True) -]) -def test_gfall_reader_gfall(gfall, index, wavelength, atomic_number, ion_charge, - energy_lower, energy_upper, energy_lower_predicted, energy_upper_predicted): +@pytest.mark.parametrize( + "index, wavelength, atomic_number, ion_charge, " + "energy_lower, energy_upper, energy_lower_predicted, energy_upper_predicted", + [ + (12, 67.5615, 4, 2, 983369.8, 1131383.0, False, False), + (17, 74.6230, 4, 2, 997455.000, 1131462.0, False, False), + (41, 16.1220, 7, 5, 3385890.000, 4006160.0, False, True), + ], +) +def test_gfall_reader_gfall( + gfall, + index, + wavelength, + atomic_number, + ion_charge, + energy_lower, + energy_upper, + energy_lower_predicted, + energy_upper_predicted, +): row = gfall.loc[index] assert row["atomic_number"] == atomic_number assert row["ion_charge"] == ion_charge - assert_allclose([row["wavelength"], row["energy_lower"], row["energy_upper"]], - [wavelength, energy_lower, energy_upper]) + assert_allclose( + [row["wavelength"], row["energy_lower"], row["energy_upper"]], + [wavelength, energy_lower, energy_upper], + ) assert row["energy_lower_predicted"] == energy_lower_predicted assert row["energy_upper_predicted"] == energy_upper_predicted def test_gfall_reader_gfall_ignore_labels(gfall): ignored_labels = ["AVERAGE", "ENERGIES", "CONTINUUM"] - assert len(gfall.loc[(gfall["label_lower"].isin(ignored_labels)) | - (gfall["label_upper"].isin(ignored_labels))]) == 0 + assert ( + len( + gfall.loc[ + (gfall["label_lower"].isin(ignored_labels)) + | (gfall["label_upper"].isin(ignored_labels)) + ] + ) + == 0 + ) def test_gfall_reader_clean_levels_labels(levels): @@ -90,27 +117,36 @@ def test_gfall_reader_clean_levels_labels(levels): assert len(levels0402.loc[(np.isclose(levels0402["energy"], 0.0))]) == 1 -@pytest.mark.parametrize("atomic_number, ion_charge, level_index, " - "energy, j, method",[ - (4, 2, 0, 0.0, 0.0, "meas"), - (4, 2, 11, 1128300.0, 2.0, "meas"), - (7, 5, 7, 4006160.0, 0.0, "theor") -]) -def test_gfall_reader_levels(levels, atomic_number, ion_charge, level_index, - energy, j, method): +@pytest.mark.parametrize( + "atomic_number, ion_charge, level_index, " "energy, j, method", + [ + (4, 2, 0, 0.0, 0.0, "meas"), + (4, 2, 11, 1128300.0, 2.0, "meas"), + (7, 5, 7, 4006160.0, 0.0, "theor"), + ], +) +def test_gfall_reader_levels( + levels, atomic_number, ion_charge, level_index, energy, j, method +): row = levels.loc[(atomic_number, ion_charge, level_index)] assert_almost_equal(row["energy"], energy) assert_almost_equal(row["j"], j) assert row["method"] == method -@pytest.mark.parametrize("atomic_number, ion_charge, level_index_lower, level_index_upper," - "wavelength, gf",[ - (4, 2, 0, 16, 8.8309, 0.12705741), - (4, 2, 6, 15, 74.6230, 2.1330449131) -]) -def test_gfall_reader_lines(lines, atomic_number, ion_charge, - level_index_lower, level_index_upper, wavelength, gf): +@pytest.mark.parametrize( + "atomic_number, ion_charge, level_index_lower, level_index_upper," "wavelength, gf", + [(4, 2, 0, 16, 8.8309, 0.12705741), (4, 2, 6, 15, 74.6230, 2.1330449131)], +) +def test_gfall_reader_lines( + lines, + atomic_number, + ion_charge, + level_index_lower, + level_index_upper, + wavelength, + gf, +): row = lines.loc[(atomic_number, ion_charge, level_index_lower, level_index_upper)] assert_almost_equal(row["wavelength"], wavelength) assert_almost_equal(row["gf"], gf) @@ -122,7 +158,7 @@ def test_gfall_hash(gfall_rdr): # Need to generate `gfall_raw` lazy attribute to get `md5`. gf_raw = gf.gfall_raw - assert gf.version == 'e2149a67d52b7cb05fa5d35e6912cc98' + assert gf.version == "e2149a67d52b7cb05fa5d35e6912cc98" @pytest.mark.remote_data @@ -130,4 +166,4 @@ def test_gfall_hash_http(gfall_rdr_http): gf = gfall_rdr_http gf_raw = gf.gfall_raw - assert gf.version == 'e2149a67d52b7cb05fa5d35e6912cc98' \ No newline at end of file + assert gf.version == "e2149a67d52b7cb05fa5d35e6912cc98" diff --git a/carsus/io/tests/test_ionization.py b/carsus/io/tests/test_ionization.py index 9496c2ab7..dea23831f 100644 --- a/carsus/io/tests/test_ionization.py +++ b/carsus/io/tests/test_ionization.py @@ -3,8 +3,10 @@ from pandas.testing import assert_series_equal -from carsus.io.nist.ionization import (NISTIonizationEnergiesParser, - NISTIonizationEnergies) +from carsus.io.nist.ionization import ( + NISTIonizationEnergiesParser, + NISTIonizationEnergies, +) test_data = """ @@ -27,27 +29,23 @@ expected_indices = list(zip(expected_at_num, expected_ion_charge)) -expected_ground_shells = ('ground_shells', - ['1s2.2s2', '1s2.2s', '1s2', '1s'] - ) +expected_ground_shells = ("ground_shells", ["1s2.2s2", "1s2.2s", "1s2", "1s"]) -expected_ground_level = ('ground_level', - ['1S0', '2S*<1/2>', '(1,3/2)<2>', '2S<1/2>'] - ) +expected_ground_level = ("ground_level", ["1S0", "2S*<1/2>", "(1,3/2)<2>", "2S<1/2>"]) -expected_j = ('J', [0.0, 0.5, 2.0, 0.5]) +expected_j = ("J", [0.0, 0.5, 2.0, 0.5]) -expected_ioniz_energy_value = ('ionization_energy_value', - [9.3226990, 18.211153, 153.8961980, 217.7185766] - ) +expected_ioniz_energy_value = ( + "ionization_energy_value", + [9.3226990, 18.211153, 153.8961980, 217.7185766], +) -expected_ioniz_energy_uncert = ('ionization_energy_uncert', - [7e-6, 4e-5, 4e-6, 1e-6] - ) +expected_ioniz_energy_uncert = ("ionization_energy_uncert", [7e-6, 4e-5, 4e-6, 1e-6]) -expected_ioniz_energy_method = ('ionization_energy_method', - ['meas', 'meas', 'intrpl', 'theor'] - ) +expected_ioniz_energy_method = ( + "ionization_energy_method", + ["meas", "meas", "intrpl", "theor"], +) @pytest.fixture @@ -66,20 +64,28 @@ def ground_levels(ioniz_energies_parser): return ioniz_energies_parser.prepare_ground_levels() -@pytest.fixture(params=[expected_ground_shells, - expected_ground_level, expected_ioniz_energy_value, - expected_ioniz_energy_uncert, expected_ioniz_energy_method]) +@pytest.fixture( + params=[ + expected_ground_shells, + expected_ground_level, + expected_ioniz_energy_value, + expected_ioniz_energy_uncert, + expected_ioniz_energy_method, + ] +) def expected_series_ioniz_energies(request): - index = pd.MultiIndex.from_tuples(tuples=expected_indices, - names=['atomic_number', 'ion_charge']) + index = pd.MultiIndex.from_tuples( + tuples=expected_indices, names=["atomic_number", "ion_charge"] + ) name, data = request.param return pd.Series(data=data, name=name, index=index) @pytest.fixture(params=[expected_j]) def expected_series_ground_levels(request): - index = pd.MultiIndex.from_tuples(tuples=expected_indices, - names=['atomic_number', 'ion_charge']) + index = pd.MultiIndex.from_tuples( + tuples=expected_indices, names=["atomic_number", "ion_charge"] + ) name, data = request.param return pd.Series(data=data, name=name, index=index) @@ -93,31 +99,29 @@ def test_prepare_ioniz_energies(ioniz_energies, expected_series_ioniz_energies): assert_series_equal(series, expected_series_ioniz_energies) - def test_prepare_ground_levels(ground_levels, expected_series_ground_levels): series = ground_levels[expected_series_ground_levels.name] assert_series_equal(series, expected_series_ground_levels) @pytest.mark.remote_data - def test_ground_levels_missing_j(): ionization_energies = NISTIonizationEnergies(spectra="Nd") ground_levels = ionization_energies.get_ground_levels() - ground_levels = ground_levels.set_index(['atomic_number', 'ion_charge']) + ground_levels = ground_levels.set_index(["atomic_number", "ion_charge"]) + + assert ground_levels.loc[(60, 5)]["g"] == 1 + assert ground_levels.loc[(60, 6)]["g"] == 1 + assert ground_levels.loc[(60, 7)]["g"] == 1 + assert ground_levels.loc[(60, 8)]["g"] == 1 + assert ground_levels.loc[(60, 9)]["g"] == 1 + assert ground_levels.loc[(60, 10)]["g"] == 1 - assert ground_levels.loc[(60, 5)]['g'] == 1 - assert ground_levels.loc[(60, 6)]['g'] == 1 - assert ground_levels.loc[(60, 7)]['g'] == 1 - assert ground_levels.loc[(60, 8)]['g'] == 1 - assert ground_levels.loc[(60, 9)]['g'] == 1 - assert ground_levels.loc[(60, 10)]['g'] == 1 def test_nist_asd_version(): - nist_ionization = NISTIonizationEnergies('H') + nist_ionization = NISTIonizationEnergies("H") version = nist_ionization.version - version_split = version.split('.') + version_split = version.split(".") assert len(version_split) > 1 - to_int = [ int(i) for i in version_split ] - + to_int = [int(i) for i in version_split] diff --git a/carsus/io/tests/test_ionization_grammar.py b/carsus/io/tests/test_ionization_grammar.py index 1d47aff99..5ca2aeeda 100644 --- a/carsus/io/tests/test_ionization_grammar.py +++ b/carsus/io/tests/test_ionization_grammar.py @@ -4,56 +4,54 @@ from carsus.io.nist.ionization_grammar import * -@pytest.mark.parametrize("test_input,exp_j",[ - ("0", 0.0), - ("2", 2.0), - ("3/2", 1.5) -]) +@pytest.mark.parametrize("test_input,exp_j", [("0", 0.0), ("2", 2.0), ("3/2", 1.5)]) def test_j(test_input, exp_j): tkns = J.parse_string(test_input) assert_almost_equal(tkns[0], exp_j) -@pytest.mark.parametrize("test_input, exp_mult, exp_l",[ - ("1S", 1, "S"), - ("2P", 2, "P"), - ("1S", 1, "S") -]) +@pytest.mark.parametrize( + "test_input, exp_mult, exp_l", [("1S", 1, "S"), ("2P", 2, "P"), ("1S", 1, "S")] +) def test_ls_term(test_input, exp_mult, exp_l): tkns = ls_term.parse_string(test_input) assert tkns["mult"] == exp_mult assert tkns["L"] == exp_l -@pytest.mark.parametrize("test_input, exp_first_j, exp_second_j",[ - ("(0,1/2)", 0.0, 0.5), - ("(3/2,1)", 1.5, 1.0), - ("(0,2)", 0.0, 2.0) -]) +@pytest.mark.parametrize( + "test_input, exp_first_j, exp_second_j", + [("(0,1/2)", 0.0, 0.5), ("(3/2,1)", 1.5, 1.0), ("(0,2)", 0.0, 2.0)], +) def test_jj_term(test_input, exp_first_j, exp_second_j): tkns = jj_term.parse_string(test_input) assert_almost_equal(tkns["first_J"], exp_first_j) assert_almost_equal(tkns["second_J"], exp_second_j) -@pytest.mark.parametrize("test_input, exp_mult, exp_l, exp_parity, exp_j",[ - ("1S0", 1, "S", 0, 0.0), - ("2P<1/2>", 2, "P", 0, 0.5), - ("1S*<4>", 1, "S", 1, 4.0) -]) +@pytest.mark.parametrize( + "test_input, exp_mult, exp_l, exp_parity, exp_j", + [("1S0", 1, "S", 0, 0.0), ("2P<1/2>", 2, "P", 0, 0.5), ("1S*<4>", 1, "S", 1, 4.0)], +) def test_level_w_ls_term(test_input, exp_mult, exp_l, exp_parity, exp_j): tkns = level.parse_string(test_input) assert tkns["ls_term"]["mult"] == exp_mult assert tkns["ls_term"]["L"] == exp_l assert tkns["parity"] == exp_parity - assert_almost_equal(tkns["J"][0], exp_j) # This assertion fails because tkns["J"] is a list and exp_j and integer. e.g. [2] == 2 - # Same thing on lines 62 and 73. + assert_almost_equal( + tkns["J"][0], exp_j + ) # This assertion fails because tkns["J"] is a list and exp_j and integer. e.g. [2] == 2 + # Same thing on lines 62 and 73. -@pytest.mark.parametrize("test_input, exp_first_j, exp_second_j, exp_parity, exp_j",[ - ("(0,1/2)0", 0.0, 0.5, 0, 0.0), - ("(3/2,5/2)<1/2>", 1.5, 2.5, 0, 0.5), - ("(1/2, 2)*<2>", 0.5, 2.0, 1, 2.0) -]) + +@pytest.mark.parametrize( + "test_input, exp_first_j, exp_second_j, exp_parity, exp_j", + [ + ("(0,1/2)0", 0.0, 0.5, 0, 0.0), + ("(3/2,5/2)<1/2>", 1.5, 2.5, 0, 0.5), + ("(1/2, 2)*<2>", 0.5, 2.0, 1, 2.0), + ], +) def test_level_w_jj_term(test_input, exp_first_j, exp_second_j, exp_parity, exp_j): tkns = level.parse_string(test_input) assert tkns["jj_term"]["first_J"] == exp_first_j @@ -62,21 +60,17 @@ def test_level_w_jj_term(test_input, exp_first_j, exp_second_j, exp_parity, exp_ assert_almost_equal(tkns["J"][0], exp_j) -@pytest.mark.parametrize("test_input, exp_parity, exp_j",[ - ("0", 0, 0.0), - ("<1/2>", 0, 0.5), - ("*<2>", 1, 2.0) -]) +@pytest.mark.parametrize( + "test_input, exp_parity, exp_j", + [("0", 0, 0.0), ("<1/2>", 0, 0.5), ("*<2>", 1, 2.0)], +) def test_level_wo_term(test_input, exp_parity, exp_j): tkns = level.parse_string(test_input) assert tkns["parity"] == exp_parity assert_almost_equal(tkns["J"][0], exp_j) -@pytest.mark.parametrize("test_input, exp_parity",[ - ("", 0), - ("*", 1) -]) +@pytest.mark.parametrize("test_input, exp_parity", [("", 0), ("*", 1)]) def test_level_wo_term_and_j(test_input, exp_parity): tkns = level.parse_string(test_input) - assert tkns["parity"] == exp_parity \ No newline at end of file + assert tkns["parity"] == exp_parity diff --git a/carsus/io/tests/test_nist_weightscomp.py b/carsus/io/tests/test_nist_weightscomp.py index 7a3773022..b3e6c09ea 100644 --- a/carsus/io/tests/test_nist_weightscomp.py +++ b/carsus/io/tests/test_nist_weightscomp.py @@ -2,8 +2,7 @@ import pandas as pd from pandas.testing import assert_frame_equal -from carsus.io.nist import (NISTWeightsCompPyparser, - NISTWeightsComp) +from carsus.io.nist import NISTWeightsCompPyparser, NISTWeightsComp from carsus.io.nist.weightscomp_grammar import * test_input = """ @@ -48,23 +47,32 @@ Notes = """ -expected_dict=[ - {'atomic_number': 35, 'atomic_weight_nominal_value': 79.904, 'atomic_weight_std_dev': 3e-3}, - {'atomic_number': 38, 'atomic_weight_nominal_value': 87.62, 'atomic_weight_std_dev': 1e-2}, - {'atomic_number': 43, 'atomic_weight_nominal_value': 97.9072124, 'atomic_weight_std_dev': 36e-7} +expected_dict = [ + { + "atomic_number": 35, + "atomic_weight_nominal_value": 79.904, + "atomic_weight_std_dev": 3e-3, + }, + { + "atomic_number": 38, + "atomic_weight_nominal_value": 87.62, + "atomic_weight_std_dev": 1e-2, + }, + { + "atomic_number": 43, + "atomic_weight_nominal_value": 97.9072124, + "atomic_weight_std_dev": 36e-7, + }, ] -expected_tuples = [ - (43, 97.9072124, 36e-7), - (35, 79.904, 3e-3), - (38, 87.62, 1e-2) -] +expected_tuples = [(43, 97.9072124, 36e-7), (35, 79.904, 3e-3), (38, 87.62, 1e-2)] @pytest.fixture def weightscomp_pyparser(): return NISTWeightsCompPyparser(input_data=test_input) + @pytest.fixture def atomic(weightscomp_pyparser): return weightscomp_pyparser.prepare_atomic_dataframe() @@ -72,8 +80,9 @@ def atomic(weightscomp_pyparser): @pytest.fixture def expected(): - return pd.DataFrame(data=expected_dict, columns=[ATOM_NUM_COL, AW_VAL_COL, AW_SD_COL]).set_index(ATOM_NUM_COL) - + return pd.DataFrame( + data=expected_dict, columns=[ATOM_NUM_COL, AW_VAL_COL, AW_SD_COL] + ).set_index(ATOM_NUM_COL) def test_weightscomp_pyparser_base_index(weightscomp_pyparser): @@ -92,7 +101,7 @@ def test_weightscomp_pyparser_prepare_atomic(atomic, expected): def test_nist_weights_version(): nist_weights = NISTWeightsComp() version = nist_weights.version - version_split = version.split('.') + version_split = version.split(".") assert len(version_split) > 1 - to_int = [ int(i) for i in version_split ] + to_int = [int(i) for i in version_split] diff --git a/carsus/io/tests/test_nist_weightscomp_grammar.py b/carsus/io/tests/test_nist_weightscomp_grammar.py index e41ff40c3..b31f2aa73 100644 --- a/carsus/io/tests/test_nist_weightscomp_grammar.py +++ b/carsus/io/tests/test_nist_weightscomp_grammar.py @@ -5,29 +5,31 @@ from numpy.testing import assert_almost_equal, assert_allclose -@pytest.mark.parametrize("test_input,expected",[ - ("1.00784", 1.00784), - ("6.0151228874", 6.0151228874) -]) +@pytest.mark.parametrize( + "test_input,expected", [("1.00784", 1.00784), ("6.0151228874", 6.0151228874)] +) def test_float_(test_input, expected): tkns = float_.parse_string(test_input) assert_almost_equal(tkns[0], expected) -@pytest.mark.parametrize("test_input,expected",[ - ("16.025750(22)", [16.025750, 2.2e-5]), - ("18.998403163(6)", [18.998403163, 6e-9]) -]) +@pytest.mark.parametrize( + "test_input,expected", + [("16.025750(22)", [16.025750, 2.2e-5]), ("18.998403163(6)", [18.998403163, 6e-9])], +) def test_ufloat(test_input, expected): tkns = ufloat.parse_string(test_input) assert_almost_equal(tkns.nominal_value, expected[0]) assert_almost_equal(tkns.std_dev, expected[1]) -@pytest.mark.parametrize("test_input,expected",[ - ("29.04254(54#)", [29.04254, 54e-5, True]), - ("27.02644(20)", [27.02644, 20e-5, False]) -]) +@pytest.mark.parametrize( + "test_input,expected", + [ + ("29.04254(54#)", [29.04254, 54e-5, True]), + ("27.02644(20)", [27.02644, 20e-5, False]), + ], +) def test_ufloat_theor(test_input, expected): tkns = ufloat_theor.parse_string(test_input) assert_almost_equal(tkns.nominal_value, expected[0]) @@ -35,19 +37,19 @@ def test_ufloat_theor(test_input, expected): assert tkns.theoretical == expected[2] -@pytest.mark.parametrize("test_input,expected",[ - ("m", 'm'), - ("g, r", 'g r') -]) +@pytest.mark.parametrize("test_input,expected", [("m", "m"), ("g, r", "g r")]) def test_notes(test_input, expected): tkns = notes.parse_string(test_input) assert tkns[0] == expected -@pytest.mark.parametrize("test_input,expected",[ - ("20.1797(6)", [20.1797, 6e-4, VAL_SD]), - ("22.98976928(2)", [22.98976928, 2e-8, VAL_SD]) -]) +@pytest.mark.parametrize( + "test_input,expected", + [ + ("20.1797(6)", [20.1797, 6e-4, VAL_SD]), + ("22.98976928(2)", [22.98976928, 2e-8, VAL_SD]), + ], +) def test_atomic_weight_value_uncertainty(test_input, expected): tkns = atomic_weight.parse_string(test_input) assert_almost_equal(tkns.nominal_value, expected[0]) @@ -55,28 +57,23 @@ def test_atomic_weight_value_uncertainty(test_input, expected): assert tkns.type == expected[2] -@pytest.mark.parametrize("test_input,expected",[ - ("[24.304,24.307]", [[24.304,24.307], INTERVAL]) -]) +@pytest.mark.parametrize( + "test_input,expected", [("[24.304,24.307]", [[24.304, 24.307], INTERVAL])] +) def test_atomic_weight_interval(test_input, expected): tkns = atomic_weight.parse_string(test_input) - assert_allclose([tkns.lwr_bnd, tkns.upr_bnd],expected[0]) + assert_allclose([tkns.lwr_bnd, tkns.upr_bnd], expected[0]) assert tkns.type == expected[1] -@pytest.mark.parametrize("test_input,expected",[ - ("[226]", [226, STABLE_MASS_NUM]) -]) +@pytest.mark.parametrize("test_input,expected", [("[226]", [226, STABLE_MASS_NUM])]) def test_atomic_weight_stable_mass_num(test_input, expected): tkns = atomic_weight.parse_string(test_input) assert tkns.stable_mass_number == expected[0] assert tkns.type == expected[1] - -@pytest.mark.parametrize("test_input,expected",[ - ("0.96941(156)", [0.96941, 156e-5]) -]) +@pytest.mark.parametrize("test_input,expected", [("0.96941(156)", [0.96941, 156e-5])]) def test_isotopic_comp(test_input, expected): tkns = isotopic_comp.parse_string(test_input) assert_almost_equal(tkns.nominal_value, expected[0]) @@ -86,13 +83,16 @@ def test_isotopic_comp(test_input, expected): def test_isotopic_comp_one(): tkns = isotopic_comp.parse_string("1") assert tkns.nominal_value == 1 - assert tkns.std_dev == '' + assert tkns.std_dev == "" -@pytest.mark.parametrize("test_input,expected",[ - ("55.00076(75#)", [55.00076, 75e-5, True]), - ("39.963998166(60)", [39.963998166, 60e-9, False]) -]) +@pytest.mark.parametrize( + "test_input,expected", + [ + ("55.00076(75#)", [55.00076, 75e-5, True]), + ("39.963998166(60)", [39.963998166, 60e-9, False]), + ], +) def test_atomic_mass(test_input, expected): tkns = atomic_mass.parse_string(test_input) assert_almost_equal(tkns.nominal_value, expected[0]) @@ -100,83 +100,130 @@ def test_atomic_mass(test_input, expected): assert tkns.theoretical == expected[2] -@pytest.mark.parametrize("test_input,expected",[ - ("He", "He"), - ("Uuo", "Uuo") -]) +@pytest.mark.parametrize("test_input,expected", [("He", "He"), ("Uuo", "Uuo")]) def test_symbol(test_input, expected): tkns = symbol.parse_string(test_input) assert tkns[0] == expected -@pytest.mark.parametrize("test_input,expected",[ - ("Atomic Number", COLUMN_NAMES_MAPPING['Atomic Number']), - ("Standard Atomic Weight", COLUMN_NAMES_MAPPING['Standard Atomic Weight']), - ("Notes", COLUMN_NAMES_MAPPING["Notes"]) -]) +@pytest.mark.parametrize( + "test_input,expected", + [ + ("Atomic Number", COLUMN_NAMES_MAPPING["Atomic Number"]), + ("Standard Atomic Weight", COLUMN_NAMES_MAPPING["Standard Atomic Weight"]), + ("Notes", COLUMN_NAMES_MAPPING["Notes"]), + ], +) def test_column_name(test_input, expected): tkns = column_name.parse_string(test_input) assert tkns[0] == expected -@pytest.mark.parametrize("test_input,expected",[ - ("""Atomic Number = 18 +@pytest.mark.parametrize( + "test_input,expected", + [ + ( + """Atomic Number = 18 Atomic Symbol = Ar Mass Number = 38 Relative Atomic Mass = 37.96273211(21) Isotopic Composition = 0.000629(7) Standard Atomic Weight = 39.948(1) Notes = g,r)""", - {ATOM_NUM_COL:18, SYMBOL_COL: 'Ar', MASS_NUM_COL: 38, - AM_VAL_COL: 37.96273211, AM_SD_COL: 21e-8, AM_THEOR_COL: False, - IC_VAL_COL: 0.000629, IC_SD_COL: 7e-6, - AW_VAL_COL: 39.948, AW_SD_COL: 1e-3, AW_TYPE_COL: VAL_SD, - NOTES_COL: 'g r'}), - - ("""Atomic Number = 19 + { + ATOM_NUM_COL: 18, + SYMBOL_COL: "Ar", + MASS_NUM_COL: 38, + AM_VAL_COL: 37.96273211, + AM_SD_COL: 21e-8, + AM_THEOR_COL: False, + IC_VAL_COL: 0.000629, + IC_SD_COL: 7e-6, + AW_VAL_COL: 39.948, + AW_SD_COL: 1e-3, + AW_TYPE_COL: VAL_SD, + NOTES_COL: "g r", + }, + ), + ( + """Atomic Number = 19 Atomic Symbol = K Mass Number = 54 Relative Atomic Mass = 53.99463(64#) Isotopic Composition = Standard Atomic Weight = 39.0983(1) Notes = """, - {ATOM_NUM_COL: 19, SYMBOL_COL: 'K', MASS_NUM_COL: 54, - AM_VAL_COL: 53.99463, AM_SD_COL: 64e-5, AM_THEOR_COL: True, - AW_VAL_COL: 39.0983, AW_SD_COL: 1e-4, AW_TYPE_COL: VAL_SD}), - - ("""Atomic Number = 3 + { + ATOM_NUM_COL: 19, + SYMBOL_COL: "K", + MASS_NUM_COL: 54, + AM_VAL_COL: 53.99463, + AM_SD_COL: 64e-5, + AM_THEOR_COL: True, + AW_VAL_COL: 39.0983, + AW_SD_COL: 1e-4, + AW_TYPE_COL: VAL_SD, + }, + ), + ( + """Atomic Number = 3 Atomic Symbol = Li Mass Number = 5 Relative Atomic Mass = 5.012538(54) Isotopic Composition = Standard Atomic Weight = [6.938,6.997] Notes = m""", - {ATOM_NUM_COL: 3, SYMBOL_COL: 'Li', MASS_NUM_COL: 5, - AM_VAL_COL: 5.012538, AM_SD_COL: 54e-6, AM_THEOR_COL: False, - AW_LWR_BND_COL: 6.938, AW_UPR_BND_COL: 6.997, AW_TYPE_COL: INTERVAL, - NOTES_COL: "m"}), - - ("""Atomic Number = 95 + { + ATOM_NUM_COL: 3, + SYMBOL_COL: "Li", + MASS_NUM_COL: 5, + AM_VAL_COL: 5.012538, + AM_SD_COL: 54e-6, + AM_THEOR_COL: False, + AW_LWR_BND_COL: 6.938, + AW_UPR_BND_COL: 6.997, + AW_TYPE_COL: INTERVAL, + NOTES_COL: "m", + }, + ), + ( + """Atomic Number = 95 Atomic Symbol = Am Mass Number = 230 Relative Atomic Mass = 230.04609(14#) Isotopic Composition = Standard Atomic Weight = Notes = """, - {ATOM_NUM_COL: 95, SYMBOL_COL: 'Am', MASS_NUM_COL: 230, - AM_VAL_COL: 230.04609, AM_SD_COL: 14e-5, AM_THEOR_COL: True}), - - ("""Atomic Number = 86 + { + ATOM_NUM_COL: 95, + SYMBOL_COL: "Am", + MASS_NUM_COL: 230, + AM_VAL_COL: 230.04609, + AM_SD_COL: 14e-5, + AM_THEOR_COL: True, + }, + ), + ( + """Atomic Number = 86 Atomic Symbol = Rn Mass Number = 211 Relative Atomic Mass = 210.9906011(73) Isotopic Composition = Standard Atomic Weight = [222] Notes = """, - {ATOM_NUM_COL: 86, SYMBOL_COL: 'Rn', MASS_NUM_COL: 211, - AM_VAL_COL: 210.9906011, AM_SD_COL: 73e-7, AM_THEOR_COL: False, - AW_TYPE_COL: STABLE_MASS_NUM, AW_STABLE_MASS_NUM_COL: 222}) -]) + { + ATOM_NUM_COL: 86, + SYMBOL_COL: "Rn", + MASS_NUM_COL: 211, + AM_VAL_COL: 210.9906011, + AM_SD_COL: 73e-7, + AM_THEOR_COL: False, + AW_TYPE_COL: STABLE_MASS_NUM, + AW_STABLE_MASS_NUM_COL: 222, + }, + ), + ], +) def test_isotope(test_input, expected): tkns = isotope.parse_string(test_input) tkns_dict = to_flat_dict(tkns) diff --git a/carsus/io/tests/test_nndc.py b/carsus/io/tests/test_nndc.py index 796a2c658..71628eeb5 100644 --- a/carsus/io/tests/test_nndc.py +++ b/carsus/io/tests/test_nndc.py @@ -26,17 +26,32 @@ def decay_data_http(nndc_reader_http): return nndc_reader_http.decay_data -@pytest.mark.parametrize("element, z, parent_e_level, decay_mode, " - "decay_mode_value, radiation, rad_subtype, rad_energy", [ - ("Ni", 28, 0.0, "EC", 100.00, "g", "XR ka2", 6.915), - ("Mn", 25, 377.749, "IT", 1.75, "e", "Auger K", 5.190) - ]) -def test_get_nuclear_dataframe(nndc_reader, element, z, parent_e_level, decay_mode, decay_mode_value, - radiation, rad_subtype, rad_energy): +@pytest.mark.parametrize( + "element, z, parent_e_level, decay_mode, " + "decay_mode_value, radiation, rad_subtype, rad_energy", + [ + ("Ni", 28, 0.0, "EC", 100.00, "g", "XR ka2", 6.915), + ("Mn", 25, 377.749, "IT", 1.75, "e", "Auger K", 5.190), + ], +) +def test_get_nuclear_dataframe( + nndc_reader, + element, + z, + parent_e_level, + decay_mode, + decay_mode_value, + radiation, + rad_subtype, + rad_energy, +): decay_data = NNDCReader._get_nuclear_decay_dataframe(nndc_reader) - df = decay_data[(decay_data.Element == element) & (decay_data["Decay Mode"] == decay_mode) & - (decay_data["Parent E(level)"] == parent_e_level) & - (decay_data["Rad subtype"] == rad_subtype)] + df = decay_data[ + (decay_data.Element == element) + & (decay_data["Decay Mode"] == decay_mode) + & (decay_data["Parent E(level)"] == parent_e_level) + & (decay_data["Rad subtype"] == rad_subtype) + ] row = df.iloc[0] assert row.Z == z @@ -48,17 +63,34 @@ def test_get_nuclear_dataframe(nndc_reader, element, z, parent_e_level, decay_mo assert row.loc["Isotope"] -@pytest.mark.parametrize("index, element, z, parent_e_level, metastable, " - "decay_mode, decay_mode_value, radiation, rad_subtype, rad_energy", [ - ("Ni56", "Ni", 28, 0.0, False, "EC", 100.00, "g", "XR ka2", 6.915), - ("Mn52", "Mn", 25, 377.749, True, "IT", 1.75, "g", "XR ka1", 5.899) - ]) -def test_prepare_nuclear_dataframes(nndc_reader, index, element, z, parent_e_level, metastable, - decay_mode, decay_mode_value, radiation, rad_subtype, rad_energy): +@pytest.mark.parametrize( + "index, element, z, parent_e_level, metastable, " + "decay_mode, decay_mode_value, radiation, rad_subtype, rad_energy", + [ + ("Ni56", "Ni", 28, 0.0, False, "EC", 100.00, "g", "XR ka2", 6.915), + ("Mn52", "Mn", 25, 377.749, True, "IT", 1.75, "g", "XR ka1", 5.899), + ], +) +def test_prepare_nuclear_dataframes( + nndc_reader, + index, + element, + z, + parent_e_level, + metastable, + decay_mode, + decay_mode_value, + radiation, + rad_subtype, + rad_energy, +): decay_data = NNDCReader._prepare_nuclear_dataframes(nndc_reader) - df = decay_data[(decay_data.index == index) & (decay_data["Decay Mode"] == decay_mode) & - (decay_data["Decay Mode Value"] == decay_mode_value) & - (decay_data["Rad subtype"] == rad_subtype)] + df = decay_data[ + (decay_data.index == index) + & (decay_data["Decay Mode"] == decay_mode) + & (decay_data["Decay Mode Value"] == decay_mode_value) + & (decay_data["Rad subtype"] == rad_subtype) + ] row = df.loc[index] assert row["Element"] == element @@ -68,16 +100,33 @@ def test_prepare_nuclear_dataframes(nndc_reader, index, element, z, parent_e_lev assert row["Rad Energy"] == rad_energy -@pytest.mark.parametrize("index, element, z, parent_e_level, metastable, " - "decay_mode, decay_mode_value, radiation, rad_subtype, rad_energy", [ - ("Ni56", "Ni", 28, 0.0, False, "EC", 100.00, "g", "XR ka2", 6.915), - ("Mn52", "Mn", 25, 377.749, True, "IT", 1.75, "g", "XR ka1", 5.899) - ]) -def test_nndc_reader_decay_data(decay_data, index, element, z, parent_e_level, metastable, - decay_mode, decay_mode_value, radiation, rad_subtype, rad_energy): - df = decay_data[(decay_data.index == index) & (decay_data["Decay Mode"] == decay_mode) & - (decay_data["Decay Mode Value"] == decay_mode_value) & - (decay_data["Rad subtype"] == rad_subtype)] +@pytest.mark.parametrize( + "index, element, z, parent_e_level, metastable, " + "decay_mode, decay_mode_value, radiation, rad_subtype, rad_energy", + [ + ("Ni56", "Ni", 28, 0.0, False, "EC", 100.00, "g", "XR ka2", 6.915), + ("Mn52", "Mn", 25, 377.749, True, "IT", 1.75, "g", "XR ka1", 5.899), + ], +) +def test_nndc_reader_decay_data( + decay_data, + index, + element, + z, + parent_e_level, + metastable, + decay_mode, + decay_mode_value, + radiation, + rad_subtype, + rad_energy, +): + df = decay_data[ + (decay_data.index == index) + & (decay_data["Decay Mode"] == decay_mode) + & (decay_data["Decay Mode Value"] == decay_mode_value) + & (decay_data["Rad subtype"] == rad_subtype) + ] row = df.loc[index] assert row["Element"] == element @@ -87,16 +136,28 @@ def test_nndc_reader_decay_data(decay_data, index, element, z, parent_e_level, m assert row["Rad Energy"] == rad_energy -@pytest.mark.parametrize("index, parent_e_level, decay_mode, decay_mode_value, " - "half_life, metastable", [ - ("Mn52", 0.0, "EC", 100.00, 483062.4, False), - ("Mn52", 377.749, "IT", 1.75, 1266.0, True), - ("Mn52", 377.749, "EC", 98.22, 1266.0, True) - ]) -def test_nndc_reader_metastable(decay_data, index, parent_e_level, decay_mode, - decay_mode_value, half_life, metastable): - df = decay_data[(decay_data.index == index) & (decay_data["Decay Mode"] == decay_mode) & - (decay_data["Parent E(level)"] == parent_e_level)] +@pytest.mark.parametrize( + "index, parent_e_level, decay_mode, decay_mode_value, " "half_life, metastable", + [ + ("Mn52", 0.0, "EC", 100.00, 483062.4, False), + ("Mn52", 377.749, "IT", 1.75, 1266.0, True), + ("Mn52", 377.749, "EC", 98.22, 1266.0, True), + ], +) +def test_nndc_reader_metastable( + decay_data, + index, + parent_e_level, + decay_mode, + decay_mode_value, + half_life, + metastable, +): + df = decay_data[ + (decay_data.index == index) + & (decay_data["Decay Mode"] == decay_mode) + & (decay_data["Parent E(level)"] == parent_e_level) + ] # picking just the first row of the dataframe for a particular nuclei row = df.iloc[0] @@ -105,16 +166,33 @@ def test_nndc_reader_metastable(decay_data, index, parent_e_level, decay_mode, assert row["Metastable"] == metastable -@pytest.mark.parametrize("index, element, z, parent_e_level, metastable, " - "decay_mode, decay_mode_value, radiation, rad_subtype, rad_energy", [ - ("Ni56", "Ni", 28, 0.0, False, "EC", 100.00, "g", "XR ka2", 6.915), - ("Mn52", "Mn", 25, 377.749, True, "IT", 1.75, "g", "XR ka1", 5.899) - ]) -def test_nndc_reader_decay_data_http(decay_data_http, index, element, z, parent_e_level, metastable, - decay_mode, decay_mode_value, radiation, rad_subtype, rad_energy): - df = decay_data_http[(decay_data_http.index == index) & (decay_data_http["Decay Mode"] == decay_mode) & - (decay_data_http["Decay Mode Value"] == decay_mode_value) & - (decay_data_http["Rad subtype"] == rad_subtype)] +@pytest.mark.parametrize( + "index, element, z, parent_e_level, metastable, " + "decay_mode, decay_mode_value, radiation, rad_subtype, rad_energy", + [ + ("Ni56", "Ni", 28, 0.0, False, "EC", 100.00, "g", "XR ka2", 6.915), + ("Mn52", "Mn", 25, 377.749, True, "IT", 1.75, "g", "XR ka1", 5.899), + ], +) +def test_nndc_reader_decay_data_http( + decay_data_http, + index, + element, + z, + parent_e_level, + metastable, + decay_mode, + decay_mode_value, + radiation, + rad_subtype, + rad_energy, +): + df = decay_data_http[ + (decay_data_http.index == index) + & (decay_data_http["Decay Mode"] == decay_mode) + & (decay_data_http["Decay Mode Value"] == decay_mode_value) + & (decay_data_http["Rad subtype"] == rad_subtype) + ] row = df.loc[index] assert row["Element"] == element diff --git a/carsus/io/tests/test_util.py b/carsus/io/tests/test_util.py index af3da2349..4cd0266a6 100644 --- a/carsus/io/tests/test_util.py +++ b/carsus/io/tests/test_util.py @@ -3,22 +3,32 @@ from numpy.testing import assert_allclose from carsus.io.util import to_flat_dict, to_nom_val_and_std_dev -@pytest.mark.parametrize("test_input,expected",[ - ('isotopic_comp = 2.1234132(12)', - {'isotopic_comp_nominal_value':'2.1234132', 'isotopic_comp_std_dev':'12'}), - ('atomic_weight = 6.8083492038(23)', - {'atomic_weight_nominal_value':'6.8083492038', 'atomic_weight_std_dev':'23'}) -]) +@pytest.mark.parametrize( + "test_input,expected", + [ + ( + "isotopic_comp = 2.1234132(12)", + {"isotopic_comp_nominal_value": "2.1234132", "isotopic_comp_std_dev": "12"}, + ), + ( + "atomic_weight = 6.8083492038(23)", + { + "atomic_weight_nominal_value": "6.8083492038", + "atomic_weight_std_dev": "23", + }, + ), + ], +) def test_to_flat_dict(test_input, expected, entry): tkns = entry.parse_string(test_input) tkns_dict = to_flat_dict(tkns) assert tkns_dict == expected -@pytest.mark.parametrize("test_input,expected",[ - ([4.10923, 4.11364], (4.111435, 0.002205)) -]) +@pytest.mark.parametrize( + "test_input,expected", [([4.10923, 4.11364], (4.111435, 0.002205))] +) def test_to_nom_val_and_std_dev(test_input, expected): mu, sigma = to_nom_val_and_std_dev(test_input) - assert_allclose((mu, sigma), expected) \ No newline at end of file + assert_allclose((mu, sigma), expected) diff --git a/carsus/io/tests/test_zeta.py b/carsus/io/tests/test_zeta.py index fb5cc544c..44f0dd52c 100644 --- a/carsus/io/tests/test_zeta.py +++ b/carsus/io/tests/test_zeta.py @@ -2,18 +2,22 @@ import pandas as pd from carsus.io.zeta import KnoxLongZeta, ZETA_DATA_URL + @pytest.fixture def reference_file_path(): return "carsus/data/knox_long_recombination_zeta.dat" + def test_knoxlongzeta_init_with_default_url(): zeta = KnoxLongZeta() assert zeta.fname == ZETA_DATA_URL + def test_knoxlongzeta_init_with_custom_file(reference_file_path): zeta = KnoxLongZeta(fname=reference_file_path) assert zeta.fname == reference_file_path + def test_knoxlongzeta_prepare_data(reference_file_path): zeta = KnoxLongZeta(reference_file_path) zeta._prepare_data() @@ -22,4 +26,4 @@ def test_knoxlongzeta_prepare_data(reference_file_path): assert isinstance(zeta.base, pd.DataFrame) assert list(zeta.base.columns) == expected_columns assert zeta.base.index.names == ["atomic_number", "ion_charge"] - assert zeta.version == "a1d4bed2982e8d6a4f8b0076bf637e49" \ No newline at end of file + assert zeta.version == "a1d4bed2982e8d6a4f8b0076bf637e49" diff --git a/carsus/io/util.py b/carsus/io/util.py index 9098f91b7..775165ba2 100644 --- a/carsus/io/util.py +++ b/carsus/io/util.py @@ -8,7 +8,8 @@ import requests from requests.adapters import HTTPAdapter, Retry -def to_flat_dict(tokens, parent_key='', sep='_'): + +def to_flat_dict(tokens, parent_key="", sep="_"): """ Creates a flattened dictionary from the named values in tokens. @@ -51,7 +52,7 @@ def to_nom_val_and_std_dev(interval): """ lwr_bnd, upr_bnd = interval - sigma = (upr_bnd - lwr_bnd)/2 + sigma = (upr_bnd - lwr_bnd) / 2 mu = lwr_bnd + sigma return (mu, sigma) @@ -79,12 +80,14 @@ def convert_species_tuple2chianti_str(species): """ atomic_number, ion_charge = species - chianti_ion_name = convert_atomic_number2symbol(atomic_number).lower() + '_' + str(ion_charge + 1) + chianti_ion_name = ( + convert_atomic_number2symbol(atomic_number).lower() + "_" + str(ion_charge + 1) + ) return chianti_ion_name def read_from_buffer(fname): - """Read a local or remote file into a buffer and get its MD5 + """Read a local or remote file into a buffer and get its MD5 checksum. To be used with `pandas.read_csv` or `pandas.read_fwf` functions. @@ -97,13 +100,13 @@ def read_from_buffer(fname): ------- bytes, str data from text file, MD5 checksum - """ + """ if fname.startswith("http"): response = retry_request(fname, "get") data = response.content else: - with open(fname, 'rb') as f: + with open(fname, "rb") as f: data = f.read() buffer = BytesIO(data) @@ -147,6 +150,7 @@ def retry_request( sess.close() return response + def get_lvl_index2id(df, levels_all): """ Matches level indexes with level IDs for a given DataFrame. @@ -176,6 +180,7 @@ def get_lvl_index2id(df, levels_all): return df + def create_artificial_fully_ionized(levels): """ Returns a DataFrame with fully ionized levels. @@ -184,9 +189,7 @@ def create_artificial_fully_ionized(levels): fully_ionized_levels = [] for atomic_number, _ in levels.groupby("atomic_number"): - fully_ionized_levels.append( - (-1, atomic_number, atomic_number, 0, 0.0, 1, True) - ) + fully_ionized_levels.append((-1, atomic_number, atomic_number, 0, 0.0, 1, True)) levels_columns = [ "level_id", @@ -198,9 +201,7 @@ def create_artificial_fully_ionized(levels): "metastable", ] - fully_ionized_levels_dtypes = [ - (key, levels.dtypes[key]) for key in levels_columns - ] + fully_ionized_levels_dtypes = [(key, levels.dtypes[key]) for key in levels_columns] fully_ionized_levels = np.array( fully_ionized_levels, dtype=fully_ionized_levels_dtypes @@ -208,6 +209,7 @@ def create_artificial_fully_ionized(levels): return pd.DataFrame(data=fully_ionized_levels) + def exclude_artificial_levels(levels_df): """Removes artificially created levels from a dataframe of levels @@ -221,4 +223,4 @@ def exclude_artificial_levels(levels_df): pandas.DataFrame Filtered levels dataframe """ - return levels_df.loc[levels_df["level_id"] != -1].set_index("level_id") \ No newline at end of file + return levels_df.loc[levels_df["level_id"] != -1].set_index("level_id") diff --git a/carsus/io/vald/__init__.py b/carsus/io/vald/__init__.py index 665f58fe0..0bd8b1009 100644 --- a/carsus/io/vald/__init__.py +++ b/carsus/io/vald/__init__.py @@ -1 +1 @@ -from .vald import VALDReader \ No newline at end of file +from .vald import VALDReader diff --git a/carsus/io/zeta.py b/carsus/io/zeta.py index e4330a1cd..da79bfb65 100644 --- a/carsus/io/zeta.py +++ b/carsus/io/zeta.py @@ -5,6 +5,7 @@ ZETA_DATA_URL = "https://media.githubusercontent.com/media/tardis-sn/carsus-db/master/zeta/knox_long_recombination_zeta.dat" + class KnoxLongZeta(BaseParser): """ Attributes @@ -13,7 +14,6 @@ class KnoxLongZeta(BaseParser): """ def __init__(self, fname=None): - if fname is None: self.fname = ZETA_DATA_URL @@ -31,16 +31,10 @@ def _prepare_data(self): self.version = checksum zeta_df = pd.read_csv( - buffer, - usecols=range(1, 23), - names=names, - comment="#", - sep=r"\s+") - - self.base = ( - pd.DataFrame(zeta_df).set_index( - ["atomic_number", "ion_charge"]) + buffer, usecols=range(1, 23), names=names, comment="#", sep=r"\s+" ) + self.base = pd.DataFrame(zeta_df).set_index(["atomic_number", "ion_charge"]) + columns = [float(c) for c in self.base.columns] self.base.columns = pd.Index(columns, name="temp", dtype=np.float64) diff --git a/carsus/tests/data/gftest.all b/carsus/tests/data/gftest.all index aa1cb033f..b2ff9e80e 100644 --- a/carsus/tests/data/gftest.all +++ b/carsus/tests/data/gftest.all @@ -60,4 +60,4 @@ 1369.4886 -1.374 7.05-4006160.000 0.0 s3p *3P 4013460.000 1.0 s3d 3D 11.07 0.00 0.00NBS 0 0 0 0.000 0 0.000 0 0 0 0 1369.4886 -0.754 7.05 4006160.000 2.0 s3p *3P 4013460.000 3.0 s3d 3D 11.06 0.00 0.00NBS 0 0 0 0.000 0 0.000 0 0 0 0 1369.4886 -1.504 7.05 4006160.000 1.0 s3p *3P 4013460.000 1.0 s3d 3D 11.07 0.00 0.00NBS 0 0 0 0.000 0 0.000 0 0 0 0 - 3815.7533 -1.347 7.05 4013460.000 2.0 s3d 3D 4016390.000 1.0 CONTINUUM 11.80 0.00 0.00NBS 0 0 0 0.000 0 0.000 0 0 0 0 \ No newline at end of file + 3815.7533 -1.347 7.05 4013460.000 2.0 s3d 3D 4016390.000 1.0 CONTINUUM 11.80 0.00 0.00NBS 0 0 0 0.000 0 0.000 0 0 0 0 diff --git a/carsus/tests/data/knox_long_recombination_zeta.dat b/carsus/tests/data/knox_long_recombination_zeta.dat index 748f64890..c7a676978 100644 --- a/carsus/tests/data/knox_long_recombination_zeta.dat +++ b/carsus/tests/data/knox_long_recombination_zeta.dat @@ -1,10 +1,10 @@ #name atomic_number ion_number t02000 t04000 t06000 t08000 t10000 t12000 t14000 t16000 t18000 t20000 t22000 t24000 t26000 t28000 t30000 t32000 t34000 t36000 t38000 t40000 Frac 1 1 0.3390 0.3846 0.4132 0.4338 0.4498 0.4627 0.4735 0.4828 0.4908 0.4978 0.5040 0.5096 0.5147 0.5193 0.5235 0.5273 0.5309 0.5342 0.5373 0.5401 -Frac 1 2 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. +Frac 1 2 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. #above is a dummy line (no recombination INTO a state with no electrons) which helps us to read the data Frac 2 1 0.4012 0.4669 0.5073 0.5363 0.5587 0.5768 0.5920 0.6050 0.6163 0.6263 0.6352 0.6432 0.6505 0.6571 0.6632 0.6689 0.6741 0.6790 0.6835 0.6878 Frac 2 2 0.2660 0.2993 0.3220 0.3395 0.3538 0.3659 0.3764 0.3856 0.3939 0.4013 0.4081 0.4143 0.4201 0.4254 0.4304 0.4350 0.4394 0.4435 0.4474 0.4511 -Frac 2 3 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. +Frac 2 3 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. #above is a dummy line (no recombination INTO a state with no electrons) which helps us to read the data Frac 3 1 0.2044 0.2549 0.2923 0.3218 0.3459 0.3662 0.3838 0.3992 0.4128 0.4251 0.4362 0.4463 0.4556 0.4641 0.4721 0.4795 0.4864 0.4929 0.4990 0.5047 Frac 3 2 0.2110 0.2429 0.2664 0.2854 0.3013 0.3151 0.3272 0.3381 0.3479 0.3568 0.3651 0.3727 0.3797 0.3864 0.3926 0.3984 0.4039 0.4091 0.4141 0.4188 @@ -24,7 +24,7 @@ Frac 6 3 0.3092 0.3468 0.3719 0.3910 0.4066 0.4197 0.4311 0.4412 0.4501 0.4582 Frac 6 4 0.1645 0.1680 0.1731 0.1787 0.1844 0.1900 0.1955 0.2009 0.2060 0.2110 0.2158 0.2205 0.2249 0.2293 0.2334 0.2375 0.2414 0.2451 0.2488 0.2523 Frac 6 5 0.1481 0.1568 0.1638 0.1700 0.1757 0.1809 0.1858 0.1903 0.1947 0.1988 0.2027 0.2064 0.2099 0.2133 0.2166 0.2198 0.2228 0.2258 0.2286 0.2314 Frac 6 6 0.1951 0.2128 0.2246 0.2339 0.2417 0.2485 0.2546 0.2602 0.2653 0.2700 0.2744 0.2785 0.2824 0.2860 0.2895 0.2929 0.2961 0.2991 0.3021 0.3049 -Frac 6 7 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. +Frac 6 7 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. #above is a dummy line (no recombination INTO a state with no electrons) which helps us to read the data Frac 7 1 0.4497 0.5323 0.5825 0.6179 0.6449 0.6665 0.6844 0.6995 0.7125 0.7240 0.7341 0.7431 0.7513 0.7587 0.7655 0.7717 0.7774 0.7828 0.7877 0.7923 Frac 7 2 0.4377 0.4964 0.5348 0.5634 0.5861 0.6050 0.6211 0.6350 0.6473 0.6583 0.6682 0.6771 0.6853 0.6929 0.6999 0.7063 0.7124 0.7180 0.7234 0.7284 @@ -33,7 +33,7 @@ Frac 7 4 0.2813 0.3061 0.3198 0.3296 0.3374 0.3441 0.3499 0.3552 0.3600 0.3644 Frac 7 5 0.1800 0.1832 0.1866 0.1903 0.1941 0.1980 0.2019 0.2057 0.2094 0.2130 0.2166 0.2201 0.2234 0.2267 0.2299 0.2330 0.2360 0.2390 0.2418 0.2446 Frac 7 6 0.1337 0.1423 0.1488 0.1544 0.1594 0.1640 0.1684 0.1724 0.1762 0.1799 0.1833 0.1866 0.1898 0.1928 0.1957 0.1986 0.2013 0.2039 0.2065 0.2090 Frac 7 7 0.1882 0.2047 0.2156 0.2240 0.2311 0.2373 0.2428 0.2478 0.2524 0.2566 0.2606 0.2643 0.2679 0.2712 0.2744 0.2775 0.2804 0.2832 0.2858 0.2884 -Frac 7 8 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. +Frac 7 8 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. #above is a dummy line (no recombination INTO a state with no electrons) which helps us to read the data Frac 8 1 0.3621 0.4425 0.4939 0.5313 0.5604 0.5840 0.6038 0.6206 0.6353 0.6483 0.6598 0.6701 0.6795 0.6881 0.6960 0.7032 0.7100 0.7162 0.7221 0.7276 Frac 8 2 0.3429 0.4036 0.4449 0.4764 0.5018 0.5231 0.5413 0.5573 0.5714 0.5840 0.5955 0.6059 0.6155 0.6243 0.6325 0.6401 0.6472 0.6539 0.6602 0.6661 @@ -43,7 +43,7 @@ Frac 8 5 0.2627 0.2901 0.3043 0.3141 0.3216 0.3278 0.3332 0.3379 0.3422 0.3462 Frac 8 6 0.1599 0.1653 0.1698 0.1740 0.1782 0.1822 0.1861 0.1899 0.1935 0.1971 0.2006 0.2039 0.2071 0.2103 0.2134 0.2163 0.2192 0.2220 0.2248 0.2275 Frac 8 7 0.1244 0.1329 0.1388 0.1438 0.1482 0.1523 0.1560 0.1596 0.1629 0.1661 0.1691 0.1720 0.1748 0.1774 0.1800 0.1825 0.1849 0.1873 0.1895 0.1917 Frac 8 8 0.1827 0.1987 0.2089 0.2167 0.2233 0.2289 0.2340 0.2386 0.2428 0.2467 0.2503 0.2538 0.2570 0.2601 0.2630 0.2658 0.2685 0.2711 0.2735 0.2759 -Frac 8 9 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. +Frac 8 9 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. #above is a dummy line (no recombination INTO a state with no electrons) which helps us to read the data Frac 9 1 0.6675 0.7112 0.7390 0.7590 0.7745 0.7870 0.7974 0.8064 0.8141 0.8209 0.8269 0.8324 0.8373 0.8418 0.8459 0.8497 0.8532 0.8565 0.8595 0.8623 Frac 9 2 0.0461 0.1164 0.1678 0.2083 0.2418 0.2703 0.2951 0.3169 0.3364 0.3540 0.3700 0.3847 0.3982 0.4108 0.4224 0.4333 0.4435 0.4532 0.4622 0.4708 @@ -64,7 +64,7 @@ Frac 10 7 0.2306 0.2637 0.2802 0.2909 0.2989 0.3051 0.3104 0.3148 0.3188 0.3224 Frac 10 8 0.1462 0.1523 0.1558 0.1589 0.1618 0.1645 0.1672 0.1699 0.1724 0.1749 0.1774 0.1798 0.1821 0.1844 0.1867 0.1889 0.1910 0.1931 0.1952 0.1972 Frac 10 9 0.1170 0.1248 0.1295 0.1332 0.1363 0.1392 0.1419 0.1444 0.1468 0.1491 0.1512 0.1533 0.1554 0.1573 0.1592 0.1611 0.1628 0.1646 0.1663 0.1679 Frac 10 10 0.1725 0.1883 0.1978 0.2049 0.2107 0.2158 0.2202 0.2242 0.2279 0.2313 0.2345 0.2374 0.2403 0.2429 0.2455 0.2479 0.2502 0.2525 0.2547 0.2567 -Frac 10 11 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. +Frac 10 11 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. #above is a dummy line (no recombination INTO a state with no electrons) which helps us to read the data Frac 11 1 0.2564 0.2930 0.3204 0.3419 0.3595 0.3743 0.3871 0.3982 0.4081 0.4169 0.4249 0.4321 0.4388 0.4449 0.4505 0.4558 0.4607 0.4653 0.4696 0.4736 Frac 11 2 0.1967 0.2254 0.2447 0.2594 0.2714 0.2814 0.2902 0.2979 0.3047 0.3109 0.3166 0.3218 0.3266 0.3310 0.3352 0.3391 0.3427 0.3462 0.3495 0.3526 @@ -417,4 +417,3 @@ Frac 28 25 0.6784 0.6305 0.6000 0.5771 0.5588 0.5434 0.5301 0.5185 0.5081 0.4987 Frac 28 26 0.0859 0.1091 0.1189 0.1245 0.1281 0.1307 0.1326 0.1341 0.1353 0.1363 0.1372 0.1380 0.1386 0.1393 0.1398 0.1403 0.1408 0.1413 0.1417 0.1421 Frac 28 27 0.0645 0.0839 0.0920 0.0965 0.0994 0.1014 0.1030 0.1041 0.1051 0.1059 0.1065 0.1071 0.1076 0.1081 0.1085 0.1088 0.1092 0.1095 0.1098 0.1101 Frac 28 28 0.1149 0.1385 0.1502 0.1579 0.1636 0.1680 0.1717 0.1748 0.1776 0.1800 0.1823 0.1843 0.1861 0.1878 0.1895 0.1910 0.1924 0.1938 0.1950 0.1963 - diff --git a/carsus/tests/data/nndc/052_6.csv b/carsus/tests/data/nndc/052_6.csv index 15a042806..4bf9d0406 100644 --- a/carsus/tests/data/nndc/052_6.csv +++ b/carsus/tests/data/nndc/052_6.csv @@ -28,4 +28,4 @@ A,Element,Z,N,Parent E(level),Uncertainty,JPi,Decay Mode,Decay Mode Value,Decay 52,Mn,25,27,0.0,,6+,EC,100,,4711.2,19,5.591 D 3,483062.4,3.45E+3 3,0.94 3,0,71.0 10,0,2.664 24,1166 9,0,0,0,0,4.70E+3 3,4711.2 19,0 AP,0 % AP,52Cr,e,Auger K,4.78,,,,44.4,5,0.002122,23 52,Mn,25,27,0.0,,6+,EC,100,,4711.2,19,5.591 D 3,483062.4,3.45E+3 3,0.94 3,0,71.0 10,0,2.664 24,1166 9,0,0,0,0,4.70E+3 3,4711.2 19,0 AP,0 % AP,52Cr,bp,,33.91,82,73.3,19,0.00118,14,4.0E-7,5 52,Mn,25,27,0.0,,6+,EC,100,,4711.2,19,5.591 D 3,483062.4,3.45E+3 3,0.94 3,0,71.0 10,0,2.664 24,1166 9,0,0,0,0,4.70E+3 3,4711.2 19,0 AP,0 % AP,52Cr,bp,,241.59,80,575.3,19,29.4,4,0.0710,10 -52,Mn,25,27,0.0,,6+,EC,100,,4711.2,19,5.591 D 3,483062.4,3.45E+3 3,0.94 3,0,71.0 10,0,2.664 24,1166 9,0,0,0,0,4.70E+3 3,4711.2 19,0 AP,0 % AP,52Cr,bp av,,242,5,,,29.4,4,0.0710,17 \ No newline at end of file +52,Mn,25,27,0.0,,6+,EC,100,,4711.2,19,5.591 D 3,483062.4,3.45E+3 3,0.94 3,0,71.0 10,0,2.664 24,1166 9,0,0,0,0,4.70E+3 3,4711.2 19,0 AP,0 % AP,52Cr,bp av,,242,5,,,29.4,4,0.0710,17 diff --git a/carsus/tests/data/nndc/052_7.csv b/carsus/tests/data/nndc/052_7.csv index aa6f27fd0..9b49fd9c4 100644 --- a/carsus/tests/data/nndc/052_7.csv +++ b/carsus/tests/data/nndc/052_7.csv @@ -28,4 +28,4 @@ A,Element,Z,N,Parent E(level),Uncertainty,JPi,Decay Mode,Decay Mode Value,Decay 52,Mn,25,27,377.749,5,2+,EC,98.22,5,4711.5,19,21.1 M 2,1266.0,2402 9,0.0223 7,0,1132 6,0,0.0633 5,1466 7,0,0,0,0,5000 13,4999 3,0 AP,0 % AP,52Cr,bp,,383.12,83,905.5,19,0.161,8,6.2E-4,3 52,Mn,25,27,377.749,5,2+,EC,98.22,5,4711.5,19,21.1 M 2,1266.0,2402 9,0.0223 7,0,1132 6,0,0.0633 5,1466 7,0,0,0,0,5000 13,4999 3,0 AP,0 % AP,52Cr,bp,,469.59,84,1102.5,19,0.0373,20,1.75E-4,9 52,Mn,25,27,377.749,5,2+,EC,98.22,5,4711.5,19,21.1 M 2,1266.0,2402 9,0.0223 7,0,1132 6,0,0.0633 5,1466 7,0,0,0,0,5000 13,4999 3,0 AP,0 % AP,52Cr,bp,,1174.01,90,2633.2,19,96.4,5,1.131,6 -52,Mn,25,27,377.749,5,2+,EC,98.22,5,4711.5,19,21.1 M 2,1266.0,2402 9,0.0223 7,0,1132 6,0,0.0633 5,1466 7,0,0,0,0,5000 13,4999 3,0 AP,0 % AP,52Cr,bp av,,1172,9,,,96.6,5,1.132,10 \ No newline at end of file +52,Mn,25,27,377.749,5,2+,EC,98.22,5,4711.5,19,21.1 M 2,1266.0,2402 9,0.0223 7,0,1132 6,0,0.0633 5,1466 7,0,0,0,0,5000 13,4999 3,0 AP,0 % AP,52Cr,bp av,,1172,9,,,96.6,5,1.132,10 diff --git a/carsus/tests/data/nndc/052_8.csv b/carsus/tests/data/nndc/052_8.csv index 864bd2cb3..446ba86ea 100644 --- a/carsus/tests/data/nndc/052_8.csv +++ b/carsus/tests/data/nndc/052_8.csv @@ -10,4 +10,4 @@ A,Element,Z,N,Parent E(level),Uncertainty,JPi,Decay Mode,Decay Mode Value,Decay 52,Mn,25,27,377.749,5,2+,IT,1.750,20,,,21.1 M 2,1266.0,6.34 11,1.96E-5 6,0,0,0.00445 7,4.78E-5 8,0,0,0,0,0,6.34 11,6.61 8,0.27 14,4.1 % 21,52Mn,e,CE K,371.199,5,,,0.001066,19,3.96E-6,7 52,Mn,25,27,377.749,5,2+,IT,1.750,20,,,21.1 M 2,1266.0,6.34 11,1.96E-5 6,0,0,0.00445 7,4.78E-5 8,0,0,0,0,0,6.34 11,6.61 8,0.27 14,4.1 % 21,52Mn,e,CE L,376.969,5,,,1.144E-4,22,4.31E-7,8 52,Mn,25,27,377.749,5,2+,IT,1.750,20,,,21.1 M 2,1266.0,6.34 11,1.96E-5 6,0,0,0.00445 7,4.78E-5 8,0,0,0,0,0,6.34 11,6.61 8,0.27 14,4.1 % 21,52Mn,e,CE M,377.738,5,,,1.54E-5,3,5.83E-8,11 -52,Mn,25,27,377.749,5,2+,IT,1.750,20,,,21.1 M 2,1266.0,6.34 11,1.96E-5 6,0,0,0.00445 7,4.78E-5 8,0,0,0,0,0,6.34 11,6.61 8,0.27 14,4.1 % 21,52Mn,e,CE N,377.738,5,,,6.38E-7,12,2.41E-9,4 \ No newline at end of file +52,Mn,25,27,377.749,5,2+,IT,1.750,20,,,21.1 M 2,1266.0,6.34 11,1.96E-5 6,0,0,0.00445 7,4.78E-5 8,0,0,0,0,0,6.34 11,6.61 8,0.27 14,4.1 % 21,52Mn,e,CE N,377.738,5,,,6.38E-7,12,2.41E-9,4 diff --git a/carsus/tests/data/nndc/056_8.csv b/carsus/tests/data/nndc/056_8.csv index 2c923b560..3bfe08d8b 100644 --- a/carsus/tests/data/nndc/056_8.csv +++ b/carsus/tests/data/nndc/056_8.csv @@ -16,4 +16,4 @@ A,Element,Z,N,Parent E(level),Uncertainty,JPi,Decay Mode,Decay Mode Value,Decay 56,Ni,28,28,0.0,,0+,EC,100,,2136,12,6.075 D 10,524880.0,1718 16,2.33 7,0,7.E-4 4,0,4.32 3,415 12,0,0,0,0,2140 19,2136 12,0 AP,0 % AP,56Co,bp,,65,5,144,12,6.E-4,6,4.E-7,4 56,Ni,28,28,0.0,,0+,EC,100,,2136,12,6.075 D 10,524880.0,1718 16,2.33 7,0,7.E-4 4,0,4.32 3,415 12,0,0,0,0,2140 19,2136 12,0 AP,0 % AP,56Co,bp,,408,5,956,12,3.E-5,3,1.2E-7,12 56,Ni,28,28,0.0,,0+,EC,100,,2136,12,6.075 D 10,524880.0,1718 16,2.33 7,0,7.E-4 4,0,4.32 3,415 12,0,0,0,0,2140 19,2136 12,0 AP,0 % AP,56Co,bp,,478,5,1114,12,3.E-5,3,1.4E-7,14 -56,Ni,28,28,0.0,,0+,EC,100,,2136,12,6.075 D 10,524880.0,1718 16,2.33 7,0,7.E-4 4,0,4.32 3,415 12,0,0,0,0,2140 19,2136 12,0 AP,0 % AP,56Co,bp av,,1.0E+2,11,,,7.E-4,6,0,AP \ No newline at end of file +56,Ni,28,28,0.0,,0+,EC,100,,2136,12,6.075 D 10,524880.0,1718 16,2.33 7,0,7.E-4 4,0,4.32 3,415 12,0,0,0,0,2140 19,2136 12,0 AP,0 % AP,56Co,bp av,,1.0E+2,11,,,7.E-4,6,0,AP diff --git a/carsus/util/__init__.py b/carsus/util/__init__.py index c6079607a..b24a8eca3 100644 --- a/carsus/util/__init__.py +++ b/carsus/util/__init__.py @@ -4,13 +4,14 @@ # functions. from carsus.util.helpers import ( - convert_camel2snake, - convert_atomic_number2symbol, - convert_symbol2atomic_number, - convert_wavelength_air2vacuum, - convert_wavelength_vacuum2air, - get_data_path, query_columns - ) + convert_camel2snake, + convert_atomic_number2symbol, + convert_symbol2atomic_number, + convert_wavelength_air2vacuum, + convert_wavelength_vacuum2air, + get_data_path, + query_columns, +) from carsus.util.selected import parse_selected_atoms, parse_selected_species from carsus.util.hash import serialize_pandas_object, hash_pandas_object diff --git a/carsus/util/colored_logger.py b/carsus/util/colored_logger.py index 371302953..66e9cd5e1 100644 --- a/carsus/util/colored_logger.py +++ b/carsus/util/colored_logger.py @@ -1,29 +1,31 @@ import logging -''' + +""" Code for Custom Logger Classes (ColoredFormatter and ColorLogger) and its helper function (formatter_message) is used from this thread http://stackoverflow.com/questions/384076/how-can-i-color-python-logging-output -''' +""" + def formatter_message(message, use_color=True): - ''' + """ Helper Function used for Coloring Log Output - ''' - #These are the sequences need to get colored ouput + """ + # These are the sequences need to get colored ouput RESET_SEQ = "\033[0m" BOLD_SEQ = "\033[1m" if use_color: - message = message.replace( - "$RESET", RESET_SEQ).replace("$BOLD", BOLD_SEQ) + message = message.replace("$RESET", RESET_SEQ).replace("$BOLD", BOLD_SEQ) else: message = message.replace("$RESET", "").replace("$BOLD", "") return message class ColoredFormatter(logging.Formatter): - ''' + """ Custom logger class for changing levels color - ''' + """ + def __init__(self, msg, use_color=True): logging.Formatter.__init__(self, msg) self.use_color = use_color @@ -33,24 +35,26 @@ def format(self, record): RESET_SEQ = "\033[0m" BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) COLORS = { - 'WARNING': YELLOW, - 'INFO': WHITE, - 'DEBUG': BLUE, - 'CRITICAL': YELLOW, - 'ERROR': RED + "WARNING": YELLOW, + "INFO": WHITE, + "DEBUG": BLUE, + "CRITICAL": YELLOW, + "ERROR": RED, } levelname = record.levelname if self.use_color and levelname in COLORS: - levelname_color = COLOR_SEQ % ( - 30 + COLORS[levelname]) + levelname + RESET_SEQ + levelname_color = ( + COLOR_SEQ % (30 + COLORS[levelname]) + levelname + RESET_SEQ + ) record.levelname = levelname_color return logging.Formatter.format(self, record) class ColoredLogger(logging.Logger): - ''' + """ Custom logger class with multiple destinations - ''' + """ + FORMAT = "[$BOLD%(name)-20s$RESET][%(levelname)-18s] %(message)s ($BOLD%(filename)s$RESET:%(lineno)d)" COLOR_FORMAT = formatter_message(FORMAT, True) @@ -63,4 +67,4 @@ def __init__(self, name): console.setFormatter(color_formatter) self.addHandler(console) - return \ No newline at end of file + return diff --git a/carsus/util/hash.py b/carsus/util/hash.py index 89e61e941..a62eda6fe 100644 --- a/carsus/util/hash.py +++ b/carsus/util/hash.py @@ -1,6 +1,7 @@ import hashlib import pickle + def serialize_pandas_object(pd_object): """Serialize Pandas objects with Pickle. @@ -42,6 +43,6 @@ def hash_pandas_object(pd_object, algorithm="md5"): hash_func = getattr(hashlib, algorithm) else: - raise ValueError('algorithm not supported') + raise ValueError("algorithm not supported") - return hash_func(serialize_pandas_object(pd_object)).hexdigest() \ No newline at end of file + return hash_func(serialize_pandas_object(pd_object)).hexdigest() diff --git a/carsus/util/helpers.py b/carsus/util/helpers.py index d684ae117..19a290452 100644 --- a/carsus/util/helpers.py +++ b/carsus/util/helpers.py @@ -7,17 +7,25 @@ def get_data_path(fname): - return os.path.join( - os.path.dirname(carsus.__file__), 'data', fname - ) + return os.path.join(os.path.dirname(carsus.__file__), "data", fname) -ATOMIC_SYMBOLS_DATA = np.genfromtxt(get_data_path('basic_atomic_data.csv'), skip_header=1, - delimiter=',', usecols=(0, 1), names=['atomic_number', 'symbol'], encoding='utf-8', dtype=None) -SYMBOL2ATOMIC_NUMBER = OrderedDict(zip(ATOMIC_SYMBOLS_DATA['symbol'], - ATOMIC_SYMBOLS_DATA['atomic_number'])) -ATOMIC_NUMBER2SYMBOL = OrderedDict(zip(ATOMIC_SYMBOLS_DATA['atomic_number'], - ATOMIC_SYMBOLS_DATA['symbol'])) +ATOMIC_SYMBOLS_DATA = np.genfromtxt( + get_data_path("basic_atomic_data.csv"), + skip_header=1, + delimiter=",", + usecols=(0, 1), + names=["atomic_number", "symbol"], + encoding="utf-8", + dtype=None, +) + +SYMBOL2ATOMIC_NUMBER = OrderedDict( + zip(ATOMIC_SYMBOLS_DATA["symbol"], ATOMIC_SYMBOLS_DATA["atomic_number"]) +) +ATOMIC_NUMBER2SYMBOL = OrderedDict( + zip(ATOMIC_SYMBOLS_DATA["atomic_number"], ATOMIC_SYMBOLS_DATA["symbol"]) +) def convert_camel2snake(name): @@ -26,8 +34,8 @@ def convert_camel2snake(name): http://stackoverflow.com/questions/1175208/elegant-python-function-to-convert-camelcase-to-snake-case """ - s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) - return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() + s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name) + return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower() def convert_wavelength_vacuum2air(wavelength_vacuum): @@ -43,10 +51,10 @@ def convert_wavelength_vacuum2air(wavelength_vacuum): float Air wavelength in Angstroms """ - sigma2 = (1e4/wavelength_vacuum)**2. - fact = 1.0 + 5.792105e-2/(238.0185 - sigma2) + 1.67917e-3/(57.362 - sigma2) + sigma2 = (1e4 / wavelength_vacuum) ** 2.0 + fact = 1.0 + 5.792105e-2 / (238.0185 - sigma2) + 1.67917e-3 / (57.362 - sigma2) - return wavelength_vacuum/fact + return wavelength_vacuum / fact def convert_wavelength_air2vacuum(wavelength_air): @@ -62,8 +70,8 @@ def convert_wavelength_air2vacuum(wavelength_air): float Vacuum wavelength in Angstroms """ - sigma2 = (1e4/wavelength_air)**2. - fact = 1.0 + 5.792105e-2/(238.0185 - sigma2) + 1.67917e-3/(57.362 - sigma2) + sigma2 = (1e4 / wavelength_air) ** 2.0 + fact = 1.0 + 5.792105e-2 / (238.0185 - sigma2) + 1.67917e-3 / (57.362 - sigma2) return wavelength_air * fact @@ -77,4 +85,4 @@ def convert_symbol2atomic_number(symbol): def query_columns(query): - return [v['name'] for v in query.column_descriptions] + return [v["name"] for v in query.column_descriptions] diff --git a/carsus/util/selected.py b/carsus/util/selected.py index cf2ca2377..aab968e6a 100644 --- a/carsus/util/selected.py +++ b/carsus/util/selected.py @@ -16,13 +16,22 @@ """ from carsus.util.helpers import convert_symbol2atomic_number -from pyparsing import Literal, Suppress, delimitedList,\ - Word, alphas, nums, Optional, Group - -hyphen = Suppress(Literal('-')) +from pyparsing import ( + Literal, + Suppress, + delimitedList, + Word, + alphas, + nums, + Optional, + Group, +) + +hyphen = Suppress(Literal("-")) element = Word(alphas) + def parse_element(tokens): symbol = tokens[0] symbol = symbol[:1].upper() + symbol[1:].lower() @@ -33,6 +42,7 @@ def parse_element(tokens): return atomic_number + element.setParseAction(parse_element) element_range = element + hyphen + element @@ -52,28 +62,34 @@ def parse_element(tokens): ion_numbers = delimitedList(ion_number ^ ion_number_range) ion_numbers.setParseAction(lambda x: sorted(set(x))) -species_entry = Group(selected_atoms).setResultsName('atomic_numbers') + \ - Group(Optional(ion_numbers)).setResultsName('ion_numbers') +species_entry = Group(selected_atoms).setResultsName("atomic_numbers") + Group( + Optional(ion_numbers) +).setResultsName("ion_numbers") def parse_species_entry(tokens): species = list() - if tokens['ion_numbers']: - species = [(atomic_number, ion_number) - for atomic_number in tokens['atomic_numbers'] - for ion_number in tokens['ion_numbers'] - if atomic_number > ion_number] + if tokens["ion_numbers"]: + species = [ + (atomic_number, ion_number) + for atomic_number in tokens["atomic_numbers"] + for ion_number in tokens["ion_numbers"] + if atomic_number > ion_number + ] else: - species = [(atomic_number, ion_number) - for atomic_number in tokens['atomic_numbers'] - for ion_number in range(atomic_number)] + species = [ + (atomic_number, ion_number) + for atomic_number in tokens["atomic_numbers"] + for ion_number in range(atomic_number) + ] return species + species_entry.setParseAction(parse_species_entry) -selected_species = delimitedList(species_entry, delim=';') +selected_species = delimitedList(species_entry, delim=";") selected_species.setParseAction(lambda x: sorted(set(x))) diff --git a/carsus/util/tests/test_helpers.py b/carsus/util/tests/test_helpers.py index 04b522ec4..8cb591d09 100644 --- a/carsus/util/tests/test_helpers.py +++ b/carsus/util/tests/test_helpers.py @@ -1,33 +1,29 @@ import pytest -from carsus.util.helpers import convert_camel2snake, \ - convert_atomic_number2symbol, convert_symbol2atomic_number +from carsus.util.helpers import ( + convert_camel2snake, + convert_atomic_number2symbol, + convert_symbol2atomic_number, +) -@pytest.mark.parametrize("input_camel_case, expected_snake_case", [ - ("Atom", "atom"), - ("CHIANTIIon", "chianti_ion"), - ("LevelJJTerm", "level_jj_term") -]) +@pytest.mark.parametrize( + "input_camel_case, expected_snake_case", + [("Atom", "atom"), ("CHIANTIIon", "chianti_ion"), ("LevelJJTerm", "level_jj_term")], +) def test_convert_camel2snake(input_camel_case, expected_snake_case): assert convert_camel2snake(input_camel_case) == expected_snake_case -@pytest.mark.parametrize("atomic_number, expected_symbol", [ - (1, "H"), - (14, "Si"), - (30, "Zn"), - (118, "Uuo") -]) +@pytest.mark.parametrize( + "atomic_number, expected_symbol", [(1, "H"), (14, "Si"), (30, "Zn"), (118, "Uuo")] +) def test_convert_atomic_number2symbol(atomic_number, expected_symbol): assert convert_atomic_number2symbol(atomic_number) == expected_symbol -@pytest.mark.parametrize("symbol, expected_atomic_number", [ - ("H", 1), - ("Si", 14), - ("Zn", 30), - ("Uuo", 118) -]) +@pytest.mark.parametrize( + "symbol, expected_atomic_number", [("H", 1), ("Si", 14), ("Zn", 30), ("Uuo", 118)] +) def test_convert_symbol2atomic_number(symbol, expected_atomic_number): - assert convert_symbol2atomic_number(symbol) == expected_atomic_number \ No newline at end of file + assert convert_symbol2atomic_number(symbol) == expected_atomic_number diff --git a/carsus/util/tests/test_selected.py b/carsus/util/tests/test_selected.py index 140d18535..6e75bab7e 100644 --- a/carsus/util/tests/test_selected.py +++ b/carsus/util/tests/test_selected.py @@ -1,86 +1,112 @@ import pytest -from carsus.util.selected import element, element_range, \ - selected_atoms, parse_selected_atoms, species_entry, \ - parse_selected_species +from carsus.util.selected import ( + element, + element_range, + selected_atoms, + parse_selected_atoms, + species_entry, + parse_selected_species, +) -@pytest.mark.parametrize("test_input, exp_atomic_number",[ - ("H", 1), - ("Zn", 30), - ("Uuo", 118), - ("h", 1) -]) +@pytest.mark.parametrize( + "test_input, exp_atomic_number", [("H", 1), ("Zn", 30), ("Uuo", 118), ("h", 1)] +) def test_element(test_input, exp_atomic_number): tokens = element.parse_string(test_input) assert tokens[0] == exp_atomic_number -@pytest.mark.parametrize("test_input, exp_atomic_numbers",[ - ("H-Li", [1, 2, 3]), - ("H-Zn", list(range(1,31))), - ("si-s", [14, 15, 16]) -]) +@pytest.mark.parametrize( + "test_input, exp_atomic_numbers", + [("H-Li", [1, 2, 3]), ("H-Zn", list(range(1, 31))), ("si-s", [14, 15, 16])], +) def test_element_range(test_input, exp_atomic_numbers): tokens = element_range.parse_string(test_input) assert tokens.asList() == exp_atomic_numbers -@pytest.mark.parametrize("test_input, exp_atomic_numbers",[ - ("H", [1,]), - ("H-Zn", list(range(1,31))), - ("h, si-s", [1, 14, 15, 16]), - ('he, h-li', [1, 2, 3]) -]) +@pytest.mark.parametrize( + "test_input, exp_atomic_numbers", + [ + ( + "H", + [ + 1, + ], + ), + ("H-Zn", list(range(1, 31))), + ("h, si-s", [1, 14, 15, 16]), + ("he, h-li", [1, 2, 3]), + ], +) def test_selected_atoms(test_input, exp_atomic_numbers): tokens = selected_atoms.parse_string(test_input) assert tokens.asList() == exp_atomic_numbers -@pytest.mark.parametrize("selected_atoms, expected_list", [ - ("H", [1]), - ("H-Li", [1, 2, 3]), - ("H, Be-B", [1, 4, 5]), - ("h, be-b", [1, 4, 5]), - (" h , be - b ", [1, 4, 5]) -]) +@pytest.mark.parametrize( + "selected_atoms, expected_list", + [ + ("H", [1]), + ("H-Li", [1, 2, 3]), + ("H, Be-B", [1, 4, 5]), + ("h, be-b", [1, 4, 5]), + (" h , be - b ", [1, 4, 5]), + ], +) def test_parse_selected_atoms(selected_atoms, expected_list): assert parse_selected_atoms(selected_atoms) == expected_list -@pytest.mark.parametrize("invalid_selected_atoms", [ - "Foo", "H-Foo", "H, Al-Foo" -]) +@pytest.mark.parametrize("invalid_selected_atoms", ["Foo", "H-Foo", "H, Al-Foo"]) def test_parse_selected_atoms_raises_invalid(invalid_selected_atoms): with pytest.raises(ValueError): parse_selected_atoms(invalid_selected_atoms) -@pytest.mark.parametrize("test_input, expected_list", [ - ("H 0", [(1, 0)]), - ("H, Li 0", [(1, 0), (3, 0)]), - ("H-Li 0-1", [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1)]), - ("H-C 0, 4", [(1, 0), (2, 0), (3, 0), (4, 0), - (5, 0), (5, 4), (6, 0), (6, 4)]), - ('H-Li', [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2)]) -]) +@pytest.mark.parametrize( + "test_input, expected_list", + [ + ("H 0", [(1, 0)]), + ("H, Li 0", [(1, 0), (3, 0)]), + ("H-Li 0-1", [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1)]), + ("H-C 0, 4", [(1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (5, 4), (6, 0), (6, 4)]), + ("H-Li", [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2)]), + ], +) def test_parse_species_entry(test_input, expected_list): tokens = species_entry.parse_string(test_input) assert tokens.asList() == expected_list -@pytest.mark.parametrize("test_species, expected_list", [ - ("H 0; li 0", [(1, 0), (3, 0)]), - ("H, Li 0", [(1, 0), (3, 0)]), - ("li 0; h", [(1, 0), (3, 0)]), - ("h 0; h-li 0", [(1, 0), (2, 0), (3, 0)]), - ("H-Li 0-1", [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1)]), - ("H-C 0, 4", [(1, 0), (2, 0), (3, 0), (4, 0), - (5, 0), (5, 4), (6, 0), (6, 4)]), - ('H-Li', [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2)]), - ('fe 12; ni-zn 23-25', [(26, 12), (28, 23), (28, 24), (28, 25), - (29, 23), (29, 24), (29, 25), - (30, 23), (30, 24), (30, 25)]) -]) +@pytest.mark.parametrize( + "test_species, expected_list", + [ + ("H 0; li 0", [(1, 0), (3, 0)]), + ("H, Li 0", [(1, 0), (3, 0)]), + ("li 0; h", [(1, 0), (3, 0)]), + ("h 0; h-li 0", [(1, 0), (2, 0), (3, 0)]), + ("H-Li 0-1", [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1)]), + ("H-C 0, 4", [(1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (5, 4), (6, 0), (6, 4)]), + ("H-Li", [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2)]), + ( + "fe 12; ni-zn 23-25", + [ + (26, 12), + (28, 23), + (28, 24), + (28, 25), + (29, 23), + (29, 24), + (29, 25), + (30, 23), + (30, 24), + (30, 25), + ], + ), + ], +) def test_parse_selected_species(test_species, expected_list): assert parse_selected_species(test_species) == expected_list diff --git a/docs/_templates/autosummary/base.rst b/docs/_templates/autosummary/base.rst index 9cabaf523..16217194a 100644 --- a/docs/_templates/autosummary/base.rst +++ b/docs/_templates/autosummary/base.rst @@ -1,2 +1,2 @@ {% extends "autosummary_core/base.rst" %} -{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} \ No newline at end of file +{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} diff --git a/docs/_templates/autosummary/module.rst b/docs/_templates/autosummary/module.rst index f38315b22..230cd6e20 100644 --- a/docs/_templates/autosummary/module.rst +++ b/docs/_templates/autosummary/module.rst @@ -1,2 +1,2 @@ {% extends "autosummary_core/module.rst" %} -{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} \ No newline at end of file +{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} diff --git a/docs/conf.py b/docs/conf.py index 38ab289fc..da359ab30 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -33,23 +33,26 @@ try: from sphinx_astropy.conf.v1 import * # noqa except ImportError: - print('ERROR: the documentation requires the sphinx-astropy package to be installed') + print( + "ERROR: the documentation requires the sphinx-astropy package to be installed" + ) sys.exit(1) # Get configuration information from setup.cfg from configparser import ConfigParser + conf = ConfigParser() -conf.read([os.path.join(os.path.dirname(__file__), '..', 'setup.cfg')]) -setup_cfg = dict(conf.items('metadata')) +conf.read([os.path.join(os.path.dirname(__file__), "..", "setup.cfg")]) +setup_cfg = dict(conf.items("metadata")) # -- General configuration ---------------------------------------------------- # By default, highlight as Python 3. -highlight_language = 'python3' +highlight_language = "python3" # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.2' +# needs_sphinx = '1.2' # To perform a Sphinx version check that needs to be more specific than # major.minor, call `check_sphinx_version("X.Y.Z")` here. @@ -57,9 +60,9 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns.append('_templates') -exclude_patterns.append('_build') -exclude_patterns.append('**.ipynb_checkpoints') +exclude_patterns.append("_templates") +exclude_patterns.append("_build") +exclude_patterns.append("**.ipynb_checkpoints") # This is added to the end of RST files - a good place to put substitutions to @@ -68,24 +71,24 @@ """ extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.autosummary', - 'sphinx.ext.intersphinx', - 'sphinx.ext.mathjax', - 'sphinx.ext.todo', - 'sphinx_copybutton', - 'sphinxcontrib.apidoc', - 'sphinxcontrib.bibtex', - 'matplotlib.sphinxext.plot_directive', - 'nbsphinx', - 'numpydoc', + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.intersphinx", + "sphinx.ext.mathjax", + "sphinx.ext.todo", + "sphinx_copybutton", + "sphinxcontrib.apidoc", + "sphinxcontrib.bibtex", + "matplotlib.sphinxext.plot_directive", + "nbsphinx", + "numpydoc", ] apidoc_output_dir = "api" -apidoc_excluded_paths = ["*tests*", "*setup_package*", "*conftest*", "*version*" ] +apidoc_excluded_paths = ["*tests*", "*setup_package*", "*conftest*", "*version*"] apidoc_separate_modules = True -bibtex_bibfiles = ['references.bib', 'publications.bib'] +bibtex_bibfiles = ["references.bib", "publications.bib"] ## https://github.com/phn/pytpm/issues/3#issuecomment-12133978 numpydoc_show_class_members = False @@ -99,20 +102,19 @@ # -- Project information ------------------------------------------------------ # This does not *have* to match the package name, but typically does -project = setup_cfg['name'] -author = setup_cfg['author'] -copyright = '2016-{0}, {1}'.format( - datetime.datetime.now().year, setup_cfg['author']) +project = setup_cfg["name"] +author = setup_cfg["author"] +copyright = "2016-{0}, {1}".format(datetime.datetime.now().year, setup_cfg["author"]) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. -import_module(setup_cfg['name']) -package = sys.modules[setup_cfg['name']] +import_module(setup_cfg["name"]) +package = sys.modules[setup_cfg["name"]] # The short X.Y version. -version = package.__version__.split('-', 1)[0] +version = package.__version__.split("-", 1)[0] # The full version, including alpha/beta/rc tags. release = package.__version__ @@ -130,43 +132,44 @@ # Add any paths that contain custom themes here, relative to this directory. # To use a different custom theme, add the directory containing the theme. import sphinx_rtd_theme + html_theme_path = sphinx_rtd_theme.get_html_theme_path() # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. To override the custom theme, set this to the # name of a builtin theme or the name of a custom theme in html_theme_path. -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" html_theme_options = { - 'logotext1': 'carsus', # white, semi-bold - 'logotext2': '', # orange, light - 'logotext3': ':docs' # white, light - } + "logotext1": "carsus", # white, semi-bold + "logotext2": "", # orange, light + "logotext3": ":docs", # white, light +} # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} html_static_path = ["_static"] templates_path = ["_templates"] # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = '' +# html_logo = '' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -html_favicon = 'tardis_logo.ico' +html_favicon = "tardis_logo.ico" # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '' +# html_last_updated_fmt = '' # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -html_title = '{0} v{1}'.format(project, release) +html_title = "{0} v{1}".format(project, release) # Output file base name for HTML help builder. -htmlhelp_basename = project + 'doc' +htmlhelp_basename = project + "doc" # Prefixes that are ignored for sorting the Python module index modindex_common_prefix = ["carsus."] @@ -176,38 +179,37 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [('index', project + '.tex', project + u' Documentation', - author, 'manual')] +latex_documents = [ + ("index", project + ".tex", project + " Documentation", author, "manual") +] # -- Options for manual page output ------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [('index', project.lower(), project + u' Documentation', - [author], 1)] +man_pages = [("index", project.lower(), project + " Documentation", [author], 1)] # -- Options for the edit_on_github extension --------------------------------- -if setup_cfg.get('edit_on_github').lower() == 'true': - - extensions += ['sphinx_astropy.ext.edit_on_github'] +if setup_cfg.get("edit_on_github").lower() == "true": + extensions += ["sphinx_astropy.ext.edit_on_github"] - edit_on_github_project = setup_cfg['github_project'] + edit_on_github_project = setup_cfg["github_project"] edit_on_github_branch = "main" edit_on_github_source_root = "" edit_on_github_doc_root = "docs" # -- Resolving issue number to links in changelog ----------------------------- -github_issues_url = 'https://github.com/{0}/issues/'.format(setup_cfg['github_project']) +github_issues_url = "https://github.com/{0}/issues/".format(setup_cfg["github_project"]) # -- Options for linkcheck output ------------------------------------------- linkcheck_retry = 5 linkcheck_ignore = [ - r'https://github\.com/tardis-sn/carsus/(?:issues|pull)/\d+', + r"https://github\.com/tardis-sn/carsus/(?:issues|pull)/\d+", ] linkcheck_timeout = 180 linkcheck_anchors = False diff --git a/docs/development/index.rst b/docs/development/index.rst index c98e1905c..d40bbbac2 100644 --- a/docs/development/index.rst +++ b/docs/development/index.rst @@ -2,7 +2,7 @@ Developer Notes *************** -The following pages contain useful information for developers about +The following pages contain useful information for developers about testing, debugging, the actual state of the code, etc. .. toctree:: diff --git a/docs/development/testing.rst b/docs/development/testing.rst index e5a53f7db..fdd854fed 100644 --- a/docs/development/testing.rst +++ b/docs/development/testing.rst @@ -2,9 +2,9 @@ Running Tests ************* -Carsus's tests are based on the -`AstroPy Package Template `_ -and `pytest `_. Then, running simple tests on your machine is +Carsus's tests are based on the +`AstroPy Package Template `_ +and `pytest `_. Then, running simple tests on your machine is straightforward: .. code :: @@ -23,7 +23,6 @@ A set of flags can be appended to the above command to run different kinds of te - `--refdata=/path/to/carsus-refdata` Run tests marked with the ``@with_refdata`` decorator. Requires the `tardis-sn/carsus-refdata `_ repository. - + - `--cov=carsus --cov-report=xml --cov-report=html` Get code coverage results using the `pytest-cov `_ plugin. - diff --git a/docs/development/units.rst b/docs/development/units.rst index 1dd476b5a..8010c5398 100644 --- a/docs/development/units.rst +++ b/docs/development/units.rst @@ -1,10 +1,10 @@ *************** Units in Carsus *************** - + In the recent years, much effort has been made to make Carsus easier to use and develop. A large piece of code was rewritten to avoid using an intermediate SQL database between -the readers and the final atomic data. +the readers and the final atomic data. This resulted in an simpler, faster and more maintainable Carsus codebase. But we lost some features in the process, being the most important one the tracking of physical diff --git a/docs/installation.rst b/docs/installation.rst index b30cae188..e940545e2 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -13,7 +13,7 @@ Prerequisites export XUVTOP=/path/to/chianti/root -#. *(optional)*. Download and extract the `CMFGEN Atomic Data `_. +#. *(optional)*. Download and extract the `CMFGEN Atomic Data `_. ==================== Clone the Repository diff --git a/docs/physics/einstein_coeff.rst b/docs/physics/einstein_coeff.rst index 1e09dd7d6..e77b15678 100644 --- a/docs/physics/einstein_coeff.rst +++ b/docs/physics/einstein_coeff.rst @@ -2,4 +2,4 @@ Einstein Coefficients ********************* -*Coming soon...* \ No newline at end of file +*Coming soon...* diff --git a/docs/publications.bib b/docs/publications.bib index 4a9eaa420..bc030d532 100644 --- a/docs/publications.bib +++ b/docs/publications.bib @@ -1,6 +1,6 @@ % Encoding: UTF-8 @misc{gillanders2021constraints, - title={Constraints on the presence of platinum and gold in the spectra of the kilonova AT2017gfo}, + title={Constraints on the presence of platinum and gold in the spectra of the kilonova AT2017gfo}, author={James H. Gillanders and Michael McCann and Stuart A. Sim. Stephen J. Smartt and Connor P. Ballance}, year={2021}, eprint={2101.08271}, @@ -18,4 +18,4 @@ @misc{passaro_e_a_2019_4062427 year = 2019, publisher = {Zenodo}, doi = {10.5281/zenodo.4062427}, -} \ No newline at end of file +} diff --git a/docs/reference/level_index_id_number.rst b/docs/reference/level_index_id_number.rst index 5b8114a07..c2182c567 100644 --- a/docs/reference/level_index_id_number.rst +++ b/docs/reference/level_index_id_number.rst @@ -6,7 +6,7 @@ Differences between level_index, level_id and level_number level_index ============= -DataFrame index for each electronic energy level. Different for each ion. +DataFrame index for each electronic energy level. Different for each ion. Third index for energy levels after atomic number and ion charge. Used in GFALL, Chianti and CMFGEN levels DataFrames. @@ -25,7 +25,7 @@ together. level_number ============= -Index in the order of the level energies within each species. -For example, species Si II has level_number 0 to n. +Index in the order of the level energies within each species. +For example, species Si II has level_number 0 to n. Often used in a multi-index with atomic_number, ion_number. -Connects upper and lower energy levels. \ No newline at end of file +Connects upper and lower energy levels. diff --git a/docs/reference/notation.rst b/docs/reference/notation.rst index ace0a8496..fb4913c7b 100644 --- a/docs/reference/notation.rst +++ b/docs/reference/notation.rst @@ -2,17 +2,17 @@ Notation in Carsus ****************** -* **Use "0" for neutral elements.** +* **Use "0" for neutral elements.** Example: ``Si 0`` is equivalent to :math:`\text{Si I}`, ``Si 1`` to :math:`\text{Si II}`, etc. * **Use "-" to grab intervals of consecsutive elements or species.** - Example: ``H-He`` selects :math:`\text{H I}` and :math:`\text{H II}` plus :math:`\text{He I}`, :math:`\text{He II}` and :math:`\text{He III}`, while ``C 0-2`` selects :math:`\text{C I}`, :math:`\text{C II}` and :math:`\text{C III}`. + Example: ``H-He`` selects :math:`\text{H I}` and :math:`\text{H II}` plus :math:`\text{He I}`, :math:`\text{He II}` and :math:`\text{He III}`, while ``C 0-2`` selects :math:`\text{C I}`, :math:`\text{C II}` and :math:`\text{C III}`. -* **Use "," to grab non-consecutive species.** +* **Use "," to grab non-consecutive species.** Example: ``Si 0, 2`` selects :math:`\text{Si I}` and :math:`\text{Si III}`. - + * **Use ";" to grab non-consecutive elements.** Example: ``H; Li`` selects :math:`\text{H I}` and :math:`\text{H II}` plus :math:`\text{Li I}`, :math:`\text{Li II}`, :math:`\text{Li III}` and :math:`\text{Li IV}`. * **Finally, mix all the above syntax as needed.** - Example: ``H; C-Si; Fe 1,3``. \ No newline at end of file + Example: ``H; C-Si; Fe 1,3``. diff --git a/setup.py b/setup.py index 99ad6017e..65129fa3e 100755 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ http://docs.astropy.org/en/latest/development/testguide.html#running-tests """ -if 'test' in sys.argv: +if "test" in sys.argv: print(TEST_HELP) sys.exit(1) @@ -59,7 +59,7 @@ http://docs.astropy.org/en/latest/install.html#builddocs """ -if 'build_docs' in sys.argv or 'build_sphinx' in sys.argv: +if "build_docs" in sys.argv or "build_sphinx" in sys.argv: print(DOCS_HELP) sys.exit(1) @@ -74,7 +74,10 @@ version = '{version}' """.lstrip() -setup(use_scm_version={'write_to': os.path.join('carsus', 'version.py'), - 'write_to_template': VERSION_TEMPLATE, - #'version_scheme': 'calver-by-date', - }) +setup( + use_scm_version={ + "write_to": os.path.join("carsus", "version.py"), + "write_to_template": VERSION_TEMPLATE, + #'version_scheme': 'calver-by-date', + } +)