diff --git a/.gitignore b/.gitignore index 35f45e747b..8c7cc3e366 100644 --- a/.gitignore +++ b/.gitignore @@ -101,3 +101,5 @@ examples/jupyter/plots .cache/ .tox/ .python-version +.coverage +htmlcov \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index de8b7dcc73..de83325e03 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,9 @@ sudo: required dist: trusty language: python python: - - "2.7" - "3.4" + - "3.5" + - "3.6" addons: apt: packages: @@ -18,29 +19,27 @@ env: global: - FC=gfortran - MPI_DIR=/usr - - PHDF5_DIR=/usr - - HDF5_DIR=/usr + - HDF5_ROOT=/usr - OMP_NUM_THREADS=2 - OPENMC_CROSS_SECTIONS=$HOME/nndc_hdf5/cross_sections.xml - OPENMC_ENDF_DATA=$HOME/endf-b-vii.1 - OPENMC_MULTIPOLE_LIBRARY=$HOME/multipole_lib - PATH=$PATH:$HOME/NJOY2016/build + - DISPLAY=:99.0 matrix: - - OPENMC_CONFIG="^hdf5-debug$" - - OPENMC_CONFIG="^omp-hdf5-debug$" - - OPENMC_CONFIG="^mpi-hdf5-debug$" - - OPENMC_CONFIG="^phdf5-debug$" - + - OMP=n MPI=n PHDF5=n + - OMP=y MPI=n PHDF5=n + - OMP=n MPI=y PHDF5=n + - OMP=n MPI=y PHDF5=y before_install: - sudo add-apt-repository ppa:nschloe/hdf5-backports -y - sudo apt-get update -q - sudo apt-get install libhdf5-serial-dev libhdf5-mpich-dev -y - install: - ./tools/ci/travis-install.sh - before_script: - ./tools/ci/travis-before-script.sh - script: - ./tools/ci/travis-script.sh +after_success: + - coveralls diff --git a/CMakeLists.txt b/CMakeLists.txt index 51bdcc4d9f..fc8b252954 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) +cmake_minimum_required(VERSION 3.0 FATAL_ERROR) project(openmc Fortran C CXX) # Setup output directories @@ -21,11 +21,6 @@ if (${UNIX}) add_definitions(-DUNIX) endif() -# Set MACOSX_RPATH -if(POLICY CMP0042) - cmake_policy(SET CMP0042 NEW) -endif() - #=============================================================================== # Command line options #=============================================================================== @@ -48,14 +43,17 @@ add_definitions(-DMAX_COORD=${maxcoord}) set(MPI_ENABLED FALSE) if($ENV{FC} MATCHES "(mpi[^/]*|ftn)$") message("-- Detected MPI wrapper: $ENV{FC}") - add_definitions(-DMPI) + add_definitions(-DOPENMC_MPI) set(MPI_ENABLED TRUE) + + # Get directory containing MPI wrapper + get_filename_component(MPI_DIR $ENV{FC} DIRECTORY) endif() # Check for Fortran 2008 MPI interface if(MPI_ENABLED AND mpif08) message("-- Using Fortran 2008 MPI bindings") - add_definitions(-DMPIF08) + add_definitions(-DOPENMC_MPIF08) endif() #=============================================================================== @@ -435,8 +433,15 @@ set(LIBOPENMC_FORTRAN_SRC src/tallies/trigger_header.F90 ) set(LIBOPENMC_CXX_SRC + src/error.h + src/hdf5_interface.h + src/random_lcg.cpp src/random_lcg.h - src/random_lcg.cpp) + src/surface.cpp + src/surface.h + src/xml_interface.h + src/pugixml/pugixml.cpp + src/pugixml/pugixml.hpp) add_library(libopenmc SHARED ${LIBOPENMC_FORTRAN_SRC} ${LIBOPENMC_CXX_SRC}) set_target_properties(libopenmc PROPERTIES OUTPUT_NAME openmc) add_executable(${program} src/main.F90) @@ -496,68 +501,3 @@ install(TARGETS ${program} libopenmc install(DIRECTORY src/relaxng DESTINATION share/openmc) install(FILES man/man1/openmc.1 DESTINATION share/man/man1) install(FILES LICENSE DESTINATION "share/doc/${program}" RENAME copyright) - -find_package(PythonInterp) -if(PYTHONINTERP_FOUND) - if(debian) - install(CODE "execute_process( - COMMAND ${PYTHON_EXECUTABLE} setup.py install - --root=debian/openmc --install-layout=deb - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})") - else() - install(CODE "set(ENV{PYTHONPATH} \"${CMAKE_INSTALL_PREFIX}/lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages\")") - install(CODE "execute_process( - COMMAND ${PYTHON_EXECUTABLE} setup.py install - --prefix=${CMAKE_INSTALL_PREFIX} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})") - endif() -endif() - -#=============================================================================== -# Regression tests -#=============================================================================== - -# This allows for dashboard configuration -include(CTest) - -# Get a list of all the tests to run -file(GLOB_RECURSE TESTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_*.py) - -# Loop through all the tests -foreach(test ${TESTS}) - # Remove unit tests - if(test MATCHES ".*unit_tests.*") - continue() - endif() - - # Get test information - get_filename_component(TEST_NAME ${test} NAME) - get_filename_component(TEST_PATH ${test} PATH) - - if (DEFINED ENV{MEM_CHECK}) - # Generate input files if needed - if (NOT EXISTS "${TEST_PATH}/geometry.xml") - execute_process(COMMAND ${PYTHON_EXECUTABLE} ${TEST_NAME} --build-inputs - WORKING_DIRECTORY ${TEST_PATH}) - endif() - - # Add serial test - add_test(NAME ${TEST_NAME} - WORKING_DIRECTORY ${TEST_PATH} - COMMAND $) - else() - # Check serial/parallel - if (${MPI_ENABLED}) - # Preform a parallel test - add_test(NAME ${TEST_NAME} - WORKING_DIRECTORY ${TEST_PATH} - COMMAND ${PYTHON_EXECUTABLE} ${TEST_NAME} --exe $ - --mpi_exec $ENV{MPI_DIR}/bin/mpiexec) - else() - # Perform a serial test - add_test(NAME ${TEST_NAME} - WORKING_DIRECTORY ${TEST_PATH} - COMMAND ${PYTHON_EXECUTABLE} ${TEST_NAME} --exe $) - endif() - endif() -endforeach(test) diff --git a/docs/source/conf.py b/docs/source/conf.py index 9330315903..db44e34f4d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -18,17 +18,15 @@ on_rtd = os.environ.get('READTHEDOCS', None) == 'True' # On Read the Docs, we need to mock a few third-party modules so we don't get # ImportErrors when building documentation -try: - from unittest.mock import MagicMock -except ImportError: - from mock import Mock as MagicMock +from unittest.mock import MagicMock MOCK_MODULES = ['numpy', 'numpy.polynomial', 'numpy.polynomial.polynomial', 'numpy.ctypeslib', 'scipy', 'scipy.sparse', 'scipy.interpolate', 'scipy.integrate', 'scipy.optimize', 'scipy.special', - 'scipy.stats', 'h5py', 'pandas', 'uncertainties', 'matplotlib', - 'matplotlib.pyplot','openmoc', 'openmc.data.reconstruct'] + 'scipy.stats', 'scipy.spatial', 'h5py', 'pandas', 'uncertainties', + 'matplotlib', 'matplotlib.pyplot','openmoc', + 'openmc.data.reconstruct'] sys.modules.update((mod_name, MagicMock()) for mod_name in MOCK_MODULES) import numpy as np @@ -253,6 +251,6 @@ napoleon_use_ivar = True intersphinx_mapping = { 'python': ('https://docs.python.org/3', None), 'numpy': ('https://docs.scipy.org/doc/numpy/', None), - 'pandas': ('http://pandas.pydata.org/pandas-docs/stable/', None), + 'pandas': ('https://pandas.pydata.org/pandas-docs/stable/', None), 'matplotlib': ('https://matplotlib.org/', None) } diff --git a/docs/source/devguide/index.rst b/docs/source/devguide/index.rst index 1a8252383c..e40e7f6359 100644 --- a/docs/source/devguide/index.rst +++ b/docs/source/devguide/index.rst @@ -10,9 +10,10 @@ as debugging. .. toctree:: :numbered: - :maxdepth: 3 + :maxdepth: 2 styleguide workflow + tests user-input docbuild diff --git a/docs/source/devguide/tests.rst b/docs/source/devguide/tests.rst new file mode 100644 index 0000000000..749737b5cb --- /dev/null +++ b/docs/source/devguide/tests.rst @@ -0,0 +1,73 @@ +.. _devguide_tests: + +========== +Test Suite +========== + +Running Tests +------------- + +The OpenMC test suite consists of two parts, a regression test suite and a unit +test suite. The regression test suite is based on regression or integrated +testing where different types of input files are configured and the full OpenMC +code is executed. Results from simulations are compared with expected +results. The unit tests are primarily intended to test individual +functions/classes in the OpenMC Python API. + +The test suite relies on the third-party `pytest `_ +package. To run either or both the regression and unit test suites, it is +assumed that you have OpenMC fully installed, i.e., the :ref:`scripts_openmc` +executable is available on your :envvar:`PATH` and the :mod:`openmc` Python +module is importable. In development where it would be onerous to continually +install OpenMC every time a small change is made, it is recommended to install +OpenMC in development/editable mode. With setuptools, this is accomplished by +running:: + + python setup.py develop + +or using pip (recommended):: + + pip install -e .[test] + +It is also assumed that you have cross section data available that is pointed to +by the :envvar:`OPENMC_CROSS_SECTIONS` and :envvar:`OPENMC_MULTIPOLE_LIBRARY` +environment variables. Furthermore, to run unit tests for the :mod:`openmc.data` +module, it is necessary to have ENDF/B-VII.1 data available and pointed to by +the :envvar:`OPENMC_ENDF_DATA` environment variable. All data sources can be +obtained using the ``tools/ci/travis-before-script.sh`` script. + +To execute the test suite, go to the ``tests/`` directory and run:: + + pytest + +If you want to collect information about source line coverage in the Python API, +you must have the `pytest-cov `_ plugin +installed and run:: + + pytest --cov=../openmc --cov-report=html + +Adding Tests to the Regression Suite +------------------------------------ + +To add a new test to the regression test suite, create a sub-directory in the +``tests/regression_tests/`` directory. To configure a test you need to add the +following files to your new test directory: + + * OpenMC input XML files, if they are not generated through the Python API + * **test.py** - Python test driver script; please refer to other tests to + see how to construct. Any output files that are generated during testing + must be removed at the end of this script. + * **inputs_true.dat** - ASCII file that contains Python API-generated XML + files concatenated together. When the test is run, inputs that are + generated are compared to this file. + * **results_true.dat** - ASCII file that contains the expected results from + the test. The file *results_test.dat* is compared to this file during the + execution of the python test driver script. When the above files have been + created, generate a *results_test.dat* file and copy it to this name and + commit. It should be noted that this file should be generated with basic + compiler options during openmc configuration and build (e.g., no MPI, no + debug/optimization). + +In addition to this description, please see the various types of tests that are +already included in the test suite to see how to create them. If all is +implemented correctly, the new test will automatically be discovered by pytest. diff --git a/docs/source/devguide/workflow.rst b/docs/source/devguide/workflow.rst index 942cee55be..9b27fd655f 100644 --- a/docs/source/devguide/workflow.rst +++ b/docs/source/devguide/workflow.rst @@ -89,139 +89,6 @@ features and bug fixes. The general steps for contributing are as follows: 6. After the pull request has been thoroughly vetted, it is merged back into the *develop* branch of mit-crpg/openmc. -.. _test suite: - -OpenMC Test Suite ------------------ - -The purpose of this test suite is to ensure that OpenMC compiles using various -combinations of compiler flags and options, and that all user input options can -be used successfully without breaking the code. The test suite is comprised of -regression tests where different types of input files are configured and the -full OpenMC code is executed. Results from simulations are compared with -expected results. The test suite is comprised of many build configurations -(e.g. debug, mpi, hdf5) and the actual tests which reside in sub-directories -in the tests directory. We recommend to developers to test their branches -before submitting a formal pull request using gfortran and Intel compilers -if available. - -The test suite is designed to integrate with cmake using ctest_. It is -configured to run with cross sections from NNDC_ augmented with 0 K elastic -scattering data for select nuclides as well as multipole data. To download the -proper data, run the following commands: - -.. code-block:: sh - - wget -O nndc_hdf5.tar.xz $(cat /.travis.yml | grep anl.box | awk '{print $2}') - tar xJvf nndc_hdf5.tar.xz - export OPENMC_CROSS_SECTIONS=$(pwd)/nndc_hdf5/cross_sections.xml - - git clone --branch=master git://github.com/smharper/windowed_multipole_library.git wmp_lib - tar xzvf wmp_lib/multipole_lib.tar.gz - export OPENMC_MULTIPOLE_LIBRARY=$(pwd)/multipole_lib - -The test suite can be run on an already existing build using: - -.. code-block:: sh - - cd build - make test - -or - -.. code-block:: sh - - cd build - ctest - -There are numerous ctest_ command line options that can be set to have -more control over which tests are executed. - -Before running the test suite python script, the following environmental -variables should be set if the default paths are incorrect: - - * **FC** - The command for a Fortran compiler (e.g. gfotran, ifort). - - * Default - *gfortran* - - * **CC** - The command for a C compiler (e.g. gcc, icc). - - * Default - *gcc* - - * **CXX** - The command for a C++ compiler (e.g. g++, icpc). - - * Default - *g++* - - * **MPI_DIR** - The path to the MPI directory. - - * Default - */opt/mpich/3.2-gnu* - - * **HDF5_DIR** - The path to the HDF5 directory. - - * Default - */opt/hdf5/1.8.16-gnu* - - * **PHDF5_DIR** - The path to the parallel HDF5 directory. - - * Default - */opt/phdf5/1.8.16-gnu* - -To run the full test suite, the following command can be executed in the -tests directory: - -.. code-block:: sh - - python run_tests.py - -A subset of build configurations and/or tests can be run. To see how to use -the script run: - -.. code-block:: sh - - python run_tests.py --help - -As an example, say we want to run all tests with debug flags only on tests -that have cone and plot in their name. Also, we would like to run this on -4 processors. We can run: - -.. code-block:: sh - - python run_tests.py -j 4 -C debug -R "cone|plot" - -Note that standard regular expression syntax is used for selecting build -configurations and tests. To print out a list of build configurations, we -can run: - -.. code-block:: sh - - python run_tests.py -p - -Adding tests to test suite -++++++++++++++++++++++++++ - -To add a new test to the test suite, create a sub-directory in the tests -directory that conforms to the regular expression *test_*. To configure -a test you need to add the following files to your new test directory, -*test_name* for example: - - * OpenMC input XML files - * **test_name.py** - Python test driver script, please refer to other - tests to see how to construct. Any output files that are generated - during testing must be removed at the end of this script. - * **inputs_true.dat** - ASCII file that contains Python API-generated XML - files concatenated together. When the test is run, inputs that are - generated are compared to this file. - * **results_true.dat** - ASCII file that contains the expected results - from the test. The file *results_test.dat* is compared to this file - during the execution of the python test driver script. When the - above files have been created, generate a *results_test.dat* file and - copy it to this name and commit. It should be noted that this file - should be generated with basic compiler options during openmc - configuration and build (e.g., no MPI/HDF5, no debug/optimization). - -In addition to this description, please see the various types of tests that -are already included in the test suite to see how to create them. If all is -implemented correctly, the new test directory will automatically be added -to the CTest framework. - Private Development ------------------- @@ -236,6 +103,27 @@ changes you've made in your private repository back to mit-crpg/openmc repository, simply follow the steps above with an extra step of pulling a branch from your private repository into a public fork. +.. _devguide_editable: + +Working in "Development" Mode +----------------------------- + +If you are making changes to the Python API during development, it is highly +suggested to install the Python API in development/editable mode using +pip_. From the root directory of the OpenMC repository, run: + +.. code-block:: sh + + pip install -e .[test] + +This installs the OpenMC Python package in `"editable" mode +`_ so +that 1) it can be imported from a Python interpreter and 2) any changes made are +immediately reflected in the installed version (that is, you don't need to keep +reinstalling it). While the same effect can be achieved using the +:envvar:`PYTHONPATH` environment variable, this is generally discouraged as it +can interfere with virtual environments. + .. _git: http://git-scm.com/ .. _GitHub: https://github.com/ .. _git flow: http://nvie.com/git-model @@ -247,3 +135,4 @@ from your private repository into a public fork. .. _Bitbucket: https://bitbucket.org .. _ctest: http://www.cmake.org/cmake/help/v2.8.12/ctest.html .. _NNDC: http://www.nndc.bnl.gov/endf/b7.1/acefiles.html +.. _pip: https://pip.pypa.io/en/stable/ diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst index 97980037c5..7176b67be0 100644 --- a/docs/source/quickinstall.rst +++ b/docs/source/quickinstall.rst @@ -57,7 +57,6 @@ are no longer supported. .. _Personal Package Archive: https://launchpad.net/~paulromano/+archive/staging .. _APT package manager: https://help.ubuntu.com/community/AptGet/Howto -.. _HDF5: http://www.hdfgroup.org/HDF5/ --------------------------------------- Installing from Source on Ubuntu 15.04+ @@ -83,9 +82,12 @@ building and installing OpenMC from source. Installing from Source on Linux or Mac OS X ------------------------------------------- -All OpenMC source code is hosted on GitHub_. If you have git_, the gfortran_ -compiler, CMake_, and HDF5_ installed, you can download and install OpenMC be -entering the following commands in a terminal: +All OpenMC source code is hosted on `GitHub +`_. If you have `git +`_, the `gcc `_ compiler suite, +`CMake `_, and `HDF5 `_ +installed, you can download and install OpenMC be entering the following +commands in a terminal: .. code-block:: sh @@ -104,11 +106,15 @@ should specify an installation directory where you have write access, e.g. cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local .. +The :mod:`openmc` Python package must be installed separately. The easiest way +to install it is using `pip `_, which is +included by default in Python 2.7 and Python 3.4+. From the root directory of +the OpenMC distribution/repository, run: + +.. code-block:: sh + + pip install . + If you want to build a parallel version of OpenMC (using OpenMP or MPI), directions can be found in the :ref:`detailed installation instructions `. - -.. _GitHub: https://github.com/mit-crpg/openmc -.. _git: http://git-scm.com -.. _gfortran: http://gcc.gnu.org/wiki/GFortran -.. _CMake: http://www.cmake.org diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index b595729236..9b11d1dce4 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -6,17 +6,18 @@ Installation and Configuration .. currentmodule:: openmc +.. _install_conda: + ---------------------------------------- Installing on Linux/Mac with conda-forge ---------------------------------------- -`Conda `_ is an open source package management -system and environment management system for installing multiple versions of -software packages and their dependencies and switching easily between -them. `conda-forge `_ is a community-led conda -channel of installable packages. For instructions on installing conda, please -consult their `documentation -`_. +Conda_ is an open source package management system and environment management +system for installing multiple versions of software packages and their +dependencies and switching easily between them. `conda-forge +`_ is a community-led conda channel of +installable packages. For instructions on installing conda, please consult their +`documentation `_. Once you have `conda` installed on your system, add the `conda-forge` channel to your configuration with: @@ -38,6 +39,8 @@ It is possible to list all of the versions of OpenMC available on your platform conda search openmc --channel conda-forge +.. _install_ppa: + ----------------------------- Installing on Ubuntu with PPA ----------------------------- @@ -68,9 +71,11 @@ are no longer supported. .. _Personal Package Archive: https://launchpad.net/~paulromano/+archive/staging .. _APT package manager: https://help.ubuntu.com/community/AptGet/Howto --------------------- -Building from Source --------------------- +.. _install_source: + +---------------------- +Installing from Source +---------------------- .. _prerequisites: @@ -191,8 +196,8 @@ switch to the source of the latest stable release, run the following commands:: git checkout master .. _GitHub: https://github.com/mit-crpg/openmc -.. _git: http://git-scm.com -.. _ssh: http://en.wikipedia.org/wiki/Secure_Shell +.. _git: https://git-scm.com +.. _ssh: https://en.wikipedia.org/wiki/Secure_Shell .. _usersguide_build: @@ -258,14 +263,15 @@ should be used: Compiling with MPI ++++++++++++++++++ -To compile with MPI, set the :envvar:`FC` and :envvar:`CC` environment variables -to the path to the MPI Fortran and C wrappers, respectively. For example, in a -bash shell: +To compile with MPI, set the :envvar:`FC`, :envvar:`CC`, and :envvar:`CXX` +environment variables to the path to the MPI Fortran, C, and C++ wrappers, +respectively. For example, in a bash shell: .. code-block:: sh - export FC=mpif90 + export FC=mpifort export CC=mpicc + export CXX=mpicxx cmake /path/to/openmc Note that in many shells, environment variables can be set for a single command, @@ -273,7 +279,7 @@ i.e. .. code-block:: sh - FC=mpif90 CC=mpicc cmake /path/to/openmc + FC=mpifort CC=mpicc CXX=mpicxx cmake /path/to/openmc Selecting HDF5 Installation +++++++++++++++++++++++++++ @@ -349,7 +355,7 @@ follows: .. code-block:: sh mkdir build && cd build - FC=ifort CC=icc FFLAGS=-mmic cmake -Dopenmp=on .. + FC=ifort CC=icc CXX=icpc FFLAGS=-mmic cmake -Dopenmp=on .. make Note that unless an HDF5 build for the Intel Xeon Phi (Knights Corner) is @@ -364,26 +370,57 @@ Testing Build To run the test suite, you will first need to download a pre-generated cross section library along with windowed multipole data. Please refer to our -:ref:`test suite` documentation for further details. +:ref:`devguide_tests` documentation for further details. --------------------- -Python Prerequisites --------------------- +--------------------- +Installing Python API +--------------------- -OpenMC's :ref:`Python API ` works with either Python 2.7 or Python -3.2+. In addition to Python itself, the API relies on a number of third-party -packages. All prerequisites can be installed using `conda -`_ (recommended), `pip -`_, or through the package manager in most Linux +If you installed OpenMC using :ref:`Conda ` or :ref:`PPA +`, no further steps are necessary in order to use OpenMC's +:ref:`Python API `. However, if you are :ref:`installing from source +`, the Python API is not installed by default when ``make +install`` is run because in many situations it doesn't make sense to install a +Python package in the same location as the ``openmc`` executable (for example, +if you are installing the package into a `virtual environment +`_). The easiest way to install +the :mod:`openmc` Python package is to use pip_, which is included by default in +Python 3.4+. From the root directory of the OpenMC distribution/repository, run: + +.. code-block:: sh + + pip install . + +pip will first check that all :ref:`required third-party packages +` have been installed, and if they are not present, +they will be installed by downloading the appropriate packages from the Python +Package Index (`PyPI `_). However, do note that since pip +runs the ``setup.py`` script which requires NumPy, you will have to first +install NumPy: + +.. code-block:: sh + + pip install numpy + +Installing in "Development" Mode +-------------------------------- + +If you are primarily doing development with OpenMC, it is strongly recommended +to install the Python package in :ref:`"editable" mode `. + +.. _usersguide_python_prereqs: + +Prerequisites +------------- + +The Python API works with Python 3.4+. In addition to Python itself, the API +relies on a number of third-party packages. All prerequisites can be installed +using Conda_ (recommended), pip_, or through the package manager in most Linux distributions. .. admonition:: Required :class: error - `six `_ - The Python API works with both Python 2.7+ and 3.2+. To do so, the six - compatibility library is used. - `NumPy `_ NumPy is used extensively within the Python API for its powerful N-dimensional array. @@ -457,3 +494,5 @@ schemas.xml file in your own OpenMC source directory. .. _RELAX NG: http://relaxng.org/ .. _NNDC: http://www.nndc.bnl.gov/endf/b7.1/acefiles.html .. _ctest: http://www.cmake.org/cmake/help/v2.8.12/ctest.html +.. _Conda: https://conda.io/docs/ +.. _pip: https://pip.pypa.io/en/stable/ diff --git a/docs/source/usersguide/scripts.rst b/docs/source/usersguide/scripts.rst index 90ca6886d1..2f199fc256 100644 --- a/docs/source/usersguide/scripts.rst +++ b/docs/source/usersguide/scripts.rst @@ -168,8 +168,8 @@ ENDF/B-VII.1. It has the following optional arguments: This script downloads `ENDF/B-VII.1 ACE data `_ from NNDC and converts it to -an HDF5 library for use with OpenMC. This data is used for OpenMC's regression -test suite. This script has the following optional arguments: +an HDF5 library for use with OpenMC. This script has the following optional +arguments: -b, --batch Suppress standard in diff --git a/openmc/arithmetic.py b/openmc/arithmetic.py index fe002f1e31..4a5e7486a8 100644 --- a/openmc/arithmetic.py +++ b/openmc/arithmetic.py @@ -2,7 +2,6 @@ import sys import copy from collections import Iterable -from six import string_types import numpy as np import pandas as pd @@ -86,18 +85,18 @@ class CrossScore(object): @left_score.setter def left_score(self, left_score): cv.check_type('left_score', left_score, - string_types + (CrossScore, AggregateScore)) + (str, CrossScore, AggregateScore)) self._left_score = left_score @right_score.setter def right_score(self, right_score): cv.check_type('right_score', right_score, - string_types + (CrossScore, AggregateScore)) + (str, CrossScore, AggregateScore)) self._right_score = right_score @binary_op.setter def binary_op(self, binary_op): - cv.check_type('binary_op', binary_op, string_types) + cv.check_type('binary_op', binary_op, str) cv.check_value('binary_op', binary_op, _TALLY_ARITHMETIC_OPS) self._binary_op = binary_op @@ -202,7 +201,7 @@ class CrossNuclide(object): @binary_op.setter def binary_op(self, binary_op): - cv.check_type('binary_op', binary_op, string_types) + cv.check_type('binary_op', binary_op, str) cv.check_value('binary_op', binary_op, _TALLY_ARITHMETIC_OPS) self._binary_op = binary_op @@ -335,7 +334,7 @@ class CrossFilter(object): @binary_op.setter def binary_op(self, binary_op): - cv.check_type('binary_op', binary_op, string_types) + cv.check_type('binary_op', binary_op, str) cv.check_value('binary_op', binary_op, _TALLY_ARITHMETIC_OPS) self._binary_op = binary_op @@ -482,12 +481,12 @@ class AggregateScore(object): @scores.setter def scores(self, scores): - cv.check_iterable_type('scores', scores, string_types) + cv.check_iterable_type('scores', scores, str) self._scores = scores @aggregate_op.setter def aggregate_op(self, aggregate_op): - cv.check_type('aggregate_op', aggregate_op, string_types +(CrossScore,)) + cv.check_type('aggregate_op', aggregate_op, (str, CrossScore)) cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS) self._aggregate_op = aggregate_op @@ -561,13 +560,12 @@ class AggregateNuclide(object): @nuclides.setter def nuclides(self, nuclides): - cv.check_iterable_type('nuclides', nuclides, - string_types + (openmc.Nuclide, CrossNuclide)) + cv.check_iterable_type('nuclides', nuclides, (str, CrossNuclide)) self._nuclides = nuclides @aggregate_op.setter def aggregate_op(self, aggregate_op): - cv.check_type('aggregate_op', aggregate_op, string_types) + cv.check_type('aggregate_op', aggregate_op, str) cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS) self._aggregate_op = aggregate_op @@ -690,7 +688,7 @@ class AggregateFilter(object): @aggregate_op.setter def aggregate_op(self, aggregate_op): - cv.check_type('aggregate_op', aggregate_op, string_types) + cv.check_type('aggregate_op', aggregate_op, str) cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS) self._aggregate_op = aggregate_op diff --git a/openmc/capi/cell.py b/openmc/capi/cell.py index 0c4b72c749..0ab3f2583e 100644 --- a/openmc/capi/cell.py +++ b/openmc/capi/cell.py @@ -1,4 +1,4 @@ -from collections import Mapping, Iterable +from collections.abc import Mapping, Iterable from ctypes import c_int, c_int32, c_double, c_char_p, POINTER from weakref import WeakValueDictionary @@ -65,10 +65,7 @@ class Cell(_FortranObjectWithID): if new: # Determine ID to assign if uid is None: - try: - uid = max(mapping) + 1 - except ValueError: - uid = 1 + uid = max(mapping, default=0) + 1 else: if uid in mapping: raise AllocationError('A cell with ID={} has already ' @@ -81,7 +78,7 @@ class Cell(_FortranObjectWithID): index = mapping[uid]._index if index not in cls.__instances: - instance = super(Cell, cls).__new__(cls) + instance = super().__new__(cls) instance._index = index if uid is not None: instance.id = uid diff --git a/openmc/capi/filter.py b/openmc/capi/filter.py index 1b52af6db9..79c19a6248 100644 --- a/openmc/capi/filter.py +++ b/openmc/capi/filter.py @@ -1,4 +1,4 @@ -from collections import Mapping +from collections.abc import Mapping from ctypes import c_int, c_int32, c_double, c_char_p, POINTER, \ create_string_buffer from weakref import WeakValueDictionary @@ -66,10 +66,7 @@ class Filter(_FortranObjectWithID): if new: # Determine ID to assign if uid is None: - try: - uid = max(mapping) + 1 - except ValueError: - uid = 1 + uid = max(mapping, default=0) + 1 else: if uid in mapping: raise AllocationError('A filter with ID={} has already ' @@ -87,7 +84,7 @@ class Filter(_FortranObjectWithID): index = mapping[uid]._index if index not in cls.__instances: - instance = super(Filter, cls).__new__(cls) + instance = super().__new__(cls) instance._index = index if uid is not None: instance.id = uid @@ -110,7 +107,7 @@ class EnergyFilter(Filter): filter_type = 'energy' def __init__(self, bins=None, uid=None, new=True, index=None): - super(EnergyFilter, self).__init__(uid, new, index) + super().__init__(uid, new, index) if bins is not None: self.bins = bins @@ -167,7 +164,7 @@ class MaterialFilter(Filter): filter_type = 'material' def __init__(self, bins=None, uid=None, new=True, index=None): - super(MaterialFilter, self).__init__(uid, new, index) + super().__init__(uid, new, index) if bins is not None: self.bins = bins diff --git a/openmc/capi/material.py b/openmc/capi/material.py index af0e9893e4..62d6df012a 100644 --- a/openmc/capi/material.py +++ b/openmc/capi/material.py @@ -1,4 +1,4 @@ -from collections import Mapping +from collections.abc import Mapping from ctypes import c_int, c_int32, c_double, c_char_p, POINTER from weakref import WeakValueDictionary @@ -78,10 +78,7 @@ class Material(_FortranObjectWithID): if new: # Determine ID to assign if uid is None: - try: - uid = max(mapping) + 1 - except ValueError: - uid = 1 + uid = max(mapping, default=0) + 1 else: if uid in mapping: raise AllocationError('A material with ID={} has already ' diff --git a/openmc/capi/nuclide.py b/openmc/capi/nuclide.py index e07872e583..f66212c971 100644 --- a/openmc/capi/nuclide.py +++ b/openmc/capi/nuclide.py @@ -1,4 +1,4 @@ -from collections import Mapping +from collections.abc import Mapping from ctypes import c_int, c_char_p, POINTER from weakref import WeakValueDictionary @@ -58,7 +58,7 @@ class Nuclide(_FortranObject): def __new__(cls, *args): if args not in cls.__instances: - instance = super(Nuclide, cls).__new__(cls) + instance = super().__new__(cls) cls.__instances[args] = instance return cls.__instances[args] diff --git a/openmc/capi/settings.py b/openmc/capi/settings.py index eede6cf47b..1063d6463e 100644 --- a/openmc/capi/settings.py +++ b/openmc/capi/settings.py @@ -11,8 +11,7 @@ _RUN_MODES = {1: 'fixed source', 5: 'volume'} _dll.openmc_set_seed.argtypes = [c_int64] -_dll.openmc_set_seed.restype = c_int -_dll.openmc_set_seed.errcheck = _error_handler +_dll.openmc_get_seed.restype = c_int64 class _Settings(object): @@ -43,7 +42,7 @@ class _Settings(object): @property def seed(self): - return c_int64.in_dll(_dll, 'seed').value + return _dll.openmc_get_seed() @seed.setter def seed(self, seed): diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index 751f0e6031..a78347177d 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -1,4 +1,4 @@ -from collections import Mapping +from collections.abc import Mapping from ctypes import c_int, c_int32, c_double, c_char_p, POINTER from weakref import WeakValueDictionary @@ -155,10 +155,7 @@ class Tally(_FortranObjectWithID): if new: # Determine ID to assign if uid is None: - try: - uid = max(mapping) + 1 - except ValueError: - uid = 1 + uid = max(mapping, default=0) + 1 else: if uid in mapping: raise AllocationError('A tally with ID={} has already ' @@ -172,7 +169,7 @@ class Tally(_FortranObjectWithID): index = mapping[uid]._index if index not in cls.__instances: - instance = super(Tally, cls).__new__(cls) + instance = super().__new__(cls) instance._index = index if uid is not None: instance.id = uid diff --git a/openmc/cell.py b/openmc/cell.py index 5975d7f70b..c7587e136a 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -1,4 +1,5 @@ -from collections import OrderedDict, Iterable +from collections import OrderedDict +from collections.abc import Iterable from copy import deepcopy from math import cos, sin, pi from numbers import Real, Integral @@ -6,7 +7,6 @@ from xml.etree import ElementTree as ET import sys import warnings -from six import string_types import numpy as np import openmc @@ -203,7 +203,7 @@ class Cell(IDManagerMixin): @name.setter def name(self, name): if name is not None: - cv.check_type('cell name', name, string_types) + cv.check_type('cell name', name, str) self._name = name else: self._name = '' @@ -211,14 +211,7 @@ class Cell(IDManagerMixin): @fill.setter def fill(self, fill): if fill is not None: - if isinstance(fill, string_types): - if fill.strip().lower() != 'void': - msg = 'Unable to set Cell ID="{0}" to use a non-Material ' \ - 'or Universe fill "{1}"'.format(self._id, fill) - raise ValueError(msg) - fill = None - - elif isinstance(fill, Iterable): + if isinstance(fill, Iterable): for i, f in enumerate(fill): if f is not None: cv.check_type('cell.fill[i]', f, openmc.Material) @@ -291,50 +284,6 @@ class Cell(IDManagerMixin): cv.check_type('cell volume', volume, Real) self._volume = volume - def add_surface(self, surface, halfspace): - """Add a half-space to the list of half-spaces whose intersection defines the - cell. - - .. deprecated:: 0.7.1 - Use the :attr:`Cell.region` property to directly specify a Region - expression. - - Parameters - ---------- - surface : openmc.Surface - Quadric surface dividing space - halfspace : {-1, 1} - Indicate whether the negative or positive half-space is to be used - - """ - - warnings.warn("Cell.add_surface(...) has been deprecated and may be " - "removed in a future version. The region for a Cell " - "should be defined using the region property directly.", - DeprecationWarning) - - if not isinstance(surface, openmc.Surface): - msg = 'Unable to add Surface "{0}" to Cell ID="{1}" since it is ' \ - 'not a Surface object'.format(surface, self._id) - raise ValueError(msg) - - if halfspace not in [-1, +1]: - msg = 'Unable to add Surface "{0}" to Cell ID="{1}" with halfspace ' \ - '"{2}" since it is not +/-1'.format(surface, self._id, halfspace) - raise ValueError(msg) - - # If no region has been assigned, simply use the half-space. Otherwise, - # take the intersection of the current region and the half-space - # specified - region = +surface if halfspace == 1 else -surface - if self.region is None: - self.region = region - else: - if isinstance(self.region, Intersection): - self.region &= region - else: - self.region = Intersection(self.region, region) - def add_volume_information(self, volume_calc): """Add volume information to a cell. diff --git a/openmc/checkvalue.py b/openmc/checkvalue.py index aa4dc067f9..dd32aa566c 100644 --- a/openmc/checkvalue.py +++ b/openmc/checkvalue.py @@ -1,5 +1,5 @@ import copy -from collections import Iterable +from collections.abc import Iterable import numpy as np @@ -288,7 +288,7 @@ class CheckedList(list): """ def __init__(self, expected_type, name, items=[]): - super(CheckedList, self).__init__() + super().__init__() self.expected_type = expected_type self.name = name for item in items: @@ -319,7 +319,7 @@ class CheckedList(list): """ check_type(self.name, item, self.expected_type) - super(CheckedList, self).append(item) + super().append(item) def insert(self, index, item): """Insert item before index @@ -333,4 +333,4 @@ class CheckedList(list): """ check_type(self.name, item, self.expected_type) - super(CheckedList, self).insert(index, item) + super().insert(index, item) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index c3df3f1047..5444334b20 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -10,13 +10,11 @@ References """ -from collections import Iterable +from collections.abc import Iterable from numbers import Real, Integral from xml.etree import ElementTree as ET import sys -from six import string_types - from openmc.clean_xml import clean_xml_indentation from openmc.checkvalue import (check_type, check_length, check_value, check_greater_than, check_less_than) @@ -338,7 +336,7 @@ class CMFD(object): @display.setter def display(self, display): - check_type('CMFD display', display, string_types) + check_type('CMFD display', display, str) check_value('CMFD display', display, ['balance', 'dominance', 'entropy', 'source']) self._display = display diff --git a/openmc/data/ace.py b/openmc/data/ace.py index 9827df5b9d..385408bd48 100644 --- a/openmc/data/ace.py +++ b/openmc/data/ace.py @@ -15,12 +15,10 @@ generates ACE-format cross sections. """ -from __future__ import division, unicode_literals from os import SEEK_CUR import struct import sys -from six import string_types import numpy as np from openmc.mixin import EqualityMixin @@ -153,7 +151,7 @@ class Library(EqualityMixin): """ def __init__(self, filename, table_names=None, verbose=False): - if isinstance(table_names, string_types): + if isinstance(table_names, str): table_names = [table_names] if table_names is not None: table_names = set(table_names) diff --git a/openmc/data/angle_distribution.py b/openmc/data/angle_distribution.py index ad3ba89193..a1f498ba61 100644 --- a/openmc/data/angle_distribution.py +++ b/openmc/data/angle_distribution.py @@ -1,4 +1,4 @@ -from collections import Iterable +from collections.abc import Iterable from io import StringIO from numbers import Real from warnings import warn @@ -34,7 +34,7 @@ class AngleDistribution(EqualityMixin): """ def __init__(self, energy, mu): - super(AngleDistribution, self).__init__() + super().__init__() self.energy = energy self.mu = mu diff --git a/openmc/data/angle_energy.py b/openmc/data/angle_energy.py index 8bf95152a9..d67cc6b266 100644 --- a/openmc/data/angle_energy.py +++ b/openmc/data/angle_energy.py @@ -1,14 +1,11 @@ from abc import ABCMeta, abstractmethod from io import StringIO -from six import add_metaclass - import openmc.data from openmc.mixin import EqualityMixin -@add_metaclass(ABCMeta) -class AngleEnergy(EqualityMixin): +class AngleEnergy(EqualityMixin, metaclass=ABCMeta): """Distribution in angle and energy of a secondary particle.""" @abstractmethod def to_hdf5(self, group): diff --git a/openmc/data/correlated.py b/openmc/data/correlated.py index 49d116efd0..8cc4509cea 100644 --- a/openmc/data/correlated.py +++ b/openmc/data/correlated.py @@ -1,4 +1,4 @@ -from collections import Iterable +from collections.abc import Iterable from numbers import Real, Integral from warnings import warn @@ -45,7 +45,7 @@ class CorrelatedAngleEnergy(AngleEnergy): """ def __init__(self, breakpoints, interpolation, energy, energy_out, mu): - super(CorrelatedAngleEnergy, self).__init__() + super().__init__() self.breakpoints = breakpoints self.interpolation = interpolation self.energy = energy diff --git a/openmc/data/decay.py b/openmc/data/decay.py index 4327b2fc35..d83338d028 100644 --- a/openmc/data/decay.py +++ b/openmc/data/decay.py @@ -1,11 +1,11 @@ -from collections import Iterable, namedtuple +from collections import namedtuple +from collections.abc import Iterable from io import StringIO from math import log from numbers import Real import re from warnings import warn -from six import string_types import numpy as np try: from uncertainties import ufloat, unumpy, UFloat @@ -278,12 +278,12 @@ class DecayMode(EqualityMixin): @modes.setter def modes(self, modes): - cv.check_type('decay modes', modes, Iterable, string_types) + cv.check_type('decay modes', modes, Iterable, str) self._modes = modes @parent.setter def parent(self, parent): - cv.check_type('parent nuclide', parent, string_types) + cv.check_type('parent nuclide', parent, str) self._parent = parent diff --git a/openmc/data/endf.py b/openmc/data/endf.py index aa185ca03f..6018719d2c 100644 --- a/openmc/data/endf.py +++ b/openmc/data/endf.py @@ -6,15 +6,13 @@ Data File ENDF-6". The latest version from June 2009 can be found at http://www-nds.iaea.org/ndspub/documents/endf/endf102/endf102.pdf """ -from __future__ import print_function, division, unicode_literals - import io import re import os from math import pi -from collections import OrderedDict, Iterable +from collections import OrderedDict +from collections.abc import Iterable -from six import string_types import numpy as np from numpy.polynomial.polynomial import Polynomial @@ -363,7 +361,7 @@ class Evaluation(object): """ def __init__(self, filename_or_obj): - if isinstance(filename_or_obj, string_types): + if isinstance(filename_or_obj, str): fh = open(filename_or_obj, 'r') else: fh = filename_or_obj diff --git a/openmc/data/energy_distribution.py b/openmc/data/energy_distribution.py index c3740beaf0..9e01a4b302 100644 --- a/openmc/data/energy_distribution.py +++ b/openmc/data/energy_distribution.py @@ -1,9 +1,8 @@ from abc import ABCMeta, abstractmethod -from collections import Iterable +from collections.abc import Iterable from numbers import Integral, Real from warnings import warn -from six import add_metaclass import numpy as np from .function import Tabulated1D, INTERPOLATION_SCHEME @@ -14,8 +13,7 @@ from .data import EV_PER_MEV from .endf import get_tab1_record, get_tab2_record -@add_metaclass(ABCMeta) -class EnergyDistribution(EqualityMixin): +class EnergyDistribution(EqualityMixin, metaclass=ABCMeta): """Abstract superclass for all energy distributions.""" def __init__(self): pass @@ -116,7 +114,7 @@ class ArbitraryTabulated(EnergyDistribution): """ def __init__(self, energy, pdf): - super(ArbitraryTabulated, self).__init__() + super().__init__() self.energy = energy self.pdf = pdf @@ -184,7 +182,7 @@ class GeneralEvaporation(EnergyDistribution): """ def __init__(self, theta, g, u): - super(GeneralEvaporation, self).__init__() + super().__init__() self.theta = theta self.g = g self.u = u @@ -247,7 +245,7 @@ class MaxwellEnergy(EnergyDistribution): """ def __init__(self, theta, u): - super(MaxwellEnergy, self).__init__() + super().__init__() self.theta = theta self.u = u @@ -380,7 +378,7 @@ class Evaporation(EnergyDistribution): """ def __init__(self, theta, u): - super(Evaporation, self).__init__() + super().__init__() self.theta = theta self.u = u @@ -516,7 +514,7 @@ class WattEnergy(EnergyDistribution): """ def __init__(self, a, b, u): - super(WattEnergy, self).__init__() + super().__init__() self.a = a self.b = b self.u = u @@ -684,7 +682,7 @@ class MadlandNix(EnergyDistribution): """ def __init__(self, efl, efh, tm): - super(MadlandNix, self).__init__() + super().__init__() self.efl = efl self.efh = efh self.tm = tm @@ -807,7 +805,7 @@ class DiscretePhoton(EnergyDistribution): """ def __init__(self, primary_flag, energy, atomic_weight_ratio): - super(DiscretePhoton, self).__init__() + super().__init__() self.primary_flag = primary_flag self.energy = energy self.atomic_weight_ratio = atomic_weight_ratio @@ -916,7 +914,7 @@ class LevelInelastic(EnergyDistribution): """ def __init__(self, threshold, mass_ratio): - super(LevelInelastic, self).__init__() + super().__init__() self.threshold = threshold self.mass_ratio = mass_ratio @@ -1021,7 +1019,7 @@ class ContinuousTabular(EnergyDistribution): """ def __init__(self, breakpoints, interpolation, energy, energy_out): - super(ContinuousTabular, self).__init__() + super().__init__() self.breakpoints = breakpoints self.interpolation = interpolation self.energy = energy diff --git a/openmc/data/fission_energy.py b/openmc/data/fission_energy.py index a7cf3dfed3..c602ba2c6f 100644 --- a/openmc/data/fission_energy.py +++ b/openmc/data/fission_energy.py @@ -1,4 +1,4 @@ -from collections import Callable +from collections.abc import Callable from copy import deepcopy from io import StringIO import sys diff --git a/openmc/data/function.py b/openmc/data/function.py index 0515a57c0c..3d09e44fc8 100644 --- a/openmc/data/function.py +++ b/openmc/data/function.py @@ -1,8 +1,7 @@ from abc import ABCMeta, abstractmethod -from collections import Iterable, Callable +from collections.abc import Iterable, Callable from numbers import Real, Integral -from six import add_metaclass import numpy as np import openmc.data @@ -14,8 +13,7 @@ INTERPOLATION_SCHEME = {1: 'histogram', 2: 'linear-linear', 3: 'linear-log', 4: 'log-linear', 5: 'log-log'} -@add_metaclass(ABCMeta) -class Function1D(EqualityMixin): +class Function1D(EqualityMixin, metaclass=ABCMeta): """A function of one independent variable with HDF5 support.""" @abstractmethod def __call__(self): pass diff --git a/openmc/data/kalbach_mann.py b/openmc/data/kalbach_mann.py index de9809df3a..4be0c15d52 100644 --- a/openmc/data/kalbach_mann.py +++ b/openmc/data/kalbach_mann.py @@ -1,4 +1,4 @@ -from collections import Iterable +from collections.abc import Iterable from numbers import Real, Integral from warnings import warn @@ -53,7 +53,7 @@ class KalbachMann(AngleEnergy): def __init__(self, breakpoints, interpolation, energy, energy_out, precompound, slope): - super(KalbachMann, self).__init__() + super().__init__() self.breakpoints = breakpoints self.interpolation = interpolation self.energy = energy diff --git a/openmc/data/laboratory.py b/openmc/data/laboratory.py index 3c240b88d1..cfedb292b1 100644 --- a/openmc/data/laboratory.py +++ b/openmc/data/laboratory.py @@ -1,4 +1,4 @@ -from collections import Iterable +from collections.abc import Iterable from numbers import Real, Integral import numpy as np @@ -44,7 +44,7 @@ class LaboratoryAngleEnergy(AngleEnergy): """ def __init__(self, breakpoints, interpolation, energy, mu, energy_out): - super(LaboratoryAngleEnergy, self).__init__() + super().__init__() self.breakpoints = breakpoints self.interpolation = interpolation self.energy = energy diff --git a/openmc/data/library.py b/openmc/data/library.py index c179f78f8c..34cd380a55 100644 --- a/openmc/data/library.py +++ b/openmc/data/library.py @@ -1,6 +1,5 @@ import os import xml.etree.ElementTree as ET -from six import string_types import h5py @@ -125,7 +124,7 @@ class DataLibrary(EqualityMixin): raise ValueError("Either path or OPENMC_CROSS_SECTIONS " "environmental variable must be set") - check_type('path', path, string_types) + check_type('path', path, str) tree = ET.parse(path) root = tree.getroot() diff --git a/openmc/data/multipole.py b/openmc/data/multipole.py index 4e77e16ea9..33078f5046 100644 --- a/openmc/data/multipole.py +++ b/openmc/data/multipole.py @@ -3,7 +3,6 @@ from math import exp, erf, pi, sqrt import h5py import numpy as np -from six import string_types from . import WMP_VERSION from .data import K_BOLTZMANN @@ -300,7 +299,7 @@ class WindowedMultipole(EqualityMixin): @formalism.setter def formalism(self, formalism): if formalism is not None: - cv.check_type('formalism', formalism, string_types) + cv.check_type('formalism', formalism, str) cv.check_value('formalism', formalism, ('MLBW', 'RM')) self._formalism = formalism diff --git a/openmc/data/neutron.py b/openmc/data/neutron.py index 6c15e4c17c..824dded24c 100644 --- a/openmc/data/neutron.py +++ b/openmc/data/neutron.py @@ -1,6 +1,6 @@ -from __future__ import division, unicode_literals import sys -from collections import OrderedDict, Iterable, Mapping, MutableMapping +from collections import OrderedDict +from collections.abc import Iterable, Mapping, MutableMapping from io import StringIO from itertools import chain from math import log10 @@ -10,7 +10,6 @@ import shutil import tempfile from warnings import warn -from six import string_types import numpy as np import h5py @@ -252,7 +251,7 @@ class IncidentNeutron(EqualityMixin): @name.setter def name(self, name): - cv.check_type('name', name, string_types) + cv.check_type('name', name, str) self._name = name @property @@ -313,7 +312,7 @@ class IncidentNeutron(EqualityMixin): def urr(self, urr): cv.check_type('probability table dictionary', urr, MutableMapping) for key, value in urr: - cv.check_type('probability table temperature', key, string_types) + cv.check_type('probability table temperature', key, str) cv.check_type('probability tables', value, ProbabilityTables) self._urr = urr @@ -861,10 +860,7 @@ class IncidentNeutron(EqualityMixin): Incident neutron continuous-energy data """ - # Create temporary directory -- it would be preferable to use - # TemporaryDirectory(), but it is only available in Python 3.2 - tmpdir = tempfile.mkdtemp() - try: + with tempfile.TemporaryDirectory() as tmpdir: # Run NJOY to create an ACE library ace_file = os.path.join(tmpdir, 'ace') xsdir_file = os.path.join(tmpdir, 'xsdir') @@ -892,8 +888,4 @@ class IncidentNeutron(EqualityMixin): data.energy['0K'] = xs.x data[2].xs['0K'] = xs - finally: - # Get rid of temporary files - shutil.rmtree(tmpdir) - return data diff --git a/openmc/data/njoy.py b/openmc/data/njoy.py index b08b3bdfb3..b2894b3d1c 100644 --- a/openmc/data/njoy.py +++ b/openmc/data/njoy.py @@ -1,4 +1,3 @@ -from __future__ import print_function import argparse from collections import namedtuple from io import StringIO @@ -150,10 +149,7 @@ def run(commands, tapein, tapeout, input_filename=None, stdout=False, with open(input_filename, 'w') as f: f.write(commands) - # Create temporary directory -- it would be preferable to use - # TemporaryDirectory(), but it is only available in Python 3.2 - tmpdir = tempfile.mkdtemp() - try: + with tempfile.TemporaryDirectory() as tmpdir: # Copy evaluations to appropriates 'tapes' for tape_num, filename in tapein.items(): tmpfilename = os.path.join(tmpdir, 'tape{}'.format(tape_num)) @@ -187,8 +183,6 @@ def run(commands, tapein, tapeout, input_filename=None, stdout=False, tmpfilename = os.path.join(tmpdir, 'tape{}'.format(tape_num)) if os.path.isfile(tmpfilename): shutil.move(tmpfilename, filename) - finally: - shutil.rmtree(tmpdir) def make_pendf(filename, pendf='pendf', error=0.001, stdout=False): diff --git a/openmc/data/product.py b/openmc/data/product.py index bcffec0daf..5b8652d771 100644 --- a/openmc/data/product.py +++ b/openmc/data/product.py @@ -1,9 +1,8 @@ -from collections import Iterable +from collections.abc import Iterable from io import StringIO from numbers import Real import sys -from six import string_types import numpy as np import openmc.checkvalue as cv @@ -113,7 +112,7 @@ class Product(EqualityMixin): @particle.setter def particle(self, particle): - cv.check_type('product particle type', particle, string_types) + cv.check_type('product particle type', particle, str) self._particle = particle @yield_.setter diff --git a/openmc/data/reaction.py b/openmc/data/reaction.py index ac3a049ab1..d3df038f55 100644 --- a/openmc/data/reaction.py +++ b/openmc/data/reaction.py @@ -1,11 +1,9 @@ -from __future__ import division, unicode_literals -from collections import Iterable, Callable, MutableMapping +from collections.abc import Iterable, Callable, MutableMapping from copy import deepcopy from numbers import Real, Integral from warnings import warn from io import StringIO -from six import string_types import numpy as np import openmc.checkvalue as cv @@ -863,7 +861,7 @@ class Reaction(EqualityMixin): def xs(self, xs): cv.check_type('reaction cross section dictionary', xs, MutableMapping) for key, value in xs.items(): - cv.check_type('reaction cross section temperature', key, string_types) + cv.check_type('reaction cross section temperature', key, str) cv.check_type('reaction cross section', value, Callable) self._xs = xs diff --git a/openmc/data/resonance.py b/openmc/data/resonance.py index 8feda92df4..d58f706eb9 100644 --- a/openmc/data/resonance.py +++ b/openmc/data/resonance.py @@ -1,4 +1,5 @@ -from collections import defaultdict, MutableSequence, Iterable +from collections import defaultdict +from collections.abc import MutableSequence, Iterable import io import numpy as np @@ -288,8 +289,8 @@ class MultiLevelBreitWigner(ResonanceRange): """ def __init__(self, target_spin, energy_min, energy_max, channel, scattering): - super(MultiLevelBreitWigner, self).__init__( - target_spin, energy_min, energy_max, channel, scattering) + super().__init__(target_spin, energy_min, energy_max, channel, + scattering) self.parameters = None self.q_value = {} self.atomic_weight_ratio = None @@ -490,8 +491,8 @@ class SingleLevelBreitWigner(MultiLevelBreitWigner): """ def __init__(self, target_spin, energy_min, energy_max, channel, scattering): - super(SingleLevelBreitWigner, self).__init__( - target_spin, energy_min, energy_max, channel, scattering) + super().__init__(target_spin, energy_min, energy_max, channel, + scattering) # Set resonance reconstruction function if _reconstruct: @@ -549,8 +550,8 @@ class ReichMoore(ResonanceRange): """ def __init__(self, target_spin, energy_min, energy_max, channel, scattering): - super(ReichMoore, self).__init__( - target_spin, energy_min, energy_max, channel, scattering) + super().__init__(target_spin, energy_min, energy_max, channel, + scattering) self.parameters = None self.angle_distribution = False self.num_l_convergence = 0 @@ -724,8 +725,7 @@ class RMatrixLimited(ResonanceRange): """ def __init__(self, energy_min, energy_max, particle_pairs, spin_groups): - super(RMatrixLimited, self).__init__(0.0, energy_min, energy_max, - None, None) + super().__init__(0.0, energy_min, energy_max, None, None) self.reduced_width = False self.formalism = 3 self.particle_pairs = particle_pairs @@ -931,8 +931,7 @@ class Unresolved(ResonanceRange): """ def __init__(self, target_spin, energy_min, energy_max, scatter): - super(Unresolved, self).__init__( - target_spin, energy_min, energy_max, None, scatter) + super().__init__(target_spin, energy_min, energy_max, None, scatter) self.energies = None self.parameters = None self.add_to_background = False diff --git a/openmc/data/thermal.py b/openmc/data/thermal.py index 68192de039..a036a2680c 100644 --- a/openmc/data/thermal.py +++ b/openmc/data/thermal.py @@ -1,4 +1,4 @@ -from collections import Iterable +from collections.abc import Iterable from difflib import get_close_matches from numbers import Real import itertools @@ -623,10 +623,7 @@ class ThermalScattering(EqualityMixin): Thermal scattering data """ - # Create temporary directory -- it would be preferable to use - # TemporaryDirectory(), but it is only available in Python 3.2 - tmpdir = tempfile.mkdtemp() - try: + with tempfile.TemporaryDirectory() as tmpdir: # Run NJOY to create an ACE library ace_file = os.path.join(tmpdir, 'ace') xsdir_file = os.path.join(tmpdir, 'xsdir') @@ -638,8 +635,5 @@ class ThermalScattering(EqualityMixin): data = cls.from_ace(lib.tables[0]) for table in lib.tables[1:]: data.add_temperature_from_ace(table) - finally: - # Get rid of temporary files - shutil.rmtree(tmpdir) return data diff --git a/openmc/data/urr.py b/openmc/data/urr.py index 1f915974b1..0edccf6f06 100644 --- a/openmc/data/urr.py +++ b/openmc/data/urr.py @@ -1,4 +1,4 @@ -from collections import Iterable +from collections.abc import Iterable from numbers import Integral, Real import numpy as np diff --git a/openmc/element.py b/openmc/element.py index 5ba19c63c4..336d1f028b 100644 --- a/openmc/element.py +++ b/openmc/element.py @@ -1,8 +1,6 @@ from collections import OrderedDict import re import os - -from six import string_types from xml.etree import ElementTree as ET import openmc @@ -29,9 +27,9 @@ class Element(str): """ def __new__(cls, name): - cv.check_type('element name', name, string_types) + cv.check_type('element name', name, str) cv.check_length('element name', name, 1, 2) - return super(Element, cls).__new__(cls, name) + return super().__new__(cls, name) @property def name(self): diff --git a/openmc/examples.py b/openmc/examples.py index 3d6a068273..d48d26839f 100644 --- a/openmc/examples.py +++ b/openmc/examples.py @@ -587,7 +587,7 @@ def slab_mg(reps=None, as_macro=True): # Define the materials file model.xs_data = xs - model.materials.cross_sections = "../1d_mgxs.h5" + model.materials.cross_sections = "../../1d_mgxs.h5" # Define surfaces. # Assembly/Problem Boundary diff --git a/openmc/executor.py b/openmc/executor.py index ada662a12f..8ad2bd8960 100644 --- a/openmc/executor.py +++ b/openmc/executor.py @@ -1,10 +1,7 @@ -from __future__ import print_function -from collections import Iterable +from collections.abc import Iterable import subprocess from numbers import Integral -from six import string_types - import openmc from openmc import VolumeCalculation @@ -203,7 +200,7 @@ def run(particles=None, threads=None, geometry_debug=False, if geometry_debug: args.append('-g') - if isinstance(restart_file, string_types): + if isinstance(restart_file, str): args += ['-r', restart_file] if tracks: diff --git a/openmc/filter.py b/openmc/filter.py index 21bb64f8d5..8763515f4e 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -1,4 +1,3 @@ -from __future__ import division from abc import ABCMeta from collections import Iterable, OrderedDict import copy @@ -8,7 +7,6 @@ from numbers import Real, Integral import operator from xml.etree import ElementTree as ET -from six import add_metaclass import numpy as np import pandas as pd @@ -66,12 +64,10 @@ class FilterMeta(ABCMeta): namespace[func_name].__doc__ = old_doc # Make the class. - return super(FilterMeta, cls).__new__(cls, name, bases, namespace, - **kwargs) + return super().__new__(cls, name, bases, namespace, **kwargs) -@add_metaclass(FilterMeta) -class Filter(IDManagerMixin): +class Filter(IDManagerMixin, metaclass=FilterMeta): """Tally modifier that describes phase-space and other characteristics. Parameters @@ -675,7 +671,7 @@ class MeshFilter(Filter): def __init__(self, mesh, filter_id=None): self.mesh = mesh - super(MeshFilter, self).__init__(mesh.id, filter_id) + super().__init__(mesh.id, filter_id) @classmethod def from_hdf5(cls, group, **kwargs): @@ -872,7 +868,7 @@ class RealFilter(Filter): # This logic is used when merging tallies with real filters return self.bins[0] >= other.bins[-1] else: - return super(RealFilter, self).__gt__(other) + return super().__gt__(other) @property def num_bins(self): @@ -1130,7 +1126,7 @@ class DistribcellFilter(Filter): def __init__(self, cell, filter_id=None): self._paths = None - super(DistribcellFilter, self).__init__(cell, filter_id) + super().__init__(cell, filter_id) @classmethod def from_hdf5(cls, group, **kwargs): diff --git a/openmc/geometry.py b/openmc/geometry.py index 0738ae615a..1ee93dfd6d 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -1,9 +1,8 @@ -from collections import OrderedDict, Iterable +from collections import OrderedDict +from collections.abc import Iterable from copy import deepcopy from xml.etree import ElementTree as ET -from six import string_types - import openmc from openmc.clean_xml import clean_xml_indentation from openmc.checkvalue import check_type @@ -14,8 +13,9 @@ class Geometry(object): Parameters ---------- - root_universe : openmc.Universe, optional - Root universe which contains all others + root : openmc.Universe or Iterable of openmc.Cell, optional + Root universe which contains all others, or an iterable of cells that + should be used to create a root universe. Attributes ---------- @@ -27,11 +27,17 @@ class Geometry(object): """ - def __init__(self, root_universe=None): + def __init__(self, root=None): self._root_universe = None self._offsets = {} - if root_universe is not None: - self.root_universe = root_universe + if root is not None: + if isinstance(root, openmc.Universe): + self.root_universe = root + else: + univ = openmc.Universe() + for cell in root: + univ.add_cell(cell) + self._root_universe = univ @property def root_universe(self): @@ -132,7 +138,7 @@ class Geometry(object): """ # Make sure we are working with an iterable return_list = (isinstance(paths, Iterable) and - not isinstance(paths, string_types)) + not isinstance(paths, str)) path_list = paths if return_list else [paths] indices = [] @@ -249,7 +255,7 @@ class Geometry(object): for cell in self.get_all_cells().values(): if cell.fill_type == 'lattice': - if cell.fill not in lattices: + if cell.fill.id not in lattices: lattices[cell.fill.id] = cell.fill return lattices @@ -306,9 +312,7 @@ class Geometry(object): elif not matching and name in material_name: materials.add(material) - materials = list(materials) - materials.sort(key=lambda x: x.id) - return materials + return sorted(materials, key=lambda x: x.id) def get_cells_by_name(self, name, case_sensitive=False, matching=False): """Return a list of cells with matching names. @@ -346,9 +350,7 @@ class Geometry(object): elif not matching and name in cell_name: cells.add(cell) - cells = list(cells) - cells.sort(key=lambda x: x.id) - return cells + return sorted(cells, key=lambda x: x.id) def get_cells_by_fill_name(self, name, case_sensitive=False, matching=False): """Return a list of cells with fills with matching names. @@ -393,9 +395,7 @@ class Geometry(object): elif not matching and name in fill_name: cells.add(cell) - cells = list(cells) - cells.sort(key=lambda x: x.id) - return cells + return sorted(cells, key=lambda x: x.id) def get_universes_by_name(self, name, case_sensitive=False, matching=False): """Return a list of universes with matching names. @@ -433,9 +433,7 @@ class Geometry(object): elif not matching and name in universe_name: universes.add(universe) - universes = list(universes) - universes.sort(key=lambda x: x.id) - return universes + return sorted(universes, key=lambda x: x.id) def get_lattices_by_name(self, name, case_sensitive=False, matching=False): """Return a list of lattices with matching names. @@ -473,9 +471,7 @@ class Geometry(object): elif not matching and name in lattice_name: lattices.add(lattice) - lattices = list(lattices) - lattices.sort(key=lambda x: x.id) - return lattices + return sorted(lattices, key=lambda x: x.id) def determine_paths(self, instances_only=False): """Determine paths through CSG tree for cells and materials. diff --git a/openmc/lattice.py b/openmc/lattice.py index 79f0d43f3b..86cfd5c41b 100644 --- a/openmc/lattice.py +++ b/openmc/lattice.py @@ -1,13 +1,11 @@ -from __future__ import division - from abc import ABCMeta -from collections import OrderedDict, Iterable +from collections import OrderedDict +from collections.abc import Iterable from copy import deepcopy from math import sqrt, floor from numbers import Real, Integral from xml.etree import ElementTree as ET -from six import add_metaclass, string_types import numpy as np import openmc.checkvalue as cv @@ -15,8 +13,7 @@ import openmc from openmc.mixin import IDManagerMixin -@add_metaclass(ABCMeta) -class Lattice(IDManagerMixin): +class Lattice(IDManagerMixin, metaclass=ABCMeta): """A repeating structure wherein each element is a universe. Parameters @@ -54,25 +51,6 @@ class Lattice(IDManagerMixin): self._outer = None self._universes = None - def __eq__(self, other): - if not isinstance(other, Lattice): - return False - elif self.id != other.id: - return False - elif self.name != other.name: - return False - elif np.any(self.pitch != other.pitch): - return False - elif self.outer != other.outer: - return False - elif np.any(self.universes != other.universes): - return False - else: - return True - - def __ne__(self, other): - return not self == other - @property def name(self): return self._name @@ -92,7 +70,7 @@ class Lattice(IDManagerMixin): @name.setter def name(self, name): if name is not None: - cv.check_type('lattice name', name, string_types) + cv.check_type('lattice name', name, str) self._name = name else: self._name = '' @@ -512,7 +490,7 @@ class RectLattice(Lattice): """ def __init__(self, lattice_id=None, name=''): - super(RectLattice, self).__init__(lattice_id, name) + super().__init__(lattice_id, name) # Initialize Lattice class attributes self._lower_left = None @@ -579,7 +557,11 @@ class RectLattice(Lattice): @property def ndim(self): - return len(self.pitch) + if self.pitch is not None: + return len(self.pitch) + else: + raise ValueError('Number of dimensions cannot be determined until ' + 'the lattice pitch has been set.') @property def shape(self): @@ -839,7 +821,7 @@ class HexLattice(Lattice): """ def __init__(self, lattice_id=None, name=''): - super(HexLattice, self).__init__(lattice_id, name) + super().__init__(lattice_id, name) # Initialize Lattice class attributes self._num_rings = None diff --git a/openmc/macroscopic.py b/openmc/macroscopic.py index cdb5cbb395..2a5a22752a 100644 --- a/openmc/macroscopic.py +++ b/openmc/macroscopic.py @@ -1,5 +1,3 @@ -from six import string_types - from openmc.checkvalue import check_type @@ -19,8 +17,8 @@ class Macroscopic(str): """ def __new__(cls, name): - check_type('name', name, string_types) - return super(Macroscopic, cls).__new__(cls, name) + check_type('name', name, str) + return super().__new__(cls, name) @property def name(self): diff --git a/openmc/material.py b/openmc/material.py index e22fe0b8ba..e409d6536c 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -4,7 +4,6 @@ from numbers import Real, Integral import warnings from xml.etree import ElementTree as ET -from six import string_types import numpy as np import openmc @@ -15,7 +14,7 @@ from .mixin import IDManagerMixin # Units for density supported by OpenMC -DENSITY_UNITS = ['g/cm3', 'g/cc', 'kg/cm3', 'atom/b-cm', 'atom/cm3', 'sum', +DENSITY_UNITS = ['g/cm3', 'g/cc', 'kg/m3', 'atom/b-cm', 'atom/cm3', 'sum', 'macro'] @@ -49,7 +48,7 @@ class Material(IDManagerMixin): density : float Density of the material (units defined separately) density_units : str - Units used for `density`. Can be one of 'g/cm3', 'g/cc', 'kg/cm3', + Units used for `density`. Can be one of 'g/cm3', 'g/cc', 'kg/m3', 'atom/b-cm', 'atom/cm3', 'sum', or 'macro'. The 'macro' unit only applies in the case of a multi-group calculation. depletable : bool @@ -217,7 +216,7 @@ class Material(IDManagerMixin): def name(self, name): if name is not None: cv.check_type('name for Material ID="{}"'.format(self._id), - name, string_types) + name, str) self._name = name else: self._name = '' @@ -243,7 +242,7 @@ class Material(IDManagerMixin): @isotropic.setter def isotropic(self, isotropic): cv.check_iterable_type('Isotropic scattering nuclides', isotropic, - string_types) + str) self._isotropic = list(isotropic) @classmethod @@ -312,7 +311,7 @@ class Material(IDManagerMixin): Parameters ---------- - units : {'g/cm3', 'g/cc', 'kg/cm3', 'atom/b-cm', 'atom/cm3', 'sum', 'macro'} + units : {'g/cm3', 'g/cc', 'kg/m3', 'atom/b-cm', 'atom/cm3', 'sum', 'macro'} Physical units of density. density : float, optional Value of the density. Must be specified unless units is given as @@ -345,7 +344,7 @@ class Material(IDManagerMixin): warnings.warn('This feature is not yet implemented in a release ' 'version of openmc') - if not isinstance(filename, string_types) and filename is not None: + if not isinstance(filename, str) and filename is not None: msg = 'Unable to add OTF material file to Material ID="{}" with a ' \ 'non-string name "{}"'.format(self._id, filename) raise ValueError(msg) @@ -379,7 +378,7 @@ class Material(IDManagerMixin): 'macroscopic data-set has already been added'.format(self._id) raise ValueError(msg) - if not isinstance(nuclide, string_types): + if not isinstance(nuclide, str): msg = 'Unable to add a Nuclide to Material ID="{}" with a ' \ 'non-string value "{}"'.format(self._id, nuclide) raise ValueError(msg) @@ -405,7 +404,7 @@ class Material(IDManagerMixin): Nuclide to remove """ - cv.check_type('nuclide', nuclide, string_types) + cv.check_type('nuclide', nuclide, str) # If the Material contains the Nuclide, delete it for nuc in self._nuclides: @@ -434,7 +433,7 @@ class Material(IDManagerMixin): 'has already been added'.format(self._id, macroscopic) raise ValueError(msg) - if not isinstance(macroscopic, string_types): + if not isinstance(macroscopic, str): msg = 'Unable to add a Macroscopic to Material ID="{}" with a ' \ 'non-string value "{}"'.format(self._id, macroscopic) raise ValueError(msg) @@ -465,7 +464,7 @@ class Material(IDManagerMixin): """ - if not isinstance(macroscopic, string_types): + if not isinstance(macroscopic, str): msg = 'Unable to remove a Macroscopic "{}" in Material ID="{}" ' \ 'since it is not a string'.format(self._id, macroscopic) raise ValueError(msg) @@ -498,7 +497,7 @@ class Material(IDManagerMixin): 'macroscopic data-set has already been added'.format(self._id) raise ValueError(msg) - if not isinstance(element, string_types): + if not isinstance(element, str): msg = 'Unable to add an Element to Material ID="{}" with a ' \ 'non-string value "{}"'.format(self._id, element) raise ValueError(msg) @@ -563,7 +562,7 @@ class Material(IDManagerMixin): 'macroscopic data-set has already been added'.format(self._id) raise ValueError(msg) - if not isinstance(name, string_types): + if not isinstance(name, str): msg = 'Unable to add an S(a,b) table to Material ID="{}" with a ' \ 'non-string table name "{}"'.format(self._id, name) raise ValueError(msg) @@ -886,7 +885,7 @@ class Materials(cv.CheckedList): """ def __init__(self, materials=None): - super(Materials, self).__init__(Material, 'materials collection') + super().__init__(Material, 'materials collection') self._cross_sections = None self._multipole_library = None @@ -903,49 +902,14 @@ class Materials(cv.CheckedList): @cross_sections.setter def cross_sections(self, cross_sections): - cv.check_type('cross sections', cross_sections, string_types) + cv.check_type('cross sections', cross_sections, str) self._cross_sections = cross_sections @multipole_library.setter def multipole_library(self, multipole_library): - cv.check_type('cross sections', multipole_library, string_types) + cv.check_type('cross sections', multipole_library, str) self._multipole_library = multipole_library - def add_material(self, material): - """Append material to collection - - .. deprecated:: 0.8 - Use :meth:`Materials.append` instead. - - Parameters - ---------- - material : openmc.Material - Material to add - - """ - warnings.warn("Materials.add_material(...) has been deprecated and may be " - "removed in a future version. Use Material.append(...) " - "instead.", DeprecationWarning) - self.append(material) - - def add_materials(self, materials): - """Add multiple materials to the collection - - .. deprecated:: 0.8 - Use compound assignment instead. - - Parameters - ---------- - materials : Iterable of openmc.Material - Materials to add - - """ - warnings.warn("Materials.add_materials(...) has been deprecated and may be " - "removed in a future version. Use compound assignment " - "instead.", DeprecationWarning) - for material in materials: - self.append(material) - def append(self, material): """Append material to collection @@ -955,7 +919,7 @@ class Materials(cv.CheckedList): Material to append """ - super(Materials, self).append(material) + super().append(material) def insert(self, index, material): """Insert material before index @@ -968,24 +932,7 @@ class Materials(cv.CheckedList): Material to insert """ - super(Materials, self).insert(index, material) - - def remove_material(self, material): - """Remove a material from the file - - .. deprecated:: 0.8 - Use :meth:`Materials.remove` instead. - - Parameters - ---------- - material : openmc.Material - Material to remove - - """ - warnings.warn("Materials.remove_material(...) has been deprecated and " - "may be removed in a future version. Use " - "Materials.remove(...) instead.", DeprecationWarning) - self.remove(material) + super().insert(index, material) def make_isotropic_in_lab(self): for material in self: diff --git a/openmc/mesh.py b/openmc/mesh.py index b315b2628b..d937e25ce9 100644 --- a/openmc/mesh.py +++ b/openmc/mesh.py @@ -3,7 +3,6 @@ from numbers import Real, Integral from xml.etree import ElementTree as ET import sys -from six import string_types import numpy as np import openmc.checkvalue as cv @@ -87,7 +86,7 @@ class Mesh(IDManagerMixin): def name(self, name): if name is not None: cv.check_type('name for mesh ID="{0}"'.format(self._id), - name, string_types) + name, str) self._name = name else: self._name = '' @@ -95,7 +94,7 @@ class Mesh(IDManagerMixin): @type.setter def type(self, meshtype): cv.check_type('type for mesh ID="{0}"'.format(self._id), - meshtype, string_types) + meshtype, str) cv.check_value('type for mesh ID="{0}"'.format(self._id), meshtype, ['regular']) self._type = meshtype diff --git a/openmc/mgxs/library.py b/openmc/mgxs/library.py index b83305783f..9add7004bb 100644 --- a/openmc/mgxs/library.py +++ b/openmc/mgxs/library.py @@ -3,10 +3,10 @@ import os import copy import pickle from numbers import Integral -from collections import OrderedDict, Iterable +from collections import OrderedDict +from collections.abc import Iterable from warnings import warn -from six import string_types import numpy as np import openmc @@ -271,7 +271,7 @@ class Library(object): @name.setter def name(self, name): - cv.check_type('name', name, string_types) + cv.check_type('name', name, str) self._name = name @mgxs_types.setter @@ -280,7 +280,7 @@ class Library(object): if mgxs_types == 'all': self._mgxs_types = all_mgxs_types else: - cv.check_iterable_type('mgxs_types', mgxs_types, string_types) + cv.check_iterable_type('mgxs_types', mgxs_types, str) for mgxs_type in mgxs_types: cv.check_value('mgxs_type', mgxs_type, all_mgxs_types) self._mgxs_types = mgxs_types @@ -814,8 +814,8 @@ class Library(object): 'since a statepoint has not yet been loaded' raise ValueError(msg) - cv.check_type('filename', filename, string_types) - cv.check_type('directory', directory, string_types) + cv.check_type('filename', filename, str) + cv.check_type('directory', directory, str) import h5py @@ -857,8 +857,8 @@ class Library(object): """ - cv.check_type('filename', filename, string_types) - cv.check_type('directory', directory, string_types) + cv.check_type('filename', filename, str) + cv.check_type('directory', directory, str) # Make directory if it does not exist if not os.path.exists(directory): @@ -892,8 +892,8 @@ class Library(object): """ - cv.check_type('filename', filename, string_types) - cv.check_type('directory', directory, string_types) + cv.check_type('filename', filename, str) + cv.check_type('directory', directory, str) # Make directory if it does not exist if not os.path.exists(directory): @@ -953,8 +953,8 @@ class Library(object): cv.check_type('domain', domain, (openmc.Material, openmc.Cell, openmc.Universe, openmc.Mesh)) - cv.check_type('xsdata_name', xsdata_name, string_types) - cv.check_type('nuclide', nuclide, string_types) + cv.check_type('xsdata_name', xsdata_name, str) + cv.check_type('nuclide', nuclide, str) cv.check_value('xs_type', xs_type, ['macro', 'micro']) if subdomain is not None: cv.check_iterable_type('subdomain', subdomain, Integral, @@ -1213,7 +1213,7 @@ class Library(object): cv.check_value('xs_type', xs_type, ['macro', 'micro']) if xsdata_names is not None: - cv.check_iterable_type('xsdata_names', xsdata_names, string_types) + cv.check_iterable_type('xsdata_names', xsdata_names, str) # If gathering material-specific data, set the xs_type to macro if not self.by_nuclide: diff --git a/openmc/mgxs/mdgxs.py b/openmc/mgxs/mdgxs.py index d3f1a0646b..2d278d62d7 100644 --- a/openmc/mgxs/mdgxs.py +++ b/openmc/mgxs/mdgxs.py @@ -1,6 +1,5 @@ -from __future__ import division - -from collections import Iterable, OrderedDict +from collections import OrderedDict +from collections.abc import Iterable import itertools from numbers import Integral import warnings @@ -9,7 +8,6 @@ import sys import copy from abc import ABCMeta -from six import add_metaclass, string_types import numpy as np import openmc @@ -29,7 +27,6 @@ MDGXS_TYPES = ['delayed-nu-fission', MAX_DELAYED_GROUPS = 8 -@add_metaclass(ABCMeta) class MDGXS(MGXS): """An abstract multi-delayed-group cross section for some energy and delayed group structures within some spatial domain. @@ -133,8 +130,8 @@ class MDGXS(MGXS): def __init__(self, domain=None, domain_type=None, energy_groups=None, delayed_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(MDGXS, self).__init__(domain, domain_type, energy_groups, - by_nuclide, name, num_polar, num_azimuthal) + super().__init__(domain, domain_type, energy_groups, by_nuclide, name, + num_polar, num_azimuthal) self._delayed_groups = None @@ -355,7 +352,7 @@ class MDGXS(MGXS): filter_bins = [] # Construct a collection of the domain filter bins - if not isinstance(subdomains, string_types): + if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=3) for subdomain in subdomains: @@ -363,7 +360,7 @@ class MDGXS(MGXS): filter_bins.append((subdomain,)) # Construct list of energy group bounds tuples for all requested groups - if not isinstance(groups, string_types): + if not isinstance(groups, str): cv.check_iterable_type('groups', groups, Integral) for group in groups: filters.append(openmc.EnergyFilter) @@ -371,7 +368,7 @@ class MDGXS(MGXS): (self.energy_groups.get_group_bounds(group),)) # Construct list of delayed group tuples for all requested groups - if not isinstance(delayed_groups, string_types): + if not isinstance(delayed_groups, str): cv.check_type('delayed groups', delayed_groups, list, int) for delayed_group in delayed_groups: filters.append(openmc.DelayedGroupFilter) @@ -475,7 +472,7 @@ class MDGXS(MGXS): """ - cv.check_iterable_type('nuclides', nuclides, string_types) + cv.check_iterable_type('nuclides', nuclides, str) cv.check_iterable_type('energy_groups', groups, Integral) cv.check_type('delayed groups', delayed_groups, list, int) @@ -551,7 +548,7 @@ class MDGXS(MGXS): """ - merged_mdgxs = super(MDGXS, self).merge(other) + merged_mdgxs = super().merge(other) # Merge delayed groups if self.delayed_groups != other.delayed_groups: @@ -581,11 +578,11 @@ class MDGXS(MGXS): """ if self.delayed_groups is None: - super(MDGXS, self).print_xs(subdomains, nuclides, xs_type) + super().print_xs(subdomains, nuclides, xs_type) return # Construct a collection of the subdomains to report - if not isinstance(subdomains, string_types): + if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral) elif self.domain_type == 'distribcell': subdomains = np.arange(self.num_subdomains, dtype=np.int) @@ -602,7 +599,7 @@ class MDGXS(MGXS): elif nuclides == 'sum': nuclides = ['sum'] else: - cv.check_iterable_type('nuclides', nuclides, string_types) + cv.check_iterable_type('nuclides', nuclides, str) else: nuclides = ['sum'] @@ -725,8 +722,8 @@ class MDGXS(MGXS): """ - cv.check_type('filename', filename, string_types) - cv.check_type('directory', directory, string_types) + cv.check_type('filename', filename, str) + cv.check_type('directory', directory, str) cv.check_value('format', format, ['csv', 'excel', 'pickle', 'latex']) cv.check_value('xs_type', xs_type, ['macro', 'micro']) @@ -816,11 +813,11 @@ class MDGXS(MGXS): """ - if not isinstance(groups, string_types): + if not isinstance(groups, str): cv.check_iterable_type('groups', groups, Integral) if nuclides != 'all' and nuclides != 'sum': - cv.check_iterable_type('nuclides', nuclides, string_types) - if not isinstance(delayed_groups, string_types): + cv.check_iterable_type('nuclides', nuclides, str) + if not isinstance(delayed_groups, str): cv.check_type('delayed groups', delayed_groups, list, int) cv.check_value('xs_type', xs_type, ['macro', 'micro']) @@ -858,7 +855,7 @@ class MDGXS(MGXS): columns = self._df_convert_columns_to_bins(df) # Select out those groups the user requested - if not isinstance(groups, string_types): + if not isinstance(groups, str): if 'group in' in df: df = df[df['group in'].isin(groups)] if 'group out' in df: @@ -1011,9 +1008,8 @@ class ChiDelayed(MDGXS): def __init__(self, domain=None, domain_type=None, energy_groups=None, delayed_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(ChiDelayed, self).__init__(domain, domain_type, energy_groups, - delayed_groups, by_nuclide, name, - num_polar, num_azimuthal) + super().__init__(domain, domain_type, energy_groups, delayed_groups, + by_nuclide, name, num_polar, num_azimuthal) self._rxn_type = 'chi-delayed' self._estimator = 'analog' @@ -1059,7 +1055,7 @@ class ChiDelayed(MDGXS): # Compute chi self._xs_tally = self.rxn_rate_tally / delayed_nu_fission_in - super(ChiDelayed, self)._compute_xs() + super()._compute_xs() # Add the coarse energy filter back to the nu-fission tally delayed_nu_fission_in.filters.append(energy_filter) @@ -1131,8 +1127,7 @@ class ChiDelayed(MDGXS): delayed_nu_fission_in.remove_filter(energy_filter) # Call super class method and null out derived tallies - slice_xs = super(ChiDelayed, self).get_slice(nuclides, groups, - delayed_groups) + slice_xs = super().get_slice(nuclides, groups, delayed_groups) slice_xs._rxn_rate_tally = None slice_xs._xs_tally = None @@ -1288,7 +1283,7 @@ class ChiDelayed(MDGXS): filter_bins = [] # Construct a collection of the domain filter bins - if not isinstance(subdomains, string_types): + if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=3) for subdomain in subdomains: @@ -1296,7 +1291,7 @@ class ChiDelayed(MDGXS): filter_bins.append((subdomain,)) # Construct list of energy group bounds tuples for all requested groups - if not isinstance(groups, string_types): + if not isinstance(groups, str): cv.check_iterable_type('groups', groups, Integral) for group in groups: filters.append(openmc.EnergyoutFilter) @@ -1304,7 +1299,7 @@ class ChiDelayed(MDGXS): (self.energy_groups.get_group_bounds(group),)) # Construct list of delayed group tuples for all requested groups - if not isinstance(delayed_groups, string_types): + if not isinstance(delayed_groups, str): cv.check_type('delayed groups', delayed_groups, list, int) for delayed_group in delayed_groups: filters.append(openmc.DelayedGroupFilter) @@ -1352,7 +1347,7 @@ class ChiDelayed(MDGXS): # Get chi delayed for user-specified nuclides in the domain else: - cv.check_iterable_type('nuclides', nuclides, string_types) + cv.check_iterable_type('nuclides', nuclides, str) xs = self.xs_tally.get_values(filters=filters, filter_bins=filter_bins, nuclides=nuclides, value=value) @@ -1525,10 +1520,8 @@ class DelayedNuFissionXS(MDGXS): def __init__(self, domain=None, domain_type=None, energy_groups=None, delayed_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(DelayedNuFissionXS, self).__init__(domain, domain_type, - energy_groups, delayed_groups, - by_nuclide, name, num_polar, - num_azimuthal) + super().__init__(domain, domain_type, energy_groups, delayed_groups, + by_nuclide, name, num_polar, num_azimuthal) self._rxn_type = 'delayed-nu-fission' @@ -1661,9 +1654,8 @@ class Beta(MDGXS): def __init__(self, domain=None, domain_type=None, energy_groups=None, delayed_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(Beta, self).__init__(domain, domain_type, energy_groups, - delayed_groups, by_nuclide, name, num_polar, - num_azimuthal) + super().__init__(domain, domain_type, energy_groups, delayed_groups, + by_nuclide, name, num_polar, num_azimuthal) self._rxn_type = 'beta' @property @@ -1689,7 +1681,7 @@ class Beta(MDGXS): # Compute beta self._xs_tally = self.rxn_rate_tally / nu_fission - super(Beta, self)._compute_xs() + super()._compute_xs() return self._xs_tally @@ -1845,9 +1837,8 @@ class DecayRate(MDGXS): def __init__(self, domain=None, domain_type=None, energy_groups=None, delayed_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(DecayRate, self).__init__(domain, domain_type, energy_groups, - delayed_groups, by_nuclide, name, - num_polar, num_azimuthal) + super().__init__(domain, domain_type, energy_groups, delayed_groups, + by_nuclide, name, num_polar, num_azimuthal) self._rxn_type = 'decay-rate' @property @@ -1882,7 +1873,7 @@ class DecayRate(MDGXS): # Compute the decay rate self._xs_tally = self.rxn_rate_tally / delayed_nu_fission - super(DecayRate, self)._compute_xs() + super()._compute_xs() return self._xs_tally @@ -1914,7 +1905,6 @@ class DecayRate(MDGXS): return self._get_homogenized_mgxs(other_mgxs, 'delayed-nu-fission') -@add_metaclass(ABCMeta) class MatrixMDGXS(MDGXS): """An abstract multi-delayed-group cross section for some energy group and delayed group structure within some spatial domain. This class is @@ -2117,7 +2107,7 @@ class MatrixMDGXS(MDGXS): filter_bins = [] # Construct a collection of the domain filter bins - if not isinstance(subdomains, string_types): + if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=3) for subdomain in subdomains: @@ -2125,7 +2115,7 @@ class MatrixMDGXS(MDGXS): filter_bins.append((subdomain,)) # Construct list of energy group bounds tuples for all requested groups - if not isinstance(in_groups, string_types): + if not isinstance(in_groups, str): cv.check_iterable_type('groups', in_groups, Integral) for group in in_groups: filters.append(openmc.EnergyFilter) @@ -2133,7 +2123,7 @@ class MatrixMDGXS(MDGXS): self.energy_groups.get_group_bounds(group),)) # Construct list of energy group bounds tuples for all requested groups - if not isinstance(out_groups, string_types): + if not isinstance(out_groups, str): cv.check_iterable_type('groups', out_groups, Integral) for group in out_groups: filters.append(openmc.EnergyoutFilter) @@ -2141,7 +2131,7 @@ class MatrixMDGXS(MDGXS): self.energy_groups.get_group_bounds(group),)) # Construct list of delayed group tuples for all requested groups - if not isinstance(delayed_groups, string_types): + if not isinstance(delayed_groups, str): cv.check_type('delayed groups', delayed_groups, list, int) for delayed_group in delayed_groups: filters.append(openmc.DelayedGroupFilter) @@ -2266,8 +2256,7 @@ class MatrixMDGXS(MDGXS): """ # Call super class method and null out derived tallies - slice_xs = super(MatrixMDGXS, self).get_slice(nuclides, in_groups, - delayed_groups) + slice_xs = super().get_slice(nuclides, in_groups, delayed_groups) slice_xs._rxn_rate_tally = None slice_xs._xs_tally = None @@ -2312,7 +2301,7 @@ class MatrixMDGXS(MDGXS): """ # Construct a collection of the subdomains to report - if not isinstance(subdomains, string_types): + if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral) elif self.domain_type == 'distribcell': subdomains = np.arange(self.num_subdomains, dtype=np.int) @@ -2329,7 +2318,7 @@ class MatrixMDGXS(MDGXS): if nuclides == 'sum': nuclides = ['sum'] else: - cv.check_iterable_type('nuclides', nuclides, string_types) + cv.check_iterable_type('nuclides', nuclides, str) else: nuclides = ['sum'] @@ -2614,12 +2603,8 @@ class DelayedNuFissionMatrixXS(MatrixMDGXS): def __init__(self, domain=None, domain_type=None, energy_groups=None, delayed_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(DelayedNuFissionMatrixXS, self).__init__(domain, domain_type, - energy_groups, - delayed_groups, - by_nuclide, name, - num_polar, - num_azimuthal) + super().__init__(domain, domain_type, energy_groups, delayed_groups, + by_nuclide, name, num_polar, num_azimuthal) self._rxn_type = 'delayed-nu-fission' self._hdf5_key = 'delayed-nu-fission matrix' self._estimator = 'analog' diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index a557e51cc1..ecedc8e63a 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -1,5 +1,3 @@ -from __future__ import division - from collections import OrderedDict from numbers import Integral import warnings @@ -8,8 +6,8 @@ import copy from abc import ABCMeta import itertools -from six import add_metaclass, string_types import numpy as np +import h5py import openmc import openmc.checkvalue as cv @@ -115,8 +113,7 @@ def _df_column_convert_to_bin(df, current_name, new_name, values_to_bin, df.rename(columns={current_name: new_name}, inplace=True) -@add_metaclass(ABCMeta) -class MGXS(object): +class MGXS(metaclass=ABCMeta): """An abstract multi-group cross section for some energy group structure within some spatial domain. @@ -579,7 +576,7 @@ class MGXS(object): @name.setter def name(self, name): - cv.check_type('name', name, string_types) + cv.check_type('name', name, str) self._name = name @by_nuclide.setter @@ -589,7 +586,7 @@ class MGXS(object): @nuclides.setter def nuclides(self, nuclides): - cv.check_iterable_type('nuclides', nuclides, string_types) + cv.check_iterable_type('nuclides', nuclides, str) self._nuclides = nuclides @estimator.setter @@ -805,7 +802,7 @@ class MGXS(object): """ - cv.check_type('nuclide', nuclide, string_types) + cv.check_type('nuclide', nuclide, str) # Get list of all nuclides in the spatial domain nuclides = self.domain.get_nuclide_densities() @@ -1032,7 +1029,7 @@ class MGXS(object): filter_bins = [] # Construct a collection of the domain filter bins - if not isinstance(subdomains, string_types): + if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=3) @@ -1043,7 +1040,7 @@ class MGXS(object): filter_bins.append(tuple(subdomain_bins)) # Construct list of energy group bounds tuples for all requested groups - if not isinstance(groups, string_types): + if not isinstance(groups, str): cv.check_iterable_type('groups', groups, Integral) filters.append(openmc.EnergyFilter) energy_bins = [] @@ -1218,7 +1215,7 @@ class MGXS(object): """ # Construct a collection of the subdomain filter bins to average across - if not isinstance(subdomains, string_types): + if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral) subdomains = [(subdomain,) for subdomain in subdomains] subdomains = [tuple(subdomains)] @@ -1375,7 +1372,7 @@ class MGXS(object): """ - cv.check_iterable_type('nuclides', nuclides, string_types) + cv.check_iterable_type('nuclides', nuclides, str) cv.check_iterable_type('energy_groups', groups, Integral) # Build lists of filters and filter bins to slice @@ -1529,7 +1526,7 @@ class MGXS(object): """ # Construct a collection of the subdomains to report - if not isinstance(subdomains, string_types): + if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral) elif self.domain_type == 'distribcell': subdomains = np.arange(self.num_subdomains, dtype=np.int) @@ -1546,7 +1543,7 @@ class MGXS(object): elif nuclides == 'sum': nuclides = ['sum'] else: - cv.check_iterable_type('nuclides', nuclides, string_types) + cv.check_iterable_type('nuclides', nuclides, str) else: nuclides = ['sum'] @@ -1682,13 +1679,8 @@ class MGXS(object): ValueError When this method is called before the multi-group cross section is computed from tally data. - ImportError - When h5py is not installed. """ - - import h5py - # Make directory if it does not exist if not os.path.exists(directory): os.makedirs(directory) @@ -1702,7 +1694,7 @@ class MGXS(object): xs_results = h5py.File(filename, 'w', libver=libver) # Construct a collection of the subdomains to report - if not isinstance(subdomains, string_types): + if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral) elif self.domain_type == 'distribcell': subdomains = np.arange(self.num_subdomains, dtype=np.int) @@ -1723,7 +1715,7 @@ class MGXS(object): elif nuclides == 'sum': nuclides = ['sum'] else: - cv.check_iterable_type('nuclides', nuclides, string_types) + cv.check_iterable_type('nuclides', nuclides, str) else: nuclides = ['sum'] @@ -1801,8 +1793,8 @@ class MGXS(object): """ - cv.check_type('filename', filename, string_types) - cv.check_type('directory', directory, string_types) + cv.check_type('filename', filename, str) + cv.check_type('directory', directory, str) cv.check_value('format', format, ['csv', 'excel', 'pickle', 'latex']) cv.check_value('xs_type', xs_type, ['macro', 'micro']) @@ -1888,10 +1880,10 @@ class MGXS(object): """ - if not isinstance(groups, string_types): + if not isinstance(groups, str): cv.check_iterable_type('groups', groups, Integral) if nuclides != 'all' and nuclides != 'sum': - cv.check_iterable_type('nuclides', nuclides, string_types) + cv.check_iterable_type('nuclides', nuclides, str) cv.check_value('xs_type', xs_type, ['macro', 'micro']) # Get a Pandas DataFrame from the derived xs tally @@ -1927,7 +1919,7 @@ class MGXS(object): columns = self._df_convert_columns_to_bins(df) # Select out those groups the user requested - if not isinstance(groups, string_types): + if not isinstance(groups, str): if 'group in' in df: df = df[df['group in'].isin(groups)] if 'group out' in df: @@ -1980,7 +1972,6 @@ class MGXS(object): return 'cm^-1' if xs_type == 'macro' else 'barns' -@add_metaclass(ABCMeta) class MatrixMGXS(MGXS): """An abstract multi-group cross section for some energy group structure within some spatial domain. This class is specifically intended for @@ -2168,7 +2159,7 @@ class MatrixMGXS(MGXS): filter_bins = [] # Construct a collection of the domain filter bins - if not isinstance(subdomains, string_types): + if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=3) filters.append(_DOMAIN_TO_FILTER[self.domain_type]) @@ -2178,7 +2169,7 @@ class MatrixMGXS(MGXS): filter_bins.append(tuple(subdomain_bins)) # Construct list of energy group bounds tuples for all requested groups - if not isinstance(in_groups, string_types): + if not isinstance(in_groups, str): cv.check_iterable_type('groups', in_groups, Integral) filters.append(openmc.EnergyFilter) for group in in_groups: @@ -2186,7 +2177,7 @@ class MatrixMGXS(MGXS): filter_bins.append(tuple(energy_bins)) # Construct list of energy group bounds tuples for all requested groups - if not isinstance(out_groups, string_types): + if not isinstance(out_groups, str): cv.check_iterable_type('groups', out_groups, Integral) for group in out_groups: filters.append(openmc.EnergyoutFilter) @@ -2301,7 +2292,7 @@ class MatrixMGXS(MGXS): """ # Call super class method and null out derived tallies - slice_xs = super(MatrixMGXS, self).get_slice(nuclides, in_groups) + slice_xs = super().get_slice(nuclides, in_groups) slice_xs._rxn_rate_tally = None slice_xs._xs_tally = None @@ -2346,7 +2337,7 @@ class MatrixMGXS(MGXS): """ # Construct a collection of the subdomains to report - if not isinstance(subdomains, string_types): + if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral) elif self.domain_type == 'distribcell': subdomains = np.arange(self.num_subdomains, dtype=np.int) @@ -2363,7 +2354,7 @@ class MatrixMGXS(MGXS): if nuclides == 'sum': nuclides = ['sum'] else: - cv.check_iterable_type('nuclides', nuclides, string_types) + cv.check_iterable_type('nuclides', nuclides, str) else: nuclides = ['sum'] @@ -2576,9 +2567,8 @@ class TotalXS(MGXS): def __init__(self, domain=None, domain_type=None, groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(TotalXS, self).__init__(domain, domain_type, - groups, by_nuclide, name, num_polar, - num_azimuthal) + super().__init__(domain, domain_type, groups, by_nuclide, name, + num_polar, num_azimuthal) self._rxn_type = 'total' @@ -2713,9 +2703,8 @@ class TransportXS(MGXS): def __init__(self, domain=None, domain_type=None, groups=None, nu=False, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(TransportXS, self).__init__(domain, domain_type, - groups, by_nuclide, name, num_polar, - num_azimuthal) + super().__init__(domain, domain_type, groups, by_nuclide, name, + num_polar, num_azimuthal) # Use tracklength estimators for the total MGXS term, and # analog estimators for the transport correction term @@ -2724,7 +2713,7 @@ class TransportXS(MGXS): self.nu = nu def __deepcopy__(self, memo): - clone = super(TransportXS, self).__deepcopy__(memo) + clone = super().__deepcopy__(memo) clone._nu = self.nu return clone @@ -2921,9 +2910,8 @@ class AbsorptionXS(MGXS): def __init__(self, domain=None, domain_type=None, groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(AbsorptionXS, self).__init__(domain, domain_type, - groups, by_nuclide, name, num_polar, - num_azimuthal) + super().__init__(domain, domain_type, groups, by_nuclide, name, + num_polar, num_azimuthal) self._rxn_type = 'absorption' @@ -3048,9 +3036,8 @@ class CaptureXS(MGXS): def __init__(self, domain=None, domain_type=None, groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(CaptureXS, self).__init__(domain, domain_type, - groups, by_nuclide, name, num_polar, - num_azimuthal) + super().__init__(domain, domain_type, groups, by_nuclide, name, + num_polar, num_azimuthal) self._rxn_type = 'capture' @property @@ -3203,16 +3190,15 @@ class FissionXS(MGXS): def __init__(self, domain=None, domain_type=None, groups=None, nu=False, prompt=False, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(FissionXS, self).__init__(domain, domain_type, - groups, by_nuclide, name, num_polar, - num_azimuthal) + super().__init__(domain, domain_type, groups, by_nuclide, name, + num_polar, num_azimuthal) self._nu = False self._prompt = False self.nu = nu self.prompt = prompt def __deepcopy__(self, memo): - clone = super(FissionXS, self).__deepcopy__(memo) + clone = super().__deepcopy__(memo) clone._nu = self.nu clone._prompt = self.prompt return clone @@ -3371,9 +3357,8 @@ class KappaFissionXS(MGXS): def __init__(self, domain=None, domain_type=None, groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(KappaFissionXS, self).__init__(domain, domain_type, - groups, by_nuclide, name, - num_polar, num_azimuthal) + super().__init__(domain, domain_type, groups, by_nuclide, name, + num_polar, num_azimuthal) self._rxn_type = 'kappa-fission' @@ -3504,13 +3489,12 @@ class ScatterXS(MGXS): def __init__(self, domain=None, domain_type=None, groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1, nu=False): - super(ScatterXS, self).__init__(domain, domain_type, - groups, by_nuclide, name, - num_polar, num_azimuthal) + super().__init__(domain, domain_type, groups, by_nuclide, name, + num_polar, num_azimuthal) self.nu = nu def __deepcopy__(self, memo): - clone = super(ScatterXS, self).__deepcopy__(memo) + clone = super().__deepcopy__(memo) clone._nu = self.nu return clone @@ -3721,9 +3705,8 @@ class ScatterMatrixXS(MatrixMGXS): def __init__(self, domain=None, domain_type=None, groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1, nu=False): - super(ScatterMatrixXS, self).__init__(domain, domain_type, - groups, by_nuclide, name, - num_polar, num_azimuthal) + super().__init__(domain, domain_type, groups, by_nuclide, name, + num_polar, num_azimuthal) self._formulation = 'simple' self._correction = 'P0' self._scatter_format = 'legendre' @@ -3734,7 +3717,7 @@ class ScatterMatrixXS(MatrixMGXS): self.nu = nu def __deepcopy__(self, memo): - clone = super(ScatterMatrixXS, self).__deepcopy__(memo) + clone = super().__deepcopy__(memo) clone._formulation = self.formulation clone._correction = self.correction clone._scatter_format = self.scatter_format @@ -3825,7 +3808,7 @@ class ScatterMatrixXS(MatrixMGXS): @property def tally_keys(self): if self.formulation == 'simple': - return super(ScatterMatrixXS, self).tally_keys + return super().tally_keys else: # Add keys for groupwise scattering cross section tally_keys = ['flux (tracklength)', 'scatter'] @@ -4155,7 +4138,7 @@ class ScatterMatrixXS(MatrixMGXS): [score_prefix + '{}'.format(i) for i in range(self.legendre_order + 1)] - super(ScatterMatrixXS, self).load_from_statepoint(statepoint) + super().load_from_statepoint(statepoint) def get_slice(self, nuclides=[], in_groups=[], out_groups=[], legendre_order='same'): @@ -4195,7 +4178,7 @@ class ScatterMatrixXS(MatrixMGXS): """ # Call super class method and null out derived tallies - slice_xs = super(ScatterMatrixXS, self).get_slice(nuclides, in_groups) + slice_xs = super().get_slice(nuclides, in_groups) slice_xs._rxn_rate_tally = None slice_xs._xs_tally = None @@ -4311,7 +4294,7 @@ class ScatterMatrixXS(MatrixMGXS): filter_bins = [] # Construct a collection of the domain filter bins - if not isinstance(subdomains, string_types): + if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=3) filters.append(_DOMAIN_TO_FILTER[self.domain_type]) subdomain_bins = [] @@ -4320,7 +4303,7 @@ class ScatterMatrixXS(MatrixMGXS): filter_bins.append(tuple(subdomain_bins)) # Construct list of energy group bounds tuples for all requested groups - if not isinstance(in_groups, string_types): + if not isinstance(in_groups, str): cv.check_iterable_type('groups', in_groups, Integral) filters.append(openmc.EnergyFilter) energy_bins = [] @@ -4330,7 +4313,7 @@ class ScatterMatrixXS(MatrixMGXS): filter_bins.append(tuple(energy_bins)) # Construct list of energy group bounds tuples for all requested groups - if not isinstance(out_groups, string_types): + if not isinstance(out_groups, str): cv.check_iterable_type('groups', out_groups, Integral) for group in out_groups: filters.append(openmc.EnergyoutFilter) @@ -4486,8 +4469,7 @@ class ScatterMatrixXS(MatrixMGXS): """ - df = super(ScatterMatrixXS, self).get_pandas_dataframe( - groups, nuclides, xs_type, paths) + df = super().get_pandas_dataframe(groups, nuclides, xs_type, paths) if self.scatter_format == 'legendre': # Add a moment column to dataframe @@ -4543,7 +4525,7 @@ class ScatterMatrixXS(MatrixMGXS): """ # Construct a collection of the subdomains to report - if not isinstance(subdomains, string_types): + if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral) elif self.domain_type == 'distribcell': subdomains = np.arange(self.num_subdomains, dtype=np.int) @@ -4560,7 +4542,7 @@ class ScatterMatrixXS(MatrixMGXS): if nuclides == 'sum': nuclides = ['sum'] else: - cv.check_iterable_type('nuclides', nuclides, string_types) + cv.check_iterable_type('nuclides', nuclides, str) else: nuclides = ['sum'] @@ -4811,9 +4793,8 @@ class MultiplicityMatrixXS(MatrixMGXS): def __init__(self, domain=None, domain_type=None, groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(MultiplicityMatrixXS, self).__init__(domain, domain_type, groups, - by_nuclide, name, num_polar, - num_azimuthal) + super().__init__(domain, domain_type, groups, by_nuclide, name, + num_polar, num_azimuthal) self._rxn_type = 'multiplicity matrix' self._estimator = 'analog' self._valid_estimators = ['analog'] @@ -4848,7 +4829,7 @@ class MultiplicityMatrixXS(MatrixMGXS): # Compute the multiplicity self._xs_tally = self.rxn_rate_tally / scatter - super(MultiplicityMatrixXS, self)._compute_xs() + super()._compute_xs() return self._xs_tally @@ -4977,9 +4958,8 @@ class ScatterProbabilityMatrix(MatrixMGXS): def __init__(self, domain=None, domain_type=None, groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(ScatterProbabilityMatrix, self).__init__( - domain, domain_type, groups, by_nuclide, - name, num_polar, num_azimuthal) + super().__init__(domain, domain_type, groups, by_nuclide, + name, num_polar, num_azimuthal) self._rxn_type = 'scatter' self._hdf5_key = 'scatter probability matrix' @@ -5021,7 +5001,7 @@ class ScatterProbabilityMatrix(MatrixMGXS): # Compute the group-to-group probabilities self._xs_tally = self.tallies[self.rxn_type] / norm - super(ScatterProbabilityMatrix, self)._compute_xs() + super()._compute_xs() return self._xs_tally @@ -5151,9 +5131,8 @@ class NuFissionMatrixXS(MatrixMGXS): def __init__(self, domain=None, domain_type=None, groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1, prompt=False): - super(NuFissionMatrixXS, self).__init__(domain, domain_type, - groups, by_nuclide, name, - num_polar, num_azimuthal) + super().__init__(domain, domain_type, groups, by_nuclide, name, + num_polar, num_azimuthal) if not prompt: self._rxn_type = 'nu-fission' self._hdf5_key = 'nu-fission matrix' @@ -5174,7 +5153,7 @@ class NuFissionMatrixXS(MatrixMGXS): self._prompt = prompt def __deepcopy__(self, memo): - clone = super(NuFissionMatrixXS, self).__deepcopy__(memo) + clone = super().__deepcopy__(memo) clone._prompt = self.prompt return clone @@ -5308,8 +5287,8 @@ class Chi(MGXS): def __init__(self, domain=None, domain_type=None, groups=None, prompt=False, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(Chi, self).__init__(domain, domain_type, groups, by_nuclide, - name, num_polar, num_azimuthal) + super().__init__(domain, domain_type, groups, by_nuclide, name, + num_polar, num_azimuthal) if not prompt: self._rxn_type = 'chi' else: @@ -5319,7 +5298,7 @@ class Chi(MGXS): self.prompt = prompt def __deepcopy__(self, memo): - clone = super(Chi, self).__deepcopy__(memo) + clone = super().__deepcopy__(memo) clone._prompt = self.prompt return clone @@ -5449,7 +5428,7 @@ class Chi(MGXS): nu_fission_in.remove_filter(energy_filter) # Call super class method and null out derived tallies - slice_xs = super(Chi, self).get_slice(nuclides, groups) + slice_xs = super().get_slice(nuclides, groups) slice_xs._rxn_rate_tally = None slice_xs._xs_tally = None @@ -5586,7 +5565,7 @@ class Chi(MGXS): filter_bins = [] # Construct a collection of the domain filter bins - if not isinstance(subdomains, string_types): + if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=3) filters.append(_DOMAIN_TO_FILTER[self.domain_type]) @@ -5596,7 +5575,7 @@ class Chi(MGXS): filter_bins.append(tuple(subdomain_bins)) # Construct list of energy group bounds tuples for all requested groups - if not isinstance(groups, string_types): + if not isinstance(groups, str): cv.check_iterable_type('groups', groups, Integral) filters.append(openmc.EnergyoutFilter) energy_bins = [] @@ -5644,7 +5623,7 @@ class Chi(MGXS): # Get chi for user-specified nuclides in the domain else: - cv.check_iterable_type('nuclides', nuclides, string_types) + cv.check_iterable_type('nuclides', nuclides, str) xs = self.xs_tally.get_values(filters=filters, filter_bins=filter_bins, nuclides=nuclides, value=value) @@ -5727,8 +5706,7 @@ class Chi(MGXS): """ # Build the dataframe using the parent class method - df = super(Chi, self).get_pandas_dataframe( - groups, nuclides, xs_type, paths=paths) + df = super().get_pandas_dataframe(groups, nuclides, xs_type, paths=paths) # If user requested micro cross sections, multiply by the atom # densities to cancel out division made by the parent class method @@ -5886,9 +5864,8 @@ class InverseVelocity(MGXS): def __init__(self, domain=None, domain_type=None, groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(InverseVelocity, self).__init__(domain, domain_type, - groups, by_nuclide, name, - num_polar, num_azimuthal) + super().__init__(domain, domain_type, groups, by_nuclide, name, + num_polar, num_azimuthal) self._rxn_type = 'inverse-velocity' def get_units(self, xs_type='macro'): diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index ebfae2fb0d..e315f33140 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -2,7 +2,6 @@ import copy from numbers import Real, Integral import os -from six import string_types import numpy as np import h5py from scipy.interpolate import interp1d @@ -381,7 +380,7 @@ class XSdata(object): @name.setter def name(self, name): - check_type('name for XSdata', name, string_types) + check_type('name for XSdata', name, str) self._name = name @energy_groups.setter @@ -2517,7 +2516,7 @@ class MGXSLibrary(object): """ - check_type('filename', filename, string_types) + check_type('filename', filename, str) # Create and write to the HDF5 file file = h5py.File(filename, "w", libver=libver) diff --git a/openmc/mixin.py b/openmc/mixin.py index d724fc32a0..09a63ae4e4 100644 --- a/openmc/mixin.py +++ b/openmc/mixin.py @@ -45,14 +45,24 @@ class IDManagerMixin(object): @id.setter def id(self, uid): - cls = type(self) - name = cls.__name__ + # The first time this is called for a class, we search through the MRO + # to determine which class actually holds next_id and used_ids. Since + # next_id is an integer (immutable), we can't modify it directly through + # the instance without just creating a new attribute + try: + cls = self._id_class + except AttributeError: + for cls in self.__class__.__mro__: + if 'next_id' in cls.__dict__: + break + if uid is None: while cls.next_id in cls.used_ids: cls.next_id += 1 self._id = cls.next_id cls.used_ids.add(cls.next_id) else: + name = cls.__name__ cv.check_type('{} ID'.format(name), uid, Integral) cv.check_greater_than('{} ID'.format(name), uid, 0, equality=True) if uid in cls.used_ids: diff --git a/openmc/model/funcs.py b/openmc/model/funcs.py index 589765d2c8..6013f6caee 100644 --- a/openmc/model/funcs.py +++ b/openmc/model/funcs.py @@ -1,5 +1,5 @@ -from __future__ import division -from collections import Iterable, OrderedDict +from collections import OrderedDict +from collections.abc import Iterable from math import sqrt from numbers import Real diff --git a/openmc/model/model.py b/openmc/model/model.py index 17de6fc2e6..89fa84e604 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1,4 +1,4 @@ -from collections import Iterable +from collections.abc import Iterable import openmc from openmc.checkvalue import check_type diff --git a/openmc/model/triso.py b/openmc/model/triso.py index 67bf3bc442..5fe51b6644 100644 --- a/openmc/model/triso.py +++ b/openmc/model/triso.py @@ -1,22 +1,17 @@ -from __future__ import division import copy import warnings import itertools import random -from collections import Iterable, defaultdict +from collections import defaultdict +from collections.abc import Iterable from numbers import Real from random import uniform, gauss from heapq import heappush, heappop from math import pi, sin, cos, floor, log10, sqrt from abc import ABCMeta, abstractproperty, abstractmethod -from six import add_metaclass import numpy as np -try: - import scipy.spatial - _SCIPY_AVAILABLE = True -except ImportError: - _SCIPY_AVAILABLE = False +import scipy.spatial import openmc import openmc.checkvalue as cv @@ -51,7 +46,7 @@ class TRISO(openmc.Cell): def __init__(self, outer_radius, fill, center=(0., 0., 0.)): self._surface = openmc.Sphere(R=outer_radius) - super(TRISO, self).__init__(fill=fill, region=-self._surface) + super().__init__(fill=fill, region=-self._surface) self.center = np.asarray(center) @property @@ -96,8 +91,7 @@ class TRISO(openmc.Cell): k_min:k_max+1, j_min:j_max+1, i_min:i_max+1])) -@add_metaclass(ABCMeta) -class _Domain(object): +class _Domain(metaclass=ABCMeta): """Container in which to pack particles. Parameters @@ -252,7 +246,7 @@ class _CubicDomain(_Domain): """ def __init__(self, length, particle_radius, center=[0., 0., 0.]): - super(_CubicDomain, self).__init__(particle_radius, center) + super().__init__(particle_radius, center) self.length = length @property @@ -331,7 +325,7 @@ class _CylindricalDomain(_Domain): """ def __init__(self, length, radius, particle_radius, center=[0., 0., 0.]): - super(_CylindricalDomain, self).__init__(particle_radius, center) + super().__init__(particle_radius, center) self.length = length self.radius = radius @@ -421,7 +415,7 @@ class _SphericalDomain(_Domain): """ def __init__(self, radius, particle_radius, center=[0., 0., 0.]): - super(_SphericalDomain, self).__init__(particle_radius, center) + super().__init__(particle_radius, center) self.radius = radius @property @@ -837,10 +831,6 @@ def _close_random_pack(domain, particles, contraction_rate): if rods: inner_diameter[0] = rods[0][0] - if not _SCIPY_AVAILABLE: - raise ImportError('SciPy must be installed to perform ' - 'close random packing.') - n_particles = len(particles) diameter = 2*domain.particle_radius diff --git a/openmc/nuclide.py b/openmc/nuclide.py index 2e5a8e1193..f7c725040f 100644 --- a/openmc/nuclide.py +++ b/openmc/nuclide.py @@ -1,7 +1,5 @@ import warnings -from six import string_types - import openmc.checkvalue as cv @@ -34,7 +32,7 @@ class Nuclide(str): '"{}" is being renamed as "{}".'.format(orig_name, name) warnings.warn(msg) - return super(Nuclide, cls).__new__(cls, name) + return super().__new__(cls, name) @property def name(self): diff --git a/openmc/plots.py b/openmc/plots.py index a60b2a3094..4414a39e13 100644 --- a/openmc/plots.py +++ b/openmc/plots.py @@ -1,10 +1,9 @@ -from collections import Iterable, Mapping +from collections.abc import Iterable, Mapping from numbers import Real, Integral from xml.etree import ElementTree as ET import sys import warnings -from six import string_types import numpy as np import openmc @@ -297,7 +296,7 @@ class Plot(IDManagerMixin): @name.setter def name(self, name): - cv.check_type('plot name', name, string_types) + cv.check_type('plot name', name, str) self._name = name @width.setter @@ -322,7 +321,7 @@ class Plot(IDManagerMixin): @filename.setter def filename(self, filename): - cv.check_type('filename', filename, string_types) + cv.check_type('filename', filename, str) self._filename = filename @color_by.setter @@ -343,7 +342,7 @@ class Plot(IDManagerMixin): @background.setter def background(self, background): cv.check_type('plot background', background, Iterable) - if isinstance(background, string_types): + if isinstance(background, str): if background.lower() not in _SVG_COLORS: raise ValueError("'{}' is not a valid color.".format(background)) else: @@ -359,7 +358,7 @@ class Plot(IDManagerMixin): for key, value in colors.items(): cv.check_type('plot color key', key, (openmc.Cell, openmc.Material)) cv.check_type('plot color value', value, Iterable) - if isinstance(value, string_types): + if isinstance(value, str): if value.lower() not in _SVG_COLORS: raise ValueError("'{}' is not a valid color.".format(value)) else: @@ -380,7 +379,7 @@ class Plot(IDManagerMixin): @mask_background.setter def mask_background(self, mask_background): cv.check_type('plot mask background', mask_background, Iterable) - if isinstance(mask_background, string_types): + if isinstance(mask_background, str): if mask_background.lower() not in _SVG_COLORS: raise ValueError("'{}' is not a valid color.".format(mask_background)) else: @@ -558,7 +557,7 @@ class Plot(IDManagerMixin): cv.check_type('background', background, Iterable) # Get a background (R,G,B) tuple to apply in alpha compositing - if isinstance(background, string_types): + if isinstance(background, str): if background.lower() not in _SVG_COLORS: raise ValueError("'{}' is not a valid color.".format(background)) background = _SVG_COLORS[background.lower()] @@ -570,7 +569,7 @@ class Plot(IDManagerMixin): # other than those the user wishes to highlight for domain, color in self.colors.items(): if domain not in domains: - if isinstance(color, string_types): + if isinstance(color, str): color = _SVG_COLORS[color.lower()] r, g, b = color r = int(((1-alpha) * background[0]) + (alpha * r)) @@ -610,7 +609,7 @@ class Plot(IDManagerMixin): if self._background is not None: subelement = ET.SubElement(element, "background") color = self._background - if isinstance(color, string_types): + if isinstance(color, str): color = _SVG_COLORS[color.lower()] subelement.text = ' '.join(str(x) for x in color) @@ -619,7 +618,7 @@ class Plot(IDManagerMixin): key=lambda x: x[0].id): subelement = ET.SubElement(element, "color") subelement.set("id", str(domain.id)) - if isinstance(color, string_types): + if isinstance(color, str): color = _SVG_COLORS[color.lower()] subelement.set("rgb", ' '.join(str(x) for x in color)) @@ -629,7 +628,7 @@ class Plot(IDManagerMixin): str(d.id) for d in self._mask_components)) color = self._mask_background if color is not None: - if isinstance(color, string_types): + if isinstance(color, str): color = _SVG_COLORS[color.lower()] subelement.set("background", ' '.join( str(x) for x in color)) @@ -674,28 +673,11 @@ class Plots(cv.CheckedList): """ def __init__(self, plots=None): - super(Plots, self).__init__(Plot, 'plots collection') + super().__init__(Plot, 'plots collection') self._plots_file = ET.Element("plots") if plots is not None: self += plots - def add_plot(self, plot): - """Add a plot to the file. - - .. deprecated:: 0.8 - Use :meth:`Plots.append` instead. - - Parameters - ---------- - plot : openmc.Plot - Plot to add - - """ - warnings.warn("Plots.add_plot(...) has been deprecated and may be " - "removed in a future version. Use Plots.append(...) " - "instead.", DeprecationWarning) - self.append(plot) - def append(self, plot): """Append plot to collection @@ -705,7 +687,7 @@ class Plots(cv.CheckedList): Plot to append """ - super(Plots, self).append(plot) + super().append(plot) def insert(self, index, plot): """Insert plot before index @@ -718,24 +700,7 @@ class Plots(cv.CheckedList): Plot to insert """ - super(Plots, self).insert(index, plot) - - def remove_plot(self, plot): - """Remove a plot from the file. - - .. deprecated:: 0.8 - Use :meth:`Plots.remove` instead. - - Parameters - ---------- - plot : openmc.Plot - Plot to remove - - """ - warnings.warn("Plots.remove_plot(...) has been deprecated and may be " - "removed in a future version. Use Plots.remove(...) " - "instead.", DeprecationWarning) - self.remove(plot) + super().insert(index, plot) def colorize(self, geometry, seed=1): """Generate a consistent color scheme for each domain in each plot. diff --git a/openmc/plotter.py b/openmc/plotter.py index 810ee5d27c..1bf6fe46fd 100644 --- a/openmc/plotter.py +++ b/openmc/plotter.py @@ -2,7 +2,6 @@ from numbers import Integral, Real from itertools import chain import string -from six import string_types import matplotlib.pyplot as plt import numpy as np @@ -137,7 +136,7 @@ def plot_xs(this, types, divisor_types=None, temperature=294., data_type=None, data_type = 'material' elif isinstance(this, openmc.Macroscopic): data_type = 'macroscopic' - elif isinstance(this, string_types): + elif isinstance(this, str): if this[-1] in string.digits: data_type = 'nuclide' else: @@ -275,7 +274,7 @@ def calculate_cexs(this, data_type, types, temperature=294., sab_name=None, # Check types cv.check_type('temperature', temperature, Real) if sab_name: - cv.check_type('sab_name', sab_name, string_types) + cv.check_type('sab_name', sab_name, str) if enrichment: cv.check_type('enrichment', enrichment, Real) @@ -648,7 +647,7 @@ def calculate_mgxs(this, data_type, types, orders=None, temperature=294., cv.check_type('temperature', temperature, Real) if enrichment: cv.check_type('enrichment', enrichment, Real) - cv.check_iterable_type('types', types, string_types) + cv.check_iterable_type('types', types, str) cv.check_type("cross_sections", cross_sections, str) library = openmc.MGXSLibrary.from_hdf5(cross_sections) diff --git a/openmc/region.py b/openmc/region.py index eeafd28326..f82db8553b 100644 --- a/openmc/region.py +++ b/openmc/region.py @@ -1,15 +1,14 @@ from abc import ABCMeta, abstractmethod -from collections import Iterable, OrderedDict, MutableSequence +from collections import OrderedDict +from collections.abc import Iterable, MutableSequence from copy import deepcopy -from six import add_metaclass import numpy as np from openmc.checkvalue import check_type -@add_metaclass(ABCMeta) -class Region(object): +class Region(metaclass=ABCMeta): """Region of space that can be assigned to a cell. Region is an abstract base class that is inherited by @@ -39,10 +38,8 @@ class Region(object): def __eq__(self, other): if not isinstance(other, type(self)): return False - elif str(self) != str(other): - return False else: - return True + return str(self) == str(other) def __ne__(self, other): return not self == other @@ -463,7 +460,7 @@ class Union(Region, MutableSequence): if memo is None: memo = {} - clone = copy.deepcopy(self) + clone = deepcopy(self) clone[:] = [n.clone(memo) for n in self] return clone @@ -584,6 +581,6 @@ class Complement(Region): if memo is None: memo = {} - clone = copy.deepcopy(self) + clone = deepcopy(self) clone.node = self.node.clone(memo) return clone diff --git a/openmc/search.py b/openmc/search.py index 7f91438fd7..75935097e4 100644 --- a/openmc/search.py +++ b/openmc/search.py @@ -1,4 +1,4 @@ -from collections import Callable +from collections.abc import Callable from numbers import Real import scipy.optimize as sopt diff --git a/openmc/settings.py b/openmc/settings.py index 9ead82ec61..62cfc27aaa 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1,10 +1,9 @@ -from collections import Iterable, MutableSequence, Mapping +from collections.abc import Iterable, MutableSequence, Mapping from numbers import Real, Integral import warnings from xml.etree import ElementTree as ET import sys -from six import string_types import numpy as np from openmc.clean_xml import clean_xml_indentation @@ -29,13 +28,6 @@ class Settings(object): deviation. create_fission_neutrons : bool Indicate whether fission neutrons should be created or not. - cross_sections : str - Indicates the path to an XML cross section listing file (usually named - cross_sections.xml). If it is not set, the - :envvar:`OPENMC_CROSS_SECTIONS` environment variable will be used for - continuous-energy calculations and - :envvar:`OPENMC_MG_CROSS_SECTIONS` will be used for multi-group - calculations to find the path to the XML cross section file. cutoff : dict Dictionary defining weight cutoff and energy cutoff. The dictionary may have three keys, 'weight', 'weight_avg' and 'energy'. Value for 'weight' @@ -60,13 +52,10 @@ class Settings(object): type are 'variance', 'std_dev', and 'rel_err'. The threshold value should be a float indicating the variance, standard deviation, or relative error used. + log_grid_bins : int + Number of bins for logarithmic energy grid search max_order : None or int Maximum scattering order to apply globally when in multi-group mode. - multipole_library : str - Indicates the path to a directory containing a windowed multipole - cross section library. If it is not set, the - :envvar:`OPENMC_MULTIPOLE_LIBRARY` environment variable will be used. A - multipole library is optional. no_reduce : bool Indicate that all user-defined and global tallies should not be reduced across processes in a parallel calculation. @@ -220,19 +209,12 @@ class Settings(object): # Uniform fission source subelement self._ufs_mesh = None - # Domain decomposition subelement - self._dd_mesh_dimension = None - self._dd_mesh_lower_left = None - self._dd_mesh_upper_right = None - self._dd_nodemap = None - self._dd_allow_leakage = False - self._dd_count_interactions = False - self._resonance_scattering = {} self._volume_calculations = cv.CheckedList( VolumeCalculation, 'volume calculations') self._create_fission_neutrons = None + self._log_grid_bins = None @property def run_mode(self): @@ -274,14 +256,6 @@ class Settings(object): def confidence_intervals(self): return self._confidence_intervals - @property - def cross_sections(self): - return self._cross_sections - - @property - def multipole_library(self): - return self._multipole_library - @property def ptables(self): return self._ptables @@ -362,30 +336,6 @@ class Settings(object): def ufs_mesh(self): return self._ufs_mesh - @property - def dd_mesh_dimension(self): - return self._dd_mesh_dimension - - @property - def dd_mesh_lower_left(self): - return self._dd_mesh_lower_left - - @property - def dd_mesh_upper_right(self): - return self._dd_mesh_upper_right - - @property - def dd_nodemap(self): - return self._dd_nodemap - - @property - def dd_allow_leakage(self): - return self._dd_allow_leakage - - @property - def dd_count_interactions(self): - return self._dd_count_interactions - @property def resonance_scattering(self): return self._resonance_scattering @@ -398,6 +348,10 @@ class Settings(object): def create_fission_neutrons(self): return self._create_fission_neutrons + @property + def log_grid_bins(self): + return self._log_grid_bins + @run_mode.setter def run_mode(self, run_mode): cv.check_value('run mode', run_mode, _RUN_MODES) @@ -484,7 +438,7 @@ class Settings(object): if key in ('summary', 'tallies'): cv.check_type("output['{}']".format(key), value, bool) else: - cv.check_type("output['path']", value, string_types) + cv.check_type("output['path']", value, str) self._output = output @verbosity.setter @@ -531,23 +485,6 @@ class Settings(object): cv.check_type('confidence interval', confidence_intervals, bool) self._confidence_intervals = confidence_intervals - @cross_sections.setter - def cross_sections(self, cross_sections): - warnings.warn('Settings.cross_sections has been deprecated and will be ' - 'removed in a future version. Materials.cross_sections ' - 'should defined instead.', DeprecationWarning) - cv.check_type('cross sections', cross_sections, string_types) - self._cross_sections = cross_sections - - @multipole_library.setter - def multipole_library(self, multipole_library): - warnings.warn('Settings.multipole_library has been deprecated and will ' - 'be removed in a future version. ' - 'Materials.multipole_library should defined instead.', - DeprecationWarning) - cv.check_type('multipole library', multipole_library, string_types) - self._multipole_library = multipole_library - @ptables.setter def ptables(self, ptables): cv.check_type('probability tables', ptables, bool) @@ -696,85 +633,6 @@ class Settings(object): cv.check_length('UFS mesh upper-right corner', ufs_mesh.upper_right, 3) self._ufs_mesh = ufs_mesh - @dd_mesh_dimension.setter - def dd_mesh_dimension(self, dimension): - # TODO: remove this when domain decomposition is merged - warnings.warn('This feature is not yet implemented in a release ' - 'version of openmc') - - cv.check_type('DD mesh dimension', dimension, Iterable, Integral) - cv.check_length('DD mesh dimension', dimension, 3) - - self._dd_mesh_dimension = dimension - - @dd_mesh_lower_left.setter - def dd_mesh_lower_left(self, lower_left): - # TODO: remove this when domain decomposition is merged - warnings.warn('This feature is not yet implemented in a release ' - 'version of openmc') - - cv.check_type('DD mesh lower left corner', lower_left, Iterable, Real) - cv.check_length('DD mesh lower left corner', lower_left, 3) - - self._dd_mesh_lower_left = lower_left - - @dd_mesh_upper_right.setter - def dd_mesh_upper_right(self, upper_right): - # TODO: remove this when domain decomposition is merged - warnings.warn('This feature is not yet implemented in a release ' - 'version of openmc') - - cv.check_type('DD mesh upper right corner', upper_right, Iterable, Real) - cv.check_length('DD mesh upper right corner', upper_right, 3) - - self._dd_mesh_upper_right = upper_right - - @dd_nodemap.setter - def dd_nodemap(self, nodemap): - # TODO: remove this when domain decomposition is merged - warnings.warn('This feature is not yet implemented in a release ' - 'version of openmc') - - cv.check_type('DD nodemap', nodemap, Iterable) - - nodemap = np.array(nodemap).flatten() - - if self._dd_mesh_dimension is None: - msg = 'Must set DD mesh dimension before setting the nodemap' - raise ValueError(msg) - else: - len_nodemap = np.prod(self._dd_mesh_dimension) - - if len(nodemap) < len_nodemap or len(nodemap) > len_nodemap: - msg = 'Unable to set DD nodemap with length "{0}" which ' \ - 'does not have the same dimensionality as the domain ' \ - 'mesh'.format(len(nodemap)) - raise ValueError(msg) - - self._dd_nodemap = nodemap - - @dd_allow_leakage.setter - def dd_allow_leakage(self, allow): - - # TODO: remove this when domain decomposition is merged - warnings.warn('This feature is not yet implemented in a release ' - 'version of openmc') - - cv.check_type('DD allow leakage', allow, bool) - - self._dd_allow_leakage = allow - - @dd_count_interactions.setter - def dd_count_interactions(self, interactions): - - # TODO: remove this when domain decomposition is merged - warnings.warn('This feature is not yet implemented in a release ' - 'version of openmc') - - cv.check_type('DD count interactions', interactions, bool) - - self._dd_count_interactions = interactions - @resonance_scattering.setter def resonance_scattering(self, res): cv.check_type('resonance scattering settings', res, Mapping) @@ -796,7 +654,7 @@ class Settings(object): cv.check_greater_than(name, value, 0) elif key == 'nuclides': cv.check_type('resonance scattering nuclides', value, - Iterable, string_types) + Iterable, str) self._resonance_scattering = res @volume_calculations.setter @@ -812,6 +670,12 @@ class Settings(object): create_fission_neutrons, bool) self._create_fission_neutrons = create_fission_neutrons + @log_grid_bins.setter + def log_grid_bins(self, log_grid_bins): + cv.check_type('log grid bins', log_grid_bins, Real) + cv.check_greater_than('log grid bins', log_grid_bins, 0) + self._log_grid_bins = log_grid_bins + def _create_run_mode_subelement(self, root): elem = ET.SubElement(root, "run_mode") elem.text = self._run_mode @@ -913,16 +777,6 @@ class Settings(object): element = ET.SubElement(root, "confidence_intervals") element.text = str(self._confidence_intervals).lower() - def _create_cross_sections_subelement(self, root): - if self._cross_sections is not None: - element = ET.SubElement(root, "cross_sections") - element.text = str(self._cross_sections) - - def _create_multipole_library_subelement(self, root): - if self._multipole_library is not None: - element = ET.SubElement(root, "multipole_library") - element.text = str(self._multipole_library) - def _create_ptables_subelement(self, root): if self._ptables is not None: element = ET.SubElement(root, "ptables") @@ -1033,33 +887,6 @@ class Settings(object): subelement = ET.SubElement(root, "ufs_mesh") subelement.text = str(self.ufs_mesh.id) - def _create_dd_subelement(self, root): - if self._dd_mesh_lower_left is not None and \ - self._dd_mesh_upper_right is not None and \ - self._dd_mesh_dimension is not None: - - element = ET.SubElement(root, "domain_decomposition") - - subelement = ET.SubElement(element, "mesh") - subsubelement = ET.SubElement(subelement, "dimension") - subsubelement.text = ' '.join(map(str, self._dd_mesh_dimension)) - - subsubelement = ET.SubElement(subelement, "lower_left") - subsubelement.text = ' '.join(map(str, self._dd_mesh_lower_left)) - - subsubelement = ET.SubElement(subelement, "upper_right") - subsubelement.text = ' '.join(map(str, self._dd_mesh_upper_right)) - - if self._dd_nodemap is not None: - subelement = ET.SubElement(element, "nodemap") - subelement.text = ' '.join(map(str, self._dd_nodemap)) - - subelement = ET.SubElement(element, "allow_leakage") - subelement.text = str(self._dd_allow_leakage).lower() - - subelement = ET.SubElement(element, "count_interactions") - subelement.text = str(self._dd_count_interactions).lower() - def _create_resonance_scattering_subelement(self, root): res = self.resonance_scattering if res: @@ -1085,6 +912,11 @@ class Settings(object): elem = ET.SubElement(root, "create_fission_neutrons") elem.text = str(self._create_fission_neutrons).lower() + def _create_log_grid_bins_subelement(self, root): + if self._log_grid_bins is not None: + elem = ET.SubElement(root, "log_grid_bins") + elem.text = str(self._log_grid_bins) + def export_to_xml(self, path='settings.xml'): """Export simulation settings to an XML file. @@ -1109,8 +941,6 @@ class Settings(object): self._create_statepoint_subelement(root_element) self._create_sourcepoint_subelement(root_element) self._create_confidence_intervals(root_element) - self._create_cross_sections_subelement(root_element) - self._create_multipole_library_subelement(root_element) self._create_energy_mode_subelement(root_element) self._create_max_order_subelement(root_element) self._create_ptables_subelement(root_element) @@ -1128,10 +958,10 @@ class Settings(object): self._create_trace_subelement(root_element) self._create_track_subelement(root_element) self._create_ufs_mesh_subelement(root_element) - self._create_dd_subelement(root_element) self._create_resonance_scattering_subelement(root_element) self._create_volume_calcs_subelement(root_element) self._create_create_fission_neutrons_subelement(root_element) + self._create_log_grid_bins_subelement(root_element) # Clean the indentation in the file to be user-readable clean_xml_indentation(root_element) diff --git a/openmc/source.py b/openmc/source.py index 1aee435147..932062278c 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -2,8 +2,6 @@ from numbers import Real import sys from xml.etree import ElementTree as ET -from six import string_types - from openmc.stats.univariate import Univariate from openmc.stats.multivariate import UnitSphere, Spatial import openmc.checkvalue as cv @@ -78,7 +76,7 @@ class Source(object): @file.setter def file(self, filename): - cv.check_type('source file', filename, string_types) + cv.check_type('source file', filename, str) self._file = filename @space.setter diff --git a/openmc/stats/multivariate.py b/openmc/stats/multivariate.py index 0ab11b7c54..ac788c3443 100644 --- a/openmc/stats/multivariate.py +++ b/openmc/stats/multivariate.py @@ -1,19 +1,17 @@ from abc import ABCMeta, abstractmethod -from collections import Iterable +from collections.abc import Iterable from math import pi from numbers import Real import sys from xml.etree import ElementTree as ET -from six import add_metaclass import numpy as np import openmc.checkvalue as cv from openmc.stats.univariate import Univariate, Uniform -@add_metaclass(ABCMeta) -class UnitSphere(object): +class UnitSphere(metaclass=ABCMeta): """Distribution of points on the unit sphere. This abstract class is used for angular distributions, since a direction is @@ -77,7 +75,7 @@ class PolarAzimuthal(UnitSphere): """ def __init__(self, mu=None, phi=None, reference_uvw=[0., 0., 1.]): - super(PolarAzimuthal, self).__init__(reference_uvw) + super().__init__(reference_uvw) if mu is not None: self.mu = mu else: @@ -130,7 +128,7 @@ class Isotropic(UnitSphere): """ def __init__(self): - super(Isotropic, self).__init__() + super().__init__() def to_xml_element(self): """Return XML representation of the isotropic distribution @@ -163,7 +161,7 @@ class Monodirectional(UnitSphere): def __init__(self, reference_uvw=[1., 0., 0.]): - super(Monodirectional, self).__init__(reference_uvw) + super().__init__(reference_uvw) def to_xml_element(self): """Return XML representation of the monodirectional distribution @@ -181,8 +179,7 @@ class Monodirectional(UnitSphere): return element -@add_metaclass(ABCMeta) -class Spatial(object): +class Spatial(metaclass=ABCMeta): """Distribution of locations in three-dimensional Euclidean space. Classes derived from this abstract class can be used for spatial @@ -225,7 +222,7 @@ class CartesianIndependent(Spatial): def __init__(self, x, y, z): - super(CartesianIndependent, self).__init__() + super().__init__() self.x = x self.y = y self.z = z @@ -301,7 +298,7 @@ class Box(Spatial): def __init__(self, lower_left, upper_right, only_fissionable=False): - super(Box, self).__init__() + super().__init__() self.lower_left = lower_left self.upper_right = upper_right self.only_fissionable = only_fissionable @@ -374,7 +371,7 @@ class Point(Spatial): """ def __init__(self, xyz=(0., 0., 0.)): - super(Point, self).__init__() + super().__init__() self.xyz = xyz @property diff --git a/openmc/stats/univariate.py b/openmc/stats/univariate.py index c0476ce45c..16a9dbb9e7 100644 --- a/openmc/stats/univariate.py +++ b/openmc/stats/univariate.py @@ -1,10 +1,9 @@ from abc import ABCMeta, abstractmethod -from collections import Iterable +from collections.abc import Iterable from numbers import Real import sys from xml.etree import ElementTree as ET -from six import add_metaclass import numpy as np import openmc.checkvalue as cv @@ -15,8 +14,7 @@ _INTERPOLATION_SCHEMES = ['histogram', 'linear-linear', 'linear-log', 'log-linear', 'log-log'] -@add_metaclass(ABCMeta) -class Univariate(EqualityMixin): +class Univariate(EqualityMixin, metaclass=ABCMeta): """Probability distribution of a single random variable. The Univariate class is an abstract class that can be derived to implement a @@ -59,7 +57,7 @@ class Discrete(Univariate): """ def __init__(self, x, p): - super(Discrete, self).__init__() + super().__init__() self.x = x self.p = p @@ -133,7 +131,7 @@ class Uniform(Univariate): """ def __init__(self, a=0.0, b=1.0): - super(Uniform, self).__init__() + super().__init__() self.a = a self.b = b @@ -194,17 +192,17 @@ class Maxwell(Univariate): Parameters ---------- theta : float - Effective temperature for distribution + Effective temperature for distribution in eV Attributes ---------- theta : float - Effective temperature for distribution + Effective temperature for distribution in eV """ def __init__(self, theta): - super(Maxwell, self).__init__() + super().__init__() self.theta = theta def __len__(self): @@ -250,21 +248,21 @@ class Watt(Univariate): Parameters ---------- a : float - First parameter of distribution + First parameter of distribution in units of eV b : float - Second parameter of distribution + Second parameter of distribution in units of 1/eV Attributes ---------- a : float - First parameter of distribution + First parameter of distribution in units of eV b : float - Second parameter of distribution + Second parameter of distribution in units of 1/eV """ def __init__(self, a=0.988e6, b=2.249e-6): - super(Watt, self).__init__() + super().__init__() self.a = a self.b = b @@ -344,7 +342,7 @@ class Tabular(Univariate): def __init__(self, x, p, interpolation='linear-linear', ignore_negative=False): - super(Tabular, self).__init__() + super().__init__() self._ignore_negative = ignore_negative self.x = x self.p = p @@ -444,10 +442,9 @@ class Legendre(Univariate): def coefficients(self, coefficients): cv.check_type('Legendre expansion coefficients', coefficients, Iterable, Real) - for l in range(len(coefficients)): - coefficients[l] *= (2.*l + 1.)/2. - self._legendre_polynomial = np.polynomial.legendre.Legendre( - coefficients) + l = np.arange(len(coefficients)) + coeffs = (2.*l + 1.)/2. * np.array(coefficients) + self._legendre_polynomial = np.polynomial.Legendre(coeffs) def to_xml_element(self, element_name): raise NotImplementedError @@ -473,7 +470,7 @@ class Mixture(Univariate): """ def __init__(self, probability, distribution): - super(Mixture, self).__init__() + super().__init__() self.probability = probability self.distribution = distribution diff --git a/openmc/summary.py b/openmc/summary.py index 34743757b4..aa98025c08 100644 --- a/openmc/summary.py +++ b/openmc/summary.py @@ -1,4 +1,4 @@ -from collections import Iterable +from collections.abc import Iterable import re import warnings diff --git a/openmc/surface.py b/openmc/surface.py index 6c29ed458c..ad5053e370 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -1,4 +1,3 @@ -from __future__ import division from abc import ABCMeta from collections import OrderedDict from copy import deepcopy @@ -6,7 +5,6 @@ from functools import partial from numbers import Real, Integral from xml.etree import ElementTree as ET -from six import add_metaclass, string_types import numpy as np from openmc.checkvalue import check_type, check_value @@ -115,14 +113,14 @@ class Surface(IDManagerMixin): @name.setter def name(self, name): if name is not None: - check_type('surface name', name, string_types) + check_type('surface name', name, str) self._name = name else: self._name = '' @boundary_type.setter def boundary_type(self, boundary_type): - check_type('boundary type', boundary_type, string_types) + check_type('boundary type', boundary_type, str) check_value('boundary type', boundary_type, _BOUNDARY_TYPES) self._boundary_type = boundary_type @@ -328,7 +326,7 @@ class Plane(Surface): def __init__(self, surface_id=None, boundary_type='transmission', A=1., B=0., C=0., D=0., name=''): - super(Plane, self).__init__(surface_id, boundary_type, name=name) + super().__init__(surface_id, boundary_type, name=name) self._type = 'plane' self._coeff_keys = ['A', 'B', 'C', 'D'] @@ -412,7 +410,7 @@ class Plane(Surface): XML element containing source data """ - element = super(Plane, self).to_xml_element() + element = super().to_xml_element() # Add periodic surface pair information if self.boundary_type == 'periodic': @@ -462,7 +460,7 @@ class XPlane(Plane): def __init__(self, surface_id=None, boundary_type='transmission', x0=0., name=''): - super(XPlane, self).__init__(surface_id, boundary_type, name=name) + super().__init__(surface_id, boundary_type, name=name) self._type = 'x-plane' self._coeff_keys = ['x0'] @@ -568,7 +566,7 @@ class YPlane(Plane): def __init__(self, surface_id=None, boundary_type='transmission', y0=0., name=''): # Initialize YPlane class attributes - super(YPlane, self).__init__(surface_id, boundary_type, name=name) + super().__init__(surface_id, boundary_type, name=name) self._type = 'y-plane' self._coeff_keys = ['y0'] @@ -674,7 +672,7 @@ class ZPlane(Plane): def __init__(self, surface_id=None, boundary_type='transmission', z0=0., name=''): # Initialize ZPlane class attributes - super(ZPlane, self).__init__(surface_id, boundary_type, name=name) + super().__init__(surface_id, boundary_type, name=name) self._type = 'z-plane' self._coeff_keys = ['z0'] @@ -738,8 +736,7 @@ class ZPlane(Plane): return point[2] - self.z0 -@add_metaclass(ABCMeta) -class Cylinder(Surface): +class Cylinder(Surface, metaclass=ABCMeta): """A cylinder whose length is parallel to the x-, y-, or z-axis. Parameters @@ -776,7 +773,7 @@ class Cylinder(Surface): """ def __init__(self, surface_id=None, boundary_type='transmission', R=1., name=''): - super(Cylinder, self).__init__(surface_id, boundary_type, name=name) + super().__init__(surface_id, boundary_type, name=name) self._coeff_keys = ['R'] self.r = R @@ -836,7 +833,7 @@ class XCylinder(Cylinder): def __init__(self, surface_id=None, boundary_type='transmission', y0=0., z0=0., R=1., name=''): - super(XCylinder, self).__init__(surface_id, boundary_type, R, name=name) + super().__init__(surface_id, boundary_type, R, name=name) self._type = 'x-cylinder' self._coeff_keys = ['y0', 'z0', 'R'] @@ -958,7 +955,7 @@ class YCylinder(Cylinder): def __init__(self, surface_id=None, boundary_type='transmission', x0=0., z0=0., R=1., name=''): - super(YCylinder, self).__init__(surface_id, boundary_type, R, name=name) + super().__init__(surface_id, boundary_type, R, name=name) self._type = 'y-cylinder' self._coeff_keys = ['x0', 'z0', 'R'] @@ -1080,7 +1077,7 @@ class ZCylinder(Cylinder): def __init__(self, surface_id=None, boundary_type='transmission', x0=0., y0=0., R=1., name=''): - super(ZCylinder, self).__init__(surface_id, boundary_type, R, name=name) + super().__init__(surface_id, boundary_type, R, name=name) self._type = 'z-cylinder' self._coeff_keys = ['x0', 'y0', 'R'] @@ -1206,7 +1203,7 @@ class Sphere(Surface): def __init__(self, surface_id=None, boundary_type='transmission', x0=0., y0=0., z0=0., R=1., name=''): - super(Sphere, self).__init__(surface_id, boundary_type, name=name) + super().__init__(surface_id, boundary_type, name=name) self._type = 'sphere' self._coeff_keys = ['x0', 'y0', 'z0', 'R'] @@ -1305,8 +1302,7 @@ class Sphere(Surface): return x**2 + y**2 + z**2 - self.r**2 -@add_metaclass(ABCMeta) -class Cone(Surface): +class Cone(Surface, metaclass=ABCMeta): """A conical surface parallel to the x-, y-, or z-axis. Parameters @@ -1354,7 +1350,7 @@ class Cone(Surface): """ def __init__(self, surface_id=None, boundary_type='transmission', x0=0., y0=0., z0=0., R2=1., name=''): - super(Cone, self).__init__(surface_id, boundary_type, name=name) + super().__init__(surface_id, boundary_type, name=name) self._coeff_keys = ['x0', 'y0', 'z0', 'R2'] self.x0 = x0 @@ -1376,7 +1372,7 @@ class Cone(Surface): @property def r2(self): - return self.coefficients['r2'] + return self.coefficients['R2'] @x0.setter def x0(self, x0): @@ -1449,7 +1445,7 @@ class XCone(Cone): def __init__(self, surface_id=None, boundary_type='transmission', x0=0., y0=0., z0=0., R2=1., name=''): - super(XCone, self).__init__(surface_id, boundary_type, x0, y0, + super().__init__(surface_id, boundary_type, x0, y0, z0, R2, name=name) self._type = 'x-cone' @@ -1525,7 +1521,7 @@ class YCone(Cone): def __init__(self, surface_id=None, boundary_type='transmission', x0=0., y0=0., z0=0., R2=1., name=''): - super(YCone, self).__init__(surface_id, boundary_type, x0, y0, z0, + super().__init__(surface_id, boundary_type, x0, y0, z0, R2, name=name) self._type = 'y-cone' @@ -1601,7 +1597,7 @@ class ZCone(Cone): def __init__(self, surface_id=None, boundary_type='transmission', x0=0., y0=0., z0=0., R2=1., name=''): - super(ZCone, self).__init__(surface_id, boundary_type, x0, y0, z0, + super().__init__(surface_id, boundary_type, x0, y0, z0, R2, name=name) self._type = 'z-cone' @@ -1666,7 +1662,7 @@ class Quadric(Surface): def __init__(self, surface_id=None, boundary_type='transmission', a=0., b=0., c=0., d=0., e=0., f=0., g=0., h=0., j=0., k=0., name=''): - super(Quadric, self).__init__(surface_id, boundary_type, name=name) + super().__init__(surface_id, boundary_type, name=name) self._type = 'quadric' self._coeff_keys = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k'] diff --git a/openmc/tallies.py b/openmc/tallies.py index 235ad2472e..d22cfdcb46 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -1,6 +1,4 @@ -from __future__ import division - -from collections import Iterable, MutableSequence +from collections.abc import Iterable, MutableSequence import copy import re from functools import partial, reduce @@ -10,7 +8,6 @@ import operator import warnings from xml.etree import ElementTree as ET -from six import string_types import numpy as np import pandas as pd import scipy.sparse as sps @@ -31,9 +28,8 @@ _PRODUCT_TYPES = ['tensor', 'entrywise'] # The following indicate acceptable types when setting Tally.scores, # Tally.nuclides, and Tally.filters -_SCORE_CLASSES = string_types + (openmc.CrossScore, openmc.AggregateScore) -_NUCLIDE_CLASSES = string_types + (openmc.Nuclide, openmc.CrossNuclide, - openmc.AggregateNuclide) +_SCORE_CLASSES = (str, openmc.CrossScore, openmc.AggregateScore) +_NUCLIDE_CLASSES = (str, openmc.CrossNuclide, openmc.AggregateNuclide) _FILTER_CLASSES = (openmc.Filter, openmc.CrossFilter, openmc.AggregateFilter) # Valid types of estimators @@ -336,30 +332,10 @@ class Tally(IDManagerMixin): self._triggers = cv.CheckedList(openmc.Trigger, 'tally triggers', triggers) - def add_trigger(self, trigger): - """Add a tally trigger to the tally - - .. deprecated:: 0.8 - Use the Tally.triggers property directly, i.e., - Tally.triggers.append(...) - - Parameters - ---------- - trigger : openmc.Trigger - Trigger to add - - """ - - warnings.warn('Tally.add_trigger(...) has been deprecated and may be ' - 'removed in a future version. Tally triggers should be ' - 'defined using the triggers property directly.', - DeprecationWarning) - self.triggers.append(trigger) - @name.setter def name(self, name): if name is not None: - cv.check_type('tally name', name, string_types) + cv.check_type('tally name', name, str) self._name = name else: self._name = '' @@ -412,85 +388,11 @@ class Tally(IDManagerMixin): raise ValueError(msg) # If score is a string, strip whitespace - if isinstance(score, string_types): + if isinstance(score, str): scores[i] = score.strip() self._scores = cv.CheckedList(_SCORE_CLASSES, 'tally scores', scores) - def add_filter(self, new_filter): - """Add a filter to the tally - - .. deprecated:: 0.8 - Use the Tally.filters property directly, i.e., - Tally.filters.append(...) - - Parameters - ---------- - new_filter : Filter, CrossFilter or AggregateFilter - A filter to specify a discretization of the tally across some - dimension (e.g., 'energy', 'cell'). The filter should be a Filter - object when a user is adding filters to a Tally for input file - generation or when the Tally is created from a StatePoint. The - filter may be a CrossFilter or AggregateFilter for derived tallies - created by tally arithmetic. - - """ - - warnings.warn('Tally.add_filter(...) has been deprecated and may be ' - 'removed in a future version. Tally filters should be ' - 'defined using the filters property directly.', - DeprecationWarning) - self.filters.append(new_filter) - - def add_nuclide(self, nuclide): - """Specify that scores for a particular nuclide should be accumulated - - .. deprecated:: 0.8 - Use the Tally.nuclides property directly, i.e., - Tally.nuclides.append(...) - - Parameters - ---------- - nuclide : str, openmc.Nuclide, CrossNuclide or AggregateNuclide - Nuclide to add to the tally. The nuclide should be a Nuclide object - when a user is adding nuclides to a Tally for input file generation. - The nuclide is a str when a Tally is created from a StatePoint file - (e.g., 'H1', 'U235') unless a Summary has been linked with the - StatePoint. The nuclide may be a CrossNuclide or AggregateNuclide - for derived tallies created by tally arithmetic. - - """ - - warnings.warn('Tally.add_nuclide(...) has been deprecated and may be ' - 'removed in a future version. Tally nuclides should be ' - 'defined using the nuclides property directly.', - DeprecationWarning) - self.nuclides.append(nuclide) - - def add_score(self, score): - """Specify a quantity to be scored - - .. deprecated:: 0.8 - Use the Tally.scores property directly, i.e., - Tally.scores.append(...) - - Parameters - ---------- - score : str, CrossScore or AggregateScore - A score to be accumulated (e.g., 'flux', 'nu-fission'). The score - should be a str when a user is adding scores to a Tally for input - file generation or when the Tally is created from a StatePoint. The - score may be a CrossScore or AggregateScore for derived tallies - created by tally arithmetic. - - """ - - warnings.warn('Tally.add_score(...) has been deprecated and may be ' - 'removed in a future version. Tally scores should be ' - 'defined using the scores property directly.', - DeprecationWarning) - self.scores.append(score) - @num_realizations.setter def num_realizations(self, num_realizations): cv.check_type('number of realizations', num_realizations, Integral) @@ -1327,7 +1229,7 @@ class Tally(IDManagerMixin): """ - cv.check_iterable_type('nuclides', nuclides, string_types) + cv.check_iterable_type('nuclides', nuclides, str) # Determine the score indices from any of the requested scores if nuclides: @@ -1362,7 +1264,7 @@ class Tally(IDManagerMixin): """ for score in scores: - if not isinstance(score, string_types + (openmc.CrossScore,)): + if not isinstance(score, (str, openmc.CrossScore)): msg = 'Unable to get score indices for score "{0}" in Tally ' \ 'ID="{1}" since it is not a string or CrossScore'\ .format(score, self.id) @@ -1508,8 +1410,6 @@ class Tally(IDManagerMixin): ------ KeyError When this method is called before the Tally is populated with data - ImportError - When Pandas can not be found on the caller's system """ @@ -1557,7 +1457,7 @@ class Tally(IDManagerMixin): column_name = 'score' for score in self.scores: - if isinstance(score, string_types + (openmc.CrossScore,)): + if isinstance(score, (str, openmc.CrossScore)): scores.append(str(score)) elif isinstance(score, openmc.AggregateScore): scores.append(score.name) @@ -2194,11 +2094,11 @@ class Tally(IDManagerMixin): raise ValueError(msg) # Check that the scores are valid - if not isinstance(score1, string_types + (openmc.CrossScore,)): + if not isinstance(score1, (str, openmc.CrossScore)): msg = 'Unable to swap score1 "{0}" in Tally ID="{1}" since it is ' \ 'not a string or CrossScore'.format(score1, self.id) raise ValueError(msg) - elif not isinstance(score2, string_types + (openmc.CrossScore,)): + elif not isinstance(score2, (str, openmc.CrossScore)): msg = 'Unable to swap score2 "{0}" in Tally ID="{1}" since it is ' \ 'not a string or CrossScore'.format(score2, self.id) raise ValueError(msg) @@ -3248,30 +3148,10 @@ class Tallies(cv.CheckedList): """ def __init__(self, tallies=None): - super(Tallies, self).__init__(Tally, 'tallies collection') + super().__init__(Tally, 'tallies collection') if tallies is not None: self += tallies - def add_tally(self, tally, merge=False): - """Append tally to collection - - .. deprecated:: 0.8 - Use :meth:`Tallies.append` instead. - - Parameters - ---------- - tally : openmc.Tally - Tally to add - merge : bool - Indicate whether the tally should be merged with an existing tally, - if possible. Defaults to False. - - """ - warnings.warn("Tallies.add_tally(...) has been deprecated and may be " - "removed in a future version. Use Tallies.append(...) " - "instead.", DeprecationWarning) - self.append(tally, merge) - def append(self, tally, merge=False): """Append tally to collection @@ -3305,10 +3185,10 @@ class Tallies(cv.CheckedList): # If no mergeable tally was found, simply add this tally if not merged: - super(Tallies, self).append(tally) + super().append(tally) else: - super(Tallies, self).append(tally) + super().append(tally) def insert(self, index, item): """Insert tally before index @@ -3321,25 +3201,7 @@ class Tallies(cv.CheckedList): Tally to insert """ - super(Tallies, self).insert(index, item) - - def remove_tally(self, tally): - """Remove a tally from the collection - - .. deprecated:: 0.8 - Use :meth:`Tallies.remove` instead. - - Parameters - ---------- - tally : openmc.Tally - Tally to remove - - """ - warnings.warn("Tallies.remove_tally(...) has been deprecated and may " - "be removed in a future version. Use Tallies.remove(...) " - "instead.", DeprecationWarning) - - self.remove(tally) + super().insert(index, item) def merge_tallies(self): """Merge any mergeable tallies together. Note that n-way merges are @@ -3365,41 +3227,6 @@ class Tallies(cv.CheckedList): # Continue iterating from the first loop break - def add_mesh(self, mesh): - """Add a mesh to the file - - .. deprecated:: 0.8 - Meshes that appear in a tally are automatically added to the - collection. - - Parameters - ---------- - mesh : openmc.Mesh - Mesh to add to the file - - """ - - warnings.warn("Tallies.add_mesh(...) has been deprecated and may be " - "removed in a future version. Meshes that appear in a " - "tally are automatically added to the collection.", - DeprecationWarning) - - def remove_mesh(self, mesh): - """Remove a mesh from the file - - .. deprecated:: 0.8 - Meshes do not need to be managed explicitly. - - Parameters - ---------- - mesh : openmc.Mesh - Mesh to remove from the file - - """ - warnings.warn("Tallies.remove_mesh(...) has been deprecated and may be " - "removed in a future version. Meshes do not need to be " - "managed explicitly.", DeprecationWarning) - def _create_tally_subelements(self, root_element): for tally in self: root_element.append(tally.to_xml_element()) diff --git a/openmc/tally_derivative.py b/openmc/tally_derivative.py index facc56f447..9be748b2cb 100644 --- a/openmc/tally_derivative.py +++ b/openmc/tally_derivative.py @@ -1,11 +1,7 @@ -from __future__ import division - import sys from numbers import Integral from xml.etree import ElementTree as ET -from six import string_types - import openmc.checkvalue as cv from openmc.mixin import EqualityMixin, IDManagerMixin @@ -81,7 +77,7 @@ class TallyDerivative(EqualityMixin, IDManagerMixin): @variable.setter def variable(self, var): if var is not None: - cv.check_type('derivative variable', var, string_types) + cv.check_type('derivative variable', var, str) cv.check_value('derivative variable', var, ('density', 'nuclide_density', 'temperature')) self._variable = var @@ -95,7 +91,7 @@ class TallyDerivative(EqualityMixin, IDManagerMixin): @nuclide.setter def nuclide(self, nuc): if nuc is not None: - cv.check_type('derivative nuclide', nuc, string_types) + cv.check_type('derivative nuclide', nuc, str) self._nuclide = nuc def to_xml_element(self): diff --git a/openmc/trigger.py b/openmc/trigger.py index 69ec8c6200..71f9c0b92b 100644 --- a/openmc/trigger.py +++ b/openmc/trigger.py @@ -2,9 +2,7 @@ from numbers import Real from xml.etree import ElementTree as ET import sys import warnings -from collections import Iterable - -from six import string_types +from collections.abc import Iterable import openmc.checkvalue as cv @@ -76,7 +74,7 @@ class Trigger(object): @scores.setter def scores(self, scores): - cv.check_type('trigger scores', scores, Iterable, string_types) + cv.check_type('trigger scores', scores, Iterable, str) # Set scores making sure not to have duplicates self._scores = [] @@ -84,23 +82,6 @@ class Trigger(object): if score not in self._scores: self._scores.append(score) - - def add_score(self, score): - """Add a score to the list of scores to be checked against the trigger. - - Parameters - ---------- - score : str - Score to append - - """ - - warnings.warn('Trigger.add_score(...) has been deprecated and may be ' - 'removed in a future version. Tally trigger scores should ' - 'be defined using the scores property directly.', - DeprecationWarning) - self.scores.append(score) - def get_trigger_xml(self, element): """Return XML representation of the trigger diff --git a/openmc/universe.py b/openmc/universe.py index 92b152ca09..55f574c536 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -1,11 +1,9 @@ -from __future__ import division from collections import OrderedDict, Iterable from copy import copy, deepcopy from numbers import Integral, Real import random import sys -from six import string_types import matplotlib.pyplot as plt import numpy as np @@ -92,12 +90,12 @@ class Universe(IDManagerMixin): return openmc.Union(regions).bounding_box else: # Infinite bounding box - return openmc.Intersection().bounding_box + return openmc.Intersection([]).bounding_box @name.setter def name(self, name): if name is not None: - cv.check_type('universe name', name, string_types) + cv.check_type('universe name', name, str) self._name = name else: self._name = '' @@ -237,7 +235,7 @@ class Universe(IDManagerMixin): # Convert to RGBA if necessary colors = copy(colors) for obj, color in colors.items(): - if isinstance(color, string_types): + if isinstance(color, str): if color.lower() not in _SVG_COLORS: raise ValueError("'{}' is not a valid color." .format(color)) @@ -322,7 +320,7 @@ class Universe(IDManagerMixin): if not isinstance(cell, openmc.Cell): msg = 'Unable to add a Cell to Universe ID="{0}" since "{1}" is not ' \ 'a Cell'.format(self._id, cell) - raise ValueError(msg) + raise TypeError(msg) cell_id = cell.id @@ -342,7 +340,7 @@ class Universe(IDManagerMixin): if not isinstance(cells, Iterable): msg = 'Unable to add Cells to Universe ID="{0}" since "{1}" is not ' \ 'iterable'.format(self._id, cells) - raise ValueError(msg) + raise TypeError(msg) for cell in cells: self.add_cell(cell) @@ -360,7 +358,7 @@ class Universe(IDManagerMixin): if not isinstance(cell, openmc.Cell): msg = 'Unable to remove a Cell from Universe ID="{0}" since "{1}" is ' \ 'not a Cell'.format(self._id, cell) - raise ValueError(msg) + raise TypeError(msg) # If the Cell is in the Universe's list of Cells, delete it if cell.id in self._cells: diff --git a/openmc/volume.py b/openmc/volume.py index cccb3c6a27..d61093a178 100644 --- a/openmc/volume.py +++ b/openmc/volume.py @@ -1,4 +1,5 @@ -from collections import Iterable, Mapping, OrderedDict +from collections import OrderedDict +from collections.abc import Iterable, Mapping from numbers import Real, Integral from xml.etree import ElementTree as ET import warnings diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000000..cdd367ea9d --- /dev/null +++ b/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +python_files = test*.py +python_classes = NoThanks +filterwarnings = ignore::UserWarning +addopts = -rs diff --git a/readme.rst b/readme.rst index b7366fe3e1..32cbb34e14 100644 --- a/readme.rst +++ b/readme.rst @@ -2,7 +2,7 @@ OpenMC Monte Carlo Particle Transport Code ========================================== -|licensebadge| |travisbadge| +|licensebadge| |travisbadge| |coverallsbadge| The OpenMC project aims to provide a fully-featured Monte Carlo particle transport code based on modern methods. It is a constructive solid geometry, @@ -74,3 +74,7 @@ OpenMC is distributed under the MIT/X license_. .. |travisbadge| image:: https://travis-ci.org/mit-crpg/openmc.svg?branch=develop :target: https://travis-ci.org/mit-crpg/openmc :alt: Travis CI build status (Linux) + +.. |coverallsbadge| image:: https://coveralls.io/repos/github/mit-crpg/openmc/badge.svg?branch=develop + :target: https://coveralls.io/github/mit-crpg/openmc?branch=develop + :alt: Code Coverage diff --git a/scripts/openmc-ace-to-hdf5 b/scripts/openmc-ace-to-hdf5 index 3235a2da63..29c987e07c 100755 --- a/scripts/openmc-ace-to-hdf5 +++ b/scripts/openmc-ace-to-hdf5 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import argparse import os diff --git a/scripts/openmc-convert-mcnp70-data b/scripts/openmc-convert-mcnp70-data index 0489b25de4..75b3b0a5a8 100755 --- a/scripts/openmc-convert-mcnp70-data +++ b/scripts/openmc-convert-mcnp70-data @@ -1,6 +1,5 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -from __future__ import print_function import argparse from collections import defaultdict import glob diff --git a/scripts/openmc-convert-mcnp71-data b/scripts/openmc-convert-mcnp71-data index 061f8f46f0..ce8c427d60 100755 --- a/scripts/openmc-convert-mcnp71-data +++ b/scripts/openmc-convert-mcnp71-data @@ -1,6 +1,5 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -from __future__ import print_function import argparse from collections import defaultdict import glob diff --git a/scripts/openmc-get-jeff-data b/scripts/openmc-get-jeff-data index 6d9e086a91..9f426a4930 100755 --- a/scripts/openmc-get-jeff-data +++ b/scripts/openmc-get-jeff-data @@ -1,6 +1,5 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -from __future__ import print_function import os from collections import defaultdict import sys @@ -9,9 +8,7 @@ import zipfile import glob import argparse from string import digits - -from six.moves import input -from six.moves.urllib.request import urlopen +from urllib.request import urlopen import openmc.data diff --git a/scripts/openmc-get-multipole-data b/scripts/openmc-get-multipole-data index bf0add2840..ea25396488 100755 --- a/scripts/openmc-get-multipole-data +++ b/scripts/openmc-get-multipole-data @@ -1,6 +1,5 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -from __future__ import print_function import os import shutil import subprocess @@ -9,9 +8,7 @@ import tarfile import glob import hashlib import argparse - -from six.moves import input -from six.moves.urllib.request import urlopen +from urllib.request import urlopen description = """ diff --git a/scripts/openmc-get-nndc-data b/scripts/openmc-get-nndc-data index e2c1e7ce3a..a04eed6cd6 100755 --- a/scripts/openmc-get-nndc-data +++ b/scripts/openmc-get-nndc-data @@ -1,6 +1,5 @@ #!/usr/bin/env python -from __future__ import print_function import os import shutil import subprocess @@ -9,9 +8,7 @@ import tarfile import glob import hashlib import argparse - -from six.moves import input -from six.moves.urllib.request import urlopen +from urllib.request import urlopen import openmc.data diff --git a/scripts/openmc-plot-mesh-tally b/scripts/openmc-plot-mesh-tally index c273a6d592..0dfb29b9b3 100755 --- a/scripts/openmc-plot-mesh-tally +++ b/scripts/openmc-plot-mesh-tally @@ -1,16 +1,16 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """Python script to plot tally data generated by OpenMC.""" import os import sys import argparse +import tkinter as tk +import tkinter.filedialog as filedialog +import tkinter.font as font +import tkinter.messagebox as messagebox +import tkinter.ttk as ttk -import six.moves.tkinter as tk -import six.moves.tkinter_filedialog as filedialog -import six.moves.tkinter_font as font -import six.moves.tkinter_messagebox as messagebox -import six.moves.tkinter_ttk as ttk from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.backends.backend_tkagg import NavigationToolbar2TkAgg from matplotlib.figure import Figure diff --git a/scripts/openmc-track-to-vtk b/scripts/openmc-track-to-vtk index 1dd632f9dd..fa154a2a7f 100755 --- a/scripts/openmc-track-to-vtk +++ b/scripts/openmc-track-to-vtk @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """Convert HDF5 particle track to VTK poly data. diff --git a/scripts/openmc-update-inputs b/scripts/openmc-update-inputs index 3eb850207e..ef7cdf47bb 100755 --- a/scripts/openmc-update-inputs +++ b/scripts/openmc-update-inputs @@ -1,10 +1,8 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """Update OpenMC's input XML files to the latest format. """ -from __future__ import print_function - import argparse from difflib import get_close_matches from itertools import chain diff --git a/scripts/openmc-update-mgxs b/scripts/openmc-update-mgxs index f33adfc005..8559affb95 100755 --- a/scripts/openmc-update-mgxs +++ b/scripts/openmc-update-mgxs @@ -1,10 +1,9 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """Update OpenMC's deprecated multi-group cross section XML files to the latest HDF5-based format. """ -from __future__ import print_function import os import warnings import xml.etree.ElementTree as ET diff --git a/scripts/openmc-validate-xml b/scripts/openmc-validate-xml index e5a45f4691..f36ba2b5d3 100755 --- a/scripts/openmc-validate-xml +++ b/scripts/openmc-validate-xml @@ -1,6 +1,4 @@ -#!/usr/bin/env python - -from __future__ import print_function +#!/usr/bin/env python3 import os import sys diff --git a/scripts/openmc-voxel-to-silovtk b/scripts/openmc-voxel-to-silovtk index 9748cc3bba..e0cfc0a5ba 100755 --- a/scripts/openmc-voxel-to-silovtk +++ b/scripts/openmc-voxel-to-silovtk @@ -1,6 +1,5 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -from __future__ import division, print_function import struct import sys from argparse import ArgumentParser diff --git a/setup.py b/setup.py index 338fe5fec8..2a42dd65dd 100755 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ with open('openmc/__init__.py', 'r') as f: kwargs = { 'name': 'openmc', 'version': version, - 'packages': find_packages(), + 'packages': find_packages(exclude=['tests*']), 'scripts': glob.glob('scripts/openmc-*'), # Data files and librarries @@ -48,11 +48,7 @@ kwargs = { 'License :: OSI Approved :: MIT License', 'Natural Language :: English', 'Topic :: Scientific/Engineering' - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.2', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', @@ -60,7 +56,7 @@ kwargs = { # Required dependencies 'install_requires': [ - 'six', 'numpy>=1.9', 'h5py', 'scipy', 'ipython', 'matplotlib', + 'numpy>=1.9', 'h5py', 'scipy', 'ipython', 'matplotlib', 'pandas', 'lxml', 'uncertainties' ], diff --git a/src/api.F90 b/src/api.F90 index eb9a996950..f07e5e38a5 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -18,7 +18,7 @@ module openmc_api use initialize, only: openmc_init use particle_header, only: Particle use plot, only: openmc_plot_geometry - use random_lcg, only: seed, openmc_set_seed + use random_lcg, only: openmc_get_seed, openmc_set_seed use settings use simulation_header use source_header, only: openmc_extend_sources, openmc_source_set_strength @@ -60,6 +60,7 @@ module openmc_api public :: openmc_get_filter_next_id public :: openmc_get_material_index public :: openmc_get_nuclide_index + public :: openmc_get_seed public :: openmc_get_tally_index public :: openmc_global_tallies public :: openmc_hard_reset @@ -79,6 +80,7 @@ module openmc_api public :: openmc_plot_geometry public :: openmc_reset public :: openmc_run + public :: openmc_set_seed public :: openmc_simulation_finalize public :: openmc_simulation_init public :: openmc_source_bank @@ -142,7 +144,7 @@ contains run_CE = .true. run_mode = NONE satisfy_triggers = .false. - seed = 1_8 + call openmc_set_seed(DEFAULT_SEED) source_latest = .false. source_separate = .false. source_write = .true. @@ -171,7 +173,7 @@ contains ! Close FORTRAN interface. call h5close_f(err) -#ifdef MPI +#ifdef OPENMC_MPI ! Free all MPI types call MPI_TYPE_FREE(MPI_BANK, err) #endif @@ -228,8 +230,6 @@ contains !=============================================================================== subroutine openmc_hard_reset() bind(C) - integer :: err - ! Reset all tallies and timers call openmc_reset() @@ -238,7 +238,7 @@ contains total_gen = 0 ! Reset the random number generator state - err = openmc_set_seed(1_8) + call openmc_set_seed(DEFAULT_SEED) end subroutine openmc_hard_reset !=============================================================================== diff --git a/src/cmfd_execute.F90 b/src/cmfd_execute.F90 index 9cdb751a40..af8d57a807 100644 --- a/src/cmfd_execute.F90 +++ b/src/cmfd_execute.F90 @@ -105,7 +105,7 @@ contains real(8) :: hxyz(3) ! cell dimensions of current ijk cell real(8) :: vol ! volume of cell real(8),allocatable :: source(:,:,:,:) ! tmp source array for entropy -#ifdef MPI +#ifdef OPENMC_MPI integer :: mpi_err ! MPI error code #endif @@ -197,7 +197,7 @@ contains end if -#ifdef MPI +#ifdef OPENMC_MPI ! Broadcast full source to all procs call MPI_BCAST(cmfd % cmfd_src, n, MPI_REAL8, 0, mpi_intracomm, mpi_err) #endif @@ -234,7 +234,7 @@ contains real(8) :: norm ! normalization factor logical :: outside ! any source sites outside mesh logical :: in_mesh ! source site is inside mesh -#ifdef MPI +#ifdef OPENMC_MPI integer :: mpi_err #endif @@ -291,7 +291,7 @@ contains if (.not. cmfd_feedback) return ! Broadcast weight factors to all procs -#ifdef MPI +#ifdef OPENMC_MPI call MPI_BCAST(cmfd % weightfactors, ng*nx*ny*nz, MPI_REAL8, 0, & mpi_intracomm, mpi_err) #endif diff --git a/src/constants.F90 b/src/constants.F90 index 331a644f09..3334a4cc76 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -43,9 +43,9 @@ module constants real(8), parameter :: TINY_BIT = 1e-8_8 ! User for precision in geometry - real(8), parameter :: FP_PRECISION = 1e-14_8 - real(8), parameter :: FP_REL_PRECISION = 1e-5_8 - real(8), parameter :: FP_COINCIDENT = 1e-12_8 + real(C_DOUBLE), bind(C, name='FP_PRECISION') :: FP_PRECISION = 1e-14_8 + real(C_DOUBLE), bind(C, name='FP_REL_PRECISION') :: FP_REL_PRECISION = 1e-5_8 + real(C_DOUBLE), bind(C, name='FP_COINCIDENT') :: FP_COINCIDENT = 1e-12_8 ! Maximum number of collisions/crossings integer, parameter :: MAX_EVENTS = 1000000 @@ -95,11 +95,10 @@ module constants ! GEOMETRY-RELATED CONSTANTS ! Boundary conditions - integer, parameter :: & - BC_TRANSMIT = 0, & ! Transmission boundary condition (default) - BC_VACUUM = 1, & ! Vacuum boundary condition - BC_REFLECT = 2, & ! Reflecting boundary condition - BC_PERIODIC = 3 ! Periodic boundary condition + integer(C_INT), bind(C, name="BC_TRANSMIT") :: BC_TRANSMIT + integer(C_INT), bind(C, name="BC_VACUUM") :: BC_VACUUM + integer(C_INT), bind(C, name="BC_REFLECT") :: BC_REFLECT + integer(C_INT), bind(C, name="BC_PERIODIC") :: BC_PERIODIC ! Logical operators for cell definitions integer, parameter :: & diff --git a/src/eigenvalue.F90 b/src/eigenvalue.F90 index 27ae4063ea..7a11d1fb48 100644 --- a/src/eigenvalue.F90 +++ b/src/eigenvalue.F90 @@ -42,11 +42,11 @@ contains type(Bank), save, allocatable :: & & temp_sites(:) ! local array of extra sites on each node -#ifdef MPI +#ifdef OPENMC_MPI integer :: mpi_err ! MPI error code integer(8) :: n ! number of sites to send/recv integer :: neighbor ! processor to send/recv data from -#ifdef MPIF08 +#ifdef OPENMC_MPIF08 type(MPI_Request) :: request(20) #else integer :: request(20) ! communication request for send/recving sites @@ -66,7 +66,7 @@ contains ! fission bank its own sites starts in order to ensure reproducibility by ! skipping ahead to the proper seed. -#ifdef MPI +#ifdef OPENMC_MPI start = 0_8 call MPI_EXSCAN(n_bank, start, 1, MPI_INTEGER8, MPI_SUM, & mpi_intracomm, mpi_err) @@ -148,7 +148,7 @@ contains ! neighboring processors, we have to perform an ALLGATHER to determine the ! indices for all processors -#ifdef MPI +#ifdef OPENMC_MPI ! First do an exclusive scan to get the starting indices for start = 0_8 call MPI_EXSCAN(index_temp, start, 1, MPI_INTEGER8, MPI_SUM, & @@ -191,7 +191,7 @@ contains call time_bank_sample % stop() call time_bank_sendrecv % start() -#ifdef MPI +#ifdef OPENMC_MPI ! ========================================================================== ! SEND BANK SITES TO NEIGHBORS @@ -343,14 +343,14 @@ contains subroutine calculate_generation_keff() real(8) :: keff_reduced -#ifdef MPI +#ifdef OPENMC_MPI integer :: mpi_err ! MPI error code #endif ! Get keff for this generation by subtracting off the starting value keff_generation = global_tallies(RESULT_VALUE, K_TRACKLENGTH) - keff_generation -#ifdef MPI +#ifdef OPENMC_MPI ! Combine values across all processors call MPI_ALLREDUCE(keff_generation, keff_reduced, 1, MPI_REAL8, & MPI_SUM, mpi_intracomm, mpi_err) @@ -584,7 +584,7 @@ contains real(8) :: total ! total weight in source bank logical :: sites_outside ! were there sites outside the ufs mesh? -#ifdef MPI +#ifdef OPENMC_MPI integer :: n ! total number of ufs mesh cells integer :: mpi_err ! MPI error code #endif @@ -608,7 +608,7 @@ contains call fatal_error("Source sites outside of the UFS mesh!") end if -#ifdef MPI +#ifdef OPENMC_MPI ! Send source fraction to all processors n = product(m % dimension) call MPI_BCAST(source_frac, n, MPI_REAL8, 0, mpi_intracomm, mpi_err) diff --git a/src/error.F90 b/src/error.F90 index 27004d729a..0a5af302d6 100644 --- a/src/error.F90 +++ b/src/error.F90 @@ -128,7 +128,7 @@ contains integer :: line_wrap ! length of line integer :: length ! length of message integer :: indent ! length of indentation -#ifdef MPI +#ifdef OPENMC_MPI integer :: mpi_err #endif @@ -180,7 +180,7 @@ contains end if end do -#ifdef MPI +#ifdef OPENMC_MPI ! Abort MPI call MPI_ABORT(mpi_intracomm, code, mpi_err) #endif @@ -194,6 +194,14 @@ contains end subroutine fatal_error + subroutine fatal_error_from_c(message, message_len) bind(C) + integer(C_INT), intent(in), value :: message_len + character(kind=C_CHAR), intent(in) :: message(message_len) + character(message_len+1) :: message_out + write(message_out, *) message + call fatal_error(message_out) + end subroutine + !=============================================================================== ! WRITE_MESSAGE displays an informational message to the log file and the ! standard output stream. diff --git a/src/error.h b/src/error.h new file mode 100644 index 0000000000..4c3373b3e3 --- /dev/null +++ b/src/error.h @@ -0,0 +1,34 @@ +#ifndef ERROR_H +#define ERROR_H + +#include +#include +#include + + +namespace openmc { + + +extern "C" void fatal_error_from_c(const char *message, int message_len); + + +void fatal_error(const char *message) +{ + fatal_error_from_c(message, strlen(message)); +} + + +void fatal_error(const std::string &message) +{ + fatal_error_from_c(message.c_str(), message.length()); +} + + +void fatal_error(const std::stringstream &message) +{ + std::string out {message.str()}; + fatal_error_from_c(out.c_str(), out.length()); +} + +} // namespace openmc +#endif // ERROR_H diff --git a/src/geometry.F90 b/src/geometry.F90 index ced62e6813..0bacd7b5d1 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -10,6 +10,8 @@ module geometry use stl_vector, only: VectorInt use string, only: to_str + use, intrinsic :: ISO_C_BINDING + implicit none contains @@ -63,7 +65,7 @@ contains in_cell = .false. exit else - actual_sense = surfaces(abs(token))%obj%sense(& + actual_sense = surfaces(abs(token)) % sense( & p%coord(p%n_coord)%xyz, p%coord(p%n_coord)%uvw) if (actual_sense .neqv. (token > 0)) then in_cell = .false. @@ -112,7 +114,7 @@ contains elseif (-token == p%surface) then stack(i_stack) = .false. else - actual_sense = surfaces(abs(token))%obj%sense(& + actual_sense = surfaces(abs(token)) % sense( & p%coord(p%n_coord)%xyz, p%coord(p%n_coord)%uvw) stack(i_stack) = (actual_sense .eqv. (token > 0)) end if @@ -480,9 +482,9 @@ contains real(8) :: d_surf ! distance to surface real(8) :: x0,y0,z0 ! coefficients for surface real(8) :: xyz_cross(3) ! coordinates at projected surface crossing + real(8) :: surf_uvw(3) ! surface normal direction logical :: coincident ! is particle on surface? type(Cell), pointer :: c - class(Surface), pointer :: surf class(Lattice), pointer :: lat ! inialize distance to infinity (huge) @@ -517,8 +519,8 @@ contains if (index_surf >= OP_UNION) cycle ! Calculate distance to surface - surf => surfaces(index_surf) % obj - d = surf % distance(p % coord(j) % xyz, p % coord(j) % uvw, coincident) + d = surfaces(index_surf) % distance(p % coord(j) % xyz, & + p % coord(j) % uvw, logical(coincident, kind=C_BOOL)) ! Check if calculated distance is new minimum if (d < d_surf) then @@ -737,9 +739,8 @@ contains ! traveling into if the surface is crossed if (.not. c % simple) then xyz_cross(:) = p % coord(j) % xyz + d_surf*p % coord(j) % uvw - surf => surfaces(abs(level_surf_cross)) % obj - if (dot_product(p % coord(j) % uvw, & - surf % normal(xyz_cross)) > ZERO) then + call surfaces(abs(level_surf_cross)) % normal(xyz_cross, surf_uvw) + if (dot_product(p % coord(j) % uvw, surf_uvw) > ZERO) then surface_crossed = abs(level_surf_cross) else surface_crossed = -abs(level_surf_cross) @@ -804,15 +805,15 @@ contains ! Copy positive neighbors to Surface instance n = neighbor_pos(i)%size() if (n > 0) then - allocate(surfaces(i)%obj%neighbor_pos(n)) - surfaces(i)%obj%neighbor_pos(:) = neighbor_pos(i)%data(1:n) + allocate(surfaces(i)%neighbor_pos(n)) + surfaces(i)%neighbor_pos(:) = neighbor_pos(i)%data(1:n) end if ! Copy negative neighbors to Surface instance n = neighbor_neg(i)%size() if (n > 0) then - allocate(surfaces(i)%obj%neighbor_neg(n)) - surfaces(i)%obj%neighbor_neg(:) = neighbor_neg(i)%data(1:n) + allocate(surfaces(i)%neighbor_neg(n)) + surfaces(i)%neighbor_neg(:) = neighbor_neg(i)%data(1:n) end if end do diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index e8c39b923c..410149d9e7 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -124,7 +124,7 @@ contains ! Setup file access property list with parallel I/O access call h5pcreate_f(H5P_FILE_ACCESS_F, plist, hdf5_err) #ifdef PHDF5 -#ifdef MPIF08 +#ifdef OPENMC_MPIF08 call h5pset_fapl_mpio_f(plist, mpi_intracomm%MPI_VAL, & MPI_INFO_NULL%MPI_VAL, hdf5_err) #else @@ -174,7 +174,7 @@ contains ! Setup file access property list with parallel I/O access call h5pcreate_f(H5P_FILE_ACCESS_F, plist, hdf5_err) #ifdef PHDF5 -#ifdef MPIF08 +#ifdef OPENMC_MPIF08 call h5pset_fapl_mpio_f(plist, mpi_intracomm%MPI_VAL, & MPI_INFO_NULL%MPI_VAL, hdf5_err) #else diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h new file mode 100644 index 0000000000..7ec9ada9b6 --- /dev/null +++ b/src/hdf5_interface.h @@ -0,0 +1,91 @@ +#ifndef HDF5_INTERFACE_H +#define HDF5_INTERFACE_H + +#include +#include + +#include "hdf5.h" + +#include "error.h" + + +namespace openmc { + + +hid_t +create_group(hid_t parent_id, char const *name) +{ + hid_t out = H5Gcreate(parent_id, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (out < 0) { + std::string err_msg{"Failed to create HDF5 group \""}; + err_msg += name; + err_msg += "\""; + fatal_error(err_msg); + } + return out; +} + + +hid_t +create_group(hid_t parent_id, const std::string &name) +{ + return create_group(parent_id, name.c_str()); +} + + +void +close_group(hid_t group_id) +{ + herr_t err = H5Gclose(group_id); + if (err < 0) { + fatal_error("Failed to close HDF5 group"); + } +} + + +template void +write_double_1D(hid_t group_id, char const *name, + std::array &buffer) +{ + hsize_t dims[1]{array_len}; + hid_t dataspace = H5Screate_simple(1, dims, NULL); + + hid_t dataset = H5Dcreate(group_id, name, H5T_NATIVE_DOUBLE, dataspace, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + H5Dwrite(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, + &buffer[0]); + + H5Sclose(dataspace); + H5Dclose(dataset); +} + + +void +write_string(hid_t group_id, char const *name, char const *buffer) +{ + size_t buffer_len = strlen(buffer); + hid_t datatype = H5Tcopy(H5T_C_S1); + H5Tset_size(datatype, buffer_len); + + hid_t dataspace = H5Screate(H5S_SCALAR); + + hid_t dataset = H5Dcreate(group_id, name, datatype, dataspace, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + H5Dwrite(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); + + H5Tclose(datatype); + H5Sclose(dataspace); + H5Dclose(dataset); +} + + +void +write_string(hid_t group_id, char const *name, const std::string &buffer) +{ + write_string(group_id, name, buffer.c_str()); +} + +} // namespace openmc +#endif //HDF5_INTERFACE_H diff --git a/src/initialize.F90 b/src/initialize.F90 index dd7894f96b..7b699b9cb7 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -44,7 +44,6 @@ contains subroutine openmc_init(intracomm) bind(C) integer, intent(in), optional :: intracomm ! MPI intracommunicator - integer :: err #ifdef _OPENMP character(MAX_WORD_LEN) :: envvar #endif @@ -52,8 +51,8 @@ contains ! Copy the communicator to a new variable. This is done to avoid changing ! the signature of this subroutine. If MPI is being used but no communicator ! was passed, assume MPI_COMM_WORLD. -#ifdef MPI -#ifdef MPIF08 +#ifdef OPENMC_MPI +#ifdef OPENMC_MPIF08 type(MPI_Comm), intent(in) :: comm ! MPI intracommunicator if (present(intracomm)) then comm % MPI_VAL = intracomm @@ -74,7 +73,7 @@ contains call time_total%start() call time_initialize%start() -#ifdef MPI +#ifdef OPENMC_MPI ! Setup MPI call initialize_mpi(comm) #endif @@ -95,7 +94,7 @@ contains ! Initialize random number generator -- if the user specifies a seed, it ! will be re-initialized later - err = openmc_set_seed(DEFAULT_SEED) + call openmc_set_seed(DEFAULT_SEED) ! Read XML input files call read_input_xml() @@ -108,7 +107,7 @@ contains end subroutine openmc_init -#ifdef MPI +#ifdef OPENMC_MPI !=============================================================================== ! INITIALIZE_MPI starts up the Message Passing Interface (MPI) and determines ! the number of processors the problem is being run with as well as the rank of @@ -116,7 +115,7 @@ contains !=============================================================================== subroutine initialize_mpi(intracomm) -#ifdef MPIF08 +#ifdef OPENMC_MPIF08 type(MPI_Comm), intent(in) :: intracomm ! MPI intracommunicator #else integer, intent(in) :: intracomm ! MPI intracommunicator @@ -124,7 +123,7 @@ contains integer :: mpi_err ! MPI error code integer :: bank_blocks(5) ! Count for each datatype -#ifdef MPIF08 +#ifdef OPENMC_MPIF08 type(MPI_Datatype) :: bank_types(5) #else integer :: bank_types(5) ! Datatypes diff --git a/src/input_xml.F90 b/src/input_xml.F90 index bd56422cb5..0b7790a51c 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -48,6 +48,14 @@ module input_xml implicit none save + interface + subroutine read_surfaces(node_ptr) bind(C, name='read_surfaces') + use ISO_C_BINDING + implicit none + type(C_PTR) :: node_ptr + end subroutine read_surfaces + end interface + contains !=============================================================================== @@ -341,7 +349,7 @@ contains ! Copy random number seed if specified if (check_for_node(root, "seed")) then call get_node_value(root, "seed", seed) - err = openmc_set_seed(seed) + call openmc_set_seed(seed) end if ! Number of bins for logarithmic grid @@ -911,28 +919,20 @@ contains integer :: id integer :: univ_id integer :: n_cells_in_univ - integer :: coeffs_reqd - integer :: i_xmin, i_xmax, i_ymin, i_ymax, i_zmin, i_zmax - real(8) :: xmin, xmax, ymin, ymax, zmin, zmax integer, allocatable :: temp_int_array(:) real(8) :: phi, theta, psi - real(8), allocatable :: coeffs(:) logical :: file_exists logical :: boundary_exists character(MAX_LINE_LEN) :: filename - character(MAX_WORD_LEN) :: word character(MAX_WORD_LEN), allocatable :: sarray(:) character(:), allocatable :: region_spec type(Cell), pointer :: c - class(Surface), pointer :: s class(Lattice), pointer :: lat type(XMLDocument) :: doc type(XMLNode) :: root type(XMLNode) :: node_cell - type(XMLNode) :: node_surf type(XMLNode) :: node_lat type(XMLNode), allocatable :: node_cell_list(:) - type(XMLNode), allocatable :: node_surf_list(:) type(XMLNode), allocatable :: node_rlat_list(:) type(XMLNode), allocatable :: node_hlat_list(:) type(VectorInt) :: tokens @@ -964,218 +964,18 @@ contains ! applied to a surface boundary_exists = .false. - ! get pointer to list of xml - call get_node_list(root, "surface", node_surf_list) + call read_surfaces(root % ptr) - ! Get number of tags - n_surfaces = size(node_surf_list) - - ! Check for no surfaces - if (n_surfaces == 0) then - call fatal_error("No surfaces found in geometry.xml!") - end if - - xmin = INFINITY - xmax = -INFINITY - ymin = INFINITY - ymax = -INFINITY - zmin = INFINITY - zmax = -INFINITY - - ! Allocate cells array + ! Allocate surfaces array allocate(surfaces(n_surfaces)) do i = 1, n_surfaces - ! Get pointer to i-th surface node - node_surf = node_surf_list(i) + surfaces(i) % ptr = surface_pointer_c(i - 1); - ! Copy and interpret surface type - word = '' - if (check_for_node(node_surf, "type")) & - call get_node_value(node_surf, "type", word) - select case(to_lower(word)) - case ('x-plane') - coeffs_reqd = 1 - allocate(SurfaceXPlane :: surfaces(i)%obj) - case ('y-plane') - coeffs_reqd = 1 - allocate(SurfaceYPlane :: surfaces(i)%obj) - case ('z-plane') - coeffs_reqd = 1 - allocate(SurfaceZPlane :: surfaces(i)%obj) - case ('plane') - coeffs_reqd = 4 - allocate(SurfacePlane :: surfaces(i)%obj) - case ('x-cylinder') - coeffs_reqd = 3 - allocate(SurfaceXCylinder :: surfaces(i)%obj) - case ('y-cylinder') - coeffs_reqd = 3 - allocate(SurfaceYCylinder :: surfaces(i)%obj) - case ('z-cylinder') - coeffs_reqd = 3 - allocate(SurfaceZCylinder :: surfaces(i)%obj) - case ('sphere') - coeffs_reqd = 4 - allocate(SurfaceSphere :: surfaces(i)%obj) - case ('x-cone') - coeffs_reqd = 4 - allocate(SurfaceXCone :: surfaces(i)%obj) - case ('y-cone') - coeffs_reqd = 4 - allocate(SurfaceYCone :: surfaces(i)%obj) - case ('z-cone') - coeffs_reqd = 4 - allocate(SurfaceZCone :: surfaces(i)%obj) - case ('quadric') - coeffs_reqd = 10 - allocate(SurfaceQuadric :: surfaces(i)%obj) - case default - call fatal_error("Invalid surface type: " // trim(word)) - end select + if (surfaces(i) % bc() /= BC_TRANSMIT) boundary_exists = .true. - s => surfaces(i)%obj - - ! Copy data into cells - if (check_for_node(node_surf, "id")) then - call get_node_value(node_surf, "id", s%id) - else - call fatal_error("Must specify id of surface in geometry XML file.") - end if - - ! Check to make sure 'id' hasn't been used - if (surface_dict % has(s%id)) then - call fatal_error("Two or more surfaces use the same unique ID: " & - // to_str(s%id)) - end if - - ! Copy surface name - if (check_for_node(node_surf, "name")) then - call get_node_value(node_surf, "name", s%name) - end if - - ! Check to make sure that the proper number of coefficients - ! have been specified for the given type of surface. Then copy - ! surface coordinates. - - n = node_word_count(node_surf, "coeffs") - if (n < coeffs_reqd) then - call fatal_error("Not enough coefficients specified for surface: " & - // trim(to_str(s%id))) - elseif (n > coeffs_reqd) then - call fatal_error("Too many coefficients specified for surface: " & - // trim(to_str(s%id))) - end if - - allocate(coeffs(n)) - call get_node_array(node_surf, "coeffs", coeffs) - - select type(s) - type is (SurfaceXPlane) - s%x0 = coeffs(1) - - ! Determine outer surfaces - xmin = min(xmin, s % x0) - xmax = max(xmax, s % x0) - if (xmin == s % x0) i_xmin = i - if (xmax == s % x0) i_xmax = i - type is (SurfaceYPlane) - s%y0 = coeffs(1) - - ! Determine outer surfaces - ymin = min(ymin, s % y0) - ymax = max(ymax, s % y0) - if (ymin == s % y0) i_ymin = i - if (ymax == s % y0) i_ymax = i - type is (SurfaceZPlane) - s%z0 = coeffs(1) - - ! Determine outer surfaces - zmin = min(zmin, s % z0) - zmax = max(zmax, s % z0) - if (zmin == s % z0) i_zmin = i - if (zmax == s % z0) i_zmax = i - type is (SurfacePlane) - s%A = coeffs(1) - s%B = coeffs(2) - s%C = coeffs(3) - s%D = coeffs(4) - type is (SurfaceXCylinder) - s%y0 = coeffs(1) - s%z0 = coeffs(2) - s%r = coeffs(3) - type is (SurfaceYCylinder) - s%x0 = coeffs(1) - s%z0 = coeffs(2) - s%r = coeffs(3) - type is (SurfaceZCylinder) - s%x0 = coeffs(1) - s%y0 = coeffs(2) - s%r = coeffs(3) - type is (SurfaceSphere) - s%x0 = coeffs(1) - s%y0 = coeffs(2) - s%z0 = coeffs(3) - s%r = coeffs(4) - type is (SurfaceXCone) - s%x0 = coeffs(1) - s%y0 = coeffs(2) - s%z0 = coeffs(3) - s%r2 = coeffs(4) - type is (SurfaceYCone) - s%x0 = coeffs(1) - s%y0 = coeffs(2) - s%z0 = coeffs(3) - s%r2 = coeffs(4) - type is (SurfaceZCone) - s%x0 = coeffs(1) - s%y0 = coeffs(2) - s%z0 = coeffs(3) - s%r2 = coeffs(4) - type is (SurfaceQuadric) - s%A = coeffs(1) - s%B = coeffs(2) - s%C = coeffs(3) - s%D = coeffs(4) - s%E = coeffs(5) - s%F = coeffs(6) - s%G = coeffs(7) - s%H = coeffs(8) - s%J = coeffs(9) - s%K = coeffs(10) - end select - - ! No longer need coefficients - deallocate(coeffs) - - ! Boundary conditions - word = '' - if (check_for_node(node_surf, "boundary")) & - call get_node_value(node_surf, "boundary", word) - select case (to_lower(word)) - case ('transmission', 'transmit', '') - s%bc = BC_TRANSMIT - case ('vacuum') - s%bc = BC_VACUUM - boundary_exists = .true. - case ('reflective', 'reflect', 'reflecting') - s%bc = BC_REFLECT - boundary_exists = .true. - case ('periodic') - s%bc = BC_PERIODIC - boundary_exists = .true. - - ! Check for specification of periodic surface - if (check_for_node(node_surf, "periodic_surface_id")) then - call get_node_value(node_surf, "periodic_surface_id", & - s % i_periodic) - end if - case default - call fatal_error("Unknown boundary condition '" // trim(word) // & - &"' specified on surface " // trim(to_str(s%id))) - end select ! Add surface to dictionary - call surface_dict % set(s%id, i) + call surface_dict % set(surfaces(i) % id(), i) end do ! Check to make sure a boundary condition was applied to at least one @@ -1186,76 +986,6 @@ contains end if end if - ! Determine opposite side for periodic boundaries - do i = 1, size(surfaces) - if (surfaces(i) % obj % bc == BC_PERIODIC) then - select type (surf => surfaces(i) % obj) - type is (SurfaceXPlane) - if (surf % i_periodic == NONE) then - if (i == i_xmin) then - surf % i_periodic = i_xmax - elseif (i == i_xmax) then - surf % i_periodic = i_xmin - else - call fatal_error("Periodic boundary condition applied to & - &interior surface.") - end if - else - surf % i_periodic = surface_dict % get(surf % i_periodic) - end if - - type is (SurfaceYPlane) - if (surf % i_periodic == NONE) then - if (i == i_ymin) then - surf % i_periodic = i_ymax - elseif (i == i_ymax) then - surf % i_periodic = i_ymin - else - call fatal_error("Periodic boundary condition applied to & - &interior surface.") - end if - else - surf % i_periodic = surface_dict % get(surf % i_periodic) - end if - - type is (SurfaceZPlane) - if (surf % i_periodic == NONE) then - if (i == i_zmin) then - surf % i_periodic = i_zmax - elseif (i == i_zmax) then - surf % i_periodic = i_zmin - else - call fatal_error("Periodic boundary condition applied to & - &interior surface.") - end if - else - surf % i_periodic = surface_dict % get(surf % i_periodic) - end if - - type is (SurfacePlane) - if (surf % i_periodic == NONE) then - call fatal_error("No matching periodic surface specified for & - &periodic boundary condition on surface " // & - trim(to_str(surf % id)) // ".") - else - surf % i_periodic = surface_dict % get(surf % i_periodic) - end if - - class default - call fatal_error("Periodic boundary condition applied to & - &non-planar surface.") - end select - - ! Make sure opposite surface is also periodic - associate (surf => surfaces(i) % obj) - if (surfaces(surf % i_periodic) % obj % bc /= BC_PERIODIC) then - call fatal_error("Could not find matching surface for periodic & - &boundary on surface " // trim(to_str(surf % id)) // ".") - end if - end associate - end if - end do - ! ========================================================================== ! READ CELLS FROM GEOMETRY.XML diff --git a/src/main.F90 b/src/main.F90 index d8e52643c1..120bdd1e0c 100644 --- a/src/main.F90 +++ b/src/main.F90 @@ -9,13 +9,13 @@ program main implicit none -#ifdef MPI +#ifdef OPENMC_MPI integer :: mpi_err ! MPI error code #endif ! Initialize run -- when run with MPI, pass communicator -#ifdef MPI -#ifdef MPIF08 +#ifdef OPENMC_MPI +#ifdef OPENMC_MPIF08 call openmc_init(MPI_COMM_WORLD % MPI_VAL) #else call openmc_init(MPI_COMM_WORLD) @@ -39,7 +39,7 @@ program main ! finalize run call openmc_finalize() -#ifdef MPI +#ifdef OPENMC_MPI ! If MPI is in use and enabled, terminate it call MPI_FINALIZE(mpi_err) #endif diff --git a/src/mesh.F90 b/src/mesh.F90 index e997890a4a..790dc7d889 100644 --- a/src/mesh.F90 +++ b/src/mesh.F90 @@ -34,7 +34,7 @@ contains integer :: n ! number of energy groups / size integer :: mesh_bin ! mesh bin integer :: e_bin ! energy bin -#ifdef MPI +#ifdef OPENMC_MPI integer :: mpi_err ! MPI error code #endif logical :: outside ! was any site outside mesh? @@ -86,7 +86,7 @@ contains cnt_(e_bin, mesh_bin) = cnt_(e_bin, mesh_bin) + bank_array(i) % wgt end do FISSION_SITES -#ifdef MPI +#ifdef OPENMC_MPI ! collect values from all processors n = size(cnt_) call MPI_REDUCE(cnt_, cnt, n, MPI_REAL8, MPI_SUM, 0, mpi_intracomm, mpi_err) diff --git a/src/message_passing.F90 b/src/message_passing.F90 index 0f2b94d1af..7391a632c7 100644 --- a/src/message_passing.F90 +++ b/src/message_passing.F90 @@ -1,7 +1,7 @@ module message_passing -#ifdef MPI -#ifdef MPIF08 +#ifdef OPENMC_MPI +#ifdef OPENMC_MPIF08 use mpi_f08 #else use mpi @@ -16,7 +16,7 @@ module message_passing integer :: rank = 0 ! rank of process logical :: master = .true. ! master process? logical :: mpi_enabled = .false. ! is MPI in use and initialized? -#ifdef MPIF08 +#ifdef OPENMC_MPIF08 type(MPI_Datatype) :: MPI_BANK ! MPI datatype for fission bank type(MPI_Comm) :: mpi_intracomm ! MPI intra-communicator #else diff --git a/src/output.F90 b/src/output.F90 index 9a144721dc..316fe95941 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -88,7 +88,7 @@ contains ! Write the date and time write(UNIT=OUTPUT_UNIT, FMT='(9X,"Date/Time | ",A)') time_stamp() -#ifdef MPI +#ifdef OPENMC_MPI ! Write number of processors write(UNIT=OUTPUT_UNIT, FMT='(5X,"MPI Processes | ",A)') & trim(to_str(n_procs)) @@ -259,7 +259,7 @@ contains ! Print surface if (p % surface /= NONE) then - write(ou,*) ' Surface = ' // to_str(sign(surfaces(i)%obj%id, p % surface)) + write(ou,*) ' Surface = ' // to_str(sign(surfaces(i)%id(), p % surface)) end if ! Display weight, energy, grid index, and interpolation factor diff --git a/src/random_lcg.F90 b/src/random_lcg.F90 index 839bd729a9..d64717cf42 100644 --- a/src/random_lcg.F90 +++ b/src/random_lcg.F90 @@ -4,8 +4,6 @@ module random_lcg implicit none - integer(C_INT64_T), bind(C) :: seed - interface function prn() result(pseudo_rn) bind(C) use ISO_C_BINDING @@ -38,11 +36,16 @@ module random_lcg integer(C_INT), value :: n end subroutine prn_set_stream - function openmc_set_seed(new_seed) result(err) bind(C) + function openmc_get_seed() result(seed) bind(C) + use ISO_C_BINDING + implicit none + integer(C_INT64_T) :: seed + end function openmc_get_seed + + subroutine openmc_set_seed(new_seed) bind(C) use ISO_C_BINDING implicit none integer(C_INT64_T), value :: new_seed - integer(C_INT) :: err - end function openmc_set_seed + end subroutine openmc_set_seed end interface end module random_lcg diff --git a/src/random_lcg.cpp b/src/random_lcg.cpp index 047bda8e54..2a57316cb6 100644 --- a/src/random_lcg.cpp +++ b/src/random_lcg.cpp @@ -2,6 +2,9 @@ #include +namespace openmc { + + // Constants extern "C" const int N_STREAMS {5}; extern "C" const int STREAM_TRACKING {0}; @@ -130,7 +133,9 @@ prn_set_stream(int i) // API FUNCTIONS //============================================================================== -extern "C" int +extern "C" int64_t openmc_get_seed() {return seed;} + +extern "C" void openmc_set_seed(int64_t new_seed) { seed = new_seed; @@ -141,5 +146,6 @@ openmc_set_seed(int64_t new_seed) } prn_set_stream(STREAM_TRACKING); } - return 0; } + +} // namespace openmc diff --git a/src/random_lcg.h b/src/random_lcg.h index 04191254f3..29b3fca47d 100644 --- a/src/random_lcg.h +++ b/src/random_lcg.h @@ -3,6 +3,9 @@ #include + +namespace openmc { + //============================================================================== // Module constants. //============================================================================== @@ -74,12 +77,19 @@ extern "C" void prn_set_stream(int n); // API FUNCTIONS //============================================================================== +//============================================================================== +//! Get OpenMC's master seed. +//============================================================================== + +extern "C" int64_t openmc_get_seed(); + //============================================================================== //! Set OpenMC's master seed. //! @param new_seed The master seed. All other seeds will be derived from this //! one. //============================================================================== -extern "C" int openmc_set_seed(int64_t new_seed); +extern "C" void openmc_set_seed(int64_t new_seed); +} // namespace openmc #endif // RANDOM_LCG_H diff --git a/src/simulation.F90 b/src/simulation.F90 index ef29e7832b..424c55e9bd 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -320,7 +320,7 @@ contains subroutine finalize_batch() -#ifdef MPI +#ifdef OPENMC_MPI integer :: mpi_err ! MPI error code #endif @@ -342,7 +342,7 @@ contains ! Check_triggers if (master) call check_triggers() -#ifdef MPI +#ifdef OPENMC_MPI call MPI_BCAST(satisfy_triggers, 1, MPI_LOGICAL, 0, & mpi_intracomm, mpi_err) #endif @@ -469,11 +469,17 @@ contains subroutine openmc_simulation_finalize() bind(C) integer :: i ! loop index -#ifdef MPI +#ifdef OPENMC_MPI integer :: n ! size of arrays integer :: mpi_err ! MPI error code + integer :: count_per_filter ! number of result values for one filter bin integer(8) :: temp real(8) :: tempr(3) ! temporary array for communication +#ifdef OPENMC_MPIF08 + type(MPI_Datatype) :: result_block +#else + integer :: result_block +#endif #endif ! Skip if simulation was never run @@ -494,13 +500,22 @@ contains ! Increment total number of generations total_gen = total_gen + current_batch*gen_per_batch -#ifdef MPI +#ifdef OPENMC_MPI ! Broadcast tally results so that each process has access to results if (allocated(tallies)) then do i = 1, size(tallies) - n = size(tallies(i) % obj % results) - call MPI_BCAST(tallies(i) % obj % results, n, MPI_DOUBLE, 0, & - mpi_intracomm, mpi_err) + associate (results => tallies(i) % obj % results) + ! Create a new datatype that consists of all values for a given filter + ! bin and then use that to broadcast. This is done to minimize the + ! chance of the 'count' argument of MPI_BCAST exceeding 2**31 + n = size(results, 3) + count_per_filter = size(results, 1) * size(results, 2) + call MPI_TYPE_CONTIGUOUS(count_per_filter, MPI_DOUBLE, & + result_block, mpi_err) + call MPI_TYPE_COMMIT(result_block, mpi_err) + call MPI_BCAST(results, n, result_block, 0, mpi_intracomm, mpi_err) + call MPI_TYPE_FREE(result_block, mpi_err) + end associate end do end if diff --git a/src/source.F90 b/src/source.F90 index 557120e979..5beecd8870 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -1,7 +1,7 @@ module source use hdf5, only: HID_T -#ifdef MPI +#ifdef OPENMC_MPI use message_passing #endif diff --git a/src/state_point.F90 b/src/state_point.F90 index f7a8928987..980a7333cd 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -26,7 +26,7 @@ module state_point use mgxs_header, only: nuclides_MG use nuclide_header, only: nuclides use output, only: time_stamp - use random_lcg, only: seed + use random_lcg, only: openmc_get_seed, openmc_set_seed use settings use simulation_header use string, only: to_str, count_digits, zero_padded, to_f_string @@ -97,7 +97,7 @@ contains call write_attribute(file_id, "path", path_input) ! Write out random number seed - call write_dataset(file_id, "seed", seed) + call write_dataset(file_id, "seed", openmc_get_seed()) ! Write run information if (run_CE) then @@ -523,7 +523,7 @@ contains integer(HID_T) :: tallies_group, tally_group real(8), allocatable :: tally_temp(:,:,:) ! contiguous array of results real(8), target :: global_temp(3,N_GLOBAL_TALLIES) -#ifdef MPI +#ifdef OPENMC_MPI integer :: mpi_err ! MPI error code real(8) :: dummy ! temporary receive buffer for non-root reduces #endif @@ -543,7 +543,7 @@ contains end if -#ifdef MPI +#ifdef OPENMC_MPI ! Reduce global tallies n_bins = size(global_tallies) call MPI_REDUCE(global_tallies, global_temp, n_bins, MPI_REAL8, MPI_SUM, & @@ -586,7 +586,7 @@ contains ! The MPI_IN_PLACE specifier allows the master to copy values into ! a receive buffer without having a temporary variable -#ifdef MPI +#ifdef OPENMC_MPI call MPI_REDUCE(MPI_IN_PLACE, tally_temp, n_bins, MPI_REAL8, & MPI_SUM, 0, mpi_intracomm, mpi_err) #endif @@ -610,7 +610,7 @@ contains deallocate(dummy_tally % results) else ! Receive buffer not significant at other processors -#ifdef MPI +#ifdef OPENMC_MPI call MPI_REDUCE(tally_temp, dummy, n_bins, MPI_REAL8, MPI_SUM, & 0, mpi_intracomm, mpi_err) #endif @@ -642,6 +642,7 @@ contains integer :: n integer :: int_array(3) integer, allocatable :: array(:) + integer(C_INT64_T) :: seed integer(HID_T) :: file_id integer(HID_T) :: cmfd_group integer(HID_T) :: tallies_group @@ -672,6 +673,7 @@ contains ! Read and overwrite random number seed call read_dataset(seed, file_id, "seed") + call openmc_set_seed(seed) ! It is not impossible for a state point to be generated from a CE run but ! to be loaded in to an MG run (or vice versa), check to prevent that. @@ -849,7 +851,7 @@ contains integer(HID_T) :: plist ! property list #else integer :: i -#ifdef MPI +#ifdef OPENMC_MPI integer :: mpi_err ! MPI error code type(Bank), allocatable, target :: temp_source(:) #endif @@ -897,7 +899,7 @@ contains dspace, dset, hdf5_err) ! Save source bank sites since the souce_bank array is overwritten below -#ifdef MPI +#ifdef OPENMC_MPI allocate(temp_source(work)) temp_source(:) = source_bank(:) #endif @@ -907,7 +909,7 @@ contains dims(1) = work_index(i+1) - work_index(i) call h5screate_simple_f(1, dims, memspace, hdf5_err) -#ifdef MPI +#ifdef OPENMC_MPI ! Receive source sites from other processes if (i > 0) then call MPI_RECV(source_bank, int(dims(1)), MPI_BANK, i, i, & @@ -933,12 +935,12 @@ contains call h5dclose_f(dset, hdf5_err) ! Restore state of source bank -#ifdef MPI +#ifdef OPENMC_MPI source_bank(:) = temp_source(:) deallocate(temp_source) #endif else -#ifdef MPI +#ifdef OPENMC_MPI call MPI_SEND(source_bank, int(work), MPI_BANK, 0, rank, & mpi_intracomm, mpi_err) #endif diff --git a/src/summary.F90 b/src/summary.F90 index 0f45ef1bbd..3aeb42178b 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -122,13 +122,11 @@ contains real(8), allocatable :: cell_temperatures(:) integer(HID_T) :: geom_group integer(HID_T) :: cells_group, cell_group - integer(HID_T) :: surfaces_group, surface_group + integer(HID_T) :: surfaces_group integer(HID_T) :: universes_group, univ_group integer(HID_T) :: lattices_group, lattice_group - real(8), allocatable :: coeffs(:) character(:), allocatable :: region_spec type(Cell), pointer :: c - class(Surface), pointer :: s class(Lattice), pointer :: lat ! Use H5LT interface to write number of geometry objects @@ -223,7 +221,7 @@ contains region_spec = trim(region_spec) // " |" case default region_spec = trim(region_spec) // " " // to_str(& - sign(surfaces(abs(k))%obj%id, k)) + sign(surfaces(abs(k))%id(), k)) end select end do call write_dataset(cell_group, "region", adjustl(region_spec)) @@ -241,92 +239,7 @@ contains ! Write information on each surface SURFACE_LOOP: do i = 1, n_surfaces - s => surfaces(i)%obj - surface_group = create_group(surfaces_group, "surface " // & - trim(to_str(s%id))) - - ! Write name for this surface - call write_dataset(surface_group, "name", s%name) - - ! Write surface type - select type (s) - type is (SurfaceXPlane) - call write_dataset(surface_group, "type", "x-plane") - allocate(coeffs(1)) - coeffs(1) = s%x0 - - type is (SurfaceYPlane) - call write_dataset(surface_group, "type", "y-plane") - allocate(coeffs(1)) - coeffs(1) = s%y0 - - type is (SurfaceZPlane) - call write_dataset(surface_group, "type", "z-plane") - allocate(coeffs(1)) - coeffs(1) = s%z0 - - type is (SurfacePlane) - call write_dataset(surface_group, "type", "plane") - allocate(coeffs(4)) - coeffs(:) = [s%A, s%B, s%C, s%D] - - type is (SurfaceXCylinder) - call write_dataset(surface_group, "type", "x-cylinder") - allocate(coeffs(3)) - coeffs(:) = [s%y0, s%z0, s%r] - - type is (SurfaceYCylinder) - call write_dataset(surface_group, "type", "y-cylinder") - allocate(coeffs(3)) - coeffs(:) = [s%x0, s%z0, s%r] - - type is (SurfaceZCylinder) - call write_dataset(surface_group, "type", "z-cylinder") - allocate(coeffs(3)) - coeffs(:) = [s%x0, s%y0, s%r] - - type is (SurfaceSphere) - call write_dataset(surface_group, "type", "sphere") - allocate(coeffs(4)) - coeffs(:) = [s%x0, s%y0, s%z0, s%r] - - type is (SurfaceXCone) - call write_dataset(surface_group, "type", "x-cone") - allocate(coeffs(4)) - coeffs(:) = [s%x0, s%y0, s%z0, s%r2] - - type is (SurfaceYCone) - call write_dataset(surface_group, "type", "y-cone") - allocate(coeffs(4)) - coeffs(:) = [s%x0, s%y0, s%z0, s%r2] - - type is (SurfaceZCone) - call write_dataset(surface_group, "type", "z-cone") - allocate(coeffs(4)) - coeffs(:) = [s%x0, s%y0, s%z0, s%r2] - - type is (SurfaceQuadric) - call write_dataset(surface_group, "type", "quadric") - allocate(coeffs(10)) - coeffs(:) = [s%A, s%B, s%C, s%D, s%E, s%F, s%G, s%H, s%J, s%K] - - end select - call write_dataset(surface_group, "coefficients", coeffs) - deallocate(coeffs) - - ! Write boundary type - select case (s%bc) - case (BC_TRANSMIT) - call write_dataset(surface_group, "boundary_type", "transmission") - case (BC_VACUUM) - call write_dataset(surface_group, "boundary_type", "vacuum") - case (BC_REFLECT) - call write_dataset(surface_group, "boundary_type", "reflective") - case (BC_PERIODIC) - call write_dataset(surface_group, "boundary_type", "periodic") - end select - - call close_group(surface_group) + call surfaces(i) % to_hdf5(surfaces_group) end do SURFACE_LOOP call close_group(surfaces_group) diff --git a/src/surface.cpp b/src/surface.cpp new file mode 100644 index 0000000000..9abfd23d2e --- /dev/null +++ b/src/surface.cpp @@ -0,0 +1,1235 @@ +#include "surface.h" + +#include +#include +#include + +#include "error.h" +#include "hdf5_interface.h" +#include "xml_interface.h" + + +namespace openmc { + +//============================================================================== +// Helper functions for reading the "coeffs" node of an XML surface element +//============================================================================== + +int word_count(const std::string &text) +{ + bool in_word = false; + int count {0}; + for (auto c = text.begin(); c != text.end(); c++) { + if (std::isspace(*c)) { + if (in_word) { + in_word = false; + count++; + } + } else { + in_word = true; + } + } + if (in_word) count++; + return count; +} + +void read_coeffs(pugi::xml_node surf_node, int surf_id, double &c1) +{ + // Check the given number of coefficients. + std::string coeffs = get_node_value(surf_node, "coeffs"); + int n_words = word_count(coeffs); + if (n_words != 1) { + std::stringstream err_msg; + err_msg << "Surface " << surf_id << " expects 1 coeff but was given " + << n_words; + fatal_error(err_msg); + } + + // Parse the coefficients. + int stat = sscanf(coeffs.c_str(), "%lf", &c1); + if (stat != 1) { + fatal_error("Something went wrong reading surface coeffs"); + } +} + +void read_coeffs(pugi::xml_node surf_node, int surf_id, double &c1, double &c2, + double &c3) +{ + // Check the given number of coefficients. + std::string coeffs = get_node_value(surf_node, "coeffs"); + int n_words = word_count(coeffs); + if (n_words != 3) { + std::stringstream err_msg; + err_msg << "Surface " << surf_id << " expects 3 coeffs but was given " + << n_words; + fatal_error(err_msg); + } + + // Parse the coefficients. + int stat = sscanf(coeffs.c_str(), "%lf %lf %lf", &c1, &c2, &c3); + if (stat != 3) { + fatal_error("Something went wrong reading surface coeffs"); + } +} + +void read_coeffs(pugi::xml_node surf_node, int surf_id, double &c1, double &c2, + double &c3, double &c4) +{ + // Check the given number of coefficients. + std::string coeffs = get_node_value(surf_node, "coeffs"); + int n_words = word_count(coeffs); + if (n_words != 4) { + std::stringstream err_msg; + err_msg << "Surface " << surf_id << " expects 4 coeffs but was given " + << n_words; + fatal_error(err_msg); + } + + // Parse the coefficients. + int stat = sscanf(coeffs.c_str(), "%lf %lf %lf %lf", &c1, &c2, &c3, &c4); + if (stat != 4) { + fatal_error("Something went wrong reading surface coeffs"); + } +} + +void read_coeffs(pugi::xml_node surf_node, int surf_id, double &c1, double &c2, + double &c3, double &c4, double &c5, double &c6, double &c7, + double &c8, double &c9, double &c10) +{ + // Check the given number of coefficients. + std::string coeffs = get_node_value(surf_node, "coeffs"); + int n_words = word_count(coeffs); + if (n_words != 10) { + std::stringstream err_msg; + err_msg << "Surface " << surf_id << " expects 10 coeffs but was given " + << n_words; + fatal_error(err_msg); + } + + // Parse the coefficients. + int stat = sscanf(coeffs.c_str(), "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", + &c1, &c2, &c3, &c4, &c5, &c6, &c7, &c8, &c9, &c10); + if (stat != 10) { + fatal_error("Something went wrong reading surface coeffs"); + } +} + +//============================================================================== +// Surface implementation +//============================================================================== + +Surface::Surface(pugi::xml_node surf_node) +{ + if (check_for_node(surf_node, "id")) { + id = stoi(get_node_value(surf_node, "id")); + } else { + fatal_error("Must specify id of surface in geometry XML file."); + } + + if (check_for_node(surf_node, "name")) { + name = get_node_value(surf_node, "name"); + } + + if (check_for_node(surf_node, "boundary")) { + std::string surf_bc = get_node_value(surf_node, "boundary"); + + if (surf_bc == "transmission" || surf_bc == "transmit" ||surf_bc.empty()) { + bc = BC_TRANSMIT; + + } else if (surf_bc == "vacuum") { + bc = BC_VACUUM; + + } else if (surf_bc == "reflective" || surf_bc == "reflect" + || surf_bc == "reflecting") { + bc = BC_REFLECT; + } else if (surf_bc == "periodic") { + bc = BC_PERIODIC; + } else { + std::stringstream err_msg; + err_msg << "Unknown boundary condition \"" << surf_bc + << "\" specified on surface " << id; + fatal_error(err_msg); + } + + } else { + bc = BC_TRANSMIT; + } + +} + +bool +Surface::sense(const double xyz[3], const double uvw[3]) const +{ + // Evaluate the surface equation at the particle's coordinates to determine + // which side the particle is on. + const double f = evaluate(xyz); + + // Check which side of surface the point is on. + if (std::abs(f) < FP_COINCIDENT) { + // Particle may be coincident with this surface. To determine the sense, we + // look at the direction of the particle relative to the surface normal (by + // default in the positive direction) via their dot product. + double norm[3]; + normal(xyz, norm); + return uvw[0] * norm[0] + uvw[1] * norm[1] + uvw[2] * norm[2] > 0.0; + } + return f > 0.0; +} + +void +Surface::reflect(const double xyz[3], double uvw[3]) const +{ + // Determine projection of direction onto normal and squared magnitude of + // normal. + double norm[3]; + normal(xyz, norm); + const double projection = norm[0]*uvw[0] + norm[1]*uvw[1] + norm[2]*uvw[2]; + const double magnitude = norm[0]*norm[0] + norm[1]*norm[1] + norm[2]*norm[2]; + + // Reflect direction according to normal. + uvw[0] -= 2.0 * projection / magnitude * norm[0]; + uvw[1] -= 2.0 * projection / magnitude * norm[1]; + uvw[2] -= 2.0 * projection / magnitude * norm[2]; +} + +void +Surface::to_hdf5(hid_t group_id) const +{ + std::string group_name {"surface "}; + group_name += std::to_string(id); + + hid_t surf_group = create_group(group_id, group_name); + + switch(bc) { + case BC_TRANSMIT : + write_string(surf_group, "boundary_type", "transmission"); + break; + case BC_VACUUM : + write_string(surf_group, "boundary_type", "vacuum"); + break; + case BC_REFLECT : + write_string(surf_group, "boundary_type", "reflective"); + break; + case BC_PERIODIC : + write_string(surf_group, "boundary_type", "periodic"); + break; + } + + if (!name.empty()) { + write_string(surf_group, "name", name); + } + + to_hdf5_inner(surf_group); + + close_group(surf_group); +} + +//============================================================================== +// PeriodicSurface implementation +//============================================================================== + +PeriodicSurface::PeriodicSurface(pugi::xml_node surf_node) + : Surface {surf_node} +{ + if (check_for_node(surf_node, "periodic_surface_id")) { + i_periodic = stoi(get_node_value(surf_node, "periodic_surface_id")); + } +} + +//============================================================================== +// Generic functions for x-, y-, and z-, planes. +//============================================================================== + +// The template parameter indicates the axis normal to the plane. +template double +axis_aligned_plane_evaluate(const double xyz[3], double offset) +{ + return xyz[i] - offset; +} + +// The template parameter indicates the axis normal to the plane. +template double +axis_aligned_plane_distance(const double xyz[3], const double uvw[3], + bool coincident, double offset) +{ + const double f = offset - xyz[i]; + if (coincident or std::abs(f) < FP_COINCIDENT or uvw[i] == 0.0) return INFTY; + const double d = f / uvw[i]; + if (d < 0.0) return INFTY; + return d; +} + +// The first template parameter indicates the axis normal to the plane. The +// other two parameters indicate the other two axes. +template void +axis_aligned_plane_normal(const double xyz[3], double uvw[3]) +{ + uvw[i1] = 1.0; + uvw[i2] = 0.0; + uvw[i3] = 0.0; +} + +//============================================================================== +// SurfaceXPlane implementation +//============================================================================== + +SurfaceXPlane::SurfaceXPlane(pugi::xml_node surf_node) + : PeriodicSurface(surf_node) +{ + read_coeffs(surf_node, id, x0); +} + +inline double SurfaceXPlane::evaluate(const double xyz[3]) const +{ + return axis_aligned_plane_evaluate<0>(xyz, x0); +} + +inline double SurfaceXPlane::distance(const double xyz[3], const double uvw[3], + bool coincident) const +{ + return axis_aligned_plane_distance<0>(xyz, uvw, coincident, x0); +} + +inline void SurfaceXPlane::normal(const double xyz[3], double uvw[3]) const +{ + axis_aligned_plane_normal<0, 1, 2>(xyz, uvw); +} + +void SurfaceXPlane::to_hdf5_inner(hid_t group_id) const +{ + write_string(group_id, "type", "x-plane"); + std::array coeffs {{x0}}; + write_double_1D(group_id, "coefficients", coeffs); +} + +bool SurfaceXPlane::periodic_translate(PeriodicSurface *other, double xyz[3], + double uvw[3]) const +{ + double other_norm[3]; + other->normal(xyz, other_norm); + if (other_norm[0] == 1 and other_norm[1] == 0 and other_norm[2] == 0) { + xyz[0] = x0; + return false; + } else { + // Assume the partner is an YPlane (the only supported partner). Use the + // evaluate function to find y0, then adjust xyz and uvw for rotational + // symmetry. + double xyz_test[3] {0, 0, 0}; + double y0 = -other->evaluate(xyz_test); + xyz[1] = xyz[0] - x0 + y0; + xyz[0] = x0; + + double u = uvw[0]; + uvw[0] = -uvw[1]; + uvw[1] = u; + + return true; + } +} + +BoundingBox +SurfaceXPlane::bounding_box() const +{ + BoundingBox out {x0, x0, -INFTY, INFTY, -INFTY, INFTY}; + return out; +} + +//============================================================================== +// SurfaceYPlane implementation +//============================================================================== + +SurfaceYPlane::SurfaceYPlane(pugi::xml_node surf_node) + : PeriodicSurface(surf_node) +{ + read_coeffs(surf_node, id, y0); +} + +inline double SurfaceYPlane::evaluate(const double xyz[3]) const +{ + return axis_aligned_plane_evaluate<1>(xyz, y0); +} + +inline double SurfaceYPlane::distance(const double xyz[3], const double uvw[3], + bool coincident) const +{ + return axis_aligned_plane_distance<1>(xyz, uvw, coincident, y0); +} + +inline void SurfaceYPlane::normal(const double xyz[3], double uvw[3]) const +{ + axis_aligned_plane_normal<1, 0, 2>(xyz, uvw); +} + +void SurfaceYPlane::to_hdf5_inner(hid_t group_id) const +{ + write_string(group_id, "type", "y-plane"); + std::array coeffs {{y0}}; + write_double_1D(group_id, "coefficients", coeffs); +} + +bool SurfaceYPlane::periodic_translate(PeriodicSurface *other, double xyz[3], + double uvw[3]) const +{ + double other_norm[3]; + other->normal(xyz, other_norm); + if (other_norm[0] == 0 and other_norm[1] == 1 and other_norm[2] == 0) { + // The periodic partner is also aligned along y. Just change the y coord. + xyz[1] = y0; + return false; + } else { + // Assume the partner is an XPlane (the only supported partner). Use the + // evaluate function to find x0, then adjust xyz and uvw for rotational + // symmetry. + double xyz_test[3] {0, 0, 0}; + double x0 = -other->evaluate(xyz_test); + xyz[0] = xyz[1] - y0 + x0; + xyz[1] = y0; + + double u = uvw[0]; + uvw[0] = uvw[1]; + uvw[1] = -u; + + return true; + } +} + +BoundingBox +SurfaceYPlane::bounding_box() const +{ + BoundingBox out {-INFTY, INFTY, y0, y0, -INFTY, INFTY}; + return out; +} + +//============================================================================== +// SurfaceZPlane implementation +//============================================================================== + +SurfaceZPlane::SurfaceZPlane(pugi::xml_node surf_node) + : PeriodicSurface(surf_node) +{ + read_coeffs(surf_node, id, z0); +} + +inline double SurfaceZPlane::evaluate(const double xyz[3]) const +{ + return axis_aligned_plane_evaluate<2>(xyz, z0); +} + +inline double SurfaceZPlane::distance(const double xyz[3], const double uvw[3], + bool coincident) const +{ + return axis_aligned_plane_distance<2>(xyz, uvw, coincident, z0); +} + +inline void SurfaceZPlane::normal(const double xyz[3], double uvw[3]) const +{ + axis_aligned_plane_normal<2, 0, 1>(xyz, uvw); +} + +void SurfaceZPlane::to_hdf5_inner(hid_t group_id) const +{ + write_string(group_id, "type", "z-plane"); + std::array coeffs {{z0}}; + write_double_1D(group_id, "coefficients", coeffs); +} + +bool SurfaceZPlane::periodic_translate(PeriodicSurface *other, double xyz[3], + double uvw[3]) const +{ + // Assume the other plane is aligned along z. Just change the z coord. + xyz[2] = z0; + return false; +} + +BoundingBox +SurfaceZPlane::bounding_box() const +{ + BoundingBox out {-INFTY, INFTY, -INFTY, INFTY, z0, z0}; + return out; +} + +//============================================================================== +// SurfacePlane implementation +//============================================================================== + +SurfacePlane::SurfacePlane(pugi::xml_node surf_node) + : PeriodicSurface(surf_node) +{ + read_coeffs(surf_node, id, A, B, C, D); +} + +double +SurfacePlane::evaluate(const double xyz[3]) const +{ + return A*xyz[0] + B*xyz[1] + C*xyz[2] - D; +} + +double +SurfacePlane::distance(const double xyz[3], const double uvw[3], + bool coincident) const +{ + const double f = A*xyz[0] + B*xyz[1] + C*xyz[2] - D; + const double projection = A*uvw[0] + B*uvw[1] + C*uvw[2]; + if (coincident or std::abs(f) < FP_COINCIDENT or projection == 0.0) { + return INFTY; + } else { + const double d = -f / projection; + if (d < 0.0) return INFTY; + return d; + } +} + +void +SurfacePlane::normal(const double xyz[3], double uvw[3]) const +{ + uvw[0] = A; + uvw[1] = B; + uvw[2] = C; +} + +void SurfacePlane::to_hdf5_inner(hid_t group_id) const +{ + write_string(group_id, "type", "plane"); + std::array coeffs {{A, B, C, D}}; + write_double_1D(group_id, "coefficients", coeffs); +} + +bool SurfacePlane::periodic_translate(PeriodicSurface *other, double xyz[3], + double uvw[3]) const +{ + // This function assumes the other plane shares this plane's normal direction. + + // Determine the distance to intersection. + double d = evaluate(xyz) / (A*A + B*B + C*C); + + // Move the particle that distance along the normal vector. + xyz[0] -= d * A; + xyz[1] -= d * B; + xyz[2] -= d * C; + + return false; +} + +BoundingBox +SurfacePlane::bounding_box() const +{ + BoundingBox out {-INFTY, INFTY, -INFTY, INFTY, -INFTY, INFTY}; + return out; +} + +//============================================================================== +// Generic functions for x-, y-, and z-, cylinders +//============================================================================== + +// The template parameters indicate the axes perpendicular to the axis of the +// cylinder. offset1 and offset2 should correspond with i1 and i2, +// respectively. +template double +axis_aligned_cylinder_evaluate(const double xyz[3], double offset1, + double offset2, double radius) +{ + const double xyz1 = xyz[i1] - offset1; + const double xyz2 = xyz[i2] - offset2; + return xyz1*xyz1 + xyz2*xyz2 - radius*radius; +} + +// The first template parameter indicates which axis the cylinder is aligned to. +// The other two parameters indicate the other two axes. offset1 and offset2 +// should correspond with i2 and i3, respectively. +template double +axis_aligned_cylinder_distance(const double xyz[3], const double uvw[3], + bool coincident, double offset1, double offset2, double radius) +{ + const double a = 1.0 - uvw[i1]*uvw[i1]; // u^2 + v^2 + if (a == 0.0) return INFTY; + + const double xyz2 = xyz[i2] - offset1; + const double xyz3 = xyz[i3] - offset2; + const double k = xyz2 * uvw[i2] + xyz3 * uvw[i3]; + const double c = xyz2*xyz2 + xyz3*xyz3 - radius*radius; + const double quad = k*k - a*c; + + if (quad < 0.0) { + // No intersection with cylinder. + return INFTY; + + } else if (coincident or std::abs(c) < FP_COINCIDENT) { + // Particle is on the cylinder, thus one distance is positive/negative + // and the other is zero. The sign of k determines if we are facing in or + // out. + if (k >= 0.0) { + return INFTY; + } else { + return (-k + sqrt(quad)) / a; + } + + } else if (c < 0.0) { + // Particle is inside the cylinder, thus one distance must be negative + // and one must be positive. The positive distance will be the one with + // negative sign on sqrt(quad). + return (-k + sqrt(quad)) / a; + + } else { + // Particle is outside the cylinder, thus both distances are either + // positive or negative. If positive, the smaller distance is the one + // with positive sign on sqrt(quad). + const double d = (-k - sqrt(quad)) / a; + if (d < 0.0) return INFTY; + return d; + } +} + +// The first template parameter indicates which axis the cylinder is aligned to. +// The other two parameters indicate the other two axes. offset1 and offset2 +// should correspond with i2 and i3, respectively. +template void +axis_aligned_cylinder_normal(const double xyz[3], double uvw[3], double offset1, + double offset2) +{ + uvw[i2] = 2.0 * (xyz[i2] - offset1); + uvw[i3] = 2.0 * (xyz[i3] - offset2); + uvw[i1] = 0.0; +} + +//============================================================================== +// SurfaceXCylinder implementation +//============================================================================== + +SurfaceXCylinder::SurfaceXCylinder(pugi::xml_node surf_node) + : Surface(surf_node) +{ + read_coeffs(surf_node, id, y0, z0, r); +} + +inline double SurfaceXCylinder::evaluate(const double xyz[3]) const +{ + return axis_aligned_cylinder_evaluate<1, 2>(xyz, y0, z0, r); +} + +inline double SurfaceXCylinder::distance(const double xyz[3], + const double uvw[3], bool coincident) const +{ + return axis_aligned_cylinder_distance<0, 1, 2>(xyz, uvw, coincident, y0, z0, + r); +} + +inline void SurfaceXCylinder::normal(const double xyz[3], double uvw[3]) const +{ + axis_aligned_cylinder_normal<0, 1, 2>(xyz, uvw, y0, z0); +} + + +void SurfaceXCylinder::to_hdf5_inner(hid_t group_id) const +{ + write_string(group_id, "type", "x-cylinder"); + std::array coeffs {{y0, z0, r}}; + write_double_1D(group_id, "coefficients", coeffs); +} + +//============================================================================== +// SurfaceYCylinder implementation +//============================================================================== + +SurfaceYCylinder::SurfaceYCylinder(pugi::xml_node surf_node) + : Surface(surf_node) +{ + read_coeffs(surf_node, id, x0, z0, r); +} + +inline double SurfaceYCylinder::evaluate(const double xyz[3]) const +{ + return axis_aligned_cylinder_evaluate<0, 2>(xyz, x0, z0, r); +} + +inline double SurfaceYCylinder::distance(const double xyz[3], + const double uvw[3], bool coincident) const +{ + return axis_aligned_cylinder_distance<1, 0, 2>(xyz, uvw, coincident, x0, z0, + r); +} + +inline void SurfaceYCylinder::normal(const double xyz[3], double uvw[3]) const +{ + axis_aligned_cylinder_normal<1, 0, 2>(xyz, uvw, x0, z0); +} + +void SurfaceYCylinder::to_hdf5_inner(hid_t group_id) const +{ + write_string(group_id, "type", "y-cylinder"); + std::array coeffs {{x0, z0, r}}; + write_double_1D(group_id, "coefficients", coeffs); +} + +//============================================================================== +// SurfaceZCylinder implementation +//============================================================================== + +SurfaceZCylinder::SurfaceZCylinder(pugi::xml_node surf_node) + : Surface(surf_node) +{ + read_coeffs(surf_node, id, x0, y0, r); +} + +inline double SurfaceZCylinder::evaluate(const double xyz[3]) const +{ + return axis_aligned_cylinder_evaluate<0, 1>(xyz, x0, y0, r); +} + +inline double SurfaceZCylinder::distance(const double xyz[3], + const double uvw[3], bool coincident) const +{ + return axis_aligned_cylinder_distance<2, 0, 1>(xyz, uvw, coincident, x0, y0, + r); +} + +inline void SurfaceZCylinder::normal(const double xyz[3], double uvw[3]) const +{ + axis_aligned_cylinder_normal<2, 0, 1>(xyz, uvw, x0, y0); +} + +void SurfaceZCylinder::to_hdf5_inner(hid_t group_id) const +{ + write_string(group_id, "type", "z-cylinder"); + std::array coeffs {{x0, y0, r}}; + write_double_1D(group_id, "coefficients", coeffs); +} + +//============================================================================== +// SurfaceSphere implementation +//============================================================================== + +SurfaceSphere::SurfaceSphere(pugi::xml_node surf_node) + : Surface(surf_node) +{ + read_coeffs(surf_node, id, x0, y0, z0, r); +} + +double SurfaceSphere::evaluate(const double xyz[3]) const +{ + const double x = xyz[0] - x0; + const double y = xyz[1] - y0; + const double z = xyz[2] - z0; + return x*x + y*y + z*z - r*r; +} + +double SurfaceSphere::distance(const double xyz[3], const double uvw[3], + bool coincident) const +{ + const double x = xyz[0] - x0; + const double y = xyz[1] - y0; + const double z = xyz[2] - z0; + const double k = x*uvw[0] + y*uvw[1] + z*uvw[2]; + const double c = x*x + y*y + z*z - r*r; + const double quad = k*k - c; + + if (quad < 0.0) { + // No intersection with sphere. + return INFTY; + + } else if (coincident or std::abs(c) < FP_COINCIDENT) { + // Particle is on the sphere, thus one distance is positive/negative and + // the other is zero. The sign of k determines if we are facing in or out. + if (k >= 0.0) { + return INFTY; + } else { + return -k + sqrt(quad); + } + + } else if (c < 0.0) { + // Particle is inside the sphere, thus one distance must be negative and + // one must be positive. The positive distance will be the one with + // negative sign on sqrt(quad) + return -k + sqrt(quad); + + } else { + // Particle is outside the sphere, thus both distances are either positive + // or negative. If positive, the smaller distance is the one with positive + // sign on sqrt(quad). + const double d = -k - sqrt(quad); + if (d < 0.0) return INFTY; + return d; + } +} + +inline void SurfaceSphere::normal(const double xyz[3], double uvw[3]) const +{ + uvw[0] = 2.0 * (xyz[0] - x0); + uvw[1] = 2.0 * (xyz[1] - y0); + uvw[2] = 2.0 * (xyz[2] - z0); +} + +void SurfaceSphere::to_hdf5_inner(hid_t group_id) const +{ + write_string(group_id, "type", "sphere"); + std::array coeffs {{x0, y0, z0, r}}; + write_double_1D(group_id, "coefficients", coeffs); +} + +//============================================================================== +// Generic functions for x-, y-, and z-, cones +//============================================================================== + +// The first template parameter indicates which axis the cone is aligned to. +// The other two parameters indicate the other two axes. offset1, offset2, +// and offset3 should correspond with i1, i2, and i3, respectively. +template double +axis_aligned_cone_evaluate(const double xyz[3], double offset1, + double offset2, double offset3, double radius_sq) +{ + const double xyz1 = xyz[i1] - offset1; + const double xyz2 = xyz[i2] - offset2; + const double xyz3 = xyz[i3] - offset3; + return xyz2*xyz2 + xyz3*xyz3 - radius_sq*xyz1*xyz1; +} + +// The first template parameter indicates which axis the cone is aligned to. +// The other two parameters indicate the other two axes. offset1, offset2, +// and offset3 should correspond with i1, i2, and i3, respectively. +template double +axis_aligned_cone_distance(const double xyz[3], const double uvw[3], + bool coincident, double offset1, double offset2, double offset3, + double radius_sq) +{ + const double xyz1 = xyz[i1] - offset1; + const double xyz2 = xyz[i2] - offset2; + const double xyz3 = xyz[i3] - offset3; + const double a = uvw[i2]*uvw[i2] + uvw[i3]*uvw[i3] + - radius_sq*uvw[i1]*uvw[i1]; + const double k = xyz2*uvw[i2] + xyz3*uvw[i3] - radius_sq*xyz1*uvw[i1]; + const double c = xyz2*xyz2 + xyz3*xyz3 - radius_sq*xyz1*xyz1; + double quad = k*k - a*c; + + double d; + + if (quad < 0.0) { + // No intersection with cone. + return INFTY; + + } else if (coincident or std::abs(c) < FP_COINCIDENT) { + // Particle is on the cone, thus one distance is positive/negative + // and the other is zero. The sign of k determines if we are facing in or + // out. + if (k >= 0.0) { + d = (-k - sqrt(quad)) / a; + } else { + d = (-k + sqrt(quad)) / a; + } + + } else { + // Calculate both solutions to the quadratic. + quad = sqrt(quad); + d = (-k - quad) / a; + const double b = (-k + quad) / a; + + // Determine the smallest positive solution. + if (d < 0.0) { + if (b > 0.0) d = b; + } else { + if (b > 0.0) { + if (b < d) d = b; + } + } + } + + // If the distance was negative, set boundary distance to infinity. + if (d <= 0.0) return INFTY; + return d; +} + +// The first template parameter indicates which axis the cone is aligned to. +// The other two parameters indicate the other two axes. offset1, offset2, +// and offset3 should correspond with i1, i2, and i3, respectively. +template void +axis_aligned_cone_normal(const double xyz[3], double uvw[3], double offset1, + double offset2, double offset3, double radius_sq) +{ + uvw[i1] = -2.0 * radius_sq * (xyz[i1] - offset1); + uvw[i2] = 2.0 * (xyz[i2] - offset2); + uvw[i3] = 2.0 * (xyz[i3] - offset3); +} + +//============================================================================== +// SurfaceXCone implementation +//============================================================================== + +SurfaceXCone::SurfaceXCone(pugi::xml_node surf_node) + : Surface(surf_node) +{ + read_coeffs(surf_node, id, x0, y0, z0, r_sq); +} + +inline double SurfaceXCone::evaluate(const double xyz[3]) const +{ + return axis_aligned_cone_evaluate<0, 1, 2>(xyz, x0, y0, z0, r_sq); +} + +inline double SurfaceXCone::distance(const double xyz[3], + const double uvw[3], bool coincident) const +{ + return axis_aligned_cone_distance<0, 1, 2>(xyz, uvw, coincident, x0, y0, z0, + r_sq); +} + +inline void SurfaceXCone::normal(const double xyz[3], double uvw[3]) const +{ + axis_aligned_cone_normal<0, 1, 2>(xyz, uvw, x0, y0, z0, r_sq); +} + +void SurfaceXCone::to_hdf5_inner(hid_t group_id) const +{ + write_string(group_id, "type", "x-cone"); + std::array coeffs {{x0, y0, z0, r_sq}}; + write_double_1D(group_id, "coefficients", coeffs); +} + +//============================================================================== +// SurfaceYCone implementation +//============================================================================== + +SurfaceYCone::SurfaceYCone(pugi::xml_node surf_node) + : Surface(surf_node) +{ + read_coeffs(surf_node, id, x0, y0, z0, r_sq); +} + +inline double SurfaceYCone::evaluate(const double xyz[3]) const +{ + return axis_aligned_cone_evaluate<1, 0, 2>(xyz, y0, x0, z0, r_sq); +} + +inline double SurfaceYCone::distance(const double xyz[3], + const double uvw[3], bool coincident) const +{ + return axis_aligned_cone_distance<1, 0, 2>(xyz, uvw, coincident, y0, x0, z0, + r_sq); +} + +inline void SurfaceYCone::normal(const double xyz[3], double uvw[3]) const +{ + axis_aligned_cone_normal<1, 0, 2>(xyz, uvw, y0, x0, z0, r_sq); +} + +void SurfaceYCone::to_hdf5_inner(hid_t group_id) const +{ + write_string(group_id, "type", "y-cone"); + std::array coeffs {{x0, y0, z0, r_sq}}; + write_double_1D(group_id, "coefficients", coeffs); +} + +//============================================================================== +// SurfaceZCone implementation +//============================================================================== + +SurfaceZCone::SurfaceZCone(pugi::xml_node surf_node) + : Surface(surf_node) +{ + read_coeffs(surf_node, id, x0, y0, z0, r_sq); +} + +inline double SurfaceZCone::evaluate(const double xyz[3]) const +{ + return axis_aligned_cone_evaluate<2, 0, 1>(xyz, z0, x0, y0, r_sq); +} + +inline double SurfaceZCone::distance(const double xyz[3], + const double uvw[3], bool coincident) const +{ + return axis_aligned_cone_distance<2, 0, 1>(xyz, uvw, coincident, z0, x0, y0, + r_sq); +} + +inline void SurfaceZCone::normal(const double xyz[3], double uvw[3]) const +{ + axis_aligned_cone_normal<2, 0, 1>(xyz, uvw, z0, x0, y0, r_sq); +} + +void SurfaceZCone::to_hdf5_inner(hid_t group_id) const +{ + write_string(group_id, "type", "z-cone"); + std::array coeffs {{x0, y0, z0, r_sq}}; + write_double_1D(group_id, "coefficients", coeffs); +} + +//============================================================================== +// SurfaceQuadric implementation +//============================================================================== + +SurfaceQuadric::SurfaceQuadric(pugi::xml_node surf_node) + : Surface(surf_node) +{ + read_coeffs(surf_node, id, A, B, C, D, E, F, G, H, J, K); +} + +double +SurfaceQuadric::evaluate(const double xyz[3]) const +{ + const double &x = xyz[0]; + const double &y = xyz[1]; + const double &z = xyz[2]; + return x*(A*x + D*y + G) + + y*(B*y + E*z + H) + + z*(C*z + F*x + J) + K; +} + +double +SurfaceQuadric::distance(const double xyz[3], + const double uvw[3], bool coincident) const +{ + const double &x = xyz[0]; + const double &y = xyz[1]; + const double &z = xyz[2]; + const double &u = uvw[0]; + const double &v = uvw[1]; + const double &w = uvw[2]; + + const double a = A*u*u + B*v*v + C*w*w + D*u*v + E*v*w + F*u*w; + const double k = (A*u*x + B*v*y + C*w*z + 0.5*(D*(u*y + v*x) + + E*(v*z + w*y) + F*(w*x + u*z) + G*u + H*v + J*w)); + const double c = A*x*x + B*y*y + C*z*z + D*x*y + E*y*z + F*x*z + G*x + H*y + + J*z + K; + double quad = k*k - a*c; + + double d; + + if (quad < 0.0) { + // No intersection with surface. + return INFTY; + + } else if (coincident or std::abs(c) < FP_COINCIDENT) { + // Particle is on the surface, thus one distance is positive/negative and + // the other is zero. The sign of k determines which distance is zero and + // which is not. + if (k >= 0.0) { + d = (-k - sqrt(quad)) / a; + } else { + d = (-k + sqrt(quad)) / a; + } + + } else { + // Calculate both solutions to the quadratic. + quad = sqrt(quad); + d = (-k - quad) / a; + double b = (-k + quad) / a; + + // Determine the smallest positive solution. + if (d < 0.0) { + if (b > 0.0) d = b; + } else { + if (b > 0.0) { + if (b < d) d = b; + } + } + } + + // If the distance was negative, set boundary distance to infinity. + if (d <= 0.0) return INFTY; + return d; +} + +void +SurfaceQuadric::normal(const double xyz[3], double uvw[3]) const +{ + const double &x = xyz[0]; + const double &y = xyz[1]; + const double &z = xyz[2]; + uvw[0] = 2.0*A*x + D*y + F*z + G; + uvw[1] = 2.0*B*y + D*x + E*z + H; + uvw[2] = 2.0*C*z + E*y + F*x + J; +} + +void SurfaceQuadric::to_hdf5_inner(hid_t group_id) const +{ + write_string(group_id, "type", "quadric"); + std::array coeffs {{A, B, C, D, E, F, G, H, J, K}}; + write_double_1D(group_id, "coefficients", coeffs); +} + +//============================================================================== + +extern "C" void +read_surfaces(pugi::xml_node *node) +{ + // Count the number of surfaces. + for (pugi::xml_node surf_node: node->children("surface")) {n_surfaces++;} + if (n_surfaces == 0) { + fatal_error("No surfaces found in geometry.xml!"); + } + + // Allocate the array of Surface pointers. + surfaces_c = new Surface* [n_surfaces]; + + // Loop over XML surface elements and populate the array. + { + pugi::xml_node surf_node; + int i_surf; + for (surf_node = node->child("surface"), i_surf = 0; surf_node; + surf_node = surf_node.next_sibling("surface"), i_surf++) { + std::string surf_type = get_node_value(surf_node, "type"); + + if (surf_type == "x-plane") { + surfaces_c[i_surf] = new SurfaceXPlane(surf_node); + + } else if (surf_type == "y-plane") { + surfaces_c[i_surf] = new SurfaceYPlane(surf_node); + + } else if (surf_type == "z-plane") { + surfaces_c[i_surf] = new SurfaceZPlane(surf_node); + + } else if (surf_type == "plane") { + surfaces_c[i_surf] = new SurfacePlane(surf_node); + + } else if (surf_type == "x-cylinder") { + surfaces_c[i_surf] = new SurfaceXCylinder(surf_node); + + } else if (surf_type == "y-cylinder") { + surfaces_c[i_surf] = new SurfaceYCylinder(surf_node); + + } else if (surf_type == "z-cylinder") { + surfaces_c[i_surf] = new SurfaceZCylinder(surf_node); + + } else if (surf_type == "sphere") { + surfaces_c[i_surf] = new SurfaceSphere(surf_node); + + } else if (surf_type == "x-cone") { + surfaces_c[i_surf] = new SurfaceXCone(surf_node); + + } else if (surf_type == "y-cone") { + surfaces_c[i_surf] = new SurfaceYCone(surf_node); + + } else if (surf_type == "z-cone") { + surfaces_c[i_surf] = new SurfaceZCone(surf_node); + + } else if (surf_type == "quadric") { + surfaces_c[i_surf] = new SurfaceQuadric(surf_node); + + } else { + std::stringstream err_msg; + err_msg << "Invalid surface type, \"" << surf_type << "\""; + fatal_error(err_msg); + } + } + } + + // Fill the surface dictionary. + for (int i_surf = 0; i_surf < n_surfaces; i_surf++) { + int id = surfaces_c[i_surf]->id; + auto in_dict = surface_dict.find(id); + if (in_dict == surface_dict.end()) { + surface_dict[id] = i_surf; + } else { + std::stringstream err_msg; + err_msg << "Two or more surfaces use the same unique ID: " << id; + fatal_error(err_msg); + } + } + + // Find the global bounding box (of periodic BC surfaces). + double xmin {INFTY}, xmax {-INFTY}, ymin {INFTY}, ymax {-INFTY}, + zmin {INFTY}, zmax {-INFTY}; + int i_xmin, i_xmax, i_ymin, i_ymax, i_zmin, i_zmax; + for (int i_surf = 0; i_surf < n_surfaces; i_surf++) { + if (surfaces_c[i_surf]->bc == BC_PERIODIC) { + // Downcast to the PeriodicSurface type. + Surface *surf_base = surfaces_c[i_surf]; + PeriodicSurface *surf = dynamic_cast(surf_base); + + // Make sure this surface inherits from PeriodicSurface. + if (!surf) { + std::stringstream err_msg; + err_msg << "Periodic boundary condition not supported for surface " + << surf_base->id + << ". Periodic BCs are only supported for planar surfaces."; + fatal_error(err_msg); + } + + // See if this surface makes part of the global bounding box. + BoundingBox bb = surf->bounding_box(); + if (bb.xmin > -INFTY and bb.xmin < xmin) { + xmin = bb.xmin; + i_xmin = i_surf; + } + if (bb.xmax < INFTY and bb.xmax > xmax) { + xmax = bb.xmax; + i_xmax = i_surf; + } + if (bb.ymin > -INFTY and bb.ymin < ymin) { + ymin = bb.ymin; + i_ymin = i_surf; + } + if (bb.ymax < INFTY and bb.ymax > ymax) { + ymax = bb.ymax; + i_ymax = i_surf; + } + if (bb.zmin > -INFTY and bb.zmin < zmin) { + zmin = bb.zmin; + i_zmin = i_surf; + } + if (bb.zmax < INFTY and bb.zmax > zmax) { + zmax = bb.zmax; + i_zmax = i_surf; + } + } + } + + // Set i_periodic for periodic BC surfaces. + for (int i_surf = 0; i_surf < n_surfaces; i_surf++) { + if (surfaces_c[i_surf]->bc == BC_PERIODIC) { + // Downcast to the PeriodicSurface type. + Surface *surf_base = surfaces_c[i_surf]; + PeriodicSurface *surf = dynamic_cast(surf_base); + + // Also try downcasting to the SurfacePlane type (which must be handled + // differently). + SurfacePlane *surf_p = dynamic_cast(surf); + + if (!surf_p) { + // This is not a SurfacePlane. + if (surf->i_periodic == C_NONE) { + // The user did not specify the matching periodic surface. See if we + // can find the partnered surface from the bounding box information. + if (i_surf == i_xmin) { + surf->i_periodic = i_xmax; + } else if (i_surf == i_xmax) { + surf->i_periodic = i_xmin; + } else if (i_surf == i_ymin) { + surf->i_periodic = i_ymax; + } else if (i_surf == i_ymax) { + surf->i_periodic = i_ymin; + } else if (i_surf == i_zmin) { + surf->i_periodic = i_zmax; + } else if (i_surf == i_zmax) { + surf->i_periodic = i_zmin; + } else { + fatal_error("Periodic boundary condition applied to interior " + "surface"); + } + } else { + // Convert the surface id to an index. + surf->i_periodic = surface_dict[surf->i_periodic]; + } + } else { + // This is a SurfacePlane. We won't try to find it's partner if the + // user didn't specify one. + if (surf->i_periodic == C_NONE) { + std::stringstream err_msg; + err_msg << "No matching periodic surface specified for periodic " + "boundary condition on surface " << surf->id; + fatal_error(err_msg); + } else { + // Convert the surface id to an index. + surf->i_periodic = surface_dict[surf->i_periodic]; + } + } + + // Make sure the opposite surface is also periodic. + if (surfaces_c[surf->i_periodic]->bc != BC_PERIODIC) { + std::stringstream err_msg; + err_msg << "Could not find matching surface for periodic boundary " + "condition on surface " << surf->id; + fatal_error(err_msg); + } + } + } +} + +} // namespace openmc diff --git a/src/surface.h b/src/surface.h new file mode 100644 index 0000000000..627a8fbae2 --- /dev/null +++ b/src/surface.h @@ -0,0 +1,427 @@ +#ifndef SURFACE_H +#define SURFACE_H + +#include +#include // For numeric_limits +#include + +#include "hdf5.h" +#include "pugixml/pugixml.hpp" + + +namespace openmc { + +//============================================================================== +// Module constants +//============================================================================== + +extern "C" const int BC_TRANSMIT {0}; +extern "C" const int BC_VACUUM {1}; +extern "C" const int BC_REFLECT {2}; +extern "C" const int BC_PERIODIC {3}; + +//============================================================================== +// Constants that should eventually be moved out of this file +//============================================================================== + +extern "C" double FP_COINCIDENT; +constexpr double INFTY{std::numeric_limits::max()}; +constexpr int C_NONE {-1}; + +//============================================================================== +// Global variables +//============================================================================== + +// Braces force n_surfaces to be defined here, not just declared. +extern "C" {int32_t n_surfaces {0};} + +class Surface; +Surface **surfaces_c; + +std::map surface_dict; + +//============================================================================== +//! Coordinates for an axis-aligned cube that bounds a geometric object. +//============================================================================== + +struct BoundingBox +{ + double xmin; + double xmax; + double ymin; + double ymax; + double zmin; + double zmax; +}; + +//============================================================================== +//! A geometry primitive used to define regions of 3D space. +//============================================================================== + +class Surface +{ +public: + int id; //!< Unique ID + //int neighbor_pos[], //!< List of cells on positive side + // neighbor_neg[]; //!< List of cells on negative side + int bc; //!< Boundary condition + std::string name{""}; //!< User-defined name + + explicit Surface(pugi::xml_node surf_node); + + virtual ~Surface() {} + + //! Determine which side of a surface a point lies on. + //! @param xyz[3] The 3D Cartesian coordinate of a point. + //! @param uvw[3] A direction used to "break ties" and pick a sense when the + //! point is very close to the surface. + //! @return true if the point is on the "positive" side of the surface and + //! false otherwise. + bool sense(const double xyz[3], const double uvw[3]) const; + + //! Determine the direction of a ray reflected from the surface. + //! @param xyz[3] The point at which the ray is incident. + //! @param uvw[3] A direction. This is both an input and an output parameter. + //! It specifies the icident direction on input and the reflected direction + //! on output. + void reflect(const double xyz[3], double uvw[3]) const; + + //! Evaluate the equation describing the surface. + //! + //! Surfaces can be described by some function f(x, y, z) = 0. This member + //! function evaluates that mathematical function. + //! @param xyz[3] A 3D Cartesian coordinate. + virtual double evaluate(const double xyz[3]) const = 0; + + //! Compute the distance between a point and the surface along a ray. + //! @param xyz[3] A 3D Cartesian coordinate. + //! @param uvw[3] The direction of the ray. + //! @param coincident A hint to the code that the given point should lie + //! exactly on the surface. + virtual double distance(const double xyz[3], const double uvw[3], + bool coincident) const = 0; + + //! Compute the local outward normal direction of the surface. + //! @param xyz[3] A 3D Cartesian coordinate. + //! @param uvw[3] This output argument provides the normal. + virtual void normal(const double xyz[3], double uvw[3]) const = 0; + + //! Write all information needed to reconstruct the surface to an HDF5 group. + //! @param group_id An HDF5 group id. + void to_hdf5(hid_t group_id) const; + +protected: + virtual void to_hdf5_inner(hid_t group_id) const = 0; +}; + +//============================================================================== +//! A `Surface` that supports periodic boundary conditions. +//! +//! Translational periodicity is supported for the `XPlane`, `YPlane`, `ZPlane`, +//! and `Plane` types. Rotational periodicity is supported for +//! `XPlane`-`YPlane` pairs. +//============================================================================== + +class PeriodicSurface : public Surface +{ +public: + int i_periodic{C_NONE}; //!< Index of corresponding periodic surface + + explicit PeriodicSurface(pugi::xml_node surf_node); + + //! Translate a particle onto this surface from a periodic partner surface. + //! @param other A pointer to the partner surface in this periodic BC. + //! @param xyz[3] A point on the partner surface that will be translated onto + //! this surface. + //! @param uvw[3] A direction that will be rotated for systems with rotational + //! periodicity. + //! @return true if this surface and its partner make a rotationally-periodic + //! boundary condition. + virtual bool periodic_translate(PeriodicSurface *other, double xyz[3], + double uvw[3]) const = 0; + + //! Get the bounding box for this surface. + virtual BoundingBox bounding_box() const = 0; +}; + +//============================================================================== +//! A plane perpendicular to the x-axis. +// +//! The plane is described by the equation \f$x - x_0 = 0\f$ +//============================================================================== + +class SurfaceXPlane : public PeriodicSurface +{ + double x0; +public: + explicit SurfaceXPlane(pugi::xml_node surf_node); + double evaluate(const double xyz[3]) const; + double distance(const double xyz[3], const double uvw[3], bool coincident) + const; + void normal(const double xyz[3], double uvw[3]) const; + void to_hdf5_inner(hid_t group_id) const; + bool periodic_translate(PeriodicSurface *other, double xyz[3], double uvw[3]) + const; + BoundingBox bounding_box() const; +}; + +//============================================================================== +//! A plane perpendicular to the y-axis. +// +//! The plane is described by the equation \f$y - y_0 = 0\f$ +//============================================================================== + +class SurfaceYPlane : public PeriodicSurface +{ + double y0; +public: + explicit SurfaceYPlane(pugi::xml_node surf_node); + double evaluate(const double xyz[3]) const; + double distance(const double xyz[3], const double uvw[3], + bool coincident) const; + void normal(const double xyz[3], double uvw[3]) const; + void to_hdf5_inner(hid_t group_id) const; + bool periodic_translate(PeriodicSurface *other, double xyz[3], double uvw[3]) + const; + BoundingBox bounding_box() const; +}; + +//============================================================================== +//! A plane perpendicular to the z-axis. +// +//! The plane is described by the equation \f$z - z_0 = 0\f$ +//============================================================================== + +class SurfaceZPlane : public PeriodicSurface +{ + double z0; +public: + explicit SurfaceZPlane(pugi::xml_node surf_node); + double evaluate(const double xyz[3]) const; + double distance(const double xyz[3], const double uvw[3], + bool coincident) const; + void normal(const double xyz[3], double uvw[3]) const; + void to_hdf5_inner(hid_t group_id) const; + bool periodic_translate(PeriodicSurface *other, double xyz[3], double uvw[3]) + const; + BoundingBox bounding_box() const; +}; + +//============================================================================== +//! A general plane. +// +//! The plane is described by the equation \f$A x + B y + C z - D = 0\f$ +//============================================================================== + +class SurfacePlane : public PeriodicSurface +{ + double A, B, C, D; +public: + explicit SurfacePlane(pugi::xml_node surf_node); + double evaluate(const double xyz[3]) const; + double distance(const double xyz[3], const double uvw[3], bool coincident) + const; + void normal(const double xyz[3], double uvw[3]) const; + void to_hdf5_inner(hid_t group_id) const; + bool periodic_translate(PeriodicSurface *other, double xyz[3], double uvw[3]) + const; + BoundingBox bounding_box() const; +}; + +//============================================================================== +//! A cylinder aligned along the x-axis. +// +//! The cylinder is described by the equation +//! \f$(y - y_0)^2 + (z - z_0)^2 - R^2 = 0\f$ +//============================================================================== + +class SurfaceXCylinder : public Surface +{ + double y0, z0, r; +public: + explicit SurfaceXCylinder(pugi::xml_node surf_node); + double evaluate(const double xyz[3]) const; + double distance(const double xyz[3], const double uvw[3], + bool coincident) const; + void normal(const double xyz[3], double uvw[3]) const; + void to_hdf5_inner(hid_t group_id) const; +}; + +//============================================================================== +//! A cylinder aligned along the y-axis. +// +//! The cylinder is described by the equation +//! \f$(x - x_0)^2 + (z - z_0)^2 - R^2 = 0\f$ +//============================================================================== + +class SurfaceYCylinder : public Surface +{ + double x0, z0, r; +public: + explicit SurfaceYCylinder(pugi::xml_node surf_node); + double evaluate(const double xyz[3]) const; + double distance(const double xyz[3], const double uvw[3], + bool coincident) const; + void normal(const double xyz[3], double uvw[3]) const; + void to_hdf5_inner(hid_t group_id) const; +}; + +//============================================================================== +//! A cylinder aligned along the z-axis. +// +//! The cylinder is described by the equation +//! \f$(x - x_0)^2 + (y - y_0)^2 - R^2 = 0\f$ +//============================================================================== + +class SurfaceZCylinder : public Surface +{ + double x0, y0, r; +public: + explicit SurfaceZCylinder(pugi::xml_node surf_node); + double evaluate(const double xyz[3]) const; + double distance(const double xyz[3], const double uvw[3], + bool coincident) const; + void normal(const double xyz[3], double uvw[3]) const; + void to_hdf5_inner(hid_t group_id) const; +}; + +//============================================================================== +//! A sphere. +// +//! The cylinder is described by the equation +//! \f$(x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 - R^2 = 0\f$ +//============================================================================== + +class SurfaceSphere : public Surface +{ + double x0, y0, z0, r; +public: + explicit SurfaceSphere(pugi::xml_node surf_node); + double evaluate(const double xyz[3]) const; + double distance(const double xyz[3], const double uvw[3], + bool coincident) const; + void normal(const double xyz[3], double uvw[3]) const; + void to_hdf5_inner(hid_t group_id) const; +}; + +//============================================================================== +//! A cone aligned along the x-axis. +// +//! The cylinder is described by the equation +//! \f$(y - y_0)^2 + (z - z_0)^2 - R^2 (x - x_0)^2 = 0\f$ +//============================================================================== + +class SurfaceXCone : public Surface +{ + double x0, y0, z0, r_sq; +public: + explicit SurfaceXCone(pugi::xml_node surf_node); + double evaluate(const double xyz[3]) const; + double distance(const double xyz[3], const double uvw[3], + bool coincident) const; + void normal(const double xyz[3], double uvw[3]) const; + void to_hdf5_inner(hid_t group_id) const; +}; + +//============================================================================== +//! A cone aligned along the y-axis. +// +//! The cylinder is described by the equation +//! \f$(x - x_0)^2 + (z - z_0)^2 - R^2 (y - y_0)^2 = 0\f$ +//============================================================================== + +class SurfaceYCone : public Surface +{ + double x0, y0, z0, r_sq; +public: + explicit SurfaceYCone(pugi::xml_node surf_node); + double evaluate(const double xyz[3]) const; + double distance(const double xyz[3], const double uvw[3], + bool coincident) const; + void normal(const double xyz[3], double uvw[3]) const; + void to_hdf5_inner(hid_t group_id) const; +}; + +//============================================================================== +//! A cone aligned along the z-axis. +// +//! The cylinder is described by the equation +//! \f$(x - x_0)^2 + (y - y_0)^2 - R^2 (z - z_0)^2 = 0\f$ +//============================================================================== + +class SurfaceZCone : public Surface +{ + double x0, y0, z0, r_sq; +public: + explicit SurfaceZCone(pugi::xml_node surf_node); + double evaluate(const double xyz[3]) const; + double distance(const double xyz[3], const double uvw[3], + bool coincident) const; + void normal(const double xyz[3], double uvw[3]) const; + void to_hdf5_inner(hid_t group_id) const; +}; + +//============================================================================== +//! A general surface described by a quadratic equation. +// +//! \f$A x^2 + B y^2 + C z^2 + D x y + E y z + F x z + G x + H y + J z + K = 0\f$ +//============================================================================== + +class SurfaceQuadric : public Surface +{ + // Ax^2 + By^2 + Cz^2 + Dxy + Eyz + Fxz + Gx + Hy + Jz + K = 0 + double A, B, C, D, E, F, G, H, J, K; +public: + explicit SurfaceQuadric(pugi::xml_node surf_node); + double evaluate(const double xyz[3]) const; + double distance(const double xyz[3], const double uvw[3], + bool coincident) const; + void normal(const double xyz[3], double uvw[3]) const; + void to_hdf5_inner(hid_t group_id) const; +}; + +//============================================================================== +// Fortran compatibility functions +//============================================================================== + +extern "C" Surface* surface_pointer(int surf_ind) {return surfaces_c[surf_ind];} + +extern "C" int surface_id(Surface *surf) {return surf->id;} + +extern "C" int surface_bc(Surface *surf) {return surf->bc;} + +extern "C" bool surface_sense(Surface *surf, double xyz[3], double uvw[3]) +{return surf->sense(xyz, uvw);} + +extern "C" void surface_reflect(Surface *surf, double xyz[3], double uvw[3]) +{surf->reflect(xyz, uvw);} + +extern "C" double +surface_distance(Surface *surf, double xyz[3], double uvw[3], bool coincident) +{return surf->distance(xyz, uvw, coincident);} + +extern "C" void surface_normal(Surface *surf, double xyz[3], double uvw[3]) +{return surf->normal(xyz, uvw);} + +extern "C" void surface_to_hdf5(Surface *surf, hid_t group) +{surf->to_hdf5(group);} + +extern "C" int surface_i_periodic(PeriodicSurface *surf) +{return surf->i_periodic;} + +extern "C" bool +surface_periodic(PeriodicSurface *surf, PeriodicSurface *other, double xyz[3], + double uvw[3]) +{return surf->periodic_translate(other, xyz, uvw);} + +extern "C" void free_memory_surfaces_c() +{ + for (int i = 0; i < n_surfaces; i++) {delete surfaces_c[i];} + delete surfaces_c; + surfaces_c = nullptr; + n_surfaces = 0; + surface_dict.clear(); +} + +} // namespace openmc +#endif // SURFACE_H diff --git a/src/surface_header.F90 b/src/surface_header.F90 index ed4ef86e09..9ed89a14e9 100644 --- a/src/surface_header.F90 +++ b/src/surface_header.F90 @@ -1,1073 +1,212 @@ module surface_header use, intrinsic :: ISO_C_BINDING + use hdf5 - use constants, only: NONE, ONE, TWO, ZERO, HALF, INFINITY, FP_COINCIDENT use dict_header, only: DictIntInt implicit none + interface + pure function surface_pointer_c(surf_ind) & + bind(C, name='surface_pointer') result(ptr) + use ISO_C_BINDING + implicit none + integer(C_INT), intent(in), value :: surf_ind + type(C_PTR) :: ptr + end function surface_pointer_c + + pure function surface_id_c(surf_ptr) bind(C, name='surface_id') result(id) + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: surf_ptr + integer(C_INT) :: id + end function surface_id_c + + pure function surface_bc_c(surf_ptr) bind(C, name='surface_bc') result(bc) + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: surf_ptr + integer(C_INT) :: bc + end function surface_bc_c + + pure function surface_sense_c(surf_ptr, xyz, uvw) & + bind(C, name='surface_sense') result(sense) + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: surf_ptr + real(C_DOUBLE), intent(in) :: xyz(3) + real(C_DOUBLE), intent(in) :: uvw(3) + logical(C_BOOL) :: sense + end function surface_sense_c + + pure subroutine surface_reflect_c(surf_ptr, xyz, uvw) & + bind(C, name='surface_reflect') + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: surf_ptr + real(C_DOUBLE), intent(in) :: xyz(3); + real(C_DOUBLE), intent(inout) :: uvw(3); + end subroutine surface_reflect_c + + pure function surface_distance_c(surf_ptr, xyz, uvw, coincident) & + bind(C, name='surface_distance') result(d) + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: surf_ptr + real(C_DOUBLE), intent(in) :: xyz(3); + real(C_DOUBLE), intent(in) :: uvw(3); + logical(C_BOOL), intent(in), value :: coincident; + real(C_DOUBLE) :: d; + end function surface_distance_c + + pure subroutine surface_normal_c(surf_ptr, xyz, uvw) & + bind(C, name='surface_normal') + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: surf_ptr + real(C_DOUBLE), intent(in) :: xyz(3); + real(C_DOUBLE), intent(out) :: uvw(3); + end subroutine surface_normal_c + + subroutine surface_to_hdf5_c(surf_ptr, group) & + bind(C, name='surface_to_hdf5') + use ISO_C_BINDING + use hdf5 + implicit none + type(C_PTR), intent(in), value :: surf_ptr + integer(HID_T), intent(in), value :: group + end subroutine surface_to_hdf5_c + + pure function surface_i_periodic_c(surf_ptr) & + bind(C, name="surface_i_periodic") result(i_periodic) + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: surf_ptr + integer(C_INT) :: i_periodic + end function surface_i_periodic_c + + function surface_periodic_c(surf_ptr, other_ptr, xyz, uvw) & + bind(C, name="surface_periodic") result(rotational) + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: surf_ptr + type(C_PTR), intent(in), value :: other_ptr + real(C_DOUBLE), intent(inout) :: xyz(3); + real(C_DOUBLE), intent(inout) :: uvw(3); + logical(C_BOOL) :: rotational + end function surface_periodic_c + + subroutine free_memory_surfaces_c() bind(C) + end subroutine free_memory_surfaces_c + end interface + !=============================================================================== ! SURFACE type defines a first- or second-order surface that can be used to ! construct closed volumes (cells) !=============================================================================== - type, abstract :: Surface - integer :: id ! Unique ID + type :: Surface integer, allocatable :: & neighbor_pos(:), & ! List of cells on positive side neighbor_neg(:) ! List of cells on negative side - integer :: bc ! Boundary condition - integer :: i_periodic = NONE ! Index of corresponding periodic surface - character(len=104) :: name = "" ! User-defined name + type(C_PTR) :: ptr + contains - procedure :: sense - procedure :: reflect - procedure(surface_evaluate_), deferred :: evaluate - procedure(surface_distance_), deferred :: distance - procedure(surface_normal_), deferred :: normal + + procedure :: id => surface_id + procedure :: bc => surface_bc + procedure :: sense => surface_sense + procedure :: reflect => surface_reflect + procedure :: distance => surface_distance + procedure :: normal => surface_normal + procedure :: to_hdf5 => surface_to_hdf5 + procedure :: i_periodic => surface_i_periodic + procedure :: periodic_translate => surface_periodic + end type Surface - abstract interface - pure function surface_evaluate_(this, xyz) result(f) - import Surface - class(Surface), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: f - end function surface_evaluate_ - - pure function surface_distance_(this, xyz, uvw, coincident) result(d) - import Surface - class(Surface), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8), intent(in) :: uvw(3) - logical, intent(in) :: coincident - real(8) :: d - end function surface_distance_ - - pure function surface_normal_(this, xyz) result(uvw) - import Surface - class(Surface), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: uvw(3) - end function surface_normal_ - end interface - -!=============================================================================== -! SURFACECONTAINER allows us to store an array of different types of surfaces -!=============================================================================== - - type :: SurfaceContainer - class(Surface), allocatable :: obj - end type SurfaceContainer - -!=============================================================================== -! All the derived types below are extensions of the abstract Surface type. They -! inherent the reflect() and sense() type-bound procedures and must implement -! evaluate(), distance(), and normal() -!=============================================================================== - - type, extends(Surface) :: SurfaceXPlane - ! x = x0 - real(8) :: x0 - contains - procedure :: evaluate => x_plane_evaluate - procedure :: distance => x_plane_distance - procedure :: normal => x_plane_normal - end type SurfaceXPlane - - type, extends(Surface) :: SurfaceYPlane - ! y = y0 - real(8) :: y0 - contains - procedure :: evaluate => y_plane_evaluate - procedure :: distance => y_plane_distance - procedure :: normal => y_plane_normal - end type SurfaceYPlane - - type, extends(Surface) :: SurfaceZPlane - ! z = z0 - real(8) :: z0 - contains - procedure :: evaluate => z_plane_evaluate - procedure :: distance => z_plane_distance - procedure :: normal => z_plane_normal - end type SurfaceZPlane - - type, extends(Surface) :: SurfacePlane - ! Ax + By + Cz = D - real(8) :: A - real(8) :: B - real(8) :: C - real(8) :: D - contains - procedure :: evaluate => plane_evaluate - procedure :: distance => plane_distance - procedure :: normal => plane_normal - end type SurfacePlane - - type, extends(Surface) :: SurfaceXCylinder - ! (y - y0)^2 + (z - z0)^2 = R^2 - real(8) :: y0 - real(8) :: z0 - real(8) :: r - contains - procedure :: evaluate => x_cylinder_evaluate - procedure :: distance => x_cylinder_distance - procedure :: normal => x_cylinder_normal - end type SurfaceXCylinder - - type, extends(Surface) :: SurfaceYCylinder - ! (x - x0)^2 + (z - z0)^2 = R^2 - real(8) :: x0 - real(8) :: z0 - real(8) :: r - contains - procedure :: evaluate => y_cylinder_evaluate - procedure :: distance => y_cylinder_distance - procedure :: normal => y_cylinder_normal - end type SurfaceYCylinder - - type, extends(Surface) :: SurfaceZCylinder - ! (x - x0)^2 + (y - y0)^2 = R^2 - real(8) :: x0 - real(8) :: y0 - real(8) :: r - contains - procedure :: evaluate => z_cylinder_evaluate - procedure :: distance => z_cylinder_distance - procedure :: normal => z_cylinder_normal - end type SurfaceZCylinder - - type, extends(Surface) :: SurfaceSphere - ! (x - x0)^2 + (y - y0)^2 + (z - z0)^2 = R^2 - real(8) :: x0 - real(8) :: y0 - real(8) :: z0 - real(8) :: r - contains - procedure :: evaluate => sphere_evaluate - procedure :: distance => sphere_distance - procedure :: normal => sphere_normal - end type SurfaceSphere - - type, extends(Surface) :: SurfaceXCone - ! (y - y0)^2 + (z - z0)^2 = R^2*(x - x0)^2 - real(8) :: x0 - real(8) :: y0 - real(8) :: z0 - real(8) :: r2 - contains - procedure :: evaluate => x_cone_evaluate - procedure :: distance => x_cone_distance - procedure :: normal => x_cone_normal - end type SurfaceXCone - - type, extends(Surface) :: SurfaceYCone - ! (x - x0)^2 + (z - z0)^2 = R^2*(y - y0)^2 - real(8) :: x0 - real(8) :: y0 - real(8) :: z0 - real(8) :: r2 - contains - procedure :: evaluate => y_cone_evaluate - procedure :: distance => y_cone_distance - procedure :: normal => y_cone_normal - end type SurfaceYCone - - type, extends(Surface) :: SurfaceZCone - ! (x - x0)^2 + (y - y0)^2 = R^2*(z - z0)^2 - real(8) :: x0 - real(8) :: y0 - real(8) :: z0 - real(8) :: r2 - contains - procedure :: evaluate => z_cone_evaluate - procedure :: distance => z_cone_distance - procedure :: normal => z_cone_normal - end type SurfaceZCone - - type, extends(Surface) :: SurfaceQuadric - ! Ax^2 + By^2 + Cz^2 + Dxy + Eyz + Fxz + Gx + Hy + Jz + K = 0 - real(8) :: A, B, C, D, E, F, G, H, J, K - contains - procedure :: evaluate => quadric_evaluate - procedure :: distance => quadric_distance - procedure :: normal => quadric_normal - end type SurfaceQuadric - integer(C_INT32_T), bind(C) :: n_surfaces ! # of surfaces - type(SurfaceContainer), allocatable, target :: surfaces(:) + type(Surface), allocatable, target :: surfaces(:) ! Dictionary that maps user IDs to indices in 'surfaces' type(DictIntInt) :: surface_dict contains -!=============================================================================== -! SENSE determines whether a point is on the 'positive' or 'negative' side of a -! surface. This routine is crucial for determining what cell a particular point -! is in. The positive side is indicated by a returned value of .true. and the -! negative side is indicated by a returned value of .false. -!=============================================================================== - - pure function sense(this, xyz, uvw) result(s) - class(Surface), intent(in) :: this ! surface - real(8), intent(in) :: xyz(3) - real(8), intent(in) :: uvw(3) - logical :: s ! sense of particle - - real(8) :: f ! surface function evaluated at point - - ! Evaluate the surface equation at the particle's coordinates to determine - ! which side the particle is on - f = this%evaluate(xyz) - - ! Check which side of surface the point is on - if (abs(f) < FP_COINCIDENT) then - ! Particle may be coincident with this surface. To determine the sense, we - ! look at the direction of the particle relative to the surface normal (by - ! default in the positive direction) via their dot product. - s = (dot_product(uvw, this%normal(xyz)) > ZERO) - else - s = (f > ZERO) - end if - end function sense - -!=============================================================================== -! REFLECT determines the direction a particle will travel if it is specularly -! reflected from the surface at a given position and direction. The position is -! needed because the reflection is performed using the surface normal, which -! depends on the position for second-order surfaces. -!=============================================================================== - - pure subroutine reflect(this, xyz, uvw) + pure function surface_id(this) result(id) class(Surface), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8), intent(inout) :: uvw(3) - - real(8) :: projection - real(8) :: magnitude - real(8) :: n(3) - - ! Construct normal vector - n(:) = this%normal(xyz) - - ! Determine projection of direction onto normal and squared magnitude of - ! normal - projection = n(1)*uvw(1) + n(2)*uvw(2) + n(3)*uvw(3) - magnitude = n(1)*n(1) + n(2)*n(2) + n(3)*n(3) - - ! Reflect direction according to normal - uvw(:) = uvw - TWO*projection/magnitude * n - end subroutine reflect - -!=============================================================================== -! SurfaceXPlane Implementation -!=============================================================================== - - pure function x_plane_evaluate(this, xyz) result(f) - class(SurfaceXPlane), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: f - - f = xyz(1) - this%x0 - end function x_plane_evaluate - - pure function x_plane_distance(this, xyz, uvw, coincident) result(d) - class(SurfaceXPlane), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8), intent(in) :: uvw(3) - logical, intent(in) :: coincident - real(8) :: d - - real(8) :: f - - f = this%x0 - xyz(1) - if (coincident .or. abs(f) < FP_COINCIDENT .or. uvw(1) == ZERO) then - d = INFINITY - else - d = f/uvw(1) - if (d < ZERO) d = INFINITY - end if - end function x_plane_distance - - pure function x_plane_normal(this, xyz) result(uvw) - class(SurfaceXPlane), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: uvw(3) - - uvw(:) = [ONE, ZERO, ZERO] - end function x_plane_normal - -!=============================================================================== -! SurfaceYPlane Implementation -!=============================================================================== - - pure function y_plane_evaluate(this, xyz) result(f) - class(SurfaceYPlane), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: f - - f = xyz(2) - this%y0 - end function y_plane_evaluate - - pure function y_plane_distance(this, xyz, uvw, coincident) result(d) - class(SurfaceYPlane), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8), intent(in) :: uvw(3) - logical, intent(in) :: coincident - real(8) :: d - - real(8) :: f - - f = this%y0 - xyz(2) - if (coincident .or. abs(f) < FP_COINCIDENT .or. uvw(2) == ZERO) then - d = INFINITY - else - d = f/uvw(2) - if (d < ZERO) d = INFINITY - end if - end function y_plane_distance - - pure function y_plane_normal(this, xyz) result(uvw) - class(SurfaceYPlane), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: uvw(3) - - uvw(:) = [ZERO, ONE, ZERO] - end function y_plane_normal - -!=============================================================================== -! SurfaceZPlane Implementation -!=============================================================================== - - pure function z_plane_evaluate(this, xyz) result(f) - - class(SurfaceZPlane), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: f - - f = xyz(3) - this%z0 - - end function z_plane_evaluate - - pure function z_plane_distance(this, xyz, uvw, coincident) result(d) - class(SurfaceZPlane), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8), intent(in) :: uvw(3) - logical, intent(in) :: coincident - real(8) :: d - - real(8) :: f - - f = this%z0 - xyz(3) - if (coincident .or. abs(f) < FP_COINCIDENT .or. uvw(3) == ZERO) then - d = INFINITY - else - d = f/uvw(3) - if (d < ZERO) d = INFINITY - end if - end function z_plane_distance - - pure function z_plane_normal(this, xyz) result(uvw) - class(SurfaceZPlane), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: uvw(3) - - uvw(:) = [ZERO, ZERO, ONE] - end function z_plane_normal - -!=============================================================================== -! SurfacePlane Implementation -!=============================================================================== - - pure function plane_evaluate(this, xyz) result(f) - class(SurfacePlane), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: f - - f = this%A*xyz(1) + this%B*xyz(2) + this%C*xyz(3) - this%D - end function plane_evaluate - - pure function plane_distance(this, xyz, uvw, coincident) result(d) - class(SurfacePlane), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8), intent(in) :: uvw(3) - logical, intent(in) :: coincident - real(8) :: d - - real(8) :: f - real(8) :: projection - - f = this%A*xyz(1) + this%B*xyz(2) + this%C*xyz(3) - this%D - projection = this%A*uvw(1) + this%B*uvw(2) + this%C*uvw(3) - if (coincident .or. abs(f) < FP_COINCIDENT .or. projection == ZERO) then - d = INFINITY - else - d = -f/projection - if (d < ZERO) d = INFINITY - end if - end function plane_distance - - pure function plane_normal(this, xyz) result(uvw) - class(SurfacePlane), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: uvw(3) - - uvw(:) = [this%A, this%B, this%C] - end function plane_normal - -!=============================================================================== -! SurfaceXCylinder Implementation -!=============================================================================== - - pure function x_cylinder_evaluate(this, xyz) result(f) - class(SurfaceXCylinder), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: f - - real(8) :: y, z - - y = xyz(2) - this%y0 - z = xyz(3) - this%z0 - f = y*y + z*z - this%r*this%r - end function x_cylinder_evaluate - - pure function x_cylinder_distance(this, xyz, uvw, coincident) result(d) - class(SurfaceXCylinder), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8), intent(in) :: uvw(3) - logical, intent(in) :: coincident - real(8) :: d - - real(8) :: y, z, k, a, c, quad - - a = ONE - uvw(1)*uvw(1) ! v^2 + w^2 - if (a == ZERO) then - d = INFINITY - else - y = xyz(2) - this%y0 - z = xyz(3) - this%z0 - k = y*uvw(2) + z*uvw(3) - c = y*y + z*z - this%r*this%r - quad = k*k - a*c - - if (quad < ZERO) then - ! no intersection with cylinder - - d = INFINITY - - elseif (coincident .or. abs(c) < FP_COINCIDENT) then - ! particle is on the cylinder, thus one distance is positive/negative - ! and the other is zero. The sign of k determines if we are facing in or - ! out - - if (k >= ZERO) then - d = INFINITY - else - d = (-k + sqrt(quad))/a - end if - - elseif (c < ZERO) then - ! particle is inside the cylinder, thus one distance must be negative - ! and one must be positive. The positive distance will be the one with - ! negative sign on sqrt(quad) - - d = (-k + sqrt(quad))/a - - else - ! particle is outside the cylinder, thus both distances are either - ! positive or negative. If positive, the smaller distance is the one - ! with positive sign on sqrt(quad) - - d = (-k - sqrt(quad))/a - if (d < ZERO) d = INFINITY - - end if - end if - end function x_cylinder_distance - - pure function x_cylinder_normal(this, xyz) result(uvw) - class(SurfaceXCylinder), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: uvw(3) - - uvw(1) = ZERO - uvw(2) = TWO*(xyz(2) - this%y0) - uvw(3) = TWO*(xyz(3) - this%z0) - end function x_cylinder_normal - -!=============================================================================== -! SurfaceYCylinder Implementation -!=============================================================================== - - pure function y_cylinder_evaluate(this, xyz) result(f) - class(SurfaceYCylinder), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: f - - real(8) :: x, z - - x = xyz(1) - this%x0 - z = xyz(3) - this%z0 - f = x*x + z*z - this%r*this%r - end function y_cylinder_evaluate - - pure function y_cylinder_distance(this, xyz, uvw, coincident) result(d) - class(SurfaceYCylinder), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8), intent(in) :: uvw(3) - logical, intent(in) :: coincident - real(8) :: d - - real(8) :: x, z, k, a, c, quad - - a = ONE - uvw(2)*uvw(2) ! u^2 + w^2 - if (a == ZERO) then - d = INFINITY - else - x = xyz(1) - this%x0 - z = xyz(3) - this%z0 - k = x*uvw(1) + z*uvw(3) - c = x*x + z*z - this%r*this%r - quad = k*k - a*c - - if (quad < ZERO) then - ! no intersection with cylinder - - d = INFINITY - - elseif (coincident .or. abs(c) < FP_COINCIDENT) then - ! particle is on the cylinder, thus one distance is positive/negative - ! and the other is zero. The sign of k determines if we are facing in or - ! out - - if (k >= ZERO) then - d = INFINITY - else - d = (-k + sqrt(quad))/a - end if - - elseif (c < ZERO) then - ! particle is inside the cylinder, thus one distance must be negative - ! and one must be positive. The positive distance will be the one with - ! negative sign on sqrt(quad) - - d = (-k + sqrt(quad))/a - - else - ! particle is outside the cylinder, thus both distances are either - ! positive or negative. If positive, the smaller distance is the one - ! with positive sign on sqrt(quad) - - d = (-k - sqrt(quad))/a - if (d < ZERO) d = INFINITY - - end if - end if - end function y_cylinder_distance - - pure function y_cylinder_normal(this, xyz) result(uvw) - class(SurfaceYCylinder), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: uvw(3) - - uvw(1) = TWO*(xyz(1) - this%x0) - uvw(2) = ZERO - uvw(3) = TWO*(xyz(3) - this%z0) - end function y_cylinder_normal - -!=============================================================================== -! SurfaceZCylinder Implementation -!=============================================================================== - - pure function z_cylinder_evaluate(this, xyz) result(f) - class(SurfaceZCylinder), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: f - - real(8) :: x, y - - x = xyz(1) - this%x0 - y = xyz(2) - this%y0 - f = x*x + y*y - this%r*this%r - end function z_cylinder_evaluate - - pure function z_cylinder_distance(this, xyz, uvw, coincident) result(d) - class(SurfaceZCylinder), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8), intent(in) :: uvw(3) - logical, intent(in) :: coincident - real(8) :: d - - real(8) :: x, y, k, a, c, quad - - a = ONE - uvw(3)*uvw(3) ! u^2 + v^2 - if (a == ZERO) then - d = INFINITY - else - x = xyz(1) - this%x0 - y = xyz(2) - this%y0 - k = x*uvw(1) + y*uvw(2) - c = x*x + y*y - this%r*this%r - quad = k*k - a*c - - if (quad < ZERO) then - ! no intersection with cylinder - - d = INFINITY - - elseif (coincident .or. abs(c) < FP_COINCIDENT) then - ! particle is on the cylinder, thus one distance is positive/negative - ! and the other is zero. The sign of k determines if we are facing in or - ! out - - if (k >= ZERO) then - d = INFINITY - else - d = (-k + sqrt(quad))/a - end if - - elseif (c < ZERO) then - ! particle is inside the cylinder, thus one distance must be negative - ! and one must be positive. The positive distance will be the one with - ! negative sign on sqrt(quad) - - d = (-k + sqrt(quad))/a - - else - ! particle is outside the cylinder, thus both distances are either - ! positive or negative. If positive, the smaller distance is the one - ! with positive sign on sqrt(quad) - - d = (-k - sqrt(quad))/a - if (d < ZERO) d = INFINITY - - end if - end if - end function z_cylinder_distance - - pure function z_cylinder_normal(this, xyz) result(uvw) - class(SurfaceZCylinder), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: uvw(3) - - uvw(1) = TWO*(xyz(1) - this%x0) - uvw(2) = TWO*(xyz(2) - this%y0) - uvw(3) = ZERO - end function z_cylinder_normal - -!=============================================================================== -! SurfaceSphere Implementation -!=============================================================================== - - pure function sphere_evaluate(this, xyz) result(f) - class(SurfaceSphere), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: f - - real(8) :: x, y, z - - x = xyz(1) - this%x0 - y = xyz(2) - this%y0 - z = xyz(3) - this%z0 - f = x*x + y*y + z*z - this%r*this%r - end function sphere_evaluate - - pure function sphere_distance(this, xyz, uvw, coincident) result(d) - class(SurfaceSphere), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8), intent(in) :: uvw(3) - logical, intent(in) :: coincident - real(8) :: d - - real(8) :: x, y, z, k, c, quad - - x = xyz(1) - this%x0 - y = xyz(2) - this%y0 - z = xyz(3) - this%z0 - k = x*uvw(1) + y*uvw(2) + z*uvw(3) - c = x*x + y*y + z*z - this%r*this%r - quad = k*k - c - - if (quad < ZERO) then - ! no intersection with sphere - - d = INFINITY - - elseif (coincident .or. abs(c) < FP_COINCIDENT) then - ! particle is on the sphere, thus one distance is positive/negative and - ! the other is zero. The sign of k determines if we are facing in or out - - if (k >= ZERO) then - d = INFINITY - else - d = -k + sqrt(quad) - end if - - elseif (c < ZERO) then - ! particle is inside the sphere, thus one distance must be negative and - ! one must be positive. The positive distance will be the one with - ! negative sign on sqrt(quad) - - d = -k + sqrt(quad) - - else - ! particle is outside the sphere, thus both distances are either positive - ! or negative. If positive, the smaller distance is the one with positive - ! sign on sqrt(quad) - - d = -k - sqrt(quad) - if (d < ZERO) d = INFINITY - - end if - end function sphere_distance - - pure function sphere_normal(this, xyz) result(uvw) - class(SurfaceSphere), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: uvw(3) - - uvw(:) = TWO*(xyz - [this%x0, this%y0, this%z0]) - end function sphere_normal - -!=============================================================================== -! SurfaceXCone Implementation -!=============================================================================== - - pure function x_cone_evaluate(this, xyz) result(f) - class(SurfaceXCone), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: f - - real(8) :: x, y, z - - x = xyz(1) - this%x0 - y = xyz(2) - this%y0 - z = xyz(3) - this%z0 - f = y*y + z*z - this%r2*x*x - end function x_cone_evaluate - - pure function x_cone_distance(this, xyz, uvw, coincident) result(d) - class(SurfaceXCone), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8), intent(in) :: uvw(3) - logical, intent(in) :: coincident - real(8) :: d - - real(8) :: x, y, z, k, a, b, c, quad - - x = xyz(1) - this%x0 - y = xyz(2) - this%y0 - z = xyz(3) - this%z0 - a = uvw(2)*uvw(2) + uvw(3)*uvw(3) - this%r2*uvw(1)*uvw(1) - k = y*uvw(2) + z*uvw(3) - this%r2*x*uvw(1) - c = y*y + z*z - this%r2*x*x - quad = k*k - a*c - - if (quad < ZERO) then - ! no intersection with cone - - d = INFINITY - - elseif (coincident .or. abs(c) < FP_COINCIDENT) then - ! particle is on the cone, thus one distance is positive/negative and the - ! other is zero. The sign of k determines which distance is zero and which - ! is not. - - if (k >= ZERO) then - d = (-k - sqrt(quad))/a - else - d = (-k + sqrt(quad))/a - end if - - else - ! calculate both solutions to the quadratic - quad = sqrt(quad) - d = (-k - quad)/a - b = (-k + quad)/a - - ! determine the smallest positive solution - if (d < ZERO) then - if (b > ZERO) then - d = b - end if - else - if (b > ZERO) d = min(d, b) - end if - end if - - ! If the distance was negative, set boundary distance to infinity - if (d <= ZERO) d = INFINITY - end function x_cone_distance - - pure function x_cone_normal(this, xyz) result(uvw) - class(SurfaceXCone), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: uvw(3) - - uvw(1) = -TWO*this%r2*(xyz(1) - this%x0) - uvw(2) = TWO*(xyz(2) - this%y0) - uvw(3) = TWO*(xyz(3) - this%z0) - end function x_cone_normal - -!=============================================================================== -! SurfaceYCone Implementation -!=============================================================================== - - pure function y_cone_evaluate(this, xyz) result(f) - class(SurfaceYCone), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: f - - real(8) :: x, y, z - - x = xyz(1) - this%x0 - y = xyz(2) - this%y0 - z = xyz(3) - this%z0 - f = x*x + z*z - this%r2*y*y - end function y_cone_evaluate - - pure function y_cone_distance(this, xyz, uvw, coincident) result(d) - class(SurfaceYCone), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8), intent(in) :: uvw(3) - logical, intent(in) :: coincident - real(8) :: d - - real(8) :: x, y, z, k, a, b, c, quad - - x = xyz(1) - this%x0 - y = xyz(2) - this%y0 - z = xyz(3) - this%z0 - a = uvw(1)*uvw(1) + uvw(3)*uvw(3) - this%r2*uvw(2)*uvw(2) - k = x*uvw(1) + z*uvw(3) - this%r2*y*uvw(2) - c = x*x + z*z - this%r2*y*y - quad = k*k - a*c - - if (quad < ZERO) then - ! no intersection with cone - - d = INFINITY - - elseif (coincident .or. abs(c) < FP_COINCIDENT) then - ! particle is on the cone, thus one distance is positive/negative and the - ! other is zero. The sign of k determines which distance is zero and which - ! is not. - - if (k >= ZERO) then - d = (-k - sqrt(quad))/a - else - d = (-k + sqrt(quad))/a - end if - - else - ! calculate both solutions to the quadratic - quad = sqrt(quad) - d = (-k - quad)/a - b = (-k + quad)/a - - ! determine the smallest positive solution - if (d < ZERO) then - if (b > ZERO) then - d = b - end if - else - if (b > ZERO) d = min(d, b) - end if - end if - - ! If the distance was negative, set boundary distance to infinity - if (d <= ZERO) d = INFINITY - end function y_cone_distance - - pure function y_cone_normal(this, xyz) result(uvw) - class(SurfaceYCone), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: uvw(3) - - uvw(1) = TWO*(xyz(1) - this%x0) - uvw(2) = -TWO*this%r2*(xyz(2) - this%y0) - uvw(3) = TWO*(xyz(3) - this%z0) - end function y_cone_normal - -!=============================================================================== -! SurfaceZCone Implementation -!=============================================================================== - - pure function z_cone_evaluate(this, xyz) result(f) - class(SurfaceZCone), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: f - - real(8) :: x, y, z - - x = xyz(1) - this%x0 - y = xyz(2) - this%y0 - z = xyz(3) - this%z0 - f = x*x + y*y - this%r2*z*z - end function z_cone_evaluate - - pure function z_cone_distance(this, xyz, uvw, coincident) result(d) - class(SurfaceZCone), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8), intent(in) :: uvw(3) - logical, intent(in) :: coincident - real(8) :: d - - real(8) :: x, y, z, k, a, b, c, quad - - x = xyz(1) - this%x0 - y = xyz(2) - this%y0 - z = xyz(3) - this%z0 - a = uvw(1)*uvw(1) + uvw(2)*uvw(2) - this%r2*uvw(3)*uvw(3) - k = x*uvw(1) + y*uvw(2) - this%r2*z*uvw(3) - c = x*x + y*y - this%r2*z*z - quad = k*k - a*c - - if (quad < ZERO) then - ! no intersection with cone - - d = INFINITY - - elseif (coincident .or. abs(c) < FP_COINCIDENT) then - ! particle is on the cone, thus one distance is positive/negative and the - ! other is zero. The sign of k determines which distance is zero and which - ! is not. - - if (k >= ZERO) then - d = (-k - sqrt(quad))/a - else - d = (-k + sqrt(quad))/a - end if - - else - ! calculate both solutions to the quadratic - quad = sqrt(quad) - d = (-k - quad)/a - b = (-k + quad)/a - - ! determine the smallest positive solution - if (d < ZERO) then - if (b > ZERO) then - d = b - end if - else - if (b > ZERO) d = min(d, b) - end if - end if - - ! If the distance was negative, set boundary distance to infinity - if (d <= ZERO) d = INFINITY - end function z_cone_distance - - pure function z_cone_normal(this, xyz) result(uvw) - class(SurfaceZCone), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: uvw(3) - - uvw(1) = TWO*(xyz(1) - this%x0) - uvw(2) = TWO*(xyz(2) - this%y0) - uvw(3) = -TWO*this%r2*(xyz(3) - this%z0) - end function z_cone_normal - -!=============================================================================== -! SurfaceQuadric Implementation -!=============================================================================== - - pure function quadric_evaluate(this, xyz) result(f) - class(SurfaceQuadric), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: f - - associate (x => xyz(1), y => xyz(2), z => xyz(3)) - f = x*(this%A*x + this%D*y + this%G) + & - y*(this%B*y + this%E*z + this%H) + & - z*(this%C*z + this%F*x + this%J) + this%K - end associate - end function quadric_evaluate - - pure function quadric_distance(this, xyz, uvw, coincident) result(d) - class(SurfaceQuadric), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8), intent(in) :: uvw(3) - logical, intent(in) :: coincident - real(8) :: d - - real(8) :: a, k, c - real(8) :: quad, b - - associate (x => xyz(1), y => xyz(2), z => xyz(3), & - u => uvw(1), v => uvw(2), w => uvw(3)) - - a = this%A*u*u + this%B*v*v + this%C*w*w + this%D*u*v + this%E*v*w + & - this%F*u*w - k = (this%A*u*x + this%B*v*y + this%C*w*z + HALF*(this%D*(u*y + v*x) + & - this%E*(v*z + w*y) + this%F*(w*x + u*z) + this%G*u + this%H*v + & - this%J*w)) - c = this%A*x*x + this%B*y*y + this%C*z*z + this%D*x*y + this%E*y*z + & - this%F*x*z + this%G*x + this%H*y + this%J*z + this%K - quad = k*k - a*c - - if (quad < ZERO) then - ! no intersection with surface - - d = INFINITY - - elseif (coincident .or. abs(c) < FP_COINCIDENT) then - ! particle is on the surface, thus one distance is positive/negative and - ! the other is zero. The sign of k determines which distance is zero and - ! which is not. - - if (k >= ZERO) then - d = (-k - sqrt(quad))/a - else - d = (-k + sqrt(quad))/a - end if - - else - ! calculate both solutions to the quadratic - quad = sqrt(quad) - d = (-k - quad)/a - b = (-k + quad)/a - - ! determine the smallest positive solution - if (d < ZERO) then - if (b > ZERO) then - d = b - end if - else - if (b > ZERO) d = min(d, b) - end if - end if - - ! If the distance was negative, set boundary distance to infinity - if (d <= ZERO) d = INFINITY - end associate - end function quadric_distance - - pure function quadric_normal(this, xyz) result(uvw) - class(SurfaceQuadric), intent(in) :: this - real(8), intent(in) :: xyz(3) - real(8) :: uvw(3) - - associate (x => xyz(1), y => xyz(2), z => xyz(3)) - uvw(1) = TWO*this%A*x + this%D*y + this%F*z + this%G - uvw(2) = TWO*this%B*y + this%D*x + this%E*z + this%H - uvw(3) = TWO*this%C*z + this%E*y + this%F*x + this%J - end associate - end function quadric_normal + integer(C_INT) :: id + id = surface_id_c(this % ptr) + end function surface_id + + pure function surface_bc(this) result(bc) + class(Surface), intent(in) :: this + integer(C_INT) :: bc + bc = surface_bc_c(this % ptr) + end function surface_bc + + pure function surface_sense(this, xyz, uvw) result(sense) + class(Surface), intent(in) :: this + real(C_DOUBLE), intent(in) :: xyz(3) + real(C_DOUBLE), intent(in) :: uvw(3) + logical(C_BOOL) :: sense + sense = surface_sense_c(this % ptr, xyz, uvw) + end function surface_sense + + pure subroutine surface_reflect(this, xyz, uvw) + class(Surface), intent(in) :: this + real(C_DOUBLE), intent(in) :: xyz(3); + real(C_DOUBLE), intent(inout) :: uvw(3); + call surface_reflect_c(this % ptr, xyz, uvw) + end subroutine surface_reflect + + pure function surface_distance(this, xyz, uvw, coincident) result(d) + class(Surface), intent(in) :: this + real(C_DOUBLE), intent(in) :: xyz(3); + real(C_DOUBLE), intent(in) :: uvw(3); + logical(C_BOOL), intent(in) :: coincident; + real(C_DOUBLE) :: d; + d = surface_distance_c(this % ptr, xyz, uvw, coincident) + end function surface_distance + + pure subroutine surface_normal(this, xyz, uvw) + class(Surface), intent(in) :: this + real(C_DOUBLE), intent(in) :: xyz(3); + real(C_DOUBLE), intent(out) :: uvw(3); + call surface_normal_c(this % ptr, xyz, uvw) + end subroutine surface_normal + + subroutine surface_to_hdf5(this, group) + class(Surface), intent(in) :: this + integer(HID_T), intent(in) :: group + call surface_to_hdf5_c(this % ptr, group) + end subroutine surface_to_hdf5 + + pure function surface_i_periodic(this) result(i_periodic) + class(Surface), intent(in) :: this + integer(C_INT) :: i_periodic + i_periodic = surface_i_periodic_c(this % ptr) + end function surface_i_periodic + + function surface_periodic(this, other, xyz, uvw) result(rotational) + class(Surface), intent(in) :: this + class(Surface), intent(in) :: other + real(C_DOUBLE), intent(inout) :: xyz(3); + real(C_DOUBLE), intent(inout) :: uvw(3); + logical(C_BOOL) :: rotational + rotational = surface_periodic_c(this % ptr, other % ptr, xyz, uvw) + end function surface_periodic !=============================================================================== ! FREE_MEMORY_SURFACES deallocates global arrays defined in this module !=============================================================================== subroutine free_memory_surfaces() - n_surfaces = 0 if (allocated(surfaces)) deallocate(surfaces) call surface_dict % clear() + call free_memory_surfaces_c() end subroutine free_memory_surfaces end module surface_header diff --git a/src/tallies/tally.F90 b/src/tallies/tally.F90 index 027e335e91..da0d8929c1 100644 --- a/src/tallies/tally.F90 +++ b/src/tallies/tally.F90 @@ -4258,7 +4258,7 @@ contains real(C_DOUBLE) :: k_tra ! Copy of batch tracklength estimate of keff real(C_DOUBLE) :: val -#ifdef MPI +#ifdef OPENMC_MPI ! Combine tally results onto master process if (reduce_tallies) call reduce_tally_results() #endif @@ -4306,7 +4306,7 @@ contains ! REDUCE_TALLY_RESULTS collects all the results from tallies onto one processor !=============================================================================== -#ifdef MPI +#ifdef OPENMC_MPI subroutine reduce_tally_results() integer :: i diff --git a/src/tallies/tally_filter_surface.F90 b/src/tallies/tally_filter_surface.F90 index daecd88917..df3bf36031 100644 --- a/src/tallies/tally_filter_surface.F90 +++ b/src/tallies/tally_filter_surface.F90 @@ -105,7 +105,7 @@ contains integer, intent(in) :: bin character(MAX_LINE_LEN) :: label - label = "Surface " // to_str(surfaces(this % surfaces(bin)) % obj % id) + label = "Surface " // to_str(surfaces(this % surfaces(bin)) % id()) end function text_label_surface end module tally_filter_surface diff --git a/src/tallies/trigger.F90 b/src/tallies/trigger.F90 index 9e9b06d205..ee2259ed48 100644 --- a/src/tallies/trigger.F90 +++ b/src/tallies/trigger.F90 @@ -2,7 +2,7 @@ module trigger use, intrinsic :: ISO_C_BINDING -#ifdef MPI +#ifdef OPENMC_MPI use message_passing #endif diff --git a/src/tracking.F90 b/src/tracking.F90 index f651517019..127da02226 100644 --- a/src/tracking.F90 +++ b/src/tracking.F90 @@ -4,7 +4,7 @@ module tracking use cross_section, only: calculate_xs use error, only: warning, write_message use geometry_header, only: cells - use geometry, only: find_cell, distance_to_boundary, cross_lattice, & + use geometry, only: find_cell, distance_to_boundary, cross_lattice,& check_cell_overlap use message_passing use mgxs_header @@ -292,20 +292,20 @@ contains real(8) :: v ! y-component of direction real(8) :: w ! z-component of direction real(8) :: norm ! "norm" of surface normal - real(8) :: d ! distance between point and plane real(8) :: xyz(3) ! Saved global coordinate integer :: i_surface ! index in surfaces logical :: rotational ! if rotational periodic BC applied logical :: found ! particle found in universe? class(Surface), pointer :: surf + class(Surface), pointer :: surf2 ! periodic partner surface i_surface = abs(p % surface) - surf => surfaces(i_surface)%obj + surf => surfaces(i_surface) if (verbosity >= 10 .or. trace) then - call write_message(" Crossing surface " // trim(to_str(surf % id))) + call write_message(" Crossing surface " // trim(to_str(surf % id()))) end if - if (surf % bc == BC_VACUUM .and. (run_mode /= MODE_PLOTTING)) then + if (surf % bc() == BC_VACUUM .and. (run_mode /= MODE_PLOTTING)) then ! ======================================================================= ! PARTICLE LEAKS OUT OF PROBLEM @@ -330,11 +330,11 @@ contains ! Display message if (verbosity >= 10 .or. trace) then call write_message(" Leaked out of surface " & - &// trim(to_str(surf % id))) + &// trim(to_str(surf % id()))) end if return - elseif (surf % bc == BC_REFLECT .and. (run_mode /= MODE_PLOTTING)) then + elseif (surf % bc() == BC_REFLECT .and. (run_mode /= MODE_PLOTTING)) then ! ======================================================================= ! PARTICLE REFLECTS FROM SURFACE @@ -357,7 +357,7 @@ contains end if ! Reflect particle off surface - call surf%reflect(p%coord(1)%xyz, p%coord(1)%uvw) + call surf % reflect(p%coord(1)%xyz, p%coord(1)%uvw) ! Make sure new particle direction is normalized u = p%coord(1)%uvw(1) @@ -378,7 +378,7 @@ contains call find_cell(p, found) if (.not. found) then call p % mark_as_lost("Couldn't find particle after reflecting& - & from surface " // trim(to_str(surf % id)) // ".") + & from surface " // trim(to_str(surf % id())) // ".") return end if @@ -388,10 +388,10 @@ contains ! Diagnostic message if (verbosity >= 10 .or. trace) then call write_message(" Reflected from surface " & - &// trim(to_str(surf%id))) + &// trim(to_str(surf%id()))) end if return - elseif (surf % bc == BC_PERIODIC .and. run_mode /= MODE_PLOTTING) then + elseif (surf % bc() == BC_PERIODIC .and. run_mode /= MODE_PLOTTING) then ! ======================================================================= ! PERIODIC BOUNDARY @@ -406,7 +406,6 @@ contains ! Score surface currents since reflection causes the direction of the ! particle to change -- artificially move the particle slightly back in ! case the surface crossing is coincident with a mesh boundary - if (active_current_tallies % size() > 0) then xyz = p % coord(1) % xyz p % coord(1) % xyz = p % coord(1) % xyz - TINY_BIT * p % coord(1) % uvw @@ -414,70 +413,19 @@ contains p % coord(1) % xyz = xyz end if - rotational = .false. - select type (surf) - type is (SurfaceXPlane) - select type (opposite => surfaces(surf % i_periodic) % obj) - type is (SurfaceXPlane) - p % coord(1) % xyz(1) = opposite % x0 - type is (SurfaceYPlane) - rotational = .true. + ! Get a pointer to the partner periodic surface. Offset the index to + ! correct for C vs. Fortran indexing. + surf2 => surfaces(surf % i_periodic() + 1) - ! Rotate direction - u = p % coord(1) % uvw(1) - v = p % coord(1) % uvw(2) - p % coord(1) % uvw(1) = v - p % coord(1) % uvw(2) = -u - - ! Rotate position - p % coord(1) % xyz(1) = surf % x0 + p % coord(1) % xyz(2) - opposite % y0 - p % coord(1) % xyz(2) = opposite % y0 - end select - - type is (SurfaceYPlane) - select type (opposite => surfaces(surf % i_periodic) % obj) - type is (SurfaceYPlane) - p % coord(1) % xyz(2) = opposite % y0 - type is (SurfaceXPlane) - rotational = .true. - - ! Rotate direction - u = p % coord(1) % uvw(1) - v = p % coord(1) % uvw(2) - p % coord(1) % uvw(1) = -v - p % coord(1) % uvw(2) = u - - ! Rotate position - p % coord(1) % xyz(2) = surf % y0 + p % coord(1) % xyz(1) - opposite % x0 - p % coord(1) % xyz(1) = opposite % x0 - end select - - type is (SurfaceZPlane) - select type (opposite => surfaces(surf % i_periodic) % obj) - type is (SurfaceZPlane) - p % coord(1) % xyz(3) = opposite % z0 - end select - - type is (SurfacePlane) - select type (opposite => surfaces(surf % i_periodic) % obj) - type is (SurfacePlane) - ! Get surface normal for opposite plane - xyz(:) = opposite % normal(p % coord(1) % xyz) - - ! Determine distance to plane - norm = xyz(1)*xyz(1) + xyz(2)*xyz(2) + xyz(3)*xyz(3) - d = opposite % evaluate(p % coord(1) % xyz) / norm - - ! Move particle along normal vector based on distance - p % coord(1) % xyz(:) = p % coord(1) % xyz(:) - d*xyz - end select - end select + ! Adjust the particle's location and direction. + rotational = surf2 % periodic_translate(surf, p % coord(1) % xyz, & + p % coord(1) % uvw) ! Reassign particle's surface if (rotational) then - p % surface = surf % i_periodic + p % surface = surf % i_periodic() + 1 else - p % surface = sign(surf % i_periodic, p % surface) + p % surface = sign(surf % i_periodic() + 1, p % surface) end if ! Figure out what cell particle is in now @@ -485,7 +433,8 @@ contains call find_cell(p, found) if (.not. found) then call p % mark_as_lost("Couldn't find particle after hitting & - &periodic boundary on surface " // trim(to_str(surf % id)) // ".") + &periodic boundary on surface " // trim(to_str(surf % id())) & + // ".") return end if @@ -495,7 +444,7 @@ contains ! Diagnostic message if (verbosity >= 10 .or. trace) then call write_message(" Hit periodic boundary on surface " & - // trim(to_str(surf%id))) + // trim(to_str(surf%id()))) end if return end if @@ -542,7 +491,7 @@ contains if (.not. found) then call p % mark_as_lost("After particle " // trim(to_str(p % id)) & - // " crossed surface " // trim(to_str(surf % id)) & + // " crossed surface " // trim(to_str(surf % id())) & // " it could not be located in any cell and it did not leak.") return end if diff --git a/src/volume_calc.F90 b/src/volume_calc.F90 index 5985a236b3..e374f21064 100644 --- a/src/volume_calc.F90 +++ b/src/volume_calc.F90 @@ -136,7 +136,7 @@ contains integer :: total_hits ! total hits for a single domain (summed over materials) integer :: min_samples ! minimum number of samples per process integer :: remainder ! leftover samples from uneven divide -#ifdef MPI +#ifdef OPENMC_MPI integer :: mpi_err ! MPI error code integer :: m ! index over materials integer :: n ! number of materials @@ -193,11 +193,13 @@ contains if (this % domain_type == FILTER_MATERIAL) then i_material = p % material - do i_domain = 1, size(this % domain_id) - if (i_material == materials(i_domain) % id) then - call check_hit(i_domain, i_material, indices, hits, n_mat) - end if - end do + if (i_material /= MATERIAL_VOID) then + do i_domain = 1, size(this % domain_id) + if (materials(i_material) % id == this % domain_id(i_domain)) then + call check_hit(i_domain, i_material, indices, hits, n_mat) + end if + end do + end if elseif (this % domain_type == FILTER_CELL) THEN do level = 1, p % n_coord @@ -279,7 +281,7 @@ contains total_hits = 0 if (master) then -#ifdef MPI +#ifdef OPENMC_MPI do j = 1, n_procs - 1 call MPI_RECV(n, 1, MPI_INTEGER, j, 0, mpi_intracomm, & MPI_STATUS_IGNORE, mpi_err) @@ -341,7 +343,7 @@ contains end do else -#ifdef MPI +#ifdef OPENMC_MPI n = master_indices(i_domain) % size() allocate(data(2*n)) do k = 0, n - 1 diff --git a/src/xml_interface.h b/src/xml_interface.h new file mode 100644 index 0000000000..778497562b --- /dev/null +++ b/src/xml_interface.h @@ -0,0 +1,48 @@ +#ifndef XML_INTERFACE_H +#define XML_INTERFACE_H + +#include // for std::transform +#include +#include + +#include "pugixml/pugixml.hpp" + + +namespace openmc { + +bool +check_for_node(pugi::xml_node node, const char *name) +{ + return node.attribute(name) || node.child(name); +} + + +std::string +get_node_value(pugi::xml_node node, const char *name) +{ + // Search for either an attribute or child tag and get the data as a char*. + const pugi::char_t *value_char; + if (node.attribute(name)) { + value_char = node.attribute(name).value(); + } else if (node.child(name)) { + value_char = node.child_value(name); + } else { + std::stringstream err_msg; + err_msg << "Node \"" << name << "\" is not a member of the \"" + << node.name() << "\" XML node"; + fatal_error(err_msg); + } + + // Convert to lowercase string. + std::string value(value_char); + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + + // Remove whitespace. + value.erase(0, value.find_first_not_of(" \t\r\n")); + value.erase(value.find_last_not_of(" \t\r\n") + 1); + + return value; +} + +} // namespace openmc +#endif // XML_INTERFACE_H diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/check_source.py b/tests/check_source.py index 3a79d44c28..78cef2a0cd 100755 --- a/tests/check_source.py +++ b/tests/check_source.py @@ -1,6 +1,4 @@ -#!/usr/bin/env python - -from __future__ import print_function +#!/usr/bin/env python3 import glob from string import whitespace diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000000..422c036fb3 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,17 @@ +from tests.regression_tests import config as regression_config + + +def pytest_addoption(parser): + parser.addoption('--exe') + parser.addoption('--mpi', action='store_true') + parser.addoption('--mpiexec') + parser.addoption('--mpi-np') + parser.addoption('--update', action='store_true') + parser.addoption('--build-inputs', action='store_true') + + +def pytest_configure(config): + opts = ['exe', 'mpi', 'mpiexec', 'mpi_np', 'update', 'build_inputs'] + for opt in opts: + if config.getoption(opt) is not None: + regression_config[opt] = config.getoption(opt) diff --git a/tests/convert_precision.py b/tests/convert_precision.py deleted file mode 100755 index 625fe49ca9..0000000000 --- a/tests/convert_precision.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -import os -import glob - -dirs = glob.glob('test_*') - -for adir in dirs: - - os.chdir(adir) - - files = glob.glob('results.py') - - if len(files) > 0: - - files = files[0] - with open(files, 'r') as fh: - intxt = fh.read() - intxt = intxt.replace('14.8E', '12.6E') - with open(files, 'w') as fh: - fh.write(intxt) - - os.chdir('..') diff --git a/tests/readme.rst b/tests/readme.rst index f2e2bb1c12..5e4b0227be 100644 --- a/tests/readme.rst +++ b/tests/readme.rst @@ -1,58 +1 @@ -================= -OpenMC Test Suite -================= - -The purpose of this test suite is to ensure that OpenMC compiles using various -combinations of compiler flags and options and that all user input options can -be used successfully without breaking the code. The test suite is based on -regression or integrated testing where different types of input files are -configured and the full OpenMC code is executed. Results from simulations -are compared with expected results. The test suite is comprised of many -build configurations (e.g. debug, mpi, hdf5) and the actual tests which -reside in sub-directories in the tests directory. - -The test suite is designed to integrate with cmake using ctest_. To run the -full test suite run: - -.. code-block:: sh - - python run_tests.py - -The test suite is configured to run with cross sections from NNDC_. To -download these cross sections please do the following: - -.. code-block:: sh - - cd ../data - python get_nndc.py - export CROSS_SECTIONS=/nndc/cross_sections.xml - -The environmental variable **CROSS_SECTIONS** can be used to quickly switch -between the cross sections set for the test suite and cross section set for -your simulations. - -A subset of build configurations and/or tests can be run. To see how to use -the script run: - -.. code-block:: sh - - python run_tests.py --help - -As an example, say we want to run all tests with debug flags only on tests -that have cone and plot in their name. Also, we would like to run this on -4 processors. We can run: - -.. code-block:: sh - - python run_tests.py -j 4 -C debug -R "cone|plot" - -Note that standard regular expression syntax is used for selecting build -configurations and tests. To print out a list of build configurations, we -can run: - -.. code-block:: sh - - python run_tests.py -p - -.. _ctest: http://www.cmake.org/cmake/help/v2.8.12/ctest.html -.. _NNDC: http://http://www.nndc.bnl.gov/endf/b7.1/acefiles.html +See docs/source/devguide/tests.rst for information on the OpenMC test suite. diff --git a/tests/regression_tests/__init__.py b/tests/regression_tests/__init__.py new file mode 100644 index 0000000000..965a7f94db --- /dev/null +++ b/tests/regression_tests/__init__.py @@ -0,0 +1,9 @@ +# Test configuration options for regression tests +config = { + 'exe': 'openmc', + 'mpi': False, + 'mpiexec': 'mpiexec', + 'mpi_np': '2', + 'update': False, + 'build_inputs': False +} diff --git a/tests/regression_tests/asymmetric_lattice/__init__.py b/tests/regression_tests/asymmetric_lattice/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_asymmetric_lattice/inputs_true.dat b/tests/regression_tests/asymmetric_lattice/inputs_true.dat similarity index 100% rename from tests/test_asymmetric_lattice/inputs_true.dat rename to tests/regression_tests/asymmetric_lattice/inputs_true.dat diff --git a/tests/test_asymmetric_lattice/results_true.dat b/tests/regression_tests/asymmetric_lattice/results_true.dat similarity index 100% rename from tests/test_asymmetric_lattice/results_true.dat rename to tests/regression_tests/asymmetric_lattice/results_true.dat diff --git a/tests/test_asymmetric_lattice/test_asymmetric_lattice.py b/tests/regression_tests/asymmetric_lattice/test.py similarity index 94% rename from tests/test_asymmetric_lattice/test_asymmetric_lattice.py rename to tests/regression_tests/asymmetric_lattice/test.py index 58c1b22ab5..d6a0ab9b73 100644 --- a/tests/test_asymmetric_lattice/test_asymmetric_lattice.py +++ b/tests/regression_tests/asymmetric_lattice/test.py @@ -1,17 +1,15 @@ -#!/usr/bin/env python - import os -import sys import glob import hashlib -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness + import openmc +from tests.testing_harness import PyAPITestHarness + class AsymmetricLatticeTestHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): - super(AsymmetricLatticeTestHarness, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Extract universes encapsulating fuel and water assemblies geometry = self._model.geometry @@ -90,6 +88,6 @@ class AsymmetricLatticeTestHarness(PyAPITestHarness): return outstr -if __name__ == '__main__': +def test_asymmetric_lattice(): harness = AsymmetricLatticeTestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/cmfd_feed/__init__.py b/tests/regression_tests/cmfd_feed/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_cmfd_feed/cmfd.xml b/tests/regression_tests/cmfd_feed/cmfd.xml similarity index 100% rename from tests/test_cmfd_feed/cmfd.xml rename to tests/regression_tests/cmfd_feed/cmfd.xml diff --git a/tests/test_cmfd_feed/geometry.xml b/tests/regression_tests/cmfd_feed/geometry.xml similarity index 100% rename from tests/test_cmfd_feed/geometry.xml rename to tests/regression_tests/cmfd_feed/geometry.xml diff --git a/tests/test_cmfd_feed/materials.xml b/tests/regression_tests/cmfd_feed/materials.xml similarity index 100% rename from tests/test_cmfd_feed/materials.xml rename to tests/regression_tests/cmfd_feed/materials.xml diff --git a/tests/test_cmfd_feed/results_true.dat b/tests/regression_tests/cmfd_feed/results_true.dat similarity index 100% rename from tests/test_cmfd_feed/results_true.dat rename to tests/regression_tests/cmfd_feed/results_true.dat diff --git a/tests/test_cmfd_feed/settings.xml b/tests/regression_tests/cmfd_feed/settings.xml similarity index 100% rename from tests/test_cmfd_feed/settings.xml rename to tests/regression_tests/cmfd_feed/settings.xml diff --git a/tests/test_cmfd_feed/tallies.xml b/tests/regression_tests/cmfd_feed/tallies.xml similarity index 100% rename from tests/test_cmfd_feed/tallies.xml rename to tests/regression_tests/cmfd_feed/tallies.xml diff --git a/tests/regression_tests/cmfd_feed/test.py b/tests/regression_tests/cmfd_feed/test.py new file mode 100644 index 0000000000..4fc39d96ad --- /dev/null +++ b/tests/regression_tests/cmfd_feed/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import CMFDTestHarness + + +def test_cmfd_feed(): + harness = CMFDTestHarness('statepoint.20.h5') + harness.main() diff --git a/tests/regression_tests/cmfd_nofeed/__init__.py b/tests/regression_tests/cmfd_nofeed/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_cmfd_nofeed/cmfd.xml b/tests/regression_tests/cmfd_nofeed/cmfd.xml similarity index 100% rename from tests/test_cmfd_nofeed/cmfd.xml rename to tests/regression_tests/cmfd_nofeed/cmfd.xml diff --git a/tests/test_cmfd_nofeed/geometry.xml b/tests/regression_tests/cmfd_nofeed/geometry.xml similarity index 100% rename from tests/test_cmfd_nofeed/geometry.xml rename to tests/regression_tests/cmfd_nofeed/geometry.xml diff --git a/tests/test_cmfd_nofeed/materials.xml b/tests/regression_tests/cmfd_nofeed/materials.xml similarity index 100% rename from tests/test_cmfd_nofeed/materials.xml rename to tests/regression_tests/cmfd_nofeed/materials.xml diff --git a/tests/test_cmfd_nofeed/results_true.dat b/tests/regression_tests/cmfd_nofeed/results_true.dat similarity index 100% rename from tests/test_cmfd_nofeed/results_true.dat rename to tests/regression_tests/cmfd_nofeed/results_true.dat diff --git a/tests/test_cmfd_nofeed/settings.xml b/tests/regression_tests/cmfd_nofeed/settings.xml similarity index 100% rename from tests/test_cmfd_nofeed/settings.xml rename to tests/regression_tests/cmfd_nofeed/settings.xml diff --git a/tests/test_cmfd_nofeed/tallies.xml b/tests/regression_tests/cmfd_nofeed/tallies.xml similarity index 100% rename from tests/test_cmfd_nofeed/tallies.xml rename to tests/regression_tests/cmfd_nofeed/tallies.xml diff --git a/tests/regression_tests/cmfd_nofeed/test.py b/tests/regression_tests/cmfd_nofeed/test.py new file mode 100644 index 0000000000..a3c7301738 --- /dev/null +++ b/tests/regression_tests/cmfd_nofeed/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import CMFDTestHarness + + +def test_cmfd_nofeed(): + harness = CMFDTestHarness('statepoint.20.h5') + harness.main() diff --git a/tests/regression_tests/complex_cell/__init__.py b/tests/regression_tests/complex_cell/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_complex_cell/geometry.xml b/tests/regression_tests/complex_cell/geometry.xml similarity index 100% rename from tests/test_complex_cell/geometry.xml rename to tests/regression_tests/complex_cell/geometry.xml diff --git a/tests/test_complex_cell/materials.xml b/tests/regression_tests/complex_cell/materials.xml similarity index 100% rename from tests/test_complex_cell/materials.xml rename to tests/regression_tests/complex_cell/materials.xml diff --git a/tests/test_complex_cell/results_true.dat b/tests/regression_tests/complex_cell/results_true.dat similarity index 100% rename from tests/test_complex_cell/results_true.dat rename to tests/regression_tests/complex_cell/results_true.dat diff --git a/tests/test_complex_cell/settings.xml b/tests/regression_tests/complex_cell/settings.xml similarity index 100% rename from tests/test_complex_cell/settings.xml rename to tests/regression_tests/complex_cell/settings.xml diff --git a/tests/test_complex_cell/tallies.xml b/tests/regression_tests/complex_cell/tallies.xml similarity index 100% rename from tests/test_complex_cell/tallies.xml rename to tests/regression_tests/complex_cell/tallies.xml diff --git a/tests/regression_tests/complex_cell/test.py b/tests/regression_tests/complex_cell/test.py new file mode 100755 index 0000000000..77cbd6cb7d --- /dev/null +++ b/tests/regression_tests/complex_cell/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_complex_cell(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/confidence_intervals/__init__.py b/tests/regression_tests/confidence_intervals/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_confidence_intervals/geometry.xml b/tests/regression_tests/confidence_intervals/geometry.xml similarity index 100% rename from tests/test_confidence_intervals/geometry.xml rename to tests/regression_tests/confidence_intervals/geometry.xml diff --git a/tests/test_confidence_intervals/materials.xml b/tests/regression_tests/confidence_intervals/materials.xml similarity index 100% rename from tests/test_confidence_intervals/materials.xml rename to tests/regression_tests/confidence_intervals/materials.xml diff --git a/tests/test_confidence_intervals/results_true.dat b/tests/regression_tests/confidence_intervals/results_true.dat similarity index 100% rename from tests/test_confidence_intervals/results_true.dat rename to tests/regression_tests/confidence_intervals/results_true.dat diff --git a/tests/test_confidence_intervals/settings.xml b/tests/regression_tests/confidence_intervals/settings.xml similarity index 100% rename from tests/test_confidence_intervals/settings.xml rename to tests/regression_tests/confidence_intervals/settings.xml diff --git a/tests/test_confidence_intervals/tallies.xml b/tests/regression_tests/confidence_intervals/tallies.xml similarity index 100% rename from tests/test_confidence_intervals/tallies.xml rename to tests/regression_tests/confidence_intervals/tallies.xml diff --git a/tests/regression_tests/confidence_intervals/test.py b/tests/regression_tests/confidence_intervals/test.py new file mode 100755 index 0000000000..3227418280 --- /dev/null +++ b/tests/regression_tests/confidence_intervals/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_confidence_intervals(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/conftest.py b/tests/regression_tests/conftest.py new file mode 100644 index 0000000000..00ef247dc4 --- /dev/null +++ b/tests/regression_tests/conftest.py @@ -0,0 +1,15 @@ +import openmc +import pytest + + +@pytest.fixture(scope='module', autouse=True) +def setup_regression_test(request): + # Reset autogenerated IDs assigned to OpenMC objects + openmc.reset_auto_ids() + + # Change to test directory + olddir = request.fspath.dirpath().chdir() + try: + yield + finally: + olddir.chdir() diff --git a/tests/regression_tests/create_fission_neutrons/__init__.py b/tests/regression_tests/create_fission_neutrons/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_create_fission_neutrons/inputs_true.dat b/tests/regression_tests/create_fission_neutrons/inputs_true.dat similarity index 100% rename from tests/test_create_fission_neutrons/inputs_true.dat rename to tests/regression_tests/create_fission_neutrons/inputs_true.dat diff --git a/tests/test_create_fission_neutrons/results_true.dat b/tests/regression_tests/create_fission_neutrons/results_true.dat similarity index 100% rename from tests/test_create_fission_neutrons/results_true.dat rename to tests/regression_tests/create_fission_neutrons/results_true.dat diff --git a/tests/test_create_fission_neutrons/test_create_fission_neutrons.py b/tests/regression_tests/create_fission_neutrons/test.py similarity index 94% rename from tests/test_create_fission_neutrons/test_create_fission_neutrons.py rename to tests/regression_tests/create_fission_neutrons/test.py index 87abeda7fe..f1c1d072cb 100755 --- a/tests/test_create_fission_neutrons/test_create_fission_neutrons.py +++ b/tests/regression_tests/create_fission_neutrons/test.py @@ -1,11 +1,7 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness import openmc +from tests.testing_harness import PyAPITestHarness + class CreateFissionNeutronsTestHarness(PyAPITestHarness): def _build_inputs(self): @@ -69,6 +65,6 @@ class CreateFissionNeutronsTestHarness(PyAPITestHarness): return outstr -if __name__ == '__main__': +def test_create_fission_neutrons(): harness = CreateFissionNeutronsTestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/density/__init__.py b/tests/regression_tests/density/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_density/geometry.xml b/tests/regression_tests/density/geometry.xml similarity index 100% rename from tests/test_density/geometry.xml rename to tests/regression_tests/density/geometry.xml diff --git a/tests/test_density/materials.xml b/tests/regression_tests/density/materials.xml similarity index 100% rename from tests/test_density/materials.xml rename to tests/regression_tests/density/materials.xml diff --git a/tests/test_density/results_true.dat b/tests/regression_tests/density/results_true.dat similarity index 100% rename from tests/test_density/results_true.dat rename to tests/regression_tests/density/results_true.dat diff --git a/tests/test_density/settings.xml b/tests/regression_tests/density/settings.xml similarity index 100% rename from tests/test_density/settings.xml rename to tests/regression_tests/density/settings.xml diff --git a/tests/regression_tests/density/test.py b/tests/regression_tests/density/test.py new file mode 100644 index 0000000000..f3ae6b4144 --- /dev/null +++ b/tests/regression_tests/density/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_density(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/diff_tally/__init__.py b/tests/regression_tests/diff_tally/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_diff_tally/inputs_true.dat b/tests/regression_tests/diff_tally/inputs_true.dat similarity index 100% rename from tests/test_diff_tally/inputs_true.dat rename to tests/regression_tests/diff_tally/inputs_true.dat diff --git a/tests/test_diff_tally/results_true.dat b/tests/regression_tests/diff_tally/results_true.dat similarity index 100% rename from tests/test_diff_tally/results_true.dat rename to tests/regression_tests/diff_tally/results_true.dat diff --git a/tests/test_diff_tally/test_diff_tally.py b/tests/regression_tests/diff_tally/test.py similarity index 76% rename from tests/test_diff_tally/test_diff_tally.py rename to tests/regression_tests/diff_tally/test.py index 02798e70e0..de5423fd9f 100644 --- a/tests/test_diff_tally/test_diff_tally.py +++ b/tests/regression_tests/diff_tally/test.py @@ -1,18 +1,16 @@ -#!/usr/bin/env python - import glob import os -import sys import pandas as pd - -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness import openmc +import pytest + +from tests.testing_harness import PyAPITestHarness + class DiffTallyTestHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): - super(DiffTallyTestHarness, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Set settings explicitly self._model.settings.batches = 3 @@ -58,30 +56,25 @@ class DiffTallyTestHarness(PyAPITestHarness): # Cover the flux score. for i in range(5): t = openmc.Tally() - t.add_score('flux') - t.add_filter(filt_mats) + t.scores = ['flux'] + t.filters = [filt_mats] t.derivative = derivs[i] self._model.tallies.append(t) # Cover supported scores with a collision estimator. for i in range(5): t = openmc.Tally() - t.add_score('total') - t.add_score('absorption') - t.add_score('scatter') - t.add_score('fission') - t.add_score('nu-fission') - t.add_filter(filt_mats) - t.add_nuclide('total') - t.add_nuclide('U235') + t.scores = ['total', 'absorption', 'scatter', 'fission', 'nu-fission'] + t.filters = [filt_mats] + t.nuclides = ['total', 'U235'] t.derivative = derivs[i] self._model.tallies.append(t) # Cover an analog estimator. for i in range(5): t = openmc.Tally() - t.add_score('absorption') - t.add_filter(filt_mats) + t.scores = ['absorption'] + t.filters = [filt_mats] t.estimator = 'analog' t.derivative = derivs[i] self._model.tallies.append(t) @@ -89,23 +82,18 @@ class DiffTallyTestHarness(PyAPITestHarness): # Energyout filter and total nuclide for the density derivatives. for i in range(2): t = openmc.Tally() - t.add_score('nu-fission') - t.add_score('scatter') - t.add_filter(filt_mats) - t.add_filter(filt_eout) - t.add_nuclide('total') - t.add_nuclide('U235') + t.scores = ['nu-fission', 'scatter'] + t.filters = [filt_mats, filt_eout] + t.nuclides = ['total', 'U235'] t.derivative = derivs[i] self._model.tallies.append(t) # Energyout filter without total nuclide for other derivatives. for i in range(2, 5): t = openmc.Tally() - t.add_score('nu-fission') - t.add_score('scatter') - t.add_filter(filt_mats) - t.add_filter(filt_eout) - t.add_nuclide('U235') + t.scores = ['nu-fission', 'scatter'] + t.filters = [filt_mats, filt_eout] + t.nuclides = ['U235'] t.derivative = derivs[i] self._model.tallies.append(t) @@ -125,6 +113,9 @@ class DiffTallyTestHarness(PyAPITestHarness): return df.to_csv(None, columns=cols, index=False, float_format='%.7e') -if __name__ == '__main__': +@pytest.mark.skipif('OPENMC_MULTIPOLE_LIBRARY' not in os.environ, + reason='OPENMC_MULTIPOLE_LIBRARY environment variable ' + 'must be set') +def test_diff_tally(): harness = DiffTallyTestHarness('statepoint.3.h5') harness.main() diff --git a/tests/regression_tests/distribmat/__init__.py b/tests/regression_tests/distribmat/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_distribmat/inputs_true.dat b/tests/regression_tests/distribmat/inputs_true.dat similarity index 100% rename from tests/test_distribmat/inputs_true.dat rename to tests/regression_tests/distribmat/inputs_true.dat diff --git a/tests/test_distribmat/results_true.dat b/tests/regression_tests/distribmat/results_true.dat similarity index 100% rename from tests/test_distribmat/results_true.dat rename to tests/regression_tests/distribmat/results_true.dat diff --git a/tests/test_distribmat/test_distribmat.py b/tests/regression_tests/distribmat/test.py similarity index 93% rename from tests/test_distribmat/test_distribmat.py rename to tests/regression_tests/distribmat/test.py index 9dd5d319a2..de1b778772 100644 --- a/tests/test_distribmat/test_distribmat.py +++ b/tests/regression_tests/distribmat/test.py @@ -1,11 +1,7 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness, PyAPITestHarness import openmc +from tests.testing_harness import TestHarness, PyAPITestHarness + class DistribmatTestHarness(PyAPITestHarness): def _build_inputs(self): @@ -97,12 +93,12 @@ class DistribmatTestHarness(PyAPITestHarness): plots.export_to_xml() def _get_results(self): - outstr = super(DistribmatTestHarness, self)._get_results() + outstr = super()._get_results() su = openmc.Summary('summary.h5') outstr += str(su.geometry.get_all_cells()[11]) return outstr -if __name__ == '__main__': +def test_distribmat(): harness = DistribmatTestHarness('statepoint.5.h5') harness.main() diff --git a/tests/regression_tests/eigenvalue_genperbatch/__init__.py b/tests/regression_tests/eigenvalue_genperbatch/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_eigenvalue_genperbatch/geometry.xml b/tests/regression_tests/eigenvalue_genperbatch/geometry.xml similarity index 100% rename from tests/test_eigenvalue_genperbatch/geometry.xml rename to tests/regression_tests/eigenvalue_genperbatch/geometry.xml diff --git a/tests/test_eigenvalue_genperbatch/materials.xml b/tests/regression_tests/eigenvalue_genperbatch/materials.xml similarity index 100% rename from tests/test_eigenvalue_genperbatch/materials.xml rename to tests/regression_tests/eigenvalue_genperbatch/materials.xml diff --git a/tests/test_eigenvalue_genperbatch/results_true.dat b/tests/regression_tests/eigenvalue_genperbatch/results_true.dat similarity index 100% rename from tests/test_eigenvalue_genperbatch/results_true.dat rename to tests/regression_tests/eigenvalue_genperbatch/results_true.dat diff --git a/tests/test_eigenvalue_genperbatch/settings.xml b/tests/regression_tests/eigenvalue_genperbatch/settings.xml similarity index 100% rename from tests/test_eigenvalue_genperbatch/settings.xml rename to tests/regression_tests/eigenvalue_genperbatch/settings.xml diff --git a/tests/regression_tests/eigenvalue_genperbatch/test.py b/tests/regression_tests/eigenvalue_genperbatch/test.py new file mode 100644 index 0000000000..0dfb112428 --- /dev/null +++ b/tests/regression_tests/eigenvalue_genperbatch/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_eigenvalue_genperbatch(): + harness = TestHarness('statepoint.7.h5') + harness.main() diff --git a/tests/regression_tests/eigenvalue_no_inactive/__init__.py b/tests/regression_tests/eigenvalue_no_inactive/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_eigenvalue_no_inactive/geometry.xml b/tests/regression_tests/eigenvalue_no_inactive/geometry.xml similarity index 100% rename from tests/test_eigenvalue_no_inactive/geometry.xml rename to tests/regression_tests/eigenvalue_no_inactive/geometry.xml diff --git a/tests/test_eigenvalue_no_inactive/materials.xml b/tests/regression_tests/eigenvalue_no_inactive/materials.xml similarity index 100% rename from tests/test_eigenvalue_no_inactive/materials.xml rename to tests/regression_tests/eigenvalue_no_inactive/materials.xml diff --git a/tests/test_eigenvalue_no_inactive/results_true.dat b/tests/regression_tests/eigenvalue_no_inactive/results_true.dat similarity index 100% rename from tests/test_eigenvalue_no_inactive/results_true.dat rename to tests/regression_tests/eigenvalue_no_inactive/results_true.dat diff --git a/tests/test_eigenvalue_no_inactive/settings.xml b/tests/regression_tests/eigenvalue_no_inactive/settings.xml similarity index 100% rename from tests/test_eigenvalue_no_inactive/settings.xml rename to tests/regression_tests/eigenvalue_no_inactive/settings.xml diff --git a/tests/regression_tests/eigenvalue_no_inactive/test.py b/tests/regression_tests/eigenvalue_no_inactive/test.py new file mode 100644 index 0000000000..5ab4900153 --- /dev/null +++ b/tests/regression_tests/eigenvalue_no_inactive/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_eigenvalue_no_inactive(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/energy_cutoff/__init__.py b/tests/regression_tests/energy_cutoff/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_energy_cutoff/inputs_true.dat b/tests/regression_tests/energy_cutoff/inputs_true.dat similarity index 100% rename from tests/test_energy_cutoff/inputs_true.dat rename to tests/regression_tests/energy_cutoff/inputs_true.dat diff --git a/tests/test_energy_cutoff/results_true.dat b/tests/regression_tests/energy_cutoff/results_true.dat similarity index 100% rename from tests/test_energy_cutoff/results_true.dat rename to tests/regression_tests/energy_cutoff/results_true.dat diff --git a/tests/test_energy_cutoff/test_energy_cutoff.py b/tests/regression_tests/energy_cutoff/test.py similarity index 94% rename from tests/test_energy_cutoff/test_energy_cutoff.py rename to tests/regression_tests/energy_cutoff/test.py index 3aa3400c7a..8b16149947 100755 --- a/tests/test_energy_cutoff/test_energy_cutoff.py +++ b/tests/regression_tests/energy_cutoff/test.py @@ -1,11 +1,7 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness import openmc +from tests.testing_harness import PyAPITestHarness + class EnergyCutoffTestHarness(PyAPITestHarness): def _build_inputs(self): @@ -73,6 +69,6 @@ class EnergyCutoffTestHarness(PyAPITestHarness): return outstr -if __name__ == '__main__': +def test_energy_cutoff(): harness = EnergyCutoffTestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/energy_grid/__init__.py b/tests/regression_tests/energy_grid/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_energy_grid/geometry.xml b/tests/regression_tests/energy_grid/geometry.xml similarity index 100% rename from tests/test_energy_grid/geometry.xml rename to tests/regression_tests/energy_grid/geometry.xml diff --git a/tests/test_energy_grid/materials.xml b/tests/regression_tests/energy_grid/materials.xml similarity index 100% rename from tests/test_energy_grid/materials.xml rename to tests/regression_tests/energy_grid/materials.xml diff --git a/tests/test_energy_grid/results_true.dat b/tests/regression_tests/energy_grid/results_true.dat similarity index 100% rename from tests/test_energy_grid/results_true.dat rename to tests/regression_tests/energy_grid/results_true.dat diff --git a/tests/test_energy_grid/settings.xml b/tests/regression_tests/energy_grid/settings.xml similarity index 100% rename from tests/test_energy_grid/settings.xml rename to tests/regression_tests/energy_grid/settings.xml diff --git a/tests/regression_tests/energy_grid/test.py b/tests/regression_tests/energy_grid/test.py new file mode 100644 index 0000000000..889bdfbc61 --- /dev/null +++ b/tests/regression_tests/energy_grid/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_energy_grid(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/energy_laws/__init__.py b/tests/regression_tests/energy_laws/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_energy_laws/geometry.xml b/tests/regression_tests/energy_laws/geometry.xml similarity index 100% rename from tests/test_energy_laws/geometry.xml rename to tests/regression_tests/energy_laws/geometry.xml diff --git a/tests/test_energy_laws/materials.xml b/tests/regression_tests/energy_laws/materials.xml similarity index 100% rename from tests/test_energy_laws/materials.xml rename to tests/regression_tests/energy_laws/materials.xml diff --git a/tests/test_energy_laws/results_true.dat b/tests/regression_tests/energy_laws/results_true.dat similarity index 100% rename from tests/test_energy_laws/results_true.dat rename to tests/regression_tests/energy_laws/results_true.dat diff --git a/tests/test_energy_laws/settings.xml b/tests/regression_tests/energy_laws/settings.xml similarity index 100% rename from tests/test_energy_laws/settings.xml rename to tests/regression_tests/energy_laws/settings.xml diff --git a/tests/test_energy_laws/test_energy_laws.py b/tests/regression_tests/energy_laws/test.py similarity index 81% rename from tests/test_energy_laws/test_energy_laws.py rename to tests/regression_tests/energy_laws/test.py index 254126ff30..8f9bbde356 100644 --- a/tests/test_energy_laws/test_energy_laws.py +++ b/tests/regression_tests/energy_laws/test.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - """The purpose of this test is to provide coverage of energy distributions that are not covered in other tests. It has a single material with the following nuclides: @@ -18,13 +16,9 @@ that use linear-linear interpolation. """ -import glob -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness +from tests.testing_harness import TestHarness -if __name__ == '__main__': +def test_energy_laws(): harness = TestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/enrichment/__init__.py b/tests/regression_tests/enrichment/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_enrichment/test_enrichment.py b/tests/regression_tests/enrichment/test.py similarity index 88% rename from tests/test_enrichment/test_enrichment.py rename to tests/regression_tests/enrichment/test.py index fa65d6dce0..a20838c7cb 100644 --- a/tests/test_enrichment/test_enrichment.py +++ b/tests/regression_tests/enrichment/test.py @@ -1,17 +1,13 @@ -#!/usr/bin/env python - import os import sys import numpy as np -sys.path.insert(0, os.pardir) -sys.path.insert(0, os.path.join(os.pardir, os.pardir)) from openmc import Material from openmc.data import NATURAL_ABUNDANCE, atomic_mass -if __name__ == '__main__': +def test_enrichment(): # This test doesn't require an OpenMC run. We just need to make sure the # element.expand() method expands Uranium to the proper enrichment. diff --git a/tests/regression_tests/entropy/__init__.py b/tests/regression_tests/entropy/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_entropy/geometry.xml b/tests/regression_tests/entropy/geometry.xml similarity index 100% rename from tests/test_entropy/geometry.xml rename to tests/regression_tests/entropy/geometry.xml diff --git a/tests/test_entropy/materials.xml b/tests/regression_tests/entropy/materials.xml similarity index 100% rename from tests/test_entropy/materials.xml rename to tests/regression_tests/entropy/materials.xml diff --git a/tests/test_entropy/results_true.dat b/tests/regression_tests/entropy/results_true.dat similarity index 100% rename from tests/test_entropy/results_true.dat rename to tests/regression_tests/entropy/results_true.dat diff --git a/tests/test_entropy/settings.xml b/tests/regression_tests/entropy/settings.xml similarity index 100% rename from tests/test_entropy/settings.xml rename to tests/regression_tests/entropy/settings.xml diff --git a/tests/test_entropy/test_entropy.py b/tests/regression_tests/entropy/test.py similarity index 85% rename from tests/test_entropy/test_entropy.py rename to tests/regression_tests/entropy/test.py index 36e2170af8..0f1052b6b8 100644 --- a/tests/test_entropy/test_entropy.py +++ b/tests/regression_tests/entropy/test.py @@ -1,12 +1,10 @@ -#!/usr/bin/env python - import glob import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness + from openmc import StatePoint +from tests.testing_harness import TestHarness + class EntropyTestHarness(TestHarness): def _get_results(self): @@ -26,6 +24,6 @@ class EntropyTestHarness(TestHarness): return outstr -if __name__ == '__main__': +def test_entropy(): harness = EntropyTestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/filter_distribcell/__init__.py b/tests/regression_tests/filter_distribcell/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_filter_distribcell/case-1/geometry.xml b/tests/regression_tests/filter_distribcell/case-1/geometry.xml similarity index 100% rename from tests/test_filter_distribcell/case-1/geometry.xml rename to tests/regression_tests/filter_distribcell/case-1/geometry.xml diff --git a/tests/test_filter_distribcell/case-1/materials.xml b/tests/regression_tests/filter_distribcell/case-1/materials.xml similarity index 100% rename from tests/test_filter_distribcell/case-1/materials.xml rename to tests/regression_tests/filter_distribcell/case-1/materials.xml diff --git a/tests/test_filter_distribcell/case-1/results_true.dat b/tests/regression_tests/filter_distribcell/case-1/results_true.dat similarity index 100% rename from tests/test_filter_distribcell/case-1/results_true.dat rename to tests/regression_tests/filter_distribcell/case-1/results_true.dat diff --git a/tests/test_filter_distribcell/case-1/settings.xml b/tests/regression_tests/filter_distribcell/case-1/settings.xml similarity index 100% rename from tests/test_filter_distribcell/case-1/settings.xml rename to tests/regression_tests/filter_distribcell/case-1/settings.xml diff --git a/tests/test_filter_distribcell/case-1/tallies.xml b/tests/regression_tests/filter_distribcell/case-1/tallies.xml similarity index 100% rename from tests/test_filter_distribcell/case-1/tallies.xml rename to tests/regression_tests/filter_distribcell/case-1/tallies.xml diff --git a/tests/test_filter_distribcell/case-2/geometry.xml b/tests/regression_tests/filter_distribcell/case-2/geometry.xml similarity index 100% rename from tests/test_filter_distribcell/case-2/geometry.xml rename to tests/regression_tests/filter_distribcell/case-2/geometry.xml diff --git a/tests/test_filter_distribcell/case-2/materials.xml b/tests/regression_tests/filter_distribcell/case-2/materials.xml similarity index 100% rename from tests/test_filter_distribcell/case-2/materials.xml rename to tests/regression_tests/filter_distribcell/case-2/materials.xml diff --git a/tests/test_filter_distribcell/case-2/results_true.dat b/tests/regression_tests/filter_distribcell/case-2/results_true.dat similarity index 100% rename from tests/test_filter_distribcell/case-2/results_true.dat rename to tests/regression_tests/filter_distribcell/case-2/results_true.dat diff --git a/tests/test_filter_distribcell/case-2/settings.xml b/tests/regression_tests/filter_distribcell/case-2/settings.xml similarity index 100% rename from tests/test_filter_distribcell/case-2/settings.xml rename to tests/regression_tests/filter_distribcell/case-2/settings.xml diff --git a/tests/test_filter_distribcell/case-2/tallies.xml b/tests/regression_tests/filter_distribcell/case-2/tallies.xml similarity index 100% rename from tests/test_filter_distribcell/case-2/tallies.xml rename to tests/regression_tests/filter_distribcell/case-2/tallies.xml diff --git a/tests/test_filter_distribcell/case-3/geometry.xml b/tests/regression_tests/filter_distribcell/case-3/geometry.xml similarity index 100% rename from tests/test_filter_distribcell/case-3/geometry.xml rename to tests/regression_tests/filter_distribcell/case-3/geometry.xml diff --git a/tests/test_filter_distribcell/case-3/materials.xml b/tests/regression_tests/filter_distribcell/case-3/materials.xml similarity index 100% rename from tests/test_filter_distribcell/case-3/materials.xml rename to tests/regression_tests/filter_distribcell/case-3/materials.xml diff --git a/tests/test_filter_distribcell/case-3/results_true.dat b/tests/regression_tests/filter_distribcell/case-3/results_true.dat similarity index 100% rename from tests/test_filter_distribcell/case-3/results_true.dat rename to tests/regression_tests/filter_distribcell/case-3/results_true.dat diff --git a/tests/test_filter_distribcell/case-3/settings.xml b/tests/regression_tests/filter_distribcell/case-3/settings.xml similarity index 100% rename from tests/test_filter_distribcell/case-3/settings.xml rename to tests/regression_tests/filter_distribcell/case-3/settings.xml diff --git a/tests/test_filter_distribcell/case-3/tallies.xml b/tests/regression_tests/filter_distribcell/case-3/tallies.xml similarity index 100% rename from tests/test_filter_distribcell/case-3/tallies.xml rename to tests/regression_tests/filter_distribcell/case-3/tallies.xml diff --git a/tests/test_filter_distribcell/case-4/geometry.xml b/tests/regression_tests/filter_distribcell/case-4/geometry.xml similarity index 100% rename from tests/test_filter_distribcell/case-4/geometry.xml rename to tests/regression_tests/filter_distribcell/case-4/geometry.xml diff --git a/tests/test_filter_distribcell/case-4/materials.xml b/tests/regression_tests/filter_distribcell/case-4/materials.xml similarity index 100% rename from tests/test_filter_distribcell/case-4/materials.xml rename to tests/regression_tests/filter_distribcell/case-4/materials.xml diff --git a/tests/test_filter_distribcell/case-4/results_true.dat b/tests/regression_tests/filter_distribcell/case-4/results_true.dat similarity index 100% rename from tests/test_filter_distribcell/case-4/results_true.dat rename to tests/regression_tests/filter_distribcell/case-4/results_true.dat diff --git a/tests/test_filter_distribcell/case-4/settings.xml b/tests/regression_tests/filter_distribcell/case-4/settings.xml similarity index 100% rename from tests/test_filter_distribcell/case-4/settings.xml rename to tests/regression_tests/filter_distribcell/case-4/settings.xml diff --git a/tests/test_filter_distribcell/case-4/tallies.xml b/tests/regression_tests/filter_distribcell/case-4/tallies.xml similarity index 100% rename from tests/test_filter_distribcell/case-4/tallies.xml rename to tests/regression_tests/filter_distribcell/case-4/tallies.xml diff --git a/tests/test_filter_distribcell/test_filter_distribcell.py b/tests/regression_tests/filter_distribcell/test.py similarity index 93% rename from tests/test_filter_distribcell/test_filter_distribcell.py rename to tests/regression_tests/filter_distribcell/test.py index f0fc270399..028c6a7790 100644 --- a/tests/test_filter_distribcell/test_filter_distribcell.py +++ b/tests/regression_tests/filter_distribcell/test.py @@ -1,16 +1,12 @@ -#!/usr/bin/env python - import glob -import hashlib import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import * + +from tests.testing_harness import * class DistribcellTestHarness(TestHarness): def __init__(self): - super(DistribcellTestHarness, self).__init__(None) + super().__init__(None) def execute_test(self): """Run OpenMC with the appropriate arguments and check the outputs.""" @@ -72,6 +68,6 @@ class DistribcellTestHarness(TestHarness): 'Tally output file does not exist.' -if __name__ == '__main__': +def test_filter_distribcell(): harness = DistribcellTestHarness() harness.main() diff --git a/tests/regression_tests/filter_energyfun/__init__.py b/tests/regression_tests/filter_energyfun/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_filter_energyfun/inputs_true.dat b/tests/regression_tests/filter_energyfun/inputs_true.dat similarity index 100% rename from tests/test_filter_energyfun/inputs_true.dat rename to tests/regression_tests/filter_energyfun/inputs_true.dat diff --git a/tests/test_filter_energyfun/results_true.dat b/tests/regression_tests/filter_energyfun/results_true.dat similarity index 100% rename from tests/test_filter_energyfun/results_true.dat rename to tests/regression_tests/filter_energyfun/results_true.dat diff --git a/tests/test_filter_energyfun/test_filter_energyfun.py b/tests/regression_tests/filter_energyfun/test.py similarity index 80% rename from tests/test_filter_energyfun/test_filter_energyfun.py rename to tests/regression_tests/filter_energyfun/test.py index 7e787edff2..7c5645b651 100644 --- a/tests/test_filter_energyfun/test_filter_energyfun.py +++ b/tests/regression_tests/filter_energyfun/test.py @@ -1,16 +1,11 @@ -#!/usr/bin/env python - -import os -import sys -import glob -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness import openmc +from tests.testing_harness import PyAPITestHarness + class FilterEnergyFunHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): - super(FilterEnergyFunHarness, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Add Am241 to the fuel. self._model.materials[1].add_nuclide('Am241', 1e-7) @@ -38,8 +33,7 @@ class FilterEnergyFunHarness(PyAPITestHarness): def _get_results(self): # Read the statepoint file. - statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] - sp = openmc.StatePoint(statepoint) + sp = openmc.StatePoint(self._sp_name) # Use tally arithmetic to compute the branching ratio. br_tally = sp.tallies[2] / sp.tallies[1] @@ -48,6 +42,6 @@ class FilterEnergyFunHarness(PyAPITestHarness): return br_tally.get_pandas_dataframe().to_string() + '\n' -if __name__ == '__main__': +def test_filter_energyfun(): harness = FilterEnergyFunHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/filter_mesh/__init__.py b/tests/regression_tests/filter_mesh/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_filter_mesh/inputs_true.dat b/tests/regression_tests/filter_mesh/inputs_true.dat similarity index 100% rename from tests/test_filter_mesh/inputs_true.dat rename to tests/regression_tests/filter_mesh/inputs_true.dat diff --git a/tests/test_filter_mesh/results_true.dat b/tests/regression_tests/filter_mesh/results_true.dat similarity index 100% rename from tests/test_filter_mesh/results_true.dat rename to tests/regression_tests/filter_mesh/results_true.dat diff --git a/tests/test_filter_mesh/test_filter_mesh.py b/tests/regression_tests/filter_mesh/test.py similarity index 90% rename from tests/test_filter_mesh/test_filter_mesh.py rename to tests/regression_tests/filter_mesh/test.py index 7b9e8bd16b..e0c6dd0f2f 100644 --- a/tests/test_filter_mesh/test_filter_mesh.py +++ b/tests/regression_tests/filter_mesh/test.py @@ -1,15 +1,11 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import HashedPyAPITestHarness import openmc +from tests.testing_harness import HashedPyAPITestHarness + class FilterMeshTestHarness(HashedPyAPITestHarness): def __init__(self, *args, **kwargs): - super(FilterMeshTestHarness, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Initialize Meshes mesh_1d = openmc.Mesh(mesh_id=1) @@ -67,6 +63,6 @@ class FilterMeshTestHarness(HashedPyAPITestHarness): self._model.tallies.append(tally) -if __name__ == '__main__': +def test_filter_mesh(): harness = FilterMeshTestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/fixed_source/__init__.py b/tests/regression_tests/fixed_source/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_fixed_source/inputs_true.dat b/tests/regression_tests/fixed_source/inputs_true.dat similarity index 100% rename from tests/test_fixed_source/inputs_true.dat rename to tests/regression_tests/fixed_source/inputs_true.dat diff --git a/tests/test_fixed_source/results_true.dat b/tests/regression_tests/fixed_source/results_true.dat similarity index 100% rename from tests/test_fixed_source/results_true.dat rename to tests/regression_tests/fixed_source/results_true.dat diff --git a/tests/test_fixed_source/test_fixed_source.py b/tests/regression_tests/fixed_source/test.py similarity index 86% rename from tests/test_fixed_source/test_fixed_source.py rename to tests/regression_tests/fixed_source/test.py index db58e2f706..7d98de9fc3 100644 --- a/tests/test_fixed_source/test_fixed_source.py +++ b/tests/regression_tests/fixed_source/test.py @@ -1,22 +1,17 @@ -#!/usr/bin/env python - -import glob -import os -import sys import numpy as np -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness + import openmc import openmc.stats +from tests.testing_harness import PyAPITestHarness + class FixedSourceTestHarness(PyAPITestHarness): def _get_results(self): """Digest info in the statepoint and return as a string.""" # Read the statepoint file. - statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] outstr = '' - with openmc.StatePoint(statepoint) as sp: + with openmc.StatePoint(self._sp_name) as sp: # Write out tally data. for i, tally_ind in enumerate(sp.tallies): tally = sp.tallies[tally_ind] @@ -36,7 +31,7 @@ class FixedSourceTestHarness(PyAPITestHarness): return outstr -if __name__ == '__main__': +def test_fixed_source(): mat = openmc.Material() mat.add_nuclide('O16', 1.0) mat.add_nuclide('U238', 0.0001) diff --git a/tests/regression_tests/infinite_cell/__init__.py b/tests/regression_tests/infinite_cell/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_infinite_cell/geometry.xml b/tests/regression_tests/infinite_cell/geometry.xml similarity index 100% rename from tests/test_infinite_cell/geometry.xml rename to tests/regression_tests/infinite_cell/geometry.xml diff --git a/tests/test_infinite_cell/materials.xml b/tests/regression_tests/infinite_cell/materials.xml similarity index 100% rename from tests/test_infinite_cell/materials.xml rename to tests/regression_tests/infinite_cell/materials.xml diff --git a/tests/test_infinite_cell/results_true.dat b/tests/regression_tests/infinite_cell/results_true.dat similarity index 100% rename from tests/test_infinite_cell/results_true.dat rename to tests/regression_tests/infinite_cell/results_true.dat diff --git a/tests/test_infinite_cell/settings.xml b/tests/regression_tests/infinite_cell/settings.xml similarity index 100% rename from tests/test_infinite_cell/settings.xml rename to tests/regression_tests/infinite_cell/settings.xml diff --git a/tests/regression_tests/infinite_cell/test.py b/tests/regression_tests/infinite_cell/test.py new file mode 100644 index 0000000000..59abec3003 --- /dev/null +++ b/tests/regression_tests/infinite_cell/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_infinite_cell(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/iso_in_lab/__init__.py b/tests/regression_tests/iso_in_lab/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_iso_in_lab/inputs_true.dat b/tests/regression_tests/iso_in_lab/inputs_true.dat similarity index 100% rename from tests/test_iso_in_lab/inputs_true.dat rename to tests/regression_tests/iso_in_lab/inputs_true.dat diff --git a/tests/test_iso_in_lab/results_true.dat b/tests/regression_tests/iso_in_lab/results_true.dat similarity index 100% rename from tests/test_iso_in_lab/results_true.dat rename to tests/regression_tests/iso_in_lab/results_true.dat diff --git a/tests/test_iso_in_lab/test_iso_in_lab.py b/tests/regression_tests/iso_in_lab/test.py similarity index 52% rename from tests/test_iso_in_lab/test_iso_in_lab.py rename to tests/regression_tests/iso_in_lab/test.py index 60b5bc2dee..0e8edc2186 100644 --- a/tests/test_iso_in_lab/test_iso_in_lab.py +++ b/tests/regression_tests/iso_in_lab/test.py @@ -1,12 +1,7 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness +from tests.testing_harness import PyAPITestHarness -if __name__ == '__main__': +def test_iso_in_lab(): # Force iso-in-lab scattering. harness = PyAPITestHarness('statepoint.10.h5') harness._model.materials.make_isotropic_in_lab() diff --git a/tests/regression_tests/lattice/__init__.py b/tests/regression_tests/lattice/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_lattice/geometry.xml b/tests/regression_tests/lattice/geometry.xml similarity index 100% rename from tests/test_lattice/geometry.xml rename to tests/regression_tests/lattice/geometry.xml diff --git a/tests/test_lattice/materials.xml b/tests/regression_tests/lattice/materials.xml similarity index 100% rename from tests/test_lattice/materials.xml rename to tests/regression_tests/lattice/materials.xml diff --git a/tests/test_lattice/results_true.dat b/tests/regression_tests/lattice/results_true.dat similarity index 100% rename from tests/test_lattice/results_true.dat rename to tests/regression_tests/lattice/results_true.dat diff --git a/tests/test_lattice/settings.xml b/tests/regression_tests/lattice/settings.xml similarity index 100% rename from tests/test_lattice/settings.xml rename to tests/regression_tests/lattice/settings.xml diff --git a/tests/regression_tests/lattice/test.py b/tests/regression_tests/lattice/test.py new file mode 100644 index 0000000000..a32b9a629c --- /dev/null +++ b/tests/regression_tests/lattice/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_lattice(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/lattice_hex/__init__.py b/tests/regression_tests/lattice_hex/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_lattice_hex/geometry.xml b/tests/regression_tests/lattice_hex/geometry.xml similarity index 100% rename from tests/test_lattice_hex/geometry.xml rename to tests/regression_tests/lattice_hex/geometry.xml diff --git a/tests/test_lattice_hex/materials.xml b/tests/regression_tests/lattice_hex/materials.xml similarity index 100% rename from tests/test_lattice_hex/materials.xml rename to tests/regression_tests/lattice_hex/materials.xml diff --git a/tests/test_lattice_hex/plots.xml b/tests/regression_tests/lattice_hex/plots.xml similarity index 100% rename from tests/test_lattice_hex/plots.xml rename to tests/regression_tests/lattice_hex/plots.xml diff --git a/tests/test_lattice_hex/results_true.dat b/tests/regression_tests/lattice_hex/results_true.dat similarity index 100% rename from tests/test_lattice_hex/results_true.dat rename to tests/regression_tests/lattice_hex/results_true.dat diff --git a/tests/test_lattice_hex/settings.xml b/tests/regression_tests/lattice_hex/settings.xml similarity index 100% rename from tests/test_lattice_hex/settings.xml rename to tests/regression_tests/lattice_hex/settings.xml diff --git a/tests/regression_tests/lattice_hex/test.py b/tests/regression_tests/lattice_hex/test.py new file mode 100644 index 0000000000..eb63f84df5 --- /dev/null +++ b/tests/regression_tests/lattice_hex/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_lattice_hex(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/lattice_mixed/__init__.py b/tests/regression_tests/lattice_mixed/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_lattice_mixed/geometry.xml b/tests/regression_tests/lattice_mixed/geometry.xml similarity index 100% rename from tests/test_lattice_mixed/geometry.xml rename to tests/regression_tests/lattice_mixed/geometry.xml diff --git a/tests/test_lattice_mixed/materials.xml b/tests/regression_tests/lattice_mixed/materials.xml similarity index 100% rename from tests/test_lattice_mixed/materials.xml rename to tests/regression_tests/lattice_mixed/materials.xml diff --git a/tests/test_lattice_mixed/plots.xml b/tests/regression_tests/lattice_mixed/plots.xml similarity index 100% rename from tests/test_lattice_mixed/plots.xml rename to tests/regression_tests/lattice_mixed/plots.xml diff --git a/tests/test_lattice_mixed/results_true.dat b/tests/regression_tests/lattice_mixed/results_true.dat similarity index 100% rename from tests/test_lattice_mixed/results_true.dat rename to tests/regression_tests/lattice_mixed/results_true.dat diff --git a/tests/test_lattice_mixed/settings.xml b/tests/regression_tests/lattice_mixed/settings.xml similarity index 100% rename from tests/test_lattice_mixed/settings.xml rename to tests/regression_tests/lattice_mixed/settings.xml diff --git a/tests/regression_tests/lattice_mixed/test.py b/tests/regression_tests/lattice_mixed/test.py new file mode 100644 index 0000000000..3df1d7a4f3 --- /dev/null +++ b/tests/regression_tests/lattice_mixed/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_lattice_mixed(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/lattice_multiple/__init__.py b/tests/regression_tests/lattice_multiple/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_lattice_multiple/geometry.xml b/tests/regression_tests/lattice_multiple/geometry.xml similarity index 100% rename from tests/test_lattice_multiple/geometry.xml rename to tests/regression_tests/lattice_multiple/geometry.xml diff --git a/tests/test_lattice_multiple/materials.xml b/tests/regression_tests/lattice_multiple/materials.xml similarity index 100% rename from tests/test_lattice_multiple/materials.xml rename to tests/regression_tests/lattice_multiple/materials.xml diff --git a/tests/test_lattice_multiple/results_true.dat b/tests/regression_tests/lattice_multiple/results_true.dat similarity index 100% rename from tests/test_lattice_multiple/results_true.dat rename to tests/regression_tests/lattice_multiple/results_true.dat diff --git a/tests/test_lattice_multiple/settings.xml b/tests/regression_tests/lattice_multiple/settings.xml similarity index 100% rename from tests/test_lattice_multiple/settings.xml rename to tests/regression_tests/lattice_multiple/settings.xml diff --git a/tests/regression_tests/lattice_multiple/test.py b/tests/regression_tests/lattice_multiple/test.py new file mode 100644 index 0000000000..f0672d9a4e --- /dev/null +++ b/tests/regression_tests/lattice_multiple/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_lattice_multiple(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/mg_basic/__init__.py b/tests/regression_tests/mg_basic/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_mg_basic/inputs_true.dat b/tests/regression_tests/mg_basic/inputs_true.dat similarity index 98% rename from tests/test_mg_basic/inputs_true.dat rename to tests/regression_tests/mg_basic/inputs_true.dat index 220b1de240..a0efdbde0f 100644 --- a/tests/test_mg_basic/inputs_true.dat +++ b/tests/regression_tests/mg_basic/inputs_true.dat @@ -32,7 +32,7 @@ - ../1d_mgxs.h5 + ../../1d_mgxs.h5 diff --git a/tests/test_mg_basic/results_true.dat b/tests/regression_tests/mg_basic/results_true.dat similarity index 100% rename from tests/test_mg_basic/results_true.dat rename to tests/regression_tests/mg_basic/results_true.dat diff --git a/tests/regression_tests/mg_basic/test.py b/tests/regression_tests/mg_basic/test.py new file mode 100644 index 0000000000..3c22e60408 --- /dev/null +++ b/tests/regression_tests/mg_basic/test.py @@ -0,0 +1,9 @@ +from openmc.examples import slab_mg + +from tests.testing_harness import PyAPITestHarness + + +def test_mg_basic(): + model = slab_mg() + harness = PyAPITestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/mg_convert/__init__.py b/tests/regression_tests/mg_convert/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_mg_convert/inputs_true.dat b/tests/regression_tests/mg_convert/inputs_true.dat similarity index 100% rename from tests/test_mg_convert/inputs_true.dat rename to tests/regression_tests/mg_convert/inputs_true.dat diff --git a/tests/test_mg_convert/results_true.dat b/tests/regression_tests/mg_convert/results_true.dat similarity index 100% rename from tests/test_mg_convert/results_true.dat rename to tests/regression_tests/mg_convert/results_true.dat diff --git a/tests/test_mg_convert/test_mg_convert.py b/tests/regression_tests/mg_convert/test.py similarity index 87% rename from tests/test_mg_convert/test_mg_convert.py rename to tests/regression_tests/mg_convert/test.py index 36bfe5338b..5b816a1cd0 100755 --- a/tests/test_mg_convert/test_mg_convert.py +++ b/tests/regression_tests/mg_convert/test.py @@ -1,15 +1,12 @@ -#!/usr/bin/env python - import os -import sys import hashlib -sys.path.insert(0, os.pardir) import numpy as np - -from testing_harness import PyAPITestHarness import openmc +from tests.testing_harness import PyAPITestHarness +from tests.regression_tests import config + # OpenMC simulation parameters batches = 10 inactive = 5 @@ -137,24 +134,18 @@ class MGXSTestHarness(PyAPITestHarness): for case in cases: build_mgxs_library(case) - if self._opts.mpi_exec is not None: - mpi_args = [self._opts.mpi_exec, '-n', self._opts.mpi_np] - openmc.run(openmc_exec=self._opts.exe, mpi_args=mpi_args) + if config['mpi']: + mpi_args = [config['mpiexec'], '-n', config['mpi_np']] + openmc.run(openmc_exec=config['exe'], mpi_args=mpi_args) else: - openmc.run(openmc_exec=self._opts.exe) + openmc.run(openmc_exec=config['exe']) - sp = openmc.StatePoint('statepoint.' + str(batches) + '.h5') - - # Write out k-combined. - outstr += 'k-combined:\n' - form = '{0:12.6E} {1:12.6E}\n' - outstr += form.format(sp.k_combined[0], sp.k_combined[1]) - - # Enforce closing statepoint and summary files so HDF5 - # does not throw an error during the next OpenMC execution - sp._f.close() - sp._summary._f.close() + with openmc.StatePoint('statepoint.{}.h5'.format(batches)) as sp: + # Write out k-combined. + outstr += 'k-combined:\n' + form = '{0:12.6E} {1:12.6E}\n' + outstr += form.format(sp.k_combined[0], sp.k_combined[1]) return outstr @@ -168,7 +159,7 @@ class MGXSTestHarness(PyAPITestHarness): return outstr def _cleanup(self): - super(MGXSTestHarness, self)._cleanup() + super()._cleanup() f = os.path.join(os.getcwd(), 'mgxs.h5') if os.path.exists(f): os.remove(f) @@ -202,6 +193,6 @@ class MGXSTestHarness(PyAPITestHarness): self._cleanup() -if __name__ == '__main__': +def test_mg_convert(): harness = MGXSTestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/mg_legendre/__init__.py b/tests/regression_tests/mg_legendre/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_mg_legendre/inputs_true.dat b/tests/regression_tests/mg_legendre/inputs_true.dat similarity index 96% rename from tests/test_mg_legendre/inputs_true.dat rename to tests/regression_tests/mg_legendre/inputs_true.dat index 9b63fb944c..7548080955 100644 --- a/tests/test_mg_legendre/inputs_true.dat +++ b/tests/regression_tests/mg_legendre/inputs_true.dat @@ -14,7 +14,7 @@ - ../1d_mgxs.h5 + ../../1d_mgxs.h5 diff --git a/tests/test_mg_legendre/results_true.dat b/tests/regression_tests/mg_legendre/results_true.dat similarity index 100% rename from tests/test_mg_legendre/results_true.dat rename to tests/regression_tests/mg_legendre/results_true.dat diff --git a/tests/test_mg_legendre/test_mg_legendre.py b/tests/regression_tests/mg_legendre/test.py similarity index 58% rename from tests/test_mg_legendre/test_mg_legendre.py rename to tests/regression_tests/mg_legendre/test.py index eee0f0800f..5a57f758e8 100644 --- a/tests/test_mg_legendre/test_mg_legendre.py +++ b/tests/regression_tests/mg_legendre/test.py @@ -1,14 +1,9 @@ -#!/usr/bin/env python - -import os -import sys - -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness from openmc.examples import slab_mg +from tests.testing_harness import PyAPITestHarness -if __name__ == '__main__': + +def test_mg_legendre(): model = slab_mg(reps=['iso']) model.settings.tabular_legendre = {'enable': False} diff --git a/tests/regression_tests/mg_max_order/__init__.py b/tests/regression_tests/mg_max_order/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_mg_max_order/inputs_true.dat b/tests/regression_tests/mg_max_order/inputs_true.dat similarity index 96% rename from tests/test_mg_max_order/inputs_true.dat rename to tests/regression_tests/mg_max_order/inputs_true.dat index 3954bc73a7..023d468d4f 100644 --- a/tests/test_mg_max_order/inputs_true.dat +++ b/tests/regression_tests/mg_max_order/inputs_true.dat @@ -14,7 +14,7 @@ - ../1d_mgxs.h5 + ../../1d_mgxs.h5 diff --git a/tests/test_mg_max_order/results_true.dat b/tests/regression_tests/mg_max_order/results_true.dat similarity index 100% rename from tests/test_mg_max_order/results_true.dat rename to tests/regression_tests/mg_max_order/results_true.dat diff --git a/tests/test_mg_max_order/test_mg_max_order.py b/tests/regression_tests/mg_max_order/test.py similarity index 55% rename from tests/test_mg_max_order/test_mg_max_order.py rename to tests/regression_tests/mg_max_order/test.py index 8ac0f58785..20cc4f805d 100644 --- a/tests/test_mg_max_order/test_mg_max_order.py +++ b/tests/regression_tests/mg_max_order/test.py @@ -1,14 +1,9 @@ -#!/usr/bin/env python - -import os -import sys - -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness from openmc.examples import slab_mg +from tests.testing_harness import PyAPITestHarness -if __name__ == '__main__': + +def test_mg_max_order(): model = slab_mg(reps=['iso']) model.settings.max_order = 1 harness = PyAPITestHarness('statepoint.10.h5', model) diff --git a/tests/regression_tests/mg_nuclide/__init__.py b/tests/regression_tests/mg_nuclide/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_mg_nuclide/inputs_true.dat b/tests/regression_tests/mg_nuclide/inputs_true.dat similarity index 98% rename from tests/test_mg_nuclide/inputs_true.dat rename to tests/regression_tests/mg_nuclide/inputs_true.dat index d42b15480d..e11b9e3f07 100644 --- a/tests/test_mg_nuclide/inputs_true.dat +++ b/tests/regression_tests/mg_nuclide/inputs_true.dat @@ -32,7 +32,7 @@ - ../1d_mgxs.h5 + ../../1d_mgxs.h5 diff --git a/tests/test_mg_nuclide/results_true.dat b/tests/regression_tests/mg_nuclide/results_true.dat similarity index 100% rename from tests/test_mg_nuclide/results_true.dat rename to tests/regression_tests/mg_nuclide/results_true.dat diff --git a/tests/test_mg_nuclide/test_mg_nuclide.py b/tests/regression_tests/mg_nuclide/test.py similarity index 51% rename from tests/test_mg_nuclide/test_mg_nuclide.py rename to tests/regression_tests/mg_nuclide/test.py index 0f3c9dd6d7..44206ef28f 100644 --- a/tests/test_mg_nuclide/test_mg_nuclide.py +++ b/tests/regression_tests/mg_nuclide/test.py @@ -1,14 +1,9 @@ -#!/usr/bin/env python - -import os -import sys - -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness from openmc.examples import slab_mg +from tests.testing_harness import PyAPITestHarness -if __name__ == '__main__': + +def test_mg_nuclide(): model = slab_mg(as_macro=False) harness = PyAPITestHarness('statepoint.10.h5', model) harness.main() diff --git a/tests/regression_tests/mg_survival_biasing/__init__.py b/tests/regression_tests/mg_survival_biasing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_mg_survival_biasing/inputs_true.dat b/tests/regression_tests/mg_survival_biasing/inputs_true.dat similarity index 98% rename from tests/test_mg_survival_biasing/inputs_true.dat rename to tests/regression_tests/mg_survival_biasing/inputs_true.dat index 057af68105..4bc79d48e3 100644 --- a/tests/test_mg_survival_biasing/inputs_true.dat +++ b/tests/regression_tests/mg_survival_biasing/inputs_true.dat @@ -32,7 +32,7 @@ - ../1d_mgxs.h5 + ../../1d_mgxs.h5 diff --git a/tests/test_mg_survival_biasing/results_true.dat b/tests/regression_tests/mg_survival_biasing/results_true.dat similarity index 100% rename from tests/test_mg_survival_biasing/results_true.dat rename to tests/regression_tests/mg_survival_biasing/results_true.dat diff --git a/tests/test_mg_survival_biasing/test_mg_survival_biasing.py b/tests/regression_tests/mg_survival_biasing/test.py similarity index 55% rename from tests/test_mg_survival_biasing/test_mg_survival_biasing.py rename to tests/regression_tests/mg_survival_biasing/test.py index 9886ad3e49..3c6c77a370 100644 --- a/tests/test_mg_survival_biasing/test_mg_survival_biasing.py +++ b/tests/regression_tests/mg_survival_biasing/test.py @@ -1,13 +1,9 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness from openmc.examples import slab_mg +from tests.testing_harness import PyAPITestHarness -if __name__ == '__main__': + +def test_mg_survival_biasing(): model = slab_mg() model.settings.survival_biasing = True harness = PyAPITestHarness('statepoint.10.h5', model) diff --git a/tests/regression_tests/mg_tallies/__init__.py b/tests/regression_tests/mg_tallies/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_mg_tallies/inputs_true.dat b/tests/regression_tests/mg_tallies/inputs_true.dat similarity index 99% rename from tests/test_mg_tallies/inputs_true.dat rename to tests/regression_tests/mg_tallies/inputs_true.dat index 0c71689aea..7b5067014f 100644 --- a/tests/test_mg_tallies/inputs_true.dat +++ b/tests/regression_tests/mg_tallies/inputs_true.dat @@ -32,7 +32,7 @@ - ../1d_mgxs.h5 + ../../1d_mgxs.h5 diff --git a/tests/test_mg_tallies/results_true.dat b/tests/regression_tests/mg_tallies/results_true.dat similarity index 100% rename from tests/test_mg_tallies/results_true.dat rename to tests/regression_tests/mg_tallies/results_true.dat diff --git a/tests/test_mg_tallies/test_mg_tallies.py b/tests/regression_tests/mg_tallies/test.py similarity index 95% rename from tests/test_mg_tallies/test_mg_tallies.py rename to tests/regression_tests/mg_tallies/test.py index aeafae3182..8952cc4ad8 100644 --- a/tests/test_mg_tallies/test_mg_tallies.py +++ b/tests/regression_tests/mg_tallies/test.py @@ -1,14 +1,10 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import HashedPyAPITestHarness import openmc from openmc.examples import slab_mg +from tests.testing_harness import HashedPyAPITestHarness -if __name__ == '__main__': + +def test_mg_tallies(): model = slab_mg(as_macro=False) # Instantiate a tally mesh diff --git a/tests/regression_tests/mgxs_library_ce_to_mg/__init__.py b/tests/regression_tests/mgxs_library_ce_to_mg/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_mgxs_library_ce_to_mg/inputs_true.dat b/tests/regression_tests/mgxs_library_ce_to_mg/inputs_true.dat similarity index 100% rename from tests/test_mgxs_library_ce_to_mg/inputs_true.dat rename to tests/regression_tests/mgxs_library_ce_to_mg/inputs_true.dat diff --git a/tests/test_mgxs_library_ce_to_mg/results_true.dat b/tests/regression_tests/mgxs_library_ce_to_mg/results_true.dat similarity index 100% rename from tests/test_mgxs_library_ce_to_mg/results_true.dat rename to tests/regression_tests/mgxs_library_ce_to_mg/results_true.dat diff --git a/tests/test_mgxs_library_ce_to_mg/test_mgxs_library_ce_to_mg.py b/tests/regression_tests/mgxs_library_ce_to_mg/test.py similarity index 69% rename from tests/test_mgxs_library_ce_to_mg/test_mgxs_library_ce_to_mg.py rename to tests/regression_tests/mgxs_library_ce_to_mg/test.py index 7ae47636e6..72f052e68e 100644 --- a/tests/test_mgxs_library_ce_to_mg/test_mgxs_library_ce_to_mg.py +++ b/tests/regression_tests/mgxs_library_ce_to_mg/test.py @@ -1,19 +1,17 @@ -#!/usr/bin/env python - import os -import sys -import glob -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness + import openmc import openmc.mgxs from openmc.examples import pwr_pin_cell +from tests.testing_harness import PyAPITestHarness +from tests.regression_tests import config + class MGXSTestHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): # Generate inputs using parent class routine - super(MGXSTestHarness, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Initialize a two-group structure energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.e6]) @@ -34,16 +32,15 @@ class MGXSTestHarness(PyAPITestHarness): def _run_openmc(self): # Initial run - if self._opts.mpi_exec is not None: - mpi_args = [self._opts.mpi_exec, '-n', self._opts.mpi_np] - openmc.run(openmc_exec=self._opts.exe, mpi_args=mpi_args) + if config['mpi']: + mpi_args = [config['mpiexec'], '-n', config['mpi_np']] + openmc.run(openmc_exec=config['exe'], mpi_args=mpi_args) else: - openmc.run(openmc_exec=self._opts.exe) + openmc.run(openmc_exec=config['exe']) # Build MG Inputs # Get data needed to execute Library calculations. - statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] - sp = openmc.StatePoint(statepoint) + sp = openmc.StatePoint(self._sp_name) self.mgxs_lib.load_from_statepoint(sp) self._model.mgxs_file, self._model.materials, \ self._model.geometry = self.mgxs_lib.create_mg_mode() @@ -58,8 +55,8 @@ class MGXSTestHarness(PyAPITestHarness): self._model.materials.export_to_xml() self._model.mgxs_file.export_to_hdf5() # Dont need tallies.xml, so remove the file - if os.path.exists('./tallies.xml'): - os.remove('./tallies.xml') + if os.path.exists('tallies.xml'): + os.remove('tallies.xml') # Enforce closing statepoint and summary files so HDF5 # does not throw an error during the next OpenMC execution @@ -67,20 +64,20 @@ class MGXSTestHarness(PyAPITestHarness): sp._summary._f.close() # Re-run MG mode. - if self._opts.mpi_exec is not None: - mpi_args = [self._opts.mpi_exec, '-n', self._opts.mpi_np] - openmc.run(openmc_exec=self._opts.exe, mpi_args=mpi_args) + if config['mpi']: + mpi_args = [config['mpiexec'], '-n', config['mpi_np']] + openmc.run(openmc_exec=config['exe'], mpi_args=mpi_args) else: - openmc.run(openmc_exec=self._opts.exe) + openmc.run(openmc_exec=config['exe']) def _cleanup(self): - super(MGXSTestHarness, self)._cleanup() - f = os.path.join(os.getcwd(), 'mgxs.h5') + super()._cleanup() + f = 'mgxs.h5' if os.path.exists(f): os.remove(f) -if __name__ == '__main__': +def test_mgxs_library_ce_to_mg(): # Set the input set to use the pincell model model = pwr_pin_cell() diff --git a/tests/regression_tests/mgxs_library_condense/__init__.py b/tests/regression_tests/mgxs_library_condense/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_mgxs_library_condense/inputs_true.dat b/tests/regression_tests/mgxs_library_condense/inputs_true.dat similarity index 100% rename from tests/test_mgxs_library_condense/inputs_true.dat rename to tests/regression_tests/mgxs_library_condense/inputs_true.dat diff --git a/tests/test_mgxs_library_condense/results_true.dat b/tests/regression_tests/mgxs_library_condense/results_true.dat similarity index 100% rename from tests/test_mgxs_library_condense/results_true.dat rename to tests/regression_tests/mgxs_library_condense/results_true.dat diff --git a/tests/test_mgxs_library_condense/test_mgxs_library_condense.py b/tests/regression_tests/mgxs_library_condense/test.py similarity index 85% rename from tests/test_mgxs_library_condense/test_mgxs_library_condense.py rename to tests/regression_tests/mgxs_library_condense/test.py index 127980da6e..167772e2fd 100644 --- a/tests/test_mgxs_library_condense/test_mgxs_library_condense.py +++ b/tests/regression_tests/mgxs_library_condense/test.py @@ -1,19 +1,15 @@ -#!/usr/bin/env python - -import os -import sys -import glob import hashlib -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness + import openmc import openmc.mgxs from openmc.examples import pwr_pin_cell +from tests.testing_harness import PyAPITestHarness + class MGXSTestHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): - super(MGXSTestHarness, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Initialize a two-group structure energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.e6]) @@ -38,8 +34,7 @@ class MGXSTestHarness(PyAPITestHarness): """Digest info in the statepoint and return as a string.""" # Read the statepoint file. - statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] - sp = openmc.StatePoint(statepoint) + sp = openmc.StatePoint(self._sp_name) # Load the MGXS library from the statepoint self.mgxs_lib.load_from_statepoint(sp) @@ -65,7 +60,7 @@ class MGXSTestHarness(PyAPITestHarness): return outstr -if __name__ == '__main__': +def test_mgxs_library_condense(): # Use the pincell model model = pwr_pin_cell() harness = MGXSTestHarness('statepoint.10.h5', model) diff --git a/tests/regression_tests/mgxs_library_distribcell/__init__.py b/tests/regression_tests/mgxs_library_distribcell/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_mgxs_library_distribcell/inputs_true.dat b/tests/regression_tests/mgxs_library_distribcell/inputs_true.dat similarity index 100% rename from tests/test_mgxs_library_distribcell/inputs_true.dat rename to tests/regression_tests/mgxs_library_distribcell/inputs_true.dat diff --git a/tests/test_mgxs_library_distribcell/results_true.dat b/tests/regression_tests/mgxs_library_distribcell/results_true.dat similarity index 100% rename from tests/test_mgxs_library_distribcell/results_true.dat rename to tests/regression_tests/mgxs_library_distribcell/results_true.dat diff --git a/tests/test_mgxs_library_distribcell/test_mgxs_library_distribcell.py b/tests/regression_tests/mgxs_library_distribcell/test.py similarity index 86% rename from tests/test_mgxs_library_distribcell/test_mgxs_library_distribcell.py rename to tests/regression_tests/mgxs_library_distribcell/test.py index 38f16c5884..5290d313bb 100644 --- a/tests/test_mgxs_library_distribcell/test_mgxs_library_distribcell.py +++ b/tests/regression_tests/mgxs_library_distribcell/test.py @@ -1,20 +1,16 @@ -#!/usr/bin/env python - -import os -import sys -import glob import hashlib -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness + import openmc import openmc.mgxs from openmc.examples import pwr_assembly +from tests.testing_harness import PyAPITestHarness + class MGXSTestHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): # Generate inputs using parent class routine - super(MGXSTestHarness, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Initialize a one-group structure energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 20.e6]) @@ -43,8 +39,7 @@ class MGXSTestHarness(PyAPITestHarness): """Digest info in the statepoint and return as a string.""" # Read the statepoint file. - statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] - sp = openmc.StatePoint(statepoint) + sp = openmc.StatePoint(self._sp_name) # Load the MGXS library from the statepoint self.mgxs_lib.load_from_statepoint(sp) @@ -69,7 +64,7 @@ class MGXSTestHarness(PyAPITestHarness): return outstr -if __name__ == '__main__': +def test_mgxs_library_distribcell(): model = pwr_assembly() harness = MGXSTestHarness('statepoint.10.h5', model) harness.main() diff --git a/tests/regression_tests/mgxs_library_hdf5/__init__.py b/tests/regression_tests/mgxs_library_hdf5/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_mgxs_library_hdf5/inputs_true.dat b/tests/regression_tests/mgxs_library_hdf5/inputs_true.dat similarity index 100% rename from tests/test_mgxs_library_hdf5/inputs_true.dat rename to tests/regression_tests/mgxs_library_hdf5/inputs_true.dat diff --git a/tests/test_mgxs_library_hdf5/results_true.dat b/tests/regression_tests/mgxs_library_hdf5/results_true.dat similarity index 100% rename from tests/test_mgxs_library_hdf5/results_true.dat rename to tests/regression_tests/mgxs_library_hdf5/results_true.dat diff --git a/tests/test_mgxs_library_hdf5/test_mgxs_library_hdf5.py b/tests/regression_tests/mgxs_library_hdf5/test.py similarity index 79% rename from tests/test_mgxs_library_hdf5/test_mgxs_library_hdf5.py rename to tests/regression_tests/mgxs_library_hdf5/test.py index 31a9d96126..9811ab2155 100644 --- a/tests/test_mgxs_library_hdf5/test_mgxs_library_hdf5.py +++ b/tests/regression_tests/mgxs_library_hdf5/test.py @@ -1,27 +1,19 @@ -#!/usr/bin/env python - import os -import sys -import glob import hashlib import numpy as np import h5py - -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness import openmc import openmc.mgxs from openmc.examples import pwr_pin_cell - -np.set_printoptions(formatter={'float_kind': '{:.8e}'.format}) +from tests.testing_harness import PyAPITestHarness class MGXSTestHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): # Generate inputs using parent class routine - super(MGXSTestHarness, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Initialize a two-group structure energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.e6]) @@ -46,8 +38,7 @@ class MGXSTestHarness(PyAPITestHarness): """Digest info in the statepoint and return as a string.""" # Read the statepoint file. - statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] - sp = openmc.StatePoint(statepoint) + sp = openmc.StatePoint(self._sp_name) # Load the MGXS library from the statepoint self.mgxs_lib.load_from_statepoint(sp) @@ -76,13 +67,17 @@ class MGXSTestHarness(PyAPITestHarness): return outstr def _cleanup(self): - super(MGXSTestHarness, self)._cleanup() - f = os.path.join(os.getcwd(), 'mgxs.h5') + super()._cleanup() + f = 'mgxs.h5' if os.path.exists(f): os.remove(f) -if __name__ == '__main__': - model = pwr_pin_cell() - harness = MGXSTestHarness('statepoint.10.h5', model) - harness.main() +def test_mgxs_library_hdf5(): + try: + np.set_printoptions(formatter={'float_kind': '{:.8e}'.format}) + model = pwr_pin_cell() + harness = MGXSTestHarness('statepoint.10.h5', model) + harness.main() + finally: + np.set_printoptions(formatter=None) diff --git a/tests/regression_tests/mgxs_library_mesh/__init__.py b/tests/regression_tests/mgxs_library_mesh/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_mgxs_library_mesh/inputs_true.dat b/tests/regression_tests/mgxs_library_mesh/inputs_true.dat similarity index 100% rename from tests/test_mgxs_library_mesh/inputs_true.dat rename to tests/regression_tests/mgxs_library_mesh/inputs_true.dat diff --git a/tests/test_mgxs_library_mesh/results_true.dat b/tests/regression_tests/mgxs_library_mesh/results_true.dat similarity index 100% rename from tests/test_mgxs_library_mesh/results_true.dat rename to tests/regression_tests/mgxs_library_mesh/results_true.dat diff --git a/tests/test_mgxs_library_mesh/test_mgxs_library_mesh.py b/tests/regression_tests/mgxs_library_mesh/test.py similarity index 86% rename from tests/test_mgxs_library_mesh/test_mgxs_library_mesh.py rename to tests/regression_tests/mgxs_library_mesh/test.py index e0a3307bc3..a9d24da934 100644 --- a/tests/test_mgxs_library_mesh/test_mgxs_library_mesh.py +++ b/tests/regression_tests/mgxs_library_mesh/test.py @@ -1,18 +1,14 @@ -#!/usr/bin/env python - -import os -import sys -import glob import hashlib -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness + import openmc import openmc.mgxs +from tests.testing_harness import PyAPITestHarness + class MGXSTestHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): - super(MGXSTestHarness, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Initialize a one-group structure energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 20.e6]) @@ -47,8 +43,7 @@ class MGXSTestHarness(PyAPITestHarness): """Digest info in the statepoint and return as a string.""" # Read the statepoint file. - statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] - sp = openmc.StatePoint(statepoint) + sp = openmc.StatePoint(self._sp_name) # Load the MGXS library from the statepoint self.mgxs_lib.load_from_statepoint(sp) @@ -70,6 +65,6 @@ class MGXSTestHarness(PyAPITestHarness): return outstr -if __name__ == '__main__': +def test_mgxs_library_mesh(): harness = MGXSTestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/mgxs_library_no_nuclides/__init__.py b/tests/regression_tests/mgxs_library_no_nuclides/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_mgxs_library_no_nuclides/inputs_true.dat b/tests/regression_tests/mgxs_library_no_nuclides/inputs_true.dat similarity index 100% rename from tests/test_mgxs_library_no_nuclides/inputs_true.dat rename to tests/regression_tests/mgxs_library_no_nuclides/inputs_true.dat diff --git a/tests/test_mgxs_library_no_nuclides/results_true.dat b/tests/regression_tests/mgxs_library_no_nuclides/results_true.dat similarity index 100% rename from tests/test_mgxs_library_no_nuclides/results_true.dat rename to tests/regression_tests/mgxs_library_no_nuclides/results_true.dat diff --git a/tests/test_mgxs_library_no_nuclides/test_mgxs_library_no_nuclides.py b/tests/regression_tests/mgxs_library_no_nuclides/test.py similarity index 84% rename from tests/test_mgxs_library_no_nuclides/test_mgxs_library_no_nuclides.py rename to tests/regression_tests/mgxs_library_no_nuclides/test.py index 793cfc7146..506ac238fc 100644 --- a/tests/test_mgxs_library_no_nuclides/test_mgxs_library_no_nuclides.py +++ b/tests/regression_tests/mgxs_library_no_nuclides/test.py @@ -1,20 +1,16 @@ -#!/usr/bin/env python - -import os -import sys -import glob import hashlib -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness + import openmc import openmc.mgxs from openmc.examples import pwr_pin_cell +from tests.testing_harness import PyAPITestHarness + class MGXSTestHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): # Generate inputs using parent class routine - super(MGXSTestHarness, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Initialize a two-group structure energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.e6]) @@ -39,8 +35,7 @@ class MGXSTestHarness(PyAPITestHarness): """Digest info in the statepoint and return as a string.""" # Read the statepoint file. - statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] - sp = openmc.StatePoint(statepoint) + sp = openmc.StatePoint(self._sp_name) # Load the MGXS library from the statepoint self.mgxs_lib.load_from_statepoint(sp) @@ -62,7 +57,7 @@ class MGXSTestHarness(PyAPITestHarness): return outstr -if __name__ == '__main__': +def test_mgxs_library_no_nuclides(): model = pwr_pin_cell() harness = MGXSTestHarness('statepoint.10.h5', model) harness.main() diff --git a/tests/regression_tests/mgxs_library_nuclides/__init__.py b/tests/regression_tests/mgxs_library_nuclides/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_mgxs_library_nuclides/inputs_true.dat b/tests/regression_tests/mgxs_library_nuclides/inputs_true.dat similarity index 100% rename from tests/test_mgxs_library_nuclides/inputs_true.dat rename to tests/regression_tests/mgxs_library_nuclides/inputs_true.dat diff --git a/tests/test_mgxs_library_nuclides/results_true.dat b/tests/regression_tests/mgxs_library_nuclides/results_true.dat similarity index 100% rename from tests/test_mgxs_library_nuclides/results_true.dat rename to tests/regression_tests/mgxs_library_nuclides/results_true.dat diff --git a/tests/test_mgxs_library_nuclides/test_mgxs_library_nuclides.py b/tests/regression_tests/mgxs_library_nuclides/test.py similarity index 83% rename from tests/test_mgxs_library_nuclides/test_mgxs_library_nuclides.py rename to tests/regression_tests/mgxs_library_nuclides/test.py index 9e54e1bb6b..c64c277098 100644 --- a/tests/test_mgxs_library_nuclides/test_mgxs_library_nuclides.py +++ b/tests/regression_tests/mgxs_library_nuclides/test.py @@ -1,19 +1,15 @@ -#!/usr/bin/env python - -import os -import sys -import glob import hashlib -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness + import openmc import openmc.mgxs from openmc.examples import pwr_pin_cell +from tests.testing_harness import PyAPITestHarness + class MGXSTestHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): - super(MGXSTestHarness, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Initialize a two-group structure energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.e6]) @@ -35,8 +31,7 @@ class MGXSTestHarness(PyAPITestHarness): """Digest info in the statepoint and return as a string.""" # Read the statepoint file. - statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] - sp = openmc.StatePoint(statepoint) + sp = openmc.StatePoint(self._sp_name) # Load the MGXS library from the statepoint self.mgxs_lib.load_from_statepoint(sp) @@ -58,7 +53,7 @@ class MGXSTestHarness(PyAPITestHarness): return outstr -if __name__ == '__main__': +def test_mgxs_library_nuclides(): model = pwr_pin_cell() harness = MGXSTestHarness('statepoint.10.h5', model) harness.main() diff --git a/tests/regression_tests/multipole/__init__.py b/tests/regression_tests/multipole/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_multipole/inputs_true.dat b/tests/regression_tests/multipole/inputs_true.dat similarity index 100% rename from tests/test_multipole/inputs_true.dat rename to tests/regression_tests/multipole/inputs_true.dat diff --git a/tests/test_multipole/results_true.dat b/tests/regression_tests/multipole/results_true.dat similarity index 100% rename from tests/test_multipole/results_true.dat rename to tests/regression_tests/multipole/results_true.dat diff --git a/tests/test_multipole/test_multipole.py b/tests/regression_tests/multipole/test.py similarity index 81% rename from tests/test_multipole/test_multipole.py rename to tests/regression_tests/multipole/test.py index 294185312a..5c79d4d6b6 100644 --- a/tests/test_multipole/test_multipole.py +++ b/tests/regression_tests/multipole/test.py @@ -1,10 +1,10 @@ -#!/usr/bin/env python import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness, PyAPITestHarness + import openmc import openmc.model +import pytest + +from tests.testing_harness import TestHarness, PyAPITestHarness def make_model(): @@ -67,21 +67,17 @@ def make_model(): class MultipoleTestHarness(PyAPITestHarness): - def execute_test(self): - if not 'OPENMC_MULTIPOLE_LIBRARY' in os.environ: - raise RuntimeError("The 'OPENMC_MULTIPOLE_LIBRARY' environment " - "variable must be specified for this test.") - else: - super(MultipoleTestHarness, self).execute_test() - def _get_results(self): - outstr = super(MultipoleTestHarness, self)._get_results() + outstr = super()._get_results() su = openmc.Summary('summary.h5') outstr += str(su.geometry.get_all_cells()[11]) return outstr -if __name__ == '__main__': +@pytest.mark.skipif('OPENMC_MULTIPOLE_LIBRARY' not in os.environ, + reason='OPENMC_MULTIPOLE_LIBRARY environment variable ' + 'must be set') +def test_multipole(): model = make_model() harness = MultipoleTestHarness('statepoint.5.h5', model) harness.main() diff --git a/tests/regression_tests/output/__init__.py b/tests/regression_tests/output/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_output/geometry.xml b/tests/regression_tests/output/geometry.xml similarity index 100% rename from tests/test_output/geometry.xml rename to tests/regression_tests/output/geometry.xml diff --git a/tests/test_output/materials.xml b/tests/regression_tests/output/materials.xml similarity index 100% rename from tests/test_output/materials.xml rename to tests/regression_tests/output/materials.xml diff --git a/tests/test_output/results_true.dat b/tests/regression_tests/output/results_true.dat similarity index 100% rename from tests/test_output/results_true.dat rename to tests/regression_tests/output/results_true.dat diff --git a/tests/test_output/settings.xml b/tests/regression_tests/output/settings.xml similarity index 100% rename from tests/test_output/settings.xml rename to tests/regression_tests/output/settings.xml diff --git a/tests/test_output/test_output.py b/tests/regression_tests/output/test.py similarity index 64% rename from tests/test_output/test_output.py rename to tests/regression_tests/output/test.py index 475b3e9f02..d9568ba4b0 100644 --- a/tests/test_output/test_output.py +++ b/tests/regression_tests/output/test.py @@ -1,10 +1,7 @@ -#!/usr/bin/env python - -import glob import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness +import glob + +from tests.testing_harness import TestHarness class OutputTestHarness(TestHarness): @@ -21,13 +18,11 @@ class OutputTestHarness(TestHarness): def _cleanup(self): TestHarness._cleanup(self) - output = glob.glob(os.path.join(os.getcwd(), 'summary.*')) - output.append(os.path.join(os.getcwd(), 'cross_sections.out')) - for f in output: - if os.path.exists(f): - os.remove(f) + f = 'summary.h5' + if os.path.exists(f): + os.remove(f) -if __name__ == '__main__': +def test_output(): harness = OutputTestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/particle_restart_eigval/__init__.py b/tests/regression_tests/particle_restart_eigval/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_particle_restart_eigval/geometry.xml b/tests/regression_tests/particle_restart_eigval/geometry.xml similarity index 100% rename from tests/test_particle_restart_eigval/geometry.xml rename to tests/regression_tests/particle_restart_eigval/geometry.xml diff --git a/tests/test_particle_restart_eigval/materials.xml b/tests/regression_tests/particle_restart_eigval/materials.xml similarity index 100% rename from tests/test_particle_restart_eigval/materials.xml rename to tests/regression_tests/particle_restart_eigval/materials.xml diff --git a/tests/test_particle_restart_eigval/results_true.dat b/tests/regression_tests/particle_restart_eigval/results_true.dat similarity index 100% rename from tests/test_particle_restart_eigval/results_true.dat rename to tests/regression_tests/particle_restart_eigval/results_true.dat diff --git a/tests/test_particle_restart_eigval/settings.xml b/tests/regression_tests/particle_restart_eigval/settings.xml similarity index 100% rename from tests/test_particle_restart_eigval/settings.xml rename to tests/regression_tests/particle_restart_eigval/settings.xml diff --git a/tests/regression_tests/particle_restart_eigval/test.py b/tests/regression_tests/particle_restart_eigval/test.py new file mode 100644 index 0000000000..a30eb97870 --- /dev/null +++ b/tests/regression_tests/particle_restart_eigval/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import ParticleRestartTestHarness + + +def test_particle_restart_eigval(): + harness = ParticleRestartTestHarness('particle_10_1030.h5') + harness.main() diff --git a/tests/regression_tests/particle_restart_fixed/__init__.py b/tests/regression_tests/particle_restart_fixed/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_particle_restart_fixed/geometry.xml b/tests/regression_tests/particle_restart_fixed/geometry.xml similarity index 100% rename from tests/test_particle_restart_fixed/geometry.xml rename to tests/regression_tests/particle_restart_fixed/geometry.xml diff --git a/tests/test_particle_restart_fixed/materials.xml b/tests/regression_tests/particle_restart_fixed/materials.xml similarity index 100% rename from tests/test_particle_restart_fixed/materials.xml rename to tests/regression_tests/particle_restart_fixed/materials.xml diff --git a/tests/test_particle_restart_fixed/results_true.dat b/tests/regression_tests/particle_restart_fixed/results_true.dat similarity index 100% rename from tests/test_particle_restart_fixed/results_true.dat rename to tests/regression_tests/particle_restart_fixed/results_true.dat diff --git a/tests/test_particle_restart_fixed/settings.xml b/tests/regression_tests/particle_restart_fixed/settings.xml similarity index 100% rename from tests/test_particle_restart_fixed/settings.xml rename to tests/regression_tests/particle_restart_fixed/settings.xml diff --git a/tests/regression_tests/particle_restart_fixed/test.py b/tests/regression_tests/particle_restart_fixed/test.py new file mode 100644 index 0000000000..463568ecca --- /dev/null +++ b/tests/regression_tests/particle_restart_fixed/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import ParticleRestartTestHarness + + +def test_particle_restart_fixed(): + harness = ParticleRestartTestHarness('particle_7_144.h5') + harness.main() diff --git a/tests/regression_tests/periodic/__init__.py b/tests/regression_tests/periodic/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_periodic/inputs_true.dat b/tests/regression_tests/periodic/inputs_true.dat similarity index 100% rename from tests/test_periodic/inputs_true.dat rename to tests/regression_tests/periodic/inputs_true.dat diff --git a/tests/test_periodic/results_true.dat b/tests/regression_tests/periodic/results_true.dat similarity index 100% rename from tests/test_periodic/results_true.dat rename to tests/regression_tests/periodic/results_true.dat diff --git a/tests/test_periodic/test_periodic.py b/tests/regression_tests/periodic/test.py similarity index 92% rename from tests/test_periodic/test_periodic.py rename to tests/regression_tests/periodic/test.py index 3883654c13..d746ebdd5f 100644 --- a/tests/test_periodic/test_periodic.py +++ b/tests/regression_tests/periodic/test.py @@ -1,11 +1,7 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness import openmc +from tests.testing_harness import PyAPITestHarness + class PeriodicTest(PyAPITestHarness): def _build_inputs(self): @@ -55,6 +51,6 @@ class PeriodicTest(PyAPITestHarness): settings.export_to_xml() -if __name__ == '__main__': +def test_periodic(): harness = PeriodicTest('statepoint.4.h5') harness.main() diff --git a/tests/regression_tests/plot/__init__.py b/tests/regression_tests/plot/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_plot/geometry.xml b/tests/regression_tests/plot/geometry.xml similarity index 100% rename from tests/test_plot/geometry.xml rename to tests/regression_tests/plot/geometry.xml diff --git a/tests/test_plot/materials.xml b/tests/regression_tests/plot/materials.xml similarity index 100% rename from tests/test_plot/materials.xml rename to tests/regression_tests/plot/materials.xml diff --git a/tests/test_plot/plots.xml b/tests/regression_tests/plot/plots.xml similarity index 100% rename from tests/test_plot/plots.xml rename to tests/regression_tests/plot/plots.xml diff --git a/tests/test_plot/results_true.dat b/tests/regression_tests/plot/results_true.dat similarity index 100% rename from tests/test_plot/results_true.dat rename to tests/regression_tests/plot/results_true.dat diff --git a/tests/test_plot/settings.xml b/tests/regression_tests/plot/settings.xml similarity index 100% rename from tests/test_plot/settings.xml rename to tests/regression_tests/plot/settings.xml diff --git a/tests/test_plot/test_plot.py b/tests/regression_tests/plot/test.py similarity index 74% rename from tests/test_plot/test_plot.py rename to tests/regression_tests/plot/test.py index 1a99c24886..bfaef019c1 100644 --- a/tests/test_plot/test_plot.py +++ b/tests/regression_tests/plot/test.py @@ -1,38 +1,33 @@ -#!/usr/bin/env python - import glob import hashlib import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness import h5py - import openmc +from tests.testing_harness import TestHarness +from tests.regression_tests import config + class PlotTestHarness(TestHarness): """Specialized TestHarness for running OpenMC plotting tests.""" def __init__(self, plot_names): - super(PlotTestHarness, self).__init__(None) + super().__init__(None) self._plot_names = plot_names def _run_openmc(self): - openmc.plot_geometry(openmc_exec=self._opts.exe) + openmc.plot_geometry(openmc_exec=config['exe']) def _test_output_created(self): """Make sure *.ppm has been created.""" for fname in self._plot_names: - assert os.path.exists(os.path.join(os.getcwd(), fname)), \ - 'Plot output file does not exist.' + assert os.path.exists(fname), 'Plot output file does not exist.' def _cleanup(self): - super(PlotTestHarness, self)._cleanup() + super()._cleanup() for fname in self._plot_names: - path = os.path.join(os.getcwd(), fname) - if os.path.exists(path): - os.remove(path) + if os.path.exists(fname): + os.remove(fname) def _get_results(self): """Return a string hash of the plot files.""" @@ -60,7 +55,7 @@ class PlotTestHarness(TestHarness): return outstr -if __name__ == '__main__': +def test_plot(): harness = PlotTestHarness(('plot_1.ppm', 'plot_2.ppm', 'plot_3.ppm', 'plot_4.h5')) harness.main() diff --git a/tests/regression_tests/ptables_off/__init__.py b/tests/regression_tests/ptables_off/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_ptables_off/geometry.xml b/tests/regression_tests/ptables_off/geometry.xml similarity index 100% rename from tests/test_ptables_off/geometry.xml rename to tests/regression_tests/ptables_off/geometry.xml diff --git a/tests/test_ptables_off/materials.xml b/tests/regression_tests/ptables_off/materials.xml similarity index 100% rename from tests/test_ptables_off/materials.xml rename to tests/regression_tests/ptables_off/materials.xml diff --git a/tests/test_ptables_off/results_true.dat b/tests/regression_tests/ptables_off/results_true.dat similarity index 100% rename from tests/test_ptables_off/results_true.dat rename to tests/regression_tests/ptables_off/results_true.dat diff --git a/tests/test_ptables_off/settings.xml b/tests/regression_tests/ptables_off/settings.xml similarity index 100% rename from tests/test_ptables_off/settings.xml rename to tests/regression_tests/ptables_off/settings.xml diff --git a/tests/regression_tests/ptables_off/test.py b/tests/regression_tests/ptables_off/test.py new file mode 100644 index 0000000000..cc316f8c3e --- /dev/null +++ b/tests/regression_tests/ptables_off/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_ptables_off(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/quadric_surfaces/__init__.py b/tests/regression_tests/quadric_surfaces/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_quadric_surfaces/geometry.xml b/tests/regression_tests/quadric_surfaces/geometry.xml similarity index 100% rename from tests/test_quadric_surfaces/geometry.xml rename to tests/regression_tests/quadric_surfaces/geometry.xml diff --git a/tests/test_quadric_surfaces/materials.xml b/tests/regression_tests/quadric_surfaces/materials.xml similarity index 100% rename from tests/test_quadric_surfaces/materials.xml rename to tests/regression_tests/quadric_surfaces/materials.xml diff --git a/tests/test_quadric_surfaces/results_true.dat b/tests/regression_tests/quadric_surfaces/results_true.dat similarity index 100% rename from tests/test_quadric_surfaces/results_true.dat rename to tests/regression_tests/quadric_surfaces/results_true.dat diff --git a/tests/test_quadric_surfaces/settings.xml b/tests/regression_tests/quadric_surfaces/settings.xml similarity index 100% rename from tests/test_quadric_surfaces/settings.xml rename to tests/regression_tests/quadric_surfaces/settings.xml diff --git a/tests/regression_tests/quadric_surfaces/test.py b/tests/regression_tests/quadric_surfaces/test.py new file mode 100755 index 0000000000..862ee9bf41 --- /dev/null +++ b/tests/regression_tests/quadric_surfaces/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_quadric_surfaces(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/reflective_plane/__init__.py b/tests/regression_tests/reflective_plane/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_reflective_plane/geometry.xml b/tests/regression_tests/reflective_plane/geometry.xml similarity index 100% rename from tests/test_reflective_plane/geometry.xml rename to tests/regression_tests/reflective_plane/geometry.xml diff --git a/tests/test_reflective_plane/materials.xml b/tests/regression_tests/reflective_plane/materials.xml similarity index 100% rename from tests/test_reflective_plane/materials.xml rename to tests/regression_tests/reflective_plane/materials.xml diff --git a/tests/test_reflective_plane/results_true.dat b/tests/regression_tests/reflective_plane/results_true.dat similarity index 100% rename from tests/test_reflective_plane/results_true.dat rename to tests/regression_tests/reflective_plane/results_true.dat diff --git a/tests/test_reflective_plane/settings.xml b/tests/regression_tests/reflective_plane/settings.xml similarity index 100% rename from tests/test_reflective_plane/settings.xml rename to tests/regression_tests/reflective_plane/settings.xml diff --git a/tests/regression_tests/reflective_plane/test.py b/tests/regression_tests/reflective_plane/test.py new file mode 100644 index 0000000000..906174f338 --- /dev/null +++ b/tests/regression_tests/reflective_plane/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_reflective_plane(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/resonance_scattering/__init__.py b/tests/regression_tests/resonance_scattering/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_resonance_scattering/inputs_true.dat b/tests/regression_tests/resonance_scattering/inputs_true.dat similarity index 100% rename from tests/test_resonance_scattering/inputs_true.dat rename to tests/regression_tests/resonance_scattering/inputs_true.dat diff --git a/tests/test_resonance_scattering/results_true.dat b/tests/regression_tests/resonance_scattering/results_true.dat similarity index 100% rename from tests/test_resonance_scattering/results_true.dat rename to tests/regression_tests/resonance_scattering/results_true.dat diff --git a/tests/test_resonance_scattering/test_resonance_scattering.py b/tests/regression_tests/resonance_scattering/test.py similarity index 90% rename from tests/test_resonance_scattering/test_resonance_scattering.py rename to tests/regression_tests/resonance_scattering/test.py index 94c9f5c3de..15e9d68944 100644 --- a/tests/test_resonance_scattering/test_resonance_scattering.py +++ b/tests/regression_tests/resonance_scattering/test.py @@ -1,11 +1,7 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness import openmc +from tests.testing_harness import PyAPITestHarness + class ResonanceScatteringTestHarness(PyAPITestHarness): def _build_inputs(self): @@ -46,6 +42,6 @@ class ResonanceScatteringTestHarness(PyAPITestHarness): settings.export_to_xml() -if __name__ == '__main__': +def test_resonance_scattering(): harness = ResonanceScatteringTestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/rotation/__init__.py b/tests/regression_tests/rotation/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_rotation/geometry.xml b/tests/regression_tests/rotation/geometry.xml similarity index 100% rename from tests/test_rotation/geometry.xml rename to tests/regression_tests/rotation/geometry.xml diff --git a/tests/test_rotation/materials.xml b/tests/regression_tests/rotation/materials.xml similarity index 100% rename from tests/test_rotation/materials.xml rename to tests/regression_tests/rotation/materials.xml diff --git a/tests/test_rotation/results_true.dat b/tests/regression_tests/rotation/results_true.dat similarity index 100% rename from tests/test_rotation/results_true.dat rename to tests/regression_tests/rotation/results_true.dat diff --git a/tests/test_rotation/settings.xml b/tests/regression_tests/rotation/settings.xml similarity index 100% rename from tests/test_rotation/settings.xml rename to tests/regression_tests/rotation/settings.xml diff --git a/tests/regression_tests/rotation/test.py b/tests/regression_tests/rotation/test.py new file mode 100644 index 0000000000..31c3d8d655 --- /dev/null +++ b/tests/regression_tests/rotation/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_rotation(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/salphabeta/__init__.py b/tests/regression_tests/salphabeta/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_salphabeta/inputs_true.dat b/tests/regression_tests/salphabeta/inputs_true.dat similarity index 100% rename from tests/test_salphabeta/inputs_true.dat rename to tests/regression_tests/salphabeta/inputs_true.dat diff --git a/tests/test_salphabeta/results_true.dat b/tests/regression_tests/salphabeta/results_true.dat similarity index 100% rename from tests/test_salphabeta/results_true.dat rename to tests/regression_tests/salphabeta/results_true.dat diff --git a/tests/test_salphabeta/test_salphabeta.py b/tests/regression_tests/salphabeta/test.py similarity index 93% rename from tests/test_salphabeta/test_salphabeta.py rename to tests/regression_tests/salphabeta/test.py index 600c703324..e69ada5ba3 100644 --- a/tests/test_salphabeta/test_salphabeta.py +++ b/tests/regression_tests/salphabeta/test.py @@ -1,13 +1,8 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) - -from testing_harness import PyAPITestHarness import openmc import openmc.model +from tests.testing_harness import PyAPITestHarness + def make_model(): model = openmc.model.Model() @@ -78,7 +73,7 @@ def make_model(): return model -if __name__ == '__main__': +def test_salphabeta(): model = make_model() harness = PyAPITestHarness('statepoint.5.h5', model) harness.main() diff --git a/tests/regression_tests/score_current/__init__.py b/tests/regression_tests/score_current/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_score_current/geometry.xml b/tests/regression_tests/score_current/geometry.xml similarity index 100% rename from tests/test_score_current/geometry.xml rename to tests/regression_tests/score_current/geometry.xml diff --git a/tests/test_score_current/materials.xml b/tests/regression_tests/score_current/materials.xml similarity index 100% rename from tests/test_score_current/materials.xml rename to tests/regression_tests/score_current/materials.xml diff --git a/tests/test_score_current/results_true.dat b/tests/regression_tests/score_current/results_true.dat similarity index 100% rename from tests/test_score_current/results_true.dat rename to tests/regression_tests/score_current/results_true.dat diff --git a/tests/test_score_current/settings.xml b/tests/regression_tests/score_current/settings.xml similarity index 100% rename from tests/test_score_current/settings.xml rename to tests/regression_tests/score_current/settings.xml diff --git a/tests/test_score_current/tallies.xml b/tests/regression_tests/score_current/tallies.xml similarity index 100% rename from tests/test_score_current/tallies.xml rename to tests/regression_tests/score_current/tallies.xml diff --git a/tests/regression_tests/score_current/test.py b/tests/regression_tests/score_current/test.py new file mode 100644 index 0000000000..21c5d08812 --- /dev/null +++ b/tests/regression_tests/score_current/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import HashedTestHarness + + +def test_score_current(): + harness = HashedTestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/seed/__init__.py b/tests/regression_tests/seed/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_seed/geometry.xml b/tests/regression_tests/seed/geometry.xml similarity index 100% rename from tests/test_seed/geometry.xml rename to tests/regression_tests/seed/geometry.xml diff --git a/tests/test_seed/materials.xml b/tests/regression_tests/seed/materials.xml similarity index 100% rename from tests/test_seed/materials.xml rename to tests/regression_tests/seed/materials.xml diff --git a/tests/test_seed/results_true.dat b/tests/regression_tests/seed/results_true.dat similarity index 100% rename from tests/test_seed/results_true.dat rename to tests/regression_tests/seed/results_true.dat diff --git a/tests/test_seed/settings.xml b/tests/regression_tests/seed/settings.xml similarity index 100% rename from tests/test_seed/settings.xml rename to tests/regression_tests/seed/settings.xml diff --git a/tests/regression_tests/seed/test.py b/tests/regression_tests/seed/test.py new file mode 100644 index 0000000000..8b2f031b3e --- /dev/null +++ b/tests/regression_tests/seed/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_seed(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/source/__init__.py b/tests/regression_tests/source/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_source/inputs_true.dat b/tests/regression_tests/source/inputs_true.dat similarity index 100% rename from tests/test_source/inputs_true.dat rename to tests/regression_tests/source/inputs_true.dat diff --git a/tests/test_source/results_true.dat b/tests/regression_tests/source/results_true.dat similarity index 100% rename from tests/test_source/results_true.dat rename to tests/regression_tests/source/results_true.dat diff --git a/tests/test_source/test_source.py b/tests/regression_tests/source/test.py similarity index 94% rename from tests/test_source/test_source.py rename to tests/regression_tests/source/test.py index a872ab4816..00171a2551 100644 --- a/tests/test_source/test_source.py +++ b/tests/regression_tests/source/test.py @@ -1,15 +1,10 @@ -#!/usr/bin/env python - from math import pi -import os -import sys import numpy as np - -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness import openmc +from tests.testing_harness import PyAPITestHarness + class SourceTestHarness(PyAPITestHarness): def _build_inputs(self): @@ -63,6 +58,6 @@ class SourceTestHarness(PyAPITestHarness): settings.export_to_xml() -if __name__ == '__main__': +def test_source(): harness = SourceTestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/source_file/__init__.py b/tests/regression_tests/source_file/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_source_file/geometry.xml b/tests/regression_tests/source_file/geometry.xml similarity index 100% rename from tests/test_source_file/geometry.xml rename to tests/regression_tests/source_file/geometry.xml diff --git a/tests/test_source_file/materials.xml b/tests/regression_tests/source_file/materials.xml similarity index 100% rename from tests/test_source_file/materials.xml rename to tests/regression_tests/source_file/materials.xml diff --git a/tests/test_source_file/results_true.dat b/tests/regression_tests/source_file/results_true.dat similarity index 100% rename from tests/test_source_file/results_true.dat rename to tests/regression_tests/source_file/results_true.dat diff --git a/tests/test_source_file/settings.xml b/tests/regression_tests/source_file/settings.xml similarity index 100% rename from tests/test_source_file/settings.xml rename to tests/regression_tests/source_file/settings.xml diff --git a/tests/test_source_file/test_source_file.py b/tests/regression_tests/source_file/test.py similarity index 96% rename from tests/test_source_file/test_source_file.py rename to tests/regression_tests/source_file/test.py index 5631814b4c..b4d9c4a31d 100644 --- a/tests/test_source_file/test_source_file.py +++ b/tests/regression_tests/source_file/test.py @@ -2,9 +2,8 @@ import glob import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import * + +from tests.testing_harness import * settings1=""" @@ -97,6 +96,6 @@ class SourceFileTestHarness(TestHarness): fh.write(settings1) -if __name__ == '__main__': +def test_source_file(): harness = SourceFileTestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/sourcepoint_batch/__init__.py b/tests/regression_tests/sourcepoint_batch/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_sourcepoint_batch/geometry.xml b/tests/regression_tests/sourcepoint_batch/geometry.xml similarity index 100% rename from tests/test_sourcepoint_batch/geometry.xml rename to tests/regression_tests/sourcepoint_batch/geometry.xml diff --git a/tests/test_sourcepoint_batch/materials.xml b/tests/regression_tests/sourcepoint_batch/materials.xml similarity index 100% rename from tests/test_sourcepoint_batch/materials.xml rename to tests/regression_tests/sourcepoint_batch/materials.xml diff --git a/tests/test_sourcepoint_batch/results_true.dat b/tests/regression_tests/sourcepoint_batch/results_true.dat similarity index 100% rename from tests/test_sourcepoint_batch/results_true.dat rename to tests/regression_tests/sourcepoint_batch/results_true.dat diff --git a/tests/test_sourcepoint_batch/settings.xml b/tests/regression_tests/sourcepoint_batch/settings.xml similarity index 100% rename from tests/test_sourcepoint_batch/settings.xml rename to tests/regression_tests/sourcepoint_batch/settings.xml diff --git a/tests/test_sourcepoint_batch/test_sourcepoint_batch.py b/tests/regression_tests/sourcepoint_batch/test.py similarity index 52% rename from tests/test_sourcepoint_batch/test_sourcepoint_batch.py rename to tests/regression_tests/sourcepoint_batch/test.py index d5ea0e48b2..3086b5610d 100644 --- a/tests/test_sourcepoint_batch/test_sourcepoint_batch.py +++ b/tests/regression_tests/sourcepoint_batch/test.py @@ -1,20 +1,15 @@ -#!/usr/bin/env python - import glob -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness + from openmc import StatePoint +from tests.testing_harness import TestHarness + class SourcepointTestHarness(TestHarness): def _test_output_created(self): - """Make sure statepoint.* files have been created.""" - statepoint = glob.glob(os.path.join(os.getcwd(), 'statepoint.*')) - assert len(statepoint) == 5, '5 statepoint files must exist.' - assert statepoint[0].endswith('h5'), \ - 'Statepoint file is not a HDF5 file.' + """Make sure statepoint files have been created.""" + statepoint = glob.glob('statepoint.*.h5') + assert len(statepoint) == 5, 'Five statepoint files must exist.' def _get_results(self): """Digest info in the statepoint and return as a string.""" @@ -22,8 +17,7 @@ class SourcepointTestHarness(TestHarness): outstr = TestHarness._get_results(self) # Read the statepoint file. - statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] - with StatePoint(statepoint) as sp: + with StatePoint(self._sp_name) as sp: # Add the source information. xyz = sp.source[0]['xyz'] outstr += ' '.join(['{0:12.6E}'.format(x) for x in xyz]) @@ -32,6 +26,6 @@ class SourcepointTestHarness(TestHarness): return outstr -if __name__ == '__main__': +def test_sourcepoint_batch(): harness = SourcepointTestHarness('statepoint.08.h5') harness.main() diff --git a/tests/regression_tests/sourcepoint_latest/__init__.py b/tests/regression_tests/sourcepoint_latest/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_sourcepoint_latest/geometry.xml b/tests/regression_tests/sourcepoint_latest/geometry.xml similarity index 100% rename from tests/test_sourcepoint_latest/geometry.xml rename to tests/regression_tests/sourcepoint_latest/geometry.xml diff --git a/tests/test_sourcepoint_latest/materials.xml b/tests/regression_tests/sourcepoint_latest/materials.xml similarity index 100% rename from tests/test_sourcepoint_latest/materials.xml rename to tests/regression_tests/sourcepoint_latest/materials.xml diff --git a/tests/test_sourcepoint_latest/results_true.dat b/tests/regression_tests/sourcepoint_latest/results_true.dat similarity index 100% rename from tests/test_sourcepoint_latest/results_true.dat rename to tests/regression_tests/sourcepoint_latest/results_true.dat diff --git a/tests/test_sourcepoint_latest/settings.xml b/tests/regression_tests/sourcepoint_latest/settings.xml similarity index 100% rename from tests/test_sourcepoint_latest/settings.xml rename to tests/regression_tests/sourcepoint_latest/settings.xml diff --git a/tests/test_sourcepoint_latest/test_sourcepoint_latest.py b/tests/regression_tests/sourcepoint_latest/test.py similarity index 58% rename from tests/test_sourcepoint_latest/test_sourcepoint_latest.py rename to tests/regression_tests/sourcepoint_latest/test.py index c64cc20cc7..d14b566ad8 100644 --- a/tests/test_sourcepoint_latest/test_sourcepoint_latest.py +++ b/tests/regression_tests/sourcepoint_latest/test.py @@ -1,22 +1,18 @@ -#!/usr/bin/env python - import os import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness + +from tests.testing_harness import TestHarness class SourcepointTestHarness(TestHarness): def _test_output_created(self): """Make sure statepoint.* and source* have been created.""" TestHarness._test_output_created(self) - source = glob.glob(os.path.join(os.getcwd(), 'source.*')) + source = glob.glob(os.path.join(os.getcwd(), 'source.*.h5')) assert len(source) == 1, 'Either multiple or no source files ' \ 'exist.' - assert source[0].endswith('h5'), \ - 'Source file is not a HDF5 file.' -if __name__ == '__main__': +def test_sourcepoint_latest(): harness = TestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/sourcepoint_restart/__init__.py b/tests/regression_tests/sourcepoint_restart/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_sourcepoint_restart/geometry.xml b/tests/regression_tests/sourcepoint_restart/geometry.xml similarity index 100% rename from tests/test_sourcepoint_restart/geometry.xml rename to tests/regression_tests/sourcepoint_restart/geometry.xml diff --git a/tests/test_sourcepoint_restart/materials.xml b/tests/regression_tests/sourcepoint_restart/materials.xml similarity index 100% rename from tests/test_sourcepoint_restart/materials.xml rename to tests/regression_tests/sourcepoint_restart/materials.xml diff --git a/tests/test_sourcepoint_restart/results_true.dat b/tests/regression_tests/sourcepoint_restart/results_true.dat similarity index 100% rename from tests/test_sourcepoint_restart/results_true.dat rename to tests/regression_tests/sourcepoint_restart/results_true.dat diff --git a/tests/test_sourcepoint_restart/settings.xml b/tests/regression_tests/sourcepoint_restart/settings.xml similarity index 100% rename from tests/test_sourcepoint_restart/settings.xml rename to tests/regression_tests/sourcepoint_restart/settings.xml diff --git a/tests/test_sourcepoint_restart/tallies.xml b/tests/regression_tests/sourcepoint_restart/tallies.xml similarity index 100% rename from tests/test_sourcepoint_restart/tallies.xml rename to tests/regression_tests/sourcepoint_restart/tallies.xml diff --git a/tests/regression_tests/sourcepoint_restart/test.py b/tests/regression_tests/sourcepoint_restart/test.py new file mode 100644 index 0000000000..32f53ec238 --- /dev/null +++ b/tests/regression_tests/sourcepoint_restart/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_sourcepoint_restart(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/statepoint_batch/__init__.py b/tests/regression_tests/statepoint_batch/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_statepoint_batch/geometry.xml b/tests/regression_tests/statepoint_batch/geometry.xml similarity index 100% rename from tests/test_statepoint_batch/geometry.xml rename to tests/regression_tests/statepoint_batch/geometry.xml diff --git a/tests/test_statepoint_batch/materials.xml b/tests/regression_tests/statepoint_batch/materials.xml similarity index 100% rename from tests/test_statepoint_batch/materials.xml rename to tests/regression_tests/statepoint_batch/materials.xml diff --git a/tests/test_statepoint_batch/results_true.dat b/tests/regression_tests/statepoint_batch/results_true.dat similarity index 100% rename from tests/test_statepoint_batch/results_true.dat rename to tests/regression_tests/statepoint_batch/results_true.dat diff --git a/tests/test_statepoint_batch/settings.xml b/tests/regression_tests/statepoint_batch/settings.xml similarity index 100% rename from tests/test_statepoint_batch/settings.xml rename to tests/regression_tests/statepoint_batch/settings.xml diff --git a/tests/test_statepoint_batch/test_statepoint_batch.py b/tests/regression_tests/statepoint_batch/test.py similarity index 66% rename from tests/test_statepoint_batch/test_statepoint_batch.py rename to tests/regression_tests/statepoint_batch/test.py index e3e2391ba1..323b28fc65 100644 --- a/tests/test_statepoint_batch/test_statepoint_batch.py +++ b/tests/regression_tests/statepoint_batch/test.py @@ -1,14 +1,9 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness +from tests.testing_harness import TestHarness class StatepointTestHarness(TestHarness): def __init__(self): - super(StatepointTestHarness, self).__init__(None) + super().__init__(None) def _test_output_created(self): """Make sure statepoint files have been created.""" @@ -18,6 +13,6 @@ class StatepointTestHarness(TestHarness): TestHarness._test_output_created(self) -if __name__ == '__main__': +def test_statepoint_batch(): harness = StatepointTestHarness() harness.main() diff --git a/tests/regression_tests/statepoint_restart/__init__.py b/tests/regression_tests/statepoint_restart/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_statepoint_restart/geometry.xml b/tests/regression_tests/statepoint_restart/geometry.xml similarity index 100% rename from tests/test_statepoint_restart/geometry.xml rename to tests/regression_tests/statepoint_restart/geometry.xml diff --git a/tests/test_statepoint_restart/materials.xml b/tests/regression_tests/statepoint_restart/materials.xml similarity index 100% rename from tests/test_statepoint_restart/materials.xml rename to tests/regression_tests/statepoint_restart/materials.xml diff --git a/tests/test_statepoint_restart/results_true.dat b/tests/regression_tests/statepoint_restart/results_true.dat similarity index 100% rename from tests/test_statepoint_restart/results_true.dat rename to tests/regression_tests/statepoint_restart/results_true.dat diff --git a/tests/test_statepoint_restart/settings.xml b/tests/regression_tests/statepoint_restart/settings.xml similarity index 100% rename from tests/test_statepoint_restart/settings.xml rename to tests/regression_tests/statepoint_restart/settings.xml diff --git a/tests/test_statepoint_restart/tallies.xml b/tests/regression_tests/statepoint_restart/tallies.xml similarity index 100% rename from tests/test_statepoint_restart/tallies.xml rename to tests/regression_tests/statepoint_restart/tallies.xml diff --git a/tests/test_statepoint_restart/test_statepoint_restart.py b/tests/regression_tests/statepoint_restart/test.py similarity index 76% rename from tests/test_statepoint_restart/test_statepoint_restart.py rename to tests/regression_tests/statepoint_restart/test.py index 9c10551dea..4575607f7d 100644 --- a/tests/test_statepoint_restart/test_statepoint_restart.py +++ b/tests/regression_tests/statepoint_restart/test.py @@ -1,16 +1,15 @@ -#!/usr/bin/env python - import glob import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness + import openmc +from tests.testing_harness import TestHarness +from tests.regression_tests import config + class StatepointRestartTestHarness(TestHarness): def __init__(self, final_sp, restart_sp): - super(StatepointRestartTestHarness, self).__init__(final_sp) + super().__init__(final_sp) self._restart_sp = restart_sp def execute_test(self): @@ -48,15 +47,15 @@ class StatepointRestartTestHarness(TestHarness): statepoint = statepoint[0] # Run OpenMC - if self._opts.mpi_exec is not None: - mpi_args = [self._opts.mpi_exec, '-n', self._opts.mpi_np] - openmc.run(restart_file=statepoint, openmc_exec=self._opts.exe, + if config['mpi']: + mpi_args = [config['mpiexec'], '-n', config['mpi_np']] + openmc.run(restart_file=statepoint, openmc_exec=config['exe'], mpi_args=mpi_args) else: - openmc.run(openmc_exec=self._opts.exe, restart_file=statepoint) + openmc.run(openmc_exec=config['exe'], restart_file=statepoint) -if __name__ == '__main__': +def test_statepoint_restart(): harness = StatepointRestartTestHarness('statepoint.10.h5', 'statepoint.07.h5') harness.main() diff --git a/tests/regression_tests/statepoint_sourcesep/__init__.py b/tests/regression_tests/statepoint_sourcesep/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_statepoint_sourcesep/geometry.xml b/tests/regression_tests/statepoint_sourcesep/geometry.xml similarity index 100% rename from tests/test_statepoint_sourcesep/geometry.xml rename to tests/regression_tests/statepoint_sourcesep/geometry.xml diff --git a/tests/test_statepoint_sourcesep/materials.xml b/tests/regression_tests/statepoint_sourcesep/materials.xml similarity index 100% rename from tests/test_statepoint_sourcesep/materials.xml rename to tests/regression_tests/statepoint_sourcesep/materials.xml diff --git a/tests/test_statepoint_sourcesep/results_true.dat b/tests/regression_tests/statepoint_sourcesep/results_true.dat similarity index 100% rename from tests/test_statepoint_sourcesep/results_true.dat rename to tests/regression_tests/statepoint_sourcesep/results_true.dat diff --git a/tests/test_statepoint_sourcesep/settings.xml b/tests/regression_tests/statepoint_sourcesep/settings.xml similarity index 100% rename from tests/test_statepoint_sourcesep/settings.xml rename to tests/regression_tests/statepoint_sourcesep/settings.xml diff --git a/tests/test_statepoint_sourcesep/test_statepoint_sourcesep.py b/tests/regression_tests/statepoint_sourcesep/test.py similarity index 60% rename from tests/test_statepoint_sourcesep/test_statepoint_sourcesep.py rename to tests/regression_tests/statepoint_sourcesep/test.py index f4bdcfb7b3..3d63ca8ee5 100644 --- a/tests/test_statepoint_sourcesep/test_statepoint_sourcesep.py +++ b/tests/regression_tests/statepoint_sourcesep/test.py @@ -1,30 +1,25 @@ -#!/usr/bin/env python - import glob import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness + +from tests.testing_harness import TestHarness class SourcepointTestHarness(TestHarness): def _test_output_created(self): """Make sure statepoint.* and source* have been created.""" TestHarness._test_output_created(self) - source = glob.glob(os.path.join(os.getcwd(), 'source.*')) + source = glob.glob('source.*.h5') assert len(source) == 1, 'Either multiple or no source files ' \ 'exist.' - assert source[0].endswith('h5'), \ - 'Source file is not a HDF5 file.' def _cleanup(self): TestHarness._cleanup(self) - output = glob.glob(os.path.join(os.getcwd(), 'source.*')) + output = glob.glob('source.*.h5') for f in output: if os.path.exists(f): os.remove(f) -if __name__ == '__main__': +def test_statepoint_sourcesep(): harness = SourcepointTestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/surface_tally/__init__.py b/tests/regression_tests/surface_tally/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_surface_tally/inputs_true.dat b/tests/regression_tests/surface_tally/inputs_true.dat similarity index 100% rename from tests/test_surface_tally/inputs_true.dat rename to tests/regression_tests/surface_tally/inputs_true.dat diff --git a/tests/test_surface_tally/results_true.dat b/tests/regression_tests/surface_tally/results_true.dat similarity index 100% rename from tests/test_surface_tally/results_true.dat rename to tests/regression_tests/surface_tally/results_true.dat diff --git a/tests/test_surface_tally/test_surface_tally.py b/tests/regression_tests/surface_tally/test.py similarity index 98% rename from tests/test_surface_tally/test_surface_tally.py rename to tests/regression_tests/surface_tally/test.py index ae525a6838..6995b7780c 100644 --- a/tests/test_surface_tally/test_surface_tally.py +++ b/tests/regression_tests/surface_tally/test.py @@ -1,13 +1,9 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness import numpy as np import openmc import pandas as pd +from tests.testing_harness import PyAPITestHarness + class SurfaceTallyTestHarness(PyAPITestHarness): def _build_inputs(self): @@ -178,6 +174,6 @@ class SurfaceTallyTestHarness(PyAPITestHarness): return outstr -if __name__ == '__main__': +def test_surface_tally(): harness = SurfaceTallyTestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/survival_biasing/__init__.py b/tests/regression_tests/survival_biasing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_survival_biasing/geometry.xml b/tests/regression_tests/survival_biasing/geometry.xml similarity index 100% rename from tests/test_survival_biasing/geometry.xml rename to tests/regression_tests/survival_biasing/geometry.xml diff --git a/tests/test_survival_biasing/materials.xml b/tests/regression_tests/survival_biasing/materials.xml similarity index 100% rename from tests/test_survival_biasing/materials.xml rename to tests/regression_tests/survival_biasing/materials.xml diff --git a/tests/test_survival_biasing/results_true.dat b/tests/regression_tests/survival_biasing/results_true.dat similarity index 100% rename from tests/test_survival_biasing/results_true.dat rename to tests/regression_tests/survival_biasing/results_true.dat diff --git a/tests/test_survival_biasing/settings.xml b/tests/regression_tests/survival_biasing/settings.xml similarity index 100% rename from tests/test_survival_biasing/settings.xml rename to tests/regression_tests/survival_biasing/settings.xml diff --git a/tests/test_survival_biasing/tallies.xml b/tests/regression_tests/survival_biasing/tallies.xml similarity index 100% rename from tests/test_survival_biasing/tallies.xml rename to tests/regression_tests/survival_biasing/tallies.xml diff --git a/tests/regression_tests/survival_biasing/test.py b/tests/regression_tests/survival_biasing/test.py new file mode 100644 index 0000000000..39e6faed8e --- /dev/null +++ b/tests/regression_tests/survival_biasing/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_survival_biasing(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/tallies/__init__.py b/tests/regression_tests/tallies/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_tallies/inputs_true.dat b/tests/regression_tests/tallies/inputs_true.dat similarity index 100% rename from tests/test_tallies/inputs_true.dat rename to tests/regression_tests/tallies/inputs_true.dat diff --git a/tests/test_tallies/results_true.dat b/tests/regression_tests/tallies/results_true.dat similarity index 100% rename from tests/test_tallies/results_true.dat rename to tests/regression_tests/tallies/results_true.dat diff --git a/tests/test_tallies/test_tallies.py b/tests/regression_tests/tallies/test.py similarity index 97% rename from tests/test_tallies/test_tallies.py rename to tests/regression_tests/tallies/test.py index 577d2babc0..e3aebfd986 100644 --- a/tests/test_tallies/test_tallies.py +++ b/tests/regression_tests/tallies/test.py @@ -1,15 +1,10 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) - -from testing_harness import HashedPyAPITestHarness from openmc.filter import * from openmc import Mesh, Tally, Tallies +from tests.testing_harness import HashedPyAPITestHarness -if __name__ == '__main__': + +def test_tallies(): harness = HashedPyAPITestHarness('statepoint.5.h5') model = harness._model diff --git a/tests/regression_tests/tally_aggregation/__init__.py b/tests/regression_tests/tally_aggregation/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_tally_aggregation/inputs_true.dat b/tests/regression_tests/tally_aggregation/inputs_true.dat similarity index 100% rename from tests/test_tally_aggregation/inputs_true.dat rename to tests/regression_tests/tally_aggregation/inputs_true.dat diff --git a/tests/test_tally_aggregation/results_true.dat b/tests/regression_tests/tally_aggregation/results_true.dat similarity index 100% rename from tests/test_tally_aggregation/results_true.dat rename to tests/regression_tests/tally_aggregation/results_true.dat diff --git a/tests/test_tally_aggregation/test_tally_aggregation.py b/tests/regression_tests/tally_aggregation/test.py similarity index 85% rename from tests/test_tally_aggregation/test_tally_aggregation.py rename to tests/regression_tests/tally_aggregation/test.py index cf291e0266..f7df543ded 100644 --- a/tests/test_tally_aggregation/test_tally_aggregation.py +++ b/tests/regression_tests/tally_aggregation/test.py @@ -1,17 +1,13 @@ -#!/usr/bin/env python - -import os -import sys -import glob import hashlib -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness + import openmc +from tests.testing_harness import PyAPITestHarness + class TallyAggregationTestHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): - super(TallyAggregationTestHarness, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Initialize the filters energy_filter = openmc.EnergyFilter([0.0, 0.253, 1.0e3, 1.0e6, 20.0e6]) @@ -28,8 +24,7 @@ class TallyAggregationTestHarness(PyAPITestHarness): """Digest info in the statepoint and return as a string.""" # Read the statepoint file. - statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] - sp = openmc.StatePoint(statepoint) + sp = openmc.StatePoint(self._sp_name) # Extract the tally of interest tally = sp.get_tally(name='distribcell tally') @@ -66,6 +61,6 @@ class TallyAggregationTestHarness(PyAPITestHarness): return outstr -if __name__ == '__main__': +def test_tally_aggregation(): harness = TallyAggregationTestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/tally_arithmetic/__init__.py b/tests/regression_tests/tally_arithmetic/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_tally_arithmetic/inputs_true.dat b/tests/regression_tests/tally_arithmetic/inputs_true.dat similarity index 100% rename from tests/test_tally_arithmetic/inputs_true.dat rename to tests/regression_tests/tally_arithmetic/inputs_true.dat diff --git a/tests/test_tally_arithmetic/results_true.dat b/tests/regression_tests/tally_arithmetic/results_true.dat similarity index 100% rename from tests/test_tally_arithmetic/results_true.dat rename to tests/regression_tests/tally_arithmetic/results_true.dat diff --git a/tests/test_tally_arithmetic/test_tally_arithmetic.py b/tests/regression_tests/tally_arithmetic/test.py similarity index 87% rename from tests/test_tally_arithmetic/test_tally_arithmetic.py rename to tests/regression_tests/tally_arithmetic/test.py index 593a0a2917..3adf0c5bef 100644 --- a/tests/test_tally_arithmetic/test_tally_arithmetic.py +++ b/tests/regression_tests/tally_arithmetic/test.py @@ -1,17 +1,13 @@ -#!/usr/bin/env python - -import os -import sys -import glob import hashlib -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness + import openmc +from tests.testing_harness import PyAPITestHarness + class TallyArithmeticTestHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): - super(TallyArithmeticTestHarness, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Initialize Mesh mesh = openmc.Mesh(mesh_id=1) @@ -43,8 +39,7 @@ class TallyArithmeticTestHarness(PyAPITestHarness): """Digest info in the statepoint and return as a string.""" # Read the statepoint file. - statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] - sp = openmc.StatePoint(statepoint) + sp = openmc.StatePoint(self._sp_name) # Load the tallies tally_1 = sp.get_tally(name='tally 1') @@ -80,6 +75,6 @@ class TallyArithmeticTestHarness(PyAPITestHarness): return outstr -if __name__ == '__main__': +def test_tally_arithmetic(): harness = TallyArithmeticTestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/tally_assumesep/__init__.py b/tests/regression_tests/tally_assumesep/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_tally_assumesep/geometry.xml b/tests/regression_tests/tally_assumesep/geometry.xml similarity index 100% rename from tests/test_tally_assumesep/geometry.xml rename to tests/regression_tests/tally_assumesep/geometry.xml diff --git a/tests/test_tally_assumesep/materials.xml b/tests/regression_tests/tally_assumesep/materials.xml similarity index 100% rename from tests/test_tally_assumesep/materials.xml rename to tests/regression_tests/tally_assumesep/materials.xml diff --git a/tests/test_tally_assumesep/results_true.dat b/tests/regression_tests/tally_assumesep/results_true.dat similarity index 100% rename from tests/test_tally_assumesep/results_true.dat rename to tests/regression_tests/tally_assumesep/results_true.dat diff --git a/tests/test_tally_assumesep/settings.xml b/tests/regression_tests/tally_assumesep/settings.xml similarity index 100% rename from tests/test_tally_assumesep/settings.xml rename to tests/regression_tests/tally_assumesep/settings.xml diff --git a/tests/test_tally_assumesep/tallies.xml b/tests/regression_tests/tally_assumesep/tallies.xml similarity index 100% rename from tests/test_tally_assumesep/tallies.xml rename to tests/regression_tests/tally_assumesep/tallies.xml diff --git a/tests/regression_tests/tally_assumesep/test.py b/tests/regression_tests/tally_assumesep/test.py new file mode 100644 index 0000000000..64595ced22 --- /dev/null +++ b/tests/regression_tests/tally_assumesep/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_tally_assumesep(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/tally_nuclides/__init__.py b/tests/regression_tests/tally_nuclides/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_tally_nuclides/geometry.xml b/tests/regression_tests/tally_nuclides/geometry.xml similarity index 100% rename from tests/test_tally_nuclides/geometry.xml rename to tests/regression_tests/tally_nuclides/geometry.xml diff --git a/tests/test_tally_nuclides/materials.xml b/tests/regression_tests/tally_nuclides/materials.xml similarity index 100% rename from tests/test_tally_nuclides/materials.xml rename to tests/regression_tests/tally_nuclides/materials.xml diff --git a/tests/test_tally_nuclides/results_true.dat b/tests/regression_tests/tally_nuclides/results_true.dat similarity index 100% rename from tests/test_tally_nuclides/results_true.dat rename to tests/regression_tests/tally_nuclides/results_true.dat diff --git a/tests/test_tally_nuclides/settings.xml b/tests/regression_tests/tally_nuclides/settings.xml similarity index 100% rename from tests/test_tally_nuclides/settings.xml rename to tests/regression_tests/tally_nuclides/settings.xml diff --git a/tests/test_tally_nuclides/tallies.xml b/tests/regression_tests/tally_nuclides/tallies.xml similarity index 100% rename from tests/test_tally_nuclides/tallies.xml rename to tests/regression_tests/tally_nuclides/tallies.xml diff --git a/tests/regression_tests/tally_nuclides/test.py b/tests/regression_tests/tally_nuclides/test.py new file mode 100644 index 0000000000..ee1f4b600e --- /dev/null +++ b/tests/regression_tests/tally_nuclides/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_tally_nuclides(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/tally_slice_merge/__init__.py b/tests/regression_tests/tally_slice_merge/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_tally_slice_merge/inputs_true.dat b/tests/regression_tests/tally_slice_merge/inputs_true.dat similarity index 100% rename from tests/test_tally_slice_merge/inputs_true.dat rename to tests/regression_tests/tally_slice_merge/inputs_true.dat diff --git a/tests/test_tally_slice_merge/results_true.dat b/tests/regression_tests/tally_slice_merge/results_true.dat similarity index 100% rename from tests/test_tally_slice_merge/results_true.dat rename to tests/regression_tests/tally_slice_merge/results_true.dat diff --git a/tests/test_tally_slice_merge/test_tally_slice_merge.py b/tests/regression_tests/tally_slice_merge/test.py similarity index 94% rename from tests/test_tally_slice_merge/test_tally_slice_merge.py rename to tests/regression_tests/tally_slice_merge/test.py index d917d2dadb..f79c8b2685 100644 --- a/tests/test_tally_slice_merge/test_tally_slice_merge.py +++ b/tests/regression_tests/tally_slice_merge/test.py @@ -1,20 +1,14 @@ -#!/usr/bin/env python - -from __future__ import division - -import os -import sys -import glob import hashlib import itertools -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness + import openmc +from tests.testing_harness import PyAPITestHarness + class TallySliceMergeTestHarness(PyAPITestHarness): def __init__(self, *args, **kwargs): - super(TallySliceMergeTestHarness, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Define nuclides and scores to add to both tallies self.nuclides = ['U235', 'U238'] @@ -85,8 +79,7 @@ class TallySliceMergeTestHarness(PyAPITestHarness): """Digest info in the statepoint and return as a string.""" # Read the statepoint file. - statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] - sp = openmc.StatePoint(statepoint) + sp = openmc.StatePoint(self._sp_name) # Extract the cell tally tallies = [sp.get_tally(name='cell tally')] @@ -171,6 +164,6 @@ class TallySliceMergeTestHarness(PyAPITestHarness): return outstr -if __name__ == '__main__': +def test_tally_slice_merge(): harness = TallySliceMergeTestHarness('statepoint.10.h5') harness.main() diff --git a/tests/regression_tests/trace/__init__.py b/tests/regression_tests/trace/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_trace/geometry.xml b/tests/regression_tests/trace/geometry.xml similarity index 100% rename from tests/test_trace/geometry.xml rename to tests/regression_tests/trace/geometry.xml diff --git a/tests/test_trace/materials.xml b/tests/regression_tests/trace/materials.xml similarity index 100% rename from tests/test_trace/materials.xml rename to tests/regression_tests/trace/materials.xml diff --git a/tests/test_trace/results_true.dat b/tests/regression_tests/trace/results_true.dat similarity index 100% rename from tests/test_trace/results_true.dat rename to tests/regression_tests/trace/results_true.dat diff --git a/tests/test_trace/settings.xml b/tests/regression_tests/trace/settings.xml similarity index 100% rename from tests/test_trace/settings.xml rename to tests/regression_tests/trace/settings.xml diff --git a/tests/regression_tests/trace/test.py b/tests/regression_tests/trace/test.py new file mode 100644 index 0000000000..79dcaa1060 --- /dev/null +++ b/tests/regression_tests/trace/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_trace(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/track_output/__init__.py b/tests/regression_tests/track_output/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_track_output/geometry.xml b/tests/regression_tests/track_output/geometry.xml similarity index 100% rename from tests/test_track_output/geometry.xml rename to tests/regression_tests/track_output/geometry.xml diff --git a/tests/test_track_output/materials.xml b/tests/regression_tests/track_output/materials.xml similarity index 100% rename from tests/test_track_output/materials.xml rename to tests/regression_tests/track_output/materials.xml diff --git a/tests/test_track_output/results_true.dat b/tests/regression_tests/track_output/results_true.dat similarity index 100% rename from tests/test_track_output/results_true.dat rename to tests/regression_tests/track_output/results_true.dat diff --git a/tests/test_track_output/settings.xml b/tests/regression_tests/track_output/settings.xml similarity index 100% rename from tests/test_track_output/settings.xml rename to tests/regression_tests/track_output/settings.xml diff --git a/tests/test_track_output/test_track_output.py b/tests/regression_tests/track_output/test.py similarity index 79% rename from tests/test_track_output/test_track_output.py rename to tests/regression_tests/track_output/test.py index 0357aae19e..22eac03bfc 100644 --- a/tests/test_track_output/test_track_output.py +++ b/tests/regression_tests/track_output/test.py @@ -1,12 +1,11 @@ -#!/usr/bin/env python - import glob import os from subprocess import call import shutil -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness + +import pytest + +from tests.testing_harness import TestHarness class TrackTestHarness(TestHarness): @@ -39,14 +38,9 @@ class TrackTestHarness(TestHarness): os.remove(f) -if __name__ == '__main__': +def test_track_output(): # If vtk python module is not available, we can't run track.py so skip this # test. - try: - import vtk - except ImportError: - print('----------------Skipping test-------------') - shutil.copy('results_true.dat', 'results_test.dat') - exit() + vtk = pytest.importorskip('vtk') harness = TrackTestHarness('statepoint.2.h5') harness.main() diff --git a/tests/regression_tests/translation/__init__.py b/tests/regression_tests/translation/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_translation/geometry.xml b/tests/regression_tests/translation/geometry.xml similarity index 100% rename from tests/test_translation/geometry.xml rename to tests/regression_tests/translation/geometry.xml diff --git a/tests/test_translation/materials.xml b/tests/regression_tests/translation/materials.xml similarity index 100% rename from tests/test_translation/materials.xml rename to tests/regression_tests/translation/materials.xml diff --git a/tests/test_translation/results_true.dat b/tests/regression_tests/translation/results_true.dat similarity index 100% rename from tests/test_translation/results_true.dat rename to tests/regression_tests/translation/results_true.dat diff --git a/tests/test_translation/settings.xml b/tests/regression_tests/translation/settings.xml similarity index 100% rename from tests/test_translation/settings.xml rename to tests/regression_tests/translation/settings.xml diff --git a/tests/regression_tests/translation/test.py b/tests/regression_tests/translation/test.py new file mode 100644 index 0000000000..876db736b9 --- /dev/null +++ b/tests/regression_tests/translation/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_translation(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/trigger_batch_interval/__init__.py b/tests/regression_tests/trigger_batch_interval/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_trigger_batch_interval/geometry.xml b/tests/regression_tests/trigger_batch_interval/geometry.xml similarity index 100% rename from tests/test_trigger_batch_interval/geometry.xml rename to tests/regression_tests/trigger_batch_interval/geometry.xml diff --git a/tests/test_trigger_batch_interval/materials.xml b/tests/regression_tests/trigger_batch_interval/materials.xml similarity index 100% rename from tests/test_trigger_batch_interval/materials.xml rename to tests/regression_tests/trigger_batch_interval/materials.xml diff --git a/tests/test_trigger_batch_interval/results_true.dat b/tests/regression_tests/trigger_batch_interval/results_true.dat similarity index 100% rename from tests/test_trigger_batch_interval/results_true.dat rename to tests/regression_tests/trigger_batch_interval/results_true.dat diff --git a/tests/test_trigger_batch_interval/settings.xml b/tests/regression_tests/trigger_batch_interval/settings.xml similarity index 100% rename from tests/test_trigger_batch_interval/settings.xml rename to tests/regression_tests/trigger_batch_interval/settings.xml diff --git a/tests/test_trigger_batch_interval/tallies.xml b/tests/regression_tests/trigger_batch_interval/tallies.xml similarity index 100% rename from tests/test_trigger_batch_interval/tallies.xml rename to tests/regression_tests/trigger_batch_interval/tallies.xml diff --git a/tests/regression_tests/trigger_batch_interval/test.py b/tests/regression_tests/trigger_batch_interval/test.py new file mode 100644 index 0000000000..e161745022 --- /dev/null +++ b/tests/regression_tests/trigger_batch_interval/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_trigger_batch_interval(): + harness = TestHarness('statepoint.15.h5') + harness.main() diff --git a/tests/regression_tests/trigger_no_batch_interval/__init__.py b/tests/regression_tests/trigger_no_batch_interval/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_trigger_no_batch_interval/geometry.xml b/tests/regression_tests/trigger_no_batch_interval/geometry.xml similarity index 100% rename from tests/test_trigger_no_batch_interval/geometry.xml rename to tests/regression_tests/trigger_no_batch_interval/geometry.xml diff --git a/tests/test_trigger_no_batch_interval/materials.xml b/tests/regression_tests/trigger_no_batch_interval/materials.xml similarity index 100% rename from tests/test_trigger_no_batch_interval/materials.xml rename to tests/regression_tests/trigger_no_batch_interval/materials.xml diff --git a/tests/test_trigger_no_batch_interval/results_true.dat b/tests/regression_tests/trigger_no_batch_interval/results_true.dat similarity index 100% rename from tests/test_trigger_no_batch_interval/results_true.dat rename to tests/regression_tests/trigger_no_batch_interval/results_true.dat diff --git a/tests/test_trigger_no_batch_interval/settings.xml b/tests/regression_tests/trigger_no_batch_interval/settings.xml similarity index 100% rename from tests/test_trigger_no_batch_interval/settings.xml rename to tests/regression_tests/trigger_no_batch_interval/settings.xml diff --git a/tests/test_trigger_no_batch_interval/tallies.xml b/tests/regression_tests/trigger_no_batch_interval/tallies.xml similarity index 100% rename from tests/test_trigger_no_batch_interval/tallies.xml rename to tests/regression_tests/trigger_no_batch_interval/tallies.xml diff --git a/tests/regression_tests/trigger_no_batch_interval/test.py b/tests/regression_tests/trigger_no_batch_interval/test.py new file mode 100644 index 0000000000..4a40def5aa --- /dev/null +++ b/tests/regression_tests/trigger_no_batch_interval/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_trigger_no_batch_interval(): + harness = TestHarness('statepoint.15.h5') + harness.main() diff --git a/tests/regression_tests/trigger_no_status/__init__.py b/tests/regression_tests/trigger_no_status/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_trigger_no_status/geometry.xml b/tests/regression_tests/trigger_no_status/geometry.xml similarity index 100% rename from tests/test_trigger_no_status/geometry.xml rename to tests/regression_tests/trigger_no_status/geometry.xml diff --git a/tests/test_trigger_no_status/materials.xml b/tests/regression_tests/trigger_no_status/materials.xml similarity index 100% rename from tests/test_trigger_no_status/materials.xml rename to tests/regression_tests/trigger_no_status/materials.xml diff --git a/tests/test_trigger_no_status/results_true.dat b/tests/regression_tests/trigger_no_status/results_true.dat similarity index 100% rename from tests/test_trigger_no_status/results_true.dat rename to tests/regression_tests/trigger_no_status/results_true.dat diff --git a/tests/test_trigger_no_status/settings.xml b/tests/regression_tests/trigger_no_status/settings.xml similarity index 100% rename from tests/test_trigger_no_status/settings.xml rename to tests/regression_tests/trigger_no_status/settings.xml diff --git a/tests/test_trigger_no_status/tallies.xml b/tests/regression_tests/trigger_no_status/tallies.xml similarity index 100% rename from tests/test_trigger_no_status/tallies.xml rename to tests/regression_tests/trigger_no_status/tallies.xml diff --git a/tests/regression_tests/trigger_no_status/test.py b/tests/regression_tests/trigger_no_status/test.py new file mode 100644 index 0000000000..75ae9a8cc9 --- /dev/null +++ b/tests/regression_tests/trigger_no_status/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_trigger_no_status(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/trigger_tallies/__init__.py b/tests/regression_tests/trigger_tallies/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_trigger_tallies/geometry.xml b/tests/regression_tests/trigger_tallies/geometry.xml similarity index 100% rename from tests/test_trigger_tallies/geometry.xml rename to tests/regression_tests/trigger_tallies/geometry.xml diff --git a/tests/test_trigger_tallies/materials.xml b/tests/regression_tests/trigger_tallies/materials.xml similarity index 100% rename from tests/test_trigger_tallies/materials.xml rename to tests/regression_tests/trigger_tallies/materials.xml diff --git a/tests/test_trigger_tallies/results_true.dat b/tests/regression_tests/trigger_tallies/results_true.dat similarity index 100% rename from tests/test_trigger_tallies/results_true.dat rename to tests/regression_tests/trigger_tallies/results_true.dat diff --git a/tests/test_trigger_tallies/settings.xml b/tests/regression_tests/trigger_tallies/settings.xml similarity index 100% rename from tests/test_trigger_tallies/settings.xml rename to tests/regression_tests/trigger_tallies/settings.xml diff --git a/tests/test_trigger_tallies/tallies.xml b/tests/regression_tests/trigger_tallies/tallies.xml similarity index 100% rename from tests/test_trigger_tallies/tallies.xml rename to tests/regression_tests/trigger_tallies/tallies.xml diff --git a/tests/regression_tests/trigger_tallies/test.py b/tests/regression_tests/trigger_tallies/test.py new file mode 100644 index 0000000000..f8493c0c68 --- /dev/null +++ b/tests/regression_tests/trigger_tallies/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_trigger_tallies(): + harness = TestHarness('statepoint.15.h5') + harness.main() diff --git a/tests/regression_tests/triso/__init__.py b/tests/regression_tests/triso/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_triso/inputs_true.dat b/tests/regression_tests/triso/inputs_true.dat similarity index 100% rename from tests/test_triso/inputs_true.dat rename to tests/regression_tests/triso/inputs_true.dat diff --git a/tests/test_triso/results_true.dat b/tests/regression_tests/triso/results_true.dat similarity index 100% rename from tests/test_triso/results_true.dat rename to tests/regression_tests/triso/results_true.dat diff --git a/tests/test_triso/test_triso.py b/tests/regression_tests/triso/test.py similarity index 95% rename from tests/test_triso/test_triso.py rename to tests/regression_tests/triso/test.py index e5c823c01f..174bba94fd 100644 --- a/tests/test_triso/test_triso.py +++ b/tests/regression_tests/triso/test.py @@ -1,18 +1,12 @@ -#!/usr/bin/env python - -import os -import sys -import glob import random from math import sqrt import numpy as np - -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness import openmc import openmc.model +from tests.testing_harness import PyAPITestHarness + class TRISOTestHarness(PyAPITestHarness): def _build_inputs(self): @@ -96,6 +90,6 @@ class TRISOTestHarness(PyAPITestHarness): mats.export_to_xml() -if __name__ == '__main__': +def test_triso(): harness = TRISOTestHarness('statepoint.5.h5') harness.main() diff --git a/tests/regression_tests/uniform_fs/__init__.py b/tests/regression_tests/uniform_fs/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_uniform_fs/geometry.xml b/tests/regression_tests/uniform_fs/geometry.xml similarity index 100% rename from tests/test_uniform_fs/geometry.xml rename to tests/regression_tests/uniform_fs/geometry.xml diff --git a/tests/test_uniform_fs/materials.xml b/tests/regression_tests/uniform_fs/materials.xml similarity index 100% rename from tests/test_uniform_fs/materials.xml rename to tests/regression_tests/uniform_fs/materials.xml diff --git a/tests/test_uniform_fs/results_true.dat b/tests/regression_tests/uniform_fs/results_true.dat similarity index 100% rename from tests/test_uniform_fs/results_true.dat rename to tests/regression_tests/uniform_fs/results_true.dat diff --git a/tests/test_uniform_fs/settings.xml b/tests/regression_tests/uniform_fs/settings.xml similarity index 100% rename from tests/test_uniform_fs/settings.xml rename to tests/regression_tests/uniform_fs/settings.xml diff --git a/tests/regression_tests/uniform_fs/test.py b/tests/regression_tests/uniform_fs/test.py new file mode 100644 index 0000000000..64d997909c --- /dev/null +++ b/tests/regression_tests/uniform_fs/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_uniform_fs(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/universe/__init__.py b/tests/regression_tests/universe/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_universe/geometry.xml b/tests/regression_tests/universe/geometry.xml similarity index 100% rename from tests/test_universe/geometry.xml rename to tests/regression_tests/universe/geometry.xml diff --git a/tests/test_universe/materials.xml b/tests/regression_tests/universe/materials.xml similarity index 100% rename from tests/test_universe/materials.xml rename to tests/regression_tests/universe/materials.xml diff --git a/tests/test_universe/results_true.dat b/tests/regression_tests/universe/results_true.dat similarity index 100% rename from tests/test_universe/results_true.dat rename to tests/regression_tests/universe/results_true.dat diff --git a/tests/test_universe/settings.xml b/tests/regression_tests/universe/settings.xml similarity index 100% rename from tests/test_universe/settings.xml rename to tests/regression_tests/universe/settings.xml diff --git a/tests/regression_tests/universe/test.py b/tests/regression_tests/universe/test.py new file mode 100644 index 0000000000..d5ed9645bd --- /dev/null +++ b/tests/regression_tests/universe/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_universe(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/void/__init__.py b/tests/regression_tests/void/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_void/geometry.xml b/tests/regression_tests/void/geometry.xml similarity index 100% rename from tests/test_void/geometry.xml rename to tests/regression_tests/void/geometry.xml diff --git a/tests/test_void/materials.xml b/tests/regression_tests/void/materials.xml similarity index 100% rename from tests/test_void/materials.xml rename to tests/regression_tests/void/materials.xml diff --git a/tests/test_void/results_true.dat b/tests/regression_tests/void/results_true.dat similarity index 100% rename from tests/test_void/results_true.dat rename to tests/regression_tests/void/results_true.dat diff --git a/tests/test_void/settings.xml b/tests/regression_tests/void/settings.xml similarity index 100% rename from tests/test_void/settings.xml rename to tests/regression_tests/void/settings.xml diff --git a/tests/regression_tests/void/test.py b/tests/regression_tests/void/test.py new file mode 100644 index 0000000000..af16f2ac80 --- /dev/null +++ b/tests/regression_tests/void/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_void(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/volume_calc/__init__.py b/tests/regression_tests/volume_calc/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_volume_calc/inputs_true.dat b/tests/regression_tests/volume_calc/inputs_true.dat similarity index 100% rename from tests/test_volume_calc/inputs_true.dat rename to tests/regression_tests/volume_calc/inputs_true.dat diff --git a/tests/test_volume_calc/results_true.dat b/tests/regression_tests/volume_calc/results_true.dat similarity index 100% rename from tests/test_volume_calc/results_true.dat rename to tests/regression_tests/volume_calc/results_true.dat diff --git a/tests/test_volume_calc/test_volume_calc.py b/tests/regression_tests/volume_calc/test.py similarity index 91% rename from tests/test_volume_calc/test_volume_calc.py rename to tests/regression_tests/volume_calc/test.py index 003528b0df..a6b62b9be9 100644 --- a/tests/test_volume_calc/test_volume_calc.py +++ b/tests/regression_tests/volume_calc/test.py @@ -1,12 +1,11 @@ -#!/usr/bin/env python - import os import glob import sys -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness + import openmc +from tests.testing_harness import PyAPITestHarness + class VolumeTest(PyAPITestHarness): def _build_inputs(self): @@ -57,8 +56,7 @@ class VolumeTest(PyAPITestHarness): def _get_results(self): outstr = '' - for i, filename in enumerate(sorted(glob.glob(os.path.join( - os.getcwd(), 'volume_*.h5')))): + for i, filename in enumerate(sorted(glob.glob('volume_*.h5'))): outstr += 'Volume calculation {}\n'.format(i) # Read volume calculation results @@ -75,6 +73,6 @@ class VolumeTest(PyAPITestHarness): def _test_output_created(self): pass -if __name__ == '__main__': +def test_volume_calc(): harness = VolumeTest('') harness.main() diff --git a/tests/run_tests.py b/tests/run_tests.py deleted file mode 100755 index 6e6b886a50..0000000000 --- a/tests/run_tests.py +++ /dev/null @@ -1,552 +0,0 @@ -#!/usr/bin/env python - -from __future__ import print_function - -import os -import sys -import shutil -import re -import glob -import socket -from subprocess import call, check_output -from collections import OrderedDict -from argparse import ArgumentParser - -# Command line parsing -parser = ArgumentParser() -parser.add_argument('-j', '--parallel', dest='n_procs', default='1', - help="Number of parallel jobs.") -parser.add_argument('-R', '--tests-regex', dest='regex_tests', - help="Run tests matching regular expression. \ - Test names are the directories present in tests folder.\ - This uses standard regex syntax to select tests.") -parser.add_argument('-C', '--build-config', dest='build_config', - help="Build configurations matching regular expression. \ - Specific build configurations can be printed out with \ - optional argument -p, --print. This uses standard \ - regex syntax to select build configurations.") -parser.add_argument('-l', '--list', action="store_true", - dest="list_build_configs", default=False, - help="List out build configurations.") -parser.add_argument("-p", "--project", dest="project", default="", - help="project name for build") -parser.add_argument("-D", "--dashboard", dest="dash", - help="Dash name -- Experimental, Nightly, Continuous") -parser.add_argument("-u", "--update", action="store_true", dest="update", - help="Allow CTest to update repo. (WARNING: may overwrite\ - changes that were not pushed.") -parser.add_argument("-s", "--script", action="store_true", dest="script", - help="Activate CTest scripting mode for coverage, valgrind\ - and dashboard capability.") -args = parser.parse_args() - -# Default compiler paths -FC = 'gfortran' -CC = 'gcc' -CXX = 'g++' -MPI_DIR = '/opt/mpich/3.2-gnu' -HDF5_DIR = '/opt/hdf5/1.8.16-gnu' -PHDF5_DIR = '/opt/phdf5/1.8.16-gnu' - -# Script mode for extra capability -script_mode = False - -# Override default compiler paths if environmental vars are found -if 'FC' in os.environ: - FC = os.environ['FC'] -if 'CC' in os.environ: - CC = os.environ['CC'] -if 'CXX' in os.environ: - CXX = os.environ['CXX'] -if 'MPI_DIR' in os.environ: - MPI_DIR = os.environ['MPI_DIR'] -if 'HDF5_DIR' in os.environ: - HDF5_DIR = os.environ['HDF5_DIR'] -if 'PHDF5_DIR' in os.environ: - PHDF5_DIR = os.environ['PHDF5_DIR'] - -# CTest script template -ctest_str = """set (CTEST_SOURCE_DIRECTORY "{source_dir}") -set (CTEST_BINARY_DIRECTORY "{build_dir}") - -set(CTEST_SITE "{host_name}") -set (CTEST_BUILD_NAME "{build_name}") -set (CTEST_CMAKE_GENERATOR "Unix Makefiles") -set (CTEST_BUILD_OPTIONS "{build_opts}") - -set(CTEST_UPDATE_COMMAND "git") - -set(CTEST_CONFIGURE_COMMAND "${{CMAKE_COMMAND}} -H${{CTEST_SOURCE_DIRECTORY}} -B${{CTEST_BINARY_DIRECTORY}} ${{CTEST_BUILD_OPTIONS}}") -set(CTEST_MEMORYCHECK_COMMAND "{valgrind_cmd}") -set(CTEST_MEMORYCHECK_COMMAND_OPTIONS "--tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes") -#set(CTEST_MEMORYCHECK_SUPPRESSIONS_FILE ${{CTEST_SOURCE_DIRECTORY}}/../tests/valgrind.supp) -set(MEM_CHECK {mem_check}) -if(MEM_CHECK) -set(ENV{{MEM_CHECK}} ${{MEM_CHECK}}) -endif() - -set(CTEST_COVERAGE_COMMAND "gcov") -set(COVERAGE {coverage}) -set(ENV{{COVERAGE}} ${{COVERAGE}}) - -{subproject} - -ctest_start("{dashboard}") -ctest_configure(RETURN_VALUE res) -{update} -ctest_build(RETURN_VALUE res) -if(NOT MEM_CHECK) -ctest_test({tests} PARALLEL_LEVEL {n_procs}, RETURN_VALUE res) -endif() -if(MEM_CHECK) -ctest_memcheck({tests} RETURN_VALUE res) -endif(MEM_CHECK) -if(COVERAGE) -ctest_coverage(RETURN_VALUE res) -endif(COVERAGE) -{submit} - -if (res EQUAL 0) -else() -message(FATAL_ERROR "") -endif() -""" - -# Define test data structure -tests = OrderedDict() - - -def cleanup(path): - """Remove generated output files.""" - for dirpath, dirnames, filenames in os.walk(path): - for fname in filenames: - for ext in ['.h5', '.ppm', '.voxel']: - if fname.endswith(ext) and fname != '1d_mgxs.h5': - os.remove(os.path.join(dirpath, fname)) - - -def which(program): - def is_exe(fpath): - return os.path.isfile(fpath) and os.access(fpath, os.X_OK) - - fpath, fname = os.path.split(program) - if fpath: - if is_exe(program): - return program - else: - for path in os.environ["PATH"].split(os.pathsep): - path = path.strip('"') - exe_file = os.path.join(path, program) - if is_exe(exe_file): - return exe_file - return None - - -class Test(object): - def __init__(self, name, debug=False, optimize=False, mpi=False, openmp=False, - phdf5=False, valgrind=False, coverage=False): - self.name = name - self.debug = debug - self.optimize = optimize - self.mpi = mpi - self.openmp = openmp - self.phdf5 = phdf5 - self.valgrind = valgrind - self.coverage = coverage - self.success = True - self.msg = None - self.skipped = False - self.cmake = ['cmake', '-H..', '-Bbuild', - '-DPYTHON_EXECUTABLE=' + sys.executable] - - # Check for MPI - if self.mpi: - if os.path.exists(os.path.join(MPI_DIR, 'bin', 'mpifort')): - self.fc = os.path.join(MPI_DIR, 'bin', 'mpifort') - else: - self.fc = os.path.join(MPI_DIR, 'bin', 'mpif90') - self.cc = os.path.join(MPI_DIR, 'bin', 'mpicc') - self.cxx = os.path.join(MPI_DIR, 'bin', 'mpicxx') - else: - self.fc = FC - self.cc = CC - self.cxx = CXX - - # Sets the build name that will show up on the CDash - def get_build_name(self): - self.build_name = args.project + '_' + self.name - return self.build_name - - # Sets up build options for various tests. It is used both - # in script and non-script modes - def get_build_opts(self): - build_str = "" - if self.debug: - build_str += "-Ddebug=ON " - if self.optimize: - build_str += "-Doptimize=ON " - if not self.openmp: - build_str += "-Dopenmp=OFF " - if self.coverage: - build_str += "-Dcoverage=ON " - if self.phdf5: - build_str += "-DHDF5_PREFER_PARALLEL=ON " - else: - build_str += "-DHDF5_PREFER_PARALLEL=OFF " - self.build_opts = build_str - return self.build_opts - - # Write out the ctest script to tests directory - def create_ctest_script(self, ctest_vars): - with open('ctestscript.run', 'w') as fh: - fh.write(ctest_str.format(**ctest_vars)) - - # Runs the ctest script which performs all the cmake/ctest/cdash - def run_ctest_script(self): - os.environ['FC'] = self.fc - os.environ['CC'] = self.cc - os.environ['CXX'] = self.cxx - if self.mpi: - os.environ['MPI_DIR'] = MPI_DIR - if self.phdf5: - os.environ['HDF5_ROOT'] = PHDF5_DIR - else: - os.environ['HDF5_ROOT'] = HDF5_DIR - rc = call(['ctest', '-S', 'ctestscript.run', '-V']) - if rc != 0: - self.success = False - self.msg = 'Failed on ctest script.' - - # Runs cmake when in non-script mode - def run_cmake(self): - build_opts = self.build_opts.split() - self.cmake += build_opts - - os.environ['FC'] = self.fc - os.environ['CC'] = self.cc - os.environ['CXX'] = self.cxx - if self.mpi: - os.environ['MPI_DIR'] = MPI_DIR - if self.phdf5: - os.environ['HDF5_ROOT'] = PHDF5_DIR - self.cmake.append('-DHDF5_PREFER_PARALLEL=ON') - else: - os.environ['HDF5_ROOT'] = HDF5_DIR - self.cmake.append('-DHDF5_PREFER_PARALLEL=OFF') - rc = call(self.cmake) - if rc != 0: - self.success = False - self.msg = 'Failed on cmake.' - - # Runs make when in non-script mode - def run_make(self): - if not self.success: - return - - # Default make string - make_list = ['make', '-s'] - - # Check for parallel - if args.n_procs is not None: - make_list.append('-j') - make_list.append(args.n_procs) - - # Run make - rc = call(make_list) - if rc != 0: - self.success = False - self.msg = 'Failed on make.' - - # Runs ctest when in non-script mode - def run_ctests(self): - if not self.success: - return - - # Default ctest string - ctest_list = ['ctest'] - - # Check for parallel - if args.n_procs is not None: - ctest_list.append('-j') - ctest_list.append(args.n_procs) - - # Check for subset of tests - if args.regex_tests is not None: - ctest_list.append('-R') - ctest_list.append(args.regex_tests) - - # Run ctests - rc = call(ctest_list) - if rc != 0: - self.success = False - self.msg = 'Failed on testing.' - - -# Simple function to add a test to the global tests dictionary -def add_test(name, debug=False, optimize=False, mpi=False, openmp=False, - phdf5=False, valgrind=False, coverage=False): - tests.update({name: Test(name, debug, optimize, mpi, openmp, phdf5, - valgrind, coverage)}) - -# List of all tests that may be run. User can add -C to command line to specify -# a subset of these configurations -add_test('hdf5-normal') -add_test('hdf5-debug', debug=True) -add_test('hdf5-optimize', optimize=True) -add_test('omp-hdf5-normal', openmp=True) -add_test('omp-hdf5-debug', openmp=True, debug=True) -add_test('omp-hdf5-optimize', openmp=True, optimize=True) -add_test('mpi-hdf5-normal', mpi=True) -add_test('mpi-hdf5-debug', mpi=True, debug=True) -add_test('mpi-hdf5-optimize', mpi=True, optimize=True) -add_test('phdf5-normal', mpi=True, phdf5=True) -add_test('phdf5-debug', mpi=True, phdf5=True, debug=True) -add_test('phdf5-optimize', mpi=True, phdf5=True, optimize=True) -add_test('phdf5-omp-normal', mpi=True, phdf5=True, openmp=True) -add_test('phdf5-omp-debug', mpi=True, phdf5=True, openmp=True, debug=True) -add_test('phdf5-omp-optimize', mpi=True, phdf5=True, openmp=True, optimize=True) -add_test('hdf5-debug_valgrind', debug=True, valgrind=True) -add_test('hdf5-debug_coverage', debug=True, coverage=True) - -# Check to see if we should just print build configuration information to user -if args.list_build_configs: - for key in tests: - print('Configuration Name: {0}'.format(key)) - print(' Debug Flags:..........{0}'.format(tests[key].debug)) - print(' Optimization Flags:...{0}'.format(tests[key].optimize)) - print(' MPI Active:...........{0}'.format(tests[key].mpi)) - print(' OpenMP Active:........{0}'.format(tests[key].openmp)) - print(' Valgrind Test:........{0}'.format(tests[key].valgrind)) - print(' Coverage Test:........{0}\n'.format(tests[key].coverage)) - exit() - -# Delete items of dictionary that don't match regular expression -if args.build_config is not None: - to_delete = [] - for key in tests: - if not re.search(args.build_config, key): - to_delete.append(key) - for key in to_delete: - del tests[key] - -# Check for dashboard and determine whether to push results to server -# Note that there are only 3 basic dashboards: -# Experimental, Nightly, Continuous. On the CDash end, these can be -# reorganized into groups when a hostname, dashboard and build name -# are matched. -if args.dash is None: - dash = 'Experimental' - submit = '' -else: - dash = args.dash - submit = 'ctest_submit()' - -# Check for update command, which will run git fetch/merge and will delete -# any changes to repo that were not pushed to remote origin -update = 'ctest_update()' if args.update else '' - -# Check for CTest scipts mode -# Sets up whether we should use just the basic ctest command or use -# CTest scripting to perform tests. -script_mode = (args.dash is not None or args.script) - -# Setup CTest script vars. Not used in non-script mode -pwd = os.getcwd() -ctest_vars = { - 'source_dir': os.path.join(pwd, os.pardir), - 'build_dir': os.path.join(pwd, 'build'), - 'host_name': socket.gethostname(), - 'dashboard': dash, - 'submit': submit, - 'update': update, - 'n_procs': args.n_procs -} - -# Check project name -subprop = "set_property(GLOBAL PROPERTY SubProject {0})" -if args.project == "": - ctest_vars.update({'subproject': ''}) -elif args.project == 'develop': - ctest_vars.update({'subproject': ''}) -else: - ctest_vars.update({'subproject': subprop.format(args.project)}) - -# Set up default valgrind tests (subset of all tests) -# Currently takes too long to run all the tests with valgrind -# Only used in script mode -valgrind_default_tests = "cmfd_feed|confidence_intervals|\ -density|eigenvalue_genperbatch|energy_grid|entropy|\ -lattice_multiple|output|plotreflective_plane|\ -rotation|salphabetascore_absorption|seed|source_energy_mono|\ -sourcepoint_batch|statepoint_interval|survival_biasing|\ -tally_assumesep|translation|uniform_fs|universe|void" - -# Delete items of dictionary if valgrind or coverage and not in script mode -to_delete = [] -if not script_mode: - for key in tests: - if re.search('valgrind|coverage', key): - to_delete.append(key) - -for key in to_delete: - del tests[key] - -# Check if tests is empty -if not tests: - print('No tests to run.') - exit() - -# Begin testing -shutil.rmtree('build', ignore_errors=True) -cleanup('.') -for key in iter(tests): - test = tests[key] - - # Extra display if not in script mode - if not script_mode: - print('-'*(len(key) + 6)) - print(key + ' tests') - print('-'*(len(key) + 6)) - sys.stdout.flush() - - # Verify fortran compiler exists - if which(test.fc) is None: - test.msg = 'Compiler not found: {0}'.format(test.fc) - test.success = False - continue - - # Verify valgrind command exists - if test.valgrind: - valgrind_cmd = which('valgrind') - if valgrind_cmd is None: - test.msg = 'No valgrind executable found.' - test.success = False - continue - else: - valgrind_cmd = '' - - # Verify gcov/lcov exist - if test.coverage: - if which('gcov') is None: - test.msg = 'No {} executable found.'.format(exe) - test.success = False - continue - - # Set test specific CTest script vars. Not used in non-script mode - ctest_vars.update({'build_name': test.get_build_name()}) - ctest_vars.update({'build_opts': test.get_build_opts()}) - ctest_vars.update({'mem_check': test.valgrind}) - ctest_vars.update({'coverage': test.coverage}) - ctest_vars.update({'valgrind_cmd': valgrind_cmd}) - - # Check for user custom tests - # INCLUDE is a CTest command that allows for a subset - # of tests to be executed. Only used in script mode. - if args.regex_tests is None: - ctest_vars.update({'tests': ''}) - - # No user tests, use default valgrind tests - if test.valgrind: - ctest_vars.update({'tests': 'INCLUDE {0}'. - format(valgrind_default_tests)}) - else: - ctest_vars.update({'tests': 'INCLUDE {0}'. - format(args.regex_tests)}) - - # Main part of code that does the ctest execution. - # It is broken up by two modes, script and non-script - if script_mode: - - # Create ctest script - test.create_ctest_script(ctest_vars) - - # Run test - test.run_ctest_script() - - else: - - # Run CMAKE to configure build - test.run_cmake() - - # Go into build directory - os.chdir('build') - - # Build OpenMC - test.run_make() - - # Run tests - test.run_ctests() - - # Leave build directory - os.chdir(os.pardir) - - # Copy over log file - if script_mode: - logfile = glob.glob('build/Testing/Temporary/LastTest_*.log') - else: - logfile = glob.glob('build/Testing/Temporary/LastTest.log') - if len(logfile) > 0: - logfilename = os.path.split(logfile[0])[1] - logfilename = os.path.splitext(logfilename)[0] - logfilename = logfilename + '_{0}.log'.format(test.name) - shutil.copy(logfile[0], logfilename) - - # For coverage builds, use lcov to generate HTML output - if test.coverage: - if which('lcov') is None or which('genhtml') is None: - print('No lcov/genhtml command found. ' - 'Could not generate coverage report.') - else: - shutil.rmtree('coverage', ignore_errors=True) - call(['lcov', '--directory', '.', '--capture', - '--output-file', 'coverage.info']) - call(['genhtml', '--output-directory', 'coverage', 'coverage.info']) - os.remove('coverage.info') - - if test.valgrind: - # Copy memcheck output to memcheck directory - shutil.rmtree('memcheck', ignore_errors=True) - os.mkdir('memcheck') - memcheck_out = glob.glob('build/Testing/Temporary/MemoryChecker.*.log') - for fname in memcheck_out: - shutil.copy(fname, 'memcheck/') - - # Remove generated XML files - xml_files = check_output(['git', 'ls-files', '.', '--exclude-standard', - '--others']).split() - for f in xml_files: - os.remove(f) - - # Clear build directory and remove binary and hdf5 files - shutil.rmtree('build', ignore_errors=True) - if script_mode: - os.remove('ctestscript.run') - cleanup('.') - -# Print out summary of results -print('\n' + '='*54) -print('Summary of Compilation Option Testing:\n') - -if sys.stdout.isatty(): - OK = '\033[92m' - FAIL = '\033[91m' - ENDC = '\033[0m' - BOLD = '\033[1m' -else: - OK = '' - FAIL = '' - ENDC = '' - BOLD = '' - -return_code = 0 - -for test in tests: - print(test + '.'*(50 - len(test)), end='') - if tests[test].success: - print(BOLD + OK + '[OK]' + ENDC) - else: - print(BOLD + FAIL + '[FAILED]' + ENDC) - print(' '*len(test)+tests[test].msg) - return_code = 1 - -sys.exit(return_code) diff --git a/tests/test_cmfd_feed/test_cmfd_feed.py b/tests/test_cmfd_feed/test_cmfd_feed.py deleted file mode 100644 index 17bebf68c0..0000000000 --- a/tests/test_cmfd_feed/test_cmfd_feed.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import CMFDTestHarness - - -if __name__ == '__main__': - harness = CMFDTestHarness('statepoint.20.h5') - harness.main() diff --git a/tests/test_cmfd_nofeed/test_cmfd_nofeed.py b/tests/test_cmfd_nofeed/test_cmfd_nofeed.py deleted file mode 100644 index 17bebf68c0..0000000000 --- a/tests/test_cmfd_nofeed/test_cmfd_nofeed.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import CMFDTestHarness - - -if __name__ == '__main__': - harness = CMFDTestHarness('statepoint.20.h5') - harness.main() diff --git a/tests/test_complex_cell/test_complex_cell.py b/tests/test_complex_cell/test_complex_cell.py deleted file mode 100755 index 0669165e25..0000000000 --- a/tests/test_complex_cell/test_complex_cell.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python - -import sys -sys.path.insert(0, '..') -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_confidence_intervals/test_confidence_intervals.py b/tests/test_confidence_intervals/test_confidence_intervals.py deleted file mode 100755 index b04fcc6eba..0000000000 --- a/tests/test_confidence_intervals/test_confidence_intervals.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_density/test_density.py b/tests/test_density/test_density.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_density/test_density.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_eigenvalue_genperbatch/test_eigenvalue_genperbatch.py b/tests/test_eigenvalue_genperbatch/test_eigenvalue_genperbatch.py deleted file mode 100644 index 59a60b63a4..0000000000 --- a/tests/test_eigenvalue_genperbatch/test_eigenvalue_genperbatch.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.7.h5') - harness.main() diff --git a/tests/test_eigenvalue_no_inactive/test_eigenvalue_no_inactive.py b/tests/test_eigenvalue_no_inactive/test_eigenvalue_no_inactive.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_eigenvalue_no_inactive/test_eigenvalue_no_inactive.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_energy_grid/test_energy_grid.py b/tests/test_energy_grid/test_energy_grid.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_energy_grid/test_energy_grid.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_infinite_cell/test_infinite_cell.py b/tests/test_infinite_cell/test_infinite_cell.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_infinite_cell/test_infinite_cell.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_lattice/test_lattice.py b/tests/test_lattice/test_lattice.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_lattice/test_lattice.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_lattice_hex/test_lattice_hex.py b/tests/test_lattice_hex/test_lattice_hex.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_lattice_hex/test_lattice_hex.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_lattice_mixed/test_lattice_mixed.py b/tests/test_lattice_mixed/test_lattice_mixed.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_lattice_mixed/test_lattice_mixed.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_lattice_multiple/test_lattice_multiple.py b/tests/test_lattice_multiple/test_lattice_multiple.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_lattice_multiple/test_lattice_multiple.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_mg_basic/test_mg_basic.py b/tests/test_mg_basic/test_mg_basic.py deleted file mode 100644 index 21871efd72..0000000000 --- a/tests/test_mg_basic/test_mg_basic.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import PyAPITestHarness -from openmc.examples import slab_mg - - -if __name__ == '__main__': - model = slab_mg() - harness = PyAPITestHarness('statepoint.10.h5', model) - harness.main() diff --git a/tests/test_particle_restart_eigval/test_particle_restart_eigval.py b/tests/test_particle_restart_eigval/test_particle_restart_eigval.py deleted file mode 100644 index 1ebbd3ea53..0000000000 --- a/tests/test_particle_restart_eigval/test_particle_restart_eigval.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import ParticleRestartTestHarness - - -if __name__ == '__main__': - harness = ParticleRestartTestHarness('particle_10_1030.h5') - harness.main() diff --git a/tests/test_particle_restart_fixed/test_particle_restart_fixed.py b/tests/test_particle_restart_fixed/test_particle_restart_fixed.py deleted file mode 100644 index df0398c6eb..0000000000 --- a/tests/test_particle_restart_fixed/test_particle_restart_fixed.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import ParticleRestartTestHarness - - -if __name__ == '__main__': - harness = ParticleRestartTestHarness('particle_7_144.h5') - harness.main() diff --git a/tests/test_ptables_off/test_ptables_off.py b/tests/test_ptables_off/test_ptables_off.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_ptables_off/test_ptables_off.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_quadric_surfaces/test_quadric_surfaces.py b/tests/test_quadric_surfaces/test_quadric_surfaces.py deleted file mode 100755 index b04fcc6eba..0000000000 --- a/tests/test_quadric_surfaces/test_quadric_surfaces.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_reflective_plane/test_reflective_plane.py b/tests/test_reflective_plane/test_reflective_plane.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_reflective_plane/test_reflective_plane.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_rotation/test_rotation.py b/tests/test_rotation/test_rotation.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_rotation/test_rotation.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_score_current/test_score_current.py b/tests/test_score_current/test_score_current.py deleted file mode 100644 index ea5886a639..0000000000 --- a/tests/test_score_current/test_score_current.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import HashedTestHarness - - -if __name__ == '__main__': - harness = HashedTestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_seed/test_seed.py b/tests/test_seed/test_seed.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_seed/test_seed.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_sourcepoint_restart/test_sourcepoint_restart.py b/tests/test_sourcepoint_restart/test_sourcepoint_restart.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_sourcepoint_restart/test_sourcepoint_restart.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_survival_biasing/test_survival_biasing.py b/tests/test_survival_biasing/test_survival_biasing.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_survival_biasing/test_survival_biasing.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_tally_assumesep/test_tally_assumesep.py b/tests/test_tally_assumesep/test_tally_assumesep.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_tally_assumesep/test_tally_assumesep.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_tally_nuclides/test_tally_nuclides.py b/tests/test_tally_nuclides/test_tally_nuclides.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_tally_nuclides/test_tally_nuclides.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_trace/test_trace.py b/tests/test_trace/test_trace.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_trace/test_trace.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_translation/test_translation.py b/tests/test_translation/test_translation.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_translation/test_translation.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_trigger_batch_interval/test_trigger_batch_interval.py b/tests/test_trigger_batch_interval/test_trigger_batch_interval.py deleted file mode 100644 index fb88ada001..0000000000 --- a/tests/test_trigger_batch_interval/test_trigger_batch_interval.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.15.h5') - harness.main() diff --git a/tests/test_trigger_no_batch_interval/test_trigger_no_batch_interval.py b/tests/test_trigger_no_batch_interval/test_trigger_no_batch_interval.py deleted file mode 100644 index fb88ada001..0000000000 --- a/tests/test_trigger_no_batch_interval/test_trigger_no_batch_interval.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.15.h5') - harness.main() diff --git a/tests/test_trigger_no_status/test_trigger_no_status.py b/tests/test_trigger_no_status/test_trigger_no_status.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_trigger_no_status/test_trigger_no_status.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_trigger_tallies/test_trigger_tallies.py b/tests/test_trigger_tallies/test_trigger_tallies.py deleted file mode 100644 index fb88ada001..0000000000 --- a/tests/test_trigger_tallies/test_trigger_tallies.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.15.h5') - harness.main() diff --git a/tests/test_uniform_fs/test_uniform_fs.py b/tests/test_uniform_fs/test_uniform_fs.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_uniform_fs/test_uniform_fs.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_universe/test_universe.py b/tests/test_universe/test_universe.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_universe/test_universe.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/test_void/test_void.py b/tests/test_void/test_void.py deleted file mode 100644 index b04fcc6eba..0000000000 --- a/tests/test_void/test_void.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -sys.path.insert(0, os.pardir) -from testing_harness import TestHarness - - -if __name__ == '__main__': - harness = TestHarness('statepoint.10.h5') - harness.main() diff --git a/tests/testing_harness.py b/tests/testing_harness.py index ad3b8ac1ec..92d477e238 100644 --- a/tests/testing_harness.py +++ b/tests/testing_harness.py @@ -1,5 +1,3 @@ -from __future__ import print_function - from difflib import unified_diff import filecmp import glob @@ -10,30 +8,21 @@ import shutil import sys import numpy as np - -sys.path.insert(0, os.path.join(os.pardir, os.pardir)) import openmc from openmc.examples import pwr_core +from tests.regression_tests import config + class TestHarness(object): """General class for running OpenMC regression tests.""" def __init__(self, statepoint_name): self._sp_name = statepoint_name - self.parser = OptionParser() - self.parser.add_option('--exe', dest='exe', default='openmc') - self.parser.add_option('--mpi_exec', dest='mpi_exec', default=None) - self.parser.add_option('--mpi_np', dest='mpi_np', default='2') - self.parser.add_option('--update', dest='update', action='store_true', - default=False) - self._opts = None - self._args = None def main(self): """Accept commandline arguments and either run or update tests.""" - (self._opts, self._args) = self.parser.parse_args() - if self._opts.update: + if config['update']: self.update_results() else: self.execute_test() @@ -61,11 +50,11 @@ class TestHarness(object): self._cleanup() def _run_openmc(self): - if self._opts.mpi_exec is not None: - openmc.run(openmc_exec=self._opts.exe, - mpi_args=[self._opts.mpi_exec, '-n', self._opts.mpi_np]) + if config['mpi']: + mpi_args = [config['mpiexec'], '-n', config['mpi_np']] + openmc.run(openmc_exec=config['exe'], mpi_args=mpi_args) else: - openmc.run(openmc_exec=self._opts.exe) + openmc.run(openmc_exec=config['exe']) def _test_output_created(self): """Make sure statepoint.* and tallies.out have been created.""" @@ -138,7 +127,7 @@ class HashedTestHarness(TestHarness): def _get_results(self): """Digest info in the statepoint and return as a string.""" - return super(HashedTestHarness, self)._get_results(True) + return super()._get_results(True) class CMFDTestHarness(TestHarness): @@ -148,7 +137,7 @@ class CMFDTestHarness(TestHarness): """Digest info in the statepoint and return as a string.""" # Write out the eigenvalue and tallies. - outstr = super(CMFDTestHarness, self)._get_results() + outstr = super()._get_results() # Read the statepoint file. statepoint = glob.glob(self._sp_name)[0] @@ -180,9 +169,9 @@ class ParticleRestartTestHarness(TestHarness): def _run_openmc(self): # Set arguments - args = {'openmc_exec': self._opts.exe} - if self._opts.mpi_exec is not None: - args['mpi_args'] = [self._opts.mpi_exec, '-n', self._opts.mpi_np] + args = {'openmc_exec': config['exe']} + if config['mpi']: + args['mpi_args'] = [config['mpiexec'], '-n', config['mpi_np']] # Initial run openmc.run(**args) @@ -231,9 +220,7 @@ class ParticleRestartTestHarness(TestHarness): class PyAPITestHarness(TestHarness): def __init__(self, statepoint_name, model=None): - super(PyAPITestHarness, self).__init__(statepoint_name) - self.parser.add_option('-b', '--build-inputs', dest='build_only', - action='store_true', default=False) + super().__init__(statepoint_name) if model is None: self._model = pwr_core() else: @@ -243,10 +230,9 @@ class PyAPITestHarness(TestHarness): def main(self): """Accept commandline arguments and either run or update tests.""" - (self._opts, self._args) = self.parser.parse_args() - if self._opts.build_only: + if config['build_inputs']: self._build_inputs() - elif self._opts.update: + elif config['update']: self.update_results() else: self.execute_test() @@ -314,7 +300,7 @@ class PyAPITestHarness(TestHarness): def _cleanup(self): """Delete XMLs, statepoints, tally, and test files.""" - super(PyAPITestHarness, self)._cleanup() + super()._cleanup() output = ['materials.xml', 'geometry.xml', 'settings.xml', 'tallies.xml', 'plots.xml', 'inputs_test.dat'] for f in output: @@ -325,4 +311,4 @@ class PyAPITestHarness(TestHarness): class HashedPyAPITestHarness(PyAPITestHarness): def _get_results(self): """Digest info in the statepoint and return as a string.""" - return super(HashedPyAPITestHarness, self)._get_results(True) + return super()._get_results(True) diff --git a/tests/unit_tests/__init__.py b/tests/unit_tests/__init__.py new file mode 100644 index 0000000000..59a520c12a --- /dev/null +++ b/tests/unit_tests/__init__.py @@ -0,0 +1,9 @@ +import numpy as np +import pytest + + +def assert_unbounded(obj): + """Assert that a region/cell is unbounded.""" + ll, ur = obj.bounding_box + assert ll == pytest.approx((-np.inf, -np.inf, -np.inf)) + assert ur == pytest.approx((np.inf, np.inf, np.inf)) diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py new file mode 100644 index 0000000000..d618b85def --- /dev/null +++ b/tests/unit_tests/conftest.py @@ -0,0 +1,71 @@ +import openmc +import pytest + + +@pytest.fixture +def run_in_tmpdir(tmpdir): + orig = tmpdir.chdir() + try: + yield + finally: + orig.chdir() + + +@pytest.fixture(scope='module') +def uo2(): + m = openmc.Material(material_id=100, name='UO2') + m.add_nuclide('U235', 1.0) + m.add_nuclide('O16', 2.0) + m.set_density('g/cm3', 10.0) + m.depletable = True + return m + + +@pytest.fixture(scope='module') +def water(): + m = openmc.Material(name='light water') + m.add_nuclide('H1', 2.0) + m.add_nuclide('O16', 1.0) + m.set_density('g/cm3', 1.0) + m.add_s_alpha_beta('c_H_in_H2O') + return m + + +@pytest.fixture(scope='module') +def sphere_model(): + model = openmc.model.Model() + m = openmc.Material() + m.add_nuclide('U235', 1.0) + m.set_density('g/cm3', 1.0) + model.materials.append(m) + + sph = openmc.Sphere(boundary_type='vacuum') + c = openmc.Cell(fill=m, region=-sph) + model.geometry.root_universe = openmc.Universe(cells=[c]) + + model.settings.particles = 100 + model.settings.batches = 10 + model.settings.run_mode = 'fixed source' + model.settings.source = openmc.Source(space=openmc.stats.Point()) + return model + + +@pytest.fixture +def cell_with_lattice(): + m_inside = [openmc.Material(), openmc.Material(), None, openmc.Material()] + m_outside = openmc.Material() + + cyl = openmc.ZCylinder(R=1.0) + inside_cyl = openmc.Cell(fill=m_inside, region=-cyl) + outside_cyl = openmc.Cell(fill=m_outside, region=+cyl) + univ = openmc.Universe(cells=[inside_cyl, outside_cyl]) + + lattice = openmc.RectLattice(name='My Lattice') + lattice.lower_left = (-4.0, -4.0) + lattice.pitch = (4.0, 4.0) + lattice.universes = [[univ, univ], [univ, univ]] + main_cell = openmc.Cell(fill=lattice) + + return ([inside_cyl, outside_cyl, main_cell], + [m_inside[0], m_inside[1], m_inside[3], m_outside], + univ, lattice) diff --git a/tests/unit_tests/test_capi.py b/tests/unit_tests/test_capi.py index ba9ac5c9e0..e1e87e27b3 100644 --- a/tests/unit_tests/test_capi.py +++ b/tests/unit_tests/test_capi.py @@ -12,6 +12,7 @@ import openmc.capi @pytest.fixture(scope='module') def pincell_model(): """Set up a model to test with and delete files when done""" + openmc.reset_auto_ids() pincell = openmc.examples.pwr_pin_cell() # Add a tally diff --git a/tests/unit_tests/test_cell.py b/tests/unit_tests/test_cell.py new file mode 100644 index 0000000000..d01d94a324 --- /dev/null +++ b/tests/unit_tests/test_cell.py @@ -0,0 +1,168 @@ +import xml.etree. ElementTree as ET + +import numpy as np +import openmc +import pytest + +from tests.unit_tests import assert_unbounded + + +def test_contains(): + # Cell with specified region + s = openmc.XPlane() + c = openmc.Cell(region=+s) + assert (1.0, 0.0, 0.0) in c + assert (-1.0, 0.0, 0.0) not in c + + # Cell with no region + c = openmc.Cell() + assert (10.0, -4., 2.0) in c + + +def test_repr(cell_with_lattice): + cells, mats, univ, lattice = cell_with_lattice + repr(cells[0]) # cell with distributed materials + repr(cells[1]) # cell with material + repr(cells[2]) # cell with lattice + + # Empty cell + c = openmc.Cell() + repr(c) + + +def test_bounding_box(): + zcyl = openmc.ZCylinder() + c = openmc.Cell(region=-zcyl) + ll, ur = c.bounding_box + assert ll == pytest.approx((-1., -1., -np.inf)) + assert ur == pytest.approx((1., 1., np.inf)) + + # Cell with no region specified + c = openmc.Cell() + assert_unbounded(c) + + +def test_clone(): + m = openmc.Material() + cyl = openmc.ZCylinder() + c = openmc.Cell(fill=m, region=-cyl) + c.temperature = 650. + + c2 = c.clone() + assert c2.id != c.id + assert c2.fill != c.fill + assert c2.region != c.region + assert c2.temperature == c.temperature + + +def test_temperature(cell_with_lattice): + # Make sure temperature propagates through universes + m = openmc.Material() + s = openmc.XPlane() + c1 = openmc.Cell(fill=m, region=+s) + c2 = openmc.Cell(fill=m, region=-s) + u1 = openmc.Universe(cells=[c1, c2]) + c = openmc.Cell(fill=u1) + + c.temperature = 400.0 + assert c1.temperature == 400.0 + assert c2.temperature == 400.0 + with pytest.raises(ValueError): + c.temperature = -100. + + # distributed temperature + cells, _, _, _ = cell_with_lattice + c = cells[0] + c.temperature = (300., 600., 900.) + + +def test_rotation(): + u = openmc.Universe() + c = openmc.Cell(fill=u) + c.rotation = (180.0, 0.0, 0.0) + assert np.allclose(c.rotation_matrix, [ + [1., 0., 0.], + [0., -1., 0.], + [0., 0., -1.] + ]) + + c.rotation = (0.0, 90.0, 0.0) + assert np.allclose(c.rotation_matrix, [ + [0., 0., -1.], + [0., 1., 0.], + [1., 0., 0.] + ]) + + +def test_get_nuclides(uo2): + c = openmc.Cell(fill=uo2) + nucs = c.get_nuclides() + assert nucs == ['U235', 'O16'] + + +def test_nuclide_densities(uo2): + c = openmc.Cell(fill=uo2) + expected_nucs = ['U235', 'O16'] + expected_density = [1.0, 2.0] + tuples = list(c.get_nuclide_densities().values()) + for nuc, density, t in zip(expected_nucs, expected_density, tuples): + assert nuc == t[0] + assert density == t[1] + + # Empty cell + c = openmc.Cell() + assert not c.get_nuclide_densities() + + +def test_get_all_universes(cell_with_lattice): + # Cell with nested universes + c1 = openmc.Cell() + u1 = openmc.Universe(cells=[c1]) + c2 = openmc.Cell(fill=u1) + u2 = openmc.Universe(cells=[c2]) + c3 = openmc.Cell(fill=u2) + univs = set(c3.get_all_universes().values()) + assert not (univs ^ {u1, u2}) + + # Cell with lattice + cells, mats, univ, lattice = cell_with_lattice + univs = set(cells[-1].get_all_universes().values()) + assert not (univs ^ {univ}) + + +def test_get_all_materials(cell_with_lattice): + # Normal cell + m = openmc.Material() + c = openmc.Cell(fill=m) + test_mats = set(c.get_all_materials().values()) + assert not(test_mats ^ {m}) + + # Cell filled with distributed materials + cells, mats, univ, lattice = cell_with_lattice + c = cells[0] + test_mats = set(c.get_all_materials().values()) + assert not (test_mats ^ set(m for m in c.fill if m is not None)) + + # Cell filled with universe + c = cells[-1] + test_mats = set(c.get_all_materials().values()) + assert not (test_mats ^ set(mats)) + + +def test_to_xml_element(cell_with_lattice): + cells, mats, univ, lattice = cell_with_lattice + + c = cells[-1] + root = ET.Element('geometry') + elem = c.create_xml_subelement(root) + assert elem.tag == 'cell' + assert elem.get('id') == str(c.id) + assert elem.get('region') is None + surf_elem = root.find('surface') + assert surf_elem.get('id') == str(cells[0].region.surface.id) + + c = cells[0] + c.temperature = 900.0 + elem = c.create_xml_subelement(root) + assert elem.get('region') == str(c.region) + assert elem.get('temperature') == str(c.temperature) diff --git a/tests/unit_tests/test_data_multipole.py b/tests/unit_tests/test_data_multipole.py index dc30677e80..4a4a9f0d21 100644 --- a/tests/unit_tests/test_data_multipole.py +++ b/tests/unit_tests/test_data_multipole.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - import os import numpy as np @@ -7,6 +5,11 @@ import pytest import openmc.data +pytestmark = pytest.mark.skipif( + 'OPENMC_MULTIPOLE_LIBRARY' not in os.environ, + reason='OPENMC_MULTIPOLE_LIBRARY environment variable must be set') + + @pytest.fixture(scope='module') def u235(): directory = os.environ['OPENMC_MULTIPOLE_LIBRARY'] diff --git a/tests/unit_tests/test_data_neutron.py b/tests/unit_tests/test_data_neutron.py index e314d32a42..5713bfbc57 100644 --- a/tests/unit_tests/test_data_neutron.py +++ b/tests/unit_tests/test_data_neutron.py @@ -213,6 +213,7 @@ def test_export_to_hdf5(tmpdir, pu239, gd154): with pytest.raises(NotImplementedError): gd154.export_to_hdf5('gd154.h5') + def test_slbw(xe135): res = xe135.resonances assert isinstance(res, openmc.data.Resonances) diff --git a/tests/unit_tests/test_geometry.py b/tests/unit_tests/test_geometry.py new file mode 100644 index 0000000000..93d2fa6341 --- /dev/null +++ b/tests/unit_tests/test_geometry.py @@ -0,0 +1,246 @@ +import xml.etree.ElementTree as ET + +import numpy as np +import openmc +import pytest + + +def test_volume(run_in_tmpdir, uo2): + """Test adding volume information from a volume calculation.""" + # Create model with nested spheres + model = openmc.model.Model() + model.materials.append(uo2) + inner = openmc.Sphere(R=1.) + outer = openmc.Sphere(R=2., boundary_type='vacuum') + c1 = openmc.Cell(fill=uo2, region=-inner) + c2 = openmc.Cell(region=+inner & -outer) + u = openmc.Universe(cells=[c1, c2]) + model.geometry.root_universe = u + model.settings.particles = 100 + model.settings.batches = 10 + model.settings.run_mode = 'fixed source' + model.settings.source = openmc.Source(space=openmc.stats.Point()) + + ll, ur = model.geometry.bounding_box + assert ll == pytest.approx((-outer.r, -outer.r, -outer.r)) + assert ur == pytest.approx((outer.r, outer.r, outer.r)) + model.settings.volume_calculations + + for domain in (c1, uo2, u): + # Run stochastic volume calculation + volume_calc = openmc.VolumeCalculation( + domains=[domain], samples=1000, lower_left=ll, upper_right=ur) + model.settings.volume_calculations = [volume_calc] + model.export_to_xml() + openmc.calculate_volumes() + + # Load results and add volume information + volume_calc.load_results('volume_1.h5') + model.geometry.add_volume_information(volume_calc) + + # get_nuclide_densities relies on volume information + nucs = set(domain.get_nuclide_densities()) + assert not nucs ^ {'U235', 'O16'} + + +def test_export_xml(run_in_tmpdir, uo2): + s1 = openmc.Sphere(R=1.) + s2 = openmc.Sphere(R=2., boundary_type='reflective') + c1 = openmc.Cell(fill=uo2, region=-s1) + c2 = openmc.Cell(fill=uo2, region=+s1 & -s2) + geom = openmc.Geometry([c1, c2]) + geom.export_to_xml() + + doc = ET.parse('geometry.xml') + root = doc.getroot() + assert root.tag == 'geometry' + cells = root.findall('cell') + assert [int(c.get('id')) for c in cells] == [c1.id, c2.id] + surfs = root.findall('surface') + assert [int(s.get('id')) for s in surfs] == [s1.id, s2.id] + + +def test_find(uo2): + xp = openmc.XPlane() + c1 = openmc.Cell(fill=uo2, region=+xp) + c2 = openmc.Cell(region=-xp) + u1 = openmc.Universe(cells=(c1, c2)) + + cyl = openmc.ZCylinder() + c3 = openmc.Cell(fill=u1, region=-cyl) + c4 = openmc.Cell(region=+cyl) + geom = openmc.Geometry((c3, c4)) + + seq = geom.find((0.5, 0., 0.)) + assert seq[-1] == c1 + seq = geom.find((-0.5, 0., 0.)) + assert seq[-1] == c2 + seq = geom.find((-1.5, 0., 0.)) + assert seq[-1] == c4 + + +def test_get_all_cells(): + cells = [openmc.Cell() for i in range(5)] + cells2 = [openmc.Cell() for i in range(3)] + cells[0].fill = openmc.Universe(cells=cells2) + geom = openmc.Geometry(cells) + + all_cells = set(geom.get_all_cells().values()) + assert not all_cells ^ set(cells + cells2) + + +def test_get_all_materials(): + m1 = openmc.Material() + m2 = openmc.Material() + c1 = openmc.Cell(fill=m1) + u1 = openmc.Universe(cells=[c1]) + + s = openmc.Sphere() + c2 = openmc.Cell(fill=u1, region=-s) + c3 = openmc.Cell(fill=m2, region=+s) + geom = openmc.Geometry([c2, c3]) + + all_mats = set(geom.get_all_materials().values()) + assert not all_mats ^ {m1, m2} + + +def test_get_all_material_cells(): + m1 = openmc.Material() + m2 = openmc.Material() + c1 = openmc.Cell(fill=m1) + u1 = openmc.Universe(cells=[c1]) + + s = openmc.Sphere() + c2 = openmc.Cell(fill=u1, region=-s) + c3 = openmc.Cell(fill=m2, region=+s) + geom = openmc.Geometry([c2, c3]) + + all_cells = set(geom.get_all_material_cells().values()) + assert not all_cells ^ {c1, c3} + + +def test_get_all_material_universes(): + m1 = openmc.Material() + m2 = openmc.Material() + c1 = openmc.Cell(fill=m1) + u1 = openmc.Universe(cells=[c1]) + + s = openmc.Sphere() + c2 = openmc.Cell(fill=u1, region=-s) + c3 = openmc.Cell(fill=m2, region=+s) + geom = openmc.Geometry([c2, c3]) + + all_univs = set(geom.get_all_material_universes().values()) + assert not all_univs ^ {u1, geom.root_universe} + + +def test_get_all_lattices(cell_with_lattice): + cells, mats, univ, lattice = cell_with_lattice + geom = openmc.Geometry([cells[-1]]) + + lats = list(geom.get_all_lattices().values()) + assert lats == [lattice] + + +def test_get_all_surfaces(uo2): + planes = [openmc.ZPlane(z0=z) for z in np.linspace(-100., 100.)] + slabs = [] + for region in openmc.model.subdivide(planes): + slabs.append(openmc.Cell(fill=uo2, region=region)) + geom = openmc.Geometry(slabs) + + surfs = set(geom.get_all_surfaces().values()) + assert not surfs ^ set(planes) + + +def test_get_by_name(): + m1 = openmc.Material(name='zircaloy') + m1.add_element('Zr', 1.0) + m2 = openmc.Material(name='Zirconium') + m2.add_element('Zr', 1.0) + + c1 = openmc.Cell(fill=m1, name='cell1') + u1 = openmc.Universe(name='Zircaloy universe', cells=[c1]) + + cyl = openmc.ZCylinder() + c2 = openmc.Cell(fill=u1, region=-cyl, name='cell2') + c3 = openmc.Cell(fill=m2, region=+cyl, name='Cell3') + root = openmc.Universe(name='root Universe', cells=[c2, c3]) + geom = openmc.Geometry(root) + + mats = set(geom.get_materials_by_name('zirc')) + assert not mats ^ {m1, m2} + mats = set(geom.get_materials_by_name('zirc', True)) + assert not mats ^ {m1} + mats = set(geom.get_materials_by_name('zirconium', False, True)) + assert not mats ^ {m2} + mats = geom.get_materials_by_name('zirconium', True, True) + assert not mats + + cells = set(geom.get_cells_by_name('cell')) + assert not cells ^ {c1, c2, c3} + cells = set(geom.get_cells_by_name('cell', True)) + assert not cells ^ {c1, c2} + cells = set(geom.get_cells_by_name('cell3', False, True)) + assert not cells ^ {c3} + cells = geom.get_cells_by_name('cell3', True, True) + assert not cells + + cells = set(geom.get_cells_by_fill_name('Zircaloy')) + assert not cells ^ {c1, c2} + cells = set(geom.get_cells_by_fill_name('Zircaloy', True)) + assert not cells ^ {c2} + cells = set(geom.get_cells_by_fill_name('Zircaloy', False, True)) + assert not cells ^ {c1} + cells = geom.get_cells_by_fill_name('Zircaloy', True, True) + assert not cells + + univs = set(geom.get_universes_by_name('universe')) + assert not univs ^ {u1, root} + univs = set(geom.get_universes_by_name('universe', True)) + assert not univs ^ {u1} + univs = set(geom.get_universes_by_name('universe', True, True)) + assert not univs + + +def test_get_lattice_by_name(cell_with_lattice): + cells, _, _, lattice = cell_with_lattice + geom = openmc.Geometry([cells[-1]]) + + f = geom.get_lattices_by_name + assert f('lattice') == [lattice] + assert f('lattice', True) == [] + assert f('Lattice', True) == [lattice] + assert f('my lattice', False, True) == [lattice] + assert f('my lattice', True, True) == [] + + +def test_clone(): + c1 = openmc.Cell() + c2 = openmc.Cell() + root = openmc.Universe(cells=[c1, c2]) + geom = openmc.Geometry(root) + + clone = geom.clone() + root_clone = clone.root_universe + + assert root.id != root_clone.id + assert not (set(root.cells) & set(root_clone.cells)) + + +def test_determine_paths(cell_with_lattice): + cells, mats, univ, lattice = cell_with_lattice + u = openmc.Universe(cells=[cells[-1]]) + geom = openmc.Geometry(u) + + geom.determine_paths() + assert len(cells[0].paths) == 4 + assert len(cells[1].paths) == 4 + assert len(cells[2].paths) == 1 + assert len(mats[0].paths) == 1 + assert len(mats[-1].paths) == 4 + + # Test get_instances + for i in range(4): + assert geom.get_instances(cells[0].paths[i]) == i + assert geom.get_instances(mats[-1].paths[i]) == i diff --git a/tests/unit_tests/test_lattice.py b/tests/unit_tests/test_lattice.py new file mode 100644 index 0000000000..47dc1c029e --- /dev/null +++ b/tests/unit_tests/test_lattice.py @@ -0,0 +1,344 @@ +from math import sqrt +import xml.etree.ElementTree as ET + +import openmc +import pytest + + +@pytest.fixture(scope='module') +def pincell1(uo2, water): + cyl = openmc.ZCylinder(R=0.35) + fuel = openmc.Cell(fill=uo2, region=-cyl) + moderator = openmc.Cell(fill=water, region=+cyl) + + univ = openmc.Universe(cells=[fuel, moderator]) + univ.fuel = fuel + univ.moderator = moderator + return univ + + +@pytest.fixture(scope='module') +def pincell2(uo2, water): + cyl = openmc.ZCylinder(R=0.4) + fuel = openmc.Cell(fill=uo2, region=-cyl) + moderator = openmc.Cell(fill=water, region=+cyl) + + univ = openmc.Universe(cells=[fuel, moderator]) + univ.fuel = fuel + univ.moderator = moderator + return univ + + +@pytest.fixture(scope='module') +def zr(): + zr = openmc.Material() + zr.add_element('Zr', 1.0) + zr.set_density('g/cm3', 1.0) + return zr + + +@pytest.fixture(scope='module') +def rlat2(pincell1, pincell2, uo2, water, zr): + """2D Rectangular lattice for testing.""" + all_zr = openmc.Cell(fill=zr) + pitch = 1.2 + n = 3 + u1, u2 = pincell1, pincell2 + lattice = openmc.RectLattice() + lattice.lower_left = (-pitch*n/2, -pitch*n/2) + lattice.pitch = (pitch, pitch) + lattice.outer = openmc.Universe(cells=[all_zr]) + lattice.universes = [ + [u1, u2, u1], + [u2, u1, u2], + [u2, u1, u1] + ] + + # Add extra attributes for comparison purpose + lattice.cells = [u1.fuel, u1.moderator, u2.fuel, u2.moderator, all_zr] + lattice.mats = [uo2, water, zr] + lattice.univs = [u1, u2, lattice.outer] + return lattice + + +@pytest.fixture(scope='module') +def rlat3(pincell1, pincell2, uo2, water, zr): + """3D Rectangular lattice for testing.""" + + # Create another universe for top layer + hydrogen = openmc.Material() + hydrogen.add_element('H', 1.0) + hydrogen.set_density('g/cm3', 0.09) + h_cell = openmc.Cell(fill=hydrogen) + u3 = openmc.Universe(cells=[h_cell]) + + all_zr = openmc.Cell(fill=zr) + pitch = 1.2 + n = 3 + u1, u2 = pincell1, pincell2 + lattice = openmc.RectLattice() + lattice.lower_left = (-pitch*n/2, -pitch*n/2, -10.0) + lattice.pitch = (pitch, pitch, 10.0) + lattice.outer = openmc.Universe(cells=[all_zr]) + lattice.universes = [ + [[u1, u2, u1], + [u2, u1, u2], + [u2, u1, u1]], + [[u3, u1, u2], + [u1, u3, u2], + [u2, u1, u1]] + ] + + # Add extra attributes for comparison purpose + lattice.cells = [u1.fuel, u1.moderator, u2.fuel, u2.moderator, + h_cell, all_zr] + lattice.mats = [uo2, water, zr, hydrogen] + lattice.univs = [u1, u2, u3, lattice.outer] + return lattice + + +@pytest.fixture(scope='module') +def hlat2(pincell1, pincell2, uo2, water, zr): + """2D Hexagonal lattice for testing.""" + all_zr = openmc.Cell(fill=zr) + pitch = 1.2 + u1, u2 = pincell1, pincell2 + lattice = openmc.HexLattice() + lattice.center = (0., 0.) + lattice.pitch = (pitch,) + lattice.outer = openmc.Universe(cells=[all_zr]) + lattice.universes = [ + [u2, u1, u1, u1, u1, u1, u1, u1, u1, u1, u1, u1], + [u2, u1, u1, u1, u1, u1], + [u2] + ] + + # Add extra attributes for comparison purpose + lattice.cells = [u1.fuel, u1.moderator, u2.fuel, u2.moderator, all_zr] + lattice.mats = [uo2, water, zr] + lattice.univs = [u1, u2, lattice.outer] + return lattice + + +@pytest.fixture(scope='module') +def hlat3(pincell1, pincell2, uo2, water, zr): + """3D Hexagonal lattice for testing.""" + + # Create another universe for top layer + hydrogen = openmc.Material() + hydrogen.add_element('H', 1.0) + hydrogen.set_density('g/cm3', 0.09) + h_cell = openmc.Cell(fill=hydrogen) + u3 = openmc.Universe(cells=[h_cell]) + + all_zr = openmc.Cell(fill=zr) + pitch = 1.2 + u1, u2 = pincell1, pincell2 + lattice = openmc.HexLattice() + lattice.center = (0., 0., 0.) + lattice.pitch = (pitch, 10.0) + lattice.outer = openmc.Universe(cells=[all_zr]) + lattice.universes = [ + [[u2, u1, u1, u1, u1, u1, u1, u1, u1, u1, u1, u1], + [u2, u1, u1, u1, u1, u1], + [u2]], + [[u1, u1, u1, u1, u1, u1, u3, u1, u1, u1, u1, u1], + [u1, u1, u1, u3, u1, u1], + [u3]] + ] + + # Add extra attributes for comparison purpose + lattice.cells = [u1.fuel, u1.moderator, u2.fuel, u2.moderator, + h_cell, all_zr] + lattice.mats = [uo2, water, zr, hydrogen] + lattice.univs = [u1, u2, u3, lattice.outer] + return lattice + + +def test_get_nuclides(rlat2, rlat3, hlat2, hlat3): + for lat in (rlat2, hlat2): + nucs = rlat2.get_nuclides() + assert sorted(nucs) == ['H1', 'O16', 'U235', + 'Zr90', 'Zr91', 'Zr92', 'Zr94', 'Zr96'] + for lat in (rlat3, hlat3): + nucs = rlat3.get_nuclides() + assert sorted(nucs) == ['H1', 'H2', 'O16', 'U235', + 'Zr90', 'Zr91', 'Zr92', 'Zr94', 'Zr96'] + + +def test_get_all_cells(rlat2, rlat3, hlat2, hlat3): + for lat in (rlat2, rlat3, hlat2, hlat3): + cells = set(lat.get_all_cells().values()) + assert not cells ^ set(lat.cells) + + +def test_get_all_materials(rlat2, rlat3, hlat2, hlat3): + for lat in (rlat2, rlat3, hlat2, hlat3): + mats = set(lat.get_all_materials().values()) + assert not mats ^ set(lat.mats) + + +def test_get_all_universes(rlat2, rlat3, hlat2, hlat3): + for lat in (rlat2, rlat3, hlat2, hlat3): + univs = set(lat.get_all_universes().values()) + assert not univs ^ set(lat.univs) + + +def test_get_universe(rlat2, rlat3, hlat2, hlat3): + u1, u2, outer = rlat2.univs + assert rlat2.get_universe((0, 0)) == u2 + assert rlat2.get_universe((1, 0)) == u1 + assert rlat2.get_universe((0, 1)) == u2 + + u1, u2, u3, outer = rlat3.univs + assert rlat3.get_universe((0, 0, 0)) == u2 + assert rlat3.get_universe((2, 2, 0)) == u1 + assert rlat3.get_universe((0, 2, 1)) == u3 + assert rlat3.get_universe((2, 1, 1)) == u2 + + u1, u2, outer = hlat2.univs + assert hlat2.get_universe((0, 0)) == u2 + assert hlat2.get_universe((0, 2)) == u2 + assert hlat2.get_universe((1, 0)) == u1 + assert hlat2.get_universe((-2, 2)) == u1 + + u1, u2, u3, outer = hlat3.univs + assert hlat3.get_universe((0, 0, 0)) == u2 + assert hlat3.get_universe((0, 0, 1)) == u3 + assert hlat3.get_universe((0, 2, 0)) == u2 + assert hlat3.get_universe((0, 2, 1)) == u1 + assert hlat3.get_universe((0, -2, 0)) == u1 + assert hlat3.get_universe((0, -2, 1)) == u3 + + +def test_find(rlat2, rlat3, hlat2, hlat3): + pitch = rlat2.pitch[0] + seq = rlat2.find((0., 0., 0.)) + assert seq[-1] == rlat2.cells[0] + seq = rlat2.find((pitch, 0., 0.)) + assert seq[-1] == rlat2.cells[2] + seq = rlat2.find((0., -pitch, 0.)) + assert seq[-1] == rlat2.cells[0] + seq = rlat2.find((pitch*100, 0., 0.)) + assert seq[-1] == rlat2.cells[-1] + seq = rlat3.find((-pitch, pitch, 5.0)) + assert seq[-1] == rlat3.cells[-2] + + pitch = hlat2.pitch[0] + seq = hlat2.find((0., 0., 0.)) + assert seq[-1] == hlat2.cells[2] + seq = hlat2.find((0.5, 0., 0.)) + assert seq[-1] == hlat2.cells[3] + seq = hlat2.find((sqrt(3)*pitch, 0., 0.)) + assert seq[-1] == hlat2.cells[0] + seq = hlat2.find((0., pitch, 0.)) + assert seq[-1] == hlat2.cells[2] + + # bottom of 3D lattice + seq = hlat3.find((0., 0., -5.)) + assert seq[-1] == hlat3.cells[2] + seq = hlat3.find((0., pitch, -5.)) + assert seq[-1] == hlat3.cells[2] + seq = hlat3.find((0., -pitch, -5.)) + assert seq[-1] == hlat3.cells[0] + seq = hlat3.find((sqrt(3)*pitch, 0., -5.)) + assert seq[-1] == hlat3.cells[0] + + # top of 3D lattice + seq = hlat3.find((0., 0., 5.)) + assert seq[-1] == hlat3.cells[-2] + seq = hlat3.find((0., pitch, 5.)) + assert seq[-1] == hlat3.cells[0] + seq = hlat3.find((0., -pitch, 5.)) + assert seq[-1] == hlat3.cells[-2] + seq = hlat3.find((sqrt(3)*pitch, 0., 5.)) + assert seq[-1] == hlat3.cells[0] + + +def test_clone(rlat2, hlat2, hlat3): + rlat_clone = rlat2.clone() + assert rlat_clone.id != rlat2.id + assert rlat_clone.lower_left == rlat2.lower_left + assert rlat_clone.pitch == rlat2.pitch + + hlat_clone = hlat2.clone() + assert hlat_clone.id != hlat2.id + assert hlat_clone.center == hlat2.center + assert hlat_clone.pitch == hlat2.pitch + + hlat_clone = hlat3.clone() + assert hlat_clone.id != hlat3.id + assert hlat_clone.center == hlat3.center + assert hlat_clone.pitch == hlat3.pitch + + +def test_repr(rlat2, rlat3, hlat2, hlat3): + repr(rlat2) + repr(rlat3) + repr(hlat2) + repr(hlat3) + + +def test_indices_rect(rlat2, rlat3): + # (y, x) indices + assert rlat2.indices == [(0, 0), (0, 1), (0, 2), + (1, 0), (1, 1), (1, 2), + (2, 0), (2, 1), (2, 2)] + # (z, y, x) indices + assert rlat3.indices == [ + (0, 0, 0), (0, 0, 1), (0, 0, 2), + (0, 1, 0), (0, 1, 1), (0, 1, 2), + (0, 2, 0), (0, 2, 1), (0, 2, 2), + (1, 0, 0), (1, 0, 1), (1, 0, 2), + (1, 1, 0), (1, 1, 1), (1, 1, 2), + (1, 2, 0), (1, 2, 1), (1, 2, 2) + ] + + +def test_indices_hex(hlat2, hlat3): + # (r, i) indices + assert hlat2.indices == ( + [(0, i) for i in range(12)] + + [(1, i) for i in range(6)] + + [(2, 0)] + ) + + # (z, r, i) indices + assert hlat3.indices == ( + [(0, 0, i) for i in range(12)] + + [(0, 1, i) for i in range(6)] + + [(0, 2, 0)] + + [(1, 0, i) for i in range(12)] + + [(1, 1, i) for i in range(6)] + + [(1, 2, 0)] + ) + + +def test_xml_rect(rlat2, rlat3): + for lat in (rlat2, rlat3): + geom = ET.Element('geometry') + lat.create_xml_subelement(geom) + elem = geom.find('lattice') + assert elem.tag == 'lattice' + assert elem.get('id') == str(lat.id) + assert len(elem.find('pitch').text.split()) == lat.ndim + assert len(elem.find('lower_left').text.split()) == lat.ndim + assert len(elem.find('universes').text.split()) == len(lat.indices) + + +def test_xml_hex(hlat2, hlat3): + for lat in (hlat2, hlat3): + geom = ET.Element('geometry') + lat.create_xml_subelement(geom) + elem = geom.find('hex_lattice') + assert elem.tag == 'hex_lattice' + assert elem.get('id') == str(lat.id) + assert len(elem.find('center').text.split()) == lat.ndim + assert len(elem.find('pitch').text.split()) == lat.ndim - 1 + assert len(elem.find('universes').text.split()) == len(lat.indices) + + +def test_show_indices(): + for i in range(1, 11): + lines = openmc.HexLattice.show_indices(i).split('\n') + assert len(lines) == 4*i - 3 diff --git a/tests/unit_tests/test_material.py b/tests/unit_tests/test_material.py new file mode 100644 index 0000000000..e92a2ac088 --- /dev/null +++ b/tests/unit_tests/test_material.py @@ -0,0 +1,141 @@ +import openmc +import openmc.model +import openmc.stats +import openmc.examples +import pytest + + +def test_attributes(uo2): + assert uo2.name == 'UO2' + assert uo2.id == 100 + assert uo2.depletable + + +def test_nuclides(uo2): + """Test adding/removing nuclides.""" + m = openmc.Material() + m.add_nuclide('U235', 1.0) + with pytest.raises(ValueError): + m.add_nuclide('H1', '1.0') + with pytest.raises(ValueError): + m.add_nuclide(1.0, 'H1') + with pytest.raises(ValueError): + m.add_nuclide('H1', 1.0, 'oa') + m.remove_nuclide('U235') + + +def test_elements(): + """Test adding elements.""" + m = openmc.Material() + m.add_element('Zr', 1.0) + m.add_element('U', 1.0, enrichment=4.5) + with pytest.raises(ValueError): + m.add_element('U', 1.0, enrichment=100.0) + with pytest.raises(ValueError): + m.add_element('Pu', 1.0, enrichment=3.0) + + +def test_density(): + m = openmc.Material() + for unit in ['g/cm3', 'g/cc', 'kg/m3', 'atom/b-cm', 'atom/cm3']: + m.set_density(unit, 1.0) + with pytest.raises(ValueError): + m.set_density('g/litre', 1.0) + + +def test_salphabeta(): + m = openmc.Material() + m.add_s_alpha_beta('c_H_in_H2O', 0.5) + + +def test_repr(): + m = openmc.Material() + m.add_nuclide('Zr90', 1.0) + m.add_nuclide('H2', 0.5) + m.add_s_alpha_beta('c_D_in_D2O') + m.set_density('sum') + m.temperature = 600.0 + repr(m) + + +def test_macroscopic(run_in_tmpdir): + m = openmc.Material(name='UO2') + m.add_macroscopic('UO2') + with pytest.raises(ValueError): + m.add_nuclide('H1', 1.0) + with pytest.raises(ValueError): + m.add_element('O', 1.0) + with pytest.raises(ValueError): + m.add_macroscopic('Other') + + m2 = openmc.Material() + m2.add_nuclide('He4', 1.0) + with pytest.raises(ValueError): + m2.add_macroscopic('UO2') + + # Make sure we can remove/add macroscopic + m.remove_macroscopic('UO2') + m.add_macroscopic('UO2') + repr(m) + + # Make sure we can export a material with macroscopic data + mats = openmc.Materials([m]) + mats.export_to_xml() + + +def test_paths(): + model = openmc.examples.pwr_assembly() + model.geometry.determine_paths() + fuel = model.materials[0] + assert fuel.num_instances == 264 + assert len(fuel.paths) == 264 + + +def test_isotropic(): + m1 = openmc.Material() + m1.add_nuclide('U235', 1.0) + m1.add_nuclide('O16', 2.0) + m1.isotropic = ['O16'] + assert m1.isotropic == ['O16'] + + m2 = openmc.Material() + m2.add_nuclide('H1', 1.0) + mats = openmc.Materials([m1, m2]) + mats.make_isotropic_in_lab() + assert m1.isotropic == ['U235', 'O16'] + assert m2.isotropic == ['H1'] + + +def test_get_nuclide_densities(uo2): + nucs = uo2.get_nuclide_densities() + for nuc, density, density_type in nucs.values(): + assert nuc in ('U235', 'O16') + assert density > 0 + assert density_type in ('ao', 'wo') + + +def test_get_nuclide_atom_densities(uo2): + nucs = uo2.get_nuclide_atom_densities() + for nuc, density in nucs.values(): + assert nuc in ('U235', 'O16') + assert density > 0 + + +def test_materials(run_in_tmpdir): + m1 = openmc.Material() + m1.add_nuclide('U235', 1.0, 'wo') + m1.add_nuclide('O16', 2.0, 'wo') + m1.set_density('g/cm3', 10.0) + m1.depletable = True + m1.temperature = 900.0 + + m2 = openmc.Material() + m2.add_nuclide('H1', 2.0) + m2.add_nuclide('O16', 1.0) + m2.add_s_alpha_beta('c_H_in_H2O') + m2.set_density('kg/m3', 1000.0) + + mats = openmc.Materials([m1, m2]) + mats.cross_sections = '/some/fake/cross_sections.xml' + mats.multipole_library = '/some/awesome/mp_lib/' + mats.export_to_xml() diff --git a/tests/unit_tests/test_plots.py b/tests/unit_tests/test_plots.py new file mode 100644 index 0000000000..d48b609c1e --- /dev/null +++ b/tests/unit_tests/test_plots.py @@ -0,0 +1,90 @@ +import openmc +import openmc.examples +import pytest + + +@pytest.fixture(scope='module') +def myplot(): + plot = openmc.Plot(name='myplot') + plot.width = (100., 100.) + plot.origin = (2., 3., -10.) + plot.pixels = (500, 500) + plot.filename = 'myplot' + plot.type = 'slice' + plot.basis = 'yz' + plot.background = (0, 0, 0) + plot.background = 'black' + + plot.color_by = 'material' + m1, m2 = openmc.Material(), openmc.Material() + plot.colors = {m1: (0, 255, 0), m2: (0, 0, 255)} + plot.colors = {m1: 'green', m2: 'blue'} + + plot.mask_components = [openmc.Material()] + plot.mask_background = (255, 255, 255) + plot.mask_background = 'white' + + plot.level = 1 + plot.meshlines = { + 'type': 'tally', + 'id': 1, + 'linewidth': 2, + 'color': (40, 30, 20) + } + return plot + + +def test_attributes(myplot): + assert myplot.name == 'myplot' + + +def test_repr(myplot): + r = repr(myplot) + assert isinstance(r, str) + + +def test_from_geometry(): + width = 25. + s = openmc.Sphere(R=width/2, boundary_type='vacuum') + c = openmc.Cell(region=-s) + univ = openmc.Universe(cells=[c]) + geom = openmc.Geometry(univ) + + for basis in ('xy', 'yz', 'xz'): + plot = openmc.Plot.from_geometry(geom, basis) + assert plot.origin == pytest.approx((0., 0., 0.)) + assert plot.width == pytest.approx((width, width)) + + +def test_highlight_domains(): + plot = openmc.Plot() + plot.color_by = 'material' + plots = openmc.Plots([plot]) + + model = openmc.examples.pwr_pin_cell() + mats = {m for m in model.materials if 'UO2' in m.name} + plots.highlight_domains(model.geometry, mats) + + +def test_to_xml_element(myplot): + elem = myplot.to_xml_element() + assert 'id' in elem.attrib + assert 'color_by' in elem.attrib + assert 'type' in elem.attrib + assert elem.find('origin') is not None + assert elem.find('width') is not None + assert elem.find('pixels') is not None + assert elem.find('background').text == '0 0 0' + + +def test_plots(run_in_tmpdir): + p1 = openmc.Plot(name='plot1') + p2 = openmc.Plot(name='plot2') + plots = openmc.Plots([p1, p2]) + assert len(plots) == 2 + + p3 = openmc.Plot(name='plot3') + plots.append(p3) + assert len(plots) == 3 + + plots.export_to_xml() diff --git a/tests/unit_tests/test_region.py b/tests/unit_tests/test_region.py new file mode 100644 index 0000000000..377c8fbd58 --- /dev/null +++ b/tests/unit_tests/test_region.py @@ -0,0 +1,162 @@ +import numpy as np +import pytest +import openmc + +from tests.unit_tests import assert_unbounded + + +@pytest.fixture +def reset(): + openmc.reset_auto_ids() + + +def test_union(reset): + s1 = openmc.XPlane(surface_id=1, x0=5) + s2 = openmc.XPlane(surface_id=2, x0=-5) + region = +s1 | -s2 + assert isinstance(region, openmc.Union) + + # Check bounding box + assert_unbounded(region) + + # __contains__ + assert (6, 0, 0) in region + assert (-6, 0, 0) in region + assert (0, 0, 0) not in region + + # string representation + assert str(region) == '(1 | -2)' + + # Combining region with intersection + s3 = openmc.YPlane(surface_id=3) + reg2 = region & +s3 + assert (6, 1, 0) in reg2 + assert (6, -1, 0) not in reg2 + assert str(reg2) == '((1 | -2) 3)' + + +def test_intersection(reset): + s1 = openmc.XPlane(surface_id=1, x0=5) + s2 = openmc.XPlane(surface_id=2, x0=-5) + region = -s1 & +s2 + assert isinstance(region, openmc.Intersection) + + # Check bounding box + ll, ur = region.bounding_box + assert ll == pytest.approx((-5, -np.inf, -np.inf)) + assert ur == pytest.approx((5, np.inf, np.inf)) + + # __contains__ + assert (6, 0, 0) not in region + assert (-6, 0, 0) not in region + assert (0, 0, 0) in region + + # string representation + assert str(region) == '(-1 2)' + + # Combining region with union + s3 = openmc.YPlane(surface_id=3) + reg2 = region | +s3 + assert (-6, 2, 0) in reg2 + assert (-6, -2, 0) not in reg2 + assert str(reg2) == '((-1 2) | 3)' + + +def test_complement(reset): + zcyl = openmc.ZCylinder(surface_id=1, R=1.) + z0 = openmc.ZPlane(surface_id=2, z0=-5.) + z1 = openmc.ZPlane(surface_id=3, z0=5.) + outside = +zcyl | -z0 | +z1 + inside = ~outside + outside_equiv = ~(-zcyl & +z0 & -z1) + inside_equiv = ~outside_equiv + + # Check bounding box + for region in (inside, inside_equiv): + ll, ur = region.bounding_box + assert ll == pytest.approx((-1., -1., -5.)) + assert ur == pytest.approx((1., 1., 5.)) + assert_unbounded(outside) + assert_unbounded(outside_equiv) + + # string represention + assert str(inside) == '~(1 | -2 | 3)' + + # evaluate method + assert (0, 0, 0) in inside + assert (0, 0, 0) not in outside + assert (0, 0, 6) not in inside + assert (0, 0, 6) in outside + + +def test_get_surfaces(): + s1 = openmc.XPlane() + s2 = openmc.YPlane() + s3 = openmc.ZPlane() + region = (+s1 & -s2) | +s3 + + # Make sure get_surfaces() returns all surfaces + surfs = set(region.get_surfaces().values()) + assert not (surfs ^ {s1, s2, s3}) + + inverse = ~region + surfs = set(inverse.get_surfaces().values()) + assert not (surfs ^ {s1, s2, s3}) + + +def test_extend_clone(): + s1 = openmc.XPlane() + s2 = openmc.YPlane() + s3 = openmc.ZPlane() + s4 = openmc.ZCylinder() + + # extend intersection + r1 = +s1 & -s2 + r1 &= +s3 & -s4 + assert r1[:] == [+s1, -s2, +s3, -s4] + + # extend union + r2 = +s1 | -s2 + r2 |= +s3 | -s4 + assert r2[:] == [+s1, -s2, +s3, -s4] + + # clone methods + r3 = r1.clone() + assert len(r3) == len(r1) + r4 = r2.clone() + assert len(r4) == len(r2) + + r5 = ~r1 + r6 = r5.clone() + + +def test_from_expression(reset): + # Create surface dictionary + s1 = openmc.ZCylinder(surface_id=1) + s2 = openmc.ZPlane(surface_id=2, z0=-10.) + s3 = openmc.ZPlane(surface_id=3, z0=10.) + surfs = {1: s1, 2: s2, 3: s3} + + r = openmc.Region.from_expression('-1 2 -3', surfs) + assert isinstance(r, openmc.Intersection) + assert r[:] == [-s1, +s2, -s3] + + r = openmc.Region.from_expression('+1 | -2 | +3', surfs) + assert isinstance(r, openmc.Union) + assert r[:] == [+s1, -s2, +s3] + + r = openmc.Region.from_expression('~(-1)', surfs) + assert r == +s1 + + # Since & has higher precendence than |, the resulting region should be an + # instance of Union + r = openmc.Region.from_expression('1 -2 | 3', surfs) + assert isinstance(r, openmc.Union) + assert isinstance(r[0], openmc.Intersection) + assert r[0][:] == [+s1, -s2] + + # ...but not if we use parentheses + r = openmc.Region.from_expression('1 (-2 | 3)', surfs) + assert isinstance(r, openmc.Intersection) + assert isinstance(r[1], openmc.Union) + assert r[1][:] == [-s2, +s3] diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py new file mode 100644 index 0000000000..e3bfc697eb --- /dev/null +++ b/tests/unit_tests/test_settings.py @@ -0,0 +1,55 @@ +import openmc +import openmc.stats + + +def test_export_to_xml(run_in_tmpdir): + s = openmc.Settings() + s.run_mode = 'fixed source' + s.batches = 1000 + s.generations_per_batch = 10 + s.inactive = 100 + s.particles = 1000000 + s.keff_trigger = {'type': 'std_dev', 'threshold': 0.001} + s.energy_mode = 'continuous-energy' + s.max_order = 5 + s.source = openmc.Source(space=openmc.stats.Point()) + s.output = {'summary': True, 'tallies': False, 'path': 'here'} + s.verbosity = 7 + s.sourcepoint = {'batches': [50, 150, 500, 1000], 'separate': True, + 'write': True, 'overwrite': True} + s.statepoint = {'batches': [50, 150, 500, 1000]} + s.confidence_intervals = True + s.cross_sections = '/path/to/cross_sections.xml' + s.multipole_library = '/path/to/wmp/' + s.ptables = True + s.run_cmfd = False + s.seed = 17 + s.survival_biasing = True + s.cutoff = {'weight': 0.25, 'weight_avg': 0.5, 'energy': 1.0e-5} + mesh = openmc.Mesh() + mesh.lower_left = (-10., -10., -10.) + mesh.upper_right = (10., 10., 10.) + mesh.dimension = (5, 5, 5) + s.entropy_mesh = mesh + s.trigger_active = True + s.trigger_max_batches = 10000 + s.trigger_batch_interval = 50 + s.no_reduce = False + s.tabular_legendre = {'enable': True, 'num_points': 50} + s.temperature = {'default': 293.6, 'method': 'interpolation', + 'multipole': True, 'range': (200., 1000.)} + s.threads = 8 + s.trace = (10, 1, 20) + s.track = [1, 1, 1, 2, 1, 1] + s.ufs_mesh = mesh + s.resonance_scattering = {'enable': True, 'method': 'ares', + 'energy_min': 1.0, 'energy_max': 1000.0, + 'nuclides': ['U235', 'U238', 'Pu239']} + s.volume_calculations = openmc.VolumeCalculation( + domains=[openmc.Cell()], samples=1000, lower_left=(-10., -10., -10.), + upper_right = (10., 10., 10.)) + s.create_fission_neutrons = True + s.log_grid_bins = 2000 + + # Make sure exporting XML works + s.export_to_xml() diff --git a/tests/unit_tests/test_source.py b/tests/unit_tests/test_source.py new file mode 100644 index 0000000000..3c963d0527 --- /dev/null +++ b/tests/unit_tests/test_source.py @@ -0,0 +1,30 @@ +import openmc +import openmc.stats + + +def test_source(): + space = openmc.stats.Point() + energy = openmc.stats.Discrete([1.0e6], [1.0]) + angle = openmc.stats.Isotropic() + + src = openmc.Source(space=space, angle=angle, energy=energy) + assert src.space == space + assert src.angle == angle + assert src.energy == energy + assert src.strength == 1.0 + + elem = src.to_xml_element() + assert 'strength' in elem.attrib + assert elem.find('space') is not None + assert elem.find('angle') is not None + assert elem.find('energy') is not None + + +def test_source_file(): + filename = 'source.h5' + src = openmc.Source(filename=filename) + assert src.file == filename + + elem = src.to_xml_element() + assert 'strength' in elem.attrib + assert 'file' in elem.attrib diff --git a/tests/unit_tests/test_stats.py b/tests/unit_tests/test_stats.py new file mode 100644 index 0000000000..388408bb22 --- /dev/null +++ b/tests/unit_tests/test_stats.py @@ -0,0 +1,179 @@ +from math import pi + +import numpy as np +import pytest +import openmc +import openmc.stats + + +def test_discrete(): + x = [0.0, 1.0, 10.0] + p = [0.3, 0.2, 0.5] + d = openmc.stats.Discrete(x, p) + assert d.x == x + assert d.p == p + assert len(d) == len(x) + d.to_xml_element('distribution') + + # Single point + d2 = openmc.stats.Discrete(1e6, 1.0) + assert d2.x == [1e6] + assert d2.p == [1.0] + assert len(d2) == 1 + + +def test_uniform(): + a, b = 10.0, 20.0 + d = openmc.stats.Uniform(a, b) + assert d.a == a + assert d.b == b + assert len(d) == 2 + + t = d.to_tabular() + assert t.x == [a, b] + assert t.p == [1/(b-a), 1/(b-a)] + assert t.interpolation == 'histogram' + + d.to_xml_element('distribution') + + +def test_maxwell(): + theta = 1.2895e6 + d = openmc.stats.Maxwell(theta) + assert d.theta == theta + assert len(d) == 1 + d.to_xml_element('distribution') + + +def test_watt(): + a, b = 0.965e6, 2.29e-6 + d = openmc.stats.Watt(a, b) + assert d.a == a + assert d.b == b + assert len(d) == 2 + d.to_xml_element('distribution') + + +def test_tabular(): + x = [0.0, 5.0, 7.0] + p = [0.1, 0.2, 0.05] + d = openmc.stats.Tabular(x, p, 'linear-linear') + assert d.x == x + assert d.p == p + assert d.interpolation == 'linear-linear' + assert len(d) == len(x) + d.to_xml_element('distribution') + + +def test_legendre(): + # Pu239 elastic scattering at 100 keV + coeffs = [1.000e+0, 1.536e-1, 1.772e-2, 5.945e-4, 3.497e-5, 1.881e-5] + d = openmc.stats.Legendre(coeffs) + assert d.coefficients == pytest.approx(coeffs) + assert len(d) == len(coeffs) + + # Integrating distribution should yield one + mu = np.linspace(-1., 1., 1000) + assert np.trapz(d(mu), mu) == pytest.approx(1.0, rel=1e-4) + + with pytest.raises(NotImplementedError): + d.to_xml_element('distribution') + + +def test_mixture(): + d1 = openmc.stats.Uniform(0, 5) + d2 = openmc.stats.Uniform(3, 7) + p = [0.5, 0.5] + mix = openmc.stats.Mixture(p, [d1, d2]) + assert mix.probability == p + assert mix.distribution == [d1, d2] + assert len(mix) == 4 + + with pytest.raises(NotImplementedError): + mix.to_xml_element('distribution') + + +def test_polar_azimuthal(): + # default polar-azimuthal should be uniform in mu and phi + d = openmc.stats.PolarAzimuthal() + assert isinstance(d.mu, openmc.stats.Uniform) + assert d.mu.a == -1. + assert d.mu.b == 1. + assert isinstance(d.phi, openmc.stats.Uniform) + assert d.phi.a == 0. + assert d.phi.b == 2*pi + + mu = openmc.stats.Discrete(1., 1.) + phi = openmc.stats.Discrete(0., 1.) + d = openmc.stats.PolarAzimuthal(mu, phi) + assert d.mu == mu + assert d.phi == phi + + elem = d.to_xml_element() + assert elem.tag == 'angle' + assert elem.attrib['type'] == 'mu-phi' + assert elem.find('mu') is not None + assert elem.find('phi') is not None + + +def test_isotropic(): + d = openmc.stats.Isotropic() + elem = d.to_xml_element() + assert elem.tag == 'angle' + assert elem.attrib['type'] == 'isotropic' + + +def test_monodirectional(): + d = openmc.stats.Monodirectional((1., 0., 0.)) + assert d.reference_uvw == pytest.approx((1., 0., 0.)) + + elem = d.to_xml_element() + assert elem.tag == 'angle' + assert elem.attrib['type'] == 'monodirectional' + + +def test_cartesian(): + x = openmc.stats.Uniform(-10., 10.) + y = openmc.stats.Uniform(-10., 10.) + z = openmc.stats.Uniform(0., 20.) + d = openmc.stats.CartesianIndependent(x, y, z) + assert d.x == x + assert d.y == y + assert d.z == z + + elem = d.to_xml_element() + assert elem.tag == 'space' + assert elem.attrib['type'] == 'cartesian' + assert elem.find('x') is not None + assert elem.find('y') is not None + + +def test_box(): + lower_left = (-10., -10., -10.) + upper_right = (10., 10., 10.) + d = openmc.stats.Box(lower_left, upper_right) + assert d.lower_left == pytest.approx(lower_left) + assert d.upper_right == pytest.approx(upper_right) + assert not d.only_fissionable + + elem = d.to_xml_element() + assert elem.tag == 'space' + assert elem.attrib['type'] == 'box' + assert elem.find('parameters') is not None + + # only fissionable parameter + d2 = openmc.stats.Box(lower_left, upper_right, True) + assert d2.only_fissionable + elem = d2.to_xml_element() + assert elem.attrib['type'] == 'fission' + + +def test_point(): + p = (-4., 2., 10.) + d = openmc.stats.Point(p) + assert d.xyz == pytest.approx(p) + + elem = d.to_xml_element() + assert elem.tag == 'space' + assert elem.attrib['type'] == 'point' + assert elem.find('parameters') is not None diff --git a/tests/unit_tests/test_surface.py b/tests/unit_tests/test_surface.py new file mode 100644 index 0000000000..3db84cc053 --- /dev/null +++ b/tests/unit_tests/test_surface.py @@ -0,0 +1,259 @@ +import numpy as np +import openmc +import pytest + + +def assert_infinite_bb(s): + ll, ur = (-s).bounding_box + assert np.all(np.isinf(ll)) + assert np.all(np.isinf(ur)) + ll, ur = (+s).bounding_box + assert np.all(np.isinf(ll)) + assert np.all(np.isinf(ur)) + + +def test_plane(): + s = openmc.Plane(A=1, B=2, C=-1, D=3, name='my plane') + assert s.a == 1 + assert s.b == 2 + assert s.c == -1 + assert s.d == 3 + assert s.boundary_type == 'transmission' + assert s.name == 'my plane' + assert s.type == 'plane' + + # Generic planes don't have well-defined bounding boxes + assert_infinite_bb(s) + + # evaluate method + x, y, z = (4, 3, 6) + assert s.evaluate((x, y, z)) == pytest.approx(s.a*x + s.b*y + s.c*z - s.d) + + # Make sure repr works + repr(s) + + +def test_xplane(): + s = openmc.XPlane(x0=3., boundary_type='reflective') + assert s.x0 == 3. + assert s.boundary_type == 'reflective' + + # Check bounding box + ll, ur = (+s).bounding_box + assert ll == pytest.approx((3., -np.inf, -np.inf)) + assert np.all(np.isinf(ur)) + ll, ur = (-s).bounding_box + assert ur == pytest.approx((3., np.inf, np.inf)) + assert np.all(np.isinf(ll)) + + # __contains__ on associated half-spaces + assert (5, 0, 0) in +s + assert (5, 0, 0) not in -s + assert (-2, 1, 10) in -s + assert (-2, 1, 10) not in +s + + # evaluate method + assert s.evaluate((5., 0., 0.)) == pytest.approx(2.) + + # Make sure repr works + repr(s) + + +def test_yplane(): + s = openmc.YPlane(y0=3.) + assert s.y0 == 3. + + # Check bounding box + ll, ur = (+s).bounding_box + assert ll == pytest.approx((-np.inf, 3., -np.inf)) + assert np.all(np.isinf(ur)) + ll, ur = s.bounding_box('-') + assert ur == pytest.approx((np.inf, 3., np.inf)) + assert np.all(np.isinf(ll)) + + # __contains__ on associated half-spaces + assert (0, 5, 0) in +s + assert (0, 5, 0) not in -s + assert (-2, 1, 10) in -s + assert (-2, 1, 10) not in +s + + # evaluate method + assert s.evaluate((0., 0., 0.)) == pytest.approx(-3.) + + +def test_zplane(): + s = openmc.ZPlane(z0=3.) + assert s.z0 == 3. + + # Check bounding box + ll, ur = (+s).bounding_box + assert ll == pytest.approx((-np.inf, -np.inf, 3.)) + assert np.all(np.isinf(ur)) + ll, ur = (-s).bounding_box + assert ur == pytest.approx((np.inf, np.inf, 3.)) + assert np.all(np.isinf(ll)) + + # __contains__ on associated half-spaces + assert (0, 0, 5) in +s + assert (0, 0, 5) not in -s + assert (-2, 1, -10) in -s + assert (-2, 1, -10) not in +s + + # evaluate method + assert s.evaluate((0., 0., 10.)) == pytest.approx(7.) + + # Make sure repr works + repr(s) + + +def test_xcylinder(): + y, z, r = 3, 5, 2 + s = openmc.XCylinder(y0=y, z0=z, R=r) + assert s.y0 == y + assert s.z0 == z + assert s.r == r + + # Check bounding box + ll, ur = (+s).bounding_box + assert np.all(np.isinf(ll)) + assert np.all(np.isinf(ur)) + ll, ur = (-s).bounding_box + assert ll == pytest.approx((-np.inf, y-r, z-r)) + assert ur == pytest.approx((np.inf, y+r, z+r)) + + # evaluate method + assert s.evaluate((0, y, z)) == pytest.approx(-r**2) + + # Make sure repr works + repr(s) + + +def test_periodic(): + x = openmc.XPlane(boundary_type='periodic') + y = openmc.YPlane(boundary_type='periodic') + x.periodic_surface = y + assert y.periodic_surface == x + with pytest.raises(TypeError): + x.periodic_surface = openmc.Sphere() + + +def test_ycylinder(): + x, z, r = 3, 5, 2 + s = openmc.YCylinder(x0=x, z0=z, R=r) + assert s.x0 == x + assert s.z0 == z + assert s.r == r + + # Check bounding box + ll, ur = (+s).bounding_box + assert np.all(np.isinf(ll)) + assert np.all(np.isinf(ur)) + ll, ur = (-s).bounding_box + assert ll == pytest.approx((x-r, -np.inf, z-r)) + assert ur == pytest.approx((x+r, np.inf, z+r)) + + # evaluate method + assert s.evaluate((x, 0, z)) == pytest.approx(-r**2) + + +def test_zcylinder(): + x, y, r = 3, 5, 2 + s = openmc.ZCylinder(x0=x, y0=y, R=r) + assert s.x0 == x + assert s.y0 == y + assert s.r == r + + # Check bounding box + ll, ur = (+s).bounding_box + assert np.all(np.isinf(ll)) + assert np.all(np.isinf(ur)) + ll, ur = (-s).bounding_box + assert ll == pytest.approx((x-r, y-r, -np.inf)) + assert ur == pytest.approx((x+r, y+r, np.inf)) + + # evaluate method + assert s.evaluate((x, y, 0)) == pytest.approx(-r**2) + + # Make sure repr works + repr(s) + + +def test_sphere(): + x, y, z, r = -3, 5, 6, 2 + s = openmc.Sphere(x0=x, y0=y, z0=z, R=r) + assert s.x0 == x + assert s.y0 == y + assert s.z0 == z + assert s.r == r + + # Check bounding box + ll, ur = (+s).bounding_box + assert np.all(np.isinf(ll)) + assert np.all(np.isinf(ur)) + ll, ur = (-s).bounding_box + assert ll == pytest.approx((x-r, y-r, z-r)) + assert ur == pytest.approx((x+r, y+r, z+r)) + + # evaluate method + assert s.evaluate((x, y, z)) == pytest.approx(-r**2) + + # Make sure repr works + repr(s) + + +def cone_common(apex, r2, cls): + x, y, z = apex + s = cls(x0=x, y0=y, z0=z, R2=r2) + assert s.x0 == x + assert s.y0 == y + assert s.z0 == z + assert s.r2 == r2 + + # Check bounding box + assert_infinite_bb(s) + + # evaluate method -- should be zero at apex + assert s.evaluate((x, y, z)) == pytest.approx(0.0) + + # Make sure repr works + repr(s) + + +def test_xcone(): + apex = (10, 0, 0) + r2 = 4 + cone_common(apex, r2, openmc.XCone) + + +def test_ycone(): + apex = (10, 0, 0) + r2 = 4 + cone_common(apex, r2, openmc.YCone) + + +def test_zcone(): + apex = (10, 0, 0) + r2 = 4 + cone_common(apex, r2, openmc.ZCone) + + +def test_quadric(): + # Make a sphere from a quadric + r = 10.0 + coeffs = {'a': 1, 'b': 1, 'c': 1, 'k': -r**2} + s = openmc.Quadric(**coeffs) + assert s.a == coeffs['a'] + assert s.b == coeffs['b'] + assert s.c == coeffs['c'] + assert s.k == coeffs['k'] + + # All other coeffs should be zero + for coeff in ('d', 'e', 'f', 'g', 'h', 'j'): + assert getattr(s, coeff) == 0.0 + + # Check bounding box + assert_infinite_bb(s) + + # evaluate method + assert s.evaluate((0., 0., 0.)) == pytest.approx(coeffs['k']) + assert s.evaluate((1., 1., 1.)) == pytest.approx(3 + coeffs['k']) diff --git a/tests/unit_tests/test_universe.py b/tests/unit_tests/test_universe.py new file mode 100644 index 0000000000..59e34e2017 --- /dev/null +++ b/tests/unit_tests/test_universe.py @@ -0,0 +1,113 @@ +import xml.etree.ElementTree as ET + +import numpy as np +import openmc +import pytest + +from tests.unit_tests import assert_unbounded + + +def test_basic(): + c1 = openmc.Cell() + c2 = openmc.Cell() + c3 = openmc.Cell() + u = openmc.Universe(name='cool', cells=(c1, c2, c3)) + assert u.name == 'cool' + + cells = set(u.cells.values()) + assert not (cells ^ {c1, c2, c3}) + + # Test __repr__ + repr(u) + + with pytest.raises(TypeError): + u.add_cell(openmc.Material()) + with pytest.raises(TypeError): + u.add_cells(c1) + + u.remove_cell(c3) + cells = set(u.cells.values()) + assert not (cells ^ {c1, c2}) + + u.clear_cells() + assert not set(u.cells) + + +def test_bounding_box(): + cyl1 = openmc.ZCylinder(R=1.0) + cyl2 = openmc.ZCylinder(R=2.0) + c1 = openmc.Cell(region=-cyl1) + c2 = openmc.Cell(region=+cyl1 & -cyl2) + + u = openmc.Universe(cells=[c1, c2]) + ll, ur = u.bounding_box + assert ll == pytest.approx((-2., -2., -np.inf)) + assert ur == pytest.approx((2., 2., np.inf)) + + u = openmc.Universe() + assert_unbounded(u) + + +def test_plot(run_in_tmpdir, sphere_model): + m = sphere_model.materials[0] + univ = sphere_model.geometry.root_universe + + colors = {m: 'limegreen'} + for basis in ('xy', 'yz', 'xz'): + univ.plot( + basis=basis, + pixels=(10, 10), + color_by='material', + colors=colors, + filename='test.png' + ) + + +def test_get_nuclides(uo2): + c = openmc.Cell(fill=uo2) + univ = openmc.Universe(cells=[c]) + nucs = univ.get_nuclides() + assert nucs == ['U235', 'O16'] + + +def test_cells(): + cells = [openmc.Cell() for i in range(5)] + cells2 = [openmc.Cell() for i in range(3)] + cells[0].fill = openmc.Universe(cells=cells2) + u = openmc.Universe(cells=cells) + assert not (set(u.cells.values()) ^ set(cells)) + + all_cells = set(u.get_all_cells().values()) + assert not (all_cells ^ set(cells + cells2)) + + +def test_get_all_materials(cell_with_lattice): + cells, mats, univ, lattice = cell_with_lattice + test_mats = set(univ.get_all_materials().values()) + assert not (test_mats ^ set(mats)) + + +def test_get_all_universes(): + c1 = openmc.Cell() + u1 = openmc.Universe(cells=[c1]) + c2 = openmc.Cell() + u2 = openmc.Universe(cells=[c2]) + c3 = openmc.Cell(fill=u1) + c4 = openmc.Cell(fill=u2) + u3 = openmc.Universe(cells=[c3, c4]) + + univs = set(u3.get_all_universes().values()) + assert not (univs ^ {u1, u2}) + + +def test_create_xml(cell_with_lattice): + cells = [openmc.Cell() for i in range(5)] + u = openmc.Universe(cells=cells) + + geom = ET.Element('geom') + u.create_xml_subelement(geom) + cell_elems = geom.findall('cell') + assert len(cell_elems) == len(cells) + assert all(c.get('universe') == str(u.id) for c in cell_elems) + assert not (set(c.get('id') for c in cell_elems) ^ + set(str(c.id) for c in cells)) diff --git a/tests/update_results.py b/tests/update_results.py deleted file mode 100755 index 5656003331..0000000000 --- a/tests/update_results.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python - -from __future__ import print_function - -import os -import re -from subprocess import Popen, call, STDOUT, PIPE -from glob import glob -from optparse import OptionParser - -parser = OptionParser() -parser.add_option('-R', '--tests-regex', dest='regex_tests', - help="Run tests matching regular expression. \ - Test names are the directories present in tests folder.\ - This uses standard regex syntax to select tests.") -(opts, args) = parser.parse_args() -cwd = os.getcwd() - -# Terminal color configurations -OKGREEN = '\033[92m' -FAIL = '\033[91m' -ENDC = '\033[0m' -BOLD = '\033[1m' - -# Get a list of all test folders -folders = glob('test_*') - -# Check to see if a subset of tests is specified on command line -if opts.regex_tests is not None: - folders = [item for item in folders if re.search(opts.regex_tests, item)] - -# Loop around directories -for adir in sorted(folders): - - # Go into that directory - os.chdir(adir) - pwd = os.path.abspath(os.path.dirname('settings.xml')) - os.putenv('PWD', pwd) - - # Print status to screen - print(adir, end="") - sz = len(adir) - for i in range(35 - sz): - print('.', end="") - - # Find the test executable - test_exec = glob('test_*.py') - assert len(test_exec) == 1, 'There must be only one test executable per ' \ - 'test directory' - - # Update the test results - proc = Popen(['python', test_exec[0], '--update']) - returncode = proc.wait() - if returncode == 0: - print(BOLD + OKGREEN + "[OK]" + ENDC) - else: - print(BOLD + FAIL + "[FAILED]" + ENDC) - - # Go back a directory - os.chdir('..') diff --git a/tests/valgrind.supp b/tests/valgrind.supp deleted file mode 100644 index ad302c5397..0000000000 --- a/tests/valgrind.supp +++ /dev/null @@ -1,63 +0,0 @@ -{ - Create HDF5 statepoint file - Memcheck:Addr4 - fun:H5_build_extpath - fun:H5F_open - fun:H5Fcreate - fun:h5fcreate_c_ - fun:__h5f_MOD_h5fcreate_f - fun:__hdf5_interface_MOD_hdf5_file_create - fun:__output_interface_MOD_file_create - fun:__state_point_MOD_write_state_point - fun:__eigenvalue_MOD_finalize_batch - fun:__eigenvalue_MOD_run_eigenvalue - fun:MAIN__ - fun:main -} -{ - Open HDF5 statepoint file - Memcheck:Addr4 - fun:H5_build_extpath - fun:H5F_open - fun:H5Fopen - fun:h5fopen_c_ - fun:__h5f_MOD_h5fopen_f - fun:__hdf5_interface_MOD_hdf5_file_open - fun:__output_interface_MOD_file_open - fun:__state_point_MOD_write_source_point - fun:__eigenvalue_MOD_finalize_batch - fun:__eigenvalue_MOD_run_eigenvalue - fun:MAIN__ - fun:main -} -{ - Create HDF5 summary file - Memcheck:Addr4 - fun:H5_build_extpath - fun:H5F_open - fun:H5Fcreate - fun:h5fcreate_c_ - fun:__h5f_MOD_h5fcreate_f - fun:__hdf5_interface_MOD_hdf5_file_create - fun:__output_interface_MOD_file_create - fun:__hdf5_summary_MOD_hdf5_write_summary - fun:__initialize_MOD_initialize_run - fun:MAIN__ - fun:main -} -{ - Create HDF5 track file - Memcheck:Addr4 - fun:H5_build_extpath - fun:H5F_open - fun:H5Fcreate - fun:h5fcreate_c_ - fun:__h5f_MOD_h5fcreate_f - fun:__hdf5_interface_MOD_hdf5_file_create - fun:__output_interface_MOD_file_create - fun:__track_output_MOD_finalize_particle_track - fun:__tracking_MOD_transport - fun:__eigenvalue_MOD_run_eigenvalue - fun:MAIN__ - fun:main -} diff --git a/tools/ci/travis-before-script.sh b/tools/ci/travis-before-script.sh index bbf4980b0f..d0df06f2fd 100755 --- a/tools/ci/travis-before-script.sh +++ b/tools/ci/travis-before-script.sh @@ -1,6 +1,10 @@ #!/bin/bash set -ex +# Allow tests that require GUI as described at: +# https://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-xvfb-to-Run-Tests-That-Require-a-GUI +sh -e /etc/init.d/xvfb start + # Download NNDC HDF5 data if [[ ! -e $HOME/nndc_hdf5/cross_sections.xml ]]; then wget https://anl.box.com/shared/static/a6sw2cep34wlz6b9i9jwiotaqoayxcxt.xz -O - | tar -C $HOME -xvJ diff --git a/tools/ci/travis-install.py b/tools/ci/travis-install.py new file mode 100644 index 0000000000..15e4d576b7 --- /dev/null +++ b/tools/ci/travis-install.py @@ -0,0 +1,70 @@ +import os +import shutil +import subprocess + + +def which(program): + def is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + path = path.strip('"') + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + return None + + +def install(omp=False, mpi=False, phdf5=False): + # Create build directory and change to it + shutil.rmtree('build', ignore_errors=True) + os.mkdir('build') + os.chdir('build') + + # Build in debug mode by default + cmake_cmd = ['cmake', '-Ddebug=on'] + + # Turn off OpenMP if specified + if not omp: + cmake_cmd.append('-Dopenmp=off') + + # Use MPI wrappers when building in parallel + if mpi: + os.environ['FC'] = 'mpifort' if which('mpifort') else 'mpif90' + os.environ['CC'] = 'mpicc' + os.environ['CXX'] = 'mpicxx' + + # Tell CMake to prefer parallel HDF5 if specified + if phdf5: + if not mpi: + raise ValueError('Parallel HDF5 must be used in ' + 'conjunction with MPI.') + cmake_cmd.append('-DHDF5_PREFER_PARALLEL=ON') + else: + cmake_cmd.append('-DHDF5_PREFER_PARALLEL=OFF') + + # Build and install + cmake_cmd.append('..') + print(' '.join(cmake_cmd)) + subprocess.check_call(cmake_cmd) + subprocess.check_call(['make', '-j']) + subprocess.check_call(['sudo', 'make', 'install']) + + +def main(): + # Convert Travis matrix environment variables into arguments for install() + omp = (os.environ.get('OMP') == 'y') + mpi = (os.environ.get('MPI') == 'y') + phdf5 = (os.environ.get('PHDF5') == 'y') + + # Build and install + install(omp, mpi, phdf5) + + +if __name__ == '__main__': + main() diff --git a/tools/ci/travis-install.sh b/tools/ci/travis-install.sh index 354ae7f7f4..3efadd6f36 100755 --- a/tools/ci/travis-install.sh +++ b/tools/ci/travis-install.sh @@ -15,5 +15,11 @@ if [[ "$TRAVIS_PYTHON_VERSION" == "3.4" ]]; then pip install pandas==0.20.3 fi -# Install OpenMC in editable mode +# Build and install OpenMC executable +python tools/ci/travis-install.py + +# Install Python API in editable mode pip install -e .[test] + +# For uploading to coveralls +pip install python-coveralls diff --git a/tools/ci/travis-script.sh b/tools/ci/travis-script.sh index 978a2811c4..ee445b517f 100755 --- a/tools/ci/travis-script.sh +++ b/tools/ci/travis-script.sh @@ -1,8 +1,14 @@ #!/bin/bash set -ex -cd tests -if [[ $TRAVIS_PYTHON_VERSION == "3.4" && $OPENMC_CONFIG == '^hdf5-debug$' ]]; then - ./check_source.py + +# Run source check +if [[ $TRAVIS_PYTHON_VERSION == "3.4" && $OMP == 'n' && $MPI == 'n' ]]; then + pushd tests && python check_source.py && popd +fi + +# Run regression and unit tests +if [[ $MPI == 'y' ]]; then + pytest --cov=openmc -v --mpi tests +else + pytest --cov=openmc -v tests fi -./run_tests.py -C $OPENMC_CONFIG -j 2 -pytest --cov=../openmc -v unit_tests/