├── .github └── workflows │ ├── anaconda-publish.yml │ ├── black.yml │ ├── conda-build-test.yml │ ├── docker_ci_main.yml │ └── python-publish.yml ├── .gitignore ├── CITATION.cff ├── LICENSE ├── README.md ├── conda ├── conda_build_config.yaml └── meta.yaml ├── pyproject.toml ├── requirements.txt ├── src └── openmc_data_downloader │ ├── __init__.py │ ├── cross_sections_directory.py │ ├── fendl_3.1d_cross_sections.xml │ ├── nndc_7.1_cross_sections.xml │ ├── nndc_8.0_cross_sections.xml │ ├── tendl_2019_cross_sections.xml │ ├── terminal_cmd.py │ └── utils.py └── tests ├── test_command_line_usage.py ├── test_cross_sections_directory.py ├── test_functions.py └── test_use_in_openmc.py /.github/workflows/anaconda-publish.yml: -------------------------------------------------------------------------------- 1 | name: anaconda-publish 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: [published] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | container: continuumio/miniconda3:4.10.3 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up conda 17 | run: | 18 | apt-get --allow-releaseinfo-change update 19 | conda install -y anaconda-client conda-build 20 | conda config --set anaconda_upload no 21 | - name: Build and publish to conda 22 | env: 23 | ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_TOKEN }} 24 | run: | 25 | conda build conda -c conda-forge --config-file conda/conda_build_config.yaml 26 | conda convert /opt/conda/conda-bld/linux-64/*.tar.bz2 --platform osx-64 --platform win-64 -o /opt/conda/conda-bld/ 27 | anaconda upload -f /opt/conda/conda-bld/*/*.tar.bz2 28 | -------------------------------------------------------------------------------- /.github/workflows/black.yml: -------------------------------------------------------------------------------- 1 | name: black 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.py' 7 | 8 | defaults: 9 | run: 10 | shell: bash 11 | 12 | jobs: 13 | black: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | with: 18 | ref: ${{ github.head_ref }} 19 | - name: Setup Python 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: 3.x 23 | - name: Install black 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install black 27 | - name: Run black 28 | run: | 29 | black . 30 | - uses: stefanzweifel/git-auto-commit-action@v4 31 | with: 32 | commit_message: "[skip ci] Apply formatting changes" 33 | -------------------------------------------------------------------------------- /.github/workflows/conda-build-test.yml: -------------------------------------------------------------------------------- 1 | name: conda-build-test 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | branches: 7 | - develop 8 | - main 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | container: continuumio/miniconda3:4.10.3 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Set up conda 19 | run: | 20 | apt-get --allow-releaseinfo-change update 21 | conda install -y anaconda-client conda-build 22 | conda config --set anaconda_upload no 23 | - name: Build and test 24 | env: 25 | GIT_DESCRIBE_TAG: 0.1 26 | run: | 27 | conda build conda -c conda-forge --config-file conda/conda_build_config.yaml 28 | -------------------------------------------------------------------------------- /.github/workflows/docker_ci_main.yml: -------------------------------------------------------------------------------- 1 | 2 | name: CI tests 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | - develop 8 | push: 9 | branches: 10 | - main 11 | - develop 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | container: 16 | image: openmc/openmc:develop 17 | 18 | steps: 19 | - name: checkout repository 20 | uses: actions/checkout@v2 21 | 22 | - name: Setup package 23 | run: | 24 | pip install . 25 | 26 | - name: test_command_line_usage.py 27 | run: | 28 | pytest tests/test_command_line_usage.py -v --cov=src --cov-report term --cov-report xml 29 | 30 | - name: test_functions.py 31 | run: | 32 | pytest tests/test_functions.py -v --cov=src --cov-report term --cov-report xml 33 | 34 | - name: test_use_in_openmc.py 35 | run: | 36 | pytest tests/test_use_in_openmc.py -v --cov=src --cov-report term --cov-report xml 37 | 38 | - name: upload test results 39 | run: | 40 | curl -s https://codecov.io/bash | bash 41 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This yml file will trigger a Github Actions event that builds and upload the 2 | # Python package to PiPy. This makes use of Twine and is triggered when a push 3 | # to the main branch occures. For more information see: 4 | # https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 5 | # and for details on the Autobump version section see: 6 | # https://github.com/grst/python-ci-versioneer 7 | 8 | name: Upload Python Package 9 | 10 | on: 11 | # allows us to run workflows manually 12 | workflow_dispatch: 13 | release: 14 | types: [created] 15 | 16 | jobs: 17 | deploy: 18 | 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Set up Python 24 | uses: actions/setup-python@v2 25 | with: 26 | python-version: '3.x' 27 | 28 | - name: Install dependencies 29 | run: | 30 | python -m pip install --upgrade pip 31 | pip install setuptools wheel build twine 32 | 33 | - name: Build and publish 34 | env: 35 | TWINE_USERNAME: __token__ 36 | TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} 37 | run: | 38 | python -m build 39 | twine check dist/* 40 | twine upload dist/* 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # OpenMC files 132 | materials.xml 133 | tallies.xml 134 | geometry.xml 135 | settings.xml 136 | *.h5 137 | 138 | **_version.py -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - family-names: "Shimwell" 5 | given-names: "Jonathan" 6 | orcid: "https://orcid.org/0000-0001-6909-0946" 7 | title: "openmc-data-downloader. A Python package for downloading h5 cross section files for use in OpenMC." 8 | version: 0.4.1 9 | date-released: 2021-11-11 10 | url: "https://github.com/openmc-data-storage/openmc_data_downloader" 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 openmc-data-storage 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![CI - Main](https://github.com/openmc-data-storage/openmc_data_downloader/actions/workflows/docker_ci_main.yml/badge.svg)](https://github.com/openmc-data-storage/openmc_data_downloader/actions/workflows/docker_ci_main.yml) 3 | [![Upload Python Package](https://github.com/openmc-data-storage/openmc_data_downloader/actions/workflows/python-publish.yml/badge.svg)](https://github.com/openmc-data-storage/openmc_data_downloader/actions/workflows/python-publish.yml) 4 | [![PyPI](https://img.shields.io/pypi/v/openmc_data_downloader?color=brightgreen&label=pypi&logo=grebrightgreenen&logoColor=green)](https://pypi.org/project/openmc_data_downloader/) 5 | [![codecov](https://codecov.io/gh/openmc-data-storage/openmc_data_downloader/branch/main/graph/badge.svg)](https://codecov.io/gh/openmc-data-storage/openmc_data_downloader) 6 | 7 | # OpenMC data downloader 8 | 9 | A Python package for downloading preprocessed cross section data in the h5 file 10 | format for use with OpenMC. 11 | 12 | This package allows you to download a fully reproducible composite nuclear data 13 | library with one command. 14 | 15 | There are several methods of obtaining complete data libraries for use with 16 | OpenMC, for example: 17 | 18 | - [OpenMC.org](https://openmc.org/) has entire libraries downloadable as compressed files 19 | - [OpenMC data repository scripts](https://github.com/openmc-dev/data/) has scripts for automatically downloading ACE and ENDF files and generating h5 files from these inputs. 20 | 21 | ## History 22 | 23 | The package was originally conceived by Jonathan Shimwell as a means of 24 | downloading a minimal selection of data for use on continuous integration 25 | platforms. 26 | The package can also used to produce traceable and reproducible 27 | nuclear data distributions. 28 | 29 | ## System Installation 30 | 31 | To install the openmc_data_downloader you need to have Python3 installed, 32 | OpenMC is also advisable if you want to run simulations using the h5 data files 33 | but not actually mandatory if you just want to download the cross sections. 34 | 35 | ```bash 36 | pip install openmc_data_downloader 37 | ``` 38 | 39 | ## Features 40 | 41 | The OpenMC data downloader is able to download cross section files for isotopes 42 | from nuclear data libraries.The user specifies the nuclear data libraries in 43 | order of their preference. When an isotope is found in multiple libraries it 44 | will be downloaded from the highest preference library. This avoid duplication 45 | of isotopes and provides a reproducible nuclear data environment. 46 | 47 | The nuclear data h5 file are downloaded from the OpenMC-data-storage 48 | repository. Prior to being added to the repository they have been automatically 49 | processed using scripts from OpenMC data repository, these scripts convert ACE 50 | and ENDF file to h5 files. 51 | 52 | The resulting h5 files are then used in and automated test suite of simulations 53 | to help ensure they are suitable for their intended purpose. 54 | 55 | Isotopes for downloading can be found in a variety of ways as demonstrated below. 56 | When downloading a cross_section.xml file is automatically created and h5 files 57 | are named with their nuclear data library and the isotope. This helps avoid 58 | downloading files that already exist locally and the ```overwrite``` argument 59 | can be used to control if these files are downloaded again. 60 | 61 | ## Usage - command line usage 62 | 63 | ### Getting a description of the input options 64 | 65 | ```bash 66 | openmc_data_downloader --help 67 | ``` 68 | 69 | ### Downloading a single isotope from the FENDL 3.1d nuclear library 70 | 71 | ```bash 72 | openmc_data_downloader -l FENDL-3.1d -i Li6 73 | ``` 74 | 75 | ### Downloading a multiple isotopes from the TENDL 2019 nuclear library 76 | 77 | ```bash 78 | openmc_data_downloader -l TENDL-2019 -i Li6 Li7 79 | ``` 80 | 81 | ### Downloading a single element from the TENDL 2019 nuclear library 82 | 83 | ```bash 84 | openmc_data_downloader -l TENDL-2019 -e Li 85 | ``` 86 | 87 | ### Downloading a multiple element from the TENDL 2019 nuclear library 88 | 89 | ```bash 90 | openmc_data_downloader -l TENDL-2019 -e Li Si Na 91 | ``` 92 | 93 | ### Downloading h5 files from the ENDF/B 7.1 NNDC library to a specific folder (destination) 94 | 95 | ```bash 96 | openmc_data_downloader -l ENDFB-7.1-NNDC -i Be9 -d my_h5_files 97 | ``` 98 | 99 | ### Downloading a combination of isotopes and element from the TENDL 2019 nuclear library 100 | 101 | ```bash 102 | openmc_data_downloader -l TENDL-2019 -e Li Si Na -i Fe56 U235 103 | ``` 104 | ### Downloading all the isotopes from the TENDL 2019 nuclear library 105 | 106 | ```bash 107 | openmc_data_downloader -l TENDL-2019 -i all 108 | ``` 109 | ### Downloading all the stable isotopes from the TENDL 2019 nuclear library 110 | 111 | ```bash 112 | openmc_data_downloader -l TENDL-2019 -i stable 113 | ``` 114 | 115 | ### Downloading all the isotopes in a materials.xml file from the TENDL 2019 nuclear library 116 | 117 | ```bash 118 | openmc_data_downloader -l TENDL-2019 -m materials.xml 119 | ``` 120 | 121 | ### Downloading 3 isotopes from ENDF/B 7.1 NNDC (first choice) and TENDL 2019 (second choice) nuclear library 122 | 123 | ```bash 124 | openmc_data_downloader -l ENDFB-7.1-NNDC TENDL-2019 -i Li6 Li7 Be9 125 | ``` 126 | 127 | ### Downloading the photon only cross section for an element ENDF/B 7.1 NNDC 128 | 129 | ```bash 130 | openmc_data_downloader -l ENDFB-7.1-NNDC -e Li -p photon 131 | ``` 132 | 133 | ### Downloading the neutron and photon cross section for an element ENDF/B 7.1 NNDC 134 | 135 | ```bash 136 | openmc_data_downloader -l ENDFB-7.1-NNDC -e Li -p neutron photon 137 | ``` 138 | 139 | ### Downloading the neutron cross section for elements and an SaB cross sections 140 | 141 | ```bash 142 | openmc_data_downloader -l ENDFB-7.1-NNDC -e Be O -s c_Be_in_BeO 143 | ``` 144 | 145 | ## Usage - within a Python environment 146 | 147 | When using the Python API the ```just_in_time_library_generator()``` function 148 | provides similar capabilities to the ```openmc_data_downloader``` terminal 149 | command. With one key difference being that ```just_in_time_library_generator()``` 150 | sets the ```OPENMC_CROSS_SECTIONS``` environmental variable to point to the 151 | newly created cross_sections.xml by default. 152 | 153 | ### Downloading the isotopes present in an OpenMC material 154 | 155 | ```python 156 | import openmc 157 | import openmc_data_downloader as odd 158 | 159 | mat1 = openmc.Material() 160 | mat1.add_element('Fe', 0.95) 161 | mat1.add_element('C', 0.05) 162 | 163 | mats = openmc.Materials([mat1]) 164 | 165 | odd.download_cross_section_data( 166 | mats, 167 | libraries=["FENDL-3.1d"], 168 | set_OPENMC_CROSS_SECTIONS=True, 169 | particles=["neutron"], 170 | ) 171 | ``` 172 | 173 | ### Downloading the isotopes present in an OpenMC material from two libraries but with a preference for ENDF/B 7.1 NNDC library over TENDL 2019 174 | 175 | ```python 176 | import openmc 177 | import openmc_data_downloader as odd 178 | 179 | mat1 = openmc.Material() 180 | mat1.add_element('Fe', 0.95) 181 | mat1.add_element('C', 0.05) 182 | 183 | mats = openmc.Materials([mat1]) 184 | 185 | odd.download_cross_section_data( 186 | mats, 187 | libraries=['ENDFB-7.1-NNDC', 'TENDL-2019'], 188 | set_OPENMC_CROSS_SECTIONS=True, 189 | particles=["neutron"], 190 | ) 191 | ``` 192 | 193 | 194 | ### Downloading neutron cross sections for a material with an SaB 195 | 196 | ```python 197 | import openmc 198 | import openmc_data_downloader as odd 199 | 200 | my_mat = openmc.Material() 201 | my_mat.add_element('Be', 0.5) 202 | my_mat.add_element('O', 0.5) 203 | my_mat.add_s_alpha_beta('Be_in_BeO') 204 | 205 | mats = openmc.Materials([my_mat]) 206 | 207 | odd.download_cross_section_data( 208 | mats, 209 | libraries=['ENDFB-7.1-NNDC', 'TENDL-2019'], 210 | set_OPENMC_CROSS_SECTIONS=True, 211 | particles=["neutron"], 212 | ) 213 | ``` 214 | 215 | ### Downloading photon and neutron cross sections for isotopes and elements from the TENDL 2019 library 216 | 217 | ```python 218 | import openmc 219 | import openmc_data_downloader as odd 220 | 221 | mat1 = openmc.Material() 222 | mat1.add_element('Fe', 0.95) 223 | mat1.add_element('C', 0.05) 224 | 225 | mats = openmc.Materials([mat1]) 226 | 227 | odd.download_cross_section_data( 228 | mats, 229 | libraries=['ENDFB-7.1-NNDC', 'TENDL-2019'], 230 | set_OPENMC_CROSS_SECTIONS=True, 231 | particles=["neutron", "photon"], 232 | ) 233 | ``` 234 | -------------------------------------------------------------------------------- /conda/conda_build_config.yaml: -------------------------------------------------------------------------------- 1 | python: 2 | - 3.10 3 | - 3.9 4 | - 3.8 5 | - 3.7 6 | -------------------------------------------------------------------------------- /conda/meta.yaml: -------------------------------------------------------------------------------- 1 | {% set name = "openmc_data_downloader" %} 2 | 3 | package: 4 | name: "{{ name|lower }}" 5 | # conda package version tag is obtained from the git release version tag 6 | version: {{ GIT_DESCRIBE_TAG }} 7 | 8 | source: 9 | path: .. 10 | 11 | build: 12 | number: 0 13 | script: python -m pip install --no-deps --ignore-installed . 14 | 15 | requirements: 16 | build: 17 | - python {{ python }} 18 | - setuptools>=65.4.0 19 | - setuptools_scm[toml]>=7.0.5 20 | run: 21 | - python 22 | - pandas 23 | - retry 24 | - openmc 25 | 26 | test: 27 | imports: 28 | - openmc_data_downloader 29 | requires: 30 | - pytest 31 | source_files: 32 | - tests/ 33 | commands: 34 | - pytest tests/test_command_line_usage.py 35 | - pytest tests/test_functions.py 36 | # test_use_in_openmc.py skipped for now as test is failing for upstream bug 37 | 38 | about: 39 | home: "https://github.com/openmc-data-storage/openmc_data_downloader" 40 | license: MIT 41 | license_family: MIT 42 | license_file: LICENSE 43 | doc_url: https://github.com/openmc-data-storage/openmc_data_downloader 44 | dev_url: https://github.com/openmc-data-storage/openmc_data_downloader 45 | summary: openmc_data_downloader - A Python package for downloading h5 cross section files for use in OpenMC. 46 | 47 | extra: 48 | recipe-maintainers: 49 | - shimwell 50 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools >= 65.5.0", 4 | "setuptools_scm[toml] >= 7.0.5", 5 | ] 6 | build-backend = "setuptools.build_meta" 7 | 8 | 9 | [project] 10 | name = "openmc_data_downloader" 11 | authors = [ 12 | { name="Jonathan Shimwell", email="mail@jshimwell.com" }, 13 | ] 14 | license = {file = "LICENSE"} 15 | description = "A tool for selectively downloading h5 files for specified isotopes / elements from your libraries of choice" 16 | readme = "README.md" 17 | requires-python = ">=3.8" 18 | keywords = ["openmc", "nuclear", "data", "download", "process", "cross", "section"] 19 | classifiers = [ 20 | "Programming Language :: Python :: 3", 21 | "License :: OSI Approved :: MIT License", 22 | "Operating System :: OS Independent", 23 | ] 24 | dependencies = [ 25 | "pandas", 26 | "retry" 27 | ] 28 | dynamic = ["version"] 29 | 30 | [tool.setuptools_scm] 31 | write_to = "src/_version.py" 32 | 33 | 34 | [project.optional-dependencies] 35 | tests = [ 36 | "pytest" 37 | ] 38 | 39 | [project.urls] 40 | "Homepage" = "https://github.com/fusion-energy/openmc_data_downloader" 41 | "Bug Tracker" = "https://github.com/fusion-energy/openmc_data_downloader/issues" 42 | 43 | [tool.setuptools] 44 | package-dir = {"" = "src"} 45 | 46 | [project.scripts] 47 | openmc_data_downloader = "openmc_data_downloader.terminal_cmd:main" 48 | 49 | [tool.setuptools.package-data] 50 | openmc_data_downloader = ["*.xml"] 51 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | pandas 3 | retry 4 | # openmc is optional, but not avaialbe via pip 5 | -------------------------------------------------------------------------------- /src/openmc_data_downloader/__init__.py: -------------------------------------------------------------------------------- 1 | try: 2 | # this works for python 3.7 and lower 3 | from importlib.metadata import version, PackageNotFoundError 4 | except (ModuleNotFoundError, ImportError): 5 | # this works for python 3.8 and higher 6 | from importlib_metadata import version, PackageNotFoundError 7 | try: 8 | __version__ = version("openmc_data_downloader") 9 | except PackageNotFoundError: 10 | from setuptools_scm import get_version 11 | 12 | __version__ = get_version(root="..", relative_to=__file__) 13 | 14 | __all__ = ["__version__"] 15 | 16 | from .cross_sections_directory import * 17 | from .utils import * 18 | -------------------------------------------------------------------------------- /src/openmc_data_downloader/cross_sections_directory.py: -------------------------------------------------------------------------------- 1 | import re 2 | import xml.etree.ElementTree as ET 3 | from pathlib import Path 4 | 5 | from numpy import nested_iters 6 | 7 | # from https://github.com/openmc-dev/openmc/blob/develop/openmc/data/data.py 8 | # remove when pip install openmc via PyPi is available 9 | NATURAL_ABUNDANCE = { 10 | "H": ["H1", "H2"], 11 | "He": ["He3", "He4"], 12 | "Li": ["Li6", "Li7"], 13 | "Be": ["Be9"], 14 | "B": ["B10", "B11"], 15 | "C": ["C12", "C13", "C0"], # C0 included for ENDF/B 7.1 NNDC 16 | "N": ["N14", "N15"], 17 | "O": ["O16", "O17", "O18"], 18 | "F": ["F19"], 19 | "Ne": ["Ne20", "Ne21", "Ne22"], 20 | "Na": ["Na23"], 21 | "Mg": ["Mg24", "Mg25", "Mg26"], 22 | "Al": ["Al27"], 23 | "Si": ["Si28", "Si29", "Si30"], 24 | "P": ["P31"], 25 | "S": ["S32", "S33", "S34", "S36"], 26 | "Cl": ["Cl35", "Cl37"], 27 | "Ar": ["Ar36", "Ar38", "Ar40"], 28 | "K": ["K39", "K40", "K41"], 29 | "Ca": ["Ca40", "Ca42", "Ca43", "Ca44", "Ca46", "Ca48"], 30 | "Sc": ["Sc45"], 31 | "Ti": ["Ti46", "Ti47", "Ti48", "Ti49", "Ti50"], 32 | "V": ["V50", "V51"], 33 | "Cr": ["Cr50", "Cr52", "Cr53", "Cr54"], 34 | "Mn": ["Mn55"], 35 | "Fe": ["Fe54", "Fe56", "Fe57", "Fe58"], 36 | "Co": ["Co59"], 37 | "Ni": ["Ni58", "Ni60", "Ni61", "Ni62", "Ni64"], 38 | "Cu": ["Cu63", "Cu65"], 39 | "Zn": ["Zn64", "Zn66", "Zn67", "Zn68", "Zn70"], 40 | "Ga": ["Ga69", "Ga71"], 41 | "Ge": ["Ge70", "Ge72", "Ge73", "Ge74", "Ge76"], 42 | "As": ["As75"], 43 | "Se": ["Se74", "Se76", "Se77", "Se78", "Se80", "Se82"], 44 | "Br": ["Br79", "Br81"], 45 | "Kr": ["Kr78", "Kr80", "Kr82", "Kr83", "Kr84", "Kr86"], 46 | "Rb": ["Rb85", "Rb87"], 47 | "Sr": ["Sr84", "Sr86", "Sr87", "Sr88"], 48 | "Y": ["Y89"], 49 | "Zr": ["Zr90", "Zr91", "Zr92", "Zr94", "Zr96"], 50 | "Nb": ["Nb93"], 51 | "Mo": ["Mo92", "Mo94", "Mo95", "Mo96", "Mo97", "Mo98", "Mo100"], 52 | "Ru": ["Ru96", "Ru98", "Ru99", "Ru100", "Ru101", "Ru102", "Ru104"], 53 | "Rh": ["Rh103"], 54 | "Pd": ["Pd102", "Pd104", "Pd105", "Pd106", "Pd108", "Pd110"], 55 | "Ag": ["Ag107", "Ag109"], 56 | "Cd": ["Cd106", "Cd108", "Cd110", "Cd111", "Cd112", "Cd113", "Cd114", "Cd116"], 57 | "In": ["In113", "In115"], 58 | "Sn": [ 59 | "Sn112", 60 | "Sn114", 61 | "Sn115", 62 | "Sn116", 63 | "Sn117", 64 | "Sn118", 65 | "Sn119", 66 | "Sn120", 67 | "Sn122", 68 | "Sn124", 69 | ], 70 | "Sb": ["Sb121", "Sb123"], 71 | "Te": ["Te120", "Te122", "Te123", "Te124", "Te125", "Te126", "Te128", "Te130"], 72 | "I": ["I127"], 73 | "Xe": [ 74 | "Xe124", 75 | "Xe126", 76 | "Xe128", 77 | "Xe129", 78 | "Xe130", 79 | "Xe131", 80 | "Xe132", 81 | "Xe134", 82 | "Xe136", 83 | ], 84 | "Cs": ["Cs133"], 85 | "Ba": ["Ba130", "Ba132", "Ba134", "Ba135", "Ba136", "Ba137", "Ba138"], 86 | "La": ["La138", "La139"], 87 | "Ce": ["Ce136", "Ce138", "Ce140", "Ce142"], 88 | "Pr": ["Pr141"], 89 | "Nd": ["Nd142", "Nd143", "Nd144", "Nd145", "Nd146", "Nd148", "Nd150"], 90 | "Sm": ["Sm144", "Sm147", "Sm148", "Sm149", "Sm150", "Sm152", "Sm154"], 91 | "Eu": ["Eu151", "Eu153"], 92 | "Gd": ["Gd152", "Gd154", "Gd155", "Gd156", "Gd157", "Gd158", "Gd160"], 93 | "Tb": ["Tb159"], 94 | "Dy": ["Dy156", "Dy158", "Dy160", "Dy161", "Dy162", "Dy163", "Dy164"], 95 | "Ho": ["Ho165"], 96 | "Er": ["Er162", "Er164", "Er166", "Er167", "Er168", "Er170"], 97 | "Tm": ["Tm169"], 98 | "Yb": ["Yb168", "Yb170", "Yb171", "Yb172", "Yb173", "Yb174", "Yb176"], 99 | "Lu": ["Lu175", "Lu176"], 100 | "Hf": ["Hf174", "Hf176", "Hf177", "Hf178", "Hf179", "Hf180"], 101 | "Ta": ["Ta180", "Ta181"], 102 | "W": ["W180", "W182", "W183", "W184", "W186"], 103 | "Re": ["Re185", "Re187"], 104 | "Os": ["Os184", "Os186", "Os187", "Os188", "Os189", "Os190", "Os192"], 105 | "Ir": ["Ir191", "Ir193"], 106 | "Pt": ["Pt190", "Pt192", "Pt194", "Pt195", "Pt196", "Pt198"], 107 | "Au": ["Au197"], 108 | "Hg": ["Hg196", "Hg198", "Hg199", "Hg200", "Hg201", "Hg202", "Hg204"], 109 | "Tl": ["Tl203", "Tl205"], 110 | "Pb": ["Pb204", "Pb206", "Pb207", "Pb208"], 111 | "Bi": ["Bi209"], 112 | "Th": ["Th230", "Th232"], 113 | "Pa": ["Pa231"], 114 | "U": ["U234", "U235", "U238"], 115 | "Ac": [], # no stable isotopes 116 | "Am": [], # no stable isotopes 117 | "At": [], # no stable isotopes 118 | "Bk": [], # no stable isotopes 119 | "Cf": [], # no stable isotopes 120 | "Cm": [], # no stable isotopes 121 | "Es": [], # no stable isotopes 122 | "Fm": [], # no stable isotopes 123 | "Fr": [], # no stable isotopes 124 | "Np": [], # no stable isotopes 125 | "Pm": [], # no stable isotopes 126 | "Po": [], # no stable isotopes 127 | "Pu": [], # no stable isotopes 128 | "Ra": [], # no stable isotopes 129 | "Rn": [], # no stable isotopes 130 | "Tc": [], # no stable isotopes 131 | } 132 | 133 | 134 | def zaid_to_isotope(zaid: str) -> str: 135 | """converts an isotope into a zaid e.g. 003006 -> Li6""" 136 | a = str(zaid)[-3:] 137 | z = str(zaid)[:-3] 138 | symbol = ATOMIC_SYMBOL[int(z)] 139 | return symbol + str(int(a)) 140 | 141 | 142 | def get_isotopes_or_elements_from_xml(filename, particle_type): 143 | if particle_type == "sab": 144 | particle_type = "thermal" 145 | tree = ET.parse(Path(__file__).parent / filename) 146 | root = tree.getroot() 147 | neutron_isotopes = [] 148 | for elem in root: 149 | if elem.attrib["type"] == particle_type: 150 | neutron_isotopes.append(elem.attrib["materials"]) 151 | if len(neutron_isotopes) == 0: 152 | raise ValueError(f"no {particle_type} were found in {filename}") 153 | return neutron_isotopes 154 | 155 | 156 | def core_dict_entry(library, name, base_url): 157 | entry = {} 158 | entry["library"] = library 159 | entry["remote_file"] = name + ".h5" 160 | entry["url"] = base_url + entry["remote_file"] 161 | entry["local_file"] = entry["library"] + "_" + entry["remote_file"] 162 | return entry 163 | 164 | 165 | def populate_neutron_cross_section_list(isotopes, base_url, library): 166 | xs_info = [] 167 | for isotope in isotopes: 168 | entry = core_dict_entry(library, isotope, base_url) 169 | entry["particle"] = "neutron" 170 | entry["isotope"] = isotope 171 | entry["element"] = re.split(r"(\d+)", entry["isotope"])[0] 172 | xs_info.append(entry) 173 | return xs_info 174 | 175 | 176 | def populate_sab_cross_section_list(sabs, base_url, library): 177 | xs_info = [] 178 | for sab in sabs: 179 | entry = core_dict_entry(library, sab, base_url) 180 | entry["particle"] = "sab" 181 | entry["sab"] = sab 182 | xs_info.append(entry) 183 | return xs_info 184 | 185 | 186 | def populate_photon_cross_section_list(elements, base_url, library): 187 | xs_info = [] 188 | for element in elements: 189 | entry = core_dict_entry(library, element, base_url) 190 | entry["particle"] = "photon" 191 | entry["element"] = element 192 | xs_info.append(entry) 193 | return xs_info 194 | 195 | 196 | def get_isotopes_or_elements_info_from_xml(particle_type, library): 197 | base_url = lib_to_base_url[(library, particle_type)] 198 | filename = lib_to_xml[library] 199 | 200 | isotopes_or_elements = get_isotopes_or_elements_from_xml(filename, particle_type) 201 | 202 | if particle_type == "photon": 203 | info = populate_photon_cross_section_list( 204 | isotopes_or_elements, base_url, library 205 | ) 206 | 207 | elif particle_type == "neutron": 208 | info = populate_neutron_cross_section_list( 209 | isotopes_or_elements, base_url, library 210 | ) 211 | elif particle_type == "sab": 212 | info = populate_sab_cross_section_list(isotopes_or_elements, base_url, library) 213 | else: 214 | raise ValueError( 215 | f'particle type {particle_type} not supported, acceptable particle types are "neutron" or "photon' 216 | ) 217 | return info 218 | 219 | 220 | # { 221 | # 'filename': 222 | # 'particle_type': 223 | # 'base_url':, 224 | # 'library': 225 | # } 226 | 227 | lib_to_xml = { 228 | "FENDL-3.1d": "fendl_3.1d_cross_sections.xml", 229 | "ENDFB-8.0-NNDC": "nndc_8.0_cross_sections.xml", 230 | "ENDFB-7.1-NNDC": "nndc_7.1_cross_sections.xml", 231 | "TENDL-2019": "tendl_2019_cross_sections.xml", 232 | } 233 | lib_to_base_url = { 234 | ( 235 | "FENDL-3.1d", 236 | "neutron", 237 | ): "https://github.com/openmc-data-storage/FENDL-3.1d/raw/main/h5_files/neutron/", 238 | ( 239 | "FENDL-3.1d", 240 | "photon", 241 | ): "https://github.com/openmc-data-storage/FENDL-3.1d/raw/main/h5_files/photon/", 242 | ( 243 | "ENDFB-8.0-NNDC", 244 | "neutron", 245 | ): "https://github.com/openmc-data-storage/ENDF-B-VIII.0-NNDC/raw/main/h5_files/neutron/", 246 | ( 247 | "ENDFB-8.0-NNDC", 248 | "sab", 249 | ): "https://github.com/openmc-data-storage/ENDF-B-VIII.0-NNDC/raw/main/h5_files/neutron/", 250 | ( 251 | "ENDFB-8.0-NNDC", 252 | "photon", 253 | ): "https://github.com/openmc-data-storage/ENDF-B-VIII.0-NNDC/raw/main/h5_files/photon/", 254 | ( 255 | "ENDFB-7.1-NNDC", 256 | "neutron", 257 | ): "https://github.com/openmc-data-storage/ENDF-B-VII.1-NNDC/raw/main/h5_files/neutron/", 258 | ( 259 | "ENDFB-7.1-NNDC", 260 | "sab", 261 | ): "https://github.com/openmc-data-storage/ENDF-B-VII.1-NNDC/raw/main/h5_files/neutron/", 262 | ( 263 | "ENDFB-7.1-NNDC", 264 | "photon", 265 | ): "https://github.com/openmc-data-storage/ENDF-B-VII.1-NNDC/raw/main/h5_files/photon/", 266 | ( 267 | "TENDL-2019", 268 | "neutron", 269 | ): "https://github.com/openmc-data-storage/TENDL-2019/raw/main/h5_files/", 270 | } 271 | 272 | neutron_xs_info = [] 273 | neutron_xs_info += get_isotopes_or_elements_info_from_xml("neutron", "TENDL-2019") 274 | neutron_xs_info += get_isotopes_or_elements_info_from_xml("neutron", "ENDFB-7.1-NNDC") 275 | neutron_xs_info += get_isotopes_or_elements_info_from_xml("neutron", "FENDL-3.1d") 276 | neutron_xs_info += get_isotopes_or_elements_info_from_xml("neutron", "ENDFB-8.0-NNDC") 277 | 278 | photon_xs_info = [] 279 | photon_xs_info += get_isotopes_or_elements_info_from_xml( 280 | "photon", 281 | "ENDFB-7.1-NNDC", 282 | ) 283 | photon_xs_info += get_isotopes_or_elements_info_from_xml("photon", "FENDL-3.1d") 284 | photon_xs_info += get_isotopes_or_elements_info_from_xml("photon", "ENDFB-8.0-NNDC") 285 | 286 | sab_xs_info = [] 287 | sab_xs_info += get_isotopes_or_elements_info_from_xml( 288 | "sab", 289 | "ENDFB-7.1-NNDC", 290 | ) 291 | sab_xs_info += get_isotopes_or_elements_info_from_xml("sab", "ENDFB-8.0-NNDC") 292 | 293 | ATOMIC_SYMBOL = { 294 | 0: "n", 295 | 1: "H", 296 | 2: "He", 297 | 3: "Li", 298 | 4: "Be", 299 | 5: "B", 300 | 6: "C", 301 | 7: "N", 302 | 8: "O", 303 | 9: "F", 304 | 10: "Ne", 305 | 11: "Na", 306 | 12: "Mg", 307 | 13: "Al", 308 | 14: "Si", 309 | 15: "P", 310 | 16: "S", 311 | 17: "Cl", 312 | 18: "Ar", 313 | 19: "K", 314 | 20: "Ca", 315 | 21: "Sc", 316 | 22: "Ti", 317 | 23: "V", 318 | 24: "Cr", 319 | 25: "Mn", 320 | 26: "Fe", 321 | 27: "Co", 322 | 28: "Ni", 323 | 29: "Cu", 324 | 30: "Zn", 325 | 31: "Ga", 326 | 32: "Ge", 327 | 33: "As", 328 | 34: "Se", 329 | 35: "Br", 330 | 36: "Kr", 331 | 37: "Rb", 332 | 38: "Sr", 333 | 39: "Y", 334 | 40: "Zr", 335 | 41: "Nb", 336 | 42: "Mo", 337 | 43: "Tc", 338 | 44: "Ru", 339 | 45: "Rh", 340 | 46: "Pd", 341 | 47: "Ag", 342 | 48: "Cd", 343 | 49: "In", 344 | 50: "Sn", 345 | 51: "Sb", 346 | 52: "Te", 347 | 53: "I", 348 | 54: "Xe", 349 | 55: "Cs", 350 | 56: "Ba", 351 | 57: "La", 352 | 58: "Ce", 353 | 59: "Pr", 354 | 60: "Nd", 355 | 61: "Pm", 356 | 62: "Sm", 357 | 63: "Eu", 358 | 64: "Gd", 359 | 65: "Tb", 360 | 66: "Dy", 361 | 67: "Ho", 362 | 68: "Er", 363 | 69: "Tm", 364 | 70: "Yb", 365 | 71: "Lu", 366 | 72: "Hf", 367 | 73: "Ta", 368 | 74: "W", 369 | 75: "Re", 370 | 76: "Os", 371 | 77: "Ir", 372 | 78: "Pt", 373 | 79: "Au", 374 | 80: "Hg", 375 | 81: "Tl", 376 | 82: "Pb", 377 | 83: "Bi", 378 | 84: "Po", 379 | 85: "At", 380 | 86: "Rn", 381 | 87: "Fr", 382 | 88: "Ra", 383 | 89: "Ac", 384 | 90: "Th", 385 | 91: "Pa", 386 | 92: "U", 387 | 93: "Np", 388 | 94: "Pu", 389 | 95: "Am", 390 | 96: "Cm", 391 | 97: "Bk", 392 | 98: "Cf", 393 | 99: "Es", 394 | 100: "Fm", 395 | 101: "Md", 396 | 102: "No", 397 | 103: "Lr", 398 | 104: "Rf", 399 | 105: "Db", 400 | 106: "Sg", 401 | 107: "Bh", 402 | 108: "Hs", 403 | 109: "Mt", 404 | 110: "Ds", 405 | 111: "Rg", 406 | 112: "Cn", 407 | 113: "Nh", 408 | 114: "Fl", 409 | 115: "Mc", 410 | 116: "Lv", 411 | 117: "Ts", 412 | 118: "Og", 413 | } 414 | 415 | 416 | all_libs = [] 417 | for entry in neutron_xs_info: 418 | all_libs.append(entry["library"]) 419 | 420 | LIB_OPTIONS = list(set(all_libs)) 421 | PARTICLE_OPTIONS = ["neutron", "photon", "sab"] 422 | 423 | nested_list = list(NATURAL_ABUNDANCE.values()) 424 | 425 | # should this come from the isotopes available in the xml files 426 | STABLE_ISOTOPE_OPTIONS = [item for sublist in nested_list for item in sublist] 427 | 428 | ALL_ISOTOPE_OPTIONS = [] 429 | for xml in [ 430 | "tendl_2019_cross_sections.xml", 431 | "nndc_7.1_cross_sections.xml", 432 | "nndc_8.0_cross_sections.xml", 433 | "fendl_3.1d_cross_sections.xml", 434 | ]: 435 | isotopes = get_isotopes_or_elements_from_xml(xml, "neutron") 436 | ALL_ISOTOPE_OPTIONS = ALL_ISOTOPE_OPTIONS + isotopes 437 | 438 | SAB_OPTIONS = [] 439 | for xml in [ 440 | "nndc_7.1_cross_sections.xml", 441 | "nndc_8.0_cross_sections.xml", 442 | ]: 443 | sabs = get_isotopes_or_elements_from_xml(xml, "sab") 444 | SAB_OPTIONS = SAB_OPTIONS + sabs 445 | 446 | ALL_ISOTOPE_OPTIONS = sorted(list(set(ALL_ISOTOPE_OPTIONS))) 447 | 448 | ALL_ELEMENT_OPTIONS = sorted( 449 | list(set([re.split(r"(\d+)", i)[0] for i in ALL_ISOTOPE_OPTIONS])) 450 | ) 451 | STABLE_ELEMENT_OPTIONS = sorted( 452 | list(set([re.split(r"(\d+)", i)[0] for i in STABLE_ISOTOPE_OPTIONS])) 453 | ) 454 | -------------------------------------------------------------------------------- /src/openmc_data_downloader/fendl_3.1d_cross_sections.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | -------------------------------------------------------------------------------- /src/openmc_data_downloader/nndc_7.1_cross_sections.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | -------------------------------------------------------------------------------- /src/openmc_data_downloader/tendl_2019_cross_sections.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | -------------------------------------------------------------------------------- /src/openmc_data_downloader/terminal_cmd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """ 4 | A nuclear data downloading package that facilitates the reproduction of cross 5 | section collections. Use the command line tool or Python API to download the h5 6 | cross sections for just the isotopes / elements that you want. Specify the 7 | preferred nuclear data libraries to use. Automatically avoid duplication and 8 | generation of custom cross_section.xml files 9 | """ 10 | 11 | import argparse 12 | from pathlib import Path 13 | import openmc_data_downloader 14 | from openmc_data_downloader.cross_sections_directory import ( 15 | lib_to_xml, 16 | NATURAL_ABUNDANCE, 17 | SAB_OPTIONS, 18 | ) 19 | import openmc 20 | 21 | 22 | def main(): 23 | parser = argparse.ArgumentParser() 24 | 25 | parser.add_argument( 26 | "-l", 27 | "--libraries", 28 | choices=openmc_data_downloader.LIB_OPTIONS, 29 | nargs="*", 30 | help="The nuclear data libraries to search through when searching for \ 31 | cross sections. Multiple libaries are acceptable and will be \ 32 | preferentially utilized in the order provided", 33 | default=[], 34 | required=True, 35 | ) 36 | parser.add_argument( 37 | "-i", 38 | "--isotopes", 39 | nargs="*", 40 | default=[], 41 | help="The isotope or isotopes to download, name of isotope e.g. 'Al27' or keyword 'all' or 'stable'", 42 | ) 43 | 44 | parser.add_argument( 45 | "-s", 46 | "--sab", 47 | nargs="*", 48 | default=[], 49 | help="The SaB cross sections to download. Options include " 50 | + " ".join(SAB_OPTIONS), 51 | ) 52 | 53 | parser.add_argument( 54 | "-e", 55 | "--elements", 56 | nargs="*", 57 | default=[], 58 | help="The element or elements to download, name of element e.g. 'Al' or keyword 'all' or 'stable'", 59 | ) 60 | parser.add_argument( 61 | "-p", 62 | "--particles", 63 | nargs="*", 64 | default=["neutron"], 65 | choices=["neutron", "photon", "sab"], 66 | help="The particle to download", 67 | ) 68 | parser.add_argument( 69 | "-m", 70 | "--materials_xml", 71 | nargs="*", 72 | default=[], 73 | help="The filename of the materials.xml file to \ 74 | provide cross sections for", 75 | ) 76 | parser.add_argument( 77 | "-d", 78 | "--destination", 79 | type=Path, 80 | default=None, 81 | help="Directory to create new library in", 82 | ) 83 | 84 | parser.add_argument( 85 | "--overwrite", action="store_true", help="Exiting files will be overwritten" 86 | ) 87 | 88 | parser.add_argument( 89 | "--no-overwrite", 90 | action="store_false", 91 | help="Exiting files will not be overwritten", 92 | ) 93 | 94 | parser.set_defaults(overwrite=False) 95 | args = parser.parse_args() 96 | 97 | if args.elements == ["all"]: 98 | args.elements = openmc_data_downloader.ALL_ELEMENT_OPTIONS 99 | if args.elements == ["stable"]: 100 | args.elements = openmc_data_downloader.STABLE_ELEMENT_OPTIONS 101 | 102 | if args.isotopes == ["all"]: 103 | args.isotopes = openmc_data_downloader.ALL_ISOTOPE_OPTIONS 104 | if args.isotopes == ["stable"]: 105 | args.isotopes = openmc_data_downloader.STABLE_ISOTOPE_OPTIONS 106 | 107 | mat = openmc.Material() 108 | for isotope in args.isotopes: 109 | mat.add_nuclide(isotope, 1) 110 | for element in args.elements: 111 | # we get the nuclides for the element and add each nuclide 112 | # adding elements expands to nuclides using the cross_sections.xml 113 | # which can fail if the element is not present in the local cross_sections.xml 114 | nuclides = NATURAL_ABUNDANCE[element] 115 | for nuclide in nuclides: 116 | mat.add_nuclide(nuclide, 1) 117 | for sab in args.sab: 118 | mat.add_s_alpha_beta(sab) 119 | 120 | mats = openmc.Materials([mat]) 121 | 122 | if args.materials_xml: 123 | for material_xml in args.materials_xml: 124 | mats_from_xml = openmc.Materials.from_xml(material_xml) 125 | mats = mats + mats_from_xml 126 | 127 | mats.download_cross_section_data( 128 | libraries=args.libraries, 129 | destination=args.destination, 130 | particles=args.particles, 131 | set_OPENMC_CROSS_SECTIONS=False, 132 | overwrite=args.overwrite, 133 | ) 134 | 135 | 136 | if __name__ == "__main__": 137 | main() 138 | -------------------------------------------------------------------------------- /src/openmc_data_downloader/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import xml.etree.ElementTree as ET 3 | from pathlib import Path 4 | import typing 5 | from typing import List, Optional, Union 6 | from urllib.parse import urlparse 7 | from urllib.request import urlopen 8 | from urllib.error import HTTPError 9 | import pandas as pd 10 | from retry import retry 11 | import openmc 12 | 13 | 14 | from openmc_data_downloader import ( 15 | ALL_ISOTOPE_OPTIONS, 16 | STABLE_ISOTOPE_OPTIONS, 17 | ALL_ELEMENT_OPTIONS, 18 | STABLE_ELEMENT_OPTIONS, 19 | LIB_OPTIONS, 20 | PARTICLE_OPTIONS, 21 | neutron_xs_info, 22 | photon_xs_info, 23 | sab_xs_info, 24 | SAB_OPTIONS, 25 | ) 26 | 27 | _BLOCK_SIZE = 16384 28 | 29 | 30 | def set_environmental_variable(cross_section_xml_path: Union[Path, str]) -> None: 31 | if not isinstance(cross_section_xml_path, Path): 32 | cross_section_xml_path = Path(cross_section_xml_path) 33 | 34 | if cross_section_xml_path.is_file() is False: 35 | raise FileNotFoundError( 36 | f"{cross_section_xml_path} was not found, therefore not setting " 37 | "OPENMC_CROSS_SECTIONS environmental variable" 38 | ) 39 | 40 | print(f"setting OPENMC_CROSS_SECTIONS to {str(cross_section_xml_path)}") 41 | os.environ["OPENMC_CROSS_SECTIONS"] = str(cross_section_xml_path) 42 | # openmc.config['cross_sections'] = cross_section_xml_path 43 | 44 | 45 | def expand_materials_to_isotopes(materials: openmc.Materials): 46 | if not isinstance(materials, openmc.Materials): 47 | raise ValueError("materials argument must be an openmc.Materials() object") 48 | if len(materials) == 0: 49 | raise ValueError( 50 | "There are no openmc.Material() entries within the openmc.Materials() object" 51 | ) 52 | 53 | isotopes_from_materials = [] 54 | for material in materials: 55 | for nuc in material.nuclides: 56 | isotopes_from_materials.append(nuc.name) 57 | 58 | return sorted(list(set(isotopes_from_materials))) 59 | 60 | 61 | def expand_materials_to_sabs(materials: openmc.Materials): 62 | if not isinstance(materials, openmc.Materials): 63 | raise ValueError("materials argument must be an openmc.Materials() object") 64 | if len(materials) == 0: 65 | raise ValueError( 66 | "There are no openmc.Material() entries within the openmc.Materials() object" 67 | ) 68 | 69 | sabs_from_materials = [] 70 | for material in materials: 71 | for sab in material._sab: 72 | sabs_from_materials.append(sab[0]) 73 | 74 | return sorted(list(set(sabs_from_materials))) 75 | 76 | 77 | def expand_materials_to_elements(materials: openmc.Materials): 78 | if not isinstance(materials, openmc.Materials): 79 | raise ValueError("materials argument must be an openmc.Materials() object") 80 | if len(materials) == 0: 81 | raise ValueError( 82 | "There are no openmc.Material() entries within the openmc.Materials() object" 83 | ) 84 | 85 | elements_from_materials = [] 86 | for material in materials: 87 | elements = material.get_elements() 88 | elements_from_materials = elements_from_materials + elements 89 | 90 | return list(set(elements_from_materials)) 91 | 92 | 93 | def download_cross_section_data( 94 | materials: openmc.Materials, 95 | libraries: typing.Iterable[str] = ( 96 | "TENDL-2019", 97 | "ENDFB-7.1-NNDC", 98 | "ENDFB-8.0-NNDC", 99 | "FENDL-3.1d", 100 | ), 101 | destination: Union[str, Path] = None, 102 | particles: Optional[typing.Iterable[str]] = ("neutron", "photon"), 103 | set_OPENMC_CROSS_SECTIONS: bool = True, 104 | overwrite: bool = False, 105 | ) -> str: 106 | """Download cross section data for materials 107 | 108 | Args: 109 | materials: Materials for which to download cross section data 110 | libraries: list of libraries from which to download cross section data. 111 | destination: Specifies a folder location to save the downloaded files. By 112 | default, the files are saved in the current working directory. 113 | particles: list of particles for which to download cross section data ("neutron", "photon") 114 | set_OPENMC_CROSS_SECTIONS: Set the OPENMC_CROSS_SECTIONS environment variable 115 | overwrite: if set to True will overwrite any existing files 116 | 117 | Raises: 118 | ValueError: If the particle is not one of the following: "neutron", "photon" 119 | 120 | Returns: 121 | Path to the cross sections xml file 122 | """ 123 | 124 | for entry in particles: 125 | if entry not in PARTICLE_OPTIONS: 126 | raise ValueError( 127 | f"The particle must be one of the following {PARTICLE_OPTIONS}. Not {entry}" 128 | ) 129 | 130 | dataframe = pd.DataFrame() 131 | 132 | if "neutron" in particles: 133 | isotopes = expand_materials_to_isotopes(materials) 134 | # filters the large dataframe of all isotopes into just the ones you want 135 | dataframe_isotopes_xs = identify_isotopes_to_download( 136 | libraries=libraries, 137 | isotopes=isotopes, 138 | ) 139 | dataframe = pd.concat([dataframe, dataframe_isotopes_xs]) 140 | 141 | if "photon" in particles: 142 | elements = expand_materials_to_elements(materials) 143 | dataframe_elements_xs = identify_elements_to_download( 144 | libraries=libraries, 145 | elements=elements, 146 | ) 147 | dataframe = pd.concat([dataframe, dataframe_elements_xs]) 148 | 149 | sabs = expand_materials_to_sabs(materials) 150 | if len(sabs) > 0: 151 | dataframe_sabs_xs = identify_sabs_to_download( 152 | libraries=libraries, 153 | sabs=sabs, 154 | ) 155 | dataframe = pd.concat([dataframe, dataframe_sabs_xs]) 156 | print("dataframe_sabs_xs", dataframe_sabs_xs) 157 | 158 | print(dataframe) 159 | 160 | download_data_frame_of( 161 | dataframe=dataframe, destination=destination, overwrite=overwrite 162 | ) 163 | 164 | cross_section_xml_path = create_cross_sections_xml(dataframe, destination) 165 | 166 | if set_OPENMC_CROSS_SECTIONS is True: 167 | materials.cross_sections = cross_section_xml_path 168 | # making the cross section xml requires openmc and returns None if 169 | # openmc is not found. 170 | if cross_section_xml_path is not None: 171 | set_environmental_variable(cross_section_xml_path) 172 | else: 173 | print( 174 | "Set your $OPENMC_CROSS_SECTIONS environmental variable to " 175 | f"{cross_section_xml_path} to use this custom library" 176 | ) 177 | 178 | return cross_section_xml_path 179 | 180 | 181 | def download_single_file( 182 | url: str, 183 | output_filename: Union[str, Path] = None, 184 | destination: Union[str, Path] = None, 185 | overwrite: bool = True, 186 | ) -> Path: 187 | """Download file from a URL 188 | 189 | Arguments: 190 | url: URL from which to download 191 | destination: Specifies a folder location to save the downloaded file 192 | 193 | Returns 194 | Name of file written locally 195 | """ 196 | 197 | if output_filename is not None: 198 | if not isinstance(output_filename, Path): 199 | output_filename = Path(output_filename) 200 | 201 | if destination is not None: 202 | if not isinstance(destination, Path): 203 | destination = Path(destination) 204 | 205 | if output_filename is None: 206 | local_path = Path(Path(urlparse(url).path).name) 207 | else: 208 | local_path = output_filename 209 | 210 | if destination is not None: 211 | Path(destination).mkdir(parents=True, exist_ok=True) 212 | local_path = destination / local_path 213 | 214 | if overwrite is False and local_path.is_file(): 215 | print(f"Skipping {local_path}, already downloaded") 216 | return local_path 217 | 218 | local_path = download_url_in_chuncks(url, local_path) 219 | 220 | return local_path 221 | 222 | 223 | @retry(HTTPError, tries=3) 224 | def download_url_in_chuncks(url, local_path): 225 | with urlopen(url) as response: 226 | # Copy file to disk in chunks 227 | print("Downloading {}... ".format(local_path), end="") 228 | 229 | with open(local_path, "wb") as fh: 230 | while True: 231 | chunk = response.read(_BLOCK_SIZE) 232 | if not chunk: 233 | break 234 | fh.write(chunk) 235 | print("") 236 | 237 | return local_path 238 | 239 | 240 | def download_data_frame_of( 241 | dataframe: pd.DataFrame, destination: Union[str, Path], overwrite: bool = True 242 | ): 243 | local_files = [] 244 | for index, row in dataframe.iterrows(): 245 | local_file = download_single_file( 246 | url=row["url"], 247 | output_filename=row["local_file"], 248 | destination=destination, 249 | overwrite=overwrite, 250 | ) 251 | local_files.append(local_file) 252 | 253 | return local_files 254 | 255 | 256 | def create_cross_sections_xml( 257 | dataframe: pd.DataFrame, destination: Union[str, Path] 258 | ) -> str: 259 | library = openmc.data.DataLibrary() 260 | for index, row in dataframe.iterrows(): 261 | if destination is None: 262 | library.register_file(Path(row["local_file"])) 263 | else: 264 | library.register_file(Path(destination) / Path(row["local_file"])) 265 | if destination is None: 266 | library.export_to_xml("cross_sections.xml") 267 | cross_sections_xml_path = "cross_sections.xml" 268 | else: 269 | if not isinstance(destination, Path): 270 | destination = Path(destination) 271 | destination.mkdir(parents=True, exist_ok=True) 272 | library.export_to_xml(destination / "cross_sections.xml") 273 | cross_sections_xml_path = str(destination / "cross_sections.xml") 274 | 275 | absolute_path = str(Path(cross_sections_xml_path).absolute()) 276 | print(f"written cross sections xml file to {absolute_path}") 277 | 278 | return absolute_path 279 | 280 | 281 | def identify_sabs_to_download( 282 | libraries: typing.Tuple[str], 283 | sabs: typing.Tuple[str], 284 | ): 285 | if sabs == []: 286 | return pd.DataFrame() 287 | elif sabs == "all" or sabs == ["all"]: 288 | sabs = SAB_OPTIONS 289 | elif sabs == "stable" or sabs == ["stable"]: 290 | sabs = SAB_OPTIONS # todo check they are all stable, perhaps not UO2 291 | 292 | if len(libraries) == 0: 293 | raise ValueError( 294 | "At least one library must be selected, options are", LIB_OPTIONS 295 | ) 296 | 297 | for sab in sabs: 298 | if sab not in SAB_OPTIONS: 299 | raise ValueError( 300 | f"Sab passing in {sab} not found in available names {SAB_OPTIONS}" 301 | ) 302 | 303 | priority_dict = {} 304 | for counter, entry in enumerate(libraries): 305 | if entry not in LIB_OPTIONS: 306 | raise ValueError( 307 | f"The library must be one of the following {LIB_OPTIONS}. Not {entry}." 308 | ) 309 | 310 | priority_dict[entry] = counter + 1 311 | 312 | print("Searching libraries with the following priority", priority_dict) 313 | 314 | # Tried to removed this dict to dataframe conversion out of the function 315 | # and into the initialization of the package but this resulted in 316 | # a SettingwithCopyWarning which can be fixed and understood here 317 | # https://www.dataquest.io/blog/settingwithcopywarning/ 318 | xs_info_df = pd.DataFrame.from_dict(sab_xs_info) 319 | 320 | is_library = xs_info_df["library"].isin(libraries) 321 | print("Sab found matching library requirements", is_library.values.sum()) 322 | 323 | is_particle = xs_info_df["particle"].isin(["sab"]) 324 | print("Sab found matching particle requirements", is_particle.values.sum()) 325 | 326 | is_sab = xs_info_df["sab"].isin(sabs) 327 | print("Sab found matching isotope requirements", is_sab.values.sum()) 328 | 329 | xs_info_df = xs_info_df[(is_sab) & (is_library) & (is_particle)] 330 | 331 | xs_info_df["priority"] = xs_info_df["library"].map(priority_dict) 332 | 333 | xs_info_df = xs_info_df.sort_values(by=["priority"]) 334 | 335 | xs_info_df = xs_info_df.drop_duplicates(subset=["sab", "particle"], keep="first") 336 | 337 | # end url is unique so this avoids downloading duplicates of the same file 338 | xs_info_df = xs_info_df.drop_duplicates(subset=["url"], keep="first") 339 | 340 | print("Sabs found matching all requirements", len(xs_info_df)) 341 | 342 | return xs_info_df 343 | 344 | 345 | def identify_isotopes_to_download( 346 | libraries: typing.Tuple[str], 347 | isotopes: typing.Tuple[str], 348 | ): 349 | if isotopes == []: 350 | return pd.DataFrame() 351 | elif isotopes == "all" or isotopes == ["all"]: 352 | isotopes = ALL_ISOTOPE_OPTIONS 353 | elif isotopes == "stable" or isotopes == ["stable"]: 354 | isotopes = STABLE_ISOTOPE_OPTIONS 355 | 356 | print("isotopes", isotopes) 357 | 358 | if len(libraries) == 0: 359 | raise ValueError( 360 | "At least one library must be selected, options are", LIB_OPTIONS 361 | ) 362 | 363 | priority_dict = {} 364 | for counter, entry in enumerate(libraries): 365 | if entry not in LIB_OPTIONS: 366 | raise ValueError( 367 | f"The library must be one of the following {LIB_OPTIONS}. Not {entry}." 368 | ) 369 | 370 | priority_dict[entry] = counter + 1 371 | 372 | print("Searching libraries with the following priority", priority_dict) 373 | 374 | # Tried to removed this dict to dataframe conversion out of the function 375 | # and into the initialization of the package but this resulted in 376 | # a SettingwithCopyWarning which can be fixed and understood here 377 | # https://www.dataquest.io/blog/settingwithcopywarning/ 378 | xs_info_df = pd.DataFrame.from_dict(neutron_xs_info) 379 | 380 | is_library = xs_info_df["library"].isin(libraries) 381 | print("Isotopes found matching library requirements", is_library.values.sum()) 382 | 383 | is_particle = xs_info_df["particle"].isin(["neutron"]) 384 | print("Isotopes found matching particle requirements", is_particle.values.sum()) 385 | 386 | is_isotope = xs_info_df["isotope"].isin(isotopes) 387 | print("Isotopes found matching isotope requirements", is_isotope.values.sum()) 388 | 389 | xs_info_df = xs_info_df[(is_isotope) & (is_library) & (is_particle)] 390 | 391 | xs_info_df["priority"] = xs_info_df["library"].map(priority_dict) 392 | 393 | xs_info_df = xs_info_df.sort_values(by=["priority"]) 394 | 395 | xs_info_df = xs_info_df.drop_duplicates( 396 | subset=["isotope", "particle"], keep="first" 397 | ) 398 | 399 | # end url is unique so this avoids downloading duplicates of the same file 400 | xs_info_df = xs_info_df.drop_duplicates(subset=["url"], keep="first") 401 | 402 | print("Isotopes found matching all requirements", len(xs_info_df)) 403 | 404 | return xs_info_df 405 | 406 | 407 | def identify_elements_to_download( 408 | libraries: typing.Tuple[str], 409 | elements: typing.Tuple[str], 410 | ): 411 | if elements == []: 412 | return pd.DataFrame() 413 | elif elements == "all" or elements == ["all"]: 414 | elements = ALL_ELEMENT_OPTIONS 415 | elif elements == "stable" or elements == ["stable"]: 416 | elements = STABLE_ELEMENT_OPTIONS 417 | 418 | print("elements", elements) 419 | 420 | if len(libraries) == 0: 421 | raise ValueError( 422 | "At least one library must be selected, options are", LIB_OPTIONS 423 | ) 424 | 425 | priority_dict = {} 426 | for counter, entry in enumerate(libraries): 427 | if entry not in LIB_OPTIONS: 428 | raise ValueError("The library must be one of the following", LIB_OPTIONS) 429 | 430 | priority_dict[entry] = counter + 1 431 | 432 | print("Searching libraries with the following priority", priority_dict) 433 | 434 | # Tried to removed this dict to dataframe conversion out of the function 435 | # and into the initialization of the package but this resulted in 436 | # a SettingwithCopyWarning which can be fixed and understood here 437 | # https://www.dataquest.io/blog/settingwithcopywarning/ 438 | xs_info_df = pd.DataFrame.from_dict(photon_xs_info) 439 | 440 | is_library = xs_info_df["library"].isin(libraries) 441 | print("Elements found matching library requirements", is_library.values.sum()) 442 | 443 | is_particle = xs_info_df["particle"].isin(["photon"]) 444 | print("Elements found matching particle requirements", is_particle.values.sum()) 445 | 446 | is_element = xs_info_df["element"].isin(elements) 447 | print("Elements found matching element requirements", is_element.values.sum()) 448 | 449 | xs_info_df = xs_info_df[(is_element) & (is_library) & (is_particle)] 450 | 451 | xs_info_df["priority"] = xs_info_df["library"].map(priority_dict) 452 | 453 | xs_info_df = xs_info_df.sort_values(by=["priority"]) 454 | 455 | xs_info_df = xs_info_df.drop_duplicates( 456 | subset=["element", "particle"], keep="first" 457 | ) 458 | 459 | # end url is unique so this avoids downloading duplicates of the same file 460 | xs_info_df = xs_info_df.drop_duplicates(subset=["url"], keep="first") 461 | 462 | print("Elements found matching all requirements", len(xs_info_df)) 463 | 464 | return xs_info_df 465 | 466 | 467 | openmc.Materials.download_cross_section_data = download_cross_section_data 468 | -------------------------------------------------------------------------------- /tests/test_command_line_usage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | __author__ = "Jonathan Shimwell" 4 | 5 | 6 | import os 7 | import time 8 | from pathlib import Path 9 | 10 | import openmc 11 | import openmc_data_downloader 12 | 13 | 14 | # def test_single_isotope_download_endf_71_wmp(): 15 | # os.system("rm *.h5") 16 | # os.system("openmc_data_downloader -l ENDFB-7.1-WMP -i H1") 17 | 18 | # assert Path("ENDFB-7.1-WMP_H1.h5").is_file() 19 | # assert len(list(Path(".").glob("*.h5"))) == 1 20 | 21 | 22 | def test_single_isotope_download_tendl_2019(): 23 | os.system("rm *.h5") 24 | os.system("openmc_data_downloader -l TENDL-2019 -i H1") 25 | 26 | assert Path("TENDL-2019_H1.h5").is_file() 27 | assert len(list(Path(".").glob("*.h5"))) == 1 28 | 29 | 30 | def test_multiple_isotope_download(): 31 | os.system("rm *.h5") 32 | os.system("openmc_data_downloader -l TENDL-2019 -i H1 He4") 33 | 34 | assert Path("TENDL-2019_H1.h5").is_file() 35 | assert Path("TENDL-2019_He4.h5").is_file() 36 | assert len(list(Path(".").glob("*.h5"))) == 2 37 | 38 | 39 | def test_correct_files_from_command_line_usage_2(): 40 | os.system("rm *.h5") 41 | os.system("openmc_data_downloader -l TENDL-2019 -e F") 42 | 43 | assert Path("TENDL-2019_F19.h5").is_file() 44 | assert len(list(Path(".").glob("*.h5"))) == 1 45 | 46 | 47 | def test_correct_files_from_command_line_usage_3(): 48 | os.system("rm *.h5") 49 | 50 | os.system("openmc_data_downloader -l ENDFB-7.1-NNDC -e Co Y") 51 | 52 | assert Path("ENDFB-7.1-NNDC_Co59.h5").is_file() 53 | assert Path("ENDFB-7.1-NNDC_Y89.h5").is_file() 54 | assert len(list(Path(".").glob("*.h5"))) == 2 55 | 56 | 57 | def test_correct_files_from_command_line_usage_4(): 58 | os.system("rm *.h5 materials.xml") 59 | 60 | my_mat = openmc.Material() 61 | my_mat.add_element("Nb", 0.5) 62 | my_mat.add_nuclide("Cs133", 0.5) 63 | os.system("rm materials.xml") 64 | openmc.Materials([my_mat]).export_to_xml() 65 | 66 | os.system("openmc_data_downloader -l TENDL-2019 -m materials.xml") 67 | 68 | assert Path("TENDL-2019_Nb93.h5").is_file() 69 | assert Path("TENDL-2019_Cs133.h5").is_file() 70 | assert len(list(Path(".").glob("*.h5"))) == 2 71 | 72 | 73 | def test_correct_files_from_command_line_usage_5(): 74 | """Tests downloading with FENDL""" 75 | os.system("rm *.h5 materials.xml") 76 | 77 | my_mat = openmc.Material() 78 | my_mat.add_element("Nb", 0.5) 79 | os.system("rm materials.xml") 80 | openmc.Materials([my_mat]).export_to_xml() 81 | 82 | os.system("openmc_data_downloader -l FENDL-3.1d -m materials.xml -p neutron") 83 | 84 | assert Path("FENDL-3.1d_Nb93.h5").is_file() 85 | assert len(list(Path(".").glob("*.h5"))) == 1 86 | 87 | 88 | def test_correct_files_from_command_line_usage_6(): 89 | """Tests downloading with FENDL as priority but without the element in 90 | FENDL""" 91 | 92 | os.system("rm *.h5") 93 | 94 | os.system("openmc_data_downloader -l FENDL-3.1d TENDL-2019 -e Pr") 95 | 96 | assert Path("TENDL-2019_Pr141.h5").is_file() 97 | assert len(list(Path(".").glob("*.h5"))) == 1 98 | 99 | 100 | def test_photon_download_of_isotope_nndc(): 101 | """Tests downloading with NNDC photon data and checks it exists""" 102 | 103 | os.system("rm *.h5") 104 | 105 | os.system("openmc_data_downloader -l ENDFB-7.1-NNDC -i He4 -p photon") 106 | 107 | assert Path("ENDFB-7.1-NNDC_He.h5").is_file() 108 | assert len(list(Path(".").glob("*.h5"))) == 1 109 | 110 | 111 | def test_photon_download_of_isotope_fendl(): 112 | """Tests downloading with FENDL photon data and checks it exists""" 113 | 114 | os.system("rm *.h5") 115 | 116 | os.system("openmc_data_downloader -l FENDL-3.1d -i He4 -p photon") 117 | 118 | assert Path("FENDL-3.1d_He.h5").is_file() 119 | assert len(list(Path(".").glob("*.h5"))) == 1 120 | 121 | 122 | def test_neutron_and_photon_download_of_isotope_fendl(): 123 | """Tests downloading with FENDL photon data and checks it exists""" 124 | 125 | os.system("rm *.h5") 126 | 127 | os.system("openmc_data_downloader -l FENDL-3.1d -i He4 -p photon neutron") 128 | 129 | assert Path("FENDL-3.1d_He.h5").is_file() 130 | assert Path("FENDL-3.1d_He4.h5").is_file() 131 | assert len(list(Path(".").glob("*.h5"))) == 2 132 | 133 | 134 | def test_sab_download_with_endf(): 135 | """Tests downloading with FENDL photon data and checks it exists""" 136 | 137 | os.system("rm *.h5") 138 | 139 | os.system( 140 | "openmc_data_downloader -l ENDFB-7.1-NNDC TENDL-2019 -e Be O -s c_Be_in_BeO" 141 | ) 142 | 143 | assert Path("ENDFB-7.1-NNDC_Be9.h5").is_file() 144 | assert Path("ENDFB-7.1-NNDC_O16.h5").is_file() 145 | assert Path("ENDFB-7.1-NNDC_O17.h5").is_file() 146 | assert Path("TENDL-2019_O18.h5").is_file() 147 | assert Path("ENDFB-7.1-NNDC_c_Be_in_BeO.h5").is_file() 148 | assert Path("materials.xml").is_file() 149 | assert len(list(Path(".").glob("*.h5"))) == 5 150 | 151 | 152 | def test_download_single_file_with_overwrite_speed_up(): 153 | """Checks that downloading with overwrite to False is quicker""" 154 | 155 | current_time = time.time() 156 | os.system("openmc_data_downloader -l ENDFB-7.1-NNDC TENDL-2019 -e Be --overwrite") 157 | time_after_download = time.time() 158 | time_to_download = time_after_download - current_time 159 | 160 | current_time = time.time() 161 | os.system( 162 | "openmc_data_downloader -l ENDFB-7.1-NNDC TENDL-2019 -e Be --no-overwrite", 163 | ) 164 | time_after_download = time.time() 165 | time_to_not_download = time_after_download - current_time 166 | 167 | assert time_to_not_download < time_to_download 168 | -------------------------------------------------------------------------------- /tests/test_cross_sections_directory.py: -------------------------------------------------------------------------------- 1 | from openmc_data_downloader import cross_sections_directory 2 | 3 | 4 | def test_neutron_isotopes(): 5 | tendl_2019_xs_neutron_info = ( 6 | cross_sections_directory.get_isotopes_or_elements_info_from_xml( 7 | "neutron", 8 | "TENDL-2019", 9 | ) 10 | ) 11 | assert len(tendl_2019_xs_neutron_info) == 630 12 | 13 | nndc_71_neutron_xs_info = ( 14 | cross_sections_directory.get_isotopes_or_elements_info_from_xml( 15 | "neutron", 16 | "ENDFB-7.1-NNDC", 17 | ) 18 | ) 19 | assert len(nndc_71_neutron_xs_info) == 423 20 | 21 | nndc_80_neutron_xs_info = ( 22 | cross_sections_directory.get_isotopes_or_elements_info_from_xml( 23 | "neutron", 24 | "ENDFB-8.0-NNDC", 25 | ) 26 | ) 27 | assert len(nndc_80_neutron_xs_info) == 556 28 | 29 | fendl_31d_neutron_xs_info = ( 30 | cross_sections_directory.get_isotopes_or_elements_info_from_xml( 31 | "neutron", 32 | "FENDL-3.1d", 33 | ) 34 | ) 35 | assert len(fendl_31d_neutron_xs_info) == 180 36 | 37 | 38 | def test_photon_elements(): 39 | nndc_71_photon_xs_info = ( 40 | cross_sections_directory.get_isotopes_or_elements_info_from_xml( 41 | "photon", 42 | "ENDFB-7.1-NNDC", 43 | ) 44 | ) 45 | assert len(nndc_71_photon_xs_info) == 100 46 | 47 | fendl_31d_photon_xs_info = ( 48 | cross_sections_directory.get_isotopes_or_elements_info_from_xml( 49 | "photon", 50 | "FENDL-3.1d", 51 | ) 52 | ) 53 | assert len(fendl_31d_photon_xs_info) == 59 54 | 55 | nndc_80_photon_xs_info = ( 56 | cross_sections_directory.get_isotopes_or_elements_info_from_xml( 57 | "photon", 58 | "ENDFB-8.0-NNDC", 59 | ) 60 | ) 61 | assert len(nndc_80_photon_xs_info) == 100 62 | 63 | 64 | def test_sab(): 65 | nndc_71_sab_xs_info = ( 66 | cross_sections_directory.get_isotopes_or_elements_info_from_xml( 67 | "sab", 68 | "ENDFB-7.1-NNDC", 69 | ) 70 | ) 71 | assert len(nndc_71_sab_xs_info) == 20 72 | 73 | nndc_80_sab_xs_info = ( 74 | cross_sections_directory.get_isotopes_or_elements_info_from_xml( 75 | "sab", 76 | "ENDFB-8.0-NNDC", 77 | ) 78 | ) 79 | assert len(nndc_80_sab_xs_info) == 34 80 | -------------------------------------------------------------------------------- /tests/test_functions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | __author__ = "Jonathan Shimwell" 4 | 5 | 6 | import os 7 | import unittest 8 | import time 9 | import pytest 10 | 11 | import openmc 12 | import pandas as pd 13 | from openmc_data_downloader import ( 14 | expand_materials_to_isotopes, 15 | identify_isotopes_to_download, 16 | identify_sabs_to_download, 17 | expand_materials_to_sabs, 18 | download_single_file, 19 | ) 20 | 21 | 22 | def test_identify_isotopes_to_download_finds_tendl_neutron(): 23 | filtered_df = identify_isotopes_to_download( 24 | libraries=["TENDL-2019"], isotopes=["Be9"] 25 | ) 26 | answer_df = pd.DataFrame.from_dict( 27 | { 28 | "library": ["TENDL-2019"], 29 | "remote_file": ["Be9.h5"], 30 | "url": [ 31 | "https://github.com/openmc-data-storage/TENDL-2019/raw/main/h5_files/Be9.h5" 32 | ], 33 | "local_file": ["TENDL-2019_Be9.h5"], 34 | "particle": ["neutron"], 35 | "isotope": ["Be9"], 36 | "element": ["Be"], 37 | "priority": [1], 38 | } 39 | ) 40 | print(filtered_df) 41 | assert len(filtered_df.values) == 1 42 | assert list(filtered_df.keys()) == list(answer_df.keys()) 43 | assert filtered_df.values[0].tolist() == answer_df.values[0].tolist() 44 | 45 | # can't get pandas dataframe comparison to work so resorted to the lists above 46 | # assert_frame_equal(answer_df, filtered_df) 47 | 48 | 49 | def test_identify_isotopes_to_download_finds_fendl_photon(): 50 | filtered_df = identify_isotopes_to_download( 51 | libraries=["FENDL-3.1d"], isotopes=["Be9"] 52 | ) 53 | answer_df = pd.DataFrame.from_dict( 54 | { 55 | "library": ["FENDL-3.1d"], 56 | "remote_file": ["Be9.h5"], 57 | "url": [ 58 | "https://github.com/openmc-data-storage/FENDL-3.1d/raw/main/h5_files/neutron/Be9.h5" 59 | ], 60 | "local_file": ["FENDL-3.1d_Be9.h5"], 61 | "particle": ["neutron"], 62 | "isotope": ["Be9"], 63 | "element": ["Be"], 64 | "priority": [1], 65 | } 66 | ) 67 | 68 | assert len(filtered_df.values) == 1 69 | assert list(filtered_df.keys()) == list(answer_df.keys()) 70 | assert filtered_df.values[0].tolist() == answer_df.values[0].tolist() 71 | 72 | 73 | def test_identify_isotopes_to_download_finds_fendl_photon_neutron(): 74 | filtered_df = identify_isotopes_to_download( 75 | libraries=["FENDL-3.1d"], isotopes=["Be9"] 76 | ) 77 | answer_df = pd.DataFrame.from_dict( 78 | { 79 | "library": ["FENDL-3.1d"], 80 | "remote_file": ["Be9.h5"], 81 | "url": [ 82 | "https://github.com/openmc-data-storage/FENDL-3.1d/raw/main/h5_files/neutron/Be9.h5", 83 | ], 84 | "local_file": ["FENDL-3.1d_Be9.h5"], 85 | "particle": ["neutron"], 86 | "isotope": ["Be9"], 87 | "element": ["Be"], 88 | "priority": [1], 89 | } 90 | ) 91 | 92 | assert len(filtered_df.values) == 1 93 | assert list(filtered_df.keys()) == list(answer_df.keys()) 94 | assert filtered_df.values[0].tolist() == answer_df.values[0].tolist() 95 | 96 | 97 | def test_identify_isotopes_to_download_finds_fendl_photon_neutron_multi_isotopes(): 98 | filtered_df = identify_isotopes_to_download( 99 | libraries=["FENDL-3.1d"], 100 | isotopes=["Fe56", "Fe57"], 101 | ) 102 | answer_df = pd.DataFrame.from_dict( 103 | { 104 | "library": ["FENDL-3.1d", "FENDL-3.1d"], 105 | "remote_file": ["Fe56.h5", "Fe57.h5"], 106 | "url": [ 107 | "https://github.com/openmc-data-storage/FENDL-3.1d/raw/main/h5_files/neutron/Fe56.h5", 108 | "https://github.com/openmc-data-storage/FENDL-3.1d/raw/main/h5_files/neutron/Fe57.h5", 109 | ], 110 | "local_file": [ 111 | "FENDL-3.1d_Fe56.h5", 112 | "FENDL-3.1d_Fe57.h5", 113 | ], 114 | "particle": ["neutron", "neutron"], 115 | "isotope": ["Fe56", "Fe57"], 116 | "element": ["Fe", "Fe"], 117 | "priority": [1, 1], 118 | } 119 | ) 120 | 121 | assert len(filtered_df.values) == 2 122 | assert list(filtered_df.keys()) == list(answer_df.keys()) 123 | assert filtered_df.values[0].tolist() == answer_df.values[0].tolist() 124 | assert filtered_df.values[1].tolist() == answer_df.values[1].tolist() 125 | 126 | 127 | def test_identify_isotopes_to_download_all(): 128 | filtered_df = identify_isotopes_to_download( 129 | libraries=["FENDL-3.1d"], isotopes=["all"] 130 | ) 131 | 132 | assert len(filtered_df.values) == 180 133 | 134 | filtered_df = identify_isotopes_to_download( 135 | libraries=["FENDL-3.1d"], isotopes="all" 136 | ) 137 | 138 | assert len(filtered_df.values) == 180 139 | 140 | 141 | def test_expand_materials_from_object_list_with_single_mat(): 142 | my_mat = openmc.Material() 143 | my_mat.add_nuclide("Pu239", 3.7047e-2) 144 | my_mat.add_nuclide("Pu240", 1.7512e-3) 145 | my_mat.add_nuclide("Pu241", 1.1674e-4) 146 | 147 | assert expand_materials_to_isotopes(openmc.Materials([my_mat])) == [ 148 | "Pu239", 149 | "Pu240", 150 | "Pu241", 151 | ] 152 | 153 | 154 | def test_expand_materials_from_object_with_single_mat(): 155 | my_mat = openmc.Material() 156 | my_mat.add_nuclide("Pu239", 3.7047e-2) 157 | my_mat.add_nuclide("Pu240", 1.7512e-3) 158 | my_mat.add_nuclide("Pu241", 1.1674e-4) 159 | 160 | assert expand_materials_to_isotopes(openmc.Materials([my_mat])) == [ 161 | "Pu239", 162 | "Pu240", 163 | "Pu241", 164 | ] 165 | 166 | 167 | def test_expand_materials_from_object_list_with_multiple_mat(): 168 | my_mat1 = openmc.Material() 169 | my_mat1.add_nuclide("Li6", 0.5) 170 | my_mat1.add_nuclide("Li7", 0.25) 171 | 172 | my_mat2 = openmc.Material() 173 | my_mat2.add_nuclide("Al27", 0.25) 174 | 175 | assert expand_materials_to_isotopes(openmc.Materials([my_mat1, my_mat2])) == [ 176 | "Al27", 177 | "Li6", 178 | "Li7", 179 | ] 180 | 181 | 182 | def test_expand_materials_from_object_list_with_openmc_materials(): 183 | my_mat1 = openmc.Material() 184 | my_mat1.add_nuclide("Li6", 0.5) 185 | my_mat1.add_nuclide("Li7", 0.25) 186 | 187 | my_mat2 = openmc.Material() 188 | my_mat2.add_nuclide("Al27", 0.25) 189 | 190 | mats = openmc.Materials([my_mat1, my_mat2]) 191 | 192 | assert expand_materials_to_isotopes(mats) == ["Al27", "Li6", "Li7"] 193 | 194 | 195 | def test_expand_material_xmls_for_sabs_with_sab(): 196 | my_mat = openmc.Material() 197 | my_mat.add_element("Be", 0.5) 198 | my_mat.add_s_alpha_beta("c_Be_in_BeO") 199 | my_mats = openmc.Materials([my_mat]) 200 | 201 | assert expand_materials_to_sabs(my_mats) == ["c_Be_in_BeO"] 202 | 203 | 204 | def test_expand_material_xmls_for_sabs_with_two_sab(): 205 | my_mat = openmc.Material() 206 | my_mat.add_element("Be", 0.5) 207 | my_mat.add_s_alpha_beta("c_Be_in_BeO") 208 | my_mat.add_s_alpha_beta("c_H_in_H2O") 209 | my_mats = openmc.Materials([my_mat]) 210 | 211 | assert expand_materials_to_sabs(my_mats) == [ 212 | "c_Be_in_BeO", 213 | "c_H_in_H2O", 214 | ] 215 | 216 | 217 | def test_expand_material_for_sabs_with_two_sab(): 218 | my_mat = openmc.Material() 219 | my_mat.add_element("Be", 0.5) 220 | my_mat.add_s_alpha_beta("c_Be_in_BeO") 221 | my_mat.add_s_alpha_beta("c_H_in_H2O") 222 | my_mats = openmc.Materials([my_mat]) 223 | assert expand_materials_to_sabs(my_mats) == ["c_Be_in_BeO", "c_H_in_H2O"] 224 | 225 | 226 | def test_expand_material_for_sabs_with_sab(): 227 | my_mat = openmc.Material() 228 | my_mat.add_element("Be", 0.5) 229 | my_mat.add_s_alpha_beta("c_H_in_H2O") 230 | my_mats = openmc.Materials([my_mat]) 231 | 232 | assert expand_materials_to_sabs(my_mats) == ["c_H_in_H2O"] 233 | 234 | 235 | def test_incorrect_material_enpty(): 236 | with pytest.raises(ValueError): 237 | expand_materials_to_sabs("my_mat") 238 | 239 | 240 | def test_incorrect_sab_name(): 241 | with pytest.raises(ValueError): 242 | identify_sabs_to_download(libraries=["ENDFB-7.1-NNDC"], sabs=["incorrect name"]) 243 | 244 | 245 | def test_incorrect_libraries(): 246 | with pytest.raises(ValueError): 247 | identify_sabs_to_download(libraries=[], sabs=["c_Fe56"]) 248 | 249 | 250 | def test_incorrect_library_name_for_sab_identifying(): 251 | with pytest.raises(ValueError): 252 | identify_sabs_to_download(libraries=["incorrect name"], sabs=["c_Fe56"]) 253 | 254 | 255 | def test_library_values_single_entry_list(): 256 | isotopes_df = identify_isotopes_to_download( 257 | libraries=["TENDL-2019"], isotopes=["Al27", "Li6"] 258 | ) 259 | 260 | assert len(isotopes_df) == 2 261 | 262 | 263 | def test_emplty_isotopes(): 264 | empty_df = identify_isotopes_to_download(libraries=["TENDL-2019"], isotopes=[]) 265 | assert len(empty_df) == 0 266 | assert isinstance(empty_df, type(pd.DataFrame())) 267 | 268 | 269 | def test_incorrect_library_values_empty(): 270 | with pytest.raises(ValueError): 271 | identify_isotopes_to_download(libraries=[], isotopes="Li6") 272 | 273 | 274 | def test_incorrect_library_values_wrong(): 275 | with pytest.raises(ValueError): 276 | identify_isotopes_to_download(libraries=["coucou"], isotopes="Li6") 277 | 278 | 279 | def test_incorrect_expand_materials_to_isotopes_with_incorrect_args(): 280 | """Checks than an error is raised when incorrect values of materials 281 | are passed""" 282 | 283 | with pytest.raises(ValueError): 284 | expand_materials_to_isotopes(1) 285 | 286 | with pytest.raises(ValueError): 287 | expand_materials_to_isotopes([1, 2, 3]) 288 | 289 | 290 | def test_download_single_file_with_overwrite_speed_up(): 291 | """Checks that downloading with overwrite to False is quicker""" 292 | 293 | current_time = time.time() 294 | download_single_file( 295 | url="https://github.com/openmc-data-storage/FENDL-3.1d/raw/main/h5_files/photon/Fe.h5", 296 | output_filename="Fe56_new_download.h5", 297 | overwrite=True, 298 | ) 299 | time_after_download = time.time() 300 | time_to_download = time_after_download - current_time 301 | 302 | current_time = time.time() 303 | download_single_file( 304 | url="https://github.com/openmc-data-storage/FENDL-3.1d/raw/main/h5_files/photon/Fe.h5", 305 | output_filename="Fe56_new_download.h5", 306 | overwrite=False, 307 | ) 308 | time_after_download = time.time() 309 | time_to_not_download = time_after_download - current_time 310 | 311 | assert time_to_not_download < time_to_download 312 | -------------------------------------------------------------------------------- /tests/test_use_in_openmc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | 5 | from pathlib import Path 6 | 7 | import openmc 8 | import openmc_data_downloader 9 | 10 | import pytest 11 | 12 | 13 | @pytest.mark.parametrize("monkeypatch", [True, False]) 14 | def test_materials_download(monkeypatch: bool): 15 | """openmc.Materials are a container for openmc.Material objects. This 16 | test checks that they are handeled correctly""" 17 | 18 | os.system("rm *.h5") 19 | os.system("rm my_custom_nuclear_data_with_materials/*.h5") 20 | 21 | # Define material 22 | my_mat_1 = openmc.Material() 23 | my_mat_1.add_nuclide("Pu239", 3.7047e-2) 24 | 25 | my_mat_2 = openmc.Material() 26 | my_mat_2.add_nuclide("Pu240", 1.7512e-3) 27 | my_mat_2.add_nuclide("As75", 1.3752e-3) 28 | mats = openmc.Materials([my_mat_1, my_mat_2]) 29 | 30 | if monkeypatch: 31 | mats.download_cross_section_data( 32 | destination="my_custom_nuclear_data_with_materials", 33 | libraries=["ENDFB-8.0-NNDC"], 34 | set_OPENMC_CROSS_SECTIONS=True, 35 | particles=["neutron"], 36 | ) 37 | else: 38 | openmc_data_downloader.download_cross_section_data( 39 | materials=mats, 40 | destination="my_custom_nuclear_data_with_materials", 41 | libraries=["ENDFB-8.0-NNDC"], 42 | set_OPENMC_CROSS_SECTIONS=True, 43 | particles=["neutron"], 44 | ) 45 | mats.export_to_xml() 46 | 47 | # Create a sphere of my_mat 48 | surf_1 = openmc.Sphere(r=1) 49 | surf_2 = openmc.Sphere(r=2, boundary_type="vacuum") 50 | cell_1 = openmc.Cell(fill=my_mat_1, region=-surf_1) 51 | cell_2 = openmc.Cell(fill=my_mat_1, region=surf_1 and -surf_2) 52 | openmc.Geometry([cell_1, cell_2]).export_to_xml() 53 | 54 | # Define settings for the simulation 55 | settings = openmc.Settings() 56 | settings.particles = 5 57 | settings.batches = 3 58 | settings.inactive = 0 59 | center = (0.0, 0.0, 0.0) 60 | settings.source = openmc.Source(space=openmc.stats.Point(center)) 61 | settings.export_to_xml() 62 | 63 | # this clears the enviromental varible just to be sure that current 64 | # system settings are not being used 65 | try: 66 | del os.environ["OPENMC_CROSS_SECTIONS"] 67 | except KeyError: 68 | # key not found on system 69 | pass 70 | 71 | os.system("echo $OPENMC_CROSS_SECTIONS") 72 | openmc.run() 73 | 74 | assert Path( 75 | "my_custom_nuclear_data_with_materials/ENDFB-8.0-NNDC_Pu239.h5" 76 | ).is_file() 77 | assert Path( 78 | "my_custom_nuclear_data_with_materials/ENDFB-8.0-NNDC_Pu240.h5" 79 | ).is_file() 80 | assert Path( 81 | "my_custom_nuclear_data_with_materials/ENDFB-8.0-NNDC_As75.h5" 82 | ).is_file() 83 | 84 | assert Path("summary.h5").is_file() 85 | assert Path("statepoint.3.h5").is_file() 86 | 87 | assert len(list(Path("my_custom_nuclear_data_with_materials").glob("*.h5"))) == 3 88 | 89 | 90 | def test_simulation_with_destination(): 91 | os.system("rm *.h5") 92 | os.system("rm my_custom_nuclear_data_dir/*.h5") 93 | 94 | # Define material 95 | my_mat = openmc.openmc.Material() 96 | my_mat.add_nuclide("Pu239", 3.7047e-2) 97 | my_mat.add_nuclide("Pu240", 1.7512e-3) 98 | my_mat.add_nuclide("As75", 1.3752e-3) 99 | my_mats = openmc.openmc.Materials([my_mat]) 100 | my_mats.download_cross_section_data( 101 | destination="my_custom_nuclear_data_dir", 102 | libraries=["TENDL-2019"], 103 | set_OPENMC_CROSS_SECTIONS=True, 104 | ) 105 | my_mats.export_to_xml() 106 | 107 | # Create a sphere of my_mat 108 | surf = openmc.Sphere(r=6.3849, boundary_type="vacuum") 109 | main_cell = openmc.Cell(fill=my_mat, region=-surf) 110 | openmc.Geometry([main_cell]).export_to_xml() 111 | 112 | # Define settings for the simulation 113 | settings = openmc.Settings() 114 | settings.particles = 10 115 | settings.batches = 2 116 | settings.inactive = 0 117 | center = (0.0, 0.0, 0.0) 118 | settings.source = openmc.Source(space=openmc.stats.Point(center)) 119 | settings.export_to_xml() 120 | 121 | # this clears the enviromental varible just to be sure that current 122 | # system settings are not being used 123 | try: 124 | del os.environ["OPENMC_CROSS_SECTIONS"] 125 | except KeyError: 126 | # key not found on system 127 | pass 128 | 129 | os.system("echo $OPENMC_CROSS_SECTIONS") 130 | openmc.run() 131 | 132 | assert Path("my_custom_nuclear_data_dir/TENDL-2019_Pu239.h5").is_file() 133 | assert Path("my_custom_nuclear_data_dir/TENDL-2019_Pu240.h5").is_file() 134 | assert Path("my_custom_nuclear_data_dir/TENDL-2019_As75.h5").is_file() 135 | 136 | assert Path("summary.h5").is_file() 137 | assert Path("statepoint.2.h5").is_file() 138 | 139 | assert len(list(Path("my_custom_nuclear_data_dir").glob("*.h5"))) == 3 140 | 141 | 142 | def test_photon_simulation_with_single_mat(): 143 | os.system("rm *.h5") 144 | 145 | # this clears the enviromental varible just to be sure that current 146 | # system settings are not being used 147 | try: 148 | del os.environ["OPENMC_CROSS_SECTIONS"] 149 | except KeyError: 150 | # key not found on system 151 | pass 152 | 153 | # Define material 154 | my_mat = openmc.Material() 155 | my_mat.add_element("Fe", 1) 156 | my_mats = openmc.Materials([my_mat]) 157 | my_mats.export_to_xml() 158 | my_mats.download_cross_section_data( 159 | libraries=["FENDL-3.1d"], 160 | # TODO find out why neutrons are needed here 161 | particles=["photon", "neutron"], 162 | set_OPENMC_CROSS_SECTIONS=True, 163 | ) 164 | 165 | # Create a sphere of my_mat 166 | surf = openmc.Sphere(r=6.3849, boundary_type="vacuum") 167 | main_cell = openmc.Cell(fill=my_mat, region=-surf) 168 | openmc.Geometry([main_cell]).export_to_xml() 169 | 170 | # Define settings for the simulation 171 | settings = openmc.Settings() 172 | settings.particles = 10 173 | settings.batches = 2 174 | settings.inactive = 0 175 | settings.run_mode = "fixed source" 176 | settings.photon_transport = True 177 | 178 | source = openmc.Source() 179 | source.space = openmc.stats.Point((0, 0, 0)) 180 | source.angle = openmc.stats.Isotropic() 181 | # This is a Co60 source, see the task on sources to understand it 182 | source.energy = openmc.stats.Discrete([1.1732e6], [1.0]) 183 | source.particle = "photon" 184 | 185 | settings.source = source 186 | settings.export_to_xml() 187 | 188 | os.system("echo $OPENMC_CROSS_SECTIONS") 189 | openmc.run() 190 | 191 | assert Path("FENDL-3.1d_Fe.h5").is_file() 192 | 193 | assert Path("summary.h5").is_file() 194 | assert Path("statepoint.2.h5").is_file() 195 | 196 | # normally 3 if just photons used 197 | assert len(list(Path(".").glob("*.h5"))) == 7 198 | 199 | 200 | def test_photon_neutron_simulation_with_single_mat(): 201 | os.system("rm *.h5") 202 | 203 | # this clears the enviromental varible just to be sure that current 204 | # system settings are not being used 205 | try: 206 | del os.environ["OPENMC_CROSS_SECTIONS"] 207 | except KeyError: 208 | # key not found on system 209 | pass 210 | 211 | # Define material 212 | my_mat = openmc.openmc.Material() 213 | my_mat.add_element("Fe", 1) 214 | my_mats = openmc.openmc.Materials([my_mat]) 215 | my_mats.export_to_xml() 216 | my_mats.download_cross_section_data( 217 | libraries=["FENDL-3.1d"], 218 | particles=["neutron", "photon"], 219 | set_OPENMC_CROSS_SECTIONS=True, 220 | ) 221 | 222 | # Create a sphere of my_mat 223 | surf = openmc.Sphere(r=6.3849, boundary_type="vacuum") 224 | main_cell = openmc.Cell(fill=my_mat, region=-surf) 225 | openmc.Geometry([main_cell]).export_to_xml() 226 | 227 | # Define settings for the simulation 228 | settings = openmc.Settings() 229 | settings.particles = 10 230 | settings.batches = 2 231 | settings.inactive = 0 232 | settings.photon_transport = True 233 | center = (0.0, 0.0, 0.0) 234 | settings.source = openmc.Source(space=openmc.stats.Point(center)) 235 | settings.run_mode = "fixed source" 236 | settings.export_to_xml() 237 | 238 | os.system("echo $OPENMC_CROSS_SECTIONS") 239 | openmc.run() 240 | 241 | assert Path("FENDL-3.1d_Fe54.h5").is_file() 242 | assert Path("FENDL-3.1d_Fe56.h5").is_file() 243 | assert Path("FENDL-3.1d_Fe57.h5").is_file() 244 | assert Path("FENDL-3.1d_Fe58.h5").is_file() 245 | assert Path("FENDL-3.1d_Fe.h5").is_file() 246 | 247 | assert Path("summary.h5").is_file() 248 | assert Path("statepoint.2.h5").is_file() 249 | assert len(list(Path(".").glob("*.h5"))) == 7 # summary and statepoint 250 | 251 | 252 | def test_simulation_with_single_mat(): 253 | os.system("rm *.h5") 254 | 255 | # this clears the enviromental varible just to be sure that current 256 | # system settings are not being used 257 | try: 258 | del os.environ["OPENMC_CROSS_SECTIONS"] 259 | except KeyError: 260 | # key not found on system 261 | pass 262 | 263 | # Define material 264 | my_mat = openmc.openmc.Material() 265 | my_mat.add_nuclide("Pu239", 3.7047e-2) 266 | my_mat.add_nuclide("Pu240", 1.7512e-3) 267 | my_mat.add_nuclide("As75", 1.3752e-3) 268 | my_mats = openmc.openmc.Materials([my_mat]) 269 | my_mats.export_to_xml() 270 | my_mats.download_cross_section_data( 271 | libraries=["TENDL-2019"], 272 | particles=["neutron"], 273 | set_OPENMC_CROSS_SECTIONS=True, 274 | ) 275 | 276 | # Create a sphere of my_mat 277 | surf = openmc.Sphere(r=6.3849, boundary_type="vacuum") 278 | main_cell = openmc.Cell(fill=my_mat, region=-surf) 279 | openmc.Geometry([main_cell]).export_to_xml() 280 | 281 | # Define settings for the simulation 282 | settings = openmc.Settings() 283 | settings.particles = 10 284 | settings.batches = 2 285 | settings.inactive = 0 286 | center = (0.0, 0.0, 0.0) 287 | settings.source = openmc.Source(space=openmc.stats.Point(center)) 288 | settings.export_to_xml() 289 | 290 | os.system("echo $OPENMC_CROSS_SECTIONS") 291 | openmc.run() 292 | 293 | assert Path("TENDL-2019_Pu239.h5").is_file() 294 | assert Path("TENDL-2019_Pu240.h5").is_file() 295 | assert Path("TENDL-2019_As75.h5").is_file() 296 | 297 | assert Path("summary.h5").is_file() 298 | assert Path("statepoint.2.h5").is_file() 299 | assert len(list(Path(".").glob("*.h5"))) == 5 # summary and statepoint 300 | 301 | 302 | def test_simulation_with_sab(): 303 | os.system("rm *.h5") 304 | 305 | # this clears the enviromental varible just to be sure that current 306 | # system settings are not being used 307 | try: 308 | del os.environ["OPENMC_CROSS_SECTIONS"] 309 | except KeyError: 310 | # key not found on system 311 | pass 312 | 313 | # Define material 314 | my_mat = openmc.Material() 315 | my_mat.add_element("Be", 0.5) 316 | my_mat.add_s_alpha_beta("c_Be_in_BeO") 317 | my_mats = openmc.openmc.Materials([my_mat]) 318 | my_mats.export_to_xml() 319 | my_mats.download_cross_section_data( 320 | libraries=["ENDFB-7.1-NNDC"], 321 | particles=["neutron"], 322 | set_OPENMC_CROSS_SECTIONS=True, 323 | ) 324 | 325 | # Create a sphere of my_mat 326 | surf = openmc.Sphere(r=6.3849, boundary_type="vacuum") 327 | main_cell = openmc.Cell(fill=my_mat, region=-surf) 328 | openmc.Geometry([main_cell]).export_to_xml() 329 | 330 | # Define settings for the simulation 331 | settings = openmc.Settings() 332 | settings.particles = 10 333 | settings.batches = 2 334 | settings.inactive = 0 335 | center = (0.0, 0.0, 0.0) 336 | settings.source = openmc.Source(space=openmc.stats.Point(center)) 337 | settings.run_mode = "fixed source" 338 | settings.export_to_xml() 339 | 340 | os.system("echo $OPENMC_CROSS_SECTIONS") 341 | openmc.run() 342 | 343 | assert Path("ENDFB-7.1-NNDC_Be9.h5").is_file() 344 | assert Path("ENDFB-7.1-NNDC_c_Be_in_BeO.h5").is_file() 345 | 346 | assert Path("summary.h5").is_file() 347 | assert Path("statepoint.2.h5").is_file() 348 | assert len(list(Path(".").glob("*.h5"))) == 4 # summary and statepoint 349 | 350 | 351 | def test_simulation_with_single_mat_list(): 352 | os.system("rm *.h5") 353 | 354 | # this clears the enviromental varible just to be sure that current 355 | # system settings are not being used 356 | try: 357 | del os.environ["OPENMC_CROSS_SECTIONS"] 358 | except KeyError: 359 | # key not found on system 360 | pass 361 | 362 | # Define material 363 | my_mat = openmc.openmc.Material() 364 | my_mat.add_nuclide("Pu239", 3.7047e-2) 365 | my_mat.add_nuclide("Pu240", 1.7512e-3) 366 | my_mat.add_nuclide("As75", 1.3752e-3) 367 | my_mats = openmc.openmc.Materials([my_mat]) 368 | my_mats.export_to_xml() 369 | my_mats.download_cross_section_data( 370 | libraries=["TENDL-2019"], 371 | particles=["neutron"], 372 | set_OPENMC_CROSS_SECTIONS=True, 373 | ) 374 | 375 | # Create a sphere of my_mat 376 | surf = openmc.Sphere(r=6.3849, boundary_type="vacuum") 377 | main_cell = openmc.Cell(fill=my_mat, region=-surf) 378 | openmc.Geometry([main_cell]).export_to_xml() 379 | 380 | # Define settings for the simulation 381 | settings = openmc.Settings() 382 | settings.particles = 10 383 | settings.batches = 2 384 | settings.inactive = 0 385 | center = (0.0, 0.0, 0.0) 386 | settings.source = openmc.Source(space=openmc.stats.Point(center)) 387 | settings.export_to_xml() 388 | 389 | os.system("echo $OPENMC_CROSS_SECTIONS") 390 | openmc.run() 391 | 392 | assert Path("TENDL-2019_Pu239.h5").is_file() 393 | assert Path("TENDL-2019_Pu240.h5").is_file() 394 | assert Path("TENDL-2019_As75.h5").is_file() 395 | 396 | assert Path("summary.h5").is_file() 397 | assert Path("statepoint.2.h5").is_file() 398 | 399 | assert len(list(Path(".").glob("*.h5"))) == 5 # summary and statepoint 400 | 401 | 402 | def test_simulation_with_multi_mat_list(): 403 | os.system("rm *.h5") 404 | 405 | # this clears the enviromental varible just to be sure that current 406 | # system settings are not being used 407 | try: 408 | del os.environ["OPENMC_CROSS_SECTIONS"] 409 | except KeyError: 410 | # key not found on system 411 | pass 412 | 413 | # Define material 414 | my_mat1 = openmc.openmc.Material() 415 | my_mat1.add_nuclide("Pu239", 3.7047e-2) 416 | my_mat1.add_nuclide("Pu240", 1.7512e-3) 417 | 418 | my_mat2 = openmc.openmc.Material() 419 | my_mat2.add_element("As", 1.3752e-3) 420 | my_mats = openmc.Materials([my_mat1, my_mat2]) 421 | my_mats.export_to_xml() 422 | 423 | # Create a sphere of my_mat 424 | surf1 = openmc.Sphere(r=1) 425 | surf2 = openmc.Sphere(r=6, boundary_type="vacuum") 426 | main_cell = openmc.Cell(fill=my_mat1, region=-surf1) 427 | outer_cell = openmc.Cell(fill=my_mat2, region=+surf1 & -surf2) 428 | universe = openmc.Universe(cells=[main_cell, outer_cell]) 429 | openmc.Geometry(universe).export_to_xml() 430 | 431 | # Define settings for the simulation 432 | settings = openmc.Settings() 433 | settings.particles = 10 434 | settings.batches = 2 435 | settings.inactive = 0 436 | center = (0.0, 0.0, 0.0) 437 | settings.source = openmc.Source(space=openmc.stats.Point(center)) 438 | settings.export_to_xml() 439 | 440 | my_mats.download_cross_section_data( 441 | libraries=["TENDL-2019"], 442 | set_OPENMC_CROSS_SECTIONS=True, 443 | ) 444 | 445 | os.system("echo $OPENMC_CROSS_SECTIONS") 446 | openmc.run() 447 | 448 | assert Path("TENDL-2019_Pu239.h5").is_file() 449 | assert Path("TENDL-2019_Pu240.h5").is_file() 450 | assert Path("TENDL-2019_As75.h5").is_file() 451 | 452 | assert Path("summary.h5").is_file() 453 | assert Path("statepoint.2.h5").is_file() 454 | 455 | assert len(list(Path(".").glob("*.h5"))) == 5 # summary and statepoint 456 | --------------------------------------------------------------------------------